npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@phcdevworks/spectre-ui

v5.4.0

Published

@phcdevworks/spectre-ui is the styling layer of the Spectre system. It transforms Spectre tokens into reusable CSS, utilities, and class recipes for consistent application interfaces.

Downloads

1,325

Readme

@phcdevworks/spectre-ui

@phcdevworks/spectre-ui is the styling layer of the Spectre system. It transforms Spectre tokens into reusable CSS, utilities, and class recipes for consistent application interfaces.

Maintained by PHCDevworks. It sits between @phcdevworks/spectre-tokens and the framework-specific adapter and component packages, so no downstream repo needs to hand-roll CSS or hardcode design values to consume Spectre's visual language.

Repository Snapshot

| Field | Value | | ---------------------- | ---------------------------------- | | Project team | project-design | | Repository role | Spectre L2 CSS and recipe contract | | Package/artifact | @phcdevworks/spectre-ui | | Current version/status | 5.4.0 |

Standard Workflow

  1. Read AGENTS.md, then the agent-specific guide for the task.
  2. Check TODO.md and ROADMAP.md for current scope.
  3. Make the smallest repo-local change that satisfies the task.
  4. Run npm run check when validation is required or practical.
  5. Update docs and CHANGELOG.md only when behavior, public contracts, or release-relevant metadata changed.

Documentation Map

| Guide | Path | | ----------- | ---------------------------- | | Agent rules | AGENTS.md | | Claude Code | CLAUDE.md | | Codex | CODEX.md | | Copilot | COPILOT.md | | Jules | JULES.md | | Grok | GROK.md | | Roadmap | ROADMAP.md | | Todo | TODO.md | | Changelog | CHANGELOG.md | | Security | SECURITY.md |

npm version CI License Node

@phcdevworks/spectre-ui is Layer 2 of the Spectre design suite. It turns @phcdevworks/spectre-tokens into reusable CSS bundles and type-safe class recipes for downstream adapters and apps.

For: adapter authors and app developers who need a stable, token-driven styling contract without re-implementing class logic themselves.

Not for: authoring design tokens (that belongs in @phcdevworks/spectre-tokens) or building framework-specific components (that belongs in adapter packages such as @phcdevworks/spectre-ui-astro).

Contributing | Code of Conduct | Changelog | Roadmap | Security Policy

Source Of Truth

@phcdevworks/spectre-tokens is the source of truth for visual values and semantic meaning. ui-contract.manifest.json is the machine-readable contract authority for this package's public styling surface.

| Layer | Path | Rule | | --------------------- | ----------------------------------------------- | -------------------------------------------------------- | | Token authority | Published @phcdevworks/spectre-tokens package | Design values and semantic meaning start there | | UI contract authority | ui-contract.manifest.json | Governs public recipes and CSS entry points | | Source CSS | src/styles/ | Token-backed CSS classes and bundle entry points | | Source recipes | src/recipes/ | Framework-agnostic class string APIs | | Generated dist | dist/ | Never edit directly — regenerated by npm run build |

After any contract-facing source change: run npm run check to validate the full UI contract.

Architecture

| Layer | Package or consumer | Responsibility | Relationship to this package | | ----- | ---------------------------------------------------------- | ---------------------------------------------------- | ---------------------------- | | 1 | @phcdevworks/spectre-tokens | Defines design values and semantic token meaning | Upstream source of truth | | 2 | @phcdevworks/spectre-ui | Translates tokens into CSS bundles and class recipes | This package | | 3 | Adapters and apps, such as @phcdevworks/spectre-ui-astro | Deliver Spectre through framework-native ergonomics | Downstream consumers |

@phcdevworks/spectre-components is a separate component package that can wrap this styling contract in Lit web components. This package owns Layer 2 only: it does not deliver components and it does not define tokens.

Key Capabilities

  • Ships precompiled CSS: index.css, base.css, components.css, and utilities.css
  • Exports type-safe class recipes for shared UI patterns
  • Keeps CSS classes and recipe APIs aligned
  • Gives adapters and apps a stable styling contract instead of re-implementing classes
  • Enforces a zero-hex approach so visual values stay tied to @phcdevworks/spectre-tokens

What This Package Owns

  • Token-backed CSS class contracts in src/styles/
  • Precompiled CSS bundles for root, base, components, and utilities
  • Framework-agnostic class recipe functions in src/recipes/
  • Contract validation that keeps CSS, recipes, exports, and docs aligned

