<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Lauri Lännenmäki</title>
    <description>The latest articles on DEV Community by Lauri Lännenmäki (@laurilllll).</description>
    <link>https://dev.to/laurilllll</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F378290%2F11b59b59-9c72-401f-82fe-a64c33d52b25.jpg</url>
      <title>DEV Community: Lauri Lännenmäki</title>
      <link>https://dev.to/laurilllll</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/laurilllll"/>
    <language>en</language>
    <item>
      <title>Remixing a template with an AI agent: from download to your own site in an hour</title>
      <dc:creator>Lauri Lännenmäki</dc:creator>
      <pubDate>Thu, 22 Oct 2026 08:16:55 +0000</pubDate>
      <link>https://dev.to/laurilllll/remixing-a-template-with-an-ai-agent-from-download-to-your-own-site-in-an-hour-85p</link>
      <guid>https://dev.to/laurilllll/remixing-a-template-with-an-ai-agent-from-download-to-your-own-site-in-an-hour-85p</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://no-code.supply/blog/remix-template-with-ai-agent/" rel="noopener noreferrer"&gt;no-code.supply&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can turn a website template into your own site in about an hour with an AI coding agent. &lt;strong&gt;The hour is real only if you already have your copy and images ready.&lt;/strong&gt; Writing them takes the rest of the day.&lt;/li&gt;
&lt;li&gt;The steps: &lt;strong&gt;unzip and open it&lt;/strong&gt;, &lt;strong&gt;let the agent read the &lt;code&gt;AGENTS.md&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;swap the brand&lt;/strong&gt; (name, colours, fonts), &lt;strong&gt;replace the copy&lt;/strong&gt;, &lt;strong&gt;add one section&lt;/strong&gt;, &lt;strong&gt;run the checks&lt;/strong&gt;, &lt;strong&gt;deploy&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Do it in that order, and one step per prompt. Big "redesign everything" prompts are where it goes wrong.&lt;/li&gt;
&lt;li&gt;I'm using &lt;a href="https://no-code.supply/templates/tender/" rel="noopener noreferrer"&gt;Tender&lt;/a&gt;, a landing page for company cards, in the HTML edition, and turning it into a product that has nothing to do with cards. The same steps work for the React edition and for any other template I sell.&lt;/li&gt;
&lt;li&gt;Prompts you can copy are below.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 &lt;a href="https://no-code.supply/" rel="noopener noreferrer"&gt;Browse the templates&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Why this works at all
&lt;/h2&gt;

