Installs into .claude/skills of the current project.
Are you the author of Flame Harness Design?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/tjdrhs90-flame-harness-design)
---
name: flame-harness-design
description: Phase 3 — define the Flutter design_tokens.dart spec (palette, typography, spacing), the game's art/visual concept, and the asset/audio sourcing plan.
argument-hint: ""
allowed-tools: [Agent, Read, Write, Edit, Bash]
---
# flame-harness-design
Phase 3 of the flutter-flame-harness pipeline. Reads the latest PRD and `config.md`, then
produces a design document that specifies the `lib/ui/design_tokens.dart` constants, the
game's visual/art concept, and the asset and audio sourcing plan (including launcher icon and
splash screen intent). Advances pipeline state to `contract`.
All file schemas (`config.md`, `state.md`, `pipeline-log.md`) and the phase transition table
are defined in `docs/harness-protocol.md` — refer to that document as the single source of
truth (§2 for `state.md` schema; §7 for the `design → contract` transition). Do not redefine
schemas here.
---
## Input
### 1. Read `docs/harness/config.md`
Extract:
| Key | Use |
|---|---|
| `app_idea` | Informs visual tone (action vs. casual vs. puzzle) |
| `app_name` | Used in splash screen heading and icon badge |
| `app_slug` | Used to derive asset directory naming conventions |
| `default_language` | The user's conversation language; write copy samples in it |
If `config.md` does not exist, abort with:
`flame-harness-design: docs/harness/config.md not found — run the orchestrator to bootstrap first.`
### 2. Read the latest PRD
Find the most recent file matching `docs/harness/plans/*-prd.md` (sort descending by filename,
take the first). If no PRD exists, abort with:
`flame-harness-design: no PRD found in docs/harness/plans/ — run flame-harness-plan first.`
Extract from the PRD:
- **Genre & core mechanic** — informs colour mood (e.g., dark sci-fi vs. bright hyper-casual)
- **Target age / tone** — age rating and energy level of the visual style
- **Monetisation hook** — AdMob placement type (banner at bottom → affects HUD spacing)
- **Win/lose conditions** — informs what UI states need distinct visual treatment
---
## Design tokens
Write a specification for `lib/ui/design_tokens.dart` that a generator phase Claude can
implement verbatim as `const` Dart values. The spec must cover every sub-section below.
### Colour palette
Derive a palette from the genre and tone. Use the following template — replace every
`<...>` with a real hex colour and rationale:
```
Primary <#RRGGBB> — main brand / call-to-action colour
PrimaryDark <#RRGGBB> — pressed / shadow state of primary
Accent <#RRGGBB> — highlights, score text, power-up glows
Background <#RRGGBB> — game canvas and screen background
Surface <#RRGGBB> — card, dialog, overlay background
OnBackground <#RRGGBB> — text / icon colour on background
OnSurface <#RRGGBB> — text / icon colour on surface
Error <#RRGGBB> — error states, health loss flash
```
Rules:
- Minimum contrast ratio of 4.5:1 for any text colour against its background.
- Background and Primary must share the same temperature (both warm or both cool).
- Accent must differ from Primary by at least 60° of hue to provide visual pop.
- For dark-themed games (sci-fi, horror) set Background ≤ `#333333`; for bright
hyper-casual games set Background ≥ `#E0E0E0`.
### Typography scale
Specify font family, weight, and size for each role. If the game uses a custom font,
name the Google Font and its import path; otherwise default to `Roboto`.
```
Display family: <FontName> weight: 900 size: 48 sp — game title on main menu
Heading1 family: <FontName> weight: 700 size: 32 sp — screen headings
Heading2 family: <FontName> weight: 700 size: 24 sp — section headings, score
Body family: <FontName> weight: 400 size: 16 sp — general UI text
Caption family: <FontName> weight: 400 size: 12 sp — labels, tooltips
Button family: <FontName> weight: 600 size: 18 sp — primary buttons
HUD family: <FontName> weight: 700 size: 20 sp — in-game HUD counters
```
Use `sp` (Flutter `TextScaler`-aware) for all sizes. Game HUD text should use
`fontFeatures: [FontFeature.tabularFigures()]` for score counters.
### Spacing scale
Define a base unit and derive the full spacing scale. Typical base is 4 dp:
```
xs: 4 dp — tight padding (icon labels)
sm: 8 dp — inner card padding
md: 16 dp — standard screen margin
lg: 24 dp — section gap
xl: 32 dp — screen top/bottom padding
xxl: 48 dp — hero area padding
```
All layout constants in `design_tokens.dart` use these named values — no raw numbers in
UI code. Spacing values are applied as `const double` fields.
Also define `minTapTarget = 48.0` (dp) — every menu/overlay button must be at least this size
(accessibility gate R10; pairs with the 4.5:1 contrast rule above).
### Radius and elevation
```
radiusSm: 4 dp — chip, small badge
radiusMd: 8 dp — card, dialog
radiusLg: 16 dp — bottom sheet, hero card
radiusFull: 999 dp — pill buttons
elevationSurface: 2 dp
elevationDialog: 8 dp
elevationFab: 12 dp
```
### `design_tokens.dart` file template
The generator must emit a file with this exact structure:
```dart
// lib/ui/design_tokens.dart
// AUTO-GENERATED by flame-harness-design — do not edit manually.
// See docs/harness/plans/<date>-design.md for rationale.
import 'package:flutter/material.dart';
abstract class DesignTokens {
// --- Colours ---
static const Color primary = Color(0xFF______);
static const Color primaryDark = Color(0xFF______);
static const Color accent = Color(0xFF______);
static const Color background = Color(0xFF______);
static const Color surface = Color(0xFF______);
static const Color onBackground = Color(0xFF______);
static const Color onSurface = Color(0xFF______);
static const Color error = Color(0xFF______);
// --- Spacing ---
static const double spaceXs = 4;
static const double spaceSm = 8;
static const double spaceMd = 16;
static const double spaceLg = 24;
static const double spaceXl = 32;
static const double spaceXxl = 48;
// --- Radius ---
static const double radiusSm = 4;
static const double radiusMd = 8;
static const double radiusLg = 16;
static const double radiusFull = 999;
// --- Elevation ---
static const double elevationSurface = 2;
static const double elevationDialog = 8;
static const double elevationFab = 12;
// --- Typography helpers ---
static const String fontFamily = '______';
}
```
Fill every `______` from the palette and typography spec written above.
---
## Visual concept
### Art direction
Describe the overall visual style in 3–5 sentences covering:
1. **Art style** — pixel art / vector flat / hand-drawn / 3-D rendered sprites
2. **Colour mood** — how the palette reinforces the genre's emotional tone
3. **World / environment** — background theme (city, space, forest, dungeon, etc.)
4. **Character / enemy design** — silhouette language (cute rounded vs. angular menacing)
5. **UI chrome style** — whether overlays feel like HUD panels, storybook pages, arcade
bezels, etc.
The description must be concrete enough that a sprite artist (or an AI image prompt)
could reproduce the style without further guidance.
### Sprite list (MVP)
List every sprite needed for the MVP core loop. For each sprite, specify:
| Sprite | Dimensions (dp) | Frames | Source |
|---|---|---|---|
| Player idle | 64×64 | 4 | <see Asset plan below> |
| Player run | 64×64 | 8 | — |
| Enemy A | 48×48 | 4 | — |
| Background layer 1 | screen width × 256 | 1 | — |
| ... | ... | ... | ... |
Dimensions are in dp at 1× scale; the asset pipeline should export at 1×, 2×, and 3×.
### Overlay and HUD layout
Describe the layout of each Flutter overlay:
- **Main menu** — what elements are visible and their rough positions
- **In-game HUD** — score, lives/health, pause button placement
- **Pause overlay** — resume, restart, settings
- **Game-over screen** — score, best score, restart, AdMob rewarded-ad revival button
- **Settings screen** — BGM toggle, SFX toggle, language toggle
Each description maps directly to a Flutter widget in `lib/screens/` or `lib/ui/`.
---
## Asset/audio plan
> **Defaults the generator can always satisfy with no sourcing:** audio = **code-synthesized**
> (`tool/build_audio.dart` → WAV); visuals = **code-drawn** (`CustomPainter`/Flame shapes from the
> palette). Specify **sprite art** below only if the concept needs it — choose CC0 pack or AI; the
> generator cleans sourced art's background with `tool/strip_bg.dart`. The harness never depends on
> art that isn't sourced, so a game can ship fully code-drawn.
### Sprite and image assets
State for each visual asset whether it will be:
| Strategy | When to use |
|---|---|
| **Free pack** (itch.io / OpenGameArt / Kenney.nl) | Hyper-casual games, generic shapes |
| **AI-generated** (Midjourney / DALL-E prompt) | Custom style with specific prompt given |
| **Custom drawn** | Unique brand-critical characters |
For every free pack named, provide the pack URL and its license (CC0, CC-BY, etc.).
For AI-generated assets, write the exact prompt template to use (substituting `<genre>`
and `<style>` from the art direction above).
Assets must be declared in `pubspec.yaml` under `flutter: assets:`. Provide the
directory entry (e.g., `assets/images/`) not individual file names — so that adding
sprites does not require `pubspec.yaml` edits.
### Audio assets
| Sound | Type | Source | License |
|---|---|---|---|
| Background music | loop (60–90 s) | <free pack URL or AI prompt> | <license> |
| Jump / tap SFX | one-shot (< 1 s) | — | — |
| Hit / damage SFX | one-shot | — | — |
| Level complete | jingle (3–5 s) | — | — |
| Game over | sting (2–4 s) | — | — |
Recommended free sources: `freesound.org` (CC0 filter), `opengameart.org` audio section,
`itch.io` free audio packs.
Audio files go in `assets/audio/`. Add `assets/audio/` as a top-level assets entry in
`pubspec.yaml`.
Package: `flame_audio` (wraps `audioplayers`). BGM uses `FlameAudio.bgm.play()`; frequent SFX use an
`AudioPool` (see the **Audio** patterns in `docs/game-gotchas.md`).
Audio plan requirements (per `docs/game-gotchas.md`):
- **iOS format**: bundle **WAV** (22 kHz mono 16-bit). iOS does not reliably play OGG — convert
OGG/MP3 with `ffmpeg -i in.ogg -ac 1 -ar 22050 -sample_fmt s16 out.wav`.
- **Volume caps**: specify safe per-channel caps (e.g. BGM ≤ 0.2, SFX ≤ 0.7) that become the
`GameConfig` defaults; the user slider is a fraction of the cap.
### flutter_launcher_icons
> Branding art source: by default the generator draws the icon/splash from a shared code painter
> (`tool/gen_icon.dart`, design-token colors); choose **AI-generated art** here instead if the
> concept warrants it. Either way the generator (§5c.9) runs `flutter_launcher_icons` /
> `flutter_native_splash` and sets the localized app display name — design only specifies intent.
Specify the icon intent so the generator can configure `flutter_launcher_icons`:
```yaml
# pubspec.yaml excerpt (dev_dependencies)
flutter_launcher_icons:
android: true
ios: true
remove_alpha_ios: true
image_path: "assets/icons/icon.png" # 1024×1024, no alpha (iOS + legacy)
adaptive_icon_background: "<hex colour>" # solid, opaque
adaptive_icon_foreground: "assets/icons/icon-fg.png" # safe-zone padded (NOT full-bleed)
adaptive_icon_monochrome: "assets/icons/icon-mono.png" # Android 13+ themed icon
min_sdk_android: 21
web:
generate: false
```
The icon must:
- Be 1024×1024 pixels, PNG format; iOS `icon.png` is **opaque** (no alpha).
- Follow the app's art direction (character portrait or logo on the `Primary` background).
- **Android adaptive foreground** (`icon-fg.png`): the motif must sit inside the **safe zone** — the
108dp canvas is masked to ≤72dp and only the centre ~66dp is guaranteed unclipped, so keep ~25%
padding on every side. Reusing the full-bleed `icon.png` here makes the icon look cropped/"zoomed"
on Android. Background is a solid colour (`Primary`/Background), not an image.
- **Android themed icon** (`icon-mono.png`): a single-colour (white-on-transparent) silhouette in the
same safe zone; Android 13+ tints it to the user's theme. Without it the icon looks out of place
among themed icons (Google Play increasingly expects one).
### flutter_native_splash
Specify the splash screen intent for `flutter_native_splash`:
```yaml
# pubspec.yaml excerpt
flutter_native_splash:
color: "<Background hex>" # matches DesignTokens.background
image: assets/images/splash.png # centred logo/character, 288×288 px recommended
android_12:
color: "<Background hex>"
image: assets/images/splash.png
icon_background_color: "<Primary hex>"
ios: true
android: true
web: false
```
The splash image must:
- Be 288×288 pixels, PNG with transparent background.
- Show the app logo or the main character on a transparent field.
- Feel consistent with the main menu's visual style.
---
## Output
### 1. Write the design document
Create `docs/harness/plans/<YYYY-MM-DD>-design.md` (use today's UTC date).
If `docs/harness/plans/` does not exist, create it before writing.
The document must include all of the following sections (downstream validators grep for these
headings):
- `## Design tokens` — with palette table, typography scale, spacing scale
- `## Visual concept` — with art direction, sprite list, overlay/HUD layout
- `## Asset/audio plan` — with sprite sourcing table, audio table, `flutter_launcher_icons`
config, `flutter_native_splash` config
Fill every section from the PRD and `config.md`; do not leave placeholder text.
### 2. Update `state.md`
Update `docs/harness/state.md` per `docs/harness-protocol.md` §2 and the transition rule
in §7 (`design` → `contract`). Per §7 rule 2, a skill that completes successfully and
advances the pipeline sets `status: running` together with `current_phase` and `next_role`
in the same atomic write:
```yaml
status: running
current_phase: design
next_role: contract
updated_at: "<ISO-8601 UTC now>"
```
Leave all other keys unchanged. Use `Edit` for a targeted update.
### 3. Append to `pipeline-log.md`
Append one row to `docs/harness/pipeline-log.md` per `docs/harness-protocol.md` §6:
```
| <ISO-8601 UTC now> | complete | design | design_tokens spec written; next: contract |
```
---
## Error handling
- If the PRD is missing or empty, abort with a clear message and set `state.md` to
`status: paused`, `pause_reason: manual_action`.
- If `config.md` cannot be read, abort immediately (do not write partial output).
- If the genre or tone cannot be determined from the PRD, choose a neutral palette
(dark background `#1A1A2E`, bright accent `#E94560`) and note the assumption in the
design document under a `## Assumptions` heading.