diff --git a/apps/storybook/.storybook/main.ts b/apps/storybook/.storybook/main.ts index c602cf97d8..4c278b9bd0 100644 --- a/apps/storybook/.storybook/main.ts +++ b/apps/storybook/.storybook/main.ts @@ -1,4 +1,5 @@ import type {StorybookConfig} from '@storybook/react-vite' +import {Features} from 'lightningcss' const config: StorybookConfig = { stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], @@ -16,5 +17,17 @@ const config: StorybookConfig = { shouldExtractLiteralValuesFromEnum: true, }, }, + async viteFinal(viteConfig) { + return { + ...viteConfig, + css: { + ...viteConfig.css, + lightningcss: { + ...viteConfig.css?.lightningcss, + exclude: Features.LightDark, + }, + }, + } + }, } export default config diff --git a/apps/storybook/package.json b/apps/storybook/package.json index aa185506cb..ce17fbaed4 100644 --- a/apps/storybook/package.json +++ b/apps/storybook/package.json @@ -29,6 +29,7 @@ "@vitejs/plugin-react": "catalog:", "@vitest/browser-playwright": "^4.1.1", "@vitest/coverage-v8": "^4.1.1", + "lightningcss": "^1.33.0", "oxc-transform-react": "catalog:", "playwright": "^1.58.2", "storybook": "^10.3.3", diff --git a/apps/storybook/src/stories/Card.stories.tsx b/apps/storybook/src/stories/Card.stories.tsx index 64e2a7018b..6f62d33283 100644 --- a/apps/storybook/src/stories/Card.stories.tsx +++ b/apps/storybook/src/stories/Card.stories.tsx @@ -5,6 +5,7 @@ import {Card as CardV3} from 'ui3' import {Card} from '../../../../packages/ui/src/components/card/Card' import {cardProps} from '../../../../packages/ui/src/components/card/card.props' import {HStack} from '../../../../packages/ui/src/components/h-stack/HStack' +import {Text} from '../../../../packages/ui/src/components/text/Text' import {TONE_WITH_DEFAULT} from '../../../../packages/ui/src/types/Tone' import {getArgTypes} from '../utils/getArgTypes' @@ -13,7 +14,7 @@ const argTypes = getArgTypes(cardProps) const meta: Meta = { title: 'Components/Card', args: { - children: 'This is a Card component.', + children: This is a Card component., as: 'div', }, argTypes, @@ -38,7 +39,9 @@ export const Default: Story = { return }, play: async ({canvas}) => { - await expect((await canvas.findByText('This is a Card component.')).dataset.ui).toBe('Card') + await expect( + (await canvas.findByText('This is a Card component.')).parentElement?.dataset.ui, + ).toBe('Card') }, } @@ -48,15 +51,15 @@ export const Tones: Story = { {TONE_WITH_DEFAULT.map((tone) => ( - {`Card ${tone} tone`} + {`Card ${tone} tone`} ))} ) }, play: async ({canvas}) => { - await expect((await canvas.findByText('Card positive tone')).classList).toContain( - 'sui-tone-positive', - ) + await expect( + (await canvas.findByText('Card positive tone')).parentElement?.classList, + ).toContain('sui-tone-positive') }, } diff --git a/apps/storybook/src/stories/Divider.stories.tsx b/apps/storybook/src/stories/Divider.stories.tsx index 82898a5f28..b19205415b 100644 --- a/apps/storybook/src/stories/Divider.stories.tsx +++ b/apps/storybook/src/stories/Divider.stories.tsx @@ -3,6 +3,7 @@ import {expect} from 'storybook/test' import {Divider} from '../../../../packages/ui/src/components/divider/Divider' import {dividerProps} from '../../../../packages/ui/src/components/divider/divider.props' +import {Text} from '../../../../packages/ui/src/components/text/Text' import {getArgTypes} from '../utils/getArgTypes' const argTypes = getArgTypes(dividerProps) @@ -29,9 +30,9 @@ export const Default: Story = { render: (props) => { return ( <> -

Content above

+ Content above -

Content below

+ Content below ) }, diff --git a/apps/storybook/src/stories/HStack.stories.tsx b/apps/storybook/src/stories/HStack.stories.tsx index f0308f4ecb..e8b54c4eb2 100644 --- a/apps/storybook/src/stories/HStack.stories.tsx +++ b/apps/storybook/src/stories/HStack.stories.tsx @@ -3,6 +3,7 @@ import {expect} from 'storybook/test' import {Card} from '../../../../packages/ui/src/components/card/Card' import {HStack} from '../../../../packages/ui/src/components/h-stack/HStack' +import {Text} from '../../../../packages/ui/src/components/text/Text' import {SPACE_INHERIT} from '../../../../packages/ui/src/types/Space' import {Square} from '../components/Square' import {getArgTypes} from '../utils/getArgTypes' @@ -40,9 +41,15 @@ export const Default: Story = { render: (props) => { return ( - Child 1 - Child 2 - Child 3 + + Child 1 + + + Child 2 + + + Child 3 + ) }, @@ -58,9 +65,15 @@ export const Inherit: Story = { return ( - Child 1 - Child 2 - Child 3 + + Child 1 + + + Child 2 + + + Child 3 + ) diff --git a/apps/storybook/src/stories/Inline.stories.tsx b/apps/storybook/src/stories/Inline.stories.tsx index b9ebcb568c..de89c077bd 100644 --- a/apps/storybook/src/stories/Inline.stories.tsx +++ b/apps/storybook/src/stories/Inline.stories.tsx @@ -4,6 +4,7 @@ import {Inline as InlineV3} from 'ui3' import {Inline} from '../../../../packages/ui/src/components/inline/Inline' import {inlineProps} from '../../../../packages/ui/src/components/inline/inline.props' +import {Text} from '../../../../packages/ui/src/components/text/Text' import {Square} from '../components/Square' import {getArgTypes} from '../utils/getArgTypes' @@ -35,9 +36,15 @@ export const Default: Story = { render: (props) => { return ( - Child 1 - Child 2 - Child 3 + + Child 1 + + + Child 2 + + + Child 3 + ) }, diff --git a/apps/storybook/src/stories/Tabs.stories.tsx b/apps/storybook/src/stories/Tabs.stories.tsx index 359ede60e4..08b50abb7c 100644 --- a/apps/storybook/src/stories/Tabs.stories.tsx +++ b/apps/storybook/src/stories/Tabs.stories.tsx @@ -11,6 +11,7 @@ import {Box} from '../../../../packages/ui/src/components/box/Box' import {Card} from '../../../../packages/ui/src/components/card/Card' import {Tabs} from '../../../../packages/ui/src/components/tabs/Tabs' import {type TabsProps, tabsProps} from '../../../../packages/ui/src/components/tabs/tabs.props' +import {Text} from '../../../../packages/ui/src/components/text/Text' import {getArgTypes} from '../utils/getArgTypes' const argTypes = getArgTypes(tabsProps) @@ -45,9 +46,15 @@ function TabsDefaultStory(props: TabsProps) { - First tab panel - Second tab panel - Third tab panel + + First tab panel + + + Second tab panel + + + Third tab panel + ) @@ -72,19 +79,29 @@ function TabsTonesAndIconsStory(props: TabsProps) { - Neutral Card in tab panel + + Neutral Card in tab panel + - Positive Card in tab panel + + Positive Card in tab panel + - Suggest Card in tab panel + + Suggest Card in tab panel + - Caution Card in tab panel + + Caution Card in tab panel + - Critical Card in tab panel + + Critical Card in tab panel + @@ -102,14 +119,17 @@ export const Default: Story = { await expect(firstTab).toHaveAttribute('aria-selected', 'true') await expect(secondTab).toHaveAttribute('aria-selected', 'false') - await expect(canvas.getByText('First tab panel')).toBeVisible() - await expect(canvas.getByText('Second tab panel')).not.toBeVisible() + await expect(canvas.getByText('First tab panel').parentElement).toBeVisible() + await expect(canvas.getByText('Second tab panel').parentElement).not.toBeVisible() - await expect(canvas.getByText('First tab panel')).toHaveAttribute( + await expect(canvas.getByText('First tab panel').parentElement).toHaveAttribute( 'aria-labelledby', firstTab.id, ) - await expect(firstTab).toHaveAttribute('aria-controls', canvas.getByText('First tab panel').id) + await expect(firstTab).toHaveAttribute( + 'aria-controls', + canvas.getByText('First tab panel').parentElement?.id, + ) }, } @@ -153,8 +173,8 @@ export const Interactions: Story = { await expect(secondTab).toHaveAttribute('aria-selected', 'true') await expect(firstTab).toHaveAttribute('aria-selected', 'false') - await expect(canvas.getByText('Second tab panel')).toBeVisible() - await expect(canvas.getByText('First tab panel')).not.toBeVisible() + await expect(canvas.getByText('Second tab panel').parentElement).toBeVisible() + await expect(canvas.getByText('First tab panel').parentElement).not.toBeVisible() }) await step('Only the selected tab is in the tab sequence', async () => { @@ -202,12 +222,12 @@ export const Interactions: Story = { await step('Enter and Space select the focused tab', async () => { await userEvent.keyboard('{Enter}') await expect(thirdTab).toHaveAttribute('aria-selected', 'true') - await expect(canvas.getByText('Third tab panel')).toBeVisible() + await expect(canvas.getByText('Third tab panel').parentElement).toBeVisible() await userEvent.keyboard('{Home}') await userEvent.keyboard(' ') await expect(firstTab).toHaveAttribute('aria-selected', 'true') - await expect(canvas.getByText('First tab panel')).toBeVisible() + await expect(canvas.getByText('First tab panel').parentElement).toBeVisible() }) await step( @@ -217,7 +237,7 @@ export const Interactions: Story = { await expect(thirdTab).toHaveFocus() await userEvent.tab() - await expect(canvas.getByText('First tab panel')).toHaveFocus() + await expect(canvas.getByText('First tab panel').parentElement).toHaveFocus() await userEvent.tab({shift: true}) await expect(firstTab).toHaveFocus() diff --git a/apps/storybook/src/stories/VStack.stories.tsx b/apps/storybook/src/stories/VStack.stories.tsx index b19cbfbfd5..5ec398f4ee 100644 --- a/apps/storybook/src/stories/VStack.stories.tsx +++ b/apps/storybook/src/stories/VStack.stories.tsx @@ -3,6 +3,7 @@ import {expect} from 'storybook/test' import {Stack as StackV3} from 'ui3' import {Card} from '../../../../packages/ui/src/components/card/Card' +import {Text} from '../../../../packages/ui/src/components/text/Text' import {VStack} from '../../../../packages/ui/src/components/v-stack/VStack' import {SPACE_INHERIT} from '../../../../packages/ui/src/types/Space' import {Square} from '../components/Square' @@ -42,9 +43,15 @@ export const Default: Story = { render: (props) => { return ( - Child 1 - Child 2 - Child 3 + + Child 1 + + + Child 2 + + + Child 3 + ) }, @@ -60,9 +67,15 @@ export const Inherit: Story = { return ( - Child 1 - Child 2 - Child 3 + + Child 1 + + + Child 2 + + + Child 3 + ) diff --git a/apps/storybook/src/styles/storybook.css b/apps/storybook/src/styles/storybook.css index b8a7594f88..884156f082 100644 --- a/apps/storybook/src/styles/storybook.css +++ b/apps/storybook/src/styles/storybook.css @@ -1,7 +1,13 @@ -html.dark body { +html body { + background: light-dark(var(--white), var(--black)); +} + +html.dark body, +html.dark body * { color-scheme: dark; } -html.light body { +html.light body, +html.light body * { color-scheme: light; } diff --git a/packages/ui/src/css/global/reset.css b/packages/ui/src/css/global/reset.css index d4be5c6155..1adc3b3368 100644 --- a/packages/ui/src/css/global/reset.css +++ b/packages/ui/src/css/global/reset.css @@ -1,17 +1,3 @@ -/* Default to border-box sizing */ -*, -*::before, -*::after { - box-sizing: border-box; -} - -/* Zero out default margins, padding, and borders */ -* { - margin: 0; - padding: 0; - border: 0 solid transparent; -} - :root { color-scheme: light dark; } @@ -21,36 +7,48 @@ -webkit-text-size-adjust: 100%; } -:where(body) { - background: light-dark(var(--white), var(--black)); +/* Reset font and color-scheme for first sui- class in a tree */ +:where([class*="sui-"]):not(:where([class*="sui-"]) :where([class*="sui-"])) { font-family: var(--sans); -webkit-font-smoothing: antialiased; } +/* Default to border-box sizing */ +:where([class*="sui-"]), +:where([class*="sui-"]) *, +:where([class*="sui-"]) *::before, +:where([class*="sui-"]) *::after { + box-sizing: border-box; +} + +/* Zero out default margins, padding, and borders */ +:where([class*="sui-"]), +:where([class*="sui-"]) * { + margin: 0; + padding: 0; + border: 0 solid transparent; +} + /* Display multimedia as block by default */ -:where( - audio, - canvas, - embed, - iframe, - img, - object, - picture, - video -){ +:is( + :where([class*="sui-"]) :where(audio, canvas, embed, iframe, img, object, picture, video), + :where(audio, canvas, embed, iframe, img, object, picture, video):where([class*="sui-"]) +) { display: block; max-width: 100%; } -:where(img, picture, video) { + +:is( + :where([class*="sui-"]) :where(img, picture, video), + :where(img, picture, video):where([class*="sui-"]) +) { block-size: auto; } /* Inherit typography in form controls */ -:where( - button, - input, - select, - textarea +:is( + :where([class*="sui-"]) :where(button, input, select, textarea), + :where(button, input, select, textarea):where([class*="sui-"]) ) { appearance: none; color: inherit; @@ -59,30 +57,36 @@ } /* Remove user agent reduced opacity on disabled form elements */ -:where( - button, - input, - select, - textarea -):disabled { +:is( + :where([class*="sui-"]) :where(button, input, select, textarea):disabled, + :where(button, input, select, textarea):where([class*="sui-"]):disabled +) { opacity: 1; } /* Remove excess space for table borders */ -:where(table) { +:is( + :where([class*="sui-"]) :where(table), + :where(table):where([class*="sui-"]) +) { border-collapse: collapse; border-spacing: 0; } -:where(code) { +/* Inherit typography for code blocks */ +:is( + :where([class*="sui-"]) :where(code), + :where(code):where([class*="sui-"]) +) { font-family: var(--mono); } -/* Enforce no animations for users that prefer reduced motion*/ +/* Enforce no animations for users that prefer reduced motion */ @media (prefers-reduced-motion: reduce) { - *, - *::before, - *::after { + :where([class*="sui-"]), + :where([class*="sui-"]) *, + :where([class*="sui-"]) *::before, + :where([class*="sui-"]) *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e82fef3090..cb9303aea7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -399,6 +399,9 @@ importers: '@vitest/coverage-v8': specifier: ^4.1.1 version: 4.1.1(@vitest/browser@4.1.1(vite@8.2.2(@types/node@24.12.0)(esbuild@0.28.1)(jiti@2.6.1)(terser@5.46.1)(tsx@4.23.5)(yaml@2.8.3))(vitest@4.1.1))(vitest@4.1.1) + lightningcss: + specifier: ^1.33.0 + version: 1.33.0 oxc-transform-react: specifier: 'catalog:' version: 0.149.0