| name | frontend-flow |
| description | Ensures frontend code quality by enforcing Design System (9 ESLint rules), Storybook coverage, and 5-step verification pipeline. Use when creating/modifying React components in frontend/src/, especially shared/ where quality gates are mandatory. Triggers on "create component", "verify frontend", "storybook coverage", "перевір фронтенд", "frontend flow". Goal - all verification passes before task completion, knowledge captured in vault. |
Frontend Flow
Quality-first frontend development workflow for Pulse Radar.
Overview
┌─────────────────────────────────────────────────────────────┐
│ QUALITY-FOCUSED FRONTEND WORKFLOW │
│ │
│ 1. SESSION 2. DISCOVERY 3. IMPLEMENT 4. STORYBOOK │
│ ──────────── ──────────── ──────────── ──────────── │
│ Journal Storybook MCP Tokens Stories │
│ Beads Vault Search Patterns Screenshots │
│ Context AGENTS.md Design System Interaction │
│ │
│ 5. VERIFICATION (CRITICAL) 6. KNOWLEDGE │
│ ──────────────────────────── ──────────── │
│ TypeScript → ESLint → Tests Journal done │
│ Story check → Accessibility Capture │
│ ALL MUST PASS Beads update │
└─────────────────────────────────────────────────────────────┘
Principle: Quality over speed. Verification must pass before task completion.
Phase 1: Session & Context
Trigger: Starting frontend work
Actions:
- Start journal session:
/obsidian:journal session "Task title" - Get issue context:
bd show {issue} - Read previous handoff if exists
Goal: Establish context and enable progress tracking.
Phase 2: Discovery (CRITICAL!)
Trigger: BEFORE creating or modifying any component
Required checks:
# 1. Check existing components in Storybook
storybook_list_components
# 2. Get props API for similar components
storybook_get_component_props componentId="..."
# 3. Search knowledge vault
/obsidian:search "relevant pattern"
# 4. Read Design System rules
frontend/AGENTS.md
Checkpoint: "Existing component found? → USE IT instead of creating new!"
Anti-pattern: Creating new component without Storybook check = quality violation.
→ See @references/discovery.md for detailed checklist.
Phase 3: Implementation (Design System)
Trigger: During code writing
Required rules:
| Rule | Use | Don't Use |
|---|---|---|
| Colors | @shared/tokens/* |
bg-red-500, text-green-600 |
| Patterns | @shared/patterns/* |
Manual composition |
| Icons | lucide-react |
heroicons, Radix icons |
| Touch | h-11 w-11 (44px) |
h-9 w-9 (36px) |
| Spacing | gap-2, gap-4, p-4 |
gap-3, p-5 (off-grid) |
| Icon buttons | aria-label="..." |
No label |
| Status | Icon + Text | Color only |
Related skills: /frontend, /design-tokens
Phase 3.5: Component Portability (NEW!)
Trigger: When creating/modifying components in shared/
Principle: Components in shared/ must be PORTABLE — work without API, store, router.
┌─────────────────────────────────────────────────────────────┐
│ PORTABLE COMPONENT = Pure Function │
│ │
│ Props → Component → UI │
│ │
│ ✅ No useQuery/useMutation inside │
│ ✅ No apiClient/fetch calls inside │
│ ✅ No useNavigate/useParams/useLocation │
│ ✅ No useStore (Zustand) directly │
│ ✅ All data via props │
│ ✅ Works in Storybook WITHOUT providers │
└─────────────────────────────────────────────────────────────┘
Anti-pattern (FORBIDDEN):
// ❌ NON-PORTABLE — violates CDD principles
import { useQuery } from '@tanstack/react-query'
import { apiClient } from '@/shared/lib/api'
export function ActivityHeatmap() {
const { data } = useQuery({ queryFn: () => apiClient.get('/activity') })
return <Heatmap data={data} />
}
Correct pattern (Presenter + Hook):
// ✅ PORTABLE PRESENTER — shared/components/
interface ActivityHeatmapProps {
data: ActivityDataPoint[]
isLoading?: boolean
className?: string
}
export function ActivityHeatmap({ data, isLoading, className }: ActivityHeatmapProps) {
if (isLoading) return <ActivityHeatmapSkeleton />
return <HeatmapRenderer data={data} className={className} />
}
// Data hook — features/activity/hooks/ or page level
export function useActivityData(timeRange: TimeRange) {
return useQuery({
queryKey: ['activity', timeRange],
queryFn: () => activityService.getActivity(timeRange)
})
}
// Page usage — composition
function DashboardPage() {
const { data, isLoading } = useActivityData('week')
return <ActivityHeatmap data={data ?? []} isLoading={isLoading} />
}
Portability Checklist:
- No useQuery/useMutation in shared/components
- No apiClient/fetch imports in shared/components
- No useNavigate/useParams in shared/components
- All data passed via typed props interface
- Optional className prop for customization
- Works in Storybook without QueryClient/Router mocks
Verification command:
# Should return 0 violations
grep -rE "useQuery|apiClient" src/shared/components --include="*.tsx" | grep -v test
Phase 3.6: Component Integration Decision (NEW!)
Trigger: When needing a new UI element
Decision Tree:
NEED NEW UI ELEMENT?
│
▼
┌─────────────────────────────┐
│ 1. Check shared/ui/ │ → EXISTS → USE IT
└─────────────────────────────┘
│ NOT FOUND
▼
┌─────────────────────────────┐
│ 2. Check shared/components/ │ → EXISTS → USE IT
└─────────────────────────────┘
│ NOT FOUND
▼
┌─────────────────────────────┐
│ 3. Check shared/patterns/ │ → EXISTS → USE IT
└─────────────────────────────┘
│ NOT FOUND
▼
┌─────────────────────────────┐
│ 4. Check shadcn registry │
│ ui.shadcn.com/docs │
└─────────────────────────────┘
│
FOUND ┴ NOT FOUND
│ │
▼ ▼
EVALUATE BUILD in shared/components
│
Fits 80%? ┬ YES → npx shadcn add → shared/ui
│
└ NO → WRAP in shared/components
Where to put new component:
| Type | Location | Example |
|---|---|---|
| shadcn primitive | shared/ui/ |
Button, Dialog, Select |
| shadcn + customize | shared/ui/ (modify) |
Badge with new variant |
| Composition | shared/patterns/ |
CardWithStatus, FormField |
| Business reusable | shared/components/ |
SearchBar, TopicSelector |
| Feature-specific | features/X/components/ |
AtomCard, ProviderList |
| Page-specific | pages/X/components/ |
DashboardStats |
Adding shadcn component example:
# 1. Check if exists
grep -r "DatePicker\|Calendar" src/shared/
# 2. Add from shadcn
npx shadcn@latest add calendar
# 3. Create composition wrapper if needed
# src/shared/components/DatePicker/index.tsx
Checkpoint: "Component location decided? Portability requirements clear?"
Phase 4: Storybook (Component Library)
Trigger: After creating/modifying component
Mandatory coverage:
| Location | Story Required? | Min Stories |
|---|---|---|
shared/ui/ |
Always | 4-6 |
shared/patterns/ |
Always | 5-8 |
shared/components/ |
Always | 4-6 |
features/*/ |
If >50 LOC or reused | 2-4 |
Actions:
- Create
.stories.tsxwithtags: ['autodocs'] - Cover all variants, states, sizes
- Add interaction tests for clickable elements
- Capture screenshot:
storybook_capture_screenshot storyId="..."
Checkpoint: "Story created with full coverage?"
Related skill: /storybook
Phase 5: Verification Pipeline (MOST IMPORTANT!)
Trigger: After code written, BEFORE considering task complete
Required pipeline (ALL steps!):
cd frontend
# 1. TypeScript — type safety
npx tsc --noEmit
# 2. ESLint — Design System compliance (9 rules)
npm run lint
# 3. Unit tests — logic correctness
npm run test:run
# 4. Story coverage — documentation
npm run story:check
# 5. Accessibility (for shared/ components)
npm run test:a11y
ESLint Design System Rules (all 9 — ERROR level):
| # | Rule | Ensures |
|---|---|---|
| 1 | no-raw-tailwind-colors |
Semantic tokens only |
| 2 | no-odd-spacing |
4px grid system |
| 3 | no-heroicons |
lucide-react only |
| 4 | no-raw-zindex |
Named z-index tokens |
| 5 | no-direct-fonts |
Font tokens only |
| 6 | no-raw-page-wrapper |
PageWrapper required |
| 7 | no-direct-api-imports |
Service classes |
| 8 | no-hardcoded-api-paths |
API_ENDPOINTS config |
| 9 | stories-require-autodocs |
Autodocs tag |
If any step fails: Fix → Re-run pipeline → Don't proceed until ALL pass.
→ See @references/verification.md for troubleshooting.
Phase 6: Knowledge Capture
Trigger: After successful verification
Actions:
- Close journal session:
/obsidian:journal done - Add findings as wikilinks:
[[знання/паттерни/pattern-name]] - Capture learnings:
/obsidian:capture auto - Update Beads:
bd comments add {issue} "Progress: ..." - Generate handoff for next session
Learning categories:
знання/паттерни/— reusable approachesзнання/рішення/— architectural decisionsзнання/помилки/— mistakes to avoid
→ See @references/documentation.md for templates.
Quality Gates
| Gate | Criterion | Blocking? |
|---|---|---|
| Discovery | Storybook checked BEFORE coding | Yes |
| Design System | Tokens instead of raw colors | Yes (ESLint blocks) |
| Storybook | Story for shared/ components | Yes |
| TypeScript | tsc --noEmit passes |
Yes |
| ESLint | 9 Design System rules pass | Yes |
| Tests | Unit tests pass | Yes |
| Story Coverage | story:check passes |
Yes |
| Accessibility | test:a11y for shared/ |
Recommended |
| Knowledge | Learnings captured in vault | Recommended |
Principle: If verification fails → task NOT complete.
Quick Reference
Commands:
| Action | Command |
|---|---|
| Start Storybook | just storybook |
| TypeScript check | npx tsc --noEmit |
| ESLint | npm run lint |
| ESLint fix | npm run lint:fix |
| Unit tests | npm run test:run |
| Story coverage | npm run story:check |
| A11y tests | npm run test:a11y |
Related skills:
| Skill | When to use |
|---|---|
/frontend |
Architecture, state management |
/storybook |
Story templates, interaction tests |
/design-tokens |
Token API reference |
/testing |
Vitest, Playwright patterns |
Example
User: "Create TopicCard component"
Claude follows frontend-flow:
1. [Session] /obsidian:journal session "TopicCard component"
2. [Discovery] BEFORE coding!
→ storybook_list_components → check existing cards
→ Found CardWithStatus — base structure exists!
→ /obsidian:search "card pattern" → found prior decisions
3. [Implementation]
→ Using @shared/tokens for colors
→ Using @shared/patterns/CardWithStatus as base
→ All icon buttons with aria-label
→ 44px touch targets
4. [Storybook]
→ TopicCard.stories.tsx with tags: ['autodocs']
→ 5 stories: Default, Empty, Loading, Error, WithBadge
→ storybook_capture_screenshot
5. [Verification] ALL must pass!
→ npx tsc --noEmit ✅
→ npm run lint ✅ (9 ESLint rules pass)
→ npm run test:run ✅
→ npm run story:check ✅
→ npm run test:a11y ✅
6. [Knowledge]
→ /obsidian:journal done
→ Findings: [[знання/паттерни/topic-card-composition]]
→ /obsidian:capture auto
Key: Verification runs COMPLETELY before task is considered done.