markdowndocs 0.10.0 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6ca89d7b3d1decf28b07cfaccfd1e2e0e0924f8ef7d56d1374e3cb600cbd4933
4
- data.tar.gz: 036b99af3190b230d1ceb327171300758b76d09392a4d28b32a9a0129b9cdaf3
3
+ metadata.gz: eb123f5ef96df8917be85966728453e124ae2f418acb7458a583c3d42254b068
4
+ data.tar.gz: e672f4cf03ee6a4fbb37a5cd2d924be8fb903256de7834adf36bfcf4e8877541
5
5
  SHA512:
6
- metadata.gz: 80f8fb1ad69d4e83407943a1655a7da909d3704ce950ccb3e01a01a515aa3b7a96804e3ae93ceea7ed2cace9497267d750010a2c6fd05b56db27abc89f597432
7
- data.tar.gz: 7b7a5ba65d0e5e549811734b03aed0bcc5d52b0c13b768f5e06599162b367998f7d4f7294fe7af2a37d82924ce1826e7e65fbfa39b616bf97828f6b68f83fe2a
6
+ metadata.gz: 7a7f53ed3821883f3f858b50039a1ca03ba4bbfafdbf9302dedfb9a0c150ab1f25e198c3ca602e13cec493a469135bdc5b0fb28b9157182e1278409136928171
7
+ data.tar.gz: 15f9aa8cda587f9239ec84290d2fb1e3f3d6cf28c9b208b33d6081af842a5ce15eb5dcfa2a131905f41a228a03d19ea1dacd97fb3e80d35aa4a79f722cf4795a
data/CHANGELOG.md CHANGED
@@ -5,6 +5,34 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.11.1] - 2026-07-13
9
+
10
+ ### Accessibility (follow-up review)
11
+
12
+ - **Caption-less tables get a numbered, localized accessible name.** v0.11.0
13
+ labelled every un-captioned table the hardcoded English `"Table"`, so a
14
+ page of tables was undifferentiated to a screen reader and unlocalized.
15
+ They are now `"Table 1"`, `"Table 2"`, … via the new
16
+ `markdowndocs.table_label` I18n key (`"Table %{number}"`).
17
+
18
+ ## [0.11.0] - 2026-07-13
19
+
20
+ ### Accessibility
21
+
22
+ - **Keyboard-scrollable tables (WCAG 2.1.1).** A wide GFM table overflows and
23
+ becomes horizontally scrollable via the host's typography CSS. The renderer
24
+ now marks every `<table>` with `tabindex="0"` so keyboard-only users can
25
+ scroll it with the arrow keys (axe `scrollable-region-focusable`). The
26
+ table's implicit `role="table"` is preserved — no `role="region"` is added,
27
+ which would strip row/column semantics — and an un-captioned table gets a
28
+ minimal `aria-label="Table"` so the focus stop is announced. `tabindex` is
29
+ now in the sanitizer's base attribute allow-list.
30
+ - **44px chrome target sizes (WCAG 2.5.5 AAA).** Sidebar table-of-contents
31
+ links, related-documentation links, breadcrumb links, and the audience
32
+ mode-switcher buttons now meet the 44×44 minimum target size (they measured
33
+ ~20–28px). Purely additive utility classes; no visual change beyond the
34
+ taller hit area.
35
+
8
36
  ## [0.10.0] - 2026-06-24
9
37
 
10
38
  ### Added
@@ -48,7 +48,7 @@ module Markdowndocs
48
48
  options = markdown_render_options
49
49
  doc = Commonmarker.parse(markdown, options: options)
50
50
  html = doc.to_html(options: options)
51
- html = apply_syntax_highlighting(html)
51
+ html = post_process_html(html)
52
52
  sanitize_html(html)
53
53
  rescue => e
54
54
  # Bare rescue is intentional: third-party errors from commonmarker,