This package is the correct place to define reusable styling structure on top of Spectre tokens.

What This Package Does Not Own

  • Design token values or semantic visual meaning. Those belong in @phcdevworks/spectre-tokens.
  • Framework components, templates, hooks, or runtime behavior. Those belong in adapter packages.
  • App-level layout, routing, data fetching, or product-specific composition.
  • Local redefinition of token meaning. Downstream consumers should consume the token contract rather than recreate it.

When To Use This Package

Use @phcdevworks/spectre-ui when you need:

  • precompiled, token-backed CSS ready to drop into any framework
  • stable, type-safe class recipes for shared UI patterns (buttons, badges, cards, inputs, etc.) that you want to remain consistent across frameworks
  • a styling contract that is enforced through tests and CI rather than conventions alone

When Not To Use This Package

Do not use @phcdevworks/spectre-ui when you need to:

  • Define new design values — add them to @phcdevworks/spectre-tokens instead.
  • Deliver framework components — use an adapter package such as @phcdevworks/spectre-ui-astro that wraps this package in framework-native components.

Installation

npm install @phcdevworks/spectre-ui

Quick Start

Vanilla HTML — CSS classes only

No framework needed. Import the CSS and use the sp-* classes directly:

<!doctype html>
<html>
  <head>
    <link
      rel="stylesheet"
      href="node_modules/@phcdevworks/spectre-ui/dist/index.css"
    />
  </head>
  <body>
    <button class="sp-btn sp-btn--primary sp-btn--md">Save</button>
    <button class="sp-btn sp-btn--ghost sp-btn--md">Cancel</button>
    <span class="sp-badge sp-badge--success sp-badge--sm">Published</span>

    <div class="sp-card sp-card--elevated">
      <p>Card content</p>
    </div>

    <div class="sp-input-wrapper">
      <label class="sp-label">Email</label>
      <input class="sp-input sp-input--md" type="email" />
    </div>
  </body>
</html>

CSS import (bundler or framework)

Import the full stylesheet:

import '@phcdevworks/spectre-ui/index.css'

Or import the bundles separately:

import '@phcdevworks/spectre-ui/base.css'
import '@phcdevworks/spectre-ui/components.css'
import '@phcdevworks/spectre-ui/utilities.css'

Class recipe usage

Class recipes are the stable styling API for adapters and apps. They return predictable class strings and keep behavior consistent across frameworks.

import {
  getBadgeClasses,
  getButtonClasses,
  getPricingCardClasses
} from '@phcdevworks/spectre-ui'

const cta = getButtonClasses({ variant: 'primary', size: 'lg' })
const badge = getBadgeClasses({ variant: 'success', size: 'sm' })
const pricingCard = getPricingCardClasses({ featured: true })

What Belongs Here Vs Elsewhere

