Code Block
Code to read and copy: file name, line numbers, highlighted lines and a monochrome syntax in weight and light. Copy rolls to Copied; a terminal copies its commands only.
View codeDated steps along a rail that fills as you scroll: each dot fills when the ink reaches it, each step rises into place the first time it is seen, the last one reached stays marked.
With your AI
Say it to your AI. It gets the exact code below, not its best guess, and knows where it goes.
Tell my company story with a Kodza Timeline.
claude mcp add -t http kodza https://kodza.dev/api/mcp
<ol class="kodza-timeline">
<li class="kodza-timeline__item">
<time class="kodza-timeline__date" datetime="2006">2006</time>
<div class="kodza-timeline__body">
<h3 class="kodza-timeline__title">Write less, do more</h3>
<p class="kodza-timeline__text">jQuery arrives. The dollar sign becomes the most typed character on the web, just ahead of the semicolon.</p>
<span class="kodza-timeline__tag">Nostalgia</span>
</div>
</li>
<li class="kodza-timeline__item">
<time class="kodza-timeline__date" datetime="2013">2013</time>
<div class="kodza-timeline__body">
<h3 class="kodza-timeline__title">Everything is a component</h3>
<p class="kodza-timeline__text">React ships. HTML moves into JavaScript, and nobody asks it whether it wanted to.</p>
<span class="kodza-timeline__tag">Paradigm</span>
</div>
</li>
<li class="kodza-timeline__item">
<time class="kodza-timeline__date" datetime="2015">2015</time>
<div class="kodza-timeline__body">
<h3 class="kodza-timeline__title">Then webpack nation attacked</h3>
<p class="kodza-timeline__text">Suddenly everything needs npm install. Config files grow longer than the code they build.</p>
<span class="kodza-timeline__tag">Dark age</span>
</div>
</li>
<li class="kodza-timeline__item">
<time class="kodza-timeline__date" datetime="2020">2020</time>
<div class="kodza-timeline__body">
<h3 class="kodza-timeline__title">Fatigue sets in</h3>
<p class="kodza-timeline__text">Too many frameworks, too much tooling, too little web. Something has to give.</p>
<span class="kodza-timeline__tag">Burnout</span>
</div>
</li>
<li class="kodza-timeline__item">
<time class="kodza-timeline__date" datetime="2024">2024</time>
<div class="kodza-timeline__body">
<h3 class="kodza-timeline__title">The vanilla renaissance</h3>
<p class="kodza-timeline__text">The platform catches up: :has(), container queries, view transitions. The build step looks nervous.</p>
<span class="kodza-timeline__tag">Comeback</span>
</div>
</li>
<li class="kodza-timeline__item">
<time class="kodza-timeline__date" datetime="2026">2026</time>
<div class="kodza-timeline__body">
<h3 class="kodza-timeline__title">You are here</h3>
<p class="kodza-timeline__text">Copy, paste, ship. No build step was harmed in the making of this timeline.</p>
<span class="kodza-timeline__tag">Now</span>
</div>
</li>
</ol>
/* Kodza License: free to use, client work included. Not to resell or redistribute, ported or not: https://kodza.dev/docs/#print-license */
/* Kodza — Timeline
Dated steps along a rail that fills as you scroll: the ink glides after the reading point,
each dot fills from its centre when the ink reaches it and its title comes to full ink, and
each step arrives piece by piece (year, title, text, tag) the first time it is seen. The
last step reached keeps a ring: it is the one you are on. Without the script, the rail is
simply drawn. Everything is tuned with the --kz-timeline-* variables. */
.kodza-timeline {
--kz-timeline-fg: #0a0a0a;
--kz-timeline-muted: rgba(10, 10, 10, 0.62);
--kz-timeline-line: rgba(10, 10, 10, 0.14);
--kz-timeline-soft: rgba(10, 10, 10, 0.06);
--kz-timeline-bg: #f2f2f0;
--kz-timeline-ink: #0a0a0a;
--kz-timeline-on-ink: #ffffff;
--kz-timeline-date: 72px;
--kz-timeline-gap: 56px;
--kz-timeline-ease: cubic-bezier(0.22, 1, 0.36, 1);
--kz-timeline-progress: 0;
position: relative;
display: grid;
gap: var(--kz-timeline-gap);
margin: 0;
padding: 0;
color: var(--kz-timeline-fg);
list-style: none;
}
/* The rail, and its ink (scaled from the top by the script) */
.kodza-timeline::before,
.kodza-timeline__fill {
content: "";
position: absolute;
top: 8px;
bottom: 8px;
left: calc(var(--kz-timeline-date) + 23px);
width: 2px;
border-radius: 1px;
background: var(--kz-timeline-line);
}
.kodza-timeline__fill {
background: var(--kz-timeline-ink);
transform: scaleY(var(--kz-timeline-progress));
transform-origin: 50% 0;
}
.kodza-timeline__item {
position: relative;
display: grid;
grid-template-columns: var(--kz-timeline-date) 48px minmax(0, 1fr);
align-items: start;
}
/* The dot: a ring, and an inner round that grows to fill it when the ink reaches it */
.kodza-timeline__item::before,
.kodza-timeline__item::after {
content: "";
grid-column: 2;
grid-row: 1;
justify-self: center;
z-index: 1;
width: 14px;
height: 14px;
margin-top: 3px;
box-sizing: border-box;
border-radius: 50%;
}
.kodza-timeline__item::before {
border: 2px solid var(--kz-timeline-line);
background: var(--kz-timeline-bg);
transition: border-color 0.4s ease, box-shadow 0.6s var(--kz-timeline-ease);
}
.kodza-timeline__item::after {
background: var(--kz-timeline-ink);
transform: scale(0);
transition: transform 0.45s cubic-bezier(0.4, 0, 0.2, 1);
}
.kodza-timeline__item.is-passed::before {
border-color: var(--kz-timeline-ink);
}
.kodza-timeline__item.is-passed::after {
transform: scale(1);
transition: transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
}
/* The step you are on: a soft ring opens around its dot */
.kodza-timeline__item.is-current::before {
box-shadow: 0 0 0 7px var(--kz-timeline-soft);
}
.kodza-timeline__date {
grid-column: 1;
grid-row: 1;
justify-self: end;
color: var(--kz-timeline-muted);
font-family: "Geist Mono", ui-monospace, "SF Mono", Menlo, monospace;
font-size: 13px;
line-height: 20px;
transition: color 0.3s ease;
}
.kodza-timeline__item.is-passed .kodza-timeline__date {
color: var(--kz-timeline-fg);
}
.kodza-timeline__body {
grid-column: 3;
grid-row: 1;
display: grid;
justify-items: start;
gap: 8px;
}
.kodza-timeline__title {
margin: 0;
color: var(--kz-timeline-muted);
font-size: 22px;
font-weight: 600;
line-height: 1.2;
letter-spacing: -0.025em;
}
.kodza-timeline__text {
max-width: 46ch;
margin: 0;
color: var(--kz-timeline-muted);
font-size: 15px;
line-height: 1.6;
text-wrap: pretty;
}
.kodza-timeline__tag {
margin-top: 4px;
padding: 4px 10px;
border-radius: 999px;
background: var(--kz-timeline-soft);
font-family: "Geist Mono", ui-monospace, "SF Mono", Menlo, monospace;
font-size: 11px;
letter-spacing: 0.04em;
text-transform: uppercase;
}
.kodza-timeline__item.is-passed .kodza-timeline__title {
color: var(--kz-timeline-fg);
}
.kodza-timeline__item.is-current .kodza-timeline__tag {
background: var(--kz-timeline-ink);
color: var(--kz-timeline-on-ink);
}
/* Each step arrives piece by piece the first time it is seen: the year slides in, then the
title, the text and the tag rise in turn (only once the script is there) */
.kodza-timeline__date,
.kodza-timeline__body > * {
transition: opacity 0.7s ease, transform 1s var(--kz-timeline-ease), color 0.5s ease, background-color 0.4s ease;
}
.kodza-timeline.is-ready .kodza-timeline__item:not(.is-seen) .kodza-timeline__date {
opacity: 0;
transform: translateX(-10px);
}
.kodza-timeline.is-ready .kodza-timeline__item:not(.is-seen) .kodza-timeline__body > * {
opacity: 0;
transform: translateY(22px);
}
.kodza-timeline__item.is-seen .kodza-timeline__body > :nth-child(1) { transition-delay: 0.06s, 0.06s, 0s, 0s; }
.kodza-timeline__item.is-seen .kodza-timeline__body > :nth-child(2) { transition-delay: 0.14s, 0.14s, 0s, 0s; }
.kodza-timeline__item.is-seen .kodza-timeline__body > :nth-child(3) { transition-delay: 0.22s, 0.22s, 0s, 0s; }
@media (max-width: 560px) {
.kodza-timeline {
--kz-timeline-date: 0px;
}
.kodza-timeline::before,
.kodza-timeline__fill {
left: 11px;
}
.kodza-timeline__item {
grid-template-columns: 24px minmax(0, 1fr);
column-gap: 16px;
}
.kodza-timeline__item::before,
.kodza-timeline__item::after {
grid-column: 1;
}
.kodza-timeline__date {
grid-column: 2;
justify-self: start;
}
.kodza-timeline__body {
grid-column: 2;
grid-row: 2;
margin-top: 6px;
}
}
@media (prefers-reduced-motion: reduce) {
.kodza-timeline__item::before,
.kodza-timeline__item::after,
.kodza-timeline__body > *,
.kodza-timeline__date {
transition-duration: 0.01ms !important;
transition-delay: 0s !important;
}
}
/* Theming example — on a dark background:
.dark-section .kodza-timeline {
--kz-timeline-fg: #f2f2f0;
--kz-timeline-muted: rgba(242, 242, 240, 0.62);
--kz-timeline-line: rgba(255, 255, 255, 0.16);
--kz-timeline-soft: rgba(255, 255, 255, 0.09);
--kz-timeline-bg: #0a0a0a;
--kz-timeline-ink: #f2f2f0;
--kz-timeline-on-ink: #0a0a0a;
}
*/
/* kodza-guard 241df1146bc4 — 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, on everything the component leaves to it */
.kodza-timeline:not(#_) {
border: revert;
border-radius: revert;
outline: revert;
text-decoration: revert;
transition: revert;
box-sizing: revert;
background-color: revert;
background-image: revert;
box-shadow: revert;
outline-offset: revert;
appearance: 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-timeline__fill:not(#_),
.kodza-timeline__body:not(#_),
.kodza-timeline__tag:not(#_) {
box-sizing: revert;
}
.kodza-timeline__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;
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-timeline__date:not(#_) {
border: revert;
margin: revert;
padding: revert;
border-radius: revert;
outline: revert;
text-decoration: 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;
font-weight: revert;
font-style: revert;
letter-spacing: revert;
text-transform: revert;
text-underline-offset: revert;
text-align: revert;
text-indent: revert;
text-shadow: revert;
vertical-align: revert;
}
.kodza-timeline__title:not(#_) {
border: revert;
padding: revert;
border-radius: revert;
outline: revert;
text-decoration: 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;
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-timeline__text:not(#_) {
border: revert;
padding: revert;
border-radius: revert;
outline: revert;
text-decoration: 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-height: revert;
font-family: revert;
font-weight: revert;
font-style: revert;
letter-spacing: revert;
text-transform: revert;
text-underline-offset: revert;
text-align: revert;
text-indent: revert;
text-shadow: revert;
vertical-align: revert;
}
.kodza-timeline__date:not(#_):where(:not([hidden])),
.kodza-timeline__title:not(#_):where(:not([hidden])),
.kodza-timeline__text:not(#_):where(:not([hidden])) {
display: revert;
}
.kodza-timeline__date:not(#_):where([hidden]),
.kodza-timeline__title:not(#_):where([hidden]),
.kodza-timeline__text:not(#_):where([hidden]) {
display: none;
}
.kodza-timeline:not(#_)::before {
box-sizing: revert;
}
/* 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-timeline:not(#_) {
position: relative;
display: grid;
padding: 0;
color: var(--kz-timeline-fg);
list-style: none;
}
.kodza-timeline__item:not(#_) {
position: relative;
display: grid;
}
.kodza-timeline__date:not(#_) {
color: var(--kz-timeline-muted);
font-family: "Geist Mono", ui-monospace, "SF Mono", Menlo, monospace;
font-size: 13px;
line-height: 20px;
transition: color 0.3s ease;
}
.kodza-timeline__item.is-passed .kodza-timeline__date:not(#_) {
color: var(--kz-timeline-fg);
}
.kodza-timeline__title:not(#_) {
margin: 0;
color: var(--kz-timeline-muted);
font-size: 22px;
font-weight: 600;
line-height: 1.2;
letter-spacing: -0.025em;
}
.kodza-timeline__text:not(#_) {
max-width: 46ch;
margin: 0;
color: var(--kz-timeline-muted);
font-size: 15px;
line-height: 1.6;
}
.kodza-timeline__item.is-passed .kodza-timeline__title:not(#_) {
color: var(--kz-timeline-fg);
}
.kodza-timeline__date:not(#_),
.kodza-timeline__body > *:where(h3, p):not(#_) {
transition: opacity 0.7s ease, transform 1s var(--kz-timeline-ease), color 0.5s ease, background-color 0.4s ease;
}
.kodza-timeline__item.is-seen .kodza-timeline__body > :nth-child(1):where(h3, p):not(#_) {
transition-delay: 0.06s, 0.06s, 0s, 0s;
}
.kodza-timeline__item.is-seen .kodza-timeline__body > :nth-child(2):where(h3, p):not(#_) {
transition-delay: 0.14s, 0.14s, 0s, 0s;
}
.kodza-timeline__item.is-seen .kodza-timeline__body > :nth-child(3):where(h3, p):not(#_) {
transition-delay: 0.22s, 0.22s, 0s, 0s;
}
@media (prefers-reduced-motion: reduce) {
.kodza-timeline__body > *:where(h3, p):not(#_),
.kodza-timeline__date:not(#_) {
transition-duration: 0.01ms !important;
transition-delay: 0s !important;
}
}
// Kodza License: free to use, client work included. Not to resell or redistribute, ported or not: https://kodza.dev/docs/#print-license
// Kodza — Timeline
// Fills the rail up to the reading point (60% down the screen), gliding after it rather than
// sticking to it, fills each dot the ink has reached, marks the last one reached as the
// current step, and lets each step arrive the first time it comes into view.
(() => {
const SELECTOR = '.kodza-timeline';
const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
// One instance. Everything it starts is tied to `signal`, so kodza:destroy stops it all
function mount(root, signal) {
const items = [...root.querySelectorAll('.kodza-timeline__item')];
const fill = Object.assign(document.createElement('span'), { className: 'kodza-timeline__fill' });
fill.setAttribute('aria-hidden', 'true');
root.prepend(fill);
let frame = 0;
let shown = 0;
// Where the ink should be: the reading point, between the ends of the rail
const target = () => {
const box = root.getBoundingClientRect();
const span = box.height - 16;
return { box, span, value: Math.min(1, Math.max(0, (innerHeight * 0.6 - box.top - 8) / span)) };
};
// The ink glides towards it (a little behind the scroll), the dots follow the ink
const update = () => {
if (signal.aborted) return;
frame = 0;
const { box, span, value } = target();
shown = reduceMotion ? value : shown + (value - shown) * 0.16;
if (Math.abs(value - shown) < 0.0005) shown = value;
root.style.setProperty('--kz-timeline-progress', shown.toFixed(4));
const reached = box.top + 8 + shown * span;
let current = null;
items.forEach((item) => {
// The dot sits 10px below the top of its step
const passed = item.getBoundingClientRect().top + 10 <= reached + 1;
item.classList.toggle('is-passed', passed);
if (passed) current = item;
});
items.forEach((item) => item.classList.toggle('is-current', item === current));
if (shown !== value) frame = requestAnimationFrame(update);
};
const schedule = () => (frame ||= requestAnimationFrame(update));
if (!reduceMotion && 'IntersectionObserver' in window) {
const io = new IntersectionObserver((entries) => entries.forEach((entry) => {
if (!entry.isIntersecting) return;
entry.target.classList.add('is-seen');
io.unobserve(entry.target);
}), { rootMargin: '0px 0px -10% 0px' });
items.forEach((item) => io.observe(item));
signal.addEventListener('abort', () => io.disconnect());
} else items.forEach((item) => item.classList.add('is-seen'));
window.addEventListener('scroll', schedule, { passive: true, signal });
window.addEventListener('resize', schedule, { signal });
shown = target().value;
update();
}
// 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 — Timeline, for React. From https://kodza.dev/components/timeline-component/
// 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: timeline-component.css and timeline-component.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 './timeline-component.css';
export type TimelineProps = Omit<ComponentPropsWithoutRef<'ol'>, "children" | "vars"> & {
/** Settings, as CSS variables on the root */
vars?: {
/** default: #0a0a0a */
"--kz-timeline-fg"?: string | number;
/** default: rgba(10, 10, 10, 0.62) */
"--kz-timeline-muted"?: string | number;
/** default: rgba(10, 10, 10, 0.14) */
"--kz-timeline-line"?: string | number;
/** default: rgba(10, 10, 10, 0.06) */
"--kz-timeline-soft"?: string | number;
/** default: #f2f2f0 */
"--kz-timeline-bg"?: string | number;
/** default: #0a0a0a */
"--kz-timeline-ink"?: string | number;
/** default: #ffffff */
"--kz-timeline-on-ink"?: string | number;
/** default: 72px */
"--kz-timeline-date"?: string | number;
/** default: 56px */
"--kz-timeline-gap"?: string | number;
/** default: cubic-bezier(0.22, 1, 0.36, 1) */
"--kz-timeline-ease"?: string | number;
};
className?: string;
};
export function Timeline({ vars, className, style, ...rest }: TimelineProps) {
const ref = useRef<ComponentRef<'ol'>>(null);
const classes = ["kodza-timeline", 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('./timeline-component.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 (
<ol key={key} {...rest} className={classes} style={{ ...vars, ...style } as CSSProperties} ref={ref}>
<li className="kodza-timeline__item">
<time className="kodza-timeline__date" dateTime="2006">2006</time>
<div className="kodza-timeline__body">
<h3 className="kodza-timeline__title">Write less, do more</h3>
<p className="kodza-timeline__text">jQuery arrives. The dollar sign becomes the most typed character on the web, just ahead of the semicolon.</p>
<span className="kodza-timeline__tag">Nostalgia</span>
</div>
</li>
<li className="kodza-timeline__item">
<time className="kodza-timeline__date" dateTime="2013">2013</time>
<div className="kodza-timeline__body">
<h3 className="kodza-timeline__title">Everything is a component</h3>
<p className="kodza-timeline__text">React ships. HTML moves into JavaScript, and nobody asks it whether it wanted to.</p>
<span className="kodza-timeline__tag">Paradigm</span>
</div>
</li>
<li className="kodza-timeline__item">
<time className="kodza-timeline__date" dateTime="2015">2015</time>
<div className="kodza-timeline__body">
<h3 className="kodza-timeline__title">Then webpack nation attacked</h3>
<p className="kodza-timeline__text">Suddenly everything needs npm install. Config files grow longer than the code they build.</p>
<span className="kodza-timeline__tag">Dark age</span>
</div>
</li>
<li className="kodza-timeline__item">
<time className="kodza-timeline__date" dateTime="2020">2020</time>
<div className="kodza-timeline__body">
<h3 className="kodza-timeline__title">Fatigue sets in</h3>
<p className="kodza-timeline__text">Too many frameworks, too much tooling, too little web. Something has to give.</p>
<span className="kodza-timeline__tag">Burnout</span>
</div>
</li>
<li className="kodza-timeline__item">
<time className="kodza-timeline__date" dateTime="2024">2024</time>
<div className="kodza-timeline__body">
<h3 className="kodza-timeline__title">The vanilla renaissance</h3>
<p className="kodza-timeline__text">The platform catches up: :has(), container queries, view transitions. The build step looks nervous.</p>
<span className="kodza-timeline__tag">Comeback</span>
</div>
</li>
<li className="kodza-timeline__item">
<time className="kodza-timeline__date" dateTime="2026">2026</time>
<div className="kodza-timeline__body">
<h3 className="kodza-timeline__title">You are here</h3>
<p className="kodza-timeline__text">Copy, paste, ship. No build step was harmed in the making of this timeline.</p>
<span className="kodza-timeline__tag">Now</span>
</div>
</li>
</ol>
);
}
Checked
Robots poked at it before you did. Every component goes through the same checks, and again each time it changes.