<header>
<section label="accounts">
<button style="--img: url(https://assets.codepen.io/2585/gui-skull.png)">
Kirby
</button>
</section>
<section label="status bar">
<datetime>11:11<small>AM</small></datetime>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24">
<label>Wifi Connected</label>
<path fill="currentColor" d="M12 21q-1.05 0-1.775-.725T9.5 18.5t.725-1.775T12 16t1.775.725t.725 1.775t-.725 1.775T12 21m0-11q1.875 0 3.563.6t3.062 1.65q.5.375.513.988T18.7 14.3q-.425.425-1.05.438t-1.125-.338q-.95-.65-2.1-1.025T12 13t-2.425.375t-2.1 1.025q-.5.35-1.125.325t-1.05-.45q-.425-.45-.425-1.062t.5-.988q1.375-1.05 3.063-1.638T12 10m0-6q3.125 0 5.888 1.025t4.962 2.9q.5.425.525 1.05t-.425 1.075q-.425.425-1.05.438t-1.125-.388q-1.8-1.475-4.037-2.287T12 7t-4.737.813T3.225 10.1q-.5.4-1.125.388t-1.05-.438Q.6 9.6.625 8.975t.525-1.05q2.2-1.875 4.963-2.9T12 4"/>
</svg>
<span class="battery">
<p>50<small>%</small></p>
<svg aria-hidden xmlns="http://www.w3.org/2000/svg" width="38" height="38" viewBox="0 0 24 24"><path fill="currentColor" d="M5.289 16.116q-.344 0-.576-.233t-.232-.575v-1.616H3v-3.384h1.48v-1.62q0-.344.233-.574q.232-.23.576-.23h14.903q.344 0 .576.233t.232.575v6.613q0 .35-.232.58t-.576.23zm.173-1H13V8.885H5.462z"/></svg>
</span>
</section>
</header>
<main class="scroll--root">
<div id="scroller" class="scroll--viewport">
<ul class="scroll--content">
<li style="--i: 1">
<div class="game">
<figure>
<picture>
<img src="https://assets.codepen.io/2585/Splatoon_2.jpg" alt="">
</picture>
<figcaption>Splatoon 2</figcaption>
</figure>
</div>
</li>
<li style="--i: 2">
<div class="game">
<figure>
<picture>
<img src="https://assets.codepen.io/2585/super-smash.jpg" alt="">
</picture>
<figcaption>Super Smash Bros.</figcaption>
</figure>
</div>
</li>
<li style="--i: 3">
<div class="game">
<figure>
<picture>
<img src="https://assets.codepen.io/2585/botw.webp" alt="">
</picture>
<figcaption>Zelda: BOTW</figcaption>
</figure>
</div>
</li>
<li style="--i: 4">
<div class="game">
<figure>
<picture>
<img src="https://assets.codepen.io/2585/Animal_Crossing_New_Horizons.jpg" alt="">
</picture>
<figcaption>Animal Crossing</figcaption>
</figure>
</div>
</li>
<li style="--i: 5">
<div class="game">
<figure>
<picture>
<img src="https://assets.codepen.io/2585/Kirby_Star_Allies.jpg" alt="">
</picture>
<figcaption>Kirby Star Allies</figcaption>
</figure>
</div>
</li>
<li style="--i: 6">
<div class="game">
<figure>
<picture>
<img src="https://assets.codepen.io/2585/totk.webp" alt="">
</picture>
<figcaption>Zelda: TOTK</figcaption>
</figure>
</div>
</li>
<li style="--i: 7">
<div class="game">
<figure>
<picture>
<img src="https://assets.codepen.io/2585/minecraft.jpg" alt="">
</picture>
<figcaption>Minecraft</figcaption>
</figure>
</div>
</li>
</ul>
</div>
</main>
<footer>
<button class="online">
<svg aria-hidden xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24">
<path fill="currentColor" d="M10.04 20.4H7.12c-.93 0-1.82-.4-2.48-1.04C4 18.7 3.6 17.81 3.6 16.88V7.12c0-.93.4-1.82 1.04-2.48C5.3 4 6.19 3.62 7.12 3.62h2.92zM7.12 2A5.12 5.12 0 0 0 2 7.12v9.76C2 19.71 4.29 22 7.12 22h4.53V2zM5.11 8c0 1.04.84 1.88 1.89 1.88c1.03 0 1.87-.84 1.87-1.88S8.03 6.12 7 6.12c-1.05 0-1.89.84-1.89 1.88m12.5 3c1.11 0 2.01.89 2.01 2c0 1.12-.9 2-2.01 2s-2.03-.88-2.03-2c0-1.11.92-2 2.03-2m-.73 11A5.12 5.12 0 0 0 22 16.88V7.12C22 4.29 19.71 2 16.88 2h-3.23v20z"/>
</svg>
Online
</button>
<button>
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24">
<label>Social</label>
<g fill="none">
<path fill="#d2705d" d="m13.087 21.388l.645.382zm.542-.916l-.646-.382zm-3.258 0l-.645.382zm.542.916l.646-.382zm-8.532-5.475l.693-.287zm5.409 3.078l-.013.75zm-2.703-.372l-.287.693zm16.532-2.706l.693.287zm-5.409 3.078l-.012-.75zm2.703-.372l.287.693zm.7-15.882l-.392.64zm1.65 1.65l.64-.391zM4.388 2.738l-.392-.64zm-1.651 1.65l-.64-.391zM9.403 19.21l.377-.649zm4.33 2.56l.541-.916l-1.29-.764l-.543.916zm-4.007-.916l.542.916l1.29-.764l-.541-.916zm2.715.152a.52.52 0 0 1-.882 0l-1.291.764c.773 1.307 2.69 1.307 3.464 0zM10.5 2.75h3v-1.5h-3zm10.75 7.75v1h1.5v-1zm-18.5 1v-1h-1.5v1zm-1.5 0c0 1.155 0 2.058.05 2.787c.05.735.153 1.347.388 1.913l1.386-.574c-.147-.352-.233-.782-.278-1.441c-.046-.666-.046-1.51-.046-2.685zm6.553 6.742c-1.256-.022-1.914-.102-2.43-.316L4.8 19.313c.805.334 1.721.408 2.977.43zM1.688 16.2A5.75 5.75 0 0 0 4.8 19.312l.574-1.386a4.25 4.25 0 0 1-2.3-2.3zm19.562-4.7c0 1.175 0 2.019-.046 2.685c-.045.659-.131 1.089-.277 1.441l1.385.574c.235-.566.338-1.178.389-1.913c.05-.729.049-1.632.049-2.787zm-5.027 8.241c1.256-.021 2.172-.095 2.977-.429l-.574-1.386c-.515.214-1.173.294-2.428.316zm4.704-4.115a4.25 4.25 0 0 1-2.3 2.3l.573 1.386a5.75 5.75 0 0 0 3.112-3.112zM13.5 2.75c1.651 0 2.837 0 3.762.089c.914.087 1.495.253 1.959.537l.783-1.279c-.739-.452-1.577-.654-2.6-.752c-1.012-.096-2.282-.095-3.904-.095zm9.25 7.75c0-1.622 0-2.891-.096-3.904c-.097-1.023-.299-1.862-.751-2.6l-1.28.783c.285.464.451 1.045.538 1.96c.088.924.089 2.11.089 3.761zm-3.53-7.124a4.25 4.25 0 0 1 1.404 1.403l1.279-.783a5.75 5.75 0 0 0-1.899-1.899zM10.5 1.25c-1.622 0-2.891 0-3.904.095c-1.023.098-1.862.3-2.6.752l.783 1.28c.464-.285 1.045-.451 1.96-.538c.924-.088 2.11-.089 3.761-.089zM2.75 10.5c0-1.651 0-2.837.089-3.762c.087-.914.253-1.495.537-1.959l-1.279-.783c-.452.738-.654 1.577-.752 2.6C1.25 7.61 1.25 8.878 1.25 10.5zm1.246-8.403a5.75 5.75 0 0 0-1.899 1.899l1.28.783a4.25 4.25 0 0 1 1.402-1.403zm7.02 17.993c-.202-.343-.38-.646-.554-.884a2.2 2.2 0 0 0-.682-.645l-.754 1.297c.047.028.112.078.224.232c.121.166.258.396.476.764zm-3.24-.349c.44.008.718.014.93.037c.198.022.275.054.32.08l.754-1.297a2.2 2.2 0 0 0-.909-.274c-.298-.033-.657-.038-1.069-.045zm6.498 1.113c.218-.367.355-.598.476-.764c.112-.154.177-.204.224-.232l-.754-1.297c-.29.17-.5.395-.682.645c-.173.238-.352.54-.555.884zm1.924-2.612c-.412.007-.771.012-1.069.045c-.311.035-.616.104-.909.274l.754 1.297c.045-.026.122-.058.32-.08c.212-.023.49-.03.93-.037z"/><path stroke="#d2705d" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 11h.009m3.982 0H12m3.991 0H16"/>
</g>
</svg>
</button>
<button>
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24">
<path fill="#ebb54a" d="M18 6h-2c0-2.21-1.79-4-4-4S8 3.79 8 6H6c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2m-6-2c1.1 0 2 .9 2 2h-4c0-1.1.9-2 2-2m6 16H6V8h2v2c0 .55.45 1 1 1s1-.45 1-1V8h4v2c0 .55.45 1 1 1s1-.45 1-1V8h2z"/>
</svg>
</button>
<button>
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24">
<path fill="#4e9cf2" d="M3.08 5L3 5.08V19h17.92c.03-.02.06-.06.08-.08V5.08L20.92 5zM5 17l3.5-4.5l2.5 3.01L14.5 11l4.5 6z" opacity="0"/><path fill="#4e9cf2" d="M21 3H3C2 3 1 4 1 5v14c0 1.1.9 2 2 2h18c1 0 2-1 2-2V5c0-1-1-2-2-2m0 15.92c-.02.03-.06.06-.08.08H3V5.08L3.08 5h17.83c.03.02.06.06.08.08v13.84zm-10-3.41L8.5 12.5L5 17h14l-4.5-6z"/>
</svg>
</button>
<button>
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 512 512">
<path d="M194.822 240H160v-34.762c0-2.817-2.604-5.238-5.303-5.238h-22.145c-2.698 0-4.552 2.372-4.552 5.178V240H92.533c-2.615 0-4.533 2.143-4.533 4.771v21.427c0 2.698 2.031 5.803 4.861 5.803H128v35.467c0 2.615 2.143 4.533 4.771 4.533h21.427c2.698 0 5.803-2.031 5.803-4.861V272h34.762c2.817 0 5.238-2.604 5.238-5.303v-22.145c-.001-2.698-2.373-4.552-5.179-4.552z" fill="currentColor"/><path d="M362.314 279.063c-10.736 0-19.451 8.515-19.451 19.153 0 10.555 8.715 19.084 19.451 19.084 10.783 0 19.498-8.529 19.498-19.084 0-10.638-8.714-19.153-19.498-19.153z" fill="currentColor"/><path d="M319.766 237.165c-10.736 0-19.451 8.603-19.451 19.168 0 10.566 8.715 19.153 19.451 19.153 10.783 0 19.498-8.587 19.498-19.153 0-10.565-8.715-19.168-19.498-19.168z" fill="currentColor"/><path d="M404.862 237.165c-10.729 0-19.442 8.603-19.442 19.168 0 10.566 8.714 19.153 19.442 19.153 10.737 0 19.452-8.587 19.452-19.153 0-10.565-8.714-19.168-19.452-19.168z" fill="currentColor"/><path d="M362.314 195.301c-10.736 0-19.451 8.602-19.451 19.169 0 10.566 8.715 19.146 19.451 19.146 10.783 0 19.498-8.579 19.498-19.146 0-10.568-8.714-19.169-19.498-19.169z" fill="currentColor"/><path d="M369.269 162c25.838 0 49.704 9.314 67.204 26.229C454.225 205.386 464 229.247 464 255.418c0 26.303-9.806 50.405-27.611 67.869-17.563 17.226-41.4 26.713-67.12 26.713h-226.53c-25.712 0-49.548-9.487-67.117-26.715C57.81 305.818 48 281.717 48 255.418c0-26.167 9.78-50.028 27.539-67.188 17.504-16.915 41.37-26.23 67.2-26.23h226.53m0-16h-226.53C81.541 146 32 191.458 32 255.418 32 319.419 81.541 366 142.739 366h226.529C430.49 366 480 319.419 480 255.418 480 191.458 430.49 146 369.269 146z" fill="currentColor"/>
</svg>
</button>
<button>
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24">
<path fill="currentColor" d="M19.5 12c0-.23-.01-.45-.03-.68l1.86-1.41c.4-.3.51-.86.26-1.3l-1.87-3.23a.987.987 0 0 0-1.25-.42l-2.15.91c-.37-.26-.76-.49-1.17-.68l-.29-2.31c-.06-.5-.49-.88-.99-.88h-3.73c-.51 0-.94.38-1 .88l-.29 2.31c-.41.19-.8.42-1.17.68l-2.15-.91c-.46-.2-1-.02-1.25.42L2.41 8.62c-.25.44-.14.99.26 1.3l1.86 1.41a7.3 7.3 0 0 0 0 1.35l-1.86 1.41c-.4.3-.51.86-.26 1.3l1.87 3.23c.25.44.79.62 1.25.42l2.15-.91c.37.26.76.49 1.17.68l.29 2.31c.06.5.49.88.99.88h3.73c.5 0 .93-.38.99-.88l.29-2.31c.41-.19.8-.42 1.17-.68l2.15.91c.46.2 1 .02 1.25-.42l1.87-3.23c.25-.44.14-.99-.26-1.3l-1.86-1.41c.03-.23.04-.45.04-.68m-7.46 3.5c-1.93 0-3.5-1.57-3.5-3.5s1.57-3.5 3.5-3.5s3.5 1.57 3.5 3.5s-1.57 3.5-3.5 3.5"/>
</svg>
</button>
<button>
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24">
<path fill="currentColor" d="M11 13V3h2v10zm1 8q-1.85 0-3.488-.712T5.65 18.35t-1.937-2.863T3 12q0-2 .825-3.775T6.15 5.15l1.4 1.4q-1.2 1-1.875 2.425T5 12q0 2.9 2.05 4.95T12 19q2.925 0 4.963-2.05T19 12q0-1.6-.663-3.025T16.45 6.55l1.4-1.4q1.5 1.3 2.325 3.075T21 12q0 1.85-.713 3.488t-1.925 2.862t-2.85 1.938T12 21"/>
</svg>
</button>
</footer>
@layer support, lessons;
@import "https://unpkg.com/open-props" layer(support.design-system);
@import "https://unpkg.com/open-props/normalize.min.css" layer(support.demo);
@import "https://unpkg.com/open-props/buttons.min.css" layer(support.demo);
/*
HTML Structure, 3 elements
.scroll--root
> .scroll--viewport
> .scroll--content
> YOUR_CONTENT
*/
@layer lessons.essentials {
/*
- always specify both axis to prevent surprises
- always specify overscroll behavior on the scrolling axis
- always match your padding with your scroll-padding (for snap)
- respect motion prefs when setting scroll-behavior
*/
.scroll--viewport {
display: flex;
place-items: center start;
overflow: auto hidden;
overscroll-behavior-x: contain;
padding-inline: var(--size-8);
scroll-padding-inline: var(--size-8);
@media (prefers-reduced-motion: no-preference) {
scroll-behavior: smooth;
}
}
}
@layer lessons.colorize {
/*
- webkit scrollbars for more customization (Chromium, Safari)
- scrollbar-color (Firefox, exclusively not Chromium even tho it has support)
- most mobile browser scrollbars completely ignore all this
*/
.scroll--viewport {
/* we'll be showing our own scroller focus states */
&:is(:focus-visible, :focus-within) {
outline-offset: -2px;
outline: none;
}
/* to be fair, Switch doesn't show it at all */
&::-webkit-scrollbar {
height: 10px;
}
/* transparent track is usually best */
&::-webkit-scrollbar-track {
background: #0000;
/* so we can push away from the edge/border */
background-clip: padding-box;
}
&::-webkit-scrollbar-thumb {
/* create 50% opacity variant of a design system surface color */
background: color-mix(in srgb,
light-dark(
var(--surface-4),
var(--surface-2)
),
#0000 50%
);
/* maximum radius */
border-radius: 1e3px;
/* creates illusion of being inset, matches background */
/* also free cool looking forced colors mode styles */
border: 3px solid var(--surface-1);
}
/* highlight thumb to indicate focus is inside */
&:is(:focus-visible, :focus-within)::-webkit-scrollbar-thumb {
background: var(--link);
}
/* if user can hover, we can make it respond to hover */
@media (hover) {
/* make the thumb color solid on hover for full legibility */
&::-webkit-scrollbar-thumb:hover {
background: light-dark(var(--surface-4), var(--surface-2));
}
/* default now to 50% faded out */
&::-webkit-scrollbar {
opacity: .5;
}
/* hover full opacity */
/* many native OS's wait for hover on the thumb, could try that */
&::-webkit-scrollbar:hover {
opacity: 1;
}
}
/* styles for Firefox */
/* this can't be @supports (scrollbar-color: white black) */
/* or not (scrollbar-color: white black) */
/* chromium is eager to switch to scrollbar-* and will drop all webkit-scrollbar
styles if there's any scent of the standard ones
*/
/* we can still provide a very nice experience for keyboard, mouse */
@supports (-moz-appearance:none) {
scrollbar-width: thin;
scrollbar-color: var(--surface-4) #0000;
transition: scrollbar-color .3s ease;
&:is(:focus-visible, :focus-within) {
scrollbar-color: var(--link) #0000;
}
@media (hover) {
scrollbar-color:
color-mix(in srgb, var(--surface-4), #0000 50%)
#0000;
&:hover {
scrollbar-color: var(--surface-4) #0000;
}
}
}
}
}
@layer lessons.containerize {
/*
- scrollers are often fixed / extrinsically sized
- this one fills vertical space between
the intrinsically sized header and footer
- children want to use @container
- scroll-state snapped, stuck, or overflowing can be queried
*/
.scroll--root {
container: --main / size;
}
.scroll--viewport {
container: --main-scroller / size scroll-state;
}
.scroll--content > li {
/* children will use snapped scroll-state() */
container-type: scroll-state;
}
}
@layer lessons.scroll-snap {
/*
- perfect resting alignments
- enables scroll-state(snapped) for showing
- highlight
- game title
- enables overscroll elastic effect
*/
.scroll--viewport {
scroll-snap-type: x mandatory;
}
.scroll--content > li {
scroll-snap-align: start;
}
}
@layer lessons.scroll-state {
/*
- upgrade if supported
- highlight snapped game art
- animate game title
*/
/* pulsing blue outline color */
@keyframes game-focus {
from {
outline-color: hsl(200 100% 50%);
}
to {
outline-color: hsl(200 100% 80%);
}
}
/* adjust space for features */
.scroll--viewport {
@supports (container-type: scroll-state) {
padding-inline: var(--size-9);
scroll-padding-inline: var(--size-9);
}
}
.game {
/* img child asks nearest scroll-state container
? are you snapped
*/
img {
@container scroll-state(snapped: x) {
/* create outline */
outline: 5px solid blue;
outline-offset: 5px;
/* infinite outline animation */
animation: game-focus .5s var(--ease-3) alternate infinite;
}
}
/* if motion is ok, animate game titles */
figcaption {
@media (prefers-reduced-motion: no-preference) {
z-index: -1;
transition:
translate .5s var(--ease-spring-2) .2s,
opacity .5s var(--ease-3) .2s;
/* you = nearest scroll-state container */
/* if you are not snapped… then hide yo'self! */
/* if you are snapped, stay visible! */
@container not scroll-state(snapped: x) {
translate: 0 50px;
opacity: 0;
}
/* saves from writing the out and in,
since in is the natural state we just tell things
what to do when they're out
*/
}
}
}
}
@layer lessons.overscroll-effect {
/*
- some OSs have overscroll effects built in
- this add some some fun to overscrolling
- also provides a hint that you're at the end
- this works because of scroll-snap-stop: x mandatory
- the scroller MUST snap on something
- we'll be creating pseudo elements that ARE NOT snaps
- so while we can scroll to see them, they'll never snap
*/
.scroll--content {
/* flex is often best for this
we dont need columns, we need a row of intrinsic sized items
*/
display: flex;
/* our overscroll effect sentinels */
&::before,
&::after {
/* empty nodes to consume space */
content: "";
display: block;
/* how much space to consume */
inline-size: 25cqi;
/* uhuh, can't shrink me */
flex-shrink: 0;
}
/* the after in this one is hella big so
the last item can be snapped/highlighted/title shown
*/
&::after {
inline-size: 90cqi;
}
}
}
@layer lessons.scroll-animation {
/*
- subtle but effective exit scrollport animation
- stylistically no entry animations
- animation-range and view(x) make this easy
*/
/* just the to keyframe effect */
@keyframes leaving-scroller {
to {
opacity: .2;
}
}
/* each game, if motion is coo and browser has support… */
.scroll--content .game {
@media (prefers-reduced-motion: no-preference) {
@supports (animation-timeline: view(x)) {
/* attach an animation to each game */
animation: leaving-scroller linear both;
/* power it with horizontal scroll viewport intersection */
animation-timeline: view(x);
/* clamp the timing to the last 50% of the element leaving */
animation-range: exit 50% exit 100%;
}
}
}
}
@layer lessons.loading-animation {
/*
- extra flair, introduce the elements
- only if it's cool with the user's pref
- would love to have used sibling-index()
- magic is mostly from @starting-style
*/
.scroll--content .game {
@media (prefers-reduced-motion: no-preference) {
transition: transform 1s var(--ease-spring-2);
transition-delay: calc(var(--i) * .05s);
@starting-style {
transform: translateX(100vw) translateZ(calc(var(--i) * -1px));
}
}
}
}
@layer support.demo {
body {
block-size: 100lvh;
display: grid;
grid-template-rows: auto 1fr min-content;
}
header {
display: flex;
place-content: space-between;
padding-block: var(--size-fluid-3);
padding-inline: var(--size-8);
@media (height < 450px) {
padding-block: var(--size-2);
}
> [label="accounts"] {
button {
padding: 2px;
aspect-ratio: 1;
border-radius: var(--radius-round);
inline-size: var(--size-8);
text-indent: 100cqi;
overflow: hidden;
background-image:
radial-gradient(#0000 65%, var(--surface-1) 0),
var(--img);
background-size: cover;
@media (height < 450px) {
inline-size: var(--size-7);
}
}
}
> [label="status bar"] {
display: flex;
place-items: center;
gap: var(--size-4);
datetime {
font-weight: bold;
font-size: var(--font-size-2);
}
}
.battery {
display: flex;
place-items: center;
gap: var(--size-1);
}
}
.scroll--root {
display: grid;
}
.scroll--viewport {
/* undo what normalize sets on the document */
/* if not undone, webkit selectors wont match */
scrollbar-color: auto;
scrollbar-width: auto;
}
.scroll--content {
display: flex;
list-style-type: none;
gap: var(--size-3);
margin: 0;
padding: 0;
> li {
padding: 0;
flex-shrink: 0;
}
}
.game {
figure {
display: flex;
flex-direction: column-reverse; /* caption on top */
gap: 1ch;
@supports (container-type: scroll-state) {
gap: var(--size-4);
}
img {
background: var(--surface-2);
block-size: 70cqb;
inline-size: auto;
aspect-ratio: 1;
object-fit: cover;
}
}
figcaption {
text-box: cap alphabetic;
text-align: center;
white-space: nowrap;
font-size: max(var(--font-size-4), 5cqb);
font-weight: 500;
letter-spacing: var(--font-letterspacing-2);
color: light-dark(var(--blue-7), var(--blue-3));
@media (height < 450px) {
font-size: .9rem;
}
}
}
footer {
display: flex;
place-content: center;
padding: var(--size-fluid-3);
gap: var(--size-3);
max-inline-size: 100vw;
overflow-x: auto;
@media (height < 450px) {
padding: var(--size-fluid-2);
}
button {
flex-shrink: 0;
inline-size: var(--size-9);
block-size: var(--size-9);
border-radius: var(--radius-round);
border-color: transparent;
padding: 0;
@media (height < 450px) {
inline-size: var(--size-8);
block-size: var(--size-8);
}
> svg {
inline-size: 60%;
height: auto;
}
&.online {
background: red;
color: white;
font-size: var(--font-size-00);
text-shadow: none;
text-transform: uppercase;
display: grid;
place-items: center;
place-content: center;
gap: 0;
> svg {
width: 80%;
height: auto;
}
}
}
}
}
const sounds = {
snap: new Audio('https://assets.codepen.io/2585/snap.mp3'),
pop: new Audio('https://assets.codepen.io/2585/pop.m4a'),
}
scroller.onclick = event => {
let target = event.target.closest('.game')
if (target) {
scroller.scrollTo({
left: target.offsetLeft,
behavior: 'smooth'
})
}
}
scroller.onscrollsnapchanging = event => {
navigator?.vibrate(50)
sounds.pop.play()
}
scroller.onscrollsnapchange = event => {
sounds.snap.play()
}
View Compiled
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.