Getting Started

Add ShailStack with a single <link> — no npm, no bundler, no config.

CDN install

<!-- Roboto is optional; ShailStack falls back to system-ui --> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700;900&display=swap" rel="stylesheet"> <link rel="stylesheet" href="https://cdn.shailstack.com/shailstack.css">

React, Vue, Next.js, Vite

Add the same <link> to index.html (or your root layout), or download shailstack.css into your project and import it once:

// main.jsx / index.js import './styles/shailstack.css';

Your first component

<div class="ss-card ss-stack ss-text-center ss-p-6"> <span class="ss-section-label">ShailStack</span> <h2 class="ss-text-2xl ss-font-bold">Hello, World!</h2> <p class="ss-text-muted">Built with utility classes.</p> <div class="ss-cluster ss-justify-center"> <button class="ss-btn ss-btn-primary ss-btn-pill">Get started</button> </div> </div>

Output

Hello, World!

Built with utility classes.

Naming: every class starts with ss-, so ShailStack can sit next to Bootstrap, Tailwind or your own CSS without collisions.

What’s new in 3.2

New layout primitives and container queries, plus a round of accessibility, dark-mode and layout fixes. Every 3.1 class still works.

New

FeatureClasses
Smart Stack.ss-stack, .ss-push
Smart Cluster.ss-cluster, .ss-push
Smart Split.ss-split, .ss-split-2-1, .ss-split-1-2
Auto Grid.ss-auto-grid
Smart Sidebar.ss-with-sidebar, .ss-sidebar-end
Content width.ss-measure
Content grid & full-bleed.ss-content-grid, .ss-full-bleed
Scroller.ss-scroller
Container queries.ss-cq, .ss-cq-row, .ss-cq-grid, .ss-cq-text, .ss-cq-hide-sm
Modern utilities.ss-text-balance, .ss-text-pretty, .ss-line-clamp, .ss-accent, .ss-overflow-clip, .ss-overflow-x-clip, .ss-min-h-dvh
Accessibility.ss-skip-link, aria-invalid styling, --ss-primary-text token

Fixed

  • Dark mode: toasts, code blocks, tooltips, .ss-btn-dark and .ss-badge-dark no longer turn into white text on a light background. --ss-border and shadows now update, and color-scheme themes native scrollbars and form controls.
  • Contrast: .ss-text-muted, success/warning/info/danger buttons, solid badges, links, tabs and section labels now meet WCAG AA (4.5:1).
  • Keyboard & focus: dropdowns open with :focus-within; inputs keep a focus indicator in Windows High Contrast; accordion focus rings are no longer clipped.
  • Layout: .ss-grid-2…6 columns can’t be stretched by long words; .ss-grid-auto* no longer overflows narrow containers; the mobile navbar menu and bottom-sheet modal display correctly.
  • Sticky: html now uses scrollbar-gutter: stable instead of overflow-y: scroll, which could stop position: sticky working.
  • Other: hover utilities animate back out; Firefox range slider styling; RTL-aware table cells and dropdowns; .ss-sr-only uses clip-path.
Visual changes to check when upgrading: .ss-text-muted is now gray-500 and.ss-text-subtle gray-600 (both darker); links, .ss-text-primary and outline/ghost/link buttons use the darker brand shade for text; success, danger and info buttons use deeper shades; warning buttons use dark text. Fills such as .ss-btn-primary and .ss-bg-primary are unchanged.

Design Tokens

All values are CSS custom properties on :root. Override a token to re-theme the whole library.

Brand colour

:root { --ss-primary: #FF1654; /* fills: buttons, badges, borders */ --ss-primary-dark: #CC0F40; /* hover shade; text on --ss-primary-light */ --ss-primary-light: #ffe4ec; /* tints and backgrounds */ --ss-primary-rgb: 255, 22, 84; /* for rgba() focus rings and shadows */ --ss-primary-text: var(--ss-primary-dark); /* brand text on the page (#ff6b8f in dark mode) */ }

Spacing scale — --ss-space-*

TokenValue
--ss-space-14px
--ss-space-28px
--ss-space-312px
--ss-space-416px
--ss-space-520px
--ss-space-624px
--ss-space-832px
--ss-space-1040px
--ss-space-1248px
--ss-space-1664px
--ss-space-2496px

Radius, shadow & z-index

TokenValue
--ss-radius-sm / md / lg / xl4px / 8px / 12px / 16px
--ss-radius-2xl / 3xl / full24px / 32px / 9999px
--ss-shadow-xs … --ss-shadow-xlfive elevation levels (darker in dark mode)
--ss-z-dropdown / sticky / modal1000 / 1020 / 1050
--ss-z-toast / tooltip1080 / 1090

Layout properties (new)

The layout primitives read optional custom properties with sensible defaults — set them inline or in your CSS.

PropertyUsed by · default
--ss-gapstack 16px · cluster 12px · split / sidebar 24px · auto grid, scroller, cq 16px
--ss-min.ss-auto-grid · 16rem minimum column width
--ss-split-at.ss-split · 40rem — narrower containers stack
--ss-sidebar.ss-with-sidebar · 16rem sidebar width
--ss-content-min.ss-with-sidebar · 55% — content never gets narrower
--ss-measure.ss-measure · 65ch
--ss-content / --ss-gutter.ss-content-grid · 72ch / 16px
--ss-item.ss-scroller · 80% item width
--ss-lines.ss-line-clamp · 2 lines

Layout Primitives

Layouts that respond to their content and the space they’re given — no breakpoints, no wrapper soup.

Default spacing lives inside :where(), so it has zero specificity: .ss-gap-*,.ss-items-* and .ss-justify-* always override it. Use --ss-gap for any other value.

Smart Stack — .ss-stack

Vertical flow with even spacing. Put .ss-push on a child to send it (and everything after it) to the bottom — handy for card footers.

<div class="ss-stack" style="height: 220px"> <h3>Title</h3> <p>Body text</p> <button class="ss-btn ss-btn-primary ss-push">Always at the bottom</button> </div>
.ss-stack

Short body text.

.ss-stack .ss-gap-2

Gap utilities override the default.

A second paragraph.

Smart Cluster — .ss-cluster

Inline items that wrap onto new lines as needed — tags, button groups, toolbars. .ss-push pushes the rest to the end of the line.

<div class="ss-cluster"> <span class="ss-chip">Design</span> <span class="ss-chip">CSS</span> <button class="ss-btn ss-btn-primary ss-btn-sm ss-push">Follow</button> </div>

Drag the corner to resize

DesignCSSLayoutAccessibilityDark mode

Smart Split — .ss-split

Children sit side by side and all stack at once when the container is narrower than--ss-split-at (default 40rem) — never an awkward 2-and-1 wrap. .ss-split-2-1 and.ss-split-1-2 make one side twice as wide.

<div class="ss-split ss-split-2-1" style="--ss-split-at: 36rem"> <div>Main (2 parts)</div> <div>Aside (1 part)</div> </div>

Drag the corner — stacks below 36rem

Main — 2 parts
Aside — 1 part

Auto Grid — .ss-auto-grid

As many columns as fit, each at least --ss-min wide (default 16rem). Unlike.ss-grid-auto it uses auto-fit, so a few items stretch to fill the row, and it never overflows.

<div class="ss-auto-grid" style="--ss-min: 10rem"> <div>1</div> <div>2</div> <div>3</div> <div>4</div> <div>5</div> </div>

Drag the corner

1
2
3
4
5

Smart Sidebar — .ss-with-sidebar

The first child is the sidebar (--ss-sidebar, default 16rem); the second fills the rest. When the content would get narrower than --ss-content-min (55%), they stack. Add .ss-sidebar-end to put the sidebar second. For a sticky sidebar, add .ss-sticky .ss-top-0 .ss-self-start to it.

<div class="ss-with-sidebar"> <aside class="ss-sticky ss-top-0 ss-self-start">Filters</aside> <main>Results</main> </div>

Drag the corner

Sidebar
Main content fills the remaining space and wraps below the sidebar when it would get too narrow.

Scroller — .ss-scroller

A horizontal, snapping row for cards, logos or galleries. Items are --ss-item wide (default 80%).

<div class="ss-scroller" style="--ss-item: 14rem"> <div class="ss-card">…</div> <div class="ss-card">…</div> </div>

Scroll sideways

Card 1
Card 2
Card 3
Card 4
Card 5
Card 6

Content Width & Full-Bleed

Readable line lengths and edge-to-edge sections, without extra wrapper elements.

Content width — .ss-measure

Caps line length at --ss-measure (default 65ch), the comfortable range for reading. Combine with .ss-mx-auto to centre it.

This paragraph stops at about 65 characters per line, however wide the page gets. Long lines are tiring to read because the eye has to travel too far back to find the start of the next line.

Content grid — .ss-content-grid + .ss-full-bleed

Every child sits in a centred column (--ss-content, default 72ch) with a gutter on small screens (--ss-gutter, 16px). Children with .ss-full-bleed break out to the full width — ideal for articles with wide images or coloured bands.

<article class="ss-content-grid"> <h1>Article title</h1> <p>Text stays at a readable width…</p> <img class="ss-full-bleed" src="hero.jpg" alt=""> <p>…and continues here.</p> </article>

Text sits in the centred content column.

.ss-full-bleed spans edge to edge

…and the text continues in the column.

Outside a content grid, .ss-full-bleed uses width: 100vw. On desktops with visible scrollbars that is slightly wider than the page, so add .ss-overflow-x-clip to <body> or a wrapper. Inside .ss-content-grid there’s no such issue.

Container Queries

Components that respond to the width of their container instead of the viewport — the same card works in a sidebar and in the main column.

ClassCSS
.ss-cqcontainer-type: inline-size — mark the container
.ss-cq-rowcolumn → row when the container is ≥ 36rem
.ss-cq-grid1 column → 2 at ≥ 36rem → 3 at ≥ 56rem
.ss-cq-hide-smdisplay: none when the container is ≤ 30rem
.ss-cq-textfont-size: clamp(18px, 5cqi, 36px) — scales with the container
<div class="ss-cq"> <article class="ss-card ss-cq-row"> <img src="thumb.jpg" alt="" class="ss-rounded ss-cq-hide-sm" width="120"> <div> <h3 class="ss-cq-text">Card title</h3> <p>Becomes a row when this card’s container is wide enough.</p> </div> </article> </div>

Drag the corner — the layout follows the box, not the window

Card titleRow when wide, stacked when narrow; the image hides below 30rem.
.ss-cq-grid 1
.ss-cq-grid 2
.ss-cq-grid 3
A container can’t query itself: put .ss-cq on a parent of the element using.ss-cq-*. Browsers without container-query support (pre-2023) keep the stacked, single-column layout.

Grid

Fixed column grids from 1 to 6 columns, plus auto-fill grids. Combine with gap utilities.

ClassCSS
.ss-grid-1 … .ss-grid-6grid-template-columns: repeat(n, minmax(0, 1fr))
.ss-grid-auto-smrepeat(auto-fill, minmax(min(140px, 100%), 1fr))
.ss-grid-autorepeat(auto-fill, minmax(min(200px, 100%), 1fr))
.ss-grid-auto-lgrepeat(auto-fill, minmax(min(280px, 100%), 1fr))
.ss-col-span-2 / -3grid-column: span 2 / span 3
.ss-col-fullgrid-column: 1 / -1
.ss-place-centerplace-items: center

.ss-grid-3 .ss-gap-3

1
2
3
4
5
6

.ss-col-span-2 + .ss-col-full

1
2 (span 2)
Full width (.ss-col-full)
Need columns that adapt without breakpoints? Use .ss-auto-grid or .ss-cq-grid.

Flexbox

Direction, wrapping, alignment, ordering and gaps.

ClassCSS
.ss-flex-row / -col / -row-rev / -col-revflex-direction
.ss-flex-wrap / -nowrapflex-wrap
.ss-flex-1 / -auto / -noneflex: 1 1 0% / 1 1 auto / none
.ss-flex-grow / .ss-flex-shrink-0flex-grow: 1 / flex-shrink: 0
.ss-justify-start / end / center / between / around / evenlyjustify-content
.ss-items-start / end / center / baseline / stretchalign-items
.ss-self-start / end / center / autoalign-self
.ss-order-first / last / 1 / 2 / 3order

Justify content

ABC
ABC
ABC
ABC

Gap

ClassCSS
.ss-gap-0 / 1 / 2 / 3 / 4 / 5 / 6 / 8 / 10 / 12gap: 0 – 48px (4px scale)
.ss-gap-x-2 / 4 / 6column-gap: 8 / 16 / 24px
.ss-gap-y-2 / 4 / 6row-gap: 8 / 16 / 24px

Responsive Prefixes

Breakpoint classes apply only inside their own width range — they are ranges, not mobile-first “and up” rules.

PrefixApplies when the viewport is
ss-xs-*≤ 480px
ss-sm-*481px – 768px
ss-md-*769px – 1024px
ss-lg-*≥ 1025px
ClassCSS
.ss-xs-hidden / -block / -flex / -flex-col / -w-full / -text-center / -grid-1≤ 480px
.ss-sm-hidden / -block / -flex / -flex-col / -grid-1 / -grid-2 / -w-full / -text-center481–768px
.ss-md-hidden / -flex / -flex-row / -grid-2 / -grid-3 / -w-full / -w-1-2769–1024px
.ss-lg-hidden / -flex / -grid-3 / -grid-4 / -w-1-2 / -w-1-3≥ 1025px
<!-- 1 column on phones, 2 on small tablets, 3 on tablets, 4 on desktop --> <div class="ss-grid-4 ss-xs-grid-1 ss-sm-grid-2 ss-md-grid-3 ss-gap-4">…</div>
Responsive classes use !important so they beat the base class. For components that should react to their own space rather than the screen, prefer layout primitives or container queries.

Typography

Font size, weight, line height, alignment and text transform.

Font sizes — .ss-text-*

.ss-text-2xs — 10px

.ss-text-xs — 11px

.ss-text-sm — 12px

.ss-text-base — 14px

.ss-text-md — 16px

.ss-text-lg — 18px

.ss-text-xl — 20px

.ss-text-2xl — 24px

.ss-text-3xl — 30px

.ss-text-4xl — 36px

Also .ss-text-5xl (48px) and .ss-text-6xl (60px).

Fluid sizes — scale with the viewport

ClassCSS
.ss-text-fluid-smclamp(14px, 2vw, 16px)
.ss-text-fluid-mdclamp(16px, 2.5vw, 20px)
.ss-text-fluid-lgclamp(20px, 3vw, 28px)
.ss-text-fluid-xlclamp(28px, 5vw, 48px)
.ss-text-fluid-heroclamp(32px, 6vw, 64px)
.ss-cq-textclamp(18px, 5cqi, 36px) — scales with its container

Weight, family, alignment & more

ClassCSS
.ss-font-thin / light / normal / medium / semibold / bold / extrabold / blackfont-weight: 100 – 900
.ss-font-sans / mono / seriffont-family
.ss-text-left / center / right / justifytext-align
.ss-uppercase / lowercase / capitalize / normal-casetext-transform
.ss-underline / line-through / no-underlinetext-decoration
.ss-italic / not-italicfont-style
.ss-lh-none / tight / snug / normal / relaxed / looseline-height: 1 – 2
.ss-tracking-tight / normal / wide / wider / widestletter-spacing
.ss-truncatesingle line with an ellipsis
.ss-nowrap / .ss-break-word / .ss-break-allwrapping control
.ss-text-balance / .ss-text-prettytext-wrap: balance / pretty (see Modern Utilities)

Colors

Text, background and border colours built from the design tokens.

Text colours

.primary — Primary.success — Success.warning — Warning.danger — Danger.info — Info.muted — Muted.subtle — Subtle.body — Body.heading — Heading
ClassCSS
.ss-text-primaryvar(--ss-primary-text) — readable brand text, lighter in dark mode
.ss-text-primary-darkvar(--ss-primary-dark)
.ss-text-success / warning / danger / infothe -dark shade of each status colour
.ss-text-muted / subtle / body / headinggray-500 / 600 / 700 / 900
.ss-text-gray-400 … -900gray scale
.ss-text-white / black#fff / #000 (fixed)

Backgrounds

.primary.primary-light.success.success-light.danger-light.warning.info-light.gray-100.gray-800

Also .ss-bg-warning-light, .ss-bg-info, .ss-bg-white, .ss-bg-black,.ss-bg-transparent, .ss-bg-gray-50 / 200 / 900. Gray and white backgrounds follow the theme: in dark mode .ss-bg-white is the dark surface.

Spacing

Padding and margin on the 4px scale: ss-p-1 = 4px, ss-p-4 = 16px, ss-p-8 = 32px. Inline/side classes are logical, so they flip in right-to-left layouts.

ClassAvailable steps
.ss-p-{n}0 1 2 3 4 5 6 8 10 12
.ss-px-{n} (padding-inline)0 2 3 4 5 6 8 10
.ss-py-{n} (padding-block)0 2 3 4 5 6 8 10 12 16
.ss-pt-{n}0 1 2 3 4 6 8 10 12 16
.ss-pb-{n}0 1 2 3 4 6 8 10 12
.ss-pl-{n} / .ss-pr-{n} (inline-start / end)0 2 4 6 8
.ss-m-{n}0 2 4 auto
.ss-mx-auto / .ss-mx-0margin-inline: auto / 0
.ss-my-{n}0 2 4 6 8 10
.ss-mt-{n}0 1 2 3 4 5 6 8 10 12 16 auto
.ss-mb-{n}0 1 2 3 4 5 6 8 10 12 auto
.ss-ml-{n} / .ss-mr-{n} (inline-start / end)0 2 4 auto

Padding scale

ss-p-1
ss-p-2
ss-p-3
ss-p-4
ss-p-6
ss-p-8

Display

How elements render in the layout.

ClassCSS
.ss-block / inline / inline-blockdisplay
.ss-flex / inline-flex / grid / inline-grid / contentsdisplay
.ss-hiddendisplay: none !important
.ss-invisible / .ss-visiblevisibility: hidden / visible
.ss-sr-only / .ss-not-sr-onlyvisually hidden but read by screen readers / undo

Sizing

Width, height and min/max sizes.

ClassCSS
.ss-w-full / auto / fit / min / maxwidth: 100% / auto / fit-content / min-content / max-content
.ss-w-1-2 / 1-3 / 2-3 / 1-4 / 3-4 / 1-5 / 4-5percentage widths
.ss-w-screenwidth: 100vw
.ss-h-full / auto / fitheight: 100% / auto / fit-content
.ss-h-screen / .ss-h-dvhheight: 100vh / 100dvh (follows mobile browser bars)
.ss-min-h-screen / .ss-min-h-dvhmin-height: 100vh / 100dvh (with 100vh fallback)
.ss-max-w-xs / sm / md / lg / xl / 2xlmax-width: 320 / 480 / 640 / 768 / 1024 / 1280px
.ss-max-w-full / nonemax-width: 100% / none
.ss-min-w-0min-width: 0 — lets flex children shrink
.ss-container (-xs/-sm/-md/-lg/-xl)centred, 1200px (480–1280px) with 16px side padding
.ss-measuremax-inline-size: 65ch (see Content Width)

Borders

Border sides, colours and radius.

.ss-border
All sides
.ss-border-t
Top
.ss-border-b
Bottom
.ss-border-l
Inline start
.ss-border-r
Inline end
.ss-border-2
2px
ClassCSS
.ss-border-0 / -x / -ynone / inline / block
.ss-border-primary / success / warning / danger / info / white / grayborder-color
.ss-rounded-0 / -sm / (default) / -lg / -xl / -2xl / -3xl / -fullborder-radius: 0 – 9999px
.ss-circleborder-radius: 50%
.ss-rounded-t / .ss-rounded-btop / bottom corners only

Shadows

Elevation from subtle to dramatic; shadows deepen automatically in dark mode.

.ss-shadow-xs

XS

.ss-shadow-sm

Small

.ss-shadow

Medium

.ss-shadow-lg

Large

.ss-shadow-xl

XL

.ss-shadow-inner

Inner

.ss-shadow-none

None

Position, Z-Index & Overflow

Positioning, stacking and overflow control.

ClassCSS
.ss-static / relative / absolute / fixed / stickyposition
.ss-inset-0 / .ss-top-0 / .ss-bottom-0inset: 0 / top: 0 / bottom: 0
.ss-left-0 / .ss-right-0inset-inline-start / end: 0
.ss-z-0 / 10 / 20 / 30 / 50 / autoz-index
.ss-z-dropdown / .ss-z-modalz-index: 1000 / 1050
.ss-overflow-hidden / auto / scroll / visibleoverflow
.ss-overflow-x-hidden / -x-auto / -y-autooverflow per axis
.ss-overflow-clip / .ss-overflow-x-clipoverflow: clip — new, keeps position: sticky working
.ss-opacity-0 / 10 / 25 / 50 / 75 / 90 / 100opacity
To stop horizontal scrolling, prefer .ss-overflow-x-clip over .ss-overflow-x-hidden:hidden turns the element into a scroll container, which silently disables position: stickyfor everything inside it.

Modern Utilities

Small, useful additions built on newer CSS. Each degrades gracefully in older browsers.

ClassCSS
.ss-text-balancetext-wrap: balance — even line lengths for headings
.ss-text-prettytext-wrap: pretty — avoids a lone last word
.ss-line-clampcut text to --ss-lines lines (default 2) with an ellipsis
.ss-accentaccent-color: var(--ss-primary) — brand native checkbox, radio, range, progress
.ss-overflow-clip / .ss-overflow-x-clipoverflow: clip (sticky-safe)
.ss-min-h-dvhmin-height: 100dvh with a 100vh fallback
.ss-object-contain / cover / fill / none, .ss-object-center / topobject-fit / object-position
.ss-ratio-square / video / 4-3 / 3-2aspect-ratio
.ss-isolateisolation: isolate — new stacking context

.ss-text-balance vs default

A headline that wraps onto two lines in an awkward way

A headline that wraps onto two lines in an awkward way

.ss-line-clamp with style="--ss-lines: 2"

ShailStack keeps your pages light: one stylesheet, no build step and no JavaScript for most components. This paragraph is longer than two lines, so everything after the second line is hidden and an ellipsis is shown — useful for card descriptions and previews where heights must stay even.

.ss-accent on native controls

70%

Transitions & Animations

Animations only run when the user hasn’t asked for reduced motion (prefers-reduced-motion).

Hover effects

.ss-hover-lift
.ss-hover-scale
.ss-hover-shadow
.ss-hover-opacity
ClassCSS
.ss-hover-lift / scale / shadow / opacity / primaryhover effect — animates in and out
.ss-transition / -none / -color / -shadowtransition presets
.ss-duration-150 / 300 / 500transition-duration
.ss-scale-90 / 95 / 105 / 110transform: scale()
.ss-rotate-45 / 90 / 180 / 270transform: rotate()
.ss-translate-centertranslate(-50%, -50%)
.ss-animate-fade-infade in (opacity)
.ss-animate-slide-up / -slide-downfade + slide 16px
.ss-animate-zoom-infade + scale from 95%
.ss-animate-spin / -pulse / -bounce / -shakeloader and attention effects
.ss-delay-100 / 200 / 300 / 500animation-delay

Components

Ready-made UI built from the tokens. Most need no JavaScript; the ones that toggle state are noted.

Buttons

Also .ss-btn-xl, .ss-btn-block (full width) and .ss-btn-icon (square, for icon-only buttons — give them an aria-label).

Badges & chips

primarysuccesswarningdangerinfograydarksolidsolidsoliddot
ChipPrimarySuccessDanger

Alerts

✓ Changes saved successfully.
⚠ Your session expires in 5 minutes.
ℹ ShailStack 3.2 is available.
A neutral note.

Cards

Default

.ss-card

Flat

.ss-card-flat

Hover

.ss-card-hover

Card parts: .ss-card-header, .ss-card-footer, .ss-card-media (edge-to-edge image), .ss-card-clickable.

Tabs, accordion & dropdown

What is ShailStack?
A lightweight utility-first CSS library. One CDN link, zero setup.
Do I need JavaScript?
Not for the accordion — it uses native <details>.

Tabs show the panel with .ss-tab-panel.ss-active — switch the class with a little JavaScript and keeparia-selected in sync. Dropdowns open on hover, on keyboard focus (:focus-within) or with.ss-open. Pill tabs: add .ss-tabs-pill.

Feedback & loading

Saved

Toasts stay dark in both themes.

Everything else

ComponentClasses
Avatar.ss-avatar (-xs … -2xl), .ss-avatar-group, .ss-avatar-wrapper + .ss-avatar-status .ss-avatar-online / offline / busy / away
Divider.ss-divider (-thick, -dashed, -dotted), .ss-divider-text
Table.ss-table-wrap > .ss-table (-striped, -bordered, -primary)
Code.ss-code-block > code, .ss-code-copy
Section.ss-section (-sm, -lg), .ss-section-header, .ss-section-label, .ss-section-title, .ss-section-sub
Navbar.ss-navbar, .ss-navbar-brand, .ss-navbar-links (.ss-open on mobile), .ss-navbar-end
Modal.ss-modal > .ss-modal-box, .ss-modal-header / -title / -body / -footer / -close (bottom sheet ≤ 768px)
Toast.ss-toast-container (.ss-top-right / top-left / bottom-right / bottom-left) > .ss-toast (-success / -warning / -danger / -info)
Stepper.ss-stepper > .ss-step (.ss-active / .ss-complete) > .ss-step-circle, .ss-step-label, .ss-step-line
Stat.ss-stat, .ss-stat-label, .ss-stat-value, .ss-stat-change .ss-stat-up / down
Hero.ss-hero, .ss-hero-title, .ss-hero-sub, .ss-hero-cta
Skeleton.ss-skeleton + -text / -title / -avatar / -btn / -card
Spinner.ss-spinner (-sm, -lg, -xl, -success, -danger, -white)
Progress.ss-progress (-xs … -xl, -success / -warning / -danger) > .ss-progress-bar
Tooltipdata-ss-tooltip="Text", data-ss-tooltip-pos="bottom"

Forms

Inputs, selects, checkboxes, toggles and ranges with visible focus states.

<label class="ss-label ss-label-required" for="email">Email</label> <input class="ss-input" id="email" type="email" aria-invalid="true" aria-describedby="email-error"> <p class="ss-form-error" id="email-error">Enter a valid email address.</p>

As it appears on your ID.

Enter a valid email address.

ClassCSS
.ss-input / .ss-select / .ss-textareatext fields; -sm / -lg sizes via .ss-input-sm / -lg
.ss-input-success / .ss-input-errorvalidation colours
[aria-invalid="true"]new — styled like .ss-input-error automatically
.ss-label / .ss-label-requiredlabel; adds a red * for required
.ss-form-group / -hint / -errorspacing, help text, error text
.ss-input-group + .ss-input-group-iconleading icon inside an input
.ss-checkbox / .ss-radiocustom checkbox and radio
.ss-toggle > input + .ss-toggle-trackswitch
.ss-rangerange slider (Chrome, Safari, Edge and Firefox)

Accessibility

Accessible defaults are built in; a few things still depend on your markup.

Built in

  • Focus: every focusable element gets a 3px brand outline on keyboard focus (:focus-visible) and none on mouse click. Form fields keep a transparent outline so Windows High Contrast still shows focus.
  • Contrast: text utilities, status buttons, badges, links and labels meet WCAG AA (4.5:1) in light and dark themes. Use --ss-primary-text (or .ss-text-primary) for brand-coloured text.
  • Motion: animations only run with prefers-reduced-motion: no-preference; with reduced motion, transitions and smooth scrolling are switched off.
  • RTL: spacing, borders, positions, tables and dropdowns use logical properties (inline-start / inline-end).
  • Keyboard: dropdowns open on focus; the accordion uses native <details>.

Helpers

ClassCSS
.ss-sr-onlyhide visually, keep for screen readers
.ss-not-sr-onlyundo .ss-sr-only
.ss-skip-linknew — “Skip to content” link, visible only on keyboard focus
.ss-focus-ringbrand focus outline for custom elements
<a class="ss-skip-link" href="#main">Skip to content</a> … <main id="main">…</main>

Your part

  • Give icon-only buttons (.ss-btn-icon, close buttons) an aria-label.
  • Tooltips are CSS-only and not reliably announced — also put the text in aria-label or visible text.
  • Add role="progressbar" with aria-valuenow to .ss-progress, and role="status" + a label to spinners.
  • Tabs, modals and dropdowns that you toggle with JavaScript need the usual ARIA (aria-selected, aria-modal, aria-expanded) and focus management.
  • Mark invalid fields with aria-invalid="true" and link the message with aria-describedby.
White text on the default brand colour (#FF1654) is 3.8:1. That passes AA only for large text (24px, or about 19px bold) — not for the default 14px button label. If you need AA everywhere, set--ss-primary: #e0003c (4.9:1) in your :root.

Dark Mode

Three ways to switch. Tokens swap automatically and native controls follow via color-scheme.

Method 1 — data attribute (recommended)

<html data-ss-theme="dark"> <!-- force dark --> <html data-ss-theme="light"> <!-- force light, ignoring the OS --> <script> // toggle const root = document.documentElement; root.dataset.ssTheme = root.dataset.ssTheme === 'dark' ? 'light' : 'dark'; </script>

Method 2 — a dark section with .ss-dark

.ss-dark paints its own dark surface and text, so it works inside a light page. Inside it, use the normal surface classes — .ss-bg-white and .ss-card become dark automatically.

<section class="ss-dark ss-p-6 ss-rounded-lg"> <h3>Dark section</h3> <div class="ss-card">This card is dark too.</div> </section>
Dark section (.ss-dark)
This card follows the dark tokens. Links stay readable.
Badge

Method 3 — automatic

With no data-ss-theme, ShailStack follows the visitor’s OS setting (prefers-color-scheme: dark). Set data-ss-theme="light" on <html> to opt out.

Code blocks, toasts and the skip link keep a dark background in both themes. To customise dark colours, override any --ss-* token inside your own [data-ss-theme="dark"] rule.

Live Playground

Edit HTML with ss-* classes on the left and see the result on the right. Drag the preview’s corner to try the container query.

Editor
Loading...
Preview

Layouts that adapt on their own

Resize the preview — this card switches to a row when its container is wide enough.