| What | Where it lives | | ------------------------------------------------ | ------------------------------------------ | | Semantic color values, spacing scale, type scale | @phcdevworks/spectre-tokens | | Token-to-CSS variable mapping | here — src/styles/ | | Precompiled CSS bundles | here — built to dist/*.css | | Class recipe functions (input → class string) | here — src/recipes/ | | Astro, React, Vue, Lit, Svelte components | Adapter packages (e.g. spectre-ui-astro) | | WordPress shortcodes or PHP templates | A WordPress adapter package | | App-level layout, routing, or data fetching | Consuming apps | | New design decisions (new colors, new spacing) | @phcdevworks/spectre-tokens |

Golden rule: this package consumes tokens and exposes class contracts. It does not define tokens and it does not deliver framework components.

Package Exports / API Surface

Recipe quick reference

All recipe functions accept a plain options object and return a class string. Boolean options are caller-owned: omission adds no corresponding modifier class, true adds it, and false omits it. Component packages define their own property defaults and pass resolved boolean values explicitly. Non-boolean recipe axes may retain documented visual fallbacks.

| Recipe | Function | Variants | Sizes | Common boolean flags | | -------------------- | ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Button | getButtonClasses | primary secondary ghost danger success cta accent inverse warning link light dark | sm md lg | disabled loading fullWidth pill iconOnly compact | | Badge | getBadgeClasses | primary secondary success warning danger neutral info ghost outline accent cta inverse brand | sm md lg | interactive dot disabled loading fullWidth, accentRail: top|right|bottom|left, accentRailColor: neutral|brand|info|success|warning|danger|cta | | Card | getCardClasses | elevated flat outline ghost | padded: sm md lg | interactive padded (also accepts a size) fullHeight disabled loading, accent: top|right|bottom|left, accentColor: neutral|brand|info|success|warning|danger|cta | | Card bleed | getCardBleedClasses | — | padded: sm md lg | edges: single edge, an array, or 'all' | | Input | getInputClasses | — | sm md lg | disabled loading fullWidth pill | | Input state | getInputClasses | state: default error success disabled loading | — | — | | IconBox | getIconBoxClasses | primary secondary success warning danger info neutral ghost accent cta outline | xs sm md lg | interactive disabled loading pill fullWidth | | PricingCard | getPricingCardClasses | — | — | featured interactive disabled loading fullHeight, accent: top|right|bottom|left, accentColor: neutral|brand|info|success|warning|danger|cta | | Rating | getRatingClasses | — | sm md lg | interactive disabled loading pill fullWidth | | Testimonial | getTestimonialClasses | elevated flat outline ghost | — | interactive disabled loading fullHeight, accent: top|right|bottom|left, accentColor: neutral|brand|info|success|warning|danger|cta | | Alert | getAlertClasses | info success warning danger neutral brand | sm md lg | dismissed dismissible | | Avatar | getAvatarClasses | — | xs sm md lg xl | shape: circle square | | Tag | getTagClasses | default primary secondary success warning danger info neutral accent cta outline ghost | sm md lg | dismissible selected disabled loading interactive fullWidth | | Spinner | getSpinnerClasses | primary secondary success warning danger info neutral accent cta inverse | sm md lg | disabled loading | | Skeleton | getSkeletonClasses | shape: text rect circle | — | animated | | Logo cloud | getLogoCloudClasses | size: sm md lg, fill: subtle card none | — | muted; item: getLogoCloudItemClasses | | Nav | getNavClasses | — | — | bordered sticky fullWidth align: start\|center\|end, accent: top|right|bottom|left, accentColor: neutral|brand|info|success|warning|danger|cta | | Toast | getToastClasses | info success warning danger neutral | — | dismissed fullWidth, accent: top|right|bottom|left, accentColor: neutral|brand|info|success|warning|danger|cta | | Tooltip | getTooltipClasses | placement: top bottom left right | — | visible, accent: top|right|bottom|left, accentColor: neutral|brand|info|success|warning|danger|cta | | Dropdown | getDropdownClasses | menu placement: bottom-start bottom-end top-start top-end | — | fullWidth mega viewport, item: active selected disabled, menu accent: top|right|bottom|left, menu accentColor: neutral|brand|info|success|warning|danger|cta | | Modal | getModalClasses | — | — | open fullWidth, accent: top|right|bottom|left, accentColor: neutral|brand|info|success|warning|danger|cta | | Container | getContainerClasses | maxWidth: prose wide, padding: sm md lg xl 2xl 3xl 4xl | — | — | | Stack | getStackClasses | direction: vertical horizontal, basis: sidebar, align: center stretch, gap: sm md lg xl 2xl 3xl 4xl | — | — | | Section | getSectionClasses | spacing: sm md lg xl 2xl 3xl 4xl, gap: sm md lg xl 2xl 3xl 4xl, hero: sm md lg (replaces spacing) | — | attached (drops top padding for a band that belongs to the section above) | | Prose | getProseClasses | — | — | — | | Grid | getGridClasses | columns: 1 2 3 4 6 12 auto | gap/columnGap/rowGap: sm md lg xl 2xl 3xl 4xl | span/offset/rowSpan/rowOffset/order: 1–12/0–11/first|last|none|1–12, per-breakpoint { base, md, lg } | | Sidebar | getSidebarClasses | — | — | bordered | | Footer | getFooterClasses | appearance: dark (default) light system, surface: page card subtle inverse hero | — | bordered fullWidth, accent: top|right|bottom|left, accentColor: neutral|brand|info|success|warning|danger|cta | | Checkbox | getCheckboxClasses | — | — | checked disabled | | Radio | getRadioClasses | — | — | checked disabled | | Select | getSelectClasses | size: sm md lg, state: default invalid success | — | fullWidth pill disabled focused loading | | Textarea | getTextareaClasses | size: sm md lg, state: default invalid success | — | fullWidth pill disabled focused loading | | Fieldset | getFieldsetClasses | — | — | disabled | | Label | getLabelClasses | — | — | disabled required | | Text | getTextClasses | color: default muted subtle meta brand onInverse onInverseMuted onSurface onSurfaceMuted onSurfaceSubtle onSurfaceMeta onSurfaceBrand, family: sans serif mono | xs–6xl | — | | Text state | getTextClasses | transform: none uppercase lowercase capitalize, weight: 400–900 (emits sp-font-{weight}) | — | — | | Heading | getHeadingClasses | level: h1–h6 (the full typography.heading preset) | — | — | | Display | getDisplayClasses | level: 1–6 (the full typography.display preset) | — | — | | Lead | getLeadClasses | — (the typography.lead preset) | — | — | | Tabs | getTabsClasses | line pill | — | vertical fullWidth, item: active disabled | | Accordion | getAccordionClasses | — | — | flush, item/header/icon/panel: expanded; native <details open> also expands | | Breadcrumb | getBreadcrumbClasses | — | — | customSeparator, item: current | | List group | getListGroupClasses | — | — | flush horizontal, item: interactive active selected disabled, accent/accentColor | | Offcanvas | getOffcanvasClasses | placement: start end top bottom | — | open (panel and backdrop) | | Carousel | getCarouselClasses | control direction: prev next | — | fade, slide/indicator: active | | Table | getTableClasses | row variant: neutral info success warning danger | sm md | striped hoverable bordered, row: selected | | Pagination | getPaginationClasses | — | sm md lg | item: active disabled | | Stepper | getStepperClasses | orientation: horizontal vertical, step state: pending active done | — | — | | Popover | getPopoverClasses | placement: top bottom left right | — | open | | Progress | getProgressClasses | bar variant: brand neutral info success warning danger | sm md lg | bar: indeterminate | | Switch | getSwitchClasses | — | sm md lg | checked disabled focused (native :checked/:disabled also apply) | | Range | getRangeClasses | — | — | disabled focused | | File input | getFileInputClasses | state: default invalid success | sm md lg | fullWidth disabled focused | | Input group | getInputGroupClasses | — | — | disabled | | Datepicker | getDatepickerClasses | — | — | day (getDayClasses): selected today outsideMonth disabled | | External auth button | getExternalAuthButtonClasses | — | — | fullWidth disabled loading | | Choice card | getChoiceCardClasses | — | — | selected disabled (a wrapped native input also drives both) |

Each recipe family also exports sub-element helpers for its structural parts (labels, wrappers, sub-containers, text elements). See the full list below.

getCardClasses also accepts an optional accent edge ('top', 'right', 'bottom', or 'left') to render a thicker decorative rail, sized from component.card.accent.thickness. accentColor selects the semantic color scale (neutral, brand, info, success, warning, danger, cta) and defaults to 'brand' when accent is set without it. Omitting accent renders no rail — border width, color, and radius on every edge stay unchanged.

getCardClasses({ variant: 'outline', accent: 'left', accentColor: 'danger' })

The same edge-rail pattern extends to nine more recipe families, each sourced from its own component.<name>.accent token group (spectre-tokens 4.9.0): getTestimonialClasses, getPricingCardClasses, getNavClasses, getFooterClasses, getModalClasses, getToastClasses, getTooltipClasses, getDropdownMenuClasses, all with the same accent edge / accentColor options as getCardClasses above. getBadgeClasses uses accentRail/accentRailColor instead, since variant: 'accent' already names its single-tone brand-accent fill.

getToastClasses({ variant: 'success', accent: 'left', accentColor: 'brand' })
getBadgeClasses({
  variant: 'outline',
  accentRail: 'top',
  accentRailColor: 'cta'
})

getCardBleedClasses lets a child of a padded card (media, a flush internal surface) run through the card's padding on one or more edges without the consumer negating the active padding token or reconstructing card corner radius locally. Pass the same padded value given to the parent getCardClasses call so the bleed amount tracks the active padding step, and edges (a single edge, an array, or 'all') for which sides run flush. Corner radius is only added where two bled edges meet (e.g. edges: ['top', 'left'] rounds the top-left corner to match the card), so a single-edge bleed stays square. Omitting padded is correct for an unpadded card — the edge classes still apply corner radius with zero bleed margin.

getCardClasses({ padded: 'lg' })
// child media flush to the top edge only
getCardBleedClasses({ padded: 'lg', edges: 'top' })
// child surface flush to every edge
getCardBleedClasses({ padded: 'lg', edges: 'all' })

Grid also accepts two independent track-sizing options for layouts equal columns cannot express: fixedTracks: { count: 1-4 } sizes every column from --sp-space-240 (15rem) and replaces columns at every breakpoint; leadingTracks: { weight: 1.5 | 1.6 | 2 | 2.5 | 3 } sizes one leading column at weightfr against the rest of columns as equal 1fr tracks. A plain weight applies at the lg breakpoint only (matching the original mega-menu/footer evidence); pass { base, md, lg } for per-breakpoint control. Both emit deterministic classes (sp-grid-fixed-tracks-*, sp-{bp-}grid-leading-{weight}-of-{columns}) — no arbitrary widths, no inline styles.

Grid also accepts rowSpan/rowOffset (same shape as span/offset: a single value or a per-breakpoint { base, md, lg } object) for dashboard-style layouts that need explicit row placement, via grid-row/grid-row-start — no explicit row-track template required, since these apply against CSS Grid's auto-generated implicit rows. Independent columnGap/rowGap options override the combined gap on a single axis when a layout needs tighter rows than columns (or vice versa).

columns: 'auto' (sp-grid-cols-auto) distributes any number of children evenly across a single row with no explicit column count — matching Bootstrap's bare .col / row-cols-auto — via grid-template-columns: repeat(auto-fit, minmax(0, 1fr)). order/order: { base, md, lg } (sp-order-*, responsive sp-{bp}-order-*) reorders a grid item independent of source order, accepting first, last, none, or 1–12.

Dropdown also accepts a mega flag (getDropdownClasses({ mega: true }) paired with getDropdownMenuClasses({ mega: true })) for mega-menu panels: the trigger wrapper (sp-dropdown--mega) cedes its positioning context to the nearest positioned ancestor — typically sp-nav, which is a positioning context by default — so the menu (sp-dropdown__menu--mega) spans that ancestor's full width instead of tracking trigger width. Combine with a getGridClasses panel inside the menu for a multi-column layout. Menu height is capped and scrollable (max-height: 70vh; overflow-y: auto) so tall panels never overflow the viewport.

A third viewport flag (getDropdownClasses({ viewport: true }) paired with getDropdownMenuClasses({ viewport: true })) breaks the menu out to the full browser viewport width instead of the trigger's or mega's positioned-ancestor width — for a wide menu that would otherwise overflow past a narrow trigger or a width-constrained nav. It uses the standard full-bleed breakout technique (left: 50%; width: 100vw; margin-left: -50vw), which assumes the menu's positioned ancestor is horizontally centered in the viewport (true for a centered sp-container-based layout); it will not center correctly inside an off-center ancestor (e.g. a fixed sidebar layout). viewport takes precedence over mega if both are set — three widening tiers: trigger width (default), mega (nav width), viewport (full browser width).

Bootstrap-scale component inventory

The component families added against the spectre-tokens 4.10.0 component.* contracts follow Bootstrap's range of component types; their look stays entirely token-driven. Each family reads its colors from its own mode-aware token group, so dark mode needs no local overrides.

getTabsClasses({ variant: 'pill' }) // 'sp-tabs sp-tabs--pill'
getTabsItemClasses({ active: true }) // 'sp-tabs__item sp-tabs__item--active is-active'
getTableClasses({ striped: true, hoverable: true })
getTableRowClasses({ variant: 'warning' })
getOffcanvasClasses({ placement: 'end', open: true })
getStepperStepClasses({ state: 'done' })

A few families need something from the caller that a class string cannot carry:

  • Progress — set the bar's inline width to the current value. indeterminate animates instead.
  • Range — Firefox paints the filled track natively. For WebKit/Blink, mirror the value as a percentage in --sp-component-range-value on the input (e.g. style="--sp-component-range-value: 40%").
  • Carousel — the viewport is a CSS scroll-snap track and works without script. fade stacks the slides and shows the one marked active, which the caller toggles.
  • Popover — like the tooltip, place it inside a position: relative trigger wrapper.

Several families also follow native state, so the matching boolean flag is optional: <details open> expands an accordion item, :checked/:disabled drive the switch, a wrapped :checked input selects a choice card, and aria-current/aria-selected mark the current tab, page, breadcrumb, or day.

getListGroupClasses also takes the accent/accentColor edge-rail options described above, sourced from component.listGroup.accent.

getDisplayClasses({ level }) (1–6) applies the typography.display presets, for hero and marketing headings one scale step larger than the matching heading level. getLeadClasses() applies typography.lead to an introductory paragraph.

Token parity

tests/token-parity.test.ts fails if @phcdevworks/spectre-tokens publishes a CSS variable that no spectre-ui stylesheet references. The only exemption is --sp-breakpoint-sm/-xl/-2xl: CSS cannot use var() in @media queries, so the generated responsive utilities consume them by value, and the test checks those values instead. Two more groups are consumed through spectre-tokens' own remap blocks rather than by name: --sp-layout-responsive-lg-* (re-points the xl–4xl layout steps at the lg breakpoint) and --sp-control-compact-* (re-points the --sp-control-* steps under [data-spectre-density="compact"]).

Motion utilities (.sp-animate-*) switch to their published animations.reducedMotion counterparts under prefers-reduced-motion.

Semantic utility classes (no recipe wrapper)

These primitives are intentionally plain CSS classes in src/styles/utilities.css with no recipe function — there is no variant or size axis to validate, so a recipe wrapper would add indirection without a type-safety benefit. Apply the class name directly.

| Class | Tokens | Usage | | ----------------------- | ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | | .sp-link | --sp-link-default --sp-link-hover --sp-link-active --sp-link-visited | Inline text links (<a>). | | .sp-link--on-inverse | --sp-link-on-inverse --sp-link-on-inverse-hover | Inline text links on a surface.inverse-backed background. | | .sp-surface--hover | --sp-surface-hover | Clickable list items, menu items, table rows on hover. | | .sp-surface--selected | --sp-surface-selected | Selected list items, menu items, table rows. | | .sp-surface--active | --sp-surface-active | Pressed/active state for clickable surfaces. | | .sp-surface--hero | --sp-surface-hero | Gradient background for a hero/jumbotron band; pair with the on-inverse text roles. | | .sp-surface--input | --sp-surface-input | An input-like well that is not itself a form control. | | .sp-surface--inverse | --sp-surface-inverse | Background for an on-dark/inverse content island; pairs with the .sp-text--on-inverse*/.sp-link--on-inverse/inverse Badge/Button variants. | | .sp-divider | --sp-surface-divider | <hr>, section separators, table borders. | | .sp-text-balance | — | Balanced line wrapping (text-wrap: balance) so a short headline does not end on an orphan word. | | .sp-tabular-nums | — | Tabular numerals for figures that repaint in place (countdowns, live prices). | | .sp-{md\|lg}-grid-cols-{n} | --sp-breakpoint-md/-lg (by value) | n equal grid columns (1 2 3 4 6 12) from that breakpoint up, overriding the sp-grid-cols-* step-down preset. |

