Closes #396.
Unified · Split
| @@ -15,7 +15,7 @@ app.templates.aboutPage = -> """ · 14 unmodified lines | ||
| 15 | 15 | <ul> |
| 16 | 16 | <li>Created and maintained by <a href="http://thibaut.me">Thibaut Courouble</a> |
| 17 | 17 | <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> | |
| 19 | 19 | </ul> |
| 20 | 20 | <p>To keep up-to-date with the latest news: |
| 21 | 21 | <ul> |
| @@ -3,7 +3,7 @@ app.templates.splash = """<div class="_splash-title">DevDocs</div>""" · 2 unmodified lines | ||
| 3 | 3 | <% if App.development? %> |
| 4 | 4 | app.templates.intro = """ |
| 5 | 5 | <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> | |
| 7 | 7 | <h2 class="_intro-title">Hi there!</h2> |
| 8 | 8 | <p>Thanks for downloading DevDocs. Here are a few things you should know: |
| 9 | 9 | <ol class="_intro-list"> |
| @@ -27,7 +27,7 @@ app.templates.intro = """ · 17 unmodified lines | ||
| 27 | 27 | <% else %> |
| 28 | 28 | app.templates.intro = """ |
| 29 | 29 | <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> | |
| 31 | 31 | <h2 class="_intro-title">Welcome!</h2> |
| 32 | 32 | <p>DevDocs combines multiple API documentations in a fast, organized, and searchable interface. |
| 33 | 33 | Here's what you should know before you start: |
| @@ -3,7 +3,7 @@ templates = app.templates · 2 unmodified lines | ||
| 3 | 3 | templates.sidebarDoc = (doc, options = {}) -> |
| 4 | 4 | link = """<a href="#{doc.fullPath()}" class="_list-item _icon-#{doc.icon} """ |
| 5 | 5 | 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">""" | |
| 7 | 7 | if options.disabled |
| 8 | 8 | link += """<span class="_list-enable" data-enable="#{doc.slug}">Enable</span>""" |
| 9 | 9 | else |
| @@ -14,10 +14,10 @@ templates.sidebarDoc = (doc, options = {}) -> · 4 unmodified lines | ||
| 14 | 14 | link + "</span></a>" |
| 15 | 15 | |
| 16 | 16 | 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>""" | |
| 18 | 18 | |
| 19 | 19 | 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>""" | |
| 21 | 21 | |
| 22 | 22 | templates.sidebarResult = (entry) -> |
| 23 | 23 | addons = if entry.isIndex() and app.disabledDocs.contains(entry.doc) |
| @@ -25,17 +25,17 @@ templates.sidebarResult = (entry) -> · 1 unmodified line | ||
| 25 | 25 | else |
| 26 | 26 | """<span class="_list-reveal" data-reset-list title="Reveal in list"></span>""" |
| 27 | 27 | 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>""" | |
| 29 | 29 | |
| 30 | 30 | templates.sidebarNoResults = -> |
| 31 | 31 | html = """ <div class="_list-note">No results.</div> """ |
| 32 | 32 | 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> | |
| 34 | 34 | """ unless app.isSingleDoc() or app.disabledDocs.isEmpty() |
| 35 | 35 | html |
| 36 | 36 | |
| 37 | 37 | 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>""" | |
| 39 | 39 | |
| 40 | 40 | templates.sidebarLabel = (doc, options = {}) -> |
| 41 | 41 | label = """<label class="_list-item""" |
| @@ -47,7 +47,7 @@ templates.sidebarLabel = (doc, options = {}) -> · 5 unmodified lines | ||
| 47 | 47 | templates.sidebarVersionedDoc = (doc, versions, options = {}) -> |
| 48 | 48 | html = """<div class="_list-item _list-dir _list-rdir _icon-#{doc.icon}""" |
| 49 | 49 | 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>""" | |
| 51 | 51 | |
| 52 | 52 | templates.sidebarDisabled = (options) -> |
| 53 | 53 | """<h6 class="_list-title"><span class="_list-arrow"></span>Disabled (#{options.count})</h6>""" |
| @@ -56,7 +56,7 @@ templates.sidebarDisabledList = (html) -> · 2 unmodified lines | ||
| 56 | 56 | """<div class="_disabled-list">#{html}</div>""" |
| 57 | 57 | |
| 58 | 58 | 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>""" | |
| 60 | 60 | |
| 61 | 61 | templates.sidebarPickerNote = """ |
| 62 | 62 | <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 | ||
| 67 | 67 | |
| 68 | 68 | templates.sidebarSettings = -> |
| 69 | 69 | sidebarFooter """ |
| 70 | + <button type="button" class="_sidebar-footer-link _sidebar-footer-edit" data-pick-docs>Select documentation</button> | |
| 70 | 71 | <button type="button" class="_sidebar-footer-link _sidebar-footer-light" title="Toggle light" data-light>Toggle light</button> |
| 71 | 72 | <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> | |
| 73 | 73 | """ |
| 74 | 74 | |
| 75 | 75 | templates.sidebarSave = -> |
| @@ -9,11 +9,13 @@ class app.views.Nav extends app.View · 8 unmodified lines | ||
| 9 | 9 | @deselect() |
| 10 | 10 | if @current = @find "a[href='#{href}']" |
| 11 | 11 | @current.classList.add @constructor.activeClass |
| 12 | + @current.setAttribute 'tabindex', '-1' | |
| 12 | 13 | return |
| 13 | 14 | |
| 14 | 15 | deselect: -> |
| 15 | 16 | if @current |
| 16 | 17 | @current.classList.remove @constructor.activeClass |
| 18 | + @current.removeAttribute 'tabindex' | |
| 17 | 19 | @current = null |
| 18 | 20 | return |
| 19 | 21 | |
| @@ -84,10 +84,11 @@ class app.views.SearchScope extends app.View · 83 unmodified lines | ||
| 84 | 84 | if @doc and not @input.value |
| 85 | 85 | $.stopEvent(event) |
| 86 | 86 | @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 | |
| 91 | 92 | return |
| 92 | 93 | |
| 93 | 94 | extractHashValue: -> |
| @@ -21,14 +21,14 @@ class app.views.DocPicker extends app.View · 20 unmodified lines | ||
| 21 | 21 | @render() |
| 22 | 22 | @findByTag('input')?.focus() |
| 23 | 23 | app.appCache?.on 'progress', @onAppCacheProgress |
| 24 | − $.on @el, 'focus', @onFocus, true | |
| 24 | + $.on @el, 'focus', @onDOMFocus, true | |
| 25 | 25 | return |
| 26 | 26 | |
| 27 | 27 | deactivate: -> |
| 28 | 28 | if super |
| 29 | 29 | @empty() |
| 30 | 30 | app.appCache?.off 'progress', @onAppCacheProgress |
| 31 | − $.off @el, 'focus', @onFocus, true | |
| 31 | + $.off @el, 'focus', @onDOMFocus, true | |
| 32 | 32 | return |
| 33 | 33 | |
| 34 | 34 | render: -> |
| @@ -84,14 +84,27 @@ class app.views.DocPicker extends app.View · 49 unmodified lines | ||
| 84 | 84 | input.name |
| 85 | 85 | |
| 86 | 86 | onClick: (event) => |
| 87 | + if @focusTimeout | |
| 88 | + clearTimeout @focusTimeout | |
| 89 | + @focusTimeout = null | |
| 87 | 90 | return if event.which isnt 1 |
| 88 | 91 | if event.target is @saveLink |
| 89 | 92 | $.stopEvent(event) |
| 90 | 93 | @save() |
| 91 | 94 | return |
| 92 | 95 | |
| 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 | |
| 95 | 108 | |
| 96 | 109 | onEnter: => |
| 97 | 110 | @save() |
| @@ -101,9 +101,11 @@ class app.views.Sidebar extends app.View · 100 unmodified lines | ||
| 101 | 101 | @reset() |
| 102 | 102 | else if event.target.hasAttribute? 'data-light' |
| 103 | 103 | $.stopEvent(event) |
| 104 | + document.activeElement?.blur() | |
| 104 | 105 | app.document.toggleLight() |
| 105 | 106 | else if event.target.hasAttribute? 'data-layout' |
| 106 | 107 | $.stopEvent(event) |
| 108 | + document.activeElement?.blur() | |
| 107 | 109 | app.document.toggleLayout() |
| 108 | 110 | return |
| 109 | 111 | |
| @@ -13,8 +13,6 @@ · 12 unmodified lines | ||
| 13 | 13 | background: $headerBackground; |
| 14 | 14 | border-bottom: 1px solid $headerBorder; |
| 15 | 15 | @extend %user-select-none; |
| 16 | − | |
| 17 | − a:focus { outline: 0; } | |
| 18 | 16 | } |
| 19 | 17 | |
| 20 | 18 | // |
| @@ -58,6 +56,8 @@ · 37 unmodified lines | ||
| 58 | 56 | } |
| 59 | 57 | |
| 60 | 58 | ._nav-current { |
| 59 | + outline: 0; | |
| 60 | + | |
| 61 | 61 | &:before, &:after { content: ''; } |
| 62 | 62 | } |
| 63 | 63 | |
| @@ -32,8 +32,6 @@ · 31 unmodified lines | ||
| 32 | 32 | } |
| 33 | 33 | } |
| 34 | 34 | |
| 35 | − a:focus { outline: 0; } | |
| 36 | − | |
| 37 | 35 | ._sidebar-hidden & { |
| 38 | 36 | display: none; |
| 39 | 37 | } |
| @@ -88,6 +86,8 @@ · 48 unmodified lines | ||
| 88 | 86 | min-height: 100%; |
| 89 | 87 | padding-bottom: 3.5rem; |
| 90 | 88 | } |
| 89 | + | |
| 90 | + a:focus { outline: 0; } | |
| 91 | 91 | } |
| 92 | 92 | |
| 93 | 93 | ._list-title { |
| @@ -422,6 +422,8 @@ · 328 unmodified lines | ||
| 422 | 422 | } |
| 423 | 423 | |
| 424 | 424 | ._sidebar-footer-edit { |
| 425 | + display: inline-block; | |
| 426 | + | |
| 425 | 427 | @if $style == 'dark' { |
| 426 | 428 | &:before { @extend %icon-settings-white; } |
| 427 | 429 | } @else { |
| @@ -159,6 +159,8 @@ td { · 158 unmodified lines | ||
| 159 | 159 | > pre:last-child, > p:last-child, > ul:last-child, > ol:last-child { margin-bottom: 0; } |
| 160 | 160 | } |
| 161 | 161 | |
| 162 | +section, main { outline: 0; } | |
| 163 | + | |
| 162 | 164 | input, button { |
| 163 | 165 | margin: 0; |
| 164 | 166 | font-family: inherit; |
| @@ -179,6 +181,11 @@ button, input[type="search"] { · 14 unmodified lines | ||
| 179 | 181 | -moz-appearance: none; |
| 180 | 182 | } |
| 181 | 183 | |
| 184 | +button:focus { | |
| 185 | + outline: 1px dotted; | |
| 186 | + outline: -webkit-focus-ring-color auto 5px; | |
| 187 | +} | |
| 188 | + | |
| 182 | 189 | input[type="search"]::-webkit-search-cancel-button, |
| 183 | 190 | input[type="search"]::-webkit-search-decoration { |
| 184 | 191 | -webkit-appearance: none; |
| @@ -22,7 +22,7 @@ · 21 unmodified lines | ||
| 22 | 22 | %hide-text { |
| 23 | 23 | white-space: nowrap; |
| 24 | 24 | overflow: hidden; |
| 25 | − color: transparent !important; | |
| 25 | + text-indent: -1000px; | |
| 26 | 26 | @extend %user-select-none; |
| 27 | 27 | } |
| 28 | 28 | |
| @@ -19,7 +19,7 @@ · 18 unmodified lines | ||
| 19 | 19 | <a href="/help" class="_nav-link">Tips</a> |
| 20 | 20 | </nav> |
| 21 | 21 | </header> |
| 22 | − <section class="_sidebar"> | |
| 22 | + <section class="_sidebar" tabindex="-1"> | |
| 23 | 23 | <div class="_list" role="navigation"> |
| 24 | 24 | <% unless @doc %> |
| 25 | 25 | <% App.docs.each do |slug, doc| %> |
| @@ -29,7 +29,7 @@ · 3 unmodified lines | ||
| 29 | 29 | </div> |
| 30 | 30 | </section> |
| 31 | 31 | <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> | |
| 33 | 33 | </div> |
| 34 | 34 | </div> |
| 35 | 35 | <style data-size="<%= app_size %>" data-resizer> |