Single-file CSS Sidebar layout

classic.css

A single-file CSS library for semantic HTML with an old desktop feel: platinum surfaces, visible bevels, sunken inputs, and page layouts that work without a build step.

Getting Started

One stylesheet, two optional scripts, no build step.

Link the stylesheet. This is the whole visual baseline:

<link rel="stylesheet" href="classic.css">

Optional scripts add behavior, not the core look:

<script src="classic.enhance.js"></script>
<script src="classic.commands.js"></script>

Philosophy

Semantic HTML first, classes only for variants.

Start with semantic HTML. Reach for classes only when HTML does not already have the concept, or when choosing a visual variant.

<article>
  <header>
    <h2>Lesson Notes</h2>
  </header>
  <p>Classic CSS styles the document structure directly.</p>
</article>

Opt-in classes are for extras:

<div class="notice" data-variant="warning">Check the required fields.</div>
<button class="secondary">Save Draft</button>

Layouts

Apply one class to body.

layout-topbar
Top navigation with a centered main content area.
layout-sidebar
Top navigation, left sidebar, and main content area. This page uses it.
layout-scroll
Narrow vertical scrolling layout for mobile-style forms and focused flows.
<body class="layout-sidebar">
  <header>
    <nav aria-label="Primary">
      <a class="brand" href="/"><strong>My App</strong></a>
      <ul>
        <li><a href="/" aria-current="page">Home</a></li>
      </ul>
    </nav>
  </header>
  <aside class="sidebar">...</aside>
  <main>...</main>
  <footer>...</footer>
</body>

On narrow screens, topbar and sidebar layouts collapse into vertical flow. With classic.enhance.js loaded, header navigation collapses behind a hamburger toggle.

Buttons

Automatic on button, button-like inputs, [role="button"], and .button.

<button>Default</button>
<button class="secondary">Secondary</button>
<button class="outline">Outline</button>
<button class="contrast">Contrast</button>
<button class="ghost">Ghost</button>

<button aria-pressed="true">Pressed</button>
<button aria-busy="true">Busy</button>
<button disabled>Disabled</button>

Forms

Two-column table layout by default: labels right-aligned on the left, controls on the right.

<form>
  <label for="name">Name</label>
  <input id="name" name="name">

  <label class="check">
    <input type="checkbox"> Send release notes
  </label>

  <label class="switch">
    <input type="checkbox"> Use platinum controls
  </label>

  <div class="cluster">
    <button type="reset" class="secondary">Reset</button>
    <button type="submit">Save</button>
  </div>
</form>

Field helpers

Validation

Both ARIA and native HTML validity are styled. Native constraints (required, pattern) show the invalid border via :user-invalid once the user has interacted. Pair the border with a message so the state is never color-only:

<input aria-invalid="true" aria-describedby="handle-error">
<small id="handle-error" data-variant="danger">Lowercase letters and dashes only.</small>

<input aria-invalid="false">
<input type="email" required>

Bare text labels

Bare form text before a control is visually placed in the label column. For real click-to-focus behavior, use actual labels or load classic.enhance.js, which upgrades the text into labels. Opt out per form with data-classic-no-autolabels.

<form>
  Name
  <input name="name">
</form>

Components

Opt-in classes for app-oriented pieces. Variants use data-variant="info|success|warning|danger".

Notices

Neutral notice.
Informational notice.
The update completed.
Check the required fields.
Unable to save.
<div class="notice" data-variant="warning">Check the required fields.</div>

Badges and Status

Badge Info Success Ready Attention Fix
<span class="badge" data-variant="info">Info</span>
<span class="status" data-variant="warning">Attention</span>

Stats

Open items 24 Healthy
Review queue 7 Attention
<div class="stat">
  <small>Open items</small>
  <strong>24</strong>
  <span class="status">Healthy</span>
</div>

Toolbar and Menu

<div class="toolbar" role="toolbar" aria-label="Editor">
  <button aria-pressed="true">B</button>
</div>

<ul class="menu">
  <li><a href="#">Edit <span class="muted">Ctrl E</span></a></li>
</ul>

Window Chrome

Untitled Document

Window container with titlebar, pane, and statusbar.

Ready
<div class="window">
  <div class="window-titlebar">Untitled Document</div>
  <div class="window-pane">...</div>
  <div class="window-statusbar">Ready</div>
</div>

Disclosure

Native details and summary

No classes needed.

Pagination

<ol class="pagination">
  <li><a href="#" aria-current="page">2</a></li>
</ol>

Others

Layout Helpers

Small composition classes for arranging content.

grid / grid-tight
Responsive card grid; tight variant reduces the gap.
stack / stack-sm
Vertical flow with consistent spacing.
cluster
Horizontal wrapping group.
split
Two-sided row that wraps on small screens.
with-sidebar
Local two-column content/sidebar layout.
surface
Bordered raised panel.
A split inside a surface Live example

Text Utilities

lead
Larger introductory paragraph.
muted
Subdued text.
compact
Reduce bottom margin.
text-center
Center text.
nowrap
Prevent wrapping.
visually-hidden
Accessible hidden text.

classic.enhance.js

Optional markup repair. The CSS is useful without it.

<form data-classic-no-autolabels>
  Name
  <input name="name">
</form>

Command Palette

Try it now: press Ctrl + K or Cmd + K.

Type commands like nav, main, forms, search, footer, or top to jump to semantic landmarks. Mark extra destinations with data-jumpable:

<section
  id="billing"
  data-jumpable
  data-jump-label="Billing"
  data-jump-aliases="invoices payments"
  data-jump-description="Jump to billing section">
  ...
</section>

Register custom commands in script:

window.ClassicCommands.register({
  name: "billing",
  aliases: ["invoice", "payments"],
  description: "Jump to billing section",
  run() {
    document.querySelector("#billing")?.scrollIntoView({ behavior: "smooth" });
  }
});

Accessibility