site-kit

components

<sa-header>

Sticky top bar with three named slots: brand, nav, actions. At narrow viewports the slots stack vertically; no hamburger menu in v2.

View source
<script type="module" src=".../dist/components/sa-header.js"></script>

<sa-header>
  <a slot="brand" href="/">acme.example</a>
  <nav slot="nav">
    <a href="/docs">Docs</a>
    <a href="/blog">Blog</a>
    <a href="/pricing">Pricing</a>
  </nav>
  <a slot="actions" href="/login">Sign in</a>
</sa-header>
KindNameNotes
slotbrandLogo or wordmark, usually inside an <a>.
slotnavA <nav> with 2–5 links.
slotactionsOne or two CTA links (sign-in, GitHub, …).
partcontainerThe outer <header>.
partbrandWrapper around the brand slot.
partnavWrapper around the nav slot.
partactionsWrapper around the actions slot.

<sa-hero>

Splash block parameterized by attributes. Optional media slot promotes the layout to two columns on wide viewports. CTA renders only when both cta-href and cta-label are set.

No media, single column

With media slot, two columns

Centered alignment, no CTA

View source
<script type="module" src=".../dist/components/sa-hero.js"></script>

<sa-hero
  eyebrow="Foundation v0.1.0"
  title="Compose your site from blocks"
  subtitle="A small kit of tokens, typography, and web components."
  cta-href="/docs/quickstart"
  cta-label="Get started">
  <img slot="media" src="/img/illustration.svg" alt="">
</sa-hero>
KindNameNotes
attributetitleRequired. Renders as the H1.
attributesubtitleOptional supporting paragraph.
attributeeyebrowOptional small label above the title.
attributecta-hrefCTA link target. Renders only with cta-label.
attributecta-labelCTA link text. Renders only with cta-href.
attributealign"left" (default) or "center".
slotmediaOptional. With align="left", promotes layout to two columns.
partcontainerOuter wrapper.
parteyebrow / title / subtitle / cta / mediaEach individually styleable.

<sa-footer>

Bottom-of-page chrome with three named slots: brand, links, copyright. Top row stacks at narrow viewports.

acme.example

Compose your site from blocks.

© 2026 Acme.
View source
<script type="module" src=".../dist/components/sa-footer.js"></script>

<sa-footer>
  <div slot="brand">
    <strong>acme.example</strong>
    <p>Compose your site from blocks.</p>
  </div>
  <nav slot="links">
    <a href="/docs">Docs</a>
    <a href="/blog">Blog</a>
    <a href="/pricing">Pricing</a>
  </nav>
  <span slot="copyright">© 2026 Acme.</span>
</sa-footer>
KindNameNotes
slotbrandLogo + optional tagline. Pinned to top-row left.
slotlinksOne or more <nav> blocks. Top-row right, flex-wraps.
slotcopyrightSingle line in the bottom row.
partcontainerThe outer <footer>.
parttopTop row (brand + links).
partbrand / links / bottom / copyrightEach individually styleable.

<sa-feature-grid> with <sa-feature> children

Auto-fitting CSS grid (minmax(280px, 1fr)). Children typically <sa-feature> instances but anything works — the grid doesn't introspect children.

Each component is a single ES module under dist/components/. A page imports only what it uses; nothing else loads.

🎨

Override --sa-accent on :root and every component picks it up automatically through CSS-custom-property inheritance.

Slots collapse cleanly when nothing is provided. This card has no icon slot — the layout adjusts without empty space.

View source
<script type="module" src=".../dist/components/sa-feature-grid.js"></script>
<script type="module" src=".../dist/components/sa-feature.js"></script>

<sa-feature-grid>
  <sa-feature title="Drop-in modules">
    <svg slot="icon" viewBox="0 0 24 24" fill="currentColor">...</svg>
    <p>Each component is a single ES module under dist/components/.</p>
  </sa-feature>
  <sa-feature title="Themed once">
    <span slot="icon">🎨</span>
    <p>Override --sa-accent and every component picks it up.</p>
  </sa-feature>
  <sa-feature title="Optional slots">
    <p>Slots collapse cleanly when not provided.</p>
  </sa-feature>
</sa-feature-grid>

<sa-feature-grid>

KindNameNotes
slot(default)Any children. <sa-feature> is typical but not required.
partcontainerThe CSS Grid container. Override to change column min-width or gap.

<sa-feature>

KindNameNotes
attributetitleRequired. Renders as the H3.
sloticonOptional. SVG (will inherit accent via currentColor) or emoji span.
slot(default)Body description. Typically a paragraph or short prose.
partcontainerThe card itself.
particon / title / bodyEach individually styleable.

<sa-chat>

Two-region layout for chat surfaces: scrollable messages on top, pinned composer on the bottom. Both regions are slot-only — the kit owns layout (max-width, sizing, hairline divider, padding via tokens), the consumer owns the appearance of individual messages and composer controls.

assistant

Hi — drop your composer in the composer slot, your messages container in the messages slot. The kit handles the layout.

user

What stays in light DOM?

assistant

Your slotted children. Selectors like #messages and #user-input stay queryable from your app code.

View source
<script type="module" src=".../dist/components/sa-chat.js"></script>

<sa-chat>
  <div slot="messages" id="messages">
    <!-- consumer's app code appends message nodes here -->
  </div>
  <form slot="composer" id="composer">
    <textarea id="user-input" placeholder="Ask anything…"></textarea>
    <button type="submit">Send</button>
  </form>
</sa-chat>
KindNameNotes
slotmessagesContainer for the messages list. Stays in light DOM so app code can query and mutate it directly.
slotcomposerThe composer (typically a <form>). Stays in light DOM for the same reason.
partmessagesThe scrollable wrapper around the messages slot. Override padding / scroll behavior here.
partcomposerThe composer wrapper. Override padding / divider / background here.
var--sa-chat-max-widthContainer width. Defaults to 880px.