@handamade/psi-tokens 0.21.0 → 0.23.0
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/README.md +1 -1
- package/dist/components/field.d.ts +14 -1
- package/dist/components/field.js +15 -1
- package/dist/components/field.vars.css +1 -0
- package/dist/components/toolbar.d.ts +10 -0
- package/dist/components/toolbar.js +11 -0
- package/dist/components/toolbar.vars.css +1 -0
- package/dist/components.css +2 -0
- package/dist/guidance.json +35 -2
- package/dist/utilities.css +14 -0
- package/llms.txt +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -29,7 +29,7 @@ Then set the theme on your root element:
|
|
|
29
29
|
|
|
30
30
|
All tokens are CSS custom properties: `--psi-bg-primary`, `--psi-fg-secondary`, `--psi-space-12`, etc.
|
|
31
31
|
|
|
32
|
-
**utilities.css is required**: it carries `.psi-container`, `.psi-text-*`, `.psi-display-*` utility classes and the `prefers-reduced-motion` zeroing for all duration tokens (D30) — required for NavBar rendering and accessibility compliance.
|
|
32
|
+
**utilities.css is required**: it carries `.psi-container`, `.psi-text-*`, `.psi-display-*`, `.psi-fg-*` (D94) utility classes and the `prefers-reduced-motion` zeroing for all duration tokens (D30) — required for NavBar rendering and accessibility compliance.
|
|
33
33
|
|
|
34
34
|
## Creating a custom theme
|
|
35
35
|
|
|
@@ -1,3 +1,16 @@
|
|
|
1
1
|
/** Field component tokens (--psi-field-*). Label/message/error colors alias
|
|
2
|
-
* gated semantic fg tokens; gap is the label→control→message grid gap (D49).
|
|
2
|
+
* gated semantic fg tokens; gap is the label→control→message grid gap (D49).
|
|
3
|
+
*
|
|
4
|
+
* label-height (D92) is the label's line box: the line height of
|
|
5
|
+
* --psi-text-14-20-medium, which the label's `font` shorthand sets. A font
|
|
6
|
+
* shorthand exposes no line-height variable to read, and --psi-space-* is
|
|
7
|
+
* scoped to gap/padding/margin, so the height is a standalone 20px literal
|
|
8
|
+
* (the --psi-toolbar-control-width precedent). The label holds it as its
|
|
9
|
+
* min-block-size, and --psi-toolbar-action-offset is derived from it, so a
|
|
10
|
+
* filter row's unlabelled button and its labelled controls read one source.
|
|
11
|
+
* Change it together with the label's font. --psi-toolbar-action-offset
|
|
12
|
+
* resolves from this token and `gap` at the theme root: overriding either on
|
|
13
|
+
* a narrower scope inside a Toolbar align="start" row also needs
|
|
14
|
+
* --psi-toolbar-action-offset set on that same scope, or the row's button
|
|
15
|
+
* keeps the theme-root offset. */
|
|
3
16
|
export declare const fieldVars: Record<string, string>;
|
package/dist/components/field.js
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
/** Field component tokens (--psi-field-*). Label/message/error colors alias
|
|
2
|
-
* gated semantic fg tokens; gap is the label→control→message grid gap (D49).
|
|
2
|
+
* gated semantic fg tokens; gap is the label→control→message grid gap (D49).
|
|
3
|
+
*
|
|
4
|
+
* label-height (D92) is the label's line box: the line height of
|
|
5
|
+
* --psi-text-14-20-medium, which the label's `font` shorthand sets. A font
|
|
6
|
+
* shorthand exposes no line-height variable to read, and --psi-space-* is
|
|
7
|
+
* scoped to gap/padding/margin, so the height is a standalone 20px literal
|
|
8
|
+
* (the --psi-toolbar-control-width precedent). The label holds it as its
|
|
9
|
+
* min-block-size, and --psi-toolbar-action-offset is derived from it, so a
|
|
10
|
+
* filter row's unlabelled button and its labelled controls read one source.
|
|
11
|
+
* Change it together with the label's font. --psi-toolbar-action-offset
|
|
12
|
+
* resolves from this token and `gap` at the theme root: overriding either on
|
|
13
|
+
* a narrower scope inside a Toolbar align="start" row also needs
|
|
14
|
+
* --psi-toolbar-action-offset set on that same scope, or the row's button
|
|
15
|
+
* keeps the theme-root offset. */
|
|
3
16
|
export const fieldVars = {
|
|
4
17
|
"label-fg": "var(--psi-fg-secondary)",
|
|
5
18
|
"message-fg": "var(--psi-fg-tertiary)",
|
|
@@ -11,4 +24,5 @@ export const fieldVars = {
|
|
|
11
24
|
// foreground the contrast matrix does not gate.
|
|
12
25
|
"required-text-fg": "var(--psi-fg-secondary)",
|
|
13
26
|
gap: "var(--psi-space-6)",
|
|
27
|
+
"label-height": "20px",
|
|
14
28
|
};
|
|
@@ -9,5 +9,15 @@
|
|
|
9
9
|
* Dialog's width={400|560|720} precedent — no shared scale reaches this
|
|
10
10
|
* range (sizeScale tops at 48, spacingScale at 144, both for heights/gaps
|
|
11
11
|
* not component widths).
|
|
12
|
+
*
|
|
13
|
+
* action-offset (D92) moves an unlabelled button or link onto the control
|
|
14
|
+
* line of a `Toolbar align="start"` row: a Field's control starts one label
|
|
15
|
+
* line and one field gap below the Field's top. It is derived from Field's
|
|
16
|
+
* own tokens rather than restated, so the two cannot drift. The calc()
|
|
17
|
+
* resolves where it is declared, `:where(:root, [data-psi-theme])`: retuning
|
|
18
|
+
* --psi-field-label-height or --psi-field-gap there moves the button too;
|
|
19
|
+
* retuning either on a narrower scope needs this token set on that scope.
|
|
20
|
+
* Bound to margin-block-start in toolbar.module.css (Toolbar CSS may read
|
|
21
|
+
* only --psi-toolbar-* and the scales, so it reads Field through this).
|
|
12
22
|
*/
|
|
13
23
|
export declare const toolbarVars: Record<string, string>;
|
|
@@ -9,7 +9,18 @@
|
|
|
9
9
|
* Dialog's width={400|560|720} precedent — no shared scale reaches this
|
|
10
10
|
* range (sizeScale tops at 48, spacingScale at 144, both for heights/gaps
|
|
11
11
|
* not component widths).
|
|
12
|
+
*
|
|
13
|
+
* action-offset (D92) moves an unlabelled button or link onto the control
|
|
14
|
+
* line of a `Toolbar align="start"` row: a Field's control starts one label
|
|
15
|
+
* line and one field gap below the Field's top. It is derived from Field's
|
|
16
|
+
* own tokens rather than restated, so the two cannot drift. The calc()
|
|
17
|
+
* resolves where it is declared, `:where(:root, [data-psi-theme])`: retuning
|
|
18
|
+
* --psi-field-label-height or --psi-field-gap there moves the button too;
|
|
19
|
+
* retuning either on a narrower scope needs this token set on that scope.
|
|
20
|
+
* Bound to margin-block-start in toolbar.module.css (Toolbar CSS may read
|
|
21
|
+
* only --psi-toolbar-* and the scales, so it reads Field through this).
|
|
12
22
|
*/
|
|
13
23
|
export const toolbarVars = {
|
|
14
24
|
"control-width": "200px",
|
|
25
|
+
"action-offset": "calc(var(--psi-field-label-height) + var(--psi-field-gap))",
|
|
15
26
|
};
|
package/dist/components.css
CHANGED
|
@@ -158,6 +158,7 @@
|
|
|
158
158
|
--psi-field-marker-fg: var(--psi-fg-danger);
|
|
159
159
|
--psi-field-required-text-fg: var(--psi-fg-secondary);
|
|
160
160
|
--psi-field-gap: var(--psi-space-6);
|
|
161
|
+
--psi-field-label-height: 20px;
|
|
161
162
|
}
|
|
162
163
|
}
|
|
163
164
|
|
|
@@ -407,6 +408,7 @@
|
|
|
407
408
|
@layer psi.components {
|
|
408
409
|
:where(:root, [data-psi-theme]) {
|
|
409
410
|
--psi-toolbar-control-width: 200px;
|
|
411
|
+
--psi-toolbar-action-offset: calc(var(--psi-field-label-height) + var(--psi-field-gap));
|
|
410
412
|
}
|
|
411
413
|
}
|
|
412
414
|
|
package/dist/guidance.json
CHANGED
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"Override component tokens (--psi-{component}-*), not semantic tokens, for one-off theming.",
|
|
55
55
|
"--psi-button-font overrides button typography across all sizes (documented D34 override; ember → mono).",
|
|
56
56
|
"Wrap labeled form controls in Field — label association, description/error line, aria-describedby and aria-invalid come wired; don't hand-roll label+message rows.",
|
|
57
|
-
"Field is for a control with a *visible* label. A toolbar filter control named by aria-label or placeholder takes neither a Field nor a hand-rolled label row — compare filter-toolbar (no labels) with table-pagination (\"Rows per page\" visible, so Field). Filters with visible labels submitted together by a Find button are filter-form: a Toolbar as=\"form\" align=\"
|
|
57
|
+
"Field is for a control with a *visible* label. A toolbar filter control named by aria-label or placeholder takes neither a Field nor a hand-rolled label row — compare filter-toolbar (no labels) with table-pagination (\"Rows per page\" visible, so Field). Filters with visible labels submitted together by a Find button are filter-form: a Toolbar as=\"form\" align=\"start\" of Fields with one-line labels, where a field's error line is the Field's own and hangs below its control (D87, D92).",
|
|
58
58
|
"Use Dialog for blocking modal flows — title/footer slots, dismissible gate; danger stays on the footer Buttons, one accent per group."
|
|
59
59
|
],
|
|
60
60
|
"states": {
|
|
@@ -190,7 +190,7 @@
|
|
|
190
190
|
"radius": "Control radius is size-invariant (D56) — it does NOT ride the size ramp, because border-radius sits on each component's base rule and all four sizes resolve to radius-8. One dial, --psi-control-radius, is the layer to override, aliased per component: --psi-button-radius (Button, and IconButton via the same token), --psi-input-radius, --psi-select-radius, --psi-checkbox-box-radius, and --psi-tooltip-radius. Checkbox and Tooltip cap themselves with min(var(--psi-control-radius), var(--psi-radius-4)) and min(…, var(--psi-radius-6)) respectively so neither over-rounds a small object — but the published radius scale's floor is radius-4, so Checkbox's cap is a no-op for every on-scale value a theme can set; it only bites at an off-scale value like 0px. Tooltip's higher ceiling does track a sharper theme, down to radius-4. Tag and Switch stay --psi-radius-full: pill-ness is component identity, not theme expression. A theme retunes every control's shape by setting --psi-control-radius once. Psi now has two radius dials, not one: --psi-control-radius (here, for interactive controls) and --psi-surface-radius (for elevated surfaces — Dialog, Panel, Menu). --psi-card-radius sits on neither dial, so a theme that retunes controls or surfaces leaves Card at its own default of radius-8."
|
|
191
191
|
},
|
|
192
192
|
"utilities": {
|
|
193
|
-
"note": "Every utility class shipped in utilities.css, generated from the same source as the CSS (D79). Scaled utility families set one property each and create no layout context: .psi-gap-* sets gap only, so pair it with display:flex or grid yourself. The bare/fixed utilities (.psi-container, .psi-media-tint, .psi-sr-only) set several. Import @handamade/psi-tokens/utilities.css — it is required, not optional; .psi-container and the reduced-motion duration zeroing live there.",
|
|
193
|
+
"note": "Every utility class shipped in utilities.css, generated from the same source as the CSS (D79). Scaled utility families set one property each and create no layout context: .psi-gap-* sets gap only, so pair it with display:flex or grid yourself. The bare/fixed utilities (.psi-container, .psi-media-tint, .psi-sr-only) set several. Every utility sits in @layer psi.utilities, above @layer psi.components, so on a component root it beats the component's own value (D94). Import @handamade/psi-tokens/utilities.css — it is required, not optional; .psi-container and the reduced-motion duration zeroing live there.",
|
|
194
194
|
"families": [
|
|
195
195
|
{
|
|
196
196
|
"prefix": "psi-gap-*",
|
|
@@ -486,6 +486,26 @@
|
|
|
486
486
|
"psi-my-144"
|
|
487
487
|
]
|
|
488
488
|
},
|
|
489
|
+
{
|
|
490
|
+
"prefix": "psi-fg-*",
|
|
491
|
+
"property": "color",
|
|
492
|
+
"scale": null,
|
|
493
|
+
"classes": [
|
|
494
|
+
"psi-fg-primary",
|
|
495
|
+
"psi-fg-secondary",
|
|
496
|
+
"psi-fg-tertiary",
|
|
497
|
+
"psi-fg-quaternary",
|
|
498
|
+
"psi-fg-primary-inverted",
|
|
499
|
+
"psi-fg-static-white",
|
|
500
|
+
"psi-fg-static-black",
|
|
501
|
+
"psi-fg-on-accent",
|
|
502
|
+
"psi-fg-accent",
|
|
503
|
+
"psi-fg-success",
|
|
504
|
+
"psi-fg-warning",
|
|
505
|
+
"psi-fg-danger",
|
|
506
|
+
"psi-fg-on-inverted"
|
|
507
|
+
]
|
|
508
|
+
},
|
|
489
509
|
{
|
|
490
510
|
"prefix": "psi-text-*",
|
|
491
511
|
"property": "font",
|
|
@@ -562,6 +582,19 @@
|
|
|
562
582
|
"psi-display-32-32-extrabold",
|
|
563
583
|
"psi-display-36-64-black",
|
|
564
584
|
"psi-display-56-128-black",
|
|
585
|
+
"psi-fg-accent",
|
|
586
|
+
"psi-fg-danger",
|
|
587
|
+
"psi-fg-on-accent",
|
|
588
|
+
"psi-fg-on-inverted",
|
|
589
|
+
"psi-fg-primary",
|
|
590
|
+
"psi-fg-primary-inverted",
|
|
591
|
+
"psi-fg-quaternary",
|
|
592
|
+
"psi-fg-secondary",
|
|
593
|
+
"psi-fg-static-black",
|
|
594
|
+
"psi-fg-static-white",
|
|
595
|
+
"psi-fg-success",
|
|
596
|
+
"psi-fg-tertiary",
|
|
597
|
+
"psi-fg-warning",
|
|
565
598
|
"psi-gap-0",
|
|
566
599
|
"psi-gap-12",
|
|
567
600
|
"psi-gap-120",
|
package/dist/utilities.css
CHANGED
|
@@ -121,6 +121,20 @@
|
|
|
121
121
|
.psi-mx-144 { margin-inline: var(--psi-space-144); }
|
|
122
122
|
.psi-my-144 { margin-block: var(--psi-space-144); }
|
|
123
123
|
|
|
124
|
+
.psi-fg-primary { color: var(--psi-fg-primary); }
|
|
125
|
+
.psi-fg-secondary { color: var(--psi-fg-secondary); }
|
|
126
|
+
.psi-fg-tertiary { color: var(--psi-fg-tertiary); }
|
|
127
|
+
.psi-fg-quaternary { color: var(--psi-fg-quaternary); }
|
|
128
|
+
.psi-fg-primary-inverted { color: var(--psi-fg-primary-inverted); }
|
|
129
|
+
.psi-fg-static-white { color: var(--psi-fg-static-white); }
|
|
130
|
+
.psi-fg-static-black { color: var(--psi-fg-static-black); }
|
|
131
|
+
.psi-fg-on-accent { color: var(--psi-fg-on-accent); }
|
|
132
|
+
.psi-fg-accent { color: var(--psi-fg-accent); }
|
|
133
|
+
.psi-fg-success { color: var(--psi-fg-success); }
|
|
134
|
+
.psi-fg-warning { color: var(--psi-fg-warning); }
|
|
135
|
+
.psi-fg-danger { color: var(--psi-fg-danger); }
|
|
136
|
+
.psi-fg-on-inverted { color: var(--psi-fg-on-inverted); }
|
|
137
|
+
|
|
124
138
|
.psi-text-12-16-regular { font: var(--psi-text-12-16-regular); }
|
|
125
139
|
.psi-text-12-16-medium { font: var(--psi-text-12-16-medium); }
|
|
126
140
|
.psi-text-14-20-regular { font: var(--psi-text-14-20-regular); }
|
package/llms.txt
CHANGED
|
@@ -14,6 +14,7 @@ OKLCH formula-based design tokens. Code is the source of truth; all artifacts ge
|
|
|
14
14
|
- Override --psi-{component}-* tokens for one-off component theming; never fork semantic tokens.
|
|
15
15
|
- Scales are pixel-true: --psi-space-8 is 8px (0.5rem). Typography: --psi-text-{size}-{lh}-{weight}.
|
|
16
16
|
- Themes: import base.css + {theme}.css + components.css + utilities.css; set data-psi-theme="{name}" ({light,dark,acme,ember}).
|
|
17
|
+
- Foreground utilities (D94): `.psi-fg-<role>` sets `color: var(--psi-fg-<role>)` for every fg token (`psi-fg-primary`, `psi-fg-secondary`, `psi-fg-tertiary`, `psi-fg-on-accent`, `psi-fg-danger`, …). Like every utility it sits in `@layer psi.utilities` and beats a component's own colour.
|
|
17
18
|
- Scoped tokens bind only their declared property groups (D46): fg* → text properties, bg*/fill*/scrim* → surfaces, border* → borders, --psi-space-* → gaps/padding/margin. Cross-family bindings that are design decisions carry both scopes (fgDanger: text+border for error borders; fillAccent: surface+border for selected controls). The token build enforces component bindings; the repo's stylelint rule (psi/token-scopes) enforces first-party consumer CSS (component modules + apps/). External consumers aren't gated automatically — dist/scope-map.json carries the machine-readable scopes to lint against.
|
|
18
19
|
|
|
19
20
|
## Motion (--psi-duration-*, --psi-ease-*)
|