Back to Top
A round button that shows up a screen down, its ring filling as you read. Its label unrolls on hover, the arrow takes off when you click.
View codeA section that pins itself while its cards slide sideways as you scroll down. Pictures drift in their frames, the counter rolls. A swipe on touch.
With your AI
Say it to your AI. It gets the exact code below, not its best guess, and knows where it goes.
Show my case studies in a Kodza Horizontal Scroll section.
claude mcp add -t http kodza https://kodza.dev/api/mcp
<section class="kodza-hscroll" aria-label="Selected work">
<div class="kodza-hscroll__sticky">
<div class="kodza-hscroll__head">
<h2 class="kodza-hscroll__title">Six shapes, one scroll.</h2>
<p class="kodza-hscroll__count" aria-hidden="true"><span class="kodza-hscroll__num">01</span><span class="kodza-hscroll__total">/ 06</span></p>
</div>
<div class="kodza-hscroll__viewport">
<ul class="kodza-hscroll__track">
<li class="kodza-hscroll__item"><a class="kodza-hscroll__card" href="#"><span class="kodza-hscroll__media"><img src="https://kodza.dev/media/kz-steps-1400.webp" alt=""></span><span class="kodza-hscroll__meta"><span class="kodza-hscroll__name">Steps</span><span class="kodza-hscroll__tag">grid-auto-flow: row</span></span></a></li>
<li class="kodza-hscroll__item"><a class="kodza-hscroll__card" href="#"><span class="kodza-hscroll__media"><img src="https://kodza.dev/media/kz-block-1400.webp" alt=""></span><span class="kodza-hscroll__meta"><span class="kodza-hscroll__name">Block</span><span class="kodza-hscroll__tag">display: block</span></span></a></li>
<li class="kodza-hscroll__item"><a class="kodza-hscroll__card" href="#"><span class="kodza-hscroll__media"><img src="https://kodza.dev/media/kz-stack-1400.webp" alt=""></span><span class="kodza-hscroll__meta"><span class="kodza-hscroll__name">Stack</span><span class="kodza-hscroll__tag">z-index: 9999</span></span></a></li>
<li class="kodza-hscroll__item"><a class="kodza-hscroll__card" href="#"><span class="kodza-hscroll__media"><img src="https://kodza.dev/media/kz-float-1400.webp" alt=""></span><span class="kodza-hscroll__meta"><span class="kodza-hscroll__name">Float</span><span class="kodza-hscroll__tag">position: absolute</span></span></a></li>
<li class="kodza-hscroll__item"><a class="kodza-hscroll__card" href="#"><span class="kodza-hscroll__media"><img src="https://kodza.dev/media/kz-arch-1400.webp" alt=""></span><span class="kodza-hscroll__meta"><span class="kodza-hscroll__name">Arch</span><span class="kodza-hscroll__tag">border-radius: 50%</span></span></a></li>
<li class="kodza-hscroll__item"><a class="kodza-hscroll__card" href="#"><span class="kodza-hscroll__media"><img src="https://kodza.dev/media/kz-columns-1400.webp" alt=""></span><span class="kodza-hscroll__meta"><span class="kodza-hscroll__name">Columns</span><span class="kodza-hscroll__tag">gap: 1rem</span></span></a></li>
</ul>
</div>
<div class="kodza-hscroll__progress" aria-hidden="true"><span></span></div>
</div>
</section>
/* Kodza License: free to use, client work included. Not to resell or redistribute, ported or not: https://kodza.dev/docs/#print-license */
/* Kodza — Horizontal Scroll
A section that pins itself while its track of cards slides sideways as you scroll down:
each picture drifts a little inside its frame, the counter rolls to the card in front,
and a thin bar follows. Tabbing to a card off screen scrolls the page to it. On touch
screens it is a plain sideways swipe with snap points, no pinning.
Everything is tuned with the --kz-hscroll-* variables. */
.kodza-hscroll {
--kz-hscroll-fg: #0a0a0a;
--kz-hscroll-muted: rgba(10, 10, 10, 0.62);
--kz-hscroll-line: rgba(10, 10, 10, 0.12);
--kz-hscroll-card: 46vh;
--kz-hscroll-gap: 16px;
--kz-hscroll-gutter: max(24px, calc((100vw - 1080px) / 2));
--kz-hscroll-radius: 16px;
--kz-hscroll-drift: 8%;
position: relative;
height: var(--kz-hscroll-height, auto);
color: var(--kz-hscroll-fg);
}
.kodza-hscroll__sticky {
position: sticky;
top: 0;
display: grid;
grid-template-columns: minmax(0, 1fr);
grid-template-rows: auto 1fr auto;
gap: 32px;
height: 100vh;
padding: 10vh 0 8vh;
overflow: hidden;
box-sizing: border-box;
}
.kodza-hscroll__head {
display: flex;
align-items: flex-end;
justify-content: space-between;
gap: 24px;
padding: 0 var(--kz-hscroll-gutter);
}
.kodza-hscroll__title {
margin: 0;
font-size: clamp(32px, 4.4vw, 62px);
font-weight: 600;
line-height: 1;
letter-spacing: -0.045em;
}
.kodza-hscroll__count {
display: flex;
gap: 6px;
margin: 0;
font-family: ui-monospace, "SF Mono", Menlo, monospace;
font-size: 12px;
font-variant-numeric: tabular-nums;
}
.kodza-hscroll__num {
display: inline-grid;
overflow: hidden;
}
.kodza-hscroll__num > span {
grid-area: 1 / 1;
}
.kodza-hscroll__total {
color: var(--kz-hscroll-muted);
}
/* The track only moves with transform */
.kodza-hscroll__viewport {
min-height: 0;
}
.kodza-hscroll__track {
display: flex;
gap: var(--kz-hscroll-gap);
width: max-content;
height: 100%;
margin: 0;
padding: 0 var(--kz-hscroll-gutter);
list-style: none;
transform: translate3d(calc(var(--kz-hscroll-x, 0) * -1px), 0, 0);
will-change: transform;
}
.kodza-hscroll__item {
height: 100%;
}
.kodza-hscroll__card {
display: grid;
grid-template-rows: 1fr auto;
gap: 14px;
height: 100%;
border-radius: var(--kz-hscroll-radius);
color: inherit;
text-decoration: none;
}
.kodza-hscroll__card:focus-visible {
outline: 2px solid var(--kz-hscroll-fg);
outline-offset: 4px;
}
.kodza-hscroll__media {
display: block;
width: var(--kz-hscroll-card);
min-height: 0;
overflow: hidden;
border-radius: var(--kz-hscroll-radius);
}
/* The picture drifts inside its frame, against the movement of the track */
.kodza-hscroll__media img {
display: block;
width: calc(100% + var(--kz-hscroll-drift) * 2);
max-width: none;
height: 100%;
margin-left: calc(var(--kz-hscroll-drift) * -1);
object-fit: cover;
transform: translate3d(calc(var(--kz-drift, 0) * var(--kz-hscroll-drift)), 0, 0) scale(1);
transition: transform 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}
.kodza-hscroll__card:hover .kodza-hscroll__media img {
transform: translate3d(calc(var(--kz-drift, 0) * var(--kz-hscroll-drift)), 0, 0) scale(1.04);
}
.kodza-hscroll__meta {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 12px;
}
.kodza-hscroll__name {
font-size: 18px;
font-weight: 600;
letter-spacing: -0.02em;
}
.kodza-hscroll__tag {
font-family: ui-monospace, "SF Mono", Menlo, monospace;
font-size: 12px;
color: var(--kz-hscroll-muted);
}
.kodza-hscroll__progress {
height: 1px;
margin: 0 var(--kz-hscroll-gutter);
background: var(--kz-hscroll-line);
}
.kodza-hscroll__progress span {
display: block;
height: 100%;
background: var(--kz-hscroll-fg);
transform: scaleX(var(--kz-hscroll-progress, 0));
transform-origin: left;
}
/* Touch screens: a plain sideways swipe with snap points */
.kodza-hscroll.is-swipe {
height: auto;
}
.kodza-hscroll.is-swipe .kodza-hscroll__sticky {
position: relative;
height: auto;
padding: 0;
overflow: visible;
}
.kodza-hscroll.is-swipe .kodza-hscroll__viewport {
overflow-x: auto;
scroll-snap-type: x mandatory;
scroll-padding-inline: var(--kz-hscroll-gutter);
scrollbar-width: none;
}
.kodza-hscroll.is-swipe .kodza-hscroll__track {
height: auto;
transform: none;
}
.kodza-hscroll.is-swipe .kodza-hscroll__item {
scroll-snap-align: start;
}
.kodza-hscroll.is-swipe .kodza-hscroll__media {
width: min(78vw, 360px);
aspect-ratio: 4 / 5;
}
@media (prefers-reduced-motion: reduce) {
.kodza-hscroll__media img {
transform: none !important;
transition: none !important;
}
}
/* Theming example — on a dark background:
.dark-section .kodza-hscroll {
--kz-hscroll-fg: #f2f2f0;
--kz-hscroll-muted: rgba(242, 242, 240, 0.62);
--kz-hscroll-line: rgba(255, 255, 255, 0.16);
}
*/
/* kodza-guard cf50e1203d2b — generated by Kodza, do not edit.
Theme guard: keeps a site's own CSS (WordPress themes, Bootstrap, CSS resets…) off this
component. :not(#_) is always true and weighs as much as an id, so these lines win over
theme rules made of classes and tags. Customise the component through its --kz-* variables. */
/* What the browser gives, where the component sets it only in some states: at the lowest weight, so its own rules below put it back */
:where(.kodza-hscroll__card):not(#_) {
outline: revert;
outline-offset: revert;
}
/* What the browser gives, on everything the component leaves to it */
.kodza-hscroll:not(#_),
.kodza-hscroll__head:not(#_),
.kodza-hscroll__num:not(#_),
.kodza-hscroll__total:not(#_),
.kodza-hscroll__viewport:not(#_),
.kodza-hscroll__media:not(#_),
.kodza-hscroll__meta:not(#_),
.kodza-hscroll__name:not(#_),
.kodza-hscroll__tag:not(#_),
.kodza-hscroll__progress:not(#_),
.kodza-hscroll__num span:where(:not([class*="kodza-"])):not(#_),
.kodza-hscroll__progress span:where(:not([class*="kodza-"])):not(#_) {
box-sizing: revert;
}
.kodza-hscroll__title:not(#_) {
border: revert;
padding: revert;
border-radius: revert;
outline: revert;
text-decoration: revert;
transition: revert;
list-style: revert;
box-sizing: revert;
float: revert;
position: revert;
background-color: revert;
background-image: revert;
box-shadow: revert;
outline-offset: revert;
appearance: revert;
max-width: revert;
max-height: revert;
color: revert;
font-family: revert;
font-style: revert;
text-transform: revert;
text-underline-offset: revert;
text-align: revert;
text-indent: revert;
text-shadow: revert;
vertical-align: revert;
}
.kodza-hscroll__count:not(#_) {
border: revert;
padding: revert;
border-radius: revert;
outline: revert;
text-decoration: revert;
transition: revert;
list-style: revert;
box-sizing: revert;
float: revert;
position: revert;
background-color: revert;
background-image: revert;
box-shadow: revert;
outline-offset: revert;
appearance: revert;
max-width: revert;
max-height: revert;
color: revert;
font-weight: revert;
font-style: revert;
line-height: revert;
letter-spacing: revert;
text-transform: revert;
text-underline-offset: revert;
text-align: revert;
text-indent: revert;
text-shadow: revert;
vertical-align: revert;
}
.kodza-hscroll__track:not(#_) {
border: revert;
border-radius: revert;
outline: revert;
text-decoration: revert;
transition: revert;
box-sizing: revert;
float: revert;
position: revert;
background-color: revert;
background-image: revert;
box-shadow: revert;
outline-offset: revert;
appearance: revert;
max-width: revert;
max-height: revert;
color: revert;
font-family: revert;
font-size: revert;
font-weight: revert;
font-style: revert;
line-height: revert;
letter-spacing: revert;
text-transform: revert;
text-underline-offset: revert;
text-align: revert;
text-indent: revert;
text-shadow: revert;
vertical-align: revert;
}
.kodza-hscroll__item:not(#_) {
border: revert;
margin: revert;
padding: revert;
border-radius: revert;
outline: revert;
text-decoration: revert;
transition: revert;
list-style: revert;
box-sizing: revert;
float: revert;
position: revert;
background-color: revert;
background-image: revert;
box-shadow: revert;
outline-offset: revert;
appearance: revert;
max-width: revert;
max-height: revert;
color: revert;
font-family: revert;
font-size: revert;
font-weight: revert;
font-style: revert;
line-height: revert;
letter-spacing: revert;
text-transform: revert;
text-underline-offset: revert;
text-align: revert;
text-indent: revert;
text-shadow: revert;
vertical-align: revert;
}
.kodza-hscroll__card:not(#_) {
border: revert;
margin: revert;
padding: revert;
transition: revert;
list-style: revert;
box-sizing: revert;
float: revert;
position: revert;
background-color: revert;
background-image: revert;
box-shadow: revert;
appearance: revert;
max-width: revert;
max-height: revert;
font-family: revert;
font-size: revert;
font-weight: revert;
font-style: revert;
line-height: revert;
letter-spacing: revert;
text-transform: revert;
text-underline-offset: revert;
text-align: revert;
text-indent: revert;
text-shadow: revert;
vertical-align: revert;
}
.kodza-hscroll__media img:where(:not([class*="kodza-"])):not(#_) {
border: revert;
padding: revert;
border-radius: revert;
outline: revert;
text-decoration: revert;
list-style: revert;
box-sizing: revert;
float: revert;
position: revert;
margin-top: revert;
margin-right: revert;
margin-bottom: revert;
background-color: revert;
background-image: revert;
box-shadow: revert;
outline-offset: revert;
appearance: revert;
max-height: revert;
color: revert;
font-family: revert;
font-size: revert;
font-weight: revert;
font-style: revert;
line-height: revert;
letter-spacing: revert;
text-transform: revert;
text-underline-offset: revert;
text-align: revert;
text-indent: revert;
text-shadow: revert;
vertical-align: revert;
}
.kodza-hscroll__title:not(#_):where(:not([hidden])),
.kodza-hscroll__item:not(#_):where(:not([hidden])) {
display: revert;
}
.kodza-hscroll__title:not(#_):where([hidden]),
.kodza-hscroll__item:not(#_):where([hidden]) {
display: none;
}
/* The component's own rules, one step stronger: real buttons and fields, what it sets on tags themes style (links, lists, headings, images…), and the root width (a theme may narrow it, never widen it) */
.kodza-hscroll__title:not(#_) {
margin: 0;
font-size: clamp(32px, 4.4vw, 62px);
font-weight: 600;
line-height: 1;
letter-spacing: -0.045em;
}
.kodza-hscroll__count:not(#_) {
display: flex;
margin: 0;
font-family: ui-monospace, "SF Mono", Menlo, monospace;
font-size: 12px;
}
.kodza-hscroll__track:not(#_) {
display: flex;
margin: 0;
padding: 0 var(--kz-hscroll-gutter);
list-style: none;
}
.kodza-hscroll__card:not(#_) {
display: grid;
border-radius: var(--kz-hscroll-radius);
color: inherit;
text-decoration: none;
}
.kodza-hscroll__card:focus-visible:not(#_) {
outline: 2px solid var(--kz-hscroll-fg);
outline-offset: 4px;
}
.kodza-hscroll__media img:not(#_) {
display: block;
max-width: none;
margin-left: calc(var(--kz-hscroll-drift) * -1);
transition: transform 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}
@media (prefers-reduced-motion: reduce) {
.kodza-hscroll__media img:not(#_) {
transition: none !important;
}
}
// Kodza License: free to use, client work included. Not to resell or redistribute, ported or not: https://kodza.dev/docs/#print-license
// Kodza — Horizontal Scroll
// Gives the section the height it needs (its sideways distance plus a screen), then maps the
// vertical scroll onto the track: position, drift of each picture, counter and bar. Tabbing
// to a card brings the page to it. On touch screens (or narrow ones) it becomes a plain
// sideways swipe instead.
(() => {
const SELECTOR = '.kodza-hscroll';
const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
const swipe = window.matchMedia('(hover: none), (max-width: 760px)');
const pad = (n) => String(n).padStart(2, '0');
// One instance. Everything it starts is tied to `signal`, so kodza:destroy stops it all
function mount(root, signal) {
const viewport = root.querySelector('.kodza-hscroll__viewport');
const track = root.querySelector('.kodza-hscroll__track');
const items = [...root.querySelectorAll('.kodza-hscroll__item')];
const num = root.querySelector('.kodza-hscroll__num');
const total = root.querySelector('.kodza-hscroll__total');
if (!viewport || !track || !items.length) return;
if (total) total.textContent = `/ ${pad(items.length)}`;
if (num) num.replaceChildren(Object.assign(document.createElement('span'), { textContent: pad(1) }));
let distance = 0;
let index = -1;
// The counter rolls: up going forward, down going back
const roll = (next) => {
if (next === index) return;
const dir = next > index ? 1 : -1;
const first = index < 0;
index = next;
if (!num || first) return;
const prev = num.lastElementChild;
const span = document.createElement('span');
span.textContent = pad(next + 1);
num.append(span);
if (reduceMotion) return prev.remove();
const timing = { duration: 450, easing: 'cubic-bezier(0.22, 1, 0.36, 1)' };
prev.animate([{ transform: 'none' }, { transform: `translateY(${dir * -110}%)` }], { ...timing, fill: 'forwards' }).finished.then(() => prev.remove());
span.animate([{ transform: `translateY(${dir * 110}%)` }, { transform: 'none' }], timing);
};
const update = () => {
const swiping = root.classList.contains('is-swipe');
const x = swiping ? viewport.scrollLeft : Math.min(distance, Math.max(0, -root.getBoundingClientRect().top));
const progress = distance > 0 ? x / distance : 0;
if (!swiping) root.style.setProperty('--kz-hscroll-x', x.toFixed(1));
root.style.setProperty('--kz-hscroll-progress', progress.toFixed(4));
// Each picture drifts against the track, by its distance to the middle of the screen
const middle = innerWidth / 2;
items.forEach((item) => {
const box = item.getBoundingClientRect();
const drift = Math.max(-1, Math.min(1, (box.left + box.width / 2 - middle) / innerWidth));
item.style.setProperty('--kz-drift', reduceMotion ? 0 : (-drift).toFixed(3));
});
roll(Math.min(items.length - 1, Math.round(progress * (items.length - 1))));
};
const measure = () => {
root.classList.toggle('is-swipe', swipe.matches);
if (swipe.matches) {
root.style.removeProperty('--kz-hscroll-height');
distance = viewport.scrollWidth - viewport.clientWidth;
} else {
distance = Math.max(0, track.scrollWidth - innerWidth);
root.style.setProperty('--kz-hscroll-height', `${distance + innerHeight}px`);
}
update();
};
// A card reached with the keyboard is brought into view
track.addEventListener('focusin', (event) => {
if (root.classList.contains('is-swipe')) return;
const item = event.target.closest('.kodza-hscroll__item');
if (!item) return;
const target = Math.min(distance, Math.max(0, item.offsetLeft - (innerWidth - item.offsetWidth) / 2));
window.scrollTo({ top: root.getBoundingClientRect().top + window.scrollY + target, behavior: reduceMotion ? 'auto' : 'smooth' });
}, { signal });
let ticking = false;
const queue = () => {
if (ticking) return;
ticking = true;
requestAnimationFrame(() => {
if (signal.aborted) return;
ticking = false;
update();
});
};
window.addEventListener('scroll', queue, { passive: true, signal });
viewport.addEventListener('scroll', queue, { passive: true, signal });
window.addEventListener('resize', measure, { signal });
swipe.addEventListener?.('change', measure, { signal });
track.querySelectorAll('img').forEach((img) => img.complete || img.addEventListener('load', measure, { once: true, signal }));
measure();
}
// Kodza lifecycle, the same in every component: `kodza:init` starts the instances inside
// event.detail.root (the whole page by default), `kodza:destroy` stops them and puts their
// markup back. For themes and apps that swap content without a page load.
const instances = new WeakMap();
const find = (scope = document) => [scope, ...scope.querySelectorAll(SELECTOR)].filter((el) => el.matches?.(SELECTOR));
const init = (scope) => find(scope).forEach((root) => {
if (root.classList.contains('is-ready')) return; // already running, maybe from another copy of this script
const controller = new AbortController();
instances.set(root, { controller, html: root.innerHTML, attrs: [...root.attributes].map((a) => [a.name, a.value]) });
mount(root, controller.signal);
root.classList.add('is-ready');
});
const destroy = (scope) => find(scope).forEach((root) => {
const instance = instances.get(root);
if (!instance) return;
instances.delete(root);
instance.controller.abort();
[...root.attributes].forEach((a) => root.removeAttribute(a.name));
instance.attrs.forEach(([name, value]) => root.setAttribute(name, value));
root.innerHTML = instance.html;
});
document.addEventListener('kodza:init', (event) => init(event.detail?.root));
document.addEventListener('kodza:destroy', (event) => destroy(event.detail?.root));
init();
// Loaded in <head>, the script runs before the markup exists: once more when it is parsed
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', () => init(), { once: true });
})();
'use client';
// Kodza — Horizontal Scroll, for React. From https://kodza.dev/components/horizontal-scroll/
// Kodza License: free to use, client work included. Not to resell or redistribute, ported or not: https://kodza.dev/docs/#print-license
// Next to this file: horizontal-scroll.css and horizontal-scroll.js, the component's CSS and JS from Kodza, as they are.
// Yours to edit: the markup is plain JSX below.
// The script changes this markup once React has rendered it (it adds layers, splits text, moves
// nodes), so React must not patch it afterwards. When a prop changes, the key gives React a fresh
// element: kodza:destroy stops the script on the old one, kodza:init starts it on the new one.
import { useRef, useEffect, type ComponentPropsWithoutRef, type ComponentRef, type CSSProperties } from 'react';
import './horizontal-scroll.css';
export type HorizontalScrollProps = Omit<ComponentPropsWithoutRef<'section'>, "children" | "vars"> & {
/** Settings, as CSS variables on the root */
vars?: {
/** default: #0a0a0a */
"--kz-hscroll-fg"?: string | number;
/** default: rgba(10, 10, 10, 0.62) */
"--kz-hscroll-muted"?: string | number;
/** default: rgba(10, 10, 10, 0.12) */
"--kz-hscroll-line"?: string | number;
/** default: 46vh */
"--kz-hscroll-card"?: string | number;
/** default: 16px */
"--kz-hscroll-gap"?: string | number;
/** default: max(24px, calc((100vw - 1080px) / 2)) */
"--kz-hscroll-gutter"?: string | number;
/** default: 16px */
"--kz-hscroll-radius"?: string | number;
/** default: 8% */
"--kz-hscroll-drift"?: string | number;
};
className?: string;
};
export function HorizontalScroll({ vars, className, style, ...rest }: HorizontalScrollProps) {
const ref = useRef<ComponentRef<'section'>>(null);
const classes = ["kodza-hscroll", className].filter(Boolean).join(' ');
// Everything the markup is made of: when it changes, React gets a fresh element (see the top)
const key = JSON.stringify([classes, vars, style, rest]);
// Starts the script on this element once React has rendered it, stops it when it goes
useEffect(() => {
const root = ref.current;
let live = true;
// @ts-ignore: a plain script, no types to find
import('./horizontal-scroll.js').then(() => {
if (live) document.dispatchEvent(new CustomEvent('kodza:init', { detail: { root } }));
});
return () => {
live = false;
document.dispatchEvent(new CustomEvent('kodza:destroy', { detail: { root } }));
};
}, [key]);
return (
<section aria-label="Selected work" key={key} {...rest} className={classes} style={{ ...vars, ...style } as CSSProperties} ref={ref}>
<div className="kodza-hscroll__sticky">
<div className="kodza-hscroll__head">
<h2 className="kodza-hscroll__title">Six shapes, one scroll.</h2>
<p className="kodza-hscroll__count" aria-hidden="true">
<span className="kodza-hscroll__num">01</span>
<span className="kodza-hscroll__total">/ 06</span>
</p>
</div>
<div className="kodza-hscroll__viewport">
<ul className="kodza-hscroll__track">
<li className="kodza-hscroll__item">
<a className="kodza-hscroll__card" href="#">
<span className="kodza-hscroll__media">
<img src="https://kodza.dev/media/kz-steps-1400.webp" alt="" />
</span>
<span className="kodza-hscroll__meta">
<span className="kodza-hscroll__name">Steps</span>
<span className="kodza-hscroll__tag">grid-auto-flow: row</span>
</span>
</a>
</li>
<li className="kodza-hscroll__item">
<a className="kodza-hscroll__card" href="#">
<span className="kodza-hscroll__media">
<img src="https://kodza.dev/media/kz-block-1400.webp" alt="" />
</span>
<span className="kodza-hscroll__meta">
<span className="kodza-hscroll__name">Block</span>
<span className="kodza-hscroll__tag">display: block</span>
</span>
</a>
</li>
<li className="kodza-hscroll__item">
<a className="kodza-hscroll__card" href="#">
<span className="kodza-hscroll__media">
<img src="https://kodza.dev/media/kz-stack-1400.webp" alt="" />
</span>
<span className="kodza-hscroll__meta">
<span className="kodza-hscroll__name">Stack</span>
<span className="kodza-hscroll__tag">z-index: 9999</span>
</span>
</a>
</li>
<li className="kodza-hscroll__item">
<a className="kodza-hscroll__card" href="#">
<span className="kodza-hscroll__media">
<img src="https://kodza.dev/media/kz-float-1400.webp" alt="" />
</span>
<span className="kodza-hscroll__meta">
<span className="kodza-hscroll__name">Float</span>
<span className="kodza-hscroll__tag">position: absolute</span>
</span>
</a>
</li>
<li className="kodza-hscroll__item">
<a className="kodza-hscroll__card" href="#">
<span className="kodza-hscroll__media">
<img src="https://kodza.dev/media/kz-arch-1400.webp" alt="" />
</span>
<span className="kodza-hscroll__meta">
<span className="kodza-hscroll__name">Arch</span>
<span className="kodza-hscroll__tag">border-radius: 50%</span>
</span>
</a>
</li>
<li className="kodza-hscroll__item">
<a className="kodza-hscroll__card" href="#">
<span className="kodza-hscroll__media">
<img src="https://kodza.dev/media/kz-columns-1400.webp" alt="" />
</span>
<span className="kodza-hscroll__meta">
<span className="kodza-hscroll__name">Columns</span>
<span className="kodza-hscroll__tag">gap: 1rem</span>
</span>
</a>
</li>
</ul>
</div>
<div className="kodza-hscroll__progress" aria-hidden="true">
<span />
</div>
</div>
</section>
);
}
Checked
Robots poked at it before you did. Every component goes through the same checks, and again each time it changes.