Shoji
import { useId, useRef, useState, type CSSProperties } from 'react';
const VIEWS = [
{ label: 'Moon', body: 'Full on the fifteenth, rising over the east wall at dusk.' },
{ label: 'Maple', body: 'Red by November. The bench under it gets the last sun.' },
{ label: 'Lantern', body: 'Granite, two hundred years old, and lit for guests.' },
{ label: 'Pond', body: 'Six carp, and a heron that calls in on Tuesdays.' },
];
const BAY = 53;
// Three screens share four bays, so one bay is always open. Screen j sits left of the gap
// or right of it.
const bayOf = (j: number, gap: number) => (j < gap ? j : j + 1);
const LEAF = 'M0-5L1.4-1.6L5-2L2.4.6L3.4 4.4L0 2.4L-3.4 4.4L-2.4.6L-5-2L-1.4-1.6Z';
const LEAVES = [
[64, 16, 0.9, 10],
[72, 24, 1.1, -20],
[82, 12, 0.8, 30],
[88, 28, 1, 5],
[96, 18, 0.9, -12],
[78, 34, 0.8, 40],
[70, 40, 0.7, -30],
[92, 38, 0.8, 18],
];
const css = `
.tabs-shoji { --shade: light-dark(#373a40, #121417); --wood: light-dark(#6d7179, #393c41); --paper: light-dark(#fbfcfd, #e5e6e9); width: 230px; font-family: Georgia, 'Times New Roman', serif; }
.tabs-shoji__house { position: relative; height: 138px; }
.tabs-shoji__frame { position: absolute; left: 3px; top: 20px; width: 224px; height: 116px; box-sizing: border-box; border: 6px solid var(--wood); border-radius: 2px; background: light-dark(#f3f4f7, #1a1c1f); box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--shade) 40%, transparent), 0 8px 14px -8px light-dark(rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.6)); overflow: hidden; }
.tabs-shoji__scene { position: absolute; inset: 0; display: block; }
.tabs-shoji__sky { fill: light-dark(#f3f4f7, #1a1c1f); }
.tabs-shoji__glow { fill: color-mix(in oklab, light-dark(#373a40, #f1f2f3) 10%, transparent); }
.tabs-shoji__moon { fill: light-dark(#373a40, #f1f2f3); }
.tabs-shoji__far { fill: light-dark(#ebedf0, #232529); }
.tabs-shoji__ground { fill: light-dark(#ebedf0, #232529); }
.tabs-shoji__ink { fill: light-dark(#cbced3, #393c41); }
.tabs-shoji__branch { fill: none; stroke: light-dark(#cbced3, #393c41); stroke-width: 2; stroke-linecap: round; }
.tabs-shoji__leaf { fill: light-dark(#6d7179, #7d8087); }
.tabs-shoji__light { fill: light-dark(#373a40, #f1f2f3); }
.tabs-shoji__water { fill: light-dark(#dbdde1, #2e3035); }
.tabs-shoji__ripple { fill: none; stroke: light-dark(#6d7179, #7d8087); stroke-width: 0.8; }
.tabs-shoji__fish { fill: light-dark(#50535b, #c6c8cd); }
/* A paper screen in a lattice, sliding on its track; the garden shows softly through. */
.tabs-shoji__screen { position: absolute; top: 0; left: 0; width: ${BAY + 1}px; height: 100%; box-sizing: border-box; background: repeating-linear-gradient(90deg, transparent 0 15.5px, var(--wood) 15.5px 17px) 2px 0, repeating-linear-gradient(transparent 0 24px, var(--wood) 24px 25.5px) 0 2px, color-mix(in oklab, var(--paper) 78%, transparent); -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px); box-shadow: inset 0 0 0 3px var(--wood), -2px 0 4px light-dark(rgba(0, 0, 0, 0.12), rgba(0, 0, 0, 0.4)); transition: transform 0.5s cubic-bezier(0.5, 0, 0.2, 1.08) var(--d, 0s); }
.tabs-shoji__screen::after { content: ''; position: absolute; top: 50%; right: 6px; width: 5px; height: 9px; margin-top: -4px; border-radius: 3px; background: color-mix(in oklab, var(--wood) 70%, var(--shade)); }
.tabs-shoji__bay { position: absolute; top: 0; width: ${BAY}px; height: 136px; padding: 0; border: 0; background: none; cursor: pointer; }
.tabs-shoji__bay span { position: absolute; left: 0; right: 0; top: 0; color: light-dark(#5f626a, #a7abb2); font-size: 11.5px; letter-spacing: 0.04em; transition: color 0.3s; }
.tabs-shoji__bay:hover span { color: light-dark(#42454c, #e5e6e9); }
.tabs-shoji__bay[aria-selected='true'] span { color: light-dark(#373a40, #f1f2f3); }
.tabs-shoji__bay:focus-visible { outline: none; }
.tabs-shoji__bay:focus-visible span { outline: 2px solid light-dark(#50535b, #c6c8cd); outline-offset: 2px; border-radius: 3px; }
.tabs-shoji__panel { min-height: 3em; margin-top: 10px; color: light-dark(#50535b, #c6c8cd); font-size: 12.5px; line-height: 1.5; }
@media (prefers-reduced-motion: reduce) {
.tabs-shoji__screen { transition: none; }
}
`;
export function Preview() {
const id = useId();
const [{ sel, from }, setState] = useState({ sel: 0, from: 0 });
const tabs = useRef<(HTMLButtonElement | null)[]>([]);
const pick = (i: number) => setState((s) => (s.sel === i ? s : { sel: i, from: s.sel }));
const n = VIEWS.length;
const onKeyDown = (e: React.KeyboardEvent) => {
const next = { ArrowRight: (sel + 1) % n, ArrowLeft: (sel + n - 1) % n, Home: 0, End: n - 1 }[e.key];
if (next === undefined) return;
e.preventDefault();
pick(next);
tabs.current[next]?.focus();
};
// The screen next to the old gap slides first, then the next, like pushing them along.
const delay = (j: number) => (sel > from ? j - from : from - 1 - j) * 0.11;
return (
<>
<style href="kit-tabs-shoji" precedence="kit">
{css}
</style>
<div className="tabs-shoji">
<div className="tabs-shoji__house">
<div className="tabs-shoji__frame" aria-hidden="true">
<svg className="tabs-shoji__scene" width="212" height="104" viewBox="0 0 212 104">
<rect className="tabs-shoji__sky" width="212" height="104" />
<circle className="tabs-shoji__glow" cx="26" cy="32" r="24" />
<circle className="tabs-shoji__moon" cx="26" cy="32" r="12" />
<path className="tabs-shoji__far" d="M0 76Q30 62 58 70T120 66T180 70T212 64V104H0Z" />
<path className="tabs-shoji__branch" d="M106 4Q90 10 80 22T60 30M88 15Q92 26 90 36M80 22Q74 30 70 42" />
{LEAVES.map(([x, y, s, r]) => (
<path key={`${x}-${y}`} className="tabs-shoji__leaf" d={LEAF} transform={`translate(${x} ${y}) rotate(${r}) scale(${s * 1.3})`} />
))}
<path className="tabs-shoji__ground" d="M0 84H212V104H0Z" />
<g className="tabs-shoji__ink">
<path d="M120 84h22l-3-5h-16zM127 79h8V64h-8zM121 64h20l-2-3h-16zM118 46h26l-4 5h-18zM129 41h4v-4h-4z" />
<path d="M118 46Q131 36 144 46Z" />
<rect x="122" y="51" width="18" height="10" />
</g>
<rect className="tabs-shoji__light" x="127" y="52.5" width="8" height="7" rx="1" />
<ellipse className="tabs-shoji__water" cx="186" cy="92" rx="26" ry="8" />
<ellipse className="tabs-shoji__ripple" cx="178" cy="91" rx="8" ry="2.4" />
<ellipse className="tabs-shoji__ripple" cx="178" cy="91" rx="13" ry="4" />
<path className="tabs-shoji__fish" d="M188 93q6-4 12-1q-6 3-12 1zM200 92l4-2.5v5z" />
<path className="tabs-shoji__branch" d="M166 86V62M170 86V58M174 86V66" />
</svg>
{[0, 1, 2].map((j) => (
<span key={j} className="tabs-shoji__screen" style={{ transform: `translateX(${bayOf(j, sel) * BAY - (bayOf(j, sel) === 3 ? 1 : 0)}px)`, '--d': `${bayOf(j, sel) !== bayOf(j, from) ? delay(j) : 0}s` } as CSSProperties} />
))}
</div>
<div role="tablist" aria-label="Views" onKeyDown={onKeyDown}>
{VIEWS.map((v, i) => (
<button
key={v.label}
ref={(el) => {
tabs.current[i] = el;
}}
className="tabs-shoji__bay"
type="button"
role="tab"
id={`${id}-tab-${i}`}
aria-selected={sel === i}
aria-controls={`${id}-panel-${i}`}
tabIndex={sel === i ? 0 : -1}
style={{ left: 9 + i * BAY }}
onClick={() => pick(i)}
>
<span>{v.label}</span>
</button>
))}
</div>
</div>
{VIEWS.map((v, i) => (
<div key={v.label} className="tabs-shoji__panel" role="tabpanel" id={`${id}-panel-${i}`} aria-labelledby={`${id}-tab-${i}`} hidden={sel !== i}>
{v.body}
</div>
))}
</div>
</>
);
}Markup where the component goes, CSS in your stylesheet, script before </body>. In Vue, Svelte or Angular, call initTabsShoji(el) on the mounted element instead.
<div class="tabs-shoji">
<div class="tabs-shoji__house">
<div class="tabs-shoji__frame" aria-hidden="true">
<svg class="tabs-shoji__scene" width="212" height="104" viewBox="0 0 212 104">
<rect class="tabs-shoji__sky" width="212" height="104"></rect>
<circle class="tabs-shoji__glow" cx="26" cy="32" r="24"></circle>
<circle class="tabs-shoji__moon" cx="26" cy="32" r="12"></circle>
<path class="tabs-shoji__far" d="M0 76Q30 62 58 70T120 66T180 70T212 64V104H0Z"></path>
<path class="tabs-shoji__branch" d="M106 4Q90 10 80 22T60 30M88 15Q92 26 90 36M80 22Q74 30 70 42"></path>
<path class="tabs-shoji__leaf" d="M0-5L1.4-1.6L5-2L2.4.6L3.4 4.4L0 2.4L-3.4 4.4L-2.4.6L-5-2L-1.4-1.6Z" transform="translate(64 16) rotate(10) scale(1.1700000000000002)"></path>
<path class="tabs-shoji__leaf" d="M0-5L1.4-1.6L5-2L2.4.6L3.4 4.4L0 2.4L-3.4 4.4L-2.4.6L-5-2L-1.4-1.6Z" transform="translate(72 24) rotate(-20) scale(1.4300000000000002)"></path>
<path class="tabs-shoji__leaf" d="M0-5L1.4-1.6L5-2L2.4.6L3.4 4.4L0 2.4L-3.4 4.4L-2.4.6L-5-2L-1.4-1.6Z" transform="translate(82 12) rotate(30) scale(1.04)"></path>
<path class="tabs-shoji__leaf" d="M0-5L1.4-1.6L5-2L2.4.6L3.4 4.4L0 2.4L-3.4 4.4L-2.4.6L-5-2L-1.4-1.6Z" transform="translate(88 28) rotate(5) scale(1.3)"></path>
<path class="tabs-shoji__leaf" d="M0-5L1.4-1.6L5-2L2.4.6L3.4 4.4L0 2.4L-3.4 4.4L-2.4.6L-5-2L-1.4-1.6Z" transform="translate(96 18) rotate(-12) scale(1.1700000000000002)"></path>
<path class="tabs-shoji__leaf" d="M0-5L1.4-1.6L5-2L2.4.6L3.4 4.4L0 2.4L-3.4 4.4L-2.4.6L-5-2L-1.4-1.6Z" transform="translate(78 34) rotate(40) scale(1.04)"></path>
<path class="tabs-shoji__leaf" d="M0-5L1.4-1.6L5-2L2.4.6L3.4 4.4L0 2.4L-3.4 4.4L-2.4.6L-5-2L-1.4-1.6Z" transform="translate(70 40) rotate(-30) scale(0.9099999999999999)"></path>
<path class="tabs-shoji__leaf" d="M0-5L1.4-1.6L5-2L2.4.6L3.4 4.4L0 2.4L-3.4 4.4L-2.4.6L-5-2L-1.4-1.6Z" transform="translate(92 38) rotate(18) scale(1.04)"></path>
<path class="tabs-shoji__ground" d="M0 84H212V104H0Z"></path>
<g class="tabs-shoji__ink">
<path d="M120 84h22l-3-5h-16zM127 79h8V64h-8zM121 64h20l-2-3h-16zM118 46h26l-4 5h-18zM129 41h4v-4h-4z"></path>
<path d="M118 46Q131 36 144 46Z"></path>
<rect x="122" y="51" width="18" height="10"></rect>
</g>
<rect class="tabs-shoji__light" x="127" y="52.5" width="8" height="7" rx="1"></rect>
<ellipse class="tabs-shoji__water" cx="186" cy="92" rx="26" ry="8"></ellipse>
<ellipse class="tabs-shoji__ripple" cx="178" cy="91" rx="8" ry="2.4"></ellipse>
<ellipse class="tabs-shoji__ripple" cx="178" cy="91" rx="13" ry="4"></ellipse>
<path class="tabs-shoji__fish" d="M188 93q6-4 12-1q-6 3-12 1zM200 92l4-2.5v5z"></path>
<path class="tabs-shoji__branch" d="M166 86V62M170 86V58M174 86V66"></path>
</svg>
<span class="tabs-shoji__screen" style="transform:translateX(53px);--d:0s"></span>
<span class="tabs-shoji__screen" style="transform:translateX(106px);--d:0s"></span>
<span class="tabs-shoji__screen" style="transform:translateX(158px);--d:0s"></span>
</div>
<div role="tablist" aria-label="Views">
<button class="tabs-shoji__bay" type="button" role="tab" id="tabs-shoji-tab-0" aria-selected="true" aria-controls="tabs-shoji-panel-0" tabindex="0" style="left:9px">
<span>Moon</span>
</button>
<button class="tabs-shoji__bay" type="button" role="tab" id="tabs-shoji-tab-1" aria-selected="false" aria-controls="tabs-shoji-panel-1" tabindex="-1" style="left:62px">
<span>Maple</span>
</button>
<button class="tabs-shoji__bay" type="button" role="tab" id="tabs-shoji-tab-2" aria-selected="false" aria-controls="tabs-shoji-panel-2" tabindex="-1" style="left:115px">
<span>Lantern</span>
</button>
<button class="tabs-shoji__bay" type="button" role="tab" id="tabs-shoji-tab-3" aria-selected="false" aria-controls="tabs-shoji-panel-3" tabindex="-1" style="left:168px">
<span>Pond</span>
</button>
</div>
</div>
<div class="tabs-shoji__panel" role="tabpanel" id="tabs-shoji-panel-0" aria-labelledby="tabs-shoji-tab-0">Full on the fifteenth, rising over the east wall at dusk.</div>
<div class="tabs-shoji__panel" role="tabpanel" id="tabs-shoji-panel-1" aria-labelledby="tabs-shoji-tab-1" hidden="">Red by November. The bench under it gets the last sun.</div>
<div class="tabs-shoji__panel" role="tabpanel" id="tabs-shoji-panel-2" aria-labelledby="tabs-shoji-tab-2" hidden="">Granite, two hundred years old, and lit for guests.</div>
<div class="tabs-shoji__panel" role="tabpanel" id="tabs-shoji-panel-3" aria-labelledby="tabs-shoji-tab-3" hidden="">Six carp, and a heron that calls in on Tuesdays.</div>
</div>.tabs-shoji { --shade: light-dark(#373a40, #121417); --wood: light-dark(#6d7179, #393c41); --paper: light-dark(#fbfcfd, #e5e6e9); width: 230px; font-family: Georgia, 'Times New Roman', serif; }
.tabs-shoji__house { position: relative; height: 138px; }
.tabs-shoji__frame { position: absolute; left: 3px; top: 20px; width: 224px; height: 116px; box-sizing: border-box; border: 6px solid var(--wood); border-radius: 2px; background: light-dark(#f3f4f7, #1a1c1f); box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--shade) 40%, transparent), 0 8px 14px -8px light-dark(rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.6)); overflow: hidden; }
.tabs-shoji__scene { position: absolute; inset: 0; display: block; }
.tabs-shoji__sky { fill: light-dark(#f3f4f7, #1a1c1f); }
.tabs-shoji__glow { fill: color-mix(in oklab, light-dark(#373a40, #f1f2f3) 10%, transparent); }
.tabs-shoji__moon { fill: light-dark(#373a40, #f1f2f3); }
.tabs-shoji__far { fill: light-dark(#ebedf0, #232529); }
.tabs-shoji__ground { fill: light-dark(#ebedf0, #232529); }
.tabs-shoji__ink { fill: light-dark(#cbced3, #393c41); }
.tabs-shoji__branch { fill: none; stroke: light-dark(#cbced3, #393c41); stroke-width: 2; stroke-linecap: round; }
.tabs-shoji__leaf { fill: light-dark(#6d7179, #7d8087); }
.tabs-shoji__light { fill: light-dark(#373a40, #f1f2f3); }
.tabs-shoji__water { fill: light-dark(#dbdde1, #2e3035); }
.tabs-shoji__ripple { fill: none; stroke: light-dark(#6d7179, #7d8087); stroke-width: 0.8; }
.tabs-shoji__fish { fill: light-dark(#50535b, #c6c8cd); }
/* A paper screen in a lattice, sliding on its track; the garden shows softly through. */
.tabs-shoji__screen { position: absolute; top: 0; left: 0; width: 54px; height: 100%; box-sizing: border-box; background: repeating-linear-gradient(90deg, transparent 0 15.5px, var(--wood) 15.5px 17px) 2px 0, repeating-linear-gradient(transparent 0 24px, var(--wood) 24px 25.5px) 0 2px, color-mix(in oklab, var(--paper) 78%, transparent); -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px); box-shadow: inset 0 0 0 3px var(--wood), -2px 0 4px light-dark(rgba(0, 0, 0, 0.12), rgba(0, 0, 0, 0.4)); transition: transform 0.5s cubic-bezier(0.5, 0, 0.2, 1.08) var(--d, 0s); }
.tabs-shoji__screen::after { content: ''; position: absolute; top: 50%; right: 6px; width: 5px; height: 9px; margin-top: -4px; border-radius: 3px; background: color-mix(in oklab, var(--wood) 70%, var(--shade)); }
.tabs-shoji__bay { position: absolute; top: 0; width: 53px; height: 136px; padding: 0; border: 0; background: none; cursor: pointer; }
.tabs-shoji__bay span { position: absolute; left: 0; right: 0; top: 0; color: light-dark(#5f626a, #a7abb2); font-size: 11.5px; letter-spacing: 0.04em; transition: color 0.3s; }
.tabs-shoji__bay:hover span { color: light-dark(#42454c, #e5e6e9); }
.tabs-shoji__bay[aria-selected='true'] span { color: light-dark(#373a40, #f1f2f3); }
.tabs-shoji__bay:focus-visible { outline: none; }
.tabs-shoji__bay:focus-visible span { outline: 2px solid light-dark(#50535b, #c6c8cd); outline-offset: 2px; border-radius: 3px; }
.tabs-shoji__panel { min-height: 3em; margin-top: 10px; color: light-dark(#50535b, #c6c8cd); font-size: 12.5px; line-height: 1.5; }
@media (prefers-reduced-motion: reduce) {
.tabs-shoji__screen { transition: none; }
}function initTabsShoji(root) {
var list = root.querySelector('[role="tablist"]');
var tabs = Array.prototype.slice.call(list.querySelectorAll('[role="tab"]'));
var panels = Array.prototype.slice.call(root.querySelectorAll('[role="tabpanel"]'));
var n = tabs.length;
var sel = tabs.findIndex(function (tab) { return tab.getAttribute('aria-selected') === 'true'; });
var screens = root.querySelectorAll('.tabs-shoji__screen');
var BAY = 53;
// Three screens share four bays, so one bay is always open.
function bayOf(j, gap) { return j < gap ? j : j + 1; }
function update(from) {
screens.forEach(function (screen, j) {
var bay = bayOf(j, sel);
// The screen next to the old gap slides first, then the next, like pushing them along.
var delay = bay !== bayOf(j, from) ? (sel > from ? j - from : from - 1 - j) * 0.11 : 0;
screen.style.setProperty('--d', delay + 's');
screen.style.transform = 'translateX(' + (bay * BAY - (bay === 3 ? 1 : 0)) + 'px)';
});
}
function pick(i) {
if (i === sel) return;
var from = sel;
sel = i;
tabs.forEach(function (tab, k) {
tab.setAttribute('aria-selected', String(k === sel));
tab.tabIndex = k === sel ? 0 : -1;
});
panels.forEach(function (panel, k) { panel.hidden = k !== sel; });
update(from);
}
function onClick(e) {
var i = tabs.indexOf(e.currentTarget);
if (i >= 0) pick(i);
}
function onKey(e) {
var next = { ArrowRight: (sel + 1) % n, ArrowLeft: (sel + n - 1) % n, Home: 0, End: n - 1 }[e.key];
if (next === undefined) return;
e.preventDefault();
pick(next);
tabs[next].focus();
}
tabs.forEach(function (tab) { tab.addEventListener('click', onClick); });
list.addEventListener('keydown', onKey);
return function () {
tabs.forEach(function (tab) { tab.removeEventListener('click', onClick); });
list.removeEventListener('keydown', onKey);
};
}
document.querySelectorAll('.tabs-shoji').forEach(initTabsShoji);Goal: Create a "Shoji" tab.
Visual behavior: Sliding paper screens over a garden: pick a view and the screens slide one after another to open that bay, the scene showing softly through the paper.
Technique:
- A real tablist of buttons with roving tabindex and a labelled panel for each tab; everything drawn follows the selection.
- Three screens share four bays, so one is always open. A screen sits left or right of the gap, and moving the gap moves only the screens in between, each by one bay.
- The screen next to the old gap goes first and each of the others follows a beat later, as if pushing them along the track.
- The garden is one drawing behind all four bays. Translucent paper with a backdrop blur lets it glow through, and the lattice is two repeating gradients.
Accessibility: Real ARIA tabs: arrow keys, Home and End move between tabs, only the selected tab is in the Tab order, and each panel is labelled by its tab. The drawing is aria-hidden. Under prefers-reduced-motion the screens are simply in their new places.
My stack: {{USER_STACK}}
My styling: {{USER_STYLING}}
My constraints: {{USER_CONSTRAINTS}}
My colors: neutral only. Black, white and grays, no hues. Use the color values above as written, or my project's closest neutral tokens.
Return a single self-contained component. No dependencies.Goal: Create a "Shoji" tab.
Visual behavior: Sliding paper screens over a garden: pick a view and the screens slide one after another to open that bay, the scene showing softly through the paper.
Technique:
- A real tablist of buttons with roving tabindex and a labelled panel for each tab; everything drawn follows the selection.
- Three screens share four bays, so one is always open. A screen sits left or right of the gap, and moving the gap moves only the screens in between, each by one bay.
- The screen next to the old gap goes first and each of the others follows a beat later, as if pushing them along the track.
- The garden is one drawing behind all four bays. Translucent paper with a backdrop blur lets it glow through, and the lattice is two repeating gradients.
Accessibility: Real ARIA tabs: arrow keys, Home and End move between tabs, only the selected tab is in the Tab order, and each panel is labelled by its tab. The drawing is aria-hidden. Under prefers-reduced-motion the screens are simply in their new places.
My stack: {{USER_STACK}}
My styling: {{USER_STYLING}}
My constraints: {{USER_CONSTRAINTS}}
My colors: use my project's existing color tokens. The color values above are grayscale placeholders. Map each one by its role (surface, text, border, accent, glow) to my theme, and keep their relative lightness so contrast and the effect still read.
Return a single self-contained component. No dependencies.Paste into Claude, ChatGPT or Cursor after filling in {{USER_STACK}}, {{USER_STYLING}} and {{USER_CONSTRAINTS}}. Neutral keeps the greys you see here; Match my project asks the model to map them onto your own colour tokens.
Free to use in any project, commercial included. MIT-0, no attribution required. Licence
About this component
Sliding paper screens over a garden: pick a view and the screens slide one after another to open that bay, the scene showing softly through the paper.
- Dependencies
- None
- Difficulty
- Intermediate
- Triggers
- click, state change, delay stagger
- Techniques
- css transition, transition delay stagger, filter, svg
- Reduced motion
- Respected
- Theme
- Light and dark (follows your page's color-scheme)
Real ARIA tabs: arrow keys, Home and End move between tabs, only the selected tab is in the Tab order, and each panel is labelled by its tab. The drawing is aria-hidden. Under prefers-reduced-motion the screens are simply in their new places.