<?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: Artclick</title>
    <description>The latest articles on DEV Community by Artclick (@_artclick).</description>
    <link>https://dev.to/_artclick</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%2F4049105%2F5e4d789f-a23c-435d-81df-3e14f5091905.png</url>
      <title>DEV Community: Artclick</title>
      <link>https://dev.to/_artclick</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/_artclick"/>
    <language>en</language>
    <item>
      <title>How the JavaScript Runtime Schedules Callbacks: A Look at the Event Loop</title>
      <dc:creator>Artclick</dc:creator>
      <pubDate>Mon, 24 Aug 2026 11:00:22 +0000</pubDate>
      <link>https://dev.to/_artclick/how-the-javascript-runtime-schedules-callbacks-a-look-at-the-event-loop-4odf</link>
      <guid>https://dev.to/_artclick/how-the-javascript-runtime-schedules-callbacks-a-look-at-the-event-loop-4odf</guid>
      <description>&lt;p&gt;&lt;strong&gt;Why doesn't your tab freeze when you call an API?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here's a question that trips up a lot of people, even after they've been writing JavaScript for a while: if JS can only run one line of code at a time, what actually happens while a &lt;code&gt;fetch()&lt;/code&gt; request is out there waiting for a response? Does the browser just sit there, frozen, unable to scroll or click or repaint the screen, until the response comes back?&lt;/p&gt;

&lt;p&gt;It doesn't. And once you understand why, a whole pile of confusing behavior in JavaScript stops feeling random and starts feeling predictable. This is that explanation, minus the hand-waving.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one-thread rule
&lt;/h2&gt;

&lt;p&gt;JavaScript runs on a single thread. One thread means one call stack, and one call stack means the engine can only execute one function at a time. There's no built-in way for your JS code to spin up a second thread and run two functions simultaneously, not the way you might in a language with native threading.&lt;/p&gt;

&lt;p&gt;If that were the whole story, any slow operation (a network call, a big file read, a multi-second timer) would lock up everything else. No animations, no clicks registering, no scrolling. Anyone who's accidentally written a giant synchronous loop has felt this firsthand. The tab genuinely freezes, because the one thread is busy and nothing else gets a turn until it's done.&lt;/p&gt;

&lt;p&gt;So how does &lt;code&gt;fetch()&lt;/code&gt; avoid doing that? It doesn't run on your JS thread at all. The browser hands it off.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handing work off to the browser
&lt;/h2&gt;

&lt;p&gt;The JavaScript engine itself (V8 in Chrome, SpiderMonkey in Firefox, whichever one your browser uses) is genuinely single threaded. But your JS code isn't running in a vacuum. It's running inside a browser, and the browser gives you a bunch of APIs that live outside that single thread: timers, the DOM, &lt;code&gt;fetch&lt;/code&gt;, geolocation, and so on. These are usually called the Web APIs, and they're implemented by the browser itself, often backed by their own threads or OS-level mechanisms.&lt;/p&gt;

&lt;p&gt;So when you call &lt;code&gt;setTimeout(fn, 3000)&lt;/code&gt;, here's what's actually happening:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Your code calls &lt;code&gt;setTimeout&lt;/code&gt;. The JS engine registers this call, hands the timer off to the browser, and immediately moves on to the next line. It does not pause and wait three seconds.&lt;/li&gt;
&lt;li&gt;The browser starts a countdown somewhere outside your JS thread.&lt;/li&gt;
&lt;li&gt;Your script keeps running, the page stays responsive, clicks still register.&lt;/li&gt;
&lt;li&gt;Three seconds later, the browser is done timing. It doesn't just barge into your JS thread and run the callback whenever it feels like it. Instead, it places the callback into a queue and waits.&lt;/li&gt;
&lt;li&gt;Only once your JS thread is completely free does that callback actually get pulled off the queue and run.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That last step, the constant checking of "is the thread free yet, and if so, what's waiting," is the event loop. It's not a separate clever piece of magic bolted onto JS. It's closer to a simple, boring loop that just keeps asking the same question over and over: anything on the stack right now? No? Then grab the next thing waiting and run it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two different queues, not one
&lt;/h2&gt;

&lt;p&gt;This is the part that catches people off guard, because most explanations gloss over it or mention it too late. There isn't just one queue of "stuff waiting to run." There are (at minimum) two, and they get treated very differently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The macrotask queue&lt;/strong&gt; (often just called the callback queue or task queue) holds things like &lt;code&gt;setTimeout&lt;/code&gt; and &lt;code&gt;setInterval&lt;/code&gt; callbacks, DOM events, and I/O completions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The microtask queue&lt;/strong&gt; holds Promise callbacks (&lt;code&gt;.then&lt;/code&gt;, &lt;code&gt;.catch&lt;/code&gt;, &lt;code&gt;.finally&lt;/code&gt;), &lt;code&gt;queueMicrotask()&lt;/code&gt;, and a few other spec-defined bits like &lt;code&gt;MutationObserver&lt;/code&gt; callbacks.&lt;/p&gt;

&lt;p&gt;The rule that matters: after each macrotask finishes, and before the engine grabs the next macrotask, it fully drains the microtask queue. Every single microtask, including any new ones that got added while draining, runs before the event loop even glances at the macrotask queue again.&lt;/p&gt;

&lt;p&gt;This is why Promises tend to "cut in line" ahead of timers, even when the timer looks like it should win.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tracing through an actual example
&lt;/h2&gt;

&lt;p&gt;Theory is fine, but this stuff really clicks once you predict an output and then check yourself. Take this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;start&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;setTimeout&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;timeout&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;promise&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;end&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;Before reading further, guess the order these four lines print in.&lt;/p&gt;

&lt;p&gt;Here's what actually happens, step by step:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;console.log('start')&lt;/code&gt; runs immediately. It's synchronous, so there's no queue involved at all, it just executes right where it sits. Prints &lt;code&gt;start&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;setTimeout(..., 0)&lt;/code&gt; gets registered with the browser. Even with a delay of zero, the callback doesn't run now, it goes to the browser's timer system first and its callback lands in the macrotask queue once the (essentially instant) delay is up.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Promise.resolve().then(...)&lt;/code&gt; schedules its callback into the microtask queue. Also not immediate.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;console.log('end')&lt;/code&gt; runs immediately, same as &lt;code&gt;start&lt;/code&gt;. Prints &lt;code&gt;end&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Now the call stack is finally empty, and the engine looks for work. It checks the microtask queue first, finds the Promise callback, and runs it. Prints &lt;code&gt;promise&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Only after the microtask queue is completely empty does the engine move to the macrotask queue and run the timeout callback. Prints &lt;code&gt;timeout&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Final output: &lt;code&gt;start&lt;/code&gt;, &lt;code&gt;end&lt;/code&gt;, &lt;code&gt;promise&lt;/code&gt;, &lt;code&gt;timeout&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The takeaway that actually matters for writing and debugging real code: a &lt;code&gt;setTimeout&lt;/code&gt; with a delay of &lt;code&gt;0&lt;/code&gt; does not mean "run this next." It means "run this once the current call stack is clear and every pending microtask has been handled." Those are very different guarantees, and mixing them up is a common source of bugs in code that assumes strict execution order.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters for callbacks and async/await
&lt;/h2&gt;

&lt;p&gt;Before Promises were standard, this same non-blocking model was handled entirely with callbacks: pass a function in, get it called back later when the work is done. That pattern is still everywhere (event listeners, &lt;code&gt;fs.readFile&lt;/code&gt; in Node, older APIs), but it has a well known failure mode once you need several async steps to happen in sequence, each depending on the last one's result. You end up nesting callback inside callback inside callback, each one indented a bit further right than the last. People call this callback hell, or the pyramid of doom, because that's genuinely what it looks like on screen, and it makes error handling and control flow painful to follow.&lt;/p&gt;

&lt;p&gt;Promises, and later &lt;code&gt;async/await&lt;/code&gt;, don't change anything about the event loop itself. They're built on top of it. &lt;code&gt;async/await&lt;/code&gt; is really just syntax that makes microtask-queue based code (Promises under the hood) read like straight-line synchronous code, which is a big part of why it replaced deeply nested callbacks for most sequential async logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  It's not just a browser thing
&lt;/h2&gt;

&lt;p&gt;Node.js runs on this exact same model. There's no browser tab involved, but the idea is identical: a single JS thread, an event loop, and a set of APIs (this time provided by libuv rather than a browser) that handle the actual I/O work like reading a file or querying a database off the main thread. Every incoming HTTP request, every database call, every file read gets treated as an event that eventually lands in a queue and gets handled once the thread is free.&lt;/p&gt;

&lt;p&gt;This is exactly why a single Node process can handle thousands of concurrent connections without falling over. It's not because it's secretly multithreaded. It's because none of those connections are sitting there blocking the one thread while they wait on I/O. The moment a request needs to wait on something slow, it gets handed off, and the thread moves on to the next thing that's actually ready to run.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;JS has one thread and one call stack. Only one thing executes at a time.&lt;/li&gt;
&lt;li&gt;Slow operations (timers, network calls, file I/O) get delegated to the browser or to Node's underlying C++ layer, not run on your JS thread.&lt;/li&gt;
&lt;li&gt;When that work finishes, its callback doesn't run instantly, it gets placed in a queue.&lt;/li&gt;
&lt;li&gt;The event loop's whole job is checking whether the call stack is empty, and if so, pulling the next thing off a queue to run.&lt;/li&gt;
&lt;li&gt;There are two queues that matter most: microtasks (Promises) and macrotasks (timers, events, I/O). Microtasks always get fully drained before the next macrotask runs.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;setTimeout(fn, 0)&lt;/code&gt; means "as soon as possible after everything currently queued," not "immediately."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once this model is actually in your head, a lot of things that used to feel like arbitrary JavaScript quirks (why your &lt;code&gt;console.log&lt;/code&gt; after a &lt;code&gt;fetch()&lt;/code&gt; runs before the data arrives, why a zero-delay timeout still runs last, why Node scales the way it does) stop being quirks and start being predictable consequences of a pretty simple system.&lt;/p&gt;




&lt;p&gt;We're ArtClick, a web development agency based in Kyoto. We build company websites, WordPress sites, and custom systems — with a focus on sites that are fast, well-designed, and easy to maintain long-term. Learn more at &lt;a href="https://artclickdev.com/" rel="noopener noreferrer"&gt;artclickdev&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>frontend</category>
      <category>programming</category>
    </item>
    <item>
      <title>10 CSS Tips and Tricks for Better Responsive Web Design</title>
      <dc:creator>Artclick</dc:creator>
      <pubDate>Thu, 20 Aug 2026 10:38:53 +0000</pubDate>
      <link>https://dev.to/_artclick/10-css-tips-and-tricks-for-better-responsive-web-design-13n1</link>
      <guid>https://dev.to/_artclick/10-css-tips-and-tricks-for-better-responsive-web-design-13n1</guid>
      <description>&lt;h3&gt;
  
  
  Modern CSS gives you tools that make most of your old media queries unnecessary. Here are ten practical techniques worth adding to your toolkit in 2026.
&lt;/h3&gt;

&lt;p&gt;Responsive design used to mean picking a handful of breakpoints, &lt;code&gt;768px&lt;/code&gt;, &lt;code&gt;1024px&lt;/code&gt;, maybe &lt;code&gt;1440px&lt;/code&gt;, and writing a media query for each one. That approach still works, but it's not really how modern CSS wants you to think anymore. A lot of the layout and typography work that used to require careful breakpoint math can now just... respond on its own, because the properties themselves understand context: viewport size, container size, even the user's own OS-level preferences.&lt;/p&gt;

&lt;p&gt;Here are ten techniques I'd consider close to essential for anyone building responsive interfaces today. Some of these will be familiar, some are newer than you'd expect.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Fluid Typography
&lt;/h3&gt;

