jig-ui 0.7.1 → 0.8.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.
@@ -24,6 +24,7 @@ These are the strongest defaults in a model's training data and the fastest way
24
24
  ### A-01 Purple and violet as the unspecified default
25
25
  ❌ A violet or indigo fill, or a violet→pink gradient, chosen because no colour was specified
26
26
  ✅ Use `--color-brand` from the brand file. The unbranded default resolves it to near-black, which ships a coherent monochrome UI and makes the missing decision visible. Then ask.
27
+ **Being asked to propose a colour does not discharge this.** A proposal is a question with a suggested answer, not a decision — so suggest one freely when asked, but ship the unbranded default alongside it and leave the brand file unchanged until a human confirms. Two agents given the same brief split on exactly this point, one deferring and one treating its own proposal as the answer, and both cited the same conflict-resolution clause to get there. The task can ask you for a recommendation; it cannot make you the one who decided.
27
28
 
28
29
  ### A-02 Gradient text on headings
29
30
  ❌ `background-clip: text` with a gradient fill and transparent text colour
@@ -90,6 +91,7 @@ In `operator`, `--radius-surface` also selects `sm`, so cards, buttons and input
90
91
  ### B-11 Unbounded line length
91
92
  ❌ Paragraphs spanning the full width of a wide viewport
92
93
  ✅ Cap at `--measure-prose` (68ch editorial, 60ch product, 72ch operator). Applies to any run of prose in any mode.
94
+ **And do not let a layout choice push it far under.** The readable band is 40–80 characters (`02-tokens.md`); the cap is the only half stated here, so a column can be halved indefinitely and still satisfy this rule while reading worse at every step. A narrow viewport that cannot reach 40 is a constraint and fine. Splitting a capped column into two side-by-side panels, and rendering prose at 41 characters in a mode whose own measure selects 68, is a choice — and it is choosing density over legibility, which is the trade `M-01` says editorial does not make.
93
95
 
94
96
  ### B-12 Centred or justified body text
95
97
  ❌ A centred paragraph of four lines. Justified text of any length.
@@ -503,7 +505,28 @@ Load `05-copy.md` whenever writing or reviewing a user-facing string.
503
505
 
504
506
  Run this against what you produced. Any "no" is a defect to fix, not a note to mention.
505
507
 
506
- 1. Would this look different from a generic template if the accent colour were removed? (A-01 → A-10)
508
+ 1. **The generic-AI tells, named rather than gestured at.** This used to read
509
+ "would this look different from a generic template if the accent colour were
510
+ removed?", which an agent that has just produced a generic template answers
511
+ yes to — and its `A-01 → A-10` range silently excluded `A-58`, `A-59`, `A-60`
512
+ and `A-67`. Check each:
513
+ - Was the accent colour **chosen**, or did it appear because none was
514
+ specified? (`A-01`)
515
+ - Any gradient text, decorative blobs, glassmorphism or neumorphism?
516
+ (`A-02`, `A-03`, `A-04`)
517
+ - Emoji standing in for icons — including ones marked `aria-hidden`?
518
+ (`A-05`)
519
+ - Three things in a three-column icon-and-heading grid because there were
520
+ three of them? (`A-06`)
521
+ - One radius on every element regardless of its size; shadow doing all the
522
+ depth work? (`A-07`, `A-08`)
523
+ - Marketing voice in an application, or placeholder content still in place?
524
+ (`A-09`, `A-10`)
525
+ - Decoration that mimics a functional signal — colour picked for variety, an
526
+ icon that looks pressable and is not? (`A-58`)
527
+ - Every list item restating the context they share? (`A-59`)
528
+ - Icons at equal weight competing with the text they support? (`A-60`)
529
+ - A border, card or panel around every group on the page? (`A-67`)
507
530
  2. Is every run of prose measure-capped and left-aligned? (B-11, B-12)
508
531
  3. Does any text or placeholder fall below 4.5:1? (C-19)
509
532
  4. Is every spacing value a `--spacing-*` token, and does `--spacing-heading-before` exceed `--spacing-heading-after`? (D-23, D-24)
@@ -523,16 +546,3 @@ Run this against what you produced. Any "no" is a defect to fix, not a note to m
523
546
  9. Does the primary action still work with JavaScript disabled? (F-41)
524
547
  10. Is there a `prefers-reduced-motion` path? (G-43)
525
548
  11. Did you reuse existing components and tokens rather than adding new ones? (H-45, H-47)
526
-
527
- ---
528
-
529
- ## Notes for the author (not for the agent)
530
-
531
- Rules carrying a deliberate house position rather than a general best practice — these are where your taste is recorded, and they should be defended or changed consciously:
532
-
533
- - **C-18** — off-white over pure white. Already evidenced in your site's `#fafaf7`, now the anchor of the default neutral ramp.
534
- - **A-07, A-08** — bordered, low-radius, low-shadow surfaces. This is a stance, not a consensus.
535
- - **F-41, G-42** — resilience and restraint weighted above visual richness. Connects directly to your writing on JS-dependent form fields.
536
- - **D-24** — heading space asymmetry. Universal advice, but stating a ratio makes it enforceable.
537
-
538
- Candidates deferred to mode profiles because they are not universal: information density, table row height, use of colour fills for status, page-section rhythm, hero presence, illustration and imagery policy, animation budget.
package/rules/01-modes.md CHANGED
@@ -205,17 +205,3 @@ Per project, one file supplying:
205
205
  - **Voice** — sentence case or title case, contraction policy, error-message tone.
206
206
 
207
207
  Default when no brand is supplied: warm neutral ramp anchored on `--color-bg-base` (`oklch(0.980 0.004 95)`, a warm off-white), no accent, 8px base radius (`--radius-sm`), border-led elevation. Greyscale output plus a stated question beats an invented purple (`A-01`).
208
-
209
- ---
210
-
211
- ## Notes for the author (not for the agent)
212
-
213
- **Decided, not derived.** These numbers are internally consistent and defensible, but several are judgement calls that should be tuned once you have run real work through them: the operator row height, the three section-rhythm values, and the motion durations. Change them in `tokens/mode.*.css`, never at the call site — this file describes them, `02-tokens.md` resolves them, and neither is where they live.
214
-
215
- **Where your taste is recorded here:**
216
- - The zero-JS default in `editorial` — a stronger position than most systems take, and consistent with your writing on JS-dependent forms.
217
- - Absolute-first timestamps in `operator` — that is the procurement instinct: the record is evidence before it is a convenience.
218
- - Typed confirmation for destructive operator actions, and no hover-hidden information in all-day tools.
219
- - Border-led elevation as the unbranded default.
220
-
221
- **Open question worth resolving before tokens.** `product` is currently defined as the midpoint of the other two, which is how it earns its place, but it is also the mode that most often needs to lean. A customer dashboard leans editorial; a billing admin screen leans operator. Consider whether `product` needs a documented `dense` variant, or whether such surfaces should simply be declared `operator`. My inclination is the latter — three modes you apply confidently beat five you deliberate over — but it is your call, and it affects how many token sets `02` has to emit.
@@ -48,7 +48,7 @@ They are the only token format every web framework consumes natively with no bui
48
48
  | Consumer | Usage |
49
49
  | --- | --- |
50
50
  | Plain CSS / any framework | `color: var(--color-text-strong)` |
51
- | Tailwind v4 | Wrap in `@theme { }` — generates utilities automatically |
51
+ | Tailwind v4 | `@import` the barrel flat, alongside `@import "tailwindcss"` — see [Colour architecture](#colour-architecture). Utility classes are opt-in and need an alias block |
52
52
  | CSS-in-JS (styled-components, emotion) | `color: var(--color-text-strong)` inside template literals |
53
53
  | Vue / Svelte / Angular | Identical to plain CSS, scoped or global |
54
54
  | React inline styles | `style={{ color: 'var(--color-text-strong)' }}` |
@@ -281,7 +281,7 @@ Resist per-component tokens (`--button-bg`). They multiply fast and rarely earn
281
281
 
282
282
  ## Naming contract
283
283
 
284
- Names align to Tailwind v4's theme namespaces. This is free for other frameworks — they are ordinary custom properties — and means the same file can be wrapped in `@theme` to generate utilities without any framework taking a dependency on Tailwind.
284
+ Names align to Tailwind v4's theme namespaces. This is free for other frameworks — they are ordinary custom properties — and means an alias block can expose any of them as Tailwind utilities without any framework taking a dependency on Tailwind.
285
285
 
286
286
  | Namespace | Holds | Layer |
287
287
  | --- | --- | --- |
@@ -390,14 +390,89 @@ In dark, elevated surfaces get **lighter**, not shadowed. Border-led elevation s
390
390
  }
391
391
  ```
392
392
 
393
- **Tailwind v4** — wrap the barrel, nothing else changes
393
+ **Tailwind v4** — the barrel is imported flat, exactly as anywhere else
394
+
395
+ ```css
396
+ @import "tailwindcss";
397
+ @import "./jig/theme.css";
398
+ ```
399
+
400
+ That is all that is required, and it is what `init` writes. Every token is
401
+ readable as `var(--color-text-strong)` in any stylesheet or component.
402
+
403
+ **Do not nest the import inside `@theme`.** Earlier versions of this file told
404
+ you to, and Tailwind rejects it outright:
405
+
406
+ ```
407
+ @theme blocks must only contain custom properties or @keyframes.
408
+ ```
409
+
410
+ `@theme` takes declarations, not an `@import`, and Jig's tokens cannot move into
411
+ one regardless: `@theme` requires them top-level and unnested, while Jig's live
412
+ in `:root` and are redeclared under `[data-theme="dark"]` and a
413
+ `prefers-color-scheme` query. That structure is what makes dark mode work.
414
+
415
+ ### Optional: Tailwind utility classes
416
+
417
+ The flat import gives you the tokens. It does **not** give you `p-card` or
418
+ `rounded-surface` as classes — Tailwind only generates utilities for names
419
+ declared in `@theme`. If you want them, add one alias block:
420
+
421
+ ```css
422
+ /* jig/utilities.css — one per project, not one per mode */
423
+ @theme inline {
424
+ --color-text-strong: var(--color-text-strong);
425
+ --color-bg-base: var(--color-bg-base);
426
+ --spacing-card: var(--spacing-card);
427
+ --radius-surface: var(--radius-surface);
428
+ /* …every token you want as a utility */
429
+ }
430
+ ```
431
+
394
432
  ```css
433
+ /* your root stylesheet */
395
434
  @import "tailwindcss";
435
+ @import "./jig/utilities.css";
436
+ ```
437
+
438
+ Then `<article class="bg-bg-base p-card rounded-surface">` works, and the value
439
+ still comes from whichever mode barrel that route loaded — so one set of
440
+ utilities serves every mode, with no `dark:` variants and nothing per-mode.
441
+
442
+ **`@theme` and `@theme inline` behave identically here.** Both generate the
443
+ utilities; both also emit a self-referential declaration you will see in the
444
+ compiled CSS:
445
+
446
+ ```css
447
+ @layer theme { :root, :host { --radius-surface: var(--radius-surface) } } /* Tailwind's */
448
+ :root { --radius-surface: var(--radius-md) } /* Jig's */
449
+ ```
450
+
451
+ **This is not a bug and must not be "fixed".** Tailwind's copy is inside
452
+ `@layer theme`; Jig's is unlayered. Unlayered declarations beat layered ones in
453
+ the cascade regardless of source order, so Jig's value always wins. Removing
454
+ either one breaks something: drop the alias and the utility stops existing, drop
455
+ Jig's and the token has no value.
456
+
457
+ **The alternative, if the duplicate bothers you:** alias to *different* names,
458
+ the way a project with its own semantic layer would.
459
+
460
+ ```css
396
461
  @theme {
397
- @import "./jig/theme.css";
462
+ --color-ink: var(--color-text-strong);
463
+ --color-paper: var(--color-bg-base);
398
464
  }
399
465
  ```
400
- Yields `bg-surface`, `rounded-surface`, `p-card`, `text-body` as utilities.
466
+
467
+ Utilities become `text-ink`, `bg-paper`. No self-reference, no duplicate
468
+ declaration, and the names read as yours rather than as Jig's. The cost is a
469
+ mapping to maintain. Both arrangements are correct; this is a naming preference,
470
+ not a correctness one.
471
+
472
+ **A missing alias fails silently.** A class whose token is not in the block
473
+ renders onto the element and matches no rule — no error, no warning, no style.
474
+ Generate the block rather than hand-maintaining it, and regenerate it when the
475
+ token layer changes.
401
476
 
402
477
  **In a monorepo, add `@source` for every workspace package that uses these
403
478
  utilities.** Tailwind v4's content detection does not cross package boundaries:
@@ -510,17 +510,3 @@ Before writing a new component, check whether it is a composite of things that a
510
510
  A new pattern earns a place here when it has been built three times. Before then it is a component, not a pattern.
511
511
 
512
512
  Each entry states: anatomy in order, complete state list, rules that are decidable, and mode variance. If a rule cannot be checked by looking at the output, it belongs in `04-principles.md`.
513
-
514
- ---
515
-
516
- ## Notes for the author (not for the agent)
517
-
518
- **Where your taste is recorded here:**
519
- - `P-01` — the whole feedback table is a position. Toasts are over-used because they are easy to build and require no layout decisions; treating them as the narrowest case rather than the default is deliberate.
520
- - `P-03` help-text-before-control. Contested — many systems put it after. Placing it before means it is read before the user commits to typing, which matters more in forms people fill once.
521
- - `P-04` one-column forms, and the no-JS baseline for the primary action.
522
- - `P-06` stable row identity, absolute timestamps, no hover-only truncation. The procurement instinct again: the record is evidence before it is a convenience.
523
-
524
- **Deliberately absent.** Navigation, cards, tabs, and toasts-as-a-component. Navigation and cards vary too much by project to have decidable rules yet — they would produce prose, not constraints. Add them once you have built enough to see the invariant.
525
-
526
- **Worth testing before extending.** These 12 cover most of what generated UI gets wrong. Point an agent at a form and a table with `00`, `01`, `02` and `03` loaded, and compare against the same task with nothing loaded. If `P-03` and `P-05` do not visibly change the output, the rules are not decidable enough and the fix is more specificity, not more patterns.
@@ -138,17 +138,3 @@ An invented accent, a decorative animation, a gradient filling an empty space
138
138
  A codebase with one consistent approach is more maintainable than one with a better approach applied to 30% of it. Note the divergence, raise it, change it deliberately as its own work — not silently, mid-task.
139
139
 
140
140
  **This tiebreaker outranks the other six.** It does not outrank Part 1: a local convention creating a genuine accessibility risk is a defect to raise, not a convention to match.
141
-
142
- ---
143
-
144
- ## Notes for the author (not for the agent)
145
-
146
- **What changed in v0.2.** Part 1 did not exist. The file was adjudicative only — seven tiebreakers that fire when rules collide, with no method for producing a rule not yet written. That meant the system handed an agent 51 known failures and no way to recognise the 52nd. The four frames are that method.
147
-
148
- Frame 3 is the most immediately useful, because it is the only idea here that produces a number. Everything else in this system is checked by inspection; interaction cost is checked by counting, which makes it the one principle an agent can be held to objectively.
149
-
150
- Frames 1 and 2 are close to reasoning already embedded in `00` — the risk frame is *why* most of those rules exist, and the rationale requirement is the decidability test that let them in. Stating them explicitly means the next rule can be derived rather than remembered.
151
-
152
- **Tiebreaker 7 remains the one to argue about**, and now has a stated ceiling: it loses to Frame 1. Without that boundary, "match the codebase" would license inheriting anything.
153
-
154
- Tiebreakers 1 and 2 are the same instinct from two directions, and both come from outside software — a document that looks wrong gets marked and filed, never destroyed.
package/rules/05-copy.md CHANGED
@@ -141,13 +141,3 @@ See `P-01` for *where* the message goes and `F-37` for field-level validation te
141
141
  6. Numerals as figures, formatted consistently? (`I-83`)
142
142
  7. One word per concept across the whole product? (`I-87`)
143
143
  8. Every error saying what happened and what to do next? (`I-90`)
144
-
145
- ---
146
-
147
- ## Notes for the author (not for the agent)
148
-
149
- **Where your taste is recorded here:** the ban on apology words in errors, and `I-90`'s requirement that the heading and button work without the body text. Both come from the same instinct as the rest of the system — the person reading is trying to get something done, and the interface should not make them wade.
150
-
151
- `I-87` is the rule most likely to need a project-specific companion. A term list belongs in the brand file's voice section, not here; this rule only says that one must exist and be followed.
152
-
153
- Deliberately absent: tone-of-voice guidance beyond plain language. Tone is a brand decision and varies per client, so it belongs in `03-brand.md` when that file exists.
package/rules.index.json CHANGED
@@ -84,9 +84,10 @@
84
84
  },
85
85
  {
86
86
  "id": "A-05",
87
- "bucket": "judgment",
88
- "severity": "note",
89
- "since": "0.1.0"
87
+ "bucket": "mechanical",
88
+ "severity": "warning",
89
+ "since": "0.1.0",
90
+ "detector": "emoji-icon"
90
91
  },
91
92
  {
92
93
  "id": "A-06",
@@ -108,15 +109,17 @@
108
109
  },
109
110
  {
110
111
  "id": "A-09",
111
- "bucket": "judgment",
112
- "severity": "note",
113
- "since": "0.1.0"
112
+ "bucket": "mechanical",
113
+ "severity": "warning",
114
+ "since": "0.1.0",
115
+ "detector": "marketing-voice"
114
116
  },
115
117
  {
116
118
  "id": "A-10",
117
- "bucket": "judgment",
118
- "severity": "note",
119
- "since": "0.1.0"
119
+ "bucket": "mechanical",
120
+ "severity": "warning",
121
+ "since": "0.1.0",
122
+ "detector": "placeholder-content"
120
123
  },
121
124
  {
122
125
  "id": "B-11",
@@ -23,6 +23,29 @@ cite the number when you follow or deliberately break one.
23
23
 
24
24
  Load `{{rules_path}}/04-principles.md` only when two rules conflict.
25
25
 
26
+ ## If the project uses Tailwind v4
27
+
28
+ Jig needs nothing from Tailwind and Tailwind needs nothing from Jig: the flat
29
+ `@import` of the token barrel is enough, and every token is readable as
30
+ `var(--color-text-strong)` from any component. Build that way unless the user
31
+ tells you otherwise.
32
+
33
+ Tailwind can additionally generate utility classes (`p-card`, `rounded-surface`)
34
+ from Jig's tokens, but only if the project declares an alias block. **That is
35
+ the user's decision, not yours.** It changes how every component in the codebase
36
+ is written, and both arrangements are correct — so when you find Tailwind v4 in
37
+ a project without one:
38
+
39
+ 1. Say you found it, and that the token layer works today without any change.
40
+ 2. Show them the two arrangements from "Optional: Tailwind utility classes" in
41
+ `{{rules_path}}/02-tokens.md` — self-referential aliases keeping Jig's names,
42
+ or aliases to names of their own.
43
+ 3. Ask which they want, and **wait**. Do not pick one and proceed.
44
+
45
+ Never write `@import "tailwindcss"` into a project that does not already have
46
+ it. Jig is framework-agnostic by design, and a token layer that drags in a CSS
47
+ framework has stopped being one.
48
+
26
49
  ## Commands
27
50
 
28
51
  {{available_commands}}
@@ -55,7 +78,7 @@ changed since, do not re-run it.
55
78
  Before you finish a task that generated or modified UI, emit this line:
56
79
 
57
80
  ```text
58
- JIG_CHECK: version=<version> mode=<mode> mechanical=<pass|fail|skipped>:<n> judgment=<ran|skipped>
81
+ JIG_CHECK: version=<version> mode=<mode> mechanical=<pass|fail|skipped>:<n> judgment=<ran|skipped> files=<n> styled=<n>
59
82
  ```
60
83
 
61
84
  `jig check` emits this same line for the half it can do, with