feat(theme): add semantic/layouts — the container system - #884

Merged
isaque-bock-azion merged 9 commits into
mainfrom
feat/theme-layout-tokens
Aug 15, 2026
Merged

feat(theme): add semantic/layouts — the container system#884
isaque-bock-azion merged 9 commits into
mainfrom
feat/theme-layout-tokens

Conversation

@gabriel-lisboa-azion

Copy link
Copy Markdown
Collaborator

What

The layout system every console page is built on, as a theme token group: ten tokens and nine @utility classes, covering three decisions expressed once — how far content sits from the app chrome (boundary), how far things sit from each other (rhythm), and how wide a reading column may get (measure).

Four container types, picked by what the page is:

ClassTypeTodayFor
.layout-columnData1620pxLists, detail dashboards
.layout-column-focusedFocused1024pxHome, single-task multi-column pages
.layout-column-formForm1024pxSettings, in-page edit forms
.layout-form-createCreate1192pxCreate pages (also retunes --layout-measure-control)

Plus .layout-boundary / .layout-boundary-inline, the two rhythm steps (--layout-section-gap, --layout-group-gap) with their .layout-section-start / .layout-group-start margin forms, and .layout-field-control for the control side of a settings row.

A derived group

Every token is a var() reference to --spacing-* / --container-*, never a literal length, and that is why none of them carries a breakpoint map. The spacing scale is already fluid (--spacing-lg is 1rem, then 1.5rem from sm), and var() is substituted at use time on the element — so a layout token follows the breakpoint override for free. Giving it its own map would duplicate the spacing scale into layout and let the two drift.

Why @utility and not @layer components

Same reason the typography utilities are emitted that way: @layer components classes are opaque to v4's variant resolver, and their modifiers silently drop. A layout class is exactly the kind you want behind a variant — md:layout-column.

Two things worth a reviewer's eye

.layout-field-control carries a :not(#\#) specificity pin. It is applied to Item.Actions, whose own root already declares shrink-0, and Vue merges both class lists onto one element. Custom @utility blocks sort before the core utilities, so without the pin shrink-0 wins and the control side can no longer yield to a long field name. Measured, not assumed — .layout-field-control at byte 19079 vs .shrink-0 at 31336 in a built bundle. #\# is an id no element can carry, so the selector always matches while reading (1,1,0); it is Tailwind's own important-strategy idiom and stays variant-safe. Only this one of the nine has it, because only this one has a live conflict.

The boundary widening nests on the column, not the boundary. Specificity is (0,2,0) either way, so correctness does not decide it. Nesting on the column keeps both declarations that can set max-width in one block, keeps .layout-boundary to the three padding declarations its name promises, and keeps "a fifth column class costs one line" true — they are generated from a COLUMN_MEASURE map and the nested rule comes along.

Also in the diff

  • emitIllustrationUtilities-style emission generalized into emitUtilities(map) with a thin emitLayoutUtilities caller.
  • Regenerating dist picks up one line main was stale on: an emitted comment still reading bg-[var(--x)] where the source has said bg-(--x) since ENG-47001.
  • Docs: a new Foundations → Layout Storybook page (built from the token source, so it cannot drift), the Max width section of DESIGN.md, a Layout row in Get Started, and the derived-group note in the theme's token README. DESIGN.md ships here rather than separately because that section is the catalog for these tokens — documenting them before they exist would be the drift the rule guards against.

Verification

  • build:tokens clean, assertNoZeroWithUnit passing, theme suite 12/12.
  • git diff dist/v4/globals.css is additions only apart from the stale comment: 10 --layout-* lines in :root, no new @media lines (every token is a bare var()), 9 new @utility blocks.
  • Storybook builds; validate-story-source --all reports 91/91 compliant.

Ten tokens and nine `@utility` classes: how far content sits from the app
chrome (BOUNDARY), how far things sit from each other (RHYTHM), and how wide
a reading column may get (MEASURE).
A DERIVED group: every token is a var() reference to --spacing-* /
--container-*, never a literal length. That is why none carries a breakpoint
map — the spacing scale is already fluid and var() is substituted at use time
on the element, so a layout token follows the override for free. Duplicating
the scale into layout would only let the two drift.
Emitting them as `@utility` rather than `@layer components` is what gives
them variants (`md:layout-column`), the same reason the typography utilities
are emitted that way.
Generalizes the utility emitter into emitUtilities(map) with a thin
emitLayoutUtilities caller, so the shape is reusable.
Regenerating dist also picks up one line main was stale on: an emitted
comment that still read `bg-[var(--x)]` where the source has said `bg-(--x)`
since ENG-47001.
The catalog's token allowlists are scraped from the built theme, so adding
semantic/layouts adds ten --layout-* entries. Regenerated rather than
hand-edited, which is what catalog:check verifies.
isaque-bock-azion
isaque-bock-azion previously approved these changes Aug 14, 2026
isaque-bock-azionand others added 3 commits August 14, 2026 17:52
…lighter type scale
`Avatar › VariantGrid` failed the visual shard on `dark-desktop` only, at
1.0301% / 1354px against a 1% threshold — deterministically, byte-identical
across two runs.
The cause is not this branch. That baseline was last regenerated on
2026-07-22 by #768 and has missed two theme changes since: #889 (opaque
neutral border tokens) and #876 (headings and body steps to font-weight 300,
body leading 1.5 → 1.375). #876 regenerated 548 baselines across three
rounds and updated this story's `--dark-tablet` file, but never the
dark-desktop one — which alone keeps the bare story id instead of a `--mode`
suffix (see .storybook/visual-modes.js), so it is the easy file to miss in a
partial regeneration. Aligning baseline to render needs a zero shift
(dx=0, dy=0); what differs is ink — text -9%, icons -7% — the lighter
weight. This branch only adds tokens, and is simply the first PR to run the
suite with #876 merged in.
Regenerated via the app-storybook-generate-baseline workflow on linux. The
`-u` path could not rewrite it on its own: stale-baseline vs clean render is
0.9875%, under the threshold, so the snapshot passed and was left alone. The
file had to be deleted so it was written as a missing snapshot. Against the
render CI actually produced, the new baseline is 0.0426% (56px).
Still stale and left alone, since both currently pass: this story's
`--dark-mobile` baseline (also 2026-07-22). Worth noting separately that the
Avatar stories source their photo from a live Unsplash URL, which
contributed the residual noise here and is what kept this story on the 1%
boundary.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@isaque-bock-azion
isaque-bock-azion dismissed stale reviews from guilherme-santana-azion and themself via 3c9dc47August 14, 2026 21:26
@isaque-bock-azion
isaque-bock-azion merged commit 282f768 into mainAug 15, 2026
23 checks passed
@isaque-bock-azion
isaque-bock-azion deleted the feat/theme-layout-tokens branch August 15, 2026 22:36
gabriel-lisboa-azion added a commit that referenced this pull request Aug 17, 2026
Brings the three commits demo lacked, so the deployed sample exercises them
alongside the four fixes carved out of this branch (#899#902), whose content was
already here:
- feat(webkit): chip's three kinds (#883)
- feat(theme): the semantic/layouts container system (#884)
- feat(theme): lightened heading and body type (#876)
Conflict resolutions worth knowing:
`build-tokens.mjs` — main has no illustration tokens, so main's side of all six
hunks was empty. Taking it would have silently deleted this branch's illustration
wiring; ours was kept. The merge then produced a DUPLICATE `emitUtilities` and
`emitLayoutUtilities` with no conflict at all (both sides had added an identical
helper in different places), which is a syntax error the merge itself reported as
clean — the second copy is removed and `emitIllustrationUtilities` reuses the
first.
`texts.data.js` — the five conflicts were all `text-body-*` weight, resolved to
main's `light` since that is the change being previewed. Resolved in place rather
than with `--theirs`, which would have discarded the file's auto-merged hunks.
Entry count held at 144.
`.size-limit.json` — union, not a side: main's `chip` plus this branch's
`footer-root` and `resizable-panel-root`.
`dist/v4/globals.*` are generated, so they were rebuilt from the merged sources
rather than hand-merged. The 466 conflicting visual baselines took main's copies;
neither side is valid for a merged tree, and this branch opens no PR the visual
gate guards.
Verified after: no `undefined` in the built CSS, illustration tokens still emitted,
main's layout utilities present, body weights now 300, no token data file lost
entries, and 154 tests pass across toast, table, sidebar, resizable-panel and chip.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@gabriel-lisboa-azion@robsongajunior@herbert-julio-azion@guilherme-santana-azion@robson-junior-azion@isaque-bock-azion@rafael-garbinatto-azion
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

feat(theme): add semantic/layouts — the container system - #884

Merged
isaque-bock-azion merged 9 commits into
mainfrom
feat/theme-layout-tokens
Aug 15, 2026
Merged

feat(theme): add semantic/layouts — the container system#884
isaque-bock-azion merged 9 commits into
mainfrom
feat/theme-layout-tokens

Conversation

@gabriel-lisboa-azion

Copy link
Copy Markdown
Collaborator

What

The layout system every console page is built on, as a theme token group: ten tokens and nine @utility classes, covering three decisions expressed once — how far content sits from the app chrome (boundary), how far things sit from each other (rhythm), and how wide a reading column may get (measure).

Four container types, picked by what the page is:

ClassTypeTodayFor
.layout-columnData1620pxLists, detail dashboards
.layout-column-focusedFocused1024pxHome, single-task multi-column pages
.layout-column-formForm1024pxSettings, in-page edit forms
.layout-form-createCreate1192pxCreate pages (also retunes --layout-measure-control)

Plus .layout-boundary / .layout-boundary-inline, the two rhythm steps (--layout-section-gap, --layout-group-gap) with their .layout-section-start / .layout-group-start margin forms, and .layout-field-control for the control side of a settings row.

A derived group

Every token is a var() reference to --spacing-* / --container-*, never a literal length, and that is why none of them carries a breakpoint map. The spacing scale is already fluid (--spacing-lg is 1rem, then 1.5rem from sm), and var() is substituted at use time on the element — so a layout token follows the breakpoint override for free. Giving it its own map would duplicate the spacing scale into layout and let the two drift.

Why @utility and not @layer components

Same reason the typography utilities are emitted that way: @layer components classes are opaque to v4's variant resolver, and their modifiers silently drop. A layout class is exactly the kind you want behind a variant — md:layout-column.

Two things worth a reviewer's eye

.layout-field-control carries a :not(#\#) specificity pin. It is applied to Item.Actions, whose own root already declares shrink-0, and Vue merges both class lists onto one element. Custom @utility blocks sort before the core utilities, so without the pin shrink-0 wins and the control side can no longer yield to a long field name. Measured, not assumed — .layout-field-control at byte 19079 vs .shrink-0 at 31336 in a built bundle. #\# is an id no element can carry, so the selector always matches while reading (1,1,0); it is Tailwind's own important-strategy idiom and stays variant-safe. Only this one of the nine has it, because only this one has a live conflict.

The boundary widening nests on the column, not the boundary. Specificity is (0,2,0) either way, so correctness does not decide it. Nesting on the column keeps both declarations that can set max-width in one block, keeps .layout-boundary to the three padding declarations its name promises, and keeps "a fifth column class costs one line" true — they are generated from a COLUMN_MEASURE map and the nested rule comes along.

Also in the diff

  • emitIllustrationUtilities-style emission generalized into emitUtilities(map) with a thin emitLayoutUtilities caller.
  • Regenerating dist picks up one line main was stale on: an emitted comment still reading bg-[var(--x)] where the source has said bg-(--x) since ENG-47001.
  • Docs: a new Foundations → Layout Storybook page (built from the token source, so it cannot drift), the Max width section of DESIGN.md, a Layout row in Get Started, and the derived-group note in the theme's token README. DESIGN.md ships here rather than separately because that section is the catalog for these tokens — documenting them before they exist would be the drift the rule guards against.

Verification

  • build:tokens clean, assertNoZeroWithUnit passing, theme suite 12/12.
  • git diff dist/v4/globals.css is additions only apart from the stale comment: 10 --layout-* lines in :root, no new @media lines (every token is a bare var()), 9 new @utility blocks.
  • Storybook builds; validate-story-source --all reports 91/91 compliant.

Ten tokens and nine `@utility` classes: how far content sits from the app
chrome (BOUNDARY), how far things sit from each other (RHYTHM), and how wide
a reading column may get (MEASURE).
A DERIVED group: every token is a var() reference to --spacing-* /
--container-*, never a literal length. That is why none carries a breakpoint
map — the spacing scale is already fluid and var() is substituted at use time
on the element, so a layout token follows the override for free. Duplicating
the scale into layout would only let the two drift.
Emitting them as `@utility` rather than `@layer components` is what gives
them variants (`md:layout-column`), the same reason the typography utilities
are emitted that way.
Generalizes the utility emitter into emitUtilities(map) with a thin
emitLayoutUtilities caller, so the shape is reusable.
Regenerating dist also picks up one line main was stale on: an emitted
comment that still read `bg-[var(--x)]` where the source has said `bg-(--x)`
since ENG-47001.
The catalog's token allowlists are scraped from the built theme, so adding
semantic/layouts adds ten --layout-* entries. Regenerated rather than
hand-edited, which is what catalog:check verifies.
isaque-bock-azion
isaque-bock-azion previously approved these changes Aug 14, 2026
isaque-bock-azionand others added 3 commits August 14, 2026 17:52
…lighter type scale
`Avatar › VariantGrid` failed the visual shard on `dark-desktop` only, at
1.0301% / 1354px against a 1% threshold — deterministically, byte-identical
across two runs.
The cause is not this branch. That baseline was last regenerated on
2026-07-22 by #768 and has missed two theme changes since: #889 (opaque
neutral border tokens) and #876 (headings and body steps to font-weight 300,
body leading 1.5 → 1.375). #876 regenerated 548 baselines across three
rounds and updated this story's `--dark-tablet` file, but never the
dark-desktop one — which alone keeps the bare story id instead of a `--mode`
suffix (see .storybook/visual-modes.js), so it is the easy file to miss in a
partial regeneration. Aligning baseline to render needs a zero shift
(dx=0, dy=0); what differs is ink — text -9%, icons -7% — the lighter
weight. This branch only adds tokens, and is simply the first PR to run the
suite with #876 merged in.
Regenerated via the app-storybook-generate-baseline workflow on linux. The
`-u` path could not rewrite it on its own: stale-baseline vs clean render is
0.9875%, under the threshold, so the snapshot passed and was left alone. The
file had to be deleted so it was written as a missing snapshot. Against the
render CI actually produced, the new baseline is 0.0426% (56px).
Still stale and left alone, since both currently pass: this story's
`--dark-mobile` baseline (also 2026-07-22). Worth noting separately that the
Avatar stories source their photo from a live Unsplash URL, which
contributed the residual noise here and is what kept this story on the 1%
boundary.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@isaque-bock-azion
isaque-bock-azion dismissed stale reviews from guilherme-santana-azion and themself via 3c9dc47August 14, 2026 21:26
@isaque-bock-azion
isaque-bock-azion merged commit 282f768 into mainAug 15, 2026
23 checks passed
@isaque-bock-azion
isaque-bock-azion deleted the feat/theme-layout-tokens branch August 15, 2026 22:36
gabriel-lisboa-azion added a commit that referenced this pull request Aug 17, 2026
Brings the three commits demo lacked, so the deployed sample exercises them
alongside the four fixes carved out of this branch (#899#902), whose content was
already here:
- feat(webkit): chip's three kinds (#883)
- feat(theme): the semantic/layouts container system (#884)
- feat(theme): lightened heading and body type (#876)
Conflict resolutions worth knowing:
`build-tokens.mjs` — main has no illustration tokens, so main's side of all six
hunks was empty. Taking it would have silently deleted this branch's illustration
wiring; ours was kept. The merge then produced a DUPLICATE `emitUtilities` and
`emitLayoutUtilities` with no conflict at all (both sides had added an identical
helper in different places), which is a syntax error the merge itself reported as
clean — the second copy is removed and `emitIllustrationUtilities` reuses the
first.
`texts.data.js` — the five conflicts were all `text-body-*` weight, resolved to
main's `light` since that is the change being previewed. Resolved in place rather
than with `--theirs`, which would have discarded the file's auto-merged hunks.
Entry count held at 144.
`.size-limit.json` — union, not a side: main's `chip` plus this branch's
`footer-root` and `resizable-panel-root`.
`dist/v4/globals.*` are generated, so they were rebuilt from the merged sources
rather than hand-merged. The 466 conflicting visual baselines took main's copies;
neither side is valid for a merged tree, and this branch opens no PR the visual
gate guards.
Verified after: no `undefined` in the built CSS, illustration tokens still emitted,
main's layout utilities present, body weights now 300, no token data file lost
entries, and 154 tests pass across toast, table, sidebar, resizable-panel and chip.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@gabriel-lisboa-azion@robsongajunior@herbert-julio-azion@guilherme-santana-azion@robson-junior-azion@isaque-bock-azion@rafael-garbinatto-azion
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(theme): add semantic/layouts — the container system - #884

Merged
isaque-bock-azion merged 9 commits into
mainfrom
feat/theme-layout-tokens
Aug 15, 2026
Merged

feat(theme): add semantic/layouts — the container system#884
isaque-bock-azion merged 9 commits into
mainfrom
feat/theme-layout-tokens

Conversation

@gabriel-lisboa-azion

Copy link
Copy Markdown
Collaborator

What

The layout system every console page is built on, as a theme token group: ten tokens and nine @utility classes, covering three decisions expressed once — how far content sits from the app chrome (boundary), how far things sit from each other (rhythm), and how wide a reading column may get (measure).

Four container types, picked by what the page is:

ClassTypeTodayFor
.layout-columnData1620pxLists, detail dashboards
.layout-column-focusedFocused1024pxHome, single-task multi-column pages
.layout-column-formForm1024pxSettings, in-page edit forms
.layout-form-createCreate1192pxCreate pages (also retunes --layout-measure-control)

Plus .layout-boundary / .layout-boundary-inline, the two rhythm steps (--layout-section-gap, --layout-group-gap) with their .layout-section-start / .layout-group-start margin forms, and .layout-field-control for the control side of a settings row.

A derived group

Every token is a var() reference to --spacing-* / --container-*, never a literal length, and that is why none of them carries a breakpoint map. The spacing scale is already fluid (--spacing-lg is 1rem, then 1.5rem from sm), and var() is substituted at use time on the element — so a layout token follows the breakpoint override for free. Giving it its own map would duplicate the spacing scale into layout and let the two drift.

Why @utility and not @layer components

Same reason the typography utilities are emitted that way: @layer components classes are opaque to v4's variant resolver, and their modifiers silently drop. A layout class is exactly the kind you want behind a variant — md:layout-column.

Two things worth a reviewer's eye

.layout-field-control carries a :not(#\#) specificity pin. It is applied to Item.Actions, whose own root already declares shrink-0, and Vue merges both class lists onto one element. Custom @utility blocks sort before the core utilities, so without the pin shrink-0 wins and the control side can no longer yield to a long field name. Measured, not assumed — .layout-field-control at byte 19079 vs .shrink-0 at 31336 in a built bundle. #\# is an id no element can carry, so the selector always matches while reading (1,1,0); it is Tailwind's own important-strategy idiom and stays variant-safe. Only this one of the nine has it, because only this one has a live conflict.

The boundary widening nests on the column, not the boundary. Specificity is (0,2,0) either way, so correctness does not decide it. Nesting on the column keeps both declarations that can set max-width in one block, keeps .layout-boundary to the three padding declarations its name promises, and keeps "a fifth column class costs one line" true — they are generated from a COLUMN_MEASURE map and the nested rule comes along.

Also in the diff

  • emitIllustrationUtilities-style emission generalized into emitUtilities(map) with a thin emitLayoutUtilities caller.
  • Regenerating dist picks up one line main was stale on: an emitted comment still reading bg-[var(--x)] where the source has said bg-(--x) since ENG-47001.
  • Docs: a new Foundations → Layout Storybook page (built from the token source, so it cannot drift), the Max width section of DESIGN.md, a Layout row in Get Started, and the derived-group note in the theme's token README. DESIGN.md ships here rather than separately because that section is the catalog for these tokens — documenting them before they exist would be the drift the rule guards against.

Verification

  • build:tokens clean, assertNoZeroWithUnit passing, theme suite 12/12.
  • git diff dist/v4/globals.css is additions only apart from the stale comment: 10 --layout-* lines in :root, no new @media lines (every token is a bare var()), 9 new @utility blocks.
  • Storybook builds; validate-story-source --all reports 91/91 compliant.

Ten tokens and nine `@utility` classes: how far content sits from the app
chrome (BOUNDARY), how far things sit from each other (RHYTHM), and how wide
a reading column may get (MEASURE).
A DERIVED group: every token is a var() reference to --spacing-* /
--container-*, never a literal length. That is why none carries a breakpoint
map — the spacing scale is already fluid and var() is substituted at use time
on the element, so a layout token follows the override for free. Duplicating
the scale into layout would only let the two drift.
Emitting them as `@utility` rather than `@layer components` is what gives
them variants (`md:layout-column`), the same reason the typography utilities
are emitted that way.
Generalizes the utility emitter into emitUtilities(map) with a thin
emitLayoutUtilities caller, so the shape is reusable.
Regenerating dist also picks up one line main was stale on: an emitted
comment that still read `bg-[var(--x)]` where the source has said `bg-(--x)`
since ENG-47001.
The catalog's token allowlists are scraped from the built theme, so adding
semantic/layouts adds ten --layout-* entries. Regenerated rather than
hand-edited, which is what catalog:check verifies.
isaque-bock-azion
isaque-bock-azion previously approved these changes Aug 14, 2026
isaque-bock-azionand others added 3 commits August 14, 2026 17:52
…lighter type scale
`Avatar › VariantGrid` failed the visual shard on `dark-desktop` only, at
1.0301% / 1354px against a 1% threshold — deterministically, byte-identical
across two runs.
The cause is not this branch. That baseline was last regenerated on
2026-07-22 by #768 and has missed two theme changes since: #889 (opaque
neutral border tokens) and #876 (headings and body steps to font-weight 300,
body leading 1.5 → 1.375). #876 regenerated 548 baselines across three
rounds and updated this story's `--dark-tablet` file, but never the
dark-desktop one — which alone keeps the bare story id instead of a `--mode`
suffix (see .storybook/visual-modes.js), so it is the easy file to miss in a
partial regeneration. Aligning baseline to render needs a zero shift
(dx=0, dy=0); what differs is ink — text -9%, icons -7% — the lighter
weight. This branch only adds tokens, and is simply the first PR to run the
suite with #876 merged in.
Regenerated via the app-storybook-generate-baseline workflow on linux. The
`-u` path could not rewrite it on its own: stale-baseline vs clean render is
0.9875%, under the threshold, so the snapshot passed and was left alone. The
file had to be deleted so it was written as a missing snapshot. Against the
render CI actually produced, the new baseline is 0.0426% (56px).
Still stale and left alone, since both currently pass: this story's
`--dark-mobile` baseline (also 2026-07-22). Worth noting separately that the
Avatar stories source their photo from a live Unsplash URL, which
contributed the residual noise here and is what kept this story on the 1%
boundary.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@isaque-bock-azion
isaque-bock-azion dismissed stale reviews from guilherme-santana-azion and themself via 3c9dc47August 14, 2026 21:26
@isaque-bock-azion
isaque-bock-azion merged commit 282f768 into mainAug 15, 2026
23 checks passed
@isaque-bock-azion
isaque-bock-azion deleted the feat/theme-layout-tokens branch August 15, 2026 22:36
gabriel-lisboa-azion added a commit that referenced this pull request Aug 17, 2026
Brings the three commits demo lacked, so the deployed sample exercises them
alongside the four fixes carved out of this branch (#899#902), whose content was
already here:
- feat(webkit): chip's three kinds (#883)
- feat(theme): the semantic/layouts container system (#884)
- feat(theme): lightened heading and body type (#876)
Conflict resolutions worth knowing:
`build-tokens.mjs` — main has no illustration tokens, so main's side of all six
hunks was empty. Taking it would have silently deleted this branch's illustration
wiring; ours was kept. The merge then produced a DUPLICATE `emitUtilities` and
`emitLayoutUtilities` with no conflict at all (both sides had added an identical
helper in different places), which is a syntax error the merge itself reported as
clean — the second copy is removed and `emitIllustrationUtilities` reuses the
first.
`texts.data.js` — the five conflicts were all `text-body-*` weight, resolved to
main's `light` since that is the change being previewed. Resolved in place rather
than with `--theirs`, which would have discarded the file's auto-merged hunks.
Entry count held at 144.
`.size-limit.json` — union, not a side: main's `chip` plus this branch's
`footer-root` and `resizable-panel-root`.
`dist/v4/globals.*` are generated, so they were rebuilt from the merged sources
rather than hand-merged. The 466 conflicting visual baselines took main's copies;
neither side is valid for a merged tree, and this branch opens no PR the visual
gate guards.
Verified after: no `undefined` in the built CSS, illustration tokens still emitted,
main's layout utilities present, body weights now 300, no token data file lost
entries, and 154 tests pass across toast, table, sidebar, resizable-panel and chip.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@gabriel-lisboa-azion@robsongajunior@herbert-julio-azion@guilherme-santana-azion@robson-junior-azion@isaque-bock-azion@rafael-garbinatto-azion
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(theme): add semantic/layouts — the container system - #884

Merged
isaque-bock-azion merged 9 commits into
mainfrom
feat/theme-layout-tokens
Aug 15, 2026
Merged

feat(theme): add semantic/layouts — the container system#884
isaque-bock-azion merged 9 commits into
mainfrom
feat/theme-layout-tokens

Conversation

@gabriel-lisboa-azion

Copy link
Copy Markdown
Collaborator

What

The layout system every console page is built on, as a theme token group: ten tokens and nine @utility classes, covering three decisions expressed once — how far content sits from the app chrome (boundary), how far things sit from each other (rhythm), and how wide a reading column may get (measure).

Four container types, picked by what the page is:

ClassTypeTodayFor
.layout-columnData1620pxLists, detail dashboards
.layout-column-focusedFocused1024pxHome, single-task multi-column pages
.layout-column-formForm1024pxSettings, in-page edit forms
.layout-form-createCreate1192pxCreate pages (also retunes --layout-measure-control)

Plus .layout-boundary / .layout-boundary-inline, the two rhythm steps (--layout-section-gap, --layout-group-gap) with their .layout-section-start / .layout-group-start margin forms, and .layout-field-control for the control side of a settings row.

A derived group

Every token is a var() reference to --spacing-* / --container-*, never a literal length, and that is why none of them carries a breakpoint map. The spacing scale is already fluid (--spacing-lg is 1rem, then 1.5rem from sm), and var() is substituted at use time on the element — so a layout token follows the breakpoint override for free. Giving it its own map would duplicate the spacing scale into layout and let the two drift.

Why @utility and not @layer components

Same reason the typography utilities are emitted that way: @layer components classes are opaque to v4's variant resolver, and their modifiers silently drop. A layout class is exactly the kind you want behind a variant — md:layout-column.

Two things worth a reviewer's eye

.layout-field-control carries a :not(#\#) specificity pin. It is applied to Item.Actions, whose own root already declares shrink-0, and Vue merges both class lists onto one element. Custom @utility blocks sort before the core utilities, so without the pin shrink-0 wins and the control side can no longer yield to a long field name. Measured, not assumed — .layout-field-control at byte 19079 vs .shrink-0 at 31336 in a built bundle. #\# is an id no element can carry, so the selector always matches while reading (1,1,0); it is Tailwind's own important-strategy idiom and stays variant-safe. Only this one of the nine has it, because only this one has a live conflict.

The boundary widening nests on the column, not the boundary. Specificity is (0,2,0) either way, so correctness does not decide it. Nesting on the column keeps both declarations that can set max-width in one block, keeps .layout-boundary to the three padding declarations its name promises, and keeps "a fifth column class costs one line" true — they are generated from a COLUMN_MEASURE map and the nested rule comes along.

Also in the diff

  • emitIllustrationUtilities-style emission generalized into emitUtilities(map) with a thin emitLayoutUtilities caller.
  • Regenerating dist picks up one line main was stale on: an emitted comment still reading bg-[var(--x)] where the source has said bg-(--x) since ENG-47001.
  • Docs: a new Foundations → Layout Storybook page (built from the token source, so it cannot drift), the Max width section of DESIGN.md, a Layout row in Get Started, and the derived-group note in the theme's token README. DESIGN.md ships here rather than separately because that section is the catalog for these tokens — documenting them before they exist would be the drift the rule guards against.

Verification

  • build:tokens clean, assertNoZeroWithUnit passing, theme suite 12/12.
  • git diff dist/v4/globals.css is additions only apart from the stale comment: 10 --layout-* lines in :root, no new @media lines (every token is a bare var()), 9 new @utility blocks.
  • Storybook builds; validate-story-source --all reports 91/91 compliant.

Ten tokens and nine `@utility` classes: how far content sits from the app
chrome (BOUNDARY), how far things sit from each other (RHYTHM), and how wide
a reading column may get (MEASURE).
A DERIVED group: every token is a var() reference to --spacing-* /
--container-*, never a literal length. That is why none carries a breakpoint
map — the spacing scale is already fluid and var() is substituted at use time
on the element, so a layout token follows the override for free. Duplicating
the scale into layout would only let the two drift.
Emitting them as `@utility` rather than `@layer components` is what gives
them variants (`md:layout-column`), the same reason the typography utilities
are emitted that way.
Generalizes the utility emitter into emitUtilities(map) with a thin
emitLayoutUtilities caller, so the shape is reusable.
Regenerating dist also picks up one line main was stale on: an emitted
comment that still read `bg-[var(--x)]` where the source has said `bg-(--x)`
since ENG-47001.
The catalog's token allowlists are scraped from the built theme, so adding
semantic/layouts adds ten --layout-* entries. Regenerated rather than
hand-edited, which is what catalog:check verifies.
isaque-bock-azion
isaque-bock-azion previously approved these changes Aug 14, 2026
isaque-bock-azionand others added 3 commits August 14, 2026 17:52
…lighter type scale
`Avatar › VariantGrid` failed the visual shard on `dark-desktop` only, at
1.0301% / 1354px against a 1% threshold — deterministically, byte-identical
across two runs.
The cause is not this branch. That baseline was last regenerated on
2026-07-22 by #768 and has missed two theme changes since: #889 (opaque
neutral border tokens) and #876 (headings and body steps to font-weight 300,
body leading 1.5 → 1.375). #876 regenerated 548 baselines across three
rounds and updated this story's `--dark-tablet` file, but never the
dark-desktop one — which alone keeps the bare story id instead of a `--mode`
suffix (see .storybook/visual-modes.js), so it is the easy file to miss in a
partial regeneration. Aligning baseline to render needs a zero shift
(dx=0, dy=0); what differs is ink — text -9%, icons -7% — the lighter
weight. This branch only adds tokens, and is simply the first PR to run the
suite with #876 merged in.
Regenerated via the app-storybook-generate-baseline workflow on linux. The
`-u` path could not rewrite it on its own: stale-baseline vs clean render is
0.9875%, under the threshold, so the snapshot passed and was left alone. The
file had to be deleted so it was written as a missing snapshot. Against the
render CI actually produced, the new baseline is 0.0426% (56px).
Still stale and left alone, since both currently pass: this story's
`--dark-mobile` baseline (also 2026-07-22). Worth noting separately that the
Avatar stories source their photo from a live Unsplash URL, which
contributed the residual noise here and is what kept this story on the 1%
boundary.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@isaque-bock-azion
isaque-bock-azion dismissed stale reviews from guilherme-santana-azion and themself via 3c9dc47August 14, 2026 21:26
@isaque-bock-azion
isaque-bock-azion merged commit 282f768 into mainAug 15, 2026
23 checks passed
@isaque-bock-azion
isaque-bock-azion deleted the feat/theme-layout-tokens branch August 15, 2026 22:36
gabriel-lisboa-azion added a commit that referenced this pull request Aug 17, 2026
Brings the three commits demo lacked, so the deployed sample exercises them
alongside the four fixes carved out of this branch (#899#902), whose content was
already here:
- feat(webkit): chip's three kinds (#883)
- feat(theme): the semantic/layouts container system (#884)
- feat(theme): lightened heading and body type (#876)
Conflict resolutions worth knowing:
`build-tokens.mjs` — main has no illustration tokens, so main's side of all six
hunks was empty. Taking it would have silently deleted this branch's illustration
wiring; ours was kept. The merge then produced a DUPLICATE `emitUtilities` and
`emitLayoutUtilities` with no conflict at all (both sides had added an identical
helper in different places), which is a syntax error the merge itself reported as
clean — the second copy is removed and `emitIllustrationUtilities` reuses the
first.
`texts.data.js` — the five conflicts were all `text-body-*` weight, resolved to
main's `light` since that is the change being previewed. Resolved in place rather
than with `--theirs`, which would have discarded the file's auto-merged hunks.
Entry count held at 144.
`.size-limit.json` — union, not a side: main's `chip` plus this branch's
`footer-root` and `resizable-panel-root`.
`dist/v4/globals.*` are generated, so they were rebuilt from the merged sources
rather than hand-merged. The 466 conflicting visual baselines took main's copies;
neither side is valid for a merged tree, and this branch opens no PR the visual
gate guards.
Verified after: no `undefined` in the built CSS, illustration tokens still emitted,
main's layout utilities present, body weights now 300, no token data file lost
entries, and 154 tests pass across toast, table, sidebar, resizable-panel and chip.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@gabriel-lisboa-azion@robsongajunior@herbert-julio-azion@guilherme-santana-azion@robson-junior-azion@isaque-bock-azion@rafael-garbinatto-azion
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

feat(theme): add semantic/layouts — the container system - #884

Merged
isaque-bock-azion merged 9 commits into
mainfrom
feat/theme-layout-tokens
Aug 15, 2026
Merged

feat(theme): add semantic/layouts — the container system#884
isaque-bock-azion merged 9 commits into
mainfrom
feat/theme-layout-tokens

Conversation

@gabriel-lisboa-azion

Copy link
Copy Markdown
Collaborator

What

The layout system every console page is built on, as a theme token group: ten tokens and nine @utility classes, covering three decisions expressed once — how far content sits from the app chrome (boundary), how far things sit from each other (rhythm), and how wide a reading column may get (measure).

Four container types, picked by what the page is:

ClassTypeTodayFor
.layout-columnData1620pxLists, detail dashboards
.layout-column-focusedFocused1024pxHome, single-task multi-column pages
.layout-column-formForm1024pxSettings, in-page edit forms
.layout-form-createCreate1192pxCreate pages (also retunes --layout-measure-control)

Plus .layout-boundary / .layout-boundary-inline, the two rhythm steps (--layout-section-gap, --layout-group-gap) with their .layout-section-start / .layout-group-start margin forms, and .layout-field-control for the control side of a settings row.

A derived group

Every token is a var() reference to --spacing-* / --container-*, never a literal length, and that is why none of them carries a breakpoint map. The spacing scale is already fluid (--spacing-lg is 1rem, then 1.5rem from sm), and var() is substituted at use time on the element — so a layout token follows the breakpoint override for free. Giving it its own map would duplicate the spacing scale into layout and let the two drift.

Why @utility and not @layer components

Same reason the typography utilities are emitted that way: @layer components classes are opaque to v4's variant resolver, and their modifiers silently drop. A layout class is exactly the kind you want behind a variant — md:layout-column.

Two things worth a reviewer's eye

.layout-field-control carries a :not(#\#) specificity pin. It is applied to Item.Actions, whose own root already declares shrink-0, and Vue merges both class lists onto one element. Custom @utility blocks sort before the core utilities, so without the pin shrink-0 wins and the control side can no longer yield to a long field name. Measured, not assumed — .layout-field-control at byte 19079 vs .shrink-0 at 31336 in a built bundle. #\# is an id no element can carry, so the selector always matches while reading (1,1,0); it is Tailwind's own important-strategy idiom and stays variant-safe. Only this one of the nine has it, because only this one has a live conflict.

The boundary widening nests on the column, not the boundary. Specificity is (0,2,0) either way, so correctness does not decide it. Nesting on the column keeps both declarations that can set max-width in one block, keeps .layout-boundary to the three padding declarations its name promises, and keeps "a fifth column class costs one line" true — they are generated from a COLUMN_MEASURE map and the nested rule comes along.

Also in the diff

  • emitIllustrationUtilities-style emission generalized into emitUtilities(map) with a thin emitLayoutUtilities caller.
  • Regenerating dist picks up one line main was stale on: an emitted comment still reading bg-[var(--x)] where the source has said bg-(--x) since ENG-47001.
  • Docs: a new Foundations → Layout Storybook page (built from the token source, so it cannot drift), the Max width section of DESIGN.md, a Layout row in Get Started, and the derived-group note in the theme's token README. DESIGN.md ships here rather than separately because that section is the catalog for these tokens — documenting them before they exist would be the drift the rule guards against.

Verification

  • build:tokens clean, assertNoZeroWithUnit passing, theme suite 12/12.
  • git diff dist/v4/globals.css is additions only apart from the stale comment: 10 --layout-* lines in :root, no new @media lines (every token is a bare var()), 9 new @utility blocks.
  • Storybook builds; validate-story-source --all reports 91/91 compliant.

Ten tokens and nine `@utility` classes: how far content sits from the app
chrome (BOUNDARY), how far things sit from each other (RHYTHM), and how wide
a reading column may get (MEASURE).
A DERIVED group: every token is a var() reference to --spacing-* /
--container-*, never a literal length. That is why none carries a breakpoint
map — the spacing scale is already fluid and var() is substituted at use time
on the element, so a layout token follows the override for free. Duplicating
the scale into layout would only let the two drift.
Emitting them as `@utility` rather than `@layer components` is what gives
them variants (`md:layout-column`), the same reason the typography utilities
are emitted that way.
Generalizes the utility emitter into emitUtilities(map) with a thin
emitLayoutUtilities caller, so the shape is reusable.
Regenerating dist also picks up one line main was stale on: an emitted
comment that still read `bg-[var(--x)]` where the source has said `bg-(--x)`
since ENG-47001.
The catalog's token allowlists are scraped from the built theme, so adding
semantic/layouts adds ten --layout-* entries. Regenerated rather than
hand-edited, which is what catalog:check verifies.
isaque-bock-azion
isaque-bock-azion previously approved these changes Aug 14, 2026
isaque-bock-azionand others added 3 commits August 14, 2026 17:52
…lighter type scale
`Avatar › VariantGrid` failed the visual shard on `dark-desktop` only, at
1.0301% / 1354px against a 1% threshold — deterministically, byte-identical
across two runs.
The cause is not this branch. That baseline was last regenerated on
2026-07-22 by #768 and has missed two theme changes since: #889 (opaque
neutral border tokens) and #876 (headings and body steps to font-weight 300,
body leading 1.5 → 1.375). #876 regenerated 548 baselines across three
rounds and updated this story's `--dark-tablet` file, but never the
dark-desktop one — which alone keeps the bare story id instead of a `--mode`
suffix (see .storybook/visual-modes.js), so it is the easy file to miss in a
partial regeneration. Aligning baseline to render needs a zero shift
(dx=0, dy=0); what differs is ink — text -9%, icons -7% — the lighter
weight. This branch only adds tokens, and is simply the first PR to run the
suite with #876 merged in.
Regenerated via the app-storybook-generate-baseline workflow on linux. The
`-u` path could not rewrite it on its own: stale-baseline vs clean render is
0.9875%, under the threshold, so the snapshot passed and was left alone. The
file had to be deleted so it was written as a missing snapshot. Against the
render CI actually produced, the new baseline is 0.0426% (56px).
Still stale and left alone, since both currently pass: this story's
`--dark-mobile` baseline (also 2026-07-22). Worth noting separately that the
Avatar stories source their photo from a live Unsplash URL, which
contributed the residual noise here and is what kept this story on the 1%
boundary.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@isaque-bock-azion
isaque-bock-azion dismissed stale reviews from guilherme-santana-azion and themself via 3c9dc47August 14, 2026 21:26
@isaque-bock-azion
isaque-bock-azion merged commit 282f768 into mainAug 15, 2026
23 checks passed
@isaque-bock-azion
isaque-bock-azion deleted the feat/theme-layout-tokens branch August 15, 2026 22:36
gabriel-lisboa-azion added a commit that referenced this pull request Aug 17, 2026
Brings the three commits demo lacked, so the deployed sample exercises them
alongside the four fixes carved out of this branch (#899#902), whose content was
already here:
- feat(webkit): chip's three kinds (#883)
- feat(theme): the semantic/layouts container system (#884)
- feat(theme): lightened heading and body type (#876)
Conflict resolutions worth knowing:
`build-tokens.mjs` — main has no illustration tokens, so main's side of all six
hunks was empty. Taking it would have silently deleted this branch's illustration
wiring; ours was kept. The merge then produced a DUPLICATE `emitUtilities` and
`emitLayoutUtilities` with no conflict at all (both sides had added an identical
helper in different places), which is a syntax error the merge itself reported as
clean — the second copy is removed and `emitIllustrationUtilities` reuses the
first.
`texts.data.js` — the five conflicts were all `text-body-*` weight, resolved to
main's `light` since that is the change being previewed. Resolved in place rather
than with `--theirs`, which would have discarded the file's auto-merged hunks.
Entry count held at 144.
`.size-limit.json` — union, not a side: main's `chip` plus this branch's
`footer-root` and `resizable-panel-root`.
`dist/v4/globals.*` are generated, so they were rebuilt from the merged sources
rather than hand-merged. The 466 conflicting visual baselines took main's copies;
neither side is valid for a merged tree, and this branch opens no PR the visual
gate guards.
Verified after: no `undefined` in the built CSS, illustration tokens still emitted,
main's layout utilities present, body weights now 300, no token data file lost
entries, and 154 tests pass across toast, table, sidebar, resizable-panel and chip.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@gabriel-lisboa-azion@robsongajunior@herbert-julio-azion@guilherme-santana-azion@robson-junior-azion@isaque-bock-azion@rafael-garbinatto-azion
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(theme): add semantic/layouts — the container system - #884

Merged
isaque-bock-azion merged 9 commits into
mainfrom
feat/theme-layout-tokens
Aug 15, 2026
Merged

feat(theme): add semantic/layouts — the container system#884
isaque-bock-azion merged 9 commits into
mainfrom
feat/theme-layout-tokens

Conversation

@gabriel-lisboa-azion

Copy link
Copy Markdown
Collaborator

What

The layout system every console page is built on, as a theme token group: ten tokens and nine @utility classes, covering three decisions expressed once — how far content sits from the app chrome (boundary), how far things sit from each other (rhythm), and how wide a reading column may get (measure).

Four container types, picked by what the page is:

ClassTypeTodayFor
.layout-columnData1620pxLists, detail dashboards
.layout-column-focusedFocused1024pxHome, single-task multi-column pages
.layout-column-formForm1024pxSettings, in-page edit forms
.layout-form-createCreate1192pxCreate pages (also retunes --layout-measure-control)

Plus .layout-boundary / .layout-boundary-inline, the two rhythm steps (--layout-section-gap, --layout-group-gap) with their .layout-section-start / .layout-group-start margin forms, and .layout-field-control for the control side of a settings row.

A derived group

Every token is a var() reference to --spacing-* / --container-*, never a literal length, and that is why none of them carries a breakpoint map. The spacing scale is already fluid (--spacing-lg is 1rem, then 1.5rem from sm), and var() is substituted at use time on the element — so a layout token follows the breakpoint override for free. Giving it its own map would duplicate the spacing scale into layout and let the two drift.

Why @utility and not @layer components

Same reason the typography utilities are emitted that way: @layer components classes are opaque to v4's variant resolver, and their modifiers silently drop. A layout class is exactly the kind you want behind a variant — md:layout-column.

Two things worth a reviewer's eye

.layout-field-control carries a :not(#\#) specificity pin. It is applied to Item.Actions, whose own root already declares shrink-0, and Vue merges both class lists onto one element. Custom @utility blocks sort before the core utilities, so without the pin shrink-0 wins and the control side can no longer yield to a long field name. Measured, not assumed — .layout-field-control at byte 19079 vs .shrink-0 at 31336 in a built bundle. #\# is an id no element can carry, so the selector always matches while reading (1,1,0); it is Tailwind's own important-strategy idiom and stays variant-safe. Only this one of the nine has it, because only this one has a live conflict.

The boundary widening nests on the column, not the boundary. Specificity is (0,2,0) either way, so correctness does not decide it. Nesting on the column keeps both declarations that can set max-width in one block, keeps .layout-boundary to the three padding declarations its name promises, and keeps "a fifth column class costs one line" true — they are generated from a COLUMN_MEASURE map and the nested rule comes along.

Also in the diff

  • emitIllustrationUtilities-style emission generalized into emitUtilities(map) with a thin emitLayoutUtilities caller.
  • Regenerating dist picks up one line main was stale on: an emitted comment still reading bg-[var(--x)] where the source has said bg-(--x) since ENG-47001.
  • Docs: a new Foundations → Layout Storybook page (built from the token source, so it cannot drift), the Max width section of DESIGN.md, a Layout row in Get Started, and the derived-group note in the theme's token README. DESIGN.md ships here rather than separately because that section is the catalog for these tokens — documenting them before they exist would be the drift the rule guards against.

Verification

  • build:tokens clean, assertNoZeroWithUnit passing, theme suite 12/12.
  • git diff dist/v4/globals.css is additions only apart from the stale comment: 10 --layout-* lines in :root, no new @media lines (every token is a bare var()), 9 new @utility blocks.
  • Storybook builds; validate-story-source --all reports 91/91 compliant.

Ten tokens and nine `@utility` classes: how far content sits from the app
chrome (BOUNDARY), how far things sit from each other (RHYTHM), and how wide
a reading column may get (MEASURE).
A DERIVED group: every token is a var() reference to --spacing-* /
--container-*, never a literal length. That is why none carries a breakpoint
map — the spacing scale is already fluid and var() is substituted at use time
on the element, so a layout token follows the override for free. Duplicating
the scale into layout would only let the two drift.
Emitting them as `@utility` rather than `@layer components` is what gives
them variants (`md:layout-column`), the same reason the typography utilities
are emitted that way.
Generalizes the utility emitter into emitUtilities(map) with a thin
emitLayoutUtilities caller, so the shape is reusable.
Regenerating dist also picks up one line main was stale on: an emitted
comment that still read `bg-[var(--x)]` where the source has said `bg-(--x)`
since ENG-47001.
The catalog's token allowlists are scraped from the built theme, so adding
semantic/layouts adds ten --layout-* entries. Regenerated rather than
hand-edited, which is what catalog:check verifies.
isaque-bock-azion
isaque-bock-azion previously approved these changes Aug 14, 2026
isaque-bock-azionand others added 3 commits August 14, 2026 17:52
…lighter type scale
`Avatar › VariantGrid` failed the visual shard on `dark-desktop` only, at
1.0301% / 1354px against a 1% threshold — deterministically, byte-identical
across two runs.
The cause is not this branch. That baseline was last regenerated on
2026-07-22 by #768 and has missed two theme changes since: #889 (opaque
neutral border tokens) and #876 (headings and body steps to font-weight 300,
body leading 1.5 → 1.375). #876 regenerated 548 baselines across three
rounds and updated this story's `--dark-tablet` file, but never the
dark-desktop one — which alone keeps the bare story id instead of a `--mode`
suffix (see .storybook/visual-modes.js), so it is the easy file to miss in a
partial regeneration. Aligning baseline to render needs a zero shift
(dx=0, dy=0); what differs is ink — text -9%, icons -7% — the lighter
weight. This branch only adds tokens, and is simply the first PR to run the
suite with #876 merged in.
Regenerated via the app-storybook-generate-baseline workflow on linux. The
`-u` path could not rewrite it on its own: stale-baseline vs clean render is
0.9875%, under the threshold, so the snapshot passed and was left alone. The
file had to be deleted so it was written as a missing snapshot. Against the
render CI actually produced, the new baseline is 0.0426% (56px).
Still stale and left alone, since both currently pass: this story's
`--dark-mobile` baseline (also 2026-07-22). Worth noting separately that the
Avatar stories source their photo from a live Unsplash URL, which
contributed the residual noise here and is what kept this story on the 1%
boundary.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@isaque-bock-azion
isaque-bock-azion dismissed stale reviews from guilherme-santana-azion and themself via 3c9dc47August 14, 2026 21:26
@isaque-bock-azion
isaque-bock-azion merged commit 282f768 into mainAug 15, 2026
23 checks passed
@isaque-bock-azion
isaque-bock-azion deleted the feat/theme-layout-tokens branch August 15, 2026 22:36
gabriel-lisboa-azion added a commit that referenced this pull request Aug 17, 2026
Brings the three commits demo lacked, so the deployed sample exercises them
alongside the four fixes carved out of this branch (#899#902), whose content was
already here:
- feat(webkit): chip's three kinds (#883)
- feat(theme): the semantic/layouts container system (#884)
- feat(theme): lightened heading and body type (#876)
Conflict resolutions worth knowing:
`build-tokens.mjs` — main has no illustration tokens, so main's side of all six
hunks was empty. Taking it would have silently deleted this branch's illustration
wiring; ours was kept. The merge then produced a DUPLICATE `emitUtilities` and
`emitLayoutUtilities` with no conflict at all (both sides had added an identical
helper in different places), which is a syntax error the merge itself reported as
clean — the second copy is removed and `emitIllustrationUtilities` reuses the
first.
`texts.data.js` — the five conflicts were all `text-body-*` weight, resolved to
main's `light` since that is the change being previewed. Resolved in place rather
than with `--theirs`, which would have discarded the file's auto-merged hunks.
Entry count held at 144.
`.size-limit.json` — union, not a side: main's `chip` plus this branch's
`footer-root` and `resizable-panel-root`.
`dist/v4/globals.*` are generated, so they were rebuilt from the merged sources
rather than hand-merged. The 466 conflicting visual baselines took main's copies;
neither side is valid for a merged tree, and this branch opens no PR the visual
gate guards.
Verified after: no `undefined` in the built CSS, illustration tokens still emitted,
main's layout utilities present, body weights now 300, no token data file lost
entries, and 154 tests pass across toast, table, sidebar, resizable-panel and chip.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@gabriel-lisboa-azion@robsongajunior@herbert-julio-azion@guilherme-santana-azion@robson-junior-azion@isaque-bock-azion@rafael-garbinatto-azion
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(theme): add semantic/layouts — the container system - #884

Merged
isaque-bock-azion merged 9 commits into
mainfrom
feat/theme-layout-tokens
Aug 15, 2026
Merged

feat(theme): add semantic/layouts — the container system#884
isaque-bock-azion merged 9 commits into
mainfrom
feat/theme-layout-tokens

Conversation

@gabriel-lisboa-azion

Copy link
Copy Markdown
Collaborator

What

The layout system every console page is built on, as a theme token group: ten tokens and nine @utility classes, covering three decisions expressed once — how far content sits from the app chrome (boundary), how far things sit from each other (rhythm), and how wide a reading column may get (measure).

Four container types, picked by what the page is:

ClassTypeTodayFor
.layout-columnData1620pxLists, detail dashboards
.layout-column-focusedFocused1024pxHome, single-task multi-column pages
.layout-column-formForm1024pxSettings, in-page edit forms
.layout-form-createCreate1192pxCreate pages (also retunes --layout-measure-control)

Plus .layout-boundary / .layout-boundary-inline, the two rhythm steps (--layout-section-gap, --layout-group-gap) with their .layout-section-start / .layout-group-start margin forms, and .layout-field-control for the control side of a settings row.

A derived group

Every token is a var() reference to --spacing-* / --container-*, never a literal length, and that is why none of them carries a breakpoint map. The spacing scale is already fluid (--spacing-lg is 1rem, then 1.5rem from sm), and var() is substituted at use time on the element — so a layout token follows the breakpoint override for free. Giving it its own map would duplicate the spacing scale into layout and let the two drift.

Why @utility and not @layer components

Same reason the typography utilities are emitted that way: @layer components classes are opaque to v4's variant resolver, and their modifiers silently drop. A layout class is exactly the kind you want behind a variant — md:layout-column.

Two things worth a reviewer's eye

.layout-field-control carries a :not(#\#) specificity pin. It is applied to Item.Actions, whose own root already declares shrink-0, and Vue merges both class lists onto one element. Custom @utility blocks sort before the core utilities, so without the pin shrink-0 wins and the control side can no longer yield to a long field name. Measured, not assumed — .layout-field-control at byte 19079 vs .shrink-0 at 31336 in a built bundle. #\# is an id no element can carry, so the selector always matches while reading (1,1,0); it is Tailwind's own important-strategy idiom and stays variant-safe. Only this one of the nine has it, because only this one has a live conflict.

The boundary widening nests on the column, not the boundary. Specificity is (0,2,0) either way, so correctness does not decide it. Nesting on the column keeps both declarations that can set max-width in one block, keeps .layout-boundary to the three padding declarations its name promises, and keeps "a fifth column class costs one line" true — they are generated from a COLUMN_MEASURE map and the nested rule comes along.

Also in the diff

  • emitIllustrationUtilities-style emission generalized into emitUtilities(map) with a thin emitLayoutUtilities caller.
  • Regenerating dist picks up one line main was stale on: an emitted comment still reading bg-[var(--x)] where the source has said bg-(--x) since ENG-47001.
  • Docs: a new Foundations → Layout Storybook page (built from the token source, so it cannot drift), the Max width section of DESIGN.md, a Layout row in Get Started, and the derived-group note in the theme's token README. DESIGN.md ships here rather than separately because that section is the catalog for these tokens — documenting them before they exist would be the drift the rule guards against.

Verification

  • build:tokens clean, assertNoZeroWithUnit passing, theme suite 12/12.
  • git diff dist/v4/globals.css is additions only apart from the stale comment: 10 --layout-* lines in :root, no new @media lines (every token is a bare var()), 9 new @utility blocks.
  • Storybook builds; validate-story-source --all reports 91/91 compliant.

Ten tokens and nine `@utility` classes: how far content sits from the app
chrome (BOUNDARY), how far things sit from each other (RHYTHM), and how wide
a reading column may get (MEASURE).
A DERIVED group: every token is a var() reference to --spacing-* /
--container-*, never a literal length. That is why none carries a breakpoint
map — the spacing scale is already fluid and var() is substituted at use time
on the element, so a layout token follows the override for free. Duplicating
the scale into layout would only let the two drift.
Emitting them as `@utility` rather than `@layer components` is what gives
them variants (`md:layout-column`), the same reason the typography utilities
are emitted that way.
Generalizes the utility emitter into emitUtilities(map) with a thin
emitLayoutUtilities caller, so the shape is reusable.
Regenerating dist also picks up one line main was stale on: an emitted
comment that still read `bg-[var(--x)]` where the source has said `bg-(--x)`
since ENG-47001.
The catalog's token allowlists are scraped from the built theme, so adding
semantic/layouts adds ten --layout-* entries. Regenerated rather than
hand-edited, which is what catalog:check verifies.
isaque-bock-azion
isaque-bock-azion previously approved these changes Aug 14, 2026
isaque-bock-azionand others added 3 commits August 14, 2026 17:52
…lighter type scale
`Avatar › VariantGrid` failed the visual shard on `dark-desktop` only, at
1.0301% / 1354px against a 1% threshold — deterministically, byte-identical
across two runs.
The cause is not this branch. That baseline was last regenerated on
2026-07-22 by #768 and has missed two theme changes since: #889 (opaque
neutral border tokens) and #876 (headings and body steps to font-weight 300,
body leading 1.5 → 1.375). #876 regenerated 548 baselines across three
rounds and updated this story's `--dark-tablet` file, but never the
dark-desktop one — which alone keeps the bare story id instead of a `--mode`
suffix (see .storybook/visual-modes.js), so it is the easy file to miss in a
partial regeneration. Aligning baseline to render needs a zero shift
(dx=0, dy=0); what differs is ink — text -9%, icons -7% — the lighter
weight. This branch only adds tokens, and is simply the first PR to run the
suite with #876 merged in.
Regenerated via the app-storybook-generate-baseline workflow on linux. The
`-u` path could not rewrite it on its own: stale-baseline vs clean render is
0.9875%, under the threshold, so the snapshot passed and was left alone. The
file had to be deleted so it was written as a missing snapshot. Against the
render CI actually produced, the new baseline is 0.0426% (56px).
Still stale and left alone, since both currently pass: this story's
`--dark-mobile` baseline (also 2026-07-22). Worth noting separately that the
Avatar stories source their photo from a live Unsplash URL, which
contributed the residual noise here and is what kept this story on the 1%
boundary.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@isaque-bock-azion
isaque-bock-azion dismissed stale reviews from guilherme-santana-azion and themself via 3c9dc47August 14, 2026 21:26
@isaque-bock-azion
isaque-bock-azion merged commit 282f768 into mainAug 15, 2026
23 checks passed
@isaque-bock-azion
isaque-bock-azion deleted the feat/theme-layout-tokens branch August 15, 2026 22:36
gabriel-lisboa-azion added a commit that referenced this pull request Aug 17, 2026
Brings the three commits demo lacked, so the deployed sample exercises them
alongside the four fixes carved out of this branch (#899#902), whose content was
already here:
- feat(webkit): chip's three kinds (#883)
- feat(theme): the semantic/layouts container system (#884)
- feat(theme): lightened heading and body type (#876)
Conflict resolutions worth knowing:
`build-tokens.mjs` — main has no illustration tokens, so main's side of all six
hunks was empty. Taking it would have silently deleted this branch's illustration
wiring; ours was kept. The merge then produced a DUPLICATE `emitUtilities` and
`emitLayoutUtilities` with no conflict at all (both sides had added an identical
helper in different places), which is a syntax error the merge itself reported as
clean — the second copy is removed and `emitIllustrationUtilities` reuses the
first.
`texts.data.js` — the five conflicts were all `text-body-*` weight, resolved to
main's `light` since that is the change being previewed. Resolved in place rather
than with `--theirs`, which would have discarded the file's auto-merged hunks.
Entry count held at 144.
`.size-limit.json` — union, not a side: main's `chip` plus this branch's
`footer-root` and `resizable-panel-root`.
`dist/v4/globals.*` are generated, so they were rebuilt from the merged sources
rather than hand-merged. The 466 conflicting visual baselines took main's copies;
neither side is valid for a merged tree, and this branch opens no PR the visual
gate guards.
Verified after: no `undefined` in the built CSS, illustration tokens still emitted,
main's layout utilities present, body weights now 300, no token data file lost
entries, and 154 tests pass across toast, table, sidebar, resizable-panel and chip.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@gabriel-lisboa-azion@robsongajunior@herbert-julio-azion@guilherme-santana-azion@robson-junior-azion@isaque-bock-azion@rafael-garbinatto-azion
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

feat(theme): add semantic/layouts — the container system - #884

Merged
isaque-bock-azion merged 9 commits into
mainfrom
feat/theme-layout-tokens
Aug 15, 2026
Merged

feat(theme): add semantic/layouts — the container system#884
isaque-bock-azion merged 9 commits into
mainfrom
feat/theme-layout-tokens

Conversation

@gabriel-lisboa-azion

Copy link
Copy Markdown
Collaborator

What

The layout system every console page is built on, as a theme token group: ten tokens and nine @utility classes, covering three decisions expressed once — how far content sits from the app chrome (boundary), how far things sit from each other (rhythm), and how wide a reading column may get (measure).

Four container types, picked by what the page is:

ClassTypeTodayFor
.layout-columnData1620pxLists, detail dashboards
.layout-column-focusedFocused1024pxHome, single-task multi-column pages
.layout-column-formForm1024pxSettings, in-page edit forms
.layout-form-createCreate1192pxCreate pages (also retunes --layout-measure-control)

Plus .layout-boundary / .layout-boundary-inline, the two rhythm steps (--layout-section-gap, --layout-group-gap) with their .layout-section-start / .layout-group-start margin forms, and .layout-field-control for the control side of a settings row.

A derived group

Every token is a var() reference to --spacing-* / --container-*, never a literal length, and that is why none of them carries a breakpoint map. The spacing scale is already fluid (--spacing-lg is 1rem, then 1.5rem from sm), and var() is substituted at use time on the element — so a layout token follows the breakpoint override for free. Giving it its own map would duplicate the spacing scale into layout and let the two drift.

Why @utility and not @layer components

Same reason the typography utilities are emitted that way: @layer components classes are opaque to v4's variant resolver, and their modifiers silently drop. A layout class is exactly the kind you want behind a variant — md:layout-column.

Two things worth a reviewer's eye

.layout-field-control carries a :not(#\#) specificity pin. It is applied to Item.Actions, whose own root already declares shrink-0, and Vue merges both class lists onto one element. Custom @utility blocks sort before the core utilities, so without the pin shrink-0 wins and the control side can no longer yield to a long field name. Measured, not assumed — .layout-field-control at byte 19079 vs .shrink-0 at 31336 in a built bundle. #\# is an id no element can carry, so the selector always matches while reading (1,1,0); it is Tailwind's own important-strategy idiom and stays variant-safe. Only this one of the nine has it, because only this one has a live conflict.

The boundary widening nests on the column, not the boundary. Specificity is (0,2,0) either way, so correctness does not decide it. Nesting on the column keeps both declarations that can set max-width in one block, keeps .layout-boundary to the three padding declarations its name promises, and keeps "a fifth column class costs one line" true — they are generated from a COLUMN_MEASURE map and the nested rule comes along.

Also in the diff

  • emitIllustrationUtilities-style emission generalized into emitUtilities(map) with a thin emitLayoutUtilities caller.
  • Regenerating dist picks up one line main was stale on: an emitted comment still reading bg-[var(--x)] where the source has said bg-(--x) since ENG-47001.
  • Docs: a new Foundations → Layout Storybook page (built from the token source, so it cannot drift), the Max width section of DESIGN.md, a Layout row in Get Started, and the derived-group note in the theme's token README. DESIGN.md ships here rather than separately because that section is the catalog for these tokens — documenting them before they exist would be the drift the rule guards against.

Verification

  • build:tokens clean, assertNoZeroWithUnit passing, theme suite 12/12.
  • git diff dist/v4/globals.css is additions only apart from the stale comment: 10 --layout-* lines in :root, no new @media lines (every token is a bare var()), 9 new @utility blocks.
  • Storybook builds; validate-story-source --all reports 91/91 compliant.

Ten tokens and nine `@utility` classes: how far content sits from the app
chrome (BOUNDARY), how far things sit from each other (RHYTHM), and how wide
a reading column may get (MEASURE).
A DERIVED group: every token is a var() reference to --spacing-* /
--container-*, never a literal length. That is why none carries a breakpoint
map — the spacing scale is already fluid and var() is substituted at use time
on the element, so a layout token follows the override for free. Duplicating
the scale into layout would only let the two drift.
Emitting them as `@utility` rather than `@layer components` is what gives
them variants (`md:layout-column`), the same reason the typography utilities
are emitted that way.
Generalizes the utility emitter into emitUtilities(map) with a thin
emitLayoutUtilities caller, so the shape is reusable.
Regenerating dist also picks up one line main was stale on: an emitted
comment that still read `bg-[var(--x)]` where the source has said `bg-(--x)`
since ENG-47001.
The catalog's token allowlists are scraped from the built theme, so adding
semantic/layouts adds ten --layout-* entries. Regenerated rather than
hand-edited, which is what catalog:check verifies.
isaque-bock-azion
isaque-bock-azion previously approved these changes Aug 14, 2026
isaque-bock-azionand others added 3 commits August 14, 2026 17:52
…lighter type scale
`Avatar › VariantGrid` failed the visual shard on `dark-desktop` only, at
1.0301% / 1354px against a 1% threshold — deterministically, byte-identical
across two runs.
The cause is not this branch. That baseline was last regenerated on
2026-07-22 by #768 and has missed two theme changes since: #889 (opaque
neutral border tokens) and #876 (headings and body steps to font-weight 300,
body leading 1.5 → 1.375). #876 regenerated 548 baselines across three
rounds and updated this story's `--dark-tablet` file, but never the
dark-desktop one — which alone keeps the bare story id instead of a `--mode`
suffix (see .storybook/visual-modes.js), so it is the easy file to miss in a
partial regeneration. Aligning baseline to render needs a zero shift
(dx=0, dy=0); what differs is ink — text -9%, icons -7% — the lighter
weight. This branch only adds tokens, and is simply the first PR to run the
suite with #876 merged in.
Regenerated via the app-storybook-generate-baseline workflow on linux. The
`-u` path could not rewrite it on its own: stale-baseline vs clean render is
0.9875%, under the threshold, so the snapshot passed and was left alone. The
file had to be deleted so it was written as a missing snapshot. Against the
render CI actually produced, the new baseline is 0.0426% (56px).
Still stale and left alone, since both currently pass: this story's
`--dark-mobile` baseline (also 2026-07-22). Worth noting separately that the
Avatar stories source their photo from a live Unsplash URL, which
contributed the residual noise here and is what kept this story on the 1%
boundary.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@isaque-bock-azion
isaque-bock-azion dismissed stale reviews from guilherme-santana-azion and themself via 3c9dc47August 14, 2026 21:26
@isaque-bock-azion
isaque-bock-azion merged commit 282f768 into mainAug 15, 2026
23 checks passed
@isaque-bock-azion
isaque-bock-azion deleted the feat/theme-layout-tokens branch August 15, 2026 22:36
gabriel-lisboa-azion added a commit that referenced this pull request Aug 17, 2026
Brings the three commits demo lacked, so the deployed sample exercises them
alongside the four fixes carved out of this branch (#899#902), whose content was
already here:
- feat(webkit): chip's three kinds (#883)
- feat(theme): the semantic/layouts container system (#884)
- feat(theme): lightened heading and body type (#876)
Conflict resolutions worth knowing:
`build-tokens.mjs` — main has no illustration tokens, so main's side of all six
hunks was empty. Taking it would have silently deleted this branch's illustration
wiring; ours was kept. The merge then produced a DUPLICATE `emitUtilities` and
`emitLayoutUtilities` with no conflict at all (both sides had added an identical
helper in different places), which is a syntax error the merge itself reported as
clean — the second copy is removed and `emitIllustrationUtilities` reuses the
first.
`texts.data.js` — the five conflicts were all `text-body-*` weight, resolved to
main's `light` since that is the change being previewed. Resolved in place rather
than with `--theirs`, which would have discarded the file's auto-merged hunks.
Entry count held at 144.
`.size-limit.json` — union, not a side: main's `chip` plus this branch's
`footer-root` and `resizable-panel-root`.
`dist/v4/globals.*` are generated, so they were rebuilt from the merged sources
rather than hand-merged. The 466 conflicting visual baselines took main's copies;
neither side is valid for a merged tree, and this branch opens no PR the visual
gate guards.
Verified after: no `undefined` in the built CSS, illustration tokens still emitted,
main's layout utilities present, body weights now 300, no token data file lost
entries, and 154 tests pass across toast, table, sidebar, resizable-panel and chip.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@gabriel-lisboa-azion@robsongajunior@herbert-julio-azion@guilherme-santana-azion@robson-junior-azion@isaque-bock-azion@rafael-garbinatto-azion