30 Tailwind CSS Sidebar Layouts
12 / 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
Pure TailwindMIT licensed

Tailwind Two-Pane Webmail Layout with List and Reading Pane

The list/detail split every mail client, messenger and support inbox uses: folder rail, 320px message list, reading pane — three independent scroll regions in one grid. The port's pleasure is line-clamp-2, which replaces the four-declaration -webkit-box incantation with one utility.

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 class="tsl-12 w-full h-svh min-h-svh grid grid-cols-[200px_320px_1fr] font-tsl12-sans bg-white text-tsl12-ink">
  <aside aria-label="Folders" class="bg-tsl12-panel border-r border-tsl12-line px-2.5 py-4 overflow-y-auto">
    <button type="button" class="w-full min-h-11 mb-3.5 rounded-[10px] border-0 bg-tsl12-accent text-white text-[13.5px] font-semibold cursor-pointer transition-[filter] duration-200 hover:brightness-110 focus-visible:outline-2 focus-visible:outline-tsl12-accent focus-visible:outline-offset-2">Compose</button>
    <nav class="flex flex-col gap-0.5">
      <a href="#" aria-current="page" class="flex items-center justify-between min-h-11 px-3 rounded-[9px] text-[13.5px] font-medium text-tsl12-mut no-underline transition-colors duration-150 hover:text-tsl12-ink hover:bg-tsl12-hover aria-[current=page]:text-tsl12-ink aria-[current=page]:bg-tsl12-tint aria-[current=page]:font-semibold focus-visible:outline-2 focus-visible:outline-tsl12-accent focus-visible:outline-offset-1">Inbox <span class="text-[11.5px] font-semibold text-tsl12-accent">12</span></a>
      <a href="#" class="flex items-center justify-between min-h-11 px-3 rounded-[9px] text-[13.5px] font-medium text-tsl12-mut no-underline transition-colors duration-150 hover:text-tsl12-ink hover:bg-tsl12-hover focus-visible:outline-2 focus-visible:outline-tsl12-accent focus-visible:outline-offset-1">Starred</a>
      <a href="#" class="flex items-center justify-between min-h-11 px-3 rounded-[9px] text-[13.5px] font-medium text-tsl12-mut no-underline transition-colors duration-150 hover:text-tsl12-ink hover:bg-tsl12-hover focus-visible:outline-2 focus-visible:outline-tsl12-accent focus-visible:outline-offset-1">Sent</a>
      <a href="#" class="flex items-center justify-between min-h-11 px-3 rounded-[9px] text-[13.5px] font-medium text-tsl12-mut no-underline transition-colors duration-150 hover:text-tsl12-ink hover:bg-tsl12-hover focus-visible:outline-2 focus-visible:outline-tsl12-accent focus-visible:outline-offset-1">Drafts <span class="text-[11.5px] font-semibold text-tsl12-accent">2</span></a>
      <a href="#" class="flex items-center justify-between min-h-11 px-3 rounded-[9px] text-[13.5px] font-medium text-tsl12-mut no-underline transition-colors duration-150 hover:text-tsl12-ink hover:bg-tsl12-hover focus-visible:outline-2 focus-visible:outline-tsl12-accent focus-visible:outline-offset-1">Archive</a>
    </nav>
  </aside>
  <div role="listbox" aria-label="Messages" class="border-r border-tsl12-line overflow-y-auto overscroll-contain [scrollbar-gutter:stable]">
    <a href="#" role="option" aria-selected="false" data-unread class="group grid grid-cols-[auto_1fr_auto] items-start gap-2.5 px-3.5 py-3 border-b border-tsl12-line no-underline text-inherit transition-colors duration-150 hover:bg-tsl12-rowhover aria-selected:bg-tsl12-tint aria-selected:shadow-[inset_2.5px_0_0_var(--color-tsl12-accent)] focus-visible:outline-2 focus-visible:outline-tsl12-accent focus-visible:-outline-offset-2"><span aria-hidden="true" class="w-[7px] h-[7px] mt-[7px] rounded-full bg-transparent group-data-unread:bg-tsl12-accent"></span><span class="min-w-0 grid gap-[3px]"><strong class="text-[13.5px] font-medium leading-tight group-data-unread:font-bold">Priya Raman</strong><em class="not-italic text-[12.5px] leading-[1.45] text-tsl12-mut line-clamp-2">Q3 roadmap review &mdash; the container query rollout is ahead of schedule and I think we can&hellip;</em></span><time class="mt-0.5 text-[11.5px] font-medium text-tsl12-mut">09:41</time></a>
    <a href="#" role="option" aria-selected="true" class="group grid grid-cols-[auto_1fr_auto] items-start gap-2.5 px-3.5 py-3 border-b border-tsl12-line no-underline text-inherit transition-colors duration-150 hover:bg-tsl12-rowhover aria-selected:bg-tsl12-tint aria-selected:shadow-[inset_2.5px_0_0_var(--color-tsl12-accent)] focus-visible:outline-2 focus-visible:outline-tsl12-accent focus-visible:-outline-offset-2"><span aria-hidden="true" class="w-[7px] h-[7px] mt-[7px] rounded-full bg-transparent group-data-unread:bg-tsl12-accent"></span><span class="min-w-0 grid gap-[3px]"><strong class="text-[13.5px] font-medium leading-tight group-data-unread:font-bold">Marcus Webb</strong><em class="not-italic text-[12.5px] leading-[1.45] text-tsl12-mut line-clamp-2">Re: Design tokens sync &mdash; agreed, let's move the semantic layer into the base package&hellip;</em></span><time class="mt-0.5 text-[11.5px] font-medium text-tsl12-mut">08:17</time></a>
    <a href="#" role="option" aria-selected="false" data-unread class="group grid grid-cols-[auto_1fr_auto] items-start gap-2.5 px-3.5 py-3 border-b border-tsl12-line no-underline text-inherit transition-colors duration-150 hover:bg-tsl12-rowhover aria-selected:bg-tsl12-tint aria-selected:shadow-[inset_2.5px_0_0_var(--color-tsl12-accent)] focus-visible:outline-2 focus-visible:outline-tsl12-accent focus-visible:-outline-offset-2"><span aria-hidden="true" class="w-[7px] h-[7px] mt-[7px] rounded-full bg-transparent group-data-unread:bg-tsl12-accent"></span><span class="min-w-0 grid gap-[3px]"><strong class="text-[13.5px] font-medium leading-tight group-data-unread:font-bold">Linear</strong><em class="not-italic text-[12.5px] leading-[1.45] text-tsl12-mut line-clamp-2">Weekly digest: 14 issues closed, 3 in review. Cycle 42 completes Friday.</em></span><time class="mt-0.5 text-[11.5px] font-medium text-tsl12-mut">Mon</time></a>
    <a href="#" role="option" aria-selected="false" class="group grid grid-cols-[auto_1fr_auto] items-start gap-2.5 px-3.5 py-3 border-b border-tsl12-line no-underline text-inherit transition-colors duration-150 hover:bg-tsl12-rowhover aria-selected:bg-tsl12-tint aria-selected:shadow-[inset_2.5px_0_0_var(--color-tsl12-accent)] focus-visible:outline-2 focus-visible:outline-tsl12-accent focus-visible:-outline-offset-2"><span aria-hidden="true" class="w-[7px] h-[7px] mt-[7px] rounded-full bg-transparent group-data-unread:bg-tsl12-accent"></span><span class="min-w-0 grid gap-[3px]"><strong class="text-[13.5px] font-medium leading-tight group-data-unread:font-bold">Amara Osei</strong><em class="not-italic text-[12.5px] leading-[1.45] text-tsl12-mut line-clamp-2">Accessibility audit results &mdash; great news on the focus-visible coverage, two items left&hellip;</em></span><time class="mt-0.5 text-[11.5px] font-medium text-tsl12-mut">Mon</time></a>
  </div>
  <main class="overflow-y-auto px-[38px] py-8 [scrollbar-gutter:stable]">
    <h1 class="text-xl font-bold leading-snug">Re: Design tokens sync</h1>
    <p class="mt-2.5 mb-[22px] text-[12.5px] font-medium text-tsl12-mut">Marcus Webb &middot; to Design Platform &middot; 08:17</p>
    <p class="max-w-[60ch] mb-3.5 text-sm leading-[1.7] text-tsl12-body text-pretty">Agreed &mdash; let's move the semantic layer into the base package. The component tokens can then alias into it, which keeps theming to a single override point.</p>
    <p class="max-w-[60ch] text-sm leading-[1.7] text-tsl12-body text-pretty">I'll draft the migration doc today. The mail list on your left scrolls independently of this pane &mdash; three scroll regions, one grid.</p>
  </main>
</section>
@import "tailwindcss";

@theme {
  --color-tsl12-panel: #f3f4f6;
  --color-tsl12-panel: oklch(0.965 0.005 240);
  --color-tsl12-ink: #363b43;
  --color-tsl12-ink: oklch(0.25 0.02 240);
  --color-tsl12-body: #4c525c;
  --color-tsl12-body: oklch(0.35 0.015 240);
  --color-tsl12-mut: #71767f;
  --color-tsl12-mut: oklch(0.5 0.015 240);
  --color-tsl12-line: #dfe1e5;
  --color-tsl12-line: oklch(0.89 0.007 240);
  --color-tsl12-accent: #3f5fd0;
  --color-tsl12-accent: oklch(0.52 0.17 255);
  --color-tsl12-hover: rgb(54 59 67 / 0.05);
  --color-tsl12-hover: color-mix(in oklab, oklch(0.25 0.02 240) 5%, transparent);
  --color-tsl12-rowhover: rgb(54 59 67 / 0.035);
  --color-tsl12-rowhover: color-mix(in oklab, oklch(0.25 0.02 240) 3.5%, transparent);
  --color-tsl12-tint: rgb(63 95 208 / 0.1);
  --color-tsl12-tint: color-mix(in oklab, oklch(0.52 0.17 255) 10%, transparent);
  --font-tsl12-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}

.tsl-12 {
  width: 100%;
  height: 100svh;
  min-height: 100svh;
  color-scheme: light;
}

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-tsl12-panel: #f3f4f6;
  --color-tsl12-panel: oklch(0.965 0.005 240);
  --color-tsl12-ink: #363b43;
  --color-tsl12-ink: oklch(0.25 0.02 240);
  --color-tsl12-body: #4c525c;
  --color-tsl12-body: oklch(0.35 0.015 240);
  --color-tsl12-mut: #71767f;
  --color-tsl12-mut: oklch(0.5 0.015 240);
  --color-tsl12-line: #dfe1e5;
  --color-tsl12-line: oklch(0.89 0.007 240);
  --color-tsl12-accent: #3f5fd0;
  --color-tsl12-accent: oklch(0.52 0.17 255);
  --color-tsl12-hover: rgb(54 59 67 / 0.05);
  --color-tsl12-hover: color-mix(in oklab, oklch(0.25 0.02 240) 5%, transparent);
  --color-tsl12-rowhover: rgb(54 59 67 / 0.035);
  --color-tsl12-rowhover: color-mix(in oklab, oklch(0.25 0.02 240) 3.5%, transparent);
  --color-tsl12-tint: rgb(63 95 208 / 0.1);
  --color-tsl12-tint: color-mix(in oklab, oklch(0.52 0.17 255) 10%, transparent);
  --font-tsl12-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}

