# Frontend Design

> Frontend design-director orchestrator. Chains typography + motion (+ shader in Phase A2) → frontend-design-spec.json → calls generate-web/generate-app for rendering. Design-director-layer per v3.7 family-separation.

- Skill: `jokerman89/frontend-design` (Agent Skill)
- Install (CLI): `npx skillmds@latest add jokerman89/frontend-design`
- Raw SKILL.md: https://api.skillmd.com/api/skills/jokerman89/frontend-design/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Design & Media
- Author: jokerman89 (https://skillmd.com/u/jokerman89)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/jokerman89/frontend-design

---


You are the `frontend-design` orchestrator skill — entrypoint for production-ready frontend design e2e per v3.7 frontend-* family.

## What this skill does

Orchestrates **design-director-decisions** (typography + motion + shader thesis + component-library-pick) → produces `frontend-design-spec.json` → calls **rendering-engine** (`generate-web` single-file/Next.js or `generate-app` full-vite/svelte/next-monorepo — Phase B) for file-output.

The skill OWNS design-decisions. It does NOT own HTML/Next.js-file-generation (that's the generate-* family per the v3.7 design-doc-boundary).

Reads operator brief → dispatches typography + motion sub-skills **in parallel** → synthesizes `frontend-design-spec.json` → calls rendering-engine.

## When to use

- "A landing page for our legal-research assistant" — full end-to-end mode for production-ready design
- Customer demo that must look Awwwards-grade
- Internal microsite where visual quality affects adoption
- Multi-format engagement where /li:cycle BUILD-phase produces an app + design must match the pitch

## When NOT to use

- Wireframe-only sketch → `/li:design-html` (existing skill)
- Single design-decision-axis (just typography or just motion) → solo sub-skill `/li:frontend-typography` or `/li:frontend-motion`
- Pure file-gen without design-direction → `/li:generate-web` directly with `--brief`
- Re-render existing run → `/li:generate-web --from-frontend-design <existing-run-dir>`

## Inputs

- Required `<brief>` — design brief text or path to brief.md
- Optional `--pattern <vault-name>` — select from `~/.lintel/brand/design-patterns/<name>/` (Phase A2 enables canonical pattern)
- Optional `--target-format <single-file|nextjs|app>` — default: `single-file`. `app` triggers generate-app (Phase B)
- Optional `--customer-share` — sets CUSTOMER_SHARE=1, triggers compliance-gate + voice-gate
- Optional `--out <path>` — output path (default: `~/.lintel/frontend-runs/<run-id>/`)
- Optional `--skip-shader` — Phase A1 default (frontend-shader skill ships in A2)

## Workflow

### Step 1 — Parse invocation + warm context

```bash
brief="${1:-}"
pattern="${PATTERN:-}"
target_format="${TARGET_FORMAT:-single-file}"
customer_share="${CUSTOMER_SHARE:-}"
out_dir="${OUT:-$HOME/.lintel/frontend-runs/$(date +%Y%m%d-%H%M%S)-${RANDOM}}"

[ -z "$brief" ] && { echo "Usage: /li:frontend-design <brief> [--pattern <name>] [--target-format <single-file|nextjs|app>]"; exit 2; }
mkdir -p "$out_dir"
```

Voice-tier resolution: default `internal`. If `--customer-share` → run the active pack's voice gate (`resolve_pack_field compliance.hooks`; none by default) first. Per L-001-discipline: skill body preserves contract, agent at invocation produces actual content.

### Step 1.5 — Design DNA pass (REQUIRED — ADR-0015/0016)

Retrieval before generation. Resolve the active design profile and search the corpus BEFORE any
design decision:

```bash
dna="${LINTEL_SKILLS_DIR:-skills}/design-dna"   # LINTEL_SKILLS_DIR = this skill's base-dir parent (plugin installs)
source "${LINTEL_SOURCE_ROOT:-${LINTEL_REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null)}}/lib/pack-resolver.sh" 2>/dev/null \
  || source "$HOME/.lintel/lib/pack-resolver.sh" 2>/dev/null
profile="$(resolve_pack_field design.profile 2>/dev/null)"
[ -z "$profile" ] || [ "$profile" = "null" ] && profile="anthropic-default"
python3 "$dna/scripts/search.py" "<product> <industry> <tone keywords from brief>" \
  --design-system -f markdown -p "<project>" > "$out_dir/design-dna.md"
```

Resolver unsourceable → surface it before defaulting (a pack's declared profile is never
silently ignored).

Precedence: **brief > profile (`$dna/profiles/$profile.yaml`) > corpus hit** — the profile is the
house default (anthropic-default: warm ink-and-paper); the corpus recommendation fills what the
profile doesn't pin (style pattern, landing structure, product-specific palette when the brief
asks for one); the brief's own words always win. Both `design-dna.md` and the profile feed
Step 2-4 dispatch and Step 5 synthesis. No python3 → use the grep fallback documented in
`skills/design-dna/SKILL.md` (the corpus is plain CSV).

### Step 2-4 — Parallel sub-skill dispatch (M-4 resolution)

**Run typography + motion sub-skills CONCURRENTLY** (single-batch Agent-tool dispatch). They are independent — both take the brief as input, neither depends on the other.

```
Concurrent dispatch:
  ├─ /li:frontend-typography --brief "$brief" --out "$out_dir/typography.json"
  └─ /li:frontend-motion --brief "$brief" --out "$out_dir/motion.json"

(Phase A2 adds parallel /li:frontend-shader → $out_dir/shader.json)
```

Both sub-skills receive `$out_dir/design-dna.md` + the active profile as context (Step 1.5
outputs): typography starts from the profile's font roles + the corpus pairing hits; motion
starts from the profile's duration/easing tokens. They deviate only where the brief demands it.

Wallclock budget: ~60s concurrent (vs ~180s sequential). Wait for both to complete before Step 5.

### Step 5 — Synthesize `frontend-design-spec.json`

Read typography.json + motion.json (+ shader.json if A2). Synthesizes into `frontend-design-spec.json`:

```json
{
  "schema_version": 1,
  "generated_at": "<iso-8601>",
  "brief_hash": "<sha256 of brief>",
  "source": "frontend-design",
  "target_format": "single-file | nextjs | app",
  "typography": { /* embedded from typography.json */ },
  "motion": { /* embedded from motion.json */ },
  "shader": null,
  "component_libraries": [
    {"name": "shadcn", "kind": "primitive"},
    {"name": "<aceternity|magic-ui|park-ui>", "kind": "motion-enhanced"}
  ],
  "layout_grammar": {
    "max_width": "1200px",
    "section_spacing": "var(--space-section)",
    "grid": "12-col"
  },
  "interaction_signature": {
    "scroll_smoothing": true,
    "hover_intent": "subtle",
    "page_transitions": "fade-or-slide"
  },
  "palette": {
    "source_profile": "anthropic-default",
    "tokens": { "<semantic-name>": "<hex>" },
    "contrast_verified": true
  },
  "style": {
    "name": "<chosen style from design-dna search>",
    "anti_patterns": ["<from the corpus reasoning rule>"]
  },
  "design_dna": {
    "profile": "anthropic-default",
    "search_query": "<the Step 1.5 query>",
    "search_ref": "design-dna.md"
  },
  "visual_thesis": "<one-paragraph synthesis>",
  "voice_tier": "internal | customer-share"
}
```

`palette`, `style` and `design_dna` are additive optional fields (ADR-0015) — `schema_version`
stays 1; readers tolerate their absence (minor-additive per the schema-evolution policy below).

**Schema-version discipline (M-5 resolution):** ALL Lintel frontend-* contract-JSON files include `"schema_version": 1`. generate-web/generate-app readers log+reject on unknown major version. Schema-evolution policy: minor changes additive (new fields tolerated), major changes require new version + migration-path.

**source-discriminator:** `"source": "frontend-design"` distinguishes from pipeline's `design-spec.json` `"source": "pipeline"` (M-1 resolution — different filenames + explicit discriminator field).

### Step 6 — Call rendering-engine

```bash
case "$target_format" in
  single-file|nextjs)
    /li:generate-web --from-frontend-design "$out_dir"
    ;;
  app)
    /li:generate-app --from-frontend-design "$out_dir"   # Phase B skill
    ;;
esac
```

Phase A1 NOTE: `--from-frontend-design` mode in generate-web ships in the Phase B PR. Phase A1 stops at frontend-design-spec.json emission + the minimum-viable roundtrip test verifies the contract is consumable.

### Step 7 — Quality gate (MANDATORY — ADR-0015)

The gate is no longer optional. Two parts, in order:

```bash
# 1. Mechanical validator on every rendered HTML artifact (exit 1 blocks).
#    Spec-only runs have no HTML yet — the guard defers validation to generate-*.
if compgen -G "$out_dir/*.html" > /dev/null; then
  python3 "$dna/scripts/validate_design.py" "$out_dir"/*.html \
    --profile "$dna/profiles/$profile.yaml" || status=BLOCKED
fi

# 2. Six-dimension audit
/li:frontend-design-review "$out_dir"
```

Validator errors → **BLOCKED** (fix and re-render; never ship over a red gate). No rendered HTML
yet (spec-only run) → validator runs in generate-web/generate-app instead; the review still runs
on the spec. python3 absent → run the review with the design-dna non-negotiables checklist
explicitly in scope.

### Step 8 — Output paths + recommendation

```
FRONTEND-DESIGN RUN COMPLETE
══════════════════════════════════════════════════════════════════

Run dir:                $out_dir
Typography spec:        $out_dir/typography.json
Motion spec:            $out_dir/motion.json
Frontend design spec:   $out_dir/frontend-design-spec.json

Voice tier:             $voice_tier
Target format:          $target_format

Next:
  Render:               /li:generate-web --from-frontend-design $out_dir   (Phase B)
  Review:               /li:frontend-design-review $out_dir                 (Phase A2)
  Extract as pattern:   /li:frontend-style-extract $out_dir/*               (Phase A2)
```

## Voice tier behavior

`voice: mixed`. Default `internal`. `--customer-share` triggers the active pack's compliance-gate + voice-gate (`resolve_pack_field compliance.hooks`; none by default).

## Status protocol

- **DONE** — both sub-skills returned, frontend-design-spec.json written, schema-validation passed
- **DONE_WITH_CONCERNS** — sub-skill returned with warnings (e.g., font-license unclear)
- **BLOCKED** — sub-skill failed, OR brief unparsable, OR customer-share check failed
- **NEEDS_CONTEXT** — brief too vague (no audience, no purpose, no aesthetic-direction)

## Integration

**Reads:**
- `<brief>` argument (path or inline text)
- `~/.lintel/brand/design-patterns/<name>/` (if `--pattern` flag set; Phase A2 enables)
- `~/.lintel/profile.yaml` (mode → voice-tier)

**Writes:**
- `~/.lintel/frontend-runs/<run-id>/typography.json` (via frontend-typography sub-skill)
- `~/.lintel/frontend-runs/<run-id>/motion.json` (via frontend-motion sub-skill)
- `~/.lintel/frontend-runs/<run-id>/frontend-design-spec.json` (Step 5 synthesis)
- Audit-log: `.claude/runtime/audit/frontend-design-runs.jsonl`

**Calls into:**
- `/li:design-dna` system search + profile resolution (Step 1.5, required) + validator (Step 7)
- `/li:frontend-typography` (sub-skill, parallel)
- `/li:frontend-motion` (sub-skill, parallel)
- `/li:generate-web --from-frontend-design <run-dir>` (Phase B)
- the active pack's voice gate (`resolve_pack_field compliance.hooks`; none by default — if customer-share)
- `/li:compliance-gate` (existing, if customer-share)

**Boundary with the generate-* family:**

frontend-design is the DESIGN-DIRECTOR-LAYER (decisions). generate-web/generate-app are the RENDERING-ENGINE-LAYER (file-output). Frontend-design CALLS into generate-* for rendering. Not vice versa. Sharp boundary per the v3.7 design-doc family-separation-table.

**Brand-asset-slots (Phase A1 documents paths; folders lazy-created):**
- `~/.lintel/brand/design-patterns/` — Phase A2 ships canonical `ultra-modern-lovable-style/`
- `~/.lintel/brand/motion-libraries/` — operator-tested GSAP/Lenis-combos
- `~/.lintel/brand/shader-snippets/` — operator-curated GLSL (Phase A2 + frontend-shader)

## Anti-patterns

- **Generating HTML inside frontend-design** — that's generate-web's job (boundary-violation per L-002). Use `--from-frontend-design` chain.
- **Sequential sub-skill dispatch** — Workflow Step 2-4 explicitly PARALLEL per M-4. Sequential breaks 10-min budget.
- **Pre-baking canonical patterns** — Phase A1 ships slot-bootstrapping only. Canonical hand-curation deferred to A2 after schema validates against operator-real briefs.
- **Bundling commercial fonts/libraries** — Lintel ships scaffolding. Operator licenses Pangram + installs GSAP/OGL/Aceternity via npm.

## Failure recovery

- Sub-skill timeout (>120s): mark sub-skill output as STUB + continue with partial synthesis; surface to operator with "partial-spec" warning
- Brief unparsable: BLOCKED, return to operator with prompt-improvement-suggestions
- Schema-validation failure on frontend-design-spec.json: BLOCKED, log diff between produced + expected schema
- Voice-gate fail (customer-share): BLOCKED, surface voice-check output verbatim

## Recommended next steps after invocation

- Phase A1: hand off `$out_dir/frontend-design-spec.json` to generate-web manually for now (Phase B automatic chain)
- Phase A2: pair with `/li:frontend-design-review` for 6-dimension audit
- Phase A2+: extract successful design via `/li:frontend-style-extract $out_dir/*` → adds to vault
- Cycle-integration: defer to Phase D after operator dogfood validates real-engagement flow

## L-001/L-002/L-003 application

- **L-001 (scaffolding-not-content):** skill body is contract. Agent at invocation produces actual typography choices, motion language, shader thesis. Canonical pattern deferred to A2. Operator-extracted patterns dominate vault long-term.
- **L-002 (grep-first):** the generate-* family is preserved. Frontend-* family = identity-anchor + design-director layer, NOT a replacement. Boundary-table-row added to the design-doc.
- **L-003 (verify-claims):** schema_version field on every contract JSON. generate-web reader verifies before consuming. Don't trust stale schemas.

