@phcdevworks/spectre-ui is Layer 2 of the Spectre design suite. It turns
@phcdevworks/spectre-tokens
into reusable CSS bundles, Tailwind tooling, 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
@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, CSS entry points, and Tailwind exports |
| Source CSS | src/styles/ |
Token-backed CSS classes and bundle entry points |
| Source recipes | src/recipes/ |
Framework-agnostic class string APIs |
| Tailwind helpers | src/tailwind/ |
Tailwind theme and preset integration |
| 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.
┌─────────────────────────────┐
│ @phcdevworks/spectre-tokens │ Layer 1 — design values, semantic tokens
│ (design source of truth) │
└──────────────┬──────────────┘
│ consumed by
▼
┌─────────────────────────────┐
│ @phcdevworks/spectre-ui │ Layer 2 — THIS PACKAGE
│ CSS bundles, recipes, │ translates tokens into structure
│ Tailwind helpers │
└──────────────┬──────────────┘
│ consumed by
▼
┌─────────────────────────────┐
│ @phcdevworks/spectre- │ Layer 3 — canonical Lit web components
│ components │ wraps CSS classes and recipes as
│ │ framework-agnostic custom elements
└──────────────┬──────────────┘
│ alongside / consumed by
▼
┌─────────────────────────────┐
│ Adapters and apps │ Layer 4 — framework-specific delivery
│ spectre-ui-astro, React, │ binds Spectre contracts to native
│ Vue, WordPress, etc. │ framework ergonomics
└─────────────────────────────┘
This package owns Layer 2 only. It does not deliver components and it does not define tokens. It translates tokens into a stable contract that adapters and apps consume.
- Ships precompiled CSS:
index.css,base.css,components.css, andutilities.css - Provides Tailwind theme and preset helpers built from Spectre tokens
- 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
- 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/ - Tailwind preset and theme helpers in
src/tailwind/ - 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.
- 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.
npm install @phcdevworks/spectre-uiNo 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>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'Use Spectre tokens as the source of truth for your Tailwind theme:
// tailwind.config.ts
import type { Config } from 'tailwindcss'
import { createSpectreTailwindPreset } from '@phcdevworks/spectre-ui/tailwind'
import tokens from '@phcdevworks/spectre-tokens'
const config: Config = {
content: ['./src/**/*.{ts,tsx,js,jsx,html}'],
presets: [createSpectreTailwindPreset({ tokens })]
}
export default configClass 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 })Use @phcdevworks/spectre-ui when you need:
- precompiled, token-backed CSS ready to drop into any framework
- a Tailwind preset or theme helper built from Spectre tokens
- 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
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. - Use raw Tailwind utilities without a shared recipe contract — import Tailwind directly and use the Spectre preset; you do not need this package's recipe layer if you are building one-off UI with utility classes.
| 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/ |
| Tailwind preset and theme helpers | here — src/tailwind/ |
| 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.
All recipe functions accept a plain options object and return a class string. All options are optional and fall back to sensible defaults.
| Recipe | Function | Variants | Sizes | Common boolean flags |
|---|---|---|---|---|
| Button | getButtonClasses |
primary secondary ghost danger success cta accent |
sm md lg |
disabled loading fullWidth pill iconOnly |
| Badge | getBadgeClasses |
primary secondary success warning danger neutral info ghost accent cta |
sm md lg |
interactive disabled loading fullWidth |
| Card | getCardClasses |
elevated flat outline ghost |
— | interactive padded fullHeight disabled loading |
| 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 |
sm md lg |
interactive disabled loading pill fullWidth |
| PricingCard | getPricingCardClasses |
— | — | featured interactive disabled loading fullHeight |
| Rating | getRatingClasses |
— | sm md lg |
interactive disabled loading pill fullWidth |
| Testimonial | getTestimonialClasses |
elevated flat outline ghost |
— | interactive disabled loading fullHeight |
Each recipe family also exports sub-element helpers for its structural parts (labels, wrappers, sub-containers, text elements). See the full list below.
The root package exports CSS path constants plus the recipe functions
re-exported from src/recipes/index.ts.
Root constants:
spectreStylesspectreBaseStylesPathspectreComponentsStylesPathspectreIndexStylesPathspectreUtilitiesStylesPath
Root recipe functions:
getBadgeClassesgetButtonClassesgetCardClassesgetIconBoxClassesgetInputClassesgetPricingCardClassesgetRatingClassesgetTestimonialClasses
Root recipe helper functions:
getInputErrorMessageClassesgetInputHelperTextClassesgetInputLabelClassesgetInputWrapperClassesgetPricingCardBadgeClassesgetPricingCardDescriptionClassesgetPricingCardPriceClassesgetPricingCardPriceContainerClassesgetRatingStarClassesgetRatingStarsClassesgetRatingTextClassesgetTestimonialAuthorClassesgetTestimonialAuthorInfoClassesgetTestimonialAuthorNameClassesgetTestimonialAuthorTitleClassesgetTestimonialQuoteClasses
The root package also re-exports the related recipe option, variant, size, and state TypeScript types defined by those recipes.
@phcdevworks/spectre-ui/tailwind exports:
createSpectreTailwindPresetcreateSpectreTailwindTheme
@phcdevworks/spectre-ui/index.css@phcdevworks/spectre-ui/base.css@phcdevworks/spectre-ui/components.css@phcdevworks/spectre-ui/utilities.css
ui-contract.manifest.json defines the public styling contract for this
package.
It covers:
- CSS entry points
- root package constants and recipe function exports
- Tailwind subpath 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, when Tailwind artifacts drift, or when CSS contract coverage no longer matches the declared surface.
Downstream packages should never redefine locally:
- Spectre design token meaning
- CSS class semantics already provided by this package
- recipe option names, variants, sizes, or states
- package CSS entry point behavior
- Tailwind helper export names
Downstream packages may:
- compose application UI with the exported classes
- wrap recipe functions in framework-specific adapters
- import CSS entry points directly in applications or adapter packages
- extend app-specific layout around Spectre contracts
Consumers should treat this package as a SemVer-governed styling contract.
Practical guidance:
- additive recipes, variants, states, and helpers are intended to be safe for existing consumers
- semantic shifts may keep the same class or option name but still affect visual output
- renames, removals, and behavior changes to existing classes or options are breaking
- generated JS, TypeScript declarations, CSS bundles, Tailwind exports, README
docs, and
ui-contract.manifest.jsonare expected to stay aligned
If a downstream package depends on a class, recipe option, CSS entry point, or Tailwind helper:
- read
CHANGELOG.mdfor contract change classification - prefer documented public exports over internal paths
- run consuming app validation after package upgrades
Contract-affecting changes should be classified in CHANGELOG.md [Unreleased]
before release.
| Classification | When to use | Examples |
|---|---|---|
additive |
New public styling surface that does not break existing consumers | Adding a recipe helper, variant, state, or CSS entry point |
semantic change |
Public name remains but behavior or visual meaning shifts | Adjusting an existing class or recipe option to map to different token intent |
breaking |
Existing consumers may need code changes | Renaming or removing a class, option, export, or CSS entry point |
Renames and removals are always breaking regardless of perceived scope.
Spectre keeps responsibilities separate:
@phcdevworks/spectre-tokensdefines design values and semantic meaning@phcdevworks/spectre-uiturns those tokens into reusable CSS, Tailwind tooling, and type-safe class recipes@phcdevworks/spectre-componentsturns those styling contracts into framework-agnostic Lit web components- Adapters and apps consume
@phcdevworks/spectre-uiinstead of re-implementing its styling layer, or wrap Spectre component contracts for a specific runtime
That separation keeps recipe behavior consistent across frameworks and reduces implementation drift.
For downstream packages and compatible apps:
- import
@phcdevworks/spectre-ui/index.cssfor the full styling contract - import split CSS entry points only when the consumer needs bundle-level control
- use recipe functions when framework adapters need stable class strings
- use
@phcdevworks/spectre-ui/tailwindfor Tailwind theme integration - consume tokens from
@phcdevworks/spectre-tokensinstead of inventing visual values locally - treat
dist/as generated package output, not an authoring surface - do not add framework runtime logic to this package
git clone https://github.com/phcdevworks/spectre-ui.git
cd spectre-ui
nvm use # picks up .nvmrc (Node 22.22.2)
npm install
npm run ci:verifyThis project requires Node.js ^22.13.0 || >=24.0.0 and npm >=10.0.0.
The checked-in package manager is npm@11.15.0.
| Command | What it does |
|---|---|
npm run check |
Full validation gate — run before every PR |
npm run ci:verify |
Underlying verification sequence used by npm run check |
npm test |
Build then run the contract and regression test suite |
npm run build |
Emit TypeScript and CSS artifacts to dist/ |
npm run lint |
ESLint with TypeScript-aware config |
npm run validate:exports |
Verify root export surface against snapshot |
npm run validate:exports:update |
Update the export snapshot after adding a public export |
npm run validate:tailwind |
Verify Tailwind exports and emitted subpath artifacts |
npm run validate:tailwind:update |
Update the Tailwind export snapshot |
npm run validate:tokens |
Check for token drift against latest published release |
validate:runtime fails — you are on the wrong Node version. Run
nvm use to switch to the version in .nvmrc, or install Node 22 or 24.
validate:tokens fails with a network error — the check requires outbound
npm registry access. In a restricted environment, run the other validators
individually; this step is the only network-dependent one in ci:verify.
Tests pass but the build shows stale output — npm test rebuilds
automatically via the pretest hook. If you ran vitest directly, run
npm run build first.
Lint fails locally but passes in CI — confirm you are on the same Node version as CI (Node 22.x or 24.x). ESLint plugin resolution can differ across runtimes.
Export snapshot out of date — run npm run validate:exports:update after
adding a public export, then commit the updated scripts/export-snapshot.json.
src/styles/for source CSSsrc/recipes/for class recipessrc/tailwind/for Tailwind helperstests/for contract and regression coverageexamples/for visual demos and verification fixtures
Planning artifacts for contract hardening live in ROADMAP.md and TODO.md.
Use examples/examples.html as the visual index for
the package demos.
Available examples include:
vanilla.htmlfor the broad component showcaseshowroom.htmlfor a richer marketing-style compositionverification.htmland focused verification fixtures for regression checks
Run the full validation gate before any pull request:
npm run checkThis runs: runtime check → lint → export validation → README validation → token drift check → build → Tailwind contract → CSS contract → tests. All steps must pass.
Claude Code (claude-sonnet-4-6) is the primary development agent for this
repository. Codex handles releases and production stabilization. Jules handles
small automated fixes and token sync passes. GitHub Copilot provides
development support.
Claude Code, Codex, and Copilot do not create git commits by default. Jules may
commit only bounded automated maintenance when the JULES.md scope and
validation gates pass. Release decisions, tags, and publishing remain with
Bradley Potts.
Protected from automated change: CSS contracts, recipe public API surface, and the zero-hex policy (no hardcoded color/spacing values). See AGENTS.md for full agent governance and boundary rules.
PHCDevworks maintains this package as part of the Spectre suite.
When contributing:
- keep styling token-driven
- keep recipe APIs and CSS classes in sync
- avoid local visual values unless clearly intentional
- run
npm run checkbefore opening a pull request
See CONTRIBUTING.md for the full workflow.
MIT © PHCDevworks. See LICENSE.