jig-ui 0.18.0 → 0.18.2

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.
package/CHANGELOG.md CHANGED
@@ -1,6 +1,56 @@
1
1
  # Changelog
2
2
 
3
- ## 0.18.0
3
+ ## 0.18.2 (2026-09-25)
4
+
5
+ Every release in this changelog now carries its date and a one-line summary,
6
+ for jig-site's Versions page.
7
+
8
+ ### Changed
9
+
10
+ - **Every release heading carries its date**, as `## 0.18.1 (2026-09-25)`,
11
+ backfilled from the tags. The npm package has no git history, so the
12
+ CHANGELOG is where a release's date travels with it; jig-site's Versions page
13
+ lists each release by date. A test keeps every released heading dated.
14
+ - **Every release opens with a one-line summary.** Five had none (0.17.1,
15
+ 0.16.1, 0.15.1, 0.12.0, 0.2.1) and now do, written from their own entries.
16
+ The Versions page shows the line under each release; a test keeps it there.
17
+
18
+ ## 0.18.1 (2026-09-25)
19
+
20
+ Two fixes found by jig-site's fourth round: the probe misread a `<details>`
21
+ menu, and `A-05` missed an arrow that renders as an emoji.
22
+
23
+ ### Fixed
24
+
25
+ - **The probe reads a `<details>` menu the way the browser shows it.** On
26
+ jig-site's Reference it reported a working phone menu as broken, and
27
+ `jig verdicts` refused a correct `P-14` verdict on the strength of it. Four
28
+ defects, each now covered by a browser test:
29
+ - Where the banner's menu is hidden at a width, the probe took the next
30
+ disclosure in some other `<nav>` (a docs rail's section tree) as the menu,
31
+ and reported "a menu button beside links that already show". A menu the
32
+ page names, or one in its banner, is now the only candidate once it exists.
33
+ - A closed `<details>` hides its content with `content-visibility` in
34
+ Chromium, which still gives its links boxes, so the closed menu's links
35
+ counted as showing and opening it "showed no more links". Visibility now
36
+ also asks `checkVisibility()`.
37
+ - A label swapped with CSS ("Menu" to "Close") keeps both words in
38
+ `textContent`; the label is now read from the rendered text.
39
+ - A native `<summary>` has no `aria-expanded` attribute; its expanded state
40
+ is its `<details>`'s `open`, which is what assistive technology is given.
41
+
42
+ Probe files are now version 6, so earlier ones are re-recorded.
43
+
44
+ - **`A-05` catches an arrow that turns into an emoji.** ↔ ↕ ↖ ↗ ↘ ↙ ↩ ↪ ▶ ◀
45
+ are text by default but have an emoji form, and the detector skipped the
46
+ whole arrow block so prose arrows would not fire. jig-site marked its
47
+ external links with `<span aria-hidden="true">↗</span>`, which some platforms
48
+ draw as a colour emoji, and `check --all` and three critiques passed it. One
49
+ of these characters now fires when it stands alone as an icon (the whole
50
+ content of an element, or a pseudo-element's `content`) without U+FE0E. The
51
+ same arrow in a sentence still does not.
52
+
53
+ ## 0.18.0 (2026-09-25)
4
54
 
5
55
  A page's frame gets tokens, a project gets a place to record where its layout
6
56
  switches, and keyboard access a rule requires fits every script budget. All of
@@ -36,7 +86,7 @@ it found building jig-site's header and Reference with the full loop.
36
86
  one pixel before and at each `--breakpoint-*` the project declares, and the
37
87
  gate's refresh keeps those probes current too.
38
88
 
39
- ## 0.17.3
89
+ ## 0.17.3 (2026-09-25)
40
90
 
41
91
  Three defects found building jig-site's header with the full loop.
42
92
 
@@ -65,7 +115,7 @@ Three defects found building jig-site's header with the full loop.
65
115
  said so; only `jig init --yes` refreshed them. `update` now refreshes each
66
116
  untouched copy wherever `init` recorded writing it.
67
117
 
68
- ## 0.17.2
118
+ ## 0.17.2 (2026-09-24)
69
119
 
70
120
  Four defects found by building one page with the full loop.
71
121
 
@@ -93,7 +143,9 @@ Four defects found by building one page with the full loop.
93
143
  stated as measured, the two outliers are trimmed, and a test renders all 286
94
144
  figures in Chrome and fails any wider or taller.
95
145
 
96
- ## 0.17.1
146
+ ## 0.17.1 (2026-09-24)
147
+
148
+ The gate judges only the critiques a session touched.
97
149
 
98
150
  ### Fixed
99
151
 
@@ -106,7 +158,7 @@ Four defects found by building one page with the full loop.
106
158
  starts with `_` is set aside and never judged. With no transcript, as when
107
159
  the gate is run by hand, it checks every critique as before.
108
160
 
109
- ## 0.17.0
161
+ ## 0.17.0 (2026-09-24)
110
162
 
111
163
  Thirteen more tells of a generated page, ten of them caught by `check`, and a
112
164
  picture for every rule. Also carries the probe and verdict fixes tagged as 0.16.2,
@@ -153,7 +205,10 @@ which was not published to npm.
153
205
  any count, `I-81` the page's headline, and `I-89` button labels as well as
154
206
  links.
155
207
 
156
- ## 0.16.1
208
+ ## 0.16.1 (2026-09-19)
209
+
210
+ Jig's own headings drop the em dash, a quotation keeps its own punctuation,
211
+ and a spec's `indexable:` is `true` or `false`.
157
212
 
158
213
  ### Fixed
159
214
 
@@ -171,7 +226,7 @@ which was not published to npm.
171
226
  reported as contradicting J-123. Anything else is now a spec-shape problem,
172
227
  and an error in `verdicts` that names the field.
173
228
 
174
- ## 0.16.0
229
+ ## 0.16.0 (2026-09-19)
175
230
 
176
231
  With more than one mode, every barrel names its mode, and none sits in the
177
232
  global stylesheet.
@@ -191,7 +246,9 @@ global stylesheet.
191
246
  An edited one is left, with a note on where its edits belong. `check` names a
192
247
  `theme.css` left beside two modes.
193
248
 
194
- ## 0.15.1
249
+ ## 0.15.1 (2026-09-19)
250
+
251
+ The gate hears a question anywhere in the message, not only on its last line.
195
252
 
196
253
  ### Fixed
197
254
 
@@ -202,7 +259,7 @@ global stylesheet.
202
259
  live `/jig decide` three times. Any sentence ending in `?` now counts; a `?`
203
260
  in a URL or a code span does not.
204
261
 
205
- ## 0.15.0
262
+ ## 0.15.0 (2026-09-19)
206
263
 
207
264
  The gate holds an agent on warnings, waits while it asks the owner a question,
208
265
  and reaches projects where the skill is installed globally.
@@ -244,7 +301,7 @@ and reaches projects where the skill is installed globally.
244
301
  - **`jig seo` says an endpoint is not a page**, so `pages=` can be checked
245
302
  against a site's route files.
246
303
 
247
- ## 0.14.1
304
+ ## 0.14.1 (2026-09-19)
248
305
 
249
306
  `jig seo` cites only what a rule says.
250
307
 
@@ -269,7 +326,7 @@ and reaches projects where the skill is installed globally.
269
326
  as `/pages/admin`, so a `noindex` Astro or Next page listed in the sitemap
270
327
  went unreported.
271
328
 
272
- ## 0.14.0
329
+ ## 0.14.0 (2026-09-18)
273
330
 
274
331
  What a stranger meets before the page, what the page must not hand them, and a
275
332
  project that states its own direction.
@@ -332,7 +389,7 @@ project that states its own direction.
332
389
  already have. Redesigning an existing page is its own path, and `spec` asks
333
390
  different questions when the page exists.
334
391
 
335
- ## 0.13.0
392
+ ## 0.13.0 (2026-09-18)
336
393
 
337
394
  Meaning before presentation, and one fewer step anyone can skip.
338
395
 
@@ -374,7 +431,9 @@ Meaning before presentation, and one fewer step anyone can skip.
374
431
  `.github`.** Jig's own vendored rules are not the project's interface.
375
432
  - **Probe files are version 3.** Older ones no longer validate.
376
433
 
377
- ## 0.12.0
434
+ ## 0.12.0 (2026-09-18)
435
+
436
+ One new rule: no em dashes in interface text (`I-118`), with a detector.
378
437
 
379
438
  ### Added
380
439
 
@@ -387,7 +446,7 @@ Meaning before presentation, and one fewer step anyone can skip.
387
446
  styles, comments, class names and documentation are not interface text.
388
447
  The en dash keeps its one job, a range read as "to" (`2-10 seats`).
389
448
 
390
- ## 0.11.1
449
+ ## 0.11.1 (2026-09-18)
391
450
 
392
451
  Two holes in the gate, both found by running the loop twice on 0.11.0 —
393
452
  once on Haiku, once on Sonnet.
@@ -406,7 +465,7 @@ once on Haiku, once on Sonnet.
406
465
  at a width where the links fit. A false positive is how a gate gets
407
466
  ignored.
408
467
 
409
- ## 0.11.0
468
+ ## 0.11.0 (2026-09-18)
410
469
 
411
470
  One release, one lesson: a measurement an agent can type is not a
412
471
  measurement. Everything here came from two live runs at the capability
@@ -435,7 +494,7 @@ floor the day 0.10.0 shipped.
435
494
  critique verdicts. Both are warned about separately, each with what
436
495
  ignoring it costs. Since 0.7.0 both had said `.jig/` holds the tokens.
437
496
 
438
- ## 0.10.0
497
+ ## 0.10.0 (2026-09-18)
439
498
 
440
499
  Mobile-first, and a design loop that a weak model cannot skip. Everything here
441
500
  was measured on live runs at the capability floor (Haiku), and most of it exists
@@ -501,7 +560,7 @@ because an instruction the agent could choose to ignore was ignored.
501
560
  - **`init --yes` says what it did not decide** — no surface mapping, and
502
561
  re-running `init` after you declare one.
503
562
 
504
- ## 0.9.0
563
+ ## 0.9.0 (2026-09-13)
505
564
 
506
565
  One new rule and one amended correction, both from the same afternoon of
507
566
  dogfooding and both about the same blind spot: H-47's correction always pointed
@@ -546,7 +605,7 @@ at a token, so an agent reading it literally always produced one.
546
605
  it.
547
606
 
548
607
 
549
- ## 0.8.2
608
+ ## 0.8.2 (2026-09-12)
550
609
 
551
610
  Every fix here was found by a consumer using Jig rather than by Jig checking
552
611
  itself: the documentation site was upgraded to 0.8.1 and then styled in
@@ -625,7 +684,7 @@ disagreeing without either knowing the other existed.
625
684
  guard built on it. Caught by its own canary the moment an option wrapped.
626
685
 
627
686
 
628
- ## 0.8.1
687
+ ## 0.8.1 (2026-09-12)
629
688
 
630
689
  Both fixes here are the same shape: 0.8.0 corrected the instance it was looking
631
690
  at and left the class alone, and in each case the commit message claimed a
@@ -685,7 +744,7 @@ by inspecting the published tarball rather than the working tree.
685
744
  is your call", "my inclination is") as well as the heading, because a rename
686
745
  would otherwise defeat it. Each tell is verified to fire on reintroduction.
687
746
 
688
- ## 0.8.0
747
+ ## 0.8.0 (2026-09-11)
689
748
 
690
749
  Everything here was found by handing Jig to agents that had never seen it and
691
750
  asking them to build something real — its own documentation site. None of it
@@ -821,7 +880,7 @@ Minor rather than patch: `explain` prints lines it did not print before, and
821
880
  whether the guard should consult the config first, which is a behaviour
822
881
  question rather than a copy one.
823
882
 
824
- ## 0.7.1
883
+ ## 0.7.1 (2026-09-11)
825
884
 
826
885
  A rule file that contradicted the tool, and the guard that kept it that way.
827
886
 
@@ -875,7 +934,7 @@ to agents that had never seen this repository and asking them to build
875
934
  something real. Everyone who could have caught the token-location defect
876
935
  already knew where the tokens were.
877
936
 
878
- ## 0.7.0
937
+ ## 0.7.0 (2026-09-11)
879
938
 
880
939
  A silent no-op, fixed, plus the missing half of 0.6.0's token-layer move.
881
940
 
@@ -917,7 +976,7 @@ to move their token layer should read these notes.
917
976
  AFTER @import "./jig/theme.css";
918
977
  ```
919
978
 
920
- ## 0.6.0
979
+ ## 0.6.0 (2026-09-11)
921
980
 
922
981
  The token layer stops hiding in a dotfolder, and `check` starts reading it back.
923
982
 
@@ -1011,7 +1070,7 @@ move it, because relocating files could break an import you wrote yourself.
1011
1070
  directive it needs. Nothing errors when this bites: the class lands on the
1012
1071
  element, no rule exists to match it, and the style simply does not apply.
1013
1072
 
1014
- ## 0.5.0
1073
+ ## 0.5.0 (2026-09-10)
1015
1074
 
1016
1075
  Four things shipped in 0.4.0 were broken in ways that reported success. `/jig
1017
1076
  update` refreshed to the version already installed and said "Updated Jig →
@@ -1105,7 +1164,7 @@ ones exist because a guard had been passing vacuously: rule 6's regex was
1105
1164
  line-anchored and so covered 103 of 133 tokens while claiming to cover all of
1106
1165
  them, and nothing at all checked that a token cited in the rules exists.
1107
1166
 
1108
- ## 0.4.0
1167
+ ## 0.4.0 (2026-09-04)
1109
1168
 
1110
1169
  Jig stops copying itself into your project. It is a skill an agent reads, and
1111
1170
  0.3.0 wrote 220KB across 17 files into every consuming repo to deliver it —
@@ -1312,7 +1371,7 @@ for you, because you may have edited one and that edit is yours to keep. Run
1312
1371
  `jig install --agent <name>` to place the new bundle, then delete `.jig/rules/`
1313
1372
  and `.jig/rules.index.json` once you have checked them for your own changes.
1314
1373
 
1315
- ## 0.3.0
1374
+ ## 0.3.0 (2026-09-03)
1316
1375
 
1317
1376
  Two new commands. `install` and `update` put the system in place; these two make
1318
1377
  it do something.
@@ -1364,7 +1423,10 @@ it do something.
1364
1423
  not apply to it.
1365
1424
 
1366
1425
 
1367
- ## 0.2.1
1426
+ ## 0.2.1 (2026-09-01)
1427
+
1428
+ Two shipped colours failed Jig's own contrast floors. They are darker now,
1429
+ and the build does the contrast arithmetic.
1368
1430
 
1369
1431
  ### Fixed
1370
1432
 
@@ -1398,7 +1460,7 @@ it do something.
1398
1460
  no build, not published. It found two gaps on its first run: there is no
1399
1461
  border-width token and no focus-ring geometry tokens.
1400
1462
 
1401
- ## 0.2.0
1463
+ ## 0.2.0 (2026-09-01)
1402
1464
 
1403
1465
  The first release that actually works end to end. `0.1.0` shipped rules that
1404
1466
  cited tokens it never installed, and a token name that did not exist.
package/dist/index.js CHANGED
@@ -2265,6 +2265,11 @@ function maskProseComments(raw) {
2265
2265
  // src/check/detectors/emoji-icon.ts
2266
2266
  var EMOJI_RE = /[\u{1F300}-\u{1FAFF}\u{1F000}-\u{1F2FF}\u{2600}-\u{27BF}\u{2B00}-\u{2BFF}\u{FE0F}]/gu;
2267
2267
  var TEXTUAL = /* @__PURE__ */ new Set(["\u2192", "\u2190", "\u2191", "\u2193", "\u2194", "\u21D2", "\u21D0", "\u2122", "\u2713", "\u2717", "\u2212", "\u2217", "\u2318", "\u2325", "\u23CE"]);
2268
+ var EMOJI_CAPABLE = "\\u2194-\\u2199\\u21A9\\u21AA\\u25B6\\u25C0";
2269
+ var LONE_GLYPH = new RegExp(
2270
+ `>\\s*([${EMOJI_CAPABLE}])(?!\\uFE0E)\\s*<|content\\s*:\\s*["']([${EMOJI_CAPABLE}])(?!\\uFE0E)["']`,
2271
+ "gu"
2272
+ );
2268
2273
  var emojiIcon = {
2269
2274
  name: "emoji-icon",
2270
2275
  appliesTo: (file) => isStyleBearing(file),
@@ -2289,6 +2294,22 @@ var emojiIcon = {
2289
2294
  )
2290
2295
  );
2291
2296
  }
2297
+ for (const m of masked.matchAll(LONE_GLYPH)) {
2298
+ const glyph = m[1] ?? m[2];
2299
+ const line = masked.slice(0, m.index + m[0].indexOf(glyph)).split("\n").length;
2300
+ if (seen.has(line)) continue;
2301
+ seen.add(line);
2302
+ findings.push(
2303
+ mkFinding(
2304
+ ctx,
2305
+ "emoji-icon",
2306
+ file,
2307
+ line,
2308
+ `A character with an emoji form used as an icon (${glyph}). Standing alone, it renders as a colour emoji on some platforms. Use an icon set, or words.`,
2309
+ lines[line - 1] ?? ""
2310
+ )
2311
+ );
2312
+ }
2292
2313
  return findings;
2293
2314
  }
2294
2315
  };
@@ -5676,10 +5697,13 @@ import { fileURLToPath as fileURLToPath2 } from "url";
5676
5697
  import { join as join33, relative as relative3, resolve as resolve4, sep as sep5 } from "path";
5677
5698
 
5678
5699
  // src/probe/script.ts
5679
- var PROBE_VERSION = 5;
5700
+ var PROBE_VERSION = 6;
5680
5701
  var PROBE_SCRIPT = `(async () => {
5681
5702
  const doc = document.documentElement;
5682
- const vis = (e) => { const r = e.getBoundingClientRect(); const s = getComputedStyle(e); return r.width > 0 && r.height > 0 && s.visibility !== 'hidden' && s.display !== 'none'; };
5703
+ // checkVisibility also sees content a closed <details> hides: Chromium hides
5704
+ // it with content-visibility, which still gives its links real boxes, so a
5705
+ // closed menu counted as open and an opened one showed "no more links".
5706
+ const vis = (e) => { const r = e.getBoundingClientRect(); const s = getComputedStyle(e); return r.width > 0 && r.height > 0 && s.visibility !== 'hidden' && s.display !== 'none' && (!e.checkVisibility || e.checkVisibility()); };
5683
5707
  const probe = document.createElement('div');
5684
5708
  probe.style.all = 'initial';
5685
5709
  document.body.appendChild(probe);
@@ -5772,19 +5796,32 @@ var PROBE_SCRIPT = `(async () => {
5772
5796
  const inChrome = (b) => !!b.closest('header, [role=banner]') ||
5773
5797
  (!!b.closest('nav, [role=navigation]') && !b.closest('main, article, aside, [role=main]'));
5774
5798
  const named = (b) => /\b(menu|navigation)\b/i.test((b.getAttribute('aria-label') || '') + ' ' + b.textContent);
5775
- const toggle = [...document.querySelectorAll('button, summary, [role=button]')].find((b) => vis(b) &&
5776
- (named(b) || (inChrome(b) && (b.tagName === 'SUMMARY' || b.hasAttribute('aria-expanded') || b.hasAttribute('aria-controls')))));
5799
+ const candidates = [...document.querySelectorAll('button, summary, [role=button]')].filter((b) =>
5800
+ named(b) || (inChrome(b) && (b.tagName === 'SUMMARY' || b.hasAttribute('aria-expanded') || b.hasAttribute('aria-controls'))));
5801
+ // A menu the page names, or one in its banner, is the menu. Where it exists
5802
+ // but is hidden at this width, the links show instead and there is no menu
5803
+ // here; the next disclosure in some other <nav> (a docs rail's tree) is not
5804
+ // a stand-in for it.
5805
+ const primary = (b) => named(b) || !!b.closest('header, [role=banner]');
5806
+ const pool = candidates.some(primary) ? candidates.filter(primary) : candidates;
5807
+ const toggle = pool.find(vis);
5777
5808
  let menu = null;
5778
5809
  if (toggle) {
5779
- const name = () => ((toggle.getAttribute('aria-label') || '') + ' ' + (toggle.textContent || '')).replace(/\\s+/g, ' ').trim();
5810
+ // The label as rendered: a menu that swaps "Menu" for "Close" with CSS keeps
5811
+ // both words in its textContent at all times, so only innerText sees the swap.
5812
+ const name = () => ((toggle.getAttribute('aria-label') || '') + ' ' + (toggle.innerText || toggle.textContent || '')).replace(/\\s+/g, ' ').trim();
5813
+ // A native <summary> carries no aria-expanded attribute; the browser exposes
5814
+ // its <details>'s open state as the expanded state, which is what counts.
5815
+ const expanded = () => toggle.getAttribute('aria-expanded') ??
5816
+ (toggle.tagName === 'SUMMARY' && toggle.parentElement && toggle.parentElement.tagName === 'DETAILS' ? String(toggle.parentElement.open) : null);
5780
5817
  // Opening is judged by what becomes visible anywhere: a menu panel is often
5781
5818
  // a sibling of the header, not inside it (arm test 3, tw-1).
5782
5819
  const allLinks = () => [...document.querySelectorAll('a')].filter(vis).length;
5783
5820
  const controlled = () => { const id = toggle.getAttribute('aria-controls'); const el = id && document.getElementById(id); return el ? vis(el) : null; };
5784
- const before = { links: allLinks(), controlled: controlled(), expanded: toggle.getAttribute('aria-expanded'), name: name(), html: toggle.innerHTML };
5821
+ const before = { links: allLinks(), controlled: controlled(), expanded: expanded(), name: name(), html: toggle.innerHTML };
5785
5822
  toggle.click();
5786
5823
  await new Promise((r) => setTimeout(r, 350));
5787
- const after = { links: allLinks(), controlled: controlled(), expanded: toggle.getAttribute('aria-expanded'), name: name(), htmlChanged: toggle.innerHTML !== before.html };
5824
+ const after = { links: allLinks(), controlled: controlled(), expanded: expanded(), name: name(), htmlChanged: toggle.innerHTML !== before.html };
5788
5825
  toggle.focus();
5789
5826
  toggle.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }));
5790
5827
  document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }));
@@ -5794,7 +5831,7 @@ var PROBE_SCRIPT = `(async () => {
5794
5831
  expandedBefore: before.expanded, expandedAfter: after.expanded,
5795
5832
  opened: after.links > before.links || (before.controlled === false && after.controlled === true),
5796
5833
  labelChanged: after.name !== before.name || after.htmlChanged,
5797
- escapeCloses: toggle.getAttribute('aria-expanded') !== 'true' && allLinks() <= before.links,
5834
+ escapeCloses: expanded() !== 'true' && allLinks() <= before.links,
5798
5835
  focusReturned: document.activeElement === toggle,
5799
5836
  };
5800
5837
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jig-ui",
3
- "version": "0.18.0",
3
+ "version": "0.18.2",
4
4
  "description": "A design system for coding agents. 143 numbered UI rules, brand x mode design tokens, and an installer for Claude Code, Codex, Cursor and opencode.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",