Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
5d29e17
feat: add Storybook design system
Arata1202 Aug 28, 2026
521b13f
fix: align Playwright dependencies
Arata1202 Aug 28, 2026
bb6f49f
refactor: use Storybook initial globals
Arata1202 Aug 28, 2026
d13515a
fix(storybook): match production component themes
Arata1202 Aug 28, 2026
7b29664
test(storybook): cover article table rendering
Arata1202 Aug 28, 2026
74d2d24
test(storybook): follow global theme in theme switch story
Arata1202 Aug 28, 2026
90d90e9
test(storybook): remove duplicate query pagination story
Arata1202 Aug 28, 2026
310eab7
fix(storybook): synchronize component theme controls
Arata1202 Aug 28, 2026
16e0a47
fix(storybook): apply mobile viewport globals
Arata1202 Aug 28, 2026
57b7c9c
test(storybook): match production alert content
Arata1202 Aug 28, 2026
c86e6cc
test(storybook): match production modal content
Arata1202 Aug 28, 2026
a14adac
docs(storybook): document application design foundations
Arata1202 Aug 28, 2026
c05eeef
test(storybook): keep foundation docs aligned
Arata1202 Aug 28, 2026
a506a64
test(storybook): satisfy sponsored article constraints
Arata1202 Aug 28, 2026
8e1c9da
test(storybook): cover remaining visual components
Arata1202 Aug 28, 2026
423f74b
test(storybook): clarify article source variants
Arata1202 Aug 28, 2026
ae57088
test(storybook): remove duplicate article source stories
Arata1202 Aug 28, 2026
75094c9
fix(storybook): load slick carousel styles
Arata1202 Aug 28, 2026
fbbfe04
test(storybook): restore mixed article sources
Arata1202 Aug 28, 2026
9c9b3cf
fix(storybook): cover accessible custom html
Arata1202 Aug 28, 2026
0c77f5a
feat(storybook): add Chromatic workflow and update dependencies
Arata1202 Aug 29, 2026
fb82370
fix: update CI/CD and design references in documentation and workflows
Arata1202 Aug 29, 2026
cd85e80
feat(storybook): add data-app-root attribute for improved theming sup…
Arata1202 Aug 29, 2026
8997369
docs: publish architecture overview
Arata1202 Aug 29, 2026
663f986
feat(storybook): add data-app-root attribute to Dialog components for…
Arata1202 Aug 29, 2026
14634e0
docs: clarify Storybook browser setup
Arata1202 Aug 29, 2026
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
37 changes: 37 additions & 0 deletions .github/workflows/chromatic.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Chromatic

on:
pull_request:
push:
branches:
- master

permissions:
contents: read

jobs:
chromatic:
name: Publish Storybook
if: github.actor != 'dependabot[bot]' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository)
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10.8.0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Publish to Chromatic
uses: chromaui/action@latest
with:
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
exitOnceUploaded: true
4 changes: 4 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,12 @@ jobs:
run: pnpm typecheck
- name: Test
run: pnpm test:run
- name: Build Storybook
run: pnpm build-storybook
- name: Install Playwright browsers
run: pnpm exec playwright install --with-deps chromium
- name: Test Storybook
run: pnpm test:storybook
- name: E2E
run: pnpm test:e2e
go:
Expand Down
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,11 @@ next-env.d.ts

# air
/tmp

/debug-storybook.log
/build-storybook.log
/storybook-build.log
/chromatic.log
/chromatic-build-*.xml
/chromatic-diagnostics.json
/storybook-static/
10 changes: 10 additions & 0 deletions .storybook/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { StorybookConfig } from '@storybook/nextjs-vite';

