import type { Meta, StoryObj } from '@storybook/react-vite'; import type React from 'react'; import { css } from 'styled-system/css'; const meta = { title: 'Foundations/Color Scale', parameters: { layout: 'padded' }, } satisfies Meta; export default meta; type Story = StoryObj; type Palette = 'primary' | 'secondary' | 'tertiary' | 'neutral' | 'error'; const PALETTES: Palette[] = [ 'primary', 'secondary', 'tertiary', 'neutral', 'error', ]; const SCALE_STEPS = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] as const; const ALPHA_STEPS = [ 'a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7', 'a8', 'a9', 'a10', 'a11', 'a12', ] as const; // ── Pre-declared css() calls — Panda CSS statically extracts these ───────────── // (dynamic template literals cannot be extracted; explicit literals are required) const scaleBg: Record> = { primary: { 1: css({ bg: 'primary.1' }), 2: css({ bg: 'primary.2' }), 3: css({ bg: 'primary.3' }), 4: css({ bg: 'primary.4' }), 5: css({ bg: 'primary.5' }), 6: css({ bg: 'primary.6' }), 7: css({ bg: 'primary.7' }), 8: css({ bg: 'primary.8' }), 9: css({ bg: 'primary.9' }), 10: css({ bg: 'primary.10' }), 11: css({ bg: 'primary.11' }), 12: css({ bg: 'primary.12' }), }, secondary: { 1: css({ bg: 'secondary.1' }), 2: css({ bg: 'secondary.2' }), 3: css({ bg: 'secondary.3' }), 4: css({ bg: 'secondary.4' }), 5: css({ bg: 'secondary.5' }), 6: css({ bg: 'secondary.6' }), 7: css({ bg: 'secondary.7' }), 8: css({ bg: 'secondary.8' }), 9: css({ bg: 'secondary.9' }), 10: css({ bg: 'secondary.10' }), 11: css({ bg: 'secondary.11' }), 12: css({ bg: 'secondary.12' }), }, tertiary: { 1: css({ bg: 'tertiary.1' }), 2: css({ bg: 'tertiary.2' }), 3: css({ bg: 'tertiary.3' }), 4: css({ bg: 'tertiary.4' }), 5: css({ bg: 'tertiary.5' }), 6: css({ bg: 'tertiary.6' }), 7: css({ bg: 'tertiary.7' }), 8: css({ bg: 'tertiary.8' }), 9: css({ bg: 'tertiary.9' }), 10: css({ bg: 'tertiary.10' }), 11: css({ bg: 'tertiary.11' }), 12: css({ bg: 'tertiary.12' }), }, neutral: { 1: css({ bg: 'neutral.1' }), 2: css({ bg: 'neutral.2' }), 3: css({ bg: 'neutral.3' }), 4: css({ bg: 'neutral.4' }), 5: css({ bg: 'neutral.5' }), 6: css({ bg: 'neutral.6' }), 7: css({ bg: 'neutral.7' }), 8: css({ bg: 'neutral.8' }), 9: css({ bg: 'neutral.9' }), 10: css({ bg: 'neutral.10' }), 11: css({ bg: 'neutral.11' }), 12: css({ bg: 'neutral.12' }), }, error: { 1: css({ bg: 'error.1' }), 2: css({ bg: 'error.2' }), 3: css({ bg: 'error.3' }), 4: css({ bg: 'error.4' }), 5: css({ bg: 'error.5' }), 6: css({ bg: 'error.6' }), 7: css({ bg: 'error.7' }), 8: css({ bg: 'error.8' }), 9: css({ bg: 'error.9' }), 10: css({ bg: 'error.10' }), 11: css({ bg: 'error.11' }), 12: css({ bg: 'error.12' }), }, }; const alphaBg: Record> = { primary: { a1: css({ bg: 'primary.a1' }), a2: css({ bg: 'primary.a2' }), a3: css({ bg: 'primary.a3' }), a4: css({ bg: 'primary.a4' }), a5: css({ bg: 'primary.a5' }), a6: css({ bg: 'primary.a6' }), a7: css({ bg: 'primary.a7' }), a8: css({ bg: 'primary.a8' }), a9: css({ bg: 'primary.a9' }), a10: css({ bg: 'primary.a10' }), a11: css({ bg: 'primary.a11' }), a12: css({ bg: 'primary.a12' }), }, secondary: { a1: css({ bg: 'secondary.a1' }), a2: css({ bg: 'secondary.a2' }), a3: css({ bg: 'secondary.a3' }), a4: css({ bg: 'secondary.a4' }), a5: css({ bg: 'secondary.a5' }), a6: css({ bg: 'secondary.a6' }), a7: css({ bg: 'secondary.a7' }), a8: css({ bg: 'secondary.a8' }), a9: css({ bg: 'secondary.a9' }), a10: css({ bg: 'secondary.a10' }), a11: css({ bg: 'secondary.a11' }), a12: css({ bg: 'secondary.a12' }), }, tertiary: { a1: css({ bg: 'tertiary.a1' }), a2: css({ bg: 'tertiary.a2' }), a3: css({ bg: 'tertiary.a3' }), a4: css({ bg: 'tertiary.a4' }), a5: css({ bg: 'tertiary.a5' }), a6: css({ bg: 'tertiary.a6' }), a7: css({ bg: 'tertiary.a7' }), a8: css({ bg: 'tertiary.a8' }), a9: css({ bg: 'tertiary.a9' }), a10: css({ bg: 'tertiary.a10' }), a11: css({ bg: 'tertiary.a11' }), a12: css({ bg: 'tertiary.a12' }), }, neutral: { a1: css({ bg: 'neutral.a1' }), a2: css({ bg: 'neutral.a2' }), a3: css({ bg: 'neutral.a3' }), a4: css({ bg: 'neutral.a4' }), a5: css({ bg: 'neutral.a5' }), a6: css({ bg: 'neutral.a6' }), a7: css({ bg: 'neutral.a7' }), a8: css({ bg: 'neutral.a8' }), a9: css({ bg: 'neutral.a9' }), a10: css({ bg: 'neutral.a10' }), a11: css({ bg: 'neutral.a11' }), a12: css({ bg: 'neutral.a12' }), }, error: { a1: css({ bg: 'error.a1' }), a2: css({ bg: 'error.a2' }), a3: css({ bg: 'error.a3' }), a4: css({ bg: 'error.a4' }), a5: css({ bg: 'error.a5' }), a6: css({ bg: 'error.a6' }), a7: css({ bg: 'error.a7' }), a8: css({ bg: 'error.a8' }), a9: css({ bg: 'error.a9' }), a10: css({ bg: 'error.a10' }), a11: css({ bg: 'error.a11' }), a12: css({ bg: 'error.a12' }), }, }; const semanticClasses: Record< Palette, { solidBg: string; subtleBg: string; surfaceBg: string; outlineBorderColor: string; plainFg: string; } > = { primary: { solidBg: css({ bg: 'primary.solid.bg' }), subtleBg: css({ bg: 'primary.subtle.bg' }), surfaceBg: css({ bg: 'primary.surface.bg' }), outlineBorderColor: css({ borderColor: 'primary.outline.border' }), plainFg: css({ color: 'primary.plain.fg' }), }, secondary: { solidBg: css({ bg: 'secondary.solid.bg' }), subtleBg: css({ bg: 'secondary.subtle.bg' }), surfaceBg: css({ bg: 'secondary.surface.bg' }), outlineBorderColor: css({ borderColor: 'secondary.outline.border' }), plainFg: css({ color: 'secondary.plain.fg' }), }, tertiary: { solidBg: css({ bg: 'tertiary.solid.bg' }), subtleBg: css({ bg: 'tertiary.subtle.bg' }), surfaceBg: css({ bg: 'tertiary.surface.bg' }), outlineBorderColor: css({ borderColor: 'tertiary.outline.border' }), plainFg: css({ color: 'tertiary.plain.fg' }), }, neutral: { solidBg: css({ bg: 'neutral.solid.bg' }), subtleBg: css({ bg: 'neutral.subtle.bg' }), surfaceBg: css({ bg: 'neutral.surface.bg' }), outlineBorderColor: css({ borderColor: 'neutral.outline.border' }), plainFg: css({ color: 'neutral.plain.fg' }), }, error: { solidBg: css({ bg: 'error.solid.bg' }), subtleBg: css({ bg: 'error.subtle.bg' }), surfaceBg: css({ bg: 'error.surface.bg' }), outlineBorderColor: css({ borderColor: 'error.outline.border' }), plainFg: css({ color: 'error.plain.fg' }), }, }; // ── Swatch components ────────────────────────────────────────────────────────── const swatchBase: React.CSSProperties = { width: '52px', height: '52px', borderRadius: '6px', flexShrink: 0, }; function ScaleStepSwatch({ palette, step, }: { palette: Palette; step: number; }) { const isSolidAction = step === 9; return (
{step} {isSolidAction && ( solid
action
)}
); } function AlphaStepSwatch({ palette, step, }: { palette: Palette; step: string; }) { return (
{step}
); } function SemanticRow({ palette }: { palette: Palette }) { const classes = semanticClasses[palette]; const label = (text: string) => ( {text} ); return (
{label('solid.bg')}
{label('subtle.bg')}
{label('surface.bg')}
{label('outline.border')}
Aa
{label('plain.fg')}
); } function PaletteLabel({ palette }: { palette: Palette }) { return ( {palette} ); } function SectionHeading({ title, description, }: { title: string; description: string; }) { return (

{title}

{description}

); } // ── Section 1: Scale steps (1–12) ───────────────────────────────────────────── export const ScaleSteps: Story = { name: '1 · Scale Steps (1–12)', render: () => (
{PALETTES.map((palette) => (
{SCALE_STEPS.map((step) => ( ))}
))}
), }; // ── Section 2: Alpha steps (a1–a12) ─────────────────────────────────────────── export const AlphaSteps: Story = { name: '2 · Alpha Steps (a1–a12)', render: () => (
{PALETTES.map((palette) => (
{palette}
{ALPHA_STEPS.map((step) => ( ))}
))}
), }; // ── Section 3: Semantic variants ────────────────────────────────────────────── export const SemanticVariants: Story = { name: '3 · Semantic Variants', render: () => (
{PALETTES.map((palette) => ( <>
))}
), }; // ── Section 4: Usage guidance ───────────────────────────────────────────────── export const UsageGuidance: Story = { name: '4 · Usage Guidance', render: () => (

