Single-file CSS Sidebar layout

platinum.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.

Downloads

Direct downloads for the stylesheet and optional script.

Getting Started

One stylesheet, one optional script, no build step.

Link the stylesheet. This is the whole visual baseline:

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

The optional script adds behavior, not the core look:

<script src="platinum.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>Platinum.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 platinum.js loaded, header navigation collapses behind a hamburger toggle and sidebar section navigation collapses behind a Sections toggle. Mobile menus close on link selection, Escape, or outside tap.

Theming

Override CSS custom properties. No config file, no build step.

Prefer token overrides over component rewrites. Useful groups include typography, color, spacing, radii, and button chrome.

:root {
  --primary: #215a9c;
  --primary-hover: #194779;
  --primary-active: #102f55;
  --surface: #ffffff;
  --background: #f7f7f7;
  --button-face: #d9d9d9;
  --radius-3: 5px;
}

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-pressed="mixed">Mixed</button>
<button aria-haspopup="menu" aria-expanded="true" aria-controls="menu">Expanded</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 aria-required="true">
<input readonly aria-readonly="true">
<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 platinum.js, which upgrades the text into labels. Opt out per form with data-classic-no-autolabels.

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

Components

Classes and matching ARIA roles 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>

ARIA State Defaults

Submitted Partial review Comfortable density Announcements
Changes saved for this lesson.
  • Lecture notes
  • Lab rubric
  • Quiz draft
Assignment Status
Unit 1 Ready
Unit 2 Review
<span role="checkbox" aria-checked="mixed" tabindex="0">Partial review</span>
<span role="switch" aria-checked="true" tabindex="0">Announcements</span>
<ul role="listbox" aria-multiselectable="true">
  <li role="option" aria-selected="true" tabindex="0">Lecture notes</li>
</ul>
<th aria-sort="ascending">Assignment</th>
<div role="progressbar" aria-valuenow="72" style="--value: 72%"></div>

Tabs

Overview

Use aria-current="page" for a static link-style tab, or aria-selected="true" on scripted ARIA tabs.

Static Keyboard friendly Panel content
<div class="tab-set">
  <nav class="tabs" aria-label="Project sections">
    <a id="project-tab-overview" class="tab" href="#component-tabs" aria-current="page">Overview</a>
    <a class="tab" href="#component-tabs">Activity</a>
    <a class="tab" href="#component-tabs">Settings</a>
  </nav>
  <section class="tab-panel" role="tabpanel" aria-labelledby="project-tab-overview">
    <h4>Overview</h4>
    <p>Current tab content goes here.</p>
  </section>
</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="#component-tabs" aria-current="page">2</a></li>
</ol>

Others

Layout Helpers

Three small composition classes for arranging content.

Class Use Example
grid Responsive card or content grid.
One Two Three
stack Vertical flow with consistent spacing.
First Second Third
cluster Horizontal wrapping group.
Alpha Beta Gamma

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.

platinum.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