30 CSS Sidebar Layouts28 / 30

Pure CSSMIT licensed

Three-Pane Productivity Workspace Shell Layout

The Slack/IDE archetype assembled from this collection's parts: a 68px icon rail (demo 14), a 264px list pane with sticky search (demo 21), and the workspace body — grid-template-columns: 72px 280px 1fr as one composed shell, with each pane owning its scroll and the whole thing collapsing progressively as space runs out.

Published Updated

Live Demo
Try it

The code

<section class="sbl-28">
  <aside class="sbl-28__dock" aria-label="Apps">
    <span class="sbl-28__logo" aria-hidden="true"></span>
    <a href="#" class="sbl-28__app" aria-current="page" aria-label="Messages"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M21 12a8 8 0 0 1-8 8H4l2.5-2.5A8 8 0 1 1 21 12z"/></svg></a>
    <a href="#" class="sbl-28__app" aria-label="Calendar"><svg viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="4" width="18" height="16" rx="3"/><path d="M3 9h18M8 2v4m8-4v4"/></svg></a>
    <a href="#" class="sbl-28__app" aria-label="Tasks"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="m4 12 4 4L20 4M4 19h16"/></svg></a>
    <span class="sbl-28__me" aria-hidden="true">JV</span>
  </aside>
  <aside class="sbl-28__list" aria-label="Conversations">
    <div class="sbl-28__search"><label><svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/></svg><input type="search" placeholder="Search…" aria-label="Search conversations"></label></div>
    <a href="#" class="sbl-28__convo" aria-current="true"><strong># frontend-guild</strong><em>Priya: subgrid landed in the design sys…</em></a>
    <a href="#" class="sbl-28__convo" data-unread><strong># releases</strong><em>Deploy 4.2.0 is live in production</em></a>
    <a href="#" class="sbl-28__convo"><strong>Marcus Webb</strong><em>You: sounds good, ship it</em></a>
    <a href="#" class="sbl-28__convo"><strong># design-crit</strong><em>Amara: posting Thursday's agenda</em></a>
  </aside>
  <main class="sbl-28__body">
    <header class="sbl-28__head"><strong># frontend-guild</strong><span>128 members</span></header>
    <div class="sbl-28__msgs">
      <p><b>Priya</b> subgrid landed in the design system — sidebar badges finally align. <time>09:12</time></p>
      <p><b>You</b> demo 06 energy. Shipping the workspace shell next. <time>09:15</time></p>
    </div>
    <div class="sbl-28__compose"><input type="text" placeholder="Message # frontend-guild" aria-label="Message input"><button type="button">Send</button></div>
  </main>
</section>
.sbl-28,
.sbl-28 *,
.sbl-28 *::before,
.sbl-28 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.sbl-28 {
  --dock: oklch(0.14 0.02 265);
  --panel: oklch(0.17 0.018 265);
  --ink: oklch(0.94 0.005 265);
  --mut: oklch(0.63 0.013 265);
  --line: oklch(0.28 0.018 265);
  --accent: oklch(0.7 0.15 265);
  display: grid;
  grid-template-columns: 68px 264px minmax(0,1fr);
  height: 100vh;
  height: 100dvh;
  font-family: system-ui,'Segoe UI',sans-serif;
  background: oklch(0.13 0.014 265);
  color: var(--ink);
}

.sbl-28__dock {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 14px 0;
  background: var(--dock);
  border-right: 1px solid var(--line);
}

.sbl-28__logo {
  width: 30px;
  height: 30px;
  border-radius: 9px;
  background: conic-gradient(from 30deg,var(--accent),oklch(0.62 0.16 320));
  margin-bottom: 10px;
}

.sbl-28__app {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: 12px;
  color: var(--mut);
  transition: color .2s ease,background-color .2s ease;
}

.sbl-28__app svg {
  width: 20px;
  height: 20px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.sbl-28__app:hover {
  color: var(--ink);
}

.sbl-28__app[aria-current="page"] {
  color: var(--accent);
  background: color-mix(in oklab,var(--accent) 15%,transparent);
}

.sbl-28__app:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.sbl-28__me {
  margin-top: auto;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  font: 700 11px/1 system-ui,sans-serif;
  color: #fff;
  background: linear-gradient(135deg,var(--accent),oklch(0.58 0.16 320));
}

.sbl-28__list {
  background: var(--panel);
  border-right: 1px solid var(--line);
  overflow-y: auto;
  overscroll-behavior: contain;
}

.sbl-28__search {
  position: sticky;
  top: 0;
  z-index: 2;
  padding: 12px;
  background: var(--panel);
  border-bottom: 1px solid var(--line);
}

.sbl-28__search label {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: 8px;
  height: 36px;
  padding: 0 11px;
  border-radius: 10px;
  border: 1px solid var(--line);
  background: oklch(0.14 0.015 265);
}

.sbl-28__search label:focus-within {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in oklab,var(--accent) 20%,transparent);
}

.sbl-28__search svg {
  width: 14px;
  height: 14px;
  fill: none;
  stroke: var(--mut);
  stroke-width: 2;
  stroke-linecap: round;
}

.sbl-28__search input {
  min-width: 0;
  border: none;
  background: none;
  color: var(--ink);
  font: 400 13px/1 system-ui,sans-serif;
  outline: none;
}

.sbl-28__convo {
  display: grid;
  gap: 3px;
  padding: 12px 14px;
  border-bottom: 1px solid color-mix(in oklab,var(--line) 55%,transparent);
  text-decoration: none;
  color: inherit;
  transition: background-color .15s ease;
}

.sbl-28__convo:hover {
  background: color-mix(in oklab,var(--ink) 4%,transparent);
}

.sbl-28__convo[aria-current="true"] {
  background: color-mix(in oklab,var(--accent) 11%,transparent);
  box-shadow: inset 2.5px 0 0 var(--accent);
}

.sbl-28__convo:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

.sbl-28__convo strong {
  font: 600 13px/1.2 system-ui,sans-serif;
}

.sbl-28__convo[data-unread] strong::after {
  content: "";
  display: inline-block;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent);
  margin-left: 7px;
}

.sbl-28__convo em {
  font: 400 12px/1.4 system-ui,sans-serif;
  font-style: normal;
  color: var(--mut);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.sbl-28__body {
  display: grid;
  grid-template-rows: auto 1fr auto;
  min-width: 0;
}

.sbl-28__head {
  display: flex;
  align-items: baseline;
  gap: 12px;
  padding: 15px 20px;
  border-bottom: 1px solid var(--line);
  font: 650 14.5px/1 system-ui,sans-serif;
}

.sbl-28__head span {
  font: 400 12px/1 system-ui,sans-serif;
  color: var(--mut);
}

.sbl-28__msgs {
  overflow-y: auto;
  padding: 18px 20px;
  display: grid;
  gap: 14px;
  align-content: start;
}

.sbl-28__msgs p {
  font: 400 13.5px/1.55 system-ui,sans-serif;
  color: var(--mut);
  max-width: 64ch;
}

.sbl-28__msgs b {
  color: var(--ink);
  font-weight: 650;
  margin-right: 6px;
}

.sbl-28__msgs time {
  margin-left: 8px;
  font-size: 11px;
  color: color-mix(in oklab,var(--mut) 65%,transparent);
}

.sbl-28__compose {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 10px;
  padding: 14px 20px;
  border-top: 1px solid var(--line);
}

.sbl-28__compose input {
  height: 42px;
  padding: 0 14px;
  border-radius: 11px;
  border: 1px solid var(--line);
  background: var(--panel);
  color: var(--ink);
  font: 400 13.5px/1 system-ui,sans-serif;
  outline: none;
}

.sbl-28__compose input:focus-visible {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in oklab,var(--accent) 20%,transparent);
}

.sbl-28__compose button {
  padding: 0 18px;
  border: none;
  border-radius: 11px;
  background: var(--accent);
  color: #fff;
  font: 600 13px/1 system-ui,sans-serif;
  cursor: pointer;
  transition: filter .2s ease;
}

.sbl-28__compose button:hover {
  filter: brightness(1.1);
}

.sbl-28__compose button:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

@media (max-width: 1000px) {
  .sbl-28 {
    grid-template-columns: 68px 220px minmax(0,1fr);
  }
}

@media (max-width: 820px) {
  .sbl-28 {
    grid-template-columns: 68px minmax(0,1fr);
  }

  .sbl-28__list {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .sbl-28__app,
    .sbl-28__convo,
    .sbl-28__compose button {
    transition: none;
  }
}
/* No JavaScript — three panes composed from demos 14, 21 and 08 on one grid: 68px 264px minmax(0,1fr), collapsing progressively by pane priority. */
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 Sidebar Layout 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: Three-Pane Productivity Workspace Shell Layout
Source: https://codefronts.com/layouts/css-sidebar-layouts/three-pane-workspace/

The Slack/IDE archetype assembled from this collection's parts: a 68px icon rail (demo 14), a 264px list pane with sticky search (demo 21), and the workspace body — grid-template-columns: 72px 280px 1fr as one composed shell, with each pane owning its scroll and the whole thing collapsing progressively as space runs out.
## HTML
```html
<section class="sbl-28">
  <aside class="sbl-28__dock" aria-label="Apps">
    <span class="sbl-28__logo" aria-hidden="true"></span>
    <a href="#" class="sbl-28__app" aria-current="page" aria-label="Messages"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M21 12a8 8 0 0 1-8 8H4l2.5-2.5A8 8 0 1 1 21 12z"/></svg></a>
    <a href="#" class="sbl-28__app" aria-label="Calendar"><svg viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="4" width="18" height="16" rx="3"/><path d="M3 9h18M8 2v4m8-4v4"/></svg></a>
    <a href="#" class="sbl-28__app" aria-label="Tasks"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="m4 12 4 4L20 4M4 19h16"/></svg></a>
    <span class="sbl-28__me" aria-hidden="true">JV</span>
  </aside>
  <aside class="sbl-28__list" aria-label="Conversations">
    <div class="sbl-28__search"><label><svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/></svg><input type="search" placeholder="Search…" aria-label="Search conversations"></label></div>
    <a href="#" class="sbl-28__convo" aria-current="true"><strong># frontend-guild</strong><em>Priya: subgrid landed in the design sys…</em></a>
    <a href="#" class="sbl-28__convo" data-unread><strong># releases</strong><em>Deploy 4.2.0 is live in production</em></a>
    <a href="#" class="sbl-28__convo"><strong>Marcus Webb</strong><em>You: sounds good, ship it</em></a>
    <a href="#" class="sbl-28__convo"><strong># design-crit</strong><em>Amara: posting Thursday's agenda</em></a>
  </aside>
  <main class="sbl-28__body">
    <header class="sbl-28__head"><strong># frontend-guild</strong><span>128 members</span></header>
    <div class="sbl-28__msgs">
      <p><b>Priya</b> subgrid landed in the design system — sidebar badges finally align. <time>09:12</time></p>
      <p><b>You</b> demo 06 energy. Shipping the workspace shell next. <time>09:15</time></p>
    </div>
    <div class="sbl-28__compose"><input type="text" placeholder="Message # frontend-guild" aria-label="Message input"><button type="button">Send</button></div>
  </main>
</section>
```
## CSS
```css
.sbl-28,
.sbl-28 *,
.sbl-28 *::before,
.sbl-28 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.sbl-28 {
  --dock: oklch(0.14 0.02 265);
  --panel: oklch(0.17 0.018 265);
  --ink: oklch(0.94 0.005 265);
  --mut: oklch(0.63 0.013 265);
  --line: oklch(0.28 0.018 265);
  --accent: oklch(0.7 0.15 265);
  display: grid;
  grid-template-columns: 68px 264px minmax(0,1fr);
  height: 100vh;
  height: 100dvh;
  font-family: system-ui,'Segoe UI',sans-serif;
  background: oklch(0.13 0.014 265);
  color: var(--ink);
}

.sbl-28__dock {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 14px 0;
  background: var(--dock);
  border-right: 1px solid var(--line);
}

.sbl-28__logo {
  width: 30px;
  height: 30px;
  border-radius: 9px;
  background: conic-gradient(from 30deg,var(--accent),oklch(0.62 0.16 320));
  margin-bottom: 10px;
}

.sbl-28__app {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: 12px;
  color: var(--mut);
  transition: color .2s ease,background-color .2s ease;
}

.sbl-28__app svg {
  width: 20px;
  height: 20px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.sbl-28__app:hover {
  color: var(--ink);
}

.sbl-28__app[aria-current="page"] {
  color: var(--accent);
  background: color-mix(in oklab,var(--accent) 15%,transparent);
}

.sbl-28__app:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.sbl-28__me {
  margin-top: auto;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  font: 700 11px/1 system-ui,sans-serif;
  color: #fff;
  background: linear-gradient(135deg,var(--accent),oklch(0.58 0.16 320));
}

.sbl-28__list {
  background: var(--panel);
  border-right: 1px solid var(--line);
  overflow-y: auto;
  overscroll-behavior: contain;
}

.sbl-28__search {
  position: sticky;
  top: 0;
  z-index: 2;
  padding: 12px;
  background: var(--panel);
  border-bottom: 1px solid var(--line);
}

.sbl-28__search label {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: 8px;
  height: 36px;
  padding: 0 11px;
  border-radius: 10px;
  border: 1px solid var(--line);
  background: oklch(0.14 0.015 265);
}

.sbl-28__search label:focus-within {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in oklab,var(--accent) 20%,transparent);
}

.sbl-28__search svg {
  width: 14px;
  height: 14px;
  fill: none;
  stroke: var(--mut);
  stroke-width: 2;
  stroke-linecap: round;
}

.sbl-28__search input {
  min-width: 0;
  border: none;
  background: none;
  color: var(--ink);
  font: 400 13px/1 system-ui,sans-serif;
  outline: none;
}

.sbl-28__convo {
  display: grid;
  gap: 3px;
  padding: 12px 14px;
  border-bottom: 1px solid color-mix(in oklab,var(--line) 55%,transparent);
  text-decoration: none;
  color: inherit;
  transition: background-color .15s ease;
}

.sbl-28__convo:hover {
  background: color-mix(in oklab,var(--ink) 4%,transparent);
}

.sbl-28__convo[aria-current="true"] {
  background: color-mix(in oklab,var(--accent) 11%,transparent);
  box-shadow: inset 2.5px 0 0 var(--accent);
}

.sbl-28__convo:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

.sbl-28__convo strong {
  font: 600 13px/1.2 system-ui,sans-serif;
}

.sbl-28__convo[data-unread] strong::after {
  content: "";
  display: inline-block;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent);
  margin-left: 7px;
}

.sbl-28__convo em {
  font: 400 12px/1.4 system-ui,sans-serif;
  font-style: normal;
  color: var(--mut);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.sbl-28__body {
  display: grid;
  grid-template-rows: auto 1fr auto;
  min-width: 0;
}

.sbl-28__head {
  display: flex;
  align-items: baseline;
  gap: 12px;
  padding: 15px 20px;
  border-bottom: 1px solid var(--line);
  font: 650 14.5px/1 system-ui,sans-serif;
}

.sbl-28__head span {
  font: 400 12px/1 system-ui,sans-serif;
  color: var(--mut);
}

.sbl-28__msgs {
  overflow-y: auto;
  padding: 18px 20px;
  display: grid;
  gap: 14px;
  align-content: start;
}

.sbl-28__msgs p {
  font: 400 13.5px/1.55 system-ui,sans-serif;
  color: var(--mut);
  max-width: 64ch;
}

.sbl-28__msgs b {
  color: var(--ink);
  font-weight: 650;
  margin-right: 6px;
}

.sbl-28__msgs time {
  margin-left: 8px;
  font-size: 11px;
  color: color-mix(in oklab,var(--mut) 65%,transparent);
}

.sbl-28__compose {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 10px;
  padding: 14px 20px;
  border-top: 1px solid var(--line);
}

.sbl-28__compose input {
  height: 42px;
  padding: 0 14px;
  border-radius: 11px;
  border: 1px solid var(--line);
  background: var(--panel);
  color: var(--ink);
  font: 400 13.5px/1 system-ui,sans-serif;
  outline: none;
}

.sbl-28__compose input:focus-visible {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in oklab,var(--accent) 20%,transparent);
}

.sbl-28__compose button {
  padding: 0 18px;
  border: none;
  border-radius: 11px;
  background: var(--accent);
  color: #fff;
  font: 600 13px/1 system-ui,sans-serif;
  cursor: pointer;
  transition: filter .2s ease;
}

.sbl-28__compose button:hover {
  filter: brightness(1.1);
}

.sbl-28__compose button:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

@media (max-width: 1000px) {
  .sbl-28 {
    grid-template-columns: 68px 220px minmax(0,1fr);
  }
}

@media (max-width: 820px) {
  .sbl-28 {
    grid-template-columns: 68px minmax(0,1fr);
  }

  .sbl-28__list {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .sbl-28__app,
    .sbl-28__convo,
    .sbl-28__compose button {
    transition: none;
  }
}
```

## JavaScript
```js
/* No JavaScript — three panes composed from demos 14, 21 and 08 on one grid: 68px 264px minmax(0,1fr), collapsing progressively by pane priority. */
```

How this works

The value of this demo is compositional: grid-template-columns:68px 264px minmax(0,1fr) is the entire top-level decision, and then each column is a pattern you've already seen — proof that the collection's demos are LEGO, not exhibits. The rail reuses 44px icon targets and tooltips; the list pane reuses sticky search + aria-selected rows; the body pane is demo 08's independent scroller with minmax(0,…) guarding against wide-content blowout (demo 13's lesson).

The progressive collapse encodes pane priority: at 1000px the list pane drops to a narrower 220px; at 820px it hides entirely, leaving rail + body — the same hierarchy Slack ships. Media queries express the priority order top-down, which is far easier to reason about than per-pane visibility toggles. Cross-pane consistency comes from sharing ONE set of custom properties — a single --accent recolors the active states of all three panes at once.

Make it yours

  • Pane widths are tokens: 68/264 here; IDEs run 48/240, mail apps 68/320 — retune two numbers.
  • Add demo 27's resize to the middle pane for user-tunable list width.
  • The rail's bottom avatar + the list's sticky search both come straight from their source demos — steal individual organs, not the whole body.
  • Fourth pane (detail inspector): extend the template with a 280px end column and demo 11's panel.

Gotchas — read before shipping

  • Compose scroll containment carefully: each pane needs its own overscroll-behavior:contain or a fast list-scroll rubber-bands the whole shell.
  • Hidden panes must be display:none (not translated away) so their controls leave the tab order (demo 13's rule at shell scale).
  • Keep exactly ONE aria-current/aria-selected chain per pane — rail says which app section, list says which conversation; don't double-mark.

Browser support

ChromeSafariFirefoxEdge
117+16.2+113+117+

Floor set by oklch(), color-mix(), subgrid as this demo is written. The core technique itself goes back further — Chrome 57+.

Baseline grid throughout — the shell runs everywhere; only the oklch palette and color-mix tints ask for 2023 engines.

Techniques used in this demo

Search CodeFronts

Loading…