Skip to content
This repository was archived by the owner on Apr 11, 2026. It is now read-only.

Commit cc5484b

Browse files
z23ccclaude
andcommitted
feat(skills): add frontend-ui-engineering skill for production-quality UI (v0.1.35)
- New skill: flow-code-frontend-ui with component architecture, design system adherence, accessibility (WCAG 2.1 AA), responsive design, and AI aesthetic avoidance - New command: /flow-code:frontend-ui for direct invocation - Worker auto-loads skill for --domain frontend tasks - Inspired by agent-skills/frontend-ui-engineering, adapted to flow-code conventions Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent d376fc6 commit cc5484b

5 files changed

Lines changed: 330 additions & 2 deletions

File tree

agents/worker.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ Parse the spec carefully. Identify:
244244
- Technical approach hints
245245
- Test requirements
246246
- Quick commands from epic spec (run these for verification)
247-
- **Domain** (from task JSON `domain` field): if set (frontend/backend/architecture/testing/docs/ops), focus your approach accordingly — e.g., backend tasks prioritize API/DB, frontend tasks prioritize UI/UX
247+
- **Domain** (from task JSON `domain` field): if set (frontend/backend/architecture/testing/docs/ops), focus your approach accordingly — e.g., backend tasks prioritize API/DB, frontend tasks prioritize UI/UX and MUST follow the `flow-code-frontend-ui` skill (component architecture, design system adherence, accessibility, AI aesthetic avoidance)
248248

