Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
- [ ] `pnpm lint`
- [ ] `pnpm typecheck`
- [ ] `pnpm build --filter=@comma/web`
- [ ] `pnpm build:storybook`

## Notes

Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,8 @@ jobs:
- name: Build web
run: pnpm build --filter=@comma/web

- name: Build Storybook
run: pnpm build:storybook

- name: Check Expo dependencies
run: pnpm --filter @comma/mobile-shell exec expo install --check
62 changes: 62 additions & 0 deletions .github/workflows/deploy-storybook.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: Deploy Storybook

on:
push:
branches:
- main
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

concurrency:
group: github-pages
cancel-in-progress: false

jobs:
build:
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10.0.0

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version-file: .node-version
cache: pnpm

- name: Setup Pages
uses: actions/configure-pages@v5

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Build Storybook
run: pnpm build:storybook

- name: Upload Storybook artifact
uses: actions/upload-pages-artifact@v4
with:
path: apps/storybook/storybook-static

deploy:
runs-on: ubuntu-latest
needs: build

environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}

steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ node_modules
.turbo
dist
build
storybook-static
*.tsbuildinfo
.expo
.env
Expand Down
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ nvm use
pnpm install
pnpm dev:web
pnpm dev:web:lan
pnpm dev:storybook
pnpm dev:mobile
pnpm dev:mobile-client
pnpm typecheck
Expand Down Expand Up @@ -61,6 +62,7 @@ pnpm dev:web:lan
pnpm dev # web + mobile-shell 전체 dev task
pnpm dev:web # React web만 실행, local/simulator
pnpm dev:web:lan # React web을 LAN 접근 가능하게 실행, 실기기용
pnpm dev:storybook # 디자인 시스템 Storybook 실행
pnpm dev:mobile # Expo shell만 실행
pnpm dev:mobile-client # installed dev-client 앱에 연결
pnpm ios:dev-client # iOS dev-client 빌드/실행
Expand All @@ -70,8 +72,19 @@ pnpm lint # Biome 검사
pnpm format # Biome 포맷
pnpm check # Biome 자동 수정 가능한 항목 반영
pnpm build # 전체 빌드
pnpm build:storybook # Storybook 정적 빌드
```

## Storybook

Storybook은 GitHub Pages로 배포합니다.

```txt
https://central-makeus.github.io/comma-Front/
```

GitHub Pages source는 repository Settings에서 `GitHub Actions`로 설정되어 있어야 합니다.

## Commit Convention

커밋 메시지는 Conventional Commits 형식을 사용합니다. Husky `commit-msg` hook이 커밋 시 자동으로 검사합니다.
Expand Down
5 changes: 5 additions & 0 deletions apps/mobile-shell/src/env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
declare const process: {
env: {
EXPO_PUBLIC_WEB_URL?: string;
};
};
27 changes: 27 additions & 0 deletions apps/storybook/.storybook/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { StorybookConfig } from '@storybook/react-vite';
import { vanillaExtractPlugin } from '@vanilla-extract/vite-plugin';

const config: StorybookConfig = {
stories: ['../src/**/*.stories.@(ts|tsx)'],
addons: [],
framework: {
name: '@storybook/react-vite',
options: {}
},
async viteFinal(config) {
config.plugins = [...(config.plugins ?? []), vanillaExtractPlugin()];
config.optimizeDeps = {
...config.optimizeDeps,
include: [
...(config.optimizeDeps?.include ?? []),
'react',
'react-dom',
'react/jsx-dev-runtime',
'react/jsx-runtime'
]
};
return config;
}
};

export default config;
23 changes: 23 additions & 0 deletions apps/storybook/.storybook/preview.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type { Preview } from '@storybook/react-vite';
import '../src/preview.css';

const preview: Preview = {
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i
}
},
backgrounds: {
default: 'Surface',
values: [
{ name: 'Surface', value: '#FDFCFC' },
{ name: 'Accent', value: '#5375B1' },
{ name: 'Dark', value: '#302C2C' }
]
}
}
};

export default preview;
26 changes: 26 additions & 0 deletions apps/storybook/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "@comma/storybook",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "storybook dev -p 6006",
"build-storybook": "storybook build",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@comma/design-system": "workspace:*",
"@vanilla-extract/css": "^1.21.0",
"react": "19.0.0",
"react-dom": "19.0.0"
},
"devDependencies": {
"@storybook/react-vite": "^10.4.6",
"@types/react": "~19.0.10",
"@types/react-dom": "19.0.3",
"@vanilla-extract/vite-plugin": "^5.2.3",
"storybook": "^10.4.6",
"typescript": "~5.8.3",
"vite": "^6.3.0"
}
}
76 changes: 76 additions & 0 deletions apps/storybook/src/Button.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { actionButton, themeClass, vars } from '@comma/design-system';
import type { Meta, StoryObj } from '@storybook/react-vite';

type ButtonStoryArgs = {
children: string;
tone: 'primary' | 'secondary';
disabled: boolean;
};

const meta = {
title: 'Design System/Button',
argTypes: {
tone: {
control: 'inline-radio',
options: ['primary', 'secondary']
},
disabled: {
control: 'boolean'
}
},
args: {
children: '다음',
tone: 'primary',
disabled: false
}
} satisfies Meta<ButtonStoryArgs>;

export default meta;

type Story = StoryObj<ButtonStoryArgs>;

export const Playground: Story = {
render: ({ children, tone, disabled }) => (
<div
className={themeClass}
style={{
minHeight: 240,
display: 'grid',
placeItems: 'center',
background: vars.color.background,
fontFamily: vars.font.body
}}
>
<button className={actionButton({ tone })} disabled={disabled} type="button">
{children}
</button>
</div>
)
};

export const Variants: Story = {
render: () => (
<div
className={themeClass}
style={{
minHeight: 240,
display: 'flex',
alignItems: 'center',
gap: 12,
padding: 32,
background: vars.color.background,
fontFamily: vars.font.body
}}
>
<button className={actionButton()} type="button">
Primary
</button>
<button className={actionButton({ tone: 'secondary' })} type="button">
Secondary
</button>
<button className={actionButton()} disabled type="button">
Disabled
</button>
</div>
)
};
107 changes: 107 additions & 0 deletions apps/storybook/src/DesignTokens.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import {
colors,
radii,
shadows,
spacing,
themeClass,
typography,
vars
} from '@comma/design-system';
import type { Meta, StoryObj } from '@storybook/react-vite';

const meta = {
title: 'Design System/Tokens'
} satisfies Meta;

export default meta;

type Story = StoryObj<typeof meta>;

const tokenGroupStyle: React.CSSProperties = {
display: 'grid',
gap: 24,
color: vars.color.textPrimary,
fontFamily: vars.font.body
};

const sectionStyle: React.CSSProperties = {
display: 'grid',
gap: 12
};

const gridStyle: React.CSSProperties = {
display: 'grid',
gridTemplateColumns: 'repeat(auto-fill, minmax(160px, 1fr))',
gap: 12
};

export const Tokens: Story = {
render: () => (
<div className={themeClass} style={{ padding: 32, background: vars.color.background }}>
<div style={tokenGroupStyle}>
<section style={sectionStyle}>
<h2 style={{ margin: 0, ...typography.heading1 }}>Colors</h2>
<div style={gridStyle}>
{Object.entries(colors).map(([name, value]) => (
<div
key={name}
style={{
border: `1px solid ${vars.color.line}`,
borderRadius: radii.md,
overflow: 'hidden',
background: vars.color.surface
}}
>
<div style={{ height: 72, background: value }} />
<div style={{ display: 'grid', gap: 4, padding: 10 }}>
<strong style={typography.label1}>{name}</strong>
<span style={{ ...typography.caption, color: vars.color.textSecondary }}>
{value}
</span>
</div>
</div>
))}
</div>
</section>

<section style={sectionStyle}>
<h2 style={{ margin: 0, ...typography.heading1 }}>Typography</h2>
<div style={sectionStyle}>
{Object.entries(typography).map(([name, style]) => (
<div key={name} style={{ color: vars.color.textPrimary, ...style }}>
{name} · 빠른 갈색 여우가 게으른 개를 뛰어넘습니다.
</div>
))}
</div>
</section>

<section style={sectionStyle}>
<h2 style={{ margin: 0, ...typography.heading1 }}>Spacing / Radius / Shadow</h2>
<div style={gridStyle}>
{Object.entries(spacing).map(([name, value]) => (
<div key={name} style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
<div style={{ width: value, height: 16, background: vars.color.accent }} />
<span style={typography.caption}>
{name}: {value}
</span>
</div>
))}
{Object.entries(shadows).map(([name, value]) => (
<div
key={name}
style={{
padding: 16,
borderRadius: radii.md,
background: vars.color.surface,
boxShadow: value
}}
>
<span style={typography.caption}>{name}</span>
</div>
))}
</div>
</section>
</div>
</div>
)
};
Loading
Loading