Back to CSS Sidebar Layouts Persistent Collapsible Light JS
Share
.sb-prs {
  display: grid; grid-template-columns: 240px 1fr;
  min-height: 480px;
  font-family: 'Helvetica Neue', 'Inter', system-ui, sans-serif;
  background: #fafafa; color: #0a0a0a;
  border-radius: 0; overflow: hidden;
  transition: grid-template-columns 0.32s cubic-bezier(0.32, 0.72, 0, 1);
  border: 1px solid #0a0a0a;
}
.sb-prs[data-collapsed="true"] { grid-template-columns: 64px 1fr; }
.sb-prs-side {
  background: #fff;
  border-right: 1px solid #0a0a0a;
  padding: 18px 14px;
  display: flex; flex-direction: column; gap: 18px;
  overflow: hidden;
}
.sb-prs-toggle {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 12px; border: 1px solid #0a0a0a; border-radius: 0;
  background: #fafafa; color: #0a0a0a;
  font: inherit; font-size: 11px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
  cursor: pointer; transition: background 0.14s;
}
.sb-prs-toggle:hover { background: #0a0a0a; color: #fff; }
.sb-prs-toggle:hover .sb-prs-burger i { background: #fff; }
.sb-prs-burger { display: inline-flex; flex-direction: column; gap: 4px; }
.sb-prs-burger i { display: block; width: 14px; height: 1.5px; background: #0a0a0a; transition: transform 0.22s; }
.sb-prs[data-collapsed="true"] .sb-prs-burger i:nth-child(1) { transform: translateY(3px) rotate(45deg); }
.sb-prs[data-collapsed="true"] .sb-prs-burger i:nth-child(2) { transform: translateY(-2.5px) rotate(-45deg); }
.sb-prs-tlabel { font-size: 11px; font-weight: 700; letter-spacing: 0.1em; transition: opacity 0.18s; }
.sb-prs[data-collapsed="true"] .sb-prs-tlabel { opacity: 0; pointer-events: none; }
.sb-prs-brand { display: flex; align-items: center; gap: 12px; padding: 8px 6px; border-bottom: 1px solid #0a0a0a; padding-bottom: 18px; white-space: nowrap; }
.sb-prs-mark { width: 12px; height: 12px; border-radius: 50%; background: #e63946; flex-shrink: 0; }
.sb-prs-name { font-size: 26px; font-weight: 800; letter-spacing: -0.03em; color: #0a0a0a; line-height: 1; transition: opacity 0.18s; }
.sb-prs[data-collapsed="true"] .sb-prs-name { opacity: 0; pointer-events: none; }
.sb-prs-side ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.sb-prs-side a {
  position: relative;
  display: grid; grid-template-columns: 30px 1fr;
  align-items: center; gap: 14px;
  padding: 12px 6px;
  font-size: 14px; font-weight: 600; color: #0a0a0a; text-decoration: none;
  border-bottom: 1px solid rgba(10,10,10,0.12);
  transition: padding-left 0.16s, color 0.16s;
}
.sb-prs-side a em { font-style: normal; font-family: 'JetBrains Mono', monospace; font-size: 10px; color: rgba(10,10,10,0.5); letter-spacing: 0.06em; font-weight: 600; }
.sb-prs-side a:hover { padding-left: 12px; color: #e63946; }
.sb-prs-side a:hover em { color: #e63946; }
.sb-prs-side a[aria-current="page"] { padding-left: 12px; }
.sb-prs-side a[aria-current="page"]::before { content: ''; position: absolute; left: 0; top: 50%; transform: translateY(-50%); width: 6px; height: 6px; border-radius: 50%; background: #e63946; box-shadow: 0 0 0 0 rgba(230,57,70,0.5); animation: sb-prs-dot 2s ease-in-out infinite; }
@keyframes sb-prs-dot { 0%, 100% { box-shadow: 0 0 0 0 rgba(230,57,70,0.5); } 50% { box-shadow: 0 0 0 6px rgba(230,57,70,0); } }
@media (prefers-reduced-motion: reduce) { .sb-prs-side a[aria-current="page"]::before { animation: none; } }
.sb-prs-lbl { white-space: nowrap; transition: opacity 0.18s; }
.sb-prs[data-collapsed="true"] .sb-prs-lbl { opacity: 0; pointer-events: none; }
.sb-prs-foot { margin-top: auto; padding-top: 12px; border-top: 1px solid #0a0a0a; }
.sb-prs-foot-lbl { font-family: 'JetBrains Mono', monospace; font-size: 10px; color: rgba(10,10,10,0.5); letter-spacing: 0.16em; transition: opacity 0.18s; }
.sb-prs[data-collapsed="true"] .sb-prs-foot-lbl { opacity: 0; }
.sb-prs-main { padding: 36px 40px; overflow: hidden; display: flex; flex-direction: column; gap: 18px; background: #fafafa; }
.sb-prs-eye { font-family: 'JetBrains Mono', monospace; font-size: 11.5px; color: #e63946; letter-spacing: 0.04em; }
.sb-prs-main h2 { margin: 0; font-size: clamp(36px, 5.5vw, 64px); font-weight: 800; line-height: 0.92; color: #0a0a0a; letter-spacing: -0.04em; }
.sb-prs-main p { margin: 0; font-size: 14.5px; color: #3a3a3a; line-height: 1.65; max-width: 480px; font-weight: 500; }
.sb-prs-main code { font-family: 'JetBrains Mono', monospace; font-size: 12.5px; color: #0a0a0a; background: rgba(10,10,10,0.06); padding: 2px 6px; border-radius: 0; font-weight: 600; border-bottom: 1px solid #e63946; }
@media (prefers-reduced-motion: reduce) { .sb-prs, .sb-prs-tlabel, .sb-prs-lbl, .sb-prs-name, .sb-prs-foot-lbl, .sb-prs-burger i { transition: none; } }
<div class="sb-prs" data-collapsed="false">
  <aside class="sb-prs-side" aria-label="Primary">
    <button class="sb-prs-toggle" type="button" aria-expanded="true" aria-controls="sb-prs-nav" aria-label="Collapse sidebar">
      <span class="sb-prs-burger" aria-hidden="true"><i></i><i></i></span>
      <span class="sb-prs-tlabel">Collapse</span>
    </button>
    <header class="sb-prs-brand">
      <span class="sb-prs-mark" aria-hidden="true"></span>
      <span class="sb-prs-name">Atlas</span>
    </header>
    <nav id="sb-prs-nav" aria-label="Primary">
      <ul>
        <li><a href="#!" aria-current="page"><em>01</em><span class="sb-prs-lbl">Index</span></a></li>
        <li><a href="#!"><em>02</em><span class="sb-prs-lbl">Projects</span></a></li>
        <li><a href="#!"><em>03</em><span class="sb-prs-lbl">Studio</span></a></li>
        <li><a href="#!"><em>04</em><span class="sb-prs-lbl">Press</span></a></li>
        <li><a href="#!"><em>05</em><span class="sb-prs-lbl">Contact</span></a></li>
      </ul>
    </nav>
    <footer class="sb-prs-foot">
      <span class="sb-prs-foot-lbl">© Atlas, MMXXVI</span>
    </footer>
  </aside>
  <main class="sb-prs-main">
    <span class="sb-prs-eye">— Persistent state · localStorage</span>
    <h2>Less,<br/>but better.</h2>
    <p>Click the toggle — the sidebar collapses to icons only. The state is written to <code>localStorage</code>, so a refresh keeps your choice. An inline IIFE applies the stored state before the page paints, preventing any flash.</p>
  </main>
</div>
/* Persistent collapsible sidebar — restores state on load + toggles on click. */
(function () {
  var KEY = 'sb-prs-collapsed';
  var wrap = document.querySelector('.sb-prs');
  if (!wrap) return;

  var stored = localStorage.getItem(KEY) === 'true';
  wrap.dataset.collapsed = stored ? 'true' : 'false';

  var btn = wrap.querySelector('.sb-prs-toggle');
  if (!btn) return;
  btn.setAttribute('aria-expanded', String(!stored));

  btn.addEventListener('click', function () {
    var nowCollapsed = wrap.dataset.collapsed !== 'true';
    wrap.dataset.collapsed = nowCollapsed ? 'true' : 'false';
    btn.setAttribute('aria-expanded', String(!nowCollapsed));
    btn.setAttribute('aria-label', nowCollapsed ? 'Expand sidebar' : 'Collapse sidebar');
    localStorage.setItem(KEY, String(nowCollapsed));
  });
})();
Live preview Edit any tab — preview updates live Ready