Skip to content

Density tokens for control sizes, and a compact preset via data-density="compact" - #5

Merged
dobexx merged 1 commit into
mainfrom
feature/density-tokens
Oct 5, 2026
Merged

dobexx merged 1 commit into
mainfrom
feature/density-tokens

Conversation

@dobexx

@dobexx dobexx commented Oct 5, 2026 •

Copy link
Copy Markdown
Member

Summary

Control heights were px literals, so a denser layout (admin screens, desktop UIs, narrow forms) meant overriding a dozen selectors and keeping the toggle's thumb, its travel and the first-line alignment in step by hand. This PR turns the control sizes into tokens, with today's values as defaults, so nothing changes unless a page opts in, and adds a compact preset:

<html data-theme="dark" data-density="compact">

Tokens

Token Default Compact
--gl-control-height (input, select, search) 52px 40px
--gl-control-padding-x (input, select, textarea) unset → var(--gl-space-md) var(--gl-space-sm)
--gl-btn-height / -sm / -lg 56px / 44px / 64px 40px / 32px / 48px
--gl-textarea-min-height 120px 88px
--gl-list-item-min-height 56px 48px
--gl-list-item-padding-y unset → var(--gl-space-md) 10px
--gl-toggle-width × --gl-toggle-height 52px × 30px 44px × 26px
--gl-toggle-thumb-size 22px 18px
--gl-check-size (checkbox, radio) 24px 20px
--gl-modal-action-height 52px 44px
  • Declared in the :root, [data-theme="dark"] block and read with today's value as fallback, the convention 1.20.0 uses for --gl-modal-max-width and --gl-select-chevron. An older token sheet next to this componentsSheet, or GlassKit Elements with data-glk-defaults="off" and no stylesheet, keeps the old sizes.
  • The toggle thumb sits (height − thumb) / 2 − 1px inside the track (3px at 52 × 30, as before) and travels width − height, from the right and to the left under :dir(rtl). The invisible inputs that lie on top of the controls since 1.20.0 take the same sizes, and the first-line margins of toggle, checkbox and radio use them, so any size stays aligned.
  • The two padding tokens are left unset, like --gl-toast-top: the rules fall back to var(--gl-space-md), so a spacing override declared anywhere still reaches fields and list rows exactly as before. The select keeps its :dir(rtl) padding pair; the token takes the text side in both directions, the chevron's room stays with the chevron.
  • --gl-list-item-padding-y makes the compact list work: with 16px top and bottom padding a plain row is about 50px, so a 48px minimum would never show.
  • Unchanged: select chevron, search icon offset, checkbox tick, radio dot.

Preset and stylesheet split

[data-density="compact"] is a bare attribute selector (like [data-theme="light"]) after the theme blocks. build-styles-js.mjs splits it into tokensCss / tokensSheet with the theme blocks and fails if it is missing there or if componentsCss declares a density token. GlassKit Elements adopts only componentsSheet in its shadow roots and puts tokensCss on the document, so the compact values reach every element by inheritance, and so does a project's own [data-density="compact"] { --gl-btn-height: 36px; }. Inside componentsSheet the preset would re-declare the tokens in every shadow root and block such overrides. A glassSheet consumer with a data-theme element inside its shadow root mirrors data-density onto it (SKILL.md); the companion GlassKit-Elements PR (JUNGHERZ/GlassKit-Elements#10) does that for its theme wrapper.

Compatibility

  • Default rendering is unchanged (see below).
  • Spacing overrides on :root or a subtree reach field and list-row padding as before.
  • Like every token of the dark block, the size defaults are re-declared by a nested data-theme="dark" element; keep data-density on <html>.

Verification (Playwright 1.63, Chromium and WebKit, 1.20.0 vs. this branch)

  • Without the attribute, dark and light, left to right and right to left: identical size, padding, chevron position, thumb position, input box, label offset and first-line centre for input, select, textarea, search, buttons (md/sm/lg), list rows, toggle (on/off), checkbox, radio and modal action. A test page is pixel-identical; index, showcase and docs (EN/DE) are pixel-identical apart from regions that also differ between two renders of 1.20.0.
  • With data-density="compact" (at load and toggled at runtime): fields 40px with 12px inner padding, buttons 40/32/48, textarea 88, list rows 48 with or without a 28px icon, toggle 44×26 with an 18px thumb and equal end gaps in both directions, checkbox/radio 20 with their inputs exactly on top, modal actions 44. Under dir="rtl" the select's 12px move right with the chevron on the left; labels stay centred on their first line; removing the attribute restores every value.
  • npm run build, npm run check:versions and npm run check:package pass.

Docs

README (Theming → Density, token list, cheat sheet), SKILL.md (density section after Right to Left, token table, rules, tokensSheet, shadow-DOM pitfall), docs.html and de/docs.html (token table, Density section with live preview), showcase.html and de/showcase.html (density switch), theme-override.css (example), CHANGELOG (Unreleased).

Release notes for the maintainer

The heights of fields, buttons, toggles, checkboxes, radios, list rows
and modal actions were px literals in the component rules. A denser
admin or desktop layout had to override a dozen selectors and keep the
toggle's thumb, its travel and the first-line alignment in step by hand.

They are tokens now, at the values GlassKit has always drawn and read
with that value as fallback, as --gl-modal-max-width is:
--gl-control-height, --gl-btn-height / -sm / -lg,
--gl-textarea-min-height, --gl-list-item-min-height, --gl-toggle-width,
--gl-toggle-height, --gl-toggle-thumb-size, --gl-check-size and
--gl-modal-action-height. The thumb's inset ((height - thumb) / 2 - 1px,
3px as before) and travel (width - height) in both directions, the
invisible inputs on top of the controls and the first-line margins of
toggle, checkbox and radio are derived from them.
--gl-control-padding-x and --gl-list-item-padding-y stay unset and fall
back to --gl-space-md, like --gl-toast-top, so a spacing override still
reaches fields and list rows wherever it is declared. The select keeps
its :dir(rtl) padding pair, with the token on the text side.

[data-density="compact"], after the theme blocks, sets 40px fields and
buttons (sm 32, lg 48), 12px inside the fields, an 88px textarea, 48px
list rows with 10px padding, a 44 x 26 toggle with an 18px thumb, 20px
checkbox and radio, 44px modal actions. build-styles-js moves it into
tokensCss with the theme blocks, so GlassKit Elements gets the values
by inheritance and a project's own density values reach its shadow
roots too.

Measured in Chromium and WebKit against 1.20.0, left to right and right
to left: without the attribute every control measures as before, and
full-page screenshots of a test page in both themes and of index,
showcase and docs (EN/DE) are pixel-identical outside regions that
differ between any two renders; with it, the compact values at load
and switched at runtime, the select's padding and chevron and the
toggle's thumb mirrored, every label still centred on its first line.

Docs and showcase (EN/DE), SKILL.md, README, theme-override.css.
@dobexx
dobexx merged commit c94bfd4 into main Oct 5, 2026
2 checks passed
dobexx added a commit that referenced this pull request Oct 5, 2026
Releases pull request #5: the control sizes of fields, buttons, toggle,
checkbox, radio, list rows and modal actions are tokens, read with the
old values as fallback, and data-density="compact" on <html> swaps in a
denser set. Without the attribute every page renders pixel-identical
to 1.20.0 (docs, showcase, index in English and German, both themes,
left to right and right to left; Chromium and WebKit).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant