Skip to content

Latest commit

 

History

301 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@phcdevworks/spectre-components

@phcdevworks/spectre-components is the web-component layer of the Spectre system. It provides accessible, framework-independent interface components built on Spectre's shared design contracts.

Maintained by PHCDevworks. It draws on Spectre's token and styling contracts to ship drop-in UI primitives, so applications that need working components — rather than raw CSS or recipes to assemble themselves — can consume Spectre without a framework-specific adapter.

Repository Snapshot

Field Value
Project team project-design
Repository role Spectre L3a Lit web component layer
Package/artifact @phcdevworks/spectre-components
Current version/status 1.21.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-components is the Layer 3 Lit-based web component package of the Spectre design system. It turns Spectre tokens (@phcdevworks/spectre-tokens) and Spectre UI styling contracts (@phcdevworks/spectre-ui) into reusable, accessible, framework-agnostic custom elements — the canonical component implementation layer for Spectre, designed to be consumed directly or wrapped by downstream adapter packages.

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

Why This Package Exists Alongside Spectre-UI

@phcdevworks/spectre-ui owns CSS: class recipes, Tailwind helpers, and the styling contract that maps Spectre tokens to visual output. It ships CSS rules and JavaScript class-name helpers — nothing more.

This package sits above that. It owns behavior: the Lit element classes that apply those CSS recipes, forward ARIA attributes to native elements, manage focus delegation, handle content projection, validate properties, and expose a stable TypeScript API surface for downstream adapters.

The separation keeps each layer focused:

Layer Package Owns
L1 @phcdevworks/spectre-tokens Design values and semantic meaning
L2 @phcdevworks/spectre-ui CSS recipes and styling contracts
L3 @phcdevworks/spectre-components Lit web component behavior and API
L4 Downstream adapters Framework-specific delivery

If you only need CSS class names, use @phcdevworks/spectre-ui directly. If you need ready-to-use HTML elements with behavior, accessibility, and a typed API, use this package.

Key Capabilities

  • Lit-based custom elements on the Custom Elements standard
  • Renders in light DOM so @phcdevworks/spectre-ui global styles apply directly — no Shadow DOM piercing required
  • ARIA attributes (aria-label, aria-labelledby, aria-describedby) are forwarded to the native element, not left on the host
  • Focus and blur delegate to the inner native element
  • Property validation with safe fallbacks in willUpdate()
  • Idempotent defineSpectre*() helpers — safe to call multiple times
  • ESM + CJS dual build with TypeScript declaration files
  • Tree-shakeable subpath exports per component

When To Use This Package

  • You are building UI with the Spectre design system and want standards-based custom elements with baked-in behavior and accessibility.
  • You want typed form controls (sp-button, sp-input, sp-select, etc.) and display primitives (sp-badge, sp-card, sp-rating, etc.) that work in any framework or in plain HTML.
  • You are writing a framework adapter (React, Vue, Astro) and need a reliable, stable element layer to wrap.

When Not To Use This Package

  • You only need CSS class names — use @phcdevworks/spectre-ui directly.
  • You are adding routing, shell logic, or app-startup orchestration — those are out of scope here.
  • You need framework-specific component files (JSX, SFCs, Astro components) — those belong in a downstream adapter package.

Installation

npm install @phcdevworks/spectre-components @phcdevworks/spectre-ui @phcdevworks/spectre-tokens

Quick Start

Plain HTML

Import the CSS layers and register all components from a script tag or entry module. These are standard custom elements — no build step required for consumption.

<!doctype html>
<html lang="en">
  <head>
    <!-- Spectre CSS layers must load before any markup is rendered -->
    <link
      rel="stylesheet"
      href="/node_modules/@phcdevworks/spectre-tokens/index.css"
    />
    <link
      rel="stylesheet"
      href="/node_modules/@phcdevworks/spectre-ui/index.css"
    />
  </head>
  <body>
    <sp-label for="email">Email address</sp-label>
    <sp-input
      id="email"
      name="email"
      type="email"
      placeholder="you@example.com"
    ></sp-input>

    <sp-button variant="primary" type="submit">Send</sp-button>
    <sp-button variant="ghost" type="button">Cancel</sp-button>

    <script type="module">
      import { defineSpectreComponents } from '/node_modules/@phcdevworks/spectre-components/dist/index.js'
      defineSpectreComponents()
    </script>
  </body>
</html>

JavaScript / TypeScript module

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

// Register everything at once
import { defineSpectreComponents } from '@phcdevworks/spectre-components'
defineSpectreComponents()

// Or register only what you use
import { defineSpectreButton } from '@phcdevworks/spectre-components/button'
import { defineSpectreInput } from '@phcdevworks/spectre-components/input'
defineSpectreButton()
defineSpectreInput()

Full form example

<sp-fieldset legend="Contact preferences">
  <sp-label for="email">Email address</sp-label>
  <sp-input id="email" name="email" type="email" required></sp-input>

  <sp-label for="bio">Bio</sp-label>
  <sp-textarea id="bio" name="bio" rows="4" maxlength="500"></sp-textarea>

  <sp-label for="role">Role</sp-label>
  <sp-select id="role" name="role">
    <option value="admin">Admin</option>
    <option value="user">User</option>
  </sp-select>

  <sp-checkbox name="terms" value="accepted" required>
    I accept the <a href="/terms">terms of service</a>
  </sp-checkbox>

  <sp-radio name="plan" value="monthly">Monthly billing</sp-radio>
  <sp-radio name="plan" value="annual">Annual billing</sp-radio>

  <sp-button variant="primary" type="submit">Save</sp-button>
  <sp-button variant="ghost" type="reset">Reset</sp-button>
</sp-fieldset>

Framework integration note

These are standard HTML custom elements. They work in every major framework that supports the Custom Elements standard:

React 19+ — supports custom element properties and events natively:

// React 19: properties and events work directly
<sp-input name="email" type="email" onInput={(e) => setValue(e.target.value)} />

React 18 and below — set attributes via ref for properties, listen for native events on the element:

const inputRef = useRef(null)
useEffect(() => {
  if (inputRef.current) inputRef.current.invalid = true
}, [])

return <sp-input ref={inputRef} name="email" />

Vue 3 — supports custom elements out of the box with v-bind and v-on directive compatibility. Mark the sp-* prefix in compilerOptions as a custom element to suppress unknown-element warnings:

// vite.config.ts
plugins: [
  vue({
    template: {
      compilerOptions: { isCustomElement: (tag) => tag.startsWith('sp-') }
    }
  })
]
<sp-input name="email" :invalid="hasError" @change="handleChange" />

Astro — use components as static custom elements or with client:load when JavaScript interactivity is needed:

---
import '@phcdevworks/spectre-tokens/index.css';
import '@phcdevworks/spectre-ui/index.css';
---
<script>
  import { defineSpectreComponents } from '@phcdevworks/spectre-components';
  defineSpectreComponents();
</script>
<sp-button variant="primary">Click me</sp-button>

Framework adapter packages that wrap these components into idiomatic JSX or SFC APIs belong in a downstream adapter — not in this package.

Accessibility

All components follow WCAG 2.1 AA baseline expectations by default.

ARIA attribute forwarding — aria-label, aria-labelledby, and aria-describedby set on the host element are automatically forwarded to the inner native element so screen readers receive them on the correct target.

Native element semantics — every component renders a real native element (<button>, <input>, <textarea>, <select>, <label>, <fieldset>) or a semantic light-DOM container with forwarded ARIA attributes, so browser accessibility APIs work without customization.

State communication

State ARIA effect
loading aria-busy="true" on the native element
invalid aria-invalid="true" on the native element
disabled native disabled attribute (removes from tab order)
required native required attribute

Focus delegation — .focus() and .blur() called on the host are delegated to the inner native element so external focus() calls work as expected.

Label association — use <sp-label for="id"> paired with id on the target control, or wrap controls inside a <sp-fieldset>. The for attribute forwards to the native <label> element.

Keyboard behavior — provided entirely by the native element inside each component. No custom keyboard handling is layered on top.

Light DOM Rendering

All components render in light DOM (createRenderRoot() { return this; }). This is intentional: it allows @phcdevworks/spectre-ui global CSS to reach the native element directly without Shadow DOM piercing.

As a result, these components have no ::part() exports — the native element is directly selectable using standard CSS combinators or the stable internal data attributes:

/* Target the native input inside sp-input */
sp-input input {
  font-size: 0.875rem;
}

/* Stable internal hook — won't break if markup restructures */
sp-input [data-sp-input-native] {
  font-size: 0.875rem;
}

Do not switch any component from light DOM to Shadow DOM without a design-system-level decision.

Components

sp-button

Renders a <button> with Spectre variant, size, loading, and pill support. Set href to render a native <a> instead, styled with the same classes — useful when the button needs to navigate rather than submit/act.

Attributes

