--- name: figma-designer description: "Design inside Figma the way this designer does: real variables, text styles, auto layout and components, on a fixed visual system (Tailwind type and radius scale, spacing tokens, two-layer color tokens, button and state rules). Use this skill whenever you create, edit or review anything in a Figma file through the Figma MCP: screens, landing pages, components, mockups, design systems, tokens, or any UI. Use it even when the user doesn't name the skill and only says things like 'design a page in Figma', 'make a button component', 'mock this up' or shares a figma.com link. Load it before writing any use_figma code." --- # Figma Designer These are the designer's own rules for working in Figma. They sit on top of the official Figma skills and don't replace them. - **Load `figma-use` before any `use_figma` call.** It holds the Plugin API rules (color ranges, font loading, layout sizing). This skill only adds design rules. - For full screens and pages, also load `figma-generate-design`. For component libraries and tokens, also load `figma-generate-library`. - Exact token names and values are in [Token reference](#token-reference) at the end of this file. Read it before you create or bind any variable or text style. ## Workflow 1. **Check the file first.** List local variable collections and text styles, and search the file's libraries (`search_design_system`) for variables, styles and components. Reuse what exists before creating anything. 2. **Set up foundations if missing.** Create everything below before you build any screen: - the `spacing` and `radius` variables - the two color layers (`Core Colors`, then `Color System` with Light and Dark modes) - the text styles Light and dark are variable modes, never two sets of hardcoded colors. - **Show the tokens on the canvas.** Place a `Foundations` frame on the `Foundations` page, the same way components sit in their own frame. Inside it, add one auto layout section for each collection: - `Core Colors`: one row per palette, one swatch per shade - `Color System`: one row per group, with the Light and Dark modes side by side using explicit variable modes on two columns - `spacing`: a bar per token, sized to its value - `radius`: a square per token, with that radius - Text styles: one sample line per style - **Bind the samples so they update with the tokens.** Every swatch fill is bound to its variable. Every spacing bar and radius sample is bound to its variable. Every text sample uses its style. When the user edits a token, the frame updates by itself. - **Labels:** each sample is labeled with its token name and, for Color System, its alias (`Surface/Primary → Grayscale/870`). Text can't bind to a color value, so hex labels don't update by themselves. Re-sync the labels whenever you create, rename or change tokens. 3. **Build section by section.** Do the hero first, then one section per step. Never build a whole page in one pass. Screenshot after each section and fix it before moving on. 4. **Use auto layout everywhere.** Every frame with children uses auto layout. Padding and gap are bound to spacing variables, never typed numbers. 5. **Bind, don't type.** - Bind every fill, stroke, radius, padding and gap to a variable. - Every text layer uses a text style. - Bind designs to `Color System` tokens only, never to `Core Colors` directly. 6. **Self check before finishing.** Run the checklist at the end of this file, fix every failure, then report what was built and what was skipped. ## Typography - **Fonts:** any font is allowed. Use the one the user or brand names; check it with `figma.listAvailableFontsAsync()` before loading. - **One typeface per file** unless the user asks for a pair. Geist Mono is allowed beside the main font only for code, data or numbers. - **No italic styles. No weights above bold.** - **Sizes come from the Tailwind type scale only:** 12, 14, 16, 18, 20, 24, 30, 36, 48, 60, 72, 96, 128. If a size falls between steps, snap to the step below and take its line height. - **Text styles.** Create the named styles in [Token reference → Text styles](#text-styles) (Heading 1 to 6, Title 1 to 5, Body 1 to 6, Button-lg/md/sm/Xsm, Caption 1 to 5). Use the names exactly, including capitalization. - Group them in folders by type, so the panel reads `Heading / Heading 1`, `Body / Body 4`, `Button / Button-md`. - Every text layer uses one of these styles. Never set a loose size or weight; changing the weight on a styled layer detaches the style. - **Which group to use:** - **Heading:** page and section headings, largest to smallest. Heading 1 is the hero. - **Title:** card titles, list item titles, labels that lead a block, nav items. - **Body:** running text. Within each size, 1 is the strongest weight and the last is regular. - **Button:** text inside buttons only. Main buttons use Button-md; small header buttons use Button-sm. - **Caption:** metadata, helper text, badges, timestamps, fine print. - **New styles.** If a project needs one, add the next number in its group (for example Body 7), built from a scale step. Never invent a new name or an off-scale size. - **Copy:** - No hyphens inside text. - No single word alone on a heading's last line. Break headings by hand at meaningful points (use a line break in the text, not a narrower frame). - The hero heading (Heading 1) has no periods, neither at the end nor between phrases. Split two ideas with a line break: `See the slop` / `Decide what to fix`, not `See the slop. Decide what to fix.` ## Spacing - Use only the `spacing/*` number variables: 0, 2, 4, 8, 12, 16, 24, 32, 40, 48, 64, 80, 96. Nothing in between. - Bind `paddingTop/Right/Bottom/Left` and `itemSpacing` to them with `setBoundVariable`. Give spacing variables the `GAP` scope, which covers both padding and gap. - When a design value falls between tokens, snap it to the nearest token and bind it. - Main buttons use 8 vertical padding (`spacing/75`) and 12 horizontal (`spacing/100`). ## Corner radius - Use Tailwind radius values only (0, 2, 4, 6, 8, 12, 16, 24, full), as `radius/*` variables with the `CORNER_RADIUS` scope. Bind each corner (`topLeftRadius`…). - **Nested radius rule.** When a shape sits inside another and the gap between them is under 32: `inner radius = outer radius − gap` Apply it only when the result is greater than 2. Example: a card at 16 with 8 padding gives inner elements 8. - Check this on every nested frame: read the parent's radius and padding, compute the child's radius, and set it to the matching `radius/*` variable. ## Color ### Token structure: two layers, in every file The names and structure are fixed. The hex values follow the user's brand. Full name list: [Token reference → Color](#color-1). 1. **`Core Colors` collection:** raw palettes. `Primary`, `Secondary`, `Accent`, `Grayscale`, and semantic palettes `Red` (error), `Green` (success) and `Yellow` (warning). - Each palette has at least 10 shades, from `00` (near white) to `1000` (near black). `500` is the base hue. - Build the shades from the brand's base colors. - Transparent colors live here too, as a `Grayscale-Alpha` palette (`700-16` … `700-80`) with the opacity stored in the variable. An alias can't add opacity, so `Overlay/*` aliases these. 2. **`Color System` collection:** semantic tokens with `Light` and `Dark` modes. Every value is an alias to a Core Color, never a raw hex. - Groups: `Border/`, `Surface/`, `Action/`, `Text/`, `Icon/`, `Overlay/`. - Each role has a base token plus the variants it needs: `-Hover`, `-Click` or `-Selected`, `-Disabled`, `-Subtle`, `-Inverted`. - Designs bind only to `Color System` tokens. A rebrand then changes only `Core Colors`. - Name tokens by role, never by raw color (no `Surface/Blue 500`). - Scope variables to what they color: `FRAME_FILL`/`SHAPE_FILL` for surfaces, `TEXT_FILL` for text, `STROKE_COLOR` for borders. Never leave `ALL_SCOPES`. ### Surfaces - **Dark mode backgrounds, only these:** `#000000` · `#181818` · `#1F1F1F` · `#272727` · `#313131` · `#131209` - **No background gradients.** Surfaces are flat. This includes buttons and fade overlays. - **Hero heading** is the one gradient exception, on the text only, left to right: - dark theme `#FFFFFF` → `#9B9B9B` - light theme `#000000` → `#666666` Bind both gradient stops to color variables (`boundVariables.color` on each stop) so the gradient switches with the mode. - **One accent color**, kept below 80% saturation. - **Borders go all the way around or not at all.** Never a single-side border on a card. To divide rows, use a 1px rectangle filled with a `Border/*` token, not a one-side stroke. ## Contrast and accessibility - Body text: at least **4.5:1** against its background. - Large text (24 and up, or 18 bold and up) and UI parts (icons, input borders, focus rings): at least **3:1**. - On gradient headings, check contrast at the lightest end of the gradient, not the darkest. - Never rely on color alone to show state. Pair it with an icon, label or weight change. - Touch targets are at least 44 × 44 on mobile frames. - If a brand color fails contrast with white text, use a darker shade of the same palette for the `Action/*` token rather than changing the text. ## Layout - Hero heading and subheading max width **680**. - No eyebrow tag (the small pill or label above a heading, like `● Design QA, not a redesign`) by default. Add one only when the user asks for it, or when an inspiration image the user uploaded shows one. - Desktop frames are 1440 wide, with a centered content container of 1200 to 1280. - Add 768 and 390 frames when the screen needs responsive versions. - Don't default to three equal cards in a row. Consider a two-column zig zag, an asymmetric grid or a bento layout. - Align shared elements across side-by-side cards: titles, prices, lists and buttons start at the same Y. ## Icons - Use Phosphor, Solar or Iconamoon. Never Material Icons or Material Symbols. - Use one stroke weight across the whole file. - Import icons as SVG (`createNodeFromSvg`) and recolor them with a bound `Icon/*` token. Never redraw an icon from primitives. ## Components and states - Anything used more than once is a component. Expose its text as component properties. - **Interactive components** get variants for **Default, Hover, Pressed, Focus, Disabled**. Inputs also get **Error** and **Filled**. - **Buttons** are one component set with these two properties, every combination present (25 variants per size): - `Type` = Primary | Secondary | Tertiary | Error | Warning - `State` = Default | Hover | Pressed | Focus | Disabled Bind fills to `Action/*` (for example `Action/Primary`, `Action/Primary-Hover`, `Action/Primary-Click`, `Action/Primary-Disabled`), borders to `Border/*` and labels to `Text/*`. Focus adds a visible ring (2px outside stroke at 3:1 or better), not just a color change. - **Data views** get **Loading** (a skeleton shaped like the real layout), **Empty** (a composed getting-started view) and **Error** states. ## Motion (prototype only) - Use Smart Animate with the custom bezier `0.22, 1, 0.36, 1`: - 500ms for layout changes - 300ms for color or opacity - Never use Figma's default linear or ease presets. ## Content - Real draft copy, never placeholder text. - Use believable names, brands and numbers for the product's industry. Avoid suspiciously round figures and stock names like "Acme". - **Banned filler:** "revolutionize", "supercharge", "cutting edge", "effortless", "transform your workflow", "take it to the next level", "harness the power", "world class". - Sentence case for headings and buttons. ## Landing pages When the task is a landing page, also follow the user's `landing-page-design` skill for page structure and copy (intake questions, section order, CTA rules). That includes the mandatory large tagline section with a word-by-word reveal, shown as a prototype interaction. If that skill isn't available, ask the user for it before planning sections. ## File hygiene - Name every layer meaningfully (`Pricing / Card / Pro`). Never leave `Frame 427`, `Group 3`, `Vector` or `Rectangle 1`. - Variable collections: `spacing`, `radius`, `Core Colors` and `Color System`, with `Light` and `Dark` modes on `Color System`. - Text styles use the names above, grouped in folders by type. - One page per area: `Foundations`, `Components`, `Screens`. - Put new top-level frames in clear canvas space, never on top of existing work. Don't delete or move the user's existing layers unless asked. ## Checklist (run before reporting) - [ ] Every text layer uses a named text style (Heading, Title, Body, Button, Caption) on the Tailwind scale - [ ] Every padding and gap is bound to a spacing variable - [ ] Every radius is bound to a radius variable, and nested radii follow the formula - [ ] Every fill and stroke is bound to a `Color System` token (never a raw hex or a Core Color), and Light and Dark modes both work - [ ] Core Colors palettes have at least 10 shades each, and every Color System token is an alias - [ ] The `Foundations` frame shows every token and text style, with samples bound to their variables and labels in sync - [ ] Contrast passes 4.5:1 for body and 3:1 for large text and UI parts - [ ] No background gradients, no single-side borders, no italics - [ ] Hero heading and subheading capped at 680, the hero heading has no periods, and there are no eyebrow tags unless asked for or shown in an uploaded inspiration image - [ ] Interactive components have all state variants, and buttons have all 5 types × 5 states - [ ] Icons from Phosphor, Solar or Iconamoon at one stroke weight - [ ] No placeholder copy, no layer named `Frame` or `Group` ## Token reference The names are fixed. Color hex values follow the user's brand; spacing, radius and type values are fixed. ### Spacing Collection `spacing`, one mode, `FLOAT`, scope `GAP`. | Token | Value | |---|---| | spacing/0 | 0 | | spacing/25 | 2 | | spacing/50 | 4 | | spacing/75 | 8 | | spacing/100 | 12 | | spacing/200 | 16 | | spacing/300 | 24 | | spacing/400 | 32 | | spacing/500 | 40 | | spacing/600 | 48 | | spacing/700 | 64 | | spacing/800 | 80 | | spacing/900 | 96 | ### Radius Collection `radius`, one mode, `FLOAT`, scope `CORNER_RADIUS`. | Token | Value | |---|---| | radius/0 | 0 | | radius/2 | 2 | | radius/4 | 4 | | radius/6 | 6 | | radius/8 | 8 | | radius/12 | 12 | | radius/16 | 16 | | radius/24 | 24 | | radius/full | 9999 | ### Text styles The font is the file's one typeface (any font). Style names go in folders (`Heading / Heading 1`). Letter spacing is 0 unless the user sets it. | Folder / Style | Scale step | Size / line height | Weight | |---|---|---|---| | Heading / Heading 1 | `text-6xl` | 60 / 60 | Regular | | Heading / Heading 2 | `text-5xl` | 48 / 48 | Regular | | Heading / Heading 3 | `text-4xl` | 36 / 40 | Regular | | Heading / Heading 4 | `text-3xl` | 30 / 36 | Regular | | Heading / Heading 5 | `text-2xl` | 24 / 32 | Regular | | Heading / Heading 6 | `text-xl` | 20 / 28 | Regular | | Title / Title 1 | `text-xl` | 20 / 28 | Medium | | Title / Title 2 | `text-lg` | 18 / 28 | Medium | | Title / Title 3 | `text-base` | 16 / 24 | Medium | | Title / Title 4 | `text-sm` | 14 / 20 | Medium | | Title / Title 5 | `text-xs` | 12 / 16 | Medium | | Body / Body 1 | `text-base` | 16 / 24 | Semibold | | Body / Body 2 | `text-base` | 16 / 24 | Medium | | Body / Body 3 | `text-base` | 16 / 24 | Regular | | Body / Body 4 | `text-sm` | 14 / 20 | Semibold | | Body / Body 5 | `text-sm` | 14 / 20 | Medium | | Body / Body 6 | `text-sm` | 14 / 20 | Regular | | Button / Button-lg | `text-lg` | 18 / 28 | Semibold | | Button / Button-md | `text-base` | 16 / 24 | Semibold | | Button / Button-sm | `text-sm` | 14 / 20 | Semibold | | Button / Button-Xsm | `text-xs` | 12 / 16 | Semibold | | Caption / Caption 1 | `text-sm` | 14 / 20 | Medium | | Caption / Caption 2 | `text-sm` | 14 / 20 | Regular | | Caption / Caption 3 | `text-xs` | 12 / 16 | Semibold | | Caption / Caption 4 | `text-xs` | 12 / 16 | Medium | | Caption / Caption 5 | `text-xs` | 12 / 16 | Regular | Font style strings differ by family. Check them with `figma.listAvailableFontsAsync()` before loading (for example Geist uses `SemiBold`, Inter-style families use `Semi Bold`). ### Color #### Layer 1: `Core Colors` One mode. Raw hex values from the brand. | Palette | Role | Shades | |---|---|---| | `Primary` | main brand hue | 00, 50, 100, 150, 200, 300, 350, 400, 450, 500, 600, 700, 800, 850, 900, 950, 1000 | | `Secondary` | second brand hue | 00, 50, 100, 150, 200, 300, 400, 500, 600, 700, 800, 850, 900, 950, 1000 | | `Accent` | accent hue | 00, 50, 100, 150, 200, 300, 400, 500, 600, 700, 800, 850, 900, 950, 1000 | | `Grayscale` | neutrals | 00, 50, 100, 150, 200, 250, 300, 400, 450, 500, 600, 700, 750, 800, 850, 870, 900, 950, 1000 | | `Red` | error | 00, 50, 100, 150, 200, 300, 400, 500, 600, 700, 800, 850, 900, 950, 1000 | | `Green` | success | 00, 50, 100, 150, 200, 300, 400, 500, 600, 700, 800, 850, 900, 950, 1000 | | `Yellow` | warning | 00, 50, 100, 150, 200, 300, 400, 500, 600, 700, 800, 850, 900, 950, 1000 | | `Grayscale-Alpha` | see-through neutrals for overlays | 700-16, 700-24, 700-40, 700-64, 700-80 (the Grayscale/700 hex at 16%, 24%, 40%, 64%, 80% opacity, stored in the variable's own opacity field) | - `00` is near white, `500` is the base hue and `1000` is near black. - Keep the Grayscale dark steps on the allowed dark backgrounds: `1000` = #000000, `870` = #181818, `850` = #1F1F1F, `800` = #272727, `750` = #313131. #### Layer 2: `Color System` Modes `Light` and `Dark`. Every value is an alias to a Core Color. The Dark column is the user's reference mapping. Build the Light column by mirroring the shade (a dark surface at Grayscale 870 becomes a light surface near Grayscale 00/50), then check contrast. **Border** | Token | Dark alias | |---|---| | Border/Primary | Grayscale/750 | | Border/Primary-Disabled | Grayscale/800 | | Border/Primary-light | Grayscale/700 | | Border/Secondary | Grayscale/750 | | Border/Tertiary | Grayscale/600 | | Border/Action-Primary | Primary/400 | | Border/Action-Disabled | Primary/600 | | Border/Action-Secondary | Grayscale/500 | | Border/Error | Red/400 | | Border/Error-disabled | Red/700 | | Border/Success | Green/400 | | Border/Warning | Yellow/300 | | Border/Warning - Disabled | Yellow/700 | **Surface** | Token | Dark alias | |---|---| | Surface/Primary | Grayscale/870 | | Surface/Primary-Hover | Grayscale/800 | | Surface/Primary-Selected | Grayscale/700 | | Surface/Primary-subtle | Grayscale/850 | | Surface/Secondary | Grayscale/800 | | Surface/Secondary-Hover | Grayscale/700 | | Surface/Secondary-Selected | Grayscale/700 | | Surface/Secondary-Subtle | Grayscale/750 | | Surface/Tertiary | Grayscale/600 | | Surface/Tertiary-Hover | Grayscale/450 | | Surface/Accent | Accent/500 | | Surface/Accent-subtle | Accent/200 | | Surface/Info | Primary/400 | | Surface/Info-subtle | Primary/150 | | Surface/Error | Red/500 | | Surface/Error-subtle | Red/100 | | Surface/Error - Darker | Red/700 | | Surface/Inverted | Grayscale/00 | | Surface/Inverted-Hover | Grayscale/250 | | Surface/Inverted-Disabled | Grayscale/450 | | Surface/Warning | Yellow/500 | | Surface/Warning-subtle | Yellow/100 | | Surface/Warning - Hover | Yellow/700 | | Surface/Success | Green/500 | | Surface/Success-Subtle | Green/100 | **Action** (button and control fills) | Token | Dark alias | |---|---| | Action/Primary | Primary/500 | | Action/Primary-Hover | Primary/700 | | Action/Primary-Click | Primary/600 | | Action/Primary-Disabled | Primary/700 | | Action/Secondary | Grayscale/600 | | Action/Secondary-Hover | Grayscale/750 | | Action/Secondary-Click | Grayscale/700 | | Action/Error | Red/700 | | Action/Error-Hover | Red/800 | | Action/Error-Disabled | Red/800 | | Action/Error-Subtle | Red/150 | | Action/Warning | Yellow/500 | | Action/Warning - Hover | Yellow/700 | | Action/Warning - Disabled | Yellow/800 | | Action/Warning - Subtle | Yellow/100 | **Text** | Token | Dark alias | |---|---| | Text/Primary | Grayscale/50 | | Text/Primary-Disabled | Grayscale/750 | | Text/Primary-Subtle | Grayscale/500 | | Text/Secondary | Grayscale/200 | | Text/Secondary-Disabled | Grayscale/500 | | Text/Tertiary | Grayscale/400 | | Text/Inverted | Grayscale/850 | | Text/Error | Red/500 | | Text/Error-Disabled | Red/800 | | Text/Error-Inverted | Red/800 | | Text/Error-Inverted-Disabled | Red/200 | | Text/Success | Green/600 | | Text/Success-Inverted | Green/700 | | Text/Success-Inverted-Disabled | Green/300 | | Text/Info | Primary/350 | | Text/Info-Inverted | Primary/700 | | Text/Info-Inverted-Disabled | Primary/200 | | Text/Warning | Yellow/500 | | Text/Warning - Disabled | Yellow/800 | | Text/Warning-Inverted | Yellow/800 | | Text/Warning-Inverted-Disabled | Yellow/300 | **Icon** | Token | Dark alias | |---|---| | Icon/Default | Grayscale/50 | | Icon/Secondary | Grayscale/200 | | Icon/Subtle | Grayscale/500 | | Icon/Inverted | Grayscale/850 | | Icon/Disabled | Grayscale/750 | | Icon/Error | Red/500 | | Icon/Error-Disabled | Red/800 | | Icon/Error-Inverted-Disabled | Red/200 | | Icon/Info | Primary/350 | | Icon/Info-Disabled | Primary/200 | | Icon/Warning | Yellow/500 | | Icon/Warning-Inverted | Yellow/700 | | Icon/Warning - Disabled | Yellow/800 | | Icon/Warning-Inverted-Disabled | Yellow/300 | | Icon/Success | Green/400 | | Icon/Success-Inverted-Disabled | Green/300 | **Overlay** | Token | Value | |---|---| | Overlay/xs | Grayscale-Alpha/700-16 | | Overlay/sm | Grayscale-Alpha/700-24 | | Overlay/md | Grayscale-Alpha/700-40 | | Overlay/lg | Grayscale-Alpha/700-64 | | Overlay/xlg | Grayscale-Alpha/700-80 | An alias can't add opacity; it takes the target as it is. So the transparency lives in Core Colors (`Grayscale-Alpha`) and Overlay aliases it. Never put opacity on an overlay layer by hand.