Repository navigation
chore(theme): promote --frame-* layout tokens to Tailwind namespaces #102
Description
Background
Follow-up from a review comment on #101 (gemini-code-assist, medium): #101 (closing #78) migrated the typography tokens to Tailwind v4's --text-* namespace (Approach A — real text-h1, text-body, … utilities), but kept the layout --frame-* tokens as raw vars consumed via arbitrary-value classes (Approach B — px-[var(--frame-gutter)], max-w-[var(--frame-max)], …), matching the existing scroll-mt-[var(--nav-h)] precedent. This was a deliberate split (see #78's "Approach (open question)" section). This issue tracks the remaining "Approach A for layout" option, should we want it.
Proposal
Promote the layout tokens in app/globals.css's @theme inline block to Tailwind v4 namespaces so standard utilities auto-generate (no bracket-soup):
--frame-gutter: clamp(1.5rem, 4vw, 4rem)→--spacing-frame-gutter⇒px-frame-gutter--frame-pad-y: clamp(5rem, 9vw, 7rem)→--spacing-frame-pad-y⇒py-frame-pad-y--frame-max: 1280px→--container-frame⇒max-w-frame(also yields a@framecontainer-query variant — harmless)--frame-min-h-cap: 54rem→ fold themin()into the token:--spacing-frame-min-h: min(100svh, 54rem)⇒min-h-frame-min-h(alternatively keep--frame-min-h-capas a raw var and leavemin-h-[min(100svh,var(--frame-min-h-cap))]as-is — it's irreducibly an arbitrary value otherwise)
Sites to update
components/ui/Frame.tsx—px-frame-gutter py-frame-pad-y min-h-frame-min-hon the<section>,max-w-frameon the inner<div>components/site/Footer.tsx—px-frame-gutter,max-w-framecomponents/site/Chrome.tsx—px-frame-gutter,max-w-frameapp/globals.css— the lone CSS refright: var(--frame-gutter)(mobile-menu close button) →var(--spacing-frame-gutter); update the--frame-*mentions in the:root--nav-hcomment and the@theme inlinecomments
Trade-off
Renames the public CSS-var API (--frame-* → --spacing-frame-* / --container-frame). No external consumers were found when #78 was scoped (docs/, content/blog/, all .css/.ts(x) — app/globals.css is the website's only stylesheet), so this is low-risk; bundle as one PR (a partial migration reintroduces the inconsistency #78 fixed).
Verification
Same triple as #78: pnpm lint && pnpm exec tsc --noEmit && pnpm test && pnpm build; pnpm dev smoke (frame gutter / max-width rail / min-height unchanged across §01–§05, /blog, a post); confirm out/_next/static/chunks/*.css is roughly flat in size.
Links
- Source comment: chore(theme): migrate inline layout/typography CSS-var styles to Tailwind #101 (comment)
- PR that landed the typography half + Approach-B layout: chore(theme): migrate inline layout/typography CSS-var styles to Tailwind #101
- Original migration issue (with the A/B discussion): chore(theme): migrate inline layout/typography CSS-var styles to Tailwind #78
Metadata
Metadata
Assignees
Labels
Type
Fields
Priority
Activity
github-actions commentedon May 13, 2026
🎯 Agentic Issue Triage
This is a well-scoped follow-up chore to promote the remaining
--frame-*layout tokens into Tailwind v4 namespaces — the "Approach A for layout" left on the table after #101 closed #78. Low risk, no external consumers, clear acceptance criteria.📋 Summary & Context
Type: Refactor / chore (CSS token migration)
Complexity: Low–medium — mechanical rename across ~4 files
Risk: Low —
app/globals.cssis the sole stylesheet; no external consumers confirmed in #78's scopeThis is the layout half of the migration that #101 only partially completed (typography tokens were promoted, layout tokens were kept as arbitrary-value classes). This issue proposes finishing the job by renaming tokens into Tailwind v4 namespaces so real utility classes are generated.
Affected files:
app/globals.css— rename tokens in@theme inline, update the loneright: var(--frame-gutter)raw CSS ref and commentscomponents/ui/Frame.tsx— replace arbitrary-value classescomponents/site/Footer.tsx— replace arbitrary-value classescomponents/site/Chrome.tsx— replace arbitrary-value classes🔧 Implementation checklist
app/globals.css@theme inline:--frame-gutter→--spacing-frame-gutter--frame-pad-y→--spacing-frame-pad-y--frame-max→--container-frame--frame-min-h-cap→--spacing-frame-min-h: min(100svh, 54rem)(or leave as raw var — see trade-off note in issue)right: var(--frame-gutter)→right: var(--spacing-frame-gutter)(mobile-menu close button)--frame-*mentions in:rootcomments and@theme inlinecommentsFrame.tsx: replacepx-[var(--frame-gutter)],py-[var(--frame-pad-y)],min-h-[...],max-w-[var(--frame-max)]with new utility classesFooter.tsx: replacepx-[var(--frame-gutter)],max-w-[var(--frame-max)]Chrome.tsx: replacepx-[var(--frame-gutter)],max-w-[var(--frame-max)]pnpm lint && pnpm exec tsc --noEmit && pnpm test && pnpm buildpnpm dev— confirm gutter/max-width/min-height are visually unchanged across §01–§05,/blog, and a postout/_next/static/chunks/*.csssize before/afterThe
--frame-min-h-captoken is used asmin-h-[min(100svh,var(--frame-min-h-cap))]— this expression is irreducibly an arbitrary value unless themin()computation is folded into the token itself. The issue proposes--spacing-frame-min-h: min(100svh, 54rem)→min-h-frame-min-h, which works but bakes in the100svhconstraint at the token level. Decide upfront whether to fold or leave this one as Approach B.🔗 Related issues & resources