[{"data":1,"prerenderedAt":2584},["ShallowReactive",2],{"api-blog-add-design-extraction-to-claude-code-mcp":3,"api-blog-related-add-design-extraction-to-claude-code-mcp":613},{"id":4,"title":5,"author":6,"body":7,"category":581,"date":582,"dateModified":583,"description":584,"extension":585,"faqs":586,"image":583,"meta":598,"navigation":84,"path":599,"readingTime":88,"relatedSlugs":600,"seo":604,"stem":605,"tags":606,"__hash__":612},"apiBlog/api-blog/add-design-extraction-to-claude-code-mcp.md","Add Design Extraction to Claude Code with MCP","Soraia",{"type":8,"value":9,"toc":566},"minimark",[10,14,100,103,116,121,124,131,135,146,150,161,200,210,214,251,254,267,271,278,430,437,441,472,476,479,514,520,524,529,538,542,545,549,552,556,562],[11,12,13],"p",{},"Two commands and Claude Code can read any website's design system instead of guessing it from a screenshot. Here is the whole setup:",[15,16,21],"pre",{"className":17,"code":18,"language":19,"meta":20,"style":20},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","claude mcp add miromiro -s user \\\n  --env MIROMIRO_API_KEY=mm_live_your_key \\\n  -- npx -y miromiro-mcp\n\nclaude mcp list   # look for \"✓ Connected\"\n","bash","",[22,23,24,53,64,79,86],"code",{"__ignoreMap":20},[25,26,29,33,37,40,43,46,49],"span",{"class":27,"line":28},"line",1,[25,30,32],{"class":31},"sBMFI","claude",[25,34,36],{"class":35},"sfazB"," mcp",[25,38,39],{"class":35}," add",[25,41,42],{"class":35}," miromiro",[25,44,45],{"class":35}," -s",[25,47,48],{"class":35}," user",[25,50,52],{"class":51},"sTEyZ"," \\\n",[25,54,56,59,62],{"class":27,"line":55},2,[25,57,58],{"class":35},"  --env",[25,60,61],{"class":35}," MIROMIRO_API_KEY=mm_live_your_key",[25,63,52],{"class":51},[25,65,67,70,73,76],{"class":27,"line":66},3,[25,68,69],{"class":35},"  --",[25,71,72],{"class":35}," npx",[25,74,75],{"class":35}," -y",[25,77,78],{"class":35}," miromiro-mcp\n",[25,80,82],{"class":27,"line":81},4,[25,83,85],{"emptyLinePlaceholder":84},true,"\n",[25,87,89,91,93,96],{"class":27,"line":88},5,[25,90,32],{"class":31},[25,92,36],{"class":35},[25,94,95],{"class":35}," list",[25,97,99],{"class":98},"sHwdD","   # look for \"✓ Connected\"\n",[11,101,102],{},"That is genuinely it. The rest of this post is what those commands do, the Cursor/Windsurf versions, and how to actually use it in a session.",[11,104,105,106,109,110,115],{},"No terminal, or you use Claude in the browser? There is also a hosted server - paste ",[22,107,108],{},"https://miromiro.app/mcp"," into any client that takes a URL. Full walkthrough for Claude web, Cursor, ChatGPT and Codex in ",[111,112,114],"a",{"href":113},"/api/blog/connect-miromiro-mcp-claude-cursor-chatgpt","how to connect the MiroMiro MCP",".",[117,118,120],"h2",{"id":119},"why-bother","Why bother",[11,122,123],{},"Ask a coding agent to \"make it look like stripe.com\" and it works from a screenshot at best, memory at worst. Either way it guesses: colors eyeballed from pixels, spacing approximated, fonts invented. The output is a plausible imitation with none of the real values.",[11,125,126,127,115],{},"With the MCP server connected, the agent stops guessing. It calls the extraction tools itself, gets the site's actual ranked palette, type scale, spacing, and shadows as data, and builds with those. Same prompt, real values. We wrote more about this gap in ",[111,128,130],{"href":129},"/api/use-cases/ai-agent-ground-truth","ground truth for AI agents",[117,132,134],{"id":133},"step-1-get-a-key","Step 1: get a key",[11,136,137,138,142,143,115],{},"Sign in and mint one at ",[111,139,141],{"href":140},"/api/dashboard/keys","miromiro.app/api/dashboard/keys",". 100 free credits a month, no card. Keys look like ",[22,144,145],{},"mm_live_...",[117,147,149],{"id":148},"step-2-add-the-server","Step 2: add the server",[11,151,152,153,156,157,160],{},"The server is a local stdio process (",[22,154,155],{},"npx miromiro-mcp",") that reads your key from ",[22,158,159],{},"MIROMIRO_API_KEY",". Run this in your terminal - not inside a Claude session; you are configuring the server before the conversation starts:",[15,162,164],{"className":17,"code":163,"language":19,"meta":20,"style":20},"claude mcp add miromiro -s user \\\n  --env MIROMIRO_API_KEY=mm_live_your_key \\\n  -- npx -y miromiro-mcp\n",[22,165,166,182,190],{"__ignoreMap":20},[25,167,168,170,172,174,176,178,180],{"class":27,"line":28},[25,169,32],{"class":31},[25,171,36],{"class":35},[25,173,39],{"class":35},[25,175,42],{"class":35},[25,177,45],{"class":35},[25,179,48],{"class":35},[25,181,52],{"class":51},[25,183,184,186,188],{"class":27,"line":55},[25,185,58],{"class":35},[25,187,61],{"class":35},[25,189,52],{"class":51},[25,191,192,194,196,198],{"class":27,"line":66},[25,193,69],{"class":35},[25,195,72],{"class":35},[25,197,75],{"class":35},[25,199,78],{"class":35},[11,201,202,205,206,209],{},[22,203,204],{},"-s user"," registers it for every project; drop it to scope to the current one. The ",[22,207,208],{},"-y"," matters: it lets npx start the package unattended, so the editor can spawn the server without an install prompt.",[117,211,213],{"id":212},"step-3-verify-and-use-it","Step 3: verify and use it",[15,215,217],{"className":17,"code":216,"language":19,"meta":20,"style":20},"claude mcp list\n# miromiro ... ✓ Connected\n\nclaude\n> Use the miromiro server to get the brand colors and fonts of stripe.com\n",[22,218,219,228,233,237,242],{"__ignoreMap":20},[25,220,221,223,225],{"class":27,"line":28},[25,222,32],{"class":31},[25,224,36],{"class":35},[25,226,227],{"class":35}," list\n",[25,229,230],{"class":27,"line":55},[25,231,232],{"class":98},"# miromiro ... ✓ Connected\n",[25,234,235],{"class":27,"line":66},[25,236,85],{"emptyLinePlaceholder":84},[25,238,239],{"class":27,"line":81},[25,240,241],{"class":31},"claude\n",[25,243,244,248],{"class":27,"line":88},[25,245,247],{"class":246},"sMK4o",">",[25,249,250],{"class":51}," Use the miromiro server to get the brand colors and fonts of stripe.com\n",[11,252,253],{},"All seven extraction tools appear in Claude's toolbox automatically: design tokens, brand, images, SVGs, fonts, Lottie, and section-to-code. You do not name tools in prompts - ask design-shaped questions and the agent picks the right one. Prompts that work well:",[255,256,257,261,264],"ul",{},[258,259,260],"li",{},"\"Extract linear.app's design tokens and restyle our Button component to match\"",[258,262,263],{},"\"Get vercel.com's hero section as Tailwind and adapt it for our landing page\"",[258,265,266],{},"\"Pull the brand palette from ourcustomer.com and generate a theme file\"",[117,268,270],{"id":269},"cursor-and-windsurf","Cursor and Windsurf",[11,272,273,274,277],{},"Both take the standard ",[22,275,276],{},"mcpServers"," JSON block:",[15,279,283],{"className":280,"code":281,"language":282,"meta":20,"style":20},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{\n  \"mcpServers\": {\n    \"miromiro\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"miromiro-mcp\"],\n      \"env\": { \"MIROMIRO_API_KEY\": \"mm_live_your_key\" }\n    }\n  }\n}\n","json",[22,284,285,290,307,321,345,378,412,418,424],{"__ignoreMap":20},[25,286,287],{"class":27,"line":28},[25,288,289],{"class":246},"{\n",[25,291,292,295,298,301,304],{"class":27,"line":55},[25,293,294],{"class":246},"  \"",[25,296,276],{"class":297},"spNyl",[25,299,300],{"class":246},"\"",[25,302,303],{"class":246},":",[25,305,306],{"class":246}," {\n",[25,308,309,312,315,317,319],{"class":27,"line":66},[25,310,311],{"class":246},"    \"",[25,313,314],{"class":31},"miromiro",[25,316,300],{"class":246},[25,318,303],{"class":246},[25,320,306],{"class":246},[25,322,323,326,330,332,334,337,340,342],{"class":27,"line":81},[25,324,325],{"class":246},"      \"",[25,327,329],{"class":328},"sbssI","command",[25,331,300],{"class":246},[25,333,303],{"class":246},[25,335,336],{"class":246}," \"",[25,338,339],{"class":35},"npx",[25,341,300],{"class":246},[25,343,344],{"class":246},",\n",[25,346,347,349,352,354,356,359,361,363,365,368,370,373,375],{"class":27,"line":88},[25,348,325],{"class":246},[25,350,351],{"class":328},"args",[25,353,300],{"class":246},[25,355,303],{"class":246},[25,357,358],{"class":246}," [",[25,360,300],{"class":246},[25,362,208],{"class":35},[25,364,300],{"class":246},[25,366,367],{"class":246},",",[25,369,336],{"class":246},[25,371,372],{"class":35},"miromiro-mcp",[25,374,300],{"class":246},[25,376,377],{"class":246},"],\n",[25,379,381,383,386,388,390,393,395,398,400,402,404,407,409],{"class":27,"line":380},6,[25,382,325],{"class":246},[25,384,385],{"class":328},"env",[25,387,300],{"class":246},[25,389,303],{"class":246},[25,391,392],{"class":246}," {",[25,394,336],{"class":246},[25,396,159],{"class":397},"swJcz",[25,399,300],{"class":246},[25,401,303],{"class":246},[25,403,336],{"class":246},[25,405,406],{"class":35},"mm_live_your_key",[25,408,300],{"class":246},[25,410,411],{"class":246}," }\n",[25,413,415],{"class":27,"line":414},7,[25,416,417],{"class":246},"    }\n",[25,419,421],{"class":27,"line":420},8,[25,422,423],{"class":246},"  }\n",[25,425,427],{"class":27,"line":426},9,[25,428,429],{"class":246},"}\n",[11,431,432,433,436],{},"Cursor: ",[22,434,435],{},"~/.cursor/mcp.json",". Windsurf: Settings → MCP. Restart the editor after saving; both show the server's tools in their MCP panel when connected.",[117,438,440],{"id":439},"things-worth-knowing","Things worth knowing",[255,442,443,450,456],{},[258,444,445,449],{},[446,447,448],"strong",{},"It reads static HTML + CSS."," No JavaScript execution, so a fully client-rendered app can return little. The tools say so rather than inventing a result, which is the behavior you want in an agent pipeline.",[258,451,452,455],{},[446,453,454],{},"Credits:"," a token extraction is 10 credits, brand 15, section-to-code 25. The free 100/month covers real experimentation; the agent sees remaining quota in each response's usage object.",[258,457,458,461,462,465,466,469,470,115],{},[446,459,460],{},"Server not connecting?"," It is almost always the key: check ",[22,463,464],{},"claude mcp list",", and confirm the env var made it in (",[22,467,468],{},"claude mcp get miromiro","). Node 18+ is required for ",[22,471,339],{},[117,473,475],{"id":474},"or-skip-mcp-entirely","Or skip MCP entirely",[11,477,478],{},"The same engine is a plain REST API if your agent framework speaks HTTP instead:",[15,480,482],{"className":17,"code":481,"language":19,"meta":20,"style":20},"curl \"https://miromiro.app/api/v1/extract?url=stripe.com\" \\\n  -H \"Authorization: Bearer $MIROMIRO_API_KEY\"\n",[22,483,484,498],{"__ignoreMap":20},[25,485,486,489,491,494,496],{"class":27,"line":28},[25,487,488],{"class":31},"curl",[25,490,336],{"class":246},[25,492,493],{"class":35},"https://miromiro.app/api/v1/extract?url=stripe.com",[25,495,300],{"class":246},[25,497,52],{"class":51},[25,499,500,503,505,508,511],{"class":27,"line":55},[25,501,502],{"class":35},"  -H",[25,504,336],{"class":246},[25,506,507],{"class":35},"Authorization: Bearer ",[25,509,510],{"class":51},"$MIROMIRO_API_KEY",[25,512,513],{"class":246},"\"\n",[11,515,516,517,115],{},"Full setup docs, including the agent quickstart, live at ",[111,518,519],{"href":519},"/api/docs/install-mcp",[117,521,523],{"id":522},"frequently-asked-questions","Frequently asked questions",[525,526,528],"h3",{"id":527},"how-do-i-add-an-mcp-server-to-claude-code","How do I add an MCP server to Claude Code?",[11,530,531,534,535,537],{},[22,532,533],{},"claude mcp add \u003Cname> ... -- \u003Ccommand>"," in your terminal, then ",[22,536,464],{}," to confirm it connected. Exact MiroMiro command at the top of this post.",[525,539,541],{"id":540},"what-tools-does-the-server-expose","What tools does the server expose?",[11,543,544],{},"Seven: design tokens, brand, images, SVGs, fonts, Lottie, and section-to-code. They register automatically; the agent picks per task.",[525,546,548],{"id":547},"why-tokens-instead-of-a-screenshot","Why tokens instead of a screenshot?",[11,550,551],{},"Screenshots make the model guess every value. Tokens are the site's real CSS - the difference between imitation and ground truth.",[525,553,555],{"id":554},"does-it-work-with-cursor-and-windsurf","Does it work with Cursor and Windsurf?",[11,557,558,559,561],{},"Yes - standard ",[22,560,276],{}," JSON (above), restart, done.",[563,564,565],"style",{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}",{"title":20,"searchDepth":55,"depth":55,"links":567},[568,569,570,571,572,573,574,575],{"id":119,"depth":55,"text":120},{"id":133,"depth":55,"text":134},{"id":148,"depth":55,"text":149},{"id":212,"depth":55,"text":213},{"id":269,"depth":55,"text":270},{"id":439,"depth":55,"text":440},{"id":474,"depth":55,"text":475},{"id":522,"depth":55,"text":523,"children":576},[577,578,579,580],{"id":527,"depth":66,"text":528},{"id":540,"depth":66,"text":541},{"id":547,"depth":66,"text":548},{"id":554,"depth":66,"text":555},"AI Engineering","2026-07-18",null,"Install the MiroMiro MCP server in two commands and Claude Code can pull any website's tokens, brand, fonts, and section code instead of guessing.","md",[587,589,592,595],{"question":528,"answer":588},"Run claude mcp add \u003Cname> with the command that starts the server, in your terminal before starting a session. For MiroMiro: claude mcp add miromiro -s user --env MIROMIRO_API_KEY=your_key -- npx -y miromiro-mcp. Verify with claude mcp list - you want to see Connected.",{"question":590,"answer":591},"What does the MiroMiro MCP server let Claude do?","Seven extraction tools appear in Claude's toolbox automatically: design tokens, brand data (logo, palette, fonts), images, SVGs, fonts, Lottie animations, and section-to-code. Claude calls them by itself when you ask design questions about a URL - no manual API calls in your prompts.",{"question":593,"answer":594},"Why give a coding agent design tokens instead of a screenshot?","Screenshots make the model guess values - it eyeballs colors, approximates spacing, and invents font names. Tokens are the site's real CSS values, so 'build it like stripe.com' produces the actual palette and type scale instead of a plausible imitation.",{"question":596,"answer":597},"Does the MiroMiro MCP server work with Cursor and Windsurf?","Yes. Both take the standard mcpServers JSON block: command npx, args ['-y', 'miromiro-mcp'], and your key in the env. In Cursor it goes in ~/.cursor/mcp.json; in Windsurf under Settings, MCP.",{},"/api-blog/add-design-extraction-to-claude-code-mcp",[601,602,603],"connect-miromiro-mcp-claude-cursor-chatgpt","how-to-extract-design-tokens-from-any-website-in-python","design-token-types-reference",{"title":5,"description":584},"api-blog/add-design-extraction-to-claude-code-mcp",[607,608,609,610,611],"mcp","claude code","ai agents","design tokens","cursor","5ZSlTLfRpZyWbN6QBl48G2MrATv3P0HPlFNY504YFvY",[614,1267,1983],{"id":615,"title":616,"author":6,"body":617,"category":581,"date":1242,"dateModified":583,"description":1243,"extension":585,"faqs":1244,"image":583,"meta":1256,"navigation":84,"path":1257,"readingTime":380,"relatedSlugs":1258,"seo":1261,"stem":1262,"tags":1263,"__hash__":1266},"apiBlog/api-blog/connect-miromiro-mcp-claude-cursor-chatgpt.md","How to Connect the MiroMiro MCP to Claude, Cursor, ChatGPT and Codex",{"type":8,"value":618,"toc":1225},[619,625,633,637,640,700,707,713,721,724,733,828,831,835,838,885,889,895,912,916,919,971,979,983,986,1079,1086,1090,1093,1105,1109,1143,1145,1148,1178,1185,1187,1191,1194,1198,1201,1205,1208,1212,1222],[11,620,621,622,624],{},"Every client that speaks MCP can connect to MiroMiro with one URL. Paste ",[22,623,108],{}," into the client's connector settings and seven design-extraction tools appear in your chats: section-to-code, brand, design tokens, images, SVGs, fonts, and Lottie.",[11,626,627,628,632],{},"You do not need an account to try it. The bare URL connects in demo mode; a ",[111,629,631],{"href":630},"/api/onboarding","free key"," (100 credits a month, no card) unlocks full results. Client-by-client steps below.",[117,634,636],{"id":635},"claude-web-and-desktop","Claude - web and desktop",[11,638,639],{},"Claude calls these custom connectors. Two ways in:",[641,642,643,661,687,690],"ol",{},[258,644,645,646,649,650,653,654,653,657,660],{},"In any chat, click the ",[446,647,648],{},"+"," button in the input, then ",[446,651,652],{},"Connectors"," → ",[446,655,656],{},"Add connector",[446,658,659],{},"Add custom connector",". (Settings → Connectors gets you to the same dialog.)",[258,662,663,664],{},"In the dialog, fill in:\n",[255,665,666,675],{},[258,667,668,671,672],{},[446,669,670],{},"Name:"," ",[22,673,674],{},"MiroMiro",[258,676,677,671,680,683,684,686],{},[446,678,679],{},"URL:",[22,681,682],{},"https://miromiro.app/mcp?access_key=mm_live_your_key"," - or just ",[22,685,108],{}," to try it keyless first",[258,688,689],{},"Leave the OAuth Client ID and Secret fields under Advanced settings empty. The key travels in the URL.",[258,691,692,693,696,697,115],{},"Click ",[446,694,695],{},"Add",". The tools are live in every chat immediately - ask \"get the brand colors and fonts of stripe.com\" and watch it call ",[22,698,699],{},"extract_brand",[11,701,702],{},[703,704],"img",{"alt":705,"src":706},"Claude's + menu: Connectors, then Add connector, then Add custom connector","/api-blog/connector1.png",[11,708,709],{},[703,710],{"alt":711,"src":712},"Claude's Add custom connector dialog with the MiroMiro name and URL filled in","/api-blog/connector2.png",[11,714,715,716,720],{},"One click faster: the ",[111,717,719],{"href":718},"/api/mcp","Add to Claude button on our MCP page"," opens this dialog with the name and URL pre-filled.",[117,722,723],{"id":611},"Cursor",[11,725,726,727,730,731,303],{},"Cursor supports install deeplinks, so the ",[111,728,729],{"href":718},"Add to Cursor button"," does the whole thing in one click. Manual version, in ",[22,732,435],{},[15,734,736],{"className":280,"code":735,"language":282,"meta":20,"style":20},"{\n  \"mcpServers\": {\n    \"miromiro\": {\n      \"url\": \"https://miromiro.app/mcp\",\n      \"headers\": { \"Authorization\": \"Bearer mm_live_your_key\" }\n    }\n  }\n}\n",[22,737,738,742,754,766,785,816,820,824],{"__ignoreMap":20},[25,739,740],{"class":27,"line":28},[25,741,289],{"class":246},[25,743,744,746,748,750,752],{"class":27,"line":55},[25,745,294],{"class":246},[25,747,276],{"class":297},[25,749,300],{"class":246},[25,751,303],{"class":246},[25,753,306],{"class":246},[25,755,756,758,760,762,764],{"class":27,"line":66},[25,757,311],{"class":246},[25,759,314],{"class":31},[25,761,300],{"class":246},[25,763,303],{"class":246},[25,765,306],{"class":246},[25,767,768,770,773,775,777,779,781,783],{"class":27,"line":81},[25,769,325],{"class":246},[25,771,772],{"class":328},"url",[25,774,300],{"class":246},[25,776,303],{"class":246},[25,778,336],{"class":246},[25,780,108],{"class":35},[25,782,300],{"class":246},[25,784,344],{"class":246},[25,786,787,789,792,794,796,798,800,803,805,807,809,812,814],{"class":27,"line":88},[25,788,325],{"class":246},[25,790,791],{"class":328},"headers",[25,793,300],{"class":246},[25,795,303],{"class":246},[25,797,392],{"class":246},[25,799,336],{"class":246},[25,801,802],{"class":397},"Authorization",[25,804,300],{"class":246},[25,806,303],{"class":246},[25,808,336],{"class":246},[25,810,811],{"class":35},"Bearer mm_live_your_key",[25,813,300],{"class":246},[25,815,411],{"class":246},[25,817,818],{"class":27,"line":380},[25,819,417],{"class":246},[25,821,822],{"class":27,"line":414},[25,823,423],{"class":246},[25,825,826],{"class":27,"line":420},[25,827,429],{"class":246},[11,829,830],{},"Cursor can send headers, so put the key there instead of in the URL - headers stay out of logs. Restart Cursor and check the MCP panel for the seven tools.",[117,832,834],{"id":833},"chatgpt","ChatGPT",[11,836,837],{},"ChatGPT only takes remote servers, which is exactly what this is:",[641,839,840,855,882],{},[258,841,842,653,845,653,848,851,852],{},[446,843,844],{},"Settings",[446,846,847],{},"Apps & Connectors",[446,849,850],{},"Advanced"," → enable ",[446,853,854],{},"Developer mode",[258,856,857,860,861],{},[446,858,859],{},"Create"," a connector:\n",[255,862,863,869,876],{},[258,864,865,671,867],{},[446,866,670],{},[22,868,674],{},[258,870,871,671,874],{},[446,872,873],{},"MCP server URL:",[22,875,682],{},[258,877,878,881],{},[446,879,880],{},"Authentication:"," No authentication (the key is in the URL)",[258,883,884],{},"Enable the connector in a chat via the tools menu.",[117,886,888],{"id":887},"codex-cli","Codex CLI",[11,890,891,892,303],{},"Add to ",[22,893,894],{},"~/.codex/config.toml",[15,896,900],{"className":897,"code":898,"language":899,"meta":20,"style":20},"language-toml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","[mcp_servers.miromiro]\nurl = \"https://miromiro.app/mcp?access_key=mm_live_your_key\"\n","toml",[22,901,902,907],{"__ignoreMap":20},[25,903,904],{"class":27,"line":28},[25,905,906],{},"[mcp_servers.miromiro]\n",[25,908,909],{"class":27,"line":55},[25,910,911],{},"url = \"https://miromiro.app/mcp?access_key=mm_live_your_key\"\n",[117,913,915],{"id":914},"claude-code","Claude Code",[11,917,918],{},"One command in your terminal, not inside a session:",[15,920,922],{"className":17,"code":921,"language":19,"meta":20,"style":20},"claude mcp add --transport http miromiro https://miromiro.app/mcp \\\n  --header \"Authorization: Bearer mm_live_your_key\"\n\nclaude mcp list   # look for \"✓ Connected\"\n",[22,923,924,945,957,961],{"__ignoreMap":20},[25,925,926,928,930,932,935,938,940,943],{"class":27,"line":28},[25,927,32],{"class":31},[25,929,36],{"class":35},[25,931,39],{"class":35},[25,933,934],{"class":35}," --transport",[25,936,937],{"class":35}," http",[25,939,42],{"class":35},[25,941,942],{"class":35}," https://miromiro.app/mcp",[25,944,52],{"class":51},[25,946,947,950,952,955],{"class":27,"line":55},[25,948,949],{"class":35},"  --header",[25,951,336],{"class":246},[25,953,954],{"class":35},"Authorization: Bearer mm_live_your_key",[25,956,513],{"class":246},[25,958,959],{"class":27,"line":66},[25,960,85],{"emptyLinePlaceholder":84},[25,962,963,965,967,969],{"class":27,"line":81},[25,964,32],{"class":31},[25,966,36],{"class":35},[25,968,95],{"class":35},[25,970,99],{"class":98},[11,972,973,974,978],{},"Claude Code also runs the ",[111,975,977],{"href":976},"/api/blog/add-design-extraction-to-claude-code-mcp","local npm package"," if you prefer the key in an env var instead of a header.",[117,980,982],{"id":981},"demo-mode-vs-a-key","Demo mode vs a key",[11,984,985],{},"The same tools respond either way. What changes:",[987,988,989,1007],"table",{},[990,991,992],"thead",{},[993,994,995,998,1001,1004],"tr",{},[996,997],"th",{},[996,999,1000],{},"No key (demo)",[996,1002,1003],{},"Free key",[996,1005,1006],{},"Paid plans",[1008,1009,1010,1024,1038,1052,1065],"tbody",{},[993,1011,1012,1016,1019,1022],{},[1013,1014,1015],"td",{},"Results",[1013,1017,1018],{},"Capped previews",[1013,1020,1021],{},"Full",[1013,1023,1021],{},[993,1025,1026,1029,1032,1035],{},[1013,1027,1028],{},"Rate limit",[1013,1030,1031],{},"5/min, shared per IP",[1013,1033,1034],{},"30/min, yours",[1013,1036,1037],{},"60 to 300/min",[993,1039,1040,1043,1046,1049],{},[1013,1041,1042],{},"Credits",[1013,1044,1045],{},"None used",[1013,1047,1048],{},"100/month",[1013,1050,1051],{},"5,000 to 200,000/month",[993,1053,1054,1057,1060,1063],{},[1013,1055,1056],{},"Usage and logs",[1013,1058,1059],{},"No",[1013,1061,1062],{},"Dashboard",[1013,1064,1062],{},[993,1066,1067,1070,1073,1076],{},[1013,1068,1069],{},"Price",[1013,1071,1072],{},"Free",[1013,1074,1075],{},"Free, no card",[1013,1077,1078],{},"From €19/month",[11,1080,1081,1082,1085],{},"Demo mode is for seeing real output before you sign up. Treat the free key as the actual starting point: it is also free, and ",[22,1083,1084],{},"extract_code"," alone is worth the 30 seconds of signup.",[117,1087,1089],{"id":1088},"what-to-ask-once-connected","What to ask once connected",[11,1091,1092],{},"You never name tools in prompts - ask design-shaped questions and the agent picks the right one:",[255,1094,1095,1098,1100,1103],{},[258,1096,1097],{},"\"Rebuild stripe.com's pricing section in React\"",[258,1099,260],{},[258,1101,1102],{},"\"What fonts does vercel.com actually load?\"",[258,1104,266],{},[117,1106,1108],{"id":1107},"common-problems","Common problems",[255,1110,1111,1121,1127,1133],{},[258,1112,1113,1116,1117,1120],{},[446,1114,1115],{},"\"Invalid or revoked API key\" in the tool result."," The key in your URL or header is wrong or was rotated. Grab the current one from ",[111,1118,1119],{"href":140},"API Keys"," - keys are shown once at creation, so rotate if you lost it.",[258,1122,1123,1126],{},[446,1124,1125],{},"Rate limited in demo mode."," The keyless tier is 5 requests a minute per IP, shared with everyone behind your NAT. A free key gives you your own 30/minute.",[258,1128,1129,1132],{},[446,1130,1131],{},"Thin results on an app-like site."," The engine reads static HTML and CSS, no JavaScript execution. A fully client-rendered app returns little, and the tools say so instead of inventing values.",[258,1134,1135,1138,1139,1142],{},[446,1136,1137],{},"Claude web has no header field."," Correct - claude.ai custom connectors cannot send custom headers. That is what ",[22,1140,1141],{},"?access_key="," is for.",[117,1144,475],{"id":474},[11,1146,1147],{},"The same engine is a plain REST API:",[15,1149,1151],{"className":17,"code":1150,"language":19,"meta":20,"style":20},"curl \"https://miromiro.app/api/v1/brand?url=stripe.com\" \\\n  -H \"Authorization: Bearer $MIROMIRO_API_KEY\"\n",[22,1152,1153,1166],{"__ignoreMap":20},[25,1154,1155,1157,1159,1162,1164],{"class":27,"line":28},[25,1156,488],{"class":31},[25,1158,336],{"class":246},[25,1160,1161],{"class":35},"https://miromiro.app/api/v1/brand?url=stripe.com",[25,1163,300],{"class":246},[25,1165,52],{"class":51},[25,1167,1168,1170,1172,1174,1176],{"class":27,"line":55},[25,1169,502],{"class":35},[25,1171,336],{"class":246},[25,1173,507],{"class":35},[25,1175,510],{"class":51},[25,1177,513],{"class":246},[11,1179,1180,1181,1184],{},"Full reference at ",[111,1182,1183],{"href":1183},"/api/docs",". You can start for free.",[117,1186,523],{"id":522},[525,1188,1190],{"id":1189},"how-do-i-add-a-custom-mcp-connector-to-claude-web","How do I add a custom MCP connector to Claude web?",[11,1192,1193],{},"The + button in the chat input → Connectors → Add connector → Add custom connector. Name it, paste the URL, click Add.",[525,1195,1197],{"id":1196},"do-i-need-a-key-to-try-it","Do I need a key to try it?",[11,1199,1200],{},"No. The bare URL runs in demo mode. Full results need a key - free, 100 credits a month, no card.",[525,1202,1204],{"id":1203},"which-clients-does-the-remote-server-work-with","Which clients does the remote server work with?",[11,1206,1207],{},"Claude web and desktop, Cursor, ChatGPT developer mode, Codex, Claude Code, Windsurf, and any MCP SDK. If it takes a server URL, it works.",[525,1209,1211],{"id":1210},"where-does-my-key-go-safest","Where does my key go safest?",[11,1213,1214,1215,1218,1219,1221],{},"Header (",[22,1216,1217],{},"Authorization: Bearer",") where the client supports it - Cursor, Claude Code, SDKs. URL (",[22,1220,1141],{},") only where headers are not an option - claude.ai, ChatGPT. Rotate keys anytime in the dashboard.",[563,1223,1224],{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}",{"title":20,"searchDepth":55,"depth":55,"links":1226},[1227,1228,1229,1230,1231,1232,1233,1234,1235,1236],{"id":635,"depth":55,"text":636},{"id":611,"depth":55,"text":723},{"id":833,"depth":55,"text":834},{"id":887,"depth":55,"text":888},{"id":914,"depth":55,"text":915},{"id":981,"depth":55,"text":982},{"id":1088,"depth":55,"text":1089},{"id":1107,"depth":55,"text":1108},{"id":474,"depth":55,"text":475},{"id":522,"depth":55,"text":523,"children":1237},[1238,1239,1240,1241],{"id":1189,"depth":66,"text":1190},{"id":1196,"depth":66,"text":1197},{"id":1203,"depth":66,"text":1204},{"id":1210,"depth":66,"text":1211},"2026-07-20","Step-by-step connector setup for every client: paste one URL into Claude web, one-click install in Cursor, developer mode in ChatGPT, one command in Claude Code. Works without a key in demo mode.",[1245,1247,1250,1253],{"question":1190,"answer":1246},"Click the + button in the chat input, choose Connectors, then Add connector and Add custom connector. Name it, paste the server URL (for MiroMiro: https://miromiro.app/mcp), and click Add. No OAuth fields needed for servers that use URL keys.",{"question":1248,"answer":1249},"Do I need an API key to try the MiroMiro MCP?","No. The bare URL https://miromiro.app/mcp connects in demo mode - capped results, shared rate limit, zero signup. For full results, append ?access_key=mm_live_your_key to the URL, or send the key as an Authorization: Bearer header in clients that support headers.",{"question":1251,"answer":1252},"How do I connect an MCP server to ChatGPT?","Settings, then Apps & Connectors, then Advanced, then enable Developer mode. Create a connector with the MCP server URL and pick No authentication. ChatGPT requires remote (URL-based) servers - it cannot run local stdio packages.",{"question":1254,"answer":1255},"What is the difference between the remote MCP server and the npm package?","Same seven tools. The remote server (https://miromiro.app/mcp) is a hosted URL - nothing to install, works in browser clients like claude.ai and ChatGPT. The npm package (npx miromiro-mcp) runs locally and reads the key from an env var, which keeps it out of any URL.",{},"/api-blog/connect-miromiro-mcp-claude-cursor-chatgpt",[1259,602,1260],"add-design-extraction-to-claude-code-mcp","best-design-to-code-apis-2026",{"title":616,"description":1243},"api-blog/connect-miromiro-mcp-claude-cursor-chatgpt",[607,32,611,833,1264,1265,609],"codex","connectors","a2tzmmGGjH3MTqWedb8mJxtpJkSYfhXTuPPKLIqpiGE",{"id":1268,"title":1269,"author":6,"body":1270,"category":1960,"date":582,"dateModified":583,"description":1961,"extension":585,"faqs":1962,"image":583,"meta":1972,"navigation":84,"path":1973,"readingTime":420,"relatedSlugs":1974,"seo":1977,"stem":1978,"tags":1979,"__hash__":1982},"apiBlog/api-blog/how-to-extract-design-tokens-from-any-website-in-python.md","How to Extract Design Tokens from Any Website in Python",{"type":8,"value":1271,"toc":1944},[1272,1279,1282,1286,1289,1315,1318,1508,1511,1519,1534,1538,1541,1580,1583,1587,1590,1627,1629,1632,1694,1698,1778,1785,1789,1792,1820,1850,1861,1865,1875,1877,1881,1884,1888,1901,1905,1908,1912,1915,1919,1941],[11,1273,1274,1275,1278],{},"The fastest way to extract design tokens from a website in Python is Playwright plus ",[22,1276,1277],{},"getComputedStyle()",": load the page in a real browser, collect the computed styles of every element, and count what repeats. The counting is the important part. A site's design system is whatever it uses everywhere, not every value that appears once in a forgotten footer.",[11,1280,1281],{},"Parsing CSS files without a browser is the approach most tutorials suggest, and it is the one that works worst on modern sites. More on that below.",[117,1283,1285],{"id":1284},"extract-colors-with-playwright","Extract colors with Playwright",[11,1287,1288],{},"First, install Playwright and a browser:",[15,1290,1292],{"className":17,"code":1291,"language":19,"meta":20,"style":20},"pip install playwright\nplaywright install chromium\n",[22,1293,1294,1305],{"__ignoreMap":20},[25,1295,1296,1299,1302],{"class":27,"line":28},[25,1297,1298],{"class":31},"pip",[25,1300,1301],{"class":35}," install",[25,1303,1304],{"class":35}," playwright\n",[25,1306,1307,1310,1312],{"class":27,"line":55},[25,1308,1309],{"class":31},"playwright",[25,1311,1301],{"class":35},[25,1313,1314],{"class":35}," chromium\n",[11,1316,1317],{},"Then collect styles from every element and count them:",[15,1319,1323],{"className":1320,"code":1321,"language":1322,"meta":20,"style":20},"language-python shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","from collections import Counter\nfrom playwright.sync_api import sync_playwright\n\nCOLLECT = \"\"\"\n() => {\n  const els = [...document.querySelectorAll('body *')].slice(0, 5000);\n  const out = { colors: [], fonts: [], sizes: [], radii: [], shadows: [] };\n  for (const el of els) {\n    const s = getComputedStyle(el);\n    out.colors.push(s.color, s.backgroundColor, s.borderTopColor);\n    out.fonts.push(s.fontFamily);\n    out.sizes.push(s.fontSize);\n    out.radii.push(s.borderTopLeftRadius);\n    out.shadows.push(s.boxShadow);\n  }\n  return out;\n}\n\"\"\"\n\ndef top(values, n=8, skip=(\"\", \"none\", \"normal\", \"auto\", \"0px\", \"rgba(0, 0, 0, 0)\")):\n    return Counter(v for v in values if v not in skip).most_common(n)\n\nwith sync_playwright() as p:\n    browser = p.chromium.launch()\n    page = browser.new_page(viewport={\"width\": 1440, \"height\": 900})\n    page.goto(\"https://stripe.com\", wait_until=\"networkidle\")\n    raw = page.evaluate(COLLECT)\n    browser.close()\n\nfor name in (\"colors\", \"fonts\", \"sizes\", \"radii\", \"shadows\"):\n    print(f\"\\n== {name} ==\")\n    for value, count in top(raw[name]):\n        print(f\"{count:>5}  {value}\")\n","python",[22,1324,1325,1330,1335,1339,1344,1349,1354,1359,1364,1369,1375,1381,1387,1393,1399,1404,1410,1415,1421,1426,1432,1438,1443,1449,1455,1461,1467,1473,1479,1484,1490,1496,1502],{"__ignoreMap":20},[25,1326,1327],{"class":27,"line":28},[25,1328,1329],{},"from collections import Counter\n",[25,1331,1332],{"class":27,"line":55},[25,1333,1334],{},"from playwright.sync_api import sync_playwright\n",[25,1336,1337],{"class":27,"line":66},[25,1338,85],{"emptyLinePlaceholder":84},[25,1340,1341],{"class":27,"line":81},[25,1342,1343],{},"COLLECT = \"\"\"\n",[25,1345,1346],{"class":27,"line":88},[25,1347,1348],{},"() => {\n",[25,1350,1351],{"class":27,"line":380},[25,1352,1353],{},"  const els = [...document.querySelectorAll('body *')].slice(0, 5000);\n",[25,1355,1356],{"class":27,"line":414},[25,1357,1358],{},"  const out = { colors: [], fonts: [], sizes: [], radii: [], shadows: [] };\n",[25,1360,1361],{"class":27,"line":420},[25,1362,1363],{},"  for (const el of els) {\n",[25,1365,1366],{"class":27,"line":426},[25,1367,1368],{},"    const s = getComputedStyle(el);\n",[25,1370,1372],{"class":27,"line":1371},10,[25,1373,1374],{},"    out.colors.push(s.color, s.backgroundColor, s.borderTopColor);\n",[25,1376,1378],{"class":27,"line":1377},11,[25,1379,1380],{},"    out.fonts.push(s.fontFamily);\n",[25,1382,1384],{"class":27,"line":1383},12,[25,1385,1386],{},"    out.sizes.push(s.fontSize);\n",[25,1388,1390],{"class":27,"line":1389},13,[25,1391,1392],{},"    out.radii.push(s.borderTopLeftRadius);\n",[25,1394,1396],{"class":27,"line":1395},14,[25,1397,1398],{},"    out.shadows.push(s.boxShadow);\n",[25,1400,1402],{"class":27,"line":1401},15,[25,1403,423],{},[25,1405,1407],{"class":27,"line":1406},16,[25,1408,1409],{},"  return out;\n",[25,1411,1413],{"class":27,"line":1412},17,[25,1414,429],{},[25,1416,1418],{"class":27,"line":1417},18,[25,1419,1420],{},"\"\"\"\n",[25,1422,1424],{"class":27,"line":1423},19,[25,1425,85],{"emptyLinePlaceholder":84},[25,1427,1429],{"class":27,"line":1428},20,[25,1430,1431],{},"def top(values, n=8, skip=(\"\", \"none\", \"normal\", \"auto\", \"0px\", \"rgba(0, 0, 0, 0)\")):\n",[25,1433,1435],{"class":27,"line":1434},21,[25,1436,1437],{},"    return Counter(v for v in values if v not in skip).most_common(n)\n",[25,1439,1441],{"class":27,"line":1440},22,[25,1442,85],{"emptyLinePlaceholder":84},[25,1444,1446],{"class":27,"line":1445},23,[25,1447,1448],{},"with sync_playwright() as p:\n",[25,1450,1452],{"class":27,"line":1451},24,[25,1453,1454],{},"    browser = p.chromium.launch()\n",[25,1456,1458],{"class":27,"line":1457},25,[25,1459,1460],{},"    page = browser.new_page(viewport={\"width\": 1440, \"height\": 900})\n",[25,1462,1464],{"class":27,"line":1463},26,[25,1465,1466],{},"    page.goto(\"https://stripe.com\", wait_until=\"networkidle\")\n",[25,1468,1470],{"class":27,"line":1469},27,[25,1471,1472],{},"    raw = page.evaluate(COLLECT)\n",[25,1474,1476],{"class":27,"line":1475},28,[25,1477,1478],{},"    browser.close()\n",[25,1480,1482],{"class":27,"line":1481},29,[25,1483,85],{"emptyLinePlaceholder":84},[25,1485,1487],{"class":27,"line":1486},30,[25,1488,1489],{},"for name in (\"colors\", \"fonts\", \"sizes\", \"radii\", \"shadows\"):\n",[25,1491,1493],{"class":27,"line":1492},31,[25,1494,1495],{},"    print(f\"\\n== {name} ==\")\n",[25,1497,1499],{"class":27,"line":1498},32,[25,1500,1501],{},"    for value, count in top(raw[name]):\n",[25,1503,1505],{"class":27,"line":1504},33,[25,1506,1507],{},"        print(f\"{count:>5}  {value}\")\n",[11,1509,1510],{},"Run it and you get something like:",[15,1512,1517],{"className":1513,"code":1515,"language":1516},[1514],"language-text","== colors ==\n 3021  rgb(255, 255, 255)\n 1204  rgb(26, 27, 37)\n  412  rgb(99, 91, 255)\n  118  rgb(66, 84, 102)\n","text",[22,1518,1515],{"__ignoreMap":20},[11,1520,1521,1522,1525,1526,1529,1530,1533],{},"That third line is the brand color, found by nothing smarter than counting. ",[22,1523,1524],{},"getComputedStyle"," has already done the hard work for you: it resolved every CSS variable chain, every ",[22,1527,1528],{},"var(--a, var(--b))"," fallback, every cascade conflict, and turned Tailwind utility soup into plain ",[22,1531,1532],{},"rgb()"," values.",[117,1535,1537],{"id":1536},"fonts-and-the-type-scale","Fonts and the type scale",[11,1539,1540],{},"The same collection already has them. Font families come back as full stacks, so take the first entry:",[15,1542,1544],{"className":1320,"code":1543,"language":1322,"meta":20,"style":20},"families = Counter(f.split(\",\")[0].strip('\"\\' ') for f in raw[\"fonts\"])\nprint(families.most_common(5))\n\nsizes = sorted(\n    {float(s.replace(\"px\", \"\")) for s, c in top(raw[\"sizes\"], n=20)},\n)\nprint(sizes)  # e.g. [12.0, 14.0, 16.0, 18.0, 20.0, 24.0, 36.0, 56.0]\n",[22,1545,1546,1551,1556,1560,1565,1570,1575],{"__ignoreMap":20},[25,1547,1548],{"class":27,"line":28},[25,1549,1550],{},"families = Counter(f.split(\",\")[0].strip('\"\\' ') for f in raw[\"fonts\"])\n",[25,1552,1553],{"class":27,"line":55},[25,1554,1555],{},"print(families.most_common(5))\n",[25,1557,1558],{"class":27,"line":66},[25,1559,85],{"emptyLinePlaceholder":84},[25,1561,1562],{"class":27,"line":81},[25,1563,1564],{},"sizes = sorted(\n",[25,1566,1567],{"class":27,"line":88},[25,1568,1569],{},"    {float(s.replace(\"px\", \"\")) for s, c in top(raw[\"sizes\"], n=20)},\n",[25,1571,1572],{"class":27,"line":380},[25,1573,1574],{},")\n",[25,1576,1577],{"class":27,"line":414},[25,1578,1579],{},"print(sizes)  # e.g. [12.0, 14.0, 16.0, 18.0, 20.0, 24.0, 36.0, 56.0]\n",[11,1581,1582],{},"That sorted list is the site's type scale. If it has fifteen entries instead of seven, you are looking at a site without a design system, which is also useful to know.",[117,1584,1586],{"id":1585},"from-raw-values-to-actual-tokens","From raw values to actual tokens",[11,1588,1589],{},"Raw counts are not tokens yet. Three cleanup steps matter:",[641,1591,1592,1601,1614],{},[258,1593,1594,671,1597,1600],{},[446,1595,1596],{},"Drop the defaults.",[22,1598,1599],{},"rgb(0, 0, 0)"," near the top does not mean the brand is black. Browser resets and inherited defaults pollute every element, so check whether a value actually appears on visible, styled elements before promoting it.",[258,1602,1603,671,1606,1609,1610,1613],{},[446,1604,1605],{},"Merge near-duplicates.",[22,1607,1608],{},"rgb(99, 91, 255)"," and ",[22,1611,1612],{},"rgb(99, 92, 255)"," are the same token with a rounding disagreement. Snap channels to a small tolerance before counting, or you will split one token's count across five entries.",[258,1615,1616,1619,1620,1623,1624,1626],{},[446,1617,1618],{},"Name by role, not by value."," The most frequent saturated color on buttons and links is ",[22,1621,1622],{},"primary",". The dominant text color is ",[22,1625,1516],{},". Frequency plus element context gets you there; k-means clustering is overkill for this and I would skip it.",[117,1628,1108],{"id":1107},[11,1630,1631],{},"Real gotchas, in the order they will bite you:",[255,1633,1634,1644,1650,1667,1680],{},[258,1635,1636,1639,1640,1643],{},[446,1637,1638],{},"Your viewport is a filter."," Computed styles reflect the current viewport only. A ",[22,1641,1642],{},"@media (max-width: 640px)"," override is invisible at 1440px. Extract at two viewports (mobile and desktop) if you care about responsive tokens.",[258,1645,1646,1649],{},[446,1647,1648],{},"Cookie banners poison the palette."," Consent overlays and chat widgets bring their own colors and fonts, and they are often thousands of DOM nodes. Dismiss them or exclude their containers before collecting.",[258,1651,1652,671,1655,1658,1659,1662,1663,1666],{},[446,1653,1654],{},"Fonts can lie.",[22,1656,1657],{},"font-family"," lists what CSS asked for, not what loaded. With ",[22,1660,1661],{},"font-display: swap"," the brand font may never arrive in a headless run. Await ",[22,1664,1665],{},"document.fonts.ready"," before collecting if exact loaded fonts matter.",[258,1668,1669,1675,1676,1679],{},[446,1670,1671,1672,115],{},"Gradients are not in ",[22,1673,1674],{},"backgroundColor"," They live in ",[22,1677,1678],{},"backgroundImage",". If a brand is gradient-heavy, collect that property too or you will report their hero as plain white.",[258,1681,1682,1685,1686,1689,1690,1693],{},[446,1683,1684],{},"Shadow DOM and iframes are invisible"," to ",[22,1687,1688],{},"querySelectorAll",". Web-component-heavy sites need you to walk ",[22,1691,1692],{},"shadowRoot","s explicitly.",[117,1695,1697],{"id":1696},"diy-vs-parsing-css-vs-an-api","DIY vs parsing CSS vs an API",[987,1699,1700,1722],{},[990,1701,1702],{},[993,1703,1704,1707,1710,1713,1716,1719],{},[996,1705,1706],{},"Approach",[996,1708,1709],{},"Tailwind / utility CSS",[996,1711,1712],{},"Runtime CSS-in-JS",[996,1714,1715],{},"Resolves var() and cascade",[996,1717,1718],{},"Media queries",[996,1720,1721],{},"Maintenance",[1008,1723,1724,1742,1760],{},[993,1725,1726,1729,1732,1734,1736,1739],{},[1013,1727,1728],{},"Playwright + computed styles (this guide)",[1013,1730,1731],{},"Yes",[1013,1733,1731],{},[1013,1735,1731],{},[1013,1737,1738],{},"Current viewport only",[1013,1740,1741],{},"Browser infra is yours to run",[993,1743,1744,1747,1750,1752,1754,1757],{},[1013,1745,1746],{},"Parse CSS files yourself (tinycss2, no browser)",[1013,1748,1749],{},"Painful",[1013,1751,1059],{},[1013,1753,1059],{},[1013,1755,1756],{},"Declared, but unused values included",[1013,1758,1759],{},"Low, but wrong on modern sites",[993,1761,1762,1765,1767,1770,1772,1775],{},[1013,1763,1764],{},"MiroMiro extract API",[1013,1766,1731],{},[1013,1768,1769],{},"No (static HTML + CSS, no JS execution)",[1013,1771,1731],{},[1013,1773,1774],{},"Yes, all declared breakpoints",[1013,1776,1777],{},"None",[11,1779,1780,1781,1784],{},"Use the Playwright approach if you are extracting from a handful of sites, need runtime-injected styles, and want full control. Skip the hand-rolled CSS-parsing route: resolving the cascade, specificity, and nested ",[22,1782,1783],{},"var()"," fallbacks yourself is a project, not a script.",[117,1786,1788],{"id":1787},"or-use-one-api-call","Or use one API call",[11,1790,1791],{},"The dedupe, the ranking, the var() chains, the resets, and every declared breakpoint - that is what our extract endpoint does for a living:",[15,1793,1794],{"className":17,"code":481,"language":19,"meta":20,"style":20},[22,1795,1796,1808],{"__ignoreMap":20},[25,1797,1798,1800,1802,1804,1806],{"class":27,"line":28},[25,1799,488],{"class":31},[25,1801,336],{"class":246},[25,1803,493],{"class":35},[25,1805,300],{"class":246},[25,1807,52],{"class":51},[25,1809,1810,1812,1814,1816,1818],{"class":27,"line":55},[25,1811,502],{"class":35},[25,1813,336],{"class":246},[25,1815,507],{"class":35},[25,1817,510],{"class":51},[25,1819,513],{"class":246},[11,1821,1822,1823,1826,1827,1826,1830,1826,1833,1826,1836,1826,1839,1826,1842,1845,1846,1849],{},"The JSON response has ",[22,1824,1825],{},"colors",", ",[22,1828,1829],{},"fontFamilies",[22,1831,1832],{},"fontSizes",[22,1834,1835],{},"fontWeights",[22,1837,1838],{},"spacing",[22,1840,1841],{},"borderRadiuses",[22,1843,1844],{},"boxShadows",", and more, already deduped and ranked. Trim it with ",[22,1847,1848],{},"?fields=colors,spacing"," if you only need part of it.",[11,1851,1852,1853,1856,1857,115],{},"If your script from this guide breaks on some site's cookie banner at 2am, that is roughly the moment an API earns its keep. You can ",[111,1854,1855],{"href":140},"get a free API key"," with 100 credits a month, no card, or try it without signing up in the ",[111,1858,1860],{"href":1859},"/api/demo","playground",[117,1862,1864],{"id":1863},"summary","Summary",[11,1866,1867,1868,1870,1871,1874],{},"Playwright plus ",[22,1869,1524],{}," plus a ",[22,1872,1873],{},"Counter"," is the honest way to extract design tokens in Python, and the frequency counting matters more than any clever clustering. Parse-the-CSS approaches are not worth your time on modern sites. I hope this saved you a debugging evening.",[117,1876,523],{"id":522},[525,1878,1880],{"id":1879},"can-i-extract-design-tokens-without-a-headless-browser","Can I extract design tokens without a headless browser?",[11,1882,1883],{},"You can parse the site's CSS with a library like tinycss2, but on modern sites this works badly: CSS-in-JS generates styles at runtime, Tailwind buries the palette in utility classes, and you cannot tell which declared values are actually used. Computed styles from a real browser are the ground truth.",[525,1885,1887],{"id":1886},"how-do-i-extract-tokens-from-a-tailwind-site-where-everything-is-utility-classes","How do I extract tokens from a Tailwind site where everything is utility classes?",[11,1889,1890,1891,1893,1894,1685,1897,1900],{},"Ignore the class names entirely and read computed styles. ",[22,1892,1524],{}," resolves ",[22,1895,1896],{},"bg-indigo-600",[22,1898,1899],{},"rgb(79, 70, 229)"," like any other CSS, so nothing in this guide changes.",[525,1902,1904],{"id":1903},"why-do-my-extracted-colors-include-values-the-site-never-visibly-uses","Why do my extracted colors include values the site never visibly uses?",[11,1906,1907],{},"Three usual suspects: browser defaults on elements you collected, cookie banners and chat widgets injecting their own palette, and transparent or inherited values you forgot to filter.",[525,1909,1911],{"id":1910},"how-many-pages-should-i-sample","How many pages should I sample?",[11,1913,1914],{},"One landing page is usually enough for brand color and typography. Add one product or app page for functional colors (success, error, info). More than three pages rarely changes the top tokens.",[117,1916,1918],{"id":1917},"related-reading","Related reading",[255,1920,1921,1928,1935],{},[258,1922,1923,1927],{},[111,1924,1926],{"href":1925},"/api/design-tokens-api","Design tokens API"," - the endpoint this post's shortcut uses",[258,1929,1930,1934],{},[111,1931,1933],{"href":1932},"/api/docs/design-tokens","Design tokens docs"," - full response shape and field list",[258,1936,1937,1940],{},[111,1938,1939],{"href":129},"Ground truth for AI agents"," - why agents need real tokens instead of guessing from screenshots",[563,1942,1943],{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}",{"title":20,"searchDepth":55,"depth":55,"links":1945},[1946,1947,1948,1949,1950,1951,1952,1953,1959],{"id":1284,"depth":55,"text":1285},{"id":1536,"depth":55,"text":1537},{"id":1585,"depth":55,"text":1586},{"id":1107,"depth":55,"text":1108},{"id":1696,"depth":55,"text":1697},{"id":1787,"depth":55,"text":1788},{"id":1863,"depth":55,"text":1864},{"id":522,"depth":55,"text":523,"children":1954},[1955,1956,1957,1958],{"id":1879,"depth":66,"text":1880},{"id":1886,"depth":66,"text":1887},{"id":1903,"depth":66,"text":1904},{"id":1910,"depth":66,"text":1911},{"id":1917,"depth":55,"text":1918},"Tutorials","Extract colors, fonts, spacing, and shadows from any website with Playwright in Python - working code, the gotchas that bite, and the one-API-call shortcut.",[1963,1965,1967,1969],{"question":1880,"answer":1964},"You can parse the site's CSS files with a library like tinycss2, but on modern sites this works badly. CSS-in-JS generates styles at runtime, utility-class sites like Tailwind bury the palette in thousands of class rules, and you cannot tell which declared values are actually used. Computed styles from a real browser are the ground truth.",{"question":1887,"answer":1966},"Ignore the class names entirely and read computed styles. getComputedStyle resolves bg-indigo-600 to rgb(79, 70, 229) the same way it resolves any other CSS, so the frequency-counting approach in this guide works unchanged.",{"question":1904,"answer":1968},"Three usual suspects: browser default styles on elements you collected, cookie banners and chat widgets injecting their own palette, and transparent or inherited values you forgot to filter. Count frequency, filter defaults, and dismiss overlays before collecting.",{"question":1970,"answer":1971},"How many pages should I sample to get a reliable palette?","One page is usually enough for brand color and typography if it is the homepage or a landing page. Add one product or app page for the functional palette (success, error, info states). More than three pages rarely changes the top tokens.",{},"/api-blog/how-to-extract-design-tokens-from-any-website-in-python",[1975,603,1976],"best-design-token-extraction-tools-2026","how-to-extract-website-color-palette-programmatically",{"title":1269,"description":1961},"api-blog/how-to-extract-design-tokens-from-any-website-in-python",[610,1322,1309,1980,1981],"css","web extraction","L5qnHk0I6Xw2LZ4KpLx52C6CU18QlatcV45aNNbr8F0",{"id":1984,"title":1985,"author":6,"body":1986,"category":2561,"date":582,"dateModified":583,"description":2562,"extension":585,"faqs":2563,"image":583,"meta":2574,"navigation":84,"path":2575,"readingTime":380,"relatedSlugs":2576,"seo":2578,"stem":2579,"tags":2580,"__hash__":2583},"apiBlog/api-blog/design-token-types-reference.md","Design Token Types: Every Token a Website Exposes (Reference)",{"type":8,"value":1987,"toc":2550},[1988,1991,1999,2003,2365,2369,2375,2387,2393,2402,2406,2412,2442,2502,2504,2508,2511,2515,2518,2522,2525,2529,2547],[11,1989,1990],{},"This is the reference we wish had existed when we built our extraction engine: every token type a live website exposes, the CSS properties each one comes from, and the gotcha that silently corrupts it. Seventeen groups, one table each way.",[11,1992,1993,1994,1998],{},"If you want the extraction code itself, that is in the ",[111,1995,1997],{"href":1996},"/api/blog/how-to-extract-design-tokens-from-any-website-in-python","Python tokens guide",". This page is the map.",[117,2000,2002],{"id":2001},"the-complete-token-table","The complete token table",[987,2004,2005,2021],{},[990,2006,2007],{},[993,2008,2009,2012,2015,2018],{},[996,2010,2011],{},"Token group",[996,2013,2014],{},"CSS sources",[996,2016,2017],{},"What good output looks like",[996,2019,2020],{},"The gotcha",[1008,2022,2023,2051,2077,2092,2112,2136,2152,2174,2196,2218,2234,2250,2266,2282,2304,2329,2348],{},[993,2024,2025,2028,2045,2048],{},[1013,2026,2027],{},"Colors",[1013,2029,2030,1826,2033,1826,2036,1826,2039,1826,2042],{},[22,2031,2032],{},"color",[22,2034,2035],{},"background-color",[22,2037,2038],{},"border-*-color",[22,2040,2041],{},"fill",[22,2043,2044],{},"stroke",[1013,2046,2047],{},"5-12 ranked values, not 200",[1013,2049,2050],{},"Browser defaults and cookie banners pollute counts",[993,2052,2053,2056,2064,2071],{},[1013,2054,2055],{},"Gradients",[1013,2057,2058,1826,2061],{},[22,2059,2060],{},"background-image",[22,2062,2063],{},"mask-image",[1013,2065,2066,2067,2070],{},"Full ",[22,2068,2069],{},"linear-gradient(...)"," strings",[1013,2072,2073,2074,2076],{},"Not in ",[22,2075,2035],{}," - miss this and gradient brands report as white",[993,2078,2079,2082,2086,2089],{},[1013,2080,2081],{},"Font families",[1013,2083,2084],{},[22,2085,1657],{},[1013,2087,2088],{},"1-3 families with roles (heading/body/mono)",[1013,2090,2091],{},"The stack lists what CSS asked for, not what loaded",[993,2093,2094,2097,2102,2105],{},[1013,2095,2096],{},"Font sizes",[1013,2098,2099],{},[22,2100,2101],{},"font-size",[1013,2103,2104],{},"A scale: 12, 14, 16, 20, 24, 36, 56",[1013,2106,2107,2108,2111],{},"Fluid ",[22,2109,2110],{},"clamp()"," values resolve differently per viewport",[993,2113,2114,2117,2122,2125],{},[1013,2115,2116],{},"Font weights",[1013,2118,2119],{},[22,2120,2121],{},"font-weight",[1013,2123,2124],{},"3-5 weights actually used",[1013,2126,2127,2128,2131,2132,2135],{},"Keyword values (",[22,2129,2130],{},"bold",") and numbers (",[22,2133,2134],{},"700",") need normalizing",[993,2137,2138,2141,2146,2149],{},[1013,2139,2140],{},"Line heights",[1013,2142,2143],{},[22,2144,2145],{},"line-height",[1013,2147,2148],{},"Unitless ratios per size tier",[1013,2150,2151],{},"Computed styles return px; divide by font-size to recover the ratio",[993,2153,2154,2157,2162,2169],{},[1013,2155,2156],{},"Letter spacing",[1013,2158,2159],{},[22,2160,2161],{},"letter-spacing",[1013,2163,2164,2165,2168],{},"Tight values on headings, ",[22,2166,2167],{},"normal"," elsewhere",[1013,2170,2171,2173],{},[22,2172,2167],{}," is a value, not an absence",[993,2175,2176,2179,2190,2193],{},[1013,2177,2178],{},"Spacing",[1013,2180,2181,1826,2184,1826,2187],{},[22,2182,2183],{},"margin",[22,2185,2186],{},"padding",[22,2188,2189],{},"gap",[1013,2191,2192],{},"A grid: 4, 8, 12, 16, 24, 32, 48, 64",[1013,2194,2195],{},"Snap to the base unit or you get 40 near-duplicates",[993,2197,2198,2201,2206,2209],{},[1013,2199,2200],{},"Border radii",[1013,2202,2203],{},[22,2204,2205],{},"border-radius",[1013,2207,2208],{},"2-4 values (subtle, card, pill)",[1013,2210,2211,1609,2214,2217],{},[22,2212,2213],{},"9999px",[22,2215,2216],{},"50%"," are the same intent, different strings",[993,2219,2220,2223,2228,2231],{},[1013,2221,2222],{},"Border widths",[1013,2224,2225],{},[22,2226,2227],{},"border-width",[1013,2229,2230],{},"1-2 values",[1013,2232,2233],{},"Mostly 1px everywhere; the outliers are the signal",[993,2235,2236,2239,2244,2247],{},[1013,2237,2238],{},"Box shadows",[1013,2240,2241],{},[22,2242,2243],{},"box-shadow",[1013,2245,2246],{},"2-4 elevation levels",[1013,2248,2249],{},"Multi-layer shadows are one token, not four",[993,2251,2252,2255,2260,2263],{},[1013,2253,2254],{},"Text shadows",[1013,2256,2257],{},[22,2258,2259],{},"text-shadow",[1013,2261,2262],{},"Usually empty; notable when not",[1013,2264,2265],{},"Rare enough that any hit is worth keeping",[993,2267,2268,2271,2276,2279],{},[1013,2269,2270],{},"Opacities",[1013,2272,2273],{},[22,2274,2275],{},"opacity",[1013,2277,2278],{},"Disabled/overlay/hover steps",[1013,2280,2281],{},"Transition intermediate values sneak in if you sample mid-animation",[993,2283,2284,2287,2295,2298],{},[1013,2285,2286],{},"Durations",[1013,2288,2289,1826,2292],{},[22,2290,2291],{},"transition-duration",[22,2293,2294],{},"animation-duration",[1013,2296,2297],{},"150ms/300ms/500ms tiers",[1013,2299,2300,2303],{},[22,2301,2302],{},"0s"," is a default, not a token",[993,2305,2306,2309,2317,2323],{},[1013,2307,2308],{},"Easings",[1013,2310,2311,1826,2314],{},[22,2312,2313],{},"transition-timing-function",[22,2315,2316],{},"animation-timing-function",[1013,2318,2319,2320],{},"The brand's ",[22,2321,2322],{},"cubic-bezier(...)",[1013,2324,2325,2328],{},[22,2326,2327],{},"ease"," defaults drown the custom curves - filter them",[993,2330,2331,2334,2339,2342],{},[1013,2332,2333],{},"Z-index layers",[1013,2335,2336],{},[22,2337,2338],{},"z-index",[1013,2340,2341],{},"The layering scale: 10, 50, 100, 9999",[1013,2343,2344,2347],{},[22,2345,2346],{},"auto"," everywhere except the 5 values that matter",[993,2349,2350,2353,2359,2362],{},[1013,2351,2352],{},"Breakpoints",[1013,2354,2355,2358],{},[22,2356,2357],{},"@media"," conditions",[1013,2360,2361],{},"3-5 widths: 640, 768, 1024, 1280",[1013,2363,2364],{},"Only visible in stylesheets - computed styles never expose them",[117,2366,2368],{"id":2367},"the-same-table-by-extraction-difficulty","The same table, by extraction difficulty",[11,2370,2371,2374],{},[446,2372,2373],{},"Free (one property read):"," colors, font families, font sizes, font weights, opacities, z-index.",[11,2376,2377,2380,2381,2383,2384,2386],{},[446,2378,2379],{},"Needs normalizing:"," line heights (px to ratio), spacing (grid snapping), radii (",[22,2382,2213],{}," = ",[22,2385,2216],{}," = pill), weights (keywords to numbers), shadows (multi-layer grouping).",[11,2388,2389,2392],{},[446,2390,2391],{},"Needs the stylesheet, not computed styles:"," breakpoints, and any media-query variant of everything else. This is the structural reason a computed-styles-only extractor reports one viewport and calls it the design system.",[11,2394,2395,2398,2399,2401],{},[446,2396,2397],{},"Needs judgment:"," which color is ",[22,2400,1622],{}," (frequency plus element context - what is on the buttons), which durations are intentional vs defaults, which fonts actually loaded.",[117,2403,2405],{"id":2404},"how-this-maps-to-the-api","How this maps to the API",[11,2407,2408,2409,303],{},"Every group in the table is a field on our extract endpoint, selectable with ",[22,2410,2411],{},"?fields=",[15,2413,2415],{"className":17,"code":2414,"language":19,"meta":20,"style":20},"curl \"https://miromiro.app/api/v1/extract?url=stripe.com&fields=colors,spacing,easings\" \\\n  -H \"Authorization: Bearer $MIROMIRO_API_KEY\"\n",[22,2416,2417,2430],{"__ignoreMap":20},[25,2418,2419,2421,2423,2426,2428],{"class":27,"line":28},[25,2420,488],{"class":31},[25,2422,336],{"class":246},[25,2424,2425],{"class":35},"https://miromiro.app/api/v1/extract?url=stripe.com&fields=colors,spacing,easings",[25,2427,300],{"class":246},[25,2429,52],{"class":51},[25,2431,2432,2434,2436,2438,2440],{"class":27,"line":55},[25,2433,502],{"class":35},[25,2435,336],{"class":246},[25,2437,507],{"class":35},[25,2439,510],{"class":51},[25,2441,513],{"class":246},[11,2443,2444,2445,1826,2447,1826,2450,1826,2452,1826,2454,1826,2456,1826,2459,1826,2462,1826,2464,1826,2466,1826,2469,1826,2471,1826,2474,1826,2477,1826,2480,1826,2483,1826,2486,2489,2490,2493,2494,2497,2498,2501],{},"The full field list: ",[22,2446,1825],{},[22,2448,2449],{},"gradients",[22,2451,1829],{},[22,2453,1832],{},[22,2455,1835],{},[22,2457,2458],{},"lineHeights",[22,2460,2461],{},"letterSpacings",[22,2463,1838],{},[22,2465,1841],{},[22,2467,2468],{},"borderWidths",[22,2470,1844],{},[22,2472,2473],{},"textShadows",[22,2475,2476],{},"opacities",[22,2478,2479],{},"durations",[22,2481,2482],{},"easings",[22,2484,2485],{},"zIndexes",[22,2487,2488],{},"breakpoints",". Add ",[22,2491,2492],{},"format=css"," or ",[22,2495,2496],{},"format=tailwind"," to get the same data as custom properties or a theme block instead of JSON. One call is 10 credits; ",[111,2499,2500],{"href":140},"100 credits a month are free",", no card.",[117,2503,523],{"id":522},[525,2505,2507],{"id":2506},"what-are-the-main-types-of-design-tokens","What are the main types of design tokens?",[11,2509,2510],{},"Color, typography (families/sizes/weights/line heights/letter spacing), spacing, borders (radii/widths), effects (shadows/gradients/opacities), motion (durations/easings), and structure (z-index, breakpoints). Seventeen groups in total from a live site.",[525,2512,2514],{"id":2513},"are-css-custom-properties-the-same-as-design-tokens","Are CSS custom properties the same as design tokens?",[11,2516,2517],{},"No. Custom properties are one storage format. Tailwind and CSS-in-JS sites often ship almost none while having rigorous token systems. Extract resolved values, not variable names.",[525,2519,2521],{"id":2520},"which-tokens-matter-most","Which tokens matter most?",[11,2523,2524],{},"Colors, type scale, spacing - in that order. They carry the identity. Radii, shadows, and motion are what make a rebuild feel like the original instead of a wireframe of it.",[525,2526,2528],{"id":2527},"can-i-extract-motion-tokens-from-a-website","Can I extract motion tokens from a website?",[11,2530,2531,2532,1826,2534,2536,2537,2539,2540,2542,2543,1609,2545,115],{},"Yes: ",[22,2533,2291],{},[22,2535,2294],{},", and their timing functions. Filter out ",[22,2538,2302],{}," and default ",[22,2541,2327],{}," or the real values drown. Our extract endpoint returns them as ",[22,2544,2479],{},[22,2546,2482],{},[563,2548,2549],{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":20,"searchDepth":55,"depth":55,"links":2551},[2552,2553,2554,2555],{"id":2001,"depth":55,"text":2002},{"id":2367,"depth":55,"text":2368},{"id":2404,"depth":55,"text":2405},{"id":522,"depth":55,"text":523,"children":2556},[2557,2558,2559,2560],{"id":2506,"depth":66,"text":2507},{"id":2513,"depth":66,"text":2514},{"id":2520,"depth":66,"text":2521},{"id":2527,"depth":66,"text":2528},"Reference","All 17 design token types a website exposes - the CSS properties each comes from, what good output looks like, and the gotcha that silently corrupts it.",[2564,2566,2569,2571],{"question":2507,"answer":2565},"Color tokens, typography tokens (families, sizes, weights, line heights, letter spacing), spacing tokens, border tokens (radii, widths), effect tokens (shadows, gradients, opacities), motion tokens (durations, easings), and structural tokens (z-index layers, breakpoints). A complete extraction from a live site covers around 17 distinct groups.",{"question":2567,"answer":2568},"Which CSS properties do design tokens come from?","Each token group maps to specific CSS properties: colors from color, background-color and border-color; the type scale from font-size; spacing from margin, padding and gap; radii from border-radius; shadows from box-shadow; motion from transition-duration, animation-duration and their timing functions; breakpoints from media query conditions.",{"question":2514,"answer":2570},"No. Custom properties are one way a site stores tokens. Many production sites - especially Tailwind and CSS-in-JS codebases - ship few or no custom properties while still having a rigorous token system. Extract from resolved values, not variable names, or you will miss most real-world systems.",{"question":2572,"answer":2573},"What order should I extract tokens in?","Colors, type scale, and spacing first - they carry most of a site's identity and are enough to theme a rough UI. Radii, shadows, and motion are the polish layer that makes a rebuild feel like the original.",{},"/api-blog/design-token-types-reference",[602,1975,2577],"how-to-detect-fonts-a-website-uses",{"title":1985,"description":2562},"api-blog/design-token-types-reference",[610,2581,1980,2582],"reference","cheatsheet","Q2H5fOPEU9oXwkGDkaF1v9diKxKTdrdCh3xo9PqdAaM",1784751617318]