Usage Guidance

{( [ ['1–2', 'App and page backgrounds'], ['3–4', 'Subtle hover and interactive backgrounds'], ['5–6', 'UI element fills (progress bars, indicators)'], ['7–8', 'Borders and separators'], ['9', 'Solid interactive fill — primary button background'], ['10', 'Hovered solid fill'], ['11–12', 'High-contrast text and icons'], [ 'a1–a12', 'Transparent overlays — render on a background to see them', ], ['solid.bg', 'Filled/solid variant buttons and badges'], ['subtle.bg', 'Subtle/soft variant buttons and badges'], [ 'surface.bg', 'Card and surface backgrounds within a color context', ], [ 'outline.*', 'Outline/ghost variant buttons (border color + optional hover bg)', ], [ 'plain.*', 'Plain/text variant buttons and links (fg color only, bg transparent)', ], ] as [string, string][] ).map(([token, use], i) => ( ))}
Token Use for
{token} {use}

Example Usage

        {`import { css } from 'styled-system/css';

// Solid action button background
css({ bg: 'primary.solid.bg', color: 'primary.solid.fg' })

// Subtle tinted button
css({ bg: 'primary.subtle.bg', color: 'primary.subtle.fg' })

// Outline/ghost button
css({
  bg: 'primary.outline.bg',
  borderColor: 'primary.outline.border',
  color: 'primary.outline.fg',
})

// Plain/text link
css({ color: 'primary.plain.fg' })

// Step-based (for custom components)
css({ bg: 'primary.1' })    // page background
css({ bg: 'primary.9' })    // solid action color
css({ color: 'primary.11' }) // readable body text`}
      
), };