Generated utility classes

src/styles/utilities.generated.css is a build-time generated file (npm run build:utilities, wired into npm run build) that expands finite token scales and the fixed layout contract into flat utility classes. It is not hand-edited; regenerate it after a @phcdevworks/spectre-tokens bump and commit the result (npm run validate:utilities fails CI if it drifts). No arbitrary visual values are supported — a design need that doesn't fit an existing token step needs a token proposal to spectre-tokens, not a raw value in markup.

| Family | Class pattern | Token/value source | | -------------- | ----------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | | Layout | .sp-{block\|flex\|grid\|hidden\|relative\|...} | CSS layout keywords and --sp-space-0 | | Flexbox | .sp-{flex-row\|flex-wrap\|justify-between\|items-center\|self-start\|content-between\|order-first\|order-{1-12}\|...} | CSS layout keywords | | Sizing | .sp-{w\|min-w\|max-w\|h\|min-h\|max-h}-{auto\|0\|full\|fit\|none} | CSS intrinsic and percentage sizing | | Overflow | .sp-overflow-{auto\|hidden\|clip\|visible\|scroll} with -x- and -y- variants | CSS overflow keywords | | Spacing | .sp-{p\|px\|py\|pt\|pr\|pb\|pl\|m\|mx\|my\|mt\|mr\|mb\|ml\|gap\|gap-x\|gap-y\|basis}-{step} | --sp-space-*; auto-margin variants are added | | Palette | .sp-{text\|bg\|border}-{hue}-{step}, including multi-segment hues such as integration-gunmetal | --sp-color-palette-* | | Color scale | .sp-{text\|bg\|border}-color-{scale}-{step}, plus .sp-{text\|bg\|border}-{black\|white} | --sp-color-{brand,accent,neutral,success,warning,error,info,indigo,violet}-* — an opt-in raw scale; prefer semantic roles | | Duration | .sp-duration-{step} | --sp-duration-* (transition-duration) | | Easing | .sp-ease-{step} | --sp-easing-* (transition-timing-function) | | Border style | .sp-border-style-{solid\|dashed\|dotted\|none} | --sp-border-style-* | | Border width | .sp-border-width-{none\|base\|thick} | --sp-border-width-* | | Icon size | .sp-icon-{xs\|sm\|md\|lg\|xl\|2xl\|3xl} | --sp-icon-* (width and height) | | Radius | .sp-rounded-{step} | --sp-radius-* | | Shadow | .sp-shadow-{step} | --sp-shadow-* | | Shadow (inset) | .sp-shadow-inset-{step} | --sp-shadow-inset-* | | Opacity | .sp-opacity-{role} | --sp-opacity-* | | Z-index | .sp-z-{role} | --sp-z-index-* | | Surface bg | .sp-bg-surface-{role} | --sp-surface-* (page, card, input, subtle, hover, selected, active, divider, inverse, overlay, hero) | | Thick border | .sp-border-thick and -t/-r/-b/-l; add any .sp-border-{color} utility for a role color | --sp-border-width-thick, divider color by default (no responsive variants) | | Chart color | .sp-{text\|bg\|border\|fill\|stroke}-chart-{role} | --sp-chart-* (bg, grid, axis, label, series-1–8, sequential-1–7, diverging-1–7) | | Elevation | .sp-elevation-{flat\|raised\|overlay\|modal} | --sp-elevation-* (shadow, surface, and z-index together) | | Font weight | .sp-font-{weight} | distinct values already published across --sp-font-{step}-weight / --sp-heading-h{n}-weight |

