Storybook Samples
Comprehensive Storybook examples for component development, testing, documentation, and design system management
Key Facts
- Category
- Development Tools
- Items
- 2
- Format Families
- sample
Sample Overview
Comprehensive Storybook examples for component development, testing, documentation, and design system management This sample set belongs to Development Tools and can be used to test related workflows inside Elysia Tools.
💻 Storybook Basics javascript
🟢 simple
⭐⭐
Fundamental Storybook setup, basic story writing, and component documentation patterns
⏱️ 30 min
🏷️ storybook, components, documentation
Prerequisites:
React, JavaScript/TypeScript, CSS, Component concepts
// Storybook Basics
// File: .storybook/main.js - Storybook Configuration
module.exports = {
stories: [
'../stories/**/*.stories.mdx',
'../stories/**/*.stories.@(js|jsx|ts|tsx|mdx)',
'../src/**/*.stories.@(js|jsx|ts|tsx|mdx)',
],
addons: [
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-interactions',
'@storybook/addon-a11y',
'@storybook/addon-docs',
'@storybook/addon-controls',
'@storybook/addon-backgrounds',
'@storybook/addon-viewport',
'@storybook/addon-toolbars',
'@storybook/addon-design-tokens',
],
framework: {
name: '@storybook/react-vite',
options: {},
},
docs: {
autodocs: 'tag',
},
features: {
buildStoriesJson: true,
storyStoreV7: true,
},
typescript: {
check: false,
reactDocgen: 'react-docgen-typescript',
reactDocgenTypescriptOptions: {
shouldExtractLiteralValuesFromEnum: true,
propFilter: (prop) => (prop.parent ? !/node_modules/.test(prop.parent.fileName) : true),
},
},
}
// File: .storybook/preview.js - Global Configuration
export const parameters = {
actions: { argTypesRegex: '^on[A-Z].*' },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
docs: {
toc: true,
},
backgrounds: {
default: 'light',
values: [
{
name: 'light',
value: '#ffffff',
},
{
name: 'dark',
value: '#333333',
},
{
name: 'gray',
value: '#f5f5f5',
},
],
},
viewport: {
viewports: {
mobile: {
name: 'Mobile',
styles: {
width: '375px',
height: '667px',
},
},
tablet: {
name: 'Tablet',
styles: {
width: '768px',
height: '1024px',
},
},
desktop: {
name: 'Desktop',
styles: {
width: '1024px',
height: '768px',
},
},
wide: {
name: 'Wide',
styles: {
width: '1440px',
height: '900px',
},
},
},
defaultViewport: 'desktop',
},
}
// File: .storybook/manager.js - Manager Configuration
import { addons } from '@storybook/addons'
addons.setConfig({
theme: {
brandTitle: 'My Component Library',
brandUrl: 'https://example.com',
brandImage: 'https://example.com/logo.png',
},
panelPosition: 'right',
sidebar: {
showRoots: true,
collapsedRoots: ['examples'],
},
})
// File: src/components/Button/Button.stories.js - Basic Component Stories
import React from 'react'
import { Button } from './Button'
// Default metadata
const buttonStoriesMeta = {
title: 'Components/Button',
component: Button,
parameters: {
layout: 'centered',
docs: {
description: {
component: 'A versatile button component with multiple variants and sizes.',
},
},
},
argTypes: {
variant: {
control: {
type: 'select',
},
options: ['primary', 'secondary', 'danger', 'ghost'],
description: 'Button style variant',
},
size: {
control: {
type: 'radio',
},
options: ['small', 'medium', 'large'],
description: 'Button size',
},
disabled: {
control: 'boolean',
description: 'Whether the button is disabled',
},
loading: {
control: 'boolean',
description: 'Show loading state',
},
onClick: {
action: 'clicked',
description: 'Click event handler',
},
children: {
control: {
type: 'text',
},
description: 'Button content',
},
},
}
// Template for creating stories
const Template = (args) => <Button {...args} />
// Default story
export const Default = Template.bind({})
Default.args = {
children: 'Click me',
}
// Different variants
export const Primary = Template.bind({})
Primary.args = {
children: 'Primary Button',
variant: 'primary',
}
export const Secondary = Template.bind({})
Secondary.args = {
children: 'Secondary Button',
variant: 'secondary',
}
export const Danger = Template.bind({})
Danger.args = {
children: 'Danger Button',
variant: 'danger',
}
export const Ghost = Template.bind({})
Ghost.args = {
children: 'Ghost Button',
variant: 'ghost',
}
// Different sizes
export const Small = Template.bind({})
Small.args = {
children: 'Small Button',
size: 'small',
variant: 'primary',
}
export const Medium = Template.bind({})
Medium.args = {
children: 'Medium Button',
size: 'medium',
variant: 'primary',
}
export const Large = Template.bind({})
Large.args = {
children: 'Large Button',
size: 'large',
variant: 'primary',
}
// States
export const Disabled = Template.bind({})
Disabled.args = {
children: 'Disabled Button',
disabled: true,
}
export const Loading = Template.bind({})
Loading.args = {
children: 'Loading...',
loading: true,
}
// Complex examples
export const WithIcon = Template.bind({})
WithIcon.args = {
children: (
<>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 2L2 7V12C2 16.55 4.84 20.74 9 22C13.16 20.74 16 16.55 16 12V7L12 2Z" fill="currentColor"/>
</svg>
<span style={{ marginLeft: '8px' }}>Secure Login</span>
</>
),
variant: 'primary',
}
// Interactive story
export const Interactive = () => {
const [count, setCount] = React.useState(0)
return (
<div>
<Button onClick={() => setCount(count + 1)}>
Clicked {count} times
</Button>
</div>
)
}
// File: src/components/Card/Card.stories.js - Complex Component Stories
import React from 'react'
import { Card } from './Card'
const cardStoriesMeta = {
title: 'Components/Card',
component: Card,
parameters: {
layout: 'padded',
docs: {
description: {
component: 'A flexible card component for displaying content in a contained format.',
},
},
},
argTypes: {
title: {
control: 'text',
description: 'Card title',
},
subtitle: {
control: 'text',
description: 'Card subtitle',
},
image: {
control: 'text',
description: 'Card image URL',
},
footer: {
control: 'text',
description: 'Card footer content',
},
interactive: {
control: 'boolean',
description: 'Whether the card is interactive (hoverable)',
},
},
}
const Template = (args) => <Card {...args} />
export const Default = Template.bind({})
Default.args = {
title: 'Card Title',
children: 'This is the card content. You can put any content here.',
}
export const WithSubtitle = Template.bind({})
WithSubtitle.args = {
title: 'Card with Subtitle',
subtitle: 'This is a subtitle',
children: 'Card content with subtitle above.',
}
export const WithImage = Template.bind({})
WithImage.args = {
title: 'Card with Image',
image: 'https://picsum.photos/400/200',
children: 'This card has an image at the top.',
}
export const WithFooter = Template.bind({})
WithFooter.args = {
title: 'Card with Footer',
footer: (
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<span>Footer content</span>
<Button>Action</Button>
</div>
),
children: 'This card has a footer section.',
}
export const Interactive = Template.bind({})
Interactive.args = {
title: 'Interactive Card',
interactive: true,
children: 'This card is interactive and responds to hover.',
}
// File: src/stories/Introduction.stories.mdx - Documentation Stories
import { Meta, Story, Canvas, ArgsTable } from '@storybook/blocks'
import { Button, Card, Input } from '../src/components'
<Meta title="Documentation/Introduction" />
# Welcome to Our Component Library
This is the documentation for our React component library built with Storybook.
## Getting Started
Our components are designed to be:
- **Accessible**: Following WCAG guidelines
- **Responsive**: Working on all screen sizes
- **Customizable**: With consistent design tokens
- **Well-tested**: With comprehensive test coverage
## Basic Usage
<Canvas>
<Story name="ButtonExample">
<Button variant="primary">Hello World</Button>
</Story>
</Canvas>
## Component Examples
### Button Component
The Button component is versatile and supports multiple variants:
<Canvas>
<Story name="ButtonVariants">
<div style={{ display: 'flex', gap: '1rem' }}>
<Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="danger">Danger</Button>
<Button variant="ghost">Ghost</Button>
</div>
</Story>
</Canvas>
<ArgsTable of={Button} />
### Card Component
Cards are perfect for displaying grouped information:
<Canvas>
<Story name="CardExample">
<Card
title="Sample Card"
image="https://picsum.photos/400/200"
footer={<Button variant="primary">Learn More</Button>}
>
This is an example card with an image and footer action.
</Card>
</Story>
</Canvas>
## Design Tokens
Our components use a consistent design token system:
```javascript
// Primary colors
colors: {
primary: '#0070f3',
secondary: '#6c757d',
success: '#28a745',
danger: '#dc3545',
warning: '#ffc107',
}
// Spacing
spacing: {
xs: '0.25rem',
sm: '0.5rem',
md: '1rem',
lg: '1.5rem',
xl: '2rem',
}
```
// File: src/components/ThemeProvider/ThemeProvider.stories.js - Theme Provider
import React from 'react'
import { ThemeProvider } from './ThemeProvider'
import { Button, Card, Input } from '../index'
const themeProviderStoriesMeta = {
title: 'Design System/ThemeProvider',
component: ThemeProvider,
parameters: {
docs: {
description: {
component: 'Theme provider for managing global styles and design tokens.',
},
},
},
}
const theme = {
colors: {
primary: '#0070f3',
secondary: '#6c757d',
background: '#ffffff',
text: '#333333',
},
spacing: {
sm: '0.5rem',
md: '1rem',
lg: '1.5rem',
xl: '2rem',
},
}
export const Default = () => (
<ThemeProvider theme={theme}>
<div style={{ padding: '2rem' }}>
<h1>Themed Components</h1>
<div style={{ marginBottom: '1rem' }}>
<Button variant="primary">Themed Button</Button>
</div>
<Card title="Themed Card">
This card uses the theme provider for consistent styling.
</Card>
</div>
</ThemeProvider>
)
export const DarkTheme = () => {
const darkTheme = {
...theme,
colors: {
...theme.colors,
background: '#1a1a1a',
text: '#ffffff',
},
}
return (
<ThemeProvider theme={darkTheme}>
<div style={{ padding: '2rem', backgroundColor: '#1a1a1a', minHeight: '100vh' }}>
<h1 style={{ color: '#ffffff' }}>Dark Theme</h1>
<div style={{ marginBottom: '1rem' }}>
<Button variant="primary">Dark Button</Button>
</div>
<Card title="Dark Card">
This card uses the dark theme.
</Card>
</div>
</ThemeProvider>
)
}
💻 Advanced Storybook Patterns javascript
🔴 complex
⭐⭐⭐⭐
Advanced Storybook techniques including interactions, testing, automation, and integration with design tools
⏱️ 45 min
🏷️ storybook, advanced, automation, testing
Prerequisites:
Storybook basics, React hooks, Testing frameworks, CSS-in-JS
// Advanced Storybook Patterns
// File: .storybook/main.js - Advanced Configuration
module.exports = {
stories: [
'../stories/**/*.stories.@(js|jsx|ts|tsx|mdx)',
'../src/**/*.stories.@(js|jsx|ts|tsx|mdx)',
],
addons: [
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-interactions',
'@storybook/addon-a11y',
'@storybook/addon-docs',
'@storybook/addon-controls',
'@storybook/addon-backgrounds',
'@storybook/addon-viewport',
'@storybook/addon-toolbars',
'@storybook/addon-design-tokens',
'@storybook/addon-storysource',
'@storybook/preset-create-react-app',
'storybook-addon-designs',
'storybook-addon-measure',
'storybook-addon-performance',
'@storybook/addon-jest',
'@storybook/testing-react',
],
framework: '@storybook/react-vite',
core: {
disableTelemetry: true,
enableCrashReports: false,
},
env: (config) => ({
...config,
STORYBOOK_ENVIRONMENT: process.env.NODE_ENV || 'development',
}),
webpackFinal: async (config) => {
// Custom webpack configuration
config.resolve.alias = {
...config.resolve.alias,
'@': require('path').resolve(__dirname, '../src'),
}
return config
},
viteFinal: async (config) => {
// Custom Vite configuration
config.optimizeDeps = {
...config.optimizeDeps,
include: ['react', 'react-dom'],
}
return config
},
}
// File: .storybook/preview-head.html - Custom Head HTML
<style>
/* Global styles for Storybook */
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
margin: 0;
padding: 0;
}
/* Custom CSS variables for theming */
:root {
--color-primary: #0070f3;
--color-secondary: #6c757d;
--color-success: #28a745;
--color-danger: #dc3545;
--color-warning: #ffc107;
--spacing-xs: 0.25rem;
--spacing-sm: 0.5rem;
--spacing-md: 1rem;
--spacing-lg: 1.5rem;
--spacing-xl: 2rem;
}
/* Loading animation */
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.loading {
animation: spin 1s linear infinite;
}
</style>
<!-- Google Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
// File: src/components/Dropdown/Dropdown.stories.js - Interactive Components
import React, { useState } from 'react'
import { Dropdown, DropdownItem } from './Dropdown'
import { userEvent, within, waitFor } from '@storybook/testing-library'
const dropdownStoriesMeta = {
title: 'Components/Dropdown',
component: Dropdown,
parameters: {
layout: 'centered',
docs: {
description: {
component: 'Interactive dropdown component with keyboard navigation and accessibility features.',
},
},
},
argTypes: {
trigger: {
control: 'text',
description: 'Trigger button text',
},
placement: {
control: {
type: 'select',
},
options: ['bottom-start', 'bottom-end', 'top-start', 'top-end'],
description: 'Dropdown placement',
},
disabled: {
control: 'boolean',
description: 'Disable the dropdown',
},
},
}
const Template = (args) => {
const [isOpen, setIsOpen] = useState(false)
return (
<Dropdown
{...args}
isOpen={isOpen}
onToggle={setIsOpen}
>
<DropdownItem onClick={() => console.log('Action 1')}>
Action 1
</DropdownItem>
<DropdownItem onClick={() => console.log('Action 2')}>
Action 2
</DropdownItem>
<DropdownItem disabled>
Disabled Item
</DropdownItem>
<DropdownItem danger onClick={() => console.log('Delete')}>
Delete
</DropdownItem>
</Dropdown>
)
}
export const Default = Template.bind({})
Default.args = {
trigger: 'Click me',
}
export const Disabled = Template.bind({})
Disabled.args = {
trigger: 'Disabled Dropdown',
disabled: true,
}
// Interactive play function for testing
Default.play = async ({ canvasElement }) => {
const canvas = within(canvasElement)
// Find and click the trigger
const trigger = canvas.getByRole('button', { name: /click me/i })
await userEvent.click(trigger)
// Wait for dropdown to appear
await waitFor(() => {
expect(canvas.getByRole('menu')).toBeInTheDocument()
})
// Verify menu items
expect(canvas.getByRole('menuitem', { name: /action 1/i })).toBeInTheDocument()
expect(canvas.getByRole('menuitem', { name: /action 2/i })).toBeInTheDocument()
expect(canvas.getByRole('menuitem', { name: /delete/i })).toBeInTheDocument()
}
// File: src/components/Form/Form.stories.js - Complex Form Stories
import React, { useState } from 'react'
import { Form, FormField, FormSubmit } from './Form'
const formStoriesMeta = {
title: 'Components/Form',
component: Form,
parameters: {
docs: {
description: {
component: 'Advanced form component with validation, field management, and submission handling.',
},
},
},
}
const FormTemplate = (args) => {
const [formData, setFormData] = useState({
email: '',
password: '',
remember: false,
})
const handleSubmit = (data) => {
console.log('Form submitted:', data)
alert('Form submitted successfully!')
}
return (
<Form {...args} onSubmit={handleSubmit} initialData={formData}>
<FormField
name="email"
label="Email Address"
type="email"
required
placeholder="Enter your email"
/>
<FormField
name="password"
label="Password"
type="password"
required
placeholder="Enter your password"
/>
<FormField
name="remember"
type="checkbox"
label="Remember me"
/>
<FormSubmit>Sign In</FormSubmit>
</Form>
)
}
export const Default = FormTemplate.bind({})
Default.args = {
title: 'Sign In',
}
export const WithValidation = FormTemplate.bind({})
WithValidation.args = {
title: 'Sign In',
validation: {
email: {
required: true,
pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
},
password: {
required: true,
minLength: 8,
},
},
}
export const MultiStep = () => {
const [currentStep, setCurrentStep] = useState(0)
const [formData, setFormData] = useState({})
const steps = [
{
title: 'Personal Information',
fields: [
{ name: 'firstName', label: 'First Name', required: true },
{ name: 'lastName', label: 'Last Name', required: true },
{ name: 'email', label: 'Email', type: 'email', required: true },
],
},
{
title: 'Address',
fields: [
{ name: 'street', label: 'Street Address', required: true },
{ name: 'city', label: 'City', required: true },
{ name: 'zipCode', label: 'ZIP Code', required: true },
],
},
{
title: 'Preferences',
fields: [
{ name: 'newsletter', type: 'checkbox', label: 'Subscribe to newsletter' },
{ name: 'notifications', type: 'checkbox', label: 'Enable notifications' },
],
},
]
const currentStepData = steps[currentStep]
return (
<div style={{ maxWidth: '500px', margin: '0 auto' }}>
<h2>{currentStepData.title}</h2>
<Form
onSubmit={(data) => {
const updatedData = { ...formData, ...data }
if (currentStep < steps.length - 1) {
setFormData(updatedData)
setCurrentStep(currentStep + 1)
} else {
console.log('Form completed:', updatedData)
alert('Form completed successfully!')
}
}}
>
{currentStepData.fields.map((field) => (
<FormField
key={field.name}
name={field.name}
label={field.label}
type={field.type || 'text'}
required={field.required}
/>
))}
<div style={{ display: 'flex', justifyContent: 'space-between', marginTop: '1rem' }}>
<button
type="button"
onClick={() => setCurrentStep(Math.max(0, currentStep - 1))}
disabled={currentStep === 0}
>
Previous
</button>
<button type="submit">
{currentStep === steps.length - 1 ? 'Submit' : 'Next'}
</button>
</div>
</Form>
</div>
)
}
// File: .storybook/test-runner.js - Automated Testing
import { test, expect } from '@storybook/test-runner'
import { waitForElementToBeRemoved } from '@testing-library/dom'
test('renders button in default state', async ({ page }) => {
await page.goto('/iframe.html?id=components-button--default')
await expect(page.locator('button')).toContainText('Click me')
})
test('button click event works', async ({ page }) => {
await page.goto('/iframe.html?id=components-button--default&args=onClick:onClick')
await page.click('button')
await expect(page.locator('.action-log')).toContainText('clicked')
})
test('form validation works', async ({ page }) => {
await page.goto('/iframe.html?id=components-form--with-validation')
// Try to submit empty form
await page.click('button[type="submit"]')
// Check for error messages
await expect(page.locator('text=Email is required')).toBeVisible()
await expect(page.locator('text=Password is required')).toBeVisible()
// Fill in form correctly
await page.fill('input[name="email"]', '[email protected]')
await page.fill('input[name="password"]', 'password123')
// Submit should succeed
await page.click('button[type="submit"]')
await waitForElementToBeRemoved(() => page.locator('text=Email is required'))
})
// File: src/stories/playground.playground.tsx - Interactive Playground
import React, { useState } from 'react'
import { Playground } from '@storybook/addon-docs'
import { Button, Input, Card } from '../src/components'
<Playground
kind="Components/Interactive"
story="playground"
>
{{
component: () => {
const [count, setCount] = useState(0)
const [text, setText] = useState('')
return (
<div style={{ padding: '2rem', maxWidth: '400px' }}>
<Card title="Interactive Playground">
<div style={{ marginBottom: '1rem' }}>
<Input
placeholder="Type something..."
value={text}
onChange={(e) => setText(e.target.value)}
/>
</div>
<div style={{ marginBottom: '1rem' }}>
<p>You typed: {text}</p>
</div>
<div style={{ display: 'flex', gap: '0.5rem' }}>
<Button onClick={() => setCount(count + 1)}>
Count: {count}
</Button>
<Button variant="secondary" onClick={() => setCount(0)}>
Reset
</Button>
</div>
</Card>
</div>
)
}
}}
</Playground>
// File: src/hooks/useLocalStorage/useLocalStorage.stories.js - Hook Stories
import React from 'react'
import { useLocalStorage } from './useLocalStorage'
import { Meta, Story, Canvas, ArgsTable } from '@storybook/blocks'
const useLocalStorageStoriesMeta = {
title: 'Hooks/useLocalStorage',
component: useLocalStorage,
parameters: {
docs: {
description: {
component: 'Custom hook for persisting state in localStorage with SSR support.',
},
},
},
}
export const Default = () => {
const [value, setValue] = useLocalStorage('my-key', 'default-value')
return (
<div>
<input
type="text"
value={value}
onChange={(e) => setValue(e.target.value)}
style={{ padding: '0.5rem', marginRight: '0.5rem' }}
/>
<button onClick={() => setValue('')}>
Clear
</button>
<p>Current value: {value}</p>
</div>
)
}
export const WithObject = () => {
const [user, setUser] = useLocalStorage('user', {
name: '',
email: '',
preferences: { theme: 'light' },
})
return (
<div>
<div style={{ marginBottom: '1rem' }}>
<input
type="text"
placeholder="Name"
value={user.name}
onChange={(e) => setUser({ ...user, name: e.target.value })}
style={{ display: 'block', marginBottom: '0.5rem', padding: '0.25rem' }}
/>
<input
type="email"
placeholder="Email"
value={user.email}
onChange={(e) => setUser({ ...user, email: e.target.value })}
style={{ display: 'block', marginBottom: '0.5rem', padding: '0.25rem' }}
/>
<select
value={user.preferences.theme}
onChange={(e) => setUser({
...user,
preferences: { ...user.preferences, theme: e.target.value }
})}
style={{ display: 'block', padding: '0.25rem' }}
>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
</div>
<pre style={{ background: '#f5f5f5', padding: '1rem', borderRadius: '4px' }}>
{JSON.stringify(user, null, 2)}
</pre>
</div>
)
}
// File: .storybook/design-tokens.js - Design Token Integration
import { addDecorator } from '@storybook/preview'
// Design tokens for consistent styling
const designTokens = {
colors: {
primary: {
50: '#eff6ff',
100: '#dbeafe',
500: '#3b82f6',
600: '#2563eb',
700: '#1d4ed8',
},
gray: {
50: '#f9fafb',
100: '#f3f4f6',
500: '#6b7280',
600: '#4b5563',
700: '#374151',
},
},
spacing: {
1: '0.25rem',
2: '0.5rem',
3: '0.75rem',
4: '1rem',
5: '1.25rem',
6: '1.5rem',
8: '2rem',
12: '3rem',
16: '4rem',
},
typography: {
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
mono: ['Fira Code', 'Monaco', 'monospace'],
},
fontSize: {
xs: '0.75rem',
sm: '0.875rem',
base: '1rem',
lg: '1.125rem',
xl: '1.25rem',
},
},
}
// Apply design tokens globally
addDecorator((Story, context) => {
return (
<div
style={{
'--color-primary-50': designTokens.colors.primary[50],
'--color-primary-500': designTokens.colors.primary[500],
'--color-primary-600': designTokens.colors.primary[600],
'--color-gray-100': designTokens.colors.gray[100],
'--color-gray-600': designTokens.colors.gray[600],
'--spacing-4': designTokens.spacing[4],
'--spacing-6': designTokens.spacing[6],
'--font-sans': designTokens.typography.fontFamily.sans.join(', '),
'--font-base': designTokens.typography.fontSize.base,
}}
>
<Story {...context} />
</div>
)
})
const exportedDesignTokens = designTokens