Font theme setup
Skill vipincode/exr-agent-skills/.claude/skills/font-theme-setup
Agent Skills for building production-grade Express + TypeScript + Mongoose backends with Claude Code
npx -y skills add vipincode/exr-agent-skills --skill font-theme-setupAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Extract a design system's tokens from a Figma file (via the Figma MCP) and apply them to a Next.js + Tailwind v4 + shadcn/ui project's THEME — rewriting globals.css color tokens in oklch, wiring fonts in layout.tsx (next/font Google + local custom fonts), and setting radius, shadows, spacing, line-heights, gradients, and background images. Use this whenever the user wants to set up or update the theme / design tokens / colors / typography / fonts from a Figma link — phrases like "set up the theme from this figma", "apply these design tokens", "update the colors and fonts from figma", "convert the palette to oklch", "configure the shadcn theme", or "wire up the fonts". This is the frontend theming skill — it edits globals.css + layout.tsx + tailwind theme, it does NOT build pages or components from a frame (that is figma-to-component) and does NOT scaffold the project (that is nextjs-bootstrap). Runs once per project (re-run to refresh tokens). Requires the Figma MCP to be connected and a Figma file/frame URL.
SKILL.md
9.9 KB, as published. Nobody here has run it
font-theme-setup
Turn a Figma file's design tokens into a Next.js + Tailwind v4 + shadcn/ui theme. The job is to read the source of truth (the Figma variables / styles) and write it into the two places a shadcn app stores its theme: src/app/globals.css (the CSS variables, colors in oklch) and src/app/layout.tsx (the fonts). Everything visual that's a token — color, type scale, radius, shadow, spacing, line-height, gradient, background — flows from Figma into those files so the rest of the app can just use Tailwind utilities and shadcn semantic classes.
This is a theming skill, not a component skill. It owns the global look. Building actual screens/components from a frame is figma-to-component; scaffolding the project is nextjs-bootstrap. Theming was deliberately deferred by the bootstrap to this skill (see the bootstrap's "Styling / theming" note).
Why oklch, and why these two files
shadcn/Tailwind v4 store theme colors as oklch CSS variables in globals.css (a :root block, a .dark block, and a @theme inline block that maps them to Tailwind color names). Figma almost always hands you hex/rgb. So the core mechanical step is hex→oklch, done for every color — that's what scripts/hex_to_oklch.py is for (don't convert by hand; it's repetitive and easy to get subtly wrong). oklch is perceptually uniform, which is why shadcn picked it: light/dark variants and hover shades stay visually consistent.
Fonts live in layout.tsx because Next's next/font (Google and local) must run there to self-host the font, expose a CSS variable, and avoid layout shift. The CSS variable it produces (e.g. --font-sans) is then referenced from globals.css/@theme. So the two files work together: layout.tsx defines the font and its variable; globals.css binds that variable into the type system.
Prerequisites & project resolution
- Resolve the project dir. Read
.claude/workspace.jsonat the repo root for thefrontendentry → that's<proj>. If absent, look for afrontend-*folder (e.g.frontend-shoply/), a plainfrontend/, or asrc/app/globals.css. If you can't find a Next.js + shadcn project, stop and say so — this skill edits an existing scaffold, it doesn't create one (point them atnextjs-bootstrap). - Read the contract files at
<proj>(or repo root):ARCHITECTURE.mdandMODULE_REGISTRY.md. You'll update their theming notes at the end. - Confirm the Figma MCP is connected and you have a URL. You need a
figma.com/design/<fileKey>/...?node-id=<n>link. If the user gave a file URL with nonode-id, ask for a node-specific link (the frame/page that holds the design-system styles), or callget_metadata(no nodeId) to list pages and pick the tokens/foundations page.
Workflow
All paths are relative to
<proj>(the resolved frontend project dir).
- Pull the tokens from Figma. Prefer
get_variable_defson the design-system / foundations node — it returns the named variables (colors, font families, sizes, spacing, radii) which map cleanly to CSS variables. Also callget_design_contexton a representative frame to see computed styles (font weights, line-heights, shadows, gradients that aren't always variables) andget_screenshotto eyeball the palette/typography. Readreferences/extracting-tokens.mdfor exactly what to ask for and how to interpret each tool's output. - Normalize into a token map. Group what you got into: colors (brand/semantic → shadcn roles), typography (font families, the size/line-height/weight scale), radius, shadows, spacing, gradients, background images. Don't invent tokens Figma doesn't have; do map Figma's semantic names onto shadcn's roles (see step 4).
- Convert every color to oklch. Collect the hex/rgb values into a JSON map
{role: "#hex"}and runpython scripts/hex_to_oklch.py --json colors.json(or pass colors as args for a quick check). Use the output verbatim — these are the values you'll paste intoglobals.css. - Map onto shadcn's semantic roles. shadcn's theme is a fixed set of roles:
background foreground card popover primary secondary muted accent destructive border input ring(+-foregroundpairs), pluschart-1..5and thesidebar-*set. Map Figma's palette onto these rather than inventing names, so every shadcn component is themed for free. Add extra brand tokens only when there's no role for them. Full mapping table + light/dark guidance:references/token-mapping.md. - Rewrite
globals.css. Update the:rootblock (light), the.darkblock (dark), and the@theme inlinemapping. Set--radiusfrom Figma's corner radius; add shadow tokens (--shadow-*), gradient tokens, and any custom font-size/line-height theme entries. Preserve shadcn's variable names and structure — you're swapping values, not restructuring.references/token-mapping.mdshows the exact block shape for Tailwind v4. - Wire fonts in
layout.tsx. Google fonts vianext/font/google, custom/brand fonts vianext/font/local(drop the font files undersrc/app/fonts/and reference them). Each font exposes a CSS variable; add those variables to<html className={...}>and bind them in@theme(--font-sans,--font-serif,--font-mono, or custom--font-display). Exact patterns for both:references/fonts.md. - Apply the rest of the theme. Radius, shadows, gradients, background images — wire them as tokens/utilities so components reference
rounded-lg,shadow-card,bg-gradient-brand, etc., never raw values.references/token-mapping.mdcovers gradients/shadows/bg-images in Tailwind v4. - Update the contract files. In
ARCHITECTURE.md, replace the "theme/fonts deferred to font-theme-setup" note with the now-concrete theme (fonts used, palette source, oklch). InMODULE_REGISTRY.md, update the decisions log line for theming/fonts (what fonts, light/dark, token source = this Figma file). Future skills (figma-to-component, builders) read these to use tokens instead of hardcoding. - Verify. Run the project's build/lint (
<pm> run build) so the CSS +layout.tsxchanges typecheck and Tailwind compiles. Spot-check/renders with the new fonts/colors. Report what changed (files touched, fonts added, # colors converted, light/dark coverage).
What to read when
references/extracting-tokens.md— which Figma MCP tool to call for which token type, and how to read each response (variables vs computed styles, dealing with nonode-id, screenshots for sanity). Read before step 1.references/token-mapping.md— the shadcn role table, Figma→role mapping, the exact Tailwind v4:root/.dark/@theme inlineblock shapes, and how radius/shadow/gradient/bg-image tokens are expressed. Read before steps 4–7.references/fonts.md—next/font/googleandnext/font/localpatterns, exposing CSS variables, binding them in@theme, and the variable-font / multiple-weight cases. Read before step 6.scripts/hex_to_oklch.py— the hex/rgb →oklch()converter. Single color, many colors, or--jsonbatch. Use it for every color.
Non-negotiables
- Colors are oklch, always. shadcn's whole theme system is oklch in Tailwind v4. Never paste raw hex into
globals.csstheme variables — convert with the script. (Hex is fine only inside a one-offlinear-gradient()if there's truly no token for it, but prefer tokens.) - Don't restructure shadcn's variables — re-value them. Keep the role names (
--primary,--background, …) and the:root/.dark/@theme inlinethree-block shape. Components depend on those names; renaming breaks them. You're changing values and adding tokens, not redesigning the system. - Map to semantic roles, don't sprinkle brand hexes. A Figma "Blue/600" becomes
--primary(and its readable text becomes--primary-foreground), so every Button/Badge/Link is themed at once. Resist creating--blue-600-style tokens unless the design truly needs a raw brand color with no semantic role. - Fonts go through
next/font, never<link>tags or@import url(). That's how Next self-hosts, prevents layout shift, and gives you the CSS variable. Google →next/font/google; brand/custom files →next/font/local. - Light AND dark. Figma usually defines both (or a clear single mode). Fill both
:rootand.dark. If the design is one mode only, say so and set dark to a sensible derivation rather than leaving stale shadcn defaults. - Theme tokens, not magic numbers. Radius, shadows, gradients, spacing → tokens/utilities so components stay declarative. A component hardcoding
border-radius: 14pxinstead of using--radiusis a regression this skill exists to prevent. - This skill is theme-only. Don't build components or pages here. If the user also wants the frame turned into components, that's
figma-to-component— hand off, don't scope-creep.