Skip to content

slogsdon/modern-developer-design-system

Repository files navigation

shane.logsdon.io β€” writing and projects on agentic workflows, web standards, and payments engineering.

Modern Developer Design System

A minimal, flat design system for developers. Inspired by Flowglad and Zed, built with native web components and zero dependencies.

Design Philosophy

  • Minimal & Flat - No heavy shadows, no unnecessary transforms
  • Editorial Confidence - Generous whitespace, deliberate typography
  • Developer-First - Clean code blocks, precise interactions
  • Native Web Components - No framework dependencies
  • TypeScript - Full type safety and excellent DX
  • Theme Support - Light and dark modes with unified accent colors
  • Zero Runtime - Pure web standards, no runtime overhead

Installation

npm install

Development

Start the development server:

npm run dev

Build for production:

npm run build

How to Preview

Build the library then open the local playground (it loads dist/index.js):

npm run build
open index.html

Components

Need ready-made layouts? See patterns.md for composition examples built from these primitives.

Forms

Button

Interactive buttons with three visual variants and sizes. Use primary for main actions (submit, save), secondary for supporting actions (cancel, back), and ghost for low-emphasis actions (help, dismiss). Ideal for forms, modals, toolbars, and CTAs.

<div style="display: flex; gap: 1rem; flex-wrap: wrap">
  <dev-button theme="light" variant="primary">Primary</dev-button>
  <dev-button theme="light" variant="secondary">Secondary</dev-button>
  <dev-button theme="light" variant="ghost">Ghost</dev-button>
  <dev-button theme="light" variant="primary" size="sm">Small</dev-button>
  <dev-button theme="light" variant="primary" size="lg">Large</dev-button>
</div>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • variant - primary | secondary | ghost (default: primary) - Visual treatment for fills/borders.
  • size - sm | md | lg (default: md) - Padding and font scale.
  • disabled - boolean (default: false) - Prevents interaction and dims the surface.
  • loading - boolean (default: false) - Shows spinner and disables button during async operations.

Slots:

  • default - Button label or inline content.

Checkbox

Accessible checkbox for binary yes/no selections. Perfect for settings, preferences, terms acceptance, and multi-select lists. Supports keyboard navigation and screen readers. Use for independent options where multiple selections are allowed.

<div style="display: grid; gap: 0.75rem">
  <dev-checkbox theme="light" checked="">Remember me</dev-checkbox>
  <dev-checkbox theme="light">Email updates</dev-checkbox>
</div>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • checked - boolean (default: false) - Controls the checked state.
  • disabled - boolean (default: false) - Prevents interaction and lowers opacity.

Slots:

  • default - Label text.

Events:

  • dev-checkbox:change - CustomEvent<{ checked: boolean }> - Emitted when the checked state changes.

Input

Versatile text input with built-in label, description, and error states. Supports single-line text or multiline textarea mode. Use for forms, search, user profile fields, and data entry. Includes validation states (success/error) and required field indicators.

<div style="display: grid; gap: 1rem">
  <dev-input
    theme="light"
    label="Email"
    required=""
    description="We'll never share your email."
    placeholder="you@example.com">
  </dev-input>
  <dev-input theme="light" label="Notes" multiline="" rows="4" help-text="Keep it concise."></dev-input>
  <dev-input theme="light" label="Username" required="" error="Username is already taken"></dev-input>
</div>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • label - string (default: "") - Top label text.
  • description - string (default: "") - Helper text shown below the control.
  • help-text - string (default: "") - Legacy helper text (falls back when description is absent).
  • error - string (default: "") - Error message shown below the control.
  • required - boolean (default: false) - Shows required indicator and sets required on the control.
  • name - string (default: "") - Name attribute passed to the underlying control.
  • type - string (default: text) - Input type (text, email, password, etc.). Ignored when multiline.
  • state - default | success | error (default: default) - Tints borders and help text.
  • multiline - boolean (default: false) - Render a textarea instead of input.
  • rows - number (default: 3) - Textarea rows when multiline.
  • placeholder - string (default: "") - Placeholder text for the control.
  • value - string (default: "") - Current value reflected on the control.
  • disabled - boolean (default: false) - Prevents interaction and dims the control/label.

Slots:

  • default - Not used; component manages internal input.

Events:

  • dev-input:input - CustomEvent<{ value: string }> - Emitted on every keystroke as the user types.
  • dev-input:change - CustomEvent<{ value: string; name: string }> - Emitted when the input loses focus after the value has changed.

Number Stepper

Numeric input with +/- buttons for precise value control. Ideal for quantities, settings with bounded ranges (threads, retry counts), or any numeric value where users need fine-grained control. Prevents invalid input and respects min/max boundaries.

<dev-number-stepper theme="light" label="Concurrency" min="1" max="32" step="1" value="6"></dev-number-stepper>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • value - number (default: 0) - Current numeric value.
  • min - number (default: 0) - Lower bound; disables decrement below it.
  • max - number (default: 100) - Upper bound for value.
  • step - number (default: 1) - Increment/decrement step.

Slots:

  • default - Label text above the control.

Events:

  • dev-number-stepper:change - CustomEvent<{ value: number }> - Emitted when the value changes via increment/decrement buttons.

Radio Group

Mutually exclusive single-choice selector. Use standard radio style for forms/surveys, or segmented variant (pill-shaped buttons) for filter controls and view switchers. Perfect for pricing tiers, time intervals, and any scenario requiring exactly one selection.

<div style="display: grid; gap: 1rem">
  <dev-radio-group theme="light" value="pro">
    <option value="free">Free</option>
    <option value="pro">Pro</option>
    <option value="enterprise">Enterprise</option>
  </dev-radio-group>
  <dev-radio-group theme="light" variant="segmented" value="weekly">
    <option value="daily">Daily</option>
    <option value="weekly">Weekly</option>
    <option value="monthly">Monthly</option>
  </dev-radio-group>
</div>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • value - string (default: "") - Selected option value.
  • variant - radio | segmented (default: radio) - Visual style for options.
  • orientation - horizontal | vertical (default: horizontal) - Layout direction.

Slots:

  • <option> - Option elements supply label/value and optional data-hint.

Events:

  • dev-radio-group:change - CustomEvent<{ value: string }> - Emitted when the selected option changes.

Slider

Visual range input for selecting numeric values by dragging. Best for approximate values, settings with visual feedback (volume, brightness, retention periods), or when the relative position matters more than exact numbers. More intuitive than steppers for large ranges.

<dev-slider theme="light" label="Retention days" min="7" max="90" value="30"></dev-slider>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • value - number (default: 50) - Current value within the range.
  • min - number (default: 0) - Lower bound for the slider.
  • max - number (default: 100) - Upper bound for the slider.
  • step - number (default: 1) - Increment granularity.

Slots:

  • default - Label displayed above the track.

Events:

  • dev-slider:change - CustomEvent<{ value: number }> - Emitted when the slider value changes.

Toggle

On/off switch for instant state changes. Use instead of checkbox when the action takes immediate effect (like enabling a feature or switching modes). Common in settings panels, feature flags, and environment switchers. Provides clear visual feedback for binary states.

<div style="display: grid; gap: 0.75rem">
  <dev-toggle theme="light" checked="">Live mode</dev-toggle>
  <dev-toggle theme="light">Preview mode</dev-toggle>
</div>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • checked - boolean (default: false) - On/off state.
  • disabled - boolean (default: false) - Prevents interaction.

Slots:

  • default - Label text.

Events:

  • dev-toggle:change - CustomEvent<{ checked: boolean }> - Emitted when the toggle state changes.

Button Group

Visually connects related buttons with shared borders. Perfect for alignment controls, view switchers (list/grid/gallery), or formatting toolbars. Groups logically related actions to reduce visual clutter. Supports horizontal or vertical orientation.

<div style="display: grid; gap: 1rem">
  <dev-button-group theme="light">
    <dev-button theme="light" variant="secondary">Left</dev-button>
    <dev-button theme="light" variant="secondary">Center</dev-button>
    <dev-button theme="light" variant="secondary">Right</dev-button>
  </dev-button-group>
  <dev-button-group theme="light" orientation="vertical">
    <dev-button theme="light" variant="secondary">Top</dev-button>
    <dev-button theme="light" variant="secondary">Middle</dev-button>
    <dev-button theme="light" variant="secondary">Bottom</dev-button>
  </dev-button-group>
</div>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • orientation - horizontal | vertical (default: horizontal) - Layout direction for the group.

Slots:

  • default - Button children to group together.

Input Group

Combines text inputs with prefix/suffix labels or icons. Essential for URLs (https://), currency ($ USD), units (%, GB, ms), or decorative icons (search magnifying glass). Creates clear context for input values and reduces user confusion.

<div style="display: grid; gap: 1rem">
  <dev-input-group theme="light">
    <span slot="prefix">https://</span>
    <input type="text" placeholder="example.com" style="outline: none; padding: 0.75rem" />
  </dev-input-group>
  <dev-input-group theme="light">
    <input type="number" placeholder="0.00" style="outline: none; padding: 0.75rem" />
    <span slot="suffix">USD</span>
  </dev-input-group>
</div>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • disabled - boolean (default: false) - Disables the entire group.

Slots:

  • prefix - Content shown before the input.
  • default - Input element in the middle.
  • suffix - Content shown after the input.

Toggle Group

Toggle buttons supporting single or multiple selections. Use single mode for alignment/view controls (mutually exclusive). Use multiple mode for text formatting (bold + italic + underline) or filter tags. More visual than radio groups or checkboxes.

<div style="display: grid; gap: 1rem">
  <dev-toggle-group theme="light" type="single" value="center">
    <span value="left">Left</span>
    <span value="center">Center</span>
    <span value="right">Right</span>
  </dev-toggle-group>
  <dev-toggle-group theme="light" type="multiple" value="bold,italic">
    <span value="bold">B</span>
    <span value="italic">I</span>
    <span value="underline">U</span>
  </dev-toggle-group>
</div>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • type - single | multiple (default: single) - Selection mode for the group.
  • orientation - horizontal | vertical (default: horizontal) - Layout direction.
  • value - string | string[] (default: "") - Selected value(s). Comma-separated for multiple.
  • disabled - boolean (default: false) - Disables all buttons in the group.

Slots:

  • default - Elements with value attributes to toggle.

Events:

  • dev-toggle-group:change - CustomEvent<{ value: string | string[] }> - Emitted when the selection changes.

Select

Dropdown menu for choosing from predefined options. Use for small-to-medium lists (under 20 items) where users pick one or multiple values. Supports keyboard navigation (arrows, Enter, Esc). For searchable lists, use Combobox instead. Common in filters, settings, and forms.

<div style="display: grid; gap: 1rem; max-width: 320px">
  <dev-select theme="light" placeholder="Choose a framework" value="react">
    <option value="react">React</option>
    <option value="vue">Vue</option>
    <option value="svelte">Svelte</option>
    <option value="solid">Solid</option>
  </dev-select>
  <dev-select theme="light" placeholder="Choose languages" multiple="" value="ts,js">
    <option value="ts">TypeScript</option>
    <option value="js">JavaScript</option>
    <option value="py">Python</option>
    <option value="go">Go</option>
  </dev-select>
</div>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • placeholder - string (default: Select option) - Text shown when no value is selected.
  • value - string | string[] (default: "") - Selected value(s). Comma-separated for multiple.
  • disabled - boolean (default: false) - Prevents interaction with the select.
  • multiple - boolean (default: false) - Enables multi-select mode with checkmarks.
  • open - boolean (default: false) - Controls dropdown visibility.

Slots:

  • <option> - Option elements provide label and value for selection.

Events:

  • dev-select:change - CustomEvent<{ value: string | string[] }> - Emitted when the selection changes.

Combobox

Searchable dropdown with live filtering. Essential for long lists (countries, timezones, libraries) where typing is faster than scrolling. Combines text input with select functionality. Use when users know what they're looking for or lists exceed 20+ items.

<div style="max-width: 320px">
  <dev-combobox theme="light" placeholder="Search frameworks…" value="react">
    <option value="react">React</option>
    <option value="vue">Vue</option>
    <option value="angular">Angular</option>
    <option value="svelte">Svelte</option>
    <option value="solid">Solid</option>
    <option value="preact">Preact</option>
    <option value="lit">Lit</option>
  </dev-combobox>
</div>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • placeholder - string (default: Search&hellip;) - Placeholder text for the search input.
  • value - string (default: "") - Currently selected value.
  • disabled - boolean (default: false) - Prevents interaction with the combobox.
  • open - boolean (default: false) - Controls dropdown visibility.

Slots:

  • <option> - Option elements provide searchable items with label and value.

Events:

  • dev-combobox:change - CustomEvent<{ value: string; label: string }> - Emitted when an option is selected.

Date Picker

Visual calendar for selecting dates. Supports single dates (appointments, deadlines), multiple dates (recurring events), or date ranges (booking systems, reports). Better UX than manual text entry. Prevents invalid dates and formats consistently as ISO strings.

<div style="display: grid; gap: 1rem; max-width: 340px">
  <dev-date-picker
    id="datePickerRange"
    theme="light"
    label="Availability"
    mode="range"
    value='["2024-08-05","2024-08-09"]'>
  </dev-date-picker>
  <dev-date-picker id="datePickerSingle" theme="light" placeholder="Pick a launch date"></dev-date-picker>
</div>
<script>
  const rangePicker = document.getElementById("datePickerRange");
  rangePicker?.addEventListener("date-change", (e) => {
    console.log("Date changed", e.detail);
  });
</script>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • mode - single | multiple | range (default: single) - Selection behavior.
  • value - string | JSON (default: "") - ISO string for single, JSON array for multiple/range.
  • placeholder - string (default: Select date) - Text when no value is selected.
  • label - string (default: "") - Optional top label.
  • disabled - boolean (default: false) - Prevents toggling the calendar and input focus.

Slots:

  • default - Not used; controlled via attributes.

Events:

  • dev-date-picker:change - CustomEvent<{ mode: string; selected: string | string[] }> - Emitted when date selection changes.

Input OTP

Specialized input for one-time passwords and verification codes. Auto-advances between character slots for seamless entry. Perfect for 2FA flows, SMS verification, or security codes. Handles paste events intelligently and provides clear visual feedback per digit.

<div style="display: grid; gap: 1rem">
  <dev-input-otp theme="light" length="6" type="number"></dev-input-otp>
  <dev-input-otp theme="light" length="4" type="number"></dev-input-otp>
</div>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • length - number (default: 6) - Number of character slots for the OTP.
  • type - text | number (default: number) - Input type (numeric or alphanumeric).
  • value - string (default: "") - Current OTP value.
  • disabled - boolean (default: false) - Prevents input interaction.

Slots:

  • default - No slots; controlled by attributes.

Events:

  • dev-input-otp:change - CustomEvent<{ value: string }> - Emitted when the OTP value changes.
  • dev-input-otp:complete - CustomEvent<{ value: string }> - Emitted when all OTP slots are filled.

Form

Container for managing form state, validation, and submissions. Automatically collects values from named inputs and dispatches form-submit events with structured data. Handles reset/submit buttons. Use for login, signup, settings, or any multi-field data entry.

<dev-form id="demoForm" theme="light">
  <div style="display: grid; gap: 1rem; max-width: 400px">
    <dev-input theme="light" label="Email" required="" name="email" placeholder="you@example.com"></dev-input>
    <dev-input theme="light" label="Password" required="" name="password" type="password" placeholder="β€’β€’β€’β€’β€’β€’β€’β€’">
    </dev-input>
    <div style="display: flex; gap: 0.75rem">
      <dev-button theme="light" type="submit">Submit</dev-button>
      <dev-button theme="light" variant="secondary" type="reset">Reset</dev-button>
    </div>
  </div>
</dev-form>
<script>
  const demoForm = document.getElementById("demoForm");
  demoForm?.addEventListener("form-submit", (e) => {
    console.log("Form submitted:", e.detail.data);
    alert("Form submitted! Check console for data.");
  });
</script>

Events:

  • dev-form:submit - CustomEvent<{ data: Record<string, unknown>; formData: FormData }> - Emitted when the form is submitted.
  • dev-form:reset - CustomEvent<{}> - Emitted when the form is reset.

Navigation

Breadcrumbs

Shows hierarchical page location for easy navigation. Essential for deep page structures (docs, admin panels, e-commerce categories). Helps users understand where they are and provides quick back-navigation. Uses aria-current for accessibility.

<dev-breadcrumbs theme="light">
  <a href="/">Home</a>
  <a href="/billing">Billing</a>
  <a aria-current="page">Invoices</a>
</dev-breadcrumbs>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • items - JSON (default: n/a) - Optional JSON array of {label, href} if not using links.

Slots:

  • <a> - Anchor elements for each crumb (preferred).

Pagination

Navigate through multi-page datasets. Use for tables, search results, or any list exceeding 10-50 items. Shows current page, total pages, and prev/next controls. Dispatches page-change events for easy integration with data fetching.

<dev-pagination theme="light" page="3" total-pages="9"></dev-pagination>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • page - number (default: 1) - Current page index (1-based).
  • total-pages - number (default: 1) - Total number of pages.

Slots:

  • default - No slots; buttons are generated.

Events:

  • dev-pagination:change - CustomEvent<{ page: number; previousPage: number }> - Emitted when the page changes.

Steps

Visual progress tracker for multi-step processes. Perfect for onboarding flows, checkout funnels, setup wizards, or form wizards. Shows completed/current/upcoming steps clearly. Helps users understand progress and reduces abandonment.

<dev-steps
  theme="light"
  current="1"
  steps='[{"label":"Create project"},{"label":"Connect billing"},{"label":"Deploy"}]'>
</dev-steps>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • steps - JSON (default: []) - Array of {label, description, state} objects.
  • current - number (default: 0) - Index for current step.
  • orientation - horizontal | vertical (default: horizontal) - Layout direction.

Slots:

  • default - Optional child nodes with data-label/description.

Tabs

Switch between related content panels without page reload. Use for dashboards (Overview/Analytics/Settings), product pages (Details/Reviews/Specs), or documentation (Guide/API/Examples). Keeps context while reducing scroll and clutter.

<dev-tabs theme="light">
  <div data-label="Overview">
    <p>Keep pricing, roadmap, and API docs close together.</p>
  </div>
  <div data-label="API">
    <dev-code-block theme="light" language="typescript" filename="client.ts"
      >import { Client } from '@modern-dev/sdk'; const client = new Client({ apiKey: process.env.API_KEY }); await
      client.deploy();</dev-code-block
    >
  </div>
  <div data-label="Changelog">
    <p>Ship weekly updates with clear notes for your team.</p>
  </div>
</dev-tabs>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • selected-index - number (default: 0) - Active tab index.

Slots:

  • <div data-label> - Panels with data-label attribute become tabs.

Events:

  • dev-tabs:change - CustomEvent<{ selectedIndex: number; previousIndex: number }> - Emitted when the active tab changes.

Toolbar

Page or section header with title and action buttons. Common in admin interfaces, detail pages, and modals. Provides consistent header pattern with clear hierarchy: title on left, actions on right. Supports optional eyebrow text for context.

<dev-toolbar theme="light">
  <span slot="title">Billing</span>
  <dev-cluster slot="actions" gap="0.5rem">
    <dev-button theme="light" variant="secondary" size="sm">Cancel</dev-button>
    <dev-button theme="light" variant="secondary" size="sm">Save</dev-button>
  </dev-cluster>
</dev-toolbar>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.

Slots:

  • title - Primary title content.
  • eyebrow - Optional label above the title.
  • actions - Right-aligned action area.

Menubar

Desktop-style horizontal menu bar with nested dropdowns and keyboard shortcuts. Use for app navigation (File/Edit/View), developer tools, or content editors. Provides familiar desktop UX with full keyboard support (arrows, Enter, Esc).

<div>
  <dev-menubar id="appMenubar" theme="light"></dev-menubar>
</div>
<script>
  window.addEventListener("DOMContentLoaded", () => {
    const menubar = document.getElementById("appMenubar");
    if (menubar && menubar.setItems) {
      menubar.setItems([
        {
          label: "File",
          items: [
            { label: "New File", shortcut: "Cmd+N" },
            { label: "Open File", shortcut: "Cmd+O" },
            { separator: true },
            { label: "Save", shortcut: "Cmd+S" },
            { label: "Save As…", shortcut: "Cmd+Shift+S" },
          ],
        },
        {
          label: "Edit",
          items: [
            { label: "Undo", shortcut: "Cmd+Z" },
            { label: "Redo", shortcut: "Cmd+Shift+Z" },
            { separator: true },
            { label: "Cut", shortcut: "Cmd+X" },
            { label: "Copy", shortcut: "Cmd+C" },
            { label: "Paste", shortcut: "Cmd+V" },
          ],
        },
        {
          label: "View",
          items: [
            { label: "Zoom In", shortcut: "Cmd++" },
            { label: "Zoom Out", shortcut: "Cmd+-" },
            { label: "Reset Zoom", shortcut: "Cmd+0" },
          ],
        },
      ]);
    }
  });
</script>

Events:

  • dev-menubar:item-select - CustomEvent<{ item: HTMLElement }> - Emitted when a menu item is selected.

Navigation Menu

Multi-level navigation with nested dropdown menus. Perfect for marketing sites, e-commerce (category hierarchies), or knowledge bases. Supports unlimited nesting depth. More web-native than Menubar, less desktop-focused.

<div>
  <dev-navigation-menu id="navMenu" theme="light"></dev-navigation-menu>
</div>
<script>
  window.addEventListener("DOMContentLoaded", () => {
    const navMenu = document.getElementById("navMenu");
    if (navMenu && navMenu.setItems) {
      navMenu.setItems([
        { label: "Home", href: "#home" },
        {
          label: "Products",
          children: [
            { label: "All Products", href: "#products" },
            { label: "New Arrivals", href: "#new" },
            {
              label: "Categories",
              children: [
                { label: "Electronics", href: "#electronics" },
                { label: "Clothing", href: "#clothing" },
                { label: "Books", href: "#books" },
              ],
            },
          ],
        },
        { label: "About", href: "#about" },
        { label: "Contact", href: "#contact" },
      ]);
    }
  });
</script>

Events:

  • dev-navigation-menu:navigate - CustomEvent<{ item: HTMLElement }> - Emitted when a navigation item is clicked.

Feedback

Alert

Inline status messages for contextual feedback. Use info for tips, success for confirmations, warning for cautions, danger for errors. Place near related content (forms, sections). Supports dismissal for non-critical messages. More subtle than modals.

<div style="display: grid; gap: 1rem">
  <dev-alert theme="light" variant="info" heading="Heads up">We rotate keys every 24 hours.</dev-alert>
  <dev-alert theme="light" variant="success" heading="Shipped" closable="">New billing API is live.</dev-alert>
  <dev-alert theme="light" variant="danger" heading="Action required" closable="">Update your webhook URL.</dev-alert>
</div>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • variant - info | success | warning | danger | neutral (default: info) - Color system for the alert.
  • closable - boolean (default: false) - Shows a dismiss button when set.
  • heading - string (default: "") - Optional heading text.

Slots:

  • default - Body copy of the alert.

Events:

  • dev-alert:close - CustomEvent<{ reason: string }> - Emitted when the alert is dismissed.

Callout

Prominent announcement banner for page-level messaging. Use for feature announcements, breaking changes, or important notices that apply to the entire page/section. More prominent than Alert but less intrusive than Modal. Great for changelogs.

<dev-callout theme="light" tone="info" title="Preview tier launched"
  >Use the new preview tier for test tenants without touching production billing.</dev-callout
>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • tone - info | success | warning | danger | neutral (default: info) - Status palette for the surface.
  • title - string (default: "") - Headline for the callout.

Slots:

  • default - Body content.

Modal

Centered overlay dialog requiring user action before dismissal. Use for confirmations (delete account), forms (create resource), or critical decisions. Blocks page interaction until closed. Includes focus trap for accessibility. Choose Sheet for less critical content.

<dev-modal theme="light" title="Create API key" open="" size="md">
  <p>Generate a scoped key for CI usage and rotation.</p>
  <dev-stack theme="light" gap="0.75rem">
    <dev-input theme="light" label="Name" placeholder="CI deploy key"></dev-input>
    <dev-input theme="light" label="Scope" placeholder="billing.write"></dev-input>
    <div style="display: flex; gap: 0.75rem; justify-content: flex-end">
      <dev-button theme="light" variant="secondary" size="sm">Cancel</dev-button>
      <dev-button theme="light" size="sm">Create key</dev-button>
    </div>
  </dev-stack>
</dev-modal>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • open - boolean (default: false) - Controls visibility of the dialog.
  • size - sm | md | lg (default: md) - Max width preset for the dialog.
  • closable - boolean (default: true) - Allows overlay/ESC close when true.
  • title - string (default: "") - Optional heading for aria-labeling.

Slots:

  • default - Dialog body content and actions.

Events:

  • dev-modal:close - CustomEvent<{ reason: "escape" | "overlay" | "button" }> - Emitted when the modal is closed.

Progress

Linear progress indicator for determinate tasks. Shows completion percentage for uploads, downloads, builds, or multi-step processes. Use when you know total duration/steps. For indeterminate loading, use Spinner instead.

<div style="display: grid; gap: 1rem">
  <dev-progress theme="light" label="Provisioning" value="64"></dev-progress>
  <dev-progress theme="light" label="Integration tests" value="18" max="24" show-value="true"></dev-progress>
</div>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • value - number (default: 0) - Current progress value.
  • max - number (default: 100) - Upper bound for the progress bar.
  • label - string (default: "") - Accessible label and visible text.
  • show-value - boolean (default: true) - Toggle display of numeric percentage.

Slots:

  • default - No slots; value is driven by attributes.

Skeleton

Placeholder blocks mimicking content structure during load. Better UX than blank space or spinners for content-heavy pages. Shows approximate layout before data arrives. Use for cards, lists, profiles, or text blocks.

<div style="display: grid; gap: 0.75rem; max-width: 420px">
  <dev-skeleton theme="light" width="100%" height="16px"></dev-skeleton>
  <dev-skeleton theme="light" width="70%" height="16px"></dev-skeleton>
  <dev-skeleton theme="light" width="90%" height="48px"></dev-skeleton>
</div>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • width - string (default: 100%) - CSS width of the placeholder.
  • height - string (default: 1.5rem) - CSS height of the placeholder.
  • rounded - boolean (default: true) - Toggle rounded corners.

Slots:

  • default - Decorative; no slots used.

Spinner

Indeterminate loading indicator for unknown durations. Use inline with text (saving changes…) or centered in loading states. Smaller footprint than Progress for quick operations. Signals activity without specific progress.

<div style="display: flex; align-items: center; gap: 0.75rem">
  <dev-spinner theme="light"></dev-spinner>
  <span style="color: rgb(111, 102, 88)">Fetching data…</span>
</div>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • size - string (default: 1.5rem) - Diameter of the spinner.

Slots:

  • default - Decorative; no slots used.

Toast Stack

Non-blocking notifications that auto-dismiss. Perfect for confirmations (saved, copied, sent), errors (network timeout), or background task updates. Stacks multiple toasts and queues them intelligently. Doesn't interrupt workflow like modals.

<dev-toast-stack id="toastStack" theme="light"></dev-toast-stack>
<div style="display: flex; gap: 0.75rem; flex-wrap: wrap">
  <dev-button id="toastSuccess" theme="light" size="sm">Success</dev-button>
  <dev-button id="toastInfo" theme="light" size="sm" variant="secondary">Info</dev-button>
</div>
<script>
  const toastStack = document.getElementById("toastStack");
  const successBtn = document.getElementById("toastSuccess");
  const infoBtn = document.getElementById("toastInfo");
  successBtn?.addEventListener("click", () => {
    toastStack?.pushToast?.({ title: "Saved", message: "Settings updated", variant: "success" });
  });
  infoBtn?.addEventListener("click", () => {
    toastStack?.pushToast?.({ title: "Heads up", message: "Deploy queued", variant: "info" });
  });
</script>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.

Slots:

  • default - Mounted stack; trigger via queue-toast events.

Tooltip

Contextual hints shown on hover or focus. Use for icon buttons (what does this do?), truncated text, or supplementary info. Keep text brief (under 10 words). Accessible via keyboard. Don't hide critical info in tooltips.

<dev-tooltip theme="light" text="Copy to clipboard" placement="right">
  <dev-button slot="trigger" theme="light" variant="secondary" size="sm">Hover me</dev-button>
</dev-tooltip>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • text - string (default: "") - Message content (or use default slot).
  • placement - top | bottom | left | right (default: top) - Tooltip position relative to trigger.
  • open - boolean (default: false) - Force open state for testing.

Slots:

  • trigger - Element that receives hover/focus handlers.
  • default - Optional custom tooltip body instead of text attr.

Popover

Rich content overlay anchored to trigger element. Use for menus, option pickers, or content that needs more space than tooltips. Click-triggered (vs tooltip's hover). Great for filter panels, color pickers, or inline forms.

<div style="display: flex; gap: 1rem; flex-wrap: wrap">
  <dev-button id="popoverTrigger" theme="light" variant="secondary" size="sm">Open Popover</dev-button>
  <dev-popover id="myPopover" theme="light" placement="bottom">
    <p style="margin: 0px">This is a popover with additional content and actions.</p>
  </dev-popover>
</div>
<script>
  window.addEventListener("DOMContentLoaded", () => {
    const popover = document.getElementById("myPopover");
    const trigger = document.getElementById("popoverTrigger");
    if (popover && trigger && popover.setTrigger && popover.toggle) {
      popover.setTrigger(trigger);
      trigger.addEventListener("click", () => popover.toggle());
    }
  });
</script>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • open - boolean (default: false) - Controls visibility of the popover.
  • placement - top | top-start | top-end | bottom | bottom-start | bottom-end | left | left-start | left-end | right | right-start | right-end (default: bottom) - Position relative to trigger.
  • offset - number (default: 8) - Distance from trigger in pixels.

Slots:

  • default - Popover content.

Hover Card

Preview card triggered by hover (with delay). Perfect for user profiles (@mentions), link previews, or item details in lists. Provides rich context without navigation. More patient than tooltips (waits before showing).

<div style="display: flex; gap: 0.5rem; align-items: center">
  <span id="hoverCardTrigger" style="text-decoration: underline; cursor: pointer">@username</span>
  <dev-hover-card id="userCard" theme="light" placement="right">
    <div slot="title" style="font-weight: 600; margin-bottom: 0.5rem">User Profile</div>
    <p style="margin: 0px; font-size: 0.9rem; color: rgb(111, 102, 88)">
      Full-stack developer with 5 years of experience building web applications.
    </p>
  </dev-hover-card>
</div>
<script>
  window.addEventListener("DOMContentLoaded", () => {
    const hoverCard = document.getElementById("userCard");
    const trigger = document.getElementById("hoverCardTrigger");
    if (hoverCard && trigger && hoverCard.setTrigger) {
      hoverCard.setTrigger(trigger);
    }
  });
</script>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • open - boolean (default: false) - Controls visibility of the card.
  • placement - top | bottom | left | right (default: top) - Position relative to trigger.
  • open-delay - number (default: 200) - Delay in ms before opening.
  • close-delay - number (default: 300) - Delay in ms before closing.

Slots:

  • trigger - Element that triggers the hover card.
  • title - Card title content.
  • description - Card description content.
  • default - Additional card content.

Sheet

Slide-in panel from screen edges (right/left/top/bottom). Use for detail views, filters, shopping carts, or settings. Less blocking than Modal, more spacious than Popover. Mobile-friendly (especially bottom sheets). Keeps main content visible.

<div style="display: flex; gap: 0.75rem; flex-wrap: wrap">
  <dev-button id="sheetBtn1" theme="light" variant="secondary" size="sm">Open Right Sheet</dev-button>
  <dev-button id="sheetBtn2" theme="light" variant="secondary" size="sm">Open Bottom Sheet</dev-button>
  <dev-sheet id="sheet1" theme="light" side="right" title="Sheet Title">
    <p style="margin: 0px 0px 1rem 0px">This is a sheet that slides in from the right side.</p>
    <p style="margin: 0px">Use sheets for secondary content, forms, or detail views.</p>
  </dev-sheet>
  <dev-sheet id="sheet2" theme="light" side="bottom" title="Bottom Sheet">
    <p style="margin: 0px">This sheet slides in from the bottom.</p>
  </dev-sheet>
</div>
<script>
  window.addEventListener("DOMContentLoaded", () => {
    const sheet1 = document.getElementById("sheet1");
    const sheet2 = document.getElementById("sheet2");
    const btn1 = document.getElementById("sheetBtn1");
    const btn2 = document.getElementById("sheetBtn2");

    if (btn1 && sheet1) {
      btn1.addEventListener("click", () => {
        sheet1.open = true;
      });
    }

    if (btn2 && sheet2) {
      btn2.addEventListener("click", () => {
        sheet2.open = true;
      });
    }
  });
</script>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • open - boolean (default: false) - Controls visibility of the sheet.
  • side - left | right | top | bottom (default: right) - Edge from which the sheet slides in.
  • title - string (default: "") - Optional title for the sheet header.

Slots:

  • default - Sheet content area.

Events:

  • dev-sheet:close - CustomEvent<{ reason: "escape" | "overlay" | "button" }> - Emitted when the sheet is closed.

Data Display

Accordion

Collapsible content sections to save vertical space. Perfect for FAQs, documentation, settings groups, or anywhere dense information needs progressive disclosure. Supports single or multiple open panels. Improves scanability and reduces overwhelming UI.

<dev-accordion theme="light" multiple="">
  <div data-label="Setup">
    <p>Install the CLI and log in with your API key.</p>
  </div>
  <div data-label="Billing">
    <p>Track invoices, payouts, and tax settings in one place.</p>
  </div>
  <div data-label="Security">
    <p>Rotate signing keys every 24 hours with one click.</p>
  </div>
</dev-accordion>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • multiple - boolean (default: false) - Allow multiple panels open at once.
  • open-indexes - string (default: 0) - Comma-delimited open indices.

Slots:

  • <div data-label> - Section content; data-label becomes trigger text.

Events:

  • dev-accordion:toggle - CustomEvent<{ index: number; open: boolean }> - Emitted when an accordion section is toggled.

Avatar

Visual user representation showing profile image or initials fallback. Use in headers, comments, team lists, or anywhere user identity matters. Automatically generates colored backgrounds based on name. Supports multiple sizes for different contexts.

<div style="display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap">
  <dev-avatar theme="light" name="Ada Lovelace"></dev-avatar>
  <dev-avatar theme="light" name="Grace Hopper" src="https://i.pravatar.cc/64?img=1"></dev-avatar>
  <dev-avatar theme="light" name="Linus Torvalds" size="lg"></dev-avatar>
</div>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • src - string (default: "") - Image source for the avatar.
  • name - string (default: "") - Used for initials and aria-label.
  • size - sm | md | lg (default: md) - Avatar dimensions.

Slots:

  • default - No slots; set src/name attributes.

Avatar Group

Compact display of multiple users with overflow handling. Shows team members, collaborators, or participants without taking excessive space. Displays count when exceeding max ("+2 more"). Perfect for project cards, document collaborators, or event attendees.

<dev-avatar-group theme="light" max="3">
  <dev-avatar theme="light" name="Alex Kim"></dev-avatar>
  <dev-avatar theme="light" name="Morgan Lee"></dev-avatar>
  <dev-avatar theme="light" name="Jordan Smith"></dev-avatar>
  <dev-avatar theme="light" name="Taylor Shaw"></dev-avatar>
</dev-avatar-group>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • max - number (default: 4) - Maximum avatars before overflow counter.

Slots:

  • <dev-avatar> - Avatar children to render in the stack.

Badge

Small status indicator for metadata and states. Use for labels (Beta, New, Pro), statuses (Active, Pending, Failed), or categories. Color-coded variants provide instant visual meaning. Compact enough for inline use within text or table cells.

<div style="display: flex; gap: 0.5rem; flex-wrap: wrap">
  <dev-badge theme="light" variant="neutral">Beta</dev-badge>
  <dev-badge theme="light" variant="info">Preview</dev-badge>
  <dev-badge theme="light" variant="success">Deployed</dev-badge>
  <dev-badge theme="light" variant="warning">Limited</dev-badge>
  <dev-badge theme="light" variant="danger">Breaking</dev-badge>
</div>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • variant - neutral | accent | info | success | warning | danger (default: neutral) - Color palette for the badge.

Slots:

  • default - Inline text/emoji/icon.

Card

Flexible content container with optional header and footer. Universal building block for dashboards, product grids, blog posts, or any grouped content. Supports elevation levels for depth. Provides consistent padding and visual boundaries.

<dev-card theme="light">
  <div slot="header">
    <strong>Card Header</strong>
  </div>
  <div>
    <p>Card content lives here. Keep it simple and readable.</p>
  </div>
  <div slot="footer">Footer actions</div>
</dev-card>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • elevation - none | sm | md | lg (default: md) - Shadow preset for the surface.
  • padding - spacing token (default: 6) - Padding scale using spacing tokens.

Slots:

  • header - Top content row.
  • default - Body content.
  • footer - Footer actions or meta.

Code Block

Syntax-highlighted code display with optional filename and line numbers. Essential for documentation, tutorials, API references, or blogs. Provides professional code presentation with language detection. Includes copy-to-clipboard functionality.

<dev-code-block theme="light" language="typescript" filename="example.ts"
  >const greet = (name: string) =&gt; { return 'Hello, ' + name + '!'; };</dev-code-block
>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • language - string (default: typescript) - Language hint for highlighting.
  • filename - string (default: "") - Optional top label for the code.
  • show-line-numbers - boolean (default: true) - Toggles line number gutter.

Slots:

  • default - Code content as text.

Empty State

Friendly placeholder for empty lists, searches, or sections. Guides users to first action instead of showing blank space. Reduces confusion and abandonment. Include clear CTAs to help users get started. Better UX than "No results found".

<dev-empty-state
  theme="light"
  title="No monitors yet"
  description="Create your first check to keep uptime in sight."
  icon="⏱">
  <dev-button slot="actions" theme="light">Create monitor</dev-button>
  <dev-button slot="actions" theme="light" variant="secondary">View docs</dev-button>
</dev-empty-state>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • title - string (default: "") - Headline for the empty state.
  • description - string (default: "") - Supporting text.
  • icon - string (default: O) - Emoji or character to display.

Slots:

  • actions - Action buttons placed below text.

Table

Simple data table with optional sorting. Use for small-to-medium datasets (under 100 rows) with basic display needs. Handles empty and loading states gracefully. For advanced features (filtering, pagination), use DataTable instead.

<dev-table
  theme="light"
  columns='[{"key":"name","label":"Name","sortable":true},{"key":"role","label":"Role"},{"key":"status","label":"Status","sortable":true}]'
  rows='[{"name":"api.gateway","role":"Service","status":"Healthy"},{"name":"billing.worker","role":"Worker","status":"Degraded"},{"name":"docs","role":"Static","status":"Healthy"}]'>
</dev-table>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • columns - JSON (default: []) - Column definitions with label/sortable.
  • rows - JSON (default: []) - Row objects keyed by column keys.
  • loading - boolean (default: false) - Shows loading placeholder row.
  • empty-text - string (default: No data available) - Message when no rows exist.

Slots:

  • default - Not used; data-driven via attributes.
  • empty-action - Optional action button shown in empty state.

Tag

Interactive labels for categories, filters, or selections. Use solid variant for static labels (keywords, topics). Enable filter mode for toggleable filters (search refinement). Add dismissible for removable selections (applied filters, email chips).

<div style="display: flex; gap: 0.5rem; flex-wrap: wrap">
  <dev-tag theme="light">Runtime</dev-tag>
  <dev-tag theme="light" variant="outline" filter="" selected="">Serverless</dev-tag>
  <dev-tag theme="light" dismissible="">Clear</dev-tag>
</div>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • variant - solid | outline (default: solid) - Fill vs outline style.
  • dismissible - boolean (default: false) - Show close button when set.
  • filter - boolean (default: false) - Adds toggle behavior for filters.
  • selected - boolean (default: false) - Selected state (when filter=true).

Slots:

  • default - Tag label.

Events:

  • dev-tag:dismiss - CustomEvent<{}> - Emitted when the tag is dismissed (dismissible=true).
  • dev-tag:toggle - CustomEvent<{ selected: boolean }> - Emitted when the tag filter state is toggled (filter=true).

Aspect Ratio

Forces content to maintain specific width:height ratio. Essential for responsive images, video embeds, or placeholder content that must preserve proportions. Prevents layout shift during load. Common ratios: 16:9 (video), 4:3 (photos), 1:1 (avatars).

<div style="display: grid; gap: 1rem; max-width: 600px">
  <dev-aspect-ratio theme="light" ratio="16/9">
    <div
      style="
        width: 100%;
        height: 100%;
        background: linear-gradient(135deg, #2e7d6e 0%, #276a5e 100%);
        display: flex;
        align-items: center;
        justify-content: center;
        color: white;
      ">
      16:9 Aspect Ratio
    </div>
  </dev-aspect-ratio>
  <dev-aspect-ratio theme="light" ratio="4/3">
    <div
      style="
        width: 100%;
        height: 100%;
        background: linear-gradient(135deg, #6f6658 0%, #4a433a 100%);
        display: flex;
        align-items: center;
        justify-content: center;
        color: white;
      ">
      4:3 Aspect Ratio
    </div>
  </dev-aspect-ratio>
</div>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • ratio - string (default: 16/9) - Aspect ratio as fraction (16/9) or decimal (1.77).

Slots:

  • default - Content to maintain aspect ratio for.

Carousel

Horizontal content slider with navigation controls and indicators. Use for image galleries, testimonials, feature showcases, or product views. Supports auto-play, looping, and swipe gestures. Caution: can reduce accessibility and engagement if overused.

<div style="max-width: 600px">
  <dev-carousel theme="light" show-indicators="" show-controls="" loop="">
    <div
      style="
        width: 100%;
        height: 300px;
        background: linear-gradient(135deg, #2e7d6e 0%, #276a5e 100%);
        display: flex;
        align-items: center;
        justify-content: center;
        color: white;
        font-size: 2rem;
        font-weight: 600;
      ">
      Slide 1
    </div>
    <div
      style="
        width: 100%;
        height: 300px;
        background: linear-gradient(135deg, #6f6658 0%, #4a433a 100%);
        display: flex;
        align-items: center;
        justify-content: center;
        color: white;
        font-size: 2rem;
        font-weight: 600;
      ">
      Slide 2
    </div>
    <div
      style="
        width: 100%;
        height: 300px;
        background: linear-gradient(135deg, #084ccf 0%, #0639a1 100%);
        display: flex;
        align-items: center;
        justify-content: center;
        color: white;
        font-size: 2rem;
        font-weight: 600;
      ">
      Slide 3
    </div>
  </dev-carousel>
</div>

Events:

  • dev-carousel:change - CustomEvent<{ currentIndex: number; previousIndex: number }> - Emitted when the active slide changes.

Calendar

Standalone month grid for date selection without input field. Use when building custom date pickers or scheduling interfaces. Supports same modes as DatePicker (single/multiple/range). More flexible than DatePicker but requires custom UI integration.

<div style="max-width: 420px">
  <dev-calendar id="demoCalendar" theme="light" mode="range" month="2024-08" selected='["2024-08-05","2024-08-08"]'>
  </dev-calendar>
</div>
<script>
  const demoCalendar = document.getElementById("demoCalendar");
  demoCalendar?.addEventListener("date-select", (e) => {
    console.log("Selected", e.detail);
  });
</script>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • mode - single | multiple | range (default: single) - Selection behavior.
  • selected - string | JSON (default: "") - ISO date string (single) or JSON array for multiple/range.
  • month - string (default: current month) - Month anchor as YYYY-MM.

Slots:

  • default - Not used; driven by attributes.

Events:

  • dev-calendar:select - CustomEvent<{ mode: string; selected: string | string[] }> - Emitted when date selection changes.

Chart

Simple SVG charts for basic data visualization. Use for dashboards, reports, or analytics views. Supports line (trends), bar (comparisons), and area (volumes) types. Lightweight alternative to heavy charting libraries for simple use cases.

<dev-chart
  theme="light"
  type="area"
  data='[{"label":"Mon","value":12},{"label":"Tue","value":18},{"label":"Wed","value":9},{"label":"Thu","value":22},{"label":"Fri","value":16}]'>
</dev-chart>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • type - line | bar | area (default: line) - Chart visualization style.
  • data - JSON (default: []) - Array of { label, value } or number list.

Slots:

  • default - Not used; data-driven via attributes.

Data Table

Full-featured table with built-in filtering, sorting, and pagination. Use for large datasets (100+ rows), admin panels, or data-heavy applications. More powerful than Table component. Handles performance and UX concerns for big data automatically.

<dev-data-table
  theme="light"
  page-size="5"
  columns='[{"key":"service","label":"Service","sortable":true},{"key":"region","label":"Region"},{"key":"status","label":"Status","sortable":true}]'
  rows='[
            {"service":"api.gateway","region":"us-east","status":"healthy"},
            {"service":"billing","region":"us-east","status":"degraded"},
            {"service":"auth","region":"eu-west","status":"healthy"},
            {"service":"jobs","region":"us-west","status":"healthy"},
            {"service":"metrics","region":"eu-west","status":"maintenance"},
            {"service":"docs","region":"us-east","status":"healthy"}
          ]'>
</dev-data-table>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • columns - JSON (default: []) - Column definitions with label/sortable.
  • rows - JSON (default: []) - Row objects keyed by column keys.
  • page-size - number (default: 5) - Rows per page for pagination.
  • loading - boolean (default: false) - Show loading placeholder row.
  • empty-text - string (default: No results found) - Empty state copy.

Slots:

  • default - Not used; data-driven via attributes.
  • empty-action - Optional action button shown in empty state.

Layout

Card Layout

Auto-responsive grid for card collections. Automatically adjusts columns based on viewport width. Use for blog previews, product grids, feature showcases, or any card-based content. Optional masonry mode for Pinterest-style layouts with varying heights.

<dev-card-layout theme="light">
  <dev-card theme="light">
    <p>Card A</p>
  </dev-card>
  <dev-card theme="light">
    <p>Card B</p>
  </dev-card>
  <dev-card theme="light">
    <p>Card C</p>
  </dev-card>
</dev-card-layout>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • gap - string (default: 1rem) - Gap between tiles.
  • columns - number (default: 3) - Column hint when masonry=true.
  • masonry - boolean (default: false) - Switch to CSS columns for masonry.

Slots:

  • default - Card children to distribute in the grid/columns.

Cluster

Horizontal layout that wraps inline elements with consistent spacing. Perfect for button groups, tags, filters, or badges that should flow naturally. Automatically wraps to multiple rows when space is constrained. Simpler than Grid for linear content.

<dev-cluster theme="light" gap="0.75rem">
  <dev-button theme="light" size="sm">Save</dev-button>
  <dev-button theme="light" variant="secondary" size="sm">Cancel</dev-button>
  <dev-button theme="light" variant="ghost" size="sm">Help</dev-button>
</dev-cluster>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • gap - string (default: 0.75rem) - Spacing between items.
  • align - CSS align-items (default: center) - Cross-axis alignment.
  • justify - CSS justify-content (default: flex-start) - Main-axis alignment.

Slots:

  • default - Inline children (buttons, badges, etc).

Grid

Responsive CSS grid that auto-fills columns based on minimum width. More flexible than CardLayout with customizable column behavior. Use for dashboard tiles, feature grids, or any equal-width content. Automatically adapts column count to available space.

<dev-grid theme="light" gap="1rem" min="180px">
  <dev-card theme="light">
    <p>Tile A</p>
  </dev-card>
  <dev-card theme="light">
    <p>Tile B</p>
  </dev-card>
  <dev-card theme="light">
    <p>Tile C</p>
  </dev-card>
  <dev-card theme="light">
    <p>Tile D</p>
  </dev-card>
</dev-grid>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • gap - string (default: 1rem) - Gap between grid items.
  • columns - number (default: 3) - Suggested column count for fallback layouts.
  • min - string (default: 240px) - Min column width for auto-fit behavior.

Slots:

  • default - Child cards/blocks to grid.

Page Section

Full-width section with centered content container. Provides consistent max-width and padding for marketing pages, documentation, or content sites. Creates visual rhythm and prevents line-length issues. Standard building block for multi-section pages.

<dev-page-section theme="light">
  <h2 style="margin: 0px">Deployments</h2>
  <p style="margin: 0px">Ship production ready releases with review gates.</p>
</dev-page-section>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • max-width - string (default: 1280px) - Content max width.
  • padding - string (default: 80px 16px) - Outer padding for the section.

Slots:

  • default - Section content; typically headings and layout primitives.

Sidebar

Application layout with persistent side navigation and main content area. Essential for dashboards, admin panels, documentation sites, or settings pages. Supports collapse/expand for mobile. Maintains scroll positions independently for nav and content.

<dev-sidebar theme="light">
  <a slot="nav" data-active="true" href="#overview">Overview</a>
  <a slot="nav" href="#usage">Usage</a>
  <a slot="nav" href="#settings">Settings</a>
  <dev-card theme="light">
    <p>Page content lives here.</p>
  </dev-card>
</dev-sidebar>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • collapsed - boolean (default: false) - Collapses nav to compact width.
  • width - string (default: 260px) - Expanded sidebar width.

Slots:

  • nav - Navigation links for the sidebar column.
  • default - Main content area.

Events:

  • dev-sidebar:toggle - CustomEvent<{ collapsed: boolean }> - Emitted when the sidebar collapse state changes.

Spacer

Empty vertical space component for precise spacing control. Use when layout components (Stack, Grid) don't provide enough control. Helpful for one-off spacing adjustments or maintaining design system spacing between disparate components.

<div>
  <dev-card theme="light">
    <p>Block above spacer</p>
  </dev-card>
  <dev-spacer theme="light" size="24px"></dev-spacer>
  <dev-card theme="light">
    <p>Block below spacer</p>
  </dev-card>
</div>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • size - string (default: 24px) - Height of the spacer block.

Slots:

  • default - None; spacer renders empty block.

Split

Two-column layout with customizable width ratio. Perfect for content+sidebar, image+text, or form+preview layouts. Ratio is adjustable (1fr 1fr for equal, 2fr 1fr for 2:1, etc). Stacks vertically on mobile for responsive behavior.

<dev-split theme="light" ratio="2fr 1fr">
  <dev-card theme="light">
    <p>Primary content area.</p>
  </dev-card>
  <dev-card theme="light">
    <p>Side content.</p>
  </dev-card>
</dev-split>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • gap - string (default: 24px) - Gap between columns.
  • ratio - string (default: 1fr 1fr) - Grid template columns value.

Slots:

  • default - Two (or more) children to place in the grid.

Stack

Vertical layout with consistent spacing between children. Most common layout primitive for forms, card content, or any vertically stacked elements. Controls gap and horizontal alignment. Preferred over manual margins for maintainable spacing.

<dev-stack theme="light" gap="1rem" align="start">
  <dev-button theme="light" variant="primary">Primary</dev-button>
  <dev-button theme="light" variant="secondary">Secondary</dev-button>
  <dev-button theme="light" variant="ghost">Ghost</dev-button>
</dev-stack>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • gap - string (default: 16px) - Vertical spacing between children.
  • align - stretch | start | center | end (default: stretch) - Cross-axis alignment.

Slots:

  • default - Child nodes stacked vertically.

Sticky Footer

Fixed bottom bar that remains visible while scrolling. Use for persistent CTAs (chat support, cookie consent), form actions, or mobile navigation. Ensures important actions are always accessible. Consider impact on mobile viewports.

<dev-sticky-footer theme="light">
  <span>Need a hand? Chat with support.</span>
  <div slot="actions">
    <dev-button theme="light" variant="secondary" size="sm">Later</dev-button>
    <dev-button theme="light" size="sm">Contact</dev-button>
  </div>
</dev-sticky-footer>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.

Slots:

  • default - Left-aligned content.
  • actions - Right-aligned action buttons.

Scroll Area

Container with styled scrollbars and max-height/width constraints. Use for logs, code output, chat histories, or any content that may overflow. Provides consistent cross-browser scrollbar styling. Prevents layout breaking from oversized content.

<div style="max-width: 500px">
  <dev-scroll-area theme="light" max-height="200px" orientation="vertical">
    <div style="padding: 1rem">
      <h3 style="margin: 0px 0px 0.75rem 0px">Scrollable Content</h3>
      <p style="margin: 0px 0px 0.5rem 0px">
        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore
        magna aliqua.
      </p>
      <p style="margin: 0px 0px 0.5rem 0px">
        Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
      </p>
      <p style="margin: 0px 0px 0.5rem 0px">
        Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
      </p>
      <p style="margin: 0px">
        Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
      </p>
    </div>
  </dev-scroll-area>
</div>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • orientation - vertical | horizontal | both (default: vertical) - Scroll direction(s).
  • max-height - string (default: "") - Maximum height for vertical scrolling.
  • max-width - string (default: "") - Maximum width for horizontal scrolling.

Slots:

  • default - Scrollable content.

Command

Keyboard-first command palette (Cmd/Ctrl+K) for power users. Inspired by VS Code, Slack, Linear. Enables fast navigation and actions without mouse. Perfect for developer tools, productivity apps, or complex dashboards. Supports fuzzy search and keyboard shortcuts.

<div>
  <dev-button id="cmdPaletteBtn" theme="light" variant="secondary" size="sm">Open Command Palette</dev-button>
  <dev-command id="cmdPalette" theme="light" placeholder="Type a command…">
    <div
      data-command-item=""
      data-id="new"
      data-label="Create new file"
      data-icon="πŸ“„"
      data-group="File"
      data-shortcut="Cmd+N"></div>
    <div
      data-command-item=""
      data-id="save"
      data-label="Save file"
      data-icon="πŸ’Ύ"
      data-group="File"
      data-shortcut="Cmd+S"></div>
    <div
      data-command-item=""
      data-id="search"
      data-label="Search files"
      data-icon="πŸ”"
      data-group="Edit"
      data-shortcut="Cmd+P"></div>
    <div data-command-item="" data-id="settings" data-label="Open settings" data-icon="βš™οΈ" data-group="View"></div>
  </dev-command>
</div>
<script>
  window.addEventListener("DOMContentLoaded", () => {
    const palette = document.getElementById("cmdPalette");
    const openBtn = document.getElementById("cmdPaletteBtn");

    if (palette && palette.setItems) {
      palette.setItems([
        { label: "Create new file", shortcut: "Cmd+N" },
        { label: "Save file", shortcut: "Cmd+S" },
        { label: "Search files", shortcut: "Cmd+P" },
        { label: "Open settings" },
      ]);
    }

    if (openBtn && palette) {
      openBtn.addEventListener("click", () => {
        palette.open = true;
      });
    }
  });
</script>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • open - boolean (default: false) - Controls visibility of the command palette.
  • placeholder - string (default: Type a command&hellip;) - Placeholder text for the search input.

Slots:

  • <div data-command-item> - Command items with data attributes (id, label, icon, group, shortcut).

Events:

  • dev-command:select - CustomEvent<{ item: HTMLElement }> - Emitted when a command item is selected.

Context Menu

Right-click contextual actions menu. Provides native-feeling interactions for file managers, kanban boards, or content editors. Shows relevant actions based on selected item. Includes keyboard shortcuts display for discoverability.

<div>
  <div
    id="ctxTarget"
    style="
      padding: 2rem;
      border: 2px dashed rgb(46, 125, 110);
      border-radius: 0.5rem;
      text-align: center;
      cursor: default;
    ">
    Right-click me to open context menu
  </div>
  <dev-context-menu id="ctxMenu" theme="light"></dev-context-menu>
</div>
<script>
  window.addEventListener("DOMContentLoaded", () => {
    const ctxMenu = document.getElementById("ctxMenu");
    const ctxTarget = document.getElementById("ctxTarget");
    if (ctxMenu && ctxTarget && ctxMenu.setItems && ctxMenu.setTrigger) {
      ctxMenu.setItems([
        { label: "Copy", icon: "πŸ“‹", shortcut: "Cmd+C" },
        { label: "Cut", icon: "βœ‚οΈ", shortcut: "Cmd+X" },
        { label: "Paste", icon: "πŸ“„", shortcut: "Cmd+V" },
        { separator: true },
        { label: "Delete", icon: "πŸ—‘οΈ", shortcut: "Del" },
      ]);
      ctxMenu.setTrigger(ctxTarget);
    }
  });
</script>

Attributes:

  • theme - light | dark (default: light) - Token palette applied inside the shadow DOM.
  • open - boolean (default: false) - Controls visibility of the menu.

Slots:

  • default - No slots; items controlled via setItems() and setTrigger() methods.

Events:

  • dev-context-menu:item-select - CustomEvent<{ item: HTMLElement }> - Emitted when a context menu item is selected.

Resizable

Draggable split pane with user-adjustable sizing. Essential for code editors, file browsers, or any interface where users want control over panel widths. Persists sizes to localStorage. Respects min/max constraints. Horizontal or vertical orientation.

<div style="height: 400px; border: 1px solid rgb(221, 221, 221); border-radius: 0.5rem; overflow: hidden">
  <dev-resizable theme="light" size="300" min-size="150" max-size="500" orientation="horizontal">
    <div slot="panel" style="padding: 1rem; background: rgb(245, 241, 234); height: 100%">
      <h3 style="margin: 0px 0px 0.5rem 0px">Resizable Panel</h3>
      <p style="margin: 0px; font-size: 0.875rem; color: rgb(111, 102, 88)">
        Drag the handle to resize this panel. Min: 150px, Max: 500px.
      </p>
    </div>
    <div style="padding: 1rem; height: 100%">
      <h3 style="margin: 0px 0px 0.5rem 0px">Main Content</h3>
      <p style="margin: 0px; font-size: 0.875rem">This area expands as you resize the left panel.</p>
    </div>
  </dev-resizable>
</div>

Events:

  • dev-resizable:resize - CustomEvent<{ width: number; height: number }> - Emitted during resize operations.
  • dev-resizable:resize-end - CustomEvent<{ width: number; height: number }> - Emitted when resize operation completes.

Item

Flexible list item with icon, label, description, and trailing content. Building block for settings lists, preference panels, or feature toggles. Supports selected and disabled states. Use for structured lists with consistent formatting.

<div style="display: grid; gap: 0.5rem; max-width: 400px">
  <dev-item theme="light" label="Email Notifications" description="Receive updates via email" icon="πŸ“§"></dev-item>
  <dev-item theme="light" label="Push Notifications" description="Get alerts on your device" icon="πŸ””" selected="">
  </dev-item>
  <dev-item theme="light" label="SMS Notifications" description="Text message alerts" icon="πŸ’¬" disabled=""></dev-item>
  <dev-item theme="light" label="Digest Mode" description="Weekly summary of activity" icon="πŸ“Š">
    <span slot="trailing" style="font-size: 0.75rem; color: rgb(46, 125, 110); font-weight: 600">NEW</span>
  </dev-item>
</div>

Events:

  • dev-item:click - CustomEvent<{ value: string; disabled: boolean }> - Emitted when the item is clicked.

Design Tokens

Colors

The design system includes two unified color palettes:

  • Light Theme - Clean white backgrounds with dark code blocks
  • Dark Theme - Pure black backgrounds with minimal elevation
  • Unified Accent - Clean blue (#0EA5E9) across both themes for consistency

Typography

  • Sans-serif: Inter/SF Pro system fonts
  • Monospace: JetBrains Mono/Fira Code for code
  • Scales: From 12px (xs) to 64px (7xl)
  • Weights: 400-800 range

Spacing

Consistent spacing scale from 4px to 128px based on the 80-120px section rhythm from the design analysis.

Architecture

All components extend BaseComponent which provides:

  • Shadow DOM encapsulation
  • Theme attribute handling
  • Lifecycle management
  • Helper methods for rendering

Components are built with:

  • Native Custom Elements API
  • TypeScript for type safety
  • CSS-in-JS via template literals
  • No external dependencies

Browser Support

  • Modern browsers with Custom Elements v1 support
  • Chrome/Edge 67+
  • Firefox 63+
  • Safari 10.1+

License

MIT

About

Flat zero-runtime web components design system in TypeScript, light/dark themed. No framework, drop into any site.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages