diff --git a/apps/storybook/stories/ProbeTheme.stories.tsx b/apps/storybook/stories/ProbeTheme.stories.tsx new file mode 100644 index 0000000000000..5547b0dc70a52 --- /dev/null +++ b/apps/storybook/stories/ProbeTheme.stories.tsx @@ -0,0 +1,317 @@ +// Copyright (c) Meta Platforms, Inc. and affiliates. + +/** + * @file ProbeTheme.stories.tsx + * @input The active Storybook theme selected in the toolbar + * @output A visual reference for every top-level defineTheme axis + * @position Core/Themes — theme-system verification, not a component demo + * + * Do not wrap these stories in . The toolbar and visual gate must own + * the active theme, otherwise the probe theme cannot be selected or tested. + */ + +import * as React from 'react'; +import type {Meta, StoryObj} from '@storybook/react'; +import * as stylex from '@stylexjs/stylex'; +import {Badge} from '@astryxdesign/core/Badge'; +import {Button} from '@astryxdesign/core/Button'; +import {Card} from '@astryxdesign/core/Card'; +import {CheckboxInput} from '@astryxdesign/core/CheckboxInput'; +import {CodeBlock} from '@astryxdesign/core/CodeBlock'; +import {Icon} from '@astryxdesign/core/Icon'; +import {RadioList, RadioListItem} from '@astryxdesign/core/RadioList'; +import {Stack} from '@astryxdesign/core/Stack'; +import {Switch} from '@astryxdesign/core/Switch'; +import {Heading, Text} from '@astryxdesign/core/Text'; +import {useTheme} from '@astryxdesign/core/theme'; + +const meta = { + title: 'Core/Themes/Probe Theme', + parameters: { + layout: 'fullscreen', + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +const iconNames = [ + 'close', + 'check', + 'success', + 'warning', + 'info', + 'calendar', + 'search', + 'copy', +] as const; + +const code = `type ThemeAxis = + | 'components' + | 'tokens' + | 'icons' + | 'indicators' + | 'fonts' + | 'syntax'; + +export const covered = true;`; + +function AxisCard({ + title, + children, +}: { + title: string; + children: React.ReactNode; +}) { + return ( + +
+ {title} + {children} +
+
+ ); +} + +function TokenSwatches() { + const {token} = useTheme(); + const tokens = [ + ['accent', '--color-accent', styles.accent], + ['body', '--color-background-body', styles.body], + ['text', '--color-text-primary', styles.text], + ['border', '--color-border', styles.border], + ] as const; + + return ( +
+ {tokens.map(([label, name, swatch]) => ( +
+
+ {label} + {token(name)} +
+ ))} +
+ ); +} + +function RegistrySpecimen() { + const [checked, setChecked] = React.useState(true); + const [radio, setRadio] = React.useState('one'); + const [enabled, setEnabled] = React.useState(true); + + return ( + +
+ {iconNames.map(name => ( +
+ + {name} +
+ ))} +
+ +
+ + + + + + +
+
+ ); +} + +function ComponentSpecimen() { + return ( + +
+
+
+ + + + + +
+
+ ); +} + +function AllAxesSheet() { + const {name, mode} = useTheme(); + + return ( +
+
+
+ Probe theme — all axes + + Select Probe (test fixture) in the Theme toolbar. + Every axis should become unmistakably synthetic. + +
+ +
+ +
+ + + + + + + + + + + + + + + AstryxProbeFace heading + Body text inherits the probe font token. + const fontAxis = 'covered'; + + + +
+ + + +
+
+
+ ); +} + +/** + * All six top-level defineTheme axes on one surface. Switch the Theme toolbar + * between Neutral and Probe; this story never pins its own theme. + */ +export const AllAxes: Story = { + name: 'All Axes', + render: () => , +}; + +/** Component targets only, for a compact pixel-diff surface. */ +export const ComponentTargets: Story = { + name: 'Component Targets', + render: () => ( +
+ +
+ ), +}; + +/** Icon and indicator swaps, separated from CSS component overrides. */ +export const Registries: Story = { + render: () => ( +
+ +
+ ), +}; + +const styles = stylex.create({ + page: { + backgroundColor: 'var(--color-background-body)', + minHeight: '100vh', + padding: 32, + }, + compactPage: { + backgroundColor: 'var(--color-background-surface)', + minHeight: '100vh', + padding: 32, + }, + header: { + alignItems: 'flex-start', + display: 'flex', + gap: 24, + justifyContent: 'space-between', + marginBottom: 24, + }, + grid: { + display: 'grid', + gap: 16, + gridTemplateColumns: 'repeat(2, minmax(0, 1fr))', + }, + wide: { + gridColumn: '1 / -1', + }, + axisCard: { + display: 'flex', + flexDirection: 'column', + gap: 16, + padding: 20, + }, + row: { + alignItems: 'center', + display: 'flex', + flexWrap: 'wrap', + gap: 12, + }, + iconRow: { + display: 'grid', + gap: 12, + gridTemplateColumns: 'repeat(4, minmax(0, 1fr))', + }, + iconCell: { + alignItems: 'center', + display: 'flex', + flexDirection: 'column', + gap: 4, + }, + controlGrid: { + display: 'grid', + gap: 16, + gridTemplateColumns: 'repeat(3, minmax(0, 1fr))', + }, + swatchGrid: { + display: 'grid', + gap: 12, + gridTemplateColumns: 'repeat(4, minmax(0, 1fr))', + }, + swatchItem: { + display: 'flex', + flexDirection: 'column', + gap: 4, + minWidth: 0, + }, + swatch: { + borderColor: 'var(--color-border)', + borderRadius: 'var(--radius-element)', + borderStyle: 'solid', + borderWidth: 2, + height: 48, + }, + accent: { + backgroundColor: 'var(--color-accent)', + }, + body: { + backgroundColor: 'var(--color-background-body)', + }, + text: { + backgroundColor: 'var(--color-text-primary)', + }, + border: { + backgroundColor: 'var(--color-border)', + }, +});