storybook — 1thay component workshop
build target:
design.1thay.com· port: 6006 · linked files: astro.md, design-tokens.md, CLAUDE.md
overview
storybook is the component development and testing environment for 1thay. components are built, documented, and tested here before integration into astro pages. storybook itself is themed with 1thay tokens (dogfooding).
project config
.storybook/main.ts
import type { StorybookConfig } from '@storybook/react-vite';
const config: StorybookConfig = {
stories: ['../src/**/*.stories.@(ts|tsx)'],
addons: [
'@storybook/addon-links', // story-to-story linking
'@storybook/addon-a11y', // accessibility audit (axe-core)
'@storybook/addon-docs', // auto-generated docs + MDX
'./figma-addon.ts', // custom Figma embed panel
],
framework: {
name: '@storybook/react-vite',
options: {},
},
staticDirs: ['../public'], // favicon, static assets
};
.storybook/preview.ts
- imports
src/tokens/tokens.cssandsrc/styles/global.css - configures backgrounds: surface (
#fff), dim (#f8fafc), dark (#0f172a) - default background: surface
- all stories tagged
autodocs
.storybook/manager.ts
- storybook UI themed with 1thay tokens: colors, fonts, border radius
- brand title: "1thay design system"
- brand url:
https://1thay.com
package.json scripts
{
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build -o dist-storybook"
}
deploy
| setting | value |
|---|---|
| platform | cloudflare pages |
| repo | kien-day/1thay-com |
| branch | main |
| build command | npm run build-storybook |
| output directory | dist-storybook/ |
| domain | design.1thay.com |
| preview | design.1thay-com.pages.dev |
component inventory
completed
| component | variants | stories | figma | astro |
|---|---|---|---|---|
| component | variants | stories | figma | astro |
| --- | --- | --- | --- | --- |
| Button | primary, secondary, ghost, destructive | 11 | ✅ | ✅ |
| Card | default, flat, raised, bordered | 8 | ✅ | ✅ |
| Input | sm, md, lg + label/helper/invalid | 9 | ✅ | ✅ |
| Icon | lucide (line), carbon (fill) | 8 | ✅ | ✅ |
| Badge | neutral, brand, success, warning, error | 6 | ✅ | ✅ |
| Toast | success, error, info | 3 | ✅ | — |
planned
table, chart, modal, select, checkbox, switch, avatar, progress, tooltip, pagination
table, badge, chart, modal, tabs, toast, select, checkbox, switch, avatar, progress, tooltip, pagination
component anatomy
every component follows this structure:
src/components/Name/
├── name.css # styles using only design tokens
├── Name.tsx # react component with typescript
├── Name.stories.tsx # storybook stories with autodocs
└── (optional sub-components)
css rules
- only use design tokens — never hardcode colors, spacing, radius, shadows
- import from
src/tokens/tokens.css(available globally via preview.ts) - use component-level tokens when available (
--btn-*,--card-*,--input-*) - lowercase text via
text-transform: lowercase - all interactive elements must have
:focus-visiblestyles
typescript rules
- use
forwardReffor form elements (Input) - export types for all props interfaces
- sub-components exported as named exports alongside root
- component library is tree-shakeable — each component is a separate import
storybook stories rules
tags: ['autodocs']on meta — auto-generates docs page- every variant has its own story
- every state has its own story
- at least one interaction test using
play()+@storybook/test - stories use descriptive vietnamese labels where appropriate
- use real design token values in story demos
story conventions
title format
components/Button # top-level component
components/Icon # icon wrappers
story naming
| type | pattern | example |
|---|---|---|
| variant | Primary, Secondary |
export const Primary: Story |
| size | Small, Large |
export const Small: Story |
| state | Disabled, Loading |
export const Disabled: Story |
| composed | WithHeaderBodyFooter |
export const WithHeaderBodyFooter: Story |
| interactive | Clickable, Typing |
export const Typing: Story |
argTypes
argTypes: {
variant: { control: 'select', options: ['primary', 'secondary', 'ghost', 'destructive'] },
size: { control: 'select', options: ['sm', 'md', 'lg'] },
disabled: { control: 'boolean' },
loading: { control: 'boolean' },
}
interaction testing
every component has at least one play() test:
import { expect, fn, userEvent, within } from '@storybook/test';
export const Clickable: Story = {
args: { onClick: fn(), children: 'click me' },
play: async ({ canvasElement, args }) => {
const canvas = within(canvasElement);
const btn = canvas.getByRole('button');
await userEvent.click(btn);
await expect(args.onClick).toHaveBeenCalled();
},
};
adding a new component
- create
src/components/<Name>/directory name.css— styles using only design tokens (see design-tokens.md)Name.tsx— react component with typescript + props interface exportName.stories.tsx— stories covering all variants, sizes, states + 1 interaction testnpm run build-storybook— verify build passes- create
src/pages/components/name.astro— documentation page (see astro.md) - update
DocsLayout.astrosidebar - update this file's component inventory
- update
project-logs.md
dogfooding
storybook itself uses 1thay design tokens:
- manager UI (sidebar, toolbar): themed with 1thay colors + Inter font
- preview: imports
tokens.css— all stories render with real tokens - backgrounds: surface, dim, dark match design token palette
- docs: autodocs show real token values in use
- a11y addon: ensures components meet WCAG standards
figma integration
mỗi component story phải có figma link qua parameters.design:
parameters: {
design: {
type: 'figma',
url: 'https://www.figma.com/embed?embed_host=storybook&url=https://www.figma.com/design/OAinJNCk0DZ1p1R1xyPfvx/1thay',
},
},
- figma file key:
OAinJNCk0DZ1p1R1xyPfvx - figma file url:
https://www.figma.com/design/OAinJNCk0DZ1p1R1xyPfvx/1thay - dùng embed URL format để hiển thị trực tiếp trong Storybook tab "Figma"
- custom addon tại
.storybook/figma-addon.ts(storybook/manager-api, types.PANEL) - khi figma có component frames, cập nhật
figma.config.jsonvới node IDs và chạy Code Connect
token sync workflow
- figma thay đổi tokens → update
tokens.css+tokens.js - chạy
npm run snapshot-tokensđể cập nhật baseline - chạy
npm run check-tokensđể verify không drift - cập nhật
project-logs.mdchangelog - cập nhật
design-tokens.mdnếu giá trị thay đổi
figma → code mapping
xem figma.config.json để biết:
- component mappings (figmaNodeId, codePath, props)
- token collection mappings (8 collections → tokens.css)
- style mappings (12 text styles + 4 shadow styles → CSS variables)
deploy
cloudflare pages (2 projects from 1 repo)
| project | build command | output dir | domain |
|---|---|---|---|
1thay-com |
npm run build |
dist |
1thay.com |
1thay-design |
npm run build-storybook |
dist-storybook |
design.1thay.com |
deploy workflow
- push lên
main→ cloudflare auto-deploy (nếu build settings đúng trong dashboard) - nếu auto-deploy fail:
npx wrangler pages deploy dist --project-name=1thay-com --branch=main - nếu auto-deploy fail:
npx wrangler pages deploy dist-storybook --project-name=1thay-design --branch=main
build settings cho 1thay-design (trong cloudflare dashboard)
- Build command:
npm run build-storybook - Build output directory:
dist-storybook - Framework preset: None (disable auto-detect — tránh detect nhầm astro)
verify trước khi deploy
npm run build # astro build → dist/
npm run build-storybook # storybook build → dist-storybook/
npm run check-tokens # token drift check (118 tokens baseline)
current state (2026-05-27)
storybook 10.4.1
| category | items |
|---|---|
| framework | react-vite, vite 6, react 19 |
| addons | links, a11y (axe-core), docs, figma (custom) |
| stories | 6 components + tokens showcase + examples |
| interaction tests | Button (click), Input (typing), Examples (login flow) |
| themes | manager UI themed with 1thay tokens (manager.ts) |
components inventory
| component | stories | figma | astro page | interaction test |
|---|---|---|---|---|
| Button | 11 | ✅ | ✅ | ✅ (clickable) |
| Card | 8 | ✅ | ✅ | — |
| Input | 9 | ✅ | ✅ | ✅ (typing) |
| Icon | 8 | ✅ | ✅ | — |
| Badge | 6 | ✅ | ✅ | — |
| Toast | 3 | ✅ | — | — |
| Examples | 6 | ✅ | — | ✅ (login flow) |
| Tokens | 8 | ✅ | — | — |
key files
| file | purpose |
|---|---|
.storybook/main.ts |
framework + addons config |
.storybook/preview.ts |
global decorators, backgrounds, controls |
.storybook/manager.ts |
manager UI theme (1thay dogfooding) |
.storybook/figma-addon.ts |
custom Figma embed panel |
src/tokens/tokens.css |
118 css custom properties (single source of truth) |
src/tokens/tokens.js |
js mirror for programmatic use |
figma.config.json |
figma code connect manifest |
scripts/check-token-drift.js |
ci token drift detection |
wrangler.toml |
astro cloudflare pages config |
wrangler.storybook.toml |
storybook cloudflare pages config |
pre-commit checklist
-
npm run buildpasses -
npm run build-storybookpasses -
npm run check-tokenspasses (no drift) - all new components have
parameters.design(figma link) - all new components have at least 1
play()interaction test - all css uses
var(--token), không hardcode giá trị - story labels dùng tiếng việt lowercase
- storybook.md component inventory updated
- project-logs.md changelog updated
linked references
- astro.md — astro documentation site setup
- figma.md — figma file, variable conventions, sync workflow
- 1thay.md — philosophy, naming conventions
- design-tokens.md — complete token reference
- design.md — google labs machine-readable spec
- project-logs.md — changelog and roadmap
- CLAUDE.md — claude code agent configuration