30 Tailwind CSS Sidebar Layouts
20 / 30

All 30 in 30 Tailwind CSS Sidebar Layouts

  1. 01Tailwind SaaS Dashboard Sidebar with Native Dark Mode Switch
  2. 02Tailwind Collapsible Admin Sidebar with Icon Tooltips
  3. 03Tailwind Documentation Sidebar with Multi-Level :target Tree
  4. 04Tailwind Responsive Sidebar to Bottom Navigation Switch Layout
  5. 05Tailwind Analytics Sidebar with CSS Subgrid Column Alignment
  6. 06Tailwind Animated Collapsible Sidebar with Icon-Only Mode
  7. 07Tailwind Sticky Sidebar Layout with Scroll-Driven Progress Bar
  8. 08Tailwind Pure CSS Collapsible Sidebar Drawer without JavaScript
  9. 09Tailwind Fixed Sidebar Layout with Independently Scrolling Content
  10. 10Tailwind Off-Canvas Navigation Drawer with the Popover API
  11. 11Tailwind Right-Aligned Sidebar Layout for Inspector Panels
  12. 12Tailwind Two-Pane Webmail Layout with List and Reading Pane
  13. 13Tailwind Dual Sidebar App Shell with Left Nav and Context Panel
  14. 14Tailwind Mini Icon Rail Sidebar with Anchor-Positioned Active Pill
  15. 15Tailwind E-Commerce Filter Sidebar with Native Details Accordion
  16. 16Tailwind Floating Glassmorphism Sidebar with Gradient Hairline
  17. 17Tailwind Sidebar Menu with Pinned User Profile Footer
  18. 18Tailwind Push-Content Sidebar Drawer with GPU Transitions
  19. 19Tailwind Off-Canvas Cart Overlay with Blurred Backdrop Scrim
  20. 20Tailwind Sidebar with Persistent Collapsed State in localStorage
  21. 21Tailwind Sidebar with Sticky Search Bar Inside the Rail
  22. 22Tailwind Section-Grouped Sidebar Navigation with Header Labels
  23. 23Tailwind Sliding Active Indicator Sidebar with CSS :target
  24. 24Tailwind Multi-Level Nested Tree Sidebar with Details Branches
  25. 25Tailwind Tabbed Sidebar with Radio-Driven Panel Switching
  26. 26Tailwind Onboarding Wizard Sidebar with Step Progress Tracking
  27. 27Tailwind Resizable Sidebar Layout Using the CSS Resize Property
  28. 28Tailwind Three-Pane Workspace Shell with Icon Rail and List
  29. 29Tailwind Adaptive Sidebar Layout Using CSS Container Queries
  30. 30Tailwind Frosted Glass Floating Sidebar with Animated Aurora
Tailwind + JSMIT licensed

Tailwind Sidebar with Persistent Collapsed State in localStorage

The collapse that survives a refresh. Geometry derives from one --w variable that a data-state attribute swaps; six lines of JavaScript move a string between localStorage and that attribute, restored before first paint so returning users never see the wrong-width flash.

Live Demo
Plain CSSTailwind v4
Try it

Mosaic by Cruip: a complete Tailwind admin dashboard template in HTML, React, Next.js and Vue · $69

Cruip · Mosaic by Cruip · Affiliate link

View Mosaic dashboard

The code

