History

Improve tab navigation

Closes #396.

Thibaut Courouble committed 10 years ago

Commit 39b98461 parent 87ede1f12 files · +54 −27Browse files

Unified · Split

12 files
  1. assets/javascripts/templates/pages/about_tmpl.coffee +1 −1
  2. assets/javascripts/templates/pages/root_tmpl.coffee.erb +2 −2
  3. assets/javascripts/templates/sidebar_tmpl.coffee +9 −9
  4. assets/javascripts/views/layout/nav.coffee +2 −0
  5. assets/javascripts/views/search/search_scope.coffee +5 −4
  6. assets/javascripts/views/sidebar/doc_picker.coffee +17 −4
  7. assets/javascripts/views/sidebar/sidebar.coffee +2 −0
  8. assets/stylesheets/components/_header.scss +2 −2
  9. assets/stylesheets/components/_sidebar.scss +4 −2
  10. assets/stylesheets/global/_base.scss +7 −0
  11. assets/stylesheets/global/_classes.scss +1 −1
  12. views/app.erb +2 −2
assets/javascripts/templates/pages/about_tmpl.coffee+1−1
@@ -15,7 +15,7 @@ app.templates.aboutPage = -> """ · 14 unmodified lines
1515 <ul>
1616 <li>Created and maintained by <a href="http://thibaut.me">Thibaut Courouble</a>
1717 <li>Free and <a href="https://github.com/Thibaut/devdocs">open source</a>
18− <iframe class="_github-btn" src="https://ghbtns.com/github-btn.html?user=Thibaut&repo=devdocs&type=watch&count=true" allowtransparency="true" frameborder="0" scrolling="0" width="100" height="20"></iframe>
18+ <iframe class="_github-btn" src="https://ghbtns.com/github-btn.html?user=Thibaut&repo=devdocs&type=watch&count=true" allowtransparency="true" frameborder="0" scrolling="0" width="100" height="20" tabindex="-1"></iframe>
1919 </ul>
2020 <p>To keep up-to-date with the latest news:
2121 <ul>
assets/javascripts/templates/pages/root_tmpl.coffee.erb+2−2
@@ -3,7 +3,7 @@ app.templates.splash = """<div class="_splash-title">DevDocs</div>""" · 2 unmodified lines
33 <% if App.development? %>
44 app.templates.intro = """
55 <div class="_intro"><div class="_intro-message">
6− <a class="_intro-hide" data-hide-intro>Stop showing this message</a>
6+ <a href="#" class="_intro-hide" data-hide-intro>Stop showing this message</a>
77 <h2 class="_intro-title">Hi there!</h2>
88 <p>Thanks for downloading DevDocs. Here are a few things you should know:
99 <ol class="_intro-list">
@@ -27,7 +27,7 @@ app.templates.intro = """ · 17 unmodified lines
2727 <% else %>
2828 app.templates.intro = """
2929 <div class="_intro"><div class="_intro-message">
30− <a class="_intro-hide" data-hide-intro>Stop showing this message</a>
30+ <a href="#" class="_intro-hide" data-hide-intro>Stop showing this message</a>
3131 <h2 class="_intro-title">Welcome!</h2>
3232 <p>DevDocs combines multiple API documentations in a fast, organized, and searchable interface.
3333 Here's what you should know before you start:
assets/javascripts/templates/sidebar_tmpl.coffee+9−9
@@ -3,7 +3,7 @@ templates = app.templates · 2 unmodified lines
33 templates.sidebarDoc = (doc, options = {}) ->
44 link = """<a href="#{doc.fullPath()}" class="_list-item _icon-#{doc.icon} """
55 link += if options.disabled then '_list-disabled' else '_list-dir'
6− link += """" data-slug="#{doc.slug}" title="#{doc.fullName}">"""
6+ link += """" data-slug="#{doc.slug}" title="#{doc.fullName}" tabindex="-1">"""
77 if options.disabled
88 link += """<span class="_list-enable" data-enable="#{doc.slug}">Enable</span>"""
99 else
@@ -14,10 +14,10 @@ templates.sidebarDoc = (doc, options = {}) -> · 4 unmodified lines
1414 link + "</span></a>"
1515
1616 templates.sidebarType = (type) ->
17− """<a href="#{type.fullPath()}" class="_list-item _list-dir" data-slug="#{type.slug}"><span class="_list-arrow"></span><span class="_list-count">#{type.count}</span><span class="_list-text">#{type.name}</span></a>"""
17+ """<a href="#{type.fullPath()}" class="_list-item _list-dir" data-slug="#{type.slug}" tabindex="-1"><span class="_list-arrow"></span><span class="_list-count">#{type.count}</span><span class="_list-text">#{type.name}</span></a>"""
1818
1919 templates.sidebarEntry = (entry) ->
20− """<a href="#{entry.fullPath()}" class="_list-item _list-hover">#{$.escape entry.name}</a>"""
20+ """<a href="#{entry.fullPath()}" class="_list-item _list-hover" tabindex="-1">#{$.escape entry.name}</a>"""
2121
2222 templates.sidebarResult = (entry) ->
2323 addons = if entry.isIndex() and app.disabledDocs.contains(entry.doc)
@@ -25,17 +25,17 @@ templates.sidebarResult = (entry) -> · 1 unmodified line
2525 else
2626 """<span class="_list-reveal" data-reset-list title="Reveal in list"></span>"""
2727 addons += """<span class="_list-count">#{entry.doc.short_version}</span>""" if entry.doc.version and not entry.isIndex()
28− """<a href="#{entry.fullPath()}" class="_list-item _list-hover _list-result _icon-#{entry.doc.icon}">#{addons}<span class="_list-text">#{$.escape entry.name}</span></a>"""
28+ """<a href="#{entry.fullPath()}" class="_list-item _list-hover _list-result _icon-#{entry.doc.icon}" tabindex="-1">#{addons}<span class="_list-text">#{$.escape entry.name}</span></a>"""
2929
3030 templates.sidebarNoResults = ->
3131 html = """ <div class="_list-note">No results.</div> """
3232 html += """
33− <div class="_list-note">Note: documentations must be <a class="_list-note-link" data-pick-docs>enabled</a> to appear in the search.</div>
33+ <div class="_list-note">Note: documentations must be <a href="#" class="_list-note-link" data-pick-docs>enabled</a> to appear in the search.</div>
3434 """ unless app.isSingleDoc() or app.disabledDocs.isEmpty()
3535 html
3636
3737 templates.sidebarPageLink = (count) ->
38− """<span class="_list-item _list-pagelink">Show more\u2026 (#{count})</span>"""
38+ """<span role="link" class="_list-item _list-pagelink">Show more\u2026 (#{count})</span>"""
3939
4040 templates.sidebarLabel = (doc, options = {}) ->
4141 label = """<label class="_list-item"""
@@ -47,7 +47,7 @@ templates.sidebarLabel = (doc, options = {}) -> · 5 unmodified lines
4747 templates.sidebarVersionedDoc = (doc, versions, options = {}) ->
4848 html = """<div class="_list-item _list-dir _list-rdir _icon-#{doc.icon}"""
4949 html += " open" if options.open
50− html + """"><span class="_list-arrow"></span>#{doc.name}</div><div class="_list _list-sub">#{versions}</div>"""
50+ html + """" tabindex="0"><span class="_list-arrow"></span>#{doc.name}</div><div class="_list _list-sub">#{versions}</div>"""
5151
5252 templates.sidebarDisabled = (options) ->
5353 """<h6 class="_list-title"><span class="_list-arrow"></span>Disabled (#{options.count})</h6>"""
@@ -56,7 +56,7 @@ templates.sidebarDisabledList = (html) -> · 2 unmodified lines
5656 """<div class="_disabled-list">#{html}</div>"""
5757
5858 templates.sidebarDisabledVersionedDoc = (doc, versions) ->
59− """<a class="_list-item _list-dir _icon-#{doc.icon} _list-disabled" data-slug="#{doc.slug_without_version}"><span class="_list-arrow"></span>#{doc.name}</a><div class="_list _list-sub">#{versions}</div>"""
59+ """<a class="_list-item _list-dir _icon-#{doc.icon} _list-disabled" data-slug="#{doc.slug_without_version}" tabindex="-1"><span class="_list-arrow"></span>#{doc.name}</a><div class="_list _list-sub">#{versions}</div>"""
6060
6161 templates.sidebarPickerNote = """
6262 <div class="_list-note">Tip: for faster and better search results, select only the docs you need.</div>
@@ -67,9 +67,9 @@ sidebarFooter = (html) -> """<div class="_sidebar-footer">#{html}</div>""" · 4 unmodified lines
6767
6868 templates.sidebarSettings = ->
6969 sidebarFooter """
70+ <button type="button" class="_sidebar-footer-link _sidebar-footer-edit" data-pick-docs>Select documentation</button>
7071 <button type="button" class="_sidebar-footer-link _sidebar-footer-light" title="Toggle light" data-light>Toggle light</button>
7172 <button type="button" class="_sidebar-footer-link _sidebar-footer-layout" title="Toggle layout" data-layout>Toggle layout</button>
72− <a href="#" class="_sidebar-footer-link _sidebar-footer-edit" data-pick-docs>Select documentation</a>
7373 """
7474
7575 templates.sidebarSave = ->
assets/javascripts/views/layout/nav.coffee+2−0
@@ -9,11 +9,13 @@ class app.views.Nav extends app.View · 8 unmodified lines
99 @deselect()
1010 if @current = @find "a[href='#{href}']"
1111 @current.classList.add @constructor.activeClass
12+ @current.setAttribute 'tabindex', '-1'
1213 return
1314
1415 deselect: ->
1516 if @current
1617 @current.classList.remove @constructor.activeClass
18+ @current.removeAttribute 'tabindex'
1719 @current = null
1820 return
1921
assets/javascripts/views/search/search_scope.coffee+5−4
@@ -84,10 +84,11 @@ class app.views.SearchScope extends app.View · 83 unmodified lines
8484 if @doc and not @input.value
8585 $.stopEvent(event)
8686 @reset()
87− else if event.which is 9 or # tab
88− event.which is 32 and (app.isMobile() or $.isTouchScreen()) # space
89− $.stopEvent(event)
90− @search @input.value[0...@input.selectionStart]
87+ else if not @doc and @input.value
88+ if event.which is 9 or # tab
89+ event.which is 32 and (app.isMobile() or $.isTouchScreen()) # space
90+ @search @input.value[0...@input.selectionStart]
91+ $.stopEvent(event) if @doc
9192 return
9293
9394 extractHashValue: ->
assets/javascripts/views/sidebar/doc_picker.coffee+17−4
@@ -21,14 +21,14 @@ class app.views.DocPicker extends app.View · 20 unmodified lines
2121 @render()
2222 @findByTag('input')?.focus()
2323 app.appCache?.on 'progress', @onAppCacheProgress
24− $.on @el, 'focus', @onFocus, true
24+ $.on @el, 'focus', @onDOMFocus, true
2525 return
2626
2727 deactivate: ->
2828 if super
2929 @empty()
3030 app.appCache?.off 'progress', @onAppCacheProgress
31− $.off @el, 'focus', @onFocus, true
31+ $.off @el, 'focus', @onDOMFocus, true
3232 return
3333
3434 render: ->
@@ -84,14 +84,27 @@ class app.views.DocPicker extends app.View · 49 unmodified lines
8484 input.name
8585
8686 onClick: (event) =>
87+ if @focusTimeout
88+ clearTimeout @focusTimeout
89+ @focusTimeout = null
8790 return if event.which isnt 1
8891 if event.target is @saveLink
8992 $.stopEvent(event)
9093 @save()
9194 return
9295
93− onFocus: (event) ->
94− $.scrollTo event.target.parentNode, null, 'continuous', bottomGap: 2
96+ onDOMFocus: (event) =>
97+ target = event.target
98+ if target.tagName is 'INPUT'
99+ $.scrollTo target.parentNode, null, 'continuous', bottomGap: 2
100+ else if target.classList.contains(app.views.ListFold.targetClass)
101+ target.blur()
102+ @focusTimeout = setTimeout =>
103+ @listFold.open(target) unless target.classList.contains(app.views.ListFold.activeClass)
104+ $('input', target.nextElementSibling).focus()
105+ @focusTimeout = null
106+ , 10
107+ return
95108
96109 onEnter: =>
97110 @save()
assets/javascripts/views/sidebar/sidebar.coffee+2−0
@@ -101,9 +101,11 @@ class app.views.Sidebar extends app.View · 100 unmodified lines
101101 @reset()
102102 else if event.target.hasAttribute? 'data-light'
103103 $.stopEvent(event)
104+ document.activeElement?.blur()
104105 app.document.toggleLight()
105106 else if event.target.hasAttribute? 'data-layout'
106107 $.stopEvent(event)
108+ document.activeElement?.blur()
107109 app.document.toggleLayout()
108110 return
109111
assets/stylesheets/components/_header.scss+2−2
@@ -13,8 +13,6 @@ · 12 unmodified lines
1313 background: $headerBackground;
1414 border-bottom: 1px solid $headerBorder;
1515 @extend %user-select-none;
16−
17− a:focus { outline: 0; }
1816 }
1917
2018 //
@@ -58,6 +56,8 @@ · 37 unmodified lines
5856 }
5957
6058 ._nav-current {
59+ outline: 0;
60+
6161 &:before, &:after { content: ''; }
6262 }
6363
assets/stylesheets/components/_sidebar.scss+4−2
@@ -32,8 +32,6 @@ · 31 unmodified lines
3232 }
3333 }
3434
35− a:focus { outline: 0; }
36−
3735 ._sidebar-hidden & {
3836 display: none;
3937 }
@@ -88,6 +86,8 @@ · 48 unmodified lines
8886 min-height: 100%;
8987 padding-bottom: 3.5rem;
9088 }
89+
90+ a:focus { outline: 0; }
9191 }
9292
9393 ._list-title {
@@ -422,6 +422,8 @@ · 328 unmodified lines
422422 }
423423
424424 ._sidebar-footer-edit {
425+ display: inline-block;
426+
425427 @if $style == 'dark' {
426428 &:before { @extend %icon-settings-white; }
427429 } @else {
assets/stylesheets/global/_base.scss+7−0
@@ -159,6 +159,8 @@ td { · 158 unmodified lines
159159 > pre:last-child, > p:last-child, > ul:last-child, > ol:last-child { margin-bottom: 0; }
160160 }
161161
162+section, main { outline: 0; }
163+
162164 input, button {
163165 margin: 0;
164166 font-family: inherit;
@@ -179,6 +181,11 @@ button, input[type="search"] { · 14 unmodified lines
179181 -moz-appearance: none;
180182 }
181183
184+button:focus {
185+ outline: 1px dotted;
186+ outline: -webkit-focus-ring-color auto 5px;
187+}
188+
182189 input[type="search"]::-webkit-search-cancel-button,
183190 input[type="search"]::-webkit-search-decoration {
184191 -webkit-appearance: none;
assets/stylesheets/global/_classes.scss+1−1
@@ -22,7 +22,7 @@ · 21 unmodified lines
2222 %hide-text {
2323 white-space: nowrap;
2424 overflow: hidden;
25− color: transparent !important;
25+ text-indent: -1000px;
2626 @extend %user-select-none;
2727 }
2828
views/app.erb+2−2
@@ -19,7 +19,7 @@ · 18 unmodified lines
1919 <a href="/help" class="_nav-link">Tips</a>
2020 </nav>
2121 </header>
22− <section class="_sidebar">
22+ <section class="_sidebar" tabindex="-1">
2323 <div class="_list" role="navigation">
2424 <% unless @doc %>
2525 <% App.docs.each do |slug, doc| %>
@@ -29,7 +29,7 @@ · 3 unmodified lines
2929 </div>
3030 </section>
3131 <div class="_container" role="document">
32− <main class="_content _content-loading" role="main"></main>
32+ <main class="_content _content-loading" role="main" tabindex="1"></main>
3333 </div>
3434 </div>
3535 <style data-size="<%= app_size %>" data-resizer>