@rackbops/styles 0.1.1 → 0.1.3

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.
@@ -63,7 +63,12 @@ tokens: `.rb-btn` (+ `--primary/--accent/--danger/--ghost`, `--sm` compact size,
63
63
  `.rb-dialog`, `.rb-tabs` / `.rb-tab` / `.rb-tabpanel`, `.rb-table` (+
64
64
  `.rb-num`, `--interactive`), `.rb-progress` / `.rb-spinner` (keyframe `rb-ember-spin`), `.rb-stepper`,
65
65
  `.rb-muted`, `.rb-pre` (+ `.rb-log`). See amber-hearth's `design.md` for the per-component
66
- notes — the behaviour is the same; only the palette is dark.
66
+ notes — the behaviour is the same, with one exception:
67
+ `components/dialog.css`'s `::backdrop` scrim is its own literal
68
+ `rgba(8, 5, 3, 0.6)` here (a heavier, near-black wash for this espresso
69
+ ground) rather than amber-hearth's lighter `rgba(43, 31, 20, 0.4)` — both are
70
+ permitted literal alpha overlays per STANDARD.md 4.2. Otherwise only the
71
+ palette is dark.
67
72
 
68
73
  ## Light counterpart
69
74
 
@@ -106,6 +106,11 @@ Class prefix `rb-`; shared token/class contract with the other themes. No
106
106
  colour.
107
107
  - **Dialog** `.rb-dialog` — native `<dialog>`, generously rounded panel,
108
108
  blurred backdrop (`backdrop-filter: blur(var(--rb-blur))`), serif bold title.
109
+ Pair divergence: `components/dialog.css`'s `::backdrop` scrim is a literal
110
+ `rgba(43, 31, 20, 0.4)` here (a warm, lighter-alpha wash suited to this
111
+ theme's cream ground); amber-ember's is a heavier `rgba(8, 5, 3, 0.6)`
112
+ near-black scrim for its espresso ground — both are permitted literal alpha
113
+ overlays per STANDARD.md 4.2, just tuned per theme rather than shared.
109
114
  - **Tabs** `.rb-tabs` / `.rb-tab` / `.rb-tabpanel` — normal-case text tabs; the
110
115
  active tab is accent-coloured, underlined with the theme's one rationed
111
116
  gradient — the only place `--rb-accent-grad` appears in this theme.
@@ -85,7 +85,11 @@ Class prefix `rb-`; shared token/class contract with the other themes.
85
85
  `--ghost` chromeless. `--sm` is a compact size — tighter padding, type down
86
86
  ~1px — for inline and table-row actions. `.rb-icon-btn` is a square icon-only
87
87
  hit target (>=2rem) — pair it with `.rb-btn` (+ an optional color variant)
88
- for close/edit/delete glyph buttons.
88
+ for close/edit/delete glyph buttons. Pair divergence: `--primary`'s
89
+ hover/focus-visible fill lightens toward white
90
+ (`color-mix(in srgb, var(--rb-accent) 88%, #fff)`); the light "Parchment"
91
+ counterpart's `components/button.css` darkens toward `--rb-text` instead —
92
+ lightening on a light ground would drop the label below AA (STANDARD.md 5.2).
89
93
  - **Card** `.rb-card` (+ `--raised`) — solid panel, hairline border, shadow-sm.
90
94
  - **NavLink** `.rb-link` — tree/sidebar row; hover fills to surface-2 and lights a
91
95
  2px accent bar on the left edge; `--active` carries the accent wash.
@@ -72,7 +72,12 @@ tokens: `.rb-wordmark` (+ `__spark`), `.rb-btn` (+ `--primary/--accent/--danger/
72
72
  `.rb-dialog`, `.rb-tabs`, `.rb-tabstrip`, `.rb-table` (+ `.rb-num`, `--interactive`),
73
73
  `.rb-progress` / `.rb-spinner`, `.rb-stepper`, `.rb-eyebrow`, `.rb-muted`, `.rb-pre` (+ `.rb-log`). See
74
74
  arcane-obsidian's `design.md` for the per-component notes — the behaviour is
75
- the same; only the palette is light.
75
+ the same, with one exception: `components/button.css`'s `--primary`
76
+ hover/focus-visible fill darkens toward `--rb-text` here
77
+ (`color-mix(in srgb, var(--rb-accent), var(--rb-text) 12%)`), rather than
78
+ lightening toward white as arcane-obsidian does — lightening on this light
79
+ ground would drop the label below AA (4.37:1; see the inline comment in that
80
+ file). Otherwise only the palette is light.
76
81
 
77
82
  ## Code syntax
78
83
 
@@ -82,8 +82,10 @@ gradient has no soft midpoint.
82
82
 
83
83
  ## Components
84
84
 
85
- Class prefix `rb-`; shared token/class contract with the other themes. Only
86
- the baseline ten no theme-specific extras.
85
+ Class prefix `rb-`; shared token/class contract with the other themes (the
86
+ full required set is `styles/contract.json` / STANDARD.md 5.1 — not restated
87
+ as a count here, since that count has drifted before) — no theme-specific
88
+ extras.
87
89
 
88
90
  - **Button** `.rb-btn` — outlined ghost by default; `--primary` is a solid
89
91
  accent fill with the offset-shadow press interaction; `--accent` an
@@ -103,7 +105,11 @@ the baseline ten — no theme-specific extras.
103
105
  `.rb-field` / `.rb-choice` / `.rb-checkbox` / `.rb-radio` / `.rb-switch` —
104
106
  sunken square fields; focus swaps the border from 1px to 2px solid accent;
105
107
  checked checkbox/radio/switch states are a flat accent fill, not a tint
106
- ring; labels are uppercase, tracked, weight 600.
108
+ ring; labels are uppercase, tracked, weight 600. Pair divergence:
109
+ `components/form.css`'s select-arrow SVG data-URI hardcodes its stroke
110
+ colour (`#8f8f8a`, matching this theme's `--rb-text-faint`) since an inline
111
+ SVG can't reference a CSS custom property; concrete-signal-light's icon
112
+ carries its own matching literal (`#5f5d58`) instead.
107
113
  - **Badge** `.rb-badge` — uppercase weight 700, near-square (`radius-pill`);
108
114
  a 16%-tint fill of its hue behind full-strength text, plus a 1px solid
109
115
  border of that same hue — badges carry a border here, unlike softer themes.
@@ -162,7 +168,9 @@ a poured-concrete off-white ground.
162
168
  | Gradient | `linear-gradient(90deg,#cc4611,#b8281f)` |
163
169
 
164
170
  See `concrete-signal-light`'s own `design.md` for its full color table and
165
- notes — the behaviour is identical; only the palette is light.
171
+ notes — the behaviour is identical (the one exception is `components/form.css`'s
172
+ select-arrow icon colour, its own literal per theme since SVG can't take a
173
+ token); only the palette is otherwise light.
166
174
 
167
175
  ## Scoping
168
176
 
@@ -60,14 +60,20 @@ transitions.
60
60
  ## Components
61
61
 
62
62
  Identical to concrete-signal — see its `design.md` for per-component notes;
63
- behaviour is the same, only the palette differs. The full baseline twelve:
63
+ behaviour is the same, only the palette differs (the full required set is
64
+ `styles/contract.json` / STANDARD.md 5.1 — not restated as a count here,
65
+ since that count has drifted before):
64
66
  `.rb-btn` (+ `--primary/--accent/--danger/--ghost`, `--sm` compact size, `.rb-icon-btn` icon-only square), `.rb-card` (+
65
67
  `--raised`), `.rb-link`, `.rb-nav-rail`, the form set (`.rb-input`/`.rb-textarea`/
66
68
  `.rb-select`/`.rb-label`/`.rb-field`/`.rb-choice`/`.rb-checkbox`/`.rb-radio`/
67
69
  `.rb-switch`), `.rb-badge`, `.rb-alert`, `.rb-dialog`, `.rb-tabs`/`.rb-tab`/
68
70
  `.rb-tabpanel`, `.rb-table` (+ `.rb-num`, `--interactive`), `.rb-progress`/`.rb-spinner`, `.rb-stepper`,
69
71
  `.rb-muted` (no italic — brutalism never softens with a slant), `.rb-pre` (+ `.rb-log`)
70
- (zero radius falls out of `--rb-radius` automatically).
72
+ (zero radius falls out of `--rb-radius` automatically). One exception:
73
+ `components/form.css`'s select-arrow SVG data-URI hardcodes its own stroke
74
+ colour (`#5f5d58`, matching this theme's `--rb-text-faint`) since an inline
75
+ SVG can't reference a CSS custom property; concrete-signal's icon carries its
76
+ own matching literal (`#8f8f8a`) instead.
71
77
 
72
78
  ## Dark counterpart
73
79
 
package/contract.json ADDED
@@ -0,0 +1,192 @@
1
+ {
2
+ "contract": 1,
3
+ "tokens": [
4
+ "bg", "surface", "surface-2", "surface-sunken",
5
+ "text", "text-soft", "text-faint",
6
+ "border", "border-strong",
7
+ "accent", "accent-fg", "accent-wash", "accent-grad",
8
+ "info", "success", "warning", "danger",
9
+ "font-display", "font-body", "font-mono",
10
+ "font-weight", "font-weight-medium", "font-weight-bold",
11
+ "heading-tracking", "label-tracking", "text-sm",
12
+ "radius", "radius-lg", "radius-pill",
13
+ "shadow-sm", "shadow-lg", "blur", "transition",
14
+ "space-1", "space-2", "space-3", "space-4", "space-5"
15
+ ],
16
+ "components": {
17
+ "button": {
18
+ "react": "Button",
19
+ "classes": ["rb-btn", "rb-btn--primary", "rb-btn--accent", "rb-btn--danger", "rb-btn--ghost", "rb-btn--sm", "rb-icon-btn"],
20
+ "notes": "variants: primary, accent, danger, ghost; --sm compact size; .rb-icon-btn icon-only square"
21
+ },
22
+ "card": {
23
+ "react": "Card",
24
+ "classes": ["rb-card", "rb-card--raised"],
25
+ "notes": "raised prop; --raised is a documented no-op in the three nazuraki ports (section 5.3)"
26
+ },
27
+ "link": {
28
+ "react": "NavLink",
29
+ "classes": ["rb-link", "rb-link--active"],
30
+ "notes": "active prop; SHOULD also match [aria-current=\"page\"] (pending CSS sweep, #65)"
31
+ },
32
+ "nav-rail": {
33
+ "react": "NavRail",
34
+ "classes": ["rb-nav-rail"],
35
+ "notes": "composes .rb-link; owns no active convention"
36
+ },
37
+ "form": {
38
+ "react": ["Field", "Label", "Input", "Textarea", "Select", "Checkbox", "Radio", "Switch"],
39
+ "classes": ["rb-field", "rb-label", "rb-input", "rb-textarea", "rb-select", "rb-choice", "rb-checkbox", "rb-radio", "rb-switch"],
40
+ "notes": "pair with Field/Label; choice controls wrap in .rb-choice"
41
+ },
42
+ "badge": {
43
+ "react": "Badge",
44
+ "classes": ["rb-badge", "rb-badge--info", "rb-badge--success", "rb-badge--warning", "rb-badge--danger"],
45
+ "notes": "semantic variants"
46
+ },
47
+ "alert": {
48
+ "react": "Alert",
49
+ "classes": ["rb-alert", "rb-alert__title", "rb-alert--info", "rb-alert--success", "rb-alert--warning", "rb-alert--danger"],
50
+ "notes": "variant + optional title"
51
+ },
52
+ "dialog": {
53
+ "react": "Dialog",
54
+ "classes": ["rb-dialog", "rb-dialog__title", "rb-dialog__actions", "rb-dialog__body"],
55
+ "notes": "native <dialog>, open/onClose/actions; __body is a documented no-op in four themes (section 5.3)"
56
+ },
57
+ "tabs": {
58
+ "react": "Tabs",
59
+ "classes": ["rb-tabs", "rb-tab", "rb-tab--active", "rb-tabpanel"],
60
+ "notes": "items: {id, label, content}[]; --active SHOULD also match [aria-selected=\"true\"] (pending CSS sweep, #65)"
61
+ },
62
+ "table": {
63
+ "react": null,
64
+ "classes": ["rb-table", "rb-table--interactive", "rb-num"],
65
+ "notes": "style directly, no React wrapper"
66
+ },
67
+ "progress": {
68
+ "react": ["Progress", "Spinner"],
69
+ "classes": ["rb-progress", "rb-spinner"],
70
+ "notes": "native <progress> pseudo-element contract enforced separately (not class-based)"
71
+ },
72
+ "muted": {
73
+ "react": null,
74
+ "classes": ["rb-muted"],
75
+ "notes": "style directly, no React wrapper"
76
+ },
77
+ "pre": {
78
+ "react": null,
79
+ "classes": ["rb-pre"],
80
+ "notes": "style directly, no React wrapper"
81
+ },
82
+ "log": {
83
+ "react": null,
84
+ "classes": ["rb-log"],
85
+ "notes": "pairs with .rb-pre; style directly, no React wrapper"
86
+ },
87
+ "stepper": {
88
+ "react": "Stepper",
89
+ "classes": ["rb-stepper", "rb-stepper__step", "rb-stepper__node", "rb-stepper__label", "rb-stepper--complete", "rb-stepper--current", "rb-stepper--upcoming"],
90
+ "notes": "--upcoming is the resting state (allowlisted, no rule needed); --current SHOULD also match [aria-current=\"step\"] (pending CSS sweep, #65)"
91
+ }
92
+ },
93
+ "allowlist": [
94
+ { "theme": "luminous-precision", "class": "rb-card--raised", "reason": "nazuraki upstream has no second elevation tier for this port" },
95
+ { "theme": "neon-butterfly", "class": "rb-card--raised", "reason": "nazuraki upstream has no second elevation tier for this port" },
96
+ { "theme": "summer-cloud", "class": "rb-card--raised", "reason": "nazuraki upstream's second elevation tier is --floating, already carried over; --raised would just duplicate it" },
97
+ { "theme": "luminous-precision", "class": "rb-dialog__body", "reason": "pads .rb-dialog directly instead of a wrapper -- sanctioned by #42's verdict" },
98
+ { "theme": "mono-field", "class": "rb-dialog__body", "reason": "pads .rb-dialog directly instead of a wrapper -- sanctioned by #42's verdict" },
99
+ { "theme": "neon-butterfly", "class": "rb-dialog__body", "reason": "pads .rb-dialog directly instead of a wrapper -- sanctioned by #42's verdict" },
100
+ { "theme": "summer-cloud", "class": "rb-dialog__body", "reason": "pads .rb-dialog directly instead of a wrapper -- sanctioned by #42's verdict" },
101
+ { "theme": "amber-ember", "class": "rb-stepper--upcoming", "reason": "default/resting state -- falls through to the base node/label rule, no override needed" },
102
+ { "theme": "amber-hearth", "class": "rb-stepper--upcoming", "reason": "default/resting state -- falls through to the base node/label rule, no override needed" },
103
+ { "theme": "arcane-obsidian", "class": "rb-stepper--upcoming", "reason": "default/resting state -- falls through to the base node/label rule, no override needed" },
104
+ { "theme": "arcane-parchment", "class": "rb-stepper--upcoming", "reason": "default/resting state -- falls through to the base node/label rule, no override needed" },
105
+ { "theme": "concrete-signal", "class": "rb-stepper--upcoming", "reason": "default/resting state -- falls through to the base node/label rule, no override needed" },
106
+ { "theme": "concrete-signal-light", "class": "rb-stepper--upcoming", "reason": "default/resting state -- falls through to the base node/label rule, no override needed" },
107
+ { "theme": "luminous-precision", "class": "rb-stepper--upcoming", "reason": "default/resting state -- falls through to the base node/label rule, no override needed" },
108
+ { "theme": "mono-field", "class": "rb-stepper--upcoming", "reason": "default/resting state -- falls through to the base node/label rule, no override needed" },
109
+ { "theme": "neon-butterfly", "class": "rb-stepper--upcoming", "reason": "default/resting state -- falls through to the base node/label rule, no override needed" },
110
+ { "theme": "rackbops-noir", "class": "rb-stepper--upcoming", "reason": "default/resting state -- falls through to the base node/label rule, no override needed" },
111
+ { "theme": "rackbops-studio", "class": "rb-stepper--upcoming", "reason": "default/resting state -- falls through to the base node/label rule, no override needed" },
112
+ { "theme": "summer-cloud", "class": "rb-stepper--upcoming", "reason": "default/resting state -- falls through to the base node/label rule, no override needed" }
113
+ ],
114
+ "extras": {
115
+ "arcane-obsidian": ["rb-wordmark", "rb-wordmark__spark", "rb-tabstrip", "rb-tabstrip__tab", "rb-tabstrip__tab--active", "rb-eyebrow"],
116
+ "arcane-parchment": ["rb-wordmark", "rb-wordmark__spark", "rb-tabstrip", "rb-tabstrip__tab", "rb-tabstrip__tab--active", "rb-eyebrow"],
117
+ "rackbops-studio": ["rb-rack", "rb-rack__top", "rb-rack__live", "rb-rack__bars", "rb-rack__foot", "rb-principles", "rb-principle", "rb-principle__n", "rb-principle__body", "rb-tags", "rb-tag", "rb-card__tag", "rb-btn__arrow", "rb-eyebrow"],
118
+ "rackbops-noir": ["rb-rack", "rb-rack__top", "rb-rack__live", "rb-rack__bars", "rb-rack__foot", "rb-principles", "rb-principle", "rb-principle__n", "rb-principle__body", "rb-tags", "rb-tag", "rb-card__tag", "rb-btn__arrow", "rb-eyebrow"],
119
+ "luminous-precision": ["rb-badge--primary", "rb-bg", "rb-progress--accent"],
120
+ "neon-butterfly": ["rb-badge--primary", "rb-bg", "rb-progress--accent"],
121
+ "summer-cloud": ["rb-badge--primary", "rb-bg", "rb-progress--accent", "rb-chip", "rb-chip--selected", "rb-card--floating"],
122
+ "concrete-signal": [],
123
+ "concrete-signal-light": [],
124
+ "amber-hearth": [],
125
+ "amber-ember": [],
126
+ "mono-field": []
127
+ },
128
+ "ariaPairs": [
129
+ {
130
+ "component": "tabs",
131
+ "class": "rb-tab--active",
132
+ "attribute": "[aria-selected=\"true\"]",
133
+ "baseSelector": "rb-tab",
134
+ "exempt": [
135
+ { "theme": "arcane-obsidian", "reason": "pending ARIA-pairing CSS sweep, tracked in #65" },
136
+ { "theme": "arcane-parchment", "reason": "pending ARIA-pairing CSS sweep, tracked in #65" },
137
+ { "theme": "rackbops-studio", "reason": "pending ARIA-pairing CSS sweep, tracked in #65" },
138
+ { "theme": "rackbops-noir", "reason": "pending ARIA-pairing CSS sweep, tracked in #65" },
139
+ { "theme": "amber-hearth", "reason": "pending ARIA-pairing CSS sweep, tracked in #65" },
140
+ { "theme": "amber-ember", "reason": "pending ARIA-pairing CSS sweep, tracked in #65" }
141
+ ]
142
+ },
143
+ {
144
+ "component": "link",
145
+ "class": "rb-link--active",
146
+ "attribute": "[aria-current=\"page\"]",
147
+ "baseSelector": "rb-link",
148
+ "exempt": [
149
+ { "theme": "amber-ember", "reason": "pending ARIA-pairing CSS sweep, tracked in #65" },
150
+ { "theme": "amber-hearth", "reason": "pending ARIA-pairing CSS sweep, tracked in #65" },
151
+ { "theme": "arcane-obsidian", "reason": "pending ARIA-pairing CSS sweep, tracked in #65" },
152
+ { "theme": "arcane-parchment", "reason": "pending ARIA-pairing CSS sweep, tracked in #65" },
153
+ { "theme": "concrete-signal", "reason": "pending ARIA-pairing CSS sweep, tracked in #65" },
154
+ { "theme": "concrete-signal-light", "reason": "pending ARIA-pairing CSS sweep, tracked in #65" },
155
+ { "theme": "luminous-precision", "reason": "pending ARIA-pairing CSS sweep, tracked in #65" },
156
+ { "theme": "mono-field", "reason": "pending ARIA-pairing CSS sweep, tracked in #65" },
157
+ { "theme": "neon-butterfly", "reason": "pending ARIA-pairing CSS sweep, tracked in #65" },
158
+ { "theme": "rackbops-noir", "reason": "pending ARIA-pairing CSS sweep, tracked in #65" },
159
+ { "theme": "rackbops-studio", "reason": "pending ARIA-pairing CSS sweep, tracked in #65" }
160
+ ]
161
+ },
162
+ {
163
+ "component": "stepper",
164
+ "class": "rb-stepper--current",
165
+ "attribute": "[aria-current=\"step\"]",
166
+ "baseSelector": "rb-stepper__step",
167
+ "exempt": [
168
+ { "theme": "amber-ember", "reason": "pending ARIA-pairing CSS sweep, tracked in #65" },
169
+ { "theme": "amber-hearth", "reason": "pending ARIA-pairing CSS sweep, tracked in #65" },
170
+ { "theme": "arcane-obsidian", "reason": "pending ARIA-pairing CSS sweep, tracked in #65" },
171
+ { "theme": "arcane-parchment", "reason": "pending ARIA-pairing CSS sweep, tracked in #65" },
172
+ { "theme": "concrete-signal", "reason": "pending ARIA-pairing CSS sweep, tracked in #65" },
173
+ { "theme": "concrete-signal-light", "reason": "pending ARIA-pairing CSS sweep, tracked in #65" },
174
+ { "theme": "luminous-precision", "reason": "pending ARIA-pairing CSS sweep, tracked in #65" },
175
+ { "theme": "mono-field", "reason": "pending ARIA-pairing CSS sweep, tracked in #65" },
176
+ { "theme": "neon-butterfly", "reason": "pending ARIA-pairing CSS sweep, tracked in #65" },
177
+ { "theme": "rackbops-noir", "reason": "pending ARIA-pairing CSS sweep, tracked in #65" },
178
+ { "theme": "rackbops-studio", "reason": "pending ARIA-pairing CSS sweep, tracked in #65" },
179
+ { "theme": "summer-cloud", "reason": "pending ARIA-pairing CSS sweep, tracked in #65" }
180
+ ]
181
+ }
182
+ ],
183
+ "dialogBackdropBlur": {
184
+ "token": "--rb-blur",
185
+ "exempt": [
186
+ { "theme": "concrete-signal", "reason": "flat scrim by design, no blur/glass anywhere in this theme", "permanent": true },
187
+ { "theme": "concrete-signal-light", "reason": "flat scrim by design, no blur/glass anywhere in this theme", "permanent": true },
188
+ { "theme": "neon-butterfly", "reason": "hard-codes blur(4px), pending CSS fix, tracked in #65" },
189
+ { "theme": "summer-cloud", "reason": "hard-codes blur(4px), pending CSS fix, tracked in #65" }
190
+ ]
191
+ }
192
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rackbops/styles",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Rackbops design-system styles: tokens, base styles, and component classes per theme.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -32,11 +32,13 @@
32
32
  "mono-field",
33
33
  "all.css",
34
34
  "manifest.json",
35
+ "contract.json",
35
36
  "css-side-effect.d.ts"
36
37
  ],
37
38
  "exports": {
38
39
  "./all": { "types": "./css-side-effect.d.ts", "default": "./all.css" },
39
40
  "./manifest": "./manifest.json",
41
+ "./contract": "./contract.json",
40
42
  "./arcane-obsidian": { "types": "./css-side-effect.d.ts", "default": "./arcane-obsidian/index.css" },
41
43
  "./arcane-obsidian/tokens": { "types": "./css-side-effect.d.ts", "default": "./arcane-obsidian/tokens.css" },
42
44
  "./arcane-obsidian/base": { "types": "./css-side-effect.d.ts", "default": "./arcane-obsidian/base.css" },
@@ -76,6 +76,16 @@ matches rackbops.com's dark-mode rack — a near-canvas block set off by its bor
76
76
  the per-component notes. The Boppy mascot asset ships with rackbops-studio
77
77
  (`styles/rackbops-studio/assets/boppy.svg`) and is shared.
78
78
 
79
+ Two components diverge beyond palette. `components/button.css`'s `--primary`
80
+ label colour (`var(--rb-bg)`) and `--accent` hover/focus fill
81
+ (`color-mix(in srgb, var(--rb-accent), var(--rb-text) 15%)`) are proper token
82
+ expressions here; rackbops-studio's equivalents are pre-existing literal
83
+ colours (`#f4f7f9`, `#c93c1c`) that predate STANDARD.md 4.2 and haven't been
84
+ migrated yet (see its `design.md`). `components/rack.css` declares its own
85
+ local `--rb-rack-panel`/`--rb-rack-line` values, tuned slightly darker here
86
+ than rackbops-studio's to read correctly against this theme's already-dark
87
+ canvas.
88
+
79
89
  ## Light counterpart
80
90
 
81
91
  `rackbops-studio` — the canonical light look. Same `--rb-*` baseline and `rb-*`
@@ -107,7 +107,12 @@ Class prefix `rb-`; shared token/class contract with the other themes.
107
107
  vermillion, `--ghost` bordered outline, `--danger` rose outline; `.rb-btn__arrow`
108
108
  slides on hover. `--sm` is a compact size for inline/table-row actions — the
109
109
  arrow scales with it. `.rb-icon-btn` is a square icon-only hit target
110
- (>=2rem), paired with `.rb-btn`.
110
+ (>=2rem), paired with `.rb-btn`. Pair divergence: `components/button.css`
111
+ carries two literal (non-token) colours here that predate the "tokens, not
112
+ literals" rule (STANDARD.md 4.2) and haven't been migrated yet — `--primary`'s
113
+ label colour (`#f4f7f9`) and `--accent`'s hover/focus fill (`#c93c1c`).
114
+ rackbops-noir's equivalents already use `var(--rb-bg)` and a `color-mix()`
115
+ formula respectively; bringing this theme in line is tracked separately.
111
116
  - **Card** `.rb-card` (+ `__tag`) — white panel, soft shadow, `-4px` hover lift.
112
117
  `--raised` makes that lift the resting state (a further `-6px` on hover).
113
118
  - **NavLink** `.rb-link` — mono header nav; hover fills to a surface pill; accent
@@ -152,7 +157,11 @@ Class prefix `rb-`; shared token/class contract with the other themes.
152
157
 
153
158
  - **Rack** `.rb-rack` (+ `__top` / `__live` / `__bars` / `__foot`) — the dark navy
154
159
  equaliser panel; a pulsing live dot and animated vermillion bars. Its inverted
155
- panel palette is fixed dark regardless of the light canvas.
160
+ panel palette is fixed dark regardless of the light canvas. Pair divergence:
161
+ `components/rack.css` declares its own local `--rb-rack-panel`/`--rb-rack-line`
162
+ custom properties with theme-specific literal values (a mini palette scoped to
163
+ this extra, the same way tokens.css carries the baseline palette) — rackbops-noir
164
+ tunes both slightly darker to sit correctly against its already-dark canvas.
156
165
  - **Principles** `.rb-principles` / `.rb-principle` (+ `__n` / `__body`) — the
157
166
  numbered `/0N` "how we work" list.
158
167
  - **Tags** `.rb-tags` / `.rb-tag` — mono capability pills in a flex-wrap row.