<section id="tsl20" data-state="expanded" class="tsl-20 group/rail w-full h-svh min-h-svh grid grid-cols-[var(--w)_1fr] [--w:264px] data-[state=collapsed]:[--w:72px] transition-[grid-template-columns] duration-300 ease-[cubic-bezier(.4,0,.2,1)] font-tsl20-sans bg-tsl20-bg text-tsl20-ink motion-reduce:transition-none">
  <aside class="flex flex-col gap-0.5 px-3 py-4 bg-tsl20-panel border-r border-tsl20-line overflow-hidden">
    <div class="grid grid-cols-[26px_1fr] items-center gap-[11px] px-2.5 pt-1 pb-[18px] whitespace-nowrap">
      <span aria-hidden="true" class="w-[26px] h-[26px] rounded-lg bg-[conic-gradient(from_160deg,var(--color-tsl20-accent),oklch(0.6_0.15_250))]"></span>
      <span class="text-[15px] font-bold tracking-tight opacity-100 transition-opacity duration-[180ms] delay-[100ms] group-data-[state=collapsed]/rail:opacity-0">Keystone</span>
    </div>
    <nav aria-label="Main" class="flex flex-col gap-0.5">
      <a href="#" aria-current="page" class="grid grid-cols-[19px_1fr] items-center gap-3 min-h-11 px-3.5 rounded-[10px] text-sm font-medium whitespace-nowrap text-tsl20-mut no-underline transition-colors duration-200 hover:text-tsl20-ink hover:bg-tsl20-hover aria-[current=page]:text-tsl20-accent aria-[current=page]:bg-tsl20-tint focus-visible:outline-2 focus-visible:outline-tsl20-accent focus-visible:outline-offset-2"><svg viewBox="0 0 24 24" aria-hidden="true" class="w-[19px] h-[19px] fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]"><rect x="3" y="3" width="7.5" height="7.5" rx="1.5"/><rect x="13.5" y="3" width="7.5" height="7.5" rx="1.5"/><rect x="3" y="13.5" width="7.5" height="7.5" rx="1.5"/><rect x="13.5" y="13.5" width="7.5" height="7.5" rx="1.5"/></svg><span class="opacity-100 transition-opacity duration-[180ms] delay-[100ms] group-data-[state=collapsed]/rail:opacity-0">Home</span></a>
      <a href="#" class="grid grid-cols-[19px_1fr] items-center gap-3 min-h-11 px-3.5 rounded-[10px] text-sm font-medium whitespace-nowrap text-tsl20-mut no-underline transition-colors duration-200 hover:text-tsl20-ink hover:bg-tsl20-hover focus-visible:outline-2 focus-visible:outline-tsl20-accent focus-visible:outline-offset-2"><svg viewBox="0 0 24 24" aria-hidden="true" class="w-[19px] h-[19px] fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]"><path d="M4 20V10m6 10V4m6 16v-7"/></svg><span class="opacity-100 transition-opacity duration-[180ms] delay-[100ms] group-data-[state=collapsed]/rail:opacity-0">Metrics</span></a>
      <a href="#" class="grid grid-cols-[19px_1fr] items-center gap-3 min-h-11 px-3.5 rounded-[10px] text-sm font-medium whitespace-nowrap text-tsl20-mut no-underline transition-colors duration-200 hover:text-tsl20-ink hover:bg-tsl20-hover focus-visible:outline-2 focus-visible:outline-tsl20-accent focus-visible:outline-offset-2"><svg viewBox="0 0 24 24" aria-hidden="true" class="w-[19px] h-[19px] fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]"><path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/></svg><span class="opacity-100 transition-opacity duration-[180ms] delay-[100ms] group-data-[state=collapsed]/rail:opacity-0">Files</span></a>
      <a href="#" class="grid grid-cols-[19px_1fr] items-center gap-3 min-h-11 px-3.5 rounded-[10px] text-sm font-medium whitespace-nowrap text-tsl20-mut no-underline transition-colors duration-200 hover:text-tsl20-ink hover:bg-tsl20-hover focus-visible:outline-2 focus-visible:outline-tsl20-accent focus-visible:outline-offset-2"><svg viewBox="0 0 24 24" aria-hidden="true" class="w-[19px] h-[19px] fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]"><circle cx="12" cy="12" r="3"/><path d="M12 2v3m0 14v3M2 12h3m14 0h3M4.9 4.9l2.1 2.1m10 10 2.1 2.1m0-14.2-2.1 2.1m-10 10-2.1 2.1"/></svg><span class="opacity-100 transition-opacity duration-[180ms] delay-[100ms] group-data-[state=collapsed]/rail:opacity-0">Settings</span></a>
    </nav>
    <button type="button" aria-expanded="true" aria-controls="tsl20" class="tsl-20-toggle mt-auto grid grid-cols-[19px_1fr] items-center gap-3 min-h-11 px-3.5 rounded-[10px] border-0 bg-transparent text-left text-sm font-medium whitespace-nowrap text-tsl20-mut cursor-pointer transition-colors duration-200 hover:text-tsl20-ink hover:bg-tsl20-hover focus-visible:outline-2 focus-visible:outline-tsl20-accent focus-visible:outline-offset-2">
      <svg viewBox="0 0 24 24" aria-hidden="true" class="w-[19px] h-[19px] fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round] transition-[rotate] duration-300 group-data-[state=collapsed]/rail:rotate-180 motion-reduce:transition-none"><path d="m14 6-6 6 6 6"/></svg>
      <span class="opacity-100 transition-opacity duration-[180ms] delay-[100ms] group-data-[state=collapsed]/rail:opacity-0">Collapse</span>
    </button>
  </aside>
  <main class="p-8 overflow-y-auto [scrollbar-gutter:stable]">
    <h1 class="text-[22px] font-semibold leading-tight mb-2.5">Home</h1>
    <p class="max-w-[47ch] text-sm leading-relaxed text-tsl20-mut text-pretty">Collapse the rail, then reload the page &mdash; the state restores from localStorage before first paint. No wrong-width flash.</p>
  </main>