&lt;p&gt;Fixed font sizes don't hold up well across different screen sizes. Something that reads comfortably on a laptop can feel oversized on a phone, or too small on an ultrawide monitor. Fluid typography lets text scale with the viewport instead of jumping between fixed values at each breakpoint.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;rem&lt;/code&gt; is relative to the root font size:&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="m"&gt;2rem&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;If the root size is &lt;code&gt;16px&lt;/code&gt;, that &lt;code&gt;2rem&lt;/code&gt; works out to &lt;code&gt;32px&lt;/code&gt;, consistent, but static. It doesn't know or care how wide the screen is.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;vw&lt;/code&gt; is relative to the viewport width instead:&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="m"&gt;5vw&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;This scales smoothly as the browser resizes, but on its own it's risky. On a small phone, &lt;code&gt;5vw&lt;/code&gt; might shrink your heading down to something barely legible. On a huge monitor, the same rule can blow it up to a size that looks absurd.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;clamp()&lt;/code&gt; is the fix, and it's the one you actually want to reach for in production. It takes a minimum, a flexible preferred value, and a maximum:&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;clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1.5rem&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="m"&gt;3rem&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;Now the heading grows smoothly with the viewport, but it will never shrink below &lt;code&gt;1.5rem&lt;/code&gt; or grow past &lt;code&gt;3rem&lt;/code&gt;. One line replaces what used to take three or four separate media query overrides.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Fluid Spacing, Not Just Fluid Type
&lt;/h3&gt;

&lt;p&gt;Once you've got &lt;code&gt;clamp()&lt;/code&gt; in your toolkit, don't stop at font sizes. Padding, margins, and gaps benefit from the exact same treatment, and it's an easy thing to overlook since most tutorials only demonstrate &lt;code&gt;clamp()&lt;/code&gt; on headlines.&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="nc"&gt;.card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding&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="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3vw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2.5rem&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.section&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;margin-block&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="m"&gt;2rem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;6vw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;6rem&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;Without this, it's common to see a layout where the text scales beautifully but the whitespace around it stays exactly the same at every screen size, which ends up looking cramped on large screens and oddly spacious on small ones. Scaling your spacing alongside your type keeps the overall proportions of a layout feeling consistent, not just the words on the page.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Intrinsic Grids That Don't Need Breakpoints At All
&lt;/h3&gt;

&lt;p&gt;A huge chunk of "responsive design" work used to be writing media queries just to change how many columns a card grid has. CSS Grid's &lt;code&gt;auto-fit&lt;/code&gt; and &lt;code&gt;minmax()&lt;/code&gt; combination removes that need almost entirely:&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="nc"&gt;.grid&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;grid-template-columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auto-fit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;minmax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;240px&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="py"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.5rem&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;Read that as: fit as many columns as will comfortably hold at least &lt;code&gt;240px&lt;/code&gt; each, and let them share the remaining space evenly. Resize the browser and the grid reflows on its own, four columns, then three, then two, then one, without a single &lt;code&gt;@media&lt;/code&gt; block. This is usually called intrinsic or content-aware layout, and once you've built a grid this way, going back to hardcoded breakpoint columns feels like doing more work for a worse result.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Container Queries
&lt;/h3&gt;

&lt;p&gt;Media queries respond to the viewport. But a component doesn't always know how much space it actually has, a card might be full-width in one layout and squeezed into a narrow sidebar in another, and a viewport-based media query has no way to tell the difference.&lt;/p&gt;

&lt;p&gt;Container queries fix this by letting an element respond to the size of its actual container, not the screen:&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="nc"&gt;.card-wrapper&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;container-type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;inline-size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;flex-direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@container&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;400px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;.card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;flex-direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;row&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;Here, the card stacks vertically by default and switches to a horizontal layout once its container, not the viewport, is at least &lt;code&gt;400px&lt;/code&gt; wide. Drop that same card into a wide main content area or a narrow sidebar on the exact same page, and it adapts correctly in both places. This is the piece that finally makes truly reusable, drop-anywhere components possible, which viewport media queries were never really able to deliver.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. The &lt;code&gt;aspect-ratio&lt;/code&gt; Property
&lt;/h3&gt;

&lt;p&gt;Responsive images and video embeds used to rely on a padding-percentage hack, wrapping the media in a container with &lt;code&gt;padding-top&lt;/code&gt; set to some calculated percentage just to reserve the right amount of space before the media loaded. It worked, but nobody found it intuitive, and it's not something you'd guess how to write from scratch.&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="nc"&gt;.video-embed&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;aspect-ratio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;9&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&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 the whole thing now. The browser reserves the correct proportional space immediately, which also means no layout shift while an image or iframe is still loading, a detail that matters for Core Web Vitals as much as it does for how polished a page feels while it's rendering.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. &lt;code&gt;min()&lt;/code&gt; and &lt;code&gt;max()&lt;/code&gt; for Flexible Constraints
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;clamp()&lt;/code&gt; gets most of the attention, but its two simpler siblings are worth knowing on their own. &lt;code&gt;min()&lt;/code&gt; picks the smallest of the values you give it, &lt;code&gt;max()&lt;/code&gt; picks the largest.&lt;/p&gt;

&lt;p&gt;A common pattern: let content take up most of the width on small screens, but stop it from stretching too wide on large ones.&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="nc"&gt;.container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;width&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="m"&gt;90%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1200px&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;On a phone, &lt;code&gt;90%&lt;/code&gt; is the smaller value, so the container hugs the screen with a bit of breathing room on either side. On a wide desktop monitor, &lt;code&gt;1200px&lt;/code&gt; becomes the smaller value, so the container caps out there instead of stretching edge to edge. One line, no media query, and it reads almost like plain English once you're used to it.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Dynamic Viewport Units: &lt;code&gt;dvh&lt;/code&gt;, &lt;code&gt;svh&lt;/code&gt;, &lt;code&gt;lvh&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;If you've ever set &lt;code&gt;height: 100vh&lt;/code&gt; on a mobile site and watched content get cut off behind the browser's address bar, this one's for you. The classic &lt;code&gt;vh&lt;/code&gt; unit is based on the largest possible viewport, ignoring the fact that mobile browser chrome, the address bar, the bottom toolbar, expands and collapses as someone scrolls, changing how much space is actually visible.&lt;/p&gt;

