Color Switcher using light-dark(): https://codepen.io/fbernack/pen/myePZYR
Popover API: https://codepen.io/fbernack/pen/rNgmexV

Popover Example by Arby: https://codepen.io/mobalti/pen/EaYVJgr

Relative Color Syntax: https://codepen.io/fbernack/pen/jEbegJg
—base: #7c3aed;

Align content vertically: https://codepen.io/fbernack/pen/EaVYrrg

Form with contenteditable toggle: https://codepen.io/fbernack/pen/GgpeqpY
@property Example: https://codepen.io/fbernack/pen/QwjZpNz

cod
<h1>Event Highlights</h1>
<div id="highlights" class="shelf">
<div class="tile" style="background-image:url('https://images.unsplash.com/photo-1619229665876-f54b2276b7bd?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D');"></div>
<div class="tile" style="background-image:url('https://images.unsplash.com/photo-1629276301687-be2af9fd6ba8?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D');"></div>
<div class="tile" style="background-image:url('https://images.unsplash.com/photo-1598387993441-a364f854c3e1?q=80&w=2076&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D');"></div>
</div>
<h2>On the Weekend</h2>
<div class="shelf">
<div class="tile" style="background-image:url('https://images.unsplash.com/photo-1507808973436-a4ed7b5e87c9?q=80&w=1480&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D');"></div>
<div class="tile" style="background-image:url('https://images.unsplash.com/photo-1415201364774-f6f0bb35f28f?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D');"></div>
<div class="tile" style="background-image:url('https://images.unsplash.com/photo-1544013360-983116d2a733?q=80&w=1674&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D');"></div>
<div class="tile" style="background-image:url('https://images.unsplash.com/photo-1485872299829-c673f5194813?q=80&w=1460&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D');"></div>
<div class="tile" style="background-image:url('https://images.unsplash.com/photo-1429962714451-bb934ecdc4ec?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D');"></div>
</div>
@property --tile {
syntax: "<length>";
inherits: false;
initial-value: 50px;
}
body {
font-family: system-ui, sans-serif;
}
.shelf {
display: flex;
gap: 1rem;
margin-bottom: 2rem;
}
.tile {
width: var(--tile);
height: var(--tile);
border-radius: 1rem;
background: lightgrey;
background-position: center;
background-size: cover;
}
#highlights .tile {
--tile: 100px;
}
@property Animating Gradient: https://codepen.io/fbernack/pen/EaVKzvw

cod
<div class="wrap"> <div class="box old" data-label="without @property"></div> <div class="box reg" data-label="with @property"></div> </div>
:root {
--c1: hsl(210 90% 62%); /* blue */
--c2: hsl(280 70% 72%); /* purple */
--blend: 10%;
}
.wrap {
display: flex;
justify-content: space-between;
gap: 1.25rem;
max-width: 460px;
margin: 2rem auto;
font-family: system-ui, sans-serif;
}
.box {
width: 200px;
height: 200px;
border-radius: 16px;
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.12);
position: relative;
overflow: hidden;
}
.box::after {
content: attr(data-label);
position: absolute;
left: 8px;
bottom: 8px;
font-size: 12px;
opacity: 0.75;
background: rgba(255, 255, 255, 0.6);
padding: 0.15rem 0.45rem;
border-radius: 0.4rem;
-webkit-backdrop-filter: blur(6px);
backdrop-filter: blur(6px);
}
/* LEFT BOX: old way -> stop jumps */
.old {
--old-stop: 20%;
background: linear-gradient(
135deg,
var(--c1) 0%,
var(--c1) calc(var(--old-stop) - var(--blend)),
var(--c2) calc(var(--old-stop) + var(--blend)),
var(--c2) 100%
);
transition: --old-stop 0.5s ease; /* discrete jump */
}
.old:hover {
--old-stop: 60%;
}
/* RIGHT BOX: stop animates smoothly */
@property --new-stop {
syntax: "<percentage>";
inherits: false;
initial-value: 20%;
}
.reg {
--new-stop: 20%;
background: linear-gradient(
135deg,
var(--c1) 0%,
var(--c1) calc(var(--new-stop) - var(--blend)),
var(--c2) calc(var(--new-stop) + var(--blend)),
var(--c2) 100%
);
transition: --new-stop 0.5s ease; /* smooth animation */
}
.reg:hover {
--new-stop: 60%;
}
Exclusive Accordion: https://codepen.io/fbernack/pen/ogjXXdj

cod
<div class="wrapper">
<div class="box">
<h1>The Accordion</h1>
<p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.</p>
<details name="my-accordion">
<summary>Lorem ipsum dolor sit amet</summary>
<p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.</p>
</details>
<details name="my-accordion" open>
<summary>Consetetur sadipscing elitr</summary>
<p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.</p>
</details>
<details name="my-accordion">
<summary>Sed diam nonumy</summary>
<p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.</p>
</details>
</div>
</div>
* {
font-family: sans-serif;
box-sizing: border-box;
}
body {
margin: 0;
body: 0;
}
.wrapper {
justify-content: center;
align-items: center;
display: flex;
min-height: 100vh;
background: #eee;
padding: 2rem;
}
.box {
width: 800px;
background: #fff;
border-radius: 2rem;
padding: 3rem 6rem;
}
summary,
details {
margin-bottom: 1em;
}
summary {
font-weight: bold;
cursor: pointer;
&::marker {
content: "";
}
&::before {
content: "+";
width: 2rem;
height: 2rem;
display: inline-block;
background: #333;
border-radius: 1rem;
text-align: center;
line-height: 2rem;
color: #fff;
font-weight: 100;
margin-right: 1rem;
}
}
details {
border-bottom: 1px solid #ccc;
&[open] summary::before {
content: "–";
}
}
Horizontal Exclusive Accordion by web.dev: https://codepen.io/web-dot-dev/pen/XWvBZNo

cod
<script>
document.documentElement.dataset.embed = window.location.search.includes('type=embed') ? "yep" : "nope";
</script>
<main>
<h1>Styling <code><details></code>: Horizontal Accordion</h1>
<p class="warning">Your browser does not support <code>::details-content</code>, so this demo won’t work as intended. Please use a browser with support – such as Chrome 131 or newer – to check out this page.</p>
<h2>Demo</h2>
<div id="demo">
<div class="accordion-wrapper">
<details name="accordion" id="boating" open>
<summary>
<span>🚤</span>
<!-- Source: https://unsplash.com/photos/white-sail-boat-on-sea-during-daytime-hXPG0Bk6JiM -->
<img draggable=false src="https://assets.codepen.io/5928893/lazarescu-alexandra-hXPG0Bk6JiM-unsplash.jpg" alt="">
</summary>
<div class="details-content-wrapper">
<h3>Boating</h3>
<p>Port mutiny draught handsomely ye furl flogging line shrouds hulk. Spirits Plate Fleet code of conduct.</p>
</div>
</details>
<details name="accordion" id="anchoring">
<summary>
<span>⚓️</span>
<!-- Source: https://unsplash.com/photos/black-metal-chain-on-grey-concrete-floor-3NjBnYmbsDg -->
<img draggable=false src="https://assets.codepen.io/5928893/sophie-walker-3NjBnYmbsDg-unsplash.jpg" alt="">
</summary>
<div class="details-content-wrapper">
<h3>Anchoring</h3>
<p>Ahoy league hands Sea Legs keelhaul salmagundi fire ship crimp Privateer galleon. Booty boom yard boatswain quarter.</p>
</div>
</details>
<details name="accordion" id="fishing">
<summary>
<span>🎣</span>
<!-- https://unsplash.com/photos/person-holding-black-fishing-rod-6_HqvY1E7NI -->
<img draggable=false src="https://assets.codepen.io/5928893/mathieu-le-roux-6_HqvY1E7NI-unsplash.jpg" alt="">
</summary>
<div class="details-content-wrapper">
<h3>Fishing</h3>
<p>No prey, no pay heave down splice the main brace furl cable snow walk the plank chase guns piracy bucko.</p>
</div>
</details>
<details name="accordion" id="lighthouses">
<summary>
<span>🔦</span>
<!-- https://unsplash.com/photos/a-white-and-red-lighthouse-sitting-on-top-of-a-cliff-3ml9wykJCWQ -->
<img draggable=false src="https://assets.codepen.io/5928893/kamy-3ml9wykJCWQ-unsplash.jpg" alt="">
</summary>
<div class="details-content-wrapper">
<h3>Lighthouses</h3>
<p>Deadlights squiffy salmagundi cable pinnace parrel topsail Corsair Arr mizzenmast.</p>
</div>
</details>
<details name="accordion" id="reefs">
<summary>
<span>🪸</span>
<!-- https://unsplash.com/photos/gray-and-black-fish-under-water-hqAGgNsMpEY -->
<img draggable=false src="https://assets.codepen.io/5928893/francesco-ungaro-hqAGgNsMpEY-unsplash.jpg" alt="">
</summary>
<div class="details-content-wrapper">
<h3>Reefs</h3>
<p>Keel yard poop deck brigantine gaff six pounders bring a spring
upon her cable interloper lad pink.</p>
</div>
</details>
</div>
</div>
<p>Demo for <a href="https://developer.chrome.com/blog/styling-details" target="_top">https://developer.chrome.com/blog/styling-details</a>, built after <a href="https://codepen.io/jakob-e/full/MWzRjNe" target="_top">https://codepen.io/jakob-e/full/MWzRjNe</a></p>
<h2>The Code</h2>
<h3>UI</h3>
<pre><code>.accordion-wrapper {
display: flex;
flex-direction: row;
gap: 1rem;
}
details {
display: flex;
flex-direction: row;
height: 30rem;
border-radius: 2rem;
overflow: hidden;
/* To make the image positioning work …*/
position: relative;
z-index: 1;
/* Hide marker */
::marker {
content: '';
}
/* The image is tucked in the summary, because otherwise it would be hidden when not [open] as it ends up in the ::details-content pseudo */
summary img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
z-index: -1;
transition: filter 0.5s ease;
}
/* Animate the image */
&[open] summary img {
filter: brightness(0.5);
}
summary {
padding: 1rem 1em;
width: 5rem;
flex-shrink: 0; /* Prevent shrinking */
text-align: center;
}
.details-content-wrapper {
padding: 1.5rem 1em;
width: 300px; /* Fixate the width of the content so that the text doesn’t wrap/unwrap when expanding the details */
}
}
.details-content-wrapper {
/* Animate-in the text when open */
p {
transform: translateY(2rem);
opacity: 0;
transition: all 0.5s ease;
transition-delay: 0.5s;
}
[open] & p {
transform: none;
opacity: 1;
transition-delay: 0.5s;
}
}</code></pre>
<h3>Animation</h3>
<pre><code>/* Animation */
::details-content {
transition: width 0.5s ease, content-visibility 0.5s ease allow-discrete;
width: 0;
}
[open]::details-content {
width: 300px;
}</code></pre>
</main>
@supports selector(::details-content) {
.warning {
display: none;
}
}
.accordion-wrapper {
display: flex;
flex-direction: row;
gap: 1rem;
width: min-content;
margin: 0 auto;
}
details {
display: flex;
flex-direction: row;
background: transparent;
color: white;
height: 30rem;
border-radius: 2rem;
overflow: hidden;
/* To make the image work …*/
position: relative;
z-index: 1;
--open-size: min(30vw, 300px);
/* Hide marker */
::marker {
content: '';
}
/* The image is tucked in the summary, because otherwise it would be hidden when not [open] as it ends up in the ::details-content pseudo */
summary img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
z-index: -1;
transition: filter 0.5s ease;
}
/* Animate the image */
&[open] summary img {
filter: brightness(0.5);
}
summary {
padding: 1rem 1em;
width: 5rem;
flex-shrink: 0; /* Prevent shrinking */
text-align: center;
span {
display: grid;
place-content: center;
width: 100%;
aspect-ratio: 1;
border-radius: 50%;
background: rgb(0 0 0 / 0.25);
}
&:focus {
outline: none;
}
}
.details-content-wrapper {
padding: 1.5rem 1em;
width: var(--open-size);
}
&:hover, &:has(summary:focus) {
outline: 3px solid cadetblue;
outline-offset: 3px;
}
}
.details-content-wrapper {
/* We need margin-trim … */
:first-child { margin-top: 0; }
:last-child { margin-bottom: 0; }
/* Animate-in the text when open */
p {
transform: translateY(2rem);
opacity: 0;
transition: all 0.5s ease;
transition-delay: 0.5s;
}
[open] & p {
transform: none;
opacity: 1;
transition-delay: 0.5s;
@starting-style {
transform: translateY(2rem);
opacity: 0;
}
}
}
/* Animation */
::details-content {
transition: width 0.5s ease, content-visibility 0.5s ease allow-discrete;
width: 0;
}
[open]::details-content {
width: var(--open-size);
}
@layer reset {
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html,
body {
height: 100%;
}
html {
font-size: 14px;
}
}
@layer baselayout {
html {
background: white;
color: #444;
font-family: system-ui;
line-height: 1.42;
overscroll-behavior-x: none;
overflow: auto;
}
main {
max-width: 90ch;
margin: 0 auto;
padding-bottom: 10rem;
}
p {
margin-bottom: 1em;
}
h1, h2 {
margin: 4em 0 1em;
}
h3 {
margin: 1em 0 0.5em;
}
#demo {
padding: 1em;
border: 1px solid #ccc;
background: #f4f6f9;
}
}
@layer code {
pre {
border: 1px solid #dedede;
padding: 1em;
background: #f7f7f7;
font-family: "Courier 10 Pitch", Courier, monospace;
overflow-x: auto;
border-left: 0.4em solid cornflowerblue;
tab-size: 4;
}
code:not(pre code) {
background: #f7f7f7;
border: 1px solid rgb(0 0 0 / 0.2);
padding: 0.1rem 0.3rem;
margin: 0.1rem 0;
border-radius: 0.2rem;
/* display: inline-block; */
-webkit-box-decoration-break: clone;
white-space: pre-wrap;
}
}
@layer warning {
.warning {
box-sizing: border-box;
padding: 1em;
margin: 1em 0;
border: 1px solid #ccc;
background: rgba(255 255 205 / 0.8);
}
.warning > :first-child {
margin-top: 0;
}
.warning > :last-child {
margin-bottom: 0;
}
.warning a {
color: blue;
}
.warning--info {
border: 1px solid #123456;
background: rgb(205 230 255 / 0.8);
}
.warning--alarm {
border: 1px solid red;
background: #ff000010;
}
}
/* Hide a bunch of stuff when embedded */
:root[data-embed="yep"] {
main {
padding: 2em 1em;
:not(p.warning, p.warning *, #demo, #demo *, #demo ~ p, #demo ~ p *) {
display: none;
}
}
#demo ~ p {
margin-top: 1em;
text-align: center;
}
}
Backdrop-Filter Liquid Glass Effect: https://codepen.io/fbernack/pen/VYvEWeZ

cod
<div class="wrapper">
<div class="liquid-glass-button">
<div class="inner">
Hover me
</div>
</div>
</div>
body {
font-family: system-ui, sans-serif;
margin: 0;
padding: 0;
}
.wrapper {
background-image: url("https://picsum.photos/id/484/2400/1600");
background-size: cover;
background-repeat: no-repeat;
background-position: center 50%;
height: 100vh;
align-content: center;
justify-items: center;
}
.liquid-glass-button {
cursor: pointer;
font-size: 2rem;
color: #fff;
box-shadow: 0 6px 6px rgba(0, 0, 0, 0.3), 0 0 20px rgba(0, 0, 0, 0.2);
border-radius: 2rem;
will-change: transform;
transition: transform 450ms cubic-bezier(0.34, 1.56, 0.64, 1);
&:hover {
transform: scale(1.2);
}
.inner {
border-radius: 2rem;
backdrop-filter: blur(3px);
padding: 1rem 2rem;
box-shadow: inset 2px 2px 1px 0 rgba(255, 255, 255, 0.4),
inset -1px -1px 1px 1px rgba(255, 255, 255, 0.4);
}
}
body {
font-family: system-ui, sans-serif;
margin: 0;
padding: 0;
}
.wrapper {
background-image: url("https://picsum.photos/id/484/2400/1600");
background-size: cover;
background-repeat: no-repeat;
background-position: center 50%;
height: 100vh;
align-content: center;
justify-items: center;
}
.liquid-glass-button {
cursor: pointer;
font-size: 2rem;
color: #fff;
box-shadow: 0 6px 6px rgba(0, 0, 0, 0.3), 0 0 20px rgba(0, 0, 0, 0.2);
border-radius: 2rem;
will-change: transform;
transition: transform 450ms cubic-bezier(0.34, 1.56, 0.64, 1);
&:hover {
transform: scale(1.2);
}
.inner {
border-radius: 2rem;
backdrop-filter: blur(3px);
padding: 1rem 2rem;
box-shadow: inset 2px 2px 1px 0 rgba(255, 255, 255, 0.4),
inset -1px -1px 1px 1px rgba(255, 255, 255, 0.4);
}
}
Backdrop-Filter Gallery: https://codepen.io/fbernack/pen/WbQQvwR

cod
<div class="container">
<div class="filter-card">
<span>blur(2px)</span>
<div class="img"><img class="img-bg" src="https://picsum.photos/id/110/200/300" alt="" />
<div class="img-overlay" style="backdrop-filter: blur(2px);"></div>
</div>
</div>
<div class="filter-card">
<span>brightness(60%)</span>
<div class="img"><img class="img-bg" src="https://picsum.photos/id/110/200/300" alt="" />
<div class="img-overlay" style="backdrop-filter: brightness(60%);"></div>
</div>
</div>
<div class="filter-card">
<span>contrast(40%)</span>
<div class="img"><img class="img-bg" src="https://picsum.photos/id/110/200/300" alt="" />
<div class="img-overlay" style="backdrop-filter: contrast(40%);"></div>
</div>
</div>
<div class="filter-card">
<span>drop-shadow(4px 4px 10px blue)</span>
<div class="img"><img class="img-bg" src="https://picsum.photos/id/110/200/300" alt="" />
<div class="img-overlay" style="backdrop-filter: drop-shadow(4px 4px 10px blue);"></div>
</div>
</div>
<div class="filter-card">
<span>grayscale(30%)</span>
<div class="img"><img class="img-bg" src="https://picsum.photos/id/110/200/300" alt="" />
<div class="img-overlay" style="backdrop-filter: grayscale(30%);"></div>
</div>
</div>
<div class="filter-card">
<span>hue-rotate(120deg)</span>
<div class="img"><img class="img-bg" src="https://picsum.photos/id/110/200/300" alt="" />
<div class="img-overlay" style="backdrop-filter: hue-rotate(120deg);"></div>
</div>
</div>
<div class="filter-card">
<span>invert(70%)</span>
<div class="img"><img class="img-bg" src="https://picsum.photos/id/110/200/300" alt="" />
<div class="img-overlay" style="backdrop-filter: invert(70%);"></div>
</div>
</div>
<div class="filter-card">
<span>opacity(20%)</span>
<div class="img"><img class="img-bg" src="https://picsum.photos/id/110/200/300" alt="" />
<div class="img-overlay" style="backdrop-filter: opacity(20%);"></div>
</div>
</div>
<div class="filter-card">
<span>sepia(90%)</span>
<div class="img"><img class="img-bg" src="https://picsum.photos/id/110/200/300" alt="" />
<div class="img-overlay" style="backdrop-filter: sepia(90%);"></div>
</div>
</div>
<div class="filter-card">
<span>saturate(80%)</span>
<div class="img"><img class="img-bg" src="https://picsum.photos/id/110/200/300" alt="" />
<div class="img-overlay" style="backdrop-filter: saturate(80%);"></div>
</div>
</div>
</div>
.container {
display: flex;
flex-wrap: wrap;
gap: 2rem;
justify-content: center;
}
.filter-card {
display: flex;
flex-direction: column;
align-items: center;
width: 260px;
}
.filter-card span {
margin-bottom: 0.5rem;
font-size: 1rem;
text-align: center;
}
.img {
width: 240px;
height: 160px;
border-radius: 1rem;
overflow: hidden;
position: relative;
}
.img .img-bg {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 1rem;
z-index: 1;
}
.img .img-overlay {
position: absolute;
inset: 0;
z-index: 2;
background: rgba(255,255,255,0.3);
pointer-events: none;
width: 50%;
right: 0;
left: auto;
border-top-right-radius: 1rem;
border-bottom-right-radius: 1rem;
}
Scrollbar-gutter & scrollbar-width: https://codepen.io/fbernack/pen/KwdrpQM


