CSS and design systems people - BlogFlock 2025-07-12T05:31:54.648Z BlogFlock Robin Rendle, Michelle Barker, Hidde de Vries, Adam Argyle, Sara Joy, Stephanie Eckles, Robin Rendle, Sara Soueidan Sizzle Rizzle - Adam Argyle https://nerdy.dev/sizzle-rizzle?utm_source=rss 2025-07-04T21:20:40.000Z <video style="display: none" src="/media/sizzle-reel-2025.mp4" alt="A quick peek into projects from my past" height="1080" width="1920" /> <p>A <small>small</small> sample of <strong>UI</strong>, <strong>code</strong>, <strong>tools</strong>, and <strong>design</strong> from my 20 professional years of webdev.</p> Fathers Day 2025 - Adam Argyle https://nerdy.dev/fathers-day-2025?utm_source=rss 2025-06-15T18:10:04.000Z <p>Happy <a href="https://en.wikipedia.org/wiki/Father%27s_Day">Father&#39;s Day</a> to other <a href="https://developer.mozilla.org/en-US/docs/Web/API/Node/parentNode"><code>parentNode</code></a>s</p> Nintendo Switch Homescreen recreated with CSS and a li'l bit of JS - Adam Argyle https://nerdy.dev/nintendo-switch-homescreen-css-recreation?utm_source=rss 2025-06-13T22:16:31.000Z <img style="display: none" src="https://nerdy.dev/media/switch-homescreen-both-light-dark.jpg" alt="Text emphasized alt text example" height="636" width="1920" /> <p>Someone across the room could hear the sounds from a device and know that it&#39;s coming from a Nintendo Switch.</p> <p>Just the audio alone is iconic; but that homescreen layout and scroll experience, :cheffs-kiss:</p> <p><video src="/media/switch-homescreen-demo.mp4" width="1920" height="636" alt="" preload="none" poster="/media/switch-homescreen-demo.avif" controls loop muted playsinline allowFullScreen /></p> <p>Let&#39;s <a href="https://codepen.io/argyleink/pen/ByNyvox">remake it with CSS</a>!</p> <ol> <li>light/dark theme + <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/forced-colors">forced colors</a> support</li> <li>automatic scroll UX upgrades based on user&#39;s browser capabilities</li> <li>keyboard, mouse, touch, allllll good</li> </ol> <style> @scope { .codepen-embed { block-size: 450px; } } </style> <p> <iframe class="codepen-embed" scrolling="no" title="null" src="https://codepen.io/argyleink/embed/preview/ByNyvox?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/ByNyvox"> by Adam Argyle (<a href="https://codepen.io/argyleink">@argyleink</a>) on <a href="https://codepen.io">CodePen</a>. </iframe> </p> <br> <p><q class="info">This is 1 of 3 scroll experiences I taught at <a href="https://cssday.nl">CSS Day</a> 2025. What a lovely conference!</q></p> <h2> HTML <a name="html" href="#html">#</a> </h2> <h3> Structure <a name="structure" href="#structure">#</a> </h3> <p>I&#39;ve found the following &quot;3 element structure&quot; for a scroller to help prevent headaches and gotchas across browsers. The structure supports you both in styling, interactions, and container queries.</p> <ol> <li><code>.scroll--root</code>: highest level container for the scroller, may have borders or spacing styles</li> <li><code>.scroll--viewport</code>: the element with <code>overflow</code>, containment and scrollbar styles</li> <li><code>.scroll--content</code>: wrapper to aid in cross browser padding oddities or other impacts that an overflow container may apply to children</li> </ol> <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)">&#x3C;</span><span style="color:var(--shiki-token-string-expression)">section</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)">"scroll--root"</span><span style="color:var(--shiki-foreground)">></span></span> <span class="line"><span style="color:var(--shiki-foreground)"> &#x3C;</span><span style="color:var(--shiki-token-string-expression)">div</span><span style="color:var(--shiki-token-function)"> id</span><span style="color:var(--shiki-foreground)">=</span><span style="color:var(--shiki-token-string-expression)">"scroller"</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)">"scroll--viewport"</span><span style="color:var(--shiki-foreground)">></span></span> <span class="line"><span style="color:var(--shiki-foreground)"> &#x3C;</span><span style="color:var(--shiki-token-string-expression)">ul</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)">"scroll--content"</span><span style="color:var(--shiki-foreground)">></span></span> <span class="line"><span style="color:var(--shiki-foreground)"> /* your content */</span></span> <span class="line"><span style="color:var(--shiki-foreground)"> &#x3C;/</span><span style="color:var(--shiki-token-string-expression)">ul</span><span style="color:var(--shiki-foreground)">></span></span> <span class="line"><span style="color:var(--shiki-foreground)"> &#x3C;/</span><span style="color:var(--shiki-token-string-expression)">div</span><span style="color:var(--shiki-foreground)">></span></span> <span class="line"><span style="color:var(--shiki-foreground)">&#x3C;/</span><span style="color:var(--shiki-token-string-expression)">section</span><span style="color:var(--shiki-foreground)">></span></span></code></pre> </code></pre> <h2> CSS <a name="css" href="#css">#</a> </h2> <h3> Essentials <a name="essentials" href="#essentials">#</a> </h3> <p>These are the things you should always put on your scroller:</p> <ol> <li>use the <code>overflow</code> shorthand and set both axes to prevent surprises</li> <li><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/overscroll-behavior"><code>overscroll-behavior</code></a> should always match the scrolling axis</li> <li>padding and <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-padding"><code>scroll-padding</code></a> should always match</li> <li>smooth <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-behavior"><code>scroll-behavior</code></a> if the use is ok with motion</li> </ol> <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)">.scroll--viewport</span><span style="color:var(--shiki-foreground)"> {</span></span> <span class="line"><span style="color:var(--shiki-token-constant)"> overflow</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> auto hidden</span><span style="color:var(--shiki-foreground)">;</span></span> <span class="line"><span style="color:var(--shiki-token-constant)"> overscroll-behavior-x</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> contain</span><span style="color:var(--shiki-foreground)">;</span></span> <span class="line"></span> <span class="line"><span style="color:var(--shiki-token-constant)"> padding-inline</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)">(--size-8)</span><span style="color:var(--shiki-foreground)">;</span></span> <span class="line"><span style="color:var(--shiki-token-constant)"> scroll-padding-inline</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)">(--size-8)</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)">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)"> scroll-behavior</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> smooth</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>See the <code>@layer lessons.essentials</code> in <a href="https://codepen.io/argyleink/pen/ByNyvox?editors=0100">the demo</a> for more comments.</p> <h3> Containerization <a name="containerization" href="#containerization">#</a> </h3> <p>Since a scroller of often extrinsically sized, it won&#39;t suffer from the side effect that container queries can cause (where they collapse). This makes scroller and container queries a natural pair.</p> <ol> <li><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/container">container</a> shorthand lets me set the name and the type(s) in one spot</li> <li><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_containment/Container_queries">container queries</a> for children to use to ask this container questions about the width and height while also providing a context for container query units like <code>cqw</code></li> <li><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_conditional_rules/Container_scroll-state_queries">scroll-state</a> for children to ask about which game is snapped</li> </ol> <p>Plus, <a href="https://developer.chrome.com/blog/css-scroll-state-queries"><code>scroll-state</code></a> queries on the scroller and on the snap items are meaningful and worth setting up while we&#39;re here.</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-function)">.scroll--root</span><span style="color:var(--shiki-foreground)"> {</span></span> <span class="line"><span style="color:var(--shiki-token-constant)"> container</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> --homescreen / size</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-function)">.scroll--viewport</span><span style="color:var(--shiki-foreground)"> {</span></span> <span class="line"><span style="color:var(--shiki-token-constant)"> container</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> --homescreen-scroller / size scroll-state</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-function)">.scroll--content</span><span style="color:var(--shiki-token-keyword)"> ></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-token-comment)"> /* children will use snapped scroll-state() */</span></span> <span class="line"><span style="color:var(--shiki-token-constant)"> container-type</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> scroll-state</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>See the <code>@layer lessons.containerize</code> in <a href="https://codepen.io/argyleink/pen/ByNyvox?editors=0100">the demo</a> for more comments.</p> <h3> Snapping <a name="snapping" href="#snapping">#</a> </h3> <p>Meaningfully, the Switch scroller snaps the scroll items to the left, in line with the layout grid. We can do this in CSS with just a couple styles.</p> <p>Since the <code>scroll-padding</code> already matches the inline <code>padding</code>, the snapping here will match the alignment perfectly.</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-function)">.scroll--viewport</span><span style="color:var(--shiki-foreground)"> {</span></span> <span class="line"><span style="color:var(--shiki-token-constant)"> scroll-snap-type</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> x mandatory</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-function)">.scroll--content</span><span style="color:var(--shiki-token-keyword)"> ></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-token-constant)"> scroll-snap-align</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> start</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>See the <code>@layer lessons.scroll-snap</code> in <a href="https://codepen.io/argyleink/pen/ByNyvox?editors=0100">the demo</a> for more comments.</p> <h3> Overscroll effect <a name="overscroll-effect" href="#overscroll-effect">#</a> </h3> <p>It&#39;s common for scrollers to offer an effect if you&#39;ve reached the beginning or end; an overscroll effect. The most common is an elastic effect, like a rubber band is tight and preventing you from going further and when you let go it snaps back. We can do this with CSS scroll snapping.</p> <p>Here&#39;s a moment where wrapping all your content in a <code>.scroll--content</code> container is valuable. The overscroll amount is the size of an invisible item at the beginning and end of the scroller. The browser will let you scroll to them, but since they won&#39;t have <code>scroll-snap-align</code> set and the scroller itself has <code>x mandatory</code> set, the scroller will never rest on these invisible overscroll spacers.</p> <ol> <li>The inline-start spacer will be <code>25cqi</code>, offering a 25% overscroll vibe</li> <li>The inline-end spacer will be larger at <code>90cqi</code>, ensuring the last game can be snapped to the start</li> </ol> <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)">.scroll--content</span><span style="color:var(--shiki-foreground)"> {</span></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)"> flex</span><span style="color:var(--shiki-foreground)">;</span></span> <span class="line"></span> <span class="line"><span style="color:var(--shiki-foreground)"> &#x26;</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)">:before</span><span style="color:var(--shiki-token-punctuation)">,</span></span> <span class="line"><span style="color:var(--shiki-token-constant)"> &#x26;::after {</span></span> <span class="line"><span style="color:var(--shiki-token-constant)"> content: </span><span style="color:var(--shiki-token-string-expression)">""</span><span style="color:var(--shiki-foreground)">;</span></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)"> block</span><span style="color:var(--shiki-foreground)">;</span></span> <span class="line"><span style="color:var(--shiki-token-constant)"> inline-size</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> 25</span><span style="color:var(--shiki-token-keyword)">cqi</span><span style="color:var(--shiki-foreground)">;</span></span> <span class="line"><span style="color:var(--shiki-token-constant)"> flex-shrink</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> <span class="line"><span style="color:var(--shiki-foreground)"> &#x26;</span><span style="color:var(--shiki-token-function)">::after</span><span style="color:var(--shiki-foreground)"> {</span></span> <span class="line"><span style="color:var(--shiki-token-constant)"> inline-size</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> 90</span><span style="color:var(--shiki-token-keyword)">cqi</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>See the <code>@layer lessons.overscroll-effect</code> in <a href="https://codepen.io/argyleink/pen/ByNyvox?editors=0100">the demo</a> for more comments.</p> <h3> Scroll Animation <a name="scroll-animation" href="#scroll-animation">#</a> </h3> <p>This was a personal addition, not part of the original Switch homescreen design.</p> <p>It&#39;s subtle; fade out the game you just scrolled past. Leave a hint or peek of it, but dim it out. This reduced cognitive load and helps the eye maintain the alignment of the snapped game to the grid.</p> <p>Here&#39;s the simple keyframes.</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)">@keyframes</span><span style="color:var(--shiki-foreground)"> leaving-scroller {</span></span> <span class="line"><span style="color:var(--shiki-foreground)"> to {</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)"> .2</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>Here we use the keyframes on a game if the browser supports the feature, and I clamp the keyframes with <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/animation-range"><code>animation-range</code></a> to only run as the element exits the scrollport.</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-function)">.game</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-foreground)"> @</span><span style="color:var(--shiki-token-constant)">supports</span><span style="color:var(--shiki-foreground)"> (</span><span style="color:var(--shiki-token-constant)">animation-timeline</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> view(x</span><span style="color:var(--shiki-foreground)">)) {</span></span> <span class="line"><span style="color:var(--shiki-token-constant)"> animation</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> leaving-scroller linear both</span><span style="color:var(--shiki-foreground)">;</span></span> <span class="line"><span style="color:var(--shiki-token-constant)"> animation-timeline</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> view(x</span><span style="color:var(--shiki-foreground)">);</span></span> <span class="line"><span style="color:var(--shiki-token-constant)"> animation-range</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> exit 50</span><span style="color:var(--shiki-token-keyword)">%</span><span style="color:var(--shiki-token-constant)"> exit 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> <span class="line"><span style="color:var(--shiki-foreground)">}</span></span></code></pre> </code></pre> <p>See the <code>@layer lessons.scroll-animation</code> in <a href="https://codepen.io/argyleink/pen/ByNyvox?editors=0100">the demo</a> for more comments.</p> <h3> Scroll State <a name="scroll-state" href="#scroll-state">#</a> </h3> <p>Here we&#39;ll use the scroll-state containerization work from an earlier lesson. Two things occur when a game snaps into place:</p> <ol> <li>the pulsing blue focus outline highlights the game and animates forever</li> <li>the title of the game slides up</li> </ol> <p>By default I chose to always show the game titles, only applying the animation effect if the browser understands <code>scroll-state</code> queries.</p> <p>Here&#39;s the keyframes for when a game is snapped:</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)">@keyframes</span><span style="color:var(--shiki-foreground)"> game-focus {</span></span> <span class="line"><span style="color:var(--shiki-foreground)"> from {</span></span> <span class="line"><span style="color:var(--shiki-token-constant)"> outline-color</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-function)"> hsl</span><span style="color:var(--shiki-token-constant)">(200 100</span><span style="color:var(--shiki-token-keyword)">%</span><span style="color:var(--shiki-token-constant)"> 50</span><span style="color:var(--shiki-token-keyword)">%</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> <span class="line"><span style="color:var(--shiki-foreground)"> to {</span></span> <span class="line"><span style="color:var(--shiki-token-constant)"> outline-color</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-function)"> hsl</span><span style="color:var(--shiki-token-constant)">(200 100</span><span style="color:var(--shiki-token-keyword)">%</span><span style="color:var(--shiki-token-constant)"> 80</span><span style="color:var(--shiki-token-keyword)">%</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> <span class="line"><span style="color:var(--shiki-foreground)">}</span></span></code></pre> </code></pre> <p>The game image uses the keyframes when it&#39;s parent <code>.game</code> parent container is snapped on x, and the image title slides down when it&#39;s parent game is not snapped.</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-function)">.game</span><span style="color:var(--shiki-foreground)"> {</span></span> <span class="line"><span style="color:var(--shiki-token-constant)"> img</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)">container</span><span style="color:var(--shiki-token-constant)"> scroll-state</span><span style="color:var(--shiki-foreground)">(</span><span style="color:var(--shiki-token-constant)">snapped</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> x</span><span style="color:var(--shiki-foreground)">) {</span></span> <span class="line"><span style="color:var(--shiki-token-constant)"> outline</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> 5</span><span style="color:var(--shiki-token-keyword)">px</span><span style="color:var(--shiki-token-constant)"> solid blue</span><span style="color:var(--shiki-foreground)">;</span></span> <span class="line"><span style="color:var(--shiki-token-constant)"> outline-offset</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> 5</span><span style="color:var(--shiki-token-keyword)">px</span><span style="color:var(--shiki-foreground)">;</span></span> <span class="line"><span style="color:var(--shiki-token-constant)"> animation</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> game-focus .5</span><span style="color:var(--shiki-token-keyword)">s</span><span style="color:var(--shiki-token-function)"> var</span><span style="color:var(--shiki-token-constant)">(--ease-3) alternate infinite</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-string-expression)"> figcaption</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)"> z-index</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-token-constant)"> transition</span><span style="color:var(--shiki-token-keyword)">:</span></span> <span class="line"><span style="color:var(--shiki-token-constant)"> translate .5</span><span style="color:var(--shiki-token-keyword)">s</span><span style="color:var(--shiki-token-function)"> var</span><span style="color:var(--shiki-token-constant)">(--ease-spring-2) .2</span><span style="color:var(--shiki-token-keyword)">s</span><span style="color:var(--shiki-token-punctuation)">,</span></span> <span class="line"><span style="color:var(--shiki-token-constant)"> opacity .5</span><span style="color:var(--shiki-token-keyword)">s</span><span style="color:var(--shiki-token-function)"> var</span><span style="color:var(--shiki-token-constant)">(--ease-3) .2</span><span style="color:var(--shiki-token-keyword)">s</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)">container</span><span style="color:var(--shiki-token-constant)"> not</span><span style="color:var(--shiki-token-constant)"> scroll-state</span><span style="color:var(--shiki-foreground)">(</span><span style="color:var(--shiki-token-constant)">snapped</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> x</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 50</span><span style="color:var(--shiki-token-keyword)">px</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> <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>See the <code>@layer lessons.scroll-state</code> in <a href="https://codepen.io/argyleink/pen/ByNyvox?editors=0100">the demo</a> for more comments.</p> <h3> Colorization <a name="colorization" href="#colorization">#</a> </h3> <p>The goal of this section is to match the Switch Homescreen&#39;s scroller style with <code>::-webkit-*</code> scroll features, while also providing a meaningful fallback with <code>scrollbar-color</code>. In addition to integrated colors and sizing, I also add a few interactive moments like when a keyboard is focused inside the scroller, or a mouse is hovering the thumb.</p> <ol> <li>Integrate scrollbar colors into the adaptive light/dark/forced colors themes</li> <li>Add hover and focus &quot;hints and feedback&quot;</li> </ol> <p>Look at how cool the forced colors styles are. <strong>Transparent borders are your friend!</strong></p> <p><picture> <source srcset="/media/switch-homescreen-forced-light-dark.avif" type="image/avif"> <source srcset="/media/switch-homescreen-forced-light-dark.webp" type="image/webp"> <img loading="lazy" src="/media/switch-homescreen-forced-light-dark.jpg" alt="" title="Title " decoding="async" width="2000" height="662" /> </picture></p> <p>Compare that with the light and dark styles:</p> <p><picture> <source srcset="/media/switch-homescreen-both-light-dark.avif" type="image/avif"> <source srcset="/media/switch-homescreen-both-light-dark.webp" type="image/webp"> <img loading="lazy" src="/media/switch-homescreen-both-light-dark.jpg" alt="" title="Title " decoding="async" width="2000" height="662" /> </picture></p> <p>It takes a lot of selectors and styles to do this robust colorization, here&#39;s the chunk that does the work.</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-function)">.scroll--viewport</span><span style="color:var(--shiki-foreground)"> {</span></span> <span class="line"><span style="color:var(--shiki-foreground)"> &#x26;</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)">is(:focus-visible</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-token-constant)"> :focus-within</span><span style="color:var(--shiki-foreground)">) {</span></span> <span class="line"><span style="color:var(--shiki-token-constant)"> outline-offset</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> -2</span><span style="color:var(--shiki-token-keyword)">px</span><span style="color:var(--shiki-foreground)">;</span></span> <span class="line"><span style="color:var(--shiki-token-constant)"> outline</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> none</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-foreground)"> &#x26;</span><span style="color:var(--shiki-token-function)">::-webkit-scrollbar</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)"> 10</span><span style="color:var(--shiki-token-keyword)">px</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-foreground)"> &#x26;</span><span style="color:var(--shiki-token-function)">::-webkit-scrollbar-track</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-constant)"> #0000</span><span style="color:var(--shiki-foreground)">;</span></span> <span class="line"><span style="color:var(--shiki-token-constant)"> background-clip</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> padding-box</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-foreground)"> &#x26;</span><span style="color:var(--shiki-token-function)">::-webkit-scrollbar-thumb</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)"> color-mix</span><span style="color:var(--shiki-token-constant)">(in srgb</span><span style="color:var(--shiki-token-punctuation)">,</span></span> <span class="line"><span style="color:var(--shiki-token-constant)"> light-dark(</span></span> <span class="line"><span style="color:var(--shiki-token-function)"> var</span><span style="color:var(--shiki-token-constant)">(--surface-4)</span><span style="color:var(--shiki-token-punctuation)">,</span></span> <span class="line"><span style="color:var(--shiki-token-function)"> var</span><span style="color:var(--shiki-token-constant)">(--surface-2)</span></span> <span class="line"><span style="color:var(--shiki-token-constant)"> )</span><span style="color:var(--shiki-token-punctuation)">,</span></span> <span class="line"><span style="color:var(--shiki-token-constant)"> #0000 50</span><span style="color:var(--shiki-token-keyword)">%</span></span> <span class="line"><span style="color:var(--shiki-foreground)"> );</span></span> <span class="line"><span style="color:var(--shiki-token-constant)"> border-radius</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> 1e3</span><span style="color:var(--shiki-token-keyword)">px</span><span style="color:var(--shiki-foreground)">;</span></span> <span class="line"><span style="color:var(--shiki-token-constant)"> border</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> 3</span><span style="color:var(--shiki-token-keyword)">px</span><span style="color:var(--shiki-token-constant)"> solid </span><span style="color:var(--shiki-token-function)">var</span><span style="color:var(--shiki-token-constant)">(--surface-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> <span class="line"><span style="color:var(--shiki-foreground)"> &#x26;</span><span style="color:var(--shiki-token-function)">:is</span><span style="color:var(--shiki-foreground)">(</span><span style="color:var(--shiki-token-function)">:focus-visible</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-token-function)"> :focus-within</span><span style="color:var(--shiki-foreground)">)</span><span style="color:var(--shiki-token-function)">::-webkit-scrollbar-thumb</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)"> var</span><span style="color:var(--shiki-token-constant)">(--link)</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)"> (</span><span style="color:var(--shiki-token-constant)">hover</span><span style="color:var(--shiki-foreground)">) {</span></span> <span class="line"><span style="color:var(--shiki-foreground)"> &#x26;</span><span style="color:var(--shiki-token-function)">::-webkit-scrollbar-thumb:hover</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-constant)"> light-dark(</span><span style="color:var(--shiki-token-function)">var</span><span style="color:var(--shiki-token-constant)">(--surface-4)</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-token-function)"> var</span><span style="color:var(--shiki-token-constant)">(--surface-2)</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-foreground)"> &#x26;</span><span style="color:var(--shiki-token-function)">::-webkit-scrollbar</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)"> .5</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-foreground)"> &#x26;</span><span style="color:var(--shiki-token-function)">::-webkit-scrollbar:hover</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> <span class="line"></span> <span class="line"><span style="color:var(--shiki-token-keyword)"> @supports</span><span style="color:var(--shiki-foreground)"> (</span><span style="color:var(--shiki-token-constant)">-moz-appearance</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)">none</span><span style="color:var(--shiki-foreground)">) {</span></span> <span class="line"><span style="color:var(--shiki-token-string-expression)"> scrollbar-width</span><span style="color:var(--shiki-foreground)">: thin;</span></span> <span class="line"><span style="color:var(--shiki-token-string-expression)"> scrollbar-color</span><span style="color:var(--shiki-foreground)">: var(--surface-4) #0000;</span></span> <span class="line"><span style="color:var(--shiki-foreground)"> transition: </span><span style="color:var(--shiki-token-string-expression)">scrollbar-color</span><span style="color:var(--shiki-foreground)"> .3s ease;</span></span> <span class="line"></span> <span class="line"><span style="color:var(--shiki-foreground)"> &#x26;</span><span style="color:var(--shiki-token-function)">:is</span><span style="color:var(--shiki-foreground)">(</span><span style="color:var(--shiki-token-function)">:focus-visible</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-token-function)"> :focus-within</span><span style="color:var(--shiki-foreground)">) {</span></span> <span class="line"><span style="color:var(--shiki-token-constant)"> scrollbar-color</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)">(--link) #0000</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)"> (</span><span style="color:var(--shiki-token-constant)">hover</span><span style="color:var(--shiki-foreground)">) {</span></span> <span class="line"><span style="color:var(--shiki-token-string-expression)"> scrollbar-color</span><span style="color:var(--shiki-foreground)">:</span></span> <span class="line"><span style="color:var(--shiki-foreground)"> color-mix(in srgb</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-foreground)"> var(--surface-4), #0000 50%)</span></span> <span class="line"><span style="color:var(--shiki-foreground)"> #0000;</span></span> <span class="line"></span> <span class="line"><span style="color:var(--shiki-foreground)"> &#x26;</span><span style="color:var(--shiki-token-function)">:hover</span><span style="color:var(--shiki-foreground)"> {</span></span> <span class="line"><span style="color:var(--shiki-token-constant)"> scrollbar-color</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)">(--surface-4) #0000</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> <span class="line"><span style="color:var(--shiki-foreground)">}</span></span></code></pre> </code></pre> <p>See the <code>@layer lessons.colorize</code> in <a href="https://codepen.io/argyleink/pen/ByNyvox?editors=0100">the demo</a> for more comments.</p> <h3> Content Introduction <a name="content-introduction" href="#content-introduction">#</a> </h3> <p>Finally, on load, introduce the content from the right, providing the hint that there&#39;s directionality in this games list, hinting at scroll.</p> <p>The star of this effect is <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@starting-style"><code>@starting-style</code></a>.</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-function)">.scroll--content</span><span style="color:var(--shiki-token-function)"> .game</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 style="color:var(--shiki-token-constant)"> transform 1</span><span style="color:var(--shiki-token-keyword)">s</span><span style="color:var(--shiki-token-function)"> var</span><span style="color:var(--shiki-token-constant)">(--ease-spring-2)</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)">(</span><span style="color:var(--shiki-token-function)">var</span><span style="color:var(--shiki-token-constant)">(--i) </span><span style="color:var(--shiki-token-keyword)">*</span><span style="color:var(--shiki-token-constant)"> .05</span><span style="color:var(--shiki-token-keyword)">s</span><span style="color:var(--shiki-token-constant)">)</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)"> transform</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-function)"> translateX</span><span style="color:var(--shiki-token-constant)">(100</span><span style="color:var(--shiki-token-keyword)">vw</span><span style="color:var(--shiki-token-constant)">) </span><span style="color:var(--shiki-token-function)">translateZ</span><span style="color:var(--shiki-token-constant)">(</span><span style="color:var(--shiki-token-function)">calc</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)">(--i) </span><span style="color:var(--shiki-token-keyword)">*</span><span style="color:var(--shiki-token-constant)"> -1</span><span style="color:var(--shiki-token-keyword)">px</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> <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>See the <code>@layer lessons.loading-animation</code> in <a href="https://codepen.io/argyleink/pen/ByNyvox?editors=0100">the demo</a> for more comments.</p> <h2> JavaScript <a name="javascript" href="#javascript">#</a> </h2> <p>Iconically, the Switch homescreen has both haptic and audibal feedback for your interactions, specifically to the scroller.</p> <ol> <li>Snap &quot;click&quot; when the scroller rests on a game</li> <li>Pop sound as each game passes by the snap area</li> <li>Haptic feedback as a game passes over the snap area</li> <li>Ability to tap to snap</li> </ol> <h3> Preload <a name="preload" href="#preload">#</a> </h3> <p>First let&#39;s preload the sounds so they can play instantly at first.</p> <pre><code class="language-js"><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)">const</span><span style="color:var(--shiki-token-constant)"> sounds</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)"> snap</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-keyword)"> new</span><span style="color:var(--shiki-token-function)"> Audio</span><span style="color:var(--shiki-foreground)">(</span><span style="color:var(--shiki-token-string-expression)">'https://assets.codepen.io/2585/snap.mp3'</span><span style="color:var(--shiki-foreground)">)</span><span style="color:var(--shiki-token-punctuation)">,</span></span> <span class="line"><span style="color:var(--shiki-foreground)"> pop</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-keyword)"> new</span><span style="color:var(--shiki-token-function)"> Audio</span><span style="color:var(--shiki-foreground)">(</span><span style="color:var(--shiki-token-string-expression)">'https://assets.codepen.io/2585/pop.m4a'</span><span style="color:var(--shiki-foreground)">)</span><span style="color:var(--shiki-token-punctuation)">,</span></span> <span class="line"><span style="color:var(--shiki-foreground)">}</span></span></code></pre> </code></pre> <h3> Tap to snap <a name="tap-to-snap" href="#tap-to-snap">#</a> </h3> <p>If a user clicks a game, scroll it to the snap position.</p> <pre><code class="language-js"><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-constant)">scroller</span><span style="color:var(--shiki-foreground)">.</span><span style="color:var(--shiki-token-function)">onclick</span><span style="color:var(--shiki-token-keyword)"> =</span><span style="color:var(--shiki-foreground)"> event </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-token-keyword)"> let</span><span style="color:var(--shiki-foreground)"> target </span><span style="color:var(--shiki-token-keyword)">=</span><span style="color:var(--shiki-token-constant)"> event</span><span style="color:var(--shiki-token-function)">.</span><span style="color:var(--shiki-token-constant)">target</span><span style="color:var(--shiki-token-function)">.closest</span><span style="color:var(--shiki-foreground)">(</span><span style="color:var(--shiki-token-string-expression)">'.game'</span><span style="color:var(--shiki-foreground)">)</span></span> <span class="line"></span> <span class="line"><span style="color:var(--shiki-token-keyword)"> if</span><span style="color:var(--shiki-foreground)"> (target) {</span></span> <span class="line"><span style="color:var(--shiki-token-constant)"> scroller</span><span style="color:var(--shiki-token-function)">.scrollTo</span><span style="color:var(--shiki-foreground)">({</span></span> <span class="line"><span style="color:var(--shiki-foreground)"> left</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> target</span><span style="color:var(--shiki-foreground)">.offsetLeft</span><span style="color:var(--shiki-token-punctuation)">,</span></span> <span class="line"><span style="color:var(--shiki-foreground)"> behavior</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-string-expression)"> 'smooth'</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> <h3> Vibrate and pop <a name="vibrate-and-pop" href="#vibrate-and-pop">#</a> </h3> <p>The lovely <a href="https://developer.chrome.com/blog/scroll-snap-events#scrollsnapchanging"><code>scrollSnapChanging</code></a> event is perfectly fit for this task.</p> <pre><code class="language-js"><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-constant)">scroller</span><span style="color:var(--shiki-foreground)">.</span><span style="color:var(--shiki-token-function)">onscrollsnapchanging</span><span style="color:var(--shiki-token-keyword)"> =</span><span style="color:var(--shiki-foreground)"> event </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-token-constant)"> navigator</span><span style="color:var(--shiki-token-function)">?.vibrate</span><span style="color:var(--shiki-foreground)">(</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-constant)"> sounds</span><span style="color:var(--shiki-token-function)">.</span><span style="color:var(--shiki-token-constant)">pop</span><span style="color:var(--shiki-token-function)">.play</span><span style="color:var(--shiki-foreground)">()</span></span> <span class="line"><span style="color:var(--shiki-foreground)">}</span></span></code></pre> </code></pre> <h3> Snap <a name="snap" href="#snap">#</a> </h3> <p>The complimentary scroll event <a href="https://developer.chrome.com/blog/scroll-snap-events#scrollsnapchange"><code>scrollSnapChange</code></a> is perfectly fit for reporting scroll has ended and it&#39;s rested on a new snap target.</p> <pre><code class="language-js"><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-constant)">scroller</span><span style="color:var(--shiki-foreground)">.</span><span style="color:var(--shiki-token-function)">onscrollsnapchange</span><span style="color:var(--shiki-token-keyword)"> =</span><span style="color:var(--shiki-foreground)"> event </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-token-constant)"> sounds</span><span style="color:var(--shiki-token-function)">.</span><span style="color:var(--shiki-token-constant)">snap</span><span style="color:var(--shiki-token-function)">.play</span><span style="color:var(--shiki-foreground)">()</span></span> <span class="line"><span style="color:var(--shiki-foreground)">}</span></span></code></pre> </code></pre> <h2> Outro <a name="outro" href="#outro">#</a> </h2> <p>You made it, which means you definitely picked up some new tricks. Go forth and make great scroll experiences!</p> <p>I&#39;ll share a link to the talk if/when it comes out, which provides a lot of commentary on these decisions and styles. But also, in case it wasn&#39;t clear in the blog post, check out the demo for all the comments, they should help connect all the dots.</p> Prefers Reduced Transparency - Adam Argyle https://nerdy.dev/prefers-reduced-transparency?utm_source=rss 2025-06-10T20:38:27.000Z <img style="display: none" src="https://nerdy.dev/media/reduced-transparency.jpg" alt="An image with a caption is showed in two styles, one with a semi-transparent overlay of the caption and one with the caption underneath the image with no transparency used at all." height="1112" width="2910" /> <p>It&#39;s not a challenge to design for <a href="https://developer.chrome.com/blog/css-prefers-reduced-transparency">reduced transparency</a>, <strong>it&#39;s an opportunity</strong>.</p> <ul> <li><a href="https://codepen.io/argyleink/full/YzBKBaR">A card with a caption</a></li> <li><a href="https://codepen.io/argyleink/full/qBmJyvv">Adaptive frosted glass</a></li> </ul> CSSday 2025 - Adam Argyle https://nerdy.dev/cssday-2025?utm_source=rss 2025-06-05T05:34:55.000Z <img style="display: none" src="https://nerdy.dev/media/ultimate-scroller.jpg" alt="CSS Day 2025 The Ultimate Scroller" height="1534" width="2954" /> <p><a href="https://cssday.nl/speakers.html#adam">Speaking</a> in just a couple hours 🤘🏻💀</p> <p><a href="https://css-day-2025.argyleink.deno.net">The Slides</a> · <a href="https://codepen.io/collection/JYdmwr">Codepen Collection</a></p> Off To CSS Day 2025 - Adam Argyle https://nerdy.dev/off-to-css-day-2025?utm_source=rss 2025-06-02T01:53:53.000Z <img style="display: none" src="https://nerdy.dev/media/cssday-2024-swag.jpg" alt="A pile of CSS Day sticker swag is on a table" height="1066" width="1600" /> <p>Off to <a href="https://cssday.nl">CSS Day</a>; see ya there‽</p> Mt Rainier Boat Banjo - Adam Argyle https://nerdy.dev/mt-rainier-boat-banjo?utm_source=rss 2025-05-29T03:58:04.000Z <img style="display: none" src="https://nerdy.dev/media/rainier-boat-banjo.jpg" alt="me on a small boat playing banjo with Mt Rainier behind me" height="1700" width="2268" /> <p><a href="https://en.wikipedia.org/wiki/Mount_Rainier">Mt. Rainier</a> in the bg, on a friend&#39;s little personal website boat, playin banjo tunes in <a href="https://benjiflaming.com/banjo-stuff/solo-banjo-tunings/double-c">double C</a>:</p> <ul> <li><a href="https://www.youtube.com/watch?v=UKt9Z38yCvA">Sally in the Garden</a></li> <li><a href="https://www.youtube.com/watch?v=Ea9GzQ7331w">Darlin&#39; Cori</a></li> <li><a href="https://www.youtube.com/watch?v=QN0_nOBUGCU">Fly Away</a></li> <li><a href="https://www.youtube.com/watch?v=BxIpGnGNT4g">Zelda&#39;s Lullaby</a></li> <li><a href="https://www.youtube.com/watch?v=aht8ywI8v6g">30 Turkeys</a></li> <li><a href="https://www.youtube.com/watch?v=eLauF1i27OE">Smoke</a></li> <li><a href="https://www.youtube.com/watch?v=tAaGKo4XVvM">Mario Overworld Theme</a></li> </ul> <p>Vibe check? This is the way.</p> Those annoying long URLs causing overflow? - Sara Joy https://sarajoy.dev/blog/short/2025-05-22-those-annoying-long-urls-causing-overflow/ 2025-05-22T15:06:00.000Z A short thort The End of Everything - Robin Rendle https://robinrendle.com/notes/the-end-of-everything/ 2025-05-21T16:56:41.000Z <p>Here’s an eye-opening snippet from Katie Mack’s excellent book <a href="https://bookshop.org/p/books/the-end-of-everything-astrophysically-speaking-katie-mack/15495266?ean=9781982103552&amp;next=t"><em>The End of Everything (Astrophysically Speaking)</em></a>:</p> <blockquote> <p>The expansion of the universe as it is occurring today more than just stretch out the light of distant galaxies. It also stretches out and dilutes the afterglow of the Big Bang itself. One of the strongest pieces of evidence for the Big Bang [...] is the fact that we can <em>actually see it</em>, simply by looking far enough away. What we see, specifically, is a dim glow, coming from all directions, of light produced in the universe’s infancy. That dim glow is actually a direct view of parts of the universe that are so far away that, from our perspective, they are still <em>on fire</em>—they’re still experiencing the hot early stage of the universe’s existence, when every part of the cosmos was hot and dense and opaque with roiling plasma, like the inside of a star. The light from that long-burned-out fire has been traveling to us all this time, and, from sufficiently distant points, has just now arrived.</p> </blockquote> <p>!</p> Tag, I'm It - Adam Argyle https://nerdy.dev/blogging-about-blogging?utm_source=rss 2025-05-12T15:21:30.000Z <p><a href="https://kilianvalkhof.com/2025/life/tag-youre-it/">Killian</a> tagged me in this blog chain letter type thing to answer some questions about blogging. I accept 🙂</p> <h2> Why did you start blogging in the first place? <a name="why-did-you-start-blogging-in-the-first-place?" href="#why-did-you-start-blogging-in-the-first-place?">#</a> </h2> <p>My first exploration into blogging was Wordpress, I called it Geek Devigner. I wanted to share about the cross of development and design (devigner). I didn&#39;t write on it much, I was still making mostly Flash and jQuery stuff. Needless to say, this wasn&#39;t a super popular topic to be blogging about.</p> <p>Later I wrote posts on Medium or Devto. This was mostly during the JAMstack wave, and I was really intent on optimizing the build systems so Jade was only rendering the render trees that changed all while making my own isomorphic framework. I also like Grunt more than Gulp, so again, this wasn&#39;t a super popular topic to be blogging about.</p> <p>After blogging and writing articles at Google, I wanted to write in my own voice and showcase CSS features while talking about CSS features. I figured most people these days are scrolling cards and like content feeds, so I modelled my site after these interactions and started blogging about CSS, Javascript and HTML stuff consistently for the first time.</p> <h2> What platform are you using to manage your blog and why did you choose it? <a name="what-platform-are-you-using-to-manage-your-blog-and-why-did-you-choose-it?" href="#what-platform-are-you-using-to-manage-your-blog-and-why-did-you-choose-it?">#</a> </h2> <p>My current site/blog is built on <a href="https://deno.com/">Deno</a> and their <a href="https://fresh.deno.dev/">Fresh</a> framework. I blogged about it <a href="https://nerdy.dev/new-year-new-site">here</a>.</p> <p><picture> <source srcset="/media/fresh-features.avif" type="image/avif"> <source srcset="/media/fresh-features.webp" type="image/webp"> <img loading="lazy" src="/media/fresh-features.jpg" alt="" title="A screenshot from the features list on the Fresh website " decoding="async" width="1538" height="854" /> </picture></p> <p>For me it was a decently minimal set of features that Fresh brought while Deno brought a small, safe, web standards focused server.</p> <p>I wanted to do lots of custom things, so I needed something that wasn&#39;t going to be in the way. I wanted to make something server first, a bit more socially integrated than previous static sites I&#39;d built, and to try out islands front-end architecture.</p> <h2> How do you write your posts? <a name="how-do-you-write-your-posts?" href="#how-do-you-write-your-posts?">#</a> </h2> <p>I write posts in a markdown file with some custom frontmatter. Notes have slightly different frontmatter than blog posts, otherwise they&#39;re the same.</p> <p>I&#39;ve written all my own adapters for images, code snippets and videos. The images are swapped for a <code>&lt;picture&gt;</code> with optimised sources. The code snippets are rendered server side. Videos are also optimized, given poster images, and other cool stuff.</p> <p>All of this is done in a code editor. Currently <a href="https://zed.dev">Zed</a>, but often <a href="https://windsurf.com/editor">Windsurf</a>, <a href="https://www.sublimetext.com">Sublime</a> or <a href="https://code.visualstudio.com">VS Code</a> (I flop around alot, but usually go back to Sublime for the nicer text rendering and bootup speeds).</p> <p>If I need any special examples or interactives, I either write an inline style or script tag, or make a web component.</p> <h2> When do you feel most inspired to write? <a name="when-do-you-feel-most-inspired-to-write?" href="#when-do-you-feel-most-inspired-to-write?">#</a> </h2> <p>When reading my RSS feed. But, that&#39;s not when I write… that&#39;s once the kids are asleep.</p> <h2> Do you publish immediately? <a name="do-you-publish-immediately?" href="#do-you-publish-immediately?">#</a> </h2> <p>Longer form posts definitely simmer or chill in a branch for a while. Shorter form posts though, those can be authored and published in just a few minutes. I needed to have both opportunities, was important to me that both types of thoughts could be posted to my site.</p> <h2> What are you generally interested in writing about? <a name="what-are-you-generally-interested-in-writing-about?" href="#what-are-you-generally-interested-in-writing-about?">#</a> </h2> <p>CSS, almost exclusively. However, every once in a while I&#39;ll share a JS tip (which is usually CSS adjacent).</p> <h2> What’s your favourite post on your blog? <a name="what’s-your-favourite-post-on-your-blog?" href="#what’s-your-favourite-post-on-your-blog?">#</a> </h2> <p>There&#39;s a few that come to mind, but the winner is <a href="https://nerdy.dev/headless-boneless-and-skinless-ui">Headless, boneless and skinless UI</a>. I liked how the piece came together around Halloween when the content was totally morbib sounding out of context.</p> <h2> Who are you writing for? <a name="who-are-you-writing-for?" href="#who-are-you-writing-for?">#</a> </h2> <p>UI and UX engineers, a few designers and other folks who want to stay up to date on CSS. Mostly writing for professionals or experts, but do try to make things minimal to reduce learning friction for everyone.</p> <h2> Any future plans for your blog? <a name="any-future-plans-for-your-blog?" href="#any-future-plans-for-your-blog?">#</a> </h2> <p>I&#39;ve got quite a long list of features in the backlog.</p> <p>I&#39;d like to add a bunch more &quot;slashers&quot;, which are just cool slash pages like <a href="https://nerdy.dev/follows">/follows</a> and <a href="https://nerdy.dev/404">/404</a>. So far I&#39;ve got <code>/talks</code>, <code>/experiments</code>, <code>/avatar</code>, <code>/toolkit</code>, and <code>/links</code> in the backlog.</p> <p>I&#39;ve got the personas already, but I want to give them their own profile pages with all their specific posts. They&#39;d also get their own RSS feeds 😉</p> <p>The most notable backlog item, is to have <a href="https://www.playbalatro.com">Balatro</a> style powerups and &quot;shiny&quot; type effects for the cards on the home feed. Just a little piece of frontmatter that adds a class to the card, then CSS does the rest: makes a post foiled, or cracked, or holographic. Be so fun to make.</p> <h2> Who am I going to tag? <a name="who-am-i-going-to-tag?" href="#who-am-i-going-to-tag?">#</a> </h2> <p>Maybe you? Anyone out there want to be tagged?</p> <p>I&#39;ll tag you right here.</p> Kandidaatstelling voor AB - Hidde's blog https://hidde.blog/ab-nl/ 2025-05-01T00:00:00.000Z <p>Mijn naam is Hidde de Vries, specialist standaarden bij <a href="https://logius.nl/">Logius</a> (deel van de Rijksoverheid). Hierbij vraag ik om je stem voor mijn verkiezing in het Advisory Board van het W3C.</p> <p>Mijn enthousiasme voor het web gaat zo'n 20 jaar terug, toen ik op de middelbare school mijn eerste website maakte. Wat een mooi platform! Sinds die tijd werkte ik onder andere als webontwikkelaar en specialist digitale toegankelijkheid, waarbij ik vaak bruggen bouwde tussen beide beroepsgroepen.</p> <p>Bij het W3C was ik al eens <a href="https://w3.org/People/Hidde">onderdeel van het Team</a> (bij WAI, 2019-2022), waar ik onder andere aan informatie over toegankelijkheid werkte en aan de WCAG en ATAG Report Tools. Momenteel ben ik actief in de Open UI Community Group, verschillende toegankelijkheids-gerelateerde Working Groups en de Web Sustainability Interest Group.</p> <p>Als ik word verkozen wil ik:</p> <ul> <li><strong>interactie met het AB verbeteren</strong>: door meer contact te zoeken met Members en de bredere community, om te zorgen dat hun inbreng terugkomt in ons advies, en door te zoeken naar consensus rondom bestuurlijke vraagstukken tussen Board, TAG, Members en verschillende werkgroepen.</li> <li><strong>het W3C aantrekkelijker maken</strong>: mensen komen nu al naar W3C om aan webstandaarden te werken. Maar we kunnen een grotere groep bereiken als we onboarding verbeteren. Ik wil uitzoeken wat mensen nu tegenhoudt om te beginnen en met die informatie verbeterslagen maken.</li> <li><strong>een nieuw geluid inbrengen</strong>: ik ben niet echt nieuw in de W3C community, maar ik ben vrij nieuw in het bestuurlijke deel. Ik hoop dat die verse blik waardevol gaat zijn voor een AB dat op zoek is naar een nieuwe identiteit en missie.</li> <li><strong>de gebruiker centraal stellen</strong>: bijna te voor de hand liggend om te vermelden, gebruikers staan immers al centraal in de <a href="https://www.w3.org/TR/w3c-vision/">W3C Vision</a>, maar in de praktijk kan het gebeuren dat zaken als toegankelijkheid, internationalisatie en privacy het onderspit delven. Er is kennis van zowel techniek als gebruikers nodig, en ik wil die van mij inzetten om processen te verbeteren.</li> </ul> <p>Wat ik wil bijdragen, in willekeurige volgorde:</p> <ul> <li>specialisme in webtoegankelijkheid (langere tijd) en duurzaamheid (sinds kort). Ik leer snel en duik graag in nieuwe dingen.</li> <li>gewerkt in verschillende sectoren die het web nodig hebben en gebruiken: overheid, een browsermaker en de maker van een authoring tool (CMS).</li> <li>veel ervaring met communiceren over standaarden richting technische communities, met name de web developer community. Ook meerdere congressen georganiseerd.</li> <li>eerdere ervaring bij W3C, als Invited Expert, Member en Team.</li> <li>recente ervaring met en diepe technische kennis van web development.</li> <li>achtergrond in filosofie en (kort) kunstmatige intelligentie, ik neem dus het perspectief van de geesteswetenschappen mee, niet alleen een technische blik. Ik kan daardoor ook goed kwesties van meerdere kanten bekijken.</li> </ul> <p>Voor vragen: stuur me gerust een berichtje op W3C Slack of email me op <a href="mailto:hidde@hiddedevries.nl">hidde@hiddedevries.nl</a>.</p> <hr/> <p>Originally posted as <a href="https://hidde.blog/ab-nl/">Kandidaatstelling voor AB</a> on <a href="https://hidde.blog">Hidde's blog</a>.</p> <p><a href="mailto:hidde@hiddedevries.nl?subject=Reply%20to:%20Kandidaatstelling voor AB">Reply via email</a></p> Running for the AB - Hidde's blog https://hidde.blog/ab/ 2025-05-01T00:00:00.000Z <p>My name is Hidde de Vries, accessibility standards specialist at <a href="https://logius.nl/about-logius">Logius</a> (part of the Dutch government). I would appreciate your support of my running for the W3C's Advisory Board.</p> <p>I fell in love with the web ever since I built my first website in high school, almost 20 years ago. What a cool platform to build for! Since then, I worked professionally as a web developer and accessibility specialist, often building bridges between these fields.</p> <p>At the W3C, I've been a <a href="https://www.w3.org/People/hidde/">member of the Team</a> (at WAI, 2019-2022), where I worked on accessibility guidance, authoring tool accesibility, the <a href="https://www.w3.org/WAI/eval/report-tool/">WCAG Report Tool</a> and the <a href="https://www.w3.org/WAI/atag/report-tool/">ATAG Report Tool</a>. I'm currently a participant in the <a href="https://open-ui.org/">Open UI Community Group</a>, various accessibility-related Working Groups and the <a href="https://www.w3.org/groups/ig/sustainableweb/">Web Sustainability Interest Group</a>.</p> <p>If elected, I want to:</p> <ul> <li><strong>help improve what engagement with the AB looks like</strong>, by doing more outreach to Members and the wider community, to ensure their input is represented in our advice, and by working to find consensus on governance issues between Board, TAG, Members and groups.</li> <li><strong>make W3C more attractive</strong>; people go to the W3C for web standardisation today, but we can reach more potential if we improve onboarding. I want to find out what stops people from getting started or engaging with our work and use that to make improvements.</li> <li><strong>bring fresh perspective on the future</strong>; though I can't say I'm new to the W3C community, I am new to most governance aspects of it. My hope is this fresh perspective can be an asset to an AB that is orienting towards a new identity and mission.</li> <li><strong>advocate for users</strong>, the primary constituents of the web. Almost too obvious to call out, as users are central to the W3C vision, and who else would we advocate for? In practice, though, user needs around accessibility, internationalisation and privacy are easily overlooked. They require matching up (deep) technical specifics with user needs. I want to use my experience with both to help improve the process.</li> </ul> <p>What I'd like to bring to the AB, in no particular order:</p> <ul> <li>specialism in web accessibility (for long) and web sustainability (as of recently). Also a fast learner happy to dive into things I'm not specialised in.</li> <li>worked in a broad range of sectors that use and need the web: public sector, including government, a browser vendor and an authoring tool vendor.</li> <li>proven record of communicating about standards development to tech communities, the web developer community in particular (see my <a href="https://hidde.blog/blog">blog</a> and <a href="https://talks.hiddedevries.nl/">talks</a>). Also co-organised developer conferences.</li> <li>previous experience at W3C, as an Invited Expert, Member and Team.</li> <li>hands-on experience and deep technical knowledge of web development</li> <li>background in Philosophy and (briefly) AI, meaning I could bring a humanities perspective and not just a technological view, and that I am trained to view issues from many angles.</li> </ul> <p>Any questions: feel free to reach out via W3C Slack or email me on <a href="mailto:hidde@hiddedevries.nl">hidde@hiddedevries.nl</a>.</p> <hr/> <p>Originally posted as <a href="https://hidde.blog/ab/">Running for the AB</a> on <a href="https://hidde.blog">Hidde's blog</a>.</p> <p><a href="mailto:hidde@hiddedevries.nl?subject=Reply%20to:%20Running for the AB">Reply via email</a></p> WebDev Challenge - S2E2 - Adam Argyle https://nerdy.dev/webdev-challenge-s2e2?utm_source=rss 2025-04-29T15:45:22.000Z <img style="display: none" src="https://nerdy.dev/media/the-louigi-boards.jpg" alt="Text emphasized alt text example" height="995" width="1920" /> <p>What could you create if you had 30 minutes to plan and 4 hours to build? Me, <a href="https://www.boot.dev/teachers/lane-wagner">Lane Wagner</a>, <a href="https://www.shook.codes/">Sarah Shook</a>, Nikki Meyers, <a href="https://bento.me/shashilo">Shashi Lo</a>, and <a href="https://www.nickyt.co/">Nick Taylor</a> took on the <a href="https://www.youtube.com/playlist?list=PLz8Iz-Fnk_eTkZvSNWXW_TKZ2UwVirT2M">Web Dev Challenge</a> to find out.</p> <iframe loading="lazy" style="aspect-ratio: 16/9; inline-size: 100%;" width="560" height="315" src="https://www.youtube.com/embed/ftYmXoH0V5I?si=4rd_h-WHQMaVY9_A" 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>They picked the Napoleon Dynamite thumbnail for Lane and I 🤣</p> <div style="transform: rotateY(.5turn)"> <p><img loading="lazy" src="https://media2.giphy.com/media/v1.Y2lkPTc5MGI3NjExMjBidjVpNTE3ZTdmNnpjcndhd2N3cjJuZXA0Y21jNzJucXNyZGhrZyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/l3vRmoOpMBTLrPEsw/giphy.gif" alt="" title="Napoleon Dynamite doing butterfly in sign language " decoding="async" width="486" height="280" /></p> </div> <h2> The Challenge <a name="the-challenge" href="#the-challenge">#</a> </h2> <p>We lucked out and got a rad one.</p> <p><q>Single player, multiplayer, cooperative, competitive, or something totally different — your challenge is to come up with <b>something fun that is played across at least two devices</b>.<br><br><a href="https://codetv.link/temporal">Temporal</a>’s workflow tools will allow you to manage sending information between devices dependably.</q></p> <p>Lane and I def <a href="https://youtu.be/ftYmXoH0V5I?si=wTkfiVPB_ko8gDvP&t=140">leaned into the &quot;fun&quot; portion</a> of the challenge.</p> <p>We chose to try a game that uses the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Media_Capture_and_Streams_API">camera video stream API</a> to feed faces to <a href="https://ai.google.dev/edge/mediapipe/solutions/guide">Media Pipe</a> (locally running a face detection machine learning model) to flap a bird&#39;s wings in a flappybird-like game.</p> <p><picture> <source srcset="/media/codetv-worktogether.avif" type="image/avif"> <source srcset="/media/codetv-worktogether.webp" type="image/webp"> <img loading="lazy" src="/media/codetv-worktogether.jpg" alt="" title="Lane and I working at a white board " decoding="async" width="1920" height="1280" /> </picture></p> <p>A Temporal <a href="https://learn.temporal.io/courses/interacting_with_workflows/">workflow</a> will capture the game round and let us see all the player moves.</p> <p><picture> <source srcset="/media/codetv-planning.avif" type="image/avif"> <source srcset="/media/codetv-planning.webp" type="image/webp"> <img loading="lazy" src="/media/codetv-planning.jpg" alt="" title="Lane and I working at a white board " decoding="async" width="1440" height="960" /> </picture></p> <p>At least that&#39;s what we planned on…</p> <picture> <source srcset="https://fonts.gstatic.com/s/e/notoemoji/latest/1f605/512.webp" type="image/webp"> <img src="https://fonts.gstatic.com/s/e/notoemoji/latest/1f605/512.gif" alt="😅" width="100" height="100"> </picture> <h2> The Teams <a name="the-teams" href="#the-teams">#</a> </h2> <p>These were the lovely folks I got to spend 4 hours with:</p> <ol> <li><a href="https://www.shook.codes/">Sarah Shook</a> &amp;&amp; Nikki Meyers</li> <li><a href="https://bento.me/shashilo">Shashi Lo</a> &amp;&amp; <a href="https://www.nickyt.co/">Nick Taylor</a></li> <li><a href="https://www.boot.dev/teachers/lane-wagner">Lane Wagner</a> &amp;&amp; I</li> </ol> <h2> The Tools <a name="the-tools" href="#the-tools">#</a> </h2> <p><a href="https://codetv.link/temporal">Temporal</a> was pretty cool, but as you&#39;ll see in the video… we um, didn&#39;t figure that out til a bit later.</p> <p><picture> <source srcset="/media/benq-codetv.avif" type="image/avif"> <source srcset="/media/benq-codetv.webp" type="image/webp"> <img loading="lazy" src="/media/benq-codetv.jpg" alt="" title="Me intently looking at the BenQ screen in the CodeTV studio " decoding="async" width="1920" height="853" /> </picture></p> <p>I did get the pleasure to use a <a href="https://benqurl.biz/4bTNsnD">BenQ RD280U</a> tho.</p> <p>The 4k 3:2 matte UHD display was really nice. They say it&#39;s made for coding, and I see what they mean.</p> <p><picture> <source srcset="/media/benq-code.avif" type="image/avif"> <source srcset="/media/benq-code.webp" type="image/webp"> <img loading="lazy" src="/media/benq-code.jpg" alt="" title="Code shown on the BenQ " decoding="async" width="1280" height="853" /> </picture></p> <p>Plus, HDR content is awesome, and I could capture high quality UI captures and place lots of windows around.</p> <p>I got to take one home and try it out. It&#39;s light years ahead of the one I had before.</p> <p><picture> <source srcset="/media/benq-display.avif" type="image/avif"> <source srcset="/media/benq-display.webp" type="image/webp"> <img loading="lazy" src="/media/benq-display.jpg" alt="" title="The monitor on my desk " decoding="async" width="1920" height="1440" /> </picture></p> <p>The arm is super strong. There&#39;s tons of charging ports. It&#39;s a really great product.</p> <h2> 3D Bird <a name="3d-bird" href="#3d-bird">#</a> </h2> <p>Here&#39;s that 3D bird <a href="https://codepen.io/argyleink/pen/xbxXPKJ">Codepen</a> that&#39;s featured in the episode.</p> <p> <iframe class="codepen-embed" scrolling="no" title="null" src="https://codepen.io/argyleink/embed/preview/xbxXPKJ?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/xbxXPKJ"> by Adam Argyle (<a href="https://codepen.io/argyleink">@argyleink</a>) on <a href="https://codepen.io">CodePen</a>. </iframe> </p> <h2> The Results <a name="the-results" href="#the-results">#</a> </h2> <p>There were no winners or losers!</p> <p><picture> <source srcset="/media/codetv-team.avif" type="image/avif"> <source srcset="/media/codetv-team.webp" type="image/webp"> <img loading="lazy" src="/media/codetv-team.jpg" alt="" title="The team " decoding="async" width="1440" height="960" /> </picture></p> <p>We all had a blast; I&#39;m lookin forward to seein the <a href="https://www.youtube.com/watch?v=ftYmXoH0V5I&list=PLz8Iz-Fnk_eTkZvSNWXW_TKZ2UwVirT2M&index=2">YouTube comments</a>, maybe a <a href="https://codepen.io/argyleink/pen/xbxXPKJ">3D bird</a> joke or two.</p> Zen Garden Leave - Adam Argyle https://nerdy.dev/zen-garden-leave?utm_source=rss 2025-04-23T17:21:16.000Z <img style="display: none" src="https://nerdy.dev/media/zen-garden-leave.png" alt="CSS Zen Garden" height="1530" width="2002" /> <p><strong>CSS Zen</strong> <a href="https://en.wikipedia.org/wiki/Garden_leave"><strong>Garden</strong> Leave</a>…</p> <p>Taking time, breathing slow, clearing my head, tending to a <a href="https://codepen.io/argyleink/pen/QwwdjJq">brutalist garden on Codepen</a>.</p> <p><small>Seems appropriate.</small></p> Carousels On Shoptalk - Adam Argyle https://nerdy.dev/carousels-on-shoptalk?utm_source=rss 2025-04-07T14:21:51.000Z <img style="display: none" src="https://res.cloudinary.com/dnpmdb8r8/image/upload//shoptalk-carousels.jpg" alt="some title" height="720" width="1280" /> <p>CSS <a href="https://drafts.csswg.org/css-overflow-5/#scroll-buttons"><code>::scroll-button()</code></a>, <a href="https://drafts.csswg.org/css-overflow-5/#scroll-markers"><code>::scroll-marker</code></a>, <a href="https://chrome.dev/carousel-configurator/">Carousel Configurator</a>, and <a href="https://chrome.dev/carousel/">Carousel Gallery</a> with <a href="https://chriscoyier.net">Chris</a> and <a href="https://daverupert.com">Dave</a> on <a href="https://shoptalkshow.com">ShopTalk Show</a>.</p> <p><a href="https://www.youtube.com/watch?v=YhN177ixoJk">Watch</a> · <a href="https://shoptalkshow.com/659/">Listen</a> · <a href="https://developer.chrome.com/blog/carousels-with-css">Read</a></p> CSS Can Do That At Smashing Meets - Adam Argyle https://nerdy.dev/css-can-do-that-at-smashing-meets?utm_source=rss 2025-04-05T18:12:17.000Z <img style="display: none" src="https://res.cloudinary.com/dnpmdb8r8/image/upload/argyleink/smashing-meets-css.jpg" alt="CSS Can We Do That at Smashing Meets CSS" height="720" width="1280" /> <p>Recording is live!</p> <p>Watch <a href="https://www.youtube.com/watch?v=we2wmGKDyhY&list=PLxQqv_fazRs1VlMEbqC0Jlw6VFd97UpT2&index=3">CSS Can We Do That on YouTube</a> via <a href="https://smashingconf.com/meets-modern-css/speakers/adam-argyle/">Smashing Meets CSS</a> online event.</p> CSS Mixins are ready for experimentation! - Adam Argyle https://nerdy.dev/css-mixins-ready-for-experimentation?utm_source=rss 2025-03-26T14:43:21.000Z <img style="display: none" src="https://res.cloudinary.com/dnpmdb8r8/image/upload/argyleink/css-mixin-experiment.png" alt="@mixin --box { aspect-ratio: 1; inline-size: 100px; block-size: 100px; } .box { @apply --box; }" height="848" width="1716" /> <p>CSS <a href="https://drafts.csswg.org/css-mixins-1/">Mixins</a> and <a href="https://developer.chrome.com/blog/delaying-shipping-of-css-functions">Functions</a> are both ready for you to hack on 🤯. I think of functions as returning a value and mixins as a way to apply a chunk of styles.</p> <p>Bramus has a great intro on <a href="https://www.bram.us/2025/02/09/css-custom-functions-teaser/"><code>@function</code></a>, don&#39;t miss it. But this post is about <code>@mixin</code>, don&#39;t miss <a href="https://css.oddbird.net/sasslike/mixins-functions/">the explainer</a>, it is packed with valuable information:</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)">@mixin</span><span style="color:var(--shiki-foreground)"> --box {</span></span> <span class="line"><span style="color:var(--shiki-token-string-expression)"> aspect-ratio</span><span style="color:var(--shiki-foreground)">: 1;</span></span> <span class="line"><span style="color:var(--shiki-token-string-expression)"> inline-size</span><span style="color:var(--shiki-foreground)">: 100px;</span></span> <span class="line"><span style="color:var(--shiki-token-string-expression)"> block-size</span><span style="color:var(--shiki-foreground)">: 100px;</span></span> <span class="line"><span style="color:var(--shiki-foreground)">}</span></span></code></pre> </code></pre> <p>and using it:</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-function)">.box</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)">apply</span><span style="color:var(--shiki-foreground)"> --box;</span></span> <span class="line"><span style="color:var(--shiki-foreground)">}</span></span></code></pre> </code></pre> <h2> Getting started <a name="getting-started" href="#getting-started">#</a> </h2> <p>For CSS Mixins as of writing this, you&#39;ll need <a href="https://www.google.com/chrome/canary/">Canary</a> and you&#39;ll need to <a href="https://developer.chrome.com/docs/web-platform/chrome-flags#command-line_flags">launch it from the command line</a> with this flag <code>CSSMixins</code>:</p> <pre><code class="language-sh"><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)">open</span><span style="color:var(--shiki-token-string)"> -a</span><span style="color:var(--shiki-token-string-expression)"> "Google Chrome Canary"</span><span style="color:var(--shiki-token-string)"> --args</span><span style="color:var(--shiki-token-string)"> --enable-features=CSSMixins</span></span></code></pre> </code></pre> <h2> Go make stuff! <a name="go-make-stuff!" href="#go-make-stuff!">#</a> </h2> <p>You have all the latest information now:</p> <ul> <li>how to enable Canary to understand mixins</li> <li>what the basic syntax is like</li> <li>links to <a href="https://css.oddbird.net/sasslike/mixins-functions/">more advanced examples and documentation</a></li> </ul> Customize A Select - Adam Argyle https://nerdy.dev/customize-a-select?utm_source=rss 2025-03-24T21:22:23.000Z <video style="display: none" src="https://res.cloudinary.com/dnpmdb8r8/video/upload/f_auto,w_auto,q_auto/argyleink/select-hype.gif" alt="A quick cut style video of a bunch of carousels from the Carousel Gallery" height="1080" width="1920" /> <p>Get ready! <a href="https://developer.chrome.com/blog/a-customizable-select">The <code>&lt;select&gt;</code> element can now be customized with CSS</a>, check out the post on the <a href="https://developer.chrome.com/blog/">Chrome Blog</a>!</p> <ul> <li>Finally render custom HTML inside an <code>&lt;option&gt;</code></li> <li>Works without JS</li> <li>Easy to progressively enhance</li> </ul> CSS Carousel In Chrome 135 - Adam Argyle https://nerdy.dev/css-carousel-in-chrome-135?utm_source=rss 2025-03-20T16:26:21.000Z <video style="display: none" src="https://res.cloudinary.com/dnpmdb8r8/video/upload/f_auto,w_auto,q_auto/argyleink/carousel-hype.gif" alt="A quick cut style video of a bunch of carousels from the Carousel Gallery" height="1080" width="1920" /> <p>We just <a href="https://developer.chrome.com/blog/carousels-with-css">announced how CSS can make carousels</a> on the <a href="https://developer.chrome.com/blog/">Chrome Blog</a>!</p> <ul> <li>Incredible accessibility</li> <li>Works without JS</li> <li>Builds upon regular scrollers</li> </ul> <p><a href="https://chrome.dev/carousel-configurator/"><strong>CSS Carousel Configurator</strong></a>:<br> I made this so you can directly see the relationship of a feature you want and the related CSS.</p> <p><a href="https://chrome.dev/carousel/"><strong>CSS Carousel Gallery</strong></a>:<br> Showcases meaningful use cases and exemplifies how to orchestrate many capabilities together.</p> Rainbow Shadow Button - Adam Argyle https://nerdy.dev/rainbow-shadow-button?utm_source=rss 2025-03-19T15:42:28.000Z <img style="display: none" src="https://res.cloudinary.com/dnpmdb8r8/image/upload/v1742399078/argyleink/rainbow-shadow-button.png" alt="A black button with a rainbow blurred shadow behind it" height="332" width="702" /> <p>Add a rainbow shadow effect with <a href="https://developer.chrome.com/docs/css-ui/access-colors-spaces#control_interpolation"><code>hue longer</code></a> interpolation and a little bit of <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/filter-function/blur"><code>blur()</code></a>.</p> <p>Try it in <a href="https://codepen.io/argyleink/pen/zxYWPxr">this Codepen</a></p>