249249
**Baseline check:**
250250
```bash

bin/flowctl

0 Bytes
Binary file not shown.

commands/flow-code/frontend-ui.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
name: flow-code:frontend-ui
3+
description: "Build production-quality UI: components, layouts, state, accessibility, design system adherence"
4+
argument-hint: "[component or page description]"
5+
---
6+
7+
# IMPORTANT: This command MUST invoke the skill `flow-code-frontend-ui`
8+
9+
The ONLY purpose of this command is to call the `flow-code-frontend-ui` skill. You MUST use that skill now.
10+
11+
**User request:** $ARGUMENTS
12+
13+
Pass the user request to the skill. The skill handles all frontend UI engineering logic.

flowctl/crates/flowctl-cli/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "flowctl-cli"
3-
version = "0.1.34"
3+
version = "0.1.35"
44
description = "CLI entry point for flowctl"
55
edition.workspace = true
66
rust-version.workspace = true
Lines changed: 315 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,315 @@
1+
---
2+
name: flow-code-frontend-ui
3+
description: "Use when building or modifying user-facing interfaces, creating components, implementing layouts, managing state, or when UI output needs to look production-quality rather than AI-generated"
4+
tier: 2
5+
user-invocable: true
6+
---
7+
<!-- SKILL_TAGS: frontend,ui,design,accessibility,components -->
8+
9+
# Frontend UI Engineering
10+
11+
> **Startup:** Follow [Startup Sequence](../_shared/preamble.md) before proceeding.
12+
13+
## flowctl Setup
14+
15+
```bash
16+
FLOWCTL="$HOME/.flow/bin/flowctl"
17+
```
18+
19+
## Overview
20+
21+
Build production-quality user interfaces that are accessible, performant, and visually polished. The goal is UI that looks like it was built by a design-aware engineer -- not like it was generated by an AI. This means real design system adherence, proper accessibility, thoughtful interaction patterns, and no generic "AI aesthetic."
22+
23+
## When to Use
24+
25+
- Building new UI components or pages
26+
- Modifying existing user-facing interfaces
27+
- Implementing responsive layouts
28+
- Adding interactivity or state management
29+
- Fixing visual or UX issues
30+
- Worker tasks tagged with `--domain frontend`
31+
32+
**When NOT to use:**
33+
- Backend-only changes with no UI impact
34+
- CLI tool development (use flow-code-api-design instead)
35+
- Pure data model / schema changes
36+
37+
## Phase 1: Audit Before Coding
38+
39+
Before writing any UI code:
40+
41+
1. **Detect existing design system:**
42+
- Search for Tailwind config, CSS variables, theme files, component library imports
43+
- Check for design tokens (`colors`, `spacing`, `typography` in config)
44+
- Identify component library (shadcn, MUI, Ant Design, Radix, custom)
45+
46+
2. **Read existing patterns:**
47+
- Find 2-3 similar components in the codebase — match their conventions
48+
- Check for shared layout components, wrappers, or utility classes
49+
- Note naming patterns (PascalCase components, kebab-case files, etc.)
50+
51+
3. **Understand the state model:**
52+
- Where does data come from? (props, context, server state, URL params)
53+
- What state management is already in use? (don't introduce a new one)
54+
55+
```
56+
SKIP this phase ONLY if you're fixing a single CSS property.
57+
```
58+
59+
## Phase 2: Component Architecture
60+
61+
### File Structure
62+
63+
Colocate everything related to a component:
64+
65+
```
66+
src/components/
67+
TaskList/
68+
TaskList.tsx # Component implementation
69+
TaskList.test.tsx # Tests
70+
use-task-list.ts # Custom hook (if complex state)
71+
types.ts # Component-specific types (if needed)
72+
```
73+
74+
### Composition Over Configuration
75+
76+
```tsx
77+
// Good: Composable
78+
<Card>
79+
<CardHeader>
80+
<CardTitle>Tasks</CardTitle>
81+
</CardHeader>
82+
<CardBody>
83+
<TaskList tasks={tasks} />
84+
</CardBody>
85+
</Card>
86+
87+
// Avoid: Over-configured
88+
<Card
89+
title="Tasks"
90+
headerVariant="large"
91+
bodyPadding="md"
92+
content={<TaskList tasks={tasks} />}
93+
/>
94+
```
95+
96+
### Separate Data from Presentation
97+
98+
```tsx
99+
// Container: handles data
100+
function TaskListContainer() {
101+
const { tasks, isLoading, error } = useTasks();
102+
if (isLoading) return <TaskListSkeleton />;
103+
if (error) return <ErrorState message="Failed to load tasks" retry={refetch} />;
104+
if (tasks.length === 0) return <EmptyState message="No tasks yet" />;
105+
return <TaskList tasks={tasks} />;
106+
}
107+
108+
// Presentation: handles rendering
109+
function TaskList({ tasks }: { tasks: Task[] }) {
110+
return (
111+
<ul role="list" className="divide-y">
112+
{tasks.map(task => <TaskItem key={task.id} task={task} />)}
113+
</ul>
114+
);
115+
}
116+
```
117+
118+
### State Management Pyramid
119+
120+
Choose the simplest approach that works:
121+
122+
```
123+
useState --> Component-specific UI state
124+
Lifted state --> Shared between 2-3 sibling components
125+
Context --> Theme, auth, locale (read-heavy, write-rare)
126+
URL state --> Filters, pagination, shareable UI state
127+
Server state (SWR) --> Remote data with caching
128+
Global store --> Complex client state shared app-wide
129+
```
130+
131+
Avoid prop drilling deeper than 3 levels. If passing props through components that don't use them, restructure or use context.
132+
133+
## Phase 3: Design System Adherence
134+
135+
### Defeat the AI Aesthetic
136+
137+
AI-generated UI has recognizable patterns. Avoid all of them:
138+
139+
| AI Default | Why It's a Problem | Production Quality |
140+
|---|---|---|
141+
| Purple/indigo everything | Every AI app looks identical | Use the project's actual color palette |
142+
| Excessive gradients | Visual noise, clashes with design systems | Flat or subtle gradients matching the system |
143+
| Rounded everything (`rounded-2xl`) | Ignores corner radius hierarchy | Consistent border-radius from design tokens |
144+
| Generic hero sections | Template-driven, no connection to content | Content-first layouts |
145+
| Lorem ipsum copy | Hides layout problems (overflow, wrapping) | Realistic placeholder content |
146+
| Oversized padding everywhere | Destroys visual hierarchy, wastes space | Consistent spacing scale |
147+
| Stock card grids | Ignores information priority | Purpose-driven layouts |
148+
| Shadow-heavy design | Competes with content, slow on low-end | Subtle or no shadows per design system |
149+
150+
### Spacing
151+
152+
Use the project's spacing scale. Don't invent values:
153+
154+
```css
155+
/* Good: on-scale */
156+
padding: 1rem; /* 16px */
157+
gap: 0.75rem; /* 12px */
158+
159+
/* Bad: arbitrary */
160+
padding: 13px;
161+
margin-top: 2.3rem;
162+
```
163+
164+
### Typography
165+
166+
Respect the type hierarchy:
167+
168+
```
169+
h1 --> Page title (one per page)
170+
h2 --> Section title
171+
h3 --> Subsection title
172+
body --> Default text
173+
small --> Secondary/helper text
174+
```
175+
176+
Don't skip heading levels. Don't use heading styles for non-heading content.
177+
178+
### Color
179+
180+
- Use semantic tokens: `text-primary`, `bg-surface`, `border-default` -- not raw hex
181+
- Ensure contrast: 4.5:1 for normal text, 3:1 for large text
182+
- Never rely on color alone to convey information (add icons, text, or patterns)
183+
184+
## Phase 4: Accessibility (WCAG 2.1 AA)
185+
186+
Every component must meet these standards:
187+
188+
### Keyboard Navigation
189+
190+
```tsx
191+
// Focusable by default
192+
<button onClick={handleClick}>Click me</button> // OK
193+
194+
// NOT focusable -- never do this
195+
<div onClick={handleClick}>Click me</div> // BAD
196+
197+
// If you must use a div, add role + tabIndex + keyboard handler
198+
<div role="button" tabIndex={0} onClick={handleClick}
199+
onKeyDown={e => e.key === 'Enter' && handleClick()}>
200+
Click me
201+
</div>
202+
```
203+
204+
### ARIA Labels
205+
206+
```tsx
207+
// Icon-only buttons need labels
208+
<button aria-label="Close dialog"><XIcon /></button>
209+
210+
// Form inputs need labels
211+
<label htmlFor="email">Email</label>
212+
<input id="email" type="email" />
213+
```
214+
215+
### Focus Management
216+
217+
```tsx
218+
// Move focus when content changes (modals, dialogs)
219+
function Dialog({ isOpen, onClose }) {
220+
const closeRef = useRef(null);
221+
useEffect(() => {
222+
if (isOpen) closeRef.current?.focus();
223+
}, [isOpen]);
224+
225+
return (
226+
<dialog open={isOpen}>
227+
<button ref={closeRef} onClick={onClose}>Close</button>
228+
{/* content */}
229+
</dialog>
230+
);
231+
}
232+
```
233+
234+
### Required States
235+
236+
Every data-driven component needs all three:
237+
238+
```tsx
239+
if (isLoading) return <Skeleton aria-busy="true" />;
240+
if (error) return <ErrorState message={error.message} retry={refetch} />;
241+
if (!data.length) return <EmptyState action={onCreate} />;
242+
return <DataList items={data} />;
243+
```
244+
245+
## Phase 5: Responsive Design
246+
247+
Design for mobile first, then expand:
248+
249+
```tsx
250+
<div className="
251+
grid grid-cols-1
252+
sm:grid-cols-2
253+
lg:grid-cols-3
254+
gap-4
255+
">
256+
```
257+
258+
Test at these breakpoints: **320px, 768px, 1024px, 1440px**.
259+
260+
### Loading and Transitions
261+
262+
```tsx
263+
// Skeleton loading (not spinners for content areas)
264+
function ListSkeleton() {
265+
return (
266+
<div className="space-y-3" aria-busy="true" aria-label="Loading">
267+
{Array.from({ length: 3 }).map((_, i) => (
268+
<div key={i} className="h-12 bg-muted animate-pulse rounded" />
269+
))}
270+
</div>
271+
);
272+
}
273+
```
274+
275+
Use optimistic updates for perceived speed on user actions (toggles, deletes).
276+
277+
## Common Rationalizations
278+
279+
| Rationalization | Reality |
280+
|---|---|
281+
| "Accessibility is a nice-to-have" | Legal requirement in many jurisdictions. Engineering quality standard. |
282+
| "We'll make it responsive later" | Retrofitting responsive design is 3x harder than building it first. |
283+
| "The design isn't final so I'll skip styling" | Use design system defaults. Unstyled UI creates broken first impressions. |
284+
| "This is just a prototype" | Prototypes become production code. Build the foundation right. |
285+
| "The AI aesthetic is fine for now" | It signals low quality to every reviewer. Use the project's design system. |
286+
| "I'll add empty/error states later" | Users hit edge cases immediately. Handle them now or ship bugs. |
287+
| "Keyboard navigation can wait" | ~15% of users rely on keyboard. Ship accessible or ship broken. |
288+
289+
## Red Flags
290+
291+
- Components over 200 lines (split them)
292+
- Inline styles or arbitrary pixel values not on the spacing scale
293+
- Missing error, loading, or empty states
294+
- No keyboard navigation testing
295+
- Color as the sole indicator of state (red/green without text/icons)
296+
- Generic AI look: purple gradients, oversized cards, uniform shadows
297+
- New state management library when one already exists in the project
298+
- `div` with `onClick` instead of `button` or `a`
299+
- Skipped heading levels (h1 then h3)
300+
- Raw hex colors instead of semantic tokens
301+
302+
## Verification
303+
304+
After building UI:
305+
306+
- [ ] Component renders without console errors or warnings
307+
- [ ] All interactive elements are keyboard accessible (Tab through the page)
308+
- [ ] Screen reader can convey page content and structure (`aria-label`, roles)
309+
- [ ] Responsive: works at 320px, 768px, 1024px, 1440px
310+
- [ ] Loading, error, and empty states all handled
311+
- [ ] Follows project's design system (spacing, colors, typography, border-radius)
312+
- [ ] No accessibility warnings from axe-core or browser dev tools
313+
- [ ] No AI aesthetic patterns (check the table in Phase 3)
314+
- [ ] Components under 200 lines each
315+
- [ ] State management matches existing project patterns

0 commit comments

Comments
 (0)