04 / 30
All 30 in 30 Tailwind CSS Sidebar Layouts
- 01Tailwind SaaS Dashboard Sidebar with Native Dark Mode Switch
- 02Tailwind Collapsible Admin Sidebar with Icon Tooltips
- 03Tailwind Documentation Sidebar with Multi-Level :target Tree
- 04Tailwind Responsive Sidebar to Bottom Navigation Switch Layout
- 05Tailwind Analytics Sidebar with CSS Subgrid Column Alignment
- 06Tailwind Animated Collapsible Sidebar with Icon-Only Mode
- 07Tailwind Sticky Sidebar Layout with Scroll-Driven Progress Bar
- 08Tailwind Pure CSS Collapsible Sidebar Drawer without JavaScript
- 09Tailwind Fixed Sidebar Layout with Independently Scrolling Content
- 10Tailwind Off-Canvas Navigation Drawer with the Popover API
- 11Tailwind Right-Aligned Sidebar Layout for Inspector Panels
- 12Tailwind Two-Pane Webmail Layout with List and Reading Pane
- 13Tailwind Dual Sidebar App Shell with Left Nav and Context Panel
- 14Tailwind Mini Icon Rail Sidebar with Anchor-Positioned Active Pill
- 15Tailwind E-Commerce Filter Sidebar with Native Details Accordion
- 16Tailwind Floating Glassmorphism Sidebar with Gradient Hairline
- 17Tailwind Sidebar Menu with Pinned User Profile Footer
- 18Tailwind Push-Content Sidebar Drawer with GPU Transitions
- 19Tailwind Off-Canvas Cart Overlay with Blurred Backdrop Scrim
- 20Tailwind Sidebar with Persistent Collapsed State in localStorage
- 21Tailwind Sidebar with Sticky Search Bar Inside the Rail
- 22Tailwind Section-Grouped Sidebar Navigation with Header Labels
- 23Tailwind Sliding Active Indicator Sidebar with CSS :target
- 24Tailwind Multi-Level Nested Tree Sidebar with Details Branches
- 25Tailwind Tabbed Sidebar with Radio-Driven Panel Switching
- 26Tailwind Onboarding Wizard Sidebar with Step Progress Tracking
- 27Tailwind Resizable Sidebar Layout Using the CSS Resize Property
- 28Tailwind Three-Pane Workspace Shell with Icon Rail and List
- 29Tailwind Adaptive Sidebar Layout Using CSS Container Queries
- 30Tailwind Frosted Glass Floating Sidebar with Animated Aurora
Tailwind Responsive Sidebar to Bottom Navigation Switch Layout
One <nav>, two personalities: under 768px an iOS-style bottom tab bar with safe-area padding, at and above it the same markup stands up into a left sidebar. No duplicate nav trees, no resize listeners — the breakpoint prefix does all of it.
Mosaic by Cruip: a complete Tailwind admin dashboard template in HTML, React, Next.js and Vue · $69
Cruip · Mosaic by Cruip · Affiliate link
The code
<section class="tsl-04 w-full h-svh min-h-svh grid grid-rows-[1fr_auto] md:grid-cols-[248px_1fr] md:grid-rows-1 font-tsl04-sans bg-tsl04-bg text-tsl04-ink">
<nav aria-label="Primary" class="order-2 md:order-1 flex flex-row md:flex-col md:gap-0.5 md:justify-start bg-tsl04-panel border-t md:border-t-0 md:border-r border-tsl04-line px-2 pt-1.5 pb-[calc(0.375rem+env(safe-area-inset-bottom,0px))] md:px-3 md:py-[18px]">
<a href="#" aria-current="page" class="flex-1 md:flex-none flex flex-col md:flex-row items-center justify-center md:justify-start gap-[3px] md:gap-3 min-h-12 md:min-h-11 md:px-3.5 rounded-xl text-[10.5px] md:text-sm font-medium text-tsl04-mut no-underline transition-colors duration-200 hover:text-tsl04-ink aria-[current=page]:text-tsl04-accent aria-[current=page]:bg-tsl04-tint focus-visible:outline-2 focus-visible:outline-tsl04-accent focus-visible:outline-offset-2"><svg viewBox="0 0 24 24" aria-hidden="true" class="w-[21px] h-[21px] shrink-0 fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]"><path d="M3 10.5 12 3l9 7.5M5.5 9.5V21h13V9.5"/></svg><span>Home</span></a>
<a href="#" class="flex-1 md:flex-none flex flex-col md:flex-row items-center justify-center md:justify-start gap-[3px] md:gap-3 min-h-12 md:min-h-11 md:px-3.5 rounded-xl text-[10.5px] md:text-sm font-medium text-tsl04-mut no-underline transition-colors duration-200 hover:text-tsl04-ink focus-visible:outline-2 focus-visible:outline-tsl04-accent focus-visible:outline-offset-2"><svg viewBox="0 0 24 24" aria-hidden="true" class="w-[21px] h-[21px] shrink-0 fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]"><circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/></svg><span>Search</span></a>
<a href="#" class="flex-1 md:flex-none flex flex-col md:flex-row items-center justify-center md:justify-start gap-[3px] md:gap-3 min-h-12 md:min-h-11 md:px-3.5 rounded-xl text-[10.5px] md:text-sm font-medium text-tsl04-mut no-underline transition-colors duration-200 hover:text-tsl04-ink focus-visible:outline-2 focus-visible:outline-tsl04-accent focus-visible:outline-offset-2"><svg viewBox="0 0 24 24" aria-hidden="true" class="w-[21px] h-[21px] shrink-0 fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]"><rect x="3" y="4" width="18" height="16" rx="3"/><path d="M3 9h18M8 2v4m8-4v4"/></svg><span>Bookings</span></a>
<a href="#" class="flex-1 md:flex-none flex flex-col md:flex-row items-center justify-center md:justify-start gap-[3px] md:gap-3 min-h-12 md:min-h-11 md:px-3.5 rounded-xl text-[10.5px] md:text-sm font-medium text-tsl04-mut no-underline transition-colors duration-200 hover:text-tsl04-ink focus-visible:outline-2 focus-visible:outline-tsl04-accent focus-visible:outline-offset-2"><svg viewBox="0 0 24 24" aria-hidden="true" class="w-[21px] h-[21px] shrink-0 fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]"><path d="M18 8a6 6 0 1 0-12 0c0 7-3 8-3 8h18s-3-1-3-8m-4.3 11a2 2 0 0 1-3.4 0"/></svg><span>Alerts</span></a>
<a href="#" class="flex-1 md:flex-none flex flex-col md:flex-row items-center justify-center md:justify-start gap-[3px] md:gap-3 min-h-12 md:min-h-11 md:px-3.5 rounded-xl text-[10.5px] md:text-sm font-medium text-tsl04-mut no-underline transition-colors duration-200 hover:text-tsl04-ink focus-visible:outline-2 focus-visible:outline-tsl04-accent focus-visible:outline-offset-2"><svg viewBox="0 0 24 24" aria-hidden="true" class="w-[21px] h-[21px] shrink-0 fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]"><circle cx="12" cy="8" r="4"/><path d="M4 21c0-3.9 3.6-6.5 8-6.5s8 2.6 8 6.5"/></svg><span>Profile</span></a>
</nav>
<main class="order-1 md:order-2 overflow-y-auto p-7 [scrollbar-gutter:stable]">
<h1 class="text-[22px] font-semibold leading-tight mb-2.5">Today</h1>
<p class="max-w-[46ch] text-sm leading-relaxed text-tsl04-mut text-pretty">Resize the viewport — under 768px this same nav element becomes a bottom tab bar with safe-area padding.</p>
</main>
</section><section class="tsl-04 w-full h-svh min-h-svh grid grid-rows-[1fr_auto] md:grid-cols-[248px_1fr] md:grid-rows-1 font-tsl04-sans bg-tsl04-bg text-tsl04-ink">
<nav aria-label="Primary" class="order-2 md:order-1 flex flex-row md:flex-col md:gap-0.5 md:justify-start bg-tsl04-panel border-t md:border-t-0 md:border-r border-tsl04-line px-2 pt-1.5 pb-[calc(0.375rem+env(safe-area-inset-bottom,0px))] md:px-3 md:py-[18px]">
<a href="#" aria-current="page" class="flex-1 md:flex-none flex flex-col md:flex-row items-center justify-center md:justify-start gap-[3px] md:gap-3 min-h-12 md:min-h-11 md:px-3.5 rounded-xl text-[10.5px] md:text-sm font-medium text-tsl04-mut no-underline transition-colors duration-200 hover:text-tsl04-ink aria-[current=page]:text-tsl04-accent aria-[current=page]:bg-tsl04-tint focus-visible:outline-2 focus-visible:outline-tsl04-accent focus-visible:outline-offset-2"><svg viewBox="0 0 24 24" aria-hidden="true" class="w-[21px] h-[21px] shrink-0 fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]"><path d="M3 10.5 12 3l9 7.5M5.5 9.5V21h13V9.5"/></svg><span>Home</span></a>
<a href="#" class="flex-1 md:flex-none flex flex-col md:flex-row items-center justify-center md:justify-start gap-[3px] md:gap-3 min-h-12 md:min-h-11 md:px-3.5 rounded-xl text-[10.5px] md:text-sm font-medium text-tsl04-mut no-underline transition-colors duration-200 hover:text-tsl04-ink focus-visible:outline-2 focus-visible:outline-tsl04-accent focus-visible:outline-offset-2"><svg viewBox="0 0 24 24" aria-hidden="true" class="w-[21px] h-[21px] shrink-0 fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]"><circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/></svg><span>Search</span></a>
<a href="#" class="flex-1 md:flex-none flex flex-col md:flex-row items-center justify-center md:justify-start gap-[3px] md:gap-3 min-h-12 md:min-h-11 md:px-3.5 rounded-xl text-[10.5px] md:text-sm font-medium text-tsl04-mut no-underline transition-colors duration-200 hover:text-tsl04-ink focus-visible:outline-2 focus-visible:outline-tsl04-accent focus-visible:outline-offset-2"><svg viewBox="0 0 24 24" aria-hidden="true" class="w-[21px] h-[21px] shrink-0 fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]"><rect x="3" y="4" width="18" height="16" rx="3"/><path d="M3 9h18M8 2v4m8-4v4"/></svg><span>Bookings</span></a>
<a href="#" class="flex-1 md:flex-none flex flex-col md:flex-row items-center justify-center md:justify-start gap-[3px] md:gap-3 min-h-12 md:min-h-11 md:px-3.5 rounded-xl text-[10.5px] md:text-sm font-medium text-tsl04-mut no-underline transition-colors duration-200 hover:text-tsl04-ink focus-visible:outline-2 focus-visible:outline-tsl04-accent focus-visible:outline-offset-2"><svg viewBox="0 0 24 24" aria-hidden="true" class="w-[21px] h-[21px] shrink-0 fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]"><path d="M18 8a6 6 0 1 0-12 0c0 7-3 8-3 8h18s-3-1-3-8m-4.3 11a2 2 0 0 1-3.4 0"/></svg><span>Alerts</span></a>
<a href="#" class="flex-1 md:flex-none flex flex-col md:flex-row items-center justify-center md:justify-start gap-[3px] md:gap-3 min-h-12 md:min-h-11 md:px-3.5 rounded-xl text-[10.5px] md:text-sm font-medium text-tsl04-mut no-underline transition-colors duration-200 hover:text-tsl04-ink focus-visible:outline-2 focus-visible:outline-tsl04-accent focus-visible:outline-offset-2"><svg viewBox="0 0 24 24" aria-hidden="true" class="w-[21px] h-[21px] shrink-0 fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]"><circle cx="12" cy="8" r="4"/><path d="M4 21c0-3.9 3.6-6.5 8-6.5s8 2.6 8 6.5"/></svg><span>Profile</span></a>
</nav>
<main class="order-1 md:order-2 overflow-y-auto p-7 [scrollbar-gutter:stable]">
<h1 class="text-[22px] font-semibold leading-tight mb-2.5">Today</h1>
<p class="max-w-[46ch] text-sm leading-relaxed text-tsl04-mut text-pretty">Resize the viewport — under 768px this same nav element becomes a bottom tab bar with safe-area padding.</p>
</main>
</section>@import "tailwindcss";
@theme {
--color-tsl04-bg: #121519;
--color-tsl04-bg: oklch(0.13 0.015 250);
--color-tsl04-panel: #191d23;
--color-tsl04-panel: oklch(0.17 0.02 250);
--color-tsl04-ink: #e9ebef;
--color-tsl04-ink: oklch(0.93 0.005 250);
--color-tsl04-mut: #8f939c;
--color-tsl04-mut: oklch(0.62 0.012 250);
--color-tsl04-line: #383d45;
--color-tsl04-line: oklch(0.28 0.02 250);
--color-tsl04-accent: #57c9df;
--color-tsl04-accent: oklch(0.75 0.14 200);
--color-tsl04-tint: rgb(87 201 223 / 0.14);
--color-tsl04-tint: color-mix(in oklab, oklch(0.75 0.14 200) 14%, transparent);
--font-tsl04-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.tsl-04 {
width: 100%;
height: 100svh;
min-height: 100svh;
color-scheme: dark;
}@import "tailwindcss";
@theme {
--color-tsl04-bg: #121519;
--color-tsl04-bg: oklch(0.13 0.015 250);
--color-tsl04-panel: #191d23;
--color-tsl04-panel: oklch(0.17 0.02 250);
--color-tsl04-ink: #e9ebef;
--color-tsl04-ink: oklch(0.93 0.005 250);
--color-tsl04-mut: #8f939c;
--color-tsl04-mut: oklch(0.62 0.012 250);
--color-tsl04-line: #383d45;
--color-tsl04-line: oklch(0.28 0.02 250);
--color-tsl04-accent: #57c9df;
--color-tsl04-accent: oklch(0.75 0.14 200);
--color-tsl04-tint: rgb(87 201 223 / 0.14);
--color-tsl04-tint: color-mix(in oklab, oklch(0.75 0.14 200) 14%, transparent);
--font-tsl04-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.tsl-04 {
width: 100%;
height: 100svh;
min-height: 100svh;
color-scheme: dark;
}Setup — do this once per project
Skip this if your project already runs Tailwind v4.
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<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.
npm install tailwindcss @tailwindcss/vite
npm install tailwindcss @tailwindcss/vite// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})Any v4 setup works — Vite, PostCSS or the CLI. Nothing in this demo is bundler-specific.
@import "tailwindcss";
@theme {
--color-tsl04-bg: #121519;
--color-tsl04-bg: oklch(0.13 0.015 250);
--color-tsl04-panel: #191d23;
--color-tsl04-panel: oklch(0.17 0.02 250);
--color-tsl04-ink: #e9ebef;
--color-tsl04-ink: oklch(0.93 0.005 250);
--color-tsl04-mut: #8f939c;
--color-tsl04-mut: oklch(0.62 0.012 250);
--color-tsl04-line: #383d45;
--color-tsl04-line: oklch(0.28 0.02 250);
--color-tsl04-accent: #57c9df;
--color-tsl04-accent: oklch(0.75 0.14 200);
--color-tsl04-tint: rgb(87 201 223 / 0.14);
--color-tsl04-tint: color-mix(in oklab, oklch(0.75 0.14 200) 14%, transparent);
--font-tsl04-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.tsl-04 {
width: 100%;
height: 100svh;
min-height: 100svh;
color-scheme: dark;
}@import "tailwindcss";
@theme {
--color-tsl04-bg: #121519;
--color-tsl04-bg: oklch(0.13 0.015 250);
--color-tsl04-panel: #191d23;
--color-tsl04-panel: oklch(0.17 0.02 250);
--color-tsl04-ink: #e9ebef;
--color-tsl04-ink: oklch(0.93 0.005 250);
--color-tsl04-mut: #8f939c;
--color-tsl04-mut: oklch(0.62 0.012 250);
--color-tsl04-line: #383d45;
--color-tsl04-line: oklch(0.28 0.02 250);
--color-tsl04-accent: #57c9df;
--color-tsl04-accent: oklch(0.75 0.14 200);
--color-tsl04-tint: rgb(87 201 223 / 0.14);
--color-tsl04-tint: color-mix(in oklab, oklch(0.75 0.14 200) 14%, transparent);
--font-tsl04-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.tsl-04 {
width: 100%;
height: 100svh;
min-height: 100svh;
color-scheme: dark;
}<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">
<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.
grid-template-areashas no utility form, and the port is better for it:order-2 md:order-1says the same thing in two classes and keeps the source order (main first) that a screen reader should meet.- UI change from the CSS original: mobile tabs went from
min-height:48pxtomin-h-12with the label inside the target, desktop rows tomin-h-11, and the main pane gainedscrollbar-gutter:stableso the bottom bar does not shift when content overflows.
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 Responsive Sidebar to Bottom Navigation Switch Layout
Source: https://codefronts.com/layouts/tailwind-css-sidebar-layouts/tailwind-responsive-sidebar-bottom-navigation-switch/
One <nav>, two personalities: under 768px an iOS-style bottom tab bar with safe-area padding, at and above it the same markup stands up into a left sidebar. No duplicate nav trees, no resize listeners — the breakpoint prefix does all of it.
## HTML
```html
<section class="tsl-04 w-full h-svh min-h-svh grid grid-rows-[1fr_auto] md:grid-cols-[248px_1fr] md:grid-rows-1 font-tsl04-sans bg-tsl04-bg text-tsl04-ink">
<nav aria-label="Primary" class="order-2 md:order-1 flex flex-row md:flex-col md:gap-0.5 md:justify-start bg-tsl04-panel border-t md:border-t-0 md:border-r border-tsl04-line px-2 pt-1.5 pb-[calc(0.375rem+env(safe-area-inset-bottom,0px))] md:px-3 md:py-[18px]">
<a href="#" aria-current="page" class="flex-1 md:flex-none flex flex-col md:flex-row items-center justify-center md:justify-start gap-[3px] md:gap-3 min-h-12 md:min-h-11 md:px-3.5 rounded-xl text-[10.5px] md:text-sm font-medium text-tsl04-mut no-underline transition-colors duration-200 hover:text-tsl04-ink aria-[current=page]:text-tsl04-accent aria-[current=page]:bg-tsl04-tint focus-visible:outline-2 focus-visible:outline-tsl04-accent focus-visible:outline-offset-2"><svg viewBox="0 0 24 24" aria-hidden="true" class="w-[21px] h-[21px] shrink-0 fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]"><path d="M3 10.5 12 3l9 7.5M5.5 9.5V21h13V9.5"/></svg><span>Home</span></a>
<a href="#" class="flex-1 md:flex-none flex flex-col md:flex-row items-center justify-center md:justify-start gap-[3px] md:gap-3 min-h-12 md:min-h-11 md:px-3.5 rounded-xl text-[10.5px] md:text-sm font-medium text-tsl04-mut no-underline transition-colors duration-200 hover:text-tsl04-ink focus-visible:outline-2 focus-visible:outline-tsl04-accent focus-visible:outline-offset-2"><svg viewBox="0 0 24 24" aria-hidden="true" class="w-[21px] h-[21px] shrink-0 fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]"><circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/></svg><span>Search</span></a>
<a href="#" class="flex-1 md:flex-none flex flex-col md:flex-row items-center justify-center md:justify-start gap-[3px] md:gap-3 min-h-12 md:min-h-11 md:px-3.5 rounded-xl text-[10.5px] md:text-sm font-medium text-tsl04-mut no-underline transition-colors duration-200 hover:text-tsl04-ink focus-visible:outline-2 focus-visible:outline-tsl04-accent focus-visible:outline-offset-2"><svg viewBox="0 0 24 24" aria-hidden="true" class="w-[21px] h-[21px] shrink-0 fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]"><rect x="3" y="4" width="18" height="16" rx="3"/><path d="M3 9h18M8 2v4m8-4v4"/></svg><span>Bookings</span></a>
<a href="#" class="flex-1 md:flex-none flex flex-col md:flex-row items-center justify-center md:justify-start gap-[3px] md:gap-3 min-h-12 md:min-h-11 md:px-3.5 rounded-xl text-[10.5px] md:text-sm font-medium text-tsl04-mut no-underline transition-colors duration-200 hover:text-tsl04-ink focus-visible:outline-2 focus-visible:outline-tsl04-accent focus-visible:outline-offset-2"><svg viewBox="0 0 24 24" aria-hidden="true" class="w-[21px] h-[21px] shrink-0 fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]"><path d="M18 8a6 6 0 1 0-12 0c0 7-3 8-3 8h18s-3-1-3-8m-4.3 11a2 2 0 0 1-3.4 0"/></svg><span>Alerts</span></a>
<a href="#" class="flex-1 md:flex-none flex flex-col md:flex-row items-center justify-center md:justify-start gap-[3px] md:gap-3 min-h-12 md:min-h-11 md:px-3.5 rounded-xl text-[10.5px] md:text-sm font-medium text-tsl04-mut no-underline transition-colors duration-200 hover:text-tsl04-ink focus-visible:outline-2 focus-visible:outline-tsl04-accent focus-visible:outline-offset-2"><svg viewBox="0 0 24 24" aria-hidden="true" class="w-[21px] h-[21px] shrink-0 fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]"><circle cx="12" cy="8" r="4"/><path d="M4 21c0-3.9 3.6-6.5 8-6.5s8 2.6 8 6.5"/></svg><span>Profile</span></a>
</nav>
<main class="order-1 md:order-2 overflow-y-auto p-7 [scrollbar-gutter:stable]">
<h1 class="text-[22px] font-semibold leading-tight mb-2.5">Today</h1>
<p class="max-w-[46ch] text-sm leading-relaxed text-tsl04-mut text-pretty">Resize the viewport — under 768px this same nav element becomes a bottom tab bar with safe-area padding.</p>
</main>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tsl04-bg: #121519;
--color-tsl04-bg: oklch(0.13 0.015 250);
--color-tsl04-panel: #191d23;
--color-tsl04-panel: oklch(0.17 0.02 250);
--color-tsl04-ink: #e9ebef;
--color-tsl04-ink: oklch(0.93 0.005 250);
--color-tsl04-mut: #8f939c;
--color-tsl04-mut: oklch(0.62 0.012 250);
--color-tsl04-line: #383d45;
--color-tsl04-line: oklch(0.28 0.02 250);
--color-tsl04-accent: #57c9df;
--color-tsl04-accent: oklch(0.75 0.14 200);
--color-tsl04-tint: rgb(87 201 223 / 0.14);
--color-tsl04-tint: color-mix(in oklab, oklch(0.75 0.14 200) 14%, transparent);
--font-tsl04-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.tsl-04 {
width: 100%;
height: 100svh;
min-height: 100svh;
color-scheme: dark;
}
```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 Responsive Sidebar to Bottom Navigation Switch Layout
Source: https://codefronts.com/layouts/tailwind-css-sidebar-layouts/tailwind-responsive-sidebar-bottom-navigation-switch/
One <nav>, two personalities: under 768px an iOS-style bottom tab bar with safe-area padding, at and above it the same markup stands up into a left sidebar. No duplicate nav trees, no resize listeners — the breakpoint prefix does all of it.
## HTML
```html
<section class="tsl-04 w-full h-svh min-h-svh grid grid-rows-[1fr_auto] md:grid-cols-[248px_1fr] md:grid-rows-1 font-tsl04-sans bg-tsl04-bg text-tsl04-ink">
<nav aria-label="Primary" class="order-2 md:order-1 flex flex-row md:flex-col md:gap-0.5 md:justify-start bg-tsl04-panel border-t md:border-t-0 md:border-r border-tsl04-line px-2 pt-1.5 pb-[calc(0.375rem+env(safe-area-inset-bottom,0px))] md:px-3 md:py-[18px]">
<a href="#" aria-current="page" class="flex-1 md:flex-none flex flex-col md:flex-row items-center justify-center md:justify-start gap-[3px] md:gap-3 min-h-12 md:min-h-11 md:px-3.5 rounded-xl text-[10.5px] md:text-sm font-medium text-tsl04-mut no-underline transition-colors duration-200 hover:text-tsl04-ink aria-[current=page]:text-tsl04-accent aria-[current=page]:bg-tsl04-tint focus-visible:outline-2 focus-visible:outline-tsl04-accent focus-visible:outline-offset-2"><svg viewBox="0 0 24 24" aria-hidden="true" class="w-[21px] h-[21px] shrink-0 fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]"><path d="M3 10.5 12 3l9 7.5M5.5 9.5V21h13V9.5"/></svg><span>Home</span></a>
<a href="#" class="flex-1 md:flex-none flex flex-col md:flex-row items-center justify-center md:justify-start gap-[3px] md:gap-3 min-h-12 md:min-h-11 md:px-3.5 rounded-xl text-[10.5px] md:text-sm font-medium text-tsl04-mut no-underline transition-colors duration-200 hover:text-tsl04-ink focus-visible:outline-2 focus-visible:outline-tsl04-accent focus-visible:outline-offset-2"><svg viewBox="0 0 24 24" aria-hidden="true" class="w-[21px] h-[21px] shrink-0 fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]"><circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/></svg><span>Search</span></a>
<a href="#" class="flex-1 md:flex-none flex flex-col md:flex-row items-center justify-center md:justify-start gap-[3px] md:gap-3 min-h-12 md:min-h-11 md:px-3.5 rounded-xl text-[10.5px] md:text-sm font-medium text-tsl04-mut no-underline transition-colors duration-200 hover:text-tsl04-ink focus-visible:outline-2 focus-visible:outline-tsl04-accent focus-visible:outline-offset-2"><svg viewBox="0 0 24 24" aria-hidden="true" class="w-[21px] h-[21px] shrink-0 fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]"><rect x="3" y="4" width="18" height="16" rx="3"/><path d="M3 9h18M8 2v4m8-4v4"/></svg><span>Bookings</span></a>
<a href="#" class="flex-1 md:flex-none flex flex-col md:flex-row items-center justify-center md:justify-start gap-[3px] md:gap-3 min-h-12 md:min-h-11 md:px-3.5 rounded-xl text-[10.5px] md:text-sm font-medium text-tsl04-mut no-underline transition-colors duration-200 hover:text-tsl04-ink focus-visible:outline-2 focus-visible:outline-tsl04-accent focus-visible:outline-offset-2"><svg viewBox="0 0 24 24" aria-hidden="true" class="w-[21px] h-[21px] shrink-0 fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]"><path d="M18 8a6 6 0 1 0-12 0c0 7-3 8-3 8h18s-3-1-3-8m-4.3 11a2 2 0 0 1-3.4 0"/></svg><span>Alerts</span></a>
<a href="#" class="flex-1 md:flex-none flex flex-col md:flex-row items-center justify-center md:justify-start gap-[3px] md:gap-3 min-h-12 md:min-h-11 md:px-3.5 rounded-xl text-[10.5px] md:text-sm font-medium text-tsl04-mut no-underline transition-colors duration-200 hover:text-tsl04-ink focus-visible:outline-2 focus-visible:outline-tsl04-accent focus-visible:outline-offset-2"><svg viewBox="0 0 24 24" aria-hidden="true" class="w-[21px] h-[21px] shrink-0 fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]"><circle cx="12" cy="8" r="4"/><path d="M4 21c0-3.9 3.6-6.5 8-6.5s8 2.6 8 6.5"/></svg><span>Profile</span></a>
</nav>
<main class="order-1 md:order-2 overflow-y-auto p-7 [scrollbar-gutter:stable]">
<h1 class="text-[22px] font-semibold leading-tight mb-2.5">Today</h1>
<p class="max-w-[46ch] text-sm leading-relaxed text-tsl04-mut text-pretty">Resize the viewport — under 768px this same nav element becomes a bottom tab bar with safe-area padding.</p>
</main>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tsl04-bg: #121519;
--color-tsl04-bg: oklch(0.13 0.015 250);
--color-tsl04-panel: #191d23;
--color-tsl04-panel: oklch(0.17 0.02 250);
--color-tsl04-ink: #e9ebef;
--color-tsl04-ink: oklch(0.93 0.005 250);
--color-tsl04-mut: #8f939c;
--color-tsl04-mut: oklch(0.62 0.012 250);
--color-tsl04-line: #383d45;
--color-tsl04-line: oklch(0.28 0.02 250);
--color-tsl04-accent: #57c9df;
--color-tsl04-accent: oklch(0.75 0.14 200);
--color-tsl04-tint: rgb(87 201 223 / 0.14);
--color-tsl04-tint: color-mix(in oklab, oklch(0.75 0.14 200) 14%, transparent);
--font-tsl04-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.tsl-04 {
width: 100%;
height: 100svh;
min-height: 100svh;
color-scheme: dark;
}
```How this works
Named grid areas become order plus a column template. Tailwind has no utility for grid-template-areas, and here it does not need one: the mobile shell is two rows with the nav second (order-2), the desktop shell is two columns with the nav first (md:order-1). One DOM node either way, so focus order, ARIA and state never fork between breakpoints — which was the whole point of the CSS original.
The tab is the responsive unit. flex-1 flex-col gap-[3px] text-[10.5px] md:flex-none md:flex-row md:gap-3 md:text-sm md:justify-start: every difference between a tab bar item and a sidebar row, stated on the element, readable in one pass. The mobile bar keeps its native-app details — min-h-12 targets including the label, and the safe-area padding that clears the iPhone home indicator.
Make it yours
- Tab count: four or five maximum on mobile; overflow the rest into a 'More' tab.
- Swap
md:for a container variant (@min-3xl/shell:) if the shell lives in a resizable pane — see demo 29. - Add a footer profile block inside the same nav for desktop only with
hidden md:flex.
Gotchas — read before shipping
env(safe-area-inset-bottom)only resolves when the page shipsviewport-fit=cover; without it iPhones overlap the home indicator.- Hit targets must be ≥44px INCLUDING the label — icon-only 32px tabs fail WCAG 2.5.8.
position:fixedbreaks inside transformed ancestors; this bar is a grid row, not fixed, precisely so it works inside any container.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
The layout itself needs nothing newer than 2018 grid; the floor is Tailwind v4's generated sheet. The most portable demo in the collection.