Downloads
Direct downloads for the stylesheet and optional script.
Single-file CSS Sidebar layout
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.
Direct downloads for the stylesheet and optional script.
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>
platinum.css: the library stylesheet.platinum.js: progressive-enhancement helpers and command palette behavior.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>
Apply one class to body.
layout-topbarlayout-sidebarlayout-scroll<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.
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;
}
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>
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: explicit label/control row.field-row: groups fields; behaves as contents so rows align with the form table.check: checkbox/radio label row.switch: custom switch row.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 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>
Classes and matching ARIA roles for app-oriented pieces. Variants use data-variant="info|success|warning|danger".
<div class="notice" data-variant="warning">Check the required fields.</div>
<span class="badge" data-variant="info">Info</span>
<span class="status" data-variant="warning">Attention</span>
<div class="stat">
<small>Open items</small>
<strong>24</strong>
<span class="status">Healthy</span>
</div>
| 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>
Use aria-current="page" for a static link-style tab, or aria-selected="true" on scripted ARIA tabs.
<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>
<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 container with titlebar, pane, and statusbar.
<div class="window">
<div class="window-titlebar">Untitled Document</div>
<div class="window-pane">...</div>
<div class="window-statusbar">Ready</div>
</div>
No classes needed.
<ol class="pagination">
<li><a href="#component-tabs" aria-current="page">2</a></li>
</ol>
avatar: circular-initials marker.dialog-preview: displays a dialog inline for demos.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
|
leadmutedcompacttext-centernowrapvisually-hiddenOptional markup repair. The CSS is useful without it.
ids.Sections toggle to sidebar navigation on narrow screens.data-classic-no-autolabels.<form data-classic-no-autolabels>
Name
<input name="name">
</form>
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>
data-jumpable: opt the element into the palette.data-jump-label: command text; falls back to aria-label, aria-labelledby, then id.data-jump-aliases: extra search terms.data-jump-description: right-side context text.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" });
}
});
platinum.js upgrades it.:focus-visible and are kept intact.aria-current, aria-invalid, aria-pressed, aria-busy, aria-expanded, aria-selected, aria-checked, aria-required, aria-readonly, and aria-sort.aria-current value for current links, tabs, pagination, and steps.aria-invalid="true" only after validation has failed, and pair it with useful help or error text in application markup.aria-pressed="true" only for toggle buttons.prefers-reduced-motion: reduce.