@handamade/psi-tokens 0.23.0 → 0.25.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.
@@ -73,4 +73,16 @@ export const buttonVars = {
73
73
  "32-font": "var(--psi-control-32-font)",
74
74
  "40-font": "var(--psi-control-40-font)",
75
75
  "48-font": "var(--psi-control-48-font)",
76
+ // ── Icon size (D97) — a direct svg child, half the height: 12/16/20/24.
77
+ // Derived, not restated, so a height retuned at the root moves the icon.
78
+ // The calc() resolves where it is declared: a retune on a narrower scope
79
+ // must set the icon size there too. IconButton does not bind it.
80
+ "24-icon-size": "calc(var(--psi-button-24-height) / 2)",
81
+ "32-icon-size": "calc(var(--psi-button-32-height) / 2)",
82
+ "40-icon-size": "calc(var(--psi-button-40-height) / 2)",
83
+ "48-icon-size": "calc(var(--psi-button-48-height) / 2)",
84
+ // ── Label cap (D98) — off by default. A consumer sets it on its own class;
85
+ // it caps a label wrapped in a direct <span> and ends it in an ellipsis.
86
+ // Size-invariant: the surrounding layout sets the cap, not the button.
87
+ "label-max-width": "none",
76
88
  };
@@ -56,5 +56,10 @@
56
56
  --psi-button-32-font: var(--psi-control-32-font);
57
57
  --psi-button-40-font: var(--psi-control-40-font);
58
58
  --psi-button-48-font: var(--psi-control-48-font);
59
+ --psi-button-24-icon-size: calc(var(--psi-button-24-height) / 2);
60
+ --psi-button-32-icon-size: calc(var(--psi-button-32-height) / 2);
61
+ --psi-button-40-icon-size: calc(var(--psi-button-40-height) / 2);
62
+ --psi-button-48-icon-size: calc(var(--psi-button-48-height) / 2);
63
+ --psi-button-label-max-width: none;
59
64
  }
60
65
  }
@@ -18,4 +18,7 @@ export const menuVars = {
18
18
  "separator-border": "var(--psi-border-faint)",
19
19
  // D82 — the ring on a focused item; until then the browser drew its own.
20
20
  "focus-ring": "var(--psi-border-focus)",
21
+ // D99 — MenuText, a line that is not an action. fgSecondary on the menu
22
+ // surface (bgSecondary) is already gated at 4.5:1, so no new pair.
23
+ "text-fg": "var(--psi-fg-secondary)",
21
24
  };
@@ -12,5 +12,6 @@
12
12
  --psi-menu-item-fg-disabled: var(--psi-fg-quaternary);
13
13
  --psi-menu-separator-border: var(--psi-border-faint);
14
14
  --psi-menu-focus-ring: var(--psi-border-focus);
15
+ --psi-menu-text-fg: var(--psi-fg-secondary);
15
16
  }
16
17
  }
@@ -79,6 +79,11 @@
79
79
  --psi-button-32-font: var(--psi-control-32-font);
80
80
  --psi-button-40-font: var(--psi-control-40-font);
81
81
  --psi-button-48-font: var(--psi-control-48-font);
82
+ --psi-button-24-icon-size: calc(var(--psi-button-24-height) / 2);
83
+ --psi-button-32-icon-size: calc(var(--psi-button-32-height) / 2);
84
+ --psi-button-40-icon-size: calc(var(--psi-button-40-height) / 2);
85
+ --psi-button-48-icon-size: calc(var(--psi-button-48-height) / 2);
86
+ --psi-button-label-max-width: none;
82
87
  }
83
88
  }
84
89
 
@@ -226,6 +231,7 @@
226
231
  --psi-menu-item-fg-disabled: var(--psi-fg-quaternary);
227
232
  --psi-menu-separator-border: var(--psi-border-faint);
228
233
  --psi-menu-focus-ring: var(--psi-border-focus);
234
+ --psi-menu-text-fg: var(--psi-fg-secondary);
229
235
  }
230
236
  }
231
237
 
@@ -151,6 +151,12 @@
151
151
  12,
152
152
  16
153
153
  ],
154
+ "iconSize": [
155
+ 12,
156
+ 16,
157
+ 20,
158
+ 24
159
+ ],
154
160
  "gap": [
155
161
  4,
156
162
  8,
@@ -186,6 +192,8 @@
186
192
  },
187
193
  "note": "Per-size geometry is data, not CSS literals (D54). Arrays are indexed by sizes[]. Read a value as --psi-control-{size}-{prop} for the label ramp and --psi-control-value-{size}-{prop} for the value ramp; components alias these as --psi-{component}-{size}-{prop}, which is the layer to override. The value ramp is one step tighter than the label ramp because a left-aligned value wants less air than a centred label (D55).",
188
194
  "iconInset": "A leading icon sits one step closer to the edge than text — an icon is a solid shape with no side bearing (D55). Applied by .size{n}:has(> svg:first-child) on Button. IconButton is height-only: it is square with padding 0, so no padding/gap/font token applies to it.",
195
+ "iconSize": "A direct svg child of a Button renders at --psi-button-{size}-icon-size, half the button's height (12/16/20/24 at 24/32/40/48), whatever the icon's own size prop: CSS width and height beat the attributes an icon writes (D97). Pass icons without a size. A wrapped icon is not sized, as it gets no inset. IconButton does not bind the ramp; its icon keeps its own size (20 by default).",
196
+ "labelMaxWidth": "A label wrapped in a direct <span> is capped by --psi-button-label-max-width, default none, and ends in an ellipsis past the cap (D98). Set the token on your own class; it is size-invariant. A bare text label is not capped (wrap it, as a trailing icon already requires). The full text stays the accessible name, and Psi adds no title.",
189
197
  "iconInsetLimits": "The selector matches on element order, and :first-child ignores text nodes. Two consequences. (1) An icon wrapped in a <span> never matches — fails safe, no inset; set --psi-button-{size}-padding-inline-icon yourself. (2) A TRAILING icon (<Button>Next<IconChevronRight /></Button>) makes the svg the first ELEMENT child, so the rule fires and narrows the start (text) side while the icon keeps full padding — the inverse of the intent. CSS cannot tell the two apart without a DOM signal. Put the icon first, or override --psi-button-{size}-padding-inline-icon to match --psi-button-{size}-padding-inline to disable the inset.",
190
198
  "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
199
  },
@@ -463,6 +463,9 @@
463
463
  "menu-separator-border": [
464
464
  "border"
465
465
  ],
466
+ "menu-text-fg": [
467
+ "text"
468
+ ],
466
469
  "navbar-bg": [
467
470
  "surface"
468
471
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@handamade/psi-tokens",
3
- "version": "0.23.0",
3
+ "version": "0.25.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "exports": {