IconTabs icon
CSS-only tabs: een rij labels met icoon, elk gekoppeld aan een tekstpaneel. Werkt zonder JS.
src/components/icon/IconTabs.astro
---
/**
* IconTabs
* CSS-only tab-blok: een rij labels met icoon, elk gekoppeld aan een
* tekstpaneel. Werkt zonder JS via radio-inputs. Prop-driven.
*
* Props:
* - tabs: { icon?: string; label: string; body: string }[]
*/
interface Tab {
icon?: string;
label: string;
body: string;
}
interface Props {
tabs?: Tab[];
}
const { tabs = [] } = Astro.props;
// WHY: radio-name moet uniek zijn per instance, anders delen meerdere
// IconTabs-blokken op één pagina dezelfde checked-state.
const uid = 'itabs-' + Math.random().toString(36).slice(2, 8);
---
<section class="bl-section itabs">
<div class="bl-inner bl-inner--narrow itabs-inner">
<div class="itabs-wrap">
{tabs.map((_, i) => (
<input class="itabs-radio" type="radio" name={uid} id={`${uid}-${i}`} checked={i === 0} />
))}
<div class="itabs-bar" role="tablist">
{tabs.map((t, i) => (
<label class="itabs-label" for={`${uid}-${i}`}>
{t.icon && <span class="itabs-icon" aria-hidden="true">{t.icon}</span>}
<span class="itabs-text">{t.label}</span>
</label>
))}
</div>
<div class="itabs-panels">
{tabs.map((t, i) => (
<div class={`itabs-panel itabs-panel--${i}`}><p>{t.body}</p></div>
))}
</div>
</div>
</div>
</section>
<style>
.itabs-wrap{--color-accent: #6366f1;--color-primary: #0a0a0a}
.itabs-radio{position:absolute;opacity:0;pointer-events:none}
.itabs-bar{display:flex;border-bottom:2px solid #e5e7eb;gap:0;flex-wrap:wrap}
.itabs-label{display:flex;align-items:center;gap:8px;padding:12px 20px;cursor:pointer;font-size:1rem;font-weight:600;color:#52525b;border-bottom:2px solid transparent;margin-bottom:-2px;transition:color .15s;white-space:nowrap}
.itabs-label:hover{color:var(--color-primary)}
.itabs-icon{font-size:1rem}
.itabs-panels{padding:24px 0}
.itabs-panel{display:none;font-size:1rem;line-height:1.7;color:#374151}
.itabs-panel p{margin:0}
.itabs-radio:nth-of-type(1):checked ~ .itabs-bar label:nth-of-type(1),
.itabs-radio:nth-of-type(2):checked ~ .itabs-bar label:nth-of-type(2),
.itabs-radio:nth-of-type(3):checked ~ .itabs-bar label:nth-of-type(3),
.itabs-radio:nth-of-type(4):checked ~ .itabs-bar label:nth-of-type(4),
.itabs-radio:nth-of-type(5):checked ~ .itabs-bar label:nth-of-type(5),
.itabs-radio:nth-of-type(6):checked ~ .itabs-bar label:nth-of-type(6){color:var(--color-accent);border-bottom-color:var(--color-accent)}
.itabs-radio:nth-of-type(1):checked ~ .itabs-panels .itabs-panel--0,
.itabs-radio:nth-of-type(2):checked ~ .itabs-panels .itabs-panel--1,
.itabs-radio:nth-of-type(3):checked ~ .itabs-panels .itabs-panel--2,
.itabs-radio:nth-of-type(4):checked ~ .itabs-panels .itabs-panel--3,
.itabs-radio:nth-of-type(5):checked ~ .itabs-panels .itabs-panel--4,
.itabs-radio:nth-of-type(6):checked ~ .itabs-panels .itabs-panel--5{display:block;animation:itabs-fade .2s ease}
@keyframes itabs-fade{from{opacity:0}to{opacity:1}}
</style>
Props
| Prop | Type | Default | Beschrijving |
|---|---|---|---|
tabs * | { icon?: string; label: string; body: string }[] | - | Tab-items (label + paneeltekst). Ondersteunt tot 6 tabs. |
* = verplicht