Attribute Type Default Description
variant primary | secondary | ghost | danger | success | cta | accent | inverse primary Visual style
size sm | md | lg md Control size
type button | submit | reset button Native button type (ignored when rendered as a link)
href string — Renders <a href> instead of <button> (unless disabled/loading)
target _blank | _self | _parent | _top — Forwarded to the native <a> when href is set
rel string — Forwarded to the native <a> when href is set
label string — Text label (overridden by content projection)
loading boolean false Busy state — disables the button/link and shows loading label
loading-label string Loading Accessible text shown during loading
disabled boolean false Disables the button; if href is also set, still renders <button disabled>
full-width boolean false Spans full container width
pill boolean false Pill / fully-rounded corners
compact boolean false Denser padding/height variant
inner-class string — Spectre utility classes applied to the native <button>/<a>
name string — Form field name
value string '' Submitted value
form string — Associates with a form by ID
autofocus boolean false Autofocus on page load
id string — Forwarded to the native element
title string — Forwarded to the native element
aria-label string — Forwarded to the native element
aria-labelledby string — Forwarded to the native element
aria-describedby string — Forwarded to the native element

Events — native button/link events bubble normally (click, focus, blur).

Content projection — place children inside <sp-button> to use them as button content instead of the label property:

<sp-button variant="primary">
  <svg aria-hidden="true">...</svg>
  Save changes
</sp-button>

Link mode:

<sp-button variant="secondary" href="/pricing" target="_blank" rel="noopener">
  View pricing
</sp-button>

Internal target — [data-sp-button-native] selects the native <button> or <a>.


sp-input

Renders an <input> with state, size, and type support.

Attributes

Attribute Type Default Description
type text | email | password | search | tel | url | number | date | datetime-local | month | time | week text Native input type
size sm | md | lg md Control size
name string — Form field name
value string '' Current value
placeholder string — Placeholder text
disabled boolean false Disables the input
loading boolean false Busy state
readonly boolean false Read-only mode
required boolean false Marks field as required
invalid boolean false Error state (aria-invalid)
success boolean false Success state
full-width boolean false Spans full container width
pill boolean false Pill / fully-rounded corners
autocomplete string — Native autocomplete hint
inputmode string — Virtual keyboard hint
min / max / step string — Numeric/date range
minlength / maxlength number — Character length constraints
form string — Associates with a form by ID
autofocus boolean false Autofocus on page load
id / title / aria-* string — Forwarded to native <input>

Events — input and change fire from the native <input> and bubble.

Internal target — [data-sp-input-native] selects the native <input>.


sp-textarea

Renders a <textarea> with row control and resize support.

Attributes — same as sp-input except no type, min, max, step, and adds:

Attribute Type Default Description
rows number 2 Visible row height

Events — input and change fire from the native <textarea>.

Internal target — [data-sp-textarea-native] selects the native <textarea>.


sp-select

Renders a <select>. Pass <option> elements as children — they are projected into the native select element.

Attributes — same as sp-input minus type, placeholder, readonly, inputmode, min, max, step, minlength, maxlength.

Events — input and change fire from the native <select>.

Content projection — <option> and <optgroup> children are moved into the native <select>:

<sp-select name="country" required>
  <option value="">Select a country</option>
  <optgroup label="Americas">
    <option value="us">United States</option>
    <option value="ca">Canada</option>
  </optgroup>
</sp-select>

Internal target — [data-sp-select-native] selects the native <select>.


sp-checkbox

Renders a <label> wrapping an <input type="checkbox"> with indicator.

Attributes

Attribute Type Default Description
name string — Form field name
value string on Submitted value when checked
checked boolean false Checked state
label string — Text label (overridden by content projection)
disabled boolean false Disables the checkbox
loading boolean false Busy state
required boolean false Marks field as required
invalid boolean false Error state
success boolean false Success state
form / autofocus / id / title / aria-* — — Forwarded to native <input>

Events — input and change fire from the native checkbox input.

Content projection — children become the label content (supports rich markup):

<sp-checkbox name="terms" value="accepted" required>
  I accept the <a href="/terms">terms of service</a>
</sp-checkbox>

Internal target — [data-sp-checkbox-native] selects the native checkbox.


sp-radio

Renders a <label> wrapping an <input type="radio"> with indicator. Group multiple sp-radio elements by giving them the same name.

Attributes — same as sp-checkbox. value defaults to on.

Events — input and change fire from the native radio input.

Content projection — same as sp-checkbox.

<sp-radio name="plan" value="monthly">Monthly — $9/mo</sp-radio>
<sp-radio name="plan" value="annual">Annual — $90/yr</sp-radio>

Internal target — [data-sp-radio-native] selects the native radio input.


sp-label

Renders a <label> with for forwarding. Use to associate a visible label with any form control.

Attributes

Attribute Type Default Description
for string — ID of the associated control (forwarded to native <label>)
id / title / aria-* string — Forwarded to native <label>

Content projection — children become the label text (supports rich markup):

<sp-label for="email">
  Email address <span aria-hidden="true">*</span>
</sp-label>

Internal target — [data-sp-label-native] selects the native <label>.


sp-fieldset

Renders a <fieldset> with optional legend and group-level state.

Attributes

Attribute Type Default Description
legend string — Text for the <legend> element
disabled boolean false Disables all controls in the group
loading boolean false Busy state
invalid boolean false Group-level error state
success boolean false Group-level success state
form / name / id / title / aria-* string — Forwarded to native <fieldset>

Content projection — children are placed inside the native <fieldset> alongside the legend:

<sp-fieldset legend="Billing address" name="billing">
  <sp-label for="city">City</sp-label>
  <sp-input id="city" name="city" required></sp-input>

  <sp-label for="zip">ZIP code</sp-label>
  <sp-input id="zip" name="zip" type="text" maxlength="10"></sp-input>
</sp-fieldset>

Internal target — [data-sp-fieldset-native] selects the native <fieldset>.


sp-badge

Renders a <span> display primitive backed by the Spectre badge recipe.

Attributes

Attribute Type Default Description
variant primary | secondary | ghost | danger | success | warning | info | accent | cta | neutral | outline | inverse primary Visual style
size sm | md | lg md Badge size
accent-rail top | right | bottom | left — Optional decorative edge-rail; omitted renders no rail. Distinct from variant: 'accent', an unrelated single-tone fill
accent-rail-color neutral | brand | info | success | warning | danger | cta brand Accent rail color; only applied when accent-rail is set
disabled boolean false Disabled visual state
loading boolean false Busy visual state
full-width boolean false Spans full container width
inner-class string — Spectre utility classes applied to the native <span>
id / title / aria-* string — Forwarded to the native <span>

Content projection — children become the badge content.

Internal target — [data-sp-badge-native] selects the native <span>.


sp-card

Renders a <div> container backed by the Spectre card recipe.

Attributes

Attribute Type Default Description
variant elevated | flat | outline | ghost elevated Visual style
padded boolean | 'sm' | 'md' | 'lg' true Card padding step; false opts out, true/"md" is default
accent top | right | bottom | left — Optional decorative edge-rail; omitted renders no rail
accent-color neutral | brand | info | success | warning | danger | cta brand Accent rail color; only applied when accent is set
full-height boolean false Spans full container height
interactive boolean false Applies interactive styling
disabled boolean false Disabled visual state
loading boolean false Busy visual state
inner-class string — Spectre utility classes applied to the native <div>
id / title / aria-* string — Forwarded to the native <div>

Content projection — children become the card content.

Internal target — [data-sp-card-native] selects the native <div>.


sp-icon-box

Renders a <div> icon container backed by the Spectre icon-box recipe.

Attributes

Attribute Type Default Description
variant primary | secondary | ghost | danger | success | warning | info | accent | cta | neutral | outline primary Visual style
size sm | md | lg md Icon-box size
disabled boolean false Disabled visual state
loading boolean false Busy visual state
interactive boolean false Applies interactive styling
pill boolean false Pill / fully-rounded corners
full-width boolean false Spans full container width
id / title / aria-* string — Forwarded to the native <div>

Content projection — children become the icon-box content.

Internal target — [data-sp-icon-box-native] selects the native <div>.


sp-rating

Renders a read-only rating visualization with generated star spans.

Attributes

Attribute Type Default Description
value number 0 Filled star count
max number 5 Total star count
size sm | md | lg md Rating size
label string — Optional visible text beside the stars
disabled boolean false Disabled visual state
loading boolean false Busy visual state
id / title / aria-* string — Forwarded to the rating container

Accessibility — renders role="img" and computes an accessible label like Rating: 4 out of 5 unless aria-label is provided.

Internal target — [data-sp-rating-native] selects the rating container.


sp-testimonial

Renders a <div> testimonial container backed by the Spectre testimonial recipe.

Attributes

