Expandable Card
A copy-paste css-only interactions component in pure HTML & CSS. Zero dependencies, framework-agnostic, MIT-licensed.
CSS-Only InteractionsHTMLCSSany framework
Copy into your project
HTML
<!-- Expandable Card — checkbox + :has() + grid-rows trick, zero JS -->
<div class="nuda-css-expand">
<input class="nuda-css-expand__input" type="checkbox" id="nuda-css-ex-1" />
<div class="nuda-css-expand__head">
<span class="nuda-css-expand__title">Project Aurora</span>
<span class="nuda-css-expand__sub">Realtime collaboration</span>
</div>
<div class="nuda-css-expand__more">
<div class="nuda-css-expand__more-inner">
<p class="nuda-css-expand__body">
Expanded details appear here, animated with the
grid-rows 0fr to 1fr trick. No JS.
</p>
</div>
</div>
<label class="nuda-css-expand__toggle" for="nuda-css-ex-1">
<span class="nuda-css-expand__txt-more">Show more</span>
<span class="nuda-css-expand__txt-less">Show less</span>
</label>
</div>CSS
/* Expandable Card
A hidden checkbox + :has() toggles the expanded region.
Height animates via the grid-template-rows 0fr -> 1fr trick
(animating grid is allowed; no width/height/margin/padding
transition). No JS. Customize: --nuda-css-ex-accent */
.nuda-css-expand {
--nuda-css-ex-accent: #e4ff54;
width: 100%;
max-width: 420px;
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 12px;
overflow: hidden;
}
.nuda-css-expand__input {
position: absolute;
width: 1px; height: 1px;
margin: -1px; overflow: hidden;
clip: rect(0, 0, 0, 0); border: 0;
}
.nuda-css-expand__head {
display: flex;
flex-direction: column;
gap: 4px;
padding: 1rem;
}
.nuda-css-expand__title { font-size: 0.95rem; font-weight: 700; color: #fff; }
.nuda-css-expand__sub { font-size: 0.8rem; color: #888; }
/* Collapsible region — grid-rows trick */
.nuda-css-expand__more {
display: grid;
grid-template-rows: 0fr;
transition: grid-template-rows 0.35s cubic-bezier(0.16, 1, 0.3, 1);
}
.nuda-css-expand__more-inner {
overflow: hidden;
min-height: 0;
}
.nuda-css-expand__body {
padding: 0 1rem;
font-size: 0.85rem;
line-height: 1.5;
color: #aaa;
opacity: 0;
transition: opacity 0.3s ease;
}
.nuda-css-expand__toggle {
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
min-height: 44px;
margin: 0.75rem 1rem 1rem;
font-size: 0.85rem;
font-weight: 600;
color: #09090b;
background: var(--nuda-css-ex-accent);
border-radius: 8px;
cursor: pointer;
}
.nuda-css-expand__toggle .nuda-css-expand__txt-less { display: none; }
/* Expanded state via :has() */
.nuda-css-expand:has(.nuda-css-expand__input:checked) .nuda-css-expand__more {
grid-template-rows: 1fr;
}
.nuda-css-expand:has(.nuda-css-expand__input:checked) .nuda-css-expand__body {
opacity: 1;
padding-bottom: 0.5rem;
}
.nuda-css-expand:has(.nuda-css-expand__input:checked) .nuda-css-expand__txt-more { display: none; }
.nuda-css-expand:has(.nuda-css-expand__input:checked) .nuda-css-expand__txt-less { display: inline; }
.nuda-css-expand__input:focus-visible + .nuda-css-expand__head .nuda-css-expand__title {
outline: 2px solid var(--nuda-css-ex-accent);
outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) {
.nuda-css-expand__more,
.nuda-css-expand__body { transition: none; }
}How to use Expandable Card
Paste the HTML where you need it and the CSS into a global stylesheet (or a <style> tag). Every class is prefixed nuda- so it never collides with Tailwind or your own styles. Tweak the CSS custom properties to match your design system.
Works in React, Vue, Svelte, Astro, Next.js, Nuxt, Laravel Blade, Django, Rails — or a single .html file. No npm install, no build step.