npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

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

About

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

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

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

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

Open Software & Tools

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

© 2026 – Pkg Stats / Ryan Hefner

@phcdevworks/spectre-components

v1.22.0

Published

@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.

Readme

@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.22.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="[email protected]"
    ></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, sp-nav, and sp-logo-cloud 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>

Spacing steps and the 8px layout grid — sp-section spacing/gap, sp-stack gap, sp-grid gap/row-gap/column-gap, and sp-container padding share one step scale: sm | md | lg | xl | 2xl | 3xl | 4xl. Every step resolves to a layout.* token on the 8px layout grid (4px is reserved for spacing inside a component), and the xl–4xl steps widen at the lg breakpoint through the token package's responsive remap. Set larger or viewport-responsive spacing through these attributes, never with sp-gap-*, sp-p*-*, or raw length overrides.

<sp-section spacing="3xl" gap="2xl">
  <sp-grid columns="3" gap="xl">...</sp-grid>
</sp-section>

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 | | padding | sm \| md \| lg \| xl \| 2xl \| 3xl \| 4xl | md | Inline padding step | | 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 \| xl \| 2xl \| 3xl \| 4xl | 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 | any gap step (JS: columnGap) | — | Overrides gap on the column axis only | | row-gap | any gap step (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.

Nested grids — when an sp-grid is a child of another grid, the parent lays out the host element, so the item-placement options (span, offset, col-start, row-span, row-offset, order) are applied as classes on the host instead of the inner <div>. The grid's own columns and gaps stay on the inner <div>. Classes you author on the host are left alone.

<sp-grid columns="3">
  <sp-grid columns="2" span="2">...</sp-grid>
  <div>Sidebar</div>
</sp-grid>

align is not reflected to a host attribute: align="center" on any element is the legacy HTML presentational hint for text-align: center.

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 | | ------------------------- | ------------------------------------------- | ------- | ------------------------------------------------------------------- | | spacing | sm \| md \| lg \| xl \| 2xl \| 3xl \| 4xl | md | Symmetric block padding step | | gap | sm \| md \| lg \| xl \| 2xl \| 3xl \| 4xl | — | Stacks direct children with this gap | | hero | sm \| md \| lg | — | Asymmetric hero padding (more above than below); replaces spacing | | attached | boolean | false | Drops the top padding of a band that belongs to the section above | | inner-class | string | — | Spectre utility classes applied to the native <section> | | id / title / aria-* | string | — | Forwarded to the native <section> |

A hero needs no sp-pt-*/sp-pb-*/sp-py-* override and no selector on the rendered <section>; choose its size with hero. Use attached on a band such as a logo strip under a hero, so the gap between the two is the hero's bottom padding alone. Back-to-back sections on different surfaces keep their own padding.

<sp-section hero="lg">...</sp-section>
<sp-section attached spacing="sm"
  ><sp-logo-cloud>...</sp-logo-cloud></sp-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 (not reflected to the host) | | gap | sm \| md \| lg \| xl \| 2xl \| 3xl \| 4xl | 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> |

align is not reflected to a host attribute, because align="center" on any element is the legacy HTML presentational hint for text-align: center. The default no longer centers text inside the stack. Authoring align="center" yourself still triggers the hint, and it is the default anyway, so leave it out.

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 | | appearance | dark \| light \| system | dark | Color palette; system follows prefers-color-scheme | | surface | page \| card \| subtle \| inverse \| hero | — | Paints the footer on a published surface role | | inner-class | string | — | Spectre utility classes applied to the native <footer> | | id / title / aria-* | string | — | Forwarded to the native <footer> |

surface changes only the background. Pair it with the appearance whose text palette suits it: light for page, card, and subtle, and the dark default for inverse and hero. Neither option needs a selector on .sp-footer or [data-sp-footer-native].

<sp-footer appearance="light" surface="subtle">...</sp-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