Skip to content

  11 min read

Claude Code and Figma: one screen from Appetite UI, step by step

Connect Figma's MCP server to Claude Code, add the Core Skill and Swift tokens, and build the Appetite UI Finance home screen in SwiftUI, dark mode included.

The Appetite UI Finance home screen in dark mode beside a Claude Code session: the prompt Build the wallet home screen in dark mode, a Figma frame link, the get_design_context and get_variable_defs calls, and four tokens with their SwiftUI names, brand/base as Color.apBrandBase, bg/weak-50 as Color.apBgWeak50, text/strong-950 as Color.apTextStrong950 and dimensions/spacing/6 as AppetiteSpacing.space6
On this page
  1. Two inputs
  2. The seven steps
  3. What the frame binds
  4. The SwiftUI view
  5. Review the output
  6. Run the slop linter
  7. When no token fits
  8. Questions
  9. Sources

To build an Appetite UI screen in SwiftUI with Claude Code, connect Figma's MCP server, copy the Core Skill into .claude/skills/, add AppetiteTokens.swift to your Xcode target, paste the link to the frame and ask. Then read the diff for token names before you run it. This article walks one screen through all of it: the Finance home screen of the Appetite UI Figma file, built from the same sentence the AI page runs, "Build the wallet home screen in dark mode."

Appetite UI is a Figma UI kit for iOS and Android apps, built like a design system. One token source drives Apple and Material components in light and dark mode, and exports to SwiftUI, Jetpack Compose, Flutter and CSS. That is why the setup is short: the frame already names every value, and the agent only has to keep the names.

Claude Code needs the frame and the rules, and they come from two places.

A Figma frame tells an agent what is on the screen. It does not tell the agent what your project calls those values in code, or that a 24 point padding is dimensions/spacing/6 and must stay that. The Figma MCP server answers the first question. Figma describes the remote server as link based: you copy the link to a frame and paste it into the prompt. The server then returns the frame's structure, a picture of it and the variables bound to its layers.

The Core Skill answers the second. It is a Markdown file that opens with "You are building inside a system. The values are already decided." Its first rule is to read the tokens before producing anything, the second is to apply semantic tokens only and never type a hex value. It is included in the Appetite UI file you buy, beside a slop linter skill and an AGENTS.md for repositories.

Figma's own numbers say the first half is no longer a niche setup. Figma reported on May 14, 2026 that "MCP weekly active users in Figma Design grew five times quarter-over-quarter" in the three months ended March 31, 2026. More people now hand frames to agents, which makes the second half the part that decides whether the code is usable.

Seven steps take one frame to a reviewed SwiftUI view.

Each step below is checked against the documentation of the tool it touches, read on October 7, 2026, and against the README in the package that comes with the Appetite UI file. The paths are the package's own.

  1. Connect the Figma MCP server to Claude Code.

    Figma recommends its plugin, claude plugin install figma@claude-plugins-official, which brings the server settings and Figma's own skills. The manual route is claude mcp add --transport http figma https://mcp.figma.com/mcp. Then type /mcp in Claude Code, select figma, choose Authenticate and click Allow Access in the browser. Claude Code confirms with "Authentication successful. Connected to figma".

  2. Give Claude Code the Appetite UI rules.

    Copy AI-context/skills/appetite-core and AI-context/skills/appetite-slop-linter from the package into .claude/skills/ in your repository, so each skill sits at .claude/skills/<name>/SKILL.md. If your team already keeps one instruction file for several agents, copy AI-context/agents/AGENTS.md to the repository root instead, with design.md and tokens/tokens.md beside it.

  3. Put the tokens in the repository and the Swift file in the target.

    Keep the package's tokens/ folder next to your code, so the agent can look a value up in tokens/tokens.md. Add tokens/swift/AppetiteTokens.swift to the Xcode app target, and add Inter and Inter Display to the target with both listed under UIAppFonts in Info.plist.

  4. Copy the link to the frame.

    In the Appetite UI file, open the Finance page, right-click the Finance / Dashboard frame and choose Copy link to selection. The server cannot browse to the link; it reads the node id from it.

  5. Prompt with the link.

    Start the message with /appetite-core, so the skill loads for certain, then write "Build the wallet home screen in dark mode." and paste the link.

  6. Review the diff against the tokens.

    No hex, no literal spacing, no hand-set font, and dark mode from the same tokens. The section on review below lists what to look for.

  7. Run the slop linter on the diff.

    Start a message with /appetite-slop-linter and ask it to audit the changed files. It reports, and fixes only when you ask.

The first three steps run once per project. In a terminal, from the root of your app repository, with APPETITE pointing at the unpacked package:

Setuponce per project
claude mcp add --transport http figma https://mcp.figma.com/mcp

mkdir -p .claude/skills
cp -r "$APPETITE/AI-context/skills/appetite-core" .claude/skills/
cp -r "$APPETITE/AI-context/skills/appetite-slop-linter" .claude/skills/
cp -r "$APPETITE/tokens" .

The claude mcp add line installs the server at local scope, for you in this project. Add --scope project to write it to .mcp.json in the repository, so your team gets the same server. Claude Code reads an AGENTS.md only when the project has no CLAUDE.md, from version 2.1.277; with a CLAUDE.md, add an @AGENTS.md line to it.

The Finance home frame already names every value.

The screen is Finance / Dashboard on the Finance page of the Appetite UI Figma file, the Home tab of the Finance app. It holds a header over a background image, with a search field and the total balance, four quick actions (Send, Receive, Pay, Top up), a Recent transactions list and a tab bar. The file draws it twice, once in Light and once in Dark, and both frames bind the same variables.

When I read the dark frame through the MCP server for this article, get_variable_defs returned the variables below, among others. The server reports each one by its CSS code name; the Figma path and the SwiftUI name come from tokens/tokens.md, where every token is listed with all of its code names.

The Finance home frame already names every value.
Figma variableSwiftUI nameWhere it sits on the screen
bg/white-0Color.apBgWhite0The page and the tab bar surface
bg/weak-50Color.apBgWeak50The 40 point tile behind each transaction icon
brand/baseColor.apBrandBaseThe View all link and the active Home tab
brand/lighterColor.apBrandLighterThe quick action tiles
text/strong-950Color.apTextStrong950Transaction names, amounts and the section title
text/soft-500Color.apTextSoft500Dates and the inactive tab labels
fg/sub-700Color.apFgSub700The icons inside the transaction tiles
state/success/base-500Color.apStateSuccessBase500The incoming amounts, Salary +$8,400.00
border/soft-100Color.apBorderSoft100The outline of the tab bar
dimensions/spacing/6AppetiteSpacing.space6The padding of the transactions block
dimensions/radius/lgAppetiteRadius.lgThe corners of the transaction tiles
dimensions/radius/2xlAppetiteRadius.xl2The corners of the quick action tiles
sm/emphasizedAppetiteFont.smEmphasizedTransaction names and amounts
6xl/emphasizedAppetiteFont.xl6EmphasizedThe balance, $28,401

One binding deserves a second look before you hand it to an agent. The balance and the search label over the header image bind static/static-white-0, a white that does not flip. That is correct here, because the image behind them is a deep blue in Light and near black in Dark, white reads on both, and the token's own description says to use it "only where the value must survive a theme switch". An agent that reads the variables keeps it as Color.apStaticWhite0. An agent that reads only the picture writes .white and gets the same pixels for the wrong reason.

The dark Finance / Dashboard frame in Figma on the left, with its balance, quick actions and recent transactions, and a Claude Code session on the right showing the prompt Build the wallet home screen in dark mode, the pasted frame link and the get_design_context and get_variable_defs calls
The frame and the session side by side: the prompt, the link and the two tool calls that read the frame.

The view the agent should return reads like the token table.

Here is the Recent transactions block as the agent should write it: a section header with the View all link, and one row per transaction with a tile, a name, a date and an amount. Every number is a token, and every color is a dynamic token color.

RecentTransactions.swiftevery name from AppetiteTokens.swift
import SwiftUI

struct Transaction: Identifiable {
    let id = UUID()
    let title: String, date: String, amount: String, icon: Image, isIncome: Bool
}

struct RecentTransactions: View {
    let transactions: [Transaction]

    var body: some View {
        VStack(alignment: .leading, spacing: AppetiteSpacing.space5) {
            HStack {
                Text("Recent transactions").appetiteText(.lgEmphasized)
                    .foregroundStyle(Color.apTextStrong950)
                Spacer()
                Button("View all") { }.font(AppetiteFont.smEmphasized)
                    .foregroundStyle(Color.apBrandBase)
                    .frame(minHeight: AppetiteSpacing.space11) // 44pt hit area
            }
            VStack(spacing: AppetiteSpacing.space3) {
                ForEach(transactions) { item in
                    HStack(spacing: AppetiteSpacing.space4) {
                        item.icon.frame(width: AppetiteIcon.sizeMd, height: AppetiteIcon.sizeMd)
                            .foregroundStyle(Color.apFgSub700)
                            .frame(width: AppetiteSpacing.space10, height: AppetiteSpacing.space10)
                            .background(Color.apBgWeak50, in: .rect(cornerRadius: AppetiteRadius.lg))
                        VStack(alignment: .leading) {
                            Text(item.title).font(AppetiteFont.smEmphasized)
                                .foregroundStyle(Color.apTextStrong950)
                            Text(item.date).font(AppetiteFont.xsDefault)
                                .foregroundStyle(Color.apTextSoft500)
                        }
                        Spacer()
                        Text(item.amount).font(AppetiteFont.smEmphasized)
                            .foregroundStyle(item.isIncome ? Color.apStateSuccessBase500 : Color.apTextStrong950)
                    }
                }
            }
        }
        .padding(AppetiteSpacing.space6)
        .background(Color.apBgWhite0)
    }
}

Typechecked on October 7, 2026 with swiftc -typecheck against the package's AppetiteTokens.swift, for macOS and for the iOS 17 simulator. The .rect(cornerRadius:) shape needs iOS 17.

The gaps in the code match the gaps in the frame: 20 between the header and the list is space5, 12 between rows is space3, 16 between the tile and the text is space4, and the block's 24 padding is space6. The one value the frame does not draw is the 44 point hit area on View all. The Button component's description in the file asks for it in code, whatever the visual size, and AppetiteSpacing.space11 is 44.

There is no colorScheme in the file. The README says it plainly: "Colors are dynamic: they follow the system appearance with no branching." Color.apBrandBase resolves to one blue in Light and a lighter blue in Dark because the token has two values, so "in dark mode" in the prompt is a preview setting, .preferredColorScheme(.dark), and not a second implementation. How those two values are set up in Figma is in Dark mode with Figma variables, not duplicate frames.

Review the diff for three things before you run it.

A generated view can look right in the simulator and still be wrong in the source. The test is the source. Read the diff and check three things.

First, no hex and no raw color. Color(red:green:blue:), a hex initializer, .white, .gray or Color.blue on a surface means the agent took a value from the picture. The exception is the one you saw above: white on the header image, which the frame binds to static/static-white-0.

Second, no literal geometry. .padding(24) is the right number today and a stale one after the first change to the scale. The Core Skill's third rule is blunt about it: "Any value that is not on the scale is wrong." A number on the scale is still a literal unless it is written as AppetiteSpacing.space6.

Third, dark mode from the same tokens. An if colorScheme == .dark branch with its own colors is a second palette, which is exactly what the token's second value replaces. The Core Skill's sixth rule says it in one line: "If you typed a value, dark mode is already broken."

A quick search over the diff catches most of the first two before you read a line:

A first pass over the diffliterals the review should not find
git diff -U0 -- '*.swift' | grep -nE '#[0-9A-Fa-f]{6}|Color\(red|\.padding\([0-9]|cornerRadius: [0-9]|size: [0-9]'

An empty result does not mean the view is right. It means the remaining review is about layout and intent, which is the part you want a person for.

A wallet home screen built in SwiftUI with Appetite UI tokens, running in the iOS 27 simulator twice: in Light with white surfaces and a light blue card, and in Dark with near-black surfaces and a dark blue card
One build in Light and in Dark. The only change between the two is the simulator appearance setting. The screen is the wallet from the run on the AI page, built with the Core Skill and AppetiteTokens.swift.

The slop linter reads the diff against the same rules.

The second skill in the package audits work against the system instead of producing it. Start a message with /appetite-slop-linter and point it at the changed files. It loads tokens/tokens.md first, and its first instruction says that if the tokens are unavailable it says so and stops, because "You cannot lint against a system you have not read."

It then sorts what it finds into Blocking, Major, Minor and Platform. A raw color with no token binding is Blocking (raw-color), and so is white text on a state fill that fails contrast. Off-scale spacing and radii, hand-set type and a detached copy of a component are Major. Every finding names where it is, what the value is and which token replaces it; the skill's own rule is that "A finding with no replacement is not a finding, it is a complaint." The report ends with what is clean, so you see whether the screen is mostly on the system or mostly off it.

It offers the fix and does not apply it unless you ask. When you do, it fixes bindings first, then geometry, then type, then structure.

A gap report is a correct answer.

Sometimes the right response from the agent is not code. The Core Skill's last rule is "When no token fits, stop and say so": report what you needed, the closest token and what you would add. The skill's own example is a dialog exit, which has no motion preset.

Treat that answer as the setup working. An agent that invents a value to finish the screen hands you a review problem; an agent that names the gap hands you a decision, and it belongs to you or to whoever owns the tokens. If a gap comes up twice, add the token to the source and rebuild the outputs, as the package README describes, so the next run has a name to reach for. How one Figma token becomes SwiftUI, Compose, Flutter and CSS follows that path for one variable.

The same steps work on any frame in the file. The Finance home screen is a good first one because it mixes an image header, a list and a tab bar on one screen. How Appetite UI works with AI agents covers what the Core Skill contains and where other tools read it, and the Figma MCP server for mobile apps covers the server side in more depth. To see the whole file before you set anything up, open the read-only Figma preview; to see this prompt run start to finish, the AI page plays it.

Questions

Do I need a paid Figma plan to use the Figma MCP server with Claude Code?

No, not for the remote server. Figma's guide lists the remote server for all seats and plans, and its setup article adds that seats other than Full and Dev may have usage limits. The Appetite UI library itself needs a paid Figma plan, because variable modes and team libraries both need one.

Does Claude Code read AGENTS.md?

Yes, when the project has no CLAUDE.md. Claude Code reads AGENTS.md directly from version 2.1.277. If your project has a CLAUDE.md, put an @AGENTS.md line in it and Claude Code reads both.

Can I select the frame in Figma instead of pasting a link?

The remote Figma MCP server works from a link. Select the frame, choose Copy link to selection and paste the link into the prompt; the server reads the node id from it.

Does the same setup work for Jetpack Compose or Flutter?

Yes. Only the third step changes: add AppetiteTokens.kt for Compose or appetite_tokens.dart for Flutter. Both come in the same package with the same token names, such as AppetiteTheme.colors.brandBase and apBrandBase.

Is the Appetite UI Core Skill free?

No. The Core Skill, the slop linter skill and AGENTS.md are included in the Appetite UI file you buy; pricing lists what comes with it. The docs as Markdown, /llms.txt and /tokens.json are public for any agent to read.

What if the agent still writes a hex value?

Run the slop linter on the diff. A color with no token binding is its first Blocking check, raw-color, and the report names the token that replaces it. If it keeps happening, start the prompt with /appetite-core so the skill is loaded before the agent writes anything.

Sources

  1. Figma Developer Docs, Set up the remote server, read October 7, 2026
  2. Figma Learn, Claude Code and Figma: Set up the MCP server, read October 7, 2026
  3. Figma Learn, Guide to the Figma MCP server, read October 7, 2026
  4. Figma Learn, How to set up the Figma remote MCP server, read October 7, 2026
  5. Figma, Figma Announces First Quarter 2026 Financial Results, read October 7, 2026
  6. Claude Code documentation, Connect Claude Code to tools via MCP, read October 7, 2026
  7. Claude Code documentation, Extend Claude with skills, read October 7, 2026
  8. Claude Code documentation, How Claude remembers your project, read October 7, 2026

Written by

Bob Poláček

Designer of Appetite UI. Builds the Figma file, the token architecture and the code output, and answers the support inbox.

Keep reading

  • AI · Sep 16, 2026 · 9 min read

    How Appetite UI works with AI agents: Claude, Cursor and the Core Skill

    The Appetite UI Core Skill, included in the file you buy, tells Claude, Cursor and other agents which token to use, so a generated screen reads your values.

  • Tokens · Sep 24, 2026 · 11 min read

    How one Figma token becomes SwiftUI, Compose, Flutter and CSS

    One Figma variable, brand/base, followed into CSS, SwiftUI, Compose and Flutter: where each code name comes from, what a mode becomes, what cannot translate.

  • Guide · Sep 24, 2026 · 9 min read

    Dark mode with Figma variables, not duplicate frames

    Dark mode in Figma without a second set of frames: semantic variables with a Light and a Dark mode, one frame per screen, and the checks to run before handoff.

Your next rebrand is one edit.

One-time payment, Lifetime updates, Unlimited client projects