Signature Library · Indian
Tiffin Carrier
An accordion built as a steel carrier: open a tier to see the food, the others close.
Items: tier name: contents, split by semicolons.
States
Default
Hover
Active
Focus
Code
tiffin-carrier.html
<div class="sig-tiffin-carrier">
<span class="sig-tiffin-carrier__handle" aria-hidden="true">
<span class="sig-tiffin-carrier__grip"></span>
</span>
<span class="sig-tiffin-carrier__lid">Lunch</span>
<div class="sig-tiffin-carrier__tier" data-tier data-open>
<button class="sig-tiffin-carrier__toggle" type="button" aria-expanded="true">
<span class="sig-tiffin-carrier__tag">Sadham</span>
<span class="sig-tiffin-carrier__mark" aria-hidden="true">▾</span>
</button>
<div class="sig-tiffin-carrier__food">
<div class="sig-tiffin-carrier__inner">Sambar rice with ghee</div>
</div>
</div>
<div class="sig-tiffin-carrier__tier" data-tier>
<button class="sig-tiffin-carrier__toggle" type="button" aria-expanded="false">
<span class="sig-tiffin-carrier__tag">Poriyal</span>
<span class="sig-tiffin-carrier__mark" aria-hidden="true">▾</span>
</button>
<div class="sig-tiffin-carrier__food">
<div class="sig-tiffin-carrier__inner">Beans and coconut</div>
</div>
</div>
<div class="sig-tiffin-carrier__tier" data-tier>
<button class="sig-tiffin-carrier__toggle" type="button" aria-expanded="false">
<span class="sig-tiffin-carrier__tag">Thayir</span>
<span class="sig-tiffin-carrier__mark" aria-hidden="true">▾</span>
</button>
<div class="sig-tiffin-carrier__food">
<div class="sig-tiffin-carrier__inner">Curd rice, pickle</div>
</div>
</div>
<span class="sig-tiffin-carrier__base" aria-hidden="true"></span>
</div>
tiffin-carrier.css
.sig-tiffin-carrier {
--sig-steel: #c9ced6;
--sig-text: #1e2127;
--sig-accent: #d3312f;
--sig-food: #f3e3c3;
--sig-w: 230px;
--sig-speed: 240ms;
}
.sig-tiffin-carrier {
position: relative;
box-sizing: border-box;
display: grid;
gap: 2px;
width: min(100%, var(--sig-w));
padding: 30px 16px 0;
font-family: system-ui, sans-serif;
}
.sig-tiffin-carrier__handle {
position: absolute;
top: 6px;
right: 2px;
bottom: 2px;
left: 2px;
border-width: 5px 5px 0;
border-style: solid;
border-color: color-mix(in srgb, var(--sig-steel) 72%, #000);
border-radius: 50% 50% 8px 8px / 26px 26px 8px 8px;
}
.sig-tiffin-carrier__grip {
position: absolute;
top: -8px;
left: calc(50% - 32px);
width: 64px;
height: 11px;
background: var(--sig-accent);
border-radius: 6px;
box-shadow: inset 0 2px 0 rgba(255,255,255,.3), inset 0 -2px 0 rgba(0,0,0,.3);
}
.sig-tiffin-carrier__lid {
position: relative;
display: block;
padding: 8px 4px 5px;
color: var(--sig-text);
font-size: 11px;
font-weight: 700;
letter-spacing: 0.1em;
text-align: center;
text-transform: uppercase;
background: linear-gradient(90deg, color-mix(in srgb, var(--sig-steel) 42%, #000), color-mix(in srgb, var(--sig-steel) 88%, #000) 9%, color-mix(in srgb, var(--sig-steel) 55%, #fff) 30%, var(--sig-steel) 52%, color-mix(in srgb, var(--sig-steel) 68%, #000) 84%, color-mix(in srgb, var(--sig-steel) 38%, #000));
border-radius: 50% 50% 5px 5px / 15px 15px 5px 5px;
box-shadow: inset 0 -2px 0 rgba(0,0,0,.28);
}
.sig-tiffin-carrier__tier {
position: relative;
overflow: hidden;
background: linear-gradient(90deg, color-mix(in srgb, var(--sig-steel) 42%, #000), color-mix(in srgb, var(--sig-steel) 88%, #000) 9%, color-mix(in srgb, var(--sig-steel) 55%, #fff) 30%, var(--sig-steel) 52%, color-mix(in srgb, var(--sig-steel) 68%, #000) 84%, color-mix(in srgb, var(--sig-steel) 38%, #000));
border-radius: 4px 4px 18px 18px / 3px 3px 10px 10px;
box-shadow: inset 0 2px 0 rgba(255,255,255,.6), inset 0 -4px 5px rgba(0,0,0,.28), 0 2px 3px rgba(0,0,0,.35);
}
.sig-tiffin-carrier__tier[data-open] {
--rows: 1fr;
--turn: 180deg;
--pad: 6px;
--tail: 10px;
}
.sig-tiffin-carrier__toggle {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
height: 32px;
padding: 0 12px;
color: var(--sig-text);
font-family: inherit;
font-size: 12px;
font-weight: 700;
background: none;
border: 0;
cursor: pointer;
}
.sig-tiffin-carrier__toggle:focus-visible {
outline: 2px solid var(--sig-accent);
outline-offset: -3px;
}
.sig-tiffin-carrier__mark {
color: var(--sig-accent);
transform: rotate(var(--turn, 0deg));
transition: transform var(--sig-speed) ease;
}
.sig-tiffin-carrier__food {
display: grid;
grid-template-rows: var(--rows, 0fr);
transition: grid-template-rows var(--sig-speed) ease;
}
.sig-tiffin-carrier__inner {
overflow: hidden;
margin: 0 10px var(--tail, 0);
padding: var(--pad, 0) 10px;
color: #3a2c18;
font-size: 12px;
line-height: 1.4;
background: var(--sig-food);
border-radius: 8px;
box-shadow: inset 0 2px 4px rgba(0,0,0,.3);
transition: padding var(--sig-speed) ease, margin var(--sig-speed) ease;
}
.sig-tiffin-carrier__base {
position: relative;
display: block;
height: 5px;
margin: 1px -14px 0;
background: color-mix(in srgb, var(--sig-steel) 72%, #000);
border-radius: 3px;
}
@media (prefers-reduced-motion: reduce) {
.sig-tiffin-carrier__mark,
.sig-tiffin-carrier__food,
.sig-tiffin-carrier__inner {
animation: none !important;
transition: none !important;
}
}
tiffin-carrier.js
document.querySelectorAll(".sig-tiffin-carrier").forEach((el) => {
const tiers = Array.from(el.querySelectorAll("[data-tier]"));
const open = (e) => {
const hit = (e.target).closest("button");
if (!hit) return;
const tier = hit.closest("[data-tier]");
const next = !tier.hasAttribute("data-open");
tiers.forEach((t) => {
const on = next && t === tier;
t.toggleAttribute("data-open", on);
(t.querySelector("button")).setAttribute("aria-expanded", String(on));
});
};
el.addEventListener("click", open);
return () => el.removeEventListener("click", open);
});
tiffin-carrier.tsx
import { useEffect, useRef } from "react";
import type React from "react";
import "./tiffin-carrier.css"; // the CSS tab
function attach(el: HTMLElement) {
const tiers = Array.from(el.querySelectorAll("[data-tier]")) as HTMLElement[];
const open = (e: MouseEvent) => {
const hit = (e.target as HTMLElement).closest("button");
if (!hit) return;
const tier = hit.closest("[data-tier]") as HTMLElement;
const next = !tier.hasAttribute("data-open");
tiers.forEach((t) => {
const on = next && t === tier;
t.toggleAttribute("data-open", on);
(t.querySelector("button") as HTMLElement).setAttribute("aria-expanded", String(on));
});
};
el.addEventListener("click", open);
return () => el.removeEventListener("click", open);
}
export function SigTiffinCarrier({ children = "Lunch", ...props }: React.ComponentProps<"div">) {
const ref = useRef<HTMLDivElement>(null);
useEffect(() => attach(ref.current!), []);
return (
<div className="sig-tiffin-carrier" ref={ref} {...props}>
<span className="sig-tiffin-carrier__handle" aria-hidden="true">
<span className="sig-tiffin-carrier__grip" />
</span>
<span className="sig-tiffin-carrier__lid">{children}</span>
<div className="sig-tiffin-carrier__tier" data-tier data-open>
<button className="sig-tiffin-carrier__toggle" type="button" aria-expanded="true">
<span className="sig-tiffin-carrier__tag">{"Sadham"}</span>
<span className="sig-tiffin-carrier__mark" aria-hidden="true">{"▾"}</span>
</button>
<div className="sig-tiffin-carrier__food">
<div className="sig-tiffin-carrier__inner">{"Sambar rice with ghee"}</div>
</div>
</div>
<div className="sig-tiffin-carrier__tier" data-tier>
<button className="sig-tiffin-carrier__toggle" type="button" aria-expanded="false">
<span className="sig-tiffin-carrier__tag">{"Poriyal"}</span>
<span className="sig-tiffin-carrier__mark" aria-hidden="true">{"▾"}</span>
</button>
<div className="sig-tiffin-carrier__food">
<div className="sig-tiffin-carrier__inner">{"Beans and coconut"}</div>
</div>
</div>
<div className="sig-tiffin-carrier__tier" data-tier>
<button className="sig-tiffin-carrier__toggle" type="button" aria-expanded="false">
<span className="sig-tiffin-carrier__tag">{"Thayir"}</span>
<span className="sig-tiffin-carrier__mark" aria-hidden="true">{"▾"}</span>
</button>
<div className="sig-tiffin-carrier__food">
<div className="sig-tiffin-carrier__inner">{"Curd rice, pickle"}</div>
</div>
</div>
<span className="sig-tiffin-carrier__base" aria-hidden="true" />
</div>
);
}
tiffin-carrier.html
<div class="sig-tiffin-carrier relative [box-sizing:border-box] grid gap-[2px] w-[min(100%,_230px)] [padding:30px_16px_0] [font-family:system-ui,_sans-serif]">
<span class="absolute [top:6px] [right:2px] [bottom:2px] [left:2px] [border-width:5px_5px_0] [border-style:solid] [border-color:color-mix(in_srgb,_#c9ced6_72%,_#000)] rounded-[50%_50%_8px_8px_/_26px_26px_8px_8px]" aria-hidden="true">
<span class="absolute [top:-8px] [left:calc(50%_-_32px)] w-[64px] h-[11px] bg-[#d3312f] rounded-[6px] [box-shadow:inset_0_2px_0_rgba(255,255,255,.3),_inset_0_-2px_0_rgba(0,0,0,.3)]"></span>
</span>
<span class="relative [display:block] [padding:8px_4px_5px] text-[#1e2127] text-[11px] font-[700] [letter-spacing:0.1em] [text-align:center] [text-transform:uppercase] [background:linear-gradient(90deg,_color-mix(in_srgb,_#c9ced6_42%,_#000),_color-mix(in_srgb,_#c9ced6_88%,_#000)_9%,_color-mix(in_srgb,_#c9ced6_55%,_#fff)_30%,_#c9ced6_52%,_color-mix(in_srgb,_#c9ced6_68%,_#000)_84%,_color-mix(in_srgb,_#c9ced6_38%,_#000))] rounded-[50%_50%_5px_5px_/_15px_15px_5px_5px] [box-shadow:inset_0_-2px_0_rgba(0,0,0,.28)]">Lunch</span>
<div class="relative overflow-hidden [background:linear-gradient(90deg,_color-mix(in_srgb,_#c9ced6_42%,_#000),_color-mix(in_srgb,_#c9ced6_88%,_#000)_9%,_color-mix(in_srgb,_#c9ced6_55%,_#fff)_30%,_#c9ced6_52%,_color-mix(in_srgb,_#c9ced6_68%,_#000)_84%,_color-mix(in_srgb,_#c9ced6_38%,_#000))] rounded-[4px_4px_18px_18px_/_3px_3px_10px_10px] [box-shadow:inset_0_2px_0_rgba(255,255,255,.6),_inset_0_-4px_5px_rgba(0,0,0,.28),_0_2px_3px_rgba(0,0,0,.35)] [&[data-open]]:[--rows:1fr] [&[data-open]]:[--turn:180deg] [&[data-open]]:[--pad:6px] [&[data-open]]:[--tail:10px]" data-tier data-open>
<button class="[display:flex] [justify-content:space-between] items-center w-[100%] h-[32px] [padding:0_12px] text-[#1e2127] [font-family:inherit] text-[12px] font-[700] [background:none] [border:0] cursor-pointer focus-visible:[outline:2px_solid_#d3312f] focus-visible:[outline-offset:-3px]" type="button" aria-expanded="true">
<span>Sadham</span>
<span class="text-[#d3312f] [transform:rotate(var(--turn,_0deg))] [transition:transform_240ms_ease] motion-reduce:[transition:none!important]" aria-hidden="true">▾</span>
</button>
<div class="grid [grid-template-rows:var(--rows,_0fr)] [transition:grid-template-rows_240ms_ease] motion-reduce:[transition:none!important]">
<div class="overflow-hidden [margin:0_10px_var(--tail,_0)] [padding:var(--pad,_0)_10px] text-[#3a2c18] text-[12px] [line-height:1.4] bg-[#f3e3c3] rounded-[8px] [box-shadow:inset_0_2px_4px_rgba(0,0,0,.3)] [transition:padding_240ms_ease,_margin_240ms_ease] motion-reduce:[transition:none!important]">Sambar rice with ghee</div>
</div>
</div>
<div class="relative overflow-hidden [background:linear-gradient(90deg,_color-mix(in_srgb,_#c9ced6_42%,_#000),_color-mix(in_srgb,_#c9ced6_88%,_#000)_9%,_color-mix(in_srgb,_#c9ced6_55%,_#fff)_30%,_#c9ced6_52%,_color-mix(in_srgb,_#c9ced6_68%,_#000)_84%,_color-mix(in_srgb,_#c9ced6_38%,_#000))] rounded-[4px_4px_18px_18px_/_3px_3px_10px_10px] [box-shadow:inset_0_2px_0_rgba(255,255,255,.6),_inset_0_-4px_5px_rgba(0,0,0,.28),_0_2px_3px_rgba(0,0,0,.35)] [&[data-open]]:[--rows:1fr] [&[data-open]]:[--turn:180deg] [&[data-open]]:[--pad:6px] [&[data-open]]:[--tail:10px]" data-tier>
<button class="[display:flex] [justify-content:space-between] items-center w-[100%] h-[32px] [padding:0_12px] text-[#1e2127] [font-family:inherit] text-[12px] font-[700] [background:none] [border:0] cursor-pointer focus-visible:[outline:2px_solid_#d3312f] focus-visible:[outline-offset:-3px]" type="button" aria-expanded="false">
<span>Poriyal</span>
<span class="text-[#d3312f] [transform:rotate(var(--turn,_0deg))] [transition:transform_240ms_ease] motion-reduce:[transition:none!important]" aria-hidden="true">▾</span>
</button>
<div class="grid [grid-template-rows:var(--rows,_0fr)] [transition:grid-template-rows_240ms_ease] motion-reduce:[transition:none!important]">
<div class="overflow-hidden [margin:0_10px_var(--tail,_0)] [padding:var(--pad,_0)_10px] text-[#3a2c18] text-[12px] [line-height:1.4] bg-[#f3e3c3] rounded-[8px] [box-shadow:inset_0_2px_4px_rgba(0,0,0,.3)] [transition:padding_240ms_ease,_margin_240ms_ease] motion-reduce:[transition:none!important]">Beans and coconut</div>
</div>
</div>
<div class="relative overflow-hidden [background:linear-gradient(90deg,_color-mix(in_srgb,_#c9ced6_42%,_#000),_color-mix(in_srgb,_#c9ced6_88%,_#000)_9%,_color-mix(in_srgb,_#c9ced6_55%,_#fff)_30%,_#c9ced6_52%,_color-mix(in_srgb,_#c9ced6_68%,_#000)_84%,_color-mix(in_srgb,_#c9ced6_38%,_#000))] rounded-[4px_4px_18px_18px_/_3px_3px_10px_10px] [box-shadow:inset_0_2px_0_rgba(255,255,255,.6),_inset_0_-4px_5px_rgba(0,0,0,.28),_0_2px_3px_rgba(0,0,0,.35)] [&[data-open]]:[--rows:1fr] [&[data-open]]:[--turn:180deg] [&[data-open]]:[--pad:6px] [&[data-open]]:[--tail:10px]" data-tier>
<button class="[display:flex] [justify-content:space-between] items-center w-[100%] h-[32px] [padding:0_12px] text-[#1e2127] [font-family:inherit] text-[12px] font-[700] [background:none] [border:0] cursor-pointer focus-visible:[outline:2px_solid_#d3312f] focus-visible:[outline-offset:-3px]" type="button" aria-expanded="false">
<span>Thayir</span>
<span class="text-[#d3312f] [transform:rotate(var(--turn,_0deg))] [transition:transform_240ms_ease] motion-reduce:[transition:none!important]" aria-hidden="true">▾</span>
</button>
<div class="grid [grid-template-rows:var(--rows,_0fr)] [transition:grid-template-rows_240ms_ease] motion-reduce:[transition:none!important]">
<div class="overflow-hidden [margin:0_10px_var(--tail,_0)] [padding:var(--pad,_0)_10px] text-[#3a2c18] text-[12px] [line-height:1.4] bg-[#f3e3c3] rounded-[8px] [box-shadow:inset_0_2px_4px_rgba(0,0,0,.3)] [transition:padding_240ms_ease,_margin_240ms_ease] motion-reduce:[transition:none!important]">Curd rice, pickle</div>
</div>
</div>
<span class="relative [display:block] h-[5px] [margin:1px_-14px_0] [background:color-mix(in_srgb,_#c9ced6_72%,_#000)] rounded-[3px]" aria-hidden="true"></span>
</div>
<!-- Needs the script from the JS tab. -->