CSS and design systems people - BlogFlock2026-02-12T02:14:58.278ZBlogFlockRobin Rendle, Michelle Barker, Sara Joy, Adam Argyle, Robin Rendle, Sara Soueidan, Hidde de Vries, Stephanie EcklesSite Updates Feb 2026 - Adam Argylehttps://nerdy.dev/site-updates-feb-2026?utm_source=rss2026-02-08T06:36:42.000Z<p><strong>Site Updates</strong></p>
<ul>
<li>Deno KV social data caching</li>
<li>new <a href="https://codepen.io/beta">Codepen 2.0</a> editor support</li>
<li>UI fixes</li>
<li><a href="/notebook">Notebook</a> update (added <a href="/notebook/color-picker.html">Color Picker</a>)</li>
<li><a href="https://pirsch.io/ref/KLV1e2zdGw">Server analytics</a> added to Notebook</li>
<li>Server page views and active users added to UI</li>
</ul>
WWW Ep230 We Accidentally Deleted Programming - Adam Argylehttps://nerdy.dev/www-ep230-we-accidentally-deleted-programming?utm_source=rss2026-02-06T00:39:55.000Z
<img style="display: none" src="https://nerdy.dev/media/www-ep230.jpg" alt="Whiskey web and whatnot episode 230" height="600" width="600" />
<p><span class="Tag">Ep #230</span><br>
<strong>We accidentally deleted programming</strong></p>
<p><a href="https://robbiethewagner.dev">Robbie</a> and I dig into the shift from code-first to spec-first development, whether TypeScript still matters in an AI-driven world, the rise of agency over specialization, and why the future might belong to prompt-driven tinkerers instead of traditional developers.</p>
<p>⤷ <a href="https://whiskey.fm/we-accidentally-deleted-programming-presented-by-coderabbit">whiskey.fm</a> · <a href="https://www.youtube.com/watch?v=9w_5Pstg1zk">youtube</a> · <a href="https://open.spotify.com/episode/26nuPJwftZKwMzi00p95sz">spotify</a> · <a href="https://podcasts.apple.com/us/podcast/we-accidentally-deleted-programming-presented-by-coderabbit/id1552776603?i=1000748337252">apple</a></p>
Nice Select - Adam Argylehttps://nerdy.dev/nice-select?utm_source=rss2026-02-03T17:17:50.000Z
<img style="display: none" src="https://nerdy.dev/media/nice-selects.jpg" alt="Text emphasized alt text example" height="980" width="2030" />
<p>This post pushes <a href="https://developer.chrome.com/blog/a-customizable-select">customizable <code><select></code></a> to an extreme. I think it turned out nice.</p>
<p>It's mostly CSS! A li'l JS sprinkle just to position the <code>::picker()</code> and the selected option together; the JS is nice to have, <strong>CSS does the real work</strong>.</p>
<p><video
src="/media/nice-select.mp4"
width="1440"
height="1080"
alt=""
preload="none"
poster="/media/nice-select.avif"
controls
loop
muted
playsinline
allowFullScreen
/></p>
<p><span class="Tag">superellipse</span> <span class="Tag">forced colors</span> <span class="Tag">light/dark theme</span> <span class="Tag">slick scrollbar</span> <span class="Tag">scroll driven animations</span> <span class="Tag">scroll-state stuck query</span> <span class="Tag">scroll-state scrollable query</span> <span class="Tag">overscroll behavior</span> <span class="Tag">smooth scroll</span> <span class="Tag">RTL support</span> <span class="Tag">text-box trim</span> <span class="Tag">spring easing</span> <span class="Tag">anchor & anchor fallbacks</span> <span class="Tag">color-mix()</span></p>
<p><small>Chrome only atm, and I keep mobile with the native select. <b>Still cool!</b></small></p>
<p><a href="https://codepen.io/editor/argyleink/pen/019c1f28-bbc2-7bac-ad4a-a7e41d3730f1">Try it</a></p>
<script type="module">
import "https://cdn.jsdelivr.net/npm/baseline-status";
</script>
<h2>
Core Architecture
<a name="core-architecture" href="#core-architecture">#</a>
</h2>
<p>This component builds on the new <code>appearance: base-select</code> foundation, combining native accessibility with sweet visual control. Progressive enhancement ensures the custom experience only loads on capable devices. </p>
<q class="info">
The examples in this post just look like regular select elements in Firefox and Safari. Firefox is <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1944403">working on it though!</a>
</q>
<ul>
<li><strong><a href="https://developer.mozilla.org/docs/Web/CSS/appearance#base-select"><code>appearance: base-select</code></a></strong><br>
unlocks <a href="https://developer.chrome.com/blog/a-customizable-select">full customization</a> of native <code><select></code> elements while retaining browser controlled accessibility and keyboard navigation</li>
<li><strong>Conditional enhancement</strong><br>
Only enables custom styling for devices with hover and fine pointer (<a href="https://developer.mozilla.org/docs/Web/CSS/@media/hover">@media (hover)</a> and <a href="https://developer.mozilla.org/docs/Web/CSS/@media/pointer">(pointer: fine)</a>)</li>
<li><strong>JavaScript + CSS hybrid</strong><br>
JS calculates an anchor offset for alignment while CSS handles all visual presentation and animation</li>
</ul>
<h2>
Alignment & Positioning
<a name="alignment-&-positioning" href="#alignment-&-positioning">#</a>
</h2>
<p>Anchor positioning plus some special JS sauce to write an inline CSS variable unlocks the picker to position near the selected option (obviously this can't work at the viewport edges). This creates a pleasant morphing effect that feels connected. </p>
<q class="info">
<p>Maybe I should have used <a href="/anchor-interpolated-morphing">AIM</a>?</p>
</q>
<ul>
<li><a href="https://developer.mozilla.org/docs/Web/CSS/CSS_anchor_positioning">CSS Anchor Positioning</a><br>
Picker uses <a href="https://developer.mozilla.org/docs/Web/CSS/Reference/Values/anchor"><code>anchor(start)</code></a> to position relative to the trigger button </li>
<li><strong>Selected option alignment</strong><br>
When dropdown opens, the currently selected option aligns vertically with the button text via <code>--_select-anchor-offset</code> <a href="https://developer.mozilla.org/docs/Web/CSS/--*">custom property</a> </li>
<li><strong>Syncs</strong>
<code>transform-origin</code><br>
With <code>50% var(--_select-anchor-offset)</code> ensures scale animations line up better with the selected option's position </li>
<li><strong>Anchor fallbacks with</strong>
<a href="https://developer.mozilla.org/docs/Web/CSS/position-try-fallbacks"><code>position-try</code></a><br>
<code>flip-block, flip-inline</code> automatically repositions the anchor position if the picker would overflow the viewport</li>
</ul>
<p><baseline-status featureId="anchor-positioning"></baseline-status></p>
<h2>
Animation & Transitions
<a name="animation-&-transitions" href="#animation-&-transitions">#</a>
</h2>
<p>Scale effects with spring physics to scroll-driven option reveals, every interaction respects user motion preferences while providing rich feedback.</p>
<ul>
<li><a href="https://developer.mozilla.org/docs/Web/CSS/Reference/At-rules/@starting-style"><code>@starting-style</code></a><br>
Defines entry animation initial state for the popover </li>
<li><strong>Spring easing</strong><br>
Uses <a href="https://open-props.style/#easing">Open Props</a> <code>--ease-spring-3</code> for playful, physics-based motion on scale and rotation</li>
<li><strong>Discrete property transitions</strong><br>
<a href="https://developer.mozilla.org/docs/Web/CSS/display">display</a> and <a href="https://developer.mozilla.org/docs/Web/CSS/overlay">overlay</a> use <a href="https://developer.mozilla.org/docs/Web/CSS/transition-behavior"><code>allow-discrete</code></a> for proper <code>::picker()</code> popover animation support</li>
<li><strong>Chevron rotation</strong><br>
<a href="https://developer.mozilla.org/docs/Web/CSS/::picker-icon"><code>::picker-icon</code></a> rotates 180° on open with spring easing</li>
<li><strong>Scroll-driven option fade-in</strong><br>
Options animate in from the bottom using <a href="https://developer.mozilla.org/docs/Web/CSS/animation-timeline"><code>animation-timeline: view()</code></a></li>
<li><strong>Respects reduced motion</strong><br>
Animations and transitions wrapped in <a href="https://developer.mozilla.org/docs/Web/CSS/@media/prefers-reduced-motion"><code>@media (prefers-reduced-motion: no-preference)</code></a></li>
<li><strong>Button press feedback</strong><br>
<a href="https://developer.mozilla.org/docs/Web/CSS/:active">Active state</a> <a href="https://developer.mozilla.org/docs/Web/CSS/scale">scales</a> down to <code>0.98</code> for tactile feedback</li>
<li><strong>Open state button feedback</strong><br>
Select scales up to <code>1.04</code> when <a href="https://developer.mozilla.org/docs/Web/CSS/:open"><code>:open</code></a>, scaling back down on close with a soft bounce</li>
</ul>
<p><baseline-status featureId="starting-style"></baseline-status>
<baseline-status featureId="transition-behavior"></baseline-status>
<baseline-status featureId="linear-easing"></baseline-status></p>
<h2>
Theming & Color Handling
<a name="theming-&-color-handling" href="#theming-&-color-handling">#</a>
</h2>
<p>Automatic light/dark adaptation leverages system colors and modern color functions for seamless theme switching. Definitely check the support for light/dark forced colors too, a really great way to debug spacing and skeletal aspects of the UI.</p>
<ul>
<li><a href="https://developer.mozilla.org/docs/Web/CSS/color-scheme"><code>color-scheme: light dark</code></a><br>
Enables automatic system color adaptation </li>
<li><a href="https://developer.mozilla.org/docs/Web/CSS/color_value/light-dark"><code>light-dark()</code> function</a><br>
Used for backgrounds that need different light/dark values</li>
<li><a href="https://developer.mozilla.org/docs/Web/CSS/system-color">System colors</a><br>
<code>Canvas</code>, <code>CanvasText</code>, <code>Highlight</code>, <code>HighlightText</code> for semantic, colors</li>
<li><a href="https://developer.mozilla.org/docs/Web/CSS/color_value/color-mix"><code>color-mix()</code></a><br>
Creates semi-transparent overlays and borders</li>
<li><strong>Data URI icons</strong><br>
Chevron and checkmark icons embedded as SVG <a href="https://developer.mozilla.org/docs/Web/URI/Schemes/data">data URIs</a> with variants for each color scheme</li>
<li><strong>Forced colors support</strong><br>
<a href="https://developer.mozilla.org/docs/Web/CSS/@media/forced-colors"><code>@media (forced-colors: active)</code></a> provides high-contrast mode adaptations (even the sticky scroll state respects this!)</li>
</ul>
<p><baseline-status featureId="color-mix"></baseline-status>
<baseline-status featureId="light-dark"></baseline-status>
<baseline-status featureId="forced-colors"></baseline-status></p>
<h2>
Layout & Spacing
<a name="layout-&-spacing" href="#layout-&-spacing">#</a>
</h2>
<p>Logical properties enable true internationalization with automatic RTL support, while text-box trim delivers pixel-perfect vertical alignment. Custom properties create a flexible design token system.</p>
<ul>
<li><a href="https://developer.mozilla.org/docs/Web/CSS/--*">CSS Custom Properties</a> <strong>design tokens</strong><br>
All spacing, sizes, and timing values defined as <code>--_select-*</code> variables</li>
<li><a href="https://developer.mozilla.org/docs/Web/CSS/CSS_logical_properties_and_values">Logical properties</a> <strong>throughout</strong><br>
<a href="https://developer.mozilla.org/docs/Web/CSS/inline-size"><code>inline-size</code></a>, <a href="https://developer.mozilla.org/docs/Web/CSS/block-size"><code>block-size</code></a>, <a href="https://developer.mozilla.org/docs/Web/CSS/inset-block-start"><code>inset-block-start</code></a>, <a href="https://developer.mozilla.org/docs/Web/CSS/margin-inline"><code>margin-inline</code></a>, <a href="https://developer.mozilla.org/docs/Web/CSS/padding-block"><code>padding-block</code></a> for full RTL support</li>
<li><a href="https://developer.mozilla.org/docs/Web/CSS/text-box"><code>text-box: trim-both cap alphabetic</code></a><br>
Precise vertical text alignment in legends</li>
<li><strong>Flexible option content</strong><br>
Options use <a href="https://developer.mozilla.org/docs/Web/CSS/CSS_flexible_box_layout">flexbox</a> with <a href="https://developer.mozilla.org/docs/Web/CSS/gap">gap</a> for icon + text layout</li>
<li><strong>Checkmark auto-positioned</strong><br>
<a href="https://developer.mozilla.org/docs/Web/CSS/order"><code>order: 2</code></a> and <a href="https://developer.mozilla.org/docs/Web/CSS/margin-inline-start"><code>margin-inline-start: auto</code></a> pushes checkmark to end of option</li>
</ul>
<p><baseline-status featureId="text-box"></baseline-status>
<baseline-status featureId="logical-properties"></baseline-status></p>
<h2>
Scrolling & Overflow
<a name="scrolling-&-overflow" href="#scrolling-&-overflow">#</a>
</h2>
<p><a href="https://developer.chrome.com/blog/css-scroll-state-queries">Scroll-state queries</a> dynamically detect when content is scrollable and when sticky headers become stuck. Custom scrollbar styling and smooth scrolling enhance the browsing experience within long option lists.</p>
<ul>
<li><a href="https://developer.mozilla.org/docs/Web/CSS/Guides/Conditional_rules/Container_scroll-state_queries">Container scroll-state queries</a><br>
<a href="https://developer.mozilla.org/docs/Web/CSS/container-type"><code>container-type: scroll-state</code></a> enables <a href="https://developer.mozilla.org/docs/Web/CSS/Guides/Conditional_rules/Container_scroll-state_queries#using_stuck_queries"><code>@container scroll-state(stuck)</code></a> detection</li>
<li><strong>Sticky legends</strong><br>
Group headers <a href="https://developer.mozilla.org/docs/Web/CSS/position#sticky">stick to top</a> when scrolling, with color change when stuck</li>
<li><strong>Custom scrollbar styling</strong><br>
<a href="https://developer.mozilla.org/docs/Web/CSS/scrollbar-width"><code>scrollbar-width: thin</code></a> and <a href="https://developer.mozilla.org/docs/Web/CSS/scrollbar-color"><code>scrollbar-color</code></a> with fade on hover-out</li>
<li><strong>Scroll containment</strong><br>
<a href="https://developer.mozilla.org/docs/Web/CSS/overscroll-behavior-block"><code>overscroll-behavior-block: contain</code></a> prevents scroll chaining</li>
<li><strong>Smooth scroll</strong><br>
<a href="https://developer.mozilla.org/docs/Web/CSS/scroll-behavior"><code>scroll-behavior: smooth</code></a> for programmatic scrolling (motion-safe)</li>
<li><strong>Dynamic scrollable detection</strong><br>
JS adds <code>.scrollable</code> class when content exceeds max height, CSS adjusts padding accordingly</li>
</ul>
<p><baseline-status featureId="container-scroll-state-queries"></baseline-status>
<baseline-status featureId="scrollbar-color"></baseline-status>
<baseline-status featureId="overscroll-behavior"></baseline-status>
<baseline-status featureId="scroll-behavior"></baseline-status></p>
<h2>
Customizable Select Features
<a name="customizable-select-features" href="#customizable-select-features">#</a>
</h2>
<p>New pseudo-elements provide surgical styling control over picker components, while superellipse corners deliver the modern aesthetic popularized by iOS. State selectors enable precise targeting of open and selected states.</p>
<ul>
<li><a href="https://developer.mozilla.org/docs/Web/CSS/::picker"><code>::picker(select)</code></a><br>
Pseudo-element for styling the dropdown picker container</li>
<li><a href="https://developer.mozilla.org/docs/Web/CSS/::picker-icon"><code>::picker-icon</code></a><br>
Pseudo-element for the dropdown chevron indicator</li>
<li><a href="https://developer.mozilla.org/docs/Web/CSS/::checkmark"><code>::checkmark</code></a><br>
Pseudo-element for the selected option indicator</li>
<li><a href="https://developer.chrome.com/blog/customize-select#selectedcontent"><code><selectedcontent></code></a><br>
Native element that mirrors selected option content in the button</li>
<li><strong>Squircle corners</strong><br>
<a href="https://developer.mozilla.org/docs/Web/CSS/corner-shape"><code>corner-shape: superellipse(1.25)</code></a> for modern rounded rectangles (with fallback)</li>
<li><a href="https://developer.mozilla.org/docs/Web/CSS/:open"><code>:open</code> pseudo-class</a><br>
State selector for when picker is visible</li>
<li><a href="https://developer.mozilla.org/docs/Web/CSS/:checked"><code>:checked</code></a> on options<br>
State selector for selected option</li>
</ul>
<p><baseline-status featureId="corner-shape"></baseline-status></p>
<h2>
Accessibility & UX
<a name="accessibility-&-ux" href="#accessibility-&-ux">#</a>
</h2>
<p>Accessibility is built-in with proper focus indicators, keyboard navigation, and semantic markup thanks to the web platform. Touch-friendly targets and overflow handling ensure the component works elegantly across all input methods.</p>
<ul>
<li><strong>Focus ring styling</strong><br>
Distinct <a href="https://developer.mozilla.org/docs/Web/CSS/:focus"><code>:focus</code></a> (subtle) and <a href="https://developer.mozilla.org/docs/Web/CSS/:focus-visible"><code>:focus-visible</code></a> (prominent)</li>
<li><strong>Disabled option support</strong><br>
<a href="https://developer.mozilla.org/docs/Web/HTML/Element/option#disabled"><code>option[disabled]</code></a> styling and hover prevention</li>
<li><strong>Text overflow handling</strong><br>
<a href="https://developer.mozilla.org/docs/Web/CSS/text-overflow"><code>text-overflow: ellipsis</code></a> in button display</li>
<li><strong>Minimum touch target</strong><br>
<code>36px</code> item height matches mobile guidelines</li>
<li><strong>Hidden option support</strong><br>
JS skips <a href="https://developer.mozilla.org/docs/Web/HTML/Global_attributes/hidden"><code>[hidden]</code></a> options in offset calculations</li>
<li><strong>RTL example included</strong><br>
Arabic language demo validates <a href="https://developer.mozilla.org/docs/Web/CSS/CSS_writing_modes">bidirectional layout</a></li>
</ul>
<h2>
Performance Optimizations
<a name="performance-optimizations" href="#performance-optimizations">#</a>
</h2>
<p>Careful optimization strategies keep animations smooth while minimizing layout thrash. Smart caching and compositor hints ensure the component performs well even on lower-end devices.</p>
<ul>
<li><a href="https://developer.mozilla.org/docs/Web/CSS/will-change"><code>will-change: scale</code></a><br>
Hints to browser for compositor optimization (also fixes text shift bug)</li>
<li><a href="https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WeakMap">WeakMap</a> <strong>offset cache</strong><br>
Avoids expensive DOM measurements on every interaction;<br>auto-garbage-collected</li>
<li><strong>Hardcoded layout constants</strong><br>
Avoids <a href="https://developer.mozilla.org/docs/Web/API/Window/getComputedStyle"><code>getComputedStyle</code></a> forced reflows during initialization. Tried dynamically computing these in a hidden instance, but couldn't get it as close as just "hardcoding / speficying them ahead of time" in a config</li>
<li><strong>Background clip</strong><br>
<a href="https://developer.mozilla.org/docs/Web/CSS/background-clip"><code>background-clip: padding-box</code></a> prevents background bleeding under border</li>
<li><strong>Parallel icon variants</strong><br>
Pre-defined black/white SVGs avoid runtime color manipulation</li>
</ul>
<h2>
Example Variations
<a name="example-variations" href="#example-variations">#</a>
</h2>
<p>The component architecture supports diverse use cases, from simple toggles to rich content pickers. Each variation demonstrates how the base patterns adapt to different content and interaction models.</p>
<ul>
<li><strong>Toggle with status indicators</strong><br>
Colored dots (green/red/gray) for on/off/disabled states</li>
<li><strong>Avatar select</strong><br>
Circular images with person names</li>
<li><strong>Multi-line options</strong><br>
Title + description layout for status picker</li>
<li><strong>Grouped options</strong><br>
Fieldsets with sticky legend headers</li>
<li><strong>Label above value</strong><br>
Color space picker with small label text above the selected value</li>
<li><strong>Flag emoji select</strong><br>
Country picker organized by region</li>
</ul>
<h2>
Try It
<a name="try-it" href="#try-it">#</a>
</h2>
<p>Experience the component in action on CodePen:</p>
<p>
<iframe
class="codepen-embed"
scrolling="no"
title="null"
src="https://codepen.io/argyleink/embed/preview/019c1f28-bbc2-7bac-ad4a-a7e41d3730f1?default-tab=result&editable=true&theme-id=43079"
frameborder="no"
loading="lazy"
allowtransparency="true"
allowfullscreen="true"
>
See the Pen <a href="https://codepen.io/argyleink/embed/preview/019c1f28-bbc2-7bac-ad4a-a7e41d3730f1"> by Adam Argyle (<a href="https://codepen.io/argyleink">@argyleink</a>)
on <a href="https://codepen.io">CodePen</a>.
</iframe>
</p>
<h2>
Wrapping Up
<a name="wrapping-up" href="#wrapping-up">#</a>
</h2>
<p>There's room for improvement, please fork and offer feedback!</p>
<p>I tried many variants of animation, trying to reduce some of the shift of the positioning, but never found something that worked across all the variants. Help me?! 🙏</p>
<p>This select component intends to showcase what's possible when we combine modern CSS features with thoughtful progressive enhancement. The <code>appearance: base-select</code> property provides the foundation, while anchor positioning, scroll-state queries, and entrance animations create a polished, accessible experience.</p>
<p>The real power lies in how these features compose together—each enhancement builds on platform primitives rather than fighting against them. The result is a component that's maintainable, accessible, and ready for the next generation of web interfaces.</p>
<p>Most importantly, this approach lets us create custom experiences without sacrificing the keyboard navigation, focus management, and screen reader support that come free with native elements.</p>
<p>For more, checkout <a href="/nice-details">nice details</a>!</p>
March Mad CSS Md - Adam Argylehttps://nerdy.dev/march-madCSS.md?utm_source=rss2026-02-02T04:02:44.000Z
<img style="display: none" src="https://nerdy.dev/media/march-madcss.jpg" alt="some title" height="496" width="1400" />
<p>This week I compete in <a href="https://syntax.fm/">Syntax</a>'s <a href="https://www.madcss.com/">March madCSS</a>; me against <strong>16 other badass devs</strong> in CSS & UI challenges 😅</p>
<p>Wish me luck 🤞</p>
Anchor Interpolated Morph (AIM) - Adam Argylehttps://nerdy.dev/anchor-interpolated-morphing?utm_source=rss2026-01-23T17:04:10.000Z
<img style="display: none" src="https://nerdy.dev/media/aim-ditto.jpg" alt="A ditto pokemon holding an anchor on an island" height="768" width="1408" />
<script type="module">
import "https://cdn.jsdelivr.net/npm/baseline-status";
</script>
<p>It's more natural when animations start near the trigger point, this CSS technique makes it easy. AIM animates from any element, and to any element. </p>
<p>Thanks to <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/anchor"><code>anchor()</code></a>, <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/anchor-size"><code>anchor-size()</code></a>, <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@starting-style"><code>@starting-style</code></a>, and <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/interpolate-size"><code>interpolate-size</code></a>. </p>
<p><baseline-status featureId="anchor-positioning"></baseline-status>
<baseline-status featureId="interpolate-size"></baseline-status>
<baseline-status featureId="starting-style"></baseline-status></p>
<p>All together they can create an <abbr title="AIM">anchor interpolated morph</abbr>: an <strong>interruptible</strong> contextual transition from where a user invoked it.</p>
<figure>
<p><video
src="/media/aim-popover.mp4"
width="1080"
height="810"
alt=""
preload="none"
poster="/media/aim-popover.avif"
controls
loop
muted
playsinline
allowFullScreen
/></p>
<figcaption>
<p>That's a <code>popover=hint</code> + <code>interestfor</code>, <br>no JS CSS transition. <a href="https://codepen.io/argyleink/pen/PwzmJjj">Try it</a></p>
</figcaption>
</figure>
<p>This is a <strong>very powerful</strong> and capable feature combination.</p>
<p>Similar results are visually possible with child elements using their parent as the anchor, but:</p>
<ol>
<li>What if they're not ancestors?</li>
<li>What if one is in the <a href="https://developer.mozilla.org/en-US/docs/Glossary/Top_layer"><code>:top-layer</code></a>?</li>
<li>What if you want semantic HTML and a healthy <a href="https://developer.mozilla.org/en-US/docs/Glossary/Accessibility_tree">accessibility OM</a>?</li>
</ol>
<p><abbr title="anchor interpolated morph">AIM</abbr> is the remedy.</p>
<h2>
FLIP
<a name="flip" href="#flip">#</a>
</h2>
<p>This technique is inspired by <a href="https://aerotwist.com">Paul Lewis's</a> coined <a href="https://aerotwist.com/blog/flip-your-animations/">animation technique called FLIP</a> (First, Last, Invert, Play).</p>
<p>The <a href="https://aerotwist.com/blog/flip-your-animations/#got-code%3F">gist of FLIP</a> is to <strong>use JS</strong> to <strong>calculate</strong> the difference in positions/size/whatever of an element, <strong>before and after</strong> some DOM change. </p>
<h2>
AIM
<a name="aim" href="#aim">#</a>
</h2>
<p>The gist of <abbr title="anchor interpolated morph">AIM</abbr> is to <strong>use CSS</strong> to <strong>access properties</strong> from where another element is (first), to where natural render is (last), and let <code>transition</code> handle invert and play.</p>
<p><strong>First:</strong> <code>anchor()</code>, <code>anchor-size()</code> & <code>@starting-style</code><br>
<strong>Last:</strong> <code>interpolate-size: allow-keywords</code> & <code>auto</code><br></p>
<p>The roles where JS was accessing properties, measuring natural sizes, and calculating the differences… are now handled by CSS. Definitely not 100% like FLIP 😅 but hopefully you see the connection. </p>
<p>Here's a less designed example (so there's less CSS to sift through) that's a <code><dialog></code> element:</p>
<figure>
<p><video
src="/media/aim-dialog.mp4"
width="1080"
height="608"
alt=""
preload="none"
poster="/media/aim-dialog.avif"
controls
loop
muted
playsinline
allowFullScreen
/></p>
<figcaption>
<p><a href="https://codepen.io/argyleink/pen/yyJaMaG">Try it</a>. You can uncomment a top-left position example too!</p>
</figcaption>
</figure>
<h3>
How it works
<a name="how-it-works" href="#how-it-works">#</a>
</h3>
<p><strong>1:</strong> The <code>anchor-name</code> property is used to identify the element that the element is morphing from. </p>
<pre><code class="language-css"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0"><code><span class="line"><span style="color:var(--shiki-token-string-expression)">button</span><span style="color:var(--shiki-foreground)"> {</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> anchor-name</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> --⚓︎-morph</span><span style="color:var(--shiki-foreground)">; </span></span>
<span class="line"><span style="color:var(--shiki-foreground)">}</span></span></code></pre>
</code></pre>
<p>Can you believe this one line of CSS enables <strong>any other element to observe the height, width, and position of this element</strong> 🤯</p>
<p><q class="info">Want more about that <a href="/css-emoji-convention">emoji naming convention</a>?</q></p>
<p><strong>2:</strong> Link up the dialog with that anchor.</p>
<pre><code class="language-css"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0"><code><span class="line"><span style="color:var(--shiki-token-string-expression)">dialog</span><span style="color:var(--shiki-foreground)"> {</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> position-anchor</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> --⚓︎-morph</span><span style="color:var(--shiki-foreground)">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--shiki-token-comment)"> /* adjust position as desired */</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> left</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> anchor(left</span><span style="color:var(--shiki-foreground)">);</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> top</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> anchor(top</span><span style="color:var(--shiki-foreground)">);</span></span>
<span class="line"><span style="color:var(--shiki-foreground)">}</span></span></code></pre>
</code></pre>
<p><strong>3:</strong> Enable transitioning to <code>auto</code> width/height.</p>
<pre><code class="language-css"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0"><code><span class="line"><span style="color:var(--shiki-token-string-expression)">dialog</span><span style="color:var(--shiki-foreground)"> {</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> interpolate-size</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> allow-keywords</span><span style="color:var(--shiki-foreground)">;</span></span>
<span class="line"><span style="color:var(--shiki-foreground)">}</span></span></code></pre>
</code></pre>
<p><strong>4:</strong> Enable transitions for all properties you want in the morph.</p>
<pre><code class="language-css"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0"><code><span class="line"><span style="color:var(--shiki-token-string-expression)">dialog</span><span style="color:var(--shiki-foreground)"> {</span></span>
<span class="line"><span style="color:var(--shiki-foreground)"> @</span><span style="color:var(--shiki-token-constant)">media</span><span style="color:var(--shiki-foreground)"> (</span><span style="color:var(--shiki-token-constant)">prefers-reduced-motion</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> no-preference</span><span style="color:var(--shiki-foreground)">) {</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> transition</span><span style="color:var(--shiki-token-keyword)">:</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> display </span><span style="color:var(--shiki-token-function)">var</span><span style="color:var(--shiki-token-constant)">(--_speed) allow-discrete</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> overlay </span><span style="color:var(--shiki-token-function)">var</span><span style="color:var(--shiki-token-constant)">(--_speed) allow-discrete</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> height </span><span style="color:var(--shiki-token-function)">var</span><span style="color:var(--shiki-token-constant)">(--_speed) </span><span style="color:var(--shiki-token-function)">var</span><span style="color:var(--shiki-token-constant)">(--ease-3)</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> width </span><span style="color:var(--shiki-token-function)">var</span><span style="color:var(--shiki-token-constant)">(--_speed) </span><span style="color:var(--shiki-token-function)">var</span><span style="color:var(--shiki-token-constant)">(--ease-3)</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> left </span><span style="color:var(--shiki-token-function)">var</span><span style="color:var(--shiki-token-constant)">(--_speed) </span><span style="color:var(--shiki-token-function)">var</span><span style="color:var(--shiki-token-constant)">(--ease-3)</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> top </span><span style="color:var(--shiki-token-function)">var</span><span style="color:var(--shiki-token-constant)">(--_speed) </span><span style="color:var(--shiki-token-function)">var</span><span style="color:var(--shiki-token-constant)">(--ease-3)</span><span style="color:var(--shiki-foreground)">;</span></span>
<span class="line"><span style="color:var(--shiki-foreground)"> }</span></span>
<span class="line"><span style="color:var(--shiki-foreground)">}</span></span></code></pre>
</code></pre>
<p><strong>5:</strong> Now we can use <code>@starting-style</code> to apply the styles from the anchor to the dialog before it opens. I call this "enter stage from" styles, and you can apply as many of the anchor values as you want. </p>
<p>I specify all corners here and setup a mask system to allow the dialog to transition to fit it's content, from the anchor size, without distorting the dialog contents. </p>
<pre><code class="language-css"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0"><code><span class="line"><span style="color:var(--shiki-token-string-expression)">dialog</span><span style="color:var(--shiki-foreground)"> {</span></span>
<span class="line"><span style="color:var(--shiki-foreground)"> @</span><span style="color:var(--shiki-token-constant)">starting-style</span><span style="color:var(--shiki-foreground)"> {</span></span>
<span class="line"><span style="color:var(--shiki-foreground)"> &[</span><span style="color:var(--shiki-token-constant)">open</span><span style="color:var(--shiki-foreground)">] {</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> left</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> anchor(left</span><span style="color:var(--shiki-foreground)">);</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> top</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> anchor(top</span><span style="color:var(--shiki-foreground)">);</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> right</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> anchor(right</span><span style="color:var(--shiki-foreground)">);</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> bottom</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> anchor(bottom</span><span style="color:var(--shiki-foreground)">);</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> width</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> anchor-size(width</span><span style="color:var(--shiki-foreground)">);</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> height</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> anchor-size(height</span><span style="color:var(--shiki-foreground)">);</span></span>
<span class="line"><span style="color:var(--shiki-foreground)"> }</span></span>
<span class="line"><span style="color:var(--shiki-foreground)"> }</span></span>
<span class="line"><span style="color:var(--shiki-foreground)">}</span></span></code></pre>
</code></pre>
<p>Notice <code>anchor-size()</code> for height and width, this combined with height and width <code>auto</code> and <code>interpolate-size: allow-keywords</code> will allow the dialog to transition to its natural size from the anchor with a nice reveal.</p>
<p>And for exit out on the <code>dialog</code>, it's similar as enter stage in my example but you could easily transition a different way or to a different element.</p>
<pre><code class="language-css"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0"><code><span class="line"><span style="color:var(--shiki-token-string-expression)">dialog</span><span style="color:var(--shiki-foreground)"> {</span></span>
<span class="line"><span style="color:var(--shiki-foreground)"> &</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)">not([open]</span><span style="color:var(--shiki-foreground)">) { </span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> left</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> anchor(left</span><span style="color:var(--shiki-foreground)">);</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> top</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> anchor(top</span><span style="color:var(--shiki-foreground)">);</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> right</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> anchor(right</span><span style="color:var(--shiki-foreground)">);</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> bottom</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> anchor(bottom</span><span style="color:var(--shiki-foreground)">);</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> width</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> anchor-size(width</span><span style="color:var(--shiki-foreground)">);</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> height</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> anchor-size(height</span><span style="color:var(--shiki-foreground)">);</span></span>
<span class="line"><span style="color:var(--shiki-foreground)"> }</span></span>
<span class="line"><span style="color:var(--shiki-foreground)">}</span></span></code></pre>
</code></pre>
<p><strong>That's it for a dialog!</strong></p>
<p>
<iframe
class="codepen-embed"
scrolling="no"
title="null"
src="https://codepen.io/argyleink/embed/preview/yyJaMaG?default-tab=result&editable=true&theme-id=43079"
frameborder="no"
loading="lazy"
allowtransparency="true"
allowfullscreen="true"
>
See the Pen <a href="https://codepen.io/argyleink/embed/preview/yyJaMaG"> by Adam Argyle (<a href="https://codepen.io/argyleink">@argyleink</a>)
on <a href="https://codepen.io">CodePen</a>.
</iframe>
</p>
<p>AIM can be used for:</p>
<ul>
<li>Dialogs</li>
<li><a href="https://codepen.io/argyleink/pen/PwzmJjj">Popovers</a></li>
<li>Tooltips</li>
<li>Page preview</li>
<li>Fullscreen takeover (go big!)</li>
<li>Quick actions</li>
<li>Disclosures</li>
<li>Submenus</li>
<li>Hovercards</li>
<li>Particles? add some CSS <code>random()</code></li>
<li>Other overlay components?</li>
</ul>
<p>Here's the popover from the video at the beginning. It's using all the above techniques, plus some extras, precise timings, easings and intentionality.</p>
<p>
<iframe
class="codepen-embed"
scrolling="no"
title="null"
src="https://codepen.io/argyleink/embed/preview/PwzmJjj?default-tab=result&editable=true&theme-id=43079"
frameborder="no"
loading="lazy"
allowtransparency="true"
allowfullscreen="true"
>
See the Pen <a href="https://codepen.io/argyleink/embed/preview/PwzmJjj"> by Adam Argyle (<a href="https://codepen.io/argyleink">@argyleink</a>)
on <a href="https://codepen.io">CodePen</a>.
</iframe>
</p>
<h2>
View Transitions
<a name="view-transitions" href="#view-transitions">#</a>
</h2>
<p>What about view transitions and morphing, don't they do that too? </p>
<p>Yep! They do, and can do some neat morphing tricks. Here's a view transitions demo I called "anything to anything".</p>
<p>
<iframe
class="codepen-embed"
scrolling="no"
title="null"
src="https://codepen.io/argyleink/embed/preview/qEWeYzd?default-tab=result&editable=true&theme-id=43079"
frameborder="no"
loading="lazy"
allowtransparency="true"
allowfullscreen="true"
>
See the Pen <a href="https://codepen.io/argyleink/embed/preview/qEWeYzd"> by Adam Argyle (<a href="https://codepen.io/argyleink">@argyleink</a>)
on <a href="https://codepen.io">CodePen</a>.
</iframe>
</p>
<p>But… that requires JS, is always a straight line and is not elegantly interruptible.</p>
<p><q>"always straight lines"… once you notice it you can't unsee it. View transitions can't curve or swing to a new destination.</q></p>
<p>Here's some slides I made using the <a href="https://github.com/argyleink/morphull">Astro Morphull repo I made</a> that use no JS view transitions so content morphs: <a href="https://cascadiajs-2025.netlify.app">https://cascadiajs-2025.netlify.app</a>.</p>
<h2>
Spatial Continuity
<a name="spatial-continuity" href="#spatial-continuity">#</a>
</h2>
<p>Traditional transitions often appear out of thin air or fade in from a generic center point. By leveraging <code>anchor()</code> and <code>@starting-style</code>, we can let CSS tell a story about where an element came from.</p>
<blockquote>
We're telling a story about where an element came from and why it’s there.
</blockquote>
<p>Go forth and make more natural transitions that feel spatially contextual. Stop letting your elements "teleport" into view, start letting them grow, stretch, and evolve directly from the components that triggered them. </p>
<p>Your users (and their muscle memory) will thank you. Speaking of, don't forget to gate the motion behind a <code>prefers-reduced-motion</code> media query (test the dialog and popovers).</p>
<p><q class="info">Hot tip: You could easily morph to a different element than the invoking element, <code>anchor()</code> doesnt care.</q></p>
<p><strong>Summary checklist for AIM:</strong></p>
<ol>
<li><strong>Set the Anchor:</strong><br><code>anchor-name</code> on the invoking element to create a spatial reference.</li>
<li><strong>Set "Enter From" and "Exit To" styles with <code>anchor()</code>:</strong><br>Use <code>@starting-style</code> to declare the entry and exit properties relative to the anchor.</li>
<li><strong>Transition:</strong><br>Orchestrate <code>transition</code> properties to allow the browser to interpolate the path, shapes and colors between the anchor point and the final layout.</li>
</ol>
People not people - Robin Rendlehttps://robinrendle.com/notes/people-not-people/2026-01-10T03:20:16.000Z<p>I can’t stop thinking about a series of blog posts by Eleanor Janega I read a couple of months ago. The first is called <em><a href="https://going-medieval.com/2017/05/26/theres-no-such-thing-as-the-dark-ages-but-ok/">There’s no such thing as the ‘Dark Ages’, but OK</a></em>, where she writes:</p>
<blockquote>
<p>The medieval period was not a period of stagnation, it was a time of progress.</p>
</blockquote>
<p>Wait, what? That’s not what I thought! This is novel information! Tell me more...</p>
<blockquote>
<p>The actual phrase ‘Dark Ages’ itself derives from the Latin <em>saeculum obscurum</em>, which Caesar Baronius – a cardinal and Church historian – came up with around 1602. He applied the term exclusively to the tenth and eleventh centuries. However, and very significantly in his use of the term, Baronius was not decrying a state of scientific malaise, or a particularly turbulent political period – he’s talking about a lack of sources surviving from that time. Indeed, Baronius sees the cut off point for the dark ages to be the Gregorian reforms of 1046, following which we see a massive increase in surviving documentation.</p>
</blockquote>
<p>So:</p>
<blockquote>
<p>Is there a time that historians use the term ‘Dark Ages’? Yeah, we do use it to talk about source survival rates. It’s not a term we use as a value judgment, however. We just mean that we don’t have a lot of evidence to go off of.</p>
</blockquote>
<p>This whole time I’ve been using “Dark Ages” and I didn’t realize that it’s a slur. I had sort of believed in the propaganda that when the glorious Roman Empire fell, all of Europe slipped into some dystopian nightmare where progress was halted for more than a thousand years out of sheer stubbornness. But wait...wasn’t the Roman Empire, ya know, an Empire? Are those typically...good?</p>
<p>Late last year Eleanor argued that this weird belief in the Roman Empire is <a href="https://going-medieval.com/2025/11/25/on-contrarian-history/">a contrarian history</a>, despite being popular. In the post, she replies to a reply-guy in her comments:</p>
<blockquote>
<p>It is contrarian to argue in defence of a system which steals from workers to give to a vanishingly small segment of a wealthy population. It is contrarian to refuse to learn about a subject and still think your opinions on it are valid. It is contrarian to ignore experts when they correct your profound and deep-seated misunderstanding.</p>
<p>However this man, and the legions of those who will go to bat for a violent and oppressive Empire, do not see themselves as contrarian because they are not engaging with actual history, they are engaging with a hegemonic historiography. They believe in the glory of the Roman Empire and its inherent good because they themselves currently live inside a violent empire that exists to funnel money to a wealthy elite. If you begin to question whether Rome was bad for the average person – if you start to ask why there was money for some people to have underfloor heating, while there wasn’t enough to adequately feed the population of Brittania – you may start asking questions about what is happening around you.</p>
</blockquote>
<p>Through these posts I felt like a sprawling network of rusty, spider-webbed cogs had begun to turn in my mind. Someone had planted these ideas in my head about the Roman Empire and the Dark Ages when I was a kid, but I’d never thought about the people who were ruled beneath and lived through it. Along the way I had internalized some propaganda and never questioned it, propaganda that made me think less of people—seeing them as sub-human dummies. That’s millions of people over thousands of years! And what? They were willing to let their cities go to hell and forgo all the wonders of Empire because of laziness?</p>
<p>This reminds me of a great lil book I read recently by Becky Chambers called <em>The Long Way to a Small, Angry Planet</em>. The novel is set hundreds of years into the future, humanity is a spacefaring civilization and works alongside dozens of alien species. The world isn’t perfect and there’s all sorts of petty conflicts and problems, but that’s not the word folks use—<em>alien</em>—to describe their compatriots in the stars. They call them <em>people</em>. Everyone is people.</p>
<p>There’s no us, there’s no them. There’s just people.</p>4 CSS Features Every Front-End Developer Should Know In 2026 - Adam Argylehttps://nerdy.dev/4-css-features-every-front-end-developer-should-know-in-2026?utm_source=rss2026-01-07T07:01:00.000Z
<img style="display: none" src="https://nerdy.dev/media/4-css-features-for-2026.jpg" alt="Punk graffiti and sticker style image saying 4 CSS features every front-end developer should know in 2026" height="768" width="1365" />
<script type="module">
import "https://cdn.jsdelivr.net/npm/baseline-status";
</script>
<p>2026; I think every front-end developer should know how to
query
<a href="#scroll-state-container-queries">scroll states</a>, trim
<a href="#trim-typographic-whitespace-with-text-box">typographic whitespace</a>, stagger with <a href="#sibling-index-and-sibling-count">sibling-index()</a>, and
use <a href="#advanced-attr()-with-type-checking">type safe <code>attr()</code></a>.</p>
<p><strong>This is only some of the CSS that shipped in 2025 you need to know.</strong></p>
<br>
<q class="info">
This post is a themed post!
<br>
Check out <a href="https://web.dev/articles/6-css-snippets-every-front-end-developer-should-know-in-2023">2023</a>, <a href="https://web.dev/articles/5-css-snippets-every-front-end-developer-should-know-in-2024">2024</a> and <a href="/6-css-snippets-every-front-end-developer-should-know-in-2025">2025</a>.
</q>
<h2>
sibling-index() and sibling-count()
<a name="sibling-index()-and-sibling-count()" href="#sibling-index()-and-sibling-count()">#</a>
</h2>
<p>Earlier this year these were just experiments, now they're available in stable Chrome and Safari!</p>
<p><baseline-status featureId="sibling-count"></baseline-status></p>
<p>They let you use an element's position relative to its siblings as values in calculations. For example, you can stagger elements with a transition delay based on their <code>sibling-index()</code>.</p>
<p>A nice trick is to subtract 1 so the <strong>first element starts immediately</strong>:</p>
<pre><code class="language-css"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0"><code><span class="line"><span style="color:var(--shiki-token-string-expression)">li</span><span style="color:var(--shiki-foreground)"> {</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> transition</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> opacity .3</span><span style="color:var(--shiki-token-keyword)">s</span><span style="color:var(--shiki-token-constant)"> ease</span><span style="color:var(--shiki-foreground)">;</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> transition-delay</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-function)"> calc</span><span style="color:var(--shiki-token-constant)">((sibling-index() - 1</span><span style="color:var(--shiki-foreground)">) * 100</span><span style="color:var(--shiki-token-constant)">ms</span><span style="color:var(--shiki-foreground)">);</span></span>
<span class="line"><span style="color:var(--shiki-foreground)">}</span></span></code></pre>
</code></pre>
<p>Stagger enter stage animations easily by combining with <code>@starting-style</code>!</p>
<pre><code class="language-css"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0"><code><span class="line"><span style="color:var(--shiki-token-string-expression)">li</span><span style="color:var(--shiki-foreground)"> {</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> transition</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> opacity .3</span><span style="color:var(--shiki-token-keyword)">s</span><span style="color:var(--shiki-token-constant)"> ease</span><span style="color:var(--shiki-foreground)">;</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> transition-delay</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-function)"> calc</span><span style="color:var(--shiki-token-constant)">((sibling-index() - 1</span><span style="color:var(--shiki-foreground)">) * 100</span><span style="color:var(--shiki-token-constant)">ms</span><span style="color:var(--shiki-foreground)">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--shiki-foreground)"> @</span><span style="color:var(--shiki-token-constant)">starting-style</span><span style="color:var(--shiki-foreground)"> {</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> opacity</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> 0</span><span style="color:var(--shiki-foreground)">;</span></span>
<span class="line"><span style="color:var(--shiki-foreground)"> }</span></span>
<span class="line"><span style="color:var(--shiki-foreground)">}</span></span></code></pre>
</code></pre>
<p>
<iframe
class="codepen-embed"
scrolling="no"
title="null"
src="https://codepen.io/argyleink/embed/preview/KwKXPYW?default-tab=result&editable=true&theme-id=43079"
frameborder="no"
loading="lazy"
allowtransparency="true"
allowfullscreen="true"
>
See the Pen <a href="https://codepen.io/argyleink/embed/preview/KwKXPYW"> by Adam Argyle (<a href="https://codepen.io/argyleink">@argyleink</a>)
on <a href="https://codepen.io">CodePen</a>.
</iframe>
</p>
<p>You can rotate hues in oklch, automatically number elements, and all sorts of fun things.</p>
<details class="nice-details">
<summary>
<svg aria-hidden viewBox="0 0 16 16" width="16" height="16">
<path d="M5 2 L12 8 L5 14" />
</svg>
<div>Resources</div>
</summary>
<ul>
<li><a href="/sibling-index">Earlier post I wrote</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/sibling-index">sibling-index()</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/sibling-count">sibling-count()</a></li>
<li><a href="https://utilitybend.com/blog/styling-siblings-with-CSS-has-never-been-easier.-Experimenting-with-sibling-count-and-sibling-index/">Brecht</a></li>
<li><a href="https://css-tricks.com/almanac/functions/s/sibling-index/">CSS Tricks</a></li>
</ul>
</details>
<h2>
@container scroll-state()
<a name="@container-scroll-state()" href="#@container-scroll-state()">#</a>
</h2>
<p>These features fit nicely into progressive enhancement, similar to <a href="https://scroll-driven-animations.style/">scroll driven animations</a>, as they're enhancements rather than requirements. IMO at least.</p>
<p><baseline-status featureId="container-scroll-state-queries"></baseline-status></p>
<p>Three states of a scroller are now queryable:<br><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Conditional_rules/Container_scroll-state_queries#using_stuck_queries">stuck</a>, <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Conditional_rules/Container_scroll-state_queries#using_snapped_queries">snapped</a>, <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@container#scrollable">scrollable</a> and scrolled.</p>
<p>To start, you need the element that's stuck, snapped, or scrollable to have <code>container-type: scroll-state</code>. Then, a child can query it with <code>@container scroll-state()</code>.</p>
<q class="warning">
An element cannot query itself, but its pseudo elements can!
</q>
<h3>
stuck
<a name="stuck" href="#stuck">#</a>
</h3>
<p>Perfectly know when <code>position: sticky</code> elements are stuck. </p>
<p><video
src="/media/scroll-state-stuck.mp4"
width="1920"
height="1080"
alt=""
preload="none"
poster="/media/scroll-state-stuck.avif"
controls
loop
muted
playsinline
allowFullScreen
/></p>
<pre><code class="language-css"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0"><code><span class="line"><span style="color:var(--shiki-token-comment)">/* when .outer-navbar is stuck */</span></span>
<span class="line"><span style="color:var(--shiki-token-keyword)">@container</span><span style="color:var(--shiki-foreground)"> scroll-state(stuck) {</span></span>
<span class="line"><span style="color:var(--shiki-token-function)"> .inner-navbar</span><span style="color:var(--shiki-foreground)"> {</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> box-shadow</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-function)"> var</span><span style="color:var(--shiki-token-constant)">(--shadow-3)</span><span style="color:var(--shiki-foreground)">;</span></span>
<span class="line"><span style="color:var(--shiki-foreground)"> }</span></span>
<span class="line"><span style="color:var(--shiki-foreground)">}</span></span></code></pre>
</code></pre>
<p>Use this to help users understand an element will now be overlaying their scroll content.</p>
<h3>
snapped
<a name="snapped" href="#snapped">#</a>
</h3>
<p>Perfectly know when scroll-snap alignment is active. </p>
<p><video
src="/media/scroll-state-snapped.mp4"
width="1920"
height="1080"
alt=""
preload="none"
poster="/media/scroll-state-snapped.avif"
controls
loop
muted
playsinline
allowFullScreen
/></p>
<pre><code class="language-css"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0"><code><span class="line"><span style="color:var(--shiki-token-comment)">/* when <li> parent is snapped */</span></span>
<span class="line"><span style="color:var(--shiki-token-keyword)">@container</span><span style="color:var(--shiki-foreground)"> scroll-state(snapped) {</span></span>
<span class="line"><span style="color:var(--shiki-token-function)"> .box</span><span style="color:var(--shiki-foreground)"> {</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> scale</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> 1.1</span><span style="color:var(--shiki-foreground)">;</span></span>
<span class="line"><span style="color:var(--shiki-foreground)"> }</span></span>
<span class="line"><span style="color:var(--shiki-foreground)">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--shiki-token-comment)">/* or not snapped! */</span></span>
<span class="line"><span style="color:var(--shiki-token-keyword)">@container</span><span style="color:var(--shiki-foreground)"> not scroll-state(snapped) {</span></span>
<span class="line"><span style="color:var(--shiki-token-function)"> .box</span><span style="color:var(--shiki-token-string-expression)"> figcaption</span><span style="color:var(--shiki-foreground)"> {</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> translate</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> 0 100</span><span style="color:var(--shiki-token-keyword)">%</span><span style="color:var(--shiki-foreground)">;</span></span>
<span class="line"><span style="color:var(--shiki-foreground)"> }</span></span>
<span class="line"><span style="color:var(--shiki-foreground)">}</span></span></code></pre>
</code></pre>
<p>Great for highlighting the item, or demoting every other item.</p>
<h3>
scrollable
<a name="scrollable" href="#scrollable">#</a>
</h3>
<p>Perfectly know when content overflows its container, and in which direction(s).</p>
<p><video
src="/media/scroll-state-scrollable.mp4"
width="1920"
height="1080"
alt=""
preload="none"
poster="/media/scroll-state-scrollable.avif"
controls
loop
muted
playsinline
allowFullScreen
/></p>
<pre><code class="language-css"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0"><code><span class="line"><span style="color:var(--shiki-token-keyword)">@container</span><span style="color:var(--shiki-foreground)"> scroll-state(scrollable) {</span></span>
<span class="line"><span style="color:var(--shiki-token-function)"> .scroll-hint</span><span style="color:var(--shiki-foreground)"> {</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> opacity</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> 1</span><span style="color:var(--shiki-foreground)">;</span></span>
<span class="line"><span style="color:var(--shiki-foreground)"> }</span></span>
<span class="line"><span style="color:var(--shiki-foreground)">}</span></span></code></pre>
</code></pre>
<p>Use this to toggle hints, scroll indicators, or adjust padding to signal more content.</p>
<h3>
scrolled
<a name="scrolled" href="#scrolled">#</a>
</h3>
<p>Perfectly know when content is scrolled in a direction.</p>
<pre><code class="language-css"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0"><code><span class="line"><span style="color:var(--shiki-token-keyword)">@container</span><span style="color:var(--shiki-foreground)"> scroll-state(scrolled: bottom) {</span></span>
<span class="line"><span style="color:var(--shiki-foreground)"> translate: 0 -100%;</span></span>
<span class="line"><span style="color:var(--shiki-foreground)">}</span></span></code></pre>
</code></pre>
<p>Use this for sticky headers or navbars that showy hidey based on scroll direction.</p>
<details class="nice-details">
<summary>
<svg aria-hidden viewBox="0 0 16 16" width="16" height="16">
<path d="M5 2 L12 8 L5 14" />
</svg>
<div>Resources</div>
</summary>
<ul>
<li><a href="https://developer.chrome.com/blog/css-scroll-state-queries">Chrome blog</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Conditional_rules/Container_scroll-state_queries">MDN</a></li>
<li><a href="https://una.im/scroll-state-scrolled">Una on scrolled</a></li>
<li><a href="https://www.bram.us/2025/10/22/solved-by-css-scroll-state-queries-hide-a-header-when-scrolling-down-show-it-again-when-scrolling-up/">Bramus on scrolled</a></li>
<li><a href="http://localhost:3030/nintendo-switch-homescreen-css-recreation">Me on remaking the Switch Homescreen</a></li>
<li><a href="http://localhost:3030/the-css-podcast-on-state-queries">The CSS Podcast</a></li>
</ul>
</details>
<h2>
text-box
<a name="text-box" href="#text-box">#</a>
</h2>
<p><code>text-box</code> lets you slice half-leading right off a text box!</p>
<p><baseline-status featureId="text-box"></baseline-status></p>
<p>Web font rendering <a href="https://matthiasott.com/notes/the-thing-with-leading-in-css">includes whitespace above and below glyphs</a> for "safe spacing", but sometimes you want pixel-perfect alignment to baselines or x-heights.</p>
<p><picture>
<source srcset="/media/text-box-trim-cap.avif" type="image/avif">
<source srcset="/media/text-box-trim-cap.webp" type="image/webp">
<img
loading="lazy"
src="/media/text-box-trim-cap.png"
alt=""
title="Title "
decoding="async"
width="1304"
height="396"
/>
</picture></p>
<p>Achieve the above image with:</p>
<pre><code class="language-css"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0"><code><span class="line"><span style="color:var(--shiki-token-string-expression)">h1</span><span style="color:var(--shiki-foreground)"> {</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> text-box</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> trim-both cap alphabetic</span><span style="color:var(--shiki-foreground)">;</span></span>
<span class="line"><span style="color:var(--shiki-foreground)">}</span></span></code></pre>
</code></pre>
<p>That one-liner trims spacing above cap height and below alphabetic baselines.</p>
<figure>
<p><video
src="/media/text-box-trimmed.mp4"
width="950"
height="534"
alt=""
preload="none"
poster="/media/text-box-trimmed.avif"
controls
loop
muted
playsinline
allowFullScreen
/></p>
<figcaption>
<p><a href="/notebook/text-box.html">Learn more in my interactive notebook</a></p>
</figcaption>
</figure>
<p>Perfect for type and grid alignment nerds. I think it'll <a href="/text-box-ftw">become the default</a>.</p>
<details class="nice-details">
<summary>
<svg aria-hidden viewBox="0 0 16 16" width="16" height="16">
<path d="M5 2 L12 8 L5 14" />
</svg>
<div>Resources</div>
</summary>
<ul>
<li><a href="https://developer.chrome.com/blog/css-text-box-trim">Chrome blog article</a></li>
<li><a href="/notebook/text-box.html">Interactive notebook</a></li>
<li><a href="https://codepen.io/collection/zxQBaL">Codepen collection</a></li>
<li><a href="http://localhost:3030/CSS4-and-CSS5-with-Una-and-SyntaxFM">Una and I on Syntax</a></li>
</ul>
</details>
<h2>
typed attr()
<a name="typed-attr()" href="#typed-attr()">#</a>
</h2>
<p>There's an advanced version of <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/attr"><code>attr()</code></a>; type-safe and more powerful.</p>
<p><baseline-status featureId="attr"></baseline-status></p>
<p>It allows <strong>using HTML attributes directly in CSS</strong> with type checking and fallbacks. </p>
<p><strong>Pass colors:</strong></p>
<pre><code class="language-html"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0"><code><span class="line"><span style="color:var(--shiki-foreground)"><</span><span style="color:var(--shiki-token-string-expression)">div</span><span style="color:var(--shiki-token-function)"> data-bg</span><span style="color:var(--shiki-foreground)">=</span><span style="color:var(--shiki-token-string-expression)">"white"</span><span style="color:var(--shiki-token-function)"> data-fg</span><span style="color:var(--shiki-foreground)">=</span><span style="color:var(--shiki-token-string-expression)">"deeppink"</span><span style="color:var(--shiki-foreground)">></</span><span style="color:var(--shiki-token-string-expression)">div</span><span style="color:var(--shiki-foreground)">></span></span></code></pre>
</code></pre>
<pre><code class="language-css"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0"><code><span class="line"><span style="color:var(--shiki-token-function)">.theme</span><span style="color:var(--shiki-foreground)"> {</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> background</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-function)"> attr</span><span style="color:var(--shiki-token-constant)">(data-bg color</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-token-constant)"> black)</span><span style="color:var(--shiki-foreground)">;</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> color</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-function)"> attr</span><span style="color:var(--shiki-token-constant)">(data-fg color</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-token-constant)"> white)</span><span style="color:var(--shiki-foreground)">;</span></span>
<span class="line"><span style="color:var(--shiki-foreground)">}</span></span></code></pre>
</code></pre>
<p><strong>Pass numbers:</strong></p>
<pre><code class="language-html"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0"><code><span class="line"><span style="color:var(--shiki-foreground)"><</span><span style="color:var(--shiki-token-string-expression)">div</span><span style="color:var(--shiki-token-function)"> class</span><span style="color:var(--shiki-foreground)">=</span><span style="color:var(--shiki-token-string-expression)">"grid"</span><span style="color:var(--shiki-token-function)"> data-columns</span><span style="color:var(--shiki-foreground)">=</span><span style="color:var(--shiki-token-string-expression)">"3"</span><span style="color:var(--shiki-foreground)">>…</</span><span style="color:var(--shiki-token-string-expression)">div</span><span style="color:var(--shiki-foreground)">></span></span></code></pre>
</code></pre>
<pre><code class="language-css"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0"><code><span class="line"><span style="color:var(--shiki-token-function)">.grid</span><span style="color:var(--shiki-foreground)"> {</span></span>
<span class="line"><span style="color:var(--shiki-foreground)"> --_columns</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-function)"> attr</span><span style="color:var(--shiki-token-constant)">(data-columns number</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-token-constant)"> 3)</span><span style="color:var(--shiki-foreground)">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> display</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> grid</span><span style="color:var(--shiki-foreground)">;</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> grid-template-columns</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-function)"> repeat</span><span style="color:var(--shiki-token-constant)">(</span><span style="color:var(--shiki-token-function)">var</span><span style="color:var(--shiki-token-constant)">(--_columns)</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-token-constant)"> 1</span><span style="color:var(--shiki-token-keyword)">fr</span><span style="color:var(--shiki-token-constant)">)</span><span style="color:var(--shiki-foreground)">;</span></span>
<span class="line"><span style="color:var(--shiki-foreground)">}</span></span></code></pre>
</code></pre>
<p>This creates a powerful bridge between HTML and CSS. </p>
<p>Here's a scroll snap example where CSS basically controls the enums and HTML must pass valid values to get the desired snap results:</p>
<pre><code class="language-html"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0"><code><span class="line"><span style="color:var(--shiki-foreground)"><</span><span style="color:var(--shiki-token-string-expression)">li</span><span style="color:var(--shiki-token-function)"> scroll-snap</span><span style="color:var(--shiki-foreground)">=</span><span style="color:var(--shiki-token-string-expression)">"start"</span><span style="color:var(--shiki-foreground)">></</span><span style="color:var(--shiki-token-string-expression)">li</span><span style="color:var(--shiki-foreground)">></span></span>
<span class="line"><span style="color:var(--shiki-foreground)"><</span><span style="color:var(--shiki-token-string-expression)">li</span><span style="color:var(--shiki-token-function)"> scroll-snap</span><span style="color:var(--shiki-foreground)">=</span><span style="color:var(--shiki-token-string-expression)">"center"</span><span style="color:var(--shiki-foreground)">></</span><span style="color:var(--shiki-token-string-expression)">li</span><span style="color:var(--shiki-foreground)">></span></span>
<span class="line"><span style="color:var(--shiki-foreground)"><</span><span style="color:var(--shiki-token-string-expression)">li</span><span style="color:var(--shiki-token-function)"> scroll-snap</span><span style="color:var(--shiki-foreground)">=</span><span style="color:var(--shiki-token-string-expression)">"end"</span><span style="color:var(--shiki-foreground)">></</span><span style="color:var(--shiki-token-string-expression)">li</span><span style="color:var(--shiki-foreground)">></span></span>
<span class="line"><span style="color:var(--shiki-foreground)"><</span><span style="color:var(--shiki-token-string-expression)">li</span><span style="color:var(--shiki-token-function)"> scroll-snap</span><span style="color:var(--shiki-foreground)">=</span><span style="color:var(--shiki-token-string-expression)">"nothing"</span><span style="color:var(--shiki-foreground)">></</span><span style="color:var(--shiki-token-string-expression)">li</span><span style="color:var(--shiki-foreground)">></span></span></code></pre>
</code></pre>
<pre><code class="language-css"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0"><code><span class="line"><span style="color:var(--shiki-foreground)">[</span><span style="color:var(--shiki-token-function)">scroll-snap</span><span style="color:var(--shiki-foreground)">] {</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> scroll-snap-align</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-function)"> attr</span><span style="color:var(--shiki-token-constant)">(scroll-snap type(start | center | end)</span><span style="color:var(--shiki-foreground)">);</span></span>
<span class="line"><span style="color:var(--shiki-foreground)">}</span></span></code></pre>
</code></pre>
<p>The <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/type"><code>type()</code></a> function validates attribute values against allowed keywords. Invalid values fall back gracefully.</p>
<p><a href="https://codepen.io/argyleink/pen/qEWyZgx">Try it on Codepen</a></p>
<details class="nice-details">
<summary>
<svg aria-hidden viewBox="0 0 16 16" width="16" height="16">
<path d="M5 2 L12 8 L5 14" />
</svg>
<div>Resources</div>
</summary>
<ul>
<li><a href="/advanced-attr">My post in 2025 about it</a></li>
<li><a href="https://codepen.io/argyleink/pen/qEWyZgx">Try it on CodePen</a></li>
<li><a href="https://developer.chrome.com/blog/advanced-attr">Chrome Developers article</a></li>
<li><a href="https://css-tip.com/value-input/">Temani Afif's post</a></li>
<li><a href="https://codepen.io/t_afif/pen/MWdmZPL">Temani slider tooltips</a></li>
</ul>
</details>
<p><strong>CSS rules.</strong></p>
<p>Now let's laugh at this hilarious obvious AI generated nano banana image I made about us having our cake and eating it too.</p>
<p><picture>
<source srcset="/media/punk-eats-css-cake.avif" type="image/avif">
<source srcset="/media/punk-eats-css-cake.webp" type="image/webp">
<img
loading="lazy"
src="/media/punk-eats-css-cake.jpg"
alt=""
title="Title "
decoding="async"
width="1200"
height="1200"
/>
</picture></p>
<p>Still can't do hands lol</p>
v19 - Robin Rendlehttps://robinrendle.com/notes/v19/2026-01-06T04:27:44.000Z<p>Watch out: a new website appears! Yesterday I published the ninenteeth official version of this website and this time round it’s inspired by the work I’ve done on developer tools these last few years. It’s my own personal IDE: you can navigate files, open up folders, and see how I’d organize an operating system.</p>
<p>My favorite bit of the new update is what I’ve done with Photos. For a good long while I’ve felt that I need a somewhat-private space to develop my photo skills, but I don’t want to be tied to anyone else’s platform. I don’t want to think about Likes, man. So for now I’ve uploaded just a few things to test the layout system out.</p>
<p>This redesign also reminded me just how powerful the web is now. Perhaps not a shocker to a lot of folks, but even humble tools like the details element and CSS Grid can be used in ways that are genuinely breathtaking. What would have taken me hours to figure out a few years ago was just a few click-clack lines of CSS yesterday.</p>
<p>Next steps? Tweak the 11ty image plugin for sure. There’s some problems I’ve introduced with it, even though the plugin is genuinely magic and ultimately will improve everyone’s experience of clicking around. Maybe I could add more typography and layout options, too? Oh and keyboard shortcuts. Oh, oh, oh and add a feed for photos. And maybe a books section? And and and…yes yes yes...</p>Games Of December 2025 - Adam Argylehttps://nerdy.dev/games-of-december-2025?utm_source=rss2025-12-27T06:12:36.000Z
<img style="display: none" src="https://nerdy.dev/media/games-of-december-2025.jpg" alt="undefined" height="1024" width="1024" />
<p>Some people read about the hero, some people play as the hero. If you don't play video games, you have 2 choices:</p>
<ol>
<li>Suffer through my ramblings</li>
<li>Realize you're missing out and give one of these a try</li>
</ol>
<p>Here's what we've been playing:</p>
<h2>
Skate Story
<a name="skate-story" href="#skate-story">#</a>
</h2>
<p>You're a glass skateboarder stuck in the Underworld who has to skate to (get out?). A rad soundtrack, interesting visuals, and a weird story. </p>
<iframe width="800" height="450" style="aspect-ratio: 16/9" src="https://www.youtube.com/embed/M79A27qQXAs?si=4Q1JfMulKIBkjL59" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
<p><a href="https://store.steampowered.com/app/1263240/Skate_Story/">Steam</a></p>
<h2>
Wheel World
<a name="wheel-world" href="#wheel-world">#</a>
</h2>
<p>Ride a fixie through cell shaded worlds to a chill beat. Race others, discover stuff, collect things, and just take in the atmosphere. </p>
<iframe width="800" height="450" style="aspect-ratio: 16/9" src="https://www.youtube.com/embed/JmL15GGO1SA?si=zsSsOaV6JfOoZrEZ" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
<p><a href="https://steamcommunity.com/app/1497460">Steam</a></p>
<h2>
Lego Voyagers
<a name="lego-voyagers" href="#lego-voyagers">#</a>
</h2>
<p>We loved <a href="https://www.youtube.com/watch?v=piiBgt3BSZI">Lego Builder's Journey</a>, and this one has local co-op (our favorite). Play as 2 little Lego's who admire rocket ships and want to build one. Use your building skills and unravel the visually engaging and beautifully lit story.</p>
<iframe width="800" height="450" style="aspect-ratio: 16/9" src="https://www.youtube.com/embed/ZBMOgbV7FTc?si=Tas_jqCG37X7r9Sa" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
<p><a href="https://store.steampowered.com/app/1544360/LEGO_Builders_Journey/">Steam</a></p>
<h2>
Bubble Bobble Sugar Dungeons
<a name="bubble-bobble-sugar-dungeons" href="#bubble-bobble-sugar-dungeons">#</a>
</h2>
<p>We love retro games around here and got super pumped when we saw they were making a 2025 version of Bubble Bobble. BUT, we played Sugar Dungeons a few times and were disappointed it was single player. While it's fine… it's lacking some soul. It's just a bit bland and doesn't really tickle the nostalgia bug like we hoped.</p>
<iframe width="800" height="450" style="aspect-ratio: 16/9" src="https://www.youtube.com/embed/CVB1ma8CzMw?si=vJ2uiNSlJnjQnNp9" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
<p><a href="https://store.steampowered.com/app/2937770/Bubble_Bobble_Sugar_Dungeons/">Steam</a></p>
<h2>
Dorf Romantik (the Boardgame)
<a name="dorf-romantik-(the-boardgame)" href="#dorf-romantik-(the-boardgame)">#</a>
</h2>
<p>This house has played a lot of <a href="https://www.youtube.com/watch?v=nBFhVgMy_3s">Dorf Romantik</a> the video game. Everyone, young to old, feels satisfied with the cozy yet solitaire-like experience you get from this game. The board game does an AMAZING job of capturing the charm of the video game while combining it with the limitations of a board game.</p>
<p>The board game is beautifully done, creating a compelling campaign arc, unlocks, and other exciting elements.</p>
<iframe width="800" height="450" style="aspect-ratio: 16/9" src="https://www.youtube.com/embed/bemz_fhAhtE?si=GRMwyZqvmBN6HG57" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
<p><a href="https://www.amazon.com/Pegasus-Spiele-51240E-Dorfromantik-Boardgame/dp/B0BQ3W9GRL">Board Game</a></p>
<h2>
Ball Pit
<a name="ball-pit" href="#ball-pit">#</a>
</h2>
<p>Whelp, we got super addicted to this one. Very arcade-like, super fun roguelike-style gameplay, and a fun gameloop. Super fun 5-20 minute sessions.</p>
<iframe width="800" height="450" style="aspect-ratio: 16/9" src="https://www.youtube.com/embed/vsCA0zhV_Nw?si=FDVMJApoQIGMtWir" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
<p><a href="https://store.steampowered.com/app/2062430/BALL_x_PIT/">Steam</a></p>
<h2>
Just Shapes and Beats
<a name="just-shapes-and-beats" href="#just-shapes-and-beats">#</a>
</h2>
<p>Connect up to 4 controllers and dodge the shapes as they react to the beat. Each level is a full on custom visual beat experience to a track from a rad retro-bit playlist. All you gotta do is dodge stuff, you can move a stick right?</p>
<iframe width="800" height="450" style="aspect-ratio: 16/9" src="https://www.youtube.com/embed/2VpqK2E8NRY?si=mxgwoTFOAb8Gln47" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
<p><a href="https://store.steampowered.com/app/531510/Just_Shapes__Beats/">Steam</a></p>
<h2>
Curse Of The Dead Gods
<a name="curse-of-the-dead-gods" href="#curse-of-the-dead-gods">#</a>
</h2>
<p>Mix a little Hades with FTL and you've got yourself this roguelite. See how far you can get, do your best not to get hit, get some synergy in a sweet build, hack and slash your way to freedom.</p>
<iframe width="800" height="450" style="aspect-ratio: 16/9" src="https://www.youtube.com/embed/y2-T3FbzvA0?si=2yurC6apGoCE1hvT" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
<p><a href="https://store.steampowered.com/app/1123770/Curse_of_the_Dead_Gods/">Steam</a></p>
Bring The Color - Adam Argylehttps://nerdy.dev/bring-the-color?utm_source=rss2025-12-16T07:05:00.000Z
<video style="display: none" src="/media/gradient-style-stops.mp4" alt="some title" height="708" width="708" />
<p>Bring the color in at <a href="https://gradient.style">gradient.style</a></p>
So Many Websites - Robin Rendlehttps://robinrendle.com/notes/So-Many-Websites/2025-12-14T18:06:50.000Z<p>I’ve been thinking about the web and how it’ll change with the increasingly…uh…worrying decline of search. It feels like the order of their results can no longer be trusted, the web no longer reliably scraped. Instead, this essential tool we’ve relied on for more than two decades has halted all progress, reversed course. In a heart beat I would pick the search engine of 2014 over the one I use today.</p>
<p>So there’s a lot of anxiety out there about how this fundamentally changes the economics of the web. Folks are worried that without search functioning properly then the web itself is fading away, becoming irrelevant, or already even broken beyond repair. But I disagree! The premise at the heart of these conversations is that websites deserve and require a mass readership to be important or worthwhile. But what if they don’t?</p>
<p>Sure, a lot of businesses were built around the economics of search and ten billion people looking at a block of text imprisoned by a swamp of ads for cars and questionable dietary supplements. Although, that doesn’t mean the web itself requires that specific business model to thrive, to still be worthwhile, to still be important. My hot take here is that ugly business models created ugly websites, but I digress.</p>
<p>This idea has been rolling around my head since I started reading <em>So Many Books</em> by Gabriel Zaid where he writes: “It is worth asking, in the first place, whether all books need or deserve a mass readership.” <em>Yikes! Whoa!</em> This line hit me like a ton of bricks because most business models on the web have assumed a mass readership is out there—but what if it isn’t? What if the web, and most websites besides the few obvious exceptions, were instead more like book publishing? A few thousand dedicated readers out there, at best.</p>
<p>I can feel the disappointment in the conversations I’ve been listening to, as if the whole point of the web is to exist at bewildering, eye-watering scale. A million clicks is so lame, man, when compared to a billion. Heck, your website only gets fifty trillion clicks a day? Is there a point to publishing that dinky website if it’s so small?</p>
<p>But perhaps the death of search is <em>good</em> for the future of the web. Perhaps websites can be free of dumb rankings and junky ads that are designed to make fractions of a penny at a time. Perhaps the web needs to be released from the burden of this business model. Perhaps mass readership isn’t possible for the vast majority of websites and was never really sustainable in the first place.</p>
<p>And perhaps we should let our websites be small and private things once again.</p>CSS Text Grow - Adam Argylehttps://nerdy.dev/css-text-grow?utm_source=rss2025-12-05T06:34:16.000Z
<video style="display: none" src="/media/text-grow.mp4" alt="some title" height="1134" width="1512 " />
<p>Fit width text in 1 line of CSS:</p>
<pre><code class="language-css"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0"><code><span class="line"><span style="color:var(--shiki-token-string-expression)">h1</span><span style="color:var(--shiki-foreground)"> {</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> text-grow</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> per-line scale</span><span style="color:var(--shiki-foreground)">;</span></span>
<span class="line"><span style="color:var(--shiki-foreground)">}</span></span></code></pre>
</code></pre>
<p><small>Prototype available in Canary 165</small></p>
<p><a href="https://codepen.io/argyleink/pen/gbrBVJG">Codepen</a> · <a href="https://github.com/w3c/csswg-drafts/issues/2528">CSSWG</a> · <a href="https://github.com/explainers-by-googlers/css-fit-text/blob/main/README.md">Explainer</a> · <a href="https://kizu.dev/fit-to-width/">Prior Art</a></p>
On The Ux Show - Adam Argylehttps://nerdy.dev/on-the-UX-show?utm_source=rss2025-12-02T00:29:57.000Z
<img style="display: none" src="https://nerdy.dev/media/the-ux-show-interview.jpg" alt="All three of us in a video call" height="720" width="1280" />
<p>Was on The UX Show with <a href="https://yashrajbharti.github.io/Design-Portfolio/">Yash Raj</a> and <a href="https://www.linkedin.com/in/victorsanchezbelmar/?locale=en_US">Victor Sanchez</a> 🤓</p>
<p><a href="https://www.youtube.com/watch?v=6fGBgUAl7Hw">Watch it on YouTube</a></p>
Using CSS to fix the irradiation illusion - Adam Argylehttps://nerdy.dev/adjust-perceived-typepace-weight-for-dark-mode-without-layout-shift?utm_source=rss2025-11-29T21:47:37.000Z
<img style="display: none" src="https://nerdy.dev/media/typography-illusion.jpg" alt="The letter A is shown on white and on dark, as an exaggerated example of the irradiation illusion" height="1072" width="1920" />
<p>Ever noticed how white text on a black background <strong>looks thicker</strong> than black text on a white background, even though the weights are the same? </p>
<p>This post teaches you how to account for this and adjust perceived font weight for dark mode <strong>without layout shift</strong>, with variable fonts and the <a href="https://fonts.google.com/knowledge/glossary/grade_axis"><code>GRAD</code></a> axis.</p>
<p><q>g…RAD! 👈😎👈</q></p>
<h2>
Feel out the problem space
<a name="feel-out-the-problem-space" href="#feel-out-the-problem-space">#</a>
</h2>
<p>Test your eyes and feel the problem space with the following demo <br>(def <a href="https://codepen.io/argyleink/full/ZYWavgP">open it up larger</a>):</p>
<p>
<iframe
class="codepen-embed"
scrolling="no"
title="null"
src="https://codepen.io/argyleink/embed/preview/ZYWavgP?default-tab=result&editable=true&theme-id=43079"
frameborder="no"
loading="lazy"
allowtransparency="true"
allowfullscreen="true"
>
See the Pen <a href="https://codepen.io/argyleink/embed/preview/ZYWavgP"> by Adam Argyle (<a href="https://codepen.io/argyleink">@argyleink</a>)
on <a href="https://codepen.io">CodePen</a>.
</iframe>
</p>
<p><strong>To test:</strong> <br>Flip between light and dark. Look closely at font weights in the "un-adjusted" and "adjusted" example content. </p>
<p><strong>The goal:</strong> <br>The visual weight to not perceptively change when switching between light and dark. This means, when in dark mode, using a negative value for <code>GRAD</code> to reduce the perceived thickness.</p>
<p><strong>Some play:</strong> <br>In dark mode, use the slider to adjust the <code>GRAD</code> axis to see how it changes the perceived weight of the font in the adjusted example. Feel free to fine tune the slider to find the perfect balance for your eyes, flipping back and forth, adjust the slider until you see no weight change when flipping themes.</p>
<p>I think it's easier to notice the difference in the paragraph text than the heading text, but you find which is easier to notice for yourself. </p>
<p><strong>It's subtle:</strong> <br>Don't expect this to jump out at you, but <strong>you'll struggle to unsee once you see</strong>.</p>
<h2>
Irradiation illusion
<a name="irradiation-illusion" href="#irradiation-illusion">#</a>
</h2>
<p><a href="https://pmc.ncbi.nlm.nih.gov/articles/PMC2041963/">Humans perceive white on black vs black on white differently.</a> We learned it from the world, the way shadows and light have given us biases and expectations. <a href="https://evidentscientific.com/en/microscope-resource/knowledge-hub/lightandcolor/humanvisionintro">Our squishy human eye</a> has been through some things you know?</p>
<p><img
loading="lazy"
src="https://upload.wikimedia.org/wikipedia/commons/4/4d/Black_and_white_squares.jpg"
alt="Irradiation illusion"
title="null"
decoding="async"
undefined
undefined
/></p>
<p>Read more about <a href="https://en.wikipedia.org/wiki/Irradiation_illusion">irradiation illusion</a>.</p>
<p>This impacts typography in dark mode, as the perceived weight of a font changes in dark mode, without intention from the CSS authors. The light text appears thicker than the dark text counterpart, even though the font weight is the same.</p>
<p><strong>CSS can account for this illusion</strong> 🤓</p>
<h2>
GRAD in variable fonts to the rescue
<a name="grad-in-variable-fonts-to-the-rescue" href="#grad-in-variable-fonts-to-the-rescue">#</a>
</h2>
<p>Before variable fonts, font weight was the only way to adjust the perceived weight of a font, but this changes the glyph size. This can cause accidental layout shift, aka a janky experience. </p>
<p><a href="https://fonts.google.com/knowledge/glossary/grade_axis"><code>GRAD</code></a> stands for "grade", and it's a variable font axis that allows us to adjust the perceived weight of a font without changing the glyph size.</p>
<h3>
Fixing bold link hovers
<a name="fixing-bold-link-hovers" href="#fixing-bold-link-hovers">#</a>
</h3>
<p>Here, you'll recognize this classic issue. Hover the links in the below demo to see a small preview of the type of layout shift that can occur. </p>
<p>
<iframe
class="codepen-embed"
scrolling="no"
title="null"
src="https://codepen.io/argyleink/embed/preview/qEZdOPE?default-tab=result&editable=true&theme-id=43079"
frameborder="no"
loading="lazy"
allowtransparency="true"
allowfullscreen="true"
>
See the Pen <a href="https://codepen.io/argyleink/embed/preview/qEZdOPE"> by Adam Argyle (<a href="https://codepen.io/argyleink">@argyleink</a>)
on <a href="https://codepen.io">CodePen</a>.
</iframe>
</p>
<p><strong>With variable fonts, we can use the <code>GRAD</code> axis to adjust the perceived weight of a font without changing the glyph size.</strong></p>
<p>This allows us to adjust the perceived weight of a font without causing layout shift. That's what the "fixed" example is using.</p>
<h2>
One step further
<a name="one-step-further" href="#one-step-further">#</a>
</h2>
<p>So far we've only adjusted the perceived weight of a font in dark mode with a negative value for <code>GRAD</code>, and for links being hovered that use a positive value for <code>GRAD</code>, but we can go even further:</p>
<p>
<iframe
class="codepen-embed"
scrolling="no"
title="null"
src="https://codepen.io/argyleink/embed/preview/mdQrqvj?default-tab=result&editable=true&theme-id=43079"
frameborder="no"
loading="lazy"
allowtransparency="true"
allowfullscreen="true"
>
See the Pen <a href="https://codepen.io/argyleink/embed/preview/mdQrqvj"> by Adam Argyle (<a href="https://codepen.io/argyleink">@argyleink</a>)
on <a href="https://codepen.io">CodePen</a>.
</iframe>
</p>
<p>This allows us to adjust the perceived weight of a font based on the <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@media/prefers-contrast">users contrast preferences</a>. </p>
<p>If the user prefers more contrast, without layout shift, we can use a positive value for <code>GRAD</code> to increase the perceived thickness. In dark mode, we can use a negative value for <code>GRAD</code> to reduce the perceived thickness while still maintaining their preference.</p>
<pre><code class="language-css"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0"><code><span class="line"><span style="color:var(--shiki-token-string-expression)">body</span><span style="color:var(--shiki-foreground)"> {</span></span>
<span class="line"><span style="color:var(--shiki-foreground)"> --GRAD</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> 0</span><span style="color:var(--shiki-foreground)">;</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> font-variation-settings</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-string-expression)"> "GRAD"</span><span style="color:var(--shiki-token-function)"> var</span><span style="color:var(--shiki-token-constant)">(--GRAD)</span><span style="color:var(--shiki-foreground)">;</span></span>
<span class="line"><span style="color:var(--shiki-foreground)">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--shiki-token-keyword)">@media</span><span style="color:var(--shiki-foreground)"> (prefers-contrast</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-foreground)"> more) { </span><span style="color:var(--shiki-token-string-expression)">body</span><span style="color:var(--shiki-foreground)"> { --GRAD</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> 700</span><span style="color:var(--shiki-foreground)"> } }</span></span>
<span class="line"><span style="color:var(--shiki-token-keyword)">@media</span><span style="color:var(--shiki-foreground)"> (prefers-contrast</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-foreground)"> less) { </span><span style="color:var(--shiki-token-string-expression)">body</span><span style="color:var(--shiki-foreground)"> { --GRAD</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> -50</span><span style="color:var(--shiki-foreground)"> } }</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--shiki-token-keyword)">@media</span><span style="color:var(--shiki-foreground)"> (prefers-color-scheme</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-foreground)"> dark) { </span><span style="color:var(--shiki-token-string-expression)">body</span><span style="color:var(--shiki-foreground)"> { --GRAD</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> -50</span><span style="color:var(--shiki-foreground)"> } }</span></span>
<span class="line"><span style="color:var(--shiki-token-keyword)">@media</span><span style="color:var(--shiki-foreground)"> (prefers-color-scheme</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-foreground)"> dark) </span><span style="color:var(--shiki-token-keyword)">and</span><span style="color:var(--shiki-foreground)"> (prefers-contrast</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-foreground)"> more) { </span><span style="color:var(--shiki-token-string-expression)">body</span><span style="color:var(--shiki-foreground)"> { --GRAD</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> 150</span><span style="color:var(--shiki-foreground)"> } }</span></span>
<span class="line"><span style="color:var(--shiki-token-keyword)">@media</span><span style="color:var(--shiki-foreground)"> (prefers-color-scheme</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-foreground)"> dark) </span><span style="color:var(--shiki-token-keyword)">and</span><span style="color:var(--shiki-foreground)"> (prefers-contrast</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-foreground)"> less) { </span><span style="color:var(--shiki-token-string-expression)">body</span><span style="color:var(--shiki-foreground)"> { --GRAD</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> -150</span><span style="color:var(--shiki-foreground)"> } }</span></span></code></pre>
</code></pre>
<p>Users will never ask for something like this. They'll just be able to easily read the content in your design in a way that feels natural.</p>
<p>Time to check if your favorite font has a <code>GRAD</code> axis.</p>
Midi Meets CSS At Beyond Tellerrand - Adam Argylehttps://nerdy.dev/midi-meets-css-at-beyond-tellerrand?utm_source=rss2025-11-27T07:16:43.000Z
<img style="display: none" src="https://nerdy.dev/media/midi-meets-css-youtube.jpg" alt="Me on stage at Beyond Tellerrand with my talk "MIDI Meets CSS"" height="720" width="1280" />
<p><a href="https://www.youtube.com/watch?v=PMx-i0UuC5Q">MIDI Meets CSS</a>; out now on YouTube!</p>
Beyond Tellerrand Talk Done - Adam Argylehttps://nerdy.dev/beyond-tellerrand-talk-done?utm_source=rss2025-11-07T02:56:09.000Z
<img style="display: none" src="https://nerdy.dev/media/btconf-speaking-1.jpg" alt="Photo from behind, in black and white, me speaking to the crowd" height="1333" width="2000" />
<p>My talk <a href="https://beyondtellerrand.com/events/berlin-2025/speakers/adam-argyle">Midi Meets CSS</a> is done! Had a blast bein a high energy nerd on stage blastin banjo and beats from a <a href="https://teenage.engineering/guides/ep-133">K.O. II</a>.</p>
<p>Marc, <a href="https://beyondtellerrand.com/events/berlin-2025">BTconf</a>; you rock. Day 1 of the talks have been so inspiring. Stoked for <a href="https://beyondtellerrand.com/events/berlin-2025/schedule/friday">day 2</a>.</p>
On General Musings With Kevin Powell About Interviewing For Frontend Roles In 2025 - Adam Argylehttps://nerdy.dev/on-general-musings-with-kevin-powell-about-interviewing-for-frontend-roles-in-2025?utm_source=rss2025-10-31T14:48:48.000Z
<img style="display: none" src="https://nerdy.dev/media/general-musings-2025.jpg" alt="Show thumbnail with Kevin and I on it" height="720" width="1280" />
<p>I was on the <a href="https://www.youtube.com/@GeneralMusings">General Musings</a> show with <a href="https://www.youtube.com/@kevinpowell">Kevin Powell</a> 🤘🏻💀</p>
<p>We chat front-end job hunting in 2025, the misalignments between the technical interviews and the roles they were looking to fill, the overall state of the industry and hiring processes and how to distinguish yourself from the crowd.</p>
<p><a href="https://zencastr.com/z/KHLM-Zlu">Audio</a> · <a href="https://www.youtube.com/watch?v=JLdh9VU7YPk">Video</a></p>
Web components know about the user, device, variables, layout and more - Adam Argylehttps://nerdy.dev/components-can-know?utm_source=rss2025-10-30T23:51:37.000Z
<img style="display: none" src="https://nerdy.dev/media/named-container-query.jpg" alt="a named container query example" height="578" width="1046" />
<p>Since I've tried and failed like 3 times to try and write a blog post on "sector driven design" or "smart components," this post is just gonna try and elaborate on what <a href="https://www.miriamsuzanne.com/">Miriam Suzanne</a> kids a li'l and says "components know stuff."</p>
<p>This is huge though, for development <strong>and design</strong>. </p>
<p>Components can own all their presentation aspects for nearly any scenario now. They can feel like magic, perfectly adapting to wherever the component author or designer has condoned. </p>
<p>A new one that not enough people talk about is, CSS named container queries. A component can easily know if it's in the sidenav, footer, article area, callout, whatever!</p>
<pre><code class="language-css"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0"><code><span class="line"><span style="color:var(--shiki-token-keyword)">@container</span><span style="color:var(--shiki-foreground)"> --some-area {</span></span>
<span class="line"><span style="color:var(--shiki-foreground)"> …</span></span>
<span class="line"><span style="color:var(--shiki-foreground)">}</span></span></code></pre>
</code></pre>
<p>That's amazing. </p>
<p>No more chasing down all the styles and variations that scatter across your application.</p>
<p>Components are aware of it's user, device, variables, layout and more. In an attempt to get y'all stoked too, here's a list of all the "stuff" that components know.</p>
<h2>
What kind of stuff?
<a name="what-kind-of-stuff?" href="#what-kind-of-stuff?">#</a>
</h2>
<p>Let's create the ultimate list of all the rad "stuff" that components know:</p>
<ol>
<li><strong>the users preferences</strong> <br>(<a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme">dark/light</a>, <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion">reduced motion</a>, <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/forced-colors">forced-colors</a>, etc)</li>
<li><strong>the users writing and reading preferences</strong> <br>(<a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_logical_properties_and_values">rtl</a>, <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/direction">lrt</a>, <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/writing-mode">etc</a>)</li>
<li><strong>the user's device size, orientation, and capabilities</strong> <br>(<a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/color-gamut">color</a>, <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/pointer">pointer</a>, etc)</li>
<li><strong>the app's design system</strong> <br>(colors, spacing, etc)</li>
<li><strong>where they are in the app</strong> <br>(<a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_containment/Container_queries">container</a> name queries)</li>
<li><strong>how much space they have</strong> <br>(<a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_containment/Container_queries">container</a> size queries)</li>
<li><strong>how many items they contain</strong> <br>(<a href="https://frontendmasters.com/blog/quantity-queries-are-very-easy-with-css-has/">quantity query</a>)</li>
<li><strong>what kind of items they contain</strong> <br>(<a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:has">:has()</a>)</li>
<li><strong>what the value of a variable for them is</strong> <br>(<a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_containment/Container_size_and_style_queries">style query</a>)</li>
<li><strong>whats in or out of view</strong> <br>(scroll animations)</li>
<li><strong>whether its scrolling or not</strong> <br>(<a href="https://developer.chrome.com/blog/css-scroll-state-queries">scroll-state</a>)</li>
<li><strong>is the user <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:hover">hovering</a>, <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:focus">focusing</a> or <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:active">activating</a> elements</strong></li>
<li><strong>whether there's focus within or not</strong> <br>(<a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-within">focus-within</a>)</li>
<li><strong>if there's valid in invalid form inputs</strong> <br>(<a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:valid">validity</a>)</li>
</ol>
<p><strong>Let me know in the comments what I missed.</strong> <br><small>I'll give ya a shout for your recommendation 🤘🏻💀</small></p>
An Abundance of Inspiration - Robin Rendlehttps://robinrendle.com/notes/an-abundance-of-inspiration/2025-10-28T16:21:49.000Z<p>Redesigning this website over the weekend gave me a curious feeling. It was a deeply cozy one, as if my work sits on a continuum of all the other websites and books every made. As if my work is a link in a long chain through time. You’ll likely spot the inspiration right away: this redesign took an LLM copyright approach to Tschichold’s work and his rather beautiful designs for Penguin Books that are still iconic almost a century later.</p>
<p>There’s other inspirations in here, too. Little details, like the code blocks from <a href="https://chsmc.org/">Chase McCoy’s website</a> which give an air of confidence to an otherwise empty, sad little box of code. Or you can peek under the hood into the code itself and find snippets like this one, <a href="https://piccalil.li/blog/creating-a-full-bleed-css-utility/">the full-bleed CSS utility</a> by Andy Bell:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.full-bleed</span> <span class="token punctuation">{</span><br /> <span class="token property">width</span><span class="token punctuation">:</span> 100vw<span class="token punctuation">;</span><br /> <span class="token property">margin-left</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span>50% - 50vw<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span></code></pre>
<p>However, the redesign of this website has always been inspired by a new font release and this time <a href="https://frerejones.com/families/edgar">Edgar</a> was the one that caught my eye. It’s stately and elegant and sick. Tobias Frere-Jones and Nina Stössinger made a system that’s clearly bookish yet without feeling prudish or uptight about it.</p>
<p>But reading <a href="https://frerejones.com/blog/designing-edgar">Edgar’s design notes</a> you can follow my inspiration back and connect the dots to other sources of inspiration, too:</p>
<blockquote>
<p>“Looking at Caslon again, I realized this wasn’t just some historical artifact. There was something deeper — something still very present and alive,” says Frere-Jones, who felt “an unexpectedly strong reaction” as he pored over the letterforms more intensely. “It was a kind of strange resonance, like when you play a certain note on a speaker and the windows rattle.”</p>
</blockquote>
<p>That’s what inspiration is: finding an old thing and watching it move once again, discovering something else in there that you can remix. Or perhaps even rescue.</p>
<p>At <a href="https://www.famsf.org/exhibitions/art-of-manga">the Art of Manga exhibition</a> at the de Young Museum last week I could hear that same hum. With my nose smushed against the glass, zooming in as far as I could to the beautiful sketches and drawings, I could see how all this work was connected: thousands upon thousands of hours. And all of it stacked on top of more work that I couldn’t see underneath with references and inspirations looted from somewhere else then tweaked and applied in strange new ways.</p>
<p>Speaking of which, I’ve been reading William Gibson’s novel <em>Neuromancer</em> lately and it feels as if I’ve found some holy text, the source of truth for all modern sci-fi. Going in I knew the book was an inspiration to dozens of books and videogames and movies but I hadn’t quite grasped the full extent of it:</p>
<blockquote>
<p>He’d operated on an almost permanent adrenaline high, a by-product of youth and proficiency, jacked into a custom cyberspace deck that projected his disembodied consciousness into the consensual hallucination that was the matrix.</p>
</blockquote>
<p>I gasped as I discovered how throwaway lines or concepts that the novel introduces for a gentle dose of color and world-building have inspired enormous universes of fresh new stories: Night City, decks, eddies, “jacking in”, chrome, corpos, and the goddamn matrix just to name a few.</p>
<p>This was how I felt redesigning my website this time round as there was just so much inspiration that it’s become overwhelming. I want to steal basically everything from <a href="https://anhvn.com/">anh’s new homepage</a> or bottle up the typographic confidence of <a href="https://nazhamid.com/">Naz’s website</a>. I want to distill the warm welcome of <a href="https://realdougwilson.com/">Doug’s</a> and soak up all the the focus and sturdiness of <a href="https://printingfilms.com/">Printing Films</a> and then for good measure package up the ordered curiosity of <a href="https://aworkinglibrary.com/">Mandy’s website</a>. And, of course, I want to put as much effort and energy into a thing as Lucas Pope put into <a href="https://youtu.be/90vqCKEEj3s?si=Sy-aoQiKiJMfRXHY">modeling his beautiful boat</a>.</p>
<p>For now this redesign will do. It’s still messy, unfinished in places. Dark mode needs a lot of work. Mobile styles look clunky, too. I’ll continue to polish the infinite-scrolling blog posts (or I’ll get sick of them in a week and quietly cut them). I might re-add an about page or more information about me since that feels a little stark and threadbare today. That stuff is always tricky to figure out though.</p>
<p>But this redesign was a good reminder that I’m always working amongst smart, inquisitive folks and all of them are taking inspiration from unlikely sources and adding their own thing to the mix. There is an abundance of inspiration out there—across media and genre and quirky publishing formats—and I just have to keep my eyes open to see it.</p>Beyond Tellerrand 2025 - Adam Argylehttps://nerdy.dev/beyond-tellerrand-2025?utm_source=rss2025-10-26T21:04:54.000Z
<video style="display: none" src="/media/midi-meets-css-preview.mp4" alt="some title" height="1086" width="1920" />
<p><strong>Stoked</strong> for my upcoming talk at <a href="https://beyondtellerrand.com/events/berlin-2025">Beyond Tellerrand</a> in Berlin next month:</p>
<p>🎶 <a href="https://beyondtellerrand.com/events/berlin-2025/speakers/adam-argyle">Midi Meets CSS</a> 🎶</p>
<p>Most folks use WebGL to visualize their audio (and it's awesome) but that's not me, CSS is. I've taken a different approach… </p>
<p><a href="https://beyondtellerrand.com/events/berlin-2025/tickets">Be there</a> or be <code>aspect-ratio: 1</code></p>