Skeletonic Stylus

Every example below is rendered with the very stylesheet being demoed. No preprocessor. No JavaScript. No extra dependencies. Copy any snippet into a fresh HTML file and it works.


Inputs

Interactive elements the visitor clicks, taps, or focuses to drive the page.

Buttons

A semantic, themable action trigger. Solid and outline variants ship six brand colours each.

<a href="#" class="button primary">Primary</a>
<a href="#" class="button secondary">Secondary</a>
<a href="#" class="button tertiary">Tertiary</a>

Primary Secondary Tertiary

Outline variants:

<a href="#" class="button primary-outline">Outline</a>
<a href="#" class="button secondary-outline">Outline</a>

Outline Outline

Accessibility note. Every .button variant ships with a :focus-visible ring and a 24×24 px minimum hit area (WCAG 2.2 SC 2.5.8). Use <button type="button"> for in-page actions and <a href> only for navigation.

Button shapes

The .btn family is a parallel shape system on top of the .button colour family. Use it when you want a square, round, or oval icon button instead of the default pill.

<button type="button" class="btn">Square</button>
<button type="button" class="btn btn-round">●</button>
<button type="button" class="btn btn-oval">Oval</button>
<button type="button" class="btn btn-outline">Outline</button>

When to use. .btn-round makes a 60×60 px circular button — ideal for a single-character icon (close, like, share). .btn-oval is a softer pill for short verbs ("Buy", "Send"). .btn-outline reads as a secondary action — pair it with a solid .btn for the primary call to action.

Button groups

Use .button-group to render a set of related buttons as a single visual unit. .block makes a single button stretch full-width — handy for forms on narrow viewports.

<div class="button-group">
  <a href="#" class="button primary">Save</a>
  <a href="#" class="button secondary">Save as draft</a>
  <a href="#" class="button warning">Discard</a>
</div>

<a href="#" class="button primary block">Full-width call to action</a>

Full-width call to action

Accessibility note. Wrap a button group in role="group" with an aria-label describing what the buttons do together (e.g. "Document actions"). Without that, assistive tech announces each button as if it were unrelated.

Badges

A compact label for status, count, or category. Text size is fixed so badges align with surrounding text.

<span class="badge">Default</span>
<span class="badge primary">Primary</span>
<span class="badge success">Success</span>
<span class="badge warning">Warning</span>
<span class="badge danger">Danger</span>

Default Primary Success Warning Danger

Accessibility note. Badges are decorative by default. When the badge carries the only signal (e.g. an unread count), wrap it in a visually-hidden helper: <span class="visually-hidden">3 unread messages</span>.


Feedback

Surfaces that tell the visitor something happened — or is about to happen.

Alerts

Status messages with semantic intent. In v2.0.0 every variant is explicitly namespaced under .alert-{primary,secondary,info,success,warning,error} so the variant class cannot collide with state classes elsewhere on the page.

<div class="alert alert-primary" role="alert">
  <strong>Heads up.</strong> This is a primary alert.
</div>
<div class="alert alert-success" role="status">
  <strong>Saved.</strong> Your changes are persisted.
</div>
<div class="alert alert-warning" role="alert">
  <strong>Careful.</strong> This action affects shared state.
</div>
<div class="alert alert-error" role="alert">
  <strong>Error.</strong> Could not save the form.
</div>
Saved. Your changes are persisted.

Accessibility note. Use role="alert" for messages that demand immediate attention (errors, warnings) and role="status" for non-urgent confirmations. Both expose the message to assistive tech the moment it appears.

Code blocks

<code>, <kbd>, <pre>, and <samp> get monospace styling and a thick inline-start accent stripe (which flips automatically under <html dir="rtl"> thanks to logical properties). Status modifiers match the alert palette so callouts and code samples can speak the same colour language.

<code class="primary">npm install @sebastienrousseau/skeletonic-stylus</code>
<code class="success">2026-04-30 — build passed</code>
<code class="warning">deprecated since v1.1.0 — removed in v2.0</code>
<code class="error">CVE-2023-44270 patched via overrides</code>
<code class="info">use `--gr-h1` to override the heading scale</code>

npm install @sebastienrousseau/skeletonic-stylus

2026-04-30 — build passed

deprecated since v1.1.0 — removed in v2.0

CVE-2023-44270 patched via overrides

use --gr-h1 to override the heading scale

To render a keyboard shortcut, use <kbd>:

Press <kbd>Ctrl</kbd>+<kbd>K</kbd> to focus the search.

Press Ctrl+K to focus the search.

RTL note. Code blocks intentionally retain direction: ltr even on <html dir="rtl"> pages — code is conventionally left-to-right. What flips is the inline-start accent stripe, so the visual anchor stays on the reading-start edge of the block.


Surface

Containers that frame and group related content.

Cards

A bordered, padded container for a single coherent unit. Pair with the flex-N grid for responsive card walls.

<section class="row">
  <article class="card flex-1">
    <div class="card-content">
      <h3 class="card-title">Lightweight</h3>
      <p>39.9 KB minified, 7.5 KB gzipped.</p>
    </div>
  </article>
  <article class="card flex-1">
    <div class="card-content">
      <h3 class="card-title">Accessible</h3>
      <p>WCAG 2.2 conformant out of the box.</p>
    </div>
  </article>
  <article class="card flex-1">
    <div class="card-content">
      <h3 class="card-title">Modular</h3>
      <p>Cascade-layered for easy overrides.</p>
    </div>
  </article>
</section>

Lightweight

39.9 KB minified, 7.5 KB gzipped.

Accessible

WCAG 2.2 conformant out of the box.

Modular

Cascade-layered for easy overrides.

Accessibility note. Wrap each card in a semantic landmark (<article>, <section>) and start its content with a heading (<h3>). Screen-reader users can then traverse the card list as first-class navigable regions.

Tables

Plain <table> elements get bordered cells and a contrast-flipped <thead> automatically — no class soup needed.

<table>
  <thead>
    <tr><th>Framework</th><th>Brotli</th><th>JS-free</th></tr>
  </thead>
  <tbody>
    <tr><td>Skeletonic</td><td>6.7 KB</td><td>Yes</td></tr>
    <tr><td>Pico CSS</td><td>10.1 KB</td><td>Yes</td></tr>
    <tr><td>Bootstrap</td><td>23.0 KB</td><td>No (Popper)</td></tr>
  </tbody>
</table>
Data table table
FrameworkBrotliJS-free
Skeletonic6.7 KBYes
Pico CSS10.1 KBYes
Bootstrap23.0 KBNo (Popper)

Accessibility note. Always use <thead> and <th> for header cells (not <td> styled to look like a header) — assistive tech announces the column header before each cell so users can navigate the data grid.


Forms

Inputs, labels, and groupings for collecting user data.

Form fields

Labels, text inputs, textareas, fieldsets and legends — all sized consistently with the rest of the design system.

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

  <label for="email">Email</label>
  <input id="email" type="email" required class="input-primary">

  <label for="msg">Message</label>
  <textarea id="msg" rows="4"></textarea>

  <button type="submit" class="button primary">Send</button>
</form>

Accessibility note. Every input must have a programmatically associated <label for="…">. Group related controls inside a <fieldset> with a <legend>. Mark required fields with required (and a visible asterisk in the label text).

Status inputs

Tint a single input to communicate validation state without rebuilding the form. The status modifier classes pair with any <input type=""> that already has element styling.

<input type="text"  class="input-primary"   placeholder="Primary">
<input type="text"  class="input-success"   placeholder="Saved">
<input type="text"  class="input-warning"   placeholder="Check this">
<input type="email" class="input-error"     value="not-an-email">
<input type="text"  class="input-info"      placeholder="FYI">

Accessibility note. Colour alone never carries meaning. Pair .input-error with aria-invalid="true", an inline message linked via aria-describedby, and a visible icon or text label.

Fieldsets

<fieldset> groups related controls; <legend> names the group. Both inherit Skeletonic's spacing and border tokens automatically.

<fieldset>
  <legend>Notification preferences</legend>
  <p>
    <input id="email-pref" type="checkbox" checked>
    <label for="email-pref">Email digest</label>
  </p>
  <p>
    <input id="sms-pref" type="checkbox">
    <label for="sms-pref">SMS alerts</label>
  </p>
</fieldset>
Notification preferences


Typography

Plain HTML, lifted to typographic standard via the cascade.

Lists

Three bullet styles ship as modifiers on <ul>square, circle, disc. Plus the default unstyled list-reset.

<ul class="square">
  <li>Square bullets</li>
  <li>Square bullets</li>
</ul>

<ul class="circle">
  <li>Circle bullets</li>
  <li>Circle bullets</li>
</ul>

<ul class="disc">
  <li>Disc bullets</li>
  <li>Disc bullets</li>
</ul>
  • Square bullets
  • Square bullets
  • Circle bullets
  • Circle bullets
  • Disc bullets
  • Disc bullets

Dividers

Ten visual variants of <hr> — solid, dashed, dotted, doubled, rounded, blurred, small, vertical, plus a centred-icon and a centred text-on-rule used throughout this site for the section separators above.

<hr class="hr-solid">
<hr class="hr-dashed">
<hr class="hr-dotted">
<hr class="hr-doubled">
<hr class="hr-rounded">
<hr class="hr-blurred">
<hr class="hr-small">
<hr class="hr-icon">
<hr class="hr-text" data-content="Section title">
<hr class="hr-vertical">

Solid


Dashed


Dotted


Doubled


Rounded


Blurred


Small (centred)


Centred icon


Centred text


Note. <hr> is a semantic, paragraph-level thematic break. Don't use it for purely decorative spacing — use a <div> or CSS margin instead. Assistive tech announces every <hr> as a section change.

Twelve named hover-effect classes for inline links, ranging from classic underline reveals to bracket animations. They're independent of colour — pair with any text colour.

<a href="#" class="link-1">Underline left → right</a>
<a href="#" class="link-2">Underline right → left</a>
<a href="#" class="link-3">Underline grow from centre</a>
<a href="#" class="link-4">Underline shrink to centre</a>
<a href="#" class="link-5">Top + bottom, left → right</a>
<a href="#" class="link-6">Top + bottom, right → left</a>
<a href="#" class="link-7">Top + bottom, grow from centre</a>
<a href="#" class="link-8">Top + bottom, opposite start</a>
<a href="#" class="link-9">Underline going up</a>
<a href="#" class="link-10">Underline going down</a>
<a href="#" class="link-11">Expanding brackets</a>
<a href="#" class="link-12">Shrinking brackets</a>

link-1 · link-2 · link-3 · link-4 · link-5 · link-6 · link-7 · link-8 · link-9 · link-10 · link-11 · link-12

RTL note. Effects 1, 2, 5, 6, 8, and 11 have explicit [dir="rtl"] overrides so "left → right" reveals semantically become "start → end" reveals on RTL pages — meaning the underline grows from the same side the reader is already starting from.


Layout

Structural primitives — grid, container, header — that frame the whole page.

Grid

Native CSS Grid since v2.0. .grid sets display: grid and a gap; .grid-cols-N (1–12) generates the column tracks; .col-span-N (1–12) lets a single child span multiple cells.

<div class="grid grid-cols-2">
  <div>Half</div>
  <div>Half</div>
</div>

<div class="grid grid-cols-3">
  <div>Third</div>
  <div>Third</div>
  <div>Third</div>
</div>

<div class="grid grid-cols-3">
  <div class="col-span-2">Two thirds</div>
  <div>Third</div>
</div>
Half
Half
Third
Third
Third
Two thirds (col-span-2)
Third

Accessibility note. Visual order should match DOM order. Avoid reordering with grid-auto-flow: dense or explicit grid-row / grid-column shuffling — screen readers and keyboard users follow the source, not the paint.

Migrating from v1.x. The .flex-N / .row flexbox grid is gone. Replace <div class="row"><div class="flex-6">… with <div class="grid grid-cols-2"><div>…. The new system is shorter, uses native CSS Grid, and avoids margin-collapse quirks.

A complete CSS-only header with a responsive hamburger toggle. Zero JavaScript. Copy the snippet below into a fresh HTML page that already loads skeletonic.min.css and the menu collapses, expands, and traps focus correctly on its own.

<style>
  /* Demo uses light-dark() so it adapts to colour-scheme. */
  .sk-header{position:relative;display:flex;align-items:center;gap:1rem;padding:.75rem 1rem;border:1px solid var(--c-border);border-radius:.5rem;background:light-dark(#fff,#1a1a1a);color:light-dark(#1a1a1a,#f5f5f5);}
  .sk-header .sk-brand{font-weight:600;color:light-dark(#0a0a0a,#f5f5f5) !important;text-decoration:none;}
  .sk-header .sk-toggle{position:absolute;left:-9999px;}
  .sk-header .sk-burger{display:none;margin-left:auto;cursor:pointer;padding:.625rem .75rem;border:1px solid var(--c-border);border-radius:.375rem;font-size:1.125rem;line-height:1;min-width:2.75rem;min-height:2.75rem;color:inherit;background:transparent;}
  .sk-header .sk-burger:focus-within,.sk-header .sk-toggle:focus-visible+.sk-burger{outline:2px solid hsl(210,100%,42%);outline-offset:2px;}
  .sk-header .sk-menu{list-style:none;display:flex;gap:.25rem;margin:0 0 0 auto;padding:0;}
  .sk-header .sk-menu a{display:inline-flex;align-items:center;min-height:2.75rem;padding:.5rem .875rem;border-radius:.375rem;color:light-dark(#1a1a1a,#f5f5f5) !important;text-decoration:none;}
  .sk-header .sk-menu a:hover{background:light-dark(#f4f4f5,#262629);}
  @media (max-width:640px){
    .sk-header .sk-burger{display:inline-flex;align-items:center;justify-content:center;}
    .sk-header .sk-menu{display:none;flex-direction:column;gap:0;position:absolute;top:calc(100% + .25rem);left:0;right:0;background:light-dark(#fff,#1a1a1a);border:1px solid var(--c-border);border-radius:.5rem;padding:.375rem;box-shadow:0 4px 24px rgba(0,0,0,.18);z-index:10;}
    .sk-header .sk-menu a{padding:.75rem 1rem;border-bottom:1px solid var(--c-border);}
    .sk-header .sk-menu li:last-child a{border-bottom:0;}
    .sk-header .sk-toggle:checked ~ .sk-menu{display:flex;}
  }
</style>
<header class="sk-header">
  <a class="sk-brand" href="#">Brand</a>
  <input class="sk-toggle" id="sk-nav-toggle" type="checkbox" aria-label="Toggle navigation">
  <label class="sk-burger" for="sk-nav-toggle" aria-hidden="true">☰</label>
  <ul class="sk-menu">
    <li><a href="#">Home</a></li>
    <li><a href="#">Docs</a></li>
    <li><a href="#">Components</a></li>
    <li><a href="#">About</a></li>
  </ul>
</header>
Brand

Accessibility note. The hidden checkbox stays in the tab order so keyboard users can open the menu with Space or Enter. The <label> carries aria-hidden because the checkbox itself is the accessible name source. Resize the window below 640 px to see the burger toggle take over.

See full a11y notes → · Browse palettes → · Framework benchmarks →