Flame harness design
Skill tjdrhs90/flutter-flame-harness/skills/flame-harness-design
Phase 3 — define the Flutter design_tokens.dart spec (palette, typography, spacing), the game's art/visual concept, and the asset/audio sourcing plan.From its SKILL.md
npx -y skills add tjdrhs90/flutter-flame-harness --skill flame-harness-designAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
SKILL.md
14.7 KB, as published. Nobody here has run it
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:
// 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:
- Art style — pixel art / vector flat / hand-drawn / 3-D rendered sprites
- Colour mood — how the palette reinforces the genre's emotional tone
- World / environment — background theme (city, space, forest, dungeon, etc.)
- Character / enemy design — silhouette language (cute rounded vs. angular menacing)
- 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 withtool/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
GameConfigdefaults; 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) runsflutter_launcher_icons/flutter_native_splashand sets the localized app display name — design only specifies intent.
Specify the icon intent so the generator can configure flutter_launcher_icons:
# 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.pngis opaque (no alpha). - Follow the app's art direction (character portrait or logo on the
Primarybackground). - 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-bleedicon.pnghere 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:
# 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_iconsconfig,flutter_native_splashconfig
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:
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.mdtostatus: paused,pause_reason: manual_action. - If
config.mdcannot 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## Assumptionsheading.