From c0fd0052823844d2af8d7acd330c85954ee22812 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Sat, 17 Oct 2026 22:07:11 +1100 Subject: [PATCH] Replace T-shirt sizes with literal px scales (#859) * Replace T-shirt size keys with curated px literal scales Introduce literalScale so fixed measurements derive both the key and rem value from the same numbers, then migrate control, icon, indicator, and Container size APIs to px* keys with no geometry changes. Co-authored-by: Luke Bennett * Fix font-size preference selector and derive controlSize from literalScale Restore data-font-size="small" in the reference app (unrelated to component sizing), cover Small and Large text-size geometry in the browser test, and build CONTROL_SIZE_VALUES from literalScale so keys cannot drift from rem. Co-authored-by: Luke Bennett --------- Co-authored-by: Cursor Agent --- .../docs/components/actions/button.mdx | 4 +- .../docs/components/actions/icon-button.mdx | 2 +- .../docs/components/actions/icon-link.mdx | 2 +- .../components/feedback/loading-spinner.mdx | 2 +- .../docs/components/forms/checkbox-field.mdx | 4 +- .../docs/components/forms/combobox-field.mdx | 2 +- .../docs/components/forms/select-field.mdx | 2 +- .../docs/components/forms/switch-field.mdx | 2 +- .../components/forms/text-input-field.mdx | 2 +- .../docs/components/layout/container.mdx | 6 +-- .../docs/components/primitives/select.mdx | 4 +- .../docs/components/primitives/text-input.mdx | 6 +-- apps/docs/content/docs/docs/iconography.mdx | 6 +-- apps/docs/content/docs/docs/styling.mdx | 2 +- .../components/code-block/code-block.css.ts | 4 +- .../src/components/code-block/code-block.tsx | 2 +- .../src/components/component-props-table.tsx | 2 +- .../components/docs-article/docs-pager.tsx | 2 +- .../src/components/docs-article/docs-toc.tsx | 2 +- .../components/docs-article/mdx-elements.tsx | 2 +- apps/docs/src/components/docs-site-nav.tsx | 4 +- apps/docs/src/components/example-block.tsx | 9 +---- .../src/components/example-code-preview.tsx | 2 +- apps/docs/src/components/icon-gallery.tsx | 16 ++++---- apps/docs/src/components/motion-tokens.tsx | 2 +- apps/docs/src/components/not-found.tsx | 2 +- apps/docs/src/components/page-actions.tsx | 4 +- .../components/playground/editor-skeleton.tsx | 2 +- .../docs/src/components/playground/editor.tsx | 2 +- .../playground/icon-toggle-button-group.tsx | 4 +- .../components/playground/playground.css.ts | 2 +- .../components/playground/preview-toolbar.tsx | 2 +- apps/docs/src/components/search-dialog.tsx | 6 +-- apps/docs/src/components/search.tsx | 4 +- apps/docs/src/components/site-nav.tsx | 4 +- .../src/components/token-reference-parts.tsx | 2 +- apps/docs/src/components/token-scales.tsx | 4 +- apps/docs/src/components/type-popover.tsx | 2 +- .../docs/src/components/typography-tokens.tsx | 2 +- apps/docs/src/examples/aspect-ratio/basic.tsx | 2 +- .../aspect-ratio/semantic-element.tsx | 2 +- apps/docs/src/examples/button/sizes.tsx | 8 ++-- .../src/examples/checkbox-field/sizes.tsx | 12 +++--- .../src/examples/combobox-field/sizes.tsx | 8 ++-- apps/docs/src/examples/container/basic.tsx | 2 +- .../examples/container/semantic-element.tsx | 2 +- apps/docs/src/examples/icon-button/size.tsx | 8 ++-- apps/docs/src/examples/icon-link/size.tsx | 8 ++-- apps/docs/src/examples/icon/custom.tsx | 2 +- apps/docs/src/examples/icon/decorative.tsx | 2 +- apps/docs/src/examples/icon/size-context.tsx | 2 +- apps/docs/src/examples/icon/sizes.tsx | 16 ++++---- .../src/examples/loading-spinner/sizes.tsx | 16 ++++---- apps/docs/src/examples/lockup/basic.tsx | 2 +- apps/docs/src/examples/lockup/interactive.tsx | 2 +- apps/docs/src/examples/select-field/sizes.tsx | 8 ++-- apps/docs/src/examples/switch-field/sizes.tsx | 12 +++--- .../text-input-field/prefix-and-suffix.tsx | 2 +- .../src/examples/text-input-field/sizes.tsx | 8 ++-- apps/docs/src/routes/index.tsx | 2 +- .../src/samples/styling/component-props.tsx | 2 +- apps/reference-app/src/app.browser.test.tsx | 16 +++++++- .../src/components/change-email-dialog.tsx | 4 +- .../src/components/profile-avatar-section.tsx | 8 ++-- .../src/components/profile-text-field.tsx | 2 +- .../src/components/settings-nav.tsx | 6 +-- apps/reference-app/src/routes/app-routes.tsx | 2 +- apps/reference-app/src/routes/home.tsx | 2 +- apps/reference-app/src/routes/preferences.tsx | 2 +- .../src/routes/settings-layout.tsx | 6 +-- docs/STYLING.md | 30 +++++++++++++- .../react/src/core/action-presentation.ts | 16 ++++---- .../src/core/button/button.browser.test.tsx | 2 +- .../react/src/core/button/button.test.ts | 2 +- .../react/src/core/button/recipe.css.ts | 2 +- .../checkbox-field.browser.test.tsx | 4 +- .../combobox-field.browser.test.tsx | 12 +++--- .../core/combobox-field/combobox-field.tsx | 8 ++-- .../core/container/container.browser.test.tsx | 22 +++++----- .../src/core/container/container.test.ts | 6 +-- .../react/src/core/container/recipe.css.ts | 22 +++++----- .../react/src/core/grid/grid.browser.test.tsx | 2 +- .../icon-button/icon-button.browser.test.tsx | 8 ++-- .../src/core/icon-button/icon-button.tsx | 2 +- .../react/src/core/icon-button/recipe.css.ts | 8 ++-- .../react/src/core/icon-button/styles.css.ts | 4 +- .../core/icon-link/icon-link.browser.test.tsx | 10 ++--- .../react/src/core/icon-link/icon-link.tsx | 2 +- .../react/src/core/icon/icon.browser.test.tsx | 2 +- .../@luke-ui/react/src/core/icon/icon.tsx | 4 +- .../react/src/core/icon/recipe.css.ts | 26 ++++++------ .../@luke-ui/react/src/core/link/link.test.ts | 2 +- .../react/src/core/link/recipe.css.ts | 2 +- .../loading-spinner.browser.test.tsx | 2 +- .../core/loading-spinner/loading-spinner.tsx | 4 +- .../src/core/loading-spinner/recipe.css.ts | 10 ++--- .../src/core/packed-consumer/react-fixture.ts | 4 +- .../react/src/core/packed-consumer/setup.ts | 2 + .../src/core/primitives/button/button.test.ts | 2 +- .../src/core/primitives/button/button.tsx | 2 +- .../src/core/primitives/button/recipe.css.ts | 20 +++++----- .../src/core/primitives/checkbox/checkbox.tsx | 2 +- .../core/primitives/checkbox/styles.css.ts | 20 +++++----- .../src/core/primitives/combobox/root.tsx | 4 +- .../core/primitives/combobox/size-context.tsx | 2 +- .../core/primitives/combobox/styles.css.ts | 26 ++++++------ .../src/core/primitives/field/recipe.css.ts | 6 +-- .../primitives/select/select.browser.test.tsx | 6 +-- .../src/core/primitives/select/select.tsx | 8 ++-- .../src/core/primitives/select/styles.css.ts | 10 ++--- .../src/core/primitives/switch/styles.css.ts | 8 ++-- .../src/core/primitives/switch/switch.tsx | 2 +- .../core/primitives/text-input/recipe.css.ts | 10 ++--- .../core/primitives/text-input/styles.css.ts | 10 ++--- .../text-input/text-input.browser.test.tsx | 20 +++++----- .../core/primitives/text-input/text-input.tsx | 16 ++++---- .../select-field.browser.test.tsx | 4 +- .../react/src/core/sizing/button-sizing.ts | 4 +- .../react/src/core/sizing/combobox-sizing.ts | 8 ++-- .../react/src/core/sizing/control-size.ts | 11 +++-- .../react/src/core/sizing/icon-sizing.test.ts | 9 +++++ .../react/src/core/sizing/icon-sizing.ts | 16 ++++---- .../styles/global-styles.browser.test.tsx | 2 +- .../src/core/styles/recipe.browser.test.ts | 6 +-- .../stylesheet-contract.browser.test.tsx | 2 +- .../switch-field.browser.test.tsx | 4 +- .../text-input-field.browser.test.tsx | 6 +-- .../text-input-field/text-input-field.tsx | 4 +- .../react/src/core/types/icon-size.ts | 4 +- .../@luke-ui/react/src/theme/contract.test.ts | 2 +- packages/@luke-ui/react/src/theme/contract.ts | 9 +++-- .../react/src/theme/literal-scale.test.ts | 40 +++++++++++++++++++ .../@luke-ui/react/src/theme/literal-scale.ts | 22 ++++++++++ packages/@luke-ui/react/src/theme/rem.ts | 2 +- .../react/src/theme/token-values.test.ts | 14 +++++++ .../@luke-ui/react/src/theme/token-values.ts | 15 ++++--- 136 files changed, 507 insertions(+), 382 deletions(-) create mode 100644 packages/@luke-ui/react/src/core/sizing/icon-sizing.test.ts create mode 100644 packages/@luke-ui/react/src/theme/literal-scale.test.ts create mode 100644 packages/@luke-ui/react/src/theme/literal-scale.ts create mode 100644 packages/@luke-ui/react/src/theme/token-values.test.ts diff --git a/apps/docs/content/docs/components/actions/button.mdx b/apps/docs/content/docs/components/actions/button.mdx index 80b83235..2cc22d61 100644 --- a/apps/docs/content/docs/components/actions/button.mdx +++ b/apps/docs/content/docs/components/actions/button.mdx @@ -20,8 +20,8 @@ reactAria: https://react-spectrum.adobe.com/react-aria/Button.html ## Size -`medium` is the default size. Use `small` in dense toolbars, tables, and other compact interfaces. -Keep related controls at the same size. +`px40` is the default size. Use `px32` in dense toolbars, tables, and other compact interfaces. Keep +related controls at the same size. diff --git a/apps/docs/content/docs/components/actions/icon-button.mdx b/apps/docs/content/docs/components/actions/icon-button.mdx index 60501d84..ad9eba93 100644 --- a/apps/docs/content/docs/components/actions/icon-button.mdx +++ b/apps/docs/content/docs/components/actions/icon-button.mdx @@ -12,7 +12,7 @@ an item. Give every icon button an accessible name because it has no visible lab ## Size -`medium` is the default. Use `small` for compact toolbars and dense row actions. Keep controls in a +`px40` is the default. Use `px32` for compact toolbars and dense row actions. Keep controls in a group at the same size. diff --git a/apps/docs/content/docs/components/actions/icon-link.mdx b/apps/docs/content/docs/components/actions/icon-link.mdx index de36d252..e013b30d 100644 --- a/apps/docs/content/docs/components/actions/icon-link.mdx +++ b/apps/docs/content/docs/components/actions/icon-link.mdx @@ -14,7 +14,7 @@ a navigation. ## Size -`medium` is the default. Use `small` for compact toolbars and dense row actions. Keep controls in a +`px40` is the default. Use `px32` for compact toolbars and dense row actions. Keep controls in a group at the same size. diff --git a/apps/docs/content/docs/components/feedback/loading-spinner.mdx b/apps/docs/content/docs/components/feedback/loading-spinner.mdx index 65fb6534..6eb9c8a2 100644 --- a/apps/docs/content/docs/components/feedback/loading-spinner.mdx +++ b/apps/docs/content/docs/components/feedback/loading-spinner.mdx @@ -11,7 +11,7 @@ final content to replace it in place. ## Size -`medium` is the default. Set `size` to match the surrounding control or content. +`px24` is the default. Set `size` to match the surrounding control or content. diff --git a/apps/docs/content/docs/components/forms/checkbox-field.mdx b/apps/docs/content/docs/components/forms/checkbox-field.mdx index 8f4bfd0a..61a613aa 100644 --- a/apps/docs/content/docs/components/forms/checkbox-field.mdx +++ b/apps/docs/content/docs/components/forms/checkbox-field.mdx @@ -34,8 +34,8 @@ option whose child options are only partly selected. ## Size -Use `size` to change the checkbox control. It does not change the label typography. `medium` is the -default. Use `small` in compact layouts and `large` where a larger control improves scanning. +Use `size` to change the checkbox control. It does not change the label typography. `px20` is the +default. Use `px16` in compact layouts and `px24` where a larger control improves scanning. diff --git a/apps/docs/content/docs/components/forms/combobox-field.mdx b/apps/docs/content/docs/components/forms/combobox-field.mdx index 0df54d5d..e8d1a129 100644 --- a/apps/docs/content/docs/components/forms/combobox-field.mdx +++ b/apps/docs/content/docs/components/forms/combobox-field.mdx @@ -53,7 +53,7 @@ Use `ComboboxSection` to group related static options. ## Size -Use `size` to set the control height and typography. Use `small` in compact layouts. `medium` is the +Use `size` to set the control height and typography. Use `px32` in compact layouts. `px40` is the default. diff --git a/apps/docs/content/docs/components/forms/select-field.mdx b/apps/docs/content/docs/components/forms/select-field.mdx index a59c35b5..ea805371 100644 --- a/apps/docs/content/docs/components/forms/select-field.mdx +++ b/apps/docs/content/docs/components/forms/select-field.mdx @@ -66,7 +66,7 @@ and can't change the value from the trigger, until pending ends. ## Size -Use `size` to set the control height and typography. Use `small` in compact layouts. `medium` is the +Use `size` to set the control height and typography. Use `px32` in compact layouts. `px40` is the default. diff --git a/apps/docs/content/docs/components/forms/switch-field.mdx b/apps/docs/content/docs/components/forms/switch-field.mdx index 6941cab2..bfda66c0 100644 --- a/apps/docs/content/docs/components/forms/switch-field.mdx +++ b/apps/docs/content/docs/components/forms/switch-field.mdx @@ -36,7 +36,7 @@ saving. ## Size -Use `size` to change the switch control. It does not change the label typography. `medium` is the +Use `size` to change the switch control. It does not change the label typography. `px20` is the default. diff --git a/apps/docs/content/docs/components/forms/text-input-field.mdx b/apps/docs/content/docs/components/forms/text-input-field.mdx index c1218692..e282d32a 100644 --- a/apps/docs/content/docs/components/forms/text-input-field.mdx +++ b/apps/docs/content/docs/components/forms/text-input-field.mdx @@ -55,7 +55,7 @@ keyboard behaviour. ## Size -Use `size` to set the input height and typography. Use `small` in compact layouts. `medium` is the +Use `size` to set the input height and typography. Use `px32` in compact layouts. `px40` is the default. The HTML numeric `size` attribute is unavailable because `size` is the Luke UI variant. diff --git a/apps/docs/content/docs/components/layout/container.mdx b/apps/docs/content/docs/components/layout/container.mdx index 3f8c2471..6b741fcb 100644 --- a/apps/docs/content/docs/components/layout/container.mdx +++ b/apps/docs/content/docs/components/layout/container.mdx @@ -4,8 +4,8 @@ description: Constrain content and establish a local size container. source: packages/@luke-ui/react/src/exports/container.ts --- -`Container` fills its available inline size up to `maxInlineSize` and centres itself. Pass a `ct` -value for a fixed maximum, or a CSS length. Pass `marginInline` to change the centring. Read +`Container` fills its available inline size up to `maxInlineSize` and centres itself. Pass a `px` +token for a fixed maximum, or a CSS length. Pass `marginInline` to change the centring. Read [Layout](/docs/layout#choose-a-layout-component) to choose a layout component. @@ -16,7 +16,7 @@ A traditional container usually only constrains and centres content. Luke UI's ` establishes a local responsive query boundary. Luke UI responsive values inside the Container resolve against its content box rather than the -document root. A `ct672` Container therefore never reaches `bp768`. `paddingInline` reduces the +document root. A `px672` Container therefore never reaches `bp768`. `paddingInline` reduces the content-box width, so it can also change where responsive styles switch. Use Container when descendants should respond to the available content area rather than the document diff --git a/apps/docs/content/docs/components/primitives/select.mdx b/apps/docs/content/docs/components/primitives/select.mdx index 21ca02be..cb652d2a 100644 --- a/apps/docs/content/docs/components/primitives/select.mdx +++ b/apps/docs/content/docs/components/primitives/select.mdx @@ -44,8 +44,8 @@ function. ## Size -Set `size` on `SelectRoot`, and the trigger, indicator, and items follow. `medium` is the default. -Use `small` in compact layouts. +Set `size` on `SelectRoot`, and the trigger, indicator, and items follow. `px40` is the default. Use +`px32` in compact layouts. ## Pending diff --git a/apps/docs/content/docs/components/primitives/text-input.mdx b/apps/docs/content/docs/components/primitives/text-input.mdx index 13fc3b9f..fd889129 100644 --- a/apps/docs/content/docs/components/primitives/text-input.mdx +++ b/apps/docs/content/docs/components/primitives/text-input.mdx @@ -85,9 +85,9 @@ receives the change event, while the root's `onChange` receives the value. ## Size -`medium` is the default. Use `small` in compact layouts. `size` on `TextInputRoot` sets the default -for the parts inside it. A `TextInput` or `TextInputControl` with its own `size` overrides the -root's. Inside a control, the control sets the size, and the input, prefix, and suffix follow it. +`px40` is the default. Use `px32` in compact layouts. `size` on `TextInputRoot` sets the default for +the parts inside it. A `TextInput` or `TextInputControl` with its own `size` overrides the root's. +Inside a control, the control sets the size, and the input, prefix, and suffix follow it. The control also sets the icon size for everything inside it, so an `Icon` in a prefix or suffix scales with the control without a `size` of its own. diff --git a/apps/docs/content/docs/docs/iconography.mdx b/apps/docs/content/docs/docs/iconography.mdx index a91eac13..2860ca85 100644 --- a/apps/docs/content/docs/docs/iconography.mdx +++ b/apps/docs/content/docs/docs/iconography.mdx @@ -19,12 +19,12 @@ API. ## Size -Set `size` to `xsmall`, `small`, `medium`, or `large`. Each size is a theme token, not a pixel -value. A custom theme can give each size a different value. +Set `size` to a curated literal key: `px16`, `px20`, `px24`, or `px32`. Each key is that many CSS +pixels at a 16px root, emitted as `rem`. -The default size is `medium`. +The default size is `px24`. To give a group of icons the same size, put an `IconSizeProvider` around them. An icon that has its own `size` prop keeps that size. diff --git a/apps/docs/content/docs/docs/styling.mdx b/apps/docs/content/docs/docs/styling.mdx index 798dd316..3d7cce5e 100644 --- a/apps/docs/content/docs/docs/styling.mdx +++ b/apps/docs/content/docs/docs/styling.mdx @@ -84,7 +84,7 @@ own: ```tsx import { textInputRecipe } from '@luke-ui/react/primitives/text-input'; -; +; ``` Do not import a recipe to restyle a Luke UI component from the outside. Use the component's props diff --git a/apps/docs/src/components/code-block/code-block.css.ts b/apps/docs/src/components/code-block/code-block.css.ts index f7c8743d..1afc0044 100644 --- a/apps/docs/src/components/code-block/code-block.css.ts +++ b/apps/docs/src/components/code-block/code-block.css.ts @@ -57,7 +57,7 @@ export const header = style({ borderBlockEndWidth: '1px', display: 'flex', gap: vars.space.sp8, - minBlockSize: vars.controlSize.medium, + minBlockSize: vars.controlSize.px40, paddingBlock: vars.space.sp4, paddingInline: vars.space.sp12, }, @@ -100,7 +100,7 @@ export const overlayActions = style({ '@layer': { components: { backgroundColor: vars.color.surface.subdued, - insetBlockStart: `calc(${vars.space.sp12} + (${vars.font.label.lineHeight} / 2) - (${vars.controlSize.small} / 2))`, + insetBlockStart: `calc(${vars.space.sp12} + (${vars.font.label.lineHeight} / 2) - (${vars.controlSize.px32} / 2))`, insetInlineEnd: vars.space.sp8, position: 'absolute', zIndex: 1, diff --git a/apps/docs/src/components/code-block/code-block.tsx b/apps/docs/src/components/code-block/code-block.tsx index dd5520f1..cff54e1b 100644 --- a/apps/docs/src/components/code-block/code-block.tsx +++ b/apps/docs/src/components/code-block/code-block.tsx @@ -107,7 +107,7 @@ export function CodeBlock({ icon={copyStatus === 'copied' ? 'check' : 'copy'} onPress={handleCopy} prominence="low" - size="small" + size="px32" tone="neutral" /> ) : null; diff --git a/apps/docs/src/components/component-props-table.tsx b/apps/docs/src/components/component-props-table.tsx index d5d62903..4e6347e8 100644 --- a/apps/docs/src/components/component-props-table.tsx +++ b/apps/docs/src/components/component-props-table.tsx @@ -133,7 +133,7 @@ function PropGroup({ aria-hidden className={disclosureStyles.chevronEnd} name="chevronRight" - size="xsmall" + size="px16" /> {group.name} diff --git a/apps/docs/src/components/docs-article/docs-pager.tsx b/apps/docs/src/components/docs-article/docs-pager.tsx index 4b19cf02..6e5c53df 100644 --- a/apps/docs/src/components/docs-article/docs-pager.tsx +++ b/apps/docs/src/components/docs-article/docs-pager.tsx @@ -120,7 +120,7 @@ function PagerLink({ direction, page }: { direction: PagerDirection; page: Item function PagerTitle({ direction, name }: { direction: PagerDirection; name: ReactNode }) { const chevron = ( - + ); diff --git a/apps/docs/src/components/docs-article/docs-toc.tsx b/apps/docs/src/components/docs-article/docs-toc.tsx index fa63d5f5..756cafe4 100644 --- a/apps/docs/src/components/docs-article/docs-toc.tsx +++ b/apps/docs/src/components/docs-article/docs-toc.tsx @@ -78,7 +78,7 @@ export function DocsTocBar({ toc }: { toc: Array }) {
{TOC_LABEL} - +