const config: StorybookConfig = {
stories: ['../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
addons: ['@storybook/addon-vitest', '@storybook/addon-a11y', '@storybook/addon-docs'],
framework: '@storybook/nextjs-vite',
staticDirs: ['../public'],
};

export default config;
107 changes: 107 additions & 0 deletions .storybook/preview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import type { Decorator, Preview } from '@storybook/nextjs-vite';
import { useEffect, useRef, type ReactNode } from 'react';
import { ThemeProvider, useTheme } from 'next-themes';
import { useGlobals } from 'storybook/preview-api';
import 'slick-carousel/slick/slick.css';
import 'slick-carousel/slick/slick-theme.css';
import '@/styles/globals.css';

type Theme = 'light' | 'dark';

type ThemeSyncProps = {
globalTheme: Theme;
updateGlobals: (globals: Record<string, unknown>) => void;
children: ReactNode;
};

function ThemeSync({ globalTheme, updateGlobals, children }: ThemeSyncProps) {
const { resolvedTheme, setTheme } = useTheme();
const previousGlobalTheme = useRef(globalTheme);
const initialized = useRef(false);

useEffect(() => {
if (resolvedTheme !== 'light' && resolvedTheme !== 'dark') {
return;
}

if (!initialized.current) {
initialized.current = true;
setTheme(globalTheme);
return;
}

if (previousGlobalTheme.current !== globalTheme) {
previousGlobalTheme.current = globalTheme;
setTheme(globalTheme);
return;
}

if (resolvedTheme !== globalTheme) {
updateGlobals({ theme: resolvedTheme });
}
}, [globalTheme, resolvedTheme, setTheme, updateGlobals]);

return children;
}

const WithTheme: Decorator = (Story, context) => {
const [, updateGlobals] = useGlobals();
const theme = context.globals.theme === 'dark' ? 'dark' : 'light';
const themeClassName = theme === 'dark' ? 'DarkTheme' : 'LightTheme';

document.body.classList.toggle('DarkTheme', theme === 'dark');
document.body.classList.toggle('LightTheme', theme === 'light');

return (
<ThemeProvider
defaultTheme={theme}
enableSystem={false}
storageKey={`storybook-theme-${context.id}`}
>
<ThemeSync globalTheme={theme} updateGlobals={updateGlobals}>
<div className={`${themeClassName} min-h-screen p-6`} data-app-root>
<Story />
</div>
</ThemeSync>
</ThemeProvider>
);
};

const preview: Preview = {
decorators: [WithTheme],
globalTypes: {
theme: {
description: 'Component theme',
toolbar: {
icon: 'paintbrush',
items: [
{ value: 'light', title: 'Light' },
{ value: 'dark', title: 'Dark' },
],
dynamicTitle: true,
},
},
},
initialGlobals: {
theme: 'light',
},
parameters: {
layout: 'fullscreen',
nextjs: {
appDirectory: true,
navigation: {
pathname: '/',
},
},
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},

a11y: { test: 'error' },
},
};

