@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
Maintainers
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
- Read AGENTS.md, then the agent-specific guide for the task.
- Check TODO.md and ROADMAP.md for current scope.
- Make the smallest repo-local change that satisfies the task.
- Run
npm run checkwhen validation is required or practical. - 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 |
@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, andutilities.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-tokensinstead. - Deliver framework components — use an adapter package such as
@phcdevworks/spectre-ui-astrothat wraps this package in framework-native components.
Installation
npm install @phcdevworks/spectre-uiQuick 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
widthto the current value.indeterminateanimates instead. - Range — Firefox paints the filled track natively. For WebKit/Blink, mirror
the value as a percentage in
--sp-component-range-valueon the input (e.g.style="--sp-component-range-value: 40%"). - Carousel — the viewport is a CSS scroll-snap track and works without
script.
fadestacks the slides and shows the one markedactive, which the caller toggles. - Popover — like the tooltip, place it inside a
position: relativetrigger 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:
spectreStylesspectreBaseStylesPathspectreComponentsStylesPathspectreIndexStylesPathspectreUtilitiesStylesPath
Root recipe functions:
getAccordionClassesgetAlertClassesgetAvatarClassesgetBadgeClassesgetBreadcrumbClassesgetButtonClassesgetCardBleedClassesgetCardClassesgetCarouselClassesgetCheckboxClassesgetChoiceCardClassesgetContainerClassesgetDatepickerClassesgetDayClassesgetDisplayClassesgetDropdownClassesgetExternalAuthButtonClassesgetFieldsetClassesgetFileInputClassesgetFooterClassesgetGridClassesgetHeadingClassesgetIconBoxClassesgetInputClassesgetInputGroupClassesgetLabelClassesgetLeadClassesgetListGroupClassesgetModalClassesgetNavClassesgetOffcanvasClassesgetPaginationClassesgetPopoverClassesgetPricingCardClassesgetProgressClassesgetProseClassesgetRadioClassesgetRangeClassesgetRatingClassesgetSectionClassesgetSelectClassesgetSidebarClassesgetSkeletonClassesgetSpinnerClassesgetStackClassesgetStepperClassesgetSwitchClassesgetTableClassesgetTabsClassesgetTagClassesgetTestimonialClassesgetTextareaClassesgetTextClassesgetToastClassesgetTooltipClasses
Root recipe helper functions:
getAccordionHeaderClassesgetAccordionIconClassesgetAccordionItemClassesgetAccordionPanelClassesgetAlertDismissClassesgetAlertIconClassesgetBreadcrumbItemClassesgetBreadcrumbLinkClassesgetBreadcrumbSeparatorClassesgetCarouselCaptionClassesgetCarouselControlClassesgetCarouselIndicatorClassesgetCarouselIndicatorsClassesgetCarouselSlideClassesgetCarouselViewportClassesgetDatepickerGridClassesgetDatepickerHeaderClassesgetDatepickerWeekdayClassesgetDropdownDividerClassesgetDropdownHeaderClassesgetDropdownItemClassesgetDropdownMenuClassesgetExternalAuthButtonIconClassesgetFieldsetLegendClassesgetFooterChipClassesgetFooterDividerClassesgetFooterHeadingClassesgetFooterLinkClassesgetFooterLinksClassesgetFooterMutedClassesgetFooterTextClassesgetInputErrorMessageClassesgetInputGroupAddonClassesgetInputHelperTextClassesgetInputLabelClassesgetInputWrapperClassesgetListGroupItemClassesgetListGroupItemHeadingClassesgetListGroupItemTextClassesgetLogoCloudClassesgetLogoCloudItemClassesgetModalOverlayClassesgetNavLinkClassesgetNavLinksClassesgetOffcanvasBackdropClassesgetOffcanvasBodyClassesgetOffcanvasFooterClassesgetOffcanvasHeaderClassesgetPaginationEllipsisClassesgetPaginationItemClassesgetPopoverArrowClassesgetPopoverBodyClassesgetPopoverHeaderClassesgetPricingCardBadgeClassesgetPricingCardDescriptionClassesgetPricingCardPriceClassesgetPricingCardPriceContainerClassesgetProgressBarClassesgetProgressLabelClassesgetRatingStarClassesgetRatingStarsClassesgetRatingTextClassesgetSidebarBackdropClassesgetSidebarGroupClassesgetSidebarGroupSummaryClassesgetSidebarHeaderClassesgetSidebarLinkClassesgetSidebarToggleClassesgetStepperIndicatorClassesgetStepperLabelClassesgetStepperStepClassesgetTableRowClassesgetTableWrapperClassesgetTabsItemClassesgetTabsListClassesgetTabsPanelClassesgetTestimonialAuthorClassesgetTestimonialAuthorInfoClassesgetTestimonialAuthorNameClassesgetTestimonialAuthorTitleClassesgetTestimonialQuoteClassesgetToastIconClasses
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
