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.
- package/CHANGELOG.md +822 -0
- package/README.md +107 -23
- package/dist/index.js +300 -15
- package/package.json +3 -2
- package/rules/00-anti-patterns.md +24 -14
- package/rules/01-modes.md +0 -14
- package/rules/02-tokens.md +80 -5
- package/rules/03-patterns.md +0 -14
- package/rules/04-principles.md +0 -14
- package/rules/05-copy.md +0 -10
- package/rules.index.json +12 -9
- package/templates/SKILL.md.tmpl +24 -1
|
@@ -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.
|
|
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.
|
package/rules/02-tokens.md
CHANGED
|
@@ -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 |
|
|
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
|
|
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** —
|
|
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
|
-
|
|
462
|
+
--color-ink: var(--color-text-strong);
|
|
463
|
+
--color-paper: var(--color-bg-base);
|
|
398
464
|
}
|
|
399
465
|
```
|
|
400
|
-
|
|
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:
|
package/rules/03-patterns.md
CHANGED
|
@@ -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.
|
package/rules/04-principles.md
CHANGED
|
@@ -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": "
|
|
88
|
-
"severity": "
|
|
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": "
|
|
112
|
-
"severity": "
|
|
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": "
|
|
118
|
-
"severity": "
|
|
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",
|
package/templates/SKILL.md.tmpl
CHANGED
|
@@ -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
|