cod
<div class="wrap">
<div class="title">
<h1>scrollbar-gutter & scrollbar-width</h1>
</div>
<div class="controls" role="group" aria-label="Controls">
<label><input id="use-gutter" type="checkbox"> Use <code>scrollbar-gutter: stable</code></label>
<button id="toggle" type="button">Toggle content</button>
<fieldset class="sw" role="radiogroup" aria-label="Scrollbar width">
<label><input type="radio" name="sw" value="auto" checked> auto</label>
<label><input type="radio" name="sw" value="thin"> thin</label>
<label><input type="radio" name="sw" value="none"> none</label>
</fieldset>
</div>
<section class="card">
<h2>Scrollable area</h2>
<div id="scroller" class="scroller">
<div id="tiles" class="tiles" data-cols="6"></div>
</div>
</section>
<small class="hint">Note: On systems with overlay scrollbars (e.g., macOS), the visible difference can be smaller.</small>
</div>
:root {
--line: #e5e7eb;
--muted: #6b7280;
--tile-gap: 12px;
}
* {
box-sizing: border-box;
}
html,
body {
height: 100%;
}
body {
margin: 0;
font-family: system-ui, sans-serif;
background: #f8fafc;
display: grid;
place-items: center;
padding: 24px;
}
.wrap {
width: min(800px, 100%);
display: grid;
gap: 16px;
}
.title {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 12px;
}
.title h1 {
font-size: 18px;
margin: 0;
}
.title small {
color: var(--muted);
}
.controls {
display: flex;
gap: 12px;
align-items: center;
flex-wrap: wrap;
}
button {
appearance: none;
border: 1px solid var(--line);
background: white;
padding: 8px 10px;
border-radius: 10px;
cursor: pointer;
}
label {
display: inline-flex;
gap: 6px;
align-items: center;
}
fieldset.sw {
display: flex;
gap: 8px;
margin: 0;
padding: 0;
border: 0;
}
.card {
background: white;
border: 1px solid var(--line);
border-radius: 12px;
padding: 12px;
display: grid;
gap: 8px;
}
.card h2 {
margin: 0;
font-size: 16px;
}
.scroller {
height: 320px;
overflow: auto;
border: 1px solid var(--line);
border-radius: 10px;
resize: vertical;
}
.scroller.gutter {
scrollbar-gutter: stable;
}
.scroller.thin {
scrollbar-width: thin;
}
.scroller.none {
scrollbar-width: none;
}
.tiles {
--tile: 80px;
display: flex;
flex-wrap: wrap;
gap: var(--tile-gap);
padding: 12px;
}
.tile {
flex: 0 0 auto;
width: var(--tile);
height: var(--tile);
border-radius: 8px;
border: 1px solid var(--line);
display: grid;
place-items: center;
font: 600 12px/1 ui-monospace, Menlo, Consolas, monospace;
color: var(--muted);
user-select: none;
background: #fff;
}
.tile:nth-child(6) {
background: red;
color: #FFF;
}
.hint {
color: var(--muted);
font-size: 12px;
}
const scroller = document.getElementById("scroller");
const tiles = document.getElementById("tiles");
const toggle = document.getElementById("toggle");
const cbGutter = document.getElementById("use-gutter");
const swRadios = document.querySelectorAll('input[name="sw"]');
let long = false; // false → 7 tiles (one row), true → many tiles (overflow)
function buildTiles(count) {
tiles.innerHTML = "";
const frag = document.createDocumentFragment();
for (let i = 1; i <= count; i++) {
const d = document.createElement("div");
d.className = "tile";
d.textContent = i;
frag.appendChild(d);
}
tiles.appendChild(frag);
}
function parseGapPx(el) {
const cs = getComputedStyle(el);
const gap = (cs.gap || cs.columnGap || "0px").split(" ")[0];
return parseFloat(gap);
}
// Baseline tile width is computed WITHOUT gutter so enabling it causes a wrap/shift
function setTileSizeBaseline() {
const cols = parseInt(tiles.dataset.cols || "6", 10);
const hadGutter = scroller.classList.contains("gutter");
if (hadGutter) scroller.classList.remove("gutter");
const cs = getComputedStyle(tiles);
const padX = parseFloat(cs.paddingLeft) + parseFloat(cs.paddingRight);
const gap = parseGapPx(tiles);
const width = tiles.clientWidth - padX;
const tile = Math.floor((width - gap * (cols - 1)) / cols);
tiles.style.setProperty("--tile", tile + "px");
if (hadGutter) scroller.classList.add("gutter");
}
function setContent() {
const cols = parseInt(tiles.dataset.cols, 10);
buildTiles(long ? cols * 8 : cols * 1);
}
function applyScrollbarWidth(value) {
scroller.classList.remove("thin", "none");
if (value === "thin") scroller.classList.add("thin");
if (value === "none") scroller.classList.add("none");
}
// Events
toggle.addEventListener("click", () => {
long = !long;
setContent();
});
cbGutter.addEventListener("change", (e) => {
scroller.classList.toggle("gutter", e.target.checked);
if (!long) {
setTileSizeBaseline();
setContent();
}
});
swRadios.forEach((r) =>
r.addEventListener("change", (e) => {
if (!e.target.checked) return;
applyScrollbarWidth(e.target.value);
if (!long && !cbGutter.checked) {
setTileSizeBaseline();
setContent();
}
})
);
window.addEventListener("resize", () => {
if (!long) {
setTileSizeBaseline();
setContent();
}
});
setTileSizeBaseline();
setContent();
MDN target-text Demo: https://mdn.github.io/css-examples/target-text/index.html#:~:text=Space%20forbids,first%20sight
cod
Открываем страницу с примером поверх данного окна!
Работает только на CSS!
Мы использовали связку checkbox + label.