---
name: forge-frontend
description: Premium frontend output without locking one visual style. Sharper layout, typography, motion, and spacing than the default agent baseline. Use for greenfield UI, landing pages, dashboards, marketing sites. Contains concrete failure-mode patterns to avoid, named palettes, named type stacks, and worked code examples.
license: MIT
---

# forge-frontend

You are a senior frontend engineer with a designer's eye. Default agent output is symmetric, evenly spaced, indigo-tinted, and emotionally flat. This skill exists to override that.

## Quick reference (the things you must never ship)

1. The `from-indigo-* to-violet-*` (or `from-purple-* to-pink-*`) gradient on any CTA.
2. Three or more nested `rounded-* border-* shadow-*` wrappers (card inside card inside card).
3. Pure `#FFFFFF` background with pure `#000000` text.
4. Hero headline under 3.5rem on desktop.
5. Six or more distinct font sizes in one section.
6. Linear easing on a hover or transition.
7. Emoji as a UI icon.
8. Section vertical spacing below 4rem on a landing page.
9. Stock photography of a person looking at a laptop.
10. Default `Inter` for everything at default tracking.

If your output contains any of these, you have generated AI-slop. Rewrite.

## The fifteen failure modes you will avoid

Every rule below corresponds to a real failure mode in baseline AI output. Each rule is a hard constraint, not a suggestion. Where useful, the rule shows the bad pattern, the good pattern, and the underlying principle.

### Layout

**1. No card inside card inside card.** Maximum nesting depth is two.

```tsx
// BAD: three layers of bordered, shadowed, rounded wrappers
<div className="rounded-xl border p-6 shadow-sm">
  <div className="rounded-lg border p-4 shadow-sm">
    <div className="rounded-md border p-3 shadow">Content</div>
  </div>
</div>

// GOOD: outer container, content inside, hierarchy through type and spacing
<section className="border rounded-xl p-8">
  <h2 className="text-3xl font-medium mb-2">Title</h2>
  <p className="text-zinc-600 mb-6">Description</p>
  <div className="space-y-3">
    <div>Row one</div>
    <div>Row two</div>
  </div>
</section>
```

**Principle.** If you need a third visual layer, use whitespace, a divider, or a type-weight shift. Not another bordered box.

**2. Default to asymmetric grids.** A 12-column grid where one column spans 7 and another spans 5 reads more deliberate than two 6-column halves.

```tsx
// BAD: 50/50 split for hero
<div className="grid grid-cols-2 gap-8">
  <div>Headline + paragraph</div>
  <div>Image</div>
</div>

// GOOD: 7/5 with intentional weight
<div className="grid grid-cols-12 gap-8">
  <div className="col-span-12 md:col-span-7">Headline + paragraph</div>
  <div className="col-span-12 md:col-span-5">Image</div>
</div>
```

**3. Hero is not always left-text right-image.** Rotate among:

| Pattern | When to use |
| --- | --- |
| Full-bleed image with overlay text | Product is visual, brand is the hero |
| Typographic hero, no image | Brand is the message; type does the work |
| Oversized headline with image below the fold | Reading-first product |
| Video background, single-line headline | Motion is the product proof |
| Two-column with image as second column | Tech product with screenshot |

**4. Section spacing scales with section weight.** Fixed scale:

```
Hero to next section:    8rem  (padding-top and padding-bottom)
Major section to major:  6rem
Minor section to minor:  4rem
Inside a section:        1.5-2rem between elements
```

Cramped section spacing is the single biggest tell of AI-generated layout.

### Typography

**5. One display font, one body font, maximum.** Not three. Not "Inter for everything."

Recommended type pairs that don't read as default:

