{
  "version": "https://jsonfeed.org/version/1",
  "title": "Front-end on LLBBL Blog",
  "icon": "https://avatars.micro.blog/avatars/2023/40/125738.jpg",
  "home_page_url": "https://llbbl.blog/",
  "feed_url": "https://llbbl.blog/feed.json",
  "items": [
      {
        "id": "http://llbbl.micro.blog/2026/05/12/three-css-features-that-finally.html",
        "title": "Three CSS Features That Finally Let Us Delete the JavaScript",
        "content_html": "<p>Every few years, CSS quietly absorbs something we used to need a JavaScript library for. This year, three big ones are landing at once: masonry layouts, scroll-driven animations, and styleable <code>&lt;select&gt;</code> elements. All three have a long history of clunky workarounds (Masonry.js, scroll event listeners, <code>div</code>-based fake dropdowns) and watching them become native CSS features is genuinely surprising progress for a single year.</p>\n<p>Here&rsquo;s what each one does.</p>\n<h2 id=\"native-masonry-layouts\">Native Masonry Layouts</h2>\n<p>Pinterest-style grids, where items of varying heights pack together without awkward vertical gaps, used to require Masonry.js or one of its cousins. Those libraries measure every element, calculate positions, and use absolute positioning to slot things in. The result works but is expensive, fragile on resize, and hard to make truly responsive.</p>\n<p>The native version is <code>display: grid-lanes</code>:</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-css\" data-lang=\"css\">.<span style=\"color:#a6e22e\">masonry-grid</span> {\n  <span style=\"color:#66d9ef\">display</span>: <span style=\"color:#66d9ef\">grid</span><span style=\"color:#f92672\">-</span>lanes;\n  <span style=\"color:#66d9ef\">grid-template-columns</span>: <span style=\"color:#a6e22e\">repeat</span>(<span style=\"color:#66d9ef\">auto</span><span style=\"color:#f92672\">-</span><span style=\"color:#66d9ef\">fill</span>, <span style=\"color:#a6e22e\">minmax</span>(<span style=\"color:#ae81ff\">200</span><span style=\"color:#66d9ef\">px</span>, <span style=\"color:#ae81ff\">1</span>fr));\n  gap: <span style=\"color:#ae81ff\">16</span><span style=\"color:#66d9ef\">px</span>;\n}\n</code></pre></div><p>Set up your columns the way you would for any responsive grid, switch the display value to <code>grid-lanes</code>, and the browser handles packing. There&rsquo;s also a <code>flow-tolerance</code> property that controls how aggressively items can shift to fill gaps.</p>\n<p>The naming has a history. The CSS Working Group debated for years between an earlier <code>grid-template-rows: masonry</code> proposal (which would have layered onto existing Grid) and a separate <code>display: masonry</code> value. The resolution, in early 2026, was a third option: a new <code>grid-lanes</code> display value that&rsquo;s distinct from Grid but borrows its column/row sizing. So if you read older articles showing <code>grid-template-rows: masonry</code>, that syntax did not ship.</p>\n<p>Safari was first to ship <code>grid-lanes</code> (Safari 26). Chrome and Firefox have it behind experimental flags as of early 2026.</p>\n<h2 id=\"scroll-driven-animations\">Scroll-Driven Animations</h2>\n<p>Reading progress bars, fade-ins as elements enter the viewport, parallax effects. All of these used to require listening to the window&rsquo;s <code>scroll</code> event and updating styles in JavaScript. Scroll events fire dozens of times per second, and if your main thread is busy doing literally anything else, the animation stutters. It&rsquo;s the most common source of jank on the modern web.</p>\n<p><code>animation-timeline: scroll()</code> ties a standard CSS keyframe animation to scroll position instead of a time duration. The browser runs it on the compositor thread, completely independent of your JavaScript. Buttery smooth, even when the main thread is on fire.</p>\n<p>Here&rsquo;s a reading progress bar in pure CSS:</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-css\" data-lang=\"css\">@<span style=\"color:#66d9ef\">keyframes</span> <span style=\"color:#f92672\">grow-progress</span> {\n  <span style=\"color:#f92672\">from</span> { <span style=\"color:#66d9ef\">transform</span>: scaleX(<span style=\"color:#ae81ff\">0</span>); }\n  <span style=\"color:#f92672\">to</span> { <span style=\"color:#66d9ef\">transform</span>: scaleX(<span style=\"color:#ae81ff\">1</span>); }\n}\n\n.<span style=\"color:#a6e22e\">progress-bar</span> {\n  <span style=\"color:#66d9ef\">position</span>: <span style=\"color:#66d9ef\">fixed</span>;\n  <span style=\"color:#66d9ef\">top</span>: <span style=\"color:#ae81ff\">0</span>;\n  <span style=\"color:#66d9ef\">left</span>: <span style=\"color:#ae81ff\">0</span>;\n  <span style=\"color:#66d9ef\">width</span>: <span style=\"color:#ae81ff\">100</span><span style=\"color:#66d9ef\">%</span>;\n  <span style=\"color:#66d9ef\">height</span>: <span style=\"color:#ae81ff\">8</span><span style=\"color:#66d9ef\">px</span>;\n  <span style=\"color:#66d9ef\">background</span>: linear-gradient(<span style=\"color:#66d9ef\">to</span> <span style=\"color:#66d9ef\">right</span>, <span style=\"color:#ae81ff\">#ff416c</span>, <span style=\"color:#ae81ff\">#ff4b2b</span>);\n  <span style=\"color:#66d9ef\">transform-origin</span>: <span style=\"color:#ae81ff\">0</span> <span style=\"color:#ae81ff\">50</span><span style=\"color:#66d9ef\">%</span>;\n  <span style=\"color:#66d9ef\">animation</span>: grow-progress <span style=\"color:#66d9ef\">linear</span>;\n  animation-timeline: <span style=\"color:#a6e22e\">scroll</span>(root <span style=\"color:#66d9ef\">block</span>);\n}\n</code></pre></div><p>No event listeners. No <code>requestAnimationFrame</code>. No throttling logic. The animation is just bound to scroll position and the browser figures out the rest.</p>\n<h2 id=\"customizable-select\">Customizable <code>&lt;select&gt;</code></h2>\n<p>This is the one with the longest backstory. The native <code>&lt;select&gt;</code> element is rendered by the operating system, which means whatever the OS decides to give you is what you get. The dropdown picker, option styling, hover states inside the list, none of it has been styleable from CSS.</p>\n<p>The workaround was <code>div</code>-based fake dropdowns built with JavaScript. Most of them broke keyboard navigation, screen reader support, or both. Accessibility regression in exchange for visual polish, every time.</p>\n<p><code>appearance: base-select</code> opts you out of the OS-rendered version and into a fully styleable structure with new pseudo-elements for the popup. To opt the popup itself in, you also apply <code>appearance: base-select</code> to <code>::picker(select)</code>:</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-css\" data-lang=\"css\">.<span style=\"color:#a6e22e\">custom-dropdown</span> {\n  <span style=\"color:#66d9ef\">appearance</span>: base-select;\n  <span style=\"color:#66d9ef\">padding</span>: <span style=\"color:#ae81ff\">10</span><span style=\"color:#66d9ef\">px</span>;\n  <span style=\"color:#66d9ef\">border-radius</span>: <span style=\"color:#ae81ff\">8</span><span style=\"color:#66d9ef\">px</span>;\n  <span style=\"color:#66d9ef\">border</span>: <span style=\"color:#ae81ff\">1</span><span style=\"color:#66d9ef\">px</span> <span style=\"color:#66d9ef\">solid</span> <span style=\"color:#ae81ff\">#ccc</span>;\n}\n\n.<span style=\"color:#a6e22e\">custom-dropdown</span>::<span style=\"color:#a6e22e\">picker</span><span style=\"color:#f92672\">(</span><span style=\"color:#f92672\">select</span><span style=\"color:#f92672\">)</span> {\n  <span style=\"color:#66d9ef\">appearance</span>: base-select;\n  <span style=\"color:#66d9ef\">background-color</span>: <span style=\"color:#ae81ff\">#1e1e1e</span>;\n  <span style=\"color:#66d9ef\">border-radius</span>: <span style=\"color:#ae81ff\">12</span><span style=\"color:#66d9ef\">px</span>;\n  <span style=\"color:#66d9ef\">box-shadow</span>: <span style=\"color:#ae81ff\">0</span> <span style=\"color:#ae81ff\">4</span><span style=\"color:#66d9ef\">px</span> <span style=\"color:#ae81ff\">12</span><span style=\"color:#66d9ef\">px</span> rgba(<span style=\"color:#ae81ff\">0</span>,<span style=\"color:#ae81ff\">0</span>,<span style=\"color:#ae81ff\">0</span>,<span style=\"color:#ae81ff\">0.2</span>);\n  <span style=\"color:#66d9ef\">padding</span>: <span style=\"color:#ae81ff\">8</span><span style=\"color:#66d9ef\">px</span>;\n}\n\n.<span style=\"color:#a6e22e\">custom-dropdown</span> <span style=\"color:#f92672\">option</span>:<span style=\"color:#a6e22e\">hover</span> {\n  <span style=\"color:#66d9ef\">background-color</span>: <span style=\"color:#ae81ff\">#333</span>;\n  <span style=\"color:#66d9ef\">cursor</span>: <span style=\"color:#66d9ef\">pointer</span>;\n}\n</code></pre></div><p>The <code>::picker(select)</code> pseudo-element is the headline feature. The popup container is the thing every custom dropdown library was reinventing from scratch, and now it&rsquo;s a styleable part of a real <code>&lt;select&gt;</code>, so native keyboard and accessibility behavior comes along for free.</p>\n<p>Browser support is Chromium-only as of early 2026 (Chrome, Edge, Opera 135+). Firefox and Safari haven&rsquo;t shipped it yet.</p>\n<h2 id=\"a-note-on-adoption\">A Note on Adoption</h2>\n<p>Support varies by feature. Scroll-driven animations are the most settled, with stable support in Chrome and Safari and a flag-gated implementation in Firefox. <code>display: grid-lanes</code> shipped first in Safari 26 and is behind flags in Chrome and Firefox. <code>appearance: base-select</code> is Chromium-only, with no Firefox or Safari implementation yet. For all three, a <code>@supports</code> block with a sane fallback is the right pattern. Check caniuse.com before you ship.</p>\n<p>But the direction is clear. Three of the most common reasons we used to pull in JavaScript are becoming native CSS features. Better performance, less code, and accessibility you don&rsquo;t have to rebuild from scratch.</p>\n<p>I&rsquo;ll take that trade.</p>\n<h2 id=\"sources\">Sources</h2>\n<ul>\n<li><a href=\"https://webkit.org/blog/17660/introducing-css-grid-lanes/\">Introducing CSS Grid Lanes — WebKit</a></li>\n<li><a href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Grid_layout/Masonry_layout\">Masonry layout — MDN</a></li>\n<li><a href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Scroll-driven_animations\">CSS scroll-driven animations — MDN</a></li>\n<li><a href=\"https://developer.chrome.com/blog/a-customizable-select\">The <code>&lt;select&gt;</code> element can now be customized with CSS — Chrome for Developers</a></li>\n<li><a href=\"https://open-ui.org/components/customizableselect/\">Customizable Select Element explainer — Open UI</a></li>\n</ul>\n<blockquote>\n<p>I&rsquo;d appreciate a follow. You can subscribe with your email below. The emails go out once a week, or you can find me on Mastodon at <a href=\"https://micro.blog/llbbl?remote_follow=1\">@logan@llbbl.blog</a>.</p>\n</blockquote>\n",
        "date_published": "2026-05-12T10:00:00-05:00",
        "url": "https://llbbl.blog/2026/05/12/three-css-features-that-finally.html",
        "tags": ["Web development","Css","Front-end"]
      }
  ]
}