&lt;p&gt;I build these templates with Claude Code. (&lt;a href="https://no-code.supply/blog/building-website-templates-with-ai/" rel="noopener noreferrer"&gt;Here's how that went.&lt;/a&gt;) Because I built them that way, I also wrote them so that an agent can edit them without breaking them. That's the whole trick.&lt;/p&gt;

&lt;p&gt;An agent is good at "change this, in the way this project already does things". It's bad at guessing what the rules of an unfamiliar project are. So every template ships with an &lt;code&gt;AGENTS.md&lt;/code&gt;, a file that says where things live, which rules must not break and what "done" means. (&lt;a href="https://no-code.supply/blog/how-to-write-agents-md/" rel="noopener noreferrer"&gt;What goes in one, and why&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;Your job in this walkthrough is small. Make sure the agent reads that file, give it clear steps, and check its work.&lt;/p&gt;




&lt;h2&gt;
  
  
  What you need
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A template.&lt;/strong&gt; I'm using the HTML edition of Tender. If you're not sure which edition to buy, &lt;a href="https://no-code.supply/blog/html-vs-react-vs-framer-templates/" rel="noopener noreferrer"&gt;here's how I'd choose&lt;/a&gt;. HTML is the easiest to start with.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An AI coding agent&lt;/strong&gt; that can read and edit files in a folder. Claude Code, Cursor, Codex or Copilot's agent mode all do. I use Claude Code, so the prompts below are written for it, but nothing in them is specific to one tool.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A code editor&lt;/strong&gt; to look at what changed. VS Code is fine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your content, ready.&lt;/strong&gt; This is the part people skip. Your product's name, a short description of what it does, a sentence for each feature, your prices, your contact details, and the images or logos you want to use. Put it all in one text file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A free account on a static host&lt;/strong&gt; for the last step. Vercel, Netlify or GitHub Pages all work.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No Node, no terminal and no build step for the HTML edition. For the React edition you'd also need Node, and you'd run &lt;code&gt;npm install&lt;/code&gt; and &lt;code&gt;npm run dev&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Download, unzip, open
&lt;/h2&gt;

&lt;p&gt;After you buy, the download is a zip file from Polar's customer portal. Unzip it somewhere you'll find it again.&lt;/p&gt;

&lt;p&gt;Tender is a landing page for a spend-management product: company cards for creative studios, with live budgets and receipts that match themselves. I picked it because its sections are the ones most product sites need (features, how it works, pricing, FAQ, a call to action), so it's a fair test of how far one template stretches. For this post I turned it into Larder, a made-up stock and ordering app for restaurant kitchens. No payment cards anywhere.&lt;/p&gt;

&lt;p&gt;For Tender's HTML edition the folder looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;index.html                the page, all the text in it
404.html, privacy.html
assets/css/tokens.css     colours, fonts, sizes. Start here to rebrand
assets/css/main.css       layout and section styles, in page order
assets/js/effects/        the card, the toasts, the calculator and the other demos
assets/fonts/             the included fonts, with their licences
assets/img/               favicon, social-share image
README.md, DESIGN.md, AGENTS.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Double-click &lt;code&gt;index.html&lt;/code&gt;. The site opens in your browser. That's the template, running, before you've done anything.&lt;/p&gt;

&lt;p&gt;Now open the folder in your agent. In Claude Code that's &lt;code&gt;cd&lt;/code&gt; into the folder and run &lt;code&gt;claude&lt;/code&gt;. In Cursor it's File, Open Folder.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fofypweoz5ey56eho8etr.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fofypweoz5ey56eho8etr.webp" alt="The Tender template open in a browser: the headline “Every card knows which job it’s for.”, a green metal card tilting in the hero and two notifications beside it" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Tender as it comes out of the zip, opened straight from the folder.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Let the agent read the AGENTS.md
&lt;/h2&gt;

&lt;p&gt;This step takes thirty seconds and saves the most trouble.&lt;/p&gt;

&lt;p&gt;Most agents read &lt;code&gt;AGENTS.md&lt;/code&gt; on their own when they start. Don't rely on it. Ask, and make the agent tell you what it found. If it can't repeat the rules back, it didn't take them in.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Read AGENTS.md, README.md and DESIGN.md in this folder. Then tell me in
five bullets: what this site is, where the colours and fonts live, where
the copy lives, which rules must never break, and what has to be true
before you say a change is done. Don't edit anything yet.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Tender, a good answer mentions things like these, all from its &lt;code&gt;AGENTS.md&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There is &lt;strong&gt;no build step and there must never be one.&lt;/strong&gt; The page has to keep working when &lt;code&gt;index.html&lt;/code&gt; is opened from disk.&lt;/li&gt;
&lt;li&gt;Colours and fonts live in &lt;code&gt;assets/css/tokens.css&lt;/code&gt;, not in &lt;code&gt;main.css&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The demos ship in their finished state. If you change a number a demo starts from, the HTML has to change with it.&lt;/li&gt;
&lt;li&gt;No external requests: no CDNs, no Google Fonts, no analytics.&lt;/li&gt;
&lt;li&gt;Exactly one &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt;, visible focus styles, and &lt;code&gt;prefers-reduced-motion&lt;/code&gt; respected.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the summary skips the build-step rule, say it once more in your own words. Without it, the first thing a helpful agent does is add a bundler.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: Swap the brand
&lt;/h2&gt;

&lt;p&gt;Do the brand in three small passes, so you can look after each one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Colours first.&lt;/strong&gt; Tender's &lt;code&gt;tokens.css&lt;/code&gt; starts with a block called "Brand, start here": a background, a primary colour, a secondary one, an accent and the text colour. Every translucent version of them (hairlines, shadows, glass) is mixed from those values, so changing five lines recolours most of the page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Rebrand the colours. Primary: a deep plum, #3B1E3F. Secondary: butter,
#F2D38A. Accent: tomato, #E5482F. Background: a warm white, #F7F3EE.
Change them in the Brand block of assets/css/tokens.css, and update the
darker and paler versions below it to match. Then search the whole
folder for the old hex values and list every place they still appear.
Check that text in the accent colour still reaches 4.5:1 on the
background, and tell me the ratios you got.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That last search is the part that matters, because "most of the page" isn't all of it. Tender's README warns that a few brand values are typed out in the HTML pages, the web manifest and the favicon. In my run the search found more than the README lists: the footer's big wordmark, the small card inside the phone mock-up, the text on one of the feature tiles and the edge of the card were all green, written straight into &lt;code&gt;main.css&lt;/code&gt;. Each was a line or two to change, and the page would have shipped with green bits on a plum brand without them.&lt;/p&gt;

&lt;p&gt;The card needed its own decision. Tender's README says the card finishes are illustration, not brand colour, so they don't follow the tokens. That's on purpose: a metal card has its own shading. The agent restyled the green finish into a plum one, following the README's "Change the card design" recipe, and renamed the swatches in the card maker to match.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Then fonts.&lt;/strong&gt; Swapping a font is more than changing a name. The file has to be in the folder, there has to be an &lt;code&gt;@font-face&lt;/code&gt; rule, and the token has to point at it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Replace the sans font with &amp;lt;your font&amp;gt;. I've put the .woff2
files in assets/fonts/. Add the @font-face rules in assets/css/fonts.css,
update --f-sans in tokens.css, and update the font preload
script in the head of every HTML page if file names changed. Put the
licences in assets/fonts/licenses/ and update CREDITS.md and fonts.json.
No Google Fonts links.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"Every HTML page" is there because of my run. The README says to update the preload script in &lt;code&gt;index.html&lt;/code&gt;. The 404 and privacy pages have the same script, and they'd have kept asking for font files that no longer exist.&lt;/p&gt;

&lt;p&gt;The "No Google Fonts links" line is not decoration. An agent's first instinct for a new font is a link to a font service. That breaks the no-external-requests rule and your privacy page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pick a font with the same proportions.&lt;/strong&gt; A designed layout is fitted to its font: three-line headlines, buttons sized to their labels, numbers lined up in columns. Swap in a wider font and headlines wrap onto a fourth line; a narrower one leaves gaps. Tender is set in Onest, so I asked the agent to compare the self-hosted fonts I already had against it: the width of the headline and of a line of body text, and the height of a lowercase x.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Compare these fonts against Onest at the weights the page uses: the
width of the hero headline at 600, the width of a body sentence at 400,
and the x-height. Report each as a ratio to Onest, then tell me which is
closest but still looks clearly different.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Schibsted Grotesk came out within 1% on every measure: the headline 0.6% wider, body text 0.7% narrower, the same x-height. It looks clearly different, with sharper, more squared-off letters than Onest's rounded ones, but every line on the page breaks in the same place. For the monospace I used Geist Mono, which I already had.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Then the name.&lt;/strong&gt; The product name is in the nav, the title tag, the footer, the 404 and privacy pages, the web manifest, the social-share tags, the card itself and the notifications.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The product is called "&amp;lt;your name&amp;gt;", not Tender. Find every place the
old name appears that a visitor or a search engine can see (all three
HTML pages, site.webmanifest, the meta tags, the structured data in the
head, alt text) and update it. Leave the window.Tender namespace in the
scripts alone. List the files you changed.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things from my run. First, don't let the agent find-and-replace the name across the whole folder. The scripts use it as their namespace (&lt;code&gt;window.Tender&lt;/code&gt;), and renaming that is how you break every demo on the page at once. Second, the name isn't only in the HTML. The little "tender" label on each notification in the hero is written in &lt;code&gt;toasts.js&lt;/code&gt;. Ask the agent to search the scripts for the name as visible text, not as code.&lt;/p&gt;

&lt;p&gt;Open the page, refresh, look. If something is off, fix it now, before the copy goes in.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwprw7629167r6xbpok33.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwprw7629167r6xbpok33.webp" alt="The same page rebranded as Larder: a plum metal card, butter and tomato accents, Schibsted Grotesk type and the headline “Every card knows which kitchen it’s for.”" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;After the brand pass: plum, butter and tomato, Schibsted Grotesk, and the name Larder. At this point it's still a card product. That changes in the next step.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Replace the copy, or the whole idea
&lt;/h2&gt;

&lt;p&gt;This is the long step, and it's the one that decides whether you finish in an hour.&lt;/p&gt;

&lt;p&gt;In the HTML edition most of the text is in &lt;code&gt;index.html&lt;/code&gt;, one section after another, and each section starts with a banner comment that says "SECTION:" and the section's name, like Hero or Pricing, so you and the agent can search for it.&lt;/p&gt;

&lt;p&gt;Work one section at a time. Paste your content file in, and tell the agent which part is for which section.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Here is my content (below). Rewrite the Hero section of index.html using
it. Keep the same HTML structure, classes and data attributes, and keep
headlines about the same length as the originals so they still fit the
layout. Change text only. Don't touch any other section.

&amp;lt;paste the hero copy&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the same for Features, How it works, Pricing, and so on.&lt;/p&gt;

&lt;p&gt;Two honest warnings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Length matters in a designed layout.&lt;/strong&gt; Tender's headline is three short lines with a hand-drawn underline under one word. In my run the headline went from "Every card knows which job it's for" to "Stock that orders its own lemons". I kept the new lines about as long as the old ones (ten, ten and twelve characters), so the headline still breaks into three lines and the underline still sits under one word. Check it at phone width straight away.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't ask the agent to invent your content.&lt;/strong&gt; It will, confidently, and it will sound like every other product. The template's copy is written in a specific voice. Yours should come from you. Use the agent to fit your words in, and to tidy them, not to produce them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some of Tender's copy isn't in the HTML, because it changes while you watch: the notifications in the hero, the receipts and approvals in the feature tiles, and the calculator's sentence. The README's "Edit the text" section lists which script holds each one. Give the agent that list, or it will rewrite the HTML and leave the old product's receipts playing on a loop.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your currency is part of the copy.&lt;/strong&gt; Tender is priced in euros. Larder is American, so in my run everything moved to dollars. Tender's &lt;code&gt;AGENTS.md&lt;/code&gt; says amounts are formatted in four scripts, which need to change together, and the HTML has the same amounts typed out for when the page first loads.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Switch the site from euros to US dollars. AGENTS.md lists the four
scripts that format amounts: change the locale and currency in all four.
Then change every euro amount typed into index.html and the scripts.
Before you finish, look for anything that converts between currencies
and check it still makes sense.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last sentence of that prompt is the one I added after my run. A plain swap of "€" for "$" left one feature tile wrong: it shows foreign spend converted into your home currency, so it listed "USD $860" being converted into dollars at the old euro rate. The fix was to make that row euros and give the tile dollar rates. It's the kind of mistake that looks fine at a glance, and that's exactly why it's worth asking for.&lt;/p&gt;

&lt;p&gt;Some things a currency swap shouldn't touch. Tender talks about VAT, and an American product would talk about sales tax. That's a decision about your business, not a find-and-replace, so in the first pass the agent listed those places for me instead of changing them. They went later, with the rest of the old idea.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Changing what the product is.&lt;/strong&gt; Most people stop at new words for the same kind of product. A template stretches further than that, if you know which parts are design and which parts are the demo product. In Tender, the card is everywhere: the hero, a "make a card" section, the phone screens, the freelancer tile and the fan of cards at the bottom. Larder doesn't sell cards, so all of it had to change.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Larder isn't a card product. It's stock and ordering software for
restaurant kitchens: counts from your phone, par levels per site,
supplier orders that write themselves, delivery notes checked against
the order. Go through the page section by section and list everything
that only makes sense for a card product: objects, demos, copy, legal
text, structured data. Don't change anything yet.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ask for the list first. Mine had more on it than I expected, and seeing it whole is how you decide what to replace, what to delete and what to keep. Then I worked through it one section at a time, like the copy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The hero card became a paper order.&lt;/strong&gt; Tender's card sits on a stage that tilts with your cursor. &lt;code&gt;AGENTS.md&lt;/code&gt; says the script only needs an element with the id &lt;code&gt;heroCard&lt;/code&gt;, so the agent swapped the card for a supplier order docket with the same id. The tilt, the shadow and the entrance animation all carried over without touching the script.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The card maker was deleted.&lt;/strong&gt; There was nothing to replace it with, and the README says a section can be cut safely. It can, on that page. The 404 and privacy pages have their own copy of the nav and footer, and both still linked to the deleted section. Search all the pages for links to anything you remove.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The demos got new data.&lt;/strong&gt; Receipts became delivery notes, approvals became big orders, freelancer cards became prep labels that count down to their use-by date. Most of that is data in &lt;code&gt;bento.js&lt;/code&gt; and the other effect scripts, so the HTML and the scripts change together.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The invisible parts.&lt;/strong&gt; The legal text in the footer described an e-money licence. The structured data in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; still listed the old plan names, priced in euros, which the currency pass had missed because it wasn't on the page. The privacy page described card payments. None of it is visible at a glance, and all of it would have shipped.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuu9vpn59iw23epmvy7ub.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuu9vpn59iw23epmvy7ub.webp" alt="Larder as a stock and ordering app: the headline “Stock that orders its own lemons.”, a paper supplier order for Ness Bay Seafood tilting on the stage, and notifications for low lemons and a sent order" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The same template as stock and ordering software. The tilting card is now a supplier order, on the same stage, moved by the same script.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A word on where this copy came from. For a made-up product, I let the agent write it, which is exactly what I said not to do with your real site. It's placeholder for a demo. Yours should come from you.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5: Add a section in the template's style
&lt;/h2&gt;

&lt;p&gt;This is the test of whether your agent is following the project, or just writing HTML.&lt;/p&gt;

&lt;p&gt;Tender's &lt;code&gt;AGENTS.md&lt;/code&gt; has a recipe for it: start from the Features or Calculator section, open with the numbered rail ("(06) Venues"), use the shared heading and lead paragraph, build with the existing panels and tiles, and add the section to both navs. If you ask for a new section without mentioning any of that, the agent will often write something that looks fine in isolation and wrong next to everything else.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Add a section called "Venues" between Stories and Pricing, using the
recipe in AGENTS.md ("Adding a section in Tender's style"). Three tiles,
one per restaurant, each with its monthly spend, its budget and a bar.
Use the existing tokens, tiles and reveal attributes, no new colours and
no inline styles beyond the bar's width. Add it to both navs, and
renumber the rails and the phone menu after it.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The words doing the work are "use the recipe in AGENTS.md". You're pointing the agent at the file, rather than describing the design again from memory.&lt;/p&gt;

&lt;p&gt;Tender numbers its sections, "(05)" for Stories, "(06)" for Pricing, and the phone menu repeats the numbers. A section in the middle means renumbering everything after it, and the recipe says so. That's why the prompt asks for it.&lt;/p&gt;

&lt;p&gt;Check the result. The new section should be impossible to tell apart from the ones around it: same rail, same heading, same tiles, same reveal. If it isn't, ask the agent to compare it with the section it copied and list the differences.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F55n9uxrylclicb4xh5ke.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F55n9uxrylclicb4xh5ke.webp" alt="A new Venues section: the rail “(06) Venues, Every site, one screen”, the headline “Three kitchens. One month-end.” and three tiles with each restaurant’s spend, budget and a progress bar" width="799" height="455"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The new Venues section. Built from the template's own tiles and tokens, plus a few lines of CSS for the grid and the bars.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 6: Replace the images
&lt;/h2&gt;

&lt;p&gt;Tender has almost no image files. The card, the phone, the receipts and the logos are drawn in HTML and CSS, so they followed the rebrand on their own. The order docket that replaced the card is HTML too, which is why the same docket could also fill the fan of cards at the bottom of the page. What's left is the social-share image, the home-screen icon and the logos in the marquee.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcqpt9t8zfioyqrzgs5fn.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcqpt9t8zfioyqrzgs5fn.webp" alt="The closing section: the headline “Put the clipboard down.” next to a fan of four paper supplier orders" width="800" height="459"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The closing section. The fan of metal cards is now a fan of supplier orders, built from the same docket as the hero.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Most templates have more images than that, and the steps are the same:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Put your images in &lt;code&gt;assets/img/&lt;/code&gt;, already resized. A hero image doesn't need to be 6000 pixels wide.&lt;/li&gt;
&lt;li&gt;Ask the agent to use them, with proper &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt;, real &lt;code&gt;alt&lt;/code&gt; text, and &lt;code&gt;loading="lazy"&lt;/code&gt; below the fold.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Replace the logos in the marquee with the SVG files in assets/img/logos/.
Follow the README's "Use your own logos and images", update the
marquee's aria-label to list the names, and don't link to any external
image host.

&amp;lt;one line per logo: file name and company name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also replace &lt;code&gt;assets/img/og-image.png&lt;/code&gt;, the picture shown when someone shares your link.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 7: Check it
&lt;/h2&gt;

&lt;p&gt;Every template I sell has the same definition of done: no console errors, no horizontal scroll from 360px to 1920px wide, visible keyboard focus, a complete page with reduced motion on, and one &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt;. Tender's &lt;code&gt;AGENTS.md&lt;/code&gt; ends with a checklist of this kind, and it adds its own items, like the page still making sense with JavaScript off.&lt;/p&gt;

&lt;p&gt;The point of that list is that you can hand it to the agent, and also check it yourself. Don't take "all done" as an answer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Run through the "Before you finish" checklist in AGENTS.md and report
each item as pass or fail, with how you checked it. Open the page at 360,
390, 768 and 1440px wide and tell me if anything overflows or wraps
badly. Search the project for any http:// or https:// URL that loads a
resource and list them. Check that there's still exactly one h1.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then do the parts an agent can't: open the site on your phone, tab through it with the keyboard, turn on reduced motion and reload, and run Lighthouse in Chrome DevTools. Don't compare your scores with the ones on the template's page. Compare them with the template you downloaded, before you changed anything.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fco5kd4aln74aiqbirr4b.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fco5kd4aln74aiqbirr4b.webp" alt="Larder on a 360-pixel-wide phone screen: the headline on three lines with “lemons” underlined, a notification and the tilted paper order" width="600" height="1267"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The new hero at 360 pixels wide.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;My remixed page had no console errors, made no requests to other servers, and didn't scroll sideways at any width from 360 to 1920 pixels. Lighthouse on mobile gave it 89 to 93 for performance, 96 for accessibility and 100 for best practices and SEO, the same as the untouched Tender on the same machine. (The 96 is Lighthouse catching the manifesto's words halfway through lighting up as you scroll. The untouched template gets the same note.)&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 8: Deploy
&lt;/h2&gt;

&lt;p&gt;The HTML edition is a folder of static files, so deploying it is the easy part.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Netlify or Vercel:&lt;/strong&gt; drag the folder onto the dashboard, or connect a Git repository. No build command, and the publish directory is the folder with &lt;code&gt;index.html&lt;/code&gt; in it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Pages:&lt;/strong&gt; push the folder to a repository and turn Pages on.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the React edition the template is a Next.js static export. Run &lt;code&gt;npm run build&lt;/code&gt; and publish the &lt;code&gt;out&lt;/code&gt; folder, or import the repository into Vercel or Netlify with &lt;code&gt;npm run build&lt;/code&gt; as the build command and &lt;code&gt;out&lt;/code&gt; as the output directory.&lt;/p&gt;

&lt;p&gt;When it's live, check three things the local version can't show you: your own domain works over HTTPS, the share image appears when you paste the link into a chat, and &lt;code&gt;robots.txt&lt;/code&gt; and the sitemap point at the right address.&lt;/p&gt;




&lt;h2&gt;
  
  
  How long it actually takes
&lt;/h2&gt;

&lt;p&gt;Here's the honest version. I timed my run with Claude Code. The agent's part took about twelve minutes: under two for the colours, the fonts and the name, under a minute for the dollars and the exchange-rate fix, under a minute for the Venues section, about seven for turning the card product into a stock app, and a minute or two for the checks. Lighthouse took longer than any single edit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Twelve minutes is the agent's time, not yours. Your part is reading what it changed and looking at the page after every step, and that's the part I'd never skip.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The copy in my run was placeholder for a made-up product, which is why it was quick. Real copy is the step that isn't fast. If you have it ready, the whole thing fits in about an hour. If you don't, you're writing for most of a day, and the agent can't change that for you. It can only fit what you write into the layout.&lt;/p&gt;

&lt;p&gt;I'd budget it like this: one hour for the template, and as long as you need for your own words.&lt;/p&gt;




&lt;h2&gt;
  
  
  What goes wrong, and how to steer it
&lt;/h2&gt;

&lt;p&gt;These are the ones I see most.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The agent adds a build step.&lt;/strong&gt; It wants a bundler, or a package.json, or a framework. Fix: the build-step rule is in &lt;code&gt;AGENTS.md&lt;/code&gt;, so quote it back: "No build step. The page must open from disk."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It reaches for an external font, icon set or CDN script.&lt;/strong&gt; Fix: "No external requests. Put it in &lt;code&gt;assets/&lt;/code&gt;." Then check the files for URLs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It renames things it shouldn't.&lt;/strong&gt; A find-and-replace on the template's name also hits the code that uses it. Fix: say which files the name lives in, and that &lt;code&gt;assets/js/&lt;/code&gt; isn't one of them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your words are longer than the demo's.&lt;/strong&gt; Headlines and wordmarks are sized for the demo text. Fix: check the page at phone width after every change, footer included.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It swaps symbols without reading what they mean.&lt;/strong&gt; A currency, a time zone or a unit can be part of how a demo works. Fix: ask it to check anything that converts or calculates after the swap.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deleting a section leaves links behind.&lt;/strong&gt; The page you deleted it from is fine. The other pages, with their own nav and footer, still point at it. Fix: ask the agent to search every HTML file for links to what you removed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It changes &lt;code&gt;main.css&lt;/code&gt; when it should change &lt;code&gt;tokens.css&lt;/code&gt;.&lt;/strong&gt; The colours drift, and your next rebrand takes twice as long. Fix: say which file in the prompt.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It says it's done and it isn't.&lt;/strong&gt; Fix: ask for the checklist item by item, with how it checked each one, and look at the page yourself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The session gets long and the agent starts forgetting the rules.&lt;/strong&gt; Fix: start a new session and have it read &lt;code&gt;AGENTS.md&lt;/code&gt; again. It has no memory of yesterday, only the file.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Do I need to know how to code?
&lt;/h3&gt;

&lt;p&gt;Not to follow this. You do need to be able to open a folder in an editor and look at a browser. If you can describe a change clearly, the agent can make it. The HTML edition is the gentlest start because there's nothing to install.&lt;/p&gt;

&lt;h3&gt;
  
  
  Which agent should I use?
&lt;/h3&gt;

&lt;p&gt;Whichever you already pay for. All the templates ship with an &lt;code&gt;AGENTS.md&lt;/code&gt;, which most agents read, and the React edition also has a &lt;code&gt;CLAUDE.md&lt;/code&gt; that points at it. I use Claude Code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Will the agent break the template?
&lt;/h3&gt;

&lt;p&gt;It can, and the &lt;code&gt;AGENTS.md&lt;/code&gt; is there to make that less likely. Keep the original download next to your working copy, or put the folder in Git, so you can always compare and undo.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I use the template for a client?
&lt;/h3&gt;

&lt;p&gt;Yes. The licence covers unlimited projects, your own and your clients', and you can charge for the sites you build and hand them over. What you can't do is resell or share the template itself, modified or not, as a template or starter. The &lt;a href="https://no-code.supply/license/" rel="noopener noreferrer"&gt;license page&lt;/a&gt; has the full terms.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrap-up
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy8gr2bkvcdi69e8c73rk.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy8gr2bkvcdi69e8c73rk.webp" alt="Before and after. Before: Tender as downloaded, a spend-management page with the headline “Every card knows which job it’s for.” and a green metal card. After: Larder, a stock and ordering app with the headline “Stock that orders its own lemons.”, a plum palette, a different typeface and a paper supplier order in place of the card" width="800" height="1102"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Same template, same layout, same scripts. A different product, about twelve minutes of agent time later.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The shape of the process is simple: make the agent read the rules, change one thing at a time, and check the result yourself. The template does the design. The agent does the typing. You do the part that was always yours, which is deciding what the site says.&lt;/p&gt;

&lt;p&gt;If you try it, I'd like to hear how long it took you. Tell me on &lt;a href="https://x.com/laurilllll" rel="noopener noreferrer"&gt;X&lt;/a&gt; or at &lt;a href="mailto:contact@no-code.supply"&gt;contact@no-code.supply&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://no-code.supply/templates/tender/" rel="noopener noreferrer"&gt;Tender&lt;/a&gt; is the template I used here. Every other template works the same way, so &lt;a href="https://no-code.supply/" rel="noopener noreferrer"&gt;pick one&lt;/a&gt; and start from its &lt;code&gt;README.md&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>html</category>
    </item>
    <item>
      <title>How to make your website stand out: seven ideas you can steal</title>
      <dc:creator>Lauri Lännenmäki</dc:creator>
      <pubDate>Thu, 22 Oct 2026 08:03:57 +0000</pubDate>
      <link>https://dev.to/laurilllll/how-to-make-your-website-stand-out-seven-ideas-you-can-steal-1np6</link>
      <guid>https://dev.to/laurilllll/how-to-make-your-website-stand-out-seven-ideas-you-can-steal-1np6</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://no-code.supply/blog/how-to-make-your-website-stand-out/" rel="noopener noreferrer"&gt;no-code.supply&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The thing people remember about a website is rarely the font or the colour. It's &lt;strong&gt;one idea that only makes sense for that business.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;The best place to find that idea is &lt;strong&gt;the thing you sell&lt;/strong&gt;: its numbers, its materials, its paperwork, its hours, its process.&lt;/li&gt;
&lt;li&gt;Seven examples from my own templates, one or two ideas each: &lt;strong&gt;lead with your one number&lt;/strong&gt;, &lt;strong&gt;use odd, true numbers&lt;/strong&gt;, &lt;strong&gt;borrow a format from print&lt;/strong&gt;, &lt;strong&gt;let people use the product&lt;/strong&gt;, &lt;strong&gt;show your working&lt;/strong&gt;, &lt;strong&gt;let the photos carry the effect&lt;/strong&gt;, &lt;strong&gt;draw the hard part&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Most of these cost nothing. &lt;strong&gt;The words and the numbers do most of the work.&lt;/strong&gt; The fancy effect is the last ten per cent.&lt;/li&gt;
&lt;li&gt;One test before you keep an idea: &lt;strong&gt;could it sit on a competitor's site?&lt;/strong&gt; If yes, it's decoration.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The follow-up to "keep one thing that's only yours"
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://no-code.supply/blog/is-your-website-ai-slop/" rel="noopener noreferrer"&gt;the post about AI slop&lt;/a&gt; I said every site needs one thing nobody else has. A photo, a line, a tool, a number that's true.&lt;/p&gt;

&lt;p&gt;That's easy to say and hard to do. So this post is the how.&lt;/p&gt;

&lt;p&gt;I'm Lauri Lännenmäki, a design engineer, and I make website templates. Every template starts with a made-up business and &lt;strong&gt;one interaction people should remember&lt;/strong&gt;. I pick that before any code exists. (&lt;a href="https://no-code.supply/blog/building-website-templates-with-ai/" rel="noopener noreferrer"&gt;More on how they're made.&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Below are seven of them, with what each one does and how you'd steal the idea for your own site. You don't need to buy anything, and you don't need to know what a shader is. Most of these work in plain HTML, in Framer, in Squarespace, wherever.&lt;/p&gt;




&lt;h2&gt;
  
  
  The rule: take it from the thing you sell
&lt;/h2&gt;

&lt;p&gt;Here's the pattern across all seven. The memorable part isn't an effect that someone picked from a gallery. It comes from the business itself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a bike shop leads with how much the bike weighs&lt;/li&gt;
&lt;li&gt;a restaurant shows its fire's opening hours&lt;/li&gt;
&lt;li&gt;a fund that writes long essays looks like a printed journal&lt;/li&gt;
&lt;li&gt;a type foundry lets you type&lt;/li&gt;
&lt;li&gt;a photographer's photos behave like film&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of those would make sense on anyone else's site. That's why they stick.&lt;/p&gt;

&lt;h3&gt;
  
  
  The swap test
&lt;/h3&gt;

&lt;p&gt;Before you keep an idea, ask two questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Could it sit on a competitor's site, unchanged?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Could it sit on a site in a completely different industry?&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A glowing gradient blob passes both. So does a bento grid. That's decoration. A seat map of &lt;em&gt;your&lt;/em&gt; dining room fails both, and that's the one you want.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Lead with your one number (Vela)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://no-code.supply/templates/vela/" rel="noopener noreferrer"&gt;Vela&lt;/a&gt; is a pre-order site for a city e-bike. The hero doesn't open with a slogan. It opens with &lt;strong&gt;15.8 kg&lt;/strong&gt;, set huge, and then one line: "The stairs just lost the argument."&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe1skattqsul7l113hik8.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe1skattqsul7l113hik8.webp" alt="Vela's hero: “15.8 kg” in huge type, the line “The stairs just lost the argument.”, a short paragraph about carrying the bike up four floors with a coffee, and a bright yellow e-bike on the right with its front light on." width="800" height="500"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Vela: the weight is the headline, and the paragraph says what it means.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The paragraph under it translates the number into a day: light enough to carry up four floors with a coffee in your other hand. A spec becomes a picture in your head.&lt;/p&gt;

&lt;p&gt;Further down, the range gets the same treatment. Instead of "90 km range" in a table, the page pins a city and rides through it as you scroll, from 07:40 to 22:15. "One charge. Sunrise to last orders."&lt;/p&gt;

&lt;p&gt;And when you pick a frame colour, the whole page repaints in it, not just the bike.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Steal this:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Find the one number that sums up why someone buys from you. Weight, time, price, distance, how many, how few.&lt;/li&gt;
&lt;li&gt;Make it the biggest thing on the page.&lt;/li&gt;
&lt;li&gt;Write the sentence under it that says &lt;strong&gt;what the number means in someone's day.&lt;/strong&gt; "15.8 kg" is a spec. "Carry it up four floors with a coffee" is a reason.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  2. Use odd, true numbers (Hearth)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://no-code.supply/templates/hearth/" rel="noopener noreferrer"&gt;Hearth&lt;/a&gt; is a tasting-menu restaurant in an old forge. There's a row of numbers about the fire:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;0&lt;/strong&gt; gas lines in the building. Everything is cooked on wood, or not cooked at all.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;11 m&lt;/strong&gt; chimney, laid in 1893.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;1.4 t&lt;/strong&gt; of oak burned a month.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;3&lt;/strong&gt; zones on the fire bed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv7p2is9nxek49wuzy8eg.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv7p2is9nxek49wuzy8eg.webp" alt="Hearth's “A night in the life of the fire” section on a dark background, with four stat cards: 0 gas lines in the building, an 11 m chimney laid in 1893, 1.4 tonnes of oak burned per month, and 3 zones on the fire bed." width="800" height="500"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Hearth: four numbers nobody else could put on their site.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Compare that with the usual row of stats: 10x faster, 99.9% uptime, 500+ happy customers. Round numbers sound made up, because they usually are. &lt;strong&gt;Odd numbers sound measured.&lt;/strong&gt; And a zero can say more than a big number.&lt;/p&gt;

&lt;p&gt;The other detail I like: a status line at the top of the page that says when the fire is lit. "The fire is resting · lit tomorrow, 16:00." It's worked out from the opening days, in the restaurant's own time zone, so it's always right. It's opening hours, written as if the building were alive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Steal this:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Write down ten true numbers about your business. Keep the ones only you could have. The year the building went up beats "est. 2019".&lt;/li&gt;
&lt;li&gt;Turn your opening hours into a sentence that changes with the time of day. "Open now, until 18:00" is a start.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  3. Borrow a format from print (Parallel)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://no-code.supply/templates/parallel/" rel="noopener noreferrer"&gt;Parallel&lt;/a&gt; is a venture fund that backs unfashionable infrastructure. Its headline is "The future is mostly plumbing." And the whole site is set like a printed quarterly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fig87lmf6v5c4pdzpuu9s.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fig87lmf6v5c4pdzpuu9s.webp" alt="Parallel's front page on warm paper: a masthead line reading “The Parallel Quarterly, Issue No. 3”, the serif headline “The future is mostly plumbing.”, a contents list with page numbers, and a “Fund III, at a glance” table." width="800" height="500"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Parallel: a fund website that reads like issue three of a journal.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The conventions come straight from paper:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an issue number and "filed from Pittsburgh &amp;amp; Rotterdam" under the header&lt;/li&gt;
&lt;li&gt;a page number in the header that changes as you scroll: "p. 1, Front page", then "p. 2, The thesis"&lt;/li&gt;
&lt;li&gt;a contents list with dotted leaders and page numbers&lt;/li&gt;
&lt;li&gt;a drop cap, and footnotes that slide into the margin&lt;/li&gt;
&lt;li&gt;figures with numbers, like "Fig. 1, Two partners, one direction". It labels the two hairlines across the page, which drift apart and back as you scroll and never touch.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even the portfolio table plays along: "Gaps in the numbering are companies in stealth."&lt;/p&gt;

&lt;p&gt;The format does a job before anyone reads a word. A fund that publishes long essays looks like it &lt;strong&gt;thinks in years, not quarters&lt;/strong&gt;. You believe the essay a little more because of how it's set.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Steal this:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Think of a printed thing your customers already trust. A menu, a receipt, a field guide, a ticket, a spec sheet, a lab report, a recipe card.&lt;/li&gt;
&lt;li&gt;Borrow &lt;strong&gt;three of its conventions&lt;/strong&gt;, not all of them. Page numbers and a drop cap are enough to read as "journal". Twelve of them and it's a costume.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  4. Let people use the product (Ferro Type)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://no-code.supply/templates/ferro/" rel="noopener noreferrer"&gt;Ferro Type&lt;/a&gt; is a type foundry. Most font sites show a pangram and a price. This one lets you play with the font before you're asked to buy it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz8xg0rne8q86qzmuh9fy.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz8xg0rne8q86qzmuh9fy.webp" alt="Ferro Type's type tester: the headline “Type something you’d never dare set in Arial.”, a panel of sliders for size, weight, width, slant and grade with presets above them, and the sample text “Ink traps stop letters” set very heavy and wide." width="800" height="500"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Ferro Type: every slider is a real axis in the font file.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The hero word reacts to your cursor. Each letter gets heavier the closer you are and wider depending on where you are.&lt;/li&gt;
&lt;li&gt;The type tester has a slider for each axis in the font file, and a button that copies the CSS for whatever you've made.&lt;/li&gt;
&lt;li&gt;The glyph inspector works with the arrow keys, "because we're not animals". Each glyph gets a note: "Flat apex, cut at 4°. A pointy one looked like it wanted to sell you something."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last bit matters as much as the tester. &lt;strong&gt;The small copy is where a site gets its voice.&lt;/strong&gt; Nobody reads a hero twice, but people do read the captions.&lt;/p&gt;

&lt;p&gt;(The demo uses Roboto Flex, a free variable font from Google Fonts, standing in for the foundry's own. If you've never played with variable font axes, it's a good one to start with.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Steal this:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What's the smallest piece of your product you can put on the page? A calculator, a sample, a before and after, a quiz, a sound.&lt;/li&gt;
&lt;li&gt;Then write the small stuff, like tooltips, captions and empty states, in the voice of the person who made it.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  5. Show your working (Datum)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://no-code.supply/templates/datum/" rel="noopener noreferrer"&gt;Datum&lt;/a&gt; is a portfolio for a designer who also builds. There's an &lt;strong&gt;Inspect&lt;/strong&gt; switch in the header (or press I). Turn it on and the page draws its own 12-column grid, the spacing between sections, the type specs, and a live measure of whatever's under your pointer. It reads all of that from the real layout, so it can't lie.&lt;/p&gt;

&lt;p&gt;For someone who sells design and code, the site &lt;em&gt;is&lt;/em&gt; the portfolio piece. Inspect mode just lets you check.&lt;/p&gt;

&lt;p&gt;The rest of the page is honest in a quieter way:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn68etyfmf6g3anvgpiuk.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn68etyfmf6g3anvgpiuk.webp" alt="Datum's services section on a dark background: “Three ways to work together.”, a 12-week ruler, and two services. Sprint is two weeks at a fixed €8,500, shown as two filled cells. Design and build is 6 to 12 weeks from €26,000, shown as white design cells overlapping with pink build cells." width="800" height="500"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Datum: prices on the page, and a week strip that shows how long each one takes.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prices are on the page.&lt;/strong&gt; A two-week sprint is a fixed €8,500.&lt;/li&gt;
&lt;li&gt;Every project and service has a &lt;strong&gt;week strip&lt;/strong&gt;: a row of cells split into design weeks and build weeks, on the same scale. You can see at a glance that one project took twelve weeks, and how much of that was building.&lt;/li&gt;
&lt;li&gt;The contact section says when the next opening is (2 February 2027) and how fast you'll get a reply.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Steal this:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you sell a service, show the process as a picture, not a paragraph. A row of boxes with weeks in them is enough.&lt;/li&gt;
&lt;li&gt;Put a price on the page, even a "from" price. Nothing feels more confident than a number.&lt;/li&gt;
&lt;li&gt;Say when you're next free. It answers the question people are too polite to ask.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  6. Let the photos carry the effect (Halation)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://no-code.supply/templates/halation/" rel="noopener noreferrer"&gt;Halation&lt;/a&gt; is a portfolio for a photographer who shoots in low light. Its effects all come from photography itself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The hero photo &lt;strong&gt;fades in from the dark&lt;/strong&gt; under a fine grain.&lt;/li&gt;
&lt;li&gt;An "hours" viewer steps through twenty photos in the order of the clock. Each one &lt;strong&gt;dims to dark and the next develops back up&lt;/strong&gt;, like a print in a tray.&lt;/li&gt;
&lt;li&gt;The series pull each frame &lt;strong&gt;into focus&lt;/strong&gt; as you scroll, next to a lens distance scale that turns as you go.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F79u96zkxgjuf7iv7jv3f.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F79u96zkxgjuf7iv7jv3f.webp" alt="Halation's hero: a full-bleed photo of a snowy fell at dusk with a lit cabin window, the photographer's name in huge wide capitals, and a small caption in the corner: “01.01 Kaamos. Pallas Fell, 12:40. No sunrise.”" width="800" height="500"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Halation: one photo, the name, and a caption with a place and a time.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Everything else stays out of the way: wide capitals for the name, small captions, no decoration. The interface is quiet so the photos can be loud.&lt;/p&gt;

&lt;p&gt;And look at the caption: "Kaamos. Pallas Fell, 12:40. No sunrise." A place, a time, and one fact. It does more for the photo than "Winter landscape" ever could.&lt;/p&gt;

&lt;p&gt;(Under the hood, the effects run as WebGPU shaders over the real images, with a plain CSS version of each for browsers that can't. I didn't write those shaders. I asked for the effect, and &lt;a href="https://no-code.supply/blog/how-i-design-now/" rel="noopener noreferrer"&gt;the agent built it&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Steal this:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If your work is visual, find the effect in your own medium. Film develops, lenses focus, paint dries, wood gets sanded, a cake rises. That's a better animation than a fade-up that every site has.&lt;/li&gt;
&lt;li&gt;Caption every image with where, when and one detail.&lt;/li&gt;
&lt;li&gt;When the images are good, make the rest of the page quieter, not louder.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  7. Draw the hard part (Good Dirt)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://no-code.supply/templates/good-dirt/" rel="noopener noreferrer"&gt;Good Dirt&lt;/a&gt; is a cracker brand that buys its grain from regenerative farms. It's the opposite of quiet: a yellow sunburst, illustrated boxes dropping into the hero, a headline that says "Crunch from the ground up."&lt;/p&gt;

&lt;p&gt;But its best idea is a diagram.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5qzn4r5jzykqumwh350v.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5qzn4r5jzykqumwh350v.webp" alt="Good Dirt's “Dig a little deeper” section: on the left, four numbered chapters by depth, starting with “0–30 cm, Topsoil: The thin bit that feeds us”; on the right, an illustrated cross-section of a wheat field with the soil layers and a depth ruler beside them." width="800" height="500"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Good Dirt: the why, drawn as a soil cross-section you dig through by scrolling.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;"Regenerative farming" is the reason the brand exists, and it's hard to explain in a sentence. So the page doesn't try. It pins a cross-section of a field and digs &lt;strong&gt;120 cm down&lt;/strong&gt; as you scroll. Roots, fungi and worms grow in as you pass each layer. The chapters have names like "The underground internet" and "The night shift". No soil-science degree required.&lt;/p&gt;

&lt;p&gt;The flavour cards do the voice trick from Ferro in a spec table:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tastes like:&lt;/strong&gt; sourdough, sea spray, Sunday&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pairs with:&lt;/strong&gt; smoked salmon, cream cheese, a Tuesday&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Crunch:&lt;/strong&gt; 9 / 10&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two real facts, then a joke in the last slot. It reads like a label and sounds like a person.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Steal this:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Find the part of your story people nod along to without understanding. Turn it into a drawing they move through: layers, steps, a timeline, a map.&lt;/li&gt;
&lt;li&gt;Write your product specs as a table, and let one line in each be human.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Where to look for your own idea
&lt;/h2&gt;

&lt;p&gt;You're not making a bike, a forge or a type foundry. That's fine. Every business has the same places to dig:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Numbers.&lt;/strong&gt; What you can measure: weight, hours, distance, years, batch numbers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Paperwork.&lt;/strong&gt; The documents your trade already has: menus, invoices, tickets, certificates, plans, receipts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tools and materials.&lt;/strong&gt; What's on your desk or your workbench, and what it does: a lens, a fire, a ruler, dough.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time.&lt;/strong&gt; When you're open, when things happen, how long the work takes, what changes by season.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Process.&lt;/strong&gt; The steps between "hello" and "done". People love seeing how something is made.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Words.&lt;/strong&gt; The jargon of your trade, explained once, kindly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pick &lt;strong&gt;one&lt;/strong&gt; of these and push it further than feels comfortable. One idea, done properly, beats five done halfway.&lt;/p&gt;

&lt;p&gt;If you work with an AI agent, it's surprisingly good at the brainstorm, as long as you give it the facts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Here's what my business does, who buys from me and why: [two or three
sentences]. List 20 things that only exist in my line of work: numbers I
could measure, documents and forms my trade uses, tools and materials,
things that happen at a particular time of day or year, and steps in how
the work gets done. For each, suggest one way it could appear on my
website. Skip anything that could appear on a website in another industry.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Most of the list will be meh. You're looking for the one that makes you go "oh, of course". Then, if you build with an agent, write it into your &lt;a href="https://no-code.supply/blog/how-to-write-agents-md/" rel="noopener noreferrer"&gt;AGENTS.md&lt;/a&gt; as a rule, so the agent doesn't smooth it away on the next change.&lt;/p&gt;




&lt;h2&gt;
  
  
  What you don't need
&lt;/h2&gt;

&lt;p&gt;Look back at the "steal this" lists. Almost nothing on them needs code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a big number and a sentence about it&lt;/li&gt;
&lt;li&gt;ten true numbers&lt;/li&gt;
&lt;li&gt;three conventions from a printed thing&lt;/li&gt;
&lt;li&gt;prices and dates&lt;/li&gt;
&lt;li&gt;captions with a place and a time&lt;/li&gt;
&lt;li&gt;a spec table with one joke in it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's writing and layout. The pinned scroll scenes and the shaders are the last ten per cent. They're fun, and the templates have them because templates are a showcase. But a site with the right number in the headline and no animation will be remembered. A site with a WebGL blob and "Elevate your workflow" won't.&lt;/p&gt;

&lt;p&gt;Do the cheap version first. Add motion only if it still needs it.&lt;/p&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What if my business is boring?
&lt;/h3&gt;

&lt;p&gt;Then you're in good company. Parallel's headline is literally about plumbing. "Boring" usually means "nobody has explained it well yet", and that's an opening. The numbers and the paperwork are often where boring businesses are most interesting.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do I need animation for a site to stand out?
&lt;/h3&gt;

&lt;p&gt;No. Every idea above has a version that's just text and layout. Motion helps when it shows something real, like a photo developing or a field being dug. It hurts when it's there because the builder had it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Won't one idea make the site feel gimmicky?
&lt;/h3&gt;

&lt;p&gt;Only if it's not true. A seat map is a gimmick at a restaurant that doesn't take bookings. Keep the idea tied to something real about you, and keep the rest of the page plain, and it reads as character, not a trick.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I use a template and still stand out?
&lt;/h3&gt;

&lt;p&gt;Yes, if you change the parts that carry the idea. Swap in your own number, your own captions, your own paperwork. If you keep the template's idea, you'll have someone else's idea. I wrote more about that in &lt;a href="https://no-code.supply/blog/is-your-website-ai-slop/" rel="noopener noreferrer"&gt;the slop post&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrap-up
&lt;/h2&gt;

&lt;p&gt;The sites people remember aren't the ones with the most effects. They're the ones where you can tell &lt;strong&gt;who made it and what they sell&lt;/strong&gt; in a few seconds, because the page is built out of that.&lt;/p&gt;

&lt;p&gt;So look at what you sell. Find the number, the paper, the tool, the hour or the step that only you have. Put it on the page, and push it.&lt;/p&gt;

&lt;p&gt;If you want to see these seven ideas working, every template has a live demo: &lt;a href="https://no-code.supply/templates/vela/" rel="noopener noreferrer"&gt;Vela&lt;/a&gt;, &lt;a href="https://no-code.supply/templates/hearth/" rel="noopener noreferrer"&gt;Hearth&lt;/a&gt;, &lt;a href="https://no-code.supply/templates/parallel/" rel="noopener noreferrer"&gt;Parallel&lt;/a&gt;, &lt;a href="https://no-code.supply/templates/ferro/" rel="noopener noreferrer"&gt;Ferro Type&lt;/a&gt;, &lt;a href="https://no-code.supply/templates/datum/" rel="noopener noreferrer"&gt;Datum&lt;/a&gt;, &lt;a href="https://no-code.supply/templates/halation/" rel="noopener noreferrer"&gt;Halation&lt;/a&gt; and &lt;a href="https://no-code.supply/templates/good-dirt/" rel="noopener noreferrer"&gt;Good Dirt&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>design</category>
      <category>ux</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Is your website AI slop? How to tell, and how to fix it</title>
      <dc:creator>Lauri Lännenmäki</dc:creator>
      <pubDate>Tue, 13 Oct 2026 18:57:55 +0000</pubDate>
      <link>https://dev.to/laurilllll/is-your-website-ai-slop-how-to-tell-and-how-to-fix-it-1fn5</link>
      <guid>https://dev.to/laurilllll/is-your-website-ai-slop-how-to-tell-and-how-to-fix-it-1fn5</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://no-code.supply/blog/is-your-website-ai-slop/" rel="noopener noreferrer"&gt;no-code.supply&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"AI slop" is what people call content that's cheap, generic and made in bulk. Websites get the label too, and people spot it in seconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Slop isn't about AI.&lt;/strong&gt; It's about nobody making the decisions. Themes and templates made sites look the same long before AI did. AI just made it free and instant.&lt;/li&gt;
&lt;li&gt;The tells fall into four groups: &lt;strong&gt;how it looks&lt;/strong&gt;, &lt;strong&gt;what it says&lt;/strong&gt;, &lt;strong&gt;what it claims&lt;/strong&gt; and &lt;strong&gt;what nobody checked&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Dodging a list doesn't work either. Ban Inter and purple, and you get Instrument Serif and mono captions instead. &lt;strong&gt;The list keeps growing.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;The fix isn't avoiding AI. It's making the calls AI can't make for you: &lt;strong&gt;your words, your photos, one thing that's only yours, and a check before you ship.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;A template is where your site starts. Ship it as it came and it'll look like everyone else's. &lt;strong&gt;That includes mine.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 &lt;a href="https://no-code.supply/" rel="noopener noreferrer"&gt;Browse the templates&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Yes, I build templates with AI
&lt;/h2&gt;

&lt;p&gt;I'm Lauri Lännenmäki, a design engineer. I've spent fifteen years designing and building for the web, and these days I build my website templates with an AI agent. (&lt;a href="https://no-code.supply/blog/building-website-templates-with-ai/" rel="noopener noreferrer"&gt;Here's how that went.&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;So yeah, I have a stake in this. You should know that before you read on.&lt;/p&gt;

&lt;p&gt;But this isn't a post about defending my templates. It's about how to spot slop when you see it, and how to make sure your own site isn't slop. Doesn't matter if you start from my template, someone else's, or a blank prompt.&lt;/p&gt;

&lt;p&gt;Let's dig in!&lt;/p&gt;




&lt;h2&gt;
  
  
  What people mean by "AI slop"
&lt;/h2&gt;

&lt;p&gt;Merriam-Webster made "slop" its &lt;a href="https://abc7ny.com/post/merriam-websters-2025-word-year-is-slop/18288780/" rel="noopener noreferrer"&gt;word of the year for 2025&lt;/a&gt;: low-quality digital content, usually made in bulk with AI.&lt;/p&gt;

&lt;p&gt;For websites, you know the one. You've seen it a thousand times. You can't tell who made it, or what they sell, until the third scroll. And by then you're not sure you believe a word of it.&lt;/p&gt;

&lt;p&gt;But notice what people &lt;em&gt;aren't&lt;/em&gt; complaining about. Nobody minds that AI was used. &lt;strong&gt;They mind that it shows.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The tells
&lt;/h2&gt;

&lt;p&gt;Any one of these on its own is fine. It's when the whole page is made of them, and nobody chose any of them, that people notice. Pretty much instantly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it looks&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A purple-to-blue gradient, with a glowing blob behind the headline.&lt;/li&gt;
&lt;li&gt;A centred hero with a little pill badge above it: "✨ Now with AI".&lt;/li&gt;
&lt;li&gt;Gradient text on the one word that matters.&lt;/li&gt;
&lt;li&gt;One default sans-serif for everything, in one weight. The font isn't the problem. Nobody choosing it is.&lt;/li&gt;
&lt;li&gt;A small uppercase caption above every single heading, usually in a mono font.&lt;/li&gt;
&lt;li&gt;Three feature cards with an outline icon each. Then a bento grid. Then three more cards.&lt;/li&gt;
&lt;li&gt;The same rounded corners and the same soft shadow on every box.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What it says&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Headlines that could sell anything: "Elevate your workflow", "Unlock your potential", "Built for the future of work".&lt;/li&gt;
&lt;li&gt;Adjectives instead of facts: seamless, powerful, stunning, next-level.&lt;/li&gt;
&lt;li&gt;Every other sentence built the same way: "It's not just a tool. It's a movement."&lt;/li&gt;
&lt;li&gt;Everything in threes. Fast. Simple. Powerful.&lt;/li&gt;
&lt;li&gt;Emoji as bullet points.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What it claims&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A wall of logos from companies that have never heard of you.&lt;/li&gt;
&lt;li&gt;Testimonials from people who don't exist, with AI headshots.&lt;/li&gt;
&lt;li&gt;A row of big round numbers: 10x faster, 99.9% uptime, 500+ integrations.&lt;/li&gt;
&lt;li&gt;"Trusted by 10,000+ teams" on a site that launched last Tuesday.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What nobody checked&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Light grey text on white that you have to squint at.&lt;/li&gt;
&lt;li&gt;A layout that breaks on a phone, or scrolls sideways.&lt;/li&gt;
&lt;li&gt;No visible focus when you press Tab, and menus you can't open with a keyboard.&lt;/li&gt;
&lt;li&gt;Links that go to &lt;code&gt;#&lt;/code&gt;, the template's name still in the footer, "Lorem ipsum" in the share preview.&lt;/li&gt;
&lt;li&gt;A white screen for five seconds while 4 MB of animation loads.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first three groups are about sameness. The last one is about care. Most slop has both.&lt;/p&gt;

&lt;p&gt;Here's the whole set on one page. Sarah J., CEO at Company, sends her regards.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4pc21qjtkx7i7j91tzjj.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4pc21qjtkx7i7j91tzjj.webp" alt="A made-up landing page for “Acme AI” with fourteen slop tells stamped on it like a bingo card: 1, a glowing purple gradient blob. 2, a “Now with AI” pill badge. 3, the headline “Elevate your workflow with AI”. 4, a subtitle made of adjectives. 5, “Trusted by 10,000+ teams worldwide”. 6, a row of placeholder logos. 7, three icon cards. 8, grey text that's hard to read. 9, a bento grid. 10, the same rounded corners and shadow on every box. 11, emoji bullets. 12, a testimonial from a person who doesn't exist. 13, placeholder text in the footer. 14, footer links that go nowhere." width="800" height="1891"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Slop bingo: one made-up page, all fourteen tells.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Slop is older than AI
&lt;/h2&gt;

&lt;p&gt;I've seen hundreds, maybe thousands, of templates in my day. Before this shop, I made Framer templates by hand. And sameness was always there.&lt;/p&gt;

&lt;p&gt;There was the era when every agency site opened with the same full-screen image slider. Then every startup drew its people in the same flat illustration style, the one with tiny heads and long, bendy arms. Then every SaaS page got the same dark hero with a glowing gradient in it.&lt;/p&gt;

&lt;p&gt;None of that was AI. It was defaults: a popular theme, a UI kit, a template, shipped as it came.&lt;/p&gt;

&lt;p&gt;Even the purple is a default. Back in 2020, Tailwind UI shipped its example buttons in indigo. That indigo got copied into a mountain of code, and the models learned from that code. In 2025, Tailwind's creator Adam Wathan &lt;a href="https://prg.sh/ramblings/Why-Your-AI-Keeps-Building-the-Same-Purple-Gradient-Website" rel="noopener noreferrer"&gt;apologised for it&lt;/a&gt; on X.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI didn't invent sameness. It just made it free and instant.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A model that learned from millions of websites gives you something close to the average website, unless somebody tells it otherwise. And the average website is exactly what people have learned to scroll past.&lt;/p&gt;




&lt;h2&gt;
  
  
  The list keeps growing
&lt;/h2&gt;

&lt;p&gt;Dan (&lt;a href="https://x.com/pizzaboy/status/2106835521460994155" rel="noopener noreferrer"&gt;@pizzaboy&lt;/a&gt;) recently posted a list of things AI has ruined for designers. Instrument Serif. Em dashes. Gradient text. Eyebrow captions and mono captions. Two-column heroes. Purple, twice. And numbers. Just... numbers.&lt;/p&gt;

&lt;p&gt;(There isn't a single em dash in this post, by the way. Make of that what you will.)&lt;/p&gt;

&lt;p&gt;It's a joke, but it's also kind of true. And look at that list again: &lt;strong&gt;most of it is good stuff.&lt;/strong&gt; Instrument Serif is a lovely free typeface. Mono captions, two-column heroes and numbers are just normal design. Designers picked them because they worked. Then they got popular, then they ended up in the training data, and now they're what AI reaches for when it wants to look tasteful.&lt;/p&gt;

&lt;p&gt;Anthropic, the company behind Claude, has a name for this: &lt;a href="https://claude.com/blog/improving-frontend-design-through-skills" rel="noopener noreferrer"&gt;distributional convergence&lt;/a&gt;. Ask a model for a landing page with no guidance and it lands on the safe middle: Inter, purple gradients on white, barely any animation. Tell it to avoid those, and it drifts to the next popular thing instead. Their own example is Space Grotesk.&lt;/p&gt;

&lt;p&gt;So here's the thing: &lt;strong&gt;you can't avoid your way out of slop.&lt;/strong&gt; Ban Inter and purple, and you get Instrument Serif and mono captions. If you design by dodging a list, the list is still making the decisions.&lt;/p&gt;

&lt;p&gt;And I'll be honest: by Dan's list, I'm guilty too. &lt;a href="https://no-code.supply/templates/nocturne/" rel="noopener noreferrer"&gt;Nocturne&lt;/a&gt;, my template for an AI sleep coach of all things, is deep blue-black with indigo and violet, and every heading and number is set in Instrument Serif. My site, &lt;a href="https://no-code.supply/" rel="noopener noreferrer"&gt;no-code.supply&lt;/a&gt;, uses small mono caps for its eyebrows and labels. Eight of my nineteen templates use a mono typeface somewhere.&lt;/p&gt;

&lt;p&gt;I'm keeping all of it. Nocturne is a night sky: a moon, a starfield, and a breathing timer you press and hold. Deep blue and a quiet serif are what that feels like. &lt;strong&gt;That's a decision.&lt;/strong&gt; Picking the same things because they're what "tasteful" looks like this year isn't.&lt;/p&gt;




&lt;h2&gt;
  
  
  Slop is what you get when nobody made the decisions
&lt;/h2&gt;

&lt;p&gt;Every website is a long list of decisions. What to say first. What to leave out. Which typeface, which colour, which one thing people will remember.&lt;/p&gt;

&lt;p&gt;A slop site is one where every one of those was left at the default. And it doesn't matter if the default came from a model, a theme or a UI kit.&lt;/p&gt;

&lt;p&gt;Here's what making those decisions looks like in practice, from my own templates. The agent wrote the code. These calls were mine.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://no-code.supply/templates/ashlar/" rel="noopener noreferrer"&gt;Ashlar&lt;/a&gt;&lt;/strong&gt; is a real-estate template. Before it was built, I checked five other real-estate templates and listed what they all do: stat counters, testimonials, an FAQ, a grid of listing cards. &lt;strong&gt;Ashlar has none of them.&lt;/strong&gt; I even dropped a good idea, a hero framed through a doorway, because one of the five already did it. The copy has rules too: never "luxury", "dream", "stunning" or "exclusive". In its own words, "Larch, unfinished" beats "stunning timber".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://no-code.supply/templates/cistern/" rel="noopener noreferrer"&gt;Cistern&lt;/a&gt;&lt;/strong&gt; is a site for a home battery. One version was built around a water tank. It worked. It also looked childish, so out it went, and I started over: near-black, one champagne accent, and a lit graphite slab instead of a water tank.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://no-code.supply/templates/purple-sector/" rel="noopener noreferrer"&gt;Purple Sector&lt;/a&gt;&lt;/strong&gt; is a web-performance studio dressed as a 1990s TV timing screen. It borrows the &lt;em&gt;look&lt;/em&gt; of motorsport graphics and nothing else: no cars, no helmets, no real teams or drivers. Same rule as every template I've made: &lt;strong&gt;don't copy anything. Ever.&lt;/strong&gt; The references set the mood. Everything on the page is made from scratch.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of that is the agent's job. It'll happily build the water tank, the stat counters and the doorway. &lt;strong&gt;Saying no is the part only you can do.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  A template is where your site starts
&lt;/h2&gt;

&lt;p&gt;Now the uncomfortable part, for someone who sells templates.&lt;/p&gt;

&lt;p&gt;Even when a template is full of good decisions, they're &lt;strong&gt;my&lt;/strong&gt; decisions, made for a business that doesn't exist. Ship it as it came and you have someone else's site with your logo on it.&lt;/p&gt;

&lt;p&gt;That's slop too. Not because AI built it, but because &lt;strong&gt;nobody made it yours.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So let's be clear about what a template is for.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What a template should handle for you:&lt;/strong&gt; the structure, a layout that works on every screen, readable contrast, keyboard access, reduced motion, fast pages, clean code. The boring, correct stuff you shouldn't have to solve again.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What it can't handle for you:&lt;/strong&gt; what you actually do, how you talk about it, your real photos, your real proof, and the one thing people will remember you by.&lt;/p&gt;

&lt;p&gt;Ashlar is a good example. Its 21 house photos were generated with AI. The houses don't exist. They're in the demo so it looks finished, and the README says to replace them before launch and never to present them as real properties. A real-estate site with made-up houses on it isn't just slop. &lt;strong&gt;It's lying.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Five changes that make a template yours
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Rewrite every word
&lt;/h3&gt;

&lt;p&gt;Not tweak. Rewrite. The demo copy was written for a made-up business, and it reads like it.&lt;/p&gt;

&lt;p&gt;Swap adjectives for facts. If a headline would work just as well on your competitor's site, it isn't done.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Before:&lt;/strong&gt; "Elevate your brand with stunning, modern design."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;After:&lt;/strong&gt; "We build websites for physiotherapy clinics. Most go live in three weeks."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The second one isn't clever. It's just true and specific. That's what makes it yours.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Use real photos
&lt;/h3&gt;

&lt;p&gt;Your team, your work, your place. A decent phone photo of something real beats a polished stock photo (or an AI image) of nothing. Every time.&lt;/p&gt;

&lt;p&gt;Don't have them yet? Take them before you launch, not after.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Change one thing in the brand, properly
&lt;/h3&gt;

&lt;p&gt;You don't need a full redesign. One colour or one typeface is enough to move it away from the demo. Change it where the template keeps its design tokens, so it follows everywhere, then check the contrast again.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Cut what you don't need
&lt;/h3&gt;

&lt;p&gt;A template shows everything it can do. You don't need all of it.&lt;/p&gt;

&lt;p&gt;No testimonials yet? Delete the section. &lt;strong&gt;Don't fake it.&lt;/strong&gt; Three services, not six. A shorter site is easier to make yours.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Keep one thing that's only yours
&lt;/h3&gt;

&lt;p&gt;One detail people remember: a photo, a line, a tool, a number that's true.&lt;/p&gt;

&lt;p&gt;Every template I sell has a signature effect. Keep it, retune it (speed, colours, on or off), or swap it for something of your own. Just make sure there's &lt;em&gt;something&lt;/em&gt; on the page nobody else has.&lt;/p&gt;




&lt;h2&gt;
  
  
  Check it before you ship
&lt;/h2&gt;

&lt;p&gt;Slop is also what nobody checked. And honestly, checking is the easy part.&lt;/p&gt;

&lt;p&gt;This isn't theory. The first build of Ashlar had more than 60 contrast failures. Sixty. Grey text a shade too light, on a page that looked great. Nobody catches all of those by eye. A checker finds every one in seconds.&lt;/p&gt;

&lt;p&gt;Ten minutes before you launch:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Read every headline out loud.&lt;/strong&gt; Could a competitor use it unchanged? Rewrite it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Search for leftovers:&lt;/strong&gt; the template's name, "lorem", "example.com" and links to &lt;code&gt;#&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Make every claim true.&lt;/strong&gt; Numbers, logos, quotes. If you can't back it up, it goes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open it on your phone&lt;/strong&gt; and scroll the whole page. Nothing should move sideways.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Press Tab&lt;/strong&gt; through the page. You should always see where you are.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run Lighthouse&lt;/strong&gt; in Chrome DevTools, and an accessibility checker like axe DevTools (a free browser extension). Fix contrast first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Turn on "reduce motion"&lt;/strong&gt; in your system settings and reload. The page should still look finished.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Send the link to a friend&lt;/strong&gt; and ask what you do. If they can't say it in one sentence, the copy isn't done.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run the reverse squint test.&lt;/strong&gt; Paste a screenshot into a fresh AI chat and ask which AI app builder made it. If it can guess, so can your visitors. (A tip from the &lt;a href="https://robotsonpayroll.substack.com/p/how-to-avoid-that-vibe-coded-look" rel="noopener noreferrer"&gt;Robots on Payroll&lt;/a&gt; newsletter.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Basic stuff, but someone has to do it.&lt;/p&gt;

&lt;p&gt;Every template I sell goes through the same kind of list before it ships: no serious accessibility issues, readable contrast, works with a keyboard, respects reduced motion, no requests to third-party servers, no sideways scrolling from 360 to 1920 pixels wide. That covers the boring, correct part. The words, the claims and the leftovers are on you.&lt;/p&gt;

&lt;p&gt;If you work with an AI agent, let it do the first pass. It doesn't get bored:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Review this site like a picky first-time visitor. List every headline
that could belong to any company, every claim I can't back up (numbers,
logos, quotes), every leftover placeholder (the template's name, lorem
ipsum, example.com, links to #), and every place where text contrast is
below WCAG AA. Don't fix anything yet. Give me the list, grouped.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you decide what to change. &lt;strong&gt;That last step is the whole point.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Is everything made with AI slop?
&lt;/h3&gt;

&lt;p&gt;No. Slop is generic, unchecked and made without care. Something can be built with AI and be none of those things. Something can be made by hand and be all of them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does Google penalise websites built with AI?
&lt;/h3&gt;

&lt;p&gt;Not for being built with AI. &lt;a href="https://developers.google.com/search/docs/fundamentals/using-gen-ai-content" rel="noopener noreferrer"&gt;Google's guidance&lt;/a&gt; is about whether content is useful to people. Using AI to churn out lots of pages without adding value can fall under its spam policy on scaled content abuse. A real business site with real content about what you do isn't that.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can people tell a site was made with AI?
&lt;/h3&gt;

&lt;p&gt;They can tell when it's generic. When it isn't, there's not much to tell.&lt;/p&gt;

&lt;p&gt;I've seen this with my own templates. Nobody called them AI slop until I said they were built with AI. Before that, they were just templates.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do I have to rewrite everything if I start from a template?
&lt;/h3&gt;

&lt;p&gt;The words and the photos, yes. The structure, the code and the checks, no. That's the part you're paying for.&lt;/p&gt;

&lt;h3&gt;
  
  
  Are your templates AI slop?
&lt;/h3&gt;

&lt;p&gt;Short answer: no. Long answer: this whole post. But you're the judge: every template has a live demo you can click through. Ship one without changing it, though, and yeah, your site will be.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrap-up
&lt;/h2&gt;

&lt;p&gt;AI made the average website free. The way out is the same as it's always been: &lt;strong&gt;make the decisions, use real things, and check your work before you ship.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A good template, mine included, gets you through the boring, correct part fast. So you have time for the part only you can do.&lt;/p&gt;

&lt;p&gt;If you want a starting point where that part is already done, &lt;a href="https://no-code.supply/" rel="noopener noreferrer"&gt;the templates are here&lt;/a&gt;. Just don't ship them as they came.&lt;/p&gt;

&lt;p&gt;If you read this far, thank you! 🙏&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>beginners</category>
      <category>design</category>
    </item>
    <item>
      <title>How to self-host Google Fonts (and why your site should)</title>
      <dc:creator>Lauri Lännenmäki</dc:creator>
      <pubDate>Fri, 09 Oct 2026 10:40:42 +0000</pubDate>
      <link>https://dev.to/laurilllll/how-to-self-host-google-fonts-and-why-your-site-should-722</link>
      <guid>https://dev.to/laurilllll/how-to-self-host-google-fonts-and-why-your-site-should-722</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://no-code.supply/blog/self-host-google-fonts/" rel="noopener noreferrer"&gt;no-code.supply&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Google Fonts is free, but the usual way of using it makes every visitor's browser contact Google before your page can look right. &lt;strong&gt;You can skip that by hosting the font files yourself.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;It takes four steps: download the &lt;code&gt;woff2&lt;/code&gt; files, write an &lt;code&gt;@font-face&lt;/code&gt; rule for each, preload the one or two files your page needs first, and check that DevTools shows zero requests to Google.&lt;/li&gt;
&lt;li&gt;The result: fewer connections, one less privacy question, and a site that doesn't break when someone else's server has a bad day.&lt;/li&gt;
&lt;li&gt;It's the same on every project I build, and this site does exactly this.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's the whole thing in one block. The rest of the post explains each line.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@font-face&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"Geist"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;normal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt; &lt;span class="m"&gt;900&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;font-display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;swap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sx"&gt;url("/fonts/geist-normal-400-latin.woff2")&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;"woff2"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="py"&gt;unicode-range&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="m"&gt;0000-00&lt;/span&gt;&lt;span class="n"&gt;FF&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="m"&gt;0131&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="m"&gt;0152-0153&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="m"&gt;02&lt;/span&gt;&lt;span class="n"&gt;BB-02BC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="m"&gt;02&lt;/span&gt;&lt;span class="n"&gt;C6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="m"&gt;02&lt;/span&gt;&lt;span class="n"&gt;DA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="m"&gt;02&lt;/span&gt;&lt;span class="n"&gt;DC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="m"&gt;0304&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="m"&gt;0308&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="m"&gt;0329&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="m"&gt;2000-206&lt;/span&gt;&lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="n"&gt;AC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="m"&gt;2122&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="m"&gt;2191&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="m"&gt;2193&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="m"&gt;2212&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="m"&gt;2215&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="n"&gt;FEFF&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="n"&gt;FFFD&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt;
  &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"preload"&lt;/span&gt;
  &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/fonts/geist-normal-400-latin.woff2"&lt;/span&gt;
  &lt;span class="na"&gt;as=&lt;/span&gt;&lt;span class="s"&gt;"font"&lt;/span&gt;
  &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"font/woff2"&lt;/span&gt;
  &lt;span class="na"&gt;crossorigin&lt;/span&gt;
&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;👉 Skip to the step-by-step&lt;/p&gt;




&lt;h2&gt;
  
  
  Why bother
&lt;/h2&gt;

&lt;p&gt;The normal Google Fonts setup is one line of HTML. You paste a &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tag, and the font works. It's popular because it's so easy.&lt;/p&gt;

&lt;p&gt;What that line does behind the scenes: your visitor's browser asks &lt;code&gt;fonts.googleapis.com&lt;/code&gt; for a stylesheet, reads it, then asks &lt;code&gt;fonts.gstatic.com&lt;/code&gt; for the actual font files. Two extra servers, both owned by someone else. Here's why I stopped doing that.&lt;/p&gt;

&lt;h3&gt;
  
  
  Privacy
&lt;/h3&gt;

&lt;p&gt;Every request to Google's servers tells Google that this visitor, with this IP address, just opened your page. You didn't ask for that, and your visitor didn't agree to it.&lt;/p&gt;

&lt;p&gt;In Europe this has already been a legal question. On 20 January 2022 a Munich court (LG München I) decided that a website owner who loaded Google Fonts from Google's servers had passed a visitor's IP address to Google without permission, and ordered a €100 payment. It was one ruling in one country, and I'm not a lawyer, so don't take this as legal advice. But the fix is so cheap that I'd rather not find out how the next case goes.&lt;/p&gt;

&lt;p&gt;If you host the fonts yourself, there's nothing to ask about. The font comes from your own domain, like the rest of your page.&lt;/p&gt;

&lt;h3&gt;
  
  
  Speed
&lt;/h3&gt;

&lt;p&gt;Loading from Google means your browser has to open new connections: look up the address of another server, set up a secure connection to it, and only then start the download. That's work your own server has already done by the time the page arrives.&lt;/p&gt;

&lt;p&gt;There was an old argument for the shared setup: if every site uses Google's copy of Roboto, a visitor probably has it cached from another site already. That stopped being true. Modern browsers keep a separate cache for each website, so a font cached while visiting site A isn't reused on site B. Chrome, Firefox and Safari all do this now (Chrome since 2020). So the shared cache saves you nothing, and you still pay for the extra connection.&lt;/p&gt;

&lt;p&gt;Self-hosting also lets you decide which files load first. More on that in a moment, because it's the part that made the biggest difference for me.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reliability
&lt;/h3&gt;

&lt;p&gt;If Google's servers are slow or blocked on someone's network, your text sits waiting, or falls back to a font you didn't pick.&lt;/p&gt;

&lt;p&gt;Files on your own domain share your site's fate. If the page loads, the font loads.&lt;/p&gt;

&lt;h3&gt;
  
  
  Control
&lt;/h3&gt;

&lt;p&gt;The font files stay the same until you change them. You pick the weights, the languages and the format. Nobody can change the stylesheet under you.&lt;/p&gt;

&lt;p&gt;On this site it's also a written rule. The project's &lt;code&gt;AGENTS.md&lt;/code&gt; says there are no third-party requests: no Google Fonts, no outside analytics, no embeds loaded on page view. (I wrote about those files in &lt;a href="https://no-code.supply/blog/how-to-write-agents-md/" rel="noopener noreferrer"&gt;How to write an AGENTS.md your AI agent actually follows&lt;/a&gt;.) Fonts are the easiest of those to get right, so they were the first thing I fixed.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to do it by hand
&lt;/h2&gt;

&lt;p&gt;There are four steps. You can do it with no tools beyond a browser and a text editor.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Find out which files Google serves
&lt;/h3&gt;

&lt;p&gt;Open the Google Fonts page for your family and copy the embed URL. For Geist it looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600;700;800&amp;amp;display=swap
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open that URL directly in a browser tab. You'll see plain CSS: a list of &lt;code&gt;@font-face&lt;/code&gt; rules, each with a &lt;code&gt;src: url(https://fonts.gstatic.com/…woff2)&lt;/code&gt; line.&lt;/p&gt;

&lt;p&gt;Two things to notice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The files are &lt;code&gt;woff2&lt;/code&gt;.&lt;/strong&gt; That's the compressed font format every current browser supports. It's the only one you need. (Older formats like &lt;code&gt;ttf&lt;/code&gt; or &lt;code&gt;woff&lt;/code&gt; are for browsers from a decade ago.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;There are several rules per weight&lt;/strong&gt;, each with a comment like &lt;code&gt;/* latin */&lt;/code&gt; or &lt;code&gt;/* latin-ext */&lt;/code&gt; and a &lt;code&gt;unicode-range&lt;/code&gt; line. That's the subsetting I'll come back to.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Heads up: Google looks at who's asking and sends different CSS to different browsers. An old or unknown browser gets &lt;code&gt;ttf&lt;/code&gt; files and no subsets. If you fetch the URL from a script, send a modern browser's &lt;code&gt;User-Agent&lt;/code&gt; header, or you'll get the wrong thing.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Download the files
&lt;/h3&gt;

&lt;p&gt;Save each &lt;code&gt;woff2&lt;/code&gt; file into your project, for example &lt;code&gt;public/fonts/&lt;/code&gt; in a Next.js project, or &lt;code&gt;/fonts/&lt;/code&gt; in a plain site. Give them names you can read, like &lt;code&gt;geist-normal-400-latin.woff2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Or use a tool. &lt;a href="https://gwfh.mranftl.com/fonts" rel="noopener noreferrer"&gt;google-webfonts-helper&lt;/a&gt; lets you pick a family, weights and subsets and gives you the files plus the CSS. &lt;a href="https://fontsource.org/" rel="noopener noreferrer"&gt;Fontsource&lt;/a&gt; packages the same fonts as npm packages, if you'd rather have them in &lt;code&gt;node_modules&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @fontsource-variable/geist
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@fontsource-variable/geist&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The font's name in your CSS is then &lt;code&gt;"Geist Variable"&lt;/code&gt;, not &lt;code&gt;"Geist"&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Write the @font-face rules
&lt;/h3&gt;

&lt;p&gt;Here's one rule, with what each line does:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@font-face&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"Geist"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;normal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;400&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;font-display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;swap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sx"&gt;url("/fonts/geist-normal-400-latin.woff2")&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;"woff2"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="py"&gt;unicode-range&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="m"&gt;0000-00&lt;/span&gt;&lt;span class="n"&gt;FF&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="m"&gt;0131&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="m"&gt;0152-0153&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="m"&gt;02&lt;/span&gt;&lt;span class="n"&gt;BB-02BC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="m"&gt;02&lt;/span&gt;&lt;span class="n"&gt;C6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="m"&gt;02&lt;/span&gt;&lt;span class="n"&gt;DA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="m"&gt;02&lt;/span&gt;&lt;span class="n"&gt;DC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="m"&gt;0304&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="m"&gt;0308&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="m"&gt;0329&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="m"&gt;2000-206&lt;/span&gt;&lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="n"&gt;AC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="m"&gt;2122&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="m"&gt;2191&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="m"&gt;2193&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="m"&gt;2212&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="m"&gt;2215&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="n"&gt;FEFF&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="n"&gt;FFFD&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;font-family&lt;/code&gt;: the name you'll use in the rest of your CSS (&lt;code&gt;font-family: "Geist", sans-serif&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;font-weight&lt;/code&gt;: which weight this file is for.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;font-display: swap&lt;/code&gt;: show the text right away in a fallback font, then swap to the real one when it arrives. The other option is a blank space until the font loads, which is worse.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;src&lt;/code&gt;: where the file is. It now points to your own domain, which is the whole point.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;unicode-range&lt;/code&gt;: which characters this file covers. The browser only downloads the file if the page uses at least one of those characters.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last one is the clever part of what Google does. The &lt;code&gt;latin&lt;/code&gt; file covers English and most of Western Europe, including Finnish ä and ö (they sit at U+00C4/U+00E4 and U+00D6/U+00F6, inside the &lt;code&gt;latin&lt;/code&gt; range). The &lt;code&gt;latin-ext&lt;/code&gt; file covers things like Polish, Czech and Turkish letters. You write one rule for each, and a page in English only ever downloads the first.&lt;/p&gt;

&lt;p&gt;Keep every subset you actually need. I keep &lt;code&gt;latin&lt;/code&gt; and &lt;code&gt;latin-ext&lt;/code&gt; and drop the rest (Cyrillic, for one) because this site doesn't use them.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Preload the files the page needs first
&lt;/h3&gt;

&lt;p&gt;Here's the catch. A browser finds a font file only after it has downloaded your CSS and worked out which rules apply. By then it has already drawn the page in the fallback font. When the real font arrives, the text changes size slightly and everything below it jumps. That jump is what Google's tools call layout shift (CLS).&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;preload&lt;/code&gt; link fixes it: it tells the browser about the file in the HTML, so the download starts right away, in parallel with the CSS.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt;
  &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"preload"&lt;/span&gt;
  &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/fonts/geist-normal-400-latin.woff2"&lt;/span&gt;
  &lt;span class="na"&gt;as=&lt;/span&gt;&lt;span class="s"&gt;"font"&lt;/span&gt;
  &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"font/woff2"&lt;/span&gt;
  &lt;span class="na"&gt;crossorigin&lt;/span&gt;
&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two details that trip people up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;crossorigin&lt;/code&gt; is required, even for files on your own domain.&lt;/strong&gt; Fonts are always fetched in a mode that needs it. Without the attribute the browser downloads the file twice, once for the preload and once for the real use, and the preload is wasted.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Only preload what's above the fold.&lt;/strong&gt; Every preloaded file competes with your other resources. I preload two files and nothing else.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's the real example from this site. The homepage uses Geist for text and Geist Mono for small labels, so &lt;code&gt;app/layout.tsx&lt;/code&gt; preloads the Latin file of each. The calls sit at the top of the root layout component, before it returns the page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;preload&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-dom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;RootLayout&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ReactNode&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;preload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/fonts/geist-normal-400-latin.woff2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;as&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;font&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;font/woff2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;crossOrigin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;anonymous&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nf"&gt;preload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/fonts/geist-mono-normal-400-latin.woff2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;as&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;font&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;font/woff2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;crossOrigin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;anonymous&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;// &amp;lt;html&amp;gt;, &amp;lt;body&amp;gt; and the rest of the layout&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;preload&lt;/code&gt; from &lt;code&gt;react-dom&lt;/code&gt; is how a React 19 app adds a &lt;code&gt;&amp;lt;link rel="preload"&amp;gt;&lt;/code&gt; to the page's head, and it works in a server component like this layout. The two files are the Latin ones from &lt;code&gt;app/fonts.css&lt;/code&gt;. &lt;code&gt;crossOrigin: "anonymous"&lt;/code&gt; is the same thing as the &lt;code&gt;crossorigin&lt;/code&gt; attribute above.&lt;/p&gt;

&lt;p&gt;Before I added these two lines, Lighthouse measured a layout shift of 0.104 on the homepage, caused by the font swap. After: 0.&lt;/p&gt;




&lt;h2&gt;
  
  
  The variable font shortcut
&lt;/h2&gt;

&lt;p&gt;Geist is a variable font. One file holds every weight from thin to black, instead of one file per weight.&lt;/p&gt;

&lt;p&gt;You can see it in my files. &lt;code&gt;public/fonts/&lt;/code&gt; has just four &lt;code&gt;woff2&lt;/code&gt; files in total: Geist and Geist Mono, each in &lt;code&gt;latin&lt;/code&gt; and &lt;code&gt;latin-ext&lt;/code&gt;. Together they're about 84 KB (29.3 and 16.5 KB for Geist, 23.1 and 14.7 KB for Geist Mono). The stylesheet still has a rule per weight (400, 500, 600, 700, 800 for Geist), but every rule points to the same file. The &lt;code&gt;400&lt;/code&gt; in the file names is a leftover from how my script named them; each file holds the whole weight range.&lt;/p&gt;

&lt;p&gt;If your font is variable, you can write one rule with a weight range instead. Google serves Geist with a range of 100 to 900:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@font-face&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"Geist"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt; &lt;span class="m"&gt;900&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sx"&gt;url("/fonts/geist-normal-400-latin.woff2")&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;"woff2"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The saving is real when you use several weights. Five static weights are five downloads. One variable file is one. Check the Google Fonts page for your family: if it shows a weight slider, it's variable.&lt;/p&gt;




&lt;h2&gt;
  
  
  The fast way: a script
&lt;/h2&gt;

&lt;p&gt;The first time I did this by hand it was slow and fiddly. After that I wrote a script, and you can have it: &lt;a href="https://no-code.supply/downloads/fetch-google-fonts.mjs" rel="noopener noreferrer"&gt;fetch-google-fonts.mjs&lt;/a&gt;. It needs Node 18 or newer. You give it the Google Fonts URL and an output folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node fetch-google-fonts.mjs &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600;700;800&amp;amp;display=swap"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  public/fonts &lt;span class="nt"&gt;--url-prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/fonts/ &lt;span class="nt"&gt;--css&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;app/fonts.css
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Swap in the URL for your own family. &lt;code&gt;--url-prefix&lt;/code&gt; is the path the browser will use for the files, and &lt;code&gt;--css&lt;/code&gt; is where the stylesheet goes.&lt;/p&gt;

&lt;p&gt;It does what the four steps above do:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Fetches the CSS URL, pretending to be a current Chrome so Google answers with &lt;code&gt;woff2&lt;/code&gt; and the subsets.&lt;/li&gt;
&lt;li&gt;Reads each &lt;code&gt;@font-face&lt;/code&gt; block and keeps the subsets I ask for (&lt;code&gt;latin&lt;/code&gt; and &lt;code&gt;latin-ext&lt;/code&gt; by default).&lt;/li&gt;
&lt;li&gt;Downloads each font file once, even when several weights share it, and writes it into the output folder.&lt;/li&gt;
&lt;li&gt;Writes a new stylesheet with the URLs pointing at my own folder instead of Google's. Weights that share one file are merged into a single rule with a range.&lt;/li&gt;
&lt;li&gt;Fetches each family's licence text and saves it in &lt;code&gt;public/fonts/licenses/&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Writes a &lt;code&gt;fonts.json&lt;/code&gt; that lists each family, its files and its licence.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It's 99 lines of Node with no dependencies, so read it before you run it. If you'd rather not run anything, the manual steps above give the same result.&lt;/p&gt;




&lt;h2&gt;
  
  
  In Next.js: next/font/google
&lt;/h2&gt;

&lt;p&gt;If you use Next.js there's a built-in route that does most of this for you: &lt;code&gt;next/font/google&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Geist&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next/font/google&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;geist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Geist&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;subsets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;latin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;RootLayout&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ReactNode&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;geist&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At build time Next.js downloads the font from Google and serves it from your own domain with the rest of your static assets, so your visitors' browsers never contact Google. It also preloads the subsets you list and adds a size-adjusted fallback font to reduce layout shift, both on by default. For a variable font like Geist you don't need to set a weight; for a static one you do.&lt;/p&gt;

&lt;p&gt;It's a good choice, and if your site is Next.js and nothing else, use it. I still did my own for this site, for one reason: the templates come in plain HTML editions with no build step, so I wanted one method that works the same in every edition. Doing it by hand also keeps the files and the licence text visible in the repository, and gives me full control over what gets preloaded.&lt;/p&gt;




&lt;h2&gt;
  
  
  Don't forget the licence
&lt;/h2&gt;

&lt;p&gt;Most Google Fonts use the SIL Open Font License (OFL). Geist does. Some use the Apache 2.0 licence instead, and a few (the Ubuntu fonts) use the Ubuntu Font Licence. Each family's folder in the &lt;a href="https://github.com/google/fonts" rel="noopener noreferrer"&gt;google/fonts&lt;/a&gt; repository has its own licence file, which is the one to trust.&lt;/p&gt;

&lt;p&gt;In plain words, the OFL lets you use, copy, embed and ship the font for free, including in commercial projects and bundled with your own software, and that covers self-hosting. What it asks in return:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can't sell the font files by themselves.&lt;/li&gt;
&lt;li&gt;Keep the copyright notice and the licence text with the font.&lt;/li&gt;
&lt;li&gt;If you modify the font, the modified version stays under the OFL, and it can't use the font's Reserved Font Name if the licence names one.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So put the licence file next to the fonts. My &lt;code&gt;public/fonts/licenses/&lt;/code&gt; folder has &lt;code&gt;geist-OFL.txt&lt;/code&gt; and &lt;code&gt;geist-mono-OFL.txt&lt;/code&gt;, and a small &lt;code&gt;fonts.json&lt;/code&gt; lists each family, its files and its licence. It takes almost no effort, and it saves a bad afternoon if someone ever asks.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to check it worked
&lt;/h2&gt;

&lt;p&gt;Don't trust that it worked. Look.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open your site in Chrome and open DevTools (right-click, Inspect).&lt;/li&gt;
&lt;li&gt;Go to the &lt;strong&gt;Network&lt;/strong&gt; tab and tick &lt;strong&gt;Disable cache&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Type &lt;code&gt;fonts&lt;/code&gt; in the filter box, or click the &lt;strong&gt;Font&lt;/strong&gt; filter, and reload.&lt;/li&gt;
&lt;li&gt;You should see only requests to your own domain, ending in &lt;code&gt;.woff2&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Now filter for &lt;code&gt;googleapis&lt;/code&gt; and then &lt;code&gt;gstatic&lt;/code&gt;. &lt;strong&gt;Both should show zero requests.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Some things to check at the same time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] No request goes to &lt;code&gt;fonts.googleapis.com&lt;/code&gt; or &lt;code&gt;fonts.gstatic.com&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;[ ] Each font file loads once. Twice means a missing &lt;code&gt;crossorigin&lt;/code&gt; on the preload.&lt;/li&gt;
&lt;li&gt;[ ] The preloaded files are the first font requests in the list.&lt;/li&gt;
&lt;li&gt;[ ] Run Lighthouse and look at the layout shift number. It should be 0 or very close.&lt;/li&gt;
&lt;li&gt;[ ] Search your code for &lt;code&gt;fonts.googleapis.com&lt;/code&gt;. Old &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tags and &lt;code&gt;@import&lt;/code&gt; lines in CSS files hide in surprising places, and one forgotten line keeps the whole thing going.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Is self-hosting Google Fonts allowed?
&lt;/h3&gt;

&lt;p&gt;Yes. The fonts are open source, and the licences allow you to download them and serve them yourself. Google even offers a download button on every family's page. Keep the licence file with the fonts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is it faster?
&lt;/h3&gt;

&lt;p&gt;Usually, yes, because there are no extra connections to set up, and you can preload the files you care about. Measure it on your own site: how much faster depends on where your server is and what else your page loads. I measured layout shift going from 0.104 to 0 here, but that came from the preload, not from self-hosting alone.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do I need to update the files when Google updates a font?
&lt;/h3&gt;

&lt;p&gt;No, nothing updates by itself. Your own copy stays exactly as you downloaded it. So ask yourself how often you really need a font updated, and whether you want it to change under a live design without you deciding. If you do want a new version, run the download again.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should I use woff2 only?
&lt;/h3&gt;

&lt;p&gt;Yes. Every current browser supports it, and it's the smallest format. You only need &lt;code&gt;ttf&lt;/code&gt; or &lt;code&gt;woff&lt;/code&gt; if you have to support very old browsers, which most sites don't.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrap-up
&lt;/h2&gt;

&lt;p&gt;Self-hosting Google Fonts is a small job. You get a page that asks no one else for permission, loads in fewer steps, and has a font that stays put.&lt;/p&gt;

&lt;p&gt;If you read this far, thank you. 🙏&lt;/p&gt;

&lt;p&gt;Every &lt;a href="https://no-code.supply/" rel="noopener noreferrer"&gt;no-code.supply template&lt;/a&gt; ships with its fonts self-hosted, files and licences included, so a template works the same with or without a connection to anyone else's server.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>css</category>
      <category>performance</category>
      <category>privacy</category>
    </item>
    <item>
      <title>I used to be a Figma expert. Here's how I design now.</title>
      <dc:creator>Lauri Lännenmäki</dc:creator>
      <pubDate>Tue, 06 Oct 2026 08:17:49 +0000</pubDate>
      <link>https://dev.to/laurilllll/i-used-to-be-a-figma-expert-heres-how-i-design-now-4lm9</link>
      <guid>https://dev.to/laurilllll/i-used-to-be-a-figma-expert-heres-how-i-design-now-4lm9</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://no-code.supply/blog/how-i-design-now/" rel="noopener noreferrer"&gt;no-code.supply&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I used to call myself a Figma expert. I still use it at my day job, but I don't design every state and every variant there anymore.&lt;/li&gt;
&lt;li&gt;Figma has its own AI now. It works, but it feels slow and much weaker than Claude working in Figma through an MCP.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Figma was never the source of truth.&lt;/strong&gt; Code is. Variants are simpler in code than in a design tool, where one button can grow to &lt;strong&gt;1,100 variants&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;I learned CSS properly so I could build anything I designed. It was all boxes anyway. Now the web expects &lt;strong&gt;shaders and three.js&lt;/strong&gt;, and I don't want to learn all that.&lt;/li&gt;
&lt;li&gt;And I don't have to. &lt;strong&gt;I'm no longer limited by my own abilities.&lt;/strong&gt; Websites, apps, games, video ads: I describe it, an agent builds it.&lt;/li&gt;
&lt;li&gt;My path there: ChatGPT and copy-paste, then Cursor, then &lt;strong&gt;Claude Code in the terminal&lt;/strong&gt;, then &lt;strong&gt;MCPs for everything&lt;/strong&gt;. Today the agent is &lt;strong&gt;Claude Opus 5.5&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Knowing how to code made the switch quicker for me. But if you've never coded, &lt;strong&gt;go straight to the agent&lt;/strong&gt; and start building.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  A bit of background
&lt;/h2&gt;

&lt;p&gt;I'm Lauri Lännenmäki, a design engineer. I've spent fifteen years designing and building for the web, most of it somewhere between Figma, Framer and code.&lt;/p&gt;

&lt;p&gt;I started using Figma when it came out. I had my own business back then, so nothing was stopping me from jumping over from Sketch. Over time I &lt;strong&gt;fell in love with it&lt;/strong&gt;, mostly because of what it let me do: build design systems that developers actually implemented.&lt;/p&gt;

&lt;p&gt;I knew the shortcuts, I built the component libraries, I set up the variables, the properties and the variants. If you'd asked me back then, I'd have said I was a &lt;strong&gt;Figma expert&lt;/strong&gt;, and I'd have meant it.&lt;/p&gt;

&lt;p&gt;This post is about why that changed. It's an opinion, not a review. Let's dig in!&lt;/p&gt;




&lt;h2&gt;
  
  
  I still use Figma, just differently
&lt;/h2&gt;

&lt;p&gt;Let me be fair first. I still work in Figma. At my 9–5, if a client has design files somewhere, they're in Figma.&lt;/p&gt;

&lt;p&gt;I just use it differently now. &lt;strong&gt;I don't design every state and every variant in Figma anymore.&lt;/strong&gt; If I have to, I start there, and then I move to code.&lt;/p&gt;

&lt;p&gt;Figma has its own agent now too. It has fixed things and made changes to my components when I didn't have the time, on the kind of tasks I knew would annoy me. It works. But, I dunno. It feels slow, and it feels &lt;strong&gt;much weaker than Claude&lt;/strong&gt; working in Figma through an MCP. (More on MCPs below.)&lt;/p&gt;

&lt;h3&gt;
  
  
  The careful way feels slow now
&lt;/h3&gt;

&lt;p&gt;You can use Figma in a lot of ways. You can rapid-fire everything, or you can take your time: name every layer, and set up all the variables, properties and variants properly.&lt;/p&gt;

&lt;p&gt;I'm the second kind. And today, that work feels &lt;strong&gt;slow and a bit stupid&lt;/strong&gt;, because I know all of it has to be done again in code. In code, I can just prompt what I want, and the variables, properties and variants are simply there.&lt;/p&gt;

&lt;p&gt;So it's not that Figma got worse. The way I work moved somewhere else.&lt;/p&gt;




&lt;h2&gt;
  
  
  Figma was never the source of truth
&lt;/h2&gt;

&lt;p&gt;Here's something I've believed for a long time, even back when I lived in Figma: &lt;strong&gt;designs should really be built in code.&lt;/strong&gt; That's where they end up anyway.&lt;/p&gt;

&lt;p&gt;Figma is a hi-fi picture of what a product &lt;em&gt;could&lt;/em&gt; be. &lt;strong&gt;It's not the source of truth. It never has been, and it never will be.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I learned that the hard way. Once I started building my design systems myself, I quickly noticed I was &lt;strong&gt;keeping Figma up to date with the code&lt;/strong&gt;, not the other way around. The code was the real thing. The Figma file was the copy I had to keep in sync.&lt;/p&gt;

&lt;h3&gt;
  
  
  The button with 1,100 variants
&lt;/h3&gt;

&lt;p&gt;Variants show this best. And the usual offender is the button.&lt;/p&gt;

&lt;p&gt;Give a button icons, sizes, states, colours and shapes, and it quickly turns into a &lt;strong&gt;sea of variants&lt;/strong&gt;. I've worked on one that has &lt;strong&gt;1,100 variants&lt;/strong&gt; in Figma. Try editing that, and your MacBook starts to sound like it's about to take off.&lt;/p&gt;

&lt;p&gt;In code, the same button is a handful of props:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"lg"&lt;/span&gt; &lt;span class="na"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"ghost"&lt;/span&gt; &lt;span class="na"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"pill"&lt;/span&gt; &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Arrow&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  Read more
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You read it top to bottom and you know exactly what it does. In a design tool, you get booleans that hide a layer, text properties that swap a string, and nested instances with their own overrides. It works, but it never felt &lt;em&gt;quite right&lt;/em&gt; to me. It's code dressed up as a canvas.&lt;/p&gt;

&lt;p&gt;So the move to code didn't feel like a loss. It felt like dropping the translation layer.&lt;/p&gt;




&lt;h2&gt;
  
  
  It was all boxes. Was.
&lt;/h2&gt;

&lt;p&gt;I'm not a designer who can't code. I know how to code to a certain degree, and I used to be &lt;strong&gt;really good at CSS&lt;/strong&gt;. I learned it on purpose, because I wanted to be able to build anything I designed. No handing a file over and hoping it comes back looking right.&lt;/p&gt;

&lt;p&gt;And for a long time that was enough, because the web was &lt;strong&gt;all boxes&lt;/strong&gt;. Flexbox, grid, a few transitions, and you could build pretty much anything that came out of a design tool.&lt;/p&gt;

&lt;p&gt;That's not what people expect from a landing page or a marketing site anymore. Now it's shaders, WebGL, three.js, things that bend and refract and react to the cursor.&lt;/p&gt;

&lt;p&gt;I'd never be able to build a three.js site by hand. No way. I wouldn't even try, because I wouldn't want to ship anything shitty. It's a whole other craft, and I'd rather spend my time on the ideas.&lt;/p&gt;




&lt;h2&gt;
  
  
  And now I don't have to
&lt;/h2&gt;

&lt;p&gt;This is the part that still feels a bit unreal.&lt;/p&gt;

&lt;p&gt;I envision something, I put it in a prompt, and the agent makes it happen. Then I get to sit back and &lt;strong&gt;marvel at the outcome&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Many of my own templates have effects I don't know how to build.&lt;/strong&gt; &lt;a href="https://no-code.supply/templates/aurel/" rel="noopener noreferrer"&gt;Aurel&lt;/a&gt; has a raymarched WebGL serum drop in its hero that bulges toward your cursor. &lt;a href="https://no-code.supply/templates/tidewater/" rel="noopener noreferrer"&gt;Tidewater&lt;/a&gt; opens with WebGL caustics, like light on the sea floor. I didn't write those shaders. I asked for an effect, and the agent decided how to build it. I never had to say "use WebGL" or explain how a shader works. Usually all I need to do is &lt;strong&gt;ask&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And it's not just websites. Here's some of what I've made with AI:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the first version of my Framer plugin, with nothing but ChatGPT&lt;/li&gt;
&lt;li&gt;two iOS apps (not published)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://thegoatdebate.io" rel="noopener noreferrer"&gt;thegoatdebate.io&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;TikTok video ads&lt;/li&gt;
&lt;li&gt;a 3D shooting game, and a 2D one&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For most of my career, what I could make was capped by what I could build myself. &lt;strong&gt;That cap is gone&lt;/strong&gt;, and I still find that super fascinating.&lt;/p&gt;

&lt;p&gt;My job didn't disappear. It moved. I still decide what something should look like, how it should feel, and whether it's good enough. I just don't need to know how to write a fragment shader to get there.&lt;/p&gt;




&lt;h2&gt;
  
  
  How I got here
&lt;/h2&gt;

&lt;p&gt;It didn't happen overnight. Looking back, every step felt like a big leap at the time.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Chatting with ChatGPT
&lt;/h3&gt;

&lt;p&gt;In the beginning I just chatted with ChatGPT. Then I started coding with it. The workflow was &lt;strong&gt;copy and paste&lt;/strong&gt;: describe the problem, copy the answer into my editor, run it, copy the error back, repeat. Clunky, but it worked. That's how the first version of my Framer plugin got built.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Cursor
&lt;/h3&gt;

&lt;p&gt;Then I moved to Cursor, and that felt like a &lt;strong&gt;giant leap forward&lt;/strong&gt;. The AI was inside the editor, it could see my files, and it could make the change itself. No more ferrying code between two windows.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Claude Code in the terminal
&lt;/h3&gt;

&lt;p&gt;Then I moved to Claude Code, in the terminal. And pretty quickly it started to feel very cool to &lt;strong&gt;not look at the code at all&lt;/strong&gt;. Sometimes I still do, but mostly I don't. I describe the outcome, the agent does the work, and I review what it made: the page, the behaviour, the result. Not the diff.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. MCPs
&lt;/h3&gt;

&lt;p&gt;Then came MCPs, and I was mind-blown all over again.&lt;/p&gt;

&lt;p&gt;An MCP (Model Context Protocol) server connects the agent to another tool, so it can read from it and act in it directly. One of my first was &lt;a href="https://github.com/southleft/figma-console-mcp" rel="noopener noreferrer"&gt;Figma Console MCP&lt;/a&gt;. I used it to debug and map out a &lt;strong&gt;massive design system&lt;/strong&gt; I help build for a Finnish company.&lt;/p&gt;

&lt;p&gt;For example, I had the agent find every gap and padding that used a hardcoded value and switch it to a variable. Then the same for colours. Doing that by hand means clicking through hundreds of components one layer at a time. I probably saved &lt;strong&gt;hundreds of hours&lt;/strong&gt; of manual, annoying work with that one MCP.&lt;/p&gt;

&lt;p&gt;Which is a little ironic for a post about leaving Figma: some of the most useful Figma work I've done lately, I did without clicking around in it myself.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. An MCP or API for everything
&lt;/h3&gt;

&lt;p&gt;Now I have an MCP or an API for pretty much everything I use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Jira&lt;/strong&gt; and &lt;strong&gt;Linear&lt;/strong&gt; for tickets&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub&lt;/strong&gt; for code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Polar&lt;/strong&gt; and &lt;strong&gt;Stripe&lt;/strong&gt; for payments&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Supabase&lt;/strong&gt; for databases&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Figma&lt;/strong&gt;, &lt;strong&gt;Framer&lt;/strong&gt;, &lt;strong&gt;Paper&lt;/strong&gt; and &lt;strong&gt;MagicPath&lt;/strong&gt; for design&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Search Console&lt;/strong&gt; for search&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The agent doesn't just write code anymore. It works across the same tools I do.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Claude Opus 5.5
&lt;/h2&gt;

&lt;p&gt;I like trying things out. Besides ChatGPT and Cursor, I've used tools like &lt;strong&gt;Higgsfield&lt;/strong&gt; for images and video, and &lt;strong&gt;ElevenLabs&lt;/strong&gt; for voice. But for building, my agent is &lt;strong&gt;Claude Opus 5.5&lt;/strong&gt;, running in Claude Code.&lt;/p&gt;

&lt;p&gt;Opus 5.5 is just a beast. &lt;strong&gt;It understands me.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I'm on a Max plan, and I've learned to make it last for both my 9–5 and my own projects on the side. And I'd pick Claude anyway, because to me &lt;strong&gt;Anthropic always feels one step ahead&lt;/strong&gt; of everyone else.&lt;/p&gt;




&lt;h2&gt;
  
  
  It helped that I could already code
&lt;/h2&gt;

&lt;p&gt;I'll be honest about one thing: I had a head start.&lt;/p&gt;

&lt;p&gt;I knew how to code, and I already knew my way around &lt;strong&gt;Node, git and the terminal&lt;/strong&gt; long before AI showed up. So when the tools moved from a chat window to an editor to the terminal, I didn't have to learn a whole new world at each step. I was already living in it.&lt;/p&gt;

&lt;p&gt;For a designer who has never coded, the same transition probably takes longer and feels less natural. You're learning the agent &lt;em&gt;and&lt;/em&gt; the environment it runs in at the same time.&lt;/p&gt;

&lt;p&gt;But that's no reason to wait. My advice: &lt;strong&gt;go straight to the agent and just build things.&lt;/strong&gt; When you feel like you're onto something and you want some version control, ask for help. Learn the basics of git, and ask the agent to teach you. You don't have to get fancy with it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Everybody is a builder now
&lt;/h2&gt;

&lt;p&gt;For most of my career, designers were limited by others. You designed something, and then you waited for a developer to build it, and hoped it came back looking right.&lt;/p&gt;

&lt;p&gt;That's over. &lt;strong&gt;Designers are no longer limited by developers.&lt;/strong&gt; Everybody is a builder now.&lt;/p&gt;

&lt;p&gt;And "build" doesn't just mean websites anymore. If you can describe it, you can make it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Websites&lt;/strong&gt;, landing pages and marketing sites&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Web apps&lt;/strong&gt;, dashboards and internal tools&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;iOS and Android apps&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plugins&lt;/strong&gt; for Framer and Figma, and browser extensions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code components&lt;/strong&gt; and whole design systems&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;3D scenes&lt;/strong&gt;, shaders and generative art&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Motion design&lt;/strong&gt;, launch videos and video ads&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Games&lt;/strong&gt;, in 2D and 3D&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Images&lt;/strong&gt;, illustrations and brand assets&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Music&lt;/strong&gt;, sound effects and voice-overs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Physical products&lt;/strong&gt; you design in 3D and send straight to a 3D printer&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automations and scripts&lt;/strong&gt; for the boring parts of your job&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your own business&lt;/strong&gt;: the shop, the checkout, the emails, the analytics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ten years ago, each of those was its own career. Now it's a prompt away. You still need taste, and you still need to know what good looks like. But &lt;strong&gt;the skills that used to stand between an idea and the finished thing are no longer the bottleneck.&lt;/strong&gt; Your ideas are.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrap-up
&lt;/h2&gt;

&lt;p&gt;The distance between an idea and a working website just got very short, and for me that distance used to be most of the work. But I don't think design tools are dead.&lt;/p&gt;

&lt;p&gt;For me, a design canvas is still &lt;strong&gt;the best place to see the big picture&lt;/strong&gt;. Claude can make magnificent visual presentations of the bigger picture when I need one, but an endless canvas is useful as hell.&lt;/p&gt;

&lt;p&gt;Same with the small stuff. Making a little tweak by hand in a canvas tool still beats trying to prompt my way to one exact detail. Although sending the agent a screenshot of the exact spot does the trick almost every time.&lt;/p&gt;

&lt;p&gt;What I'm waiting for is the next step: &lt;strong&gt;just talking to my computer and seeing things happen in (almost) real time&lt;/strong&gt;, pretty much like in the &lt;a href="https://x.com/OpenAI/status/2095595741528125780" rel="noopener noreferrer"&gt;GPT-6 Astra launch video&lt;/a&gt;. That would be sick.&lt;/p&gt;

&lt;p&gt;If you read this far, thank you! 🙏&lt;/p&gt;

&lt;p&gt;And if you want to see what this way of working produces, &lt;a href="https://no-code.supply/" rel="noopener noreferrer"&gt;the templates are here&lt;/a&gt;. Every one of them was built this way.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>design</category>
      <category>figma</category>
      <category>webdev</category>
    </item>
    <item>
      <title>HTML vs React vs Framer: which website template edition should you buy?</title>
      <dc:creator>Lauri Lännenmäki</dc:creator>
      <pubDate>Mon, 05 Oct 2026 10:14:55 +0000</pubDate>
      <link>https://dev.to/laurilllll/html-vs-react-vs-framer-which-website-template-edition-should-you-buy-3o02</link>
      <guid>https://dev.to/laurilllll/html-vs-react-vs-framer-which-website-template-edition-should-you-buy-3o02</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://no-code.supply/blog/html-vs-react-vs-framer-templates/" rel="noopener noreferrer"&gt;no-code.supply&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every no-code.supply template comes as &lt;strong&gt;HTML&lt;/strong&gt; and &lt;strong&gt;React&lt;/strong&gt;. Three of them (Oda, Signal and Tender) also come as &lt;strong&gt;Framer&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTML&lt;/strong&gt;: plain files, no build step, goes on any host. The easiest edition to hand to an AI agent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;React&lt;/strong&gt;: a Next.js project. Pick it if you'll keep building on the site, or you already live in React.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Framer&lt;/strong&gt;: a remix link. Pick it if you or your client edit in Framer's visual editor, by hand or with Framer Agent. You get no source files.&lt;/li&gt;
&lt;li&gt;Unsure? Get the &lt;strong&gt;Bundle&lt;/strong&gt; (HTML + React) and decide later.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 &lt;a href="https://no-code.supply/" rel="noopener noreferrer"&gt;Browse the templates&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Why this post exists
&lt;/h2&gt;

&lt;p&gt;I get the same question a lot: "Which edition should I buy?" The honest answer is that none of them is better. They're the same design, built for different ways of working.&lt;/p&gt;

&lt;p&gt;So here's what's in each one, who it fits, and where it will annoy you. I make these templates, so I know the weak spots. Let's dig in!&lt;/p&gt;




&lt;h2&gt;
  
  
  The three editions at a glance
&lt;/h2&gt;

&lt;p&gt;The design, the copy and the sections are the same in every edition. What changes is what you get to work with.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;HTML&lt;/th&gt;
&lt;th&gt;React&lt;/th&gt;
&lt;th&gt;Framer&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;What you download&lt;/td&gt;
&lt;td&gt;A folder of static files&lt;/td&gt;
&lt;td&gt;A Next.js project&lt;/td&gt;
&lt;td&gt;A remix link&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build step&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;npm install&lt;/code&gt;, &lt;code&gt;npm run dev&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;None, it's Framer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Where it runs&lt;/td&gt;
&lt;td&gt;Any static host, or straight from disk&lt;/td&gt;
&lt;td&gt;Vercel, Netlify or any static host&lt;/td&gt;
&lt;td&gt;Framer hosting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;You edit it in&lt;/td&gt;
&lt;td&gt;Any code editor or AI agent&lt;/td&gt;
&lt;td&gt;Any code editor or AI agent&lt;/td&gt;
&lt;td&gt;Framer's visual editor, by hand or with Framer Agent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Source files&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No, it stays a Framer project&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Available for&lt;/td&gt;
&lt;td&gt;Every template&lt;/td&gt;
&lt;td&gt;Every template&lt;/td&gt;
&lt;td&gt;Oda, Signal, Tender&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  HTML: just files
&lt;/h2&gt;

&lt;p&gt;The HTML edition is plain HTML, CSS and JavaScript. Unzip it, double-click &lt;code&gt;index.html&lt;/code&gt;, and the site opens. No accounts, no plugins, no framework.&lt;/p&gt;

&lt;p&gt;In the zip: &lt;code&gt;index.html&lt;/code&gt;, an &lt;code&gt;assets&lt;/code&gt; folder with the CSS and JS, a &lt;code&gt;404.html&lt;/code&gt;, a privacy page and the usual extras (&lt;code&gt;robots.txt&lt;/code&gt;, a web manifest). Plus a &lt;code&gt;README.md&lt;/code&gt;, a &lt;code&gt;DESIGN.md&lt;/code&gt; and an &lt;code&gt;AGENTS.md&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It's for you if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You want the least amount of moving parts. Edit a file, refresh the page.&lt;/li&gt;
&lt;li&gt;You're putting it on a cheap or odd host: Netlify, Vercel, GitHub Pages, shared hosting, an S3 bucket.&lt;/li&gt;
&lt;li&gt;You want your AI coding agent to do the editing. There's nothing to install and nothing to break, and &lt;a href="https://no-code.supply/blog/how-to-write-agents-md/" rel="noopener noreferrer"&gt;the &lt;code&gt;AGENTS.md&lt;/code&gt;&lt;/a&gt; tells it the rules.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The trade-offs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No components or templating. If you add ten pages, you'll copy the header ten times.&lt;/li&gt;
&lt;li&gt;No CMS. Content lives in the HTML.&lt;/li&gt;
&lt;li&gt;Growing it into an app means switching to something else.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every one of mine is built so it still works opened from disk, with no build step and no CDN. That's a rule, not an accident.&lt;/p&gt;




&lt;h2&gt;
  
  
  React: a project you can grow
&lt;/h2&gt;

&lt;p&gt;The React edition is a Next.js 16 project with React 19, Tailwind 4 and Motion. It's set up as a static export. All the copy lives in one content file, so changing the words doesn't mean hunting through components.&lt;/p&gt;

&lt;p&gt;You run &lt;code&gt;npm install&lt;/code&gt;, then &lt;code&gt;npm run dev&lt;/code&gt;, like any Next.js app. &lt;code&gt;npm run check&lt;/code&gt; runs lint, typecheck and build in one go. It also ships with an &lt;code&gt;AGENTS.md&lt;/code&gt;, a &lt;code&gt;DESIGN.md&lt;/code&gt; and a &lt;code&gt;README.md&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It's for you if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You already work in React or Next.js and want to build on top of the design.&lt;/li&gt;
&lt;li&gt;The site will grow: more pages, a CMS later, a form that talks to something, maybe an app section.&lt;/li&gt;
&lt;li&gt;You want components, types and a linter catching mistakes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The trade-offs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need Node and a terminal. That's a real hurdle if you've never used them.&lt;/li&gt;
&lt;li&gt;A bit more to maintain: dependencies update, and sooner or later you'll run an upgrade.&lt;/li&gt;
&lt;li&gt;For a five-section landing page that never changes, it's more machinery than you need. HTML does that job with less.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Framer: edit it visually, by hand or with Framer Agent
&lt;/h2&gt;

&lt;p&gt;The Framer edition is the whole project, copied into your own Framer workspace through a remix link. That includes the pages, the CMS collections, the code components and the Framer Skills that teach Framer Agent how the site is built.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It's for you if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You design in Framer and want to restyle things in the visual editor by hand, or ask Framer Agent to do it, not write code.&lt;/li&gt;
&lt;li&gt;A client will edit the site themselves, and Framer's editor is what they know.&lt;/li&gt;
&lt;li&gt;You want hosting, publishing and a CMS in one place.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The trade-offs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No source files to hand to a code editor. The project stays inside Framer.&lt;/li&gt;
&lt;li&gt;You depend on Framer's platform and its pricing for hosting.&lt;/li&gt;
&lt;li&gt;Only three templates have one so far: &lt;a href="https://no-code.supply/templates/oda/" rel="noopener noreferrer"&gt;Oda&lt;/a&gt;, &lt;a href="https://no-code.supply/templates/signal/" rel="noopener noreferrer"&gt;Signal&lt;/a&gt; and &lt;a href="https://no-code.supply/templates/tender/" rel="noopener noreferrer"&gt;Tender&lt;/a&gt;. You can buy it on its own, and it's also part of All-Access.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I come from Framer, so I'm fond of it. But "no code" has a flip side. If you want a developer to own the site, one of the code editions is a better start.&lt;/p&gt;




&lt;h2&gt;
  
  
  So which one?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Pick HTML if…&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you want the simplest thing that works, anywhere&lt;/li&gt;
&lt;li&gt;you'll edit it by hand or with an AI agent&lt;/li&gt;
&lt;li&gt;the site is a landing page or a small brochure site&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pick React if…&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you or your developer already use React&lt;/li&gt;
&lt;li&gt;you expect the site to keep growing&lt;/li&gt;
&lt;li&gt;you want components and a proper dev setup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pick Framer if…&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you or your client edit in Framer's visual editor, by hand or with Framer Agent&lt;/li&gt;
&lt;li&gt;you want hosting and a CMS handled for you&lt;/li&gt;
&lt;li&gt;you don't need the source files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A rough rule: &lt;strong&gt;HTML for the fewest surprises, React for the longest runway, Framer for the least code.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Bundle, bundle-all and All-Access
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bundle&lt;/strong&gt; is HTML + React in one download. It makes sense when you're not sure yet. Prototype in HTML, move to React when the site outgrows it, or give a client the simple one and keep the other.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTML + React + Framer&lt;/strong&gt; is the same idea for the three templates that have all three. It's for studios and freelancers who deliver in different tools depending on the client.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://no-code.supply/all-access/" rel="noopener noreferrer"&gt;All-Access&lt;/a&gt;&lt;/strong&gt; is every template in every edition it comes in (HTML, React, and Framer where there is one), including the ones that arrive in later drops. It pays off if you'll use more than a couple of templates. It doesn't include Grit UI (the Framer UI kit) or the separately sold Framer code components.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you only need one template for one site, one edition is plenty. Prices and what's included are on each &lt;a href="https://no-code.supply/" rel="noopener noreferrer"&gt;template's page&lt;/a&gt; and on the &lt;a href="https://no-code.supply/all-access/" rel="noopener noreferrer"&gt;All-Access page&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Can I switch editions later?
&lt;/h3&gt;

&lt;p&gt;Not by converting one into the other. They're separate builds of the same design. That's why the Bundle exists.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is there any difference in design between editions?
&lt;/h3&gt;

&lt;p&gt;No. The design, sections and copy are the same.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do I need to know how to code?
&lt;/h3&gt;

&lt;p&gt;Not for HTML, if you edit with an AI agent or just change text. React needs Node and a terminal. Framer needs neither.&lt;/p&gt;

&lt;h3&gt;
  
  
  Which edition is best with an AI coding agent?
&lt;/h3&gt;

&lt;p&gt;HTML or React. Both ship with an &lt;code&gt;AGENTS.md&lt;/code&gt;. HTML has nothing to install, so there's less that can go wrong. The Framer edition has its own Framer Skills for Framer's agent.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do updates cost extra?
&lt;/h3&gt;

&lt;p&gt;No. Updates to a template you own are free and show up in Polar's customer portal.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrap-up
&lt;/h2&gt;

&lt;p&gt;If you're still stuck, start with HTML. It's the easiest to change your mind from, and you can always buy React later. The &lt;a href="https://no-code.supply/license/" rel="noopener noreferrer"&gt;license&lt;/a&gt; and &lt;a href="https://no-code.supply/refunds/" rel="noopener noreferrer"&gt;refunds&lt;/a&gt; pages spell out the details.&lt;/p&gt;

&lt;p&gt;Thanks for reading! 🙏&lt;/p&gt;

&lt;p&gt;Want to see the editions for yourself? Open any &lt;a href="https://no-code.supply/" rel="noopener noreferrer"&gt;no-code.supply template&lt;/a&gt; and check what's included.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>framer</category>
      <category>html</category>
    </item>
    <item>
      <title>How to write an AGENTS.md your AI agent actually follows</title>
      <dc:creator>Lauri Lännenmäki</dc:creator>
      <pubDate>Sun, 04 Oct 2026 19:26:16 +0000</pubDate>
      <link>https://dev.to/laurilllll/how-to-write-an-agentsmd-your-ai-agent-actually-follows-5g7m</link>
      <guid>https://dev.to/laurilllll/how-to-write-an-agentsmd-your-ai-agent-actually-follows-5g7m</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://no-code.supply/blog/how-to-write-agents-md/" rel="noopener noreferrer"&gt;no-code.supply&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An &lt;code&gt;AGENTS.md&lt;/code&gt; is a file that tells your AI coding agent how a project works and which rules it must not break.&lt;/li&gt;
&lt;li&gt;Most of them get ignored because they're written like a wiki page. &lt;strong&gt;Write it like instructions for a new colleague on their first day.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;What works: &lt;strong&gt;where things live&lt;/strong&gt;, &lt;strong&gt;numbered rules with the reason next to them&lt;/strong&gt;, &lt;strong&gt;one recipe for the most common task&lt;/strong&gt;, and &lt;strong&gt;a checklist for when it's done&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;What doesn't: vague advice ("write clean code"), things the agent can read from the code anyway, and docs nobody updates.&lt;/li&gt;
&lt;li&gt;Every template I sell ships with one, and here's exactly how they're written.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 &lt;a href="https://no-code.supply/" rel="noopener noreferrer"&gt;See the templates&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Why I care about this file
&lt;/h2&gt;

&lt;p&gt;I built 15 website templates with an AI coding agent, Claude Code, in about a week. (&lt;a href="https://no-code.supply/blog/building-website-templates-with-ai/" rel="noopener noreferrer"&gt;The whole story is here&lt;/a&gt;.) Two repositories, dozens of sessions, and the agent starting from zero every single time.&lt;/p&gt;

&lt;p&gt;That's the thing people forget: &lt;strong&gt;your agent has no memory of yesterday.&lt;/strong&gt; Every session, it opens the project like a new hire on day one. Whatever it knows about how your project works, it learns from what's written down. If nothing is written down, it guesses. And guessing is where the weird bugs come from.&lt;/p&gt;

&lt;p&gt;Then there's the second reason. Every template I sell ships with an &lt;code&gt;AGENTS.md&lt;/code&gt;, because most buyers won't edit a template by hand. They'll open it in Cursor or Claude Code and say "make the hero blue." That file is the difference between their assistant following the template's rules and their assistant quietly breaking them.&lt;/p&gt;

&lt;p&gt;So I've written a lot of these by now. Let's dig in!&lt;/p&gt;




&lt;h2&gt;
  
  
  A quick caveat: this is how it works today
&lt;/h2&gt;

&lt;p&gt;Everything in this post is how things work in October 2026. AI coding tools change every few months, and the rules have already changed more than once. A year ago, every tool wanted its own file. Now most of them read &lt;code&gt;AGENTS.md&lt;/code&gt;, and even Claude Code reads it.&lt;/p&gt;

&lt;p&gt;So take the file names and tool details with a pinch of salt. The thinking behind them should last longer: &lt;strong&gt;write down what the agent can't read from the code, and say why.&lt;/strong&gt; That was good advice for onboarding human developers long before AI, and I don't see it going away.&lt;/p&gt;




&lt;h2&gt;
  
  
  What AGENTS.md is (and CLAUDE.md)
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;AGENTS.md&lt;/code&gt; is a plain markdown file in the root of a project. AI coding agents read it before they start working. It's an open convention, so one file works in most tools: Codex, Cursor, GitHub Copilot and others read it.&lt;/p&gt;

&lt;p&gt;Claude Code has its own file, &lt;code&gt;CLAUDE.md&lt;/code&gt;. You don't need two copies. My &lt;code&gt;CLAUDE.md&lt;/code&gt; files have one line in them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;@AGENTS.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That tells Claude Code to pull in &lt;code&gt;AGENTS.md&lt;/code&gt;. One source of truth, every tool covered.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Claude Code can now read &lt;code&gt;AGENTS.md&lt;/code&gt; on its own, so a project with only an &lt;code&gt;AGENTS.md&lt;/code&gt; works out of the box. But if there's also a &lt;code&gt;CLAUDE.md&lt;/code&gt; in the project, Claude Code reads that one instead by default. So if you have both, keep the one-line import. (&lt;a href="https://code.claude.com/docs/en/memory#agents-md" rel="noopener noreferrer"&gt;Claude Code docs&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can also have more than one. My templates have an &lt;code&gt;AGENTS.md&lt;/code&gt; per edition, one for the HTML version and one for the React version, because the rules are different. A plain HTML site that has to open from a double-clicked file has nothing in common with a Next.js build.&lt;/p&gt;

&lt;p&gt;When there's more than one, the closest file wins. Agents read the &lt;code&gt;AGENTS.md&lt;/code&gt; nearest to the file they're editing, so a subfolder's file can add to or override the root one. Put the rules that apply everywhere in the root file, and the rules for one part of the project next to that part. Claude Code works the same way with &lt;code&gt;CLAUDE.md&lt;/code&gt;: a subfolder's file loads when Claude starts working in that folder.&lt;/p&gt;




&lt;h2&gt;
  
  
  What to put in it
&lt;/h2&gt;

&lt;p&gt;Every &lt;code&gt;AGENTS.md&lt;/code&gt; I write has the same five parts. Here they are, with real pieces from &lt;a href="https://no-code.supply/templates/oda/" rel="noopener noreferrer"&gt;Oda&lt;/a&gt;, an architecture studio template.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. What this is, in two sentences
&lt;/h3&gt;

&lt;p&gt;Start with what the project is and the one constraint that shapes everything else.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## What this is&lt;/span&gt;
A static, dependency-free landing page for an architecture studio:
&lt;span class="sb"&gt;`index.html`&lt;/span&gt; + CSS + classic JS. There is no build step and there must
never be one. The page must keep working when &lt;span class="sb"&gt;`index.html`&lt;/span&gt; is opened
directly from disk.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That last sentence does a lot of work. Without it, the first thing a helpful agent does is add a bundler.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Where things live
&lt;/h3&gt;

&lt;p&gt;A map. Not every file, just the ones the agent needs to find to do its job, and &lt;strong&gt;where a change should go&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Where things live&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Copy and structure: &lt;span class="sb"&gt;`index.html`&lt;/span&gt;, one SECTION banner comment per
  section (Hero, Studio, Work, Process …).
&lt;span class="p"&gt;-&lt;/span&gt; Design tokens: &lt;span class="sb"&gt;`assets/css/tokens.css`&lt;/span&gt;. Change values here, not in &lt;span class="sb"&gt;`main.css`&lt;/span&gt;.
&lt;span class="p"&gt;-&lt;/span&gt; Shared helpers: &lt;span class="sb"&gt;`assets/js/kit.js`&lt;/span&gt; + &lt;span class="sb"&gt;`assets/css/kit.css`&lt;/span&gt; (vendored —
  don't edit; call &lt;span class="sb"&gt;`kit.*`&lt;/span&gt; helpers instead of re-implementing).
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See the pattern? Each line says where something is &lt;em&gt;and&lt;/em&gt; what to do with it. "Change values here, not there." "Don't edit, call these instead." That's the part that changes the agent's behaviour.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Rules that must not break
&lt;/h3&gt;

&lt;p&gt;This is the heart of the file. Short, numbered, and each one with its reason.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Rules that must not break&lt;/span&gt;
&lt;span class="p"&gt;1.&lt;/span&gt; No external requests: no CDNs, Google Fonts, analytics or remote images.
&lt;span class="p"&gt;2.&lt;/span&gt; Classic scripts with &lt;span class="sb"&gt;`defer`&lt;/span&gt; only. Never &lt;span class="sb"&gt;`type="module"`&lt;/span&gt; or &lt;span class="sb"&gt;`import`&lt;/span&gt;:
   they fail over &lt;span class="sb"&gt;`file://`&lt;/span&gt;.
&lt;span class="p"&gt;3.&lt;/span&gt; Respect &lt;span class="sb"&gt;`prefers-reduced-motion`&lt;/span&gt;: new animations need a static
   equivalent.
&lt;span class="p"&gt;6.&lt;/span&gt; Exactly one &lt;span class="sb"&gt;`&amp;lt;h1&amp;gt;`&lt;/span&gt;; keep headings ordered, landmarks, the skip link
   and &lt;span class="sb"&gt;`:focus-visible`&lt;/span&gt; styles.
&lt;span class="p"&gt;8.&lt;/span&gt; Use existing tokens. If you need a new one, add it to &lt;span class="sb"&gt;`tokens.css`&lt;/span&gt;
   with a comment. Text in the accent colour uses &lt;span class="sb"&gt;`--timber-ink`&lt;/span&gt; (large)
   or &lt;span class="sb"&gt;`--timber-deep`&lt;/span&gt; (small), never &lt;span class="sb"&gt;`--timber`&lt;/span&gt;, which fails contrast.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things make these rules stick:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The reason.&lt;/strong&gt; "Never &lt;code&gt;type="module"&lt;/code&gt;" alone looks like a style preference, and an agent will happily trade a style preference for something it thinks is better. "They fail over &lt;code&gt;file://&lt;/code&gt;" is a fact it can't argue with.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Numbers.&lt;/strong&gt; A numbered list is easy to point at. "You broke rule 2" is a much shorter conversation than explaining it again.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rule 8 is my favourite kind of rule. It's oddly specific, and that's the point. The accent colour looks great, but it fails contrast on small text. Nothing in the code tells you that. Without the rule, the next agent would happily use it for a caption.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. A recipe for the most common task
&lt;/h3&gt;

&lt;p&gt;Think about what people will ask for most. For a template, it's "add a section." So the file shows exactly how a section is built in this template, with a snippet to copy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Adding a section in the template's style&lt;/span&gt;
Copy the Materials or People section as a starting point:
&lt;span class="p"&gt;
-&lt;/span&gt; Header: the ruled &lt;span class="sb"&gt;`.sec-head`&lt;/span&gt; with &lt;span class="sb"&gt;`(NN) Name`&lt;/span&gt;, a subtitle and the
  next free sheet number.
&lt;span class="p"&gt;-&lt;/span&gt; Layout: &lt;span class="sb"&gt;`.wrap`&lt;/span&gt; + the 12-column &lt;span class="sb"&gt;`.g12`&lt;/span&gt;; spacing comes from &lt;span class="sb"&gt;`.sec`&lt;/span&gt;.
&lt;span class="p"&gt;-&lt;/span&gt; Headings: &lt;span class="sb"&gt;`.h2`&lt;/span&gt; with the last word in &lt;span class="sb"&gt;`&amp;lt;span class="dim"&amp;gt;`&lt;/span&gt;.
&lt;span class="p"&gt;-&lt;/span&gt; Reveals: &lt;span class="sb"&gt;`data-reveal="draft"`&lt;/span&gt; with &lt;span class="sb"&gt;`data-reveal-delay`&lt;/span&gt; in ms.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is how a new section ends up looking like it belongs, instead of looking like the agent's idea of a section. Agents copy what they see. Give them the right thing to copy.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. A checklist for "done"
&lt;/h3&gt;

&lt;p&gt;End with what to check before calling the work finished.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Before you finish&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; [ ] No console errors. No horizontal scroll at 390px wide.
&lt;span class="p"&gt;-&lt;/span&gt; [ ] The page still works opened from disk.
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Reduced motion shows a complete page.
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Keyboard: every control reachable, visible focus.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a code project, the best version of this is one command. My React editions just say: run &lt;code&gt;npm run check&lt;/code&gt; (lint, typecheck and build). If the agent can run it, it will, and it'll fix what fails before you ever see it.&lt;/p&gt;




&lt;h2&gt;
  
  
  What to leave out
&lt;/h2&gt;

&lt;p&gt;Just as important. Everything in the file competes for the agent's attention, so every line has to earn its place.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Vague advice.&lt;/strong&gt; "Write clean, maintainable code." "Follow best practices." The agent already thinks it does. These lines change nothing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What the code already says.&lt;/strong&gt; Don't list every component or explain what React is. The agent can read the code. Write down what it &lt;em&gt;can't&lt;/em&gt; read: the intent, the constraints, the decisions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;History.&lt;/strong&gt; "We used to use X, then switched to Y." Unless the old way is a trap it'll fall into, leave it out.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secrets.&lt;/strong&gt; No API keys, tokens or passwords. The file lives in the repo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Long essays.&lt;/strong&gt; If a section needs paragraphs of background, put it in its own doc (I use &lt;code&gt;DESIGN.md&lt;/code&gt; for the design rationale) and link to it from &lt;code&gt;AGENTS.md&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I don't have a line count I aim for. Mine ended up between 46 and 102 lines, depending on how much the template does. The test I use: if a part needs paragraphs of background, it belongs in another file.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common mistakes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Describing instead of directing
&lt;/h3&gt;

&lt;p&gt;Compare these two. Describing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;The project uses design tokens for colours and spacing.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Directing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Use existing tokens. If you need a new one, add it to &lt;span class="sb"&gt;`tokens.css`&lt;/span&gt;
with a comment. Never hard-code hex values.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first one is true. The second one is useful. Write in the imperative: &lt;em&gt;use&lt;/em&gt;, &lt;em&gt;keep&lt;/em&gt;, &lt;em&gt;never&lt;/em&gt;, &lt;em&gt;change it here&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rules without a reason
&lt;/h3&gt;

&lt;p&gt;An agent weighs your rule against everything else it knows. A rule with a reason wins that fight. A rule without one often doesn't, especially when the agent has a "better" idea.&lt;/p&gt;

&lt;h3&gt;
  
  
  Writing it once and never touching it again
&lt;/h3&gt;

&lt;p&gt;A stale &lt;code&gt;AGENTS.md&lt;/code&gt; is worse than none, because the agent trusts it. If a file moved or a command changed, the agent follows the old map and gets lost confidently.&lt;/p&gt;

&lt;p&gt;My fix: &lt;strong&gt;every time the agent makes a mistake, I ask whether a line in &lt;code&gt;AGENTS.md&lt;/code&gt; would have prevented it.&lt;/strong&gt; If yes, it goes in. Most of my best rules came from bugs.&lt;/p&gt;

&lt;p&gt;Here's a real one. Every template's docs explain how to move a section, like the hero, into another site. On 26 September I had the agent actually do it: follow the steps word for word and move each template's hero into a separate test site with its own clashing styles.&lt;/p&gt;

&lt;p&gt;It broke in four templates. In some, the hero's entrance waited for a signal from the preloader or the nav. Move the hero without them, and the headline never appeared. In another, the hero's buttons needed a different section to exist.&lt;/p&gt;

&lt;p&gt;The fixes took minutes. The more important part was the new rule that went into every template's &lt;code&gt;AGENTS.md&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;9.&lt;/span&gt; Every section must start on its own. An entrance that waits for a
   boot/intro signal must not rely on the preloader or the nav to
   send it: call the fallback from the section's own effect as well.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now every agent that adds a section, mine or a buyer's, knows about it before it makes the mistake.&lt;/p&gt;

&lt;h3&gt;
  
  
  Writing it all yourself
&lt;/h3&gt;

&lt;p&gt;You don't have to. Ask the agent to write the first version after it has worked on the project for a while: "Write an &lt;code&gt;AGENTS.md&lt;/code&gt; for this project: where things live, rules that must not break, how to add a section, and a checklist for done." Then &lt;strong&gt;review it like any other part of the product.&lt;/strong&gt; Cut the vague lines, add the reasons, fix what's wrong.&lt;/p&gt;

&lt;p&gt;That's how all of mine were made. The agent that built the template also wrote down how it built it, and I edited it.&lt;/p&gt;




&lt;h2&gt;
  
  
  A template you can copy
&lt;/h2&gt;

&lt;p&gt;Here's the skeleton I start from. Fill in the brackets, delete what doesn't apply.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# AGENTS.md — [Project name]&lt;/span&gt;

Instructions for AI coding assistants working on this project.
Read this before editing.

&lt;span class="gu"&gt;## What this is&lt;/span&gt;
[One or two sentences. What it is, and the one constraint that shapes
everything else.]

&lt;span class="gu"&gt;## Where things live&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; [Thing]: &lt;span class="sb"&gt;`path/to/file`&lt;/span&gt;. [What to change here, or what not to touch.]
&lt;span class="p"&gt;-&lt;/span&gt; [Thing]: &lt;span class="sb"&gt;`path/to/file`&lt;/span&gt;. [...]

&lt;span class="gu"&gt;## Rules that must not break&lt;/span&gt;
&lt;span class="p"&gt;1.&lt;/span&gt; [Rule]: [why].
&lt;span class="p"&gt;2.&lt;/span&gt; [Rule]: [why].
&lt;span class="p"&gt;3.&lt;/span&gt; [Rule]: [why].

&lt;span class="gu"&gt;## [The most common task]&lt;/span&gt;
[Which file to copy, which patterns to reuse, where the copy goes.]

&lt;span class="gu"&gt;## Commands&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`[dev command]`&lt;/span&gt; — [what it does]
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`[check command]`&lt;/span&gt; — run before you finish.

&lt;span class="gu"&gt;## Before you finish&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; [ ] [Check]
&lt;span class="p"&gt;-&lt;/span&gt; [ ] [Check]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What's the difference between AGENTS.md and CLAUDE.md?
&lt;/h3&gt;

&lt;p&gt;They do the same job. &lt;code&gt;AGENTS.md&lt;/code&gt; is the shared convention most AI coding tools read. &lt;code&gt;CLAUDE.md&lt;/code&gt; is Claude Code's own file. Claude Code also reads &lt;code&gt;AGENTS.md&lt;/code&gt; now, but only when there's no &lt;code&gt;CLAUDE.md&lt;/code&gt;. Simplest setup: put everything in &lt;code&gt;AGENTS.md&lt;/code&gt;, and if you need a &lt;code&gt;CLAUDE.md&lt;/code&gt; too, make it a single line, &lt;code&gt;@AGENTS.md&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where does AGENTS.md go?
&lt;/h3&gt;

&lt;p&gt;In the root of the project. You can add more in subfolders when a part of the project has its own rules, like my templates' HTML and React editions. The agent reads the one closest to the file it's editing, so the most specific rules win.&lt;/p&gt;

&lt;h3&gt;
  
  
  How long should an AGENTS.md be?
&lt;/h3&gt;

&lt;p&gt;As short as it can be while still covering where things live, the rules, the most common task and the checklist. Mine are between 46 and 102 lines.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does my AI agent actually follow it?
&lt;/h3&gt;

&lt;p&gt;Much better than without one, and much better when the rules come with reasons. It's not a guarantee. That's why the file ends with a checklist and, when possible, a command that checks the work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should I write it myself or let the AI write it?
&lt;/h3&gt;

&lt;p&gt;Both. Let the agent write the first draft from the project, then edit it yourself. You know the decisions and the reasons. It knows the files.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrap-up
&lt;/h2&gt;

&lt;p&gt;An &lt;code&gt;AGENTS.md&lt;/code&gt; is the cheapest quality tool you have. Ten minutes of writing saves you from explaining the same thing in every session, and it keeps your rules alive after you've forgotten why you made them.&lt;/p&gt;

&lt;p&gt;If you read this far, thank you! 🙏&lt;/p&gt;

&lt;p&gt;Want to see a real one in action? Every &lt;a href="https://no-code.supply/" rel="noopener noreferrer"&gt;no-code.supply template&lt;/a&gt; ships with an &lt;code&gt;AGENTS.md&lt;/code&gt;, a &lt;code&gt;DESIGN.md&lt;/code&gt; and a &lt;code&gt;README.md&lt;/code&gt;, so your own AI assistant can edit it the way it was built. 🔥&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How I built 15 website templates with an AI coding agent in a week</title>
      <dc:creator>Lauri Lännenmäki</dc:creator>
      <pubDate>Sat, 03 Oct 2026 11:18:26 +0000</pubDate>
      <link>https://dev.to/laurilllll/how-i-built-15-website-templates-with-an-ai-coding-agent-in-a-week-5f0h</link>
      <guid>https://dev.to/laurilllll/how-i-built-15-website-templates-with-an-ai-coding-agent-in-a-week-5f0h</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://no-code.supply" rel="noopener noreferrer"&gt;no-code.supply&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I built 15 website templates, each with its own brand, in about a week, with an AI coding agent (Claude Code) doing the typing.&lt;/li&gt;
&lt;li&gt;The agent pretty much one-shotted the designs. My job moved from building to directing: the idea, the quality bar, and the final call.&lt;/li&gt;
&lt;li&gt;Where AI really shines: speed, bug hunting, and packaging it all as a business.&lt;/li&gt;
&lt;li&gt;Every template is AI-ready: it ships with docs your own AI assistant can read, so it edits the template the way it was built.&lt;/li&gt;
&lt;li&gt;Yes, some people call it AI slop. They're templates. A template is where your site starts, not where it ends.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 &lt;a href="https://no-code.supply/#templates" rel="noopener noreferrer"&gt;Browse the templates&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Why I did this
&lt;/h2&gt;

&lt;p&gt;I'm Lauri Lännenmäki, a design engineer. I've spent fifteen years designing and building for the web, most of it somewhere between Figma, Framer and code. I'm a consultant in Finland, and I freelance on nights and weekends. Before this I made Grit UI and Fhibli UI for Framer, and a handful of Framer code components. I'm also a certified Framer Pro Expert.&lt;/p&gt;

&lt;p&gt;I've wanted my own template shop for a long time. What stopped me was always the same thing: time. Before this I only made Framer templates, and a good one took me at least a month, on and off. Doing fifteen of them, each drawn from scratch and shipped in two code editions, was never going to happen on nights and weekends.&lt;/p&gt;

&lt;p&gt;So I wanted to find out: can one designer build a real template business with an AI agent doing most of the typing?&lt;/p&gt;

&lt;p&gt;Short answer: yes. Let's dig in!&lt;/p&gt;




&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;p&gt;Two repositories and one agent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The templates repo:&lt;/strong&gt; where every template is designed, built, reviewed and packaged. It also holds a small shared kit (the helpers every template reuses) and the tooling that checks and exports everything.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The storefront:&lt;/strong&gt; &lt;a href="https://no-code.supply" rel="noopener noreferrer"&gt;no-code.supply&lt;/a&gt;, a static Next.js site that shows and sells the templates. Checkout runs on Polar.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The agent:&lt;/strong&gt; Claude Code, on one of the higher-usage plans.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every template ships in two editions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;HTML:&lt;/strong&gt; plain HTML, CSS and JavaScript. It opens straight from the folder you download. No build step, no accounts, no framework to learn.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;React:&lt;/strong&gt; a Next.js + React + Tailwind project, with every word of copy in one file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some also get a Framer edition: &lt;a href="https://no-code.supply/templates/oda/" rel="noopener noreferrer"&gt;Oda&lt;/a&gt; and &lt;a href="https://no-code.supply/templates/signal/" rel="noopener noreferrer"&gt;Signal&lt;/a&gt; so far.&lt;/p&gt;




&lt;h2&gt;
  
  
  How one template gets made
&lt;/h2&gt;

&lt;p&gt;Every template goes through the same phases. The agent does most of the work in each one. I decide when a phase is done.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The concept
&lt;/h3&gt;

&lt;p&gt;This part is mine. Before any code exists, I decide what the template is: the industry, the brand, the mood, the type pairing, the palette and the one interaction people should remember.&lt;/p&gt;

&lt;p&gt;Oda is an architecture studio designed as a drawing set: grid paper, hairline title blocks, sheet numbers, and line drawings that draw themselves as you scroll. Signal is a security SaaS. Meridian is travel. Each one gets its own identity, not a recolour of the last one.&lt;/p&gt;

&lt;p&gt;For every concept I gave the agent 5–10 references: images and websites with the mood I was after, plus things I generally like myself. Alongside them I wrote down the typography (the type scale and its style), the spacing and the colour palette. And one rule above everything else: don't copy anything. Ever. The references set the mood: the feel, how dense the page is, how it moves. The layouts, copy, imagery and code are all made from scratch.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The first build
&lt;/h3&gt;

&lt;p&gt;The agent builds the HTML edition from the concept. And this is the part that still surprises me: &lt;strong&gt;the first version is usually very close to done&lt;/strong&gt;. I don't have a dramatic before/after to show you, because there isn't much of one. The first builds were just amazing. I had to change very little in any of the templates. Here and there I removed a section, but nothing much.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Review, docs and fixes
&lt;/h3&gt;

&lt;p&gt;Close to done isn't done. This is where most of the time goes, and it's where the quality comes from.&lt;/p&gt;

&lt;p&gt;Every template goes through the same definition of done before it ships:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Zero console errors or warnings.&lt;/li&gt;
&lt;li&gt;No horizontal overflow at 360, 390, 768, 1024, 1440 and 1920 pixels wide.&lt;/li&gt;
&lt;li&gt;0 serious or critical axe issues, a keyboard-only pass, and visible focus styles everywhere.&lt;/li&gt;
&lt;li&gt;WCAG 2.2 AA contrast for text.&lt;/li&gt;
&lt;li&gt;prefers-reduced-motion shows a complete, good-looking static page.&lt;/li&gt;
&lt;li&gt;No third-party requests at all. Fonts are self-hosted. No CDNs, no analytics.&lt;/li&gt;
&lt;li&gt;The HTML edition works from a double-clicked index.html, not just from a web server.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lighthouse scores are a goal, not a rule. I aim for Accessibility ≥ 95, Best Practices ≥ 95 and SEO 100, but when a template's design calls for it, I'll accept a lower score.&lt;/p&gt;

&lt;p&gt;That's a lot of checking. Luckily, checking is something the agent is very good at (more on that below).&lt;/p&gt;

&lt;h3&gt;
  
  
  4. The React edition
&lt;/h3&gt;

&lt;p&gt;Same design, same copy, rebuilt as a Next.js project, then put through the same checklist.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Packaging
&lt;/h3&gt;

&lt;p&gt;Zips, previews, a changelog, product copy, and the export to the storefront. More on this below too, because it turned out to be the most surprising part.&lt;/p&gt;

&lt;p&gt;All in, it took about &lt;strong&gt;3–6 hours&lt;/strong&gt; to go from a design to a finished template in both editions.&lt;/p&gt;




&lt;h2&gt;
  
  
  AI-ready templates: what that actually means
&lt;/h2&gt;

&lt;p&gt;This is the part I'm most proud of, and it's the reason I call these AI-ready templates.&lt;/p&gt;

&lt;p&gt;Most people who buy a template in 2026 won't edit it by hand. They'll open it in Cursor, Claude Code or whatever assistant they use, and say "change the hero copy and make it blue." The problem: the assistant doesn't know how the template was built. So it guesses. It hard-codes a colour, breaks the mobile layout, or adds a dependency that breaks the no-build-step promise.&lt;/p&gt;

&lt;p&gt;So every template ships with three files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;README.md&lt;/code&gt;:&lt;/strong&gt; for you. How to install it, edit it, and move a section into another site.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;DESIGN.md&lt;/code&gt;:&lt;/strong&gt; the design rationale. Tokens, type scale, the ideas behind the layout.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;AGENTS.md&lt;/code&gt;:&lt;/strong&gt; for your AI assistant. Where things live, how the effects work, and the rules that must not break.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's a small piece of Oda's &lt;code&gt;AGENTS.md&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Rules that must not break&lt;/span&gt;
&lt;span class="p"&gt;1.&lt;/span&gt; No external requests: no CDNs, Google Fonts, analytics or remote images.
&lt;span class="p"&gt;3.&lt;/span&gt; Respect &lt;span class="sb"&gt;`prefers-reduced-motion`&lt;/span&gt;: new animations need a static equivalent.
&lt;span class="p"&gt;6.&lt;/span&gt; Exactly one &lt;span class="sb"&gt;`&amp;lt;h1&amp;gt;`&lt;/span&gt;; keep headings ordered, landmarks, the skip link and &lt;span class="sb"&gt;`:focus-visible`&lt;/span&gt; styles.
&lt;span class="p"&gt;8.&lt;/span&gt; Use existing tokens. If you need a new one, add it to &lt;span class="sb"&gt;`tokens.css`&lt;/span&gt; with a comment.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These files aren't an afterthought. The agent that built the template also wrote down how it built it, and I reviewed those docs like any other part of the product. So when your assistant opens the template, it follows the same rules the original was built with.&lt;/p&gt;

&lt;p&gt;The Framer edition works the same way: it ships with Framer Skills that teach Framer's own agent how the site is built.&lt;/p&gt;

&lt;p&gt;That's the idea behind AI-ready: the template is the starting point, and your AI assistant can take it the rest of the way without breaking it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where AI was great
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Speed
&lt;/h3&gt;

&lt;p&gt;The obvious one, but the numbers are still silly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;24 September: the templates repo starts.&lt;/li&gt;
&lt;li&gt;26 September: the shop launches with the first six templates.&lt;/li&gt;
&lt;li&gt;29 September: nine templates are out.&lt;/li&gt;
&lt;li&gt;1 October: all 15 are done.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;About a week. And not a full-time week, either: I did this around my consulting work, so the hours are hard to count.&lt;/p&gt;

&lt;p&gt;Honestly, without AI I wouldn't have started this project at all. One template would have taken me months, let alone building an entire shop around them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bug hunting
&lt;/h3&gt;

&lt;p&gt;This one surprised me. The agent spotted a lot of bugs by itself and fixed them without me telling it to. A few examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tender's form error colours failed contrast.&lt;/strong&gt; You only see an error message when a form fails, so it's easy to never check its colour.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Focus styles.&lt;/strong&gt; Invisible with a mouse. You only notice them when you go through the page with a keyboard.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An HTML comment could swallow markup in the step that exports a template to the store.&lt;/strong&gt; A section could quietly disappear from a demo, with no error anywhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Oda's reveal animations got clipped&lt;/strong&gt;, cutting off the edges of elements as they animated in.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The list is much longer. Many times it corrected itself before I'd even noticed anything was wrong.&lt;/p&gt;

&lt;p&gt;The quality checklist above is only realistic because of this. Checking six screen widths, two editions, reduced motion, keyboard navigation and contrast, on every template, after every change, is exactly the kind of boring, careful work people skip. The agent doesn't get bored.&lt;/p&gt;

&lt;h3&gt;
  
  
  Packaging it all as a business
&lt;/h3&gt;

&lt;p&gt;This is the one I didn't see coming. Designing templates is maybe half of running a template shop. The other half is everything around them, and the agent built most of that too:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The storefront itself:&lt;/strong&gt; a static Next.js site with product pages, pricing, an All-Access offer and the legal pages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The export pipeline:&lt;/strong&gt; one command takes a finished template and writes its product page data, previews and live demo into the store.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Polar sync:&lt;/strong&gt; a script that creates the products in Polar, uploads each edition's zip, and writes the checkout links back to the site. When I ship an update, it swaps the files so every past buyer gets the new version.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Share images:&lt;/strong&gt; every product gets its own Open Graph image, generated at build time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The launch video:&lt;/strong&gt; a script that films the store and the demos frame by frame and renders them to MP4. No screen recording, no editing app.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without AI, the cost of all this would have been enormous for me. It would have been a long year of sleepless nights.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I still had to bring
&lt;/h2&gt;

&lt;p&gt;The agent did the typing. It didn't decide what's good. A few things were still my job:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The concept.&lt;/strong&gt; Every template starts with an idea only I can have: what it is, who it's for, what makes it memorable. And the rule that nothing gets copied.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The bar.&lt;/strong&gt; The definition of done above came from me. The agent meets the bar you set. Set it low and it'll happily ship something mediocre.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The final call.&lt;/strong&gt; I reviewed every template visually for bugs and checked that every interaction works. Basic stuff, but someone has to do it.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  "Isn't this just AI slop?"
&lt;/h2&gt;

&lt;p&gt;It's a fair question to ask. Anything built with AI right now gets called AI slop sooner or later, so I'd rather answer it up front.&lt;/p&gt;

&lt;p&gt;Here's the thing: &lt;strong&gt;they look good, and they're templates.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A template isn't the final site. It's where the final site starts. Nobody ships a template untouched. You add your brand, your copy, your photos, your product, your decisions. The template's job is to get you to a good, working, accessible starting point fast, so you can spend your time on the parts only you can do.&lt;/p&gt;

&lt;p&gt;And "AI-generated" isn't what makes something slop. &lt;strong&gt;Slop is what you get when nobody made the decisions.&lt;/strong&gt; Default fonts, default layouts, nobody checking whether it works on a phone or with a keyboard. Every one of these templates has its own concept, its own type pairing and its own signature interaction, and every one goes through the same checklist before it ships.&lt;/p&gt;

&lt;p&gt;The AI wrote a lot of the code. It didn't decide what good looks like. I've seen hundreds, maybe thousands, of templates in my day, and to me these are top-tier by any standard.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I'd tell a designer starting today
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Just try it.&lt;/strong&gt; This is the biggest lesson. Get familiar with these tools. They're here to stay, and they can make your life a lot easier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Start with the concept, not the prompt.&lt;/strong&gt; The clearer your idea, the closer the first build gets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write your definition of done first.&lt;/strong&gt; Think about the big picture you want to achieve. The agent will meet whatever bar you set.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Make the agent write the docs.&lt;/strong&gt; They help you, your buyers, and the next agent that opens the project.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hand it the tedious stuff.&lt;/strong&gt; You don't have to do everything with AI. Give it the boring, careful work and keep the fun parts for yourself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Let AI do the business parts too.&lt;/strong&gt; Checkout, exports, share images, even the launch video. That's where one person gets a lot more leverage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Experiment.&lt;/strong&gt; Try things you wouldn't have had time for before.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h4&gt;
  
  
  Can AI design a website on its own?
&lt;/h4&gt;

&lt;p&gt;It can build a good one from a clear concept, and fast. The concept, the quality bar and the final call still come from a person. That's where the difference between a generic site and a good one comes from.&lt;/p&gt;

&lt;h4&gt;
  
  
  Are AI-built website templates any good?
&lt;/h4&gt;

&lt;p&gt;They can be, if someone sets a real bar.&lt;/p&gt;

&lt;h4&gt;
  
  
  What is an AI-ready template?
&lt;/h4&gt;

&lt;p&gt;A template that ships with docs your AI coding assistant can read: a README.md, a DESIGN.md and an AGENTS.md. The assistant then edits the template the way it was built, instead of guessing.&lt;/p&gt;

&lt;h4&gt;
  
  
  How long does it take to build a template with AI?
&lt;/h4&gt;

&lt;p&gt;For me, about 3–6 hours from a design to a finished template, including both editions and the full quality checklist.&lt;/p&gt;

&lt;h4&gt;
  
  
  Do I need to know how to code to use these templates?
&lt;/h4&gt;

&lt;p&gt;No. The HTML edition runs straight from the folder you download, with no build step, and the included docs let your AI assistant make changes for you.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrap-up
&lt;/h2&gt;

&lt;p&gt;Building this shop with an AI agent changed what I think one designer can do. And I'm just getting started: more templates, and more posts like this one, are on the way.&lt;/p&gt;

&lt;p&gt;If you read this far, thank you! 🙏&lt;/p&gt;

&lt;p&gt;And if you'd rather skip all of this and start from something that has already been through the checklist, &lt;a href="https://no-code.supply" rel="noopener noreferrer"&gt;the templates are here&lt;/a&gt;. &lt;a href="https://no-code.supply/all-access/" rel="noopener noreferrer"&gt;All-Access&lt;/a&gt; gets you every template, including every one I add later. 🔥&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>design</category>
      <category>showdev</category>
    </item>
    <item>
      <title>How to create Dark Mode using only CSS</title>
      <dc:creator>Lauri Lännenmäki</dc:creator>
      <pubDate>Thu, 03 Mar 2022 22:32:55 +0000</pubDate>
      <link>https://dev.to/laurilllll/how-to-create-dark-mode-using-only-css-2cb4</link>
      <guid>https://dev.to/laurilllll/how-to-create-dark-mode-using-only-css-2cb4</guid>
      <description>&lt;p&gt;Dark Mode and dark interfaces are not a new thing as they have been a trend for many years. When Apple released a dark mode option with the iOS 13 update back in 2019, the trend took off.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using modern CSS, you can create dark mode with a few lines of code.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's dive in!&lt;/p&gt;

&lt;h4&gt;
  
  
  📚 Content
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Dark mode is more than a trend&lt;/li&gt;
&lt;li&gt;Dark mode is accessibility&lt;/li&gt;
&lt;li&gt;
How to set up dark mode with CSS

&lt;ul&gt;
&lt;li&gt;Create CSS Custom Properties (variables)&lt;/li&gt;
&lt;li&gt;Create conditional styling&lt;/li&gt;
&lt;li&gt;Apply styles&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Demo and final code&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  ✨ Bonus Content
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Color-scheme meta tag and CSS property&lt;/li&gt;
&lt;li&gt;
How to test your code

&lt;ul&gt;
&lt;li&gt;Google Chrome Dev Tools&lt;/li&gt;
&lt;li&gt;Device OS Settings&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  TL;DR;
&lt;/h2&gt;

&lt;p&gt;You can create dark mode using only &lt;strong&gt;CSS Custom Properties aka CSS variables&lt;/strong&gt; and &lt;strong&gt;CSS media feature &lt;code&gt;prefers-color-scheme&lt;/code&gt;&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* 1. Create global variables for text and background color */&lt;/span&gt;
&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--color-text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;black&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--color-background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* 2. Add conditional styling for dark mode preference */&lt;/span&gt;
&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefers-color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* Re-assign previous variables */&lt;/span&gt;
  &lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;--color-text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* black =&amp;gt; white */&lt;/span&gt;
    &lt;span class="py"&gt;--color-background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;black&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* white =&amp;gt; black */&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* 3. Assign variables to page color and background */&lt;/span&gt;
&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--color-text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--color-background&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it 🔥 View my &lt;strong&gt;CSS Only Dark Mode&lt;/strong&gt; &lt;a href="https://codepen.io/laurilllll/pen/yLPKojZ" rel="noopener noreferrer"&gt;Codepen&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Dark mode is more than a trend
&lt;/h2&gt;

&lt;p&gt;Dark mode is not a design trend. Dark interfaces can be easier on the eyes and reduce eye strain and fatigue. They can actually help reduce the sensation of discomfort felt when staring at websites with light backgrounds.&lt;/p&gt;

&lt;p&gt;Dark mode also saves your device's battery life. It can reduce battery usage, especially on mobile devices.&lt;/p&gt;




&lt;h2&gt;
  
  
  Dark mode is accessibility
&lt;/h2&gt;

&lt;p&gt;Dark mode is also an accessibility (a11y) feature. &lt;strong&gt;It's about respecting your users and their preferences so they can use your website the way they like.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Besides responsive media queries, modern CSS has many cool media features and &lt;code&gt;prefers-color-scheme&lt;/code&gt; is one of them.  &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme" rel="noopener noreferrer"&gt;Quote from MDN Web Docs&lt;/a&gt; 👇&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The prefers-color-scheme CSS media feature is used to detect if the user has requested a light or dark color theme. The user might indicate this preference through an operating system setting (e.g. light or dark mode) or a user agent setting.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Users can choose if they prefer light or dark mode from their operating system settings.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs4llkxl3oc2oi1ju3mx4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs4llkxl3oc2oi1ju3mx4.png" alt="Picture from macOS general settings." width="748" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(macOS)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzf9qaupb806yxk67fobl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzf9qaupb806yxk67fobl.png" alt="Picture from iOS Display &amp;amp; Brightness settings." width="800" height="390"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(iOS)&lt;/p&gt;

&lt;p&gt;With CSS, you can serve the user the theme they prefer. Media queries are used to conditionally apply styles. Media feature &lt;code&gt;prefers-color-scheme&lt;/code&gt; is a &lt;strong&gt;Media Query&lt;/strong&gt;, so you write it like you'd write your breakpoints:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* Breakpoint */&lt;/span&gt;
&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;800px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* Add conditional styles here */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* Prefers Color Scheme */&lt;/span&gt;
&lt;span class="c"&gt;/* Dark */&lt;/span&gt;
&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefers-color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* Add conditional styles here */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;/* Light */&lt;/span&gt;
&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefers-color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;light&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* Add conditional styles here */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  How to set up dark mode with CSS
&lt;/h2&gt;

&lt;h4&gt;
  
  
  Create CSS Custom Properties (variables)
&lt;/h4&gt;

&lt;p&gt;To setup color styles, I recommend using &lt;strong&gt;variables&lt;/strong&gt; to keep your code DRY (don't repeat yourself). If you aren't familiar with CSS variables, you can read about them from &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/--*" rel="noopener noreferrer"&gt;MDN Web Docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For the sake of this tutorial we keep things simple and use black and white as our colors. And we only style our text color and background color.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* 1. Create global variables for text and background color */&lt;/span&gt;
&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--color-text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;black&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--color-background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Create conditional styling
&lt;/h4&gt;

&lt;p&gt;Next, let's apply &lt;strong&gt;conditional styling for users who prefer dark mode&lt;/strong&gt;. By using a media query, we re-assign previous variables with new inverted values.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* 2. Add conditional styling for dark mode preference */&lt;/span&gt;
&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefers-color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* Re-assign previous variables */&lt;/span&gt;
  &lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;--color-text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* black =&amp;gt; white */&lt;/span&gt;
    &lt;span class="py"&gt;--color-background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;black&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* white =&amp;gt; black */&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Apply styles
&lt;/h4&gt;

&lt;p&gt;And finally, we &lt;strong&gt;apply the variables&lt;/strong&gt; to our design:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* 3. Assign variables to page color and background */&lt;/span&gt;
&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--color-text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--color-background&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Demo and final code
&lt;/h2&gt;

&lt;p&gt;See the final effect in action of user changing between light and dark mode in OS settings: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzd6aqe9w1bidmjnu44ui.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzd6aqe9w1bidmjnu44ui.gif" alt="GIF animation of user changing between light and dark mode in OS settings." width="200" height="123"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;👇 The final code in full.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* 1. Create global variables for text and background color */&lt;/span&gt;
&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--color-text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;black&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--color-background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* 2. Add conditional styling for dark mode preference */&lt;/span&gt;
&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefers-color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* Re-assign previous variables */&lt;/span&gt;
  &lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;--color-text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* black =&amp;gt; white */&lt;/span&gt;
    &lt;span class="py"&gt;--color-background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;black&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* white =&amp;gt; black */&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* 3. Assign variables to page color and background */&lt;/span&gt;
&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--color-text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--color-background&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can view the code and play with it in my &lt;a href="https://codepen.io/laurilllll/pen/yLPKojZ" rel="noopener noreferrer"&gt;Codepen&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  ✨ Bonus Content
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Color-scheme meta tag and CSS property
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;color-scheme&lt;/code&gt; meta tag and CSS property takes care of two important things for you. This next quote is from &lt;a href="https://web.dev/color-scheme/" rel="noopener noreferrer"&gt;Google Developers web.dev&lt;/a&gt; and I've taken the liberty to highlight some parts of it 👇&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;They both make your life as a developer easier by allowing you to &lt;strong&gt;opt your page in to theme-specific defaults of the user agent stylesheet&lt;/strong&gt;, such as, for example, form controls, scroll bars, as well as CSS system colors. At the same time, this feature &lt;strong&gt;prevents browsers from applying any transformations on their own.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;By the order of values, you can determine the default appearance. Here's the syntax for both CSS and the corresponding HTML meta tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dark&lt;/span&gt; &lt;span class="n"&gt;light&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(In the CSS example, the page author prefers &lt;strong&gt;dark&lt;/strong&gt; theme.)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"color-scheme"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"light dark"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(In the HTML example, the page author prefers &lt;strong&gt;light&lt;/strong&gt; theme.)&lt;/p&gt;

&lt;p&gt;It is recommended to specify the &lt;code&gt;color-scheme&lt;/code&gt; via the meta tag, so the browser can adopt to the preferred scheme faster.&lt;/p&gt;




&lt;h3&gt;
  
  
  How to test your code
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Google Chrome Dev Tools&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can test your code with Google Chrome Dev Tools.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open up your Dev Tools Inspection panel by pressing &lt;code&gt;Command+Option+C&lt;/code&gt; (Mac) or &lt;code&gt;Control+Shift+C&lt;/code&gt; (Windows, Linux, Chrome OS).&lt;/li&gt;
&lt;li&gt;Click on the &lt;strong&gt;three dots&lt;/strong&gt; in the &lt;strong&gt;upper right corner&lt;/strong&gt; =&amp;gt; select &lt;strong&gt;More tools&lt;/strong&gt; =&amp;gt; click &lt;strong&gt;Rendering&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;From the Rendering tab you can find the emulation for &lt;code&gt;prefers-colors-scheme&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;See the screenshots below 👇&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxp5k9l4zqn2i3ujfqrc7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxp5k9l4zqn2i3ujfqrc7.png" alt="Image of Google Chrome Dev Tools' Rendering tab highlighting prefers-color-scheme emulation." width="800" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhilrpchexfd3yantizcc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhilrpchexfd3yantizcc.png" alt="Image of Google Chrome Dev Tools' Rendering tab prefers-color-scheme emulation turned to emulate dark mode." width="800" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Device OS settings&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As shown before, you can change the appearance from you device settings. Screenshots are from macOS and iOS. You can find the same settings from Android and Windows devices.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs4llkxl3oc2oi1ju3mx4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs4llkxl3oc2oi1ju3mx4.png" alt="Picture from macOS general settings." width="748" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzf9qaupb806yxk67fobl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzf9qaupb806yxk67fobl.png" alt="Picture from iOS Display &amp;amp; Brightness settings." width="800" height="390"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Resources and further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;CSS Only Dark Mode &lt;a href="https://codepen.io/laurilllll/pen/yLPKojZ" rel="noopener noreferrer"&gt;Codepen&lt;/a&gt; by me&lt;/li&gt;
&lt;li&gt;Color-scheme meta tag and CSS property at &lt;a href="https://web.dev/color-scheme/" rel="noopener noreferrer"&gt;web.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;CSS Media Feature &lt;code&gt;prefers-color-scheme&lt;/code&gt; at &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme" rel="noopener noreferrer"&gt;MDN Web Docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;CSS Custom Properties aka CSS Variables at &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/--*" rel="noopener noreferrer"&gt;MDN Web Docs&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Thank you 🙏
&lt;/h2&gt;

&lt;p&gt;I hope you found this article helpful 🔥&lt;/p&gt;

&lt;p&gt;You can &lt;a href="https://twitter.com/laurilllll" rel="noopener noreferrer"&gt;follow me on Twitter&lt;/a&gt; for more &lt;strong&gt;CSS&lt;/strong&gt;, &lt;strong&gt;Design System&lt;/strong&gt; and &lt;strong&gt;Figma&lt;/strong&gt; content 💪 &lt;/p&gt;

</description>
      <category>css</category>
      <category>design</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Figma Plugin API: How to Load and Use Fonts</title>
      <dc:creator>Lauri Lännenmäki</dc:creator>
      <pubDate>Sun, 06 Feb 2022 07:45:12 +0000</pubDate>
      <link>https://dev.to/laurilllll/figma-plugin-api-how-to-load-and-use-fonts-bj2</link>
      <guid>https://dev.to/laurilllll/figma-plugin-api-how-to-load-and-use-fonts-bj2</guid>
      <description>&lt;p&gt;Working with Figma text through the Figma Plugin API is a lot trickier than you might think. All the text inside Figma files are &lt;em&gt;nodes&lt;/em&gt; called &lt;strong&gt;TextNodes&lt;/strong&gt;. Essentially, nodes are &lt;strong&gt;layers&lt;/strong&gt; in Figma. With text nodes you have to think about &lt;em&gt;mixed styles&lt;/em&gt;, &lt;em&gt;missing fonts&lt;/em&gt; and &lt;em&gt;loading fonts&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;For now, we concentrate on loading fonts.&lt;/p&gt;

&lt;p&gt;If you wish to change the content of a TextNode, &lt;strong&gt;you must load its font&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Here's one way of loading a font and manipulating text:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Load font via async function&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myFontLoadingFunction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;figma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loadFontAsync&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Work Sans&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Bold&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Another function where we use the loaded font&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myAnotherFunction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Create text layer&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myTextLayer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;figma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createText&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="c1"&gt;// Set the font&lt;/span&gt;
  &lt;span class="nx"&gt;myTextLayer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fontName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Work Sans&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Bold&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// Set text to uppercase&lt;/span&gt;
  &lt;span class="nx"&gt;myTextLayer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textCase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;UPPER&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="c1"&gt;// Set text content&lt;/span&gt;
  &lt;span class="nx"&gt;myTextLayer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;characters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;This is my title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="c1"&gt;// Add text layer to current page in Figma&lt;/span&gt;
  &lt;span class="nx"&gt;figma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentPage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myTextLayer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Use font only after the Promise is resolved&lt;/span&gt;
&lt;span class="nf"&gt;myFontLoadingFunction&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;myAnotherFunction&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's basically it. If you want to know more about this subject, just keep on reading.&lt;/p&gt;




&lt;h3&gt;
  
  
  Glossary
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;About loading fonts&lt;/li&gt;
&lt;li&gt;How to load fonts&lt;/li&gt;
&lt;li&gt;What can you do with fonts and text&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can find the official &lt;em&gt;Figma Plugin documentation&lt;/em&gt; &lt;a href="https://www.figma.com/plugin-docs/api/TextNode/" rel="noopener noreferrer"&gt;page about &lt;strong&gt;TextNodes&lt;/strong&gt; here&lt;/a&gt; but I believe the docs could be better and that's why I wrote this blog post.&lt;/p&gt;




&lt;h2&gt;
  
  
  About loading fonts
&lt;/h2&gt;

&lt;p&gt;The docs tells us this important thing about text and fonts:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The important thing with text is that &lt;strong&gt;changing the content of a text node requires its font to be loaded and that fonts are not always available.&lt;/strong&gt; If you attempt to change, say, &lt;code&gt;fontName&lt;/code&gt; without first loading the font for that text node, the plugin will throw an exception.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I found out that the exception will look like this in your console.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F68j548abd5v9xp9cyvqb.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F68j548abd5v9xp9cyvqb.jpg" alt="Image of an error in console." width="701" height="154"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So, basically every time you make changes to text content or its properties, you have to load its font.&lt;/strong&gt; Only when you're changing properties like fill colour or stroke, you &lt;strong&gt;do not&lt;/strong&gt; need to load the fonts.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to load fonts
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;TextNodes&lt;/strong&gt; have a property called &lt;code&gt;fontName&lt;/code&gt; which is a javascript object that contains the font family and font style. The default font is &lt;code&gt;{ family: "Roboto", style: "Regular" }&lt;/code&gt;. In our example we want to use &lt;strong&gt;Work Sans, Bold&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;(As a sidenote: You can load any font thats already accessible in the Figma editor. You cannot load fonts from the internet.)&lt;/p&gt;

&lt;p&gt;The documentation says that &lt;em&gt;"Loading a font is done via &lt;code&gt;figma.loadFontAsync(fontname)&lt;/code&gt;"&lt;/em&gt; and maybe for someone that is enough but, as a designer, I was a bit confused when I tried it and it didn't work.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Like in the docs =&amp;gt; figma.loadFontAsync(fontname)&lt;/span&gt;
&lt;span class="nx"&gt;figma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loadFontAsync&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Work Sans&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Bold&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On it's own, this code isn't enough. This is when the &lt;strong&gt;error&lt;/strong&gt; appears in the console. As we dive deeper into the docs we find this about the &lt;strong&gt;loadFontAsync&lt;/strong&gt; property:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;(loadFontAsync) Makes a font available in the plugin for use when creating and modifying text. Calling this function is necessary to modify any property of a text node that may cause the rendered text to change, including .characters, .fontSize, .fontName, etc.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And from it's signature (found in the docs) &lt;code&gt;loadFontAsync(fontName: FontName): Promise&amp;lt;void&amp;gt;&lt;/code&gt; we can see that it returns a &lt;strong&gt;Promise&lt;/strong&gt;. So, we must enchance our code by creating an "async/await" function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myFontLoadingFunction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;figma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loadFontAsync&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Work Sans&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Bold&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the &lt;strong&gt;loadFontAsync&lt;/strong&gt; Promise is resolved, we can use the &lt;code&gt;then()&lt;/code&gt; method that returns a new Promise and when that is a success we can do what we initially wanted.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;myFontLoadingFunction&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Success!&lt;/span&gt;

  &lt;span class="c1"&gt;// Add your code here to do something with the loaded font!&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can read more about &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise" rel="noopener noreferrer"&gt;Promises&lt;/a&gt; and the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then" rel="noopener noreferrer"&gt;&lt;code&gt;then()&lt;/code&gt;&lt;/a&gt; from the MDN docs.&lt;/p&gt;

&lt;p&gt;All the code once more:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Load font via async function&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myFontLoadingFunction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;figma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loadFontAsync&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Work Sans&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Bold&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Another function where we use the loaded font&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myAnotherFunction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Create text layer&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myTextLayer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;figma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createText&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="c1"&gt;// Set the font&lt;/span&gt;
  &lt;span class="nx"&gt;myTextLayer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fontName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Work Sans&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Bold&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// Set text to uppercase&lt;/span&gt;
  &lt;span class="nx"&gt;myTextLayer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textCase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;UPPER&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="c1"&gt;// Set text content&lt;/span&gt;
  &lt;span class="nx"&gt;myTextLayer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;characters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;This is my title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="c1"&gt;// Add text layer to current page in Figma&lt;/span&gt;
  &lt;span class="nx"&gt;figma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentPage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myTextLayer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Use font only after the Promise is resolved&lt;/span&gt;
&lt;span class="nf"&gt;myFontLoadingFunction&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;myAnotherFunction&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  What can you do with fonts and text
&lt;/h2&gt;

&lt;p&gt;After you have your fonts loaded you can use the following properties and functions of a TextNode:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;characters&lt;/strong&gt; = set/write your text content&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;fontSize&lt;/strong&gt; = set font size&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;fontName&lt;/strong&gt; = set font family and style/weight&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;textStyleId&lt;/strong&gt; = set TextStyle using TextStyle id&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;textCase&lt;/strong&gt; = set text casing to uppercase etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;textDecoration&lt;/strong&gt; = set decoration to underline/strikethrough&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;letterSpacing&lt;/strong&gt; = set letter spacing in pixels or percents&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;lineHeight&lt;/strong&gt; = set line height in pixels or percents&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;setRangeFontSize()&lt;/strong&gt; = set font size to characters in range&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;setRangeFontName()&lt;/strong&gt; = set font to characters in range&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;setRangeTextCase()&lt;/strong&gt; = set text casing to characters in range&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;setRangeTextDecoration()&lt;/strong&gt; = set text decoration to characters in range&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;setRangeLetterSpacing()&lt;/strong&gt; = set letter spacing to characters in range&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;setRangeLineHeight()&lt;/strong&gt; = set line height to characters in range&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;setRangeTextStyleId()&lt;/strong&gt; = set TextStyle to characters in range&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can check out all the &lt;strong&gt;TextNode&lt;/strong&gt; properties and functions &lt;a href="https://www.figma.com/plugin-docs/api/TextNode/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;Have fun developing Figma plugins 🔥 I hope you found this article helpful!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/laurilllll" rel="noopener noreferrer"&gt;Follow me on Twitter&lt;/a&gt; for more &lt;strong&gt;Figma&lt;/strong&gt;, &lt;strong&gt;CSS&lt;/strong&gt; and &lt;strong&gt;Design System&lt;/strong&gt; content 💪 &lt;/p&gt;

</description>
      <category>typescript</category>
      <category>html</category>
      <category>figma</category>
      <category>plugin</category>
    </item>
    <item>
      <title>How to create responsive typography using CSS — Three different methods explained</title>
      <dc:creator>Lauri Lännenmäki</dc:creator>
      <pubDate>Wed, 20 Jan 2021 20:52:14 +0000</pubDate>
      <link>https://dev.to/laurilllll/how-to-create-responsive-typography-using-css-three-different-methods-explained-50f8</link>
      <guid>https://dev.to/laurilllll/how-to-create-responsive-typography-using-css-three-different-methods-explained-50f8</guid>
      <description>&lt;p&gt;Good typography is one of the key elements of a website. Let's take a look at &lt;strong&gt;three different methods&lt;/strong&gt; on &lt;strong&gt;how to create responsive typography using CSS&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The three methods are:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Responsive typography using &lt;strong&gt;only media queries&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Responsive typography using &lt;strong&gt;a CSS custom property as a multiplier&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Responsive typography using &lt;strong&gt;the CSS clamp() function&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Check out the final &lt;a href="https://codepen.io/laurilllll/pen/ZEWZvBg" rel="noopener noreferrer"&gt;Codepen with all the methods&lt;/a&gt;. Resize the viewport to see the effect.&lt;/p&gt;




&lt;h3&gt;
  
  
  Why should you use responsive typography?
&lt;/h3&gt;

&lt;p&gt;The web is used on various screen sizes. Your site has to work on &lt;strong&gt;mobile&lt;/strong&gt;, &lt;strong&gt;tablet&lt;/strong&gt;, &lt;strong&gt;laptop&lt;/strong&gt;, &lt;strong&gt;desktop&lt;/strong&gt; and basically everything in between so there's a lot to take into consideration.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;All of the method examples below are &lt;strong&gt;simplified&lt;/strong&gt; as they only cover &lt;strong&gt;three selectors&lt;/strong&gt; and &lt;strong&gt;one breakpoint&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can use this simple &lt;strong&gt;HTML&lt;/strong&gt; to try out all the methods in this tutorial:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;How to create responsive typography using CSS&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
  Vestibulum molestie sapien eget orci pellentesque, et aliquam lectus convallis. Phasellus neque velit, ultricies ut lacus at, finibus lobortis dui. Proin eget diam elit.
&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Three different methods explained&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
  Pellentesque sagittis nisl nec rhoncus porta. Quisque luctus turpis nec turpis consequat fermentum et vitae diam.
&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Let's dig in!&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Method 1: Responsive typography using only media queries
&lt;/h2&gt;

&lt;p&gt;This is the most basic method in our list.&lt;/p&gt;

&lt;p&gt;Simply, &lt;strong&gt;declare your text styles&lt;/strong&gt; and &lt;strong&gt;increase the font-size on bigger screens&lt;/strong&gt; using a breakpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* Declare text styles */&lt;/span&gt;
&lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;h2&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.5em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* Increase font sizes by 1.5x on bigger screens */&lt;/span&gt;
&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;48rem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;h2&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2.25em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.5em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check out the result from this &lt;a href="https://codepen.io/laurilllll/pen/bGwJbJb" rel="noopener noreferrer"&gt;Codepen&lt;/a&gt;. Resize the viewport to see the effect.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/laurilllll/embed/bGwJbJb?height=600&amp;amp;default-tab=css,result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Using this method is totally fine and it does the job very well.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;downside is the large amount of code&lt;/strong&gt; you have to write. The codebase gets larger with every new selector you need to add. You have to write the styles &lt;strong&gt;separately&lt;/strong&gt; for &lt;strong&gt;every selector in each breakpoint&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In a real world you would probably have text selectors &lt;strong&gt;h1&lt;/strong&gt;, &lt;strong&gt;h2&lt;/strong&gt;, &lt;strong&gt;h3&lt;/strong&gt;, &lt;strong&gt;h4&lt;/strong&gt;, &lt;strong&gt;h5&lt;/strong&gt;, &lt;strong&gt;h6&lt;/strong&gt;, &lt;strong&gt;p&lt;/strong&gt; and at least &lt;strong&gt;two breakpoints&lt;/strong&gt;. At that point, this code is would've already gone &lt;strong&gt;from 20 lines to 67 lines&lt;/strong&gt;. That's &lt;strong&gt;3.35x&lt;/strong&gt; increase in the amount of code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The next method will show how to &lt;strong&gt;decrease the code amount&lt;/strong&gt; and make it &lt;strong&gt;easier to maintain!&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Method 2: Responsive typography using a CSS custom property as a multiplier
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;CSS custom properties&lt;/strong&gt; (variables) are super powerful. In the previous method, we increased the font sizes &lt;em&gt;manually&lt;/em&gt; one selector at a time. Now, we'll &lt;strong&gt;declare a multiplier variable&lt;/strong&gt; for the font-sizes and only &lt;strong&gt;increase the value of the multiplier&lt;/strong&gt; inside a breakpoint.&lt;/p&gt;

&lt;p&gt;Let's look at the code example in full and then break it down:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* Declare a font size multiplier variable */&lt;/span&gt;
&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--text-multiplier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* Increase the size of the multiplier on bigger screens */&lt;/span&gt;
&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;48rem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;--text-multiplier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* Declare text styles using calc() function and the multiplier */&lt;/span&gt;
&lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2em&lt;/span&gt; &lt;span class="err"&gt;*&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--text-multiplier&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;h2&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1.5em&lt;/span&gt; &lt;span class="err"&gt;*&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--text-multiplier&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1em&lt;/span&gt; &lt;span class="err"&gt;*&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--text-multiplier&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check out the result from this &lt;a href="https://codepen.io/laurilllll/pen/ZEpZzPB" rel="noopener noreferrer"&gt;Codepen&lt;/a&gt;. Resize the viewport to see the effect.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/laurilllll/embed/ZEpZzPB?height=600&amp;amp;default-tab=css,result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The first step&lt;/strong&gt; is to &lt;strong&gt;declare a variable&lt;/strong&gt; that we use to control our font size later on. We set the default value to &lt;strong&gt;1&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--text-multiplier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The second step&lt;/strong&gt; is to &lt;strong&gt;increase the multiplier value&lt;/strong&gt; inside a breakpoint. Unlike SASS variables, CSS custom properties &lt;strong&gt;can be used inside media queries&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;48rem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;--text-multiplier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The last step&lt;/strong&gt; is to declare our font-sizes but this time we use &lt;strong&gt;the calc() function&lt;/strong&gt; so we can utilise our &lt;strong&gt;multiplier&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2em&lt;/span&gt; &lt;span class="err"&gt;*&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--text-multiplier&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;h2&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1.5em&lt;/span&gt; &lt;span class="err"&gt;*&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--text-multiplier&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1em&lt;/span&gt; &lt;span class="err"&gt;*&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--text-multiplier&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, &lt;strong&gt;we only need to declare our font sizes once&lt;/strong&gt;. This is a huge improvement compared to the first method.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Compared to the first method, we only have &lt;strong&gt;17 lines of code compared to 20&lt;/strong&gt;. If we would add four more selectors and one breakpoint, our code would &lt;strong&gt;only increase from 17 lines to 34&lt;/strong&gt;. Remember that using the first method it would require &lt;strong&gt;67 lines&lt;/strong&gt;. Almost twice as much.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The decrease of code is due to our elegant way of using a &lt;strong&gt;CSS custom property as a multiplier&lt;/strong&gt;. Adding another breakpoint takes only &lt;strong&gt;three lines of code&lt;/strong&gt; because we don't have to modify our font sizes at all. The code is also a lot &lt;strong&gt;easier to maintain and modify&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Adding breakpoints is as simple as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* Add more breakpoints */&lt;/span&gt;
&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;64rem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--text-multiplier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.75&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80rem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--text-multiplier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2.0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To the user, this methods &lt;strong&gt;looks exactly the same&lt;/strong&gt; as the first but &lt;strong&gt;the code is much better&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Next up, we'll take a look at a &lt;strong&gt;new CSS clamp() function&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Method 3: Responsive typography using the CSS clamp() function
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;clamp() function&lt;/strong&gt; is pretty powerful! You can set a value using three parameters: &lt;strong&gt;a minimum value&lt;/strong&gt;, &lt;strong&gt;a preferred value&lt;/strong&gt; and &lt;strong&gt;a maximum value&lt;/strong&gt;. Basically you'll have a 3-in-1 value!&lt;/p&gt;

&lt;p&gt;Example declaration for responsive text using the &lt;strong&gt;clamp() function&lt;/strong&gt; looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;font-size: clamp(2rem, 5vw, 3rem);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows us to create responsive typography &lt;strong&gt;without any media queries&lt;/strong&gt;. &lt;strong&gt;With only one line of code&lt;/strong&gt; we set the minimum value (mobile), the maximum value (desktop) and the preferred value.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The font size will become fluid&lt;/strong&gt; as it will always be the &lt;strong&gt;preferred value inside the min-max range&lt;/strong&gt;. Very powerful!&lt;/p&gt;

&lt;p&gt;To set the responsive typography for our demo, it is as simple as this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* Declare text styles */&lt;/span&gt;
&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* Font minimum, preferred and maximum value */&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--min&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--val&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--max&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* Font size variables */&lt;/span&gt;
&lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--min&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* minimum value */&lt;/span&gt;
  &lt;span class="py"&gt;--val&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5vw&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* preferred value = 5% viewport width */&lt;/span&gt;
  &lt;span class="py"&gt;--max&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* maximum value */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;h2&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--min&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.5em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c"&gt;/* minimum value */&lt;/span&gt;
  &lt;span class="py"&gt;--val&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4vw&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;     &lt;span class="c"&gt;/* preferred value = 4% viewport width */&lt;/span&gt;
  &lt;span class="py"&gt;--max&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2.25em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c"&gt;/* maximum value */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--min&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c"&gt;/* minimum value */&lt;/span&gt;
  &lt;span class="py"&gt;--val&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2.5vw&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* preferred value = 2.5% viewport width */&lt;/span&gt;
  &lt;span class="py"&gt;--max&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.5em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* maximum value */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check out the result from this &lt;a href="https://codepen.io/laurilllll/pen/ExgJYRm" rel="noopener noreferrer"&gt;Codepen&lt;/a&gt;. Resize the viewport to see the effect.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/laurilllll/embed/ExgJYRm?height=600&amp;amp;default-tab=css,result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;If you look at the code, you'll see that by using &lt;strong&gt;CSS custom properties together with the clamp() function&lt;/strong&gt; we are able to &lt;strong&gt;declare all the text styles at the same time with only on line of code&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;After that, we just set the &lt;strong&gt;--min&lt;/strong&gt;, &lt;strong&gt;--val&lt;/strong&gt; and &lt;strong&gt;--max&lt;/strong&gt; custom properties for each selector and we are done!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;CSS custom properties can be &lt;strong&gt;scoped inside selectors&lt;/strong&gt; so you can &lt;strong&gt;use the same custom properties with multiple selectors&lt;/strong&gt; just like we have done here with our &lt;strong&gt;h1&lt;/strong&gt;, &lt;strong&gt;h2&lt;/strong&gt; and &lt;strong&gt;p&lt;/strong&gt;. These custom properties will not clash together even though they have the same name.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This method takes up 18 lines of code and with four more selectors and one breakpoint it would be 38 lines. So, it's slightly larger than on the second method but we get &lt;strong&gt;fluid typography&lt;/strong&gt; in return.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The &lt;strong&gt;clamp() function&lt;/strong&gt; is pretty new, so you should check if it has the browser support you need from &lt;a href="https://caniuse.com/css-math-functions" rel="noopener noreferrer"&gt;caniuse.com&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrap up
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;CSS is powerful!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In many cases, there's more than one way to achieve the desired result.&lt;/p&gt;

&lt;p&gt;If you read this far, thank you! I hope you learned something new.&lt;/p&gt;

&lt;p&gt;Here's the final &lt;a href="https://codepen.io/laurilllll/pen/ZEWZvBg" rel="noopener noreferrer"&gt;Codepen with all the methods&lt;/a&gt; once more. Resize the viewport to see the effect.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/laurilllll/embed/ZEWZvBg?height=600&amp;amp;default-tab=css,result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;




&lt;h2&gt;
  
  
  Thank you 🙏
&lt;/h2&gt;

&lt;p&gt;I hope you found this article helpful 🔥&lt;/p&gt;

&lt;p&gt;You can &lt;a href="https://twitter.com/laurilllll" rel="noopener noreferrer"&gt;follow me on Twitter&lt;/a&gt; for more &lt;strong&gt;CSS&lt;/strong&gt;, &lt;strong&gt;Design System&lt;/strong&gt; and &lt;strong&gt;Figma&lt;/strong&gt; content 💪 &lt;/p&gt;

</description>
      <category>css</category>
      <category>html</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
