feat: add design phase and platform app generation phase
Add a UI/UX design phase (/define-design, /update-design) and a platform app generation phase (/generate-app web|flutter|winui3) to the workflow, positioned between /setup-project and /add-feature. - commands: define-design, generate-app, update-design - add-feature: check docs/design/ before implementation; update design files first when a feature changes the UI - skills: ui-design, design-tokens, platform-ui-generation - docs/design/ structure documented (source of truth = ui-blueprint.json; SVGs are references only; generated UI follows design-tokens.json) - README and AGENTS.md updated with the new workflow
This commit is contained in:
137
.opencode/skills/design-tokens/SKILL.md
Normal file
137
.opencode/skills/design-tokens/SKILL.md
Normal file
@ -0,0 +1,137 @@
|
||||
---
|
||||
name: design-tokens
|
||||
description: Guide for creating and maintaining design-tokens.json, the single source of styling values for Web, Flutter, and WinUI 3. Use when creating or updating design tokens.
|
||||
---
|
||||
|
||||
# Design Tokens Skill
|
||||
|
||||
This skill explains how to create and maintain `docs/design/design-tokens.json` — the single source of truth for all styling values (color, typography, spacing, radius, shadow, motion, breakpoints). Generated platform code must read from these tokens and must never hard-code values.
|
||||
|
||||
## Core Principle
|
||||
|
||||
> One token definition → many platform outputs. Never hard-code a color, size, or spacing in generated code.
|
||||
|
||||
## Format
|
||||
|
||||
Use a JSON structure inspired by the Design Tokens Community Group format. Every token is an object with `$type` and `$value`:
|
||||
|
||||
```json
|
||||
{
|
||||
"color": {
|
||||
"background": {
|
||||
"primary": { "$type": "color", "$value": "#0F172A" }
|
||||
}
|
||||
},
|
||||
"spacing": {
|
||||
"md": { "$type": "dimension", "$value": "16px" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Supported `$type` values: `color`, `dimension`, `fontFamily`, `fontWeight`, `duration`, `cubicBezier`, `number`, `strokeStyle`.
|
||||
|
||||
## Token Groups
|
||||
|
||||
`design-tokens.json` should include these groups (omit a group only if it truly does not apply):
|
||||
|
||||
- **color** — semantic color tokens
|
||||
- **typography** — font families, sizes, weights, line heights
|
||||
- **spacing** — spacing scale (xs, sm, md, lg, xl, …)
|
||||
- **radius** — border radius scale
|
||||
- **shadow** / **elevation** — box-shadow / elevation tokens
|
||||
- **motion** — animation durations and easings
|
||||
- **breakpoint** — responsive breakpoints (include when `web` is a target)
|
||||
|
||||
## Rules
|
||||
|
||||
### Naming tokens
|
||||
|
||||
- Use **semantic names**, not descriptive names. `color.text.primary` (good) vs `color.slate-900` (bad).
|
||||
- Use a consistent scale for numeric families: `sm`, `md`, `lg`, `xl` — or numeric `4`, `8`, `12` — pick one and stick to it.
|
||||
- Group by role then by emphasis: `color.background.primary`, `color.background.muted`, `color.text.primary`, `color.text.secondary`, `color.accent.primary`, `color.state.success`, `color.state.danger`.
|
||||
- Token names are referenced verbatim in `ui-blueprint.json` (e.g. `"padding": "lg"`). They must match exactly.
|
||||
|
||||
### Using semantic color names
|
||||
|
||||
- Define colors by **role** (background, text, accent, border, state, overlay) and **emphasis** (primary, secondary, muted).
|
||||
- Do not expose raw hex scales as the only option. If you need a raw scale, keep it in a separate `color.palette` group and reference it from semantic tokens.
|
||||
- Provide both light and dark values where the app supports them: `color.background.primary` (light) and `color.background.primary-dark`, or a `dark` sub-object — pick one convention and document it in `platform-mapping.md`.
|
||||
|
||||
### Avoiding hard-coded values in generated code
|
||||
|
||||
- Generated Web code uses CSS variables / Tailwind theme keys / theme objects derived from tokens.
|
||||
- Generated Flutter code uses `ThemeData`, `ColorScheme`, and constants derived from tokens.
|
||||
- Generated WinUI 3 code uses `ResourceDictionary` entries derived from tokens.
|
||||
- **Never** inline `#0F172A` or `16px` in generated source. Always reference the token-derived variable/resource.
|
||||
- If a value is needed that has no token, add the token to `design-tokens.json` first, then use it.
|
||||
|
||||
### Mapping tokens to platform-specific code
|
||||
|
||||
`design-tokens.json` is platform-agnostic. The conversion to each platform is documented in `docs/design/platform-mapping.md` and performed by the **platform-ui-generation** skill. This skill only defines and maintains the tokens.
|
||||
|
||||
## Example
|
||||
|
||||
```json
|
||||
{
|
||||
"color": {
|
||||
"background": {
|
||||
"primary": { "$type": "color", "$value": "#0F172A" },
|
||||
"muted": { "$type": "color", "$value": "#1E293B" }
|
||||
},
|
||||
"text": {
|
||||
"primary": { "$type": "color", "$value": "#F8FAFC" },
|
||||
"secondary": { "$type": "color", "$value": "#94A3B8" }
|
||||
},
|
||||
"accent": {
|
||||
"primary": { "$type": "color", "$value": "#38BDF8" }
|
||||
},
|
||||
"state": {
|
||||
"success": { "$type": "color", "$value": "#22C55E" },
|
||||
"danger": { "$type": "color", "$value": "#EF4444" }
|
||||
}
|
||||
},
|
||||
"typography": {
|
||||
"fontFamily": {
|
||||
"base": { "$type": "fontFamily", "$value": "Inter, system-ui, sans-serif" }
|
||||
},
|
||||
"size": {
|
||||
"sm": { "$type": "dimension", "$value": "14px" },
|
||||
"md": { "$type": "dimension", "$value": "16px" },
|
||||
"lg": { "$type": "dimension", "$value": "20px" }
|
||||
}
|
||||
},
|
||||
"spacing": {
|
||||
"sm": { "$type": "dimension", "$value": "8px" },
|
||||
"md": { "$type": "dimension", "$value": "16px" },
|
||||
"lg": { "$type": "dimension", "$value": "24px" }
|
||||
},
|
||||
"radius": {
|
||||
"card": { "$type": "dimension", "$value": "16px" },
|
||||
"pill": { "$type": "dimension", "$value": "9999px" }
|
||||
},
|
||||
"shadow": {
|
||||
"card": { "$type": "shadow", "$value": "0 4px 12px rgba(0,0,0,0.15)" }
|
||||
},
|
||||
"motion": {
|
||||
"duration": {
|
||||
"fast": { "$type": "duration", "$value": "120ms" },
|
||||
"base": { "$type": "duration", "$value": "200ms" }
|
||||
}
|
||||
},
|
||||
"breakpoint": {
|
||||
"sm": { "$type": "dimension", "$value": "640px" },
|
||||
"md": { "$type": "dimension", "$value": "768px" },
|
||||
"lg": { "$type": "dimension", "$value": "1024px" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Maintenance Checklist
|
||||
|
||||
When updating tokens:
|
||||
|
||||
- [ ] JSON is valid.
|
||||
- [ ] Token names referenced in `ui-blueprint.json` still exist (no broken references after a rename).
|
||||
- [ ] New tokens follow the naming and semantic-name rules.
|
||||
- [ ] No duplicate tokens (same role under two names).
|
||||
- [ ] `platform-mapping.md` still covers how every token group maps to each target platform.
|
||||
112
.opencode/skills/platform-ui-generation/SKILL.md
Normal file
112
.opencode/skills/platform-ui-generation/SKILL.md
Normal file
@ -0,0 +1,112 @@
|
||||
---
|
||||
name: platform-ui-generation
|
||||
description: Guide for converting the design files (design-tokens.json + ui-blueprint.json) into application code for Web, Flutter, or WinUI 3. Use when running /generate-app or implementing UI against the design spec.
|
||||
---
|
||||
|
||||
# Platform UI Generation Skill
|
||||
|
||||
This skill explains how to convert the design files under `docs/design/` into application code. It is used by `/generate-app` and by `/add-feature` when implementing UI.
|
||||
|
||||
## Core Principles
|
||||
|
||||
1. **Source of truth**: `docs/design/ui-blueprint.json` defines structure; `docs/design/design-tokens.json` defines values.
|
||||
2. **No hard-coded values**: every color, size, spacing, radius, and duration in generated code must come from a token.
|
||||
3. **Reusable components first**: extract reusable components; do not inline the entire tree into screens.
|
||||
4. **No feature logic during scaffolding**: `/generate-app` creates the shell, theme, routes, screens, and shared components only. Features come from `/add-feature`.
|
||||
5. **Respect the platform mapping**: `docs/design/platform-mapping.md` documents how each artifact maps to each platform.
|
||||
|
||||
## Inputs
|
||||
|
||||
- `docs/design/design-brief.md` — direction
|
||||
- `docs/design/design-tokens.json` — values
|
||||
- `docs/design/ui-blueprint.json` — structure (source of truth)
|
||||
- `docs/design/platform-mapping.md` — per-platform conversion rules
|
||||
- `docs/architecture.md` — technology choices (framework, language)
|
||||
|
||||
## General Conversion Rules
|
||||
|
||||
- One screen in the blueprint → one screen/page in the target platform.
|
||||
- One route in the blueprint → one route entry in the navigation config.
|
||||
- A reusable component referenced in multiple screens → a shared component file/widget/control.
|
||||
- Component `variant` → a variant/style modifier on the shared component.
|
||||
- Component `state` → visual states the component must support (default, hover, pressed, disabled, etc.).
|
||||
- `action: navigate:<screen-id>` → navigation to the corresponding route.
|
||||
- Other `action:` values → wire to a handler/placeholder; do not implement feature logic.
|
||||
|
||||
## Web Mapping
|
||||
|
||||
- **design tokens →** CSS variables, a Tailwind config, or a theme object.
|
||||
- `color.*` → CSS custom properties (`--color-background-primary`) or Tailwind theme colors.
|
||||
- `spacing.*`, `radius.*`, `typography.*` → CSS variables / Tailwind theme keys.
|
||||
- `breakpoint.*` → responsive breakpoints.
|
||||
- **ui-blueprint →**
|
||||
- `screens` → pages/routes.
|
||||
- `layout` → layout components (vertical/horizontal stack, grid).
|
||||
- `components` → React (or framework) components, one file per reusable component.
|
||||
- Use **semantic HTML** (`<header>`, `<main>`, `<nav>`, `<button>`, `<label>`) where possible.
|
||||
- Respect responsive layout requirements using the breakpoint tokens.
|
||||
- If no framework is specified in `architecture.md`, choose a simple default suitable for the repository and note the choice.
|
||||
|
||||
## Flutter Mapping
|
||||
|
||||
- **design tokens →**
|
||||
- `color.*` → `ColorScheme` + a color constants file.
|
||||
- `typography.*` → `TextTheme` / text styles.
|
||||
- `spacing.*`, `radius.*` → constants (`spacing_md`, `radiusCard`).
|
||||
- `shadow.*` → `BoxShadow` / elevation.
|
||||
- `motion.duration.*` → `Duration` constants.
|
||||
- Assemble into a `ThemeData`.
|
||||
- **ui-blueprint →**
|
||||
- `screens` → `Screen` widgets + routes (named routes or a router).
|
||||
- `layout.type` → `Column` / `Row` / `Wrap` / `ListView` with `padding` and `SizedBox` gaps from spacing tokens.
|
||||
- `components` → reusable widgets, one file per widget.
|
||||
- Keep clean separation between: `screens/`, `widgets/`, `theme/`, `models/`, `services/`.
|
||||
|
||||
## WinUI 3 Mapping
|
||||
|
||||
- **design tokens →**
|
||||
- `color.*`, `spacing.*`, `radius.*`, `typography.*` → a `ResourceDictionary` (`.xaml`) with `Color`, `Thickness`, `CornerRadius`, `FontFamily`, `Style` resources.
|
||||
- Assemble theme resources and styles keyed by the token names.
|
||||
- **ui-blueprint →**
|
||||
- `screens` → XAML pages (`Page`).
|
||||
- `layout.type` → `StackPanel` / `Grid` with `Padding`/`Margin` from token resources.
|
||||
- `components` → `UserControl`s, one file per reusable component.
|
||||
- Navigation → `Frame` + navigation entries per route.
|
||||
- Use **C# and XAML**.
|
||||
- Keep UI logic separated from business logic (code-behind stays thin; logic goes to services/viewmodels).
|
||||
|
||||
## Shared Component Contract
|
||||
|
||||
Each generated reusable component must:
|
||||
|
||||
- Accept the `variant` as a parameter/prop/style modifier.
|
||||
- Support the `states` declared in the blueprint (default, hover, pressed, disabled).
|
||||
- Reference tokens for all visual values — no hard-coded literals.
|
||||
- Stay presentational: no feature/business logic.
|
||||
|
||||
## Scaffolding Scope (what `/generate-app` creates)
|
||||
|
||||
- App entry point and shell
|
||||
- Theme/styling system from tokens
|
||||
- Router/navigation config from routes
|
||||
- One screen per blueprint screen (layout + components wired, but no feature logic)
|
||||
- One shared component file per reusable component in `components`
|
||||
|
||||
## Out of Scope (handled by `/add-feature`)
|
||||
|
||||
- Feature/business logic
|
||||
- Data fetching and state management for features
|
||||
- Form validation and submission behavior
|
||||
- Anything not described in the current project documents
|
||||
|
||||
## Generation Checklist
|
||||
|
||||
Before finishing a generation:
|
||||
|
||||
- [ ] Every screen in the blueprint has a corresponding generated screen.
|
||||
- [ ] Every route is wired in the navigation config.
|
||||
- [ ] Every reusable component in `components` has a generated file.
|
||||
- [ ] No hard-coded color/size/spacing in generated code — all from tokens.
|
||||
- [ ] Components accept `variant` and support declared `states`.
|
||||
- [ ] No feature logic was implemented beyond the project documents.
|
||||
- [ ] `docs/repository-structure.md` updated if the layout changed.
|
||||
156
.opencode/skills/ui-design/SKILL.md
Normal file
156
.opencode/skills/ui-design/SKILL.md
Normal file
@ -0,0 +1,156 @@
|
||||
---
|
||||
name: ui-design
|
||||
description: Guide for creating the UI/UX design specification (design brief, UI blueprint JSON, and SVG wireframes). Use when defining or updating the design under docs/design/.
|
||||
---
|
||||
|
||||
# UI Design Skill
|
||||
|
||||
This skill explains how to create an **AI-readable, implementation-ready** UI/UX design specification. It prefers structured, machine-consumable descriptions over vague visual descriptions.
|
||||
|
||||
## Core Principle
|
||||
|
||||
> The source of truth is structured text/JSON. Visual images (SVG) are references for humans, not inputs for code generation.
|
||||
|
||||
Prefer describing **what a component is and how it behaves** over describing **what it looks like**. "A primary button with label 'Save' that triggers `action:save`" is better than "a nice blue button in the corner".
|
||||
|
||||
## Outputs
|
||||
|
||||
This skill produces three kinds of artifacts under `docs/design/`:
|
||||
|
||||
1. `design-brief.md` — the UI/UX direction (human-readable, but concrete)
|
||||
2. `ui-blueprint.json` — the source of truth for UI generation (machine-readable)
|
||||
3. `screens/*.svg` — visual wireframes for human review (derived from the blueprint)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before creating the design, read:
|
||||
|
||||
- `docs/product-requirements.md`
|
||||
- `docs/functional-design.md`
|
||||
- `docs/architecture.md`
|
||||
|
||||
The design must serve the product requirements and stay consistent with the functional design.
|
||||
|
||||
## Creating `design-brief.md`
|
||||
|
||||
A concrete, opinionated document. Avoid vague phrases like "modern and clean" without elaboration. Include every section below:
|
||||
|
||||
- **Target users** — who the UI is for, and their key contexts/constraints
|
||||
- **Design concept** — the one-paragraph idea the UI embodies
|
||||
- **Visual mood** — concrete descriptors (density, light/dark, rounding, motion amount)
|
||||
- **Layout principles** — grid, alignment, content width, spacing rhythm
|
||||
- **Navigation principles** — primary navigation pattern, depth, back behavior
|
||||
- **Accessibility considerations** — target contrast, touch-target sizes, focus visibility, font scaling
|
||||
- **Platform-specific notes** — how the design adapts to web / Flutter / WinUI 3
|
||||
- **Examples of preferred UI style** — concrete references and why they fit
|
||||
- **Examples of UI style to avoid** — anti-patterns to stay away from
|
||||
|
||||
## Creating `ui-blueprint.json` (source of truth)
|
||||
|
||||
This is the most important file. It must be valid JSON and fully describe the app's UI structure so that a generator can produce Web, Flutter, or WinUI 3 code without guessing.
|
||||
|
||||
### Required top-level fields
|
||||
|
||||
- `app.name`
|
||||
- `app.platformTargets` — e.g. `["web", "flutter", "winui3"]`
|
||||
- `app.style` — short style summary string
|
||||
- `screens` — array of screen objects
|
||||
- `components` — map of reusable component definitions
|
||||
|
||||
### Screen object shape
|
||||
|
||||
Each screen has:
|
||||
|
||||
- `id` — stable identifier (used as the SVG filename)
|
||||
- `title`
|
||||
- `route`
|
||||
- `layout` — type + spacing tokens (e.g. `{ "type": "vertical", "padding": "lg", "gap": "md" }`)
|
||||
- `components` — a tree of component nodes
|
||||
|
||||
### Component node shape
|
||||
|
||||
Each node has:
|
||||
|
||||
- `type` — e.g. `header`, `card`, `text`, `button`, `list`, `input`, `image`, `nav`
|
||||
- `variant` — optional, must exist in the component's `variants`
|
||||
- `role` — optional semantic role (e.g. `title`, `subtitle`, `body`)
|
||||
- `content` / `label` — text content where applicable
|
||||
- `action` — user action, e.g. `navigate:details`, `submit`, `toggle:filters`
|
||||
- `children` — nested component nodes
|
||||
|
||||
### Reusable component definition
|
||||
|
||||
```json
|
||||
"components": {
|
||||
"button": {
|
||||
"variants": ["primary", "secondary", "ghost"],
|
||||
"states": ["default", "hover", "pressed", "disabled"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Example (excerpt)
|
||||
|
||||
```json
|
||||
{
|
||||
"app": {
|
||||
"name": "Example App",
|
||||
"platformTargets": ["web", "flutter", "winui3"],
|
||||
"style": "friendly, modern, rounded, calm"
|
||||
},
|
||||
"screens": [
|
||||
{
|
||||
"id": "home",
|
||||
"title": "Home",
|
||||
"route": "/",
|
||||
"layout": { "type": "vertical", "padding": "lg", "gap": "md" },
|
||||
"components": [
|
||||
{ "type": "header", "title": "Dashboard", "subtitle": "Welcome back" },
|
||||
{
|
||||
"type": "card",
|
||||
"variant": "primary",
|
||||
"children": [
|
||||
{ "type": "text", "role": "title", "content": "Start here" },
|
||||
{ "type": "button", "label": "Open", "action": "navigate:details" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"components": {
|
||||
"button": {
|
||||
"variants": ["primary", "secondary", "ghost"],
|
||||
"states": ["default", "hover", "pressed", "disabled"]
|
||||
},
|
||||
"card": {
|
||||
"variants": ["primary", "secondary", "danger"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Rules
|
||||
|
||||
- Every `variant` referenced in a screen must exist in `components[type].variants`.
|
||||
- Every spacing/radius/color name referenced in layout/components must exist in `design-tokens.json`.
|
||||
- Use `action:` prefixes consistently: `navigate:<screen-id>`, `submit`, `toggle:<id>`, `open:<id>`, etc.
|
||||
- One screen = one route. Do not overload a screen with multiple routes.
|
||||
- Keep the blueprint free of hard-coded pixel values; reference token names instead.
|
||||
|
||||
## Creating SVG wireframes (`screens/*.svg`)
|
||||
|
||||
- Generate **one SVG per screen**, named `<screen-id>.svg`.
|
||||
- The SVG is a **low-fidelity wireframe**, not a pixel-perfect mock. Use boxes, labels, and placeholders.
|
||||
- It must reflect the component tree of its screen in `ui-blueprint.json`.
|
||||
- **If the blueprint and an SVG disagree, the blueprint wins.** Regenerate the SVG.
|
||||
- SVGs exist only so humans can review the design at a glance.
|
||||
|
||||
## Consistency Checklist
|
||||
|
||||
Before finishing:
|
||||
|
||||
- [ ] Every screen in the blueprint has a matching SVG.
|
||||
- [ ] Every `variant` used in a screen is declared in `components`.
|
||||
- [ ] Every token name used in the blueprint exists in `design-tokens.json`.
|
||||
- [ ] The brief and the blueprint describe the same app (same screens, same navigation).
|
||||
- [ ] No hard-coded values in the blueprint.
|
||||
Reference in New Issue
Block a user