Skip to content

refactor(ui): converge spacing to --space-* scale, ban bare px (#430 PR3) - #448

Merged
jackwener merged 6 commits into
mainfrom
opencode/spacing-converge-pr3
Jul 3, 2026
Merged

refactor(ui): converge spacing to --space-* scale, ban bare px (#430 PR3)#448
jackwener merged 6 commits into
mainfrom
opencode/spacing-converge-pr3

Conversation

@Astro-Han

@Astro-Han Astro-Han commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Converge every padding/gap/margin magic number onto a single --spacing: 4px ruler with a 14-value --space-* scale, then lock it with a contract test that bans bare Npx. Mirrors the proven radius/typography converge pattern.

Why

--spacing was 0.25rem, which under the 15px root font-size equals 3.75px, not the 4px the comment claimed. This silently broke alignment between Tailwind's spacing scale (p-2 = 7.5px) and hand-written px (padding: 8px) — two unrelated scales. On top of that bug, 687 CSS sites and 125 TSX sites used bare px values across 28 distinct values, with no governance against drift.

Closes #430

Scope

Changed:

  • --spacing bug fix: 0.25rem → absolute 4px in both :root and @theme inline, so Tailwind's p-N/gap-N/m-N and hand-written var(--space-N) share one ruler (the Tailwind scale shifts +6.7%: p-2 from 7.5px → 8px).
  • --space-* scale (14 values): 0/2/4/6/8/10/12 below 16 (even numbers, dense UI needs the 6 and 10 half-steps), 16/20/24/32/40/48/64 at and above (pure 4px multiples). 1px stays a hairline literal.
  • 687 CSS bare px → var(--space-*): padding/gap/margin across all renderer CSS. Non-scale values snapped to nearest tier: 3→2, 5→4, 7→6, 9→8, 11→12, 14→12, 18→16, 22→24, 26→24, 28→24, 30→32, 34→32, 36→40, 52→48, 56→64, 72→64. Negative margins use calc(var(--space-N) * -1). Responsive clamp()/max() bounds stay literal (viewport params, not spacing beats).
  • 125 TSX arbitrary → scale utility: p-[8px]p-2, gap-[6px]gap-1.5, etc.
  • spacing-converge-contract.test.ts: bans bare Npx in padding/margin/gap (CSS + TSX), pins token values, verifies @theme inline bridge. Allows 0/auto/inherit/1px hairline and clamp()/max()/min() responsive params (only when they contain a responsive dimension like vh/vw/%; pure-px max(8px, 12px) is rejected). Traces semantic custom properties to catch indirect bare-px references (e.g. --local-gap: 8px; gap: var(--local-gap)).
  • 6 existing contract tests updated: expectations aligned from old bare-px literals to new var(--space-*) / scale-utility forms.
  • docs/design-system.md §1.5 refreshed.
  • spacing.stories.tsx redesigned for the 14-value --space-* scale (was 6 arbitrary steps with stale 0.25rem comment and wrong step*4px labels). Story layout gaps also use var(--space-*) tokens.
  • Bonus fix: visible-copy-hygiene-contract.test.ts had 2 failing assertions on a clean main (leftover from refactor(ui): split --accent into semantic aliases, lock call-site governance #441's --accent--link semantic split). Fixed in a separate commit so the PR starts green.

Not included:

Verification

Commands run:

  • npm run rebuild — full build passes (all 6 @maka/* workspaces).
  • npm run -w @maka/desktop test1699/1699 pass (was 1681; +18 new spacing contract tests across 3 review rounds).
  • tsc -p tsconfig.main.json and tsc -p tsconfig.json — TS compilation clean.
  • npm run -w @maka/desktop build-storybook — Storybook build succeeds.
  • CSS bundle: 371.84kB → 387.01kB (+15.17kB, +4.1%) — the var(--space-*) references and @theme inline --spacing bridge.

Screenshot smoke (2026-07-03):

  • Electron real window launched (main + esbuild bundle from worktree; dev.mjs has a pre-existing node v26 esbuild binary incompatibility, not introduced by this PR).
  • Main chat interface: confirmed no density regression from --spacing 4px ruler (+6.7% Tailwind shift), 18→16, 14→12.
  • Storybook Spacing story: confirmed 14-value scale renders correctly.
  • Settings/modal: not individually screenshotted; covered by build + test suite + contract.

Known issue (not introduced by this PR):

User-facing impact

  • All Tailwind-based spacing (p-*, gap-*, m-*) grows ~6.7% because the ruler corrects from 3.75px to 4px. Dense UI elements (buttons, chips, cards) get marginally more breathing room — this is the intended "align Tailwind with hand-written px" effect from refactor(ui): converge design-token magic numbers (motion, typography, spacing) #430.
  • 18px chat turn gaps and composer padding → 16px (slightly tighter).
  • 14px card vertical padding → 12px (slightly tighter).
  • 22/26/28/30/34/52/56px one-off values snap to the nearest scale tier (±2-4px on a handful of surfaces).

Reviewer notes

  • Commits are atomic: (1) contract fix for refactor(ui): split --accent into semantic aliases, lock call-site governance #441 fallout, (2) --spacing bug + scale definition, (3) full bare-px replacement, (4) contract test, (5) docs, (6) storybook story redesign, (7) review round 2 fixes (margin-block-end, calc tightening, sidebar 8px), (8) review round 3 fixes (clamp/max/min responsive-only, semantic var tracing, story inline tokens).
  • The --spacing: 4px in @theme inline is the single most impactful line — it makes Tailwind's entire spacing scale switch from 3.75px to 4px base. This is the core fix, not a side effect.
  • The contract's isInsideResponsiveFunction now requires a responsive dimension (vh/vw/%/etc) inside clamp/max/min — pure-px max(8px, 12px) is rejected. Semantic var tracing catches --local: 8px; gap: var(--local).

Astro-Han and others added 6 commits July 3, 2026 15:57
…430 PR3)

The --spacing token was 0.25rem, which under the 15px root font-size
equals 3.75px, not the 4px the comment claimed. This silently broke
alignment between Tailwind's spacing scale (p-2 = 7.5px) and hand-written
px (padding: 8px) — two unrelated scales.

Fix the ruler to absolute 4px in both :root and @theme inline so Tailwind
utilities (p-N, gap-N, m-N) and hand-written var(--space-N) share one
source. Define a 14-value scale covering the codebase's real spacing
distribution: 0/2/4/6/8/10/12 below 16 (even numbers, dense UI needs the
6 and 10 half-steps), 16/20/24/32/40/48/64 at and above (pure 4px
multiples). 1px stays a literal (hairline). Deliberately absent: 14/18/
22/26/28/30/34/36/52/56 — snapped to the nearest scale value in the
follow-up commits to kill magic numbers.

Per the #430 adversarial review, spacing has no single-property anchor
like border-radius, so the upcoming contract scopes to padding/margin/
gap only (not width/height/inset/grid).
…430 PR3)

Replace every bare px value in padding/gap/margin across renderer CSS
(689 sites) and TSX arbitrary spacing utilities (125 sites) with
var(--space-*) tokens or Tailwind scale utilities. Snap non-scale values
to the nearest scale tier: 3->2, 5->4, 7->6, 9->8, 11->12, 14->12,
18->16, 22->24, 26->24, 28->24, 30->32, 34->32, 36->40, 52->48, 56->64,
72->64. Negative margins use calc(var(--space-N) * -1). 1px stays a
literal (hairline). Responsive clamp()/max() bounds stay literal (they
are viewport parameters, not spacing beats).

Update 6 contract tests that pinned the old bare-px literals to expect
the new var(--space-*) or scale-utility forms. The chat-header inset
contract's pxDeclaration helper now resolves var(--space-*) tokens to
their px equivalents so the geometric invariant (buttons x width + gaps
+ clearance = inset) still holds.
…/margin (#430 PR3)

Lock the spacing vocabulary so future PRs can't drift back to ad-hoc
px values. The contract enforces:

1. CSS padding/margin/gap must use var(--space-*), calc(var(--spacing)
   or var(--space-N)), or literals (0/auto/inherit/1px hairline). Bare
   Npx is banned. Responsive clamp()/max()/min() bounds are exempt
   (viewport parameters, not spacing beats).

2. --spacing is 4px (absolute, not 0.25rem) and --space-* tokens are
   pinned to calc(var(--spacing) * N) with the correct multipliers.

3. The @theme inline bridge exports --spacing: 4px so Tailwind p-N/
   gap-N/m-N share the ruler with hand-written var(--space-N).

4. TSX has no arbitrary p-[Npx]/gap-[Npx]/m-[Npx] (except 1px hairline).

Spacing has no single-property anchor like border-radius (adversarial
review in #430), so the contract scopes to padding/margin/gap only.

Also fixes 3 single-line declarations the batch replacement missed
(margin: 2px 0, padding: 16px 20px, margin-top: 2px in single-line
multi-declaration rules).
…PR3)

Old story showed 6 arbitrary calc(var(--spacing) * N) steps with a
stale 0.25rem comment and a step*4px label that was silently wrong
(--spacing was 3.75px, not 4px). Redesign to show the full 14-value
--space-* scale with token name, pixel value, visual bar, and usage.
Add a 1px hairline section and keep the layout-geometry section.
The ratchet (#459) froze 442 legacy off-grid px values per file as a
transitional mechanism. This PR replaces bare px spacing wholesale
with the --space-* scale and bans new bare px outright
(spacing-converge-contract), which strictly supersedes the ratchet:
after the conversion the ratchet baselines are all stale-high and its
anti-rust slack check would fail. One mechanism, the stronger one.
@jackwener
jackwener force-pushed the opencode/spacing-converge-pr3 branch from 4c81bd1 to bdd4ecb Compare July 3, 2026 08:04
@jackwener
jackwener merged commit 4b3858b into main Jul 3, 2026
@jackwener
jackwener deleted the opencode/spacing-converge-pr3 branch July 3, 2026 08:04
@jackwener

Copy link
Copy Markdown
Member

Merged,感谢!🎉 这是 #430 系列里含金量最高的一条 —— --spacing: 0.25rem 在 15px root 下 = 3.75px 的真 bug 修得非常关键(Tailwind 与手写 px 从此一把尺),687+125 处的收敛 + 契约也把 D1 决策(4pt 系统)一次做完。

Maintainer 落地时做了三件事:

  1. Rebase 到今天的 main(跨 fix(ui): stop showing healthy legacy sessions as 已阻塞; compact sidebar timestamps; localize bypass label #452/fix(ui): revive dead darwin glass palette, fix token typos, dark mint contrast #454/feat(design): refinement roadmap from 4-source skill study + P-SHADOW recipes #457-460 七个 CSS PR,4 处冲突:保留了 oklch shadow 修复、P-RADIUS 同心圆角块、off-black CTA 的 oklch 配方,套上你的 var(--space-*) token 形式)
  2. 退役 feat(design): P-4PT spacing ratchet contract + P-TEXT orphan tier cleanup #459 的 4pt ratchet 契约 —— 你的 spacing-converge ban 严格强于渐进 ratchet(转换后 ratchet 基线全部虚高、防锈检查必炸),一个机制留强的
  3. 补了你自述缺的截图验证:first-run / module-skills / settings-general / turn-narrative 双主题 fixture 全部健康;composer 区的暖纸底色经比对确认是 fix(ui): revive dead darwin glass palette, fix token typos, dark mint contrast #454 玻璃色板复活的设计意图(main 上已有),非本 PR 回归

全量 1696/1696 pass。

Astro-Han pushed a commit that referenced this pull request Jul 5, 2026
…#532)

The spacing-converge pass (#448) moved footer-action / lineage-badge
marker shells off bare-px arbitraries (gap-[6px], px-[8px], py-[4px],
gap-[3px], px-[5px]) onto the 4px-ruler scale (gap-1.5, px-2, py-1,
gap-0.5, px-1) but left the contract test's expected literals behind,
so 'npm --workspace @maka/ui test' has been red on main since then.
Update the pinned literals; the merge semantics under test (UiButton
base utilities like gap-2 must merge out) are unchanged and still pass.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

refactor(ui): converge design-token magic numbers (motion, typography, spacing)

2 participants