Module: Odin::Forms::Accessibility
- Defined in:
- lib/odin/forms/accessibility.rb
Overview
Pure helpers for generating accessible markup and computing WCAG contrast.
Class Method Summary collapse
-
.contrast_ratio(fg, bg) ⇒ Object
── WCAG contrast ──────────────────────────────────────────────────────.
-
.field_aria_attrs(element, page_index) ⇒ Object
ARIA and id attributes for a field element.
-
.field_group_html(_group_name, legend, content) ⇒ Object
Wrap content in a <fieldset>/<legend> for grouped controls.
-
.field_label_html(label, input_id) ⇒ Object
A <label> associated with the given input id.
-
.generate_field_id(element_name, page_index) ⇒ Object
Unique, stable HTML element id for a field.
- .linearize(channel) ⇒ Object
- .meets_contrast_aa(fg, bg, font_size) ⇒ Object
- .parse_hex(hex) ⇒ Object
- .relative_luminance(hex) ⇒ Object
-
.skip_link_html(form_title) ⇒ Object
Skip-navigation link targeting the form content anchor.
-
.sr_only_html(text) ⇒ Object
Visually-hidden text that remains announced to screen readers.
-
.tab_order_sort(elements) ⇒ Object
Field elements only, sorted by reading order (top-to-bottom, left-to-right).
Class Method Details
.contrast_ratio(fg, bg) ⇒ Object
── WCAG contrast ──────────────────────────────────────────────────────
64 65 66 67 68 69 70 |
# File 'lib/odin/forms/accessibility.rb', line 64 def contrast_ratio(fg, bg) l1 = relative_luminance(fg) l2 = relative_luminance(bg) lighter = [l1, l2].max darker = [l1, l2].min (lighter + 0.05) / (darker + 0.05) end |
.field_aria_attrs(element, page_index) ⇒ Object
ARIA and id attributes for a field element.
20 21 22 23 24 25 26 27 |
# File 'lib/odin/forms/accessibility.rb', line 20 def field_aria_attrs(element, page_index) attrs = { "id" => generate_field_id(element.name, page_index), "aria-label" => element[:"aria-label"] || element[:label], } attrs["aria-required"] = "true" if element[:required] attrs end |
.field_group_html(_group_name, legend, content) ⇒ Object
Wrap content in a <fieldset>/<legend> for grouped controls.
30 31 32 33 34 35 |
# File 'lib/odin/forms/accessibility.rb', line 30 def field_group_html(_group_name, legend, content) %(<fieldset class="odin-form-fieldset">) + %(<legend class="odin-form-legend">#{legend}</legend>) + content + "</fieldset>" end |
.field_label_html(label, input_id) ⇒ Object
A <label> associated with the given input id.
15 16 17 |
# File 'lib/odin/forms/accessibility.rb', line 15 def field_label_html(label, input_id) %(<label for="#{input_id}" class="odin-form-label">#{label}</label>) end |
.generate_field_id(element_name, page_index) ⇒ Object
Unique, stable HTML element id for a field.
10 11 12 |
# File 'lib/odin/forms/accessibility.rb', line 10 def generate_field_id(element_name, page_index) "odin-field-#{page_index}-#{element_name}" end |
.linearize(channel) ⇒ Object
77 78 79 80 |
# File 'lib/odin/forms/accessibility.rb', line 77 def linearize(channel) srgb = channel / 255.0 srgb <= 0.04045 ? srgb / 12.92 : (((srgb + 0.055) / 1.055)**2.4) end |
.meets_contrast_aa(fg, bg, font_size) ⇒ Object
72 73 74 75 |
# File 'lib/odin/forms/accessibility.rb', line 72 def meets_contrast_aa(fg, bg, font_size) ratio = contrast_ratio(fg, bg) font_size >= 18 ? ratio >= 3.0 : ratio >= 4.5 end |
.parse_hex(hex) ⇒ Object
82 83 84 85 86 87 |
# File 'lib/odin/forms/accessibility.rb', line 82 def parse_hex(hex) clean = hex.start_with?("#") ? hex[1..] : hex raise ArgumentError, %(Invalid hex colour: "#{hex}") unless clean.match?(/\A[0-9a-fA-F]{6}\z/) [clean[0, 2].to_i(16), clean[2, 2].to_i(16), clean[4, 2].to_i(16)] end |
.relative_luminance(hex) ⇒ Object
89 90 91 92 |
# File 'lib/odin/forms/accessibility.rb', line 89 def relative_luminance(hex) r, g, b = parse_hex(hex) (0.2126 * linearize(r)) + (0.7152 * linearize(g)) + (0.0722 * linearize(b)) end |
.skip_link_html(form_title) ⇒ Object
Skip-navigation link targeting the form content anchor.
38 39 40 41 42 |
# File 'lib/odin/forms/accessibility.rb', line 38 def skip_link_html(form_title) %(<a class="odin-form-sr-only odin-form-skip" href="#odin-form-content">) + "Skip to #{form_title}" + "</a>" end |
.sr_only_html(text) ⇒ Object
Visually-hidden text that remains announced to screen readers.
45 46 47 |
# File 'lib/odin/forms/accessibility.rb', line 45 def sr_only_html(text) %(<span class="odin-form-sr-only">#{text}</span>) end |
.tab_order_sort(elements) ⇒ Object
Field elements only, sorted by reading order (top-to-bottom, left-to-right).
50 51 52 53 54 55 56 57 58 59 60 |
# File 'lib/odin/forms/accessibility.rb', line 50 def tab_order_sort(elements) elements.select(&:field?).sort do |a, b| ay = a[:y] || 0 by = b[:y] || 0 if ay != by ay <=> by else (a[:x] || 0) <=> (b[:x] || 0) end end end |