</section>
@import "tailwindcss";

@theme {
  --color-tsl20-bg: #101617;
  --color-tsl20-bg: oklch(0.13 0.012 195);
  --color-tsl20-panel: #161f21;
  --color-tsl20-panel: oklch(0.17 0.015 195);
  --color-tsl20-ink: #e8eff0;
  --color-tsl20-ink: oklch(0.94 0.005 195);
  --color-tsl20-mut: #929d9e;
  --color-tsl20-mut: oklch(0.64 0.012 195);
  --color-tsl20-line: #333e40;
  --color-tsl20-line: oklch(0.28 0.016 195);
  --color-tsl20-accent: #5cd6d3;
  --color-tsl20-accent: oklch(0.78 0.13 185);
  --color-tsl20-hover: rgb(232 239 240 / 0.07);
  --color-tsl20-hover: color-mix(in oklab, oklch(0.94 0.005 195) 7%, transparent);
  --color-tsl20-tint: rgb(92 214 211 / 0.14);
  --color-tsl20-tint: color-mix(in oklab, oklch(0.78 0.13 185) 14%, transparent);
  --font-tsl20-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}

.tsl-20 {
  width: 100%;
  height: 100svh;
  min-height: 100svh;
  color-scheme: dark;
}
(() => {
  const KEY = 'tsl20-sidebar';
  const shell = document.getElementById('tsl20');
  const btn = shell?.querySelector('.tsl-20-toggle');
  if (!shell || !btn) return;
  // Restore before paint: in production, inline this expression in a <script> right after the shell's opening tag.
  let saved = null;
  try { saved = localStorage.getItem(KEY); } catch (e) {}
  if (saved === 'collapsed' || saved === 'expanded') apply(saved);
  btn.addEventListener('click', () => {
    apply(shell.dataset.state === 'expanded' ? 'collapsed' : 'expanded');
  });
  function apply(state) {
    shell.dataset.state = state;                              // utilities read this
    btn.setAttribute('aria-expanded', String(state === 'expanded')); // AT reads this
    try { localStorage.setItem(KEY, state); } catch (e) {}    // tomorrow reads this
  }
})();

Setup — do this once per project

Skip this if your project already runs Tailwind v4.

1Load Tailwind
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>

Compiles in the browser — perfect for a CodePen or a quick test, not for production.

2Add the stylesheetRequiredthe @theme block is what makes the custom utilities exist
@import "tailwindcss";

@theme {
  --color-tsl20-bg: #101617;
  --color-tsl20-bg: oklch(0.13 0.012 195);
  --color-tsl20-panel: #161f21;
  --color-tsl20-panel: oklch(0.17 0.015 195);
  --color-tsl20-ink: #e8eff0;
  --color-tsl20-ink: oklch(0.94 0.005 195);
  --color-tsl20-mut: #929d9e;
  --color-tsl20-mut: oklch(0.64 0.012 195);
  --color-tsl20-line: #333e40;
  --color-tsl20-line: oklch(0.28 0.016 195);
  --color-tsl20-accent: #5cd6d3;
  --color-tsl20-accent: oklch(0.78 0.13 185);
  --color-tsl20-hover: rgb(232 239 240 / 0.07);
  --color-tsl20-hover: color-mix(in oklab, oklch(0.94 0.005 195) 7%, transparent);
  --color-tsl20-tint: rgb(92 214 211 / 0.14);
  --color-tsl20-tint: color-mix(in oklab, oklch(0.78 0.13 185) 14%, transparent);
  --font-tsl20-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}

.tsl-20 {
  width: 100%;
  height: 100svh;
  min-height: 100svh;
  color-scheme: dark;
}
3Fonts used by this demooptional — skip if you have your own type stack
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap">

What changed in the port

Which parts became utilities, which stayed CSS, and why.

  • data-[state=collapsed]: proves the variant system does not care where state comes from. Swap the checkbox for an attribute and every class in demo 06 still applies — only the prefix changes.
  • The shell carries group/rail so labels can read its attribute from two levels down. Named groups are the answer whenever state lives above the element that reacts to it.
  • UI change from the CSS original: the toggle is a real <button> with aria-expanded and aria-controls (unchanged in intent, but now min-h-11), and String() wraps the boolean so aria-expanded is always a valid string value.
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS demo from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Tailwind Sidebar with Persistent Collapsed State in localStorage
Source: https://codefronts.com/layouts/tailwind-css-sidebar-layouts/tailwind-sidebar-persistent-collapsed-state-localstorage/

