Control ramp: per-size geometry as tokens (D54–D55) - #58
Merged
Conversation
Per-size geometry on Button/IconButton/Input/Select lives as literals in CSS Modules, so it is invisible to the manifest, guidance.json, the MCP, the DTCG export and the Figma plugin; unreachable by componentOverrides (D34); and ungated by CI. That is why Input and Select shipped flat at space-8 while Button scales 8/12/16/20 — at size 48 a Button insets its label 20px and an adjacent Input insets its text 8px. D54 introduces a `control` token family (surface.ts posture, D51) that the four sized controls alias. D55 splits label and value ramps, adds text-18-28-regular so the value ramp runs regular end to end, and gives icon-leading controls an optical inset via :has(). Also gitignores analytics/ for local-only analysis reports. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
^0.7.2 resolves to <0.8.0, so shipping the Input/Select padding change as a patch would land it on consumers' next install without an explicit bump. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The psi/component-tokens-only stylelint rule maps icon-button -> button in
its ALIASES table, so icon-button.module.css may only bind --psi-button-*.
IconButton therefore declares no ramp tokens of its own and binds
--psi-button-{n}-height. Token count corrects to 52 aliases / 80 total.
Also records that the same rule enforces the alias tier: --psi-control-* is
neither an own-component nor a scale token, so no CSS Module can bind the
family directly.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Six tasks: text-18-28-regular combo, the control family, Button/IconButton binding plus the optical inset, Input/Select binding plus the anti-drift test, the guidance.json geometry block, and story/VR/changeset. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Input and Select ran regular at 24/32/40 and jumped to medium at 48 only because no sans 18-28-regular existed. Adding it lets the value ramp run regular end to end.
Per-size geometry for Button/IconButton/Input/Select becomes data instead of literals in CSS Modules. Token-only family, surface.ts posture (D51). Label and value ramps fork on padding and font; height and gap are shared.
Component tokens do not reach the MCP — it bakes guidance.json and resolved/*.json, and resolved carries only semantic theme tokens. Without this an agent still could not ask what padding a 40px Button uses. Adds a drift test (controller amendment) that mirrors guidance.geometry against controlVars in control.ts, so a retuned ramp cannot silently diverge from what guidance.json reports.
Size geometry moves from CSS literals onto --psi-button-{n}-* aliases of
the control family. Adds the D55 optical icon inset via :has() — a leading
icon sits one step closer to the edge than text. Text-only Buttons are
pixel-identical; ember's --psi-button-font override is preserved.
Both were flat at space-8 at every size while Button scaled 8/12/16/20 — at size 48 a Button inset its label 20px and an adjacent Input inset its text 8px. Both now bind the shared value ramp (8/8/12/16), and the anti-drift test asserts they resolve to the same token at every size.
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
14 baselines: 12 updated, 2 new. Every one traced to an intended change. - input/select --all-sizes: the fix. Sizes 24 and 32 are unchanged in the diff and only 40/48 shift, exactly as the value ramp 8/8/12/16 predicts. - field--default, dialog--form-dialog: contain Input/Select at 40/48 and inherit the padding change; labels and help text unchanged. - typography--specimens (4 themes): the new text-18-28-regular combo inserts one row, shifting everything below it. Rows above are unchanged. - button--icon-leading: new story. Renders the optical inset — the icon sits closer to the left edge than the label does to the right. components-button--all-sizes is NOT in this commit, in either theme. That absence is the acceptance gate: it proves container + text-inset reproduces the pre-D54 padding, and that ember's --psi-button-font mono override still resolves at every size. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1. guidance.json told agents a 40px IconButton insets 16px. It sets
padding: 0 and binds only --psi-button-{n}-height. components.IconButton
is now "height-only". That artifact exists to answer exactly this
question, and it was wrong for one of four components — Task 5 wrote the
block, Task 3 wrote the binding, neither reviewer saw both.
2. The :has(> svg:first-child) inset fails WRONG on trailing icons, not just
safe on wrapped ones. :first-child ignores text nodes, so in
<Button>Next<Icon /></Button> the svg is the first ELEMENT child: the rule
fires and narrows the start (text) side while the icon keeps full padding
— the inverse of the intent. CSS cannot tell the cases apart without a DOM
signal, and adding one means the API change D55 exists to avoid. Accepted
and documented in the spec and guidance.geometry.iconInsetLimits rather
than silently shipped.
3. The spec claimed the anti-drift test makes the defect "unrepeatable". It
gates the token layer only. The original defect lived in input.module.css,
and nothing here gates CSS Modules: stylelint allows --psi-space-*, the
scope gate skips geometry by design, and no test reads a module. Claim
corrected; the real gate filed as a follow-up.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Per-size geometry for the four sized controls moves out of CSS Module literals into a shared
--psi-control-*token family, fixing a divergence that shipped in production.Spec:
docs/superpowers/specs/2026-07-31-control-ramp-tokens-design.mdPlan:
docs/superpowers/plans/2026-07-31-control-ramp-tokens.mdThe defect
Buttonscaled its inline padding across the ramp (8/12/16/20) whileInputandSelectsat flat atspace-8at every size. Atsize={48}a Button inset its label 20px and an adjacent Input inset its text 8px — in every form Psi ships.Nothing caught it because geometry wasn't tokenized: invisible to the manifest,
guidance.json, the MCP, the DTCG export and the Figma plugin; unreachable bycomponentOverrides(D34); ungated by CI.What changed
D54 — a
controltoken-only family (thesurface.tsposture from D51), 28 tokens, aliased per component as--psi-{component}-{size}-{prop}. That alias tier isn't merely preferred:psi/component-tokens-onlyallows a module to bind only its own component's tokens or a scale token, so--psi-control-*physically cannot be bound directly.D55 — label and value ramps fork;
text-18-28-regularadded so the value ramp runs regular end to end; icon-leading buttons get an optical inset.Visible changes
Input/Select at 48 also move
medium→regular. Buttons with a leading icon gain the optical inset (12 [icon] 8 [label] 16at size 40) and a ramped gap (4/8/8/8, was flat 6).Text-only Buttons are pixel-identical — the icon inset is derived so
container + text-insetreproduces the old padding exactly.Acceptance gate for review
components-button--all-sizesmust come back byte-identical. If that baseline appears in the changed set, the icon-inset derivation is wrong and the fix is the token values, not the baseline.The anti-drift test is the cycle's real acceptance test: Input and Select must resolve to the same
control-value-*token at every size, which is what makes this defect structurally unrepeatable rather than merely fixed.Notes
^0.7.xresolves to<0.8.0, so a patch would land the padding change on consumers' next install without an opt-in.🤖 Generated with Claude Code