{"id":"l-fig-3-2","courseId":"figma-ui-design","lang":"en","kind":"lesson","summary":"Combine related components into a variant set with clear property names, then add boolean, text and instance swap properties so Steady’s button and habit row stay small and easy to use.","html":"<p>Steady’s primary button looks simple until you list what it needs: a primary, secondary and ghost type; default, pressed and disabled states; medium and small sizes; an optional icon; and any label. Build each of those as a separate component and you have dozens of near-copies, and a designer choosing between <code dir=\"ltr\">Button Primary Small Disabled With Icon</code> and its neighbours.</p>\n<p>Variants and component properties turn that pile into one component with a few clear switches.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">Variants: one set, many versions</h2>\n<p>A <strong>component set</strong> holds related components called <strong>variants</strong>. Each variant is described by <strong>variant properties</strong>, written as <code dir=\"ltr\">Property=Value</code> pairs. For Steady’s button:</p>\n<ul>\n<li><code dir=\"ltr\">Type</code>: Primary, Secondary, Ghost</li>\n<li><code dir=\"ltr\">State</code>: Default, Pressed, Disabled</li>\n<li><code dir=\"ltr\">Size</code>: Medium, Small</li>\n</ul>\n<p>You create variants either by selecting several existing components and clicking <strong>Combine as variants</strong>, or by selecting a component and adding a variant from the right sidebar, then renaming the properties and values. Figma treats the variant at the top-left of the set as the default.</p>\n<p>On an instance, people no longer hunt through assets. They place one Button and pick values from dropdowns: Type Secondary, State Disabled. The names you choose here are the names developers will see in Dev Mode, so make them match code where you can. If the code says <code dir=\"ltr\">variant=&quot;secondary&quot;</code>, consider calling your property <code dir=\"ltr\">Variant</code> rather than <code dir=\"ltr\">Type</code>, and agree on it with engineering early.</p>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 680 260\" role=\"img\" aria-labelledby=\"l-fig-3-2-t1\"><defs><marker id=\"arrow-l-fig-3-2-1\" viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto-start-reverse\"><path d=\"M0 0 L10 5 L0 10 z\" class=\"d-arrowhead\"/></marker></defs>\n  <title id=\"l-fig-3-2-t1\">A three-by-three grid of button variants. Columns are the states Default, Pressed and Disabled; rows are the types Primary, Secondary and Ghost. A dashed border surrounds the whole set.</title>\n  <rect class=\"d-box d-dashed\" x=\"110\" y=\"30\" width=\"560\" height=\"220\" rx=\"12\"/>\n  <text class=\"d-label-muted\" x=\"210\" y=\"22\" text-anchor=\"middle\">Default</text>\n  <text class=\"d-label-muted\" x=\"390\" y=\"22\" text-anchor=\"middle\">Pressed</text>\n  <text class=\"d-label-muted\" x=\"570\" y=\"22\" text-anchor=\"middle\">Disabled</text>\n  <text class=\"d-label-muted\" x=\"100\" y=\"80\" text-anchor=\"end\">Primary</text>\n  <text class=\"d-label-muted\" x=\"100\" y=\"150\" text-anchor=\"end\">Secondary</text>\n  <text class=\"d-label-muted\" x=\"100\" y=\"220\" text-anchor=\"end\">Ghost</text>\n  <rect class=\"d-box-primary\" x=\"140\" y=\"56\" width=\"140\" height=\"40\" rx=\"10\"/>\n  <text class=\"d-label-strong\" x=\"210\" y=\"81\" text-anchor=\"middle\">Save habit</text>\n  <rect class=\"d-box-primary\" x=\"320\" y=\"56\" width=\"140\" height=\"40\" rx=\"10\"/>\n  <text class=\"d-label-strong\" x=\"390\" y=\"81\" text-anchor=\"middle\">Save habit</text>\n  <rect class=\"d-box\" x=\"500\" y=\"56\" width=\"140\" height=\"40\" rx=\"10\"/>\n  <text class=\"d-label-muted\" x=\"570\" y=\"81\" text-anchor=\"middle\">Save habit</text>\n  <rect class=\"d-box-accent\" x=\"140\" y=\"126\" width=\"140\" height=\"40\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"210\" y=\"151\" text-anchor=\"middle\">Save habit</text>\n  <rect class=\"d-box-accent\" x=\"320\" y=\"126\" width=\"140\" height=\"40\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"390\" y=\"151\" text-anchor=\"middle\">Save habit</text>\n  <rect class=\"d-box\" x=\"500\" y=\"126\" width=\"140\" height=\"40\" rx=\"10\"/>\n  <text class=\"d-label-muted\" x=\"570\" y=\"151\" text-anchor=\"middle\">Save habit</text>\n  <text class=\"d-label\" x=\"210\" y=\"221\" text-anchor=\"middle\">Save habit</text>\n  <rect class=\"d-box\" x=\"320\" y=\"196\" width=\"140\" height=\"40\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"390\" y=\"221\" text-anchor=\"middle\">Save habit</text>\n  <text class=\"d-label-muted\" x=\"570\" y=\"221\" text-anchor=\"middle\">Save habit</text>\n</svg><figcaption>Steady’s button component set: every Type and State combination for one size</figcaption></figure>\n<h2 id=\"sec-2\" tabindex=\"-1\">Component properties: switches without multiplication</h2>\n<p>Variant properties multiply. Three types × three states × two sizes is 18 variants. Add a two-value <code dir=\"ltr\">Icon</code> variant property and it becomes 36, every one of which has to be built and kept in sync. This is called variant explosion, and it is the most common way design systems become unmaintainable.</p>\n<p>Component properties handle the differences that do not need a separately designed version:</p>\n<ul>\n<li><strong>Boolean</strong>: shows or hides a layer. <code dir=\"ltr\">Show icon</code> true or false. One layer, no new variants.</li>\n<li><strong>Text</strong>: exposes a text layer’s content. <code dir=\"ltr\">Label</code> = “Save habit”. People edit it from the properties panel without hunting for the layer.</li>\n<li><strong>Instance swap</strong>: chooses which component fills a nested instance. <code dir=\"ltr\">Icon</code> = Plus, Check or Bell. You can set <strong>preferred values</strong>, a short list of sensible icons, so nobody puts a trash can on the Save button.</li>\n<li><strong>Slot</strong>: at the time of writing, a newer property type that marks an area where instances can hold their own content, such as the body of a bottom sheet, while everything around it stays linked.</li>\n</ul>\n<p>You add these on the main component: select the layer, then use the property option next to its visibility (boolean), its text content (text) or its nested instance (instance swap) in the right sidebar, or create one from the properties section and connect it to a layer.</p>\n<aside class=\"callout callout-tip\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5\"/><path d=\"M9 18h6\"/><path d=\"M10 22h4\"/></svg><span><span class=\"callout-kind\">Tip</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>The deciding question</span></p>\n<p>Ask: <em>does this difference restyle several layers together?</em> A pressed button changes fill, label color and maybe a shadow at once, so State is a variant. Showing an icon changes one layer’s visibility, so it is a boolean. Which icon is an instance swap. What the label says is text.</p>\n</aside>\n<h2 id=\"sec-3\" tabindex=\"-1\">Build order matters</h2>\n<p>Add text, boolean and instance swap properties to the first component <strong>before</strong> you add more variants. New variants created from it inherit those property connections. If you build 18 variants first, you will connect the label layer 18 times.</p>\n<p>The Habit row gets the same treatment:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"text\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">Text</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code>Habit row (component set)\n  State      variant        To do | Done | Missed\n  Name       text           &quot;Read 20 pages&quot;\n  Show streak boolean       true\n  Icon       instance swap  Habit icon/Book  (preferred: Book, Water, Run, Sleep)</code></pre></div>\n<p>Three variants instead of dozens, because Done and Missed restyle several layers (check fill, name color, a strikethrough) while everything else is a property.</p>\n<p>When a component nests other components, people would normally have to dig into layers to reach the inner ones’ properties. At the time of writing, a component author can <strong>expose nested instances</strong>, so the Check button’s own properties appear alongside the Habit row’s in the panel.</p>\n<aside class=\"callout callout-mistake\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\"/><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/></svg><span><span class=\"callout-kind\">Common mistake</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>Naming values by appearance</span></p>\n<p><code dir=\"ltr\">State=Teal</code> and <code dir=\"ltr\">State=Grey</code> make sense today and lie tomorrow when the brand color changes. Name values by meaning, <code dir=\"ltr\">Done</code> and <code dir=\"ltr\">To do</code>, so the names survive a redesign and match what the code calls them.</p>\n</aside>\n<p>Your kit now has a handful of flexible components. Their colors and numbers, though, are still typed in by hand. Next you move those values into styles and variables.</p>\n","toc":[{"id":"sec-1","text":"Variants: one set, many versions","level":2},{"id":"sec-2","text":"Component properties: switches without multiplication","level":2},{"id":"sec-3","text":"Build order matters","level":2}],"takeaways":["Variants group versions of a component in one component set, described by properties such as Type and State.","Use variants for differences that restyle several layers at once, such as states, types and sizes.","Use a boolean property for show or hide, a text property for editable labels and an instance swap property for choosing a nested component.","Every variant property multiplies the variant count, while boolean, text and instance swap properties do not.","Slots, at the time of writing, mark areas of a component where instances can hold free content without detaching."],"further":[{"title":"Create and use variants","url":"https://help.figma.com/hc/en-us/articles/360056440594-Create-and-use-variants"},{"title":"Explore component properties","url":"https://help.figma.com/hc/en-us/articles/5579474826519-Explore-component-properties"},{"title":"The difference between slots, instance swaps, and variants","url":"https://help.figma.com/hc/en-us/articles/38741465279895-The-difference-between-slots-instance-swaps-and-variants"}],"quiz":[{"q":"<p>Steady’s button has Type (Primary, Secondary, Ghost), State (Default, Pressed, Disabled) and Size (Medium, Small) as variant properties. A teammate wants “with icon” and “without icon” as another variant property. How many variants would that create, and what is the alternative?</p>","options":[{"text":"36 variants. A boolean property for the icon keeps it at 18.","why":"Correct. 3 × 3 × 2 = 18, and a two-value variant doubles it. A boolean toggles the icon layer without new variants."},{"text":"20 variants. There is no alternative.","why":"Variant properties multiply, they do not add. And booleans exist precisely for show or hide."},{"text":"18 variants. Figma merges duplicate layouts automatically.","why":"Figma never merges variants. Each combination is a separate variant you have to maintain."},{"text":"36 variants. An instance swap property keeps it at 18.","why":"The count is right, but instance swap chooses which icon, not whether it shows. Visibility is a boolean’s job."}],"answer":0},{"q":"<p>Which property type fits the habit name in the Habit row component?</p>","options":[{"text":"A variant property with one value per habit.","why":"Habit names are unlimited user content. Variants are for a small fixed set of designed versions."},{"text":"A boolean property.","why":"Booleans only show or hide a layer. They cannot hold text content."},{"text":"A text property, so the name can be edited from the properties panel or on the canvas.","why":"Correct. A text property exposes the string so people can change it without digging into layers."},{"text":"An instance swap property.","why":"Instance swap chooses a nested component. A name is plain text, not a component."}],"answer":2},{"q":"<p>When should a difference be a variant rather than a boolean?</p>","options":[{"text":"When it is used on more than three screens.","why":"Usage count does not decide the property type. Use the nature of the change."},{"text":"When the difference restyles several layers together, like a Pressed state changing fill, label color and shadow.","why":"Correct. Booleans only toggle one layer’s visibility. Coordinated visual changes need a designed variant."},{"text":"When the developer asks for an enum.","why":"Developer input is valuable, but the design reason is how many layers change and how."},{"text":"Always, because variants are newer than booleans.","why":"Variants are not newer, and defaulting to them causes variant explosion."}],"answer":1},{"q":"<p>The New habit sheet component must hold different content in different flows (a form, a confirmation, an icon grid). What fits best at the time of writing?</p>","options":[{"text":"One variant per possible content.","why":"Every new flow would need a new variant, and the set would grow without end."},{"text":"Detach the sheet for each use.","why":"Detached sheets miss every future fix to the sheet’s header, handle and padding."},{"text":"A boolean for every element that might appear.","why":"Booleans can only hide layers that already exist, so the sheet would carry every possible element."},{"text":"A slot for the sheet’s body, so each instance can hold its own content while the sheet chrome stays linked.","why":"Correct. Slots exist for exactly this, a flexible content area inside a linked component."}],"answer":3}],"exercise":{"kind":"order","items":["build-base","make-component","add-props","add-variants","style-variants","test-instance"],"title":"Build the button component in the right order","prompt":"<p>You are building Steady’s button: Type and State as variant properties, plus a text property for the label, a boolean to show the icon and an instance swap for which icon. Put the steps in the order that avoids redoing work.</p>\n","hints":["A component property can only be created on something that is already a component.","New variants copy the property connections of the component they were created from.","Base frame, component, properties, variants, styling, then test."],"explanation":"<p>Start with a well-built <strong>auto layout frame</strong> with named Icon and Label layers, because everything after inherits its structure. <strong>Make it a component</strong>, then add the <strong>text, boolean and instance swap properties</strong> while there is only one variant to connect them to. Next <strong>add variants</strong> and name the variant properties Type and State; each new variant inherits those property connections. Then <strong>restyle</strong> each variant for its type and state. Finally <strong>test an instance</strong> by switching every property, including long labels and the icon off, to prove nothing breaks before anyone else uses it.</p>\n","checkLabels":{},"itemText":{"build-base":"Build the default primary button as an auto layout frame with named Icon and Label layers.","make-component":"Turn the frame into a main component.","add-props":"Add a text property for Label, a boolean for Show icon and an instance swap for Icon.","add-variants":"Add variants and name the variant properties Type and State with their values.","style-variants":"Restyle each variant: secondary and ghost colors, pressed and disabled states.","test-instance":"Place an instance and switch every property, with a long label and the icon off, to check nothing breaks."}},"words":703,"readMinutes":3}