stave.dev

ShadowComponent

A minimal base class for Web Components. No build step. No bundler. No virtual DOM. Just the platform, with the boilerplate removed.

Quick Start

Write an HTML <template>, extend ShadowComponent, call customElements.define(). That's it — you have a Web Component.

What is ShadowComponent?

ShadowComponent is a lightweight base class that sits on top of the native Web Components API. It eliminates the repetitive boilerplate of creating custom elements — attaching shadow roots, cloning templates, creating observable property getters and setters — so you can focus on what your component actually does.

There's no build step, no compilation, no transpilation. Your component files are plain HTML with a <template>, a <style>, and a <script>. The browser loads them directly.

What It Does For You

Minimal Example

A complete component in one file:

<template id="greeting-card">
    <style>
        :host {
            display: block;
            padding: 1rem;
            border: 1px solid var(--border-color, #ccc);
            border-radius: 8px;
        }
        h2 { margin: 0 0 0.5rem; color: var(--accent-primary, #e85d26); }
    </style>
    <h2><slot name="name">Friend</slot></h2>
    <p><slot name="message">Welcome!</slot></p>
</template>

<script>
class GreetingCard extends ShadowComponent {
    static observables = ["name", "message"];
}
customElements.define("greeting-card", GreetingCard);
</script>

Use it in HTML like any other element:

<greeting-card>
    <span slot="name">Claude</span>
    <span slot="message">Good to see you.</span>
</greeting-card>

Naming Conventions

Prefix Usage Examples
shadow-* Framework-level components (auto-registered on DOMContentLoaded) shadow-header, shadow-nav, shadow-toast
module-component Module-scoped components loaded with a specific module stave-code, stave-callout
descriptive-name Standalone components with self-registering scripts color-picker, loading-indicator, help-icon
Auto-Registration

Any <template> with an id starting with shadow- is automatically registered as a custom element on DOMContentLoaded. Other components register themselves explicitly with customElements.define().

Documentation