Attribute Type Default Description
variant elevated | flat | outline | ghost elevated Visual style
accent top | right | bottom | left — Optional decorative edge-rail; omitted renders no rail
accent-color neutral | brand | info | success | warning | danger | cta brand Accent rail color; only applied when accent is set
full-height boolean false Spans full container height
interactive boolean false Applies interactive styling
disabled boolean false Disabled visual state
loading boolean false Busy visual state
id / title / aria-* string — Forwarded to the native <div>

Content projection — children become the testimonial content.

Internal target — [data-sp-testimonial-native] selects the native <div>.


sp-alert

Renders a <div role="alert"> display primitive backed by the Spectre alert recipe.

Attributes

Attribute Type Default Description
variant info | success | warning | danger | neutral | brand info Visual style
size sm | md | lg md Alert size
dismissible boolean false Renders a close button that dismisses the alert
dismiss-label string Dismiss Accessible name of the close button
dismissed boolean false Dismissed (hidden) state
disabled boolean false Disabled visual state
loading boolean false Busy visual state
full-width boolean false Spans full container width
id / title / aria-* string — Forwarded to the native <div>

Content projection — an element with slot="icon" renders in the leading icon slot, colored by the variant; all other children become the alert content.

Accessibility — renders role="alert" and reflects the loading state to aria-busy.

Events — sp-dismiss (bubbling) when the close button dismisses the alert.

Internal targets — [data-sp-alert-native] selects the native <div>; [data-sp-alert-dismiss] selects the close button.


sp-avatar

Renders a <div> avatar container backed by the Spectre avatar recipe.

Attributes

Attribute Type Default Description
size xs | sm | md | lg | xl md Avatar size
shape circle | square circle Avatar shape
interactive boolean false Applies interactive styling
disabled boolean false Disabled visual state
loading boolean false Busy visual state
full-width boolean false Spans full container width
placeholder boolean false Placeholder background and color
id / title / aria-* string — Forwarded to the native <div>

Content projection — children become the avatar content (an <img>, initials, or an icon).

Accessibility — reflects the loading state to aria-busy.

Internal target — [data-sp-avatar-native] selects the native <div>.


sp-spinner

Renders a <div role="status"> loading indicator backed by the Spectre spinner recipe.

Attributes

Attribute Type Default Description
variant primary | secondary | success | warning | danger | info | neutral | accent | cta — Arc color
size sm | md | lg md Spinner size
disabled boolean false Disabled visual state
loading boolean true Busy visual state
id / title / aria-* string — Forwarded to the native <div>

Accessibility — renders role="status" and reflects the loading state to aria-busy. Defaults aria-label to Loading unless aria-label is provided.

Internal target — [data-sp-spinner-native] selects the native <div>.


sp-tag

Renders a <span> tag/chip backed by the Spectre tag recipe.

Attributes

Attribute Type Default Description
variant default | primary | secondary | success | warning | danger | info | neutral | accent | cta | outline | ghost default Tag color
size sm | md | lg md Tag size
interactive boolean false Applies interactive styling
selected boolean false Selected/active visual state
dismissible boolean false Reserves space for a dismiss icon
disabled boolean false Disabled visual state
loading boolean false Busy visual state
full-width boolean false Spans full container width
id / title / aria-* string — Forwarded to the native <span>

Content projection — children become the tag label (and any projected dismiss icon).

Accessibility — reflects the loading state to aria-busy.

Internal target — [data-sp-tag-native] selects the native <span>.


sp-pricing-card

Renders a <div> pricing card container backed by the Spectre pricing-card recipe.

Attributes

Attribute Type Default Description
featured boolean false Highlights the card as featured
accent top | right | bottom | left — Optional decorative edge-rail; omitted renders no rail
accent-color neutral | brand | info | success | warning | danger | cta brand Accent rail color; only applied when accent is set
interactive boolean false Applies interactive styling
disabled boolean false Disabled visual state
loading boolean false Busy visual state
full-height boolean false Spans full container height
id / title / aria-* string — Forwarded to the native <div>

Content projection — children become the pricing card content (heading, price, feature list, call-to-action, etc.).

Accessibility — reflects the loading state to aria-busy.

Internal target — [data-sp-pricing-card-native] selects the native <div>.


Layout components

sp-container, sp-grid, sp-section, sp-stack, sp-footer, and sp-nav share two contracts:

  • Host display — the host element defaults to display: block (set via inline style in connectedCallback, so a consumer's own style="display: ..." always wins) instead of the browser's default inline custom-element box. This keeps backgrounds, margins, and full-width inner content from being trapped inside an inline box.
  • inner-class — an inner-class attribute (innerClass JS property) appends consumer-supplied Spectre utility classes to the native inner element the component's recipe classes render on, without touching the host's own class attribute. Host class and inner-class are distinct targets: host class affects the custom-element box itself, inner-class affects the styled element inside it. Only tokens matching sp-* (Spectre utility class syntax) are applied; anything else is silently dropped.
<sp-stack class="my-host-hook" inner-class="sp-bg-primary-500 sp-p-8">
  ...
</sp-stack>

sp-container

Renders a <div> layout container backed by the Spectre container recipe.

Attributes

Attribute Type Default Description
max-width prose — Constrains content to a max width
inner-class string — Spectre utility classes applied to the native <div>
id / title / aria-* string — Forwarded to the native <div>

Content projection — children become the container content.

Internal target — [data-sp-container-native] selects the native <div>.


sp-grid

Renders a <div> grid layout backed by the Spectre grid recipe.

Attributes

Attribute Type Default Description
columns 1 | 2 | 3 | 4 | 6 | 12 1 Number of grid columns
gap sm | md | lg md Gap between grid items
align start | center | end | baseline | stretch — Cross-axis alignment of grid items
span 1-12 | 'full' or { base?, md?, lg? } (JS property only) — Column span for a grid item, single value or per-breakpoint
column-gap sm | md | lg (JS: columnGap) — Overrides gap on the column axis only
row-gap sm | md | lg (JS: rowGap) — Overrides gap on the row axis only
offset 0-11 or { base?, md?, lg? } (JS property only) — Column offset for a grid item, single value or per-breakpoint
row-span 1-12 | 'full' or { base?, md?, lg? } (JS: rowSpan) — Row span for a grid item, single value or per-breakpoint
row-offset 0-11 or { base?, md?, lg? } (JS: rowOffset) — Row offset for a grid item, single value or per-breakpoint
order 'first' | 'last' | 'none' | 1-12 or { base?, md?, lg? } (JS property only) — Reorders a grid item independent of source order
leading-tracks { weight: 1.5|1.6|2|2.5|3 | { base?, md?, lg? } } (JS: leadingTracks) — One wider leading column plus columns - 1 equal columns
fixed-tracks { count: 1|2|3|4 } (JS: fixedTracks) — Fixed-width repeated tracks (--sp-space-240), replaces columns
explicit-template { template: 'edge-fluid-edge'|'label-fluid-fluid', weight? } (JS: explicitTemplate) — Named asymmetric column shape; replaces columns/leadingTracks/fixedTracks
inner-class string — Spectre utility classes applied to the native <div>
id / title / aria-* / role string — Forwarded to the native <div>

leading-tracks, fixed-tracks, explicit-template, and any per-breakpoint { base?, md?, lg? } shape are JS-property-only (set via the DOM property, not an HTML attribute string).

Setting role (e.g. role="table") reflects it directly onto the native <div>. sp-grid renders its light-DOM children into that single container, so a table-shaped role structure (role="row"/role="cell" on children) is the consumer's responsibility — sp-grid does not synthesize row/cell roles for projected content.

Content projection — children become grid items.

Internal target — [data-sp-grid-native] selects the native <div>.


sp-section

Renders a <section> layout wrapper backed by the Spectre section recipe.

Attributes

Attribute Type Default Description
inner-class string — Spectre utility classes applied to the native <section>
id / title / aria-* string — Forwarded to the native <section>

Content projection — children become the section content.

Internal target — [data-sp-section-native] selects the native <section>.


sp-stack

Renders a <div> flex stack backed by the Spectre stack recipe.

Attributes

Attribute Type Default Description
direction vertical | horizontal vertical Stack axis
basis sidebar — Reserves sidebar-sized basis on items
align center | stretch center Cross-axis alignment
gap sm | md | lg md Gap between stack items
inner-class string — Spectre utility classes applied to the native <div>
id / title / aria-* string — Forwarded to the native <div>

Content projection — children become stack items.

Internal target — [data-sp-stack-native] selects the native <div>.


sp-footer

Renders a <footer> backed by the Spectre footer recipe.

Attributes

Attribute Type Default Description
bordered boolean false Adds a top border
accent top | right | bottom | left — Optional decorative edge-rail; omitted renders no rail
accent-color neutral | brand | info | success | warning | danger | cta brand Accent rail color; only applied when accent is set
full-width boolean false Spans full container width
inner-class string — Spectre utility classes applied to the native <footer>
id / title / aria-* string — Forwarded to the native <footer>

Content projection — children become the footer content (links, legal text, etc.).

Internal target — [data-sp-footer-native] selects the native <footer>.


sp-footer-link

Renders an <a> link styled for sp-footer content, backed by the Spectre footer link recipe.

Attributes

Attribute Type Default Description
href string — Link target (dropped when disabled)
active boolean false Marks the link as the current page (aria-current="page")
disabled boolean false Disables the link (aria-disabled, tabindex="-1", no href)
id / title / aria-* string — Forwarded to the native <a>

Content projection — children become the link text.

Internal target — [data-sp-footer-link-native] selects the native <a>.

<sp-footer>
  <sp-footer-link href="/privacy">Privacy</sp-footer-link>
  <sp-footer-link href="/terms" active>Terms</sp-footer-link>
</sp-footer>

sp-footer-chip

Renders a <span> chip styled for sp-footer content (e.g. a version or status badge), backed by the Spectre footer chip recipe.

Attributes

Attribute Type Default Description
disabled boolean false Disabled visual state (aria-disabled)
id / title / aria-* string — Forwarded to the native <span>

Content projection — children become the chip text.

Internal target — [data-sp-footer-chip-native] selects the native <span>.


sp-nav

Renders a <nav> backed by the Spectre nav recipe.

Attributes

Attribute Type Default Description
bordered boolean false Adds a bottom border
accent top | right | bottom | left — Optional decorative edge-rail; omitted renders no rail
accent-color neutral | brand | info | success | warning | danger | cta brand Accent rail color; only applied when accent is set
sticky boolean false Sticks the nav to the viewport
full-width boolean false Spans full container width
align start | center | end — Horizontal alignment of nav content within the bar
inner-class string — Spectre utility classes applied to the native <nav>
id / title / aria-* string — Forwarded to the native <nav>

Content projection — children become the nav content (links, brand mark, etc.).

Internal target — [data-sp-nav-native] selects the native <nav>.


sp-nav-item

Renders a nav link that can optionally become a dropdown trigger, backed by the same Spectre dropdown recipe as sp-dropdown. Place it inside sp-nav alongside plain <a> links.

Attributes

Attribute Type Default Description
dropdown boolean false Renders a dropdown trigger + menu instead of a plain <a>
href string — Link target when dropdown is false
label string — Trigger/link text when no content is projected
mega boolean false Anchors the menu to the nearest positioned ancestor instead of the trigger, spanning its full width (dropdown mode only)
open boolean false Open/closed menu state (dropdown mode only)
placement string 'bottom-start' Menu position: bottom-start, bottom-end, top-start, top-end
id / title / aria-* string — Forwarded to the rendered <a> or trigger <button>

Content projection — in dropdown mode, children become the menu content; a child with slot="trigger" becomes the trigger content instead of label. Menu content can be a plain list of links or an sp-grid for a full mega-menu layout. In link mode, children become the link's content.

Events — sp-open and sp-close (dropdown mode only), both bubbles.

Internal targets — [data-sp-nav-item-trigger] selects the trigger button; [data-sp-nav-item-menu] selects the menu panel.

<sp-nav>
  <sp-nav-item href="/">Home</sp-nav-item>
  <sp-nav-item dropdown label="Products">
    <sp-grid columns="3" gap="lg">
      <div>Column one</div>
      <div>Column two</div>
      <div>Column three</div>
    </sp-grid>
  </sp-nav-item>
  <sp-nav-item dropdown mega label="Solutions">
    <sp-grid columns="4" gap="lg">
      <div>Column one</div>
      <div>Column two</div>
      <div>Column three</div>
      <div>Column four</div>
    </sp-grid>
  </sp-nav-item>
</sp-nav>

mega requires a positioned ancestor to anchor against — sp-nav is a positioning context by default, so nesting inside it is sufficient.


sp-sidebar

Renders an off-canvas <aside> with a toggle button and backdrop, backed by the Spectre sidebar recipe.

Attributes

Attribute Type Default Description
bordered boolean false Adds a trailing border
hide-toggle boolean false Suppresses the built-in toggle button (e.g. when using sp-sidebar-toggle elsewhere)
open boolean false Open/closed off-canvas state
toggle-label string Toggle sidebar Accessible label for the built-in toggle button
id / title / aria-* string — Forwarded to the native <aside>

Content projection — children become the sidebar content (links, navigation groups, etc.).

Behavior — clicking the toggle button opens/closes the sidebar; clicking the backdrop or pressing Esc closes it. Toggling sets data-sidebar-open ("true"/"false") on the host element, which the Spectre CSS uses to show or hide the off-canvas panel and backdrop.

Events — sp-open and sp-close, both bubbling CustomEvents with no detail.

Internal target — [data-sp-sidebar-native] selects the native <aside>.


sp-sidebar-link

Renders an <a> link styled for sp-sidebar navigation content, backed by the Spectre sidebar link recipe.

Attributes

Attribute Type Default Description
href string — Link target (dropped when disabled)
active boolean false Marks the link as the current page (aria-current="page")
disabled boolean false Disables the link (aria-disabled, tabindex="-1", no href)
level parent | child parent Indents/styles the link as a nested (child) sidebar link
id / title / aria-* string — Forwarded to the native <a>

Content projection — children become the link text.

Internal target — [data-sp-sidebar-link-native] selects the native <a>.

<sp-sidebar>
  <sp-sidebar-link href="/dashboard" active>Dashboard</sp-sidebar-link>
  <sp-sidebar-link href="/settings">Settings</sp-sidebar-link>
  <sp-sidebar-link href="/settings/billing" level="child"
    >Billing</sp-sidebar-link
  >
</sp-sidebar>

sp-sidebar-toggle

Renders a standalone trigger button that opens/closes a remote sp-sidebar, backed by the Spectre sidebar-toggle recipe. Use it to place a toggle somewhere other than inside the sidebar itself (a header, a nav bar) — pair it with hide-toggle on the target sp-sidebar to suppress that sidebar's own built-in toggle, or leave both in place to control the same sidebar from two triggers.

Attributes

Attribute Type Default Description
for string — id of the target sp-sidebar's native content (the id set on the sp-sidebar element itself)
label string Toggle sidebar Accessible label when no icon content is projected
id / title / aria-* string — Forwarded to the native <button>

Content projection — children become the button's icon content; falls back to a default ☰ glyph when empty.

Behavior — clicking the button toggles the target sp-sidebar's open property and mirrors its own aria-expanded state, including staying in sync when the target is opened/closed by another trigger (its own built-in toggle, the backdrop, Esc, or a second sp-sidebar-toggle).

<sp-nav>
  <sp-sidebar-toggle for="app-sidebar"></sp-sidebar-toggle>
</sp-nav>

<sp-sidebar id="app-sidebar" hide-toggle>
  <a href="/dashboard">Dashboard</a>
</sp-sidebar>

Internal target — [data-sp-sidebar-toggle-native] selects the native <button>.


sp-dropdown

Renders a trigger button and a menu container, backed by the Spectre dropdown recipes.

Attributes

Attribute Type Default Description
open boolean false Open/closed menu state
placement bottom-start | bottom-end | top-start | top-end bottom-start Menu position relative to trigger
accent top | right | bottom | left — Optional decorative edge-rail on the menu; omitted renders no rail
accent-color neutral | brand | info | success | warning | danger | cta brand Accent rail color; only applied when accent is set
full-width boolean false Spans full container width
mega boolean false Anchors the menu to the nearest positioned ancestor instead of the trigger, spanning its full width
viewport boolean false Breaks the menu out to the full browser viewport width; takes precedence over mega if both are set
trigger-label string Toggle menu Visible/accessible trigger text when no slot="trigger" content is projected
id / title / aria-* string — Forwarded to the trigger button

Content projection — an element with slot="trigger" becomes the trigger button content; all other children become the menu content.

Behavior — clicking the trigger toggles the menu; clicking outside the component or pressing Esc closes it and returns focus to the trigger.

Events — sp-open and sp-close, both bubbling CustomEvents with no detail.

Internal targets — [data-sp-dropdown-trigger] selects the trigger button, [data-sp-dropdown-menu] selects the menu container.


sp-modal

Renders a full-screen overlay and dialog, backed by the Spectre modal recipes.

Attributes

Attribute Type Default Description
open boolean false Open/closed dialog state
accent top | right | bottom | left — Optional decorative edge-rail; omitted renders no rail
accent-color neutral | brand | info | success | warning | danger | cta brand Accent rail color; only applied when accent is set
full-width boolean false Spans full container width
id / title / aria-* string — Forwarded to the native dialog element

Content projection — children become the dialog content.

Behavior — traps Tab/Shift+Tab focus within the dialog while open, closes on Esc or a backdrop click, focuses the first focusable element on open, and restores focus to the previously focused element on close.

Events — sp-close, a bubbling CustomEvent with no detail.

Internal target — [data-sp-modal-native] selects the native dialog element; [data-sp-modal-overlay] selects the overlay backdrop.


sp-toast

Renders a <div role="status"> notification, backed by the Spectre toast recipes, with an imperative show/dismiss API.

Attributes

Attribute Type Default Description
variant info | success | warning | danger info Visual style
accent top | right | bottom | left — Optional decorative edge-rail; omitted renders no rail
accent-color neutral | brand | info | success | warning | danger | cta brand Accent rail color; only applied when accent is set
dismissed boolean false Dismissed visual state
full-width boolean false Spans full container width
auto-dismiss number — Milliseconds before auto-dismissing
id / title / aria-* string — Forwarded to the native <div>

Content projection — an element with slot="icon" becomes the toast icon; all other children become the toast body content.

Methods — show() and dismiss() toggle the dismissed state imperatively.

Events — sp-show and sp-dismiss, both bubbling CustomEvents with no detail.

Accessibility — renders role="status" with aria-live="polite" and aria-atomic="true".

Internal target — [data-sp-toast-native] selects the native <div>.


sp-tooltip

Renders a trigger wrapper and a role="tooltip" body, backed by the Spectre tooltip recipe.

Attributes

Attribute Type Default Description
placement top | bottom | left | right top Tooltip position relative to trigger
accent top | right | bottom | left — Optional decorative edge-rail; omitted renders no rail
accent-color neutral | brand | info | success | warning | danger | cta brand Accent rail color; only applied when accent is set
visible boolean false Visible/hidden tooltip state
id / title / aria-* string — Forwarded to the tooltip body

Content projection — an element with slot="tooltip" becomes the tooltip body; all other children become the trigger content.

Behavior — becomes visible on trigger mouseenter/focusin and hides on mouseleave/focusout.

Events — sp-show and sp-hide, both bubbling CustomEvents with no detail.

Internal target — [data-sp-tooltip-native] selects the native tooltip body.


sp-text

Renders a text primitive backed by the Spectre text recipe. The rendered tag switches with level while the recipe call and styling stay the same.

Attributes

Attribute Type Default Description
level h1 | h2 | h3 | h4 | h5 | h6 | p | span p Rendered element tag
size xs | sm | md | lg | xl | 2xl | 3xl | 4xl | 5xl | 6xl md Text size scale
variant default | muted | subtle | meta | brand | onInverse | onInverseMuted default Text color role
family sans | serif | mono — Optional font family override
transform none | uppercase | lowercase | capitalize — Optional text transform
id / title / aria-* string — Forwarded to the rendered native element

Content projection — children become the text content.

Internal target — [data-sp-text-native] selects the rendered native element.


sp-tabs / sp-tab-panel

Renders an ARIA tablist from its sp-tab-panel children, backed by the Spectre tabs recipes. Each panel supplies its tab's label; sp-tabs renders the tab buttons and owns selection.

<sp-tabs aria-label="Account settings">
  <sp-tab-panel label="Profile">Profile settings</sp-tab-panel>
  <sp-tab-panel label="Security">Security settings</sp-tab-panel>
</sp-tabs>

sp-tabs attributes

Attribute Type Default Description
selected-index number 0 Selected panel; a disabled or missing panel falls back to the first enabled one
variant line | pill line Indicator-edge or segmented-control treatment
vertical boolean false Places the tab list beside the panels
full-width boolean false Tabs share the list width equally
aria-label string — Forwarded to the tablist
id / title string — Forwarded to the tabs container

sp-tab-panel attributes

Attribute Type Default Description
label string — Tab button text
disabled boolean false Disables the tab
selected boolean false Reflects selection; set by the parent sp-tabs, not by authors
id / title string — Forwarded to the native role="tabpanel" element

Behavior — follows the WAI-ARIA tabs pattern with automatic activation: roving tabindex, ArrowLeft/ArrowRight (ArrowUp/ArrowDown when vertical), Home, and End, skipping disabled tabs.

Events — sp-change (bubbling) with detail: { index } when the user selects a tab.

Internal targets — [data-sp-tabs-native] selects the tabs container, [data-sp-tabs-tab] each tab button, and [data-sp-tab-panel-native] each panel.


sp-accordion / sp-accordion-item

Renders a stack of disclosure items backed by the Spectre accordion recipes.

<sp-accordion>
  <sp-accordion-item label="Shipping" open>Ships in 2 days.</sp-accordion-item>
  <sp-accordion-item label="Returns">30 day returns.</sp-accordion-item>
</sp-accordion>

sp-accordion attributes

Attribute Type Default Description
flush boolean false Drops the outer border and radius
multiple boolean false Lets several items stay open; otherwise opening one closes the others
id / title string — Forwarded to the accordion container

sp-accordion-item attributes

Attribute Type Default Description
label string — Header text
open boolean false Expanded state
disabled boolean false Prevents toggling
id / title / aria-* string — Forwarded to the header button

Content projection — an element with slot="header" replaces the label text in the header; all other children become the panel content.

Accessibility — the header is a native <button> with aria-expanded and aria-controls; the panel is a role="region" labelled by its header.

Events — sp-open and sp-close (bubbling) from the item when the user toggles it. The single-open behavior responds to user toggles only; setting open programmatically does not close sibling items.

Host classes — the item host carries the sp-accordion__item recipe classes (author classes are preserved) so the recipe's divider between sibling items applies.

Internal targets — [data-sp-accordion-native], [data-sp-accordion-item-header], [data-sp-accordion-item-panel].


sp-breadcrumb

Renders a <nav> breadcrumb trail backed by the Spectre breadcrumb recipes. Each child element becomes one item; the last one is the current page.

<sp-breadcrumb>
  <a href="/">Home</a>
  <a href="/library">Library</a>
  <span>Data</span>
</sp-breadcrumb>
Attribute Type Default Description
separator string — Replaces the built-in / with a decorative (aria-hidden) separator
aria-label string Breadcrumb Forwarded to the <nav>
id / title string — Forwarded to the <nav>

Accessibility — renders <nav> › <ol> › <li>; the last item gets aria-current="page". Projected <a> children receive the sp-breadcrumb__link class.

Internal target — [data-sp-breadcrumb-native] selects the <nav>.


sp-list-group / sp-list-group-item

Renders a list group backed by the Spectre list-group recipes. Each sp-list-group-item becomes a native row: a <li> when every item is static, or an <a> (with href), <button> (with interactive), or <div> otherwise.

<sp-list-group aria-label="Mailboxes">
  <sp-list-group-item href="/inbox" active>Inbox</sp-list-group-item>
  <sp-list-group-item interactive>Archive</sp-list-group-item>
</sp-list-group>

sp-list-group attributes

Attribute Type Default Description
flush boolean false Drops the outer border and radius
horizontal boolean false Lays rows out in a row
accent top | right | bottom | left — Optional decorative edge-rail
accent-color neutral | brand | info | success | warning | danger | cta brand Accent rail color; only applied with accent
id / title / aria-* string — Forwarded to the list container

sp-list-group-item attributes

Attribute Type Default Description
href string — Renders the row as a link
target string — Link target
interactive boolean false Renders the row as a button
active boolean false Current item; sets aria-current="true"
selected boolean false Subtle checked tint, distinct from active
disabled boolean false Disables a button row; removes a link row's href
id / title / aria-label string — Forwarded to the rendered row

Content projection — the item host (display: contents) moves inside the rendered row, so its children, including later edits, render in place. Use the sp-list-group__heading / sp-list-group__text classes for two-line rows.

Events — sp-select (bubbling) from the item when a link or button row is activated.

Internal targets — [data-sp-list-group-native] selects the list container, [data-sp-list-group-row] each row.


sp-offcanvas

Renders a slide-in dialog panel and backdrop backed by the Spectre offcanvas recipes.

Attribute Type Default Description
open boolean false Open/closed state
placement start | end | top | bottom start Viewport edge the panel slides in from
label string — Header title; also labels the dialog
close-label string Close Accessible name of the header close button
id / title / aria-* string — Forwarded to the native dialog element

Content projection — slot="header" replaces the label title, slot="footer" fills the footer region, and all other children become the body.

Behavior — while open: traps Tab/Shift+Tab focus, closes on Esc, the close button, or a backdrop click, focuses the first focusable element once the panel is visible, and restores focus on close. The closed panel is inert.

Events — sp-close (bubbling) when the user closes it.

Internal targets — [data-sp-offcanvas-native] (dialog), [data-sp-offcanvas-backdrop], [data-sp-offcanvas-close].


sp-carousel

Renders a slide carousel backed by the Spectre carousel recipes. Each child element becomes one slide. The viewport is a native scroll-snap track, so it stays swipeable without script.

<sp-carousel aria-label="Featured work">
  <img alt="Project one" src="one.jpg" />
  <img alt="Project two" src="two.jpg" />
</sp-carousel>
Attribute Type Default Description
index number 0 Active slide
fade boolean false Cross-fades instead of scroll-snapping
loop boolean false Wraps from the last slide to the first and back
hide-controls boolean false Hides the previous/next buttons
hide-indicators boolean false Hides the slide indicators
previous-label string Previous slide Accessible name of the previous button
next-label string Next slide Accessible name of the next button
aria-label string Carousel Forwarded to the carousel region
id / title string — Forwarded to the carousel region

Accessibility — follows the WAI-ARIA carousel pattern: a role="region" with aria-roledescription="carousel", slides as role="group" with aria-roledescription="slide" and an "n of N" label, and a polite live region. ArrowLeft/ArrowRight move between slides. There is no autoplay.

Events — sp-change (bubbling) with detail: { index } when the user changes slides, including by swiping.

Internal targets — [data-sp-carousel-native], [data-sp-carousel-viewport], [data-sp-carousel-slide], [data-sp-carousel-prev], [data-sp-carousel-next], [data-sp-carousel-indicator].


sp-table

Styles an authored <table> with the Spectre table recipes and wraps it in the horizontal-scroll wrapper. The table must be authored inside the component, because the HTML parser drops table parts outside a <table>.

<sp-table striped aria-label="Team members">
  <table>
    <thead><tr><th scope="col">Name</th></tr></thead>
    <tbody><tr><td>Ada</td></tr></tbody>
  </table>
</sp-table>
Attribute Type Default Description
size sm | md md Cell density
striped boolean false Tints alternating body rows
hoverable boolean false Tints body rows under the pointer
bordered boolean false Borders every cell
aria-label / aria-labelledby string — Makes the wrapper a focusable, labelled scroll region
id / title string — Forwarded to the wrapper

Recipe classes are added to the authored <table> alongside its own classes. For contextual rows, use aria-selected="true" or the sp-table__row--{neutral|info|success|warning|danger|selected} classes.

Internal target — [data-sp-table-native] selects the wrapper.


sp-pagination

Renders page navigation backed by the Spectre pagination recipes.

<sp-pagination total="20" page="3"></sp-pagination>
<sp-pagination total="20" href-template="/posts?page={page}"></sp-pagination>
Attribute Type Default Description
page number 1 Current page, clamped to 1…total
total number 1 Page count
siblings number 1 Pages shown either side of the current one before an ellipsis
size sm | md | lg md Item size
href-template string — Renders links; {page} is replaced with the page number
previous-label string Previous Previous control text
next-label string Next Next control text
aria-label string Pagination Forwarded to the <nav>
id / title string — Forwarded to the <nav>

Accessibility — the current page has aria-current="page", page items are labelled "Page N", and boundary controls are disabled (aria-disabled without href in link mode).

Events — sp-change (bubbling) with detail: { page } when the user picks a page. In link mode the browser still follows the link.

Internal target — [data-sp-pagination-native] selects the <nav>.


sp-stepper

Renders a progress stepper backed by the Spectre stepper recipes. Each child element becomes one step label.

<sp-stepper aria-label="Checkout" current="1">
  <span>Cart</span>
  <span>Shipping</span>
  <span>Payment</span>
</sp-stepper>
Attribute Type Default Description
current number 0 Active step; earlier steps are done, later ones pending
orientation horizontal | vertical horizontal Layout direction
id / title / aria-* string — Forwarded to the native <ol>

Accessibility — renders an <ol>; the active step has aria-current="step". Done steps show a check mark, others their number. Setting current to the step count marks every step done.

Internal target — [data-sp-stepper-native] selects the <ol>.


sp-switch

Renders a native <input type="checkbox" role="switch"> inside a <label>, backed by the Spectre switch recipe. It submits with its form like a checkbox.

Attribute Type Default Description
checked boolean false On/off state; follows user toggles
size sm | md | lg md Track size
label string — Visible label when no children are projected
name / value / form string — Native form participation (value is on)
disabled / required boolean false Native constraints
focused boolean false Forced focus look (see Shared Conventions)
id / title / aria-* string — Forwarded to the native input

Content projection — children become the label text.


sp-range

Renders a native <input type="range"> backed by the Spectre range recipe. It mirrors the value into --sp-component-range-value so WebKit/Blink paint the filled track.

Attribute Type Default Description
value number 50 Current value, clamped to min…max
min / max / step number 0 / 100 / 1 Native range bounds
name / form string — Native form participation
disabled / focused boolean false Disabled state / forced focus look
id / title / aria-* string — Forwarded to the native input

sp-file-input

Renders a native <input type="file"> backed by the Spectre file-input recipe, which styles the browser's file selector button. The files getter returns the native FileList.

Attribute Type Default Description
size sm | md | lg md Control size
invalid / success boolean false Validation state (invalid wins)
accept / multiple / required string / boolean — Native file constraints
name / form string — Native form participation
full-width / disabled / focused boolean false Width, disabled, forced focus look
id / title / aria-* string — Forwarded to the native input

sp-input-group

Fuses addons and native controls into one bordered control, backed by the Spectre input-group recipes. The recipe styles its direct children, so the group takes native <input>, <select>, <button>, and file inputs rather than sp-* wrappers. Controls without an sp-* class get their recipe class (buttons use the secondary variant). Children marked slot="addon" become addons. Authored order is preserved.

<sp-input-group aria-label="Handle">
  <span slot="addon">@</span>
  <input aria-label="Username" />
  <button type="button">Check</button>
</sp-input-group>
Attribute Type Default Description
disabled boolean false Disabled look for the whole group
id / title / aria-* string — Forwarded to the role="group" wrapper

sp-choice-card

Renders a whole-card option: a <label> wrapping a native radio or checkbox, backed by the Spectre choice-card recipe. Selection, focus, and disabled styling follow the native input. Radio cards that share a name keep their checked properties in sync.

Attribute Type Default Description
type radio | checkbox radio Native input type
checked boolean false Selection state
name / value / form string — Native form participation
disabled / required boolean false Native constraints
hovered / focused boolean false Forced looks
id / aria-describedby string — Forwarded to the native input

Content projection — children become the card content (the input's label).


sp-progress

Renders a role="progressbar" track and fill backed by the Spectre progress recipes.

Attribute Type Default Description
value / max number 0 / 100 Progress, clamped to 0…max
variant brand | neutral | info | success | warning | danger brand Fill color role
size sm | md | lg md Track height
indeterminate boolean false Animated sweep; omits aria-valuenow
label string — Visible label above the track; labels the bar
value-text string — aria-valuetext (e.g. "3 of 8 files")
id / title / aria-* string — Forwarded to the track

sp-popover

A click-toggled, non-modal role="dialog" anchored to its trigger, backed by the Spectre popover recipes. Unlike sp-tooltip it stays open for interaction.

Attribute Type Default Description
open boolean false Open state
placement top | bottom | left | right bottom Side the panel opens on
label string — Header title; also labels the dialog
trigger-label string Show details Trigger text when no slot="trigger" content
id / title string — Forwarded to the dialog panel

Content projection — slot="trigger" fills the trigger button, slot="header" replaces the label title, other children fill the body.

Behavior — the trigger toggles it (aria-expanded/aria-controls); Esc closes it and refocuses the trigger; an outside click closes it. Without a header the dialog is labelled by its trigger.

Events — sp-open and sp-close (bubbling).


sp-datepicker

Renders an inline calendar backed by the Spectre datepicker and day recipes. Values are local ISO dates (YYYY-MM-DD), with no time-zone shift.

Attribute Type Default Description
value string — Selected date; malformed dates are dropped
min / max string — Selectable range; days outside it are disabled
week-start number 0 First weekday (0 Sunday … 6 Saturday)
locale string browser locale Month, weekday, and day-label formatting
name string — Submits the value through a hidden input
previous-label / next-label string Previous month / Next month Month button labels
aria-label string month title Labels the calendar group

Accessibility — every day is a button labelled with its full date, aria-pressed on the selected day and aria-current="date" on today. One day is the tab stop (roving tabindex). Arrows move by day and week, Home/End go to the week's start and end, and PageUp/PageDown change the month (Shift for a year). The month title is a polite live region.

Events — sp-change (bubbling) with detail: { value }.


sp-external-auth-button

One neutral button treatment for every third-party sign-in provider, backed by the Spectre external-auth-button recipes. It deliberately carries no provider colors. Put the provider logo in slot="icon" and the label in the default slot.

Attribute Type Default Description
href string — Renders a link instead of a button
type button | submit button Native button type
full-width / disabled / loading boolean false Width, disabled, busy states
hovered / focused / active boolean false Forced looks
id / title / aria-label string — Forwarded to the native element

sp-card-bleed

A band inside sp-card (usually media) that runs flush through the card's padding, backed by getCardBleedClasses.

Attribute Type Default Description
edges space-separated top right bottom left, or all — Edges to bleed through
padded sm | md | lg, or bare — The card padding step being escaped (bare means md)

sp-prose

Wraps authored long-form HTML (headings, lists, code, quotes, rules) in the Spectre prose surface. An aria-label also makes it a labelled region.


Shared conventions (spectre-ui 5.3.0 parity)

Forced interaction states — components whose recipe supports them accept hovered, focused, and active booleans (hovered/focused only where the recipe has no pressed state). They force the look for documentation, previews, and visual tests; real interaction is still styled by native pseudo-classes. Where a component already uses active to mean "current" (sp-footer-link, sp-sidebar-link, sp-list-group-item, sp-nav-item), it keeps that meaning.

Part markers — class-only recipe parts are applied in place to authored elements that opt in with a slot marker, keeping authored order. Nothing is styled by tag name.

Component Markers
sp-footer heading, text, muted, links, divider (any depth)
sp-nav links
sp-sidebar header; group on a <details> (its <summary> is styled)
sp-dropdown / sp-nav-item menus item, header, divider
sp-list-group-item heading, text
sp-carousel slides caption (inside a slide)
sp-table rows data-variant="neutral|info|success|warning|danger" on a <tr>

Additions to existing components

Component Added
sp-button warning, link, light, dark variants
sp-badge brand variant, dot notification mode, interactive
sp-spinner / sp-toast inverse / neutral variants
sp-alert interactive
sp-rating interactive, pill, full-width
sp-text onSurface* variants; preset="heading | display | lead" with heading-level (h1–h6, defaults to the element level) and display-level (1–6)
sp-container padding (sm | md | lg); max-width gains none and wide
sp-section spacing and gap (sm | md | lg)
sp-stack basis="none"
sp-grid col-start (1–12, or per-breakpoint JSON like span)
sp-nav-item active (current page, aria-current), disabled, and dropdown viewport, full-width, accent/accent-color
sp-input label, helper-text, error-message (wraps the input only when set; an error sets the error state and aria-invalid)
sp-select / sp-textarea focused
sp-pricing-card badge, price, description, header, footer slots
sp-testimonial quote, author-image, author-name, author-title slots

Package Exports / API Surface

Root — @phcdevworks/spectre-components

Exports everything from all component entry points plus the bulk registration helper.

Bulk registration

import { defineSpectreComponents } from '@phcdevworks/spectre-components'
defineSpectreComponents() // registers all sp-* elements

Per-component helpers (same as individual entry points): defineSpectreButton, defineSpectreInput, defineSpectreTextarea, defineSpectreSelect, defineSpectreCheckbox, defineSpectreRadio, defineSpectreLabel, defineSpectreFieldset, defineSpectreBadge, defineSpectreCard, defineSpectreIconBox, defineSpectreRating, defineSpectreTestimonial, defineSpectreAlert, defineSpectreAvatar, defineSpectreSpinner, defineSpectreTag, defineSpectrePricingCard, defineSpectreContainer, defineSpectreGrid, defineSpectreSection, defineSpectreStack

Element classes: SpectreButtonElement, SpectreInputElement, SpectreTextareaElement, SpectreSelectElement, SpectreCheckboxElement, SpectreRadioElement, SpectreLabelElement, SpectreFieldsetElement, SpectreBadgeElement, SpectreCardElement, SpectreIconBoxElement, SpectreRatingElement, SpectreTestimonialElement, SpectreAlertElement, SpectreAvatarElement, SpectreSpinnerElement, SpectreTagElement, SpectrePricingCardElement, SpectreContainerElement, SpectreGridElement, SpectreSectionElement, SpectreStackElement

Button constants and types: spectreButtonVariants, spectreButtonSizes, spectreButtonTypes, SpectreButtonVariant, SpectreButtonSize, SpectreButtonType, SpectreButtonProps

Input / textarea / select constants and types: spectreInputSizes, spectreInputTypes, SpectreInputSize, SpectreInputType, SpectreInputProps, SpectreTextareaProps, SpectreSelectProps

Props interfaces (checkbox / radio / label / fieldset): SpectreCheckboxProps, SpectreRadioProps, SpectreLabelProps, SpectreFieldsetProps

Display constants and types: spectreBadgeVariants, spectreBadgeSizes, spectreCardVariants, spectreIconBoxVariants, spectreIconBoxSizes, spectreRatingSizes, spectreTestimonialVariants, spectreAlertVariants, spectreAlertSizes, spectreAvatarShapes, spectreAvatarSizes, spectreSpinnerVariants, spectreSpinnerSizes, spectreTagVariants, spectreTagSizes, SpectreBadgeVariant, SpectreBadgeSize, SpectreCardVariant, SpectreIconBoxVariant, SpectreIconBoxSize, SpectreRatingSize, SpectreTestimonialVariant, SpectreAlertVariant, SpectreAlertSize, SpectreAvatarShape, SpectreAvatarSize, SpectreSpinnerVariant, SpectreSpinnerSize, SpectreTagVariant, SpectreTagSize, SpectreBadgeProps, SpectreCardProps, SpectreIconBoxProps, SpectreRatingProps, SpectreTestimonialProps, SpectreAlertProps, SpectreAvatarProps, SpectreSpinnerProps, SpectreTagProps, SpectrePricingCardProps

Layout constants and types: spectreContainerMaxWidths, spectreGridColumns, spectreGridGaps, spectreGridAligns, spectreStackAligns, spectreStackBases, spectreStackDirections, spectreStackGaps, SpectreContainerMaxWidth, SpectreGridColumns, SpectreGridGap, SpectreGridAlign, SpectreStackAlign, SpectreStackBasis, SpectreStackDirection, SpectreStackGap, SpectreContainerProps, SpectreGridProps, SpectreSectionProps, SpectreStackProps

Interactive constants and types: spectreDropdownPlacements, spectreToastVariants, spectreTooltipPlacements, SpectreDropdownPlacement, SpectreToastVariant, SpectreTooltipPlacement, SpectreFooterProps, SpectreNavProps, SpectreSidebarProps, SpectreSidebarToggleProps, SpectreDropdownProps, SpectreModalProps, SpectreToastProps, SpectreTooltipProps

Broad component inventory: defineSpectreTabs, defineSpectreTabPanel, defineSpectreAccordion, defineSpectreAccordionItem, defineSpectreBreadcrumb, defineSpectreListGroup, defineSpectreListGroupItem, defineSpectreOffcanvas, defineSpectreCarousel, defineSpectreTable, defineSpectrePagination, defineSpectreStepper; their element classes and *Props interfaces; and spectreTabsVariants, spectreOffcanvasPlacements, spectreTableSizes, spectrePaginationSizes, spectreStepperOrientations with the matching SpectreTabsVariant, SpectreOffcanvasPlacement, SpectreTableSize, SpectrePaginationSize, and SpectreStepperOrientation types

spectre-ui 5.3.0 parity: defineSpectreSwitch, defineSpectreRange, defineSpectreFileInput, defineSpectreInputGroup, defineSpectreChoiceCard, defineSpectreProgress, defineSpectrePopover, defineSpectreDatepicker, defineSpectreExternalAuthButton, defineSpectreCardBleed, defineSpectreProse; their element classes and *Props interfaces; and spectreProgressVariants, spectreProgressSizes, spectreSwitchSizes, spectreFileInputSizes, spectrePopoverPlacements, spectreContainerPaddings, spectreSectionSpacings, spectreGridColStarts, spectreTableRowVariants, spectreTextPresets, spectreHeadingLevels, spectreDisplayLevels with their matching types

Subpath entry points

Each entry point registers only that component and exports only its surface:

Entry point Registers Key exports
.../button sp-button defineSpectreButton, SpectreButtonElement, button constants and types
.../input sp-input defineSpectreInput, SpectreInputElement, input constants and types
.../textarea sp-textarea defineSpectreTextarea, SpectreTextareaElement, SpectreTextareaProps
.../select sp-select defineSpectreSelect, SpectreSelectElement, SpectreSelectProps
.../checkbox sp-checkbox defineSpectreCheckbox, SpectreCheckboxElement, SpectreCheckboxProps
.../radio sp-radio defineSpectreRadio, SpectreRadioElement, SpectreRadioProps
.../label sp-label defineSpectreLabel, SpectreLabelElement, SpectreLabelProps
.../fieldset sp-fieldset defineSpectreFieldset, SpectreFieldsetElement, SpectreFieldsetProps
.../badge sp-badge defineSpectreBadge, SpectreBadgeElement, badge constants and types
.../card sp-card defineSpectreCard, SpectreCardElement, card constants and types
.../icon-box sp-icon-box defineSpectreIconBox, SpectreIconBoxElement, icon-box constants and types
.../rating sp-rating defineSpectreRating, SpectreRatingElement, rating constants and types
.../testimonial sp-testimonial defineSpectreTestimonial, SpectreTestimonialElement, testimonial constants and types
.../alert sp-alert defineSpectreAlert, SpectreAlertElement, alert constants and types
.../avatar sp-avatar defineSpectreAvatar, SpectreAvatarElement, avatar constants and types
.../spinner sp-spinner defineSpectreSpinner, SpectreSpinnerElement, spinner constants and types
.../tag sp-tag defineSpectreTag, SpectreTagElement, tag constants and types
.../pricing-card sp-pricing-card defineSpectrePricingCard, SpectrePricingCardElement, SpectrePricingCardProps
.../container sp-container defineSpectreContainer, SpectreContainerElement, container constants and types
.../grid sp-grid defineSpectreGrid, SpectreGridElement, grid constants and types
.../section sp-section defineSpectreSection, SpectreSectionElement, SpectreSectionProps
.../stack sp-stack defineSpectreStack, SpectreStackElement, stack constants and types
.../nav sp-nav defineSpectreNav, SpectreNavElement, SpectreNavProps
.../nav-item sp-nav-item defineSpectreNavItem, SpectreNavItemElement, SpectreNavItemProps
.../sidebar sp-sidebar defineSpectreSidebar, SpectreSidebarElement, SpectreSidebarProps
.../sidebar-link sp-sidebar-link defineSpectreSidebarLink, SpectreSidebarLinkElement, SpectreSidebarLinkProps
.../sidebar-toggle sp-sidebar-toggle defineSpectreSidebarToggle, SpectreSidebarToggleElement, SpectreSidebarToggleProps
.../dropdown sp-dropdown defineSpectreDropdown, SpectreDropdownElement, dropdown constants and types
.../footer sp-footer defineSpectreFooter, SpectreFooterElement, SpectreFooterProps
.../footer-link sp-footer-link defineSpectreFooterLink, SpectreFooterLinkElement, SpectreFooterLinkProps
.../footer-chip sp-footer-chip defineSpectreFooterChip, SpectreFooterChipElement, SpectreFooterChipProps
.../modal sp-modal defineSpectreModal, SpectreModalElement, SpectreModalProps
.../toast sp-toast defineSpectreToast, SpectreToastElement, toast constants and types
.../tooltip sp-tooltip defineSpectreTooltip, SpectreTooltipElement, tooltip constants and types
.../tabs sp-tabs defineSpectreTabs, SpectreTabsElement, tabs constants and types
.../tab-panel sp-tab-panel defineSpectreTabPanel, SpectreTabPanelElement, SpectreTabPanelProps
.../accordion sp-accordion defineSpectreAccordion, SpectreAccordionElement, SpectreAccordionProps
.../accordion-item sp-accordion-item defineSpectreAccordionItem, SpectreAccordionItemElement, SpectreAccordionItemProps
.../breadcrumb sp-breadcrumb defineSpectreBreadcrumb, SpectreBreadcrumbElement, SpectreBreadcrumbProps
.../list-group sp-list-group defineSpectreListGroup, SpectreListGroupElement, SpectreListGroupProps
.../list-group-item sp-list-group-item defineSpectreListGroupItem, SpectreListGroupItemElement, SpectreListGroupItemProps
.../offcanvas sp-offcanvas defineSpectreOffcanvas, SpectreOffcanvasElement, offcanvas constants and types
.../carousel sp-carousel defineSpectreCarousel, SpectreCarouselElement, SpectreCarouselProps
.../table sp-table defineSpectreTable, SpectreTableElement, table constants and types
.../pagination sp-pagination defineSpectrePagination, SpectrePaginationElement, pagination constants and types
.../stepper sp-stepper defineSpectreStepper, SpectreStepperElement, stepper constants and types
.../card-bleed sp-card-bleed defineSpectreCardBleed, SpectreCardBleedElement, SpectreCardBleedProps
.../prose sp-prose defineSpectreProse, SpectreProseElement, SpectreProseProps
.../progress sp-progress defineSpectreProgress, SpectreProgressElement, progress constants and types
.../switch sp-switch defineSpectreSwitch, SpectreSwitchElement, switch constants and types
.../range sp-range defineSpectreRange, SpectreRangeElement, SpectreRangeProps
.../file-input sp-file-input defineSpectreFileInput, SpectreFileInputElement, file-input constants and types
.../external-auth-button sp-external-auth-button defineSpectreExternalAuthButton, SpectreExternalAuthButtonElement, SpectreExternalAuthButtonProps
.../choice-card sp-choice-card defineSpectreChoiceCard, SpectreChoiceCardElement, SpectreChoiceCardProps
.../input-group sp-input-group defineSpectreInputGroup, SpectreInputGroupElement, SpectreInputGroupProps
.../popover sp-popover defineSpectrePopover, SpectrePopoverElement, popover constants and types
.../datepicker sp-datepicker defineSpectreDatepicker, SpectreDatepickerElement, SpectreDatepickerProps

Size constants are shared between input, textarea, and select. Import spectreInputSizes / SpectreInputSize from .../input when needed alongside textarea or select.

Relationship To The Rest Of Spectre

spectre-tokens  →  design values (colors, spacing, typography)
spectre-ui      →  CSS recipes and Tailwind helpers
spectre-components  →  Lit web component behavior  ← you are here
[adapters]      →  React / Vue / Astro wrappers

The Golden Rule: tokens define meaning, UI defines structure, components define behavior, adapters define delivery. This package only owns the behavior layer.

Development

git clone https://github.com/phcdevworks/spectre-components.git
cd spectre-components
npm install
npm run check        # full release validation gate

Requires Node.js ^22.13.0 || >=24.0.0 and npm 12.0.2.

Command Purpose
npm run check Full validation (lint → typecheck → test → build → export, contract, invariant, and ecosystem checks)
npm run build Compile ESM + CJS with declarations into dist/
npm test Run Vitest suite under happy-dom
npm run test:browser Run native browser behavior regressions (also run in CI)
npm run test:visual Run opt-in screenshot regressions
npm run lint ESLint
npm run check:exports Verify packed ESM/CommonJS entry points and declaration targets
npm run check:contract Verify built exports match components.contract.json
npm run check:invariants Verify light-DOM and no-hardcoded-visual invariants
npm run check:ecosystem Validate spectre.manifest.json
npm run dev tsup watch mode
npm run clean Remove dist/ and coverage/

Key source areas:

  • src/components/ — one directory per custom element
  • src/utils/ — base.ts, projectable.ts, form.ts, dom.ts
  • src/index.ts — root public API and bulk registration helper
  • tests/ — component behavior coverage (Vitest + happy-dom)
  • scripts/check-exports.ts — post-build export resolution check

Troubleshooting

Build fails with type errors — TypeScript 6 is required. Run npm install, then npm run build.

Tests fail in CI but pass locally — Tests run under happy-dom. Confirm you are on Node ^22.13.0 || >=24.0.0. CI tests both versions.

Custom element already defined — Each defineSpectre*() helper is idempotent; calling it twice is safe. If you see conflicts, two different versions of this package may be loaded in the same page.

Styles are not applying — The Spectre CSS layers must load before components are registered. Import @phcdevworks/spectre-tokens/index.css and @phcdevworks/spectre-ui/index.css at the top of your entry module.

Properties not reflecting in React 18 — React 18 sets custom element properties as attributes. Use a ref to set properties imperatively, or upgrade to React 19 which supports custom elements fully.

Validation

Run the full validation gate before any pull request:

npm run check

This runs: lint → typecheck → tests → build → export validation → contract validation → invariant checks → ecosystem manifest validation. All steps must pass.

AI And Automation Boundaries

Claude Code (claude-sonnet-4-6) is the primary development agent for this repository. Codex handles releases, including cutting tagged releases and GitHub Releases, and production stabilization. Jules handles small automated fixes and dependency updates. GitHub Copilot provides development support.

All AI agents with repository access (Claude Code, Codex, Copilot, Jules) have commit, push, and tag authority in this repository. Publishing to npm remains Bradley Potts's sole authority. See AGENTS.md for the full commit-policy and release-authority grant.

Protected from automated change: component public API surface (tags, properties, events, slots, ARIA), the light-DOM rendering model, and the zero-hardcode-values rule. See AGENTS.md for full agent governance and boundary rules.

Contributing

PHCDevworks maintains this package as part of the Spectre suite.

Contribution boundaries:

  • Components must consume @phcdevworks/spectre-ui class helpers — do not recreate CSS locally.
  • Design values must come from @phcdevworks/spectre-tokens — do not hardcode colors, spacing, or other visual primitives.
  • Component tags, properties, events, slots, and ARIA behavior are stable API — breaking changes require a semver major bump.
  • Render in light DOM only — Shadow DOM changes require design-system approval.
  • No framework-specific code — no JSX, SFCs, or Astro components in this package.
  • Run npm run check before opening a pull request.

See CONTRIBUTING.md for the full guide.

License

MIT © PHCDevworks. See LICENSE.

About

@phcdevworks/spectre-components is the web-component layer of the Spectre system. It provides accessible, framework-independent interface components built on Spectre’s shared design contracts.

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages