<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: abhi</title>
    <description>The latest articles on DEV Community by abhi (@planetabhi_).</description>
    <link>https://dev.to/planetabhi_</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4089133%2F6102c25c-7898-4ed5-9166-c73807fa4f49.jpg</url>
      <title>DEV Community: abhi</title>
      <link>https://dev.to/planetabhi_</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/planetabhi_"/>
    <language>en</language>
    <item>
      <title>Structuring Variables in Figma</title>
      <dc:creator>abhi</dc:creator>
      <pubDate>Sat, 29 Aug 2026 08:04:14 +0000</pubDate>
      <link>https://dev.to/planetabhi_/structuring-variables-in-figma-32d8</link>
      <guid>https://dev.to/planetabhi_/structuring-variables-in-figma-32d8</guid>
      <description>&lt;p&gt;Building a scalable UI library in Figma starts with thoughtful organization of variables. As projects grow, managing primitives, semantics, and themes without proper structure can quickly become overwhelming. &lt;/p&gt;

&lt;p&gt;This guide explores a clear, implementation-aligned approach to structuring variables by purpose, from foundational values to reusable design tokens and dynamic content. This approach reduces redundancy, streamlines updates, and maintains consistency across components and abstractions.&lt;/p&gt;

&lt;h4&gt;
  
  
  Understanding variables in Figma
&lt;/h4&gt;

&lt;p&gt;Before diving into structuring, it helps to understand what variables are and why they matter. At their core, variables in Figma store reusable values that can be applied to various design properties and prototyping actions. They're incredibly useful for saving time and effort when creating designs, managing design systems, and building complex prototyping flows.&lt;/p&gt;

&lt;p&gt;When designing a button, instead of manually setting the color, padding, and border radius each time, variables store these values for reuse. When the button's color needs to change across an organization's projects, updating the variable once applies the change everywhere. This capability makes variables essential for maintaining design consistency.&lt;/p&gt;

&lt;h5&gt;
  
  
  Types of variables
&lt;/h5&gt;

&lt;p&gt;Figma offers six types of variables, each serving a unique purpose:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Defined by&lt;/th&gt;
&lt;th&gt;&amp;nbsp;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Color&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Solid fills&lt;/td&gt;
&lt;td&gt;Solid values like #000000. Used for theming and organizing palette.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Number&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Number values&lt;/td&gt;
&lt;td&gt;Hold values like 16. Used for responsive, language, and text styles.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;String&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Text strings&lt;/td&gt;
&lt;td&gt;Use text (like 'Namaste') for language, text styles, and prototype variants.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Boolean&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;True, false values&lt;/td&gt;
&lt;td&gt;Boolean variables use true/false. Used to show/hide layers.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Timing&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Number values&lt;/td&gt;
&lt;td&gt;Duration in milliseconds.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Easing&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Number values&lt;/td&gt;
&lt;td&gt;Easing curve or spring animation.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h5&gt;
  
  
  Collections and groups
&lt;/h5&gt;

&lt;p&gt;Figma provides collections and groups to help keep your variables organized:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Both collections and groups make variables more organized and easier to find.&lt;/li&gt;
&lt;li&gt;A collection brings together related variables and modes, making organization more intuitive.&lt;/li&gt;
&lt;li&gt;Within collections, you can further organize variables by placing them into logical groups.&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  Variables vs. styles
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;Variables define reusable values like colors and spacing, while styles are predefined sets of design properties, such as text formatting and effects.&lt;/li&gt;
&lt;li&gt;Unlike styles, variables enable dynamic design changes across different contexts. For instance, you can easily switch between light and dark modes or adjust padding for different devices, creating more adaptable component systems.&lt;/li&gt;
&lt;li&gt;Variables offer greater design flexibility, allowing you to change specific values like button text or color for individual instances. Styles help maintain design consistency for elements like button styles, headings, or color palettes.&lt;/li&gt;
&lt;li&gt;Variables store individual values, while styles store collections of related values.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Methodology
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;Organize collections by abstraction levels.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Figma variables can be organized into four collections, each serving a distinct purpose.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌───────────────┐                                                             
│  Collections  │                                                             
└───────────────┘                                                             
        │                                                                     
        ├───────────────────┬──────────────────┬───────────────────┐          
        ▼                   ▼                  ▼                   ▼          
┌───────────────┐ ┌───────────────────┐ ┌─────────────┐ ┌────────────────────┐
│ Design Tokens │ │ Global Primitives │ │  Language   │ │ Private Primitives │
│  (Published)  │ │    (Published)    │ │ (Published) │ │(Hidden &amp;amp; agnostic) │
└───────────────┘ └───────────────────┘ └─────────────┘ └────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&amp;nbsp;&lt;/th&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Visibility&lt;/th&gt;
&lt;th&gt;Code Definition&lt;/th&gt;
&lt;th&gt;&amp;nbsp;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;01&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Design Tokens&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Published&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;In sync&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Context for the intended use of a design primitive.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;02&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Global Primitives&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Published&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;In sync&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Basic building blocks of the design.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;03&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Language&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Published&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Optional&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Text strings for adapting to different languages.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;04&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Private Primitives&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Hidden&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;N/A&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Raw values intended for internal use.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  The rationale
&lt;/h4&gt;

&lt;p&gt;Through many iterations with enterprise projects, this collection structure has evolved to balance flexibility with governance. It addresses several key challenges when scaling design systems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Clarity in Abstraction&lt;/strong&gt;: Separating raw values from semantic tokens aligns design with development. This prevents mixing hex codes with contextual tokens, helping everyone avoid style inconsistencies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistency at Scale&lt;/strong&gt;: Private primitives help enforce mathematical scales, ensuring uniform spacing throughout interfaces. Designers can use these scaled values rather than manual padding, creating consistent UI element ratios.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Efficient Localization&lt;/strong&gt;: A dedicated language collection separates text from components, making it simple to switch between languages with minimal effort.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code as the Source of Truth&lt;/strong&gt;: "Design Tokens" and "Global Primitives" generate raw, tech-agnostic values. Figma structures mirror codebase themes directly, making design-to-development handoff much smoother.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach provides structure to prevent chaos while preserving the flexibility needed for complex product ecosystems.&lt;/p&gt;




&lt;h4&gt;
  
  
  Collections
&lt;/h4&gt;

&lt;p&gt;Each collection contains groups that provide both precision and flexibility for different use cases.&lt;/p&gt;

&lt;h5&gt;
  
  
  Collection 01: Design tokens
&lt;/h5&gt;

&lt;p&gt;Design Tokens provide a meaningful context for how a design primitive should be used. For example, a design token called &lt;code&gt;--background-warning&lt;/code&gt; instantly conveys a sense of urgency or potential caution.&lt;/p&gt;

&lt;p&gt;These tokens define colors, spacing, shadows, and sizing for components, with groups that enforce consistent styling across your interface.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Collection visibility (Figma): &lt;code&gt;Published&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Theme definition (Code): &lt;code&gt;In sync&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌───────────────────┐                         
│   Design Tokens   │                                            
└───────────────────┘                         
          │                                   
          │  ┌────────────┐     ┌────────────┐
          ├─▶│   Border   │──┬─▶│   Width    │
          │  └────────────┘  │  └────────────┘
          │                  │  ┌────────────┐
          │                  └─▶│   Radius   │
          │                     └────────────┘
          │  ┌────────────┐     ┌────────────┐
          ├─▶│   Colors   │──┬─▶│ Background │
          │  └────────────┘  │  └────────────┘
          │  ┌────────────┐  │  ┌────────────┐
          ├─▶│   Shadow   │  ├─▶│    Base    │
          │  └────────────┘  │  └────────────┘
          │  ┌────────────┐  │  ┌────────────┐
          ├─▶│   Sizing   │  ├─▶│   Border   │
          │  └────────────┘  │  └────────────┘
          │  ┌────────────┐  │  ┌────────────┐
          ├─▶│  Spacing   │  ├─▶│  Content   │
          │  └────────────┘  │  └────────────┘
          │  ┌────────────┐  │  ┌────────────┐
          └─▶│   Alias    │  ├─▶│ Highlight  │
             └────────────┘  │  └────────────┘
                             │  ┌────────────┐
                             └─▶│Illustration│
                                └────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the codebase, &lt;code&gt;design-tokens.js&lt;/code&gt; might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;designTokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;border&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;width&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;default&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0.063rem&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;radius&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;small&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0.25rem&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;colors&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;background&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;primary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;oklch(100% 0 0)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;primary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;oklch(59.23% 0.2221 261.8)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;shadow&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;default&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0px 0px 0px rgba(0, 0, 0, 0);&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sizing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;xs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1rem&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;spacing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;xs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0.25rem&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Collection 02: Global primitives
&lt;/h5&gt;

&lt;p&gt;Global Primitives form your design system's fundamental building blocks, like colors, spacing, and sizing. They create the foundation of your design but aren't typically used directly in components or layouts.&lt;/p&gt;

&lt;p&gt;These primitives store base values such as colors (grey-90, red-50), font sizes, and weights. These can be organized into themes (Default, High Contrast) for quick switching.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Collection visibility (Figma): &lt;code&gt;Published&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Theme definition (Code): &lt;code&gt;In sync&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌───────────────────┐                                                                  
│ Global Primitives │                                                                 
└───────────────────┘                                                                  
          │                                                                            
          │  ┌────────────┐     ┌────────────────┐     ┌────────────┐    ┌────────────┐
          ├─▶│   Colors   │──┬─▶│ Default theme  │──┬─▶│    Grey    │─┬─▶│  grey-90   │
          │  └────────────┘  │  └────────────────┘  │  └────────────┘ │  └────────────┘
          │                  │  ┌────────────────┐  │  ┌────────────┐ │  ┌────────────┐
          │                  ├─▶│ High contrast  │  ├─▶│    Red     │ ├─▶│  grey-80   │
          │                  │  └────────────────┘  │  └────────────┘ │  └────────────┘
          │                  │  ┌────────────────┐  │  ┌────────────┐ │  ┌────────────┐
          │                  └─▶│ More themes... │  ├─▶│   Orange   │ ├─▶│  grey-70   │
          │                     └────────────────┘  │  └────────────┘ │  └────────────┘
          │                                         │  ┌────────────┐ │  ┌────────────┐
          │                                         ├─▶│   Yellow   │ ├─▶│  grey-60   │
          │                                         │  └────────────┘ │  └────────────┘
          │  ┌────────────┐     ┌────────────────┐  │  ┌────────────┐ │  ┌────────────┐
          └─▶│ Typography │──┬─▶│  Font family   │  ├─▶│   Green    │ ├─▶│  grey-50   │
             └────────────┘  │  └────────────────┘  │  └────────────┘ │  └────────────┘
                             │  ┌────────────────┐  │  ┌────────────┐ │  ┌────────────┐
                             ├─▶│   Font size    │  ├─▶│    Mint    │ ├─▶│  grey-40   │
                             │  └────────────────┘  │  └────────────┘ │  └────────────┘
                             │  ┌────────────────┐  │  ┌────────────┐ │  ┌────────────┐
                             ├─▶│  Font weight   │  ├─▶│    Teal    │ ├─▶│  grey-30   │
                             │  └────────────────┘  │  └────────────┘ │  └────────────┘
                             │  ┌────────────────┐  │  ┌────────────┐ │  ┌────────────┐
                             └─▶│  Line height   │  ├─▶│    Cyan    │ ├─▶│  grey-20   │
                                └────────────────┘  │  └────────────┘ │  └────────────┘
                                                    │  ┌────────────┐ │  ┌────────────┐
                                                    ├─▶│    Blue    │ ├─▶│  grey-10   │
                                                    │  └────────────┘ │  └────────────┘
                                                    │  ┌────────────┐ │  ┌────────────┐
                                                    ├─▶│   Indigo   │ └─▶│  grey-00   │
                                                    │  └────────────┘    └────────────┘
                                                    │  ┌────────────┐                  
                                                    ├─▶│   Purple   │                  
                                                    │  └────────────┘                  
                                                    │  ┌────────────┐                  
                                                    ├─▶│    Pink    │                  
                                                    │  └────────────┘                  
                                                    │  ┌────────────┐                  
                                                    └─▶│   Brown    │                  
                                                       └────────────┘                  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's a simplified example of what &lt;code&gt;global.js&lt;/code&gt; could look like in practice:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;globalPrimitives&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;colors&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;default&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;grey&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;grey-90&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;oklch(0% 0 0)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;grey-00&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;oklch(100% 0 0)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;typography&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fontFamily&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;default&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;system-ui, sans-serif;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fontSize&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;m&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1rem&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fontWeight&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;regular&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lineHeight&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;m&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1.5&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Collection 03: Language
&lt;/h5&gt;

&lt;p&gt;The Language collection holds text strings for global areas, components, patterns, and abstractions. This centralizes translations, making them accessible throughout the design system.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Collection visibility (Figma): &lt;code&gt;Published&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Theme definition (Code): &lt;code&gt;Optional&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌───────────────────┐                                  
│     Language      │                                                                    
└───────────────────┘                                  
          │                                            
          │  ┌────────────────┐      ┌────────────────┐
          ├─▶│Global Patterns │───┬─▶│     Header     │
          │  └────────────────┘   │  └────────────────┘
          │  ┌────────────────┐   │  ┌────────────────┐
          ├─▶│  Abstractions  │   ├─▶│    Sidebar     │
          │  └────────────────┘   │  └────────────────┘
          │  ┌────────────────┐   │  ┌────────────────┐
          └─▶│    More...     │   ├─▶│     Footer     │
             └────────────────┘   │  └────────────────┘
                                  │  ┌────────────────┐
                                  └─▶│    More...     │
                                     └────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Collection 04. Private primitives
&lt;/h5&gt;

&lt;p&gt;Private primitives contain fundamental values (numbers, scales, black/white) that are used by other collections. They help build spacing scales and colors for other collections, ensuring mathematical consistency across your design system.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Collection visibility (Figma): &lt;code&gt;Hidden&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Theme definition (Code): &lt;code&gt;Not applicable&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌───────────────────┐          
│Private Primitives │                   
└───────────────────┘          
          │                    
          │  ┌────────────────┐
          ├─▶│      zero      │
          │  └────────────────┘
          │  ┌────────────────┐
          ├─▶│   1, 2, 4...   │
          │  └────────────────┘
          │  ┌────────────────┐
          ├─▶│      50vh      │
          │  └────────────────┘
          │  ┌────────────────┐
          ├─▶│  black, white  │
          │  └────────────────┘
          │  ┌────────────────┐
          ├─▶│     solid      │
          │  └────────────────┘
          │  ┌────────────────┐
          └─▶│ regular, bold  │
             └────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h4&gt;
  
  
  Bringing it all together
&lt;/h4&gt;

&lt;p&gt;By organizing Figma variables into these four collections, the result is a system that's both robust and scalable. This structured approach enhances collaboration, scalability, and flexibility, enabling teams to build better user interfaces with greater efficiency.&lt;/p&gt;

&lt;h5&gt;
  
  
  Variables in Figma
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
├── Design Tokens
│   ├── Border
│   │   ├── Width
│   │   └── Radius
│   ├── Colors
│   │   ├── Background
│   │   ├── Base
│   │   ├── Border
│   │   └── ...
│   ├── Shadow
│   ├── Sizing
│   ├── Spacing
│   └── ...
├── Global Primitives
│   ├── Colors
│   │   ├── Default theme/
│   │   ├── High contrast theme/
│   │   └── ...
│   └── Typography
│       ├── Font family
│       ├── Font size
│       ├── Font weight
│       └── Line height
├── Language
│   ├── Global areas
│   │   ├── Header
│   │   ├── Sidebar
│   │   ├── Footer
│   │   └── ...
│   ├── Patterns
│   ├── Abstractions
│   └── ...
└── Private Primitives/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Organization alone isn't sufficient. Figma variables should stay in sync with the codebase, creating a single source of truth. This can be achieved through automated scripts and &lt;a href="https://github.com/planetabhi/figma-mcp-server" rel="noopener noreferrer"&gt;Figma local MCP server&lt;/a&gt; that bridge the gap between design and development.&lt;/p&gt;

&lt;h5&gt;
  
  
  In the codebase
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nx"&gt;themes&lt;/span&gt;
    &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;definitions&lt;/span&gt;
    &lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;dark&lt;/span&gt;
    &lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;light&lt;/span&gt;
    &lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nx"&gt;tests&lt;/span&gt;
    &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;design&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;   &lt;span class="c1"&gt;// generated from `Design Tokens` Figma collection&lt;/span&gt;
    &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;          &lt;span class="c1"&gt;// generated from `Global Primitives` Figma collection&lt;/span&gt;
    &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nx"&gt;utils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This structure provides the flexibility needed for complex projects while maintaining the rigor required for enterprise-level design systems.&lt;/p&gt;

</description>
      <category>figma</category>
      <category>tokens</category>
      <category>designsystem</category>
    </item>
    <item>
      <title>Invite an AI to Look Upon Your Designs</title>
      <dc:creator>abhi</dc:creator>
      <pubDate>Sat, 22 Aug 2026 06:55:16 +0000</pubDate>
      <link>https://dev.to/planetabhi_/invite-an-ai-to-look-upon-your-figma-designs-4930</link>
      <guid>https://dev.to/planetabhi_/invite-an-ai-to-look-upon-your-figma-designs-4930</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Who will look upon your work before the world does? Let it be an AI, quietly, inside Figma, reading each frame and pinning its honest thoughts to the very place they belong.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let Claude, GPT, Gemini, or any AI model wander through your designs, offer its quiet counsel, and leave its thoughts pinned upon the Figma canvas.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A gentle word before you begin.&lt;/strong&gt; This hands an AI model real power over your Figma files. It may leave comments upon the canvas, and take them away again. So begin where nothing is precious, on a blank, new, or draft file, where a mistake carries no sorrow. Try a few reviews, watch how the assistant speaks, and learn the prompts that answer you kindly. When trust has grown between you, carry it to the files that matter.&lt;/p&gt;




&lt;p&gt;The purely local &lt;a href="https://github.com/planetabhi/figma-mcp-server" rel="noopener noreferrer"&gt;Figma MCP Server&lt;/a&gt; with full Figma REST API coverage opens a door through which any AI assistant that speaks MCP may come and look upon your designs. It keeps company with clients such as GitHub Copilot in VS Code, Claude Desktop, and Cursor, and with models such as Claude, GPT, and Gemini. The assistant reads your work, offers its honest reflection, and pins its comments to the canvas. And because it runs upon your own machine, your Figma token never wanders from your hands.&lt;/p&gt;

&lt;h3&gt;
  
  
  What you can do with it
&lt;/h3&gt;

&lt;p&gt;Speak to your assistant in plain, unhurried language, and it can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Look upon your frames. It renders a screenshot of any screen, then reads the layers and components beneath when it needs to see closer.&lt;/li&gt;
&lt;li&gt;Weigh the design. It's hierarchy, spacing, contrast, alignment, and consistency.&lt;/li&gt;
&lt;li&gt;Hold the work against your design system, it's tokens and components.&lt;/li&gt;
&lt;li&gt;Leave its feedback in Figma, pinned to the very frame it speaks of.&lt;/li&gt;
&lt;li&gt;Move through the comments already there. It reads every open thread, gathers them, sets them in order of urgency, and turns them into a clear list of things to do.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Before you start
&lt;/h3&gt;

&lt;p&gt;Three small things must be gathered first:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;An AI client that supports MCP, such as GitHub Copilot in VS Code, Claude Desktop, or Cursor.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bun.sh/" rel="noopener noreferrer"&gt;Bun.sh&lt;/a&gt; installed. It runs the server.&lt;/li&gt;
&lt;li&gt;A Figma personal access token, which you create in Step 1.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With these three in hand, the path ahead is short.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 01: Get your Figma token
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;In Figma, go to &lt;strong&gt;Settings → Security → Personal access tokens&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Name it, and check all items under the &lt;strong&gt;Users&lt;/strong&gt; and &lt;strong&gt;Files&lt;/strong&gt; scopes. Files lets the assistant read your designs and post comments; Users lets it identify who is commenting. These are the minimum scopes it needs.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Generate token&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Copy it now, for Figma will show it only this once. Guard it as you would a password.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 02: Connect the server to your AI client
&lt;/h3&gt;

&lt;p&gt;Point your client toward the server but once, and offer it your token. Most clients will keep it safe in your system's secret storage, never lying bare in a plain file.&lt;/p&gt;

&lt;h4&gt;
  
  
  VS Code (GitHub Copilot)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Open the folder you want to work in. Any project root or test folder works.&lt;/li&gt;
&lt;li&gt;Inside it, create a folder named &lt;code&gt;.vscode&lt;/code&gt;, and inside that a file named &lt;code&gt;mcp.json&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Paste this into &lt;code&gt;mcp.json&lt;/code&gt; and save:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"inputs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"promptString"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"figma-api-key"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Figma API Key"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"servers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"figma"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bunx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"figma-mcp-server"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"FIGMA_API_KEY"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"${input:figma-api-key}"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Open the &lt;strong&gt;Command Palette&lt;/strong&gt; (&lt;code&gt;Cmd+Shift+P&lt;/code&gt; on Mac, &lt;code&gt;Ctrl+Shift+P&lt;/code&gt; on Windows) and run &lt;strong&gt;MCP: List Servers&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Pick &lt;code&gt;figma&lt;/code&gt;, then &lt;strong&gt;Start Server&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Paste the token from Step 01 when prompted. VS Code stores it in your OS secret storage: macOS Keychain, Credential Manager on Windows, or your keyring on Linux.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Claude Desktop, Cursor, and other clients
&lt;/h4&gt;

&lt;p&gt;Add the same server to your client's MCP config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"figma"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bunx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"figma-mcp-server"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"FIGMA_API_KEY"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"your_figma_api_key_here"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This block keeps the token in plain sight, so keep this file away from version control. Restart the client, then see that the Figma server has joined you in its MCP settings. Seek the mode that lets the assistant reach for outside tools; each client calls it by a different name, be it agent, tools, or MCP.&lt;/p&gt;

&lt;p&gt;Once the bond is made, the server brings forth the Figma tools. In VS Code, the Output console shows the connection alive and names the tools it may use, such as reading frames and leaving comments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;[info] Connection state: Starting
[info] Connection state: Running
[info] Discovered Figma tools
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 03: Grab a frame link
&lt;/h3&gt;

&lt;p&gt;To point the assistant at a single frame is swifter and more sure than to hand it the whole file. It attends to what you care for, rather than wandering through everything.&lt;/p&gt;

&lt;p&gt;In Figma, right-click the frame and choose &lt;strong&gt;Copy/Paste as → Copy link to selection&lt;/strong&gt;. You get a URL like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;figma.com/design/AbCdEf123456/Project?node-id=42-1337
                 ^^^^^^^^^^^^                 ^^^^^^^
                 file key                     frame
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Paste that link into your prompt just as it is. The file key tells the assistant which file to enter, and the node-id which frame to seek. To review several frames, offer a link for each, or point the assistant at a whole page and ask it to consider every top-level frame upon it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A word on cost and limits.&lt;/strong&gt; Every review spends model tokens, and the Figma API sets a limit on how often you may ask within a span of time. To review whole files, or many frames at once, wears through both the sooner. Begin with a few frames, and should you meet a limit, wait a little while and ask for less.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 04: Ask for a review
&lt;/h3&gt;

&lt;p&gt;First, give the assistant its bearings. Tell it the purpose of the screen, the device it is made for, and the people who will meet it. A frame seen alone, without its story, will be faulted for what was meant all along, its states, its breakpoints.&lt;/p&gt;

&lt;p&gt;Open your AI client and let it reach for tools or act as an agent, so it may call upon the Figma tools. In VS Code, open Copilot Chat and turn it to Agent mode. Then paste the frame link and tell it what to look for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Review this frame: https://www.figma.com/design/AbCdEf123456/Project?node-id=42-1337. Check spacing consistency, color contrast, and visual hierarchy. List the top issues before posting anything.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The assistant enters the frame, considers it, and returns a gathered list. It will name the very components and layers, not merely "the blue button somewhere."&lt;/p&gt;

&lt;p&gt;Should it say it cannot reach the frame, look again at the token scopes from Step 1, and be sure the link leads to a frame you yourself may open.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 05: Post it to the canvas
&lt;/h3&gt;

&lt;p&gt;When the notes sit right with you, send them home to Figma:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Post each issue as a comment, pinned to the frame it's about. Suffix every comment with "(Added by Claude)" so we know which model left it.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ask the assistant to sign what it leaves (say, "Added by Claude" or "Added by Gemini"). This matters most when many hands, or many models, tend the same file: every word stays known by its author. Choose a prefix or a suffix, and hold to it.&lt;/p&gt;

&lt;p&gt;The comments settle where the trouble lies. Your companions open Figma and find the review pinned upon the work itself, marked with the name of whoever wrote it.&lt;/p&gt;




&lt;h4&gt;
  
  
  When not to use it
&lt;/h4&gt;

&lt;p&gt;The assistant looks upon a rendered image and the data of the layers. It cannot feel motion, nor follow a prototype's flow, nor know the touch of real use; and it does not stand in place of testing with people, or of true research.&lt;/p&gt;

&lt;h4&gt;
  
  
  Why it's worth it
&lt;/h4&gt;

&lt;p&gt;Set it up but once, and a design review becomes a single sentence spoken to your assistant. The reviews keep their steady voice across many designers, they catch the small faults of spacing and contrast before the handoff, and they lighten the rounds of review. The same quiet helper can later read your design tokens, weigh your components, or trace a file's history, all through plain conversation.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;When your work is finished.&lt;/strong&gt; Take the key from your config, then remove the token in Figma under &lt;strong&gt;Settings → Security&lt;/strong&gt;. And so it is fully revoked.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://github.com/planetabhi/figma-mcp-server" rel="noopener noreferrer"&gt;Explore Figma MCP Server ↗&lt;/a&gt;&lt;/p&gt;

</description>
      <category>figma</category>
      <category>mcp</category>
      <category>design</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Design Diff, a Visual Feedback Loop for Coding Agents</title>
      <dc:creator>abhi</dc:creator>
      <pubDate>Sat, 22 Aug 2026 06:33:18 +0000</pubDate>
      <link>https://dev.to/planetabhi_/design-diff-a-visual-feedback-loop-for-coding-agents-17c9</link>
      <guid>https://dev.to/planetabhi_/design-diff-a-visual-feedback-loop-for-coding-agents-17c9</guid>
      <description>&lt;p&gt;A coding agent can write the markup, wire the styles, and ship a page in seconds. It can even look at that page. What it cannot do is measure it against the design, pixel for pixel. Glancing at a screenshot, it will not catch that the heading sits a few pixels too low, that the color has cooled by a shade, that the font quietly failed to load. It builds by eye, and the page drifts from the design with nothing exact to catch it.&lt;/p&gt;

&lt;p&gt;Design Diff gives it that measure. It lays the design over a screenshot of the live page at the exact same pixel size, then returns one number for what matched and the box around what did not. Call it, read the score, fix what the box points at, call it again. That loop runs until the page matches the design, and no one has to be watching.&lt;/p&gt;

&lt;p&gt;It is built agent-first, a command and a library that speak plain JSON, so any agent or script can drive it. The same signal serves people just as well, from developers closing a handoff to design engineers building a system to designers who want proof rather than a promise.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install
&lt;/h3&gt;

&lt;p&gt;It runs on &lt;a href="https://bun.sh" rel="noopener noreferrer"&gt;Bun&lt;/a&gt; and needs no global install, since &lt;code&gt;bunx&lt;/code&gt; fetches and runs it in one step. The first run downloads Chromium once. Every run after that is instant.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quickstart
&lt;/h2&gt;

&lt;p&gt;You have a running page and a PNG of the design. Point the tool at both and let them stand side by side.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bunx design-diff http://localhost:3000 &lt;span class="nt"&gt;--png&lt;/span&gt; home.png &lt;span class="nt"&gt;--open&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It answers in three ways. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A short report in the terminal. &lt;/li&gt;
&lt;li&gt;An interactive page in the browser where you lay one image over the other. &lt;/li&gt;
&lt;li&gt;And a folder of artifacts, written to &lt;code&gt;.design-diff&lt;/code&gt; by default, one timestamped run at a time, or wherever you point &lt;code&gt;--out&lt;/code&gt;. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;An agent skips the browser and reads the JSON, but the loop is the same, question to answer and back again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DESIGN DIFF
────────────────────────

Visual match    92.4%

Diff bounds
x=0..1440
y=812..980
page coverage=17.1%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ninety-two percent of the pixels line up. The rest is gathered near the foot of the frame, between the lines it names. You know where to look before you even open the report, and knowing where the trouble sits is half the fix.&lt;/p&gt;

&lt;p&gt;Every flag appears in context throughout this piece, and &lt;code&gt;design-diff --help&lt;/code&gt; lists them all.&lt;/p&gt;

&lt;h2&gt;
  
  
  How measurement works
&lt;/h2&gt;

&lt;p&gt;The loop only works if the number can be trusted, and three things make it so. A shared frame of reference, a page caught at the right moment, and a score you can read at a glance.&lt;/p&gt;

&lt;h3&gt;
  
  
  One frame of reference
&lt;/h3&gt;

&lt;p&gt;Measurement needs a common ground. Two things can be compared only on the same scale, so the design sets the size and the page is captured at that exact width and height. Laid on one measure, they line up point for point, with no room for a false reading.&lt;/p&gt;

&lt;h3&gt;
  
  
  Caught at the right moment
&lt;/h3&gt;

&lt;p&gt;Design Diff does not capture in haste. It waits for the fonts to load, for the images to arrive, for the network to settle, and it turns off animation and the blinking caret. Most false diffs come from catching a page mid-render. If the fonts still are not ready, it says so in the output instead of handing you a bad score in silence. For an agent looping toward a match, this is what keeps the signal honest. The number moves only when the page truly changed, not when it was caught too early.&lt;/p&gt;

&lt;h3&gt;
  
  
  A score you can read
&lt;/h3&gt;

&lt;p&gt;Every run prints three measures and writes them to metrics.json in the run folder.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;What it tells you&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Visual match&lt;/td&gt;
&lt;td&gt;How many pixels line up. The headline score.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Diff bounds&lt;/td&gt;
&lt;td&gt;The box enclosing every changed pixel, in CSS pixels. A tight box means one component is off. A box spanning the page points at a layout, viewport, or font problem.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Page coverage&lt;/td&gt;
&lt;td&gt;How much of the page that box spans. Tells a local issue from a global one.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The shape of the diff is the diagnosis. A small box on a button is a mistaken color. A box that spans the page is a font that never loaded or a viewport that does not match. Read the shape, not the number alone. The number says something is wrong, the shape says what. For an agent this is a ready-made reward signal. A match percentage to climb, and the diff bounds to point the next edit at.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://localhost:3000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"matchPercent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;92.4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"diffPercent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;7.6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"changedPixels"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;107251&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"totalPixels"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1411200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"coveragePercent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;17.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"diffBounds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"x"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"y"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;812&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"width"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1440&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"height"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;168&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"readiness"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"fontsReady"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"imagesComplete"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"paths"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"design"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".design-diff/2026-08-16T14-22-09-482Z/design.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"page"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".design-diff/2026-08-16T14-22-09-482Z/dom.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"heatmap"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".design-diff/2026-08-16T14-22-09-482Z/heatmap.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"overlay"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".design-diff/2026-08-16T14-22-09-482Z/overlay.html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"metrics"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".design-diff/2026-08-16T14-22-09-482Z/metrics.json"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Working with real designs and real pages
&lt;/h2&gt;

&lt;p&gt;Real work is messier than a clean mockup against a static page. Design Diff meets it where it is, whether the source of truth lives in Figma, the page carries content that was always going to change, loads late, sits behind a login, or exists only as a screenshot on disk.&lt;/p&gt;

&lt;h3&gt;
  
  
  Straight from Figma
&lt;/h3&gt;

&lt;p&gt;No manual export. Give it a file key and a frame id and it fetches the frame size and PNG straight from the Figma API.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;DESIGN_DIFF_FIGMA_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;figd_your_token
bunx design-diff http://localhost:3000 &lt;span class="nt"&gt;--file&lt;/span&gt; abc123 &lt;span class="nt"&gt;--frame&lt;/span&gt; 10-2 &lt;span class="nt"&gt;--open&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The frame id is the one in the frame URL, like 10-2. A read-only token is enough. This is the closest thing to a live handoff. The designer moves a component, you run it again, you see the drift at once. An agent can aim straight at a Figma frame as its target and never touch a local file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mask what is meant to change
&lt;/h3&gt;

&lt;p&gt;Real pages carry things that were always going to change. An avatar, a timestamp, a live counter, a random hero image. Counting those as faults is a quarrel with the nature of the page. Mask them so they never count.&lt;/p&gt;

&lt;p&gt;By a fixed rectangle, in CSS pixels, repeated as often as you like.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bunx design-diff http://localhost:3000 &lt;span class="nt"&gt;--png&lt;/span&gt; home.png &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--ignore&lt;/span&gt; 24,24,48,48 &lt;span class="nt"&gt;--ignore&lt;/span&gt; 0,900,1440,120
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or by CSS selector, which is wiser when the region moves or resizes between runs. Every element that matches is masked by its own live bounding box.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bunx design-diff http://localhost:3000 &lt;span class="nt"&gt;--png&lt;/span&gt; home.png &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--ignore-selector&lt;/span&gt; &lt;span class="s2"&gt;"[data-dynamic], time, .avatar"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Masking by selector is what turns a flaky score into a steady one. It silences the noise that was never a real difference. For an agent iterating toward 100, a stable score is everything, since it needs the number to reflect its own edits and nothing else.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wait for readiness
&lt;/h3&gt;

&lt;p&gt;Some pages wake late, or fetch their hero after a round-trip. Hold the capture until a chosen selector appears. If the selector never shows within fifteen seconds, it fails loudly rather than quietly handing you a bad screenshot.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bunx design-diff http://localhost:3000 &lt;span class="nt"&gt;--png&lt;/span&gt; home.png &lt;span class="nt"&gt;--wait-for&lt;/span&gt; &lt;span class="s2"&gt;".hero-loaded"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pages behind a login
&lt;/h3&gt;

&lt;p&gt;Save a session once with Playwright, then hand it over. No credentials ever touch the tool itself. The saved file holds only cookies and local storage. When it expires, make it again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bunx playwright codegen &lt;span class="nt"&gt;--save-storage&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;auth.json https://your-app/login
&lt;span class="c"&gt;# log in in the window, then close it&lt;/span&gt;
bunx design-diff https://your-app/dashboard &lt;span class="nt"&gt;--png&lt;/span&gt; dash.png &lt;span class="nt"&gt;--auth&lt;/span&gt; auth.json &lt;span class="nt"&gt;--open&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  From two images
&lt;/h3&gt;

&lt;p&gt;Sometimes both images already sit on disk and there is no page to visit. Let the browser sleep and lay the two side by side.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bunx design-diff &lt;span class="nt"&gt;--actual&lt;/span&gt; screenshot.png &lt;span class="nt"&gt;--png&lt;/span&gt; design.png &lt;span class="nt"&gt;--json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the fast path an agent uses in a tight loop. It has already captured the page by its own means, so Design Diff only diffs the two files and launches no browser at all. It works offline, in tests, and in sandboxes. With no live page there is no waiting and no selector masking, and scale is ignored since there is no screen to answer to, so the bounds come back in plain image pixels. Everything else is the same.&lt;/p&gt;

&lt;h2&gt;
  
  
  Outputs for agents and humans
&lt;/h2&gt;

&lt;p&gt;The same run answers two very different readers. A machine wants a number and a box. A person wants to see it. Design Diff gives each what it needs from one pass.&lt;/p&gt;

&lt;h3&gt;
  
  
  The interactive report
&lt;/h3&gt;

&lt;p&gt;The HTML report is not a static picture. It is a tool for holding two images as one.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A reveal slider wipes between the design and the page. Drag the handle to move the seam across the frame.&lt;/li&gt;
&lt;li&gt;An opacity slider fades the design onto the page so a small shift shows itself.&lt;/li&gt;
&lt;li&gt;A heatmap lights up every changed pixel so nothing hides.&lt;/li&gt;
&lt;li&gt;A bounds box outlines the changed region so your eye goes straight to it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is retina-safe. When your export is drawn at twice the size, pass &lt;code&gt;--scale 2&lt;/code&gt; and it keeps the measure honest.&lt;/p&gt;

&lt;h3&gt;
  
  
  A shareable image
&lt;/h3&gt;

&lt;p&gt;The HTML report is good in front of you, but it cannot travel into a pull request comment. Pass &lt;code&gt;--annotate&lt;/code&gt; and it draws the diff box onto the page screenshot itself.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bunx design-diff http://localhost:3000 &lt;span class="nt"&gt;--png&lt;/span&gt; home.png &lt;span class="nt"&gt;--annotate&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No browser needed to view it. Drop it into a review, a Slack thread, a CI artifact. Nothing is written when there is no diff, so an annotated file always means there is something to look at.&lt;/p&gt;

&lt;h3&gt;
  
  
  JSON and the CI gate
&lt;/h3&gt;

&lt;p&gt;Agents and pipelines want the numbers, not the page. &lt;code&gt;--json&lt;/code&gt; prints only the metrics object to stdout, so a script or an agent reads it with no parsing. &lt;code&gt;--fail-under&lt;/code&gt; sets the bar. If the visual match falls below it, the process exits 1 and the build goes red.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bunx design-diff http://localhost:3000 &lt;span class="nt"&gt;--png&lt;/span&gt; home.png &lt;span class="nt"&gt;--json&lt;/span&gt; &lt;span class="nt"&gt;--fail-under&lt;/span&gt; 98
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the agent contract. Read the JSON, act on matchPercent and diffBounds, run again, and stop when the score is high enough.&lt;/p&gt;

&lt;p&gt;One thing to keep straight. &lt;code&gt;--fail-under&lt;/code&gt; is a percentage measured against the visual match. It is not &lt;code&gt;--threshold&lt;/code&gt;, which is the per-pixel color sensitivity. One keeps the gate, the other tunes how strict each pixel comparison is. When you weigh many pages, or the same page again and again, pass &lt;code&gt;--no-overlay&lt;/code&gt; to skip the HTML and keep only the metrics.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using it programmatically
&lt;/h2&gt;

&lt;p&gt;The CLI is a convenience. The loop really lives in the library, one function you can call as fast as you can edit.&lt;/p&gt;

&lt;h3&gt;
  
  
  The function
&lt;/h3&gt;

&lt;p&gt;The CLI is a thin wrapper over one function that returns everything it computed, along with the path to every artifact.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;designDiff&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;design-diff&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;designDiff&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:3000&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// or actual: "screenshot.png" for image mode&lt;/span&gt;
  &lt;span class="na"&gt;design&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;home.png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;           &lt;span class="c1"&gt;// or { fileKey, frameId }&lt;/span&gt;
  &lt;span class="na"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;ignore&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[data-dynamic]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;48&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;48&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
  &lt;span class="na"&gt;waitFor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.hero-loaded&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;matchPercent&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;98&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You get back matchPercent, diffPercent, changedPixels, totalPixels, coveragePercent, diffBounds, the readiness signals, and the paths to the design, page, heatmap, overlay, annotated image, and metrics. The same object it writes to metrics.json, with nothing useful thrown away. An agent reads matchPercent to know how close it is and diffBounds to know where to edit next, then calls the function again.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reuse the browser
&lt;/h3&gt;

&lt;p&gt;Checking a whole app? Launch Chromium once and pass it into every call. It is a real speed win when a suite or an agent runs the tool over and over.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;designDiff&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;launchBrowser&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;design-diff&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;launchBrowser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;pages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;designDiff&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;design&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;design&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;matchPercent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The honest limits
&lt;/h2&gt;

&lt;p&gt;Design Diff compares pixels, not perception, so read the score as a guide and not a verdict. Anti-aliasing and font rendering vary across machines, so a real page against a design export settles just short of a clean hundred even when it looks right. Choose a threshold rather than chase the last point. The design and the page must be the same size, since a large mismatch is a hard error by design. And a high score is necessary but not sufficient, because a shifted component can still hide inside it. The diff box and a human eye stay the final check.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bottom line
&lt;/h2&gt;

&lt;p&gt;Visual regression tools compare an app to its own past, which catches change no one intended. Design Diff compares the app to the design, which catches the thing that matters at handoff, the gap between what was drawn and what was built.&lt;/p&gt;

&lt;p&gt;It is small and asks no allegiance to a service. Most tools in this space are built for a person to look at a report. This one is built to be read by a machine and acted on in a loop, so an agent can drive the page toward the design on its own, and it still hands a person a picture that ends the argument. Point it at your page, read the box, and close the gap.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/planetabhi/design-diff" rel="noopener noreferrer"&gt;Explore the Design Diff ↗&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>design</category>
      <category>tooling</category>
      <category>agents</category>
    </item>
  </channel>
</rss>
