Tabs
Tabs
Two highlights on one row of tabs. The selected fill moves on click, and a lighter one follows the pointer.
Spring catalogue
Nine prints, two waiting for review. The next proof ships on Friday.
import { createMovingHighlight } from "@reuse-sh/interactions";
const tabs = document.querySelector('[role="tablist"]');
// The selected fill moves only when aria-selected changes.
const selected = createMovingHighlight(tabs, {
rowSelector: '[role="tab"]',
selectedSelector: '[aria-selected="true"]',
mode: "selected",
className: "tab-selected",
});
// A lighter fill follows the pointer and never rests.
const hover = createMovingHighlight(tabs, {
rowSelector: '[role="tab"]',
selectedSelector: "[data-none]",
className: "tab-hover",
});import { createMovingHighlight } from "@reuse-sh/interactions";
import { useEffect, useRef } from "react";
export function Tabs({ tabs, selected, onSelect }) {
const ref = useRef(null);
useEffect(() => {
const row = '[role="tab"]';
const fill = createMovingHighlight(ref.current, {
rowSelector: row, selectedSelector: '[aria-selected="true"]',
mode: "selected", className: "tab-selected",
});
const hover = createMovingHighlight(ref.current, {
rowSelector: row, selectedSelector: "[data-none]", className: "tab-hover",
});
return () => { hover.destroy(); fill.destroy(); };
}, []);
return (
<div ref={ref} role="tablist">
{tabs.map((tab) => (
<button key={tab} role="tab" aria-selected={tab === selected} onClick={() => onSelect(tab)}>
{tab}
</button>
))}
</div>
);
}