Responsive variants use the sp-{breakpoint}-{utility} prefix form (for example, sp-md-p-4, sp-lg-gap-8, sp-md-flex, and sp-lg-justify-between) at every published breakpoint (sm, md, lg, xl, 2xl), matching the step-down convention Grid already established. Grid's hand-authored column-count utilities keep their own md/lg-only convention. This prefix syntax is a locked decision (see TODO.md Phase 7 P0); it will not change without a major-version breaking release.

All exported stylesheets declare the cascade order tokens, base, components, then utilities. Utilities therefore override component defaults regardless of whether consumers load the standalone component and utility bundles in the opposite order, and every Spectre layer — including the package's own :root[data-spectre-theme="dark"] token defaults — overrides only within that order. Unlayered application CSS still overrides all Spectre layers, and any layered override a consumer declares (including a scoped :root[data-spectre-theme="dark"] block in the app's own CSS) wins over the package's tokens layer without needing !important.

Color modes can be scoped. Put data-spectre-theme="dark", "light", "high-contrast", or "system" on any element (a header, footer, or hero band), and every Spectre component inside it switches mode, because component variables are re-declared on each themed element. system follows the visitor's prefers-color-scheme. Base styles also take the text selection, caret, and scrollbar colors from the mode tokens. Buttons, inputs, and selects are sized by --sp-control-*. Put data-spectre-density="compact" on any element to use the compact steps inside it. Use .sp-prose kbd to style keycaps.

