---
name: distinctive-frontend
description: Generate distinctive, production-grade frontend interfaces. Use whenever building UI, components, pages, artifacts, or refactoring existing screens for visual quality. Forces concrete commitment and reflex-rejection before any code is written.
---

# Distinctive Frontend

Most AI-generated UI converges on the same handful of choices: Inter, slate-on-white, purple→blue gradient, rounded cards in a 3-column grid, Lucide icons, shadcn defaults. This skill stops that by forcing you to (1) commit to concrete coordinates BEFORE writing code, (2) explicitly enumerate and reject your reflex defaults in writing, (3) audit for slop fingerprints AFTER.

Run all five phases in order. Skipping is the failure mode. Do not start coding before Phase 3.

---

## Tailwind CSS (CDN) — baseline setup

This skill uses **Tailwind CSS via the Play CDN** as the styling layer. Load it once in `<head>`:

```html
<script src="https://cdn.tailwindcss.com"></script>
```

The Play CDN ships the full JIT engine in the browser — every utility is available, including arbitrary values (`text-[1.375rem]`, `bg-[oklch(0.72_0.15_65)]`), variants (`focus-visible:`, `@container`), and `tailwind.config` overrides via an inline `<script>` block before any markup. It is intended for prototyping and artifacts, not production builds.

Tailwind is the **default expression layer** — when a rule in this skill can be written as a utility class, write it as a utility class. Drop to a `<style>` block only for: custom properties (design tokens), `@font-face`, complex keyframes, `::selection`, the one distinctive detail if it needs raw CSS, and anything Tailwind genuinely cannot express. Do not hand-write utilities Tailwind already has.

Define design tokens as CSS custom properties in a `<style>` block, then reference them from Tailwind via arbitrary values (`bg-[var(--surface)]`, `text-[var(--text)]`) or via a `tailwind.config` `theme.extend` block. Either is fine; pick one and stay consistent inside a single artifact.

---

## Phase 1 — Anchor (do this in writing, before any code)

Write these five lines literally. Do not generalize. Do not skip.

1. **Physical reference object** — a real, non-digital thing this UI should feel like. NOT "modern dashboard" or "clean SaaS". Examples: *a wax-sealed envelope*, *a 1970s Braun calculator*, *a fabric care label*, *a Japanese train ticket*, *a chemistry lab notebook*, *a vinyl record sleeve*, *a hand-stamped library card*, *a pharmacy prescription bag*, *a museum exhibit caption*, *a 1980s mainframe terminal manual*. Specific enough to imagine in your hand.

2. **Three brand words** — concrete and uncomfortable. NOT "modern", "clean", "elegant", "professional", "innovative", "sleek", "intuitive", "futuristic". Use words like *opinionated*, *quiet*, *mechanical*, *patient*, *stubborn*, *worn*, *cold*, *warm-but-formal*, *bureaucratic*, *handmade*, *unimpressed*.

3. **Density dial (1–10)** — 1 = breathing room, single column, gallery white. 10 = Bloomberg terminal, every pixel earning its place.

4. **Restraint dial (1–10)** — 1 = maximalist, layered, decorated. 10 = severely minimal, almost nothing on the page.

5. **Strangeness dial (1–10)** — 1 = totally familiar patterns. 10 = the user has to learn how to use it. Most genuinely-designed interfaces sit at 4–7. Below 3 is forgettable; above 8 is hostile to most products.

If two dials are both above 7 or both below 3, you have a coherence problem — re-pick.

---

## Phase 2 — Anti-Attractor Pass (the most important phase)

Your training data has defaults. They will reassert themselves unless you name and reject them out loud. For EACH category, list the **three things you would reach for first**, cross all three out, then pick from outside the list.

**Type — reflex rejection.** Your three reflexes are almost certainly from: Inter, Geist, DM Sans, Plus Jakarta, Outfit, Space Grotesk, Instrument Serif, Instrument Sans, Fraunces, Playfair, IBM Plex, Söhne, Satoshi. Reject all three. Pick from the curated list below — it is a substitute for browsing, not a substitute for thinking. Pick the face that fits the **physical reference object**, not the brand words.

Load fonts once via a Google Fonts `<link>` in `<head>`, then apply them with Tailwind via arbitrary families (`font-['Hanken_Grotesk']`) or — preferred — by extending `tailwind.config.theme.fontFamily` so you can write `font-sans`, `font-serif`, `font-display`.

**Sans-serifs (curated, free, Google-hosted):**
1. **Hanken Grotesk** — warm, precise grotesk in the ABC Diatype register. Variable weight, free on Google Fonts. The "better than Helvetica" pick.
2. **Figtree** — quiet humanist sans, designed to support display type rather than compete. Good workhorse body face.
3. **Albert Sans** — geometric sans with perpendicular terminals and warm curves; lands near Aeonik territory.
4. **Archivo** — grotesk with width and weight axes; the closest free pick for editorial systems that need condensed display cuts.
5. **Rubik** — geometric sans with subtle rounded quirks. Distinctive without being loud.
6. **Schibsted Grotesk** — newer Scandinavian grotesk built for Schibsted media. Same register as Söhne with more character than Inter.