The collapse that survives a refresh. Geometry derives from one --w variable that a data-state attribute swaps; six lines of JavaScript move a string between localStorage and that attribute, restored before first paint so returning users never see the wrong-width flash.
## HTML
```html
<section id="tsl20" data-state="expanded" class="tsl-20 group/rail w-full h-svh min-h-svh grid grid-cols-[var(--w)_1fr] [--w:264px] data-[state=collapsed]:[--w:72px] transition-[grid-template-columns] duration-300 ease-[cubic-bezier(.4,0,.2,1)] font-tsl20-sans bg-tsl20-bg text-tsl20-ink motion-reduce:transition-none">
  <aside class="flex flex-col gap-0.5 px-3 py-4 bg-tsl20-panel border-r border-tsl20-line overflow-hidden">
    <div class="grid grid-cols-[26px_1fr] items-center gap-[11px] px-2.5 pt-1 pb-[18px] whitespace-nowrap">
      <span aria-hidden="true" class="w-[26px] h-[26px] rounded-lg bg-[conic-gradient(from_160deg,var(--color-tsl20-accent),oklch(0.6_0.15_250))]"></span>
      <span class="text-[15px] font-bold tracking-tight opacity-100 transition-opacity duration-[180ms] delay-[100ms] group-data-[state=collapsed]/rail:opacity-0">Keystone</span>
    </div>
    <nav aria-label="Main" class="flex flex-col gap-0.5">
      <a href="#" aria-current="page" class="grid grid-cols-[19px_1fr] items-center gap-3 min-h-11 px-3.5 rounded-[10px] text-sm font-medium whitespace-nowrap text-tsl20-mut no-underline transition-colors duration-200 hover:text-tsl20-ink hover:bg-tsl20-hover aria-[current=page]:text-tsl20-accent aria-[current=page]:bg-tsl20-tint focus-visible:outline-2 focus-visible:outline-tsl20-accent focus-visible:outline-offset-2"><svg viewBox="0 0 24 24" aria-hidden="true" class="w-[19px] h-[19px] fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]"><rect x="3" y="3" width="7.5" height="7.5" rx="1.5"/><rect x="13.5" y="3" width="7.5" height="7.5" rx="1.5"/><rect x="3" y="13.5" width="7.5" height="7.5" rx="1.5"/><rect x="13.5" y="13.5" width="7.5" height="7.5" rx="1.5"/></svg><span class="opacity-100 transition-opacity duration-[180ms] delay-[100ms] group-data-[state=collapsed]/rail:opacity-0">Home</span></a>
      <a href="#" class="grid grid-cols-[19px_1fr] items-center gap-3 min-h-11 px-3.5 rounded-[10px] text-sm font-medium whitespace-nowrap text-tsl20-mut no-underline transition-colors duration-200 hover:text-tsl20-ink hover:bg-tsl20-hover focus-visible:outline-2 focus-visible:outline-tsl20-accent focus-visible:outline-offset-2"><svg viewBox="0 0 24 24" aria-hidden="true" class="w-[19px] h-[19px] fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]"><path d="M4 20V10m6 10V4m6 16v-7"/></svg><span class="opacity-100 transition-opacity duration-[180ms] delay-[100ms] group-data-[state=collapsed]/rail:opacity-0">Metrics</span></a>
      <a href="#" class="grid grid-cols-[19px_1fr] items-center gap-3 min-h-11 px-3.5 rounded-[10px] text-sm font-medium whitespace-nowrap text-tsl20-mut no-underline transition-colors duration-200 hover:text-tsl20-ink hover:bg-tsl20-hover focus-visible:outline-2 focus-visible:outline-tsl20-accent focus-visible:outline-offset-2"><svg viewBox="0 0 24 24" aria-hidden="true" class="w-[19px] h-[19px] fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]"><path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/></svg><span class="opacity-100 transition-opacity duration-[180ms] delay-[100ms] group-data-[state=collapsed]/rail:opacity-0">Files</span></a>
      <a href="#" class="grid grid-cols-[19px_1fr] items-center gap-3 min-h-11 px-3.5 rounded-[10px] text-sm font-medium whitespace-nowrap text-tsl20-mut no-underline transition-colors duration-200 hover:text-tsl20-ink hover:bg-tsl20-hover focus-visible:outline-2 focus-visible:outline-tsl20-accent focus-visible:outline-offset-2"><svg viewBox="0 0 24 24" aria-hidden="true" class="w-[19px] h-[19px] fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]"><circle cx="12" cy="12" r="3"/><path d="M12 2v3m0 14v3M2 12h3m14 0h3M4.9 4.9l2.1 2.1m10 10 2.1 2.1m0-14.2-2.1 2.1m-10 10-2.1 2.1"/></svg><span class="opacity-100 transition-opacity duration-[180ms] delay-[100ms] group-data-[state=collapsed]/rail:opacity-0">Settings</span></a>
    </nav>
    <button type="button" aria-expanded="true" aria-controls="tsl20" class="tsl-20-toggle mt-auto grid grid-cols-[19px_1fr] items-center gap-3 min-h-11 px-3.5 rounded-[10px] border-0 bg-transparent text-left text-sm font-medium whitespace-nowrap text-tsl20-mut cursor-pointer transition-colors duration-200 hover:text-tsl20-ink hover:bg-tsl20-hover focus-visible:outline-2 focus-visible:outline-tsl20-accent focus-visible:outline-offset-2">
      <svg viewBox="0 0 24 24" aria-hidden="true" class="w-[19px] h-[19px] fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round] transition-[rotate] duration-300 group-data-[state=collapsed]/rail:rotate-180 motion-reduce:transition-none"><path d="m14 6-6 6 6 6"/></svg>
      <span class="opacity-100 transition-opacity duration-[180ms] delay-[100ms] group-data-[state=collapsed]/rail:opacity-0">Collapse</span>
    </button>
  </aside>
  <main class="p-8 overflow-y-auto [scrollbar-gutter:stable]">
    <h1 class="text-[22px] font-semibold leading-tight mb-2.5">Home</h1>
    <p class="max-w-[47ch] text-sm leading-relaxed text-tsl20-mut text-pretty">Collapse the rail, then reload the page &mdash; the state restores from localStorage before first paint. No wrong-width flash.</p>
  </main>
</section>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-tsl20-bg: #101617;
  --color-tsl20-bg: oklch(0.13 0.012 195);
  --color-tsl20-panel: #161f21;
  --color-tsl20-panel: oklch(0.17 0.015 195);
  --color-tsl20-ink: #e8eff0;
  --color-tsl20-ink: oklch(0.94 0.005 195);
  --color-tsl20-mut: #929d9e;
  --color-tsl20-mut: oklch(0.64 0.012 195);
  --color-tsl20-line: #333e40;
  --color-tsl20-line: oklch(0.28 0.016 195);
  --color-tsl20-accent: #5cd6d3;
  --color-tsl20-accent: oklch(0.78 0.13 185);
  --color-tsl20-hover: rgb(232 239 240 / 0.07);
  --color-tsl20-hover: color-mix(in oklab, oklch(0.94 0.005 195) 7%, transparent);
  --color-tsl20-tint: rgb(92 214 211 / 0.14);
  --color-tsl20-tint: color-mix(in oklab, oklch(0.78 0.13 185) 14%, transparent);
  --font-tsl20-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}

.tsl-20 {
  width: 100%;
  height: 100svh;
  min-height: 100svh;
  color-scheme: dark;
}
```