@@ -66,7 +66,7 @@ module Markdowndocs
66
66
  %(<pre class="markdowndocs-render-error">#{escaped}</pre>)
67
67
  end
68
68
 
69
- def apply_syntax_highlighting(html)
69
+ def post_process_html(html)
70
70
  # HTML5 parsing preserves case-sensitive SVG/MathML foreign-content
71
71
  # attributes (e.g. viewBox) that Nokogiri::HTML would lowercase.
72
72
  doc = Nokogiri::HTML5.fragment(html)
@@ -84,9 +84,44 @@ module Markdowndocs
84
84
  end
85
85
  end
86
86
 
87
+ mark_tables_keyboard_accessible(doc)
88
+
87
89
  doc.to_html
88
90
  end
89
91
 
92
+ # A wide GFM table overflows its column and becomes horizontally
93
+ # scrollable — via the host's typography/prose CSS, which the engine
94
+ # does not control and cannot inspect at render time. A scrollable
95
+ # region that isn't keyboard-focusable strands keyboard-only users
96
+ # (WCAG 2.1.1; axe `scrollable-region-focusable`).
97
+ #
98
+ # `tabindex="0"` directly on the <table> makes it focusable so a
99
+ # keyboard user can scroll it with the arrow keys, whether the host CSS
100
+ # scrolls the table itself or a wrapper. We deliberately do NOT add
101
+ # `role="region"`: on a <table> that would override the implicit
102
+ # `role="table"` and strip row/column semantics from screen readers.
103
+ # A <caption> already names the table for AT; when absent we add a
104
+ # minimal aria-label so the focus stop is announced. The focus ring is
105
+ # the user-agent default (no engine CSS suppresses it).
106
+ #
107
+ # Trade-off: every table becomes a tab stop, not only the ones that
108
+ # actually overflow — scroll state is unknowable without a browser, and
109
+ # a static (JS-free) engine fix is worth that small amount of extra tab
110
+ # travel.
111
+ def mark_tables_keyboard_accessible(doc)
112
+ doc.css("table").each_with_index do |table, index|
113
+ table["tabindex"] = "0"
114
+ next if table.at_css("caption") || table["aria-label"]
115
+
116
+ # A caption-less table still needs an accessible name for its focus
117
+ # stop — but a page of identically-named "Table" regions is
118
+ # undifferentiated to a screen reader tabbing through. Number them,
119
+ # and route the label through I18n so non-English docs localize it.
120
+ table["aria-label"] = I18n.t("markdowndocs.table_label", number: index + 1,
121
+ default: "Table %{number}")
122
+ end
123
+ end
124
+
90
125
  def lexer_exists?(language)
91
126
  Rouge::Lexer.find(language).present?
92
127
  rescue
@@ -131,6 +166,7 @@ module Markdowndocs
131
166
  href title src alt align class lang
132
167
  role aria-label aria-labelledby aria-describedby aria-hidden
133
168
  open
169
+ tabindex
134
170
  ].freeze
135
171
 
136
172
  # Curated structural SVG subset. Deliberately excludes script,
@@ -159,7 +195,7 @@ module Markdowndocs
159
195
  transform opacity text-anchor dominant-baseline
160
196
  font-size font-family font-weight
161
197
  marker-start marker-end markerWidth markerHeight refX refY orient
162
- id xmlns focusable tabindex
198
+ id xmlns focusable
163
199
  ].freeze
164
200
 
165
201
  def sanitize_html(html)
@@ -19,7 +19,8 @@
19
19
  <% if item[:current] %>
20
20
  <span class="font-medium text-gray-900 dark:text-slate-100" aria-current="page"><%= item[:name] %></span>
21
21
  <% elsif item[:path] %>
22
- <%= link_to item[:name], item[:path], class: "hover:text-indigo-700 dark:hover:text-indigo-300 transition-colors" %>
22
+ <%# inline-flex min-h-11 (44px) meets WCAG 2.5.5 AAA target size. %>
23
+ <%= link_to item[:name], item[:path], class: "hover:text-indigo-700 dark:hover:text-indigo-300 transition-colors inline-flex min-h-11 items-center" %>
23
24
  <% else %>
24
25
  <span class="text-gray-700 dark:text-slate-300"><%= item[:name] %></span>
25
26
  <% end %>
@@ -21,7 +21,9 @@
21
21
  aria-pressed="<%= current_mode == mode %>"
22
22
  data-mode="<%= mode %>"
23
23
  data-action="click->docs-mode#rememberFocus"
