Tastemaker
Generate genuinely beautiful, on-brand UI instead of generic "AI slop" — use whenever the user asks to build, design, style, or improve a UI, landing page, dashboard, app screen, or component, whenever a PRD/spec needs a design pass before implementation, whenever the user pastes reference images/Pinterest/Dribbble links and wants the app to look like them, or whenever the user complains the AI-generated UI looks generic, boring, cookie-cutter, or "like every other AI app." Make sure to trigger this even if the user doesn't say "design" explicitly — phrases like "make this look good", "build the frontend for X", "this looks like every other SaaS site", or "match this vibe" all qualify. Also triggers on two verbs, "study"/"extract the look of" a reference screenshot or URL, and "audit"/"review"/"why does this look AI-generated" for critiquing existing UI.From its SKILL.md
npx -y skills add codeswithroh/tastemaker --skill tastemakerAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
SKILL.md
52.7 KB, ~12.0k tokens by cl100k_base, as published. Nobody here has run it
Tastemaker
The problem this solves
Ask an LLM to build a UI and it defaults to the same handful of patterns: indigo-to-purple gradients, the same rounded card with a soft shadow, the same generic hero layout. This isn't a prompting failure — it's what happens when a model has to invent visual taste from scratch, from a text description, with no grounding and no memory of what the person asking actually likes.
Most "design skill" approaches try to fix this by handing the model a bigger catalog of canned styles and palettes to pick from. That helps a little, but it's still generic — a library of canned options, not your taste, and it forgets everything the moment the session ends.
Tastemaker works differently, on three ideas:
- Ground in real pixels, not descriptions. If the user gives you references (images, screenshots, URLs), extract tokens from the actual reference — deterministically, with a script — instead of writing a text summary of the vibe and generating from that summary. Text-mediated style transfer is lossy; that's most of why AI UI looks generic even when the prompt describes something specific.
- Remember, don't re-derive. Once a project's style is established, lock it and reuse it for every subsequent screen or component in that project. Across projects, keep a lightweight personal profile of what this specific developer keeps vs. rejects, so returning users start warm instead of from zero.
- Scope to what's actually being built. If a PRD or spec exists, use it to figure out exactly which screens/components need design work, and target effort there — not a generic "here's a design system" dump disconnected from the real product.
- Treat craft as many small choices that compound. Taste is not one hero gradient or one dramatic animation. It is the right component library, the right hierarchy, the right empty state, the right easing, and the decision to delete motion where daily use would make it annoying.
Read this file top to bottom before starting. It's short by design; the reference files below hold the deep material and are only worth opening when the step calls for them.
Modes
Tastemaker has one default behavior and three explicit verbs. Detect which one the request is before starting — most requests are the default.
| Mode | When | What it does |
|---|---|---|
| build (default) | The user asks to design, build, style, or improve UI. | The Workflow below (Steps 0-5). |
| study | The user pastes a screenshot or URL of a design they admire and wants to learn from it — "study this," "what makes this work," "match this vibe." | Extract the reusable DNA (macrostructure, archetypes, type pairing, color anchor) — never the pixels — into a diagnosis, then optionally build the user's own content with it or lock it into the style lock. Load references/verbs/study.md first. |
| audit | The user points at existing UI and wants a critique, not a change — "audit this," "why does this look AI-generated," "review this page." | Score it against the numbered gate list in references/anti-slop-checklist.md, return a ranked punch list by severity. Does not edit. Load references/verbs/audit.md first. |
| comps | The user wants reference comps only, not a built page — "give me some comps," "mock up a few hero options," a brand-kit board before committing to code. | Reuse the palette generator, macrostructure/component-catalog picks, and logo-sourcing rules to build a structured brief per comp, handed off to the user's own image generator (ChatGPT Images, Codex image mode, etc.) — tastemaker doesn't call an image API itself. Writes .tastemaker/comps-brief.md so a later build pass can pick up the same palette/structure decisions instead of re-deriving them. Load references/verbs/comps.md first. |
If a request doesn't clearly map to study, audit, or comps, it's the default build. If the user pastes a reference image or URL with no verb, ask once whether to study it (extract reusable DNA) or treat it as a reference to ground a fresh build (Step 2's extract-palette path). A follow-up like "now fix it" after an audit, "build it" after a study, or "now build this for real" after comps, hands off into the build Workflow.
Aesthetic modes (optional add-ons, not part of the default skill)
Separate from the four workflow Modes above: an aesthetic mode is a named, opt-in style override — brutalist, minimalist, or similar — layered on top of the same shared engine (the palette generator, the structure/diversification system, the anti-slop gates), swapping specific dials (shape language, type character, motion feel, palette constraints) rather than replacing the workflow. None ship with the base skill; they're separate add-ons that, if present, live at references/modes/<name>.md.
Check for one before Step 2's default mood path. If references/modes/ exists and contains a file matching what the user's asking for ("build this in brutalist mode," or similar), or the project's .tastemaker/style-lock.md already records an active mode from a previous build in this project, read that file first and apply it as the override layer Step 2 (palette) and Step 4 (build defaults) work within — instead of picking one of the five default moods below. A mode file states explicitly which of the base skill's rules it overrides and which stay in force (structure, asset sourcing, and the anti-slop gates it doesn't name are always unchanged) — follow that file's own scope, don't guess at what else might apply. Most projects have no references/modes/ folder at all; when there isn't one, proceed straight to Step 2's default moods as normal.
Workflow
Step 0 — Load memory, don't start cold
Read references/taste-memory.md before writing or promoting any preference. Then check for .tastemaker/style-lock.md in the project root first.
- Exists → this project already has an established style. Read it and reuse those exact tokens/assets for the new work. Do not re-derive a palette or type pairing from scratch — that's exactly the drift this file exists to prevent. Only revisit it if the user explicitly asks to change direction. If it records an active Aesthetic mode (see
references/style-lock-format.md), read the matchingreferences/modes/<name>.mdand keep applying it — don't silently fall back to a default mood partway through a project. Also read.tastemaker/log.jsonif present (the structural build log, seereferences/diversification.md) — it records the macrostructure and archetype picks of previous builds so this build can rotate to a different shape instead of repeating one. If.tastemaker/decisions.logexists, scan the latest resolved entries for explicit keep/reject decisions before changing a locked choice. - Doesn't exist → this is a fresh project. Also check
~/.tastemaker/profile.md(outside the repo, in the user's home directory) for a personal taste profile accumulated across their other projects. If it exists, treat it as a strong prior: state the 1-3 profile priors you are applying, then still ground this project in its own brief and assets. If neither file exists, this is a genuinely cold start; go to Step 1.
Memory precedence is strict: the current user request wins, then .tastemaker/style-lock.md, then resolved project decisions in .tastemaker/decisions.log, then ~/.tastemaker/profile.md. Pending-review decisions guide review, but they never count as approval.
Step 1 — Figure out what you're actually building
Before touching color or type, scope the work:
- If a PRD, spec, issue, or design brief exists in the project, read it and extract the concrete list of screens/components that need UI (e.g. "onboarding: 3 steps," "empty state for no results," "pricing table," "settings page"). Design effort should map onto this list — don't generate a generic design system disconnected from what's actually being shipped.
- If no spec exists, ask the user directly (briefly) what screens are in scope, rather than guessing. A design system for the wrong surface area is wasted work.
- Classify every screen before designing it: marketing narrative, app shell, transactional form, data view, editor/canvas, settings, empty/loading/success state. This classification controls density, component choice, and motion. A marketing page can teach through scroll; a dashboard earns trust by getting out of the way.
Step 1.25 — Build the reference field
Read references/reference-intelligence.md before a cold start, a major redesign, or any request where the user wants a modern, polished, professional, or visually stunning result without supplying references.
- State the one-line design read: surface type, audience, visitor mode, visual lane, and dials for variance, motion, density, and art direction.
- Create or update
.tastemaker/reference-board.mdwith direct competitors, adjacent products, cultural sources, interface systems, and anti-references. If live browsing or screenshots are available, use real sources and record the URLs. If they are not available, mark the board as inferred rather than viewed. - Decide whether the work should use an official design system, the repo's existing component stack, or a custom aesthetic lane. Check dependencies before importing anything.
- Write the direction contract into the style lock or build stamp: thesis, first viewport, system, and risk.
Step 1.5 — Pick the right building blocks, and source them instead of fabricating them
Read both files here; they cover different halves of the same decision:
references/library-selection.md— behavioral primitives: dialogs, popovers, menus, selects, toasts, command palettes, drag and drop, virtualization, number animation, gesture motion. Things that are hard to get right.references/component-sourcing.md— visual components and blocks: heroes, pricing tables, bento grids, dashboards, charts, marketing sections, and the shadcn-compatible registries (Watermelon, KokonutUI, bklit) plus MCP component servers they come from. Things that are hard to make look finished.
The governing principle: Tastemaker directs, it does not fabricate from scratch what a production-grade registry already ships. Hand-rolled charts, bento grids, and pricing tables are a reliable "AI-built" tell. Pull the part, then spend the design effort restyling it to the locked tokens and enforcing one visual system across everything pulled — that coherence pass is the actual design work, and skipping it produces something worse than hand-rolling.
- Detect the stack before reaching for any registry (
component-sourcing.mdStep 0). Most registries are React + Tailwind + shadcn; emittingnpx shadcn add …at a static-HTML or SwiftUI project is a real failure. On a stack that can't consume them, port the pattern by hand and say that's what happened. - Check what the repo already uses before adding a dependency. Extend the existing stack when it is healthy.
- Hand-roll only when the stack can't consume a registry, the interaction is genuinely simple and static, or the project forbids dependencies.
Step 2 — Establish the style, grounded in something real
This only runs on a cold start (Step 0 found neither file), or when the user explicitly asks to change the project's direction.
- Check the target script first. If the PRD, the user's request, or the actual UI copy is in a non-Latin script (Korean, Japanese, Chinese, and others), the two-family Latin pairing model below does not apply — read
references/style-tokens.md's Non-Latin script typography section before picking type. It's a different model (one family across a weight scale, not two families), not a font substitution inside the same one. - If the user has references (pasted images, a Pinterest board export, screenshots, URLs to sites they like): run
scripts/extract_palette.pyagainst the image(s) to get deterministic dominant colors, contrast ratios, and lightness stats — real numbers pulled from real pixels, not a guess. Combine that with your own visual read of the reference (layout density, corner radii, shadow depth, whether it leans playful/serious/technical) to write a concrete style brief. Anchor every token to something visible in the reference — if you can't point to why a color or pattern is in the brief, don't include it. Once you've assigned extracted colors to Primary/Accent roles, runscripts/check_contrast.py --palette ...on the assignment — a color that looked fine as a dominant swatch in the source image can still fail as a button-label background once it's assigned that role (seereferences/style-tokens.md's Contrast floor section for a concrete example of this exact failure). - If the user has no references, generate a fresh palette from the app idea rather than picking from a fixed set. Classify the idea's mood from the keyword table in
references/style-tokens.md, then runscripts/generate_palette.py --mood <mood>(add--mode light|darkif the product implies one). This produces a new, legible-by-construction palette every run, so two similar projects don't come out identical, which is the whole point: no monoculture. Pair it with the mood's font set from that file's type-pairing catalog. Only ask a direct question when the idea genuinely spans two moods with no lean, per that file's "When to actually ask" section. State in one line which mood was inferred and why. Usereferences/component-patterns.mdfor layout pattern choice the same way (adapt, don't apply unchanged). (The five hex palettes instyle-tokens.mdare reference anchors showing each mood's intended character, not palettes to ship.) - If the product needs an actual light/dark toggle (common for internal tools, less common for a marketing site) rather than one locked mode, that's a decision to make explicitly here, not default into. See
references/style-tokens.md's Runtime dark/light toggle section: generate the companion pair from the same--seedin both--mode lightand--mode dark, verify both withcheck_contrast.py --matrix, and record the toggle decision in the lock's Dark mode line perreferences/style-lock-format.md. - Either way (generated, or extracted from references), the palette comes with its contrast matrix:
generate_palette.pyprints it, and for a reference-extracted palette you runscripts/check_contrast.py --matrix text=.. bg=.. surface=.. primary=.. accent=.. border=.. on-primary=..yourself. Write the result — palette, type, and the legal-pairings summary from the matrix — to.tastemaker/style-lock.md's Color contract section (seereferences/style-lock-format.mdfor the exact structure). This is what makes the lock a contract over which colors may touch, not just a set of hexes that happened to pass once; every later step in this project reuses it instead of re-deriving.
Step 2.5 — Pick the structure, and diversify against project memory
Color is now locked and varies per project. But two sites with different palettes still read as the same template if they share the same page shape — the generic hero → 3-feature-cards → testimonial → CTA → footer rhythm is the strongest "an AI built this" tell at the page level, and it survives a perfect palette. This step makes structure vary the way color already does. Skip it for App shell screens (dashboards/internal tools) — their shape is governed by references/component-patterns.md's App shell section; this step is for public/marketing pages, where structural sameness bites hardest.
- Check project memory first. Read
.tastemaker/log.json(created by previous builds; seereferences/diversification.md) alongside the style lock. It records the last builds' macrostructure and archetype picks. - Work out the narrative arc before picking a shape. Per
references/narrative-arc.md: what's the actual promise (hook), what's genuinely broken or at stake (problem), how does the product fix it (solution), what's the concrete flow (how it works), what's the real evidence (proof), and what's the ask (close)? This is the brief's actual argument — decide it before reaching for a macrostructure, since the shape exists to carry the argument, not the other way around. Minimum four distinct beats; five is the default. If a beat is deliberately merged or skipped, say so and why. - Pick a macrostructure by name from
references/macrostructures.md— the whole-page skeleton — matched to the arc just worked out, and different from the last build's per the rotation rule. - Pick the component archetypes that fill it from
references/component-catalog.md— nav, hero, feature, proof, CTA, footer, section-head — each with its variation knobs, and each assigned to a beat from the arc. Nav, footer, and hero must each differ from the last build's; if you reuse an archetype, change a knob. - State the rotation and the arc out loud in one line before building (the accountability step — picking on the page is what breaks the default-attractor): which macrostructure and archetypes you picked, how they differ from the last build, and the beat sequence. See
references/diversification.mdandreferences/narrative-arc.mdfor the exact formats. - This is what the CSS stamp and the
.tastemaker/log.jsonentry (both written at Step 4) record. Within a single project, keep pages coherent (shared nav/footer/type frame); across projects, structure varies.
The picks feed Step 3 (which assets each archetype needs) and Step 4 (the build itself).
Step 3 — Real assets, all of them, in the same pass — and attribution-free by design
A site with no real photography, no illustrations, and no motion reads as static and generic no matter how good the color/type tokens are — this step is what makes a generated site feel dynamic and alive. The goal is a complete site in a single pass: every section that needs a photo has a real photo, every concept has an illustration, every icon is in place, and it all animates — the first time, with no follow-up round of "now add the images." Every source below is chosen to make that possible: API-first (fetchable automatically, no human browsing step) and attribution-free (nothing the end user ever has to see). This is deliberate — an attribution credit sitting on a finished marketing site is a visual hindrance no real product ships, so this skill sources only from places that don't require one.
For landing pages, visual product sites, launch pages, and motion-heavy pages, read references/asset-curation.md before sourcing files. Build an asset cast first: hero anchor, mode range, process artifacts, proof, texture object, and micro assets. Record the cast in .tastemaker/style-lock.md or .tastemaker/reference-board.md. If one screenshot family appears more than twice, add another asset role or remove the repetition. This catches the common failure where a page technically has images but still feels repetitive and under-curated.
For every asset the scoped screens need:
-
Decide illustration vs. real photography per section. Sections showing something factual or physical (office, product-in-use, people, places) call for real photography; sections conveying an abstract concept (mission, values, an idea, a feature benefit) call for illustration. Both get filled in this same pass — neither is optional.
-
Real photography → Openverse, via
scripts/fetch_photos.py, automatically and with no API key. Runscripts/fetch_photos.py "<search terms>" --out design/assets/photosfor every photo-appropriate section — no key, no signup, nothing to set up. It searches Openverse (800M+ openly-licensed images), filtered by default to CC0 + Public-Domain-Mark, which legally require zero attribution — nothing on the site, ever. This is what makes photos and icons zero-setup: keyless in every session, no accounts, no keys. (Illustrations are the one asset type that may need a single one-time setup step — populating~/.ideagram/undraw/— see the Illustrations bullet below; once that's done once, it's reused across every future project.) (Optional upgrade:--source pixabayuses Pixabay for more stock-polished, full-res imagery if a section needs it — also attribution-free, but needs a freePIXABAY_API_KEY. Use it only when Openverse's more eclectic pool doesn't have a clean match. Unsplash is deliberately not used at all, because its API forces visible on-site attribution.) -
Credit the sources in the code, never on the page.
fetch_photos.pywrites aCREDITScomment block (creator + source + license per photo) into the photos folder. This is a voluntary courtesy — CC0/PDM require nothing — so paste it into a code comment at the top of your HTML/CSS as a genuine thank-you to the people whose work you're using. It's visible to any developer reading the source and invisible to the end user. That's the honest middle ground: generous credit, zero visual hindrance. Never promote it to visible on-page text. -
Logo → preserve first; construct only on a real cold start. See
references/logo-sourcing.md. Before designing a mark, search the repository and brief for an existing logo, favicon, brand kit, or distribution asset. If one exists, treat it as locked: reuse it byte-for-byte unless the user explicitly requests a rebrand. Do not recolor, reinterpret, or replace an established mark merely because it clashes with the new page direction. Only when no identity exists should you construct a simple geometric mark from primitive shapes in the locked palette (followingideagram/references/style-contract.md) — not a letter dropped in a colored box. Once the mark is established, runscripts/export_favicons.py <mark>.svg --out design/assets/favicons/and wire the favicon set into<head>. -
Illustrations → the vendored
ideagram/skill, always available, no cross-skill dependency.ideagram/is bundled directly inside this skill (seeideagram/SKILL.md) specifically so illustration sourcing never depends on whether a separateideagramskill happens to be installed in the current session — it's part of tastemaker now, not an optional sibling. Two things trigger this step, and both use the exact same workflow:- Implicit — Step 3's own illustration-vs-photography split above identifies a concept-driven section (mission, values, an abstract feature benefit) that calls for illustration rather than a photo.
- Explicit — the user's own request uses the word "illustration" (or "illustrate") anywhere, for anything — a whole site, one section, or an ad-hoc "add an illustration of X here." Treat that word as a direct instruction to run this workflow for that concept immediately, not just a hint to weigh against other options.
The workflow itself (full detail in
ideagram/SKILL.md, read it before the first use in a project): distill the concept to one sentence, match it against a local unDraw library at~/.ideagram/undraw/(real illustrator-grade SVGs, not hand-drawn-by-LLM path data), recolor the match to this project's locked accent withideagram/scripts/recolor_undraw.py, validate withideagram/scripts/validate_assets.py. If~/.ideagram/undraw/doesn't exist yet or has no index, say so plainly and either ask the user to grab 20-30 free illustrations from undraw.co (30 seconds, no attribution needed) or fall back toideagram/assets/primitivescomposition — and be upfront that the fallback is a real quality drop from real unDraw art, perideagram's own honesty rule. Don't silently accept the downgrade as if it were the intended result.- The illustration isn't done until it's in the page. Save the finished SVG to
design/assets/illustrations/, then actually reference that file path in the section's markup (<img src="design/assets/illustrations/<name>.svg" alt="...">or inlined<svg>) as part of the same pass — generating an on-brand illustration and leaving it unused on disk isn't a completed step. - Record what happened in
.tastemaker/style-lock.md's Assets section (illustration vs. photography split line) — including whether the library was populated or the primitive fallback was used, so a later session in this project doesn't have to rediscover that state.
-
Icons → Iconify, via
scripts/fetch_icons.py, automatically, matched to the mood. Iconify's public API needs no key, returns SVGs already tinted to the accent color, and draws from permissively-licensed open sets (Lucide, Tabler, Phosphor, Heroicons, Material Symbols, Iconoir, Solar, Carbon, MingCute, Fluent) that require no attribution. Pick one set per project and stay in it so every icon shares one stroke weight — but which set is the point that used to get skipped: pass--mood <the project's locked mood>so the set varies with the project, and each mood maps to two candidate sets rather than one, picked between per run the same waygenerate_palette.py --seedvaries the palette (the mapping lives in the script's docstring) — so two "technical" projects aren't guaranteed the same Lucide icons any more than two "premium" ones are guaranteed the same palette.scripts/fetch_icons.py --search "<terms>" --mood <mood>to discover names, then--icons name1 name2 --mood <mood> --color "#<accent>" --out design/assets/iconsto fetch. Name--setdirectly only when neither of the mood's candidates fits this project. Don't fall back to emoji-as-icons or hand-drawn one-offs when a two-line fetch gets a consistent, real icon set. -
When even a populated unDraw library has no real fit for a concept (per
ideagram/SKILL.mdStep 2's "don't force a bad match" rule), or an image-gen tool happens to be available for a bespoke style:references/illustration-sources.mdcovers the remaining options, including Streamline as a manual, attribution-aware exception. This is now the rarer path —ideagram's unDraw-match workflow (once the library is populated) is the norm, not Openverse/Iconify-style full automation, because the win here is real illustrator-grade art, not a zero-touch fetch. -
Last-resort fallback, never the plan: if a specific asset genuinely can't be sourced (no network, no populated unDraw library and the user can't add one), build it code-native — SVG shapes / CSS gradients from the locked palette — rather than leaving a gap or a grey box. Say plainly when this happened; don't imply a real photo/illustration exists where a placeholder does. Note this should be rare for photos and icons specifically, since both fetch without any key — the illustration path is the one place a one-time manual step (populating
~/.ideagram/undraw/) may genuinely still be needed. -
Save everything into
design/assets/(photos, illustrations, icons in their own subfolders) so it's reusable across screens, and runscripts/validate_assets.pyover any SVGs before use — a malformed SVG (classically a--inside a<!-- -->comment) reads fine as text but renders as a broken image in strict browsers, invisible unless actually parsed. -
Motion → GSAP + ScrollTrigger, in the same pass, not a later polish step. Wire up
assets/gsap-starter.jsfor scroll-driven reveals and staggered entrances, and for anything with a narrative/storytelling shape (a landing page that unfolds section by section) build a GSAP scroll timeline perreferences/animation-guidelines.md— pinned sections, scrubbed reveals, sequenced hero moments. This is what turns a stack of static sections into a site that tells a story as you scroll, and it's default, not optional. The dependency-freereveal.css/reveal.jspair remains only as a fallback for contexts that can't take a GSAP dependency. -
Artifact component kit → use when the page needs curated visual scenes. For static marketing pages or projects without a mature design system, copy or adapt
assets/artifact-kit.cssandassets/artifact-kit.jsinstead of rebuilding the same artifact-board/mode-runway/proof-ledger patterns from scratch. For React/Next apps, use Radix/shadcn/React Aria for interactive primitives and use the artifact kit only as visual composition guidance. A component library helps with structure and accessibility; it does not replace asset curation.
Step 4 — Build the actual screens
Now generate the PRD-scoped screens/components, constrained to .tastemaker/style-lock.md and the asset files from Step 3. Point explicitly at file paths and token values rather than re-describing the vibe in prose each time — concrete constraints produce consistent output; restated vibes drift.
For high-risk UI, prototype before committing. If the user asks for a hero, pricing card, onboarding step, dashboard card, command palette, toast, empty state, or a motion-heavy component and the direction is not obvious, build 2-3 variants in an isolated picker using references/prototype-variants.md. Variants must differ by layout, density, motion, or interaction model. Color swaps are not variants. Promote only the winner.
Eight defaults are non-negotiable at build time — they are the difference between "a styled document" and "a designed product," and generated sites reliably skip them unless forced:
- Show, don't tell — visual representation over text, always. This is the single biggest thing that separates a real product site from an AI-generated one, and the easiest to get wrong because writing another paragraph is the path of least resistance. The default failure mode is a wall of feature cards, each with a heading and two sentences of prose explaining a benefit. Real product design shows the benefit instead: a product-UI mockup, a before/after comparison, an actual chart, a diagram, a numbered visual flow, a stat with one label, an annotated screenshot. Before writing a paragraph to explain something, ask whether a visual could carry it with a caption instead — and default to the visual. Concretely: a "fast analytics" claim becomes a real chart, not a sentence about speed; a "3-step onboarding" becomes three visual panels, not a bulleted list; a feature becomes a small UI mockup of that feature, not a description of it. Text earns its place only where a visual genuinely can't carry the meaning (a headline, a short subhead, a caption, a CTA label). Every section should be mostly something to look at, with text as the caption — not mostly text with a decorative icon. See
references/component-patterns.mdfor the show-don't-tell pattern per section type. This is a hard default, verified in the anti-slop checklist, not a stylistic preference. - The hero has one job and one visual focus. For every default app or site with a public-facing hero, read and apply
references/hero-guidelines.mdbefore building above the fold. Start from one sharp promise, one short explanation, one primary action (plus at most one secondary action), and one product-relevant visual. Do not turn the hero into a miniature dashboard of the site's features: workflow rails, metric sidebars, floating badges, proof stamps, orbit decorations, file/status footers, and extra microcopy compete with the value proposition. Push explanation and process below the fold. Complexity is allowed inside the product; it is not automatically allowed in the product's first impression. - Motion is wired in this pass, not deferred — the track depends on the screen, not the project. Every screen ships with GSAP by default; this is a build-step requirement, not just an asset-sourcing note from Step 3. A finished page with zero motion is a skipped step, not a minimalist choice. But which motion depends on what the screen actually is, per
references/animation-guidelines.md:- Marketing/landing screens (a page the user scrolls through once): wire
assets/gsap-starter.jsfor baseline reveals and build a real sequenced hero timeline plus at least one scroll-storytelling beat (scrubbed reveal, pinned section, parallax). - App shell screens (dashboards, settings, anything behind a persistent sidebar/topbar the user works inside rather than scrolls through): a scroll-driven hero timeline has nothing to attach to and is the wrong tool. Use the App shell motion track instead: panel/tab-switch transitions, staggered list/table entrances on data-load, animated state changes, and skeleton loading states.
- A single project can have both kinds of screen (a public landing page in front of an authenticated app); give each screen the track that fits it, don't apply one default everywhere. Don't hand back a static screen and call motion a follow-up, and don't force a hero-timeline treatment onto a dashboard just because that's the default for the other track.
- Marketing/landing screens (a page the user scrolls through once): wire
- No section is asset-empty. Per Step 3, every section that calls for a photo/illustration/icon/mockup actually has one — no flat color blocks or bare text where a visual belongs. In a clean hero, this means one meaningful visual rather than several decorative ones.
- Every color pairing the build introduces is a legal one. Step 2 locked a contract, not just five hexes — the Color contract section of
.tastemaker/style-lock.mdsays which pairings are text-safe (>=4.5:1) and which are UI-safe (>=3.0:1). When a screen needs a pairing not already in that list (a badge fill with a label, a disabled state, a hover, a state-carrying border), that is a flag, not a free choice: pick a pairing that's already legal for the purpose, or runscripts/check_contrast.py --matrixagain with the new token added and update the lock before shipping it. Don't invent a pairing and assume it's fine because the palette passed once — early hand-picked drafts for two moods inreferences/style-tokens.mdfailed exactly this way, caught only by actually running the numbers, which is why color generates against this contract now (scripts/generate_palette.py) instead of being hand-tuned and hoped.- When a pairing fails, work through this order — don't loop on the same failing value hoping a re-run changes the math:
- Reuse a pairing already legal for the purpose. The fastest fix is almost always already in the lock's text-safe/UI-safe lists — check there before inventing a new value.
- Nudge the new color's lightness within its own hue family, not the hue itself (shifting lightness preserves the palette's character; shifting hue doesn't), re-run
check_contrast.py --matrixon the result, and only accept it once the matrix confirms the pairing actually clears its floor — this is the same lightness-solving approachscripts/generate_palette.pyalready uses at generation time, applied ad hoc to a single new token. - If nudging would visibly break the color's intended role (a brand-fixed accent that can't move, a hue chosen deliberately for the mood), fall back to a known-safe neutral already in the palette (
textoron-primary) for that specific pairing instead of forcing the original color to work. - If none of those apply (a hard external constraint, like a client's fixed brand hex that genuinely can't satisfy the floor for its intended use), stop and surface the conflict to the user rather than shipping the failing pairing or silently substituting something they didn't ask for.
- When a pairing fails, work through this order — don't loop on the same failing value hoping a re-run changes the math:
- Spacing follows the scale, not habit — and on a landing page, section rhythm is generous by default, not cramped. Per
references/style-tokens.md's Spacing scale section: pick the project's tokens once, record them in.tastemaker/style-lock.md's Density & spacing section, and reuse them — don't let each card or section improvise its own padding. The rule that actually governs card spacing: internal spacing (a card's own padding) should be equal to or less than external spacing (the gap between that card and its neighbors) — violating this is what makes a layout read as cramped in one place and empty in another at the same time. Content cards (pricing tiers, feature cards, testimonials) have a real floor:space-6(24px) minimum internal padding, not whatever a compact stat tile uses. Section-level padding is its own, separate failure mode: capping every landing-page section at a tight, uniform value is exactly what makes a page read as "everything cramped, nothing gets its own moment" even when card-level spacing is fine. Weight section padding by role — a pivotal section (hero, primary proof) earnsspace-32–space-48(128–192px), not the same value as a connective one — per that file's Section-level padding section. Don't reach for the same padding value everywhere regardless of what the element is, at either scale. - Every motion choice passes the motion gate. Before shipping motion, answer: how often will the user see this, what purpose does it serve, can it stay within the timing budget, and does it help the task? Delete motion that fails. Run
python3 scripts/audit_motion.py <paths>and fix hard failures:transition: all,ease-inon UI,scale(0), layout-property animation, hover motion without pointer gating, movement without reduced-motion handling, and UI motion over 300ms without a stated reason. - Core app states are designed, not implied. For app screens, build the populated, loading, empty, error, disabled, focus, hover, pressed, and success states. An app screen that only looks good with perfect sample data is unfinished.
- Interface craft rules apply to everything shipped, including pulled components. Read
references/interface-quality-rules.md— keyboard access, visible focus states, labelled inputs,alttext, explicit image dimensions, URL-reflected state, no blocked paste,Intl.*for dates/numbers, real overflow handling. These are what separate "looks designed" from "is built well," and a component pulled from a registry does not get a pass on them: restyling it to the locked tokens is the same pass where you verify it clears these. Its Flag-these-on-sight list overlaps withscripts/anti_slop_scan.pyandscripts/audit_motion.py— all three should come back clean.
Stamp the build and record it in project memory. The first non-empty line of the built CSS (or the top of an inline <style>) is a comment recording the structural picks, mood, palette seed, and contrast result — the format is in references/diversification.md. In the same pass, append an entry to .tastemaker/log.json (create it if absent) with the macrostructure and archetype picks. This is the durable record the next build reads to rotate against — skipping it is how the skill drifts back into building the same shape every time.
references/anti-slop-checklist.md carries two checks that bracket the build. Before you finalize, run its pre-emit self-critique: score the planned output 1-5 on six axes (show-don't-tell, philosophy, hierarchy, specificity, restraint, variety) and revise anything scoring below 3 — catching weakness there is cheaper than catching it in the gate sweep. After you build, run its numbered gate list (mood-scoped: some gates loosen or tighten per the project's mood) — it catches the specific tells (generic gradient defaults, emoji-as-icons, contrast checked on only one pairing, text-walls where visuals belong, static/no-motion pages, transition: all and other motion tells, mid-render token improvisation, the generic hero→3-cards→CTA→footer template, invented metrics) that make output read as AI-generated regardless of how good the underlying tokens were. Record the six critique scores in the build stamp. Then run the mechanical scans:
python3 scripts/anti_slop_scan.py <changed-ui-paths>
python3 scripts/audit_motion.py <changed-ui-paths>
Fix HIGH findings before handoff. MEDIUM findings need either a fix or a short reason they are earned by the brief. Then run the motion review in references/animation-guidelines.md; the final check is not "does it animate?" It is "does the interface feel faster, clearer, and more trustworthy because of the motion?"
Step 5 — Close the loop: store taste, then reuse it
Taste lives in what gets kept vs. rejected. Read references/taste-memory.md before writing memory.
Every design pass ends with decision capture:
- Interactive session (the normal case): ask one quick, specific keep/reject question rather than an open-ended "thoughts?" Example: "keep this hero density, or try a quieter variant?" Log the real answer to
.tastemaker/decisions.log. - Autonomous/single-pass run (no one available to answer, e.g. a background task): do not fabricate approval. Append a
pending-reviewentry with the choice, surface, axis, and reason, so a later session can turn it into a real kept/rejected verdict. - Follow-up session: read pending entries first, ask the user to resolve the relevant one if it affects the new work, then append a fresh kept/rejected entry. Do not edit old log lines to make the history cleaner.
Use three memory layers:
.tastemaker/style-lock.mdstores the current project's rules..tastemaker/decisions.logstores append-only keep/reject/pending evidence.~/.tastemaker/profile.mdstores durable cross-project preferences.
Promote a decision into ~/.tastemaker/profile.md only when it is resolved and reusable outside this project: the user explicitly asks to carry it forward, the same preference repeats across resolved entries, or it describes a durable axis like density, motion feel, typography, assets, hierarchy, or shape language. Do not promote pending entries, client constraints, one-off brand requirements, time-pressure fallbacks, or hesitant approvals.
At handoff, say exactly what changed: decision log updated or not, style lock updated or not, profile promoted or not. This answers the carry-over question directly: project decisions persist in the repo, and personal preferences persist locally in the user's home directory.
Reference files
| File | Read when |
|---|---|
references/style-lock-format.md | Writing or updating .tastemaker/style-lock.md |
references/taste-memory.md | Step 0 / Step 5 — reading, logging, resolving, or promoting user design preferences across sessions and projects |
references/reference-intelligence.md | Step 1.25 — building a reference field, design read, dials, quality bar, and direction contract when the user gives no references or asks for a major visual upgrade |
references/modes/<name>.md | Before Step 2, only if this folder exists and the user's request (or the project's style lock) names an aesthetic mode — see "Aesthetic modes" above. Not present in the base skill; an optional add-on. |
references/style-tokens.md | Cold start with no references — auto-selects a matched palette + Google-Font pairing from the app idea's mood, plus spacing/radius/shadow scales |
references/narrative-arc.md | Step 2.5, read before the macrostructure pick — the six-beat story arc (hook/problem/solution/how-it-works/proof/close) grounded in StoryBrand and PAS, so a page's sections build an argument, not just a varied shape |
references/macrostructures.md | Step 2.5 — picking the whole-page shape for a public/marketing page (Feature Stack, Editorial Index, Long-Scroll Narrative, Bento Showcase, …) so structure varies per project, not just color |
references/component-catalog.md | Step 2.5 — the named component archetypes (nav/hero/feature/proof/CTA/footer/section-head) with stable IDs + variation knobs that fill a macrostructure |
references/diversification.md | Step 2.5 / Step 4 — the project-memory engine (.tastemaker/log.json), the rotation rule that forces each build to differ structurally from the last, and the build-stamp format |
references/verbs/study.md | The study verb — extracting reusable design DNA from a screenshot or URL (never pixels), then building from it or locking it |
references/verbs/audit.md | The audit verb — scoring existing UI against the numbered gate list and returning a ranked punch list, without editing |
references/verbs/comps.md | The comps verb — building an image-generator brief from the palette generator + structure engine + logo-sourcing rules, for reference comps before any code exists |
references/component-patterns.md | Choosing a layout pattern for a given screen type (landing, app shell/dashboard, pricing, onboarding, empty states) |
references/hero-guidelines.md | Building or revising any landing-page hero — attention budget, copy limits, single-focus visual, responsive and motion checks |
references/logo-sourcing.md | Step 3 — preserving an existing identity, or designing a real cold-start mark (no letter-in-a-box) + wordmark and favicons |
references/anti-slop-checklist.md | The pre-emit self-critique (before finalizing, Step 4) + the numbered, mood-scoped gate sweep (after building) — the two quality checks that bracket every build |
references/tech-stack-guides.md | Implementing tokens/components in a specific stack (React/Next/Tailwind, Vue, SwiftUI, Flutter) |
references/animation-guidelines.md | Adding motion (Step 3/4) — GSAP + ScrollTrigger is the default engine, including scroll-storytelling timelines; read this first |
references/library-selection.md | Step 1.5 — behavioral primitives: before adding or hand-rolling dialogs, toasts, command menus, drag, virtualization, or animation libraries |
references/component-sourcing.md | Step 1.5 — visual components and blocks: the shadcn-compatible registries (Watermelon, KokonutUI, bklit), component MCP servers, Motion, the stack-detection gate that decides whether any of them apply, and the coherence rules for restyling what gets pulled |
references/interface-quality-rules.md | Step 4 — interface craft gates (accessibility, focus, forms, images, performance, URL state, locale, copy). Adapted from Vercel's Web Interface Guidelines; applies to pulled components too |
references/prototype-variants.md | When the right design direction is uncertain and a component/screen needs 2-3 real variants in an isolated picker |
references/asset-curation.md | Step 3 — building the asset cast, selecting artifact roles, avoiding repeated screenshot families, and using the artifact kit for visual/motion scenes |
references/illustration-sources.md | The attribution-free asset sourcing map (Step 3) — Openverse for photos, the vendored ideagram/ for illustrations, Iconify for icons, Streamline as a manual exception; plus the "credit in code, never on the page" pattern |
ideagram/SKILL.md | Step 3, Illustrations — the vendored illustration workflow itself (unDraw-library match → recolor → validate). Read before the first illustration in a project. Triggered implicitly by a concept-driven section, or explicitly whenever the user says "illustration"/"illustrate." |
Scripts
| Script | Purpose |
|---|---|
scripts/generate_palette.py | Generate a fresh, contrast-valid palette for a mood (OKLCH + color harmony, per-role lightness solved against the contract). The default cold-start path, so output isn't one of five fixed options. Usage: python3 scripts/generate_palette.py --mood <premium|warm|technical|playful|elegant> [--mode light|dark] [--seed N]. Prints the roles, a preview URL, and the contrast matrix to record in the lock. |
scripts/extract_palette.py | Deterministic color/contrast extraction from reference image(s). Usage: python3 scripts/extract_palette.py <image_path> [image_path ...] |
scripts/check_contrast.py | WCAG contrast check for a palette. --palette text=hex bg=hex primary=hex accent=hex checks the critical few pairings; check_contrast.py <hex1> <hex2> checks a single pair. --matrix text=hex bg=hex surface=hex primary=hex accent=hex border=hex on-primary=hex computes every pairing in the token set and reports which are text-safe (>=4.5:1), UI-safe (>=3.0:1), or decorative — this is what Step 2 records as the Color contract in .tastemaker/style-lock.md, and what Step 4 checks new pairings against (see references/style-lock-format.md). Run this on any palette, the reference anchors, an extracted reference-image palette, or a user-supplied brand color, not just the starter ones. |
scripts/anti_slop_scan.py | Static scan for high-confidence AI UI tells: generic purple/cyan gradients, gradient text, h-screen, dead links, missing alt text, placeholder copy, AI-copy phrases, emoji icons, eyebrow spam, and transition-all. Usage: python3 scripts/anti_slop_scan.py <changed-ui-paths> |
scripts/validate_assets.py | Validate SVG assets are well-formed before shipping them (Step 3/4). Usage: python3 scripts/validate_assets.py <file_or_directory> |
scripts/fetch_photos.py | Fetch real photography from Openverse — no API key, CC0/public-domain (attribution-free), writes a voluntary code-comment CREDITS block. Usage: python3 scripts/fetch_photos.py "<query>" --out design/assets/photos. Optional --source pixabay (needs PIXABAY_API_KEY) for higher-curation imagery. |
scripts/fetch_icons.py | Fetch icons from Iconify — no API key, attribution-free, pre-tinted to the accent, set chosen by mood so projects don't all get the same icons. Usage: python3 scripts/fetch_icons.py --search "<terms>" --mood <mood> then --icons a b c --mood <mood> --color "#hex" --out design/assets/icons (or --set <prefix> to name one directly) |
scripts/recolor_svg.py | Recolor local SVG files (already on disk) to match the locked accent color. Usage: python3 scripts/recolor_svg.py <path> --accent "#hex" --preserve-dark |
scripts/export_favicons.py | Export a logo mark SVG to favicon.ico, apple-touch-icon, PWA manifest icons, and an OG-card PNG (needs cairosvg + system cairo, same as ideagram/scripts/export_png.py). Usage: python3 scripts/export_favicons.py <mark>.svg --out design/assets/favicons/ |
scripts/audit_motion.py | Scan CSS/HTML/JS/TS/TSX for common motion craft failures: transition: all, ease-in, scale(0), long UI durations, layout-property animation, ungated hover motion, and missing reduced-motion handling. Usage: python3 scripts/audit_motion.py site references assets |
ideagram/scripts/build_library_index.py | Index a local unDraw library (~/.ideagram/undraw/) into index.md/index.json for matching. Run once after populating/updating the library. |
ideagram/scripts/recolor_undraw.py | Recolor a matched unDraw illustration's accent to the project's locked accent, preserving skin/ink/clothing/neutrals. The illustration workhorse — see ideagram/SKILL.md Step 3. |
ideagram/scripts/extract_component.py | Lift a whole figure/device/panel out of a source unDraw SVG to compose a custom scene, for the rare case no single library illustration fits (ideagram/SKILL.md Step 4). |
ideagram/scripts/export_png.py | Export a finished illustration to social/presentation PNG sizes (needs cairosvg + system cairo). |
Assets
| File | Use when |
|---|---|
assets/gsap-starter.js | Default motion for every project. Wires the data-reveal/data-reveal-group markup convention to GSAP + ScrollTrigger — reduced-motion-aware via gsap.matchMedia(). Requires GSAP/ScrollTrigger loaded first (see references/tech-stack-guides.md for CDN vs. npm per stack). |
assets/artifact-kit.css + assets/artifact-kit.js | Reusable artifact-board, mode-runway, process-ledger, and asset-motion helpers for premium landing pages and visual product sites. Use after references/asset-curation.md. |
assets/reveal.css + assets/reveal.js | Fallback only, for contexts that can't take a GSAP dependency — same markup convention as gsap-starter.js, so switching between them requires no markup changes. |
assets/anime-starter.js | Scoped alternative, not a default swap — only when a page needs SVG motion-path/shape-morphing (alone or combined with scroll-reveals), per references/animation-guidelines.md's anime.js section. Same data-reveal/data-reveal-group markup convention. |
A note on honesty
Don't claim a step happened if it didn't. If there was no image-generation tool available and you fell back to curated icons + code-native visuals, say so plainly rather than implying custom illustrations were generated. If no references were given and the style came from the starter scaffolding, say that too. The whole point of this skill is to close the gap between "looks AI-generated" and "looks intentional" — silently overclaiming what happened undermines the exact trust it's trying to build.
What ships with it: 43 files
336.5 KB alongside SKILL.md, 17 of them executable
assets/
- anime-starter.jsruns3.7 KB
- artifact-kit.css2.9 KB
- artifact-kit.jsruns1.5 KB
- gsap-starter.jsruns3.0 KB
- reveal.css1.2 KB
- reveal.jsruns1.5 KB
references/
- animation-guidelines.md22.8 KB
- anti-slop-checklist.md25.2 KB
- asset-curation.md4.2 KB
- component-catalog.md14.7 KB
- component-patterns.md19.7 KB
- component-sourcing.md12.9 KB
- diversification.md6.4 KB
- hero-guidelines.md12.3 KB
- illustration-sources.md9.3 KB
- interface-quality-rules.md6.7 KB
- library-selection.md4.3 KB
- logo-sourcing.md5.4 KB
- macrostructures.md8.7 KB
- narrative-arc.md6.4 KB
- prototype-variants.md2.9 KB
- reference-intelligence.md5.2 KB
- style-lock-format.md12.8 KB
- style-tokens.md32.0 KB
- taste-memory.md5.4 KB
- tech-stack-guides.md4.6 KB
- verbs/audit.md4.8 KB
- verbs/comps.md6.9 KB
- verbs/study.md7.1 KB
scripts/
- anti_slop_scan.pyruns7.5 KB
- audit_motion.pyruns6.8 KB
- check_contrast.pyruns6.6 KB
- check_domains.pyruns6.0 KB
- export_favicons.pyruns5.2 KB
- extract_palette.pyruns4.0 KB
- fetch_icons.pyruns7.8 KB
- fetch_photos.pyruns10.6 KB
- generate_palette.pyruns9.4 KB
- _netguard.pyruns3.1 KB
- recolor_svg.pyruns3.8 KB
3 more files not listed here. See all 43 in the repository.
Gives 0 of the 12 instructions most design frontend skills give in ~12.0k tokens
Counted across 1,169 of the 1,878 authors here whose files we hold, read 2026-08-07
- Use CSS variables for color consistencyin 72 of 1169, across 23 files
- Commit to one bold aesthetic direction before codingin 72 of 1169, across 27 files
- Match implementation complexity to the aesthetic visionin 70 of 1169, across 20 files
- Add atmospheric background effects and texturesin 57 of 1169, across 9 files
- Use unexpected spatial compositions and layoutsin 56 of 1169, across 8 files
- Implement real working codein 55 of 1169, across 7 files
- Vary themes and aesthetics across different designsin 48 of 1169, across 7 files
- Launch chromium in headless modein 47 of 1169, across 4 files
- Close the browser when donein 47 of 1169, across 4 files
- Run provided scripts with help flag firstin 47 of 1169, across 4 files
- Wait for network idle statein 47 of 1169, across 4 files
- Use descriptive selectors for elementsin 47 of 1169, across 4 files
Said here and by no other author read
- read this file completely before starting
- detect request mode before starting
- check for existing style lock first
- reuse exact locked tokens for new work
- ask the user what screens are in scope
- classify every screen before designing it
Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.