export default preview;
91 changes: 33 additions & 58 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
- [アーキテクチャ](#アーキテクチャ)
- [環境構築](#環境構築)
- [スポンサー記事](#スポンサー記事)
- [Storybook](#storybook)
- [Terraform](#terraform)
- [テスト](#テスト)
- [OneSignalテスト通知](#onesignalテスト通知)
Expand All @@ -30,8 +31,7 @@

<ul>
<li><a href="https://realunivlog.com">リアル大学生</a></li>
<li><a href="https://www.figma.com/design/Fa4LsgTvBhWAu4sIcwYy1O/NextBlogApp?node-id=2102-4673">Figma(Archived Design Tokens)</a></li>
<li><a href="https://www.figma.com/design/Fa4LsgTvBhWAu4sIcwYy1O/NextBlogApp?node-id=0-1">Figma(Archived UI)</a></li>
<li><a href="https://master--6a922f631bdb93f72e661b33.chromatic.com/">Storybook</a></li>
</ul>

<p align="right">(<a href="#top">トップへ</a>)</p>
Expand All @@ -45,69 +45,16 @@
| CMS | microCMS, Zenn RSS |
| Infrastructure | Cloudflare Pages, Vercel, Amazon S3, Terraform |
| Environment setup | Node.js, pnpm, Docker Compose |
| CI/CD | GitHub Actions, CodeQL, Dependabot |
| Design | Canva(Figmaはアーカイブ済み) |
| CI/CD | GitHub Actions, Chromatic, CodeQL, Dependabot |
| Design | Storybook, Canva |
| Google | AdSense, Analytics, Search Console, reCAPTCHA |
| Integrations | PWA, OneSignal, Sentry, Iframely, Instagram |

<p align="right">(<a href="#top">トップへ</a>)</p>

## アーキテクチャ

```mermaid
flowchart TB
developer[Developer] --> github[GitHub Repository]

subgraph ci[CI]
github --> actions[GitHub Actions]
actions --> quality[Lint / Typecheck / Test / CodeQL]
end

subgraph build[Static Build]
github --> pagesBuild[Cloudflare Pages Build<br/>pnpm build]
microcms[microCMS<br/>Blog / Category / Tag] --> pagesBuild
zenn[Zenn RSS] --> pagesBuild
pagesBuild --> generated[Static Output<br/>HTML / JS / CSS / RSS / sitemap]
end

generated --> pages[Cloudflare Pages<br/>out]
pages --> browser[User Browser]

subgraph runtime[Browser Runtime]
browser --> app[Next.js Client App]
app --> thirdParty[Google Analytics / AdSense<br/>OneSignal / Iframely / Instagram]
app -. Client errors .-> sentry[Sentry]
end

subgraph vercelFunctions[Vercel Functions]
app --> searchApi[Vercel Go Function<br/>/api/search]
searchApi --> microcmsBlogContentApi[microCMS<br/>Blog Content API]
searchApi --> zenn
app --> sendEmailApi[Vercel Go Function<br/>/api/sendemail]
recaptchaApi[Vercel Go Function<br/>/api/recaptcha]
sendEmailApi --> smtp[SMTP / Gmail]
sendEmailApi --> recaptcha[Google reCAPTCHA]
recaptchaApi --> recaptcha
cron[Vercel Cron<br/>/api/cron/linkchecker] --> linkcheckerApi[Vercel Go Function<br/>Link Checker]
linkcheckerApi --> microcmsBlogContentApi
linkcheckerApi --> smtp
linkcheckerApi --> zenn
linkcheckerApi --> s3
linkcheckerApi --> oneSignalApi
linkcheckerApi --> pagesDeployHook[Cloudflare Pages Deploy Hook]
pagesDeployHook --> pagesBuild
microcmsWebhook[microCMS<br/>Content Webhook] --> microcmsbackupApi[Vercel Go Function<br/>/api/webhook/microcmsbackup]
microcmsbackupApi --> microcmsBlogContentApi
microcmsbackupApi --> s3[AWS S3<br/>Backup CSV<br/>Notification Marker]
microcmsbackupApi --> oneSignalApi[OneSignal<br/>Push Notification API]
end

searchApi -. Server errors .-> sentry
sendEmailApi -. Server errors .-> sentry
recaptchaApi -. Server errors .-> sentry
linkcheckerApi -. Server errors .-> sentry
microcmsbackupApi -. Server errors .-> sentry
```
![NextBlogAppアーキテクチャ](public/images/architecture/nextblogapp.png)

<p align="right">(<a href="#top">トップへ</a>)</p>

Expand Down Expand Up @@ -168,6 +115,28 @@ microCMSの`blog` APIに以下のフィールドを追加する。

<p align="right">(<a href="#top">トップへ</a>)</p>

## Storybook

共通UIと主要な表示状態はStorybookで確認する。ツールバーからLight/Darkテーマを切り替えられ、AccessibilityパネルとブラウザテストでWCAG違反や操作を検証できる。

```bash
# Storybookを起動
pnpm storybook

# 静的Storybookを生成
pnpm build-storybook

# Chromiumをインストール(初回またはPlaywright更新後)
pnpm exec playwright install chromium

# StoryとアクセシビリティをChromiumで検証
pnpm test:storybook
```

Storyは対象コンポーネントと同じディレクトリの`index.stories.tsx`へ配置する。基礎スタイルと共通fixtureは`src/stories/`で管理する。

<p align="right">(<a href="#top">トップへ</a>)</p>

## Terraform

```
Expand Down Expand Up @@ -200,6 +169,10 @@ pnpm lint
pnpm typecheck
pnpm test:run

# Storybookの静的ビルド / ブラウザテスト
pnpm build-storybook
pnpm test:storybook

# Goの静的解析 / テスト(Docker)
docker compose run --rm go go vet ./...
docker compose run --rm go go test ./...
Expand Down Expand Up @@ -247,6 +220,7 @@ scripts/send-onesignal-test-notifications.sh --send
```text
.
├── .github/workflows/ # CI/CD・運用ワークフロー
├── .storybook/ # Storybook設定
├── api/ # Vercel Functionsのエントリーポイント
├── cmd/ # ローカルGo APIサーバー
├── docker/ # 開発用Dockerイメージ
Expand All @@ -258,6 +232,7 @@ scripts/send-onesignal-test-notifications.sh --send
├── src/
│ ├── app/ # Next.js App Router
│ ├── components/ # UIコンポーネント
│ ├── stories/ # 基礎スタイル・共通Story fixture
│ ├── config/ # 環境変数の参照
│ ├── contents/ # 固定ページのコンテンツ
│ ├── hooks/ # React Hooks
Expand Down
4 changes: 4 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// For more info, see https://github.com/storybookjs/eslint-plugin-storybook#configuration-flat-config-format
import storybook from 'eslint-plugin-storybook';

import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';
import nextPlugin from '@next/eslint-plugin-next';
Expand Down Expand Up @@ -75,4 +78,5 @@ export default [
'@typescript-eslint/no-explicit-any': 'off',
},
},
...storybook.configs['flat/recommended'],
];
Loading
Loading