trykeyline 0.1.0 → 0.1.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/.claude-plugin/marketplace.json +1 -1
- package/LICENSE +0 -16
- package/bin/keyline.mjs +2 -1
- package/package.json +1 -1
- package/plugins/keyline/.claude-plugin/plugin.json +1 -1
- package/plugins/keyline/plugin.json +22 -0
- package/plugins/keyline/skills/keyline/SKILL.md +22 -32
- package/plugins/keyline/skills/keyline/commands/audit.md +8 -3
- package/plugins/keyline/skills/keyline/commands/critique.md +3 -26
- package/plugins/keyline/skills/keyline/commands/init.md +5 -4
- package/plugins/keyline/skills/keyline/commands/polish.md +2 -2
- package/plugins/keyline/skills/keyline/commands/refine.md +6 -16
- package/plugins/keyline/skills/keyline/commands/sync.md +1 -3
- package/plugins/keyline/skills/keyline/commands/ux-laws.md +26 -0
- package/plugins/keyline/skills/keyline/domains/color/deriving-a-ramp.md +1 -1
- package/plugins/keyline/skills/keyline/domains/color/guide.md +10 -46
- package/plugins/keyline/skills/keyline/domains/color/status-and-exceptions.md +2 -2
- package/plugins/keyline/skills/keyline/domains/depth/elevation-recipes.md +2 -8
- package/plugins/keyline/skills/keyline/domains/depth/guide.md +12 -41
- package/plugins/keyline/skills/keyline/domains/layout/guide.md +31 -56
- package/plugins/keyline/skills/keyline/domains/layout/measure-and-rhythm.md +16 -16
- package/plugins/keyline/skills/keyline/domains/layout/radius-lines-and-layers.md +2 -2
- package/plugins/keyline/skills/keyline/domains/motion/guide.md +12 -47
- package/plugins/keyline/skills/keyline/domains/motion/patterns.md +1 -1
- package/plugins/keyline/skills/keyline/domains/motion/performance.md +1 -1
- package/plugins/keyline/skills/keyline/domains/patterns/guide.md +20 -62
- package/plugins/keyline/skills/keyline/domains/patterns/iteration.md +1 -9
- package/plugins/keyline/skills/keyline/domains/type/guide.md +13 -37
- package/plugins/keyline/skills/keyline/domains/type/ladders.md +1 -1
- package/plugins/keyline/skills/keyline/domains/type/type-audit.md +5 -8
- package/plugins/keyline/skills/keyline/glass/color/assets/color-tokens.css +34 -165
- package/plugins/keyline/skills/keyline/glass/color/assets/theme.ts +10 -70
- package/plugins/keyline/skills/keyline/glass/color/guide.md +3 -30
- package/plugins/keyline/skills/keyline/glass/color/intent-ramp.md +2 -34
- package/plugins/keyline/skills/keyline/glass/color/theming.md +5 -72
- package/plugins/keyline/skills/keyline/glass/depth/assets/GlassPanel.tsx +14 -121
- package/plugins/keyline/skills/keyline/glass/depth/assets/glass.css +29 -188
- package/plugins/keyline/skills/keyline/glass/depth/depth-cues.md +1 -16
- package/plugins/keyline/skills/keyline/glass/depth/guide.md +4 -73
- package/plugins/keyline/skills/keyline/glass/depth/performance-and-fallbacks.md +3 -11
- package/plugins/keyline/skills/keyline/glass/layout/adaptive-presentations.md +5 -91
- package/plugins/keyline/skills/keyline/glass/layout/assets/SectionNav.tsx +3 -6
- package/plugins/keyline/skills/keyline/glass/layout/assets/layout-tokens.css +11 -138
- package/plugins/keyline/skills/keyline/glass/layout/guide.md +10 -15
- package/plugins/keyline/skills/keyline/glass/layout/measure-and-rhythm.md +4 -6
- package/plugins/keyline/skills/keyline/glass/layout/radius-and-elevation.md +3 -2
- package/plugins/keyline/skills/keyline/glass/motion/assets/DropMenu.tsx +4 -6
- package/plugins/keyline/skills/keyline/glass/motion/assets/motion-presets.ts +10 -184
- package/plugins/keyline/skills/keyline/glass/motion/assets/motion-tokens.css +21 -105
- package/plugins/keyline/skills/keyline/glass/motion/easing-and-duration.md +2 -31
- package/plugins/keyline/skills/keyline/glass/motion/guide.md +10 -41
- package/plugins/keyline/skills/keyline/glass/motion/reduced-motion.md +1 -7
- package/plugins/keyline/skills/keyline/glass/motion/scroll-performance.md +3 -32
- package/plugins/keyline/skills/keyline/glass/motion/shared-element.md +1 -1
- package/plugins/keyline/skills/keyline/glass/motion/sound.md +10 -80
- package/plugins/keyline/skills/keyline/glass/type/assets/prose.tsx +8 -43
- package/plugins/keyline/skills/keyline/glass/type/assets/type-tokens.css +49 -86
- package/plugins/keyline/skills/keyline/glass/type/guide.md +5 -7
- package/plugins/keyline/skills/keyline/glass/type/long-form-prose.md +9 -74
- package/plugins/keyline/skills/keyline/glass/type/scale.md +2 -6
- package/plugins/keyline/skills/keyline/glass/type/weight-and-tracking.md +4 -20
- package/plugins/keyline/skills/keyline/scripts/check/css.mjs +5 -14
- package/plugins/keyline/skills/keyline/scripts/check/design.mjs +3 -1
- package/plugins/keyline/skills/keyline/scripts/check/files.mjs +70 -26
- package/plugins/keyline/skills/keyline/scripts/check/markup.mjs +3 -3
- package/plugins/keyline/skills/keyline/scripts/check/rules.mjs +9 -1
- package/plugins/keyline/skills/keyline/scripts/check.mjs +6 -3
- package/plugins/keyline/skills/keyline/scripts/contrast.mjs +2 -0
- package/plugins/keyline/skills/keyline/scripts/scan.mjs +29 -45
- package/plugins/keyline/skills/keyline/system/design-md.md +4 -4
- package/plugins/keyline/skills/keyline/system/guide.md +4 -8
- package/plugins/keyline/skills/keyline/system/presets.md +16 -16
- package/plugins/keyline/skills/keyline/system/roles.md +8 -8
package/LICENSE
CHANGED
|
@@ -19,19 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
19
19
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
20
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
21
|
SOFTWARE.
|
|
22
|
-
|
|
23
|
-
---
|
|
24
|
-
|
|
25
|
-
WHAT THIS LICENCE DOES AND DOESN'T COVER
|
|
26
|
-
|
|
27
|
-
The licence above covers the template: the components, hooks, styles, build
|
|
28
|
-
configuration and scripts.
|
|
29
|
-
|
|
30
|
-
It does not cover content. Text, photographs, designs, writing, résumés, logos
|
|
31
|
-
and brand marks belong to whoever made them, are not licensed here, and are not
|
|
32
|
-
yours to republish by virtue of having a copy of this repository. If you cloned
|
|
33
|
-
this from someone's personal site, replace their content with your own.
|
|
34
|
-
|
|
35
|
-
Third-party marks in `src/lib/stackIcons.tsx` are generated from simple-icons
|
|
36
|
-
(CC0-1.0). The brands they represent remain the property of their owners and
|
|
37
|
-
their trademark guidelines still apply.
|
package/bin/keyline.mjs
CHANGED
|
@@ -90,7 +90,8 @@ Options:
|
|
|
90
90
|
-t, --tool <tool[,tool…]> which agent — asked for when not given, Claude Code
|
|
91
91
|
when there's no terminal to ask on. One of:
|
|
92
92
|
${TOOL_IDS.join(", ")}
|
|
93
|
-
--
|
|
93
|
+
--agent <tool[,tool…]> the same as --tool
|
|
94
|
+
--user for every project on this machine, not just this one
|
|
94
95
|
--dir <path> into <path>, whatever the agent
|
|
95
96
|
-f, --force overwrite a copy that's already there
|
|
96
97
|
-h, --help this
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "trykeyline",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Interface design laws for coding agents: one skill that reads your codebase, writes a DESIGN.md in your own token names, then critiques, audits and fixes type, colour, layout, depth, motion and product patterns through it.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"design",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "keyline",
|
|
3
3
|
"displayName": "Keyline",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.2",
|
|
5
5
|
"description": "Interface design laws for coding agents. /keyline init reads your codebase and writes a DESIGN.md in your own token names; critique, audit, type, color, layout, depth, motion, patterns and polish work through it, with dials set by a preset (editorial, product, expressive or glass).",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Mihir Modi",
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
|
3
|
+
"name": "keyline",
|
|
4
|
+
"version": "0.1.2",
|
|
5
|
+
"description": "Interface design laws for coding agents. /keyline init reads your codebase and writes a DESIGN.md in your own token names; critique, audit, type, color, layout, depth, motion, patterns and polish work through it, with dials set by a preset (editorial, product, expressive or glass).",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "Mihir Modi",
|
|
8
|
+
"url": "https://modimihir.com"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://github.com/Mihirmodi27/keyline#readme",
|
|
11
|
+
"repository": "https://github.com/Mihirmodi27/keyline",
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"keywords": [
|
|
14
|
+
"design-system",
|
|
15
|
+
"design-tokens",
|
|
16
|
+
"typography",
|
|
17
|
+
"motion",
|
|
18
|
+
"colour",
|
|
19
|
+
"layout",
|
|
20
|
+
"accessibility"
|
|
21
|
+
]
|
|
22
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: keyline
|
|
3
|
-
description: "Design, review and fix interfaces with one set of laws applied in the project's own tokens
|
|
3
|
+
description: "Design, review and fix interfaces with one set of laws, applied in the project's own tokens and names. Commands: init (write DESIGN.md), sync, critique, audit, type, color, layout, depth, motion, patterns, polish. Use to set up or document a design system, critique or audit a UI, or improve typography, colour, contrast, dark mode, spacing, layout, hierarchy, cards, shadows, glass and translucent surfaces, animation, hover and focus states, UI copy, forms, dashboards, and agent and AI chat interfaces. Also for screens that feel generic, boxy, cluttered or template-made, and for building new UI. Not for backend-only work."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Keyline
|
|
@@ -13,45 +13,35 @@ Paths in these files are relative to this skill's folder. In shell commands it i
|
|
|
13
13
|
|
|
14
14
|
`/keyline <command> [target]` — the target is a route, file, directory or component; without one, the command takes the whole project, or the screen the conversation is about.
|
|
15
15
|
|
|
16
|
-
| Command | Does |
|
|
17
|
-
|
|
18
|
-
| `init` | Scans the codebase
|
|
19
|
-
| `sync` | Reconciles `DESIGN.md` with code that has moved
|
|
20
|
-
| `critique` | A designer's read of one screen
|
|
21
|
-
| `audit` | Every law checked with evidence, per domain
|
|
22
|
-
| `type` |
|
|
23
|
-
| `color` | Tokens
|
|
24
|
-
| `layout` | Measure, spacing
|
|
25
|
-
| `depth` | Elevation
|
|
26
|
-
| `motion` | Durations, curves, springs,
|
|
27
|
-
| `patterns` |
|
|
28
|
-
| `polish` | All six domains in order, one
|
|
29
|
-
|
|
30
|
-
**No command named?** Pick the one the request is about, say which, and go: "set up", "document", "extract" → `init`; "review", "what's wrong", "feedback" → `critique`; "check everything", "lint", "score" → `audit`; "font", "headings", "typography" → `type`; "colour", "contrast", "dark mode" → `color`; "spacing", "padding", "alignment", "cramped" → `layout`; "shadow", "card", "glass", "modal" → `depth`; "animate", "transition", "feels janky" → `motion`; "copy", "labels", "form", "dialog" → `patterns`; "make it better", "clean this up", "finish" → `polish`. Building something new runs the relevant domains'
|
|
16
|
+
| Command | Does |
|
|
17
|
+
|---|---|
|
|
18
|
+
| `init` | Scans the codebase and writes `DESIGN.md` |
|
|
19
|
+
| `sync` | Reconciles `DESIGN.md` with code that has moved |
|
|
20
|
+
| `critique` | A designer's ranked read of one screen |
|
|
21
|
+
| `audit` | Every law checked with evidence, scored per domain |
|
|
22
|
+
| `type` | Scale, weights, leading, tracking, numerals, fonts |
|
|
23
|
+
| `color` | Tokens, contrast, status colours, themes |
|
|
24
|
+
| `layout` | Measure, spacing, radii, lines, z-index, breakpoints |
|
|
25
|
+
| `depth` | Elevation, shadows, scrims, translucent surfaces |
|
|
26
|
+
| `motion` | Durations, curves, springs, reduced motion |
|
|
27
|
+
| `patterns` | Labels, buttons, forms, dialogs, agent UIs, dashboards |
|
|
28
|
+
| `polish` | All six domains in order, one pass each |
|
|
29
|
+
|
|
30
|
+
**No command named?** Pick the one the request is about, say which, and go: "set up", "document", "extract" → `init`; "review", "what's wrong", "feedback" → `critique`; "check everything", "lint", "score" → `audit`; "font", "headings", "typography" → `type`; "colour", "contrast", "dark mode" → `color`; "spacing", "padding", "alignment", "cramped" → `layout`; "shadow", "card", "glass", "modal" → `depth`; "animate", "transition", "feels janky" → `motion`; "copy", "labels", "form", "dialog" → `patterns`; "make it better", "clean this up", "finish" → `polish`. Building something new runs the relevant domains' `## Building it for a project` sections, in polish order (patterns has none; its nearest is `## Iterating on feedback`). `colour`, `typography`, `spacing`, `animate` and `copy` work as aliases.
|
|
31
31
|
|
|
32
32
|
## Before any command
|
|
33
33
|
|
|
34
34
|
1. **Read `DESIGN.md`** at the project root. `## Profile` gives the preset and any areas on another preset; each domain has its own section; `## Decisions` overrides the preset's defaults and must be respected; `## Open issues` is what's already known.
|
|
35
|
-
2. **No `DESIGN.md`:** for `init`, that's the job. For anything else, run `init`
|
|
36
|
-
3. **Load the command's file**, `commands/<command>.md`. It says which guides to read; read them, don't work from memory of them.
|
|
37
|
-
4. **Preset `glass`:** the exact system from modimihir.com.
|
|
35
|
+
2. **No `DESIGN.md`:** for `init`, that's the job. For anything else, run `init` for the domains this command touches — `## Profile` and those domains' sections — do the task, then recommend a full `/keyline init`. Don't refuse.
|
|
36
|
+
3. **Load the command's file**, `commands/<command>.md`. It says which guides to read, and which sections; read them, don't work from memory of them.
|
|
37
|
+
4. **Preset `glass`:** the exact system from modimihir.com. Beside each domain guide, read `glass/<domain>/guide.md` for the exact recipes behind the values in `DESIGN.md`; from its `assets/`, take only the files its `## Assets` list names for what you're changing. There is no `glass/patterns/`: patterns take the dials of the preset nearest the reader.
|
|
38
38
|
|
|
39
39
|
## What every command holds to
|
|
40
40
|
|
|
41
|
-
- **The project's names, always.** Reason in roles (`text.secondary`, `duration.small`), write code in whatever the project calls them. Never rename a project's tokens; never add a second token system beside the first.
|
|
41
|
+
- **The project's names, always.** Reason in roles (`text.secondary`, `duration.small`), write code in whatever the project calls them. Never rename a project's tokens; never add a second token system beside the first. Where a role you need has no row, reuse a value already doing that job; otherwise add one in the project's naming style, marked `proposed`.
|
|
42
42
|
- **Measure, don't estimate.** Mechanical laws are checked by `<keyline>/scripts/check.mjs`; contrast comes from `<keyline>/scripts/contrast.mjs`; inventories from `<keyline>/scripts/scan.mjs`; counts from the code. A finding carries its evidence: a ratio, a count, a `file:line`.
|
|
43
43
|
- **Laws hold under every preset; dials move between them.** A change that would break a law, or move a dial off the preset, is said out loud and asked about first. When the user agrees, it goes under `## Decisions` with its date and reason.
|
|
44
44
|
- **Keep what the design already chose.** An existing interface keeps its brand colour and gradients wherever they do a job, and its corners stay at least as round. Only a law removes something, and the report names that law: a pass that leaves the screen greyer or boxier than it found it has to say why.
|
|
45
45
|
- **Findings are not fixes.** `init`, `sync`, `critique` and `audit` change no code. The refining commands change only what their domain owns, and name the owning domain for anything else they notice.
|
|
46
|
-
- **Write back.** A decision that changes the system — a new role, a moved dial, a law knowingly broken — goes into `DESIGN.md`, so the next session starts from it
|
|
47
|
-
- **Look at the result.** When the project can be rendered, render it after a change and look at both themes, a narrow width, and reduced motion where they apply. Code that reads right can still look wrong.
|
|
48
|
-
|
|
49
|
-
## Where things are
|
|
50
|
-
|
|
51
|
-
| Folder | Holds |
|
|
52
|
-
|---|---|
|
|
53
|
-
| `commands/` | One file per command: what to read, the steps, what to report |
|
|
54
|
-
| `system/` | Setup: the canonical `roles.md`, the four `presets.md`, the `design-md.md` format, `detection.md` for each stack |
|
|
55
|
-
| `domains/<domain>/` | Each domain's `guide.md` — laws, dials per preset, building, auditing, checklist — and its deeper references |
|
|
56
|
-
| `glass/<domain>/` | The `glass` preset: modimihir.com's exact values, with drop-in tokens and components in `assets/` |
|
|
57
|
-
| `scripts/` | `check.mjs` (the detector: 24 mechanical laws, `--json`, exit codes for CI), `scan.mjs` (token and value inventory) and `contrast.mjs` (measured contrast, translucency composited). Node 18+, no dependencies |
|
|
46
|
+
- **Write back.** A decision that changes the system — a new role, a moved dial, a law knowingly broken, a change a law forced that a later session might undo (marked *law*) — goes into `DESIGN.md`, so the next session starts from it: roles and values in the domain's section, decisions under `## Decisions` (format: `system/design-md.md`). Open issues are one line each with evidence; fixed ones come off.
|
|
47
|
+
- **Look at the result.** When the project can be rendered, render it before and after a change and look at both themes, a narrow width, and reduced motion where they apply. Code that reads right can still look wrong.
|
|
@@ -2,7 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
Every law, checked with evidence, per domain — the full inspection `critique` is the summary of. Findings go into `DESIGN.md` so later sessions start from them.
|
|
4
4
|
|
|
5
|
-
**Read:** `DESIGN.md`, then
|
|
5
|
+
**Read:** `DESIGN.md`, then from every `domains/<domain>/guide.md` (all six unless the user narrows it):
|
|
6
|
+
|
|
7
|
+
- **The checklist** — each law's `### N.` heading and its bold first sentence: `awk 'FNR==1{p=0; print "== " FILENAME} /^## /{p = /^## Laws/} p' <keyline>/domains/*/guide.md | grep -oE '^(== |### ).*|^\*\*([^*]|\*[^*])+\*\*'`
|
|
8
|
+
- **`## Auditing existing code`**, and `## Checklist` where a guide still has one: `awk 'FNR==1{p=0; print "== " FILENAME} /^## /{p = /^## (Auditing|Checklist)/} p' <keyline>/domains/*/guide.md`
|
|
9
|
+
|
|
10
|
+
Read a law's full text where a finding needs it. Under preset `glass`, also each `glass/<domain>/guide.md` checklist (there is no `glass/patterns/`): `awk 'FNR==1{p=0; print "== " FILENAME} /^## /{p = /^## Checklist/} p' <keyline>/glass/*/guide.md`
|
|
6
11
|
|
|
7
12
|
**Steps:**
|
|
8
13
|
|
|
@@ -20,6 +25,6 @@ Every law, checked with evidence, per domain — the full inspection `critique`
|
|
|
20
25
|
| 1 | Several laws broken across templates; the domain isn't working as a system |
|
|
21
26
|
| 0 | No system: values typed inline, nothing on a ladder |
|
|
22
27
|
|
|
23
|
-
**Report:** a table of the six domains with score and finding count, then findings grouped by domain, worst first, one line each with evidence — `file:line`, a ratio, a count — and the law, with its UX-law name where one explains it (
|
|
28
|
+
**Report:** a table of the six domains with score and finding count, then findings grouped by domain, worst first, one line each with evidence — `file:line`, a ratio, a count — and the law, with its UX-law name where one explains it (`commands/ux-laws.md`). Close with the order to fix in: usually the lowest score first, unless a law in another domain blocks it.
|
|
24
29
|
|
|
25
|
-
**Write back:** every finding under `## Open issues` in `DESIGN.md`, replacing entries now fixed.
|
|
30
|
+
**Write back:** every finding under `## Open issues` in `DESIGN.md`, replacing entries now fixed.
|
|
@@ -2,37 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
A designer's read of one screen, flow or component: what a reader notices first, what's working, and the few changes that would matter most. Judgement, ranked — not a checklist. For every law checked with evidence, use `/keyline audit`.
|
|
4
4
|
|
|
5
|
-
**Read:** `DESIGN.md`. Then the law headings (the `### N.` lines) of all six
|
|
5
|
+
**Read:** `DESIGN.md`. Then the law headings (the `### N.` lines) of all six domain guides — enough to know which laws exist and cite them by number: `grep -n '^### [0-9]' <keyline>/domains/*/guide.md`. Read a law's full text only for the two or three domains where the screen's biggest problems sit, after step 3 has found them: `awk '/^## /{p = /^## Laws/} p' <keyline>/domains/<domain>/guide.md`. A critique ranks a few changes; it doesn't need every law in full, which is what `/keyline audit` is for.
|
|
6
6
|
|
|
7
7
|
**Steps:**
|
|
8
8
|
|
|
9
9
|
1. **Look at it.** Render it if you can — the running app, a preview, a screenshot through whatever browser tool you have — at a desktop and a phone width, in each theme. Read the code too, for what rendering hides (states, focus, reduced motion). If nothing can be rendered, say the critique is from code alone. With a headless Chrome and no other tool, `--screenshot --window-size=1440,900` shoots a page; dark mode needs the page's own theme switch (its `data-theme` or class) set in the URL or DOM, because Chrome's `--blink-settings` flag only gives light reliably.
|
|
10
10
|
2. **Name what the screen is for and who reads it**, in one sentence. Everything below is measured against it.
|
|
11
11
|
3. **Read it as its reader would, in order:** what the eye lands on first, second, third — and whether that order matches what matters. Then how it groups, what competes, what's missing, what's noise.
|
|
12
|
-
4. **Tie each problem to a law**, by domain and number (`layout law 3`), so the fix is a rule rather than a taste. Where a named UX law explains *why* the reader feels it, name that too (`layout law 13, Fitts's law`) — it tells the owner the problem is about people, not preference. The names
|
|
13
|
-
|
|
14
|
-
| UX law | The reader… | Where Keyline holds it |
|
|
15
|
-
|---|---|---|
|
|
16
|
-
| Aesthetic-usability | forgives a polished screen, and overlooks its problems in testing | Here: judge the laws, not the polish — a beautiful screen still gets every check |
|
|
17
|
-
| Hick's law | takes longer to choose as choices grow | patterns law 8 |
|
|
18
|
-
| Fitts's law | hits big, near targets faster | layout law 13 |
|
|
19
|
-
| Jakob's law | expects this product to work like the others they use | patterns law 14 |
|
|
20
|
-
| Miller's law | holds only a few chunks at once | patterns law 8 |
|
|
21
|
-
| Doherty threshold | keeps pace with responses under ≈ 400ms | patterns law 15; motion law 3 |
|
|
22
|
-
| Proximity | groups what sits close | layout law 2 |
|
|
23
|
-
| Similarity | groups what looks alike | patterns law 3; layout law 3 (one style per device) |
|
|
24
|
-
| Common region | groups what shares a boundary | layout law 3 — the quietest boundary that holds, a card last |
|
|
25
|
-
| Visual hierarchy | reads in the order size, weight, colour and position set | type laws 3–4; layout law 2; step 3 above |
|
|
26
|
-
| Progressive disclosure | copes when detail arrives on request | patterns law 8; layout law 11 |
|
|
27
|
-
| Recognition over recall | finds seeing easier than remembering | patterns law 16; patterns law 7 (shortcuts shown where the action lives) |
|
|
28
|
-
| Von Restorff | remembers the one thing that differs | colour law 7; motion law 9; one primary button (patterns law 13) |
|
|
29
|
-
| Peak-end rule | remembers the best or worst moment and the end | patterns law 17 |
|
|
30
|
-
| Goal-gradient | speeds up near the goal | patterns law 17 |
|
|
31
|
-
| Serial position | remembers the first and last items | patterns law 3; primary destinations first in the shell (layout dials) |
|
|
32
|
-
| Tesler's law | shouldn't carry complexity the product can | patterns law 16 |
|
|
33
|
-
| Postel's law | types things in many forms | patterns law 16 |
|
|
34
|
-
| Occam's razor | is served by the fewest parts that do the job | motion law 1; patterns law 12; `commands/refine.md` |
|
|
35
|
-
| Feedback loops | needs to see every action land | patterns law 15 |
|
|
12
|
+
4. **Tie each problem to a law**, by domain and number (`layout law 3`), so the fix is a rule rather than a taste. Where a named UX law explains *why* the reader feels it, name that too (`layout law 13, Fitts's law`) — it tells the owner the problem is about people, not preference. The names, and where Keyline holds each, are in `commands/ux-laws.md`.
|
|
36
13
|
|
|
37
14
|
**Report:**
|
|
38
15
|
|
|
@@ -40,4 +17,4 @@ A designer's read of one screen, flow or component: what a reader notices first,
|
|
|
40
17
|
- **Changes, most important first** — at most five. Each: what's wrong, as the reader feels it; the law; the fix in a sentence, in the project's names; and the command that makes it (`/keyline layout`).
|
|
41
18
|
- **Keep** — what's working and shouldn't be touched by the fixes.
|
|
42
19
|
|
|
43
|
-
Don't pad to five. Don't list what's fine as a problem to fix.
|
|
20
|
+
Don't pad to five. Don't list what's fine as a problem to fix.
|
|
@@ -4,12 +4,13 @@ Fit Keyline to this project: find out once what it calls things, who it's for an
|
|
|
4
4
|
|
|
5
5
|
**Read:** `system/guide.md`, then `system/roles.md`, `system/presets.md` and `system/design-md.md`. `system/detection.md` when the scan leaves you unsure where a stack keeps its tokens.
|
|
6
6
|
|
|
7
|
-
**Steps:** follow `system/guide.md` from step 1 to step 5 — scan, map by usage, measure contrast, choose the preset from the reader, ask at most four questions, write the file. Before writing `## Open issues`, read the law headings (the `### N.` lines) of each domain guide you cite, so every citation names the right law.
|
|
7
|
+
**Steps:** follow `system/guide.md` from step 1 to step 5 — scan, map by usage, measure contrast, choose the preset from the reader, ask at most four questions, write the file. Before writing `## Open issues`, read the law headings (the `### N.` lines) of each domain guide you cite, so every citation names the right law: `grep -n '^### [0-9]' <keyline>/domains/*/guide.md`. Once the preset is chosen, read every guide's `## Dials` for its column, since `## Decisions` records each dial the project sets differently: `awk 'FNR==1{p=0; print "== " FILENAME} /^## /{p = /^## Dials/} p' <keyline>/domains/*/guide.md`.
|
|
8
8
|
|
|
9
|
+
- **Preset `glass`:** the values come from the tables in each `glass/<domain>/guide.md`, under the project's existing names wherever it has them, not from the whole folder. There is no `glass/patterns/`; patterns take the dials of the preset nearest the reader.
|
|
9
10
|
- **`DESIGN.md` already exists:** this is `/keyline sync`, not a rewrite. Say so and run that instead.
|
|
10
|
-
- **A new project with no interface yet:** there is nothing to scan. Ask what the product is and who reads it, propose a preset, and write `## Profile` with the preset's defaults from
|
|
11
|
-
- **
|
|
11
|
+
- **A new project with no interface yet:** there is nothing to scan. Ask what the product is and who reads it, propose a preset, and write `## Profile` and each domain's section with the preset's defaults from those `## Dials`, marked as defaults. Generate tokens only if asked (`system/guide.md` §6).
|
|
12
|
+
- **Run for some domains only** (another command found `DESIGN.md` missing): write `## Profile` and those domains' sections, and leave the rest for a full run.
|
|
12
13
|
|
|
13
14
|
**Report**, briefly: the preset and the evidence for it, how many roles mapped cleanly, which names you proposed, and the three open issues that matter most. Then the next step — usually `/keyline critique` on the main screen, or `/keyline polish` if the user wants changes.
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
Generating tokens is a separate request.
|
|
@@ -9,10 +9,10 @@ All six refining commands on one target, in order, each as its own reviewable pa
|
|
|
9
9
|
1. Read `DESIGN.md`. If it's missing, run `/keyline init` first and say so — polish without a profile is guessing.
|
|
10
10
|
2. Agree the target: one screen, flow or component. "The whole app" is several polishes; offer to start with the screen readers see most.
|
|
11
11
|
3. Run `commands/refine.md` once per domain, in order, reading each domain's guide when its turn comes. Keep each pass to its domain.
|
|
12
|
-
4. **Settle.** Later passes create and change elements the earlier ones never saw — the depth pass turns a callout into a note, and nothing has given the note its type role. Re-run each earlier domain's
|
|
12
|
+
4. **Settle.** Later passes create and change elements the earlier ones never saw — the depth pass turns a callout into a note, and nothing has given the note its type role. Re-run each earlier domain's checklist (`commands/refine.md` step 7) on the elements later passes created or changed, and fix within that domain.
|
|
13
13
|
5. Render the result beside the starting point.
|
|
14
14
|
|
|
15
|
-
Polish improves the screen it's given; it doesn't move navigation or add features (`commands/refine.md`, the
|
|
15
|
+
Polish improves the screen it's given; it doesn't move navigation or add features (`commands/refine.md`, the limits).
|
|
16
16
|
|
|
17
17
|
**Report**, per domain in order: what changed and why, in a line or two each — a domain with nothing to fix says so — then what the settling pass changed. Then what was measured, what went into `DESIGN.md`, and anything left for later with its command.
|
|
18
18
|
|
|
@@ -2,28 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
`type`, `color`, `layout`, `depth`, `motion` and `patterns` share one procedure. Each command's own file names its guide and what it covers; this is the rest.
|
|
4
4
|
|
|
5
|
-
1. **Read the domain's `guide.md`
|
|
5
|
+
1. **Read the parts of the domain's `guide.md` a pass uses**, not the whole file: `## Laws`, `## Dials` (the preset's targets, which `DESIGN.md`'s values are judged against), `## Auditing existing code`, `## Writing back to DESIGN.md`, and `## Checklist` if it has one — `awk '/^## /{p = /^## (Laws|Dials|Auditing|Writing back|Checklist)/} p' <keyline>/domains/<domain>/guide.md`. Add `## Building it for a project` only when adding a role or proposing a set for a domain with no tokens (patterns has none; its nearest is `## Iterating on feedback`), read the same way with its heading in the pattern. Then the references its laws point to, for the parts the target touches. Under preset `glass`, read `glass/<domain>/guide.md` too (there is no `glass/patterns/`), and take from its `assets/` only the files that guide's own `## Assets` list names for what you're changing.
|
|
6
6
|
2. **Read the domain's section of `DESIGN.md`** and every `## Decisions` entry it covers. A decision beats a dial; never "fix" a decided value back.
|
|
7
7
|
3. **Find the problems first.** Run `node <keyline>/scripts/check.mjs <target> --root <project root> --rule <domain>` for what can be checked mechanically, then the guide's `## Auditing existing code` on the target only, and list what breaks a law before changing anything. With no target, take the screen the conversation is about; if there isn't one, ask which, or offer the whole project as a longer pass.
|
|
8
|
-
4. **Fix in the project's names**, at the smallest scope that removes the cause — a token over twenty call sites, a shared component over its instances. Add a role only where the guide's `## Building it` says one is missing, in the project's naming style, marked `proposed` in `DESIGN.md`. Where the project has no tokens for the domain at all — no type tokens, no motion tokens — propose a full set in the project's style, each marked `proposed`, and fix against it.
|
|
9
|
-
5. **
|
|
10
|
-
6. **Sweep** every route the changed rule reaches and report what moved
|
|
11
|
-
7. **
|
|
12
|
-
8. **Write back** to `DESIGN.md`: the domain's section if a role or value changed, `## Decisions` for anything moved off the preset (with the user's go-ahead) and for any change a law forced that a later session might undo (marked *law*), and strike fixed `## Open issues`.
|
|
13
|
-
9. **Run the guide's `## Checklist`** on what you changed, and the detector again on the target: nothing you touched should still be flagged.
|
|
8
|
+
4. **Fix in the project's names**, at the smallest scope that removes the cause — a token over twenty call sites, a shared component over its instances. Add a role only where the guide's `## Building it for a project` says one is missing, in the project's naming style, marked `proposed` in `DESIGN.md`. Where the project has no tokens for the domain at all — no type tokens, no motion tokens — propose a full set in the project's style, each marked `proposed`, and fix against it.
|
|
9
|
+
5. **A fix that belongs to an earlier domain** in polish order but only became visible in this pass — the note this pass made from a callout now needs its type role — is made here, and reported as that domain's. Anything else another domain owns is reported with its command.
|
|
10
|
+
6. **Sweep** every route the changed rule reaches and report what moved: `awk '/^## /{p = /^## The sweep/} p' <keyline>/domains/patterns/iteration.md`.
|
|
11
|
+
7. **Check what you changed** against the domain's checklist — each law's bold first sentence, plus anything left under `## Checklist` — and run the detector again on the target: nothing you touched should still be flagged.
|
|
14
12
|
|
|
15
13
|
**Three limits hold in every pass:**
|
|
16
14
|
|
|
17
15
|
- **The brief wins over the preset.** An element the product or its brief requires stays, even where a dial or a law would cut it — a dashboard's activity feed, a top bar the product has. The pass changes how it's built, not whether it exists.
|
|
18
16
|
- **Improving is not restructuring.** Improving an existing screen doesn't move its navigation or add features — a collapsible icon rail, a theme toggle, a new view. Those go under `## Open issues` unless the user asks for them.
|
|
19
|
-
- **Improving keeps what the design already chose
|
|
20
|
-
|
|
21
|
-
**Where a preset's default and the existing design disagree**, this is the order, and the one place it is decided:
|
|
22
|
-
|
|
23
|
-
- **Dividers.** The design's section device stays: panels stay panels, made quieter (layout law 3). The preset's "full-bleed hairline, no tiles" describes a new system. One thing changes either way: a row of stat tiles folds into one strip of figures parted by hairlines, because that is a law about figures, not a divider dial — unless keeping the tiles is recorded under `## Decisions`.
|
|
24
|
-
- **Corners.** The nesting law (layout law 5) never sharpens a corner. Where inner = outer − inset would come out sharper than the inner corner is now, raise the outer radius or trim the inset until they share a centre; if neither fits, the corners aren't concentric and the inner keeps its radius.
|
|
25
|
-
- **Line weight.** The design's line weights stay, like its corners. Product's 0.5px hairline is for a new system, or for an existing line that reads heavy and whose colour is strong enough to survive halving; a border already near the surface colour keeps its 1px.
|
|
26
|
-
|
|
27
|
-
Record each of these calls under `## Decisions`, so the next run makes the same one.
|
|
17
|
+
- **Improving keeps what the design already chose** (`SKILL.md`). Where a preset's default and the existing design disagree on dividers, corners or line weight, `## Refining an existing design` in the layout guide decides; read it when the pass touches layout or depth: `awk '/^## /{p = /^## Refining an existing design/} p' <keyline>/domains/layout/guide.md`.
|
|
28
18
|
|
|
29
19
|
**Report:** what was wrong, as a reader would feel it; what changed, by token or component, with the law; what was measured (sizes, ratios, durations); and anything noticed outside the domain, with its command.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Bring `DESIGN.md` back in line with code that has moved since it was written. An audit of the file, not a rewrite of it.
|
|
4
4
|
|
|
5
|
-
**Read:** `system/
|
|
5
|
+
**Read:** `system/design-md.md` for the format.
|
|
6
6
|
|
|
7
7
|
**Steps:**
|
|
8
8
|
|
|
@@ -13,5 +13,3 @@ Bring `DESIGN.md` back in line with code that has moved since it was written. An
|
|
|
13
13
|
5. Propose the edits as a short diff and apply them. Every name the user chose and every `## Decisions` entry survives; a decision the code now contradicts is reported, never silently dropped.
|
|
14
14
|
|
|
15
15
|
**Report:** what drifted, what's newly fixed, what's newly broken, and anything in `## Decisions` the code no longer follows.
|
|
16
|
-
|
|
17
|
-
**Changes no code** — only `DESIGN.md`.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# UX laws
|
|
2
|
+
|
|
3
|
+
The named laws behind what a reader feels, and where Keyline holds each one. `critique` and `audit` cite a name beside a finding only where it explains why the reader feels the problem: the names are reasons, not a checklist. In the table, "step 3 above" is `critique`'s step 3: reading the screen in order.
|
|
4
|
+
|
|
5
|
+
| UX law | The reader… | Where Keyline holds it |
|
|
6
|
+
|---|---|---|
|
|
7
|
+
| Aesthetic-usability | forgives a polished screen, and overlooks its problems in testing | Here: judge the laws, not the polish — a beautiful screen still gets every check |
|
|
8
|
+
| Hick's law | takes longer to choose as choices grow | patterns law 8 |
|
|
9
|
+
| Fitts's law | hits big, near targets faster | layout law 13 |
|
|
10
|
+
| Jakob's law | expects this product to work like the others they use | patterns law 14 |
|
|
11
|
+
| Miller's law | holds only a few chunks at once | patterns law 8 |
|
|
12
|
+
| Doherty threshold | keeps pace with responses under ≈ 400ms | patterns law 15; motion law 3 |
|
|
13
|
+
| Proximity | groups what sits close | layout law 2 |
|
|
14
|
+
| Similarity | groups what looks alike | patterns law 3; layout law 3 (one style per device) |
|
|
15
|
+
| Common region | groups what shares a boundary | layout law 3 — the quietest boundary that holds, a card last |
|
|
16
|
+
| Visual hierarchy | reads in the order size, weight, colour and position set | type laws 3–4; layout law 2; step 3 above |
|
|
17
|
+
| Progressive disclosure | copes when detail arrives on request | patterns law 8; layout law 11 |
|
|
18
|
+
| Recognition over recall | finds seeing easier than remembering | patterns law 16; patterns law 7 (shortcuts shown where the action lives) |
|
|
19
|
+
| Von Restorff | remembers the one thing that differs | colour law 7; motion law 9; one primary button (patterns law 13) |
|
|
20
|
+
| Peak-end rule | remembers the best or worst moment and the end | patterns law 17 |
|
|
21
|
+
| Goal-gradient | speeds up near the goal | patterns law 17 |
|
|
22
|
+
| Serial position | remembers the first and last items | patterns law 3; primary destinations first in the shell (layout dials) |
|
|
23
|
+
| Tesler's law | shouldn't carry complexity the product can | patterns law 16 |
|
|
24
|
+
| Postel's law | types things in many forms | patterns law 16 |
|
|
25
|
+
| Occam's razor | is served by the fewest parts that do the job | motion law 1; patterns law 12; `commands/refine.md` |
|
|
26
|
+
| Feedback loops | needs to see every action land | patterns law 15 |
|
|
@@ -64,7 +64,7 @@ The brand hue is given; derive the rest per theme. A logo's colour is where the
|
|
|
64
64
|
1. **`accent` in light.** The highest in-gamut chroma at a lightness where `text.on-accent` clears 4.5:1 — with white text, usually L 0.45–0.55. If the script reports clipping, lower the chroma, not the lightness.
|
|
65
65
|
2. **`accent` in dark.** Either one value, L roughly 0.72–0.78 with chroma trimmed a little, with `text.on-accent` flipped to the dark `surface.page`; or, where the brand keeps white text on its fill, two: the fill near its light-theme lightness, and a lighter `accent.ink` at 4.5:1 on the dark page for links, icons and brand text, since no single value clears both. Measure each as a new colour.
|
|
66
66
|
3. **Its hover.** 0.05 in L away from the page — darker in light, lighter in dark: more contrast in both.
|
|
67
|
-
4. **`surface.selected`.** For a selected row in a dense table, the accent hue at the `surface.selected` lightness, chroma about 0.03. For the current nav item or tab under product and expressive, the stronger tint colour law 7
|
|
67
|
+
4. **`surface.selected`.** For a selected row in a dense table, the accent hue at the `surface.selected` lightness, chroma about 0.03. For the current nav item or tab under product and expressive, the stronger tint colour law 7 defines, carrying `accent.ink`. Re-measure the *lowest* text tier on each: this fails most often, because a tint costs a little luminance.
|
|
68
68
|
5. **`focus`.** The accent, if it clears 3:1 on every surface including `surface.selected`, and on the page: `outline-offset` leaves a gap of page colour, so a ring round an accent button is seen against the page.
|
|
69
69
|
|
|
70
70
|
**When the brand colour can't be text.** Yellows, limes and light cyans have no lightness at usable chroma where white text clears 4.5:1, and fail as text on white. Keep them for fills and marks with dark `text.on-accent`, and derive a darker token for anything text-sized — `text.link`, if links are coloured. Name both for their jobs; neither is "brand-yellow".
|
|
@@ -4,12 +4,7 @@ A near-monochrome reading site, a dense product with a full status palette and a
|
|
|
4
4
|
|
|
5
5
|
## Before you start
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
- **No `DESIGN.md`:** run `/keyline init` for this domain only, do the task, then recommend the full setup.
|
|
10
|
-
- **A role you need has no row:** reuse a value already doing that job; otherwise add one in the project's naming style and record it in `DESIGN.md`.
|
|
11
|
-
- **The task would break a law or move a dial off its preset:** say so and ask; if the user confirms, record it with its reason under `## Decisions`.
|
|
12
|
-
- **Preset `glass`:** the values are in `DESIGN.md`; `glass/color/guide.md` has the exact recipes behind them.
|
|
7
|
+
Reads `DESIGN.md` `## Colour`.
|
|
13
8
|
|
|
14
9
|
## Laws
|
|
15
10
|
|
|
@@ -32,7 +27,7 @@ The allowed literals:
|
|
|
32
27
|
|
|
33
28
|
**A text colour is picked for text contrast, never a mid-ramp step borrowed from the border and fill range.**
|
|
34
29
|
|
|
35
|
-
Text needs 4.5:1 against its surface; a border or icon a user must see to operate a control, 3:1; a decorative edge, only visibility. One value rarely serves both, least in a *light* theme, where mid steps are light tints on white
|
|
30
|
+
Text needs 4.5:1 against its surface; a border or icon a user must see to operate a control, 3:1; a decorative edge, only visibility. One value rarely serves both, least in a *light* theme, where mid steps are light tints on white: a timestamp borrowing a border-hover step can sit at 1.66:1.
|
|
36
31
|
|
|
37
32
|
Dark mode hides this — the same indices sit far lighter on near-black, so every mid step passes, and a system reviewed in dark ships a light theme whose body copy fails. So `text.primary`, `text.secondary`, `text.tertiary` and any tiers between are separate tokens. Icons take their label's tier or one below — an icon has more visual mass than text at the same value — and both advance together on hover. Decorative marks with no text (bullets, dots, inactive rail markers) are `mark`, stay on the ramp and aren't held to text contrast; kept faint, a list doesn't read as a column of dark dots.
|
|
38
33
|
|
|
@@ -84,23 +79,17 @@ A status palette is a set of **pairs**, each a foreground at 4.5:1 on the page a
|
|
|
84
79
|
|
|
85
80
|
**Every hue has one job, written down. Each chromatic spend beyond the accent and the status palette is named for what it's for, with a reason that can't spread to another element or another job, and contained so that only the mark takes the colour.**
|
|
86
81
|
|
|
87
|
-
Colour is attention, spent because of what the element *is*, never as decoration.
|
|
82
|
+
Colour is attention, spent because of what the element *is*, never as decoration. "It's important", "it's new" or "it's the brand colour" can spread, and becomes a palette within two components, because every author thinks their element qualifies; a reason naming a category of one can't. Only the mark takes the colour, never the text inside. Record each as `exception.<what-it's-for>` in `DESIGN.md`, a family as `exception.<question>.<member>`, with its reason; how many a system holds is a dial. First ask whether weight, position and space can do the job. Most can. That test is for colour being *added*.
|
|
88
83
|
|
|
89
84
|
**Colour the design already has stays.** When improving an existing interface, its brand colour stays wherever it does a job: the selected nav item, the primary data series, a progress or usage bar, a brand mark, the gradient every avatar shares. Each of those is the owner's decision and part of how the product is recognised. Turning it grey is a regression that no law asks for. Colour comes out only when it breaks a law, and the change cites that law: text under its contrast floor, a second hue competing with the primary action, colour carrying a state on its own, or body text dyed for emphasis. Even then, the first fix is the shade (re-pick it until it passes) rather than taking the colour away. Muting the brand for calm is a dial for a new system, not a fix for an existing one.
|
|
90
85
|
|
|
91
|
-
**
|
|
92
|
-
|
|
93
|
-
Only the edge, the icon or the glyph takes the colour, never the text inside: a coloured paragraph shares the loudness until it means nothing. Record each as `exception.<what-it's-for>` in `DESIGN.md`, a family as `exception.<question>.<member>`, with its reason: how many a system holds is a dial; a written, non-transferable job is not. First ask whether weight, position and space can do the job. Most can.
|
|
94
|
-
|
|
95
|
-
**One hue acts, and colour points at what still needs a person.** The accent's jobs — primary action, focus, "your turn" — share one hue, and it marks one action per view. A second highlight hue beside it (a teal next to a blue) splits one meaning in two, and readers start asking which colour means what; whatever wanted it goes to a status pair, a neutral marker or weight. A hue family is not a highlight: it sits on marks and asks nothing of the reader. A place in the data with one member, today's column on a calendar, may be a written exception.
|
|
86
|
+
**One hue acts.** The accent's jobs — primary action, focus, "your turn" — share one hue, and it marks one action per view. A second highlight hue beside it splits one meaning in two; whatever wanted it goes to a status pair, a neutral marker or weight.
|
|
96
87
|
|
|
97
88
|
**Where the reader *is* takes the accent as a tint, not a fill.** Under product and expressive, the current nav item sits on `surface.selected`: the accent at about 25% over the surface it sits on, with the icon and label in `accent.ink`. Measure that label at 4.5:1 on the tint in each theme, and deepen the ink, not the tint, until it passes. The tint marks a place and the solid fill marks the action, so the selected item and the primary button never read as two calls to act. A page tab marks its current tab with layout law 3's underline instead, in `text.primary`. **This is the one definition of current**: the type guide adds weight to it, the motion guide moves it, neither re-picks it. Editorial and glass keep the current item neutral (a marker, a rule or weight), because a reading page has little chrome and the accent there belongs to focus.
|
|
98
89
|
|
|
99
|
-
**
|
|
90
|
+
**"Too grey" is answered with jobs, not decoration**: one hue family per question the reader can't answer at a glance — what, how, where, who — and no hue crosses into another's job. Whether done takes success colour is a dial; a chart's primary series takes the accent at full strength; code takes syntax highlighting under every preset, counted against no limit.
|
|
100
91
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
→ Hue families, figures, done states, charts, live and AI marks, destructive rows: `domains/color/status-and-exceptions.md`
|
|
92
|
+
→ The exception test and containment, hue families, figures, done states, charts, live and AI marks, destructive rows: `domains/color/status-and-exceptions.md`
|
|
104
93
|
|
|
105
94
|
### 8. A theme is a re-pick, not a reuse
|
|
106
95
|
|
|
@@ -140,7 +129,7 @@ A build-time token (a Sass variable, an inlined JS constant, a static theme obje
|
|
|
140
129
|
| Chromatic exceptions beyond accent and status | ≤ 1 | None by default; a hue family per question the reader can't answer at a glance (what, where, who), plus one-member exceptions — each listed | ≤ 4, each named |
|
|
141
130
|
| Hues that call for action | 1, the accent | 1, the accent | Brand hues, each placed by rule |
|
|
142
131
|
| Completed states | Uncoloured | `status.success` where completion is the news; uncoloured in a sequence passed through | Uncoloured, or per brand, sequences included, counted among the exceptions |
|
|
143
|
-
| Current location | Uncoloured: a marker, rule or weight | `surface.selected` (
|
|
132
|
+
| Current location | Uncoloured: a marker, rule or weight | `surface.selected` (law 7) with icon and label in `accent.ink` | As product, or per brand |
|
|
144
133
|
| Link colour | Text colour, underlined | `accent` | `accent` or a brand hue, underlined in prose |
|
|
145
134
|
| Text tiers, `text.primary` to `text.tertiary` | 3–5 | 3 | 3–4 |
|
|
146
135
|
| Minimum step between adjacent tiers (ratio of ratios) | 1.15× | 1.4× | 1.25× |
|
|
@@ -170,7 +159,7 @@ A build-time token (a Sass variable, an inlined JS constant, a static theme obje
|
|
|
170
159
|
3. **Fix the ends, per theme:** `surface.page` and `text.primary` near-white and near-black in light, the dial's dark lightness and a light grey in dark.
|
|
171
160
|
4. **Place surfaces and lines** (`surface.raised`, `surface.hover`, `surface.pressed`, `surface.subtle`, `border.default`, `border.hover`) as small steps down from the page in light (or `surface.raised` a step up, white on a grey page), up in dark, spaced by contrast against it; what must be seen (an input's edge, an icon, the focus ring) clears 3:1.
|
|
172
161
|
5. **Solve the text tiers from contrast targets** (tertiary at the dial's target, the rest at least the minimum step apart) on each tier's worst surface; verify per theme with `contrast.mjs --ladder`.
|
|
173
|
-
6. **Derive the accent from the brand hue**, per theme: `text.on-accent` at 4.5:1 and `focus` at 3:1 on every surface; then its hover and `surface.selected` tint (
|
|
162
|
+
6. **Derive the accent from the brand hue**, per theme: `text.on-accent` at 4.5:1 and `focus` at 3:1 on every surface; then its hover and the `surface.selected` tint (law 7; translucent, so it re-tints with the theme), re-measuring the lowest text tier on it too.
|
|
174
163
|
7. **Status pairs**, if the preset needs them; **exceptions and hue families**, each passing law 7, every mark at 3:1 on its grounds.
|
|
175
164
|
8. **The translucent set**: black at low alpha in light, white in dark, for `border.layered` and any layered washes.
|
|
176
165
|
9. **Write it** as custom properties, one block per theme, with the framework's tokens pointing at them (law 10), and record the ratios in `DESIGN.md`.
|
|
@@ -203,41 +192,16 @@ Collect evidence, then measure. The greps assume CSS, Tailwind or CSS-in-JS; ada
|
|
|
203
192
|
|
|
204
193
|
Then measure, in every theme: each text tier on its worst surface (`contrast.mjs --ladder`), the focus colour at `--min 3` on every surface it can land on, each status foreground on the page and its tint.
|
|
205
194
|
|
|
206
|
-
Report each finding as one line in `DESIGN.md` `## Open issues`, with evidence — a ratio, a count, a file: "`text.tertiary` (`--ink-3`) is 4.21:1 on `surface.hover` in light — fails 4.5:1. Used in 22 files." Don't fix what wasn't asked for.
|
|
207
|
-
|
|
208
195
|
## Writing back to DESIGN.md
|
|
209
196
|
|
|
210
197
|
- **The ratios.** Every text tier and the focus colour, per theme, with the surface measured on; re-measure when a value in `## Colour` changes.
|
|
211
198
|
- **New roles.** Any you added — a missing `surface.selected`, a tier between secondary and tertiary — in the project's naming style, marked `proposed` until accepted.
|
|
212
199
|
- **Exceptions.** Each `exception.<name>` under **Exceptions**: what it's for, and why that reason can't spread; a family with its question, its members and the marks it may take.
|
|
213
|
-
- **Moved dials.** Under `## Decisions`, dated, with the reason: "Links in `accent`, not text colour: the docs area is navigation-heavy and readers scan for links."
|
|
214
|
-
- **Open issues.** As in the audit; remove the line once fixed.
|
|
215
200
|
|
|
216
201
|
## Checklist
|
|
217
202
|
|
|
218
|
-
- [ ] Every colour is a job-named token; literals only in artwork and shared multi-layer constants.
|
|
219
|
-
- [ ] Components use roles (`accent`, its hover, `surface.selected`), not brand-hue steps.
|
|
220
|
-
- [ ] `text.primary` through `text.tertiary` are their own tokens, never border or fill steps.
|
|
221
|
-
- [ ] Every tier clears 4.5:1 on its worst surface, per theme, computed with `contrast.mjs`.
|
|
222
|
-
- [ ] The text ladder strictly descends in every theme.
|
|
223
|
-
- [ ] Icons take their label's tier or one below; `mark` is only for decoration without text.
|
|
224
203
|
- [ ] Control edges and icons that must be seen clear 3:1.
|
|
225
|
-
- [ ] Anything layered is translucent (`border.layered` and kin), inverting per theme.
|
|
226
|
-
- [ ] `focus` clears 3:1 on every surface it can land on, uses `:focus-visible` with an offset, never sits beside its own colour.
|
|
227
|
-
- [ ] No state or status is signalled by colour alone.
|
|
228
204
|
- [ ] Each `status.*` is a fg/bg pair per theme, fg at 4.5:1 on the page and its tint.
|
|
229
|
-
- [ ]
|
|
230
|
-
- [ ] Each hue family answers one question (what, how, where, who) and never another's.
|
|
231
|
-
- [ ] One hue calls for action; the reader's current location follows the dial (the accent tint under product and expressive, uncoloured under editorial); done is coloured only where the dial says completion is the news.
|
|
232
|
-
- [ ] Accents, status pairs, exceptions and family hues are re-picked per theme; every gradient stop moves; marks clear 3:1 on every ground.
|
|
233
|
-
- [ ] Dark `surface.page` and light `text.primary` are near-black, not `#000`.
|
|
234
|
-
- [ ] With a toggle, the theme is a root attribute or class set by a blocking inline script before the stylesheet.
|
|
235
|
-
- [ ] `color-scheme` is declared in every theme.
|
|
236
|
-
- [ ] Context-dependent and multi-layer values are runtime custom properties; preference overrides follow every theme block.
|
|
205
|
+
- [ ] Dark `surface.page` and light `text.primary` are near-black, not `#000`; `color-scheme` is declared in every theme.
|
|
237
206
|
- [ ] Every brand colour and gradient the original spent on a job is still there, or the law that removed it is named.
|
|
238
|
-
- [ ] Product and expressive: the current nav item
|
|
239
|
-
- [ ] Dial check: palette, accent's jobs and link colour match the preset.
|
|
240
|
-
- [ ] Dial check: exception count and done colour match the preset.
|
|
241
|
-
- [ ] Dial check: tier count, minimum step and tertiary target match the preset.
|
|
242
|
-
- [ ] Dial check: status palette present if the preset requires it.
|
|
243
|
-
- [ ] Dial check: theme control and dark page lightness match, or `## Decisions` says why not.
|
|
207
|
+
- [ ] Product and expressive: the current nav item's label clears 4.5:1 in `accent.ink` on its tint; the primary chart series is the accent at full strength.
|
|
@@ -84,7 +84,7 @@ input[aria-invalid="true"] { border-color: var(--status-danger-fg); }
|
|
|
84
84
|
.field-error svg { color: var(--status-danger-fg); } /* only the mark takes the colour */
|
|
85
85
|
```
|
|
86
86
|
|
|
87
|
-
The neutral case
|
|
87
|
+
The neutral case, a current label one grey tier darker, needs a rule or marker too (colour law 6).
|
|
88
88
|
|
|
89
89
|
A ring that fills as work moves is legible in greyscale, so its colour only confirms what the shape says; that is what lets a product colour done without colour carrying it.
|
|
90
90
|
|
|
@@ -99,7 +99,7 @@ Law 7's dial, decided by whether completion is the news:
|
|
|
99
99
|
| A long list of ticked checkboxes | A light grey box with an ink tick | An inverted ink or accent block per tick shouts down a long list; the tick's shape carries the state |
|
|
100
100
|
| A switch | Its strong fill, on | A switch has no tick, so the fill is the state |
|
|
101
101
|
|
|
102
|
-
The second row reverses the first on purpose
|
|
102
|
+
The second row reverses the first on purpose. Record which lists take which row under `## Decisions`, so the next audit doesn't "fix" one into the other. Under `expressive`, completion may take a brand colour in any row, counted among its exceptions.
|
|
103
103
|
|
|
104
104
|
Where the reader *is* in the interface (the current nav item or tab) takes the accent tint under product and expressive and stays neutral under editorial and glass (colour law 7). A place in the data with one member is different: on a calendar board, today's column may take a filled `accent` with `text.on-accent`, the one time the brand marks a place rather than an action. Record it as `exception.today`; it doesn't make a second.
|
|
105
105
|
|
|
@@ -53,13 +53,7 @@ inset highlight inset 0 1px 0 white at h (optional, per preset)
|
|
|
53
53
|
|
|
54
54
|
## Per preset
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
| Preset | Floating recipe (light) |
|
|
59
|
-
|---|---|
|
|
60
|
-
| `editorial` | `inset 0 1px 0 rgb(255 255 255 / 0.5), 0 1px 1px rgb(0 0 0 / 0.02), 0 8px 16px -4px rgb(0 0 0 / 0.04), 0 24px 32px -8px rgb(0 0 0 / 0.06)` |
|
|
61
|
-
| `product` | `0 1px 1px rgb(0 0 0 / 0.04), 0 12px 24px -8px rgb(0 0 0 / 0.10)` — edges carry the rest |
|
|
62
|
-
| `expressive` | `inset 0 1px 0 rgb(255 255 255 / 0.6), 0 1px 2px rgb(0 0 0 / 0.06), 0 10px 20px -6px rgb(0 0 0 / 0.10), 0 32px 48px -12px rgb(0 0 0 / 0.16)` |
|
|
56
|
+
Each preset's layers and peak opacity are the depth dials, built by the proportions above: editorial's floating shadow is the example, product's is the ring edge below.
|
|
63
57
|
|
|
64
58
|
Product leans on `border.default` with short shadows, so dense screens don't fill with grey, and draws that edge as a ring inside the shadow on cards, menus and dialogs alike (below). Expressive may add one decorative level — a branded pocket, a hero card — named, with its own bundle.
|
|
65
59
|
|
|
@@ -114,7 +108,7 @@ A card animating by transform, as in a reordering stack, can't change its border
|
|
|
114
108
|
}
|
|
115
109
|
```
|
|
116
110
|
|
|
117
|
-
Half a pixel is one device pixel on a 2x screen
|
|
111
|
+
Half a pixel is one device pixel on a 2x screen; a shadow's spread doesn't snap to the pixel the way a border does, so on 1x it antialiases to a faint line, which is the point — quieter than any border — and why the contrast preference gets a real one. Forced colours paints the transparent border.
|
|
118
112
|
|
|
119
113
|
## Moving between levels
|
|
120
114
|
|