&lt;p&gt;Newer units account for this directly:&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="nc"&gt;.hero&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;min-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="n"&gt;dvh&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;dvh&lt;/code&gt; stands for dynamic viewport height, and it adjusts live as the browser's UI shows or hides. There's also &lt;code&gt;svh&lt;/code&gt; (small viewport height, calculated assuming the browser UI is fully expanded) and &lt;code&gt;lvh&lt;/code&gt; (large viewport height, assuming it's fully collapsed), for cases where you specifically want one behavior or the other instead of the dynamically adjusting version. For most full-height hero sections and mobile layouts, &lt;code&gt;dvh&lt;/code&gt; is the one you want, and switching to it fixes a genuinely common and annoying mobile bug in one word.&lt;/p&gt;

&lt;h3&gt;
  
  
  8. Logical Properties Instead of Physical Ones
&lt;/h3&gt;

&lt;p&gt;This one's less about screen size and more about a different axis of "responsive", to writing direction and layout context, but it belongs on this list because it prevents a specific category of layout bug that only shows up once you're supporting more than one language or more than one layout direction.&lt;/p&gt;

&lt;p&gt;Instead of physical directions like &lt;code&gt;margin-left&lt;/code&gt; or &lt;code&gt;padding-right&lt;/code&gt;, logical properties describe position relative to the flow of the content:&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="nc"&gt;.card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;margin-inline-start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;padding-block&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.5rem&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;margin-inline-start&lt;/code&gt; means "the start edge in the inline direction", which is the left in English, but automatically becomes the right in Arabic or Hebrew, without you writing a single RTL-specific override. &lt;code&gt;padding-block&lt;/code&gt; covers top and bottom together as one shorthand. If there's any chance your site ever ships in a right-to-left language, or you just want spacing rules that are correct by construction rather than by accident, this is worth adopting as your default habit rather than something you retrofit later.&lt;/p&gt;

&lt;h3&gt;
  
  
  9. Responsive Images With &lt;code&gt;srcset&lt;/code&gt; and &lt;code&gt;sizes&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;CSS handles how big an image displays, but it can't reduce how many bytes get downloaded in the first place. That's what &lt;code&gt;srcset&lt;/code&gt; and &lt;code&gt;sizes&lt;/code&gt; are for, letting the browser choose the most appropriately sized image file for the current viewport, instead of shipping one large image to every device regardless of screen size:&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;img&lt;/span&gt;
  &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"hero-800.jpg"&lt;/span&gt;
  &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"hero-400.jpg 400w, hero-800.jpg 800w, hero-1600.jpg 1600w"&lt;/span&gt;
  &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"(max-width: 600px) 100vw, 50vw"&lt;/span&gt;
  &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"A wide shot of the product lineup"&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;&lt;code&gt;srcset&lt;/code&gt; lists the available image files along with their actual widths. &lt;code&gt;sizes&lt;/code&gt; tells the browser how much space the image will occupy at different viewport widths, and the browser combines that with the device's pixel density to pick the smallest file that will still look sharp. A phone downloads the &lt;code&gt;400w&lt;/code&gt; version instead of the same &lt;code&gt;1600w&lt;/code&gt; file a desktop gets, which is a meaningful difference in load time on a mobile connection, and it's handled declaratively, no JavaScript resizing or lazy-loading library required for this part.&lt;/p&gt;

&lt;h3&gt;
  
  
  10. Respecting User Preferences, Not Just Screen Size
&lt;/h3&gt;

&lt;p&gt;Responsive design started out being entirely about screen dimensions, but modern CSS can respond to the person on the other end too, not just their device. Two media features are worth building in as defaults rather than afterthoughts.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;prefers-color-scheme&lt;/code&gt; lets you offer a dark theme automatically, based on the operating system setting the user already chose, rather than making them find a toggle on your site:&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;light&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="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ffffff&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="m"&gt;#111827&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;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="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#0f172a&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="m"&gt;#e5e7eb&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;code&gt;prefers-reduced-motion&lt;/code&gt; respects a genuinely important accessibility setting for people with vestibular disorders who can get real physical discomfort from large animated motion:&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="nc"&gt;.hero-animation&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;slide-in&lt;/span&gt; &lt;span class="m"&gt;0.6s&lt;/span&gt; &lt;span class="n"&gt;ease-out&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;prefers-reduced-motion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;.hero-animation&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&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;Neither of these has anything to do with screen width, but both are about the same underlying idea every technique on this list shares: a layout that adapts to the actual conditions it's being viewed under, rather than assuming one fixed context for every visitor.&lt;/p&gt;

&lt;h3&gt;
  
  
  Putting it together
&lt;/h3&gt;

&lt;p&gt;None of these ten replace each other, and in a real project they mostly stack. A card grid might use &lt;code&gt;auto-fit&lt;/code&gt;/&lt;code&gt;minmax()&lt;/code&gt; for its columns, &lt;code&gt;clamp()&lt;/code&gt; for its internal padding, &lt;code&gt;aspect-ratio&lt;/code&gt; on its thumbnail images, and a container query to switch its internal layout once it's dropped into a narrower sidebar, all at the same time, on the same component. That's really the shift modern CSS represents: less time spent enumerating every breakpoint by hand, more time spent describing the actual relationship you want, and letting the browser handle the arithmetic.&lt;/p&gt;




&lt;p&gt;We're ArtClick, a web development agency based in Kyoto. We build company websites, WordPress sites, and custom systems — with a focus on sites that are fast, well-designed, and easy to maintain long-term. Learn more at &lt;a href="https://artclickdev.com/?utm_source=devto" rel="noopener noreferrer"&gt;artclickdev&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Follow us for more CSS tutorials, web dev tips, and resources.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>css</category>
      <category>programming</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Stop Compiling Your Nested CSS. The Browser Does It Now</title>
      <dc:creator>Artclick</dc:creator>
      <pubDate>Wed, 19 Aug 2026 08:11:36 +0000</pubDate>
      <link>https://dev.to/_artclick/stop-compiling-your-nested-css-the-browser-does-it-now-41n</link>
      <guid>https://dev.to/_artclick/stop-compiling-your-nested-css-the-browser-does-it-now-41n</guid>
      <description>&lt;p&gt;For years, nesting selectors inside each other was one of the main reasons teams reached for Sass in the first place. You'd write &lt;code&gt;.card { .title { ... } }&lt;/code&gt;, run it through a compiler, and get flat CSS out the other end. It was one of those "why doesn't the browser just do this" features, and honestly, it's a little wild that it took this long. But native CSS nesting is here now, it's Baseline in 2026, meaning it works across Chrome, Edge, Firefox, and Safari without a build step, and it's worth actually learning properly instead of just copy-pasting Sass habits into it and hoping for the best.&lt;/p&gt;

&lt;p&gt;That second part matters more than people expect. Native nesting looks almost identical to Sass nesting on the surface, but the parsing rules underneath are stricter, and a couple of the differences will genuinely trip you up the first time you hit them. This guide walks through the syntax, the &lt;code&gt;&amp;amp;&lt;/code&gt; selector, nesting at-rules like &lt;code&gt;@media&lt;/code&gt;, and the handful of gotchas worth knowing before you delete your Sass dependency.&lt;/p&gt;

&lt;h2&gt;
  
  
  Browser support, briefly
&lt;/h2&gt;

&lt;p&gt;Native CSS nesting is supported by Chrome, Edge, Firefox, and Safari as of 2026, and it's considered Baseline Widely Available. The one thing worth knowing is that there were two versions of the spec: an early, stricter one that required the &lt;code&gt;&amp;amp;&lt;/code&gt; symbol in more places, and a later "relaxed" version that infers it automatically in most cases. All current browser versions support the relaxed syntax, so unless you specifically need to support an old browser version, you can write nesting the way this guide shows it.&lt;/p&gt;

&lt;p&gt;If you do need a safety net for older environments, wrap your nested rules in a feature query:&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;@supports&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* nested rules go here */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Browsers that don't understand nesting will drop the unsupported rules entirely rather than ignoring just the nesting part, so this is worth doing if any real chunk of your audience is on an older browser. For most projects in 2026 though, you probably don't need it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The basic syntax
&lt;/h2&gt;

&lt;p&gt;Here's the simplest possible example. Instead of writing this the old, flat way:&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="nc"&gt;.card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.card&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.25rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.card&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;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#4b5563&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 write 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="nc"&gt;.card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="err"&gt;h2&lt;/span&gt; &lt;span class="err"&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.25rem&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;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#4b5563&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both compile down to exactly the same thing. The nested &lt;code&gt;h2&lt;/code&gt; rule is understood as &lt;code&gt;.card h2&lt;/code&gt;, and the nested &lt;code&gt;p&lt;/code&gt; rule as &lt;code&gt;.card p&lt;/code&gt;. No ampersand needed for this case, since element selectors like &lt;code&gt;h2&lt;/code&gt; and &lt;code&gt;p&lt;/code&gt; are unambiguous, the browser knows you mean "an h2 inside .card," not "a property called h2."&lt;/p&gt;

&lt;h2&gt;
  
  
  The &lt;code&gt;&amp;amp;&lt;/code&gt; selector, and when you actually need it
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;&amp;amp;&lt;/code&gt; symbol represents the parent selector, and while it's optional in a lot of cases under the relaxed syntax, there are specific situations where you still need it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You need it when combining with the parent to form a compound selector.&lt;/strong&gt; If you want to target &lt;code&gt;.card&lt;/code&gt; itself when it also has a &lt;code&gt;.featured&lt;/code&gt; class, you can't just nest &lt;code&gt;.featured&lt;/code&gt; on its own, that would mean &lt;code&gt;.card .featured&lt;/code&gt; (a descendant), not &lt;code&gt;.card.featured&lt;/code&gt; (the same element with both classes). You need the ampersand directly against it:&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="nc"&gt;.card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#e5e7eb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="err"&gt;&amp;amp;.featured&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;border-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#6366f1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;You need it for pseudo-classes and pseudo-elements&lt;/strong&gt;, though in practice most people write these with &lt;code&gt;&amp;amp;&lt;/code&gt; anyway even where it's technically optional, since it reads more clearly:&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="nc"&gt;.card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;box-shadow&lt;/span&gt; &lt;span class="m"&gt;0.2s&lt;/span&gt; &lt;span class="n"&gt;ease&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="err"&gt;&amp;amp;:hover&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;box-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt; &lt;span class="m"&gt;12px&lt;/span&gt; &lt;span class="nb"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;::before&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;You need it, doubled up, for combinators like adjacent siblings.&lt;/strong&gt; This one surprises people. If you want to select a sibling element that comes right after the current selector, you write the ampersand twice, once to represent the parent, and again as the actual sibling combinator:&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="nc"&gt;.card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That compiles to &lt;code&gt;.card + .card&lt;/code&gt;, styling a card that immediately follows another card. It looks strange the first time you see it, but it makes sense once you remember &lt;code&gt;&amp;amp;&lt;/code&gt; is just standing in for the literal parent selector text, and &lt;code&gt;+&lt;/code&gt; still needs something on both sides of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nesting pseudo-classes without repeating yourself
&lt;/h2&gt;

&lt;p&gt;This is the single most common real-world use case, and it's the one that alone justifies switching. Instead of writing every state of an interactive element as a separate flat rule:&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;button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#4338ca&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="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="nt"&gt;button&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#3730a3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="nd"&gt;:focus-visible&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;outline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#a5b4fc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="nd"&gt;:disabled&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#9ca3af&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;not-allowed&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 keep every state colocated with the base rule it belongs to:&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;button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#4338ca&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="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="err"&gt;&amp;amp;:hover&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#3730a3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;:focus-visible&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;outline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#a5b4fc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;:disabled&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#9ca3af&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;not-allowed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Functionally identical output. The difference is entirely about where you have to look while editing. When every state a button can be in lives inside one block, you're a lot less likely to update the hover color and forget the focus state sitting three hundred lines further down the file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nesting at-rules: media queries, container queries, and more
&lt;/h2&gt;

&lt;p&gt;This is where native nesting pulls ahead of what a lot of people are used to from Sass. You can nest &lt;code&gt;@media&lt;/code&gt;, &lt;code&gt;@container&lt;/code&gt;, &lt;code&gt;@supports&lt;/code&gt;, and &lt;code&gt;@layer&lt;/code&gt; directly inside a rule, which keeps responsive and conditional logic sitting right next to the property it actually affects, instead of off in a separate media query block somewhere else in the file:&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="nc"&gt;.sidebar&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="err"&gt;@media&lt;/span&gt; &lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;768px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;@container&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;40rem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compare that to the traditional approach, where you'd have &lt;code&gt;.sidebar { width: 20rem; }&lt;/code&gt; in one place and &lt;code&gt;@media (max-width: 768px) { .sidebar { width: 100%; } }&lt;/code&gt; somewhere else entirely, often much further down the stylesheet. Nesting the media query keeps the full story of "what width can this element be" in one spot. For a component with several responsive tweaks, this alone can cut a meaningful amount of back-and-forth scrolling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Specificity: the good news
&lt;/h2&gt;

&lt;p&gt;Nesting doesn't add any specificity of its own. A nested rule has exactly the same specificity as the equivalent rule written out flat. &lt;code&gt;.card h2&lt;/code&gt; nested inside &lt;code&gt;.card&lt;/code&gt; calculates identically to &lt;code&gt;.card h2&lt;/code&gt; written on one line. This is worth knowing because it means nesting is purely a syntax convenience, it doesn't change how the cascade resolves conflicts, and you don't need to relearn specificity rules to use it safely.&lt;/p&gt;

&lt;p&gt;That said, nesting makes it very easy to accidentally write something overly specific just by going too many levels deep:&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="nc"&gt;.page&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;.content&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;.card&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
      &lt;span class="err"&gt;.title&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#111827&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="err"&gt;}&lt;/span&gt;
  &lt;span class="err"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That compiles to &lt;code&gt;.page .content .card .title&lt;/code&gt;, four selectors deep, which is going to be annoying to override later even though nothing about nesting itself caused the problem, you'd have written an equally gnarly selector by hand if you'd typed it flat. The lesson isn't "nesting is dangerous," it's the same advice that applied before nesting existed: keep selectors as shallow as the actual DOM structure requires, and don't nest just because you can.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gotcha that catches almost everyone once
&lt;/h2&gt;

&lt;p&gt;Here's the one that's worth reading twice. In the relaxed syntax, a nested rule starting with an identifier that looks like a type selector is fine, &lt;code&gt;h2 { }&lt;/code&gt; inside &lt;code&gt;.card&lt;/code&gt; is unambiguous. But CSS rules are parsed top to bottom, declarations first, and if a nested selector could be confused with a custom property or declaration, the parser needs the &lt;code&gt;&amp;amp;&lt;/code&gt; to disambiguate it.&lt;/p&gt;

&lt;p&gt;In practice this mostly comes up with pseudo-elements and certain edge-case selectors, and the safest habit, honestly, is this: when in doubt, just add the &lt;code&gt;&amp;amp;&lt;/code&gt;. It's never wrong to include it even where it's technically optional, and a lot of style guides in 2026 recommend always writing it for consistency, precisely so you're not making a judgment call about whether this particular selector needs it every time you write one. I'd rather see &lt;code&gt;&amp;amp; p { }&lt;/code&gt; everywhere in a codebase than half the nested rules with &lt;code&gt;&amp;amp;&lt;/code&gt; and half without, purely for the sake of not having to think about which category a given selector falls into.&lt;/p&gt;

&lt;h2&gt;
  
  
  A real component, built with nesting
&lt;/h2&gt;

&lt;p&gt;Here's a small card component pulling everything together, base styles, a modifier, hover and focus states, a sibling gap, and a responsive tweak, all in one block instead of scattered across a stylesheet:&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="nc"&gt;.card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;flex-direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#e5e7eb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;12px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.25rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ffffff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;box-shadow&lt;/span&gt; &lt;span class="m"&gt;0.2s&lt;/span&gt; &lt;span class="n"&gt;ease&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;border-color&lt;/span&gt; &lt;span class="m"&gt;0.2s&lt;/span&gt; &lt;span class="n"&gt;ease&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="err"&gt;&amp;amp;:hover&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;box-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt; &lt;span class="nb"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;0.08&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;.featured&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;border-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#6366f1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&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;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0.5rem&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.15rem&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="m"&gt;#111827&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;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&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="m"&gt;#4b5563&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;line-height&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="nc"&gt;.tag&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;inline-block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.75rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.2rem&lt;/span&gt; &lt;span class="m"&gt;0.6rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;999px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#eef2ff&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="m"&gt;#4338ca&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;0.8rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;fit-content&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;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;480px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="err"&gt;h2&lt;/span&gt; &lt;span class="err"&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;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="err"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every rule that has anything to do with &lt;code&gt;.card&lt;/code&gt; lives inside that one block. Compare that to how this would've looked as six or seven separate flat rules scattered wherever they happened to get added over the life of the file, and it's easy to see why this feature alone gets people to finally drop a Sass build step they've been carrying around for a decade.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you're migrating from Sass
&lt;/h2&gt;

&lt;p&gt;The syntax is close enough that most Sass nesting will look almost identical once you drop it into native CSS, but there are a few differences worth checking for before you assume a straight copy-paste will work.&lt;/p&gt;

&lt;p&gt;Sass lets you nest selectors that don't start with a combinator or an identifier in ways native CSS doesn't allow, and Sass's &lt;code&gt;&amp;amp;&lt;/code&gt; supports some string-concatenation tricks, like &lt;code&gt;&amp;amp;-active&lt;/code&gt; to produce &lt;code&gt;.button-active&lt;/code&gt;, that native CSS doesn't support at all, the native &lt;code&gt;&amp;amp;&lt;/code&gt; only works as a full selector, not as a text fragment you can glue other characters onto. Sass also compiles at build time, so it can be more forgiving about ambiguous-looking rules; the browser parses your CSS live, so it applies the stricter rules described above. Run your migrated stylesheet through the browser and actually check computed styles rather than assuming visual parity, particularly on any BEM-style modifier classes built with string concatenation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Should you drop your preprocessor?
&lt;/h2&gt;

&lt;p&gt;If your only reason for using Sass was nesting, and you're not relying on its other features, real mixins, functions, math operations that go beyond what &lt;code&gt;calc()&lt;/code&gt; and &lt;code&gt;clamp()&lt;/code&gt; now handle, you can probably drop it for new projects without losing much. For a big existing codebase, it's less about ripping the preprocessor out immediately and more about not reaching for &lt;code&gt;&amp;amp;-modifier&lt;/code&gt; string tricks in new code going forward, so the eventual migration is smaller when you do get to it.&lt;/p&gt;

&lt;p&gt;Either way, native nesting is worth learning properly rather than treating it as "Sass, but in the browser." The rules are close enough to bite you exactly when you're not paying attention, and different enough that it's worth the twenty minutes to actually understand where they diverge.&lt;/p&gt;




&lt;p&gt;We're ArtClick, a web development agency based in Kyoto. We build company websites, WordPress sites, and custom systems — with a focus on sites that are fast, well-designed, and easy to maintain long-term. Learn more at &lt;a href="https://artclickdev.com/?utm_source=devto" rel="noopener noreferrer"&gt;artclickdev&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>css</category>
      <category>frontend</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Artclick</dc:creator>
      <pubDate>Tue, 18 Aug 2026 05:24:35 +0000</pubDate>
      <link>https://dev.to/_artclick/-2pnj</link>
      <guid>https://dev.to/_artclick/-2pnj</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/_artclick/styling-dropdown-menus-in-css-got-a-whole-lot-better-1208" class="crayons-story__hidden-navigation-link"&gt;Styling dropdown menus in CSS got a whole lot better!&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/_artclick" class="crayons-avatar  crayons-avatar--l  "&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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4049105%2F5e4d789f-a23c-435d-81df-3e14f5091905.png" alt="_artclick profile" class="crayons-avatar__image" width="800" height="600"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/_artclick" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Artclick
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Artclick
                
                
              
              &lt;div id="story-author-preview-content-4422725" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/_artclick" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4049105%2F5e4d789f-a23c-435d-81df-3e14f5091905.png" class="crayons-avatar__image" alt="" width="800" height="600"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Artclick&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/_artclick/styling-dropdown-menus-in-css-got-a-whole-lot-better-1208" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Aug 18&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/_artclick/styling-dropdown-menus-in-css-got-a-whole-lot-better-1208" id="article-link-4422725"&gt;
          Styling dropdown menus in CSS got a whole lot better!
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/css"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;css&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/frontend"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;frontend&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/html"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;html&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/_artclick/styling-dropdown-menus-in-css-got-a-whole-lot-better-1208" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;2&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/_artclick/styling-dropdown-menus-in-css-got-a-whole-lot-better-1208#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            10 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>Styling dropdown menus in CSS got a whole lot better!</title>
      <dc:creator>Artclick</dc:creator>
      <pubDate>Tue, 18 Aug 2026 05:21:29 +0000</pubDate>
      <link>https://dev.to/_artclick/styling-dropdown-menus-in-css-got-a-whole-lot-better-1208</link>
      <guid>https://dev.to/_artclick/styling-dropdown-menus-in-css-got-a-whole-lot-better-1208</guid>
      <description>&lt;p&gt;For as long as most of us have been writing CSS, the &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; element has been the one form control everyone quietly gave up on. You could change its border, maybe its font, and then you hit a wall. The dropdown panel itself, the arrow icon, the checkmark next to the selected item: all of that lived deep in the browser's operating system layer, completely out of CSS's reach. If you wanted a dropdown that actually matched your design system, the "solution" was to reach for a JavaScript library like Select2 or Choices.js, throw away the real &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt;, and rebuild the whole thing out of divs, complete with your own keyboard navigation, focus trapping, and screen reader support.&lt;/p&gt;

&lt;p&gt;That's finally changing. A new CSS property called &lt;code&gt;appearance: base-select&lt;/code&gt; lets you keep the real, native &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; element (with all its built-in accessibility and keyboard behavior) while styling every single part of it: the button, the dropdown popup, the individual options, the arrow icon, and the selected checkmark. No JavaScript. No rebuilt component. Just CSS doing what it should have been able to do years ago.&lt;/p&gt;

&lt;p&gt;In this article we'll build a fully custom dropdown from scratch, starting with a plain &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; and ending with something that looks like it came out of a design system, using nothing but HTML and CSS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Browser support first, because it matters here
&lt;/h2&gt;

&lt;p&gt;This feature is genuinely new and not yet Baseline. As of writing, it works in Chromium-based browsers (Chrome 135+, Edge, and other Chromium forks), and support in Firefox and Safari is still catching up. Check the current numbers on &lt;a href="https://caniuse.com/mdn-css_properties_appearance_base-select" rel="noopener noreferrer"&gt;caniuse.com&lt;/a&gt; before shipping this to production.&lt;/p&gt;

&lt;p&gt;The good news is that this feature was designed with progressive enhancement built in. If a browser doesn't understand &lt;code&gt;appearance: base-select&lt;/code&gt;, it just ignores the declaration and renders a normal, fully functional native &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt;. Nothing breaks. Your users on unsupported browsers get the boring-but-reliable dropdown they've always had, and users on supporting browsers get the fully styled version. That's a rare and pleasant kind of "new CSS feature" to work with.&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting point: a completely ordinary select
&lt;/h2&gt;

&lt;p&gt;Before touching any CSS, here's the markup we're working with. We're building a "favorite language" picker, with a small icon next to each option:&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;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"lang-select"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Pick your favorite language:&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;select&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"lang-select"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;selectedcontent&amp;gt;&amp;lt;/selectedcontent&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;🟨&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"label"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;JavaScript&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"py"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;🐍&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"label"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Python&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"rs"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;🦀&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"label"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Rust&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"go"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;🐹&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"label"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Go&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"ts"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;🔷&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"label"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;TypeScript&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/select&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things here look unfamiliar if you haven't seen this feature before:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; as the first child of &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt;.&lt;/strong&gt; This used to be invalid markup. Now, when you include it, it replaces the default closed-state button of the select with your own button element. If you skip it, the browser falls back to its default rendering for the closed state.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A &lt;code&gt;&amp;lt;selectedcontent&amp;gt;&lt;/code&gt; element inside that button.&lt;/strong&gt; This is a new HTML element whose whole job is to mirror whatever option is currently selected. Under the hood the browser clones the selected &lt;code&gt;&amp;lt;option&amp;gt;&lt;/code&gt;'s content into it. This is what lets you show an icon &lt;em&gt;and&lt;/em&gt; text in the closed select, not just plain text.&lt;/p&gt;

&lt;p&gt;Also worth noting: options can now contain real markup. Historically, anything except plain text inside an &lt;code&gt;&amp;lt;option&amp;gt;&lt;/code&gt; got silently stripped. Now you can put &lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt;, images, and other non-interactive inline content in there, and it'll actually render.&lt;/p&gt;

&lt;p&gt;If a browser doesn't support any of this, it degrades gracefully: the button/selectedcontent structure is ignored, the option markup collapses down to its text content, and you get a normal select with "JavaScript", "Python", etc. as plain text options.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: opting in
&lt;/h2&gt;

&lt;p&gt;None of the styling below works until you explicitly opt in, on both the select itself and its dropdown panel:&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;select&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="nd"&gt;::picker&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;appearance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;base-select&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;::picker(select)&lt;/code&gt; is a new pseudo-element that represents the popup panel, the part that shows when you click the select. You can opt the &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; in on its own without opting in the picker, but you can't do the reverse: the picker can only go into base-select mode if its parent select has too.&lt;/p&gt;

&lt;p&gt;Once both are opted in, the browser strips its OS-level chrome from both pieces and renders the plainest possible version of a select. From here, it behaves like any other element you can put a border, background, or padding on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: styling the select button
&lt;/h2&gt;

&lt;p&gt;The select button is the always-visible part, the piece someone clicks to open the dropdown. This is your highest-visibility surface, so it's worth spending the most design effort here.&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;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;justify-content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;space-between&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;220px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.6rem&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#d0d5dd&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ffffff&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;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;border-color&lt;/span&gt; &lt;span class="m"&gt;0.2s&lt;/span&gt; &lt;span class="n"&gt;ease&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;box-shadow&lt;/span&gt; &lt;span class="m"&gt;0.2s&lt;/span&gt; &lt;span class="n"&gt;ease&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#98a2b3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="nd"&gt;:focus-visible&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;outline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#6366f1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;box-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;3px&lt;/span&gt; &lt;span class="nb"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;99&lt;/span&gt; &lt;span class="m"&gt;102&lt;/span&gt; &lt;span class="m"&gt;241&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;0.15&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;Nothing exotic here, it's the same declarations you'd write for any button. That's really the headline of this whole feature: the select button stops being a special case and becomes just another styleable box.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cleaning up what shows inside the button
&lt;/h3&gt;

&lt;p&gt;Remember the icon spans inside each &lt;code&gt;&amp;lt;option&amp;gt;&lt;/code&gt;? Because &lt;code&gt;&amp;lt;selectedcontent&amp;gt;&lt;/code&gt; clones the &lt;em&gt;entire&lt;/em&gt; selected option's content, the icon comes along for the ride into the closed button too. That's usually not what you want, since it can throw off the button's height and spacing. Target it directly and hide it:&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;selectedcontent&lt;/span&gt; &lt;span class="nc"&gt;.icon&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&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;This only affects how the content looks inside the closed button. The icon still renders normally inside the open dropdown, because that's a separate rendering context.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: styling the picker icon (the little arrow)
&lt;/h2&gt;

&lt;p&gt;That small down-facing arrow that used to be untouchable OS chrome now has its own pseudo-element: &lt;code&gt;::picker-icon&lt;/code&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;select&lt;/span&gt;&lt;span class="nd"&gt;::picker-icon&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="m"&gt;#667085&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rotate&lt;/span&gt; &lt;span class="m"&gt;0.2s&lt;/span&gt; &lt;span class="n"&gt;ease&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="nd"&gt;:open::picker-icon&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;rotate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;180deg&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 &lt;code&gt;:open&lt;/code&gt; pseudo-class targets the select button specifically while its picker is showing, which is exactly what you need to flip the arrow when the dropdown opens. This combination, targeting the icon and reacting to open state, used to require JavaScript toggling a class. Now it's two CSS rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: styling the dropdown picker itself
&lt;/h2&gt;

&lt;p&gt;This is the part that used to be completely off-limits: the popup panel holding all the options. It's addressed with &lt;code&gt;::picker(select)&lt;/code&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;select&lt;/span&gt;&lt;span class="nd"&gt;::picker&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#d0d5dd&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.4rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.4rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ffffff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;box-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt; &lt;span class="m"&gt;24px&lt;/span&gt; &lt;span class="nb"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;16&lt;/span&gt; &lt;span class="m"&gt;24&lt;/span&gt; &lt;span class="m"&gt;40&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;0.12&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;A detail worth calling out: the picker is a genuine &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Popover_API" rel="noopener noreferrer"&gt;popover&lt;/a&gt; under the hood. When it opens, its contents get promoted to the browser's top layer, the same mechanism used by &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; and native popovers. That's why it correctly renders above everything else on the page and why it automatically closes other open popovers when it appears, without you writing any z-index hacks or click-outside handlers.&lt;/p&gt;

&lt;p&gt;Because it's a popover, you also get transition support for free. If you want the dropdown to fade and scale in instead of just snapping open:&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;select&lt;/span&gt;&lt;span class="nd"&gt;::picker&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-4px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0.98&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;opacity&lt;/span&gt; &lt;span class="m"&gt;0.15s&lt;/span&gt; &lt;span class="n"&gt;ease&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;transform&lt;/span&gt; &lt;span class="m"&gt;0.15s&lt;/span&gt; &lt;span class="n"&gt;ease&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;display&lt;/span&gt; &lt;span class="m"&gt;0.15s&lt;/span&gt; &lt;span class="n"&gt;allow-discrete&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;overlay&lt;/span&gt; &lt;span class="m"&gt;0.15s&lt;/span&gt; &lt;span class="n"&gt;allow-discrete&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="nd"&gt;:open::picker&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;opacity&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="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;scale&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="k"&gt;@starting-style&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;select&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;picker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;select&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-4px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0.98&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;@starting-style&lt;/code&gt; block defines what the picker looks like the instant before it transitions in, which is necessary because it's animating from &lt;code&gt;display: none&lt;/code&gt;, a state CSS can't normally transition from. &lt;code&gt;allow-discrete&lt;/code&gt; is what makes animating &lt;code&gt;display&lt;/code&gt; and &lt;code&gt;overlay&lt;/code&gt; possible at all. This pairing looks unusual the first time you see it, but it's becoming a standard pattern anywhere popovers, dialogs, or view transitions are involved.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: styling the options
&lt;/h2&gt;

&lt;p&gt;Each &lt;code&gt;&amp;lt;option&amp;gt;&lt;/code&gt; is now a fully flexible container. It comes with &lt;code&gt;display: flex&lt;/code&gt; applied by the browser's default base-select styles, which is convenient since our markup already has an icon span and a label span sitting side by side.&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;option&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.6rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.55rem&lt;/span&gt; &lt;span class="m"&gt;0.7rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6px&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;0.95rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;option&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nt"&gt;option&lt;/span&gt;&lt;span class="nd"&gt;:focus&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#f2f4f7&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;option&lt;/span&gt; &lt;span class="nc"&gt;.icon&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.1rem&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 reach for the exact same pseudo-classes you already know: &lt;code&gt;:hover&lt;/code&gt;, &lt;code&gt;:focus&lt;/code&gt;, &lt;code&gt;:first-of-type&lt;/code&gt;, &lt;code&gt;:nth-of-type(odd)&lt;/code&gt; for zebra striping, and so on. There's no special "option styling API" to learn, it's just CSS applied to an element that finally accepts it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: styling the selected option and its checkmark
&lt;/h2&gt;

&lt;p&gt;Two more new selectors round this out. &lt;code&gt;:checked&lt;/code&gt; targets whichever option currently matches the select's value, and &lt;code&gt;::checkmark&lt;/code&gt; targets the little indicator next to it inside the open dropdown.&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;option&lt;/span&gt;&lt;span class="nd"&gt;:checked&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#eef2ff&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;600&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;option&lt;/span&gt;&lt;span class="nd"&gt;::checkmark&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;order&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="py"&gt;margin-inline-start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;auto&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="m"&gt;#6366f1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&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;Setting &lt;code&gt;order: 1&lt;/code&gt; and pushing the checkmark to the end with an auto margin moves it from its default position at the start of the row to the end, a small flexbox trick that suddenly works here because options are flex containers. You can also replace the checkmark glyph entirely through &lt;code&gt;content&lt;/code&gt;, or hide it altogether with &lt;code&gt;display: none&lt;/code&gt; if your selected-state background color is enough of a signal on its own.&lt;/p&gt;

&lt;p&gt;One accessibility note worth remembering: &lt;code&gt;::checkmark&lt;/code&gt; and &lt;code&gt;::picker-icon&lt;/code&gt; are purely visual. They're excluded from the accessibility tree, so whatever you put in their &lt;code&gt;content&lt;/code&gt; won't be read out by screen readers. That's fine, since the underlying &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; still reports the correct selected value through normal accessibility APIs, but don't rely on a checkmark glyph alone to communicate meaning that assistive tech users would otherwise miss.&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting it all together
&lt;/h2&gt;

&lt;p&gt;Here's the complete, working example:&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;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"lang-select"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Pick your favorite language:&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;select&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"lang-select"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;selectedcontent&amp;gt;&amp;lt;/selectedcontent&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;🟨&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"label"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;JavaScript&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"py"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;🐍&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"label"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Python&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"rs"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;🦀&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"label"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Rust&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"go"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;🐹&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"label"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Go&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"ts"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;🔷&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"label"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;TypeScript&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/select&amp;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 css"&gt;&lt;code&gt;&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="nd"&gt;::picker&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;appearance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;base-select&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;justify-content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;space-between&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;220px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.6rem&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#d0d5dd&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ffffff&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;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;border-color&lt;/span&gt; &lt;span class="m"&gt;0.2s&lt;/span&gt; &lt;span class="n"&gt;ease&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;box-shadow&lt;/span&gt; &lt;span class="m"&gt;0.2s&lt;/span&gt; &lt;span class="n"&gt;ease&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#98a2b3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="nd"&gt;:focus-visible&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;outline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#6366f1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;box-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;3px&lt;/span&gt; &lt;span class="nb"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;99&lt;/span&gt; &lt;span class="m"&gt;102&lt;/span&gt; &lt;span class="m"&gt;241&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;0.15&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;selectedcontent&lt;/span&gt; &lt;span class="nc"&gt;.icon&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="nd"&gt;::picker-icon&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="m"&gt;#667085&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rotate&lt;/span&gt; &lt;span class="m"&gt;0.2s&lt;/span&gt; &lt;span class="n"&gt;ease&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="nd"&gt;:open::picker-icon&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;rotate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;180deg&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="nd"&gt;::picker&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#d0d5dd&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.4rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.4rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ffffff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;box-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt; &lt;span class="m"&gt;24px&lt;/span&gt; &lt;span class="nb"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;16&lt;/span&gt; &lt;span class="m"&gt;24&lt;/span&gt; &lt;span class="m"&gt;40&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;0.12&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-4px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0.98&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;opacity&lt;/span&gt; &lt;span class="m"&gt;0.15s&lt;/span&gt; &lt;span class="n"&gt;ease&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;transform&lt;/span&gt; &lt;span class="m"&gt;0.15s&lt;/span&gt; &lt;span class="n"&gt;ease&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;display&lt;/span&gt; &lt;span class="m"&gt;0.15s&lt;/span&gt; &lt;span class="n"&gt;allow-discrete&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;overlay&lt;/span&gt; &lt;span class="m"&gt;0.15s&lt;/span&gt; &lt;span class="n"&gt;allow-discrete&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="nd"&gt;:open::picker&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;opacity&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="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;scale&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="k"&gt;@starting-style&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;select&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;picker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;select&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-4px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0.98&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;option&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.6rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.55rem&lt;/span&gt; &lt;span class="m"&gt;0.7rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6px&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;0.95rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;option&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nt"&gt;option&lt;/span&gt;&lt;span class="nd"&gt;:focus&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#f2f4f7&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;option&lt;/span&gt; &lt;span class="nc"&gt;.icon&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.1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;option&lt;/span&gt;&lt;span class="nd"&gt;:checked&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#eef2ff&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;600&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;option&lt;/span&gt;&lt;span class="nd"&gt;::checkmark&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;order&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="py"&gt;margin-inline-start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;auto&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="m"&gt;#6366f1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&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;Drop that into any Chromium-based browser and you'll get a fully custom dropdown, complete with icons, a rotating arrow, a fading-in panel, and a repositioned checkmark, all without a single line of JavaScript.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this is a bigger deal than it sounds
&lt;/h2&gt;

&lt;p&gt;It's easy to read "you can now style a select" and shrug, but the actual shift underneath is significant. Every JS-powered "custom select" library exists because the real &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; couldn't be styled, so teams rebuilt it from scratch using divs and ARIA attributes, and in doing so quietly re-implemented keyboard navigation, focus management, typeahead search, and screen reader semantics. Most of these reimplementations get some part of that wrong, because native form controls are deceptively hard to fully replicate.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;appearance: base-select&lt;/code&gt; sidesteps the problem entirely by keeping the real element. You still get native keyboard support, native form submission, and native accessibility behavior, for free, because it's still an actual &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt;. You're just no longer locked out of its visual layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Should you use this today?
&lt;/h2&gt;

&lt;p&gt;Given the current browser support, treat this as progressive enhancement rather than a drop-in replacement for your existing custom dropdown component:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you're building something new and can tolerate a plainer fallback in non-supporting browsers, this is a great candidate, less JavaScript, smaller bundle, and better baseline accessibility than most hand-rolled dropdowns.&lt;/li&gt;
&lt;li&gt;If you already ship a JS-based select component for a production product with broad browser support requirements, keep it for now, but keep an eye on this feature's Baseline status. It's likely to make that component unnecessary within the next couple of years.&lt;/li&gt;
&lt;li&gt;Either way, it's worth trying out in a side project now. The mental model (button, picker, picker-icon, checkmark) is small enough to learn in an afternoon, and it'll only become more relevant as support widens.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; element spent decades as the one thing in CSS everyone had to work around. That's no longer true, and it's worth getting familiar with while it's still new.&lt;/p&gt;




&lt;p&gt;We're ArtClick, a web development agency based in Kyoto. We build company websites, WordPress sites, and custom systems — with a focus on sites that are fast, well-designed, and easy to maintain long-term. Learn more at &lt;a href="https://artclickdev.com/?utm_source=devto" rel="noopener noreferrer"&gt;artclickdev&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>css</category>
      <category>frontend</category>
      <category>webdev</category>
      <category>html</category>
    </item>
    <item>
      <title>CSS Can Now Count Siblings on Its Own, No JavaScript Required</title>
      <dc:creator>Artclick</dc:creator>
      <pubDate>Mon, 17 Aug 2026 10:43:07 +0000</pubDate>
      <link>https://dev.to/_artclick/css-can-now-count-siblings-on-its-own-no-javascript-required-59kk</link>
      <guid>https://dev.to/_artclick/css-can-now-count-siblings-on-its-own-no-javascript-required-59kk</guid>
      <description>&lt;p&gt;There's a small category of problems that almost every frontend developer has solved with JavaScript at some point, even though it never really felt like a JavaScript problem. Numbering list items. Staggering an animation across a set of cards. Calculating how far along a stepper someone is. All of it boils down to one simple question: &lt;em&gt;where am I in this list, and how many of us are there?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Browsers have always known the answer. We just never had a way to ask CSS directly, so we reached for JS instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  The JS workaround we've all written
&lt;/h2&gt;

&lt;p&gt;Something like this probably looks familiar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cards&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.card&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;cards&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;card&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&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="nx"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--index&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--count&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cards&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&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;It's not hard code. But it's extra JS, extra DOM traversal, and one more thing to keep in sync if the list changes. And the information it's computing, "which position is this, out of how many", was already sitting inside the browser's own layout engine the whole time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter &lt;code&gt;sibling-index()&lt;/code&gt; and &lt;code&gt;sibling-count()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;New CSS functions give you that information natively:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;sibling-index()&lt;/code&gt; returns the current element's position among its siblings&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sibling-count()&lt;/code&gt; returns the total number of siblings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No traversal, no JS, no manual bookkeeping.&lt;/p&gt;

&lt;p&gt;Take a simple list:&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;ul&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;HTML&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;CSS&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;JavaScript&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ul&amp;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 css"&gt;&lt;code&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="nd"&gt;::before&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;sibling-index&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 renders &lt;code&gt;1&lt;/code&gt;, &lt;code&gt;2&lt;/code&gt;, &lt;code&gt;3&lt;/code&gt; next to each item, computed entirely by the browser. Pair it with &lt;code&gt;sibling-count()&lt;/code&gt; and you can build a "step X of Y" style label with zero script:&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;li&lt;/span&gt;&lt;span class="nd"&gt;::after&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;" / "&lt;/span&gt; &lt;span class="n"&gt;sibling-count&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 plaintext"&gt;&lt;code&gt;1 / 3
2 / 3
3 / 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why this is more useful than automatic numbering
&lt;/h2&gt;

&lt;p&gt;Numbering list items is a nice party trick, but the real unlock is that CSS now has a sense of &lt;em&gt;scale&lt;/em&gt;, it knows not just where an element sits, but how big the whole collection is. That's what makes calculation-based layouts possible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Staggered animations&lt;/strong&gt;, without a &lt;code&gt;forEach&lt;/code&gt; loop:&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="nc"&gt;.item&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;animation-delay&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="n"&gt;sibling-index&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="err"&gt;*&lt;/span&gt; &lt;span class="m"&gt;100ms&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;Every item in the list gets its own delay automatically, no JS assigning &lt;code&gt;animationDelay&lt;/code&gt; one element at a time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Progress and position math&lt;/strong&gt;, without hardcoded percentages:&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="nc"&gt;.step&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--progress&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="n"&gt;sibling-index&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="n"&gt;sibling-count&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 single line gives you each step's relative position in the sequence, exactly what you'd want for timelines, multi-step onboarding flows, or a progress bar built from a variable number of stages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Adaptive timelines&lt;/strong&gt; are a good real-world example:&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;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"timeline"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"node"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"node"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"node"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"node"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;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 css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.node&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;animation-delay&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="n"&gt;sibling-index&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="err"&gt;*&lt;/span&gt; &lt;span class="m"&gt;150ms&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;Add or remove a &lt;code&gt;.node&lt;/code&gt; and the stagger timing adjusts on its own. Nothing to recalculate in JS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this beats &lt;code&gt;:nth-child()&lt;/code&gt; for dynamic content
&lt;/h2&gt;

&lt;p&gt;Most of us currently handle this kind of staggering with something like:&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="nc"&gt;.card&lt;/span&gt;&lt;span class="nd"&gt;:nth-child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;animation-delay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100ms&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.card&lt;/span&gt;&lt;span class="nd"&gt;:nth-child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;animation-delay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200ms&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.card&lt;/span&gt;&lt;span class="nd"&gt;:nth-child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;3&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;animation-delay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;300ms&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;It works, right up until someone inserts a new &lt;code&gt;.card&lt;/code&gt; in the middle of the list and every delay after it is now describing the wrong element. &lt;code&gt;:nth-child()&lt;/code&gt; is fundamentally &lt;em&gt;positional&lt;/em&gt;, it targets a slot in the DOM, not a relationship to the group.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sibling-index()&lt;/code&gt; is relational instead. It doesn't care how the list was generated or how many items came before your latest change, it just answers "where am I, relative to my siblings" at render time. That distinction matters a lot once your markup is coming from a CMS, an API response, or a mapped React list, exactly the situations where hardcoded &lt;code&gt;:nth-child()&lt;/code&gt; rules get fragile fast.&lt;/p&gt;

&lt;h2&gt;
  
  
  Worth knowing before you reach for it
&lt;/h2&gt;

&lt;p&gt;This is a newer CSS feature and support isn't universal yet, so it's not a safe swap for production logic across the board. Treat it as progressive enhancement for modern browsers for now, and keep a JS or &lt;code&gt;:nth-child()&lt;/code&gt; fallback where broad compatibility actually matters. Worth checking current support on caniuse before leaning on it for anything critical.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bigger pattern here
&lt;/h2&gt;

&lt;p&gt;This fits a trend that's been building for a few years now. CSS picked up &lt;code&gt;:has()&lt;/code&gt;, container queries, native nesting, anchor positioning, view transitions, and now sibling-awareness through these two functions. Each one used to be squarely "you need JavaScript for that." One by one, that list is getting shorter.&lt;/p&gt;

&lt;p&gt;The useful mental shift isn't "can CSS replace JavaScript here", it's "can the browser do this more efficiently than my JS can". Increasingly, for layout- and DOM-structure-related problems, the answer is yes, the browser already has the information, it was just never exposed to the stylesheet before.&lt;/p&gt;

&lt;p&gt;So next time you catch yourself writing JS purely to number items, stagger an animation, or calculate a position within a group, it's worth pausing and asking whether CSS already knows the answer. Chances are, it's starting to.&lt;/p&gt;




&lt;p&gt;We're ArtClick, a web development agency based in Kyoto. We build company websites, WordPress sites, and custom systems — with a focus on sites that are fast, well-designed, and easy to maintain long-term. Learn more at &lt;a href="https://artclickdev.com/?utm_source=devto" rel="noopener noreferrer"&gt;artclickdev&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>css</category>
      <category>frontend</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Artclick</dc:creator>
      <pubDate>Mon, 17 Aug 2026 09:55:23 +0000</pubDate>
      <link>https://dev.to/_artclick/-5fkl</link>
      <guid>https://dev.to/_artclick/-5fkl</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/_artclick/why-good-frontend-developers-still-matter-in-the-age-of-ai-51ca" class="crayons-story__hidden-navigation-link"&gt;Why Good Frontend Developers Still Matter in the Age of AI&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/_artclick" class="crayons-avatar  crayons-avatar--l  "&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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4049105%2F5e4d789f-a23c-435d-81df-3e14f5091905.png" alt="_artclick profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/_artclick" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Artclick
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Artclick
                
                
              
              &lt;div id="story-author-preview-content-4416750" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/_artclick" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4049105%2F5e4d789f-a23c-435d-81df-3e14f5091905.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Artclick&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/_artclick/why-good-frontend-developers-still-matter-in-the-age-of-ai-51ca" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Aug 17&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/_artclick/why-good-frontend-developers-still-matter-in-the-age-of-ai-51ca" id="article-link-4416750"&gt;
          Why Good Frontend Developers Still Matter in the Age of AI
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/css"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;css&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/frontend"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;frontend&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/programming"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;programming&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/_artclick/why-good-frontend-developers-still-matter-in-the-age-of-ai-51ca" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;1&lt;span class="hidden s:inline"&gt;&amp;nbsp;reaction&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/_artclick/why-good-frontend-developers-still-matter-in-the-age-of-ai-51ca#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            5 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Artclick</dc:creator>
      <pubDate>Mon, 17 Aug 2026 09:46:39 +0000</pubDate>
      <link>https://dev.to/_artclick/-1cj7</link>
      <guid>https://dev.to/_artclick/-1cj7</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/_artclick/this-tool-lets-you-add-themeable-production-ready-maps-into-your-react-app-52mp" class="crayons-story__hidden-navigation-link"&gt;This Tool Lets You Add Themeable Production-Ready Maps Into Your React App 🔥&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/_artclick" class="crayons-avatar  crayons-avatar--l  "&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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4049105%2F5e4d789f-a23c-435d-81df-3e14f5091905.png" alt="_artclick profile" class="crayons-avatar__image" width="800" height="600"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/_artclick" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Artclick
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Artclick
                
                
              
              &lt;div id="story-author-preview-content-4415951" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/_artclick" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4049105%2F5e4d789f-a23c-435d-81df-3e14f5091905.png" class="crayons-avatar__image" alt="" width="800" height="600"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Artclick&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/_artclick/this-tool-lets-you-add-themeable-production-ready-maps-into-your-react-app-52mp" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Aug 17&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/_artclick/this-tool-lets-you-add-themeable-production-ready-maps-into-your-react-app-52mp" id="article-link-4415951"&gt;
          This Tool Lets You Add Themeable Production-Ready Maps Into Your React App 🔥
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/javascript"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;javascript&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/programming"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;programming&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/opensource"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;opensource&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/_artclick/this-tool-lets-you-add-themeable-production-ready-maps-into-your-react-app-52mp" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;2&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/_artclick/this-tool-lets-you-add-themeable-production-ready-maps-into-your-react-app-52mp#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            5 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>Why Good Frontend Developers Still Matter in the Age of AI</title>
      <dc:creator>Artclick</dc:creator>
      <pubDate>Mon, 17 Aug 2026 09:46:21 +0000</pubDate>
      <link>https://dev.to/_artclick/why-good-frontend-developers-still-matter-in-the-age-of-ai-51ca</link>
      <guid>https://dev.to/_artclick/why-good-frontend-developers-still-matter-in-the-age-of-ai-51ca</guid>
      <description>&lt;p&gt;Ask any AI coding assistant to build a button with Tailwind and it'll nail it in seconds: &lt;code&gt;bg-blue-500 text-white px-4 py-2 rounded-md hover:bg-blue-600&lt;/code&gt;. Ask the same tool to fix why your flex container is collapsing on mobile, or why a &lt;code&gt;z-index&lt;/code&gt; isn't taking effect, and things get shakier fast.&lt;/p&gt;

&lt;p&gt;That gap is a useful lens for a bigger question a lot of frontend developers are quietly asking themselves right now: if AI can write so much of our code, do we still matter? The honest answer is yes, and CSS turns out to be one of the clearest places to see exactly why.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Tailwind is a good match for AI
&lt;/h2&gt;

&lt;p&gt;Tailwind's whole design is built around small, predictable, tokenized classes. &lt;code&gt;bg-&lt;/code&gt;, &lt;code&gt;p-&lt;/code&gt;, &lt;code&gt;flex&lt;/code&gt;, &lt;code&gt;items-center&lt;/code&gt; — each one maps to a single, well-defined rule, and the naming convention barely changes from project to project.&lt;/p&gt;

&lt;p&gt;That predictability is exactly what language models are optimized for. They're pattern-completion engines at heart: show them thousands of examples of &lt;code&gt;p-4&lt;/code&gt; sitting next to &lt;code&gt;flex justify-between&lt;/code&gt;, scraped from tutorials, open-source repos, and Stack Overflow answers, and they get remarkably good at reproducing that pattern convincingly. Tailwind's classes are so consistent and so heavily represented in training data that generating them almost becomes a memorization problem, not a reasoning one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why plain CSS trips AI up
&lt;/h2&gt;

&lt;p&gt;Classic CSS doesn't have any of that consistency, and that's precisely the point of it, it's meant to be shaped by whoever's writing it. Which is great for developers, and rough for pattern-matching models:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Naming is a free-for-all. One codebase uses &lt;code&gt;.btn-primary&lt;/code&gt;, another uses &lt;code&gt;.buttonMain&lt;/code&gt;, a third follows BEM (&lt;code&gt;.block__element--modifier&lt;/code&gt;), a fourth uses CSS Modules or scoped styles entirely.&lt;/li&gt;
&lt;li&gt;Styles can live anywhere — external stylesheets, &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; blocks, inline attributes — with no fixed relationship to the markup they affect.&lt;/li&gt;
&lt;li&gt;The connection between a class name and its visual outcome is indirect. An AI model can't actually &lt;em&gt;see&lt;/em&gt; your rendered page; it's inferring meaning from text patterns that vary wildly between projects.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On top of that, CSS isn't just syntax, it's a layout system with cascade, specificity, inheritance, stacking contexts, and formatting contexts all interacting at once. Diagnosing a real bug usually means reasoning across several of these layers simultaneously:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is that &lt;code&gt;z-index&lt;/code&gt; failing because of a stacking context created somewhere up the tree?&lt;/li&gt;
&lt;li&gt;Is a margin collapsing because of block formatting context rules?&lt;/li&gt;
&lt;li&gt;Is a &lt;code&gt;width&lt;/code&gt; getting silently overridden by a &lt;code&gt;min-width&lt;/code&gt; declared somewhere else entirely?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of that is a text-completion problem. It's closer to debugging a physics simulation, and that's a different kind of reasoning than "what token comes next," which is fundamentally what an LLM is doing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The staleness problem nobody talks about
&lt;/h2&gt;

&lt;p&gt;There's a second issue that's less about reasoning and more about timing: most models are trained on a snapshot of the web that's already months, sometimes years, old by the time you're using them. That's a real problem for CSS specifically, because the language has been moving unusually fast lately.&lt;/p&gt;

&lt;p&gt;Ask an AI assistant about &lt;code&gt;:has()&lt;/code&gt;, &lt;code&gt;container-type&lt;/code&gt;, or &lt;code&gt;view-timeline&lt;/code&gt;, and you'll often get a confident-sounding answer with no mention of actual browser support, or worse, a subtly wrong explanation presented with total confidence. It won't tell you Chromium shipped a feature months before Firefox did. It won't check caniuse.com before answering. It simply doesn't know what it doesn't know, and it has no built-in way to flag that uncertainty to you.&lt;/p&gt;

&lt;p&gt;Meanwhile the platform keeps moving:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Logical properties are reshaping how RTL and internationalized layouts get built&lt;/li&gt;
&lt;li&gt;Container queries are quietly replacing a lot of what media queries used to be responsible for&lt;/li&gt;
&lt;li&gt;Scroll-driven animations are making JS-based scroll listeners feel like a legacy pattern&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your only source of CSS knowledge is what a chatbot tells you, you're building on a stale foundation without realizing it. The spec, the CSS Working Group drafts, and actual browser support tables are still the ground truth, an AI's confidence level is not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why understanding CSS still matters, even if you use Tailwind
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;AI doesn't do design reasoning.&lt;/strong&gt; It's good at pattern completion, not at judging whether a layout looks balanced or a component adapts gracefully across breakpoints. It can suggest a plausible flex setup, but it has no sense of &lt;em&gt;why&lt;/em&gt; that setup is the right one for your specific design.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tailwind is still CSS underneath.&lt;/strong&gt; If you don't actually understand what &lt;code&gt;flex&lt;/code&gt;, &lt;code&gt;justify-between&lt;/code&gt;, or &lt;code&gt;overflow-hidden&lt;/code&gt; do at the CSS level, you're just stacking utility classes and hoping something sticks. Tailwind doesn't remove the need to understand spacing systems, breakpoints, positioning, and the box model, it just asks you to think about those things in smaller, composable pieces. Without that mental model, you can't reason about why your utility stack behaves the way it does when something breaks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real bugs still need a human.&lt;/strong&gt; A hidden scrollbar, a layout shift that only shows up on one device, an overflow caused by a &lt;code&gt;min-width&lt;/code&gt; three components away, these are exactly the cases where AI tends to guess rather than diagnose. Inspecting the cascade, spotting an unintended inheritance, checking &lt;code&gt;box-sizing&lt;/code&gt;, testing across real devices: this is still fundamentally human work, because it requires actually seeing and interacting with the rendered result, not just reading the code that produced it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part AI genuinely can't do
&lt;/h2&gt;

&lt;p&gt;There's a layer of frontend work that's less about correctness and more about feel: nudging padding until a layout stops feeling cramped, adjusting contrast until text is both accessible and pleasant to read, picking a type scale ratio that feels right on mobile, aligning things until a page feels intentional rather than assembled.&lt;/p&gt;

&lt;p&gt;None of that comes from a training set. It comes from having actually looked at hundreds of interfaces, built a sense for what "off" looks like, and developed the kind of taste that only comes from experience. AI can generate a technically valid layout. It can't tell you it looks wrong, because it has no eyes and no taste, it only has patterns.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this leaves things
&lt;/h2&gt;

&lt;p&gt;AI coding tools maybe useful for scaffolding, for Tailwind-heavy work, and for speeding up the boring parts. But that doesn't make frontend developers less necessary, it just relocates where the value is. Less time typing out predictable class combinations, more time on the things AI structurally can't do: diagnosing real bugs, reasoning across the cascade, and knowing when a layout is technically valid but still wrong.&lt;/p&gt;

&lt;p&gt;The developers who understand the cascade, keep up with what's shipping in browsers, and have built an eye for what looks right are exactly the ones who can catch it when the AI's confident answer is quietly mistaken, and fix it when it is. That skill set isn't going away because AI got better at autocomplete. If anything, it's becoming the differentiator.&lt;/p&gt;




&lt;p&gt;We're ArtClick, a web development agency based in Kyoto. We build company websites, WordPress sites, and custom systems — with a focus on sites that are fast, well-designed, and easy to maintain long-term. Learn more at &lt;a href="https://artclickdev.com/?utm_source=devto" rel="noopener noreferrer"&gt;artclickdev&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>css</category>
      <category>webdev</category>
      <category>frontend</category>
      <category>programming</category>
    </item>
    <item>
      <title>This Tool Lets You Add Themeable Production-Ready Maps Into Your React App 🔥</title>
      <dc:creator>Artclick</dc:creator>
      <pubDate>Mon, 17 Aug 2026 08:15:50 +0000</pubDate>
      <link>https://dev.to/_artclick/this-tool-lets-you-add-themeable-production-ready-maps-into-your-react-app-52mp</link>
      <guid>https://dev.to/_artclick/this-tool-lets-you-add-themeable-production-ready-maps-into-your-react-app-52mp</guid>
      <description>&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsamzq4n344c59nmlxaf6.gif" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsamzq4n344c59nmlxaf6.gif" alt="Mapcn Demo" width="540" height="304"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you've ever added a map to a React app, you know the drill. Pick a library, wrestle with API keys, fight the wrapper's styling system to make it match your design, then give up and drop down to raw MapLibre or Mapbox anyway. Most map libraries are either too opinionated to customize or too low-level to be a good starting point.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.mapcn.dev/" rel="noopener noreferrer"&gt;mapcn&lt;/a&gt; takes a different approach, and it's the same approach that made shadcn/ui popular for regular components: you don't install a map component as a black-box dependency, you copy the actual source into your project and own it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it actually is
&lt;/h2&gt;

&lt;p&gt;mapcn is a collection of accessible, customizable map components for React, built on top of &lt;a href="https://maplibre.org/" rel="noopener noreferrer"&gt;MapLibre GL&lt;/a&gt; and styled with Tailwind CSS. It's designed to slot directly into a shadcn/ui project, following the same "copy, don't install" philosophy shadcn made popular.&lt;/p&gt;

&lt;p&gt;A few things stood out to me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No API key required.&lt;/strong&gt; It ships with free CARTO basemap tiles by default, so you get a working map immediately, with zero signup friction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Theme aware out of the box.&lt;/strong&gt; The map tiles automatically switch between light and dark styles based on your app's theme.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You own the code.&lt;/strong&gt; Since components are copied into your project rather than pulled in as an opaque dependency, you can edit anything, there's no version lock-in, and no fighting an abstraction layer to override one style.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not locked to one tile provider.&lt;/strong&gt; Because it stays close to MapLibre's own style spec, you can swap in tiles from OpenStreetMap, MapTiler, Stadia Maps, Thunderforest, or basically any MapLibre-compatible provider whenever you outgrow the free default.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full TypeScript support&lt;/strong&gt;, and it drops down to the raw MapLibre instance whenever you need more control than the components expose.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;If you already have Tailwind CSS and shadcn/ui set up, adding the map component is one command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm dlx shadcn@latest add @mapcn/map
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This installs &lt;code&gt;maplibre-gl&lt;/code&gt; and adds the map component into your project, the same way any other shadcn component gets added.&lt;/p&gt;

&lt;p&gt;Then it's a normal React component:&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="nb"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;MapControls&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;@/components/ui/map&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Card&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;@/components/ui/card&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;MyMap&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="nc"&gt;Card&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"h-[320px] p-0 overflow-hidden"&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="nc"&gt;Map&lt;/span&gt; &lt;span class="na"&gt;center&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;74.006&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;40.7128&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;zoom&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;11&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="nc"&gt;MapControls&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="nc"&gt;Map&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="nc"&gt;Card&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;That's a rendered, interactive, theme-aware map with zoom controls, no API key, no config file.&lt;/p&gt;

&lt;p&gt;One implementation detail worth knowing: MapLibre parses map tiles in a Web Worker, which ships as a separate file. mapcn loads that worker from unpkg by default, pinned to your installed version, so there's no manual setup. If you're running under a strict Content Security Policy, you'll need to allow &lt;code&gt;script-src 'self' https://unpkg.com&lt;/code&gt; and &lt;code&gt;worker-src 'self' blob:&lt;/code&gt; (plus whatever your basemap host needs). If you'd rather self-host the worker entirely, you can copy the worker files into your &lt;code&gt;public/&lt;/code&gt; folder and point &lt;code&gt;MapLibreGL.setWorkerUrl()&lt;/code&gt; at them instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Markers, popups, and tooltips
&lt;/h2&gt;

&lt;p&gt;This is where the "composable" part of mapcn's pitch really shows. Instead of one giant marker prop with a dozen config options, you compose small pieces:&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="nb"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;MapMarker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;MarkerContent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;MarkerPopup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;MarkerTooltip&lt;/span&gt;&lt;span class="p"&gt;,&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;@/components/ui/map&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;locations&lt;/span&gt; &lt;span class="o"&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;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Empire State Building&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;73.9857&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;40.7484&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Central Park&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;73.9654&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;40.7829&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Times Square&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;73.9855&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;40.758&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;MarkersExample&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;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"h-[420px] w-full"&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="nc"&gt;Map&lt;/span&gt; &lt;span class="na"&gt;center&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;73.98&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;40.76&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;zoom&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="si"&gt;}&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;locations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;location&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;MapMarker&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lng&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lat&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="nc"&gt;MarkerContent&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;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"bg-primary size-4 rounded-full border-2 border-white shadow-lg"&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="nc"&gt;MarkerContent&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="nc"&gt;MarkerTooltip&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;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&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;MarkerTooltip&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="nc"&gt;MarkerPopup&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;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"space-y-1"&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;p&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-foreground font-medium"&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;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&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;p&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;p&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-muted-foreground text-xs"&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;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;, &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&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="nt"&gt;p&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;div&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="nc"&gt;MarkerPopup&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="nc"&gt;MapMarker&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&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;Map&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;div&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;Because &lt;code&gt;MarkerPopup&lt;/code&gt; accepts arbitrary JSX, nothing stops you from building a rich popup with an image, a rating, and action buttons using your existing shadcn &lt;code&gt;Button&lt;/code&gt; and &lt;code&gt;Card&lt;/code&gt; components. It ends up looking like a genuine part of your app's UI instead of a plugin bolted on top of a map.&lt;/p&gt;

&lt;p&gt;Markers can also be made draggable with a single &lt;code&gt;draggable&lt;/code&gt; prop plus an &lt;code&gt;onDrag&lt;/code&gt; handler, which is handy for location pickers or "confirm your address" style flows.&lt;/p&gt;

&lt;p&gt;One thing worth flagging: &lt;code&gt;MapMarker&lt;/code&gt; renders actual DOM elements, so it's a great fit for anywhere from a handful up to a few hundred markers. If you're plotting thousands of points, the docs point you toward rendering markers as a GeoJSON layer instead, which is far cheaper for large datasets.&lt;/p&gt;

&lt;h2&gt;
  
  
  Beyond basic markers
&lt;/h2&gt;

&lt;p&gt;The component set goes further than pins on a map. Looking through the docs, mapcn also ships:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Routes and arcs&lt;/strong&gt; — for drawing paths and curved connections between points, useful for delivery tracking or "flights between cities" style visualizations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GeoJSON support&lt;/strong&gt; — for rendering larger, more complex geographic datasets as map layers rather than individual DOM markers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clustering&lt;/strong&gt; — for grouping nearby points so your map doesn't turn into a wall of overlapping pins when the data gets dense.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom controls&lt;/strong&gt; — the &lt;code&gt;MapControls&lt;/code&gt; component in the basic example is itself composable and themeable.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why this matters if you build client sites
&lt;/h2&gt;

&lt;p&gt;For a lot of client work, whatever map library you reach for immediately becomes the thing that visually doesn't match the rest of the site. Store locators, property listings, delivery tracking, service-area maps, they all tend to need a "location finder" screen at some point, and it usually shows up as a default-blue Google Maps embed sitting awkwardly next to a carefully designed page.&lt;/p&gt;

&lt;p&gt;Because mapcn is Tailwind-styled and copy-paste by design, a map built with it can actually inherit your design tokens instead of fighting them. And since there's no required API key, it's a genuinely fast way to prototype a location feature before deciding whether a paid tile provider is worth it for a given project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to look next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.mapcn.dev/" rel="noopener noreferrer"&gt;mapcn.dev&lt;/a&gt; — homepage and live demos&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.mapcn.dev/docs" rel="noopener noreferrer"&gt;Documentation&lt;/a&gt; — installation and philosophy&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.mapcn.dev/docs/basic-map" rel="noopener noreferrer"&gt;Component docs&lt;/a&gt; — Map, Controls, Markers, Popups, Routes, Arcs, GeoJSON, Clusters&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/AnmolSaini16/mapcn" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; — source, currently at 11k+ stars&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're already in the shadcn/ui + Tailwind ecosystem and need a map at some point, this is worth a look before reaching for a heavier, more opinionated library.&lt;/p&gt;




&lt;p&gt;We're ArtClick, a web development agency based in Kyoto. We build company websites, WordPress sites, and custom systems — with a focus on sites that are fast, well-designed, and easy to maintain long-term. Learn more at &lt;a href="https://artclickdev.com/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=mapcn_article" rel="noopener noreferrer"&gt;artclickdev.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
      <category>opensource</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Artclick</dc:creator>
      <pubDate>Thu, 13 Aug 2026 09:37:24 +0000</pubDate>
      <link>https://dev.to/_artclick/-2f4k</link>
      <guid>https://dev.to/_artclick/-2f4k</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/_artclick/10-underrated-nodejs-tools-to-add-to-your-developer-toolkit-2cd8" class="crayons-story__hidden-navigation-link"&gt;10 Underrated Node.js Tools to Add to Your Developer Toolkit&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/_artclick" class="crayons-avatar  crayons-avatar--l  "&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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4049105%2F5e4d789f-a23c-435d-81df-3e14f5091905.png" alt="_artclick profile" class="crayons-avatar__image" width="800" height="600"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/_artclick" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Artclick
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Artclick
                
                
              
              &lt;div id="story-author-preview-content-4386585" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/_artclick" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4049105%2F5e4d789f-a23c-435d-81df-3e14f5091905.png" class="crayons-avatar__image" alt="" width="800" height="600"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Artclick&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/_artclick/10-underrated-nodejs-tools-to-add-to-your-developer-toolkit-2cd8" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Aug 13&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/_artclick/10-underrated-nodejs-tools-to-add-to-your-developer-toolkit-2cd8" id="article-link-4386585"&gt;
          10 Underrated Node.js Tools to Add to Your Developer Toolkit
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/programming"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;programming&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/node"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;node&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/_artclick/10-underrated-nodejs-tools-to-add-to-your-developer-toolkit-2cd8" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;1&lt;span class="hidden s:inline"&gt;&amp;nbsp;reaction&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/_artclick/10-underrated-nodejs-tools-to-add-to-your-developer-toolkit-2cd8#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            5 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>10 Underrated Node.js Tools to Add to Your Developer Toolkit</title>
      <dc:creator>Artclick</dc:creator>
      <pubDate>Thu, 13 Aug 2026 09:37:05 +0000</pubDate>
      <link>https://dev.to/_artclick/10-underrated-nodejs-tools-to-add-to-your-developer-toolkit-2cd8</link>
      <guid>https://dev.to/_artclick/10-underrated-nodejs-tools-to-add-to-your-developer-toolkit-2cd8</guid>
      <description>&lt;p&gt;Most "best Node.js packages" lists just rename Express, Lodash, and Axios in a different order. Fine tools, all three — but they're not the ones that actually saved me time this year. The ones that did are smaller, quieter, and mostly never trend on anything. They just sit in &lt;code&gt;package.json&lt;/code&gt; doing their one job well, and I only notice them when they're missing from a new project.&lt;/p&gt;

&lt;p&gt;Here are ten of them, what they replace, and where I'd actually reach for each.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. &lt;code&gt;execa&lt;/code&gt; — for when &lt;code&gt;child_process&lt;/code&gt; isn't worth the pain
&lt;/h2&gt;

&lt;p&gt;Node's built-in &lt;code&gt;child_process.exec&lt;/code&gt; technically works, but you're back to string-escaping shell commands and manually handling stdout/stderr buffering the moment you do anything beyond "run one command and forget about it."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;execa&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="s1"&gt;execa&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;stdout&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;execa&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;git&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rev-parse&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--short&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;HEAD&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Arguments are passed as an array, not concatenated into a shell string, so you stop worrying about escaping. It also throws a real error with the command's actual stderr attached when something fails, instead of leaving you to go dig through a callback.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. &lt;code&gt;zx&lt;/code&gt; — for scripts you'd normally reach for bash for
&lt;/h2&gt;

&lt;p&gt;Same author's ecosystem, different problem: &lt;code&gt;zx&lt;/code&gt; is for the "this really should just be a bash script, except I want real variables and error handling" situation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cp"&gt;#!/usr/bin/env zx
&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;branch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;`git branch --show-current`&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;$&lt;/span&gt;&lt;span class="s2"&gt;`npm run build`&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;$&lt;/span&gt;&lt;span class="s2"&gt;`rsync -av ./dist/ user@server:/var/www/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;branch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;/`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's genuinely fun to write deploy scripts in this. The caveat: don't reach for it inside application code — it's a scripting tool, not a library you import into a server. Keep it in your &lt;code&gt;scripts/&lt;/code&gt; folder.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. &lt;code&gt;p-limit&lt;/code&gt; — for when &lt;code&gt;Promise.all&lt;/code&gt; is too eager
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Promise.all&lt;/code&gt; fires everything at once. That's fine for five requests. It's how you accidentally DDoS your own API, or your own database connection pool, the moment "five" becomes "five thousand."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;pLimit&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;p-limit&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;limit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pLimit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// max 5 concurrent&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;userIds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;fetchUserData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&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;This is the fix I reach for every single time I'm processing a batch of anything — API calls, file uploads, database writes. It's about forty lines of source code and it's saved me from rate-limit bans more times than I can count.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. &lt;code&gt;nanoid&lt;/code&gt; — for IDs that don't need to be a UUID
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;uuid&lt;/code&gt; works, but a v4 UUID is 36 characters and most of the time you don't actually need RFC-compliant UUIDs — you need a short, unique, URL-safe string.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;nanoid&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="s1"&gt;nanoid&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;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;nanoid&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// 'V1StGXR8_Z5jdHi6B-myT'&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shortId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;nanoid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 'IRFa-VaY'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Smaller output, faster generation, and it's URL-safe by default so you can drop it straight into a route without encoding it. I use this for anything that doesn't need to interoperate with a system that specifically expects UUID format.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. &lt;code&gt;pino&lt;/code&gt; — for logging that doesn't slow down production
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;console.log&lt;/code&gt; is fine in development and a genuine performance problem in a high-throughput production service — synchronous, unstructured, and unfiltered by log level.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;pino&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pino&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;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pino&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user logged in&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;payment failed&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;It outputs structured JSON by default, which sounds like a downside until your logs need to go into something like Datadog or Elasticsearch and you realize structured logs were the whole point. Pipe it through &lt;code&gt;pino-pretty&lt;/code&gt; locally if raw JSON in your terminal makes your eyes glaze over — I do, every project.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. &lt;code&gt;why-is-node-running&lt;/code&gt; — for the process that won't exit
&lt;/h2&gt;

&lt;p&gt;Every Node developer has had this moment: your script is done, but the process just... sits there. Something's holding an open handle — a socket, a timer, a database connection — and you have no idea which.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;why&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;why-is-node-running&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;why&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It prints exactly what's keeping the event loop alive, with a stack trace pointing at where each handle was created. I've used this to find a forgotten &lt;code&gt;setInterval&lt;/code&gt; and an unclosed database pool in two separate "why won't this exit" debugging sessions that would otherwise have eaten an afternoon each.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. &lt;code&gt;tsx&lt;/code&gt; — for running TypeScript without a build step
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;ts-node&lt;/code&gt; works, but it's slow to start and its ESM support has been a source of pain for years. &lt;code&gt;tsx&lt;/code&gt; is a much faster drop-in for the common case of "I just want to run this TypeScript file right now."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx tsx script.ts
npx tsx watch server.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;watch&lt;/code&gt; flag alone replaced a &lt;code&gt;nodemon&lt;/code&gt; + &lt;code&gt;ts-node&lt;/code&gt; combo in most of my newer projects. It's not trying to be a full build tool — for that you still want &lt;code&gt;tsc&lt;/code&gt; or a bundler — but for local scripts and dev servers it's the fastest path from "TypeScript file" to "running code" I've used.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. &lt;code&gt;defu&lt;/code&gt; — for merging config objects without losing your mind
&lt;/h2&gt;

&lt;p&gt;Every project ends up needing to merge a default config with user-supplied overrides, and &lt;code&gt;{ ...defaults, ...userConfig }&lt;/code&gt; breaks the moment either object has nested properties — a shallow spread happily throws away half your defaults.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defu&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="s1"&gt;defu&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;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defu&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;localhost&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&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;defu&lt;/code&gt; does a proper recursive merge, only filling in values that are actually missing, at any depth. Small utility, but it quietly prevents a specific class of "why did my nested config option get wiped out" bug.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. &lt;code&gt;picocolors&lt;/code&gt; — for terminal colors without the dependency weight
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;chalk&lt;/code&gt; is the household name here, and it's a fine library — but if all you need is basic ANSI colors in CLI output, &lt;code&gt;picocolors&lt;/code&gt; does the same job at a fraction of the size, with no dependencies of its own.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;pc&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;picocolors&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;green&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;✓ Build succeeded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;red&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;✗ Build failed&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;If you're building a CLI tool that other people will install, every dependency you pull in is a dependency they inherit. This is the kind of swap that doesn't change what your code does, only how much it costs the next person to install it.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. &lt;code&gt;ora&lt;/code&gt; — for CLI spinners that don't leave you guessing
&lt;/h2&gt;

&lt;p&gt;If your script does anything that takes more than half a second, a silent terminal makes people wonder if it's frozen. &lt;code&gt;ora&lt;/code&gt; gives you a spinner with almost no ceremony:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;ora&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ora&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;spinner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ora&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Deploying to production...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;deploy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;spinner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;succeed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Deployed successfully&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;It's a small thing, but it's the difference between a CLI tool that feels finished and one that feels like a script someone ran once and never polished. Worth the two minutes it takes to wire in.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pattern across all ten
&lt;/h2&gt;

&lt;p&gt;None of these are exciting on their own. That's kind of the point — the tools that actually save time in practice are usually the ones solving one specific, annoying problem precisely, not the ones with the biggest feature list. Before reaching for a heavier framework or writing something from scratch, it's worth checking whether one of these already exists for exactly the problem in front of you. More often than I expect, one does.&lt;/p&gt;




&lt;p&gt;We're ArtClick, a web development agency based in Kyoto. We build company websites, WordPress sites, and custom systems — with a focus on sites that are fast, well-designed, and easy to maintain long-term. Learn more at &lt;a href="https://artclickdev.com/" rel="noopener noreferrer"&gt;artclickdev&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>node</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