Root package

The root package exports CSS path constants plus the recipe functions re-exported from src/recipes/index.ts.

Root constants:

  • spectreStyles
  • spectreBaseStylesPath
  • spectreComponentsStylesPath
  • spectreIndexStylesPath
  • spectreUtilitiesStylesPath

Root recipe functions:

  • getAccordionClasses
  • getAlertClasses
  • getAvatarClasses
  • getBadgeClasses
  • getBreadcrumbClasses
  • getButtonClasses
  • getCardBleedClasses
  • getCardClasses
  • getCarouselClasses
  • getCheckboxClasses
  • getChoiceCardClasses
  • getContainerClasses
  • getDatepickerClasses
  • getDayClasses
  • getDisplayClasses
  • getDropdownClasses
  • getExternalAuthButtonClasses
  • getFieldsetClasses
  • getFileInputClasses
  • getFooterClasses
  • getGridClasses
  • getHeadingClasses
  • getIconBoxClasses
  • getInputClasses
  • getInputGroupClasses
  • getLabelClasses
  • getLeadClasses
  • getListGroupClasses
  • getModalClasses
  • getNavClasses
  • getOffcanvasClasses
  • getPaginationClasses
  • getPopoverClasses
  • getPricingCardClasses
  • getProgressClasses
  • getProseClasses
  • getRadioClasses
  • getRangeClasses
  • getRatingClasses
  • getSectionClasses
  • getSelectClasses
  • getSidebarClasses
  • getSkeletonClasses
  • getSpinnerClasses
  • getStackClasses
  • getStepperClasses
  • getSwitchClasses
  • getTableClasses
  • getTabsClasses
  • getTagClasses
  • getTestimonialClasses
  • getTextareaClasses
  • getTextClasses
  • getToastClasses
  • getTooltipClasses

Root recipe helper functions:

  • getAccordionHeaderClasses
  • getAccordionIconClasses
  • getAccordionItemClasses
  • getAccordionPanelClasses
  • getAlertDismissClasses
  • getAlertIconClasses
  • getBreadcrumbItemClasses
  • getBreadcrumbLinkClasses
  • getBreadcrumbSeparatorClasses
  • getCarouselCaptionClasses
  • getCarouselControlClasses
  • getCarouselIndicatorClasses
  • getCarouselIndicatorsClasses
  • getCarouselSlideClasses
  • getCarouselViewportClasses
  • getDatepickerGridClasses
  • getDatepickerHeaderClasses
  • getDatepickerWeekdayClasses
  • getDropdownDividerClasses
  • getDropdownHeaderClasses
  • getDropdownItemClasses
  • getDropdownMenuClasses
  • getExternalAuthButtonIconClasses
  • getFieldsetLegendClasses
  • getFooterChipClasses
  • getFooterDividerClasses
  • getFooterHeadingClasses
  • getFooterLinkClasses
  • getFooterLinksClasses
  • getFooterMutedClasses
  • getFooterTextClasses
  • getInputErrorMessageClasses
  • getInputGroupAddonClasses
  • getInputHelperTextClasses
  • getInputLabelClasses
  • getInputWrapperClasses
  • getListGroupItemClasses
  • getListGroupItemHeadingClasses
  • getListGroupItemTextClasses
  • getLogoCloudClasses
  • getLogoCloudItemClasses
  • getModalOverlayClasses
  • getNavLinkClasses
  • getNavLinksClasses
  • getOffcanvasBackdropClasses
  • getOffcanvasBodyClasses
  • getOffcanvasFooterClasses
  • getOffcanvasHeaderClasses
  • getPaginationEllipsisClasses
  • getPaginationItemClasses
  • getPopoverArrowClasses
  • getPopoverBodyClasses
  • getPopoverHeaderClasses
  • getPricingCardBadgeClasses
  • getPricingCardDescriptionClasses
  • getPricingCardPriceClasses
  • getPricingCardPriceContainerClasses
  • getProgressBarClasses
  • getProgressLabelClasses
  • getRatingStarClasses
  • getRatingStarsClasses
  • getRatingTextClasses
  • getSidebarBackdropClasses
  • getSidebarGroupClasses
  • getSidebarGroupSummaryClasses
  • getSidebarHeaderClasses
  • getSidebarLinkClasses
  • getSidebarToggleClasses
  • getStepperIndicatorClasses
  • getStepperLabelClasses
  • getStepperStepClasses
  • getTableRowClasses
  • getTableWrapperClasses
  • getTabsItemClasses
  • getTabsListClasses
  • getTabsPanelClasses
  • getTestimonialAuthorClasses
  • getTestimonialAuthorInfoClasses
  • getTestimonialAuthorNameClasses
  • getTestimonialAuthorTitleClasses
  • getTestimonialQuoteClasses
  • getToastIconClasses

The root package also re-exports the related recipe option, variant, size, and state TypeScript types defined by those recipes.

CSS entry points

  • @phcdevworks/spectre-ui/index.css
  • @phcdevworks/spectre-ui/base.css
  • @phcdevworks/spectre-ui/components.css
  • @phcdevworks/spectre-ui/utilities.css

Public Contract Guarantees

ui-contract.manifest.json defines the public styling contract for this package.

It covers:

  • CSS entry points
  • root package constants and recipe function exports
  • recipe families, variants, sizes, and public states

Every contract-facing surface must match that manifest. Validation fails when README documentation omits manifest-declared exports, when export snapshots drift, or when CSS contract coverage no longer matches the declared surface.

Sidebar interactive-state contract

getSidebarClasses is the first recipe family with an interactive-state CSS contract. Below `break