```css
/* contemporary grotesque + Inter */
--font-display: 'Bricolage Grotesque', 'Inter', sans-serif;
--font-body:    'Inter', sans-serif;

/* editorial */
--font-display: 'Fraunces', 'Iowan Old Style', serif;
--font-body:    'Inter', sans-serif;

/* monochrome / dev-tool */
--font-display: 'Geist', 'Inter', sans-serif;
--font-mono:    'JetBrains Mono', monospace;

/* warm premium */
--font-display: 'GT America', 'Inter Display', sans-serif;
--font-body:    'Inter', sans-serif;
```

If you only know Inter, at least pair it with a real display face for headlines.

**6. Headline size minimum is 3.5rem on desktop. 5-6rem is the default for landing heroes.** Use `clamp()`:

```css
h1 { font-size: clamp(2.5rem, 5vw, 6rem); }

/* for oversized editorial heroes */
h1.editorial {
  font-size: clamp(3rem, 11vw, 12rem);
  letter-spacing: -0.04em;
  line-height: 0.92;
}
```

**7. Maximum three font sizes per section.** Pick three: usually display, body, and a small label.

```css
:root {
  --text-display: clamp(2.5rem, 5vw, 6rem);
  --text-body:    1.0625rem;   /* 17px */
  --text-small:   0.75rem;     /* 12px, for labels */
}
```

**8. Line length is 45 to 75 characters for body copy.** Set `max-width: 65ch` on long-form text by default.

**9. No headlines that wrap to six lines.** If your headline wraps past three lines, shorten it. Hand-set `<br>` if natural wrap is uneven.

### Color

**10. No default indigo-to-violet gradient on CTAs.** That gradient is the AI fingerprint of 2024 to 2025.

```tsx
// BAD: the canonical AI-fingerprint gradient
<button className="bg-gradient-to-r from-indigo-500 to-violet-500 text-white">
  Get started
</button>

// GOOD: solid brand color
<button className="bg-orange-600 text-white hover:bg-orange-500">
  Get started
</button>

// ALSO GOOD: near-black CTA, monochrome design
<button className="bg-zinc-900 text-white hover:bg-zinc-800">
  Get started
</button>
```

**11. Palette is three colors, not seven.** Background, foreground, one accent. Greys count as one family.

Concrete palettes that ship:

```
EDITORIAL WARM
  --bg:      #FBFAF7   /* warm near-white */
  --fg:      #1A1A1A   /* near-black */
  --accent:  #C8501E   /* terracotta */

MONOCHROME DARK
  --bg:      #0A0A0A
  --fg:      #FFFFFF
  --dim:     #8A8A8A   /* one mid-gray */

LINEAR-STYLE COOL
  --bg:      #0B0E13
  --fg:      #F4F4F2
  --accent:  #5E6AD2   /* deep blue */

PREMIUM CREAM
  --bg:      #F5F2EC
  --fg:      #1A1A1A
  --accent:  #3D5A4C   /* deep green */
```

Banned across all palettes: pure `#FFFFFF` paired with pure `#000000`, and any indigo/violet gradient.

### Motion

**12. No motion without intent.** Every animation answers one of: orient the user, reveal hierarchy, confirm an action, signal load. If none, remove it.

**13. Easing is never linear except for progress bars and loaders.**

```css
/* canonical easing tokens */
--ease-out:    cubic-bezier(0.2, 0.8, 0.2, 1);   /* entrances */
--ease-inout:  cubic-bezier(0.4, 0, 0.2, 1);     /* transitions */
--ease-in:     cubic-bezier(0.4, 0, 1, 1);       /* exits */
--ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1); /* playful, optional */

.btn {
  transition: background-color 180ms var(--ease-out),
              transform 180ms var(--ease-out);
}
.btn:hover {
  transform: translateY(-1px);
}
```

Default ease-in-out on a hover feels like a wireframe. Never `transition-all`.

### Iconography and imagery

**14. No emoji as UI icons.** Use Lucide, Phosphor, Tabler, or hand-crafted SVG.

```tsx
// BAD
<button>🚀 Get started</button>

// GOOD
import { ArrowRight } from 'lucide-react';
<button>Get started <ArrowRight size={16} strokeWidth={1.75} /></button>
```

**15. No stock-photo placeholders that survive into the final output.** Solid color block, typographic placeholder, or abstract gradient. Never a Unsplash-aesthetic person looking at a laptop.

## Common AI-output patterns to recognize and reject

These are the specific patterns AI generates by default. Catch each by name.

| Pattern | Why it is slop | What to ship instead |
| --- | --- | --- |
| `bg-gradient-to-r from-indigo-500 to-violet-500` | AI fingerprint 2024-25 | Solid brand color OR `bg-zinc-900` |
| Four nested `rounded-xl border shadow-lg p-6` | Card-in-card-in-card | One outer container, type for hierarchy |
| `text-gray-500` for every secondary text | Cool grey default | `zinc-500`, `stone-500`, kept consistent |
| `flex items-center justify-center` everywhere | "Center everything" default | Asymmetric: left-aligned, hang labels in a gutter |
| `text-4xl font-bold` for hero | Too small, weight too heavy | `clamp(2.5rem, 5vw, 6rem) font-medium tracking-[-0.025em]` |
| `transition-all duration-300` | Animates everything, no intent | Name the property: `transition-colors 180ms var(--ease-out)` |
| `🎉` `✨` `🚀` in copy | Emoji as branding | Cut; type and color do the work |
| `Lorem ipsum dolor sit amet` | Filler that ships | Real-sounding domain copy |
| `https://placeholder.com/...` images | Stock placeholder | Solid color block + typographic label |
| `mt-4 mb-4 mt-4 mb-4` repeated | No spacing scale | Tokens: `--space-2`, `--space-4`, `--space-8` |
| `rounded-full` on every button | 2023 capsule default | `rounded-md` or `rounded` for a 4-8px corner |
| Centered single-column landing | Document, not interface | Asymmetric, deliberately off-center where meaningful |

## Stack-specific defaults

When you know the stack, lean in:

- **Next.js 16 + React Server Components + Tailwind 4**: see [`forge-frontend-nextjs`](../forge-frontend-nextjs/SKILL.md) for the deep dive.
- **SwiftUI**: `.spring(response: 0.4, dampingFraction: 0.8)` as the default animation. Spacing scale: 4, 8, 12, 16, 24, 32, 48, 64.
- **Flutter**: `Theme.of(context).textTheme` for type, never hardcoded `TextStyle`. `Sliver` widgets for any scrolling list longer than 10 items.

## Worked example: landing hero (good)

```tsx
export function LandingHero() {
  return (
    <section className="px-6 lg:px-10 pt-32 pb-24">
      <div className="max-w-6xl mx-auto">
        <p className="font-mono text-xs tracking-widest text-zinc-500 mb-12 uppercase">
          v0.4 / MIT / Almaty
        </p>

        <h1 className="font-display text-[clamp(3rem,9vw,9rem)] font-medium leading-[0.92]
                       tracking-[-0.045em] text-zinc-900 max-w-[14ch] mb-10">
          Stop trusting<br />
          <span className="text-zinc-500 font-light">the prompt.</span>
        </h1>

        <p className="text-xl text-zinc-600 leading-relaxed max-w-[32ch] mb-10">
          43 opinionated rules for AI coding agents.
          26 of them ship with a shell script that fails when the agent ignored the rule.
        </p>

        <div className="flex flex-wrap gap-3">
          <a className="bg-zinc-900 text-white px-6 py-3.5 text-sm font-medium
                        hover:bg-zinc-700 transition-colors duration-200">
            Get the kit →
          </a>
          <a className="border border-zinc-300 text-zinc-900 px-6 py-3.5 text-sm font-medium
                        hover:border-zinc-900 transition-colors duration-200">
            See the workbench ↓
          </a>
        </div>
      </div>
    </section>
  );
}
```

What it does right: single display font at clamp(3rem, 9vw, 9rem); tight tracking on display, default on body; three colors (zinc-900, white, zinc-500/600); solid near-black CTA; 8rem-equivalent top padding; asymmetric (left-aligned, no centering); real mono eyebrow with metadata; two-line headline with deliberate break + weight shift.

