easyfaa 1.3.0 → 1.4.0

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: e41cc835bf5b8eddca08eed86e7e4c8d5a679a7e43dbe3a8743315c7bfe8a631
4
- data.tar.gz: 0bc8bd729ad389dc5e1fcd7dc3d1bbbc4f5a8b645e93d8a848fc964acb4329ef
3
+ metadata.gz: 9cf9ab3a959cb56e785c5c96c161274202c33969bbeb10abbc7e30940667a0e8
4
+ data.tar.gz: d22d8d3ce9ffcb9dbcf929ce1da1fd61055b4386eb8a1b6b0ad0ca84398fa034
5
5
  SHA512:
6
- metadata.gz: f489dc08a53fb54ebd66023f2fab050ba6ccd52eb029aa7b5ac4484387dfe03ce28a6d91001b801ac0d1a354e8e2df9f8d3a6b1563ead7ff7e1799fda9ac59b8
7
- data.tar.gz: ebc2e9639a4511f9cb0dc9f2bb8be74389a9e4e5fdaf8a86950826da3f05ea6fa57d9ed3d300e7aebe2330f308d8006742ca5e96192520df6a64aaf40df1a24e
6
+ metadata.gz: 526076cc7c5791a386448bc4a341c99b705937852375eae431c8a02dcc3d23784041d820826b0acaf7173d6caa83991bb5e969b0d2d56f595c5dabe887687c86
7
+ data.tar.gz: 372900beae78b9ca7894f786c19b81144d9a48a619768bb184d164de3915f55cdddd52e3b41145d27cc1803de7baeb60b89d005fb2faf7f083cfd33299c699f6
data/CHANGELOG.md CHANGED
@@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
5
5
  For more information about changelogs, check [Keep a Changelog](http://keepachangelog.com) and
6
6
  [Vandamme](http://tech-angels.github.io/vandamme).
7
7
 
8
+ ## 1.4.0 - 2026-11-13
9
+
10
+ * No text is smaller than 14px: a card's fact labels, `tie-chip`, `why-box`, the tooltip, `modal-fine`, the steps'
11
+ numbers and `record-table`'s headings were 12 or 13px, and the search button's own `font` was invalid, which left
12
+ it at the browser's 13.33px
13
+ * A dialog holds the focus while open (its `[autofocus]` field or its ×, then Tab going round what it holds) and gives
14
+ it back to what opened it
15
+ * The tab shown is `aria-current`, for a screen reader
16
+ * In high contrast (forced colors), a switch's track and knob are drawn in the system's colors, on and off apart, and
17
+ a tab's dot in its text's
18
+
8
19
  ## 1.3.0 - 2026-11-13
9
20
 
10
21
  * A switch that is on is `--color-switch-on`: graphite (#3a3a3a, `--color-graphite`, the gray of a
@@ -1,13 +1,30 @@
1
1
  // Every dialog of render "easy/modal" lets its reader out: its ×, Esc or a click around it closes it where it is,
2
2
  // without loading a page, and the address becomes the × link's (the page without what opened it), or loses the #id
3
- // that opened it. A link to that #id opens it again. Without the script, the × is a plain link.
3
+ // that opened it. A link to that #id opens it again. Without the script, the × is a plain link. Open, it holds the
4
+ // focus: on its [autofocus] field or its ×, Tab going round what it holds, and back on what opened it once closed.
4
5
  // ES5 alone: every easy app has to work on old iPhones, Androids and Windows browsers.
5
6
  (function () {
6
7
  if (!document.querySelector || !document.addEventListener) { return; }
8
+ var FOCUSABLE = 'a[href], button:not([disabled]), input:not([disabled]):not([type=hidden]), ' +
9
+ 'select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
10
+ var opener = null;
11
+
12
+ // What a key can reach in the dialog, as it is drawn.
13
+ function reachable(modal) {
14
+ var all = modal.querySelectorAll(FOCUSABLE), reach = [];
15
+ for (var i = 0; i < all.length; i++) { if (all[i].offsetWidth || all[i].offsetHeight) { reach.push(all[i]); } }
16
+ return reach;
17
+ }
18
+
19
+ function hold(modal) {
20
+ var first = modal.querySelector('[autofocus]') || modal.querySelector('.modal-x');
21
+ if (first) { first.focus(); }
22
+ }
7
23
 
8
24
  function close(modal) {
9
25
  var link = modal.querySelector('.modal-x'), href = link && link.getAttribute('href');
10
26
  modal.className = modal.className.replace(/\s*\bis-(open|closed)\b/g, '') + ' is-closed';
27
+ if (opener && document.body.contains(opener)) { opener.focus(); }
11
28
  if (!window.history || !history.replaceState) { return; }
12
29
  if (href && href.charAt(0) !== '#') { history.replaceState(null, '', href + location.hash); }
13
30
  else if (location.hash === '#' + modal.id) { history.replaceState(null, '', location.pathname + location.search); }
@@ -29,6 +46,10 @@
29
46
  return node && node.nodeType === 1 ? node : null;
30
47
  }
31
48
 
49
+ // What had the focus before a dialog took it, which gets it back: by the time a link to the dialog's #id opens it,
50
+ // the browser has moved the focus off that link.
51
+ document.addEventListener('focusin', function (event) { if (!up(event.target, 'modal')) { opener = event.target; } });
52
+
32
53
  // The ×, or the dimmed page around the box (the dialog itself, not what it holds).
33
54
  document.addEventListener('click', function (event) {
34
55
  var x = up(event.target, 'modal-x');
@@ -38,12 +59,22 @@
38
59
  close(modal);
39
60
  });
40
61
  document.addEventListener('keydown', function (event) {
41
- if (event.key !== 'Escape' && event.key !== 'Esc' && event.keyCode !== 27) { return; }
42
62
  var modal = shown();
43
- if (modal) { event.preventDefault(); close(modal); }
63
+ if (!modal) { return; }
64
+ if (event.key === 'Escape' || event.key === 'Esc' || event.keyCode === 27) { event.preventDefault(); close(modal); }
65
+ else if (event.key === 'Tab' || event.keyCode === 9) {
66
+ var reach = reachable(modal), at = reach.indexOf(document.activeElement);
67
+ if (!reach.length) { return; }
68
+ var next = event.shiftKey ? (at <= 0 ? reach.length - 1 : -1) : (at === reach.length - 1 || at < 0 ? 0 : -1);
69
+ if (next >= 0) { event.preventDefault(); reach[next].focus(); }
70
+ }
44
71
  });
45
72
  window.addEventListener('hashchange', function () {
46
73
  var target = location.hash.length > 1 && document.getElementById(location.hash.slice(1));
47
- if (target) { target.className = target.className.replace(/\s*\bis-closed\b/g, ''); }
74
+ if (!target || !/(^|\s)modal(\s|$)/.test(target.className)) { return; }
75
+ target.className = target.className.replace(/\s*\bis-closed\b/g, '');
76
+ hold(target);
48
77
  });
78
+ var open = shown();
79
+ if (open) { hold(open); }
49
80
  })();
@@ -12,10 +12,13 @@
12
12
  function show(shown) {
13
13
  for (var i = 0; i < links.length; i++) {
14
14
  var panel = document.getElementById(id(links[i]));
15
- strip(links[i], 'is-on'); strip(panel, 'is-off');
16
- if (id(links[i]) === shown) { links[i].className += ' is-on'; } else { panel.className += ' is-off'; }
15
+ strip(links[i], 'is-on'); strip(panel, 'is-off'); links[i].removeAttribute('aria-current');
16
+ if (id(links[i]) === shown) { links[i].className += ' is-on'; links[i].setAttribute('aria-current', 'true'); }
17
+ else { panel.className += ' is-off'; }
17
18
  }
18
19
  }
20
+ // A screen reader hears which tab is shown; the page was drawn showing the one marked is-on.
21
+ for (var i = 0; i < links.length; i++) { if (/\bis-on\b/.test(links[i].className)) { show(id(links[i])); } }
19
22
  for (var i = 0; i < links.length; i++) {
20
23
  links[i].addEventListener('click', function (event) {
21
24
  event.preventDefault();
@@ -62,7 +62,7 @@
62
62
  .plane-card-facts div{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%;padding:8px;min-width:0}
63
63
  @media (min-width:880px){.plane-card-facts div{-ms-flex-preferred-size:25%;flex-basis:25%;max-width:25%}}
64
64
  @media (max-width:380px){.plane-card-facts div{-ms-flex-preferred-size:100%;flex-basis:100%;max-width:100%}}
65
- .plane-card-facts dt{font-size:13px;color:var(--color-text-muted);text-transform:uppercase;letter-spacing:.05em}
65
+ .plane-card-facts dt{font-size:14px;color:var(--color-text-muted);text-transform:uppercase;letter-spacing:.05em}
66
66
  .plane-card-facts dd{margin:2px 0 0;font-weight:600}
67
67
  .plane-card-facts.glance{border:1px solid var(--color-line);border-radius:12px;margin:16px 0 0}
68
68
  .plane-card-foot{border-top:1px solid var(--color-line);padding:14px 24px;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;background:var(--color-surface-1)}
@@ -182,14 +182,14 @@ ol.ad-card-list.js-live li.ad-card.superseded{display:block}
182
182
  .level-chip.not_applying{background:var(--color-ok-bg);color:var(--color-ok)}
183
183
  .level-chip.superseded{background:var(--color-ok-bg);color:var(--color-ok)}
184
184
  .ad-card-top .ad-card-kind{font-size:14px;padding:5px 10px;border-radius:999px;background:var(--color-surface-2)}
185
- .tie-chip{font-size:13px;color:var(--color-text-2);border:1px solid var(--color-line);border-radius:999px;padding:4px 10px}
185
+ .tie-chip{font-size:14px;color:var(--color-text-2);border:1px solid var(--color-line);border-radius:999px;padding:4px 10px}
186
186
  .ad-card-effective{font-size:14px;color:var(--color-text-muted);margin-left:auto!important}
187
187
  .ad-card h3,.ad-card h4{font-family:"Outfit","Helvetica Neue",Arial,sans-serif;font-weight:600;font-size:19px;line-height:1.2;margin:4px 0 6px}
188
188
  .ad-card h3 a,.ad-card h4 a{text-decoration:none}
189
189
  .ad-card h3 a:hover,.ad-card h4 a:hover{text-decoration:underline}
190
190
  .ad-card h3 .t,.ad-card h4 .t{display:block;font-family:system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-weight:400;color:var(--color-text-2);font-size:16px;margin-top:2px}
191
191
  .why-box{margin:8px 0 0;padding:12px 14px;border-radius:10px;background:var(--color-surface-1)}
192
- .why-box b{display:block;font-size:13px;text-transform:uppercase;letter-spacing:.05em;color:var(--color-text-muted);margin-bottom:2px}
192
+ .why-box b{display:block;font-size:14px;text-transform:uppercase;letter-spacing:.05em;color:var(--color-text-muted);margin-bottom:2px}
193
193
  .chip-list{list-style:none;margin:0 -4px 16px;padding:0;display:flex;flex-wrap:wrap}
194
194
  .chip-list li{margin:4px;padding:6px 12px;border-radius:999px;background:var(--color-surface-2);font-size:14px}
195
195
  .chip-list .in{background:var(--color-ok-bg);color:var(--color-ok);font-weight:600}
@@ -215,11 +215,11 @@ ol.ad-card-list.js-live li.ad-card.superseded{display:block}
215
215
  .modal-x:focus-visible{outline:3px solid var(--color-focus);outline-offset:2px}
216
216
  /* The bubble shows on hover or focus (a tap focuses it), its text the data-tip; the ring is the keyboard's alone */
217
217
  .has-tip{position:relative;display:inline-block}
218
- .has-tip[data-tip]:hover::after,.has-tip[data-tip]:focus::after,.has-tip[data-tip]:focus-within::after{content:attr(data-tip);position:absolute;bottom:calc(100% + 8px);left:10px;width:max-content;max-width:min(280px,80vw);padding:6px 10px;border-radius:6px;background:var(--color-text);color:var(--color-bg);font-size:13px;font-weight:600;line-height:1.35;pointer-events:none;z-index:60}
218
+ .has-tip[data-tip]:hover::after,.has-tip[data-tip]:focus::after,.has-tip[data-tip]:focus-within::after{content:attr(data-tip);position:absolute;bottom:calc(100% + 8px);left:10px;width:max-content;max-width:min(280px,80vw);padding:6px 10px;border-radius:6px;background:var(--color-text);color:var(--color-bg);font-size:14px;font-weight:600;line-height:1.35;pointer-events:none;z-index:60}
219
219
  .has-tip[data-tip]:hover::before,.has-tip[data-tip]:focus::before,.has-tip[data-tip]:focus-within::before{content:'';position:absolute;bottom:calc(100% + 2px);left:24px;border:6px solid transparent;border-bottom:0;border-top-color:var(--color-text);pointer-events:none;z-index:60}
220
220
  .has-tip:focus:not(:focus-visible){outline:0}.has-tip:focus-visible{outline:2px solid var(--color-primary);outline-offset:2px;border-radius:6px}
221
221
  .modal-lede{margin:0 0 4px;color:var(--color-text-2)}
222
- .modal-fine{margin:14px 0 0;font-size:13px;color:var(--color-text-muted)}
222
+ .modal-fine{margin:14px 0 0;font-size:14px;color:var(--color-text-muted)}
223
223
  /* The sign-in code: one real field over six slots (shared/_sign_in), as bh's pin_field draws
224
224
  one. Until the script has drawn them, the field shows as any other. */
225
225
  .pin-code{position:relative;max-width:380px;margin:14px auto 4px}
@@ -246,13 +246,13 @@ ol.ad-card-list.js-live li.ad-card.superseded{display:block}
246
246
  .search-hero-lede{font-size:18px;color:var(--color-text-2);max-width:43rem;margin:0 auto 18px}
247
247
  .search-form{max-width:40rem;margin:0 auto;text-align:left}
248
248
  .search-form label{display:block;font-weight:600;margin-bottom:8px;text-align:center}
249
- @media (max-width:430px){.search-form label{font-size:13px}}
249
+ @media (max-width:430px){.search-form label{font-size:14px}}
250
250
  .search-field{display:flex;background:var(--color-bg);border:2px solid var(--color-text-muted);border-radius:14px;box-shadow:0 6px 24px rgba(11,30,58,.12);overflow:hidden}
251
251
  .search-field input{flex:1;min-width:0;width:100%;border:0;background:transparent;color:var(--color-text);font-size:20px;padding:16px 18px;font-family:inherit}
252
252
  /* The field, rounded, shows where typing goes: the input's own outline would be square inside it. */
253
253
  .search-field input:focus{outline:0}
254
254
  .search-field:focus-within{box-shadow:0 0 0 4px var(--color-primary-soft);border-color:var(--color-focus)}
255
- .search-field button{border:0;background:var(--color-primary);color:var(--color-on-primary);font:600 18px/1 inherit;font-family:inherit;padding:0 22px;cursor:pointer;min-width:56px}
255
+ .search-field button{border:0;background:var(--color-primary);color:var(--color-on-primary);font-family:inherit;font-weight:600;font-size:18px;line-height:1;padding:0 22px;cursor:pointer;min-width:56px}
256
256
  .search-field button:hover{background:var(--color-primary-strong)}
257
257
  .search-field button .i{width:22px;height:22px;vertical-align:-5px}
258
258
  /* A click on the button draws no ring over the field; a keyboard's focus is ringed inside it. */
@@ -372,7 +372,7 @@ ol.ad-card-list.js-live li.ad-card.superseded{display:block}
372
372
  .steps-bar{display:flex;flex-wrap:wrap;list-style:none;margin:16px 0 0;padding:0;border-bottom:1px solid var(--color-line)}
373
373
  .steps-bar li{margin:0 4px -1px 0}
374
374
  .steps-bar span{display:block;padding:10px 14px;color:var(--color-text-2);font-weight:600;border-bottom:3px solid transparent}
375
- .steps-bar b{display:inline-block;width:22px;height:22px;line-height:22px;margin-right:6px;border-radius:50%;background:var(--color-surface-2);text-align:center;font-size:13px}
375
+ .steps-bar b{display:inline-block;width:22px;height:22px;line-height:22px;margin-right:6px;border-radius:50%;background:var(--color-surface-2);text-align:center;font-size:14px}
376
376
  .steps-bar .is-on{color:var(--color-text);border-bottom-color:var(--color-primary)}
377
377
  .steps-bar .is-on b{background:var(--color-primary);color:var(--color-on-primary)}
378
378
  .steps-bar .is-past b,.steps-bar .is-done b{background:var(--color-ok-bg);color:var(--color-ok)}
@@ -400,7 +400,7 @@ ol.ad-card-list.js-live li.ad-card.superseded{display:block}
400
400
  .record-table tr.is-not-complied td{background:var(--color-bad-bg)}
401
401
  .record-table-scroll{overflow-x:auto;margin-top:10px}
402
402
  .record-table{width:100%;border-collapse:collapse;font-size:14px}
403
- .record-table th{font-size:12px;text-transform:uppercase;letter-spacing:.04em;color:var(--color-text-muted);text-align:left;padding:6px 8px;border-bottom:2px solid var(--color-line)}
403
+ .record-table th{font-size:14px;text-transform:uppercase;letter-spacing:.04em;color:var(--color-text-muted);text-align:left;padding:6px 8px;border-bottom:2px solid var(--color-line)}
404
404
  .record-table td{padding:8px;border-bottom:1px solid var(--color-line);vertical-align:top}
405
405
 
406
406
  /* logbook-entry: a logbook entry to print on label paper and stick in, its signature line (-sign), and while a draft, a line saying so (-draft) */
@@ -483,6 +483,15 @@ mark.search-hit{background:var(--color-primary-soft);color:inherit;border-radius
483
483
  .switch[aria-checked="false"],.switch[aria-busy="true"]{color:var(--color-text-muted)}
484
484
  .switch[aria-busy="true"]{cursor:progress}
485
485
  .switch[aria-busy="true"]::before{opacity:.55}
486
+ /* High contrast (forced colors) drops every background, and with it a switch's track and knob: they keep the
487
+ system's own colors there, off an outline with a knob in the text's color, on the highlight with its text's. */
488
+ @media (forced-colors:active){
489
+ .switches li.switches-toggle::before,.switches li.switches-toggle.is-off::before,.switch::before{forced-color-adjust:none;background:Canvas;border-color:ButtonText}
490
+ .switches li.switches-toggle::after,.switches li.switches-toggle.is-off::after,.switch::after{forced-color-adjust:none;background:ButtonText;box-shadow:none}
491
+ .switches li.switches-toggle:not(.is-off)::before,.switch[aria-checked="true"]::before{background:Highlight;border-color:Highlight}
492
+ .switches li.switches-toggle:not(.is-off)::after,.switch[aria-checked="true"]::after{background:HighlightText}
493
+ .part-tabs a .part-tabs-dot{forced-color-adjust:none;background:CanvasText}
494
+ }
486
495
  /* plus-link: opens a form in place, or adds a row to one, and changes nothing until that form is saved: blue bold
487
496
  words after a plus. */
488
497
  .plus-link{display:inline;padding:0;border:0;background:none;color:var(--color-primary);font:inherit;font-weight:600;text-decoration:none;cursor:pointer}
@@ -1,4 +1,4 @@
1
1
  module EasyFAA
2
2
  # Version of the gem, read by the gemspec and by hosts checking compatibility.
3
- VERSION = '1.3.0'
3
+ VERSION = '1.4.0'
4
4
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: easyfaa
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - claudiob