**Serifs:**
1. **Newsreader** — variable across optical sizes, made for long-form reading. Idiosyncratic without being precious.
2. **Source Serif 4** — refined transitional serif, variable, scales from text to display.
3. **Lora** — warm, literary, excellent body text. The reliable editorial pick.
4. **Libre Caslon Text** — historical bones, contemporary execution. Pairs well with grotesks.
5. **Cormorant Garamond** — narrow, high-contrast, fashion/editorial register.
6. **Young Serif** — inscriptional, stone-carved, brand-identity-ready. Display only.
7. **DM Serif Display** — softer display serif by Colophon Foundry for Google Fonts.

**Display & expressive:**
1. **Bricolage Grotesque** — variable font with grade and width axes. Genuinely exciting recent addition to Google Fonts.
2. **Big Shoulders Display** — tall, narrow, condensed display sans. The free pick for poster moments and editorial hero type.

**Body font** — same reflex-rejection procedure. System fonts via Tailwind's `font-sans` default stack are an underrated answer when performance > personality. A monospace body (`font-mono`) is a real choice for the right brand. One family in multiple weights often beats two competing typefaces.

**Color** — your reflex is one of: indigo/violet, slate/zinc, emerald-on-dark, cyan-on-navy, neon-on-black, holographic gradient. Reject. Pick a hue from somewhere unexpected: a paint chip from a 1980s appliance, a botanical illustration, a vintage transit map, the actual color of your physical reference object. Use OKLCH — Tailwind's Play CDN accepts it in arbitrary values (`bg-[oklch(0.96_0.01_85)]`) and in `@theme`/`theme.extend` overrides. Tint neutrals 0.005–0.015 toward the brand hue — NOT toward warm orange or cool blue by reflex.

**Theme** — derive from context, not vibes. Late-night use, creative tools, focus apps → dark. Documents, daytime tools, anxious moments → light. Never pick dark "to look cool" or light "to play it safe". Use Tailwind's `dark:` variant driven by a `class` strategy so you can toggle it deliberately.

**Layout** — your reflex is centered hero → three feature cards → CTA → footer. Reject. Try: asymmetric two-column, single-column with offset margins, sidebar-led, full-bleed editorial, table-as-layout, tabs-as-pages, marginalia in the gutter. The grid is a starting point you may break.

**Iconography** — your reflex is Lucide. Reject for anything brand-facing. Options: Phosphor (multiple weights), Tabler, Iconoir, Material Symbols (custom weight/grade), or build 3–6 custom SVGs in your reference object's style. Lucide icons in rounded-square containers above feature headings are AI fingerprint #1.

---

## Phase 3 — Build

Each step uses the commitments from Phases 1–2. Order matters.

1. **Semantic HTML first**, no styling. Get the structure right.
2. **Type scale**: pick ONE ratio (1.2 / 1.25 / 1.333 / 1.5) and 5 sizes max. Body is fixed `rem` for app UI — Tailwind's `text-sm` / `text-base` / `text-lg` are already rem-based, so prefer them. Use arbitrary values (`text-[clamp(2rem,5vw,4rem)]`) only for marketing headings.
3. **Spacing scale**: Tailwind's default 4pt scale is the scale — `gap-1 gap-2 gap-3 gap-4 gap-6 gap-8 gap-12 gap-16 gap-24`. Use `gap-*` on flex/grid parents, never margins between siblings (no `space-y-*` either if you can use `gap-*`). Vary spacing for hierarchy; do not apply the same padding everywhere.
4. **Color tokens**: primitive layer (raw OKLCH values as CSS custom properties in a `<style>` block) → semantic layer (`--surface`, `--text`, `--accent`). Reference from Tailwind via `bg-[var(--surface)]` / `text-[var(--text)]`. Redefine only the semantic layer under `.dark` for dark mode.
5. **ONE distinctive detail (mandatory)** — a single element no other AI-generated site of this type would have. Marginalia in the gutter. A serial number in the footer. A hand-drawn divider. A custom cursor on one specific element. An unusual timestamp format. The way numbers tick over. ONE thing the user will screenshot. Without this, you have failed regardless of how clean the rest is.
6. **All eight states** for every interactive element: default, `hover:`, `focus-visible:`, `active:`, `disabled:`, loading, error, success. Keyboard users never see `hover:`, so never rely on it alone.
7. **Motion** on `transform` and `opacity` only. Durations 100–500ms — Tailwind's `duration-150` / `duration-300` / `duration-500` cover most cases; use arbitrary values for in-betweens. For easing, the Tailwind defaults are generic — override with arbitrary values: `ease-[cubic-bezier(0.16,1,0.3,1)]` for entrances, `ease-[cubic-bezier(0.7,0,0.84,0)]` for exits. No bounce, no elastic. Respect `motion-reduce:` variants to honor `prefers-reduced-motion`.

---

## Phase 4 — Slop Audit (mandatory before declaring done)

Walk this list. Any "yes" means rewrite that part.

- [ ] Display or body font is on this banned list: Inter, Geist, DM Sans, Plus Jakarta, Outfit, Space Grotesk, Instrument Serif, Instrument Sans, Fraunces, Playfair, IBM Plex, Söhne, Satoshi. (The Phase 2 curated list is the recommended pool — anything from there is fine.)
- [ ] Background or accent contains a `bg-gradient-to-*` from purple/violet/indigo → blue/cyan/teal, OR any neon-on-black "futuristic" gradient.
- [ ] `bg-clip-text` on any element. Gradient text is banned, no exceptions.
- [ ] Any `border-l-*` or `border-r-*` greater than 1px as a colored accent stripe on a card, alert, list item, or callout.
- [ ] A grid of 3 or 4 identical cards, each with a rounded-square icon above a heading above two lines of body text.
- [ ] A "hero metric" block: huge number, small label, supporting stats, gradient accent.
- [ ] A card nested inside another card.
- [ ] Glassmorphism / `backdrop-blur-*` translucent panels used decoratively rather than to solve a contrast problem.
- [ ] Pure `bg-black` / `bg-white` / `text-black` / `text-white` anywhere on the page (use tinted neutrals via tokens).
- [ ] Lucide icons in rounded-square containers above feature headings.
- [ ] Tailwind defaults: `bg-slate-900`, `bg-zinc-50`, `text-gray-500`, `rounded-2xl`, `shadow-lg`, `hover:scale-105`, `bg-gradient-to-r from-purple-600 to-blue-600`. If you are writing these class strings, stop.
- [ ] Every button is a primary button. Use ghost, link, and secondary styles too.
- [ ] Body text is `text-center` for more than one short paragraph.
- [ ] The "one distinctive detail" from Phase 3 step 5 is missing or weak.
- [ ] If you screenshotted it and showed someone, would they immediately say "AI made this"?

---

## Phase 5 — The Final Test

Cover the logo and the copy. Squint. If the layout, color, and type alone do not communicate the **physical reference object** from Phase 1, you built someone else's design. Return to Phase 3 step 5 and push the distinctive detail harder.

---

## Hard rules (always)

- OKLCH for all color, expressed via CSS custom properties or Tailwind arbitrary values. No HSL. No hex except brand-given values.
- WCAG AA minimum: 4.5:1 body text, 3:1 large text and UI components.
- `rem` for type (Tailwind's `text-*` utilities are already rem). Never `px` for type. Respect user zoom.
- `focus-visible:` variant always set, never raw `outline-none` without a replacement ring.
- Animate only `transform` and `opacity` (Tailwind: `transition-transform`, `transition-opacity`, or `transition-[transform,opacity]`).
- Native `<dialog>`, the Popover API, and CSS anchor positioning before custom JS.
- Container queries for components (`@container` + `@sm:` / `@md:` variants), viewport queries (`sm:` / `md:` / `lg:`) for page layout.
- Optimistic UI for cheap actions; undo over confirm for destructive ones.
- Button labels are verb + object: "Save changes" not "OK". "Delete project" not "Yes".
- One font family in multiple weights is often enough. Two is the maximum unless you can defend a third in writing.
- Cap body line length at 65–75ch via `max-w-[65ch]` / `max-w-prose`.
- Tabular numerals via Tailwind's `tabular-nums` utility for any data table or numeric column.

---

## Why this works (so you don't undermine it)

The slop list above looks negative, but its purpose is to force a *coordinate*, not just a refusal. Every banned pattern has a hundred alternatives — your job is to pick one of them with intention. The dials and the physical reference object give you the coordinate. The anti-attractor pass forces the rejection out of your hands and onto the page where you can see it. The distinctive detail is what someone will remember tomorrow.

If you find yourself thinking "but this is the right choice for this kind of product" about any banned pattern, that thought is the fingerprint of training-data convergence. Every other model is having the same thought right now. Pick something else.

---

## Placeholder images

You may include raster images via `<img>` when the page genuinely benefits from them. Use this exact placeholder source, and write the `alt` attribute as a detailed image prompt — a downstream step will replace each placeholder with a real generated image using that prompt as the input.

Use:

```html
<img
  src="https://placehold.co/1200x800/eeeeee/999999?text=+"
  alt="A detailed image prompt describing exactly what should appear here. Be specific about subject, framing, lens, lighting, palette and mood. Example: 'Wide-angle photograph of a sunlit Parisian café terrace at 8am, wet cobblestones after rain, espresso cups steaming on a marble counter, soft golden light, 35mm, warm muted palette.'"
  width="1200" height="800"
/>
```

Rules:
- Pick meaningful dimensions that match the aspect you want in the layout.
- The `alt` MUST read like a real image-generation prompt — specific subject, framing, lens, lighting, palette, mood. Never a vague label like "hero image" or "coffee".
- Only the `placehold.co` URL above. No stock photos, no unsplash URLs, no emoji stand-ins.
- Images should support the design, not carry it. A distinctive typographic layout without images beats a generic layout with big photos.
- Not every page needs images. If the design is stronger without them, ship it without them.

## Return format

Return ONE complete `<!doctype html>` document. Inline styles where needed, Tailwind via the Play CDN, Google Fonts via `<link>` in `<head>`. No prose outside the HTML, no markdown fences.