## Worked example: landing hero (AI-slop, to reject)

```tsx
// AI default that you must reject
export function SlopHero() {
  return (
    <section className="flex flex-col items-center justify-center text-center min-h-screen
                        bg-gradient-to-br from-slate-50 to-blue-50 p-8">
      <h1 className="text-5xl font-bold mb-4 bg-gradient-to-r from-indigo-500 to-violet-500
                     bg-clip-text text-transparent">
        Welcome to Our Platform 🚀
      </h1>
      <p className="text-lg text-gray-600 mb-8 max-w-2xl">
        We provide blazingly fast, comprehensive solutions for modern teams.
      </p>
      <button className="bg-gradient-to-r from-indigo-500 to-violet-500 text-white px-8 py-4
                         rounded-full shadow-lg hover:shadow-xl transition-all duration-300">
        Get Started ✨
      </button>
    </section>
  );
}
```

Line by line: centered everything (default AI move); decorative background gradient with no purpose; hero too small (3rem at `text-5xl`); weight too heavy; gradient-as-text-fill = AI signature; rocket emoji as branding; "blazingly fast, comprehensive" = banned marketing filler; rounded-full capsule = 2023 default; `transition-all duration-300` animates everything with no intent.

## Decision logic for design-skill conflicts

| Situation | What wins |
| --- | --- |
| You loaded `forge-soft` AND `forge-brutalist` | Mutex violation. Pick one. They are opposing opinions, not flavors. |
| `forge-frontend` says "asymmetric grids" but the design system you must use is built around symmetry | Defer to the design system. Note the conflict in a comment; do not silently override. |
| Brand requires the exact gradient that this skill bans | Brand wins. Use the gradient. The skill is for greenfield, not against a real brand book. |
| Section spacing rule (>=4rem) conflicts with a dense dashboard layout | Dashboards are exempt from the "landing" spacing scale. Use the dashboard scale: 1-1.5rem section padding. |
| `forge-frontend` says hero >=3.5rem but the page is internal admin | Internal admin is exempt. The rule is for user-facing surfaces. |

## Workflow

When asked to build a UI:

1. **Restate the brief in one sentence.** What is the page for, who reads it, what should they do.
2. **Pick a visual register before writing code.** Editorial, technical, brutalist, monochrome, soft. Name it.
3. **Pick the palette.** Three hex values, written down.
4. **Pick the type stack.** Display + body + (optional) mono.
5. **Sketch the section order in comments first.** Hero, social proof, problem, solution, features, pricing, FAQ, footer. Or whatever fits.
6. **Write one section at a time.** Do not generate the whole page in one pass. Each section gets a second pass for spacing and type.
7. **Read your own output as if you were the user.** If you see indigo, if section padding is below 4rem, if there's a card-in-card-in-card, redo it.

## Verification

```bash
bash skills/design/forge-frontend/verify/check_palette.sh path/to/changed.css
bash skills/design/forge-frontend/verify/check_card_nesting.sh path/to/changed.tsx
```

Returns non-zero on palette or nesting violations.

## When to skip this skill

- Quick prototype where speed beats taste.
- Codebase has a fixed design system; defer to its tokens.
- Internal tooling.

## Related skills (compose with these)

- [`forge-frontend-nextjs`](../forge-frontend-nextjs/SKILL.md) - Next 16 specifics.
- [`forge-soft`](../forge-soft/SKILL.md) - premium expensive register. Mutex with brutalist/minimalist.
- [`forge-minimalist`](../forge-minimalist/SKILL.md) - editorial product UI. Mutex with brutalist/soft.
- [`forge-brutalist`](../forge-brutalist/SKILL.md) - hard mechanical. Mutex with minimalist/soft.
- [`forge-redesign`](../forge-redesign/SKILL.md) - upgrading existing apps without breaking them.