## JavaScript
```js
(() => {
  const KEY = 'tsl20-sidebar';
  const shell = document.getElementById('tsl20');
  const btn = shell?.querySelector('.tsl-20-toggle');
  if (!shell || !btn) return;
  // Restore before paint: in production, inline this expression in a <script> right after the shell's opening tag.
  let saved = null;
  try { saved = localStorage.getItem(KEY); } catch (e) {}
  if (saved === 'collapsed' || saved === 'expanded') apply(saved);
  btn.addEventListener('click', () => {
    apply(shell.dataset.state === 'expanded' ? 'collapsed' : 'expanded');
  });
  function apply(state) {
    shell.dataset.state = state;                              // utilities read this
    btn.setAttribute('aria-expanded', String(state === 'expanded')); // AT reads this
    try { localStorage.setItem(KEY, state); } catch (e) {}    // tomorrow reads this
  }
})();
```

How this works

The CSS knows nothing about storage. JavaScript's entire job is moving one string; there is no style logic in the script and no persistence logic in the markup. That separation is what lets a designer retheme the rail without touching the controller.

Variants read attributes as happily as pseudo-classes. data-[state=collapsed]: works exactly like has-checked: in the pure-CSS demos, so this rail is the same design with a different state owner — useful when the state must outlive the page.

Flash prevention is the production detail: apply the saved state in an inline script during parsing, before the sidebar paints. The same trick theme switchers use.

Make it yours

  • Per-page defaults: key the storage as tsl20:{pathname}.
  • Live cross-tab sync: listen for the storage event, two more lines.
  • The same persist-an-attribute helper handles theme, density and pinned-panel preferences.

Gotchas — read before shipping

  • The restore must run INLINE and before the markup, or the default paints first and snaps — the FOUC of layout.
  • Write aria-expanded wherever you write data-state; split writers always drift.
  • localStorage throws in some private-browsing modes; the try/catch is not decoration.

Browser support

ChromeSafariFirefoxEdge
111+16.4+128+111+

Animatable grid tracks give the smooth collapse; the persistence pattern itself works anywhere localStorage exists.

Techniques used in this demo

Utilitiesfirst-class utility or variant

Theme tokensset in @theme, used by a utility

Search CodeFronts

Loading…