24
- class="px-3 py-1.5 text-sm font-medium rounded-md border transition-colors <%= (current_mode == mode) ? 'border-cyan-500 bg-cyan-50 text-cyan-900 dark:bg-cyan-900/40 dark:border-cyan-400 dark:text-cyan-100' : 'border-slate-200 text-slate-700 hover:bg-slate-50 dark:border-slate-700 dark:text-slate-300 dark:hover:bg-slate-700' %>"
24
+ <%# inline-flex min-h-11 (44px) meets WCAG 2.5.5 AAA target size the
25
+ toggle buttons measured ~28px tall. %>
26
+ class="inline-flex min-h-11 items-center justify-center px-3 py-1.5 text-sm font-medium rounded-md border transition-colors <%= (current_mode == mode) ? 'border-cyan-500 bg-cyan-50 text-cyan-900 dark:bg-cyan-900/40 dark:border-cyan-400 dark:text-cyan-100' : 'border-slate-200 text-slate-700 hover:bg-slate-50 dark:border-slate-700 dark:text-slate-300 dark:hover:bg-slate-700' %>"
25
27
  >
26
28
  <%= t("markdowndocs.modes.#{mode}", default: mode.titleize) %>
27
29
  </button>
@@ -7,7 +7,10 @@
7
7
  <ul class="space-y-2">
8
8
  <% toc_items.each do |item| %>
9
9
  <li class="<%= 'ml-4' if item[:level] == 3 %>">
10
- <a href="#<%= item[:slug] %>" class="text-sm text-gray-700 dark:text-slate-300 hover:text-indigo-700 dark:hover:text-indigo-300 transition-colors block py-1">
10
+ <%# min-h-11 (44px) meets WCAG 2.5.5 AAA target size sidebar TOC links
11
+ measured ~20px. flex + items-center keeps the label vertically
12
+ centered in the taller hit area. %>
13
+ <a href="#<%= item[:slug] %>" class="text-sm text-gray-700 dark:text-slate-300 hover:text-indigo-700 dark:hover:text-indigo-300 transition-colors flex min-h-11 items-center">
11
14
  <%= item[:text] %>
12
15
  </a>
13
16
  </li>
@@ -23,7 +26,8 @@
23
26
  <ul class="space-y-3">
24
27
  <% related_docs.each do |related_doc| %>
25
28
  <li>
26
- <%= link_to related_doc.title, markdowndocs.doc_path(related_doc.slug), class: "text-sm text-indigo-700 dark:text-indigo-300 hover:text-indigo-800 dark:hover:text-indigo-200 font-medium transition-colors" %>
29
+ <%# inline-flex min-h-11 (44px) meets WCAG 2.5.5 AAA target size. %>
30
+ <%= link_to related_doc.title, markdowndocs.doc_path(related_doc.slug), class: "text-sm text-indigo-700 dark:text-indigo-300 hover:text-indigo-800 dark:hover:text-indigo-200 font-medium transition-colors inline-flex min-h-11 items-center" %>
27
31
  <p class="text-xs text-gray-700 dark:text-slate-300 mt-1"><%= related_doc.description %></p>
28
32
  </li>
29
33
  <% end %>
@@ -1,6 +1,7 @@
1
1
  en:
2
2
  markdowndocs:
3
3
  back_to_documentation: "Back to Documentation"
4
+ table_label: "Table %{number}"
4
5
  navigation_sidebar: "Navigation & Related Docs"
5
6
  viewing_mode: "Viewing Mode"
6
7
  select_viewing_mode: "Select documentation viewing mode"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Markdowndocs
4
- VERSION = "0.10.0"
4
+ VERSION = "0.11.1"
5
5
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: markdowndocs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.0
4
+ version: 0.11.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dave Chmura
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2026-06-24 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: rails
@@ -115,7 +114,6 @@ metadata:
115
114
  homepage_uri: https://github.com/dschmura/markdowndocs
116
115
  source_code_uri: https://github.com/dschmura/markdowndocs
117
116
  changelog_uri: https://github.com/dschmura/markdowndocs/blob/main/CHANGELOG.md
118
- post_install_message:
119
117
  rdoc_options: []
120
118
  require_paths:
121
119
  - lib
@@ -130,8 +128,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
130
128
  - !ruby/object:Gem::Version
131
129
  version: '0'
132
130
  requirements: []
133
- rubygems_version: 3.4.19
134
- signing_key:
131
+ rubygems_version: 3.6.9
135
132
  specification_version: 4
136
133
  summary: A drop-in markdown documentation site for Rails apps
137
134
  test_files: []