simple-tabs-component
Version:
Keyboard accessible Tabs Web Component that changes to an Accordion in small spaces.
139 lines (122 loc) • 3.35 kB
CSS
/* Scoped Style Sheet (loaded inside the Web Component's <template>) */
:host {
--_color: var(--st-color, #0000ff);
--_tab-font: var(--st-tab-font, system-ui);
--_max-tabs: var(--st-max-tabs, 10);
--_color-primary: light-dark(
oklch(from var(--_color) 50% c h),
oklch(from var(--_color) 85% 75% h)
);
--_color-muted: light-dark(
oklch(from var(--_color) 90% 10% h),
oklch(from var(--_color) 40% 20% h)
);
container: tabs / inline-size;
display: block;
}
*, *::before, *::after { box-sizing: border-box; }
.simple-tabs {
display: grid;
gap: 0;
align-content: start;
grid-template-columns: repeat(var(--_max-tabs), auto) 1fr;
grid-template-rows: auto auto;
}
[role=tab] {
grid-row: 1;
width: max-content;
font-weight: bold;
font-family: var(--_tab-font);
padding: .5em 1em;
margin-block-end: 0;
box-shadow: 0 3px 0 var(--_color-muted);
z-index: 1;
&:has(:checked) {
pointer-events: none;
box-shadow: 0 3px 0 var(--_color-primary);
}
&:not(:has(:checked)):hover {
cursor: pointer;
color: var(--_color-primary);
}
/* when :focus-visible (on keyboard navigation) */
&:has([type=radio]:focus-visible) {
outline: solid 1.5px var(--_color-primary);
outline-offset: -3px;
border-radius: 8px 8px 0 0;
}
/* visually hide radio inputs */
input[type=radio] {
clip: rect(0 0 0 0); clip-path: inset(50%); height: 1px; overflow: hidden; position: absolute; width: 1px;
}
}
[role=tabpanel] {
grid-row: 2;
grid-column: 1 / -1;
transition: opacity 300ms ease-in-out;
&:where([role=tab]:has(:checked) + *) { pointer-events: unset; }
&:where([role=tab]:not(:has(:checked)) + *) { display: none; }
& > :first-child {
margin-block-start: 0;
border-top: solid 3px var(--_color-muted);
padding-block-start: 1rem;
}
}
/* Stack (into an accordion) in small spaces */
@container tabs (width <= 600px) {
.simple-tabs {
grid-template-columns: unset;
grid-template-rows: repeat(auto-fill, auto);
[role=tab] {
grid-row: unset;
width: 100%;
padding-inline: 0;
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: nowrap;
&:has(:checked) {
margin-bottom: -1.5rem;
box-shadow: none;
box-shadow: 0 3px 0 var(--_color-primary);
border: 0;
}
&:not(:has(:checked)) {
box-shadow: 0 2px 0 var(--_color-muted);
}
&:has(:focus-visible) {
margin-block-start: 10px;
outline-offset: 1px;
padding-inline: 0.5rem;
}
/* chevrons ▾▿ */
&::after {
color: inherit;
width: 1em;
height: 1em;
display: inline-grid;
place-content: center;
content: "▾";
/**
content: "";
background-image: url('data:image/svg+xml,%3Csvg width="1em" height="1em" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"%3E%3Cpath d="M7 10l5 5 5-5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/%3E%3C/svg%3E');
/**/
}
&:has(:checked)::after {
color: var(--_color-primary);
}
}
[role=tabpanel] {
grid-column: unset;
grid-row: unset;
margin-block-start: 1.5rem;
border-bottom: solid 2px var(--_color-muted);
box-shadow: none;
/* &:last-child { border-bottom-color: transparent; } */
& > div {
border-top: solid 2px transparent;
border-top: solid 2px var(--_color-primary);
}
}
}
}