.tsl-12 {
  width: 100%;
  height: 100svh;
  min-height: 100svh;
  color-scheme: light;
}
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.

  • group-data-unread: is how a row-level attribute reaches the dot and the sender name without a descendant selector. The row declares group, the children opt in — the same shape as the CSS original, minus the coupling to a class name.
  • line-clamp-2 deserves its reputation. Four declarations, one of them prefixed, all of them required, collapse into a single utility that cannot be half-applied.
  • UI change from the CSS original: folder rows and the compose button are min-h-11, the list gained scrollbar-gutter:stable so hover-revealed scrollbars stop nudging rows, and the reading pane's body text moved to a dedicated token at 4.6:1 contrast rather than an inline oklch(0.35…).
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 Two-Pane Webmail Layout with List and Reading Pane
Source: https://codefronts.com/layouts/tailwind-css-sidebar-layouts/tailwind-two-pane-webmail-list-reading-layout/

The list/detail split every mail client, messenger and support inbox uses: folder rail, 320px message list, reading pane &mdash; three independent scroll regions in one grid. The port's pleasure is line-clamp-2, which replaces the four-declaration -webkit-box incantation with one utility.
## HTML
```html
<section class="tsl-12 w-full h-svh min-h-svh grid grid-cols-[200px_320px_1fr] font-tsl12-sans bg-white text-tsl12-ink">
  <aside aria-label="Folders" class="bg-tsl12-panel border-r border-tsl12-line px-2.5 py-4 overflow-y-auto">
    <button type="button" class="w-full min-h-11 mb-3.5 rounded-[10px] border-0 bg-tsl12-accent text-white text-[13.5px] font-semibold cursor-pointer transition-[filter] duration-200 hover:brightness-110 focus-visible:outline-2 focus-visible:outline-tsl12-accent focus-visible:outline-offset-2">Compose</button>
    <nav class="flex flex-col gap-0.5">
      <a href="#" aria-current="page" class="flex items-center justify-between min-h-11 px-3 rounded-[9px] text-[13.5px] font-medium text-tsl12-mut no-underline transition-colors duration-150 hover:text-tsl12-ink hover:bg-tsl12-hover aria-[current=page]:text-tsl12-ink aria-[current=page]:bg-tsl12-tint aria-[current=page]:font-semibold focus-visible:outline-2 focus-visible:outline-tsl12-accent focus-visible:outline-offset-1">Inbox <span class="text-[11.5px] font-semibold text-tsl12-accent">12</span></a>
      <a href="#" class="flex items-center justify-between min-h-11 px-3 rounded-[9px] text-[13.5px] font-medium text-tsl12-mut no-underline transition-colors duration-150 hover:text-tsl12-ink hover:bg-tsl12-hover focus-visible:outline-2 focus-visible:outline-tsl12-accent focus-visible:outline-offset-1">Starred</a>
      <a href="#" class="flex items-center justify-between min-h-11 px-3 rounded-[9px] text-[13.5px] font-medium text-tsl12-mut no-underline transition-colors duration-150 hover:text-tsl12-ink hover:bg-tsl12-hover focus-visible:outline-2 focus-visible:outline-tsl12-accent focus-visible:outline-offset-1">Sent</a>
      <a href="#" class="flex items-center justify-between min-h-11 px-3 rounded-[9px] text-[13.5px] font-medium text-tsl12-mut no-underline transition-colors duration-150 hover:text-tsl12-ink hover:bg-tsl12-hover focus-visible:outline-2 focus-visible:outline-tsl12-accent focus-visible:outline-offset-1">Drafts <span class="text-[11.5px] font-semibold text-tsl12-accent">2</span></a>
      <a href="#" class="flex items-center justify-between min-h-11 px-3 rounded-[9px] text-[13.5px] font-medium text-tsl12-mut no-underline transition-colors duration-150 hover:text-tsl12-ink hover:bg-tsl12-hover focus-visible:outline-2 focus-visible:outline-tsl12-accent focus-visible:outline-offset-1">Archive</a>
    </nav>
  </aside>
  <div role="listbox" aria-label="Messages" class="border-r border-tsl12-line overflow-y-auto overscroll-contain [scrollbar-gutter:stable]">
    <a href="#" role="option" aria-selected="false" data-unread class="group grid grid-cols-[auto_1fr_auto] items-start gap-2.5 px-3.5 py-3 border-b border-tsl12-line no-underline text-inherit transition-colors duration-150 hover:bg-tsl12-rowhover aria-selected:bg-tsl12-tint aria-selected:shadow-[inset_2.5px_0_0_var(--color-tsl12-accent)] focus-visible:outline-2 focus-visible:outline-tsl12-accent focus-visible:-outline-offset-2"><span aria-hidden="true" class="w-[7px] h-[7px] mt-[7px] rounded-full bg-transparent group-data-unread:bg-tsl12-accent"></span><span class="min-w-0 grid gap-[3px]"><strong class="text-[13.5px] font-medium leading-tight group-data-unread:font-bold">Priya Raman</strong><em class="not-italic text-[12.5px] leading-[1.45] text-tsl12-mut line-clamp-2">Q3 roadmap review &mdash; the container query rollout is ahead of schedule and I think we can&hellip;</em></span><time class="mt-0.5 text-[11.5px] font-medium text-tsl12-mut">09:41</time></a>
    <a href="#" role="option" aria-selected="true" class="group grid grid-cols-[auto_1fr_auto] items-start gap-2.5 px-3.5 py-3 border-b border-tsl12-line no-underline text-inherit transition-colors duration-150 hover:bg-tsl12-rowhover aria-selected:bg-tsl12-tint aria-selected:shadow-[inset_2.5px_0_0_var(--color-tsl12-accent)] focus-visible:outline-2 focus-visible:outline-tsl12-accent focus-visible:-outline-offset-2"><span aria-hidden="true" class="w-[7px] h-[7px] mt-[7px] rounded-full bg-transparent group-data-unread:bg-tsl12-accent"></span><span class="min-w-0 grid gap-[3px]"><strong class="text-[13.5px] font-medium leading-tight group-data-unread:font-bold">Marcus Webb</strong><em class="not-italic text-[12.5px] leading-[1.45] text-tsl12-mut line-clamp-2">Re: Design tokens sync &mdash; agreed, let's move the semantic layer into the base package&hellip;</em></span><time class="mt-0.5 text-[11.5px] font-medium text-tsl12-mut">08:17</time></a>
    <a href="#" role="option" aria-selected="false" data-unread class="group grid grid-cols-[auto_1fr_auto] items-start gap-2.5 px-3.5 py-3 border-b border-tsl12-line no-underline text-inherit transition-colors duration-150 hover:bg-tsl12-rowhover aria-selected:bg-tsl12-tint aria-selected:shadow-[inset_2.5px_0_0_var(--color-tsl12-accent)] focus-visible:outline-2 focus-visible:outline-tsl12-accent focus-visible:-outline-offset-2"><span aria-hidden="true" class="w-[7px] h-[7px] mt-[7px] rounded-full bg-transparent group-data-unread:bg-tsl12-accent"></span><span class="min-w-0 grid gap-[3px]"><strong class="text-[13.5px] font-medium leading-tight group-data-unread:font-bold">Linear</strong><em class="not-italic text-[12.5px] leading-[1.45] text-tsl12-mut line-clamp-2">Weekly digest: 14 issues closed, 3 in review. Cycle 42 completes Friday.</em></span><time class="mt-0.5 text-[11.5px] font-medium text-tsl12-mut">Mon</time></a>
    <a href="#" role="option" aria-selected="false" class="group grid grid-cols-[auto_1fr_auto] items-start gap-2.5 px-3.5 py-3 border-b border-tsl12-line no-underline text-inherit transition-colors duration-150 hover:bg-tsl12-rowhover aria-selected:bg-tsl12-tint aria-selected:shadow-[inset_2.5px_0_0_var(--color-tsl12-accent)] focus-visible:outline-2 focus-visible:outline-tsl12-accent focus-visible:-outline-offset-2"><span aria-hidden="true" class="w-[7px] h-[7px] mt-[7px] rounded-full bg-transparent group-data-unread:bg-tsl12-accent"></span><span class="min-w-0 grid gap-[3px]"><strong class="text-[13.5px] font-medium leading-tight group-data-unread:font-bold">Amara Osei</strong><em class="not-italic text-[12.5px] leading-[1.45] text-tsl12-mut line-clamp-2">Accessibility audit results &mdash; great news on the focus-visible coverage, two items left&hellip;</em></span><time class="mt-0.5 text-[11.5px] font-medium text-tsl12-mut">Mon</time></a>
  </div>
  <main class="overflow-y-auto px-[38px] py-8 [scrollbar-gutter:stable]">
    <h1 class="text-xl font-bold leading-snug">Re: Design tokens sync</h1>
    <p class="mt-2.5 mb-[22px] text-[12.5px] font-medium text-tsl12-mut">Marcus Webb &middot; to Design Platform &middot; 08:17</p>
    <p class="max-w-[60ch] mb-3.5 text-sm leading-[1.7] text-tsl12-body text-pretty">Agreed &mdash; let's move the semantic layer into the base package. The component tokens can then alias into it, which keeps theming to a single override point.</p>
    <p class="max-w-[60ch] text-sm leading-[1.7] text-tsl12-body text-pretty">I'll draft the migration doc today. The mail list on your left scrolls independently of this pane &mdash; three scroll regions, one grid.</p>
  </main>
</section>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-tsl12-panel: #f3f4f6;
  --color-tsl12-panel: oklch(0.965 0.005 240);
  --color-tsl12-ink: #363b43;
  --color-tsl12-ink: oklch(0.25 0.02 240);
  --color-tsl12-body: #4c525c;
  --color-tsl12-body: oklch(0.35 0.015 240);
  --color-tsl12-mut: #71767f;
  --color-tsl12-mut: oklch(0.5 0.015 240);
  --color-tsl12-line: #dfe1e5;
  --color-tsl12-line: oklch(0.89 0.007 240);
  --color-tsl12-accent: #3f5fd0;
  --color-tsl12-accent: oklch(0.52 0.17 255);
  --color-tsl12-hover: rgb(54 59 67 / 0.05);
  --color-tsl12-hover: color-mix(in oklab, oklch(0.25 0.02 240) 5%, transparent);
  --color-tsl12-rowhover: rgb(54 59 67 / 0.035);
  --color-tsl12-rowhover: color-mix(in oklab, oklch(0.25 0.02 240) 3.5%, transparent);
  --color-tsl12-tint: rgb(63 95 208 / 0.1);
  --color-tsl12-tint: color-mix(in oklab, oklch(0.52 0.17 255) 10%, transparent);
  --font-tsl12-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}

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

How this works

State lives in attributes, and v4 has a variant for each. data-unread:font-bold on the sender, data-unread:bg-tsl12-accent on the dot, aria-selected:bg-tsl12-tint on the open row. The CSS original already used attribute selectors for exactly this reason; the port keeps the discipline and moves it onto the row, where a reviewer can check the pairing at a glance.

The preview clamp is the single biggest utility win in the collection. display:-webkit-box + -webkit-box-orient + -webkit-line-clamp + overflow:hidden is a four-part incantation where omitting any one part silently disables the whole thing. line-clamp-2 cannot be got wrong.

Make it yours

  • Under 900px, hide the folder rail behind demo 08's drawer — every real mail client does exactly this.
  • 320px is the industry-constant list width; 360px if you show avatars.
  • line-clamp-1 for dense ops inboxes, line-clamp-3 for support queues where context matters more than density.

Gotchas — read before shipping

  • Each row should be ONE interactive element. Nesting buttons inside a clickable row creates unreachable controls.
  • min-w-0 on the text column, or the preview refuses to truncate and pushes the timestamp off the row.
  • Never signal unread with colour alone — the dot plus the bold weight here is the WCAG-safe double signal.

Browser support

ChromeSafariFirefoxEdge
111+16.4+128+111+

Line clamp shipped prefixed everywhere years ago and Tailwind emits the prefixes; the rest is baseline grid.

Techniques used in this demo

Utilitiesfirst-class utility or variant

Search CodeFronts

Loading…