From d4fb03e72870407178dd33d845920e8662ddfb30 Mon Sep 17 00:00:00 2001 From: Cindy Zhang Date: Mon, 24 Aug 2026 18:35:04 -0700 Subject: [PATCH 1/2] docs(Icon): storybook demo for theming the icon size scale --- .../stories/IconSizeTheming.stories.tsx | 234 ++++++++++++++++++ 1 file changed, 234 insertions(+) create mode 100644 apps/storybook/stories/IconSizeTheming.stories.tsx diff --git a/apps/storybook/stories/IconSizeTheming.stories.tsx b/apps/storybook/stories/IconSizeTheming.stories.tsx new file mode 100644 index 0000000000000..14712164029db --- /dev/null +++ b/apps/storybook/stories/IconSizeTheming.stories.tsx @@ -0,0 +1,234 @@ +// Copyright (c) Meta Platforms, Inc. and affiliates. + +import type {Meta, StoryObj} from '@storybook/react'; +import {useState} from 'react'; +import {Icon} from '@astryxdesign/core/Icon'; +import {Card} from '@astryxdesign/core/Card'; +import {HStack, VStack} from '@astryxdesign/core/Layout'; +import {Text, Heading} from '@astryxdesign/core/Text'; +import {TextInput} from '@astryxdesign/core/TextInput'; +import {Selector} from '@astryxdesign/core/Selector'; +import {DateInput, type DateInputProps} from '@astryxdesign/core/DateInput'; +import {TimeInput, type ISOTimeString} from '@astryxdesign/core/TimeInput'; +import {Theme, defineTheme} from '@astryxdesign/core/theme'; +import {neutralTheme} from '@astryxdesign/theme-neutral'; +import {BellIcon} from '@heroicons/react/24/outline'; + +// ============================================================================= +// Themes +// ============================================================================= + +const SIZES = ['xsm', 'sm', 'md', 'lg'] as const; + +const FRUITS: string[] = ['Apple', 'Banana', 'Orange']; + +/** + * Icon's own scale is 12/16/20/24px. A theme re-points the whole scale by + * targeting each `size:*` key on the `icon` target. + * + * `fontSize` is set alongside the box because registry icons (`icon="search"`) + * are 1em-based SVGs inside a span — without it the span resizes and the glyph + * inside it does not. + */ +function iconScale( + xsm: string, + sm: string, + md: string, + lg: string, +): Record> { + return Object.fromEntries( + ([xsm, sm, md, lg] as const).map((value, i) => [ + `size:${SIZES[i]}`, + {width: value, height: value, fontSize: value}, + ]), + ); +} + +const compactIcons = defineTheme({ + name: 'compact-icons', + extends: neutralTheme, + components: { + icon: iconScale('0.625rem', '0.75rem', '0.875rem', '1rem'), + }, +}); + +const spaciousIcons = defineTheme({ + name: 'spacious-icons', + extends: neutralTheme, + components: { + icon: iconScale('1rem', '1.375rem', '1.75rem', '2.25rem'), + }, +}); + +const VARIANTS = [ + {label: 'Default', theme: neutralTheme, scale: '12 / 16 / 20 / 24'}, + {label: 'Compact', theme: compactIcons, scale: '10 / 12 / 14 / 16'}, + {label: 'Spacious', theme: spaciousIcons, scale: '16 / 22 / 28 / 36'}, +] as const; + +// ============================================================================= +// Panels +// ============================================================================= + +function Panel({ + label, + scale, + children, +}: { + label: string; + scale: string; + children: React.ReactNode; +}) { + return ( + + + + {label} + + {scale}px + + + {children} + + + ); +} + +function SizeRow() { + return ( + + + {SIZES.map(size => ( + + + + {size} + + + ))} + + + {SIZES.map(size => ( + + + + {size} + + + ))} + + + ); +} + +function InputsWithBuiltInIcons() { + const [text, setText] = useState(''); + const [fruit, setFruit] = useState(''); + const [date, setDate] = useState(); + const [time, setTime] = useState(); + + return ( + + + + + + + ); +} + +function Columns({render}: {render: () => React.ReactNode}) { + return ( +
+ {VARIANTS.map(({label, theme, scale}) => ( + + + {render()} + + + ))} +
+ ); +} + +// ============================================================================= +// Stories +// ============================================================================= + +const meta: Meta = { + title: 'Core/Icon/Size Theming', + parameters: { + docs: { + description: { + component: + 'Icon size is a themeable property. A theme re-points the whole ' + + '`xsm`/`sm`/`md`/`lg` scale by targeting the `icon` component with ' + + '`size:*` keys — no component or call-site changes.\n\n' + + '```ts\n' + + 'defineTheme({\n' + + " name: 'spacious-icons',\n" + + ' components: {\n' + + ' icon: {\n' + + " 'size:sm': {width: '1.375rem', height: '1.375rem', fontSize: '1.375rem'},\n" + + " 'size:md': {width: '1.75rem', height: '1.75rem', fontSize: '1.75rem'},\n" + + ' },\n' + + ' },\n' + + '});\n' + + '```\n\n' + + 'Set `fontSize` alongside the box: registry icons (`icon="search"`) ' + + 'are 1em-based SVGs in a span, so the box alone resizes the span and ' + + 'not the glyph. Component-mode icons (`icon={BellIcon}`) need only ' + + 'width/height, and setting `fontSize` on them is harmless.', + }, + }, + }, +}; + +export default meta; + +/** + * The four sizes under three themes. Top row is a registry icon + * (`icon="search"`), bottom row an SVG component (`icon={BellIcon}`) — both + * follow the theme. + */ +export const SizeScale: StoryObj = { + render: () => } />, +}; + +/** + * The same themes reaching icons a consumer never renders directly: the + * TextInput start icon, the Selector chevron, the DateInput calendar toggle + * and the TimeInput clock. Each is an internal ``, so the + * theme's `size:sm` rule retunes all of them at once. + */ +export const BuiltInComponentIcons: StoryObj = { + render: () => } />, +}; From 279302be08f0cf3fec982ee885762ccea3358b68 Mon Sep 17 00:00:00 2001 From: Cindy Zhang Date: Mon, 24 Aug 2026 19:12:16 -0700 Subject: [PATCH 2/2] docs(Icon): tag the size-theming story autodocs so its description renders --- apps/storybook/stories/IconSizeTheming.stories.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/storybook/stories/IconSizeTheming.stories.tsx b/apps/storybook/stories/IconSizeTheming.stories.tsx index 14712164029db..1528c218f9c4a 100644 --- a/apps/storybook/stories/IconSizeTheming.stories.tsx +++ b/apps/storybook/stories/IconSizeTheming.stories.tsx @@ -185,6 +185,7 @@ function Columns({render}: {render: () => React.ReactNode}) { const meta: Meta = { title: 'Core/Icon/Size Theming', + tags: ['autodocs'], parameters: { docs: { description: {