1thay — design system
trăm nghe không bằng một thấy · single source of truth in figma
reference files: 1thay.md · figma.md · astro.md · storybook.md · design-tokens.md · project-logs.md
project overview
1thay is a design system for saas dashboards & infographics. it serves 1thay.com (astro docs), design.1thay.com (storybook), and figma.
key principle: figma is the single source of truth for design tokens. code mirrors figma.
stack:
astro: 5.x (static site)
storybook: 8.x (component dev)
react: 19.x (components)
figma: OAinJNCk0DZ1p1R1xyPfvx (token source)
deploy: cloudflare pages (auto from github)
fonts: Hanken Grotesk, Inter, Merriweather, Space Grotesk
icons: lucide (line stroke), @carbon/icons-react (shape fill)
core rules
- figma first — tokens change in figma, then sync to
src/tokens/tokens.css. never hardcode values. - lowercase — all ui text is lowercase. code identifiers follow js conventions (camelCase, PascalCase).
- bento grid — layouts use css grid with consistent
--radius-lgand spacing tokens. - storybook before astro — components are built and tested in storybook first, then used in astro pages.
- single file of sources —
src/tokens/tokens.cssis imported by both astro and storybook. - no style prop on react components in astro — react components receive style objects, not strings. wrap with astro divs for layout spacing.
project structure
src/
├── tokens/ # single source of truth for tokens
│ ├── tokens.css css custom properties (both astro + storybook import)
│ └── tokens.js js export (storybook theme, programmatic use)
├── components/ # react components (storybook + astro)
│ ├── Button/ *.tsx, *.css, *.stories.tsx
│ ├── Card/ root + Header/Body/Footer sub-components
│ ├── Input/ forwardRef + label + helperText
│ └── Icon/ lucide (line) + carbon (fill) wrappers
├── layouts/ # astro layouts
│ ├── BaseLayout.astro google fonts + global css
│ └── DocsLayout.astro sidebar nav (foundations/components/patterns)
├── pages/ # astro pages
│ ├── index.astro home hub
│ ├── foundations/ colors, typography, spacing, iconography
│ ├── components/ button, card, input (+ planned)
│ └── patterns/ signup, layout, navigation, data-viz
└── styles/
└── global.css base styles + token import
design tokens
all tokens live in src/tokens/tokens.css as css custom properties:
| category | prefix | examples |
|---|---|---|
| colors | --color- |
--color-brand-600, --color-neutral-500 |
| typography | --text- |
--text-hero, --text-body, --text-ui |
| spacing | --space- |
--space-xs through --space-3xl |
| radius | --radius- |
--radius-none through --radius-full |
| shadow | --shadow- |
--shadow-sm through --shadow-xl |
| size | --size- |
--size-icon-md, --size-btn-md, --size-input-md |
| opacity | --opacity- |
--opacity-disabled, --opacity-hover |
| z-index | --z- |
--z-dropdown through --z-tooltip |
| icon | --icon- |
--icon-stroke |
| component | --btn-, --card-, --input- |
component-level tokens |
how to add a component
- create
src/components/<Name>/directory name.css— styles using only design tokensName.tsx— react component with typescriptName.stories.tsx— storybook stories with autodocs + interaction testnpm run build-storybook— verifysrc/pages/components/name.astro— astro documentation page- update
DocsLayout.astrosidebar npm run build— verify
how to add a token
- change it in figma first (the source of truth)
- update
src/tokens/tokens.cssto match - update
src/tokens/tokens.jsto match - verify:
npm run build && npm run build-storybook
figma
- file: OAinJNCk0DZ1p1R1xyPfvx
- publish: assets panel (alt+2) → book icon → publish library → tick variables, text styles, effect styles
- use in other files: assets → libraries → enable "1thay"
- collections: Primitives, Color (Light/Dark), Spacing, Radius, Size, Opacity, Z-Index, Typography
commands
npm run dev # astro dev server
npm run build # astro production build → dist/
npm run storybook # storybook dev server (port 6006)
npm run build-storybook # storybook build → dist-storybook/
npm run lint # astro type check
deploy
- 1thay.com: cloudflare pages → github kien-day/1thay-com → framework: astro →
dist/ - design.1thay.com: cloudflare pages → github kien-day/1thay-com →
npm run build-storybook→dist-storybook/ - auto-deploy on push to main
naming conventions
- files/folders: kebab-case (
button-primary.astro,brand-colors.css) - components: PascalCase (
Button,Input,DocsLayout) - tokens:
--category-property-variant(--color-brand-600,--space-md) - figma variables:
category/name(blue-reputa/600,spacing/md) - figma pages:
◆ namefor docs,◇ namefor components - no abbreviations unless industry standard (ui, btn, sm/md/lg)
brands
- blue-reputa (default) — from
#064BAE - red-premier — from
#A10B2E - switch via
data-brand="red-premier"attribute