[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"verticals":3,"article-design-system-api-for-machines":32,"code:tsx:true:1ti6zlb":175,"code:tsx:true:1p9wb8e":176},[4,20],{"id":5,"slug":6,"name":7,"tagline":8,"description":9,"accentFrom":10,"accentTo":11,"icon":12,"defaultLocale":13,"locales":14,"features":16,"position":19},"019fe637-3d33-714b-b57f-23e163ffca0c","dev","Web Development","Build. Learn. Ship.","Practical courses, engineering-grade articles and open-source tools for people who ship.","violet-500","cyan-400","◇","en",[13,15],"fa",{"courses":17,"paths":17,"articles":17,"exams":18,"flashcards":18,"packages":17,"community":17,"certificates":17,"teams":17,"commerce":17},true,false,0,{"id":21,"slug":22,"name":23,"tagline":24,"description":25,"accentFrom":26,"accentTo":10,"icon":27,"defaultLocale":13,"locales":28,"features":30,"position":31},"019fe637-3dc2-754c-8657-0f175bfee7c6","lang","Languages","Learn a language the way you learn a codebase.","Structured paths, listening drills and spaced repetition that actually sticks.","amber-400","⌘",[13,15,29],"es",{"courses":17,"paths":17,"articles":18,"exams":18,"flashcards":18,"packages":18,"community":17,"certificates":18,"teams":18,"commerce":17},2,{"id":33,"slug":34,"title":35,"subtitle":36,"excerpt":37,"coverUrl":38,"locale":13,"readingMinutes":39,"publishedAt":40,"viewCount":41,"likeCount":19,"commentCount":19,"author":42,"vertical":47,"topic":48,"tags":51,"_count":63,"playground":36,"body":64,"bodyMd":166,"seo":167,"translationGroupId":169,"thread":170,"assessments":172,"translations":173,"quiz":36},"019fe65f-a25a-702f-b8a0-ae60efb61964","design-system-api-for-machines","Your Design System Is Now an API for Machines",null,"Component libraries used to be for humans. Now they're the rails an AI plans against — and that changes how you should build them.","\u002Fmedia\u002Fcovers\u002Fdesign-system-api-for-machines.png",3,"2026-06-26T07:29:19.042Z",4,{"id":43,"name":44,"username":45,"avatarUrl":36,"headline":46},"019fe637-3c25-7088-9034-39c9f15dc3c8","Parsa Jiravand","parsa","Frontend engineer · building bestpractic",{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":49,"name":50},"designsystems","Design Systems",[52,55,58,60],{"slug":53,"name":54,"color":36},"webdev","Webdev",{"slug":56,"name":57,"color":36},"frontend","Frontend",{"slug":49,"name":59,"color":36},"Designsystems",{"slug":61,"name":62,"color":36},"ai","Ai",{"assessments":19},{"blocks":65,"version":165},[66,70,73,78,81,84,90,93,97,100,103,106,109,116,119,125,128,131,134,139,142,145,148,151,154,157],{"id":67,"html":68,"type":69},"b1","\u003Cp>We&#39;ve always justified design systems with human reasons: consistency, faster onboarding, fewer one-off buttons. All true. But there&#39;s a new reason that quietly outweighs the rest — \u003Cstrong>your design system is becoming the API that machines build UI against.\u003C\u002Fstrong>\u003C\u002Fp>","paragraph",{"id":71,"html":72,"type":69},"b2","\u003Cp>When an AI assistant generates a screen, the quality of its output is bounded almost entirely by the vocabulary you give it. A loose, undocumented pile of components produces loose, inconsistent output. A tight, well-typed, well-named system produces output that looks like your team wrote it. The design system stops being documentation and becomes a \u003Cem>constraint surface\u003C\u002Fem>.\u003C\u002Fp>",{"id":74,"html":75,"text":76,"type":77,"level":31},"b3","What &quot;good rails&quot; actually look like","What \"good rails\" actually look like","heading",{"id":79,"html":80,"type":69},"b4","\u003Cp>If a model (or a junior dev, honestly — same requirements) is going to compose your components correctly without hand-holding, the system has to be legible \u003Cem>from the types and names alone.\u003C\u002Fem>\u003C\u002Fp>",{"id":82,"html":83,"type":69},"b5","\u003Cp>\u003Cstrong>Names that describe intent, not appearance.\u003C\u002Fstrong>\u003C\u002Fp>",{"id":85,"code":86,"type":87,"language":88,"highlight":89},"b6","\u002F\u002F Weak: the model has to guess when to use which\n\u003CBlueButton \u002F>  \u003CSmallButton \u002F>  \u003CRoundButton \u002F>\n\n\u002F\u002F Strong: intent is in the name, variants are props\n\u003CButton variant=\"primary\" size=\"sm\" \u002F>","code","tsx",[],{"id":91,"html":92,"type":69},"b7","\u003Cp>\u003Cstrong>Props that make illegal states unrepresentable.\u003C\u002Fstrong> If a component can be configured into a broken combination, something will eventually configure it that way. Push the constraints into the type system:\u003C\u002Fp>",{"id":94,"code":95,"type":87,"language":88,"highlight":96},"b8","\u002F\u002F A toast is either transient (auto-dismiss) OR action-required — never both.\ntype ToastProps =\n  | { kind: \"transient\"; durationMs: number }\n  | { kind: \"action\"; onAction: () => void; actionLabel: string };",[],{"id":98,"html":99,"type":69},"b9","\u003Cp>A union like this is worth more than a paragraph of docs, because it&#39;s \u003Cem>enforced\u003C\u002Fem>. Neither a human nor a model can pass an \u003Ccode>onAction\u003C\u002Fcode> to a transient toast.\u003C\u002Fp>",{"id":101,"html":102,"type":69},"b10","\u003Cp>\u003Cstrong>One obvious way to do each thing.\u003C\u002Fstrong> Three different ways to render a modal is three ways to get it subtly wrong. Every redundant path is a fork where generated code can drift from your conventions.\u003C\u002Fp>",{"id":104,"html":105,"text":105,"type":77,"level":31},"b11","The shift in where you spend effort",{"id":107,"html":108,"type":69},"b12","\u003Cp>Old priority order for a design system:\u003C\u002Fp>",{"id":110,"type":111,"items":112,"ordered":17},"b13","list",[113,114,115],"Visual polish","Documentation \u002F Storybook","Type safety (nice to have)",{"id":117,"html":118,"type":69},"b14","\u003Cp>New priority order:\u003C\u002Fp>",{"id":120,"type":111,"items":121,"ordered":17},"b15",[122,123,113,124],"\u003Cstrong>Type safety and clear contracts\u003C\u002Fstrong> — the machine-legible layer","Clear, intent-based naming","Docs (increasingly, the types \u003Cem>are\u003C\u002Fem> the docs)",{"id":126,"html":127,"type":69},"b16","\u003Cp>This isn&#39;t because polish stopped mattering — it&#39;s because polish is now the cheap part. What&#39;s expensive and high-leverage is the part that lets \u003Cem>everything built on top\u003C\u002Fem> be correct by default.\u003C\u002Fp>",{"id":129,"html":130,"text":130,"type":77,"level":31},"b17","A concrete test",{"id":132,"html":133,"type":69},"b18","\u003Cp>Want to know if your design system is ready for this world? Try this: hand an AI assistant \u003Cem>only\u003C\u002Fem> your component type definitions (no screenshots, no prose) and ask it to build a settings page. Then look at the output.\u003C\u002Fp>",{"id":135,"type":111,"items":136,"ordered":18},"b19",[137,138],"If it composed sensible, on-brand UI from the types alone — your contracts are doing their job.","If it invented props that don&#39;t exist, or reached for raw \u003Ccode>&lt;div&gt;\u003C\u002Fcode>s and inline styles — that&#39;s a map of exactly where your system is ambiguous or incomplete.",{"id":140,"html":141,"type":69},"b20","\u003Cp>That gap \u003Cem>is\u003C\u002Fem> your design-system backlog now. The components it had to fake are the ones missing a clear contract.\u003C\u002Fp>",{"id":143,"html":144,"text":144,"type":77,"level":31},"b21","The takeaway",{"id":146,"html":147,"type":69},"b22","\u003Cp>Build your design system as if its primary consumer can&#39;t see — because increasingly, one of its primary consumers can&#39;t. It reads types, names, and constraints, not Figma frames. The systems that thrive in the next few years won&#39;t be the prettiest; they&#39;ll be the ones whose rules are so clearly encoded that neither a tired engineer at 5pm nor a model at scale can hold them wrong.\u003C\u002Fp>",{"id":149,"html":150,"type":69},"b23","\u003Cp>A design system used to be a favor you did for your teammates. Now it&#39;s the interface your whole org — humans and machines — programs against. Build it like an API, because that&#39;s what it is.\u003C\u002Fp>",{"id":152,"type":153},"b24","divider",{"id":155,"html":156,"type":69},"b25","\u003Cp>\u003Cem>Thanks for reading! Let&#39;s stay connected:\u003C\u002Fem>\u003C\u002Fp>",{"id":158,"type":111,"items":159,"ordered":18},"b26",[160,161,162,163,164],"⭐ \u003Cstrong>GitHub\u003C\u002Fstrong> — follow me and star the projects: \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fparsajiravand\">github.com\u002Fparsajiravand\u003C\u002Fa>","💬 \u003Cstrong>Discord\u003C\u002Fstrong> — join the frontend best-practices community: \u003Ca href=\"https:\u002F\u002Fdiscord.gg\u002Fd9KRhuAwQ\">discord.gg\u002Fd9KRhuAwQ\u003C\u002Fa>","📸 \u003Cstrong>Instagram\u003C\u002Fstrong> — frontend best practices, daily: \u003Ca href=\"https:\u002F\u002Fwww.instagram.com\u002Fbestpractice___\u002F\">@bestpractice___\u003C\u002Fa>","💼 \u003Cstrong>LinkedIn\u003C\u002Fstrong> — \u003Ca href=\"https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fparsa-jiravand\u002F\">linkedin.com\u002Fin\u002Fparsa-jiravand\u003C\u002Fa>","✉️ \u003Cstrong>Email\u003C\u002Fstrong> (work &amp; contract inquiries): \u003Ca href=\"mailto:bestpractice2026@gmail.com\">bestpractice2026@gmail.com\u003C\u002Fa>",1,"We've always justified design systems with human reasons: consistency, faster onboarding, fewer one-off buttons. All true. But there's a new reason that quietly outweighs the rest — **your design system is becoming the API that machines build UI against.**\n\nWhen an AI assistant generates a screen, the quality of its output is bounded almost entirely by the vocabulary you give it. A loose, undocumented pile of components produces loose, inconsistent output. A tight, well-typed, well-named system produces output that looks like your team wrote it. The design system stops being documentation and becomes a *constraint surface*.\n\n## What \"good rails\" actually look like\n\nIf a model (or a junior dev, honestly — same requirements) is going to compose your components correctly without hand-holding, the system has to be legible *from the types and names alone.*\n\n**Names that describe intent, not appearance.**\n\n```tsx\n\u002F\u002F Weak: the model has to guess when to use which\n\u003CBlueButton \u002F>  \u003CSmallButton \u002F>  \u003CRoundButton \u002F>\n\n\u002F\u002F Strong: intent is in the name, variants are props\n\u003CButton variant=\"primary\" size=\"sm\" \u002F>\n```\n\n**Props that make illegal states unrepresentable.** If a component can be configured into a broken combination, something will eventually configure it that way. Push the constraints into the type system:\n\n```tsx\n\u002F\u002F A toast is either transient (auto-dismiss) OR action-required — never both.\ntype ToastProps =\n  | { kind: \"transient\"; durationMs: number }\n  | { kind: \"action\"; onAction: () => void; actionLabel: string };\n```\n\nA union like this is worth more than a paragraph of docs, because it's *enforced*. Neither a human nor a model can pass an `onAction` to a transient toast.\n\n**One obvious way to do each thing.** Three different ways to render a modal is three ways to get it subtly wrong. Every redundant path is a fork where generated code can drift from your conventions.\n\n## The shift in where you spend effort\n\nOld priority order for a design system:\n1. Visual polish\n2. Documentation \u002F Storybook\n3. Type safety (nice to have)\n\nNew priority order:\n1. **Type safety and clear contracts** — the machine-legible layer\n2. Clear, intent-based naming\n3. Visual polish\n4. Docs (increasingly, the types *are* the docs)\n\nThis isn't because polish stopped mattering — it's because polish is now the cheap part. What's expensive and high-leverage is the part that lets *everything built on top* be correct by default.\n\n## A concrete test\n\nWant to know if your design system is ready for this world? Try this: hand an AI assistant *only* your component type definitions (no screenshots, no prose) and ask it to build a settings page. Then look at the output.\n\n- If it composed sensible, on-brand UI from the types alone — your contracts are doing their job.\n- If it invented props that don't exist, or reached for raw `\u003Cdiv>`s and inline styles — that's a map of exactly where your system is ambiguous or incomplete.\n\nThat gap *is* your design-system backlog now. The components it had to fake are the ones missing a clear contract.\n\n## The takeaway\n\nBuild your design system as if its primary consumer can't see — because increasingly, one of its primary consumers can't. It reads types, names, and constraints, not Figma frames. The systems that thrive in the next few years won't be the prettiest; they'll be the ones whose rules are so clearly encoded that neither a tired engineer at 5pm nor a model at scale can hold them wrong.\n\nA design system used to be a favor you did for your teammates. Now it's the interface your whole org — humans and machines — programs against. Build it like an API, because that's what it is.\n\n---\n\n*Thanks for reading! Let's stay connected:*\n\n- ⭐ **GitHub** — follow me and star the projects: [github.com\u002Fparsajiravand](https:\u002F\u002Fgithub.com\u002Fparsajiravand)\n- 💬 **Discord** — join the frontend best-practices community: [discord.gg\u002Fd9KRhuAwQ](https:\u002F\u002Fdiscord.gg\u002Fd9KRhuAwQ)\n- 📸 **Instagram** — frontend best practices, daily: [@bestpractice___](https:\u002F\u002Fwww.instagram.com\u002Fbestpractice___\u002F)\n- 💼 **LinkedIn** — [linkedin.com\u002Fin\u002Fparsa-jiravand](https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fparsa-jiravand\u002F)\n- ✉️ **Email** (work & contract inquiries): [bestpractice2026@gmail.com](mailto:bestpractice2026@gmail.com)",{"title":35,"canonical":168,"description":37},"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fdesign-system-api-for-machines","019fe65f-a25b-72ba-8ee8-974398c88c20",{"id":171,"locked":18},"019fe65f-a971-74e8-a343-d36a29f251b7",[],[174],{"locale":13,"slug":34},"\u003Cdiv class=\"shj shj-lang-tsx shj-multiline\" data-lang=\"tsx\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-cmnt\">\u002F\u002F Weak: the model has to guess when to use which\n\u003C\u002Fspan>\u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">BlueButton\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">\u002F&gt;\u003C\u002Fspan>  \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">SmallButton\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">\u002F&gt;\u003C\u002Fspan>  \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">RoundButton\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">\u002F&gt;\u003C\u002Fspan>\n\n\u003Cspan class=\"shj-cmnt\">\u002F\u002F Strong: intent is in the name, variants are props\n\u003C\u002Fspan>\u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">Button\u003C\u002Fspan> \u003Cspan class=\"shj-class\">variant\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"primary\"\u003C\u002Fspan> \u003Cspan class=\"shj-class\">size\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"sm\"\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">\u002F&gt;\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-tsx shj-multiline\" data-lang=\"tsx\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-cmnt\">\u002F\u002F A toast is either transient (auto-dismiss) OR action-required — never both.\n\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">type\u003C\u002Fspan> \u003Cspan class=\"shj-class\">ToastProps\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan>\n  \u003Cspan class=\"shj-oper\">|\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> kind\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"transient\"\u003C\u002Fspan>; durationMs\u003Cspan class=\"shj-type\">: number\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n  \u003Cspan class=\"shj-oper\">|\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> kind\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"action\"\u003C\u002Fspan>; onAction\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">void\u003C\u002Fspan>; actionLabel\u003Cspan class=\"shj-type\">: string\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>;\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>"]