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

@jasonshimmy/cer-material

v0.3.4

Published

Material Design 3 web components built on @jasonshimmy/custom-elements-runtime

Downloads

920

Readme

@jasonshimmy/cer-material

Material Design 3 web components built on @jasonshimmy/custom-elements-runtime.

All components are standard custom elements — framework-agnostic and usable in plain HTML, React, Vue, Angular, Svelte, or any other environment.

For live demos and usage examples, see https://cer-material.netlify.app/.

Learn more about the author at jasonshimmy.com and check out the changelog for recent updates.


Installation

npm install @jasonshimmy/cer-material @jasonshimmy/custom-elements-runtime

@jasonshimmy/custom-elements-runtime is a peer dependency and must be installed alongside this package.

Font setup

Components use Material Symbols and Roboto. Add both to your HTML <head>:

<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet" />
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20,400,0,0&display=swap" rel="stylesheet" />

CDN / No build tool

A self-contained IIFE bundle (runtime included) is available for use without a bundler — ideal for CodePen, JSFiddle, plain HTML files, or any environment where you just want a <script> tag.

Add the fonts, theme stylesheet, and bundle script to your HTML <head>:

<!-- Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet" />
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20,400,0,0&display=swap" rel="stylesheet" />

<!-- MD3 design tokens -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jasonshimmy/cer-material/dist/theme.css" />

<!-- cer-material (runtime included) -->
<script src="https://cdn.jsdelivr.net/npm/@jasonshimmy/cer-material/dist/cer-material.iife.js"></script>

All components are registered automatically once the script loads — no import needed:

<md-button variant="filled" label="Save"></md-button>
<md-text-field label="Email" type="email"></md-text-field>

Note: The IIFE bundle includes the runtime (~78 kB gzip). If you are also using @jasonshimmy/custom-elements-runtime directly in the same page, be aware the runtime will be loaded twice.


Setup in CER App Framework

Install dependencies:

npm install typeface-roboto material-symbols @jasonshimmy/cer-material

Create a plugin in your CER App Framework project:

// plugins/cer-material.ts
import 'typeface-roboto';
import 'material-symbols/outlined.css';
import '@jasonshimmy/cer-material/theme.css';
import '@jasonshimmy/cer-material';

export default { name: 'cer-material' };

Quick start

// Import the MD3 design token theme (CSS custom properties)
import '@jasonshimmy/cer-material/theme.css';

// Register all components
import '@jasonshimmy/cer-material';

Then use any component tag directly in your HTML or templates:

<md-button variant="filled" label="Save"></md-button>
<md-text-field label="Email" type="email"></md-text-field>

Theme only

If you need the MD3 CSS custom properties without registering components:

import '@jasonshimmy/cer-material/theme.css';

Two-way bindings (:model)

All stateful components emit update:* events enabling concise two-way data binding with :model — no need to manually pair a :prop setter with a @event handler.

| Syntax | Syncs | Components | |---|---|---| | :model="${ref}" | value | md-text-field, md-slider, md-search, md-date-picker, md-time-picker | | :model:checked="${ref}" | checked | md-checkbox | | :model:selected="${ref}" | selected | md-switch, md-chip (filter), md-icon-button (toggle), md-segmented-button | | :model:activeTab="${ref}" | active tab id | md-tabs | | :model:active="${ref}" | active item id | md-navigation-bar, md-navigation-rail, md-navigation-drawer | | :model:open="${ref}" | open / visible | md-dialog, md-menu, md-snackbar, md-bottom-sheet, md-side-sheet, md-fab-menu, md-navigation-drawer, md-date-picker, md-time-picker |

All original change, close, tab-change, and other events still fire for backward compatibility — :model is additive.

// Verbose (still works)
<md-text-field :value="${email}" @change="${e => email = e.detail}"></md-text-field>

// Concise with :model
<md-text-field :model="${email}"></md-text-field>

Radio groups: :model:checked writes the radio's value string to the ref when selected, but does not derive the checked boolean. Use :checked="${selected === radio.value}" for the display state alongside @change for radio groups.


Design tokens

The theme is a plain CSS file (@jasonshimmy/cer-material/theme.css) that exposes every MD3 system token as a CSS custom property on :root. Override them after importing to customise the palette:

:root {
  --md-sys-color-primary: #0057b8;
  --md-sys-color-on-primary: #ffffff;
  --md-sys-color-primary-container: #d6e4ff;
}

Dark mode is handled automatically via @media (prefers-color-scheme: dark).

Available token groups:

| Group | Example properties | |---|---| | Primary | --md-sys-color-primary, --md-sys-color-on-primary, --md-sys-color-primary-container | | Secondary | --md-sys-color-secondary, --md-sys-color-secondary-container | | Tertiary | --md-sys-color-tertiary, --md-sys-color-tertiary-container | | Error | --md-sys-color-error, --md-sys-color-error-container | | Surface | --md-sys-color-surface, --md-sys-color-surface-variant, --md-sys-color-surface-container | | Outline | --md-sys-color-outline, --md-sys-color-outline-variant | | Elevation | --md-sys-elevation-1--md-sys-elevation-4 | | Typography | --md-sys-typescale-font | | Shape | --md-sys-shape-corner-none--md-sys-shape-corner-full |


Components

<md-app-bar>

MD3 top app bar with four layout variants, a leading navigation icon, title area, and trailing action icons.

| Prop | Type | Default | Description | |---|---|---|---| | variant | 'small' \| 'medium' \| 'large' \| 'center' | 'small' | Layout and title size | | title | string | '' | Title text | | leading-icon | string | 'menu' | Material Symbol for the leading nav button | | trailing-icons | string[] | [] | Array of Material Symbol names for trailing action buttons | | scrolled | boolean | false | Applies elevated/tinted scroll state |

Slots: title — custom title content; trailing — completely custom trailing area.

Events: nav — leading icon clicked; action (detail: string) — the clicked icon name.

<md-app-bar
  variant="small"
  title="Inbox"
  leading-icon="menu"
  :bind="${{ trailingIcons: ['search', 'more_vert'] }}"
  @nav="${openDrawer}"
  @action="${handleAction}"
></md-app-bar>

<md-badge>

Overlays a numeric label or a small dot indicator on top of slotted content.

| Prop | Type | Default | Description | |---|---|---|---| | value | string \| number | '' | Badge text; empty renders a small dot | | small | boolean | false | Forces the small dot style regardless of value |

Slots: default — the element the badge attaches to.

<md-badge value="3">
  <md-icon-button icon="notifications"></md-icon-button>
</md-badge>

<md-bottom-sheet>

Slide-up bottom sheet with optional drag-to-dismiss, focus trap, and scroll lock.

| Prop | Type | Default | Description | |---|---|---|---| | open | boolean | false | Shows/hides the sheet | | headline | string | '' | Sheet header text | | show-handle | boolean | true | Renders the drag handle pill | | variant | 'standard' \| 'modal' | 'standard' | modal adds a scrim overlay |

Slots: default — sheet body content.

Events: close — sheet dismissed.

<md-bottom-sheet
  :model:open="${isOpen}"
  headline="Options"
  variant="modal"
>
  <p>Your content here</p>
</md-bottom-sheet>

<md-button>

MD3 button in five style variants with optional leading icon.

| Prop | Type | Default | Description | |---|---|---|---| | variant | 'filled' \| 'outlined' \| 'text' \| 'elevated' \| 'tonal' | 'filled' | Visual style | | label | string | '' | Button text | | icon | string | '' | Leading Material Symbol name | | type | 'button' \| 'submit' \| 'reset' | 'button' | Native button type | | disabled | boolean | false | Disables the button |

<md-button variant="filled" label="Save" icon="save"></md-button>
<md-button variant="outlined" label="Cancel"></md-button>
<md-button variant="text" label="Learn more"></md-button>

<md-button-group>

Horizontally connected group of buttons sharing a unified variant style.

| Prop | Type | Default | Description | |---|---|---|---| | variant | 'filled' \| 'outlined' \| 'tonal' \| 'text' \| 'elevated' | 'outlined' | Shared style for all items | | disabled | boolean | false | Disables all buttons | | items | { id: string; label: string; icon?: string; disabled?: boolean }[] | [] | Button definitions |

Events: click (detail: { id: string; index: number }) — a button was clicked.

<md-button-group
  variant="outlined"
  :bind="${{ items: [
    { id: 'day', label: 'Day' },
    { id: 'week', label: 'Week' },
    { id: 'month', label: 'Month' },
  ] }}"
  @click="${handleGroupClick}"
></md-button-group>

<md-card>

MD3 card container that optionally becomes an interactive button with a ripple state layer.

| Prop | Type | Default | Description | |---|---|---|---| | variant | 'elevated' \| 'filled' \| 'outlined' | 'elevated' | Visual style | | clickable | boolean | false | Makes the whole card an accessible button |

Slots: default — card content.

Events: click — emitted when clickable is true.

<md-card variant="outlined" clickable @click="${openDetail}">
  <h3>Card Title</h3>
  <p>Supporting text goes here.</p>
</md-card>

<md-carousel>

Horizontal snap-scroll carousel rendering image or color-placeholder cards with overlay text.

| Prop | Type | Default | Description | |---|---|---|---| | items | { id: string; headline?: string; supportingText?: string; image?: string; color?: string }[] | [] | Carousel item data | | variant | 'multi-browse' \| 'uncontained' \| 'full-screen' | 'multi-browse' | Layout density |

Events: select (detail: string) — the id of the clicked card.

<md-carousel
  variant="multi-browse"
  :bind="${{ items: [
    { id: '1', headline: 'Mountains', image: '/img/mountains.jpg' },
    { id: '2', headline: 'Ocean', color: '#0077b6' },
  ] }}"
  @select="${handleSelect}"
></md-carousel>

<md-checkbox>

MD3 checkbox with animated check/dash icon and an optional inline text label.

| Prop | Type | Default | Description | |---|---|---|---| | checked | boolean | false | Checked state | | indeterminate | boolean | false | Indeterminate (dash) state | | disabled | boolean | false | Disables interaction | | label | string | '' | Inline label text |

Events: change (detail: boolean) — new checked state.

<md-checkbox
  label="Accept terms"
  :model:checked="${accepted}"
></md-checkbox>

<md-chip>

MD3 chip in four variants — assist, filter (toggle), input (removable), and suggestion.

| Prop | Type | Default | Description | |---|---|---|---| | variant | 'assist' \| 'filter' \| 'input' \| 'suggestion' | 'assist' | Chip type | | label | string | '' | Chip text | | icon | string | '' | Leading Material Symbol | | selected | boolean | false | Active state (filter variant) | | disabled | boolean | false | Disables interaction |

Events: click; remove — the × button was tapped (input variant only).

<md-chip variant="filter" label="Unread" :model:selected="${filterUnread}"></md-chip>
<md-chip variant="input" label="[email protected]" @remove="${removeChip}"></md-chip>

<md-date-picker>

Full MD3 date picker with calendar grid, month/year navigation, optional date range, and min/max constraints.

| Prop | Type | Default | Description | |---|---|---|---| | variant | 'dialog' \| 'docked' | 'dialog' | Modal dialog or inline docked layout | | value | string | '' | Selected date (YYYY-MM-DD) | | range-start | string | '' | Range start date (YYYY-MM-DD) | | range-end | string | '' | Range end date (YYYY-MM-DD) | | min | string | '' | Minimum selectable date | | max | string | '' | Maximum selectable date | | open | boolean | false | Controls visibility | | label | string | 'Select date' | Trigger field label | | aria-label | string | 'Date picker' | Accessible label |

Events: change (detail: string) — selected date in YYYY-MM-DD format; close.

<md-date-picker
  label="Start date"
  :model="${startDate}"
  min="2024-01-01"
  :model:open="${pickerOpen}"
></md-date-picker>

<md-dialog>

MD3 modal dialog with scale-in animation, optional icon/headline, scrollable body, and an actions slot.

| Prop | Type | Default | Description | |---|---|---|---| | open | boolean | false | Shows/hides the dialog | | headline | string | '' | Dialog title | | icon | string | '' | Leading Material Symbol in the header |

Slots: default — scrollable body content; actions — action buttons row.

Events: close — dialog dismissed.

<md-dialog :model:open="${dialogOpen}" headline="Delete item?">
  <p>This action cannot be undone.</p>
  <div slot="actions">
    <md-button variant="text" label="Cancel" @click="${() => dialogOpen = false}"></md-button>
    <md-button variant="filled" label="Delete" @click="${confirmDelete}"></md-button>
  </div>
</md-dialog>

<md-divider>

Thin horizontal or vertical separator rule with inset variants for list and layout use.

| Prop | Type | Default | Description | |---|---|---|---| | inset | boolean | false | Insets both ends | | inset-start | boolean | false | Insets the leading end only | | inset-end | boolean | false | Insets the trailing end only | | vertical | boolean | false | Renders as a vertical rule |

<md-divider></md-divider>
<md-divider inset-start></md-divider>
<md-divider vertical></md-divider>

<md-fab>

MD3 Floating Action Button in four color variants, four sizes, and an extended (icon + label) pill form.

| Prop | Type | Default | Description | |---|---|---|---| | variant | 'primary' \| 'secondary' \| 'tertiary' \| 'surface' | 'primary' | Background color role | | size | 'small' \| 'regular' \| 'medium' \| 'large' | 'regular' | Button size | | icon | string | 'add' | Material Symbol | | label | string | '' | Non-empty value makes it an extended FAB | | lowered | boolean | false | Reduces elevation | | aria-label | string | '' | Accessible label |

Events: click

<md-fab icon="edit" variant="primary"></md-fab>
<md-fab icon="add" label="Compose" variant="secondary" size="regular"></md-fab>

<md-fab-menu>

FAB speed dial that expands upward to reveal labeled action items. Only one instance can be open at a time.

| Prop | Type | Default | Description | |---|---|---|---| | icon | string | 'add' | Material Symbol for the collapsed state | | close-icon | string | 'close' | Material Symbol for the expanded state | | variant | 'primary' \| 'secondary' \| 'tertiary' | 'primary' | FAB color role | | open | boolean | false | Expanded state | | items | { id: string; icon: string; label: string; disabled?: boolean }[] | [] | Action item definitions | | aria-label | string | 'Speed dial' | Accessible label |

Events: open; close; select (detail: { id: string }) — an item was chosen.

<md-fab-menu
  :model:open="${fabOpen}"
  :bind="${{ items: [
    { id: 'share', icon: 'share', label: 'Share' },
    { id: 'copy', icon: 'content_copy', label: 'Copy link' },
  ] }}"
  @select="${handleFabSelect}"
></md-fab-menu>

<md-icon-button>

MD3 icon button with four style variants and optional toggle (pressed/selected) behaviour.

| Prop | Type | Default | Description | |---|---|---|---| | variant | 'standard' \| 'filled' \| 'tonal' \| 'outlined' | 'standard' | Visual style | | icon | string | 'more_vert' | Material Symbol | | selected | boolean | false | Pressed/selected state (toggle mode) | | selected-icon | string | '' | Alternate symbol shown when selected | | toggle | boolean | false | Enables toggle behaviour | | disabled | boolean | false | Disables interaction | | aria-label | string | '' | Accessible label |

Events: click; change (detail: boolean) — new selected state (toggle mode).

<md-icon-button icon="favorite_border" selected-icon="favorite" toggle :model:selected="${isFav}"></md-icon-button>
<md-icon-button icon="delete" variant="outlined" @click="${onDelete}"></md-icon-button>

<md-list> / <md-list-item>

MD3 list container and individual list items with leading/trailing content, headlines, supporting text, and selected/disabled states.

<md-list>

| Prop | Type | Default | Description | |---|---|---|---| | role | string | 'list' | ARIA role |

Slots: default — <md-list-item> children.

<md-list-item>

| Prop | Type | Default | Description | |---|---|---|---| | headline | string | '' | Primary text | | supporting-text | string | '' | Secondary text | | leading-icon | string | '' | Leading Material Symbol | | trailing-icon | string | '' | Trailing Material Symbol | | trailing-supporting-text | string | '' | Trailing metadata text | | disabled | boolean | false | Disables the item | | selected | boolean | false | Highlights the item as active | | type | 'text' \| 'link' \| 'checkbox' \| 'radio' | 'text' | Item interaction type |

Slots: leading — custom leading content; default — inline content after headline; trailing — custom trailing content.

Events: click

<md-list>
  <md-list-item headline="Inbox" leading-icon="inbox" trailing-supporting-text="24"></md-list-item>
  <md-list-item headline="Sent" leading-icon="send" selected></md-list-item>
  <md-list-item headline="Trash" leading-icon="delete" disabled></md-list-item>
</md-list>

<md-loading-indicator>

MD3 four-color indeterminate circular spinner.

| Prop | Type | Default | Description | |---|---|---|---| | size | 'small' \| 'medium' \| 'large' | 'medium' | Spinner size | | aria-label | string | 'Loading' | Accessible announcement text |

<md-loading-indicator size="large"></md-loading-indicator>

<md-menu>

Contextual dropdown menu with four anchor positions, keyboard navigation, and divider support.

| Prop | Type | Default | Description | |---|---|---|---| | open | boolean | false | Shows/hides the menu | | items | { id: string; label: string; icon?: string; disabled?: boolean; divider?: boolean }[] | [] | Menu item definitions | | anchor | 'bottom-start' \| 'bottom-end' \| 'top-start' \| 'top-end' | 'bottom-start' | Popup position relative to the trigger |

Slots: trigger — the element that opens the menu.

Events: select (detail: string) — the id of the chosen item; close.

<md-menu
  :model:open="${menuOpen}"
  :bind="${{ items: [
    { id: 'edit', label: 'Edit', icon: 'edit' },
    { id: 'dup', label: 'Duplicate', icon: 'content_copy' },
    { id: 'del', label: 'Delete', icon: 'delete', divider: true },
  ] }}"
  @select="${handleMenuSelect}"
>
  <md-icon-button slot="trigger" icon="more_vert" @click="${() => menuOpen = !menuOpen}"></md-icon-button>
</md-menu>

<md-navigation-bar>

MD3 bottom navigation bar for mobile with active pill indicator, filled icon for the active item, and badge support.

| Prop | Type | Default | Description | |---|---|---|---| | items | { id: string; label: string; icon: string; badge?: string }[] | [] | Destination definitions | | active | string | '' | id of the active destination |

Events: change (detail: string) — the id of the selected destination.

<md-navigation-bar
  :model:active="${activeTab}"
  :bind="${{ items: [
    { id: 'home', label: 'Home', icon: 'home' },
    { id: 'search', label: 'Search', icon: 'search' },
    { id: 'profile', label: 'Profile', icon: 'person', badge: '2' },
  ] }}"
></md-navigation-bar>

<md-navigation-drawer>

MD3 navigation drawer in standard (in-layout, width-animated) or modal (slide-in overlay) form with section headers and dividers.

| Prop | Type | Default | Description | |---|---|---|---| | open | boolean | false | Shows/hides the drawer | | headline | string | '' | Header text | | variant | 'standard' \| 'modal' | 'standard' | modal adds a scrim | | items | { id?: string; label?: string; icon?: string; section?: string; divider?: boolean; disabled?: boolean }[] | [] | Navigation item definitions | | active | string | '' | id of the active item |

Slots: default — extra content below the item list.

Events: close; change (detail: string) — the id of the selected item.

<md-navigation-drawer
  variant="modal"
  headline="Mail"
  :model:open="${drawerOpen}"
  :model:active="${currentRoute}"
  :bind="${{ items: navItems }}"
  @change="${e => navigate(e.detail)}"
></md-navigation-drawer>

<md-navigation-rail>

MD3 vertical navigation rail for tablet/desktop with optional top FAB and hamburger menu icon.

| Prop | Type | Default | Description | |---|---|---|---| | items | { id: string; label: string; icon: string; badge?: string }[] | [] | Destination definitions | | active | string | '' | id of the active destination | | fab | boolean | false | Shows a FAB at the top | | fab-icon | string | 'add' | FAB Material Symbol | | menu-icon | boolean | false | Shows a hamburger menu icon |

Events: change (detail: string) — the id of the selected destination; fab-click; menu-click.

<md-navigation-rail
  :model:active="${activeRoute}"
  menu-icon
  :bind="${{ items: [
    { id: 'inbox', label: 'Inbox', icon: 'inbox' },
    { id: 'sent', label: 'Sent', icon: 'send' },
  ] }}"
  @change="${e => navigate(e.detail)}"
></md-navigation-rail>

<md-progress>

MD3 progress indicator — linear (with buffer track) or circular — in determinate or indeterminate mode.

| Prop | Type | Default | Description | |---|---|---|---| | variant | 'linear' \| 'circular' | 'linear' | Indicator shape | | value | number | 0 | Progress value (0–100) | | indeterminate | boolean | false | Animated looping mode | | buffer | number | 100 | Buffer track value 0–100 (linear only) | | aria-label | string | 'Loading' | Accessible label |

<md-progress variant="linear" value="65"></md-progress>
<md-progress variant="circular" indeterminate></md-progress>

<md-radio>

MD3 radio button with animated inner circle, name/value for grouping, and an optional inline label.

| Prop | Type | Default | Description | |---|---|---|---| | checked | boolean | false | Selected state | | disabled | boolean | false | Disables interaction | | name | string | '' | Radio group name | | value | string | '' | Form value | | label | string | '' | Inline label text |

Events: change (detail: string) — the value of the selected radio.

<md-radio name="size" value="s" label="Small"></md-radio>
<md-radio name="size" value="m" label="Medium" checked></md-radio>
<md-radio name="size" value="l" label="Large"></md-radio>

<md-search>

MD3 search bar with a leading icon, animated clear button, and optional avatar.

| Prop | Type | Default | Description | |---|---|---|---| | value | string | '' | Current input value | | placeholder | string | 'Search' | Placeholder text | | leading-icon | string | 'search' | Material Symbol for the leading icon | | show-avatar | boolean | false | Renders an avatar button on the trailing end |

Events: clear; search (detail: string) — the search query when Enter is pressed.

<md-search
  placeholder="Search contacts"
  :model="${query}"
  @search="${e => runSearch(e.detail)}"
></md-search>

<md-segmented-button>

MD3 segmented button group for single-select or multi-select toggle behaviour.

| Prop | Type | Default | Description | |---|---|---|---| | segments | { id: string; label?: string; icon?: string; disabled?: boolean }[] | [] | Segment definitions | | selected | string \| string[] | '' | Selected segment id (or array for multi-select) | | multiselect | boolean | false | Allows multiple selections | | aria-label | string | '' | Accessible group label |

Events: change (detail: string | string[]) — selected segment id or array of ids (multiselect).

<md-segmented-button
  :bind="${{ segments: [
    { id: 'day', label: 'Day' },
    { id: 'week', label: 'Week' },
    { id: 'month', label: 'Month' },
  ] }}"
  :model:selected="${view}"
></md-segmented-button>

<md-side-sheet>

MD3 side sheet — standard (in-layout, right-side panel) or modal (slide-in from the right with scrim).

| Prop | Type | Default | Description | |---|---|---|---| | open | boolean | false | Shows/hides the sheet | | headline | string | '' | Header title text | | variant | 'standard' \| 'modal' | 'standard' | Layout mode | | divider | boolean | true | Renders a left-edge border | | show-back-button | boolean | false | Shows the back button in the header (modal variant) |

Slots: default — sheet body content.

Events: close; back — header back button clicked (modal variant).

<md-side-sheet variant="modal" headline="Details" :model:open="${sheetOpen}">
  <p>Side panel content</p>
</md-side-sheet>

<md-slider>

MD3 range slider with a custom-styled track, optional floating value label, and optional tick marks.

| Prop | Type | Default | Description | |---|---|---|---| | min | number | 0 | Minimum value | | max | number | 100 | Maximum value | | value | number | 50 | Current value | | step | number | 1 | Step increment | | disabled | boolean | false | Disables the slider | | labeled | boolean | false | Shows a floating value bubble on drag | | ticks | boolean | false | Renders tick marks at each step | | aria-label | string | '' | Accessible label |

<md-slider min="0" max="50" step="5" labeled :model="${vol}"></md-slider>

<md-snackbar>

MD3 snackbar that slides up from the bottom with an optional action button.

| Prop | Type | Default | Description | |---|---|---|---| | open | boolean | false | Shows/hides the snackbar | | message | string | '' | Message text | | action-label | string | '' | Label for the optional action button; empty hides it |

Events: action — action button clicked; close — dismiss button clicked.

<md-snackbar
  :model:open="${showSnack}"
  message="Item deleted"
  action-label="Undo"
  @action="${undoDelete}"
></md-snackbar>

<md-split-button>

MD3 split button combining a primary action and an arrow-toggle dropdown for secondary actions.

| Prop | Type | Default | Description | |---|---|---|---| | label | string | 'Action' | Primary button label | | icon | string | '' | Leading Material Symbol on the primary button | | variant | 'filled' \| 'outlined' \| 'tonal' | 'filled' | Visual style | | disabled | boolean | false | Disables both sections | | items | { id: string; label: string; icon?: string; disabled?: boolean }[] | [] | Dropdown action definitions |

Events: click — primary button pressed; select (detail: { id: string }) — dropdown item chosen.

<md-split-button
  label="Save"
  icon="save"
  :bind="${{ items: [
    { id: 'save-draft', label: 'Save as draft' },
    { id: 'save-copy', label: 'Save a copy' },
  ] }}"
  @click="${save}"
  @select="${handleSplitSelect}"
></md-split-button>

<md-switch>

MD3 toggle switch with animated thumb, state-layer ripple, and optional check/close thumb icons.

| Prop | Type | Default | Description | |---|---|---|---| | selected | boolean | false | On/off state | | disabled | boolean | false | Disables interaction | | icons | boolean | false | Renders a check icon when on and a close icon when off |

Events: change (detail: boolean) — new selected state.

<md-switch :model:selected="${darkMode}" icons></md-switch>

<md-tabs>

MD3 tabbed navigation with an active indicator bar, icon and badge support, keyboard navigation, and a single panel slot.

| Prop | Type | Default | Description | |---|---|---|---| | variant | 'primary' \| 'secondary' | 'primary' | Tab style (primary has icons above labels; secondary is text-only) | | tabs | { id: string; label: string; icon?: string; badge?: string }[] | [] | Tab definitions | | active-tab | string | '' | id of the active tab |

Slots: default — the active tab panel content.

Events: tab-change (detail: string) — the id of the newly active tab.

<md-tabs
  variant="primary"
  :model:activeTab="${activeTab}"
  :bind="${{ tabs: [
    { id: 'flights', label: 'Flights', icon: 'flight' },
    { id: 'hotels', label: 'Hotels', icon: 'hotel' },
  ] }}"
>
  <!-- render panel based on activeTab -->
</md-tabs>

<md-text-field>

MD3 text field (filled or outlined) with animated floating label, leading/trailing icons, error state, and supporting text.

| Prop | Type | Default | Description | |---|---|---|---| | variant | 'filled' \| 'outlined' | 'filled' | Visual style | | label | string | 'Label' | Floating label text | | value | string | '' | Input value | | type | string | 'text' | Native input type | | placeholder | string | '' | Placeholder text (shown when no label floats) | | disabled | boolean | false | Disables the field | | error | boolean | false | Applies error styling | | error-text | string | '' | Error helper text beneath the field | | supporting-text | string | '' | Helper text beneath the field (non-error) | | leading-icon | string | '' | Leading Material Symbol | | trailing-icon | string | '' | Trailing Material Symbol | | required | boolean | false | Marks the field as required | | readonly | boolean | false | Makes the field read-only |

<md-text-field
  variant="outlined"
  label="Email"
  type="email"
  leading-icon="mail"
  :model="${email}"
  :error="${!!emailError}"
  :error-text="${emailError}"
></md-text-field>

<md-time-picker>

MD3 time picker modal with an interactive clock dial or keyboard input mode, AM/PM toggle, and 12/24-hour support.

| Prop | Type | Default | Description | |---|---|---|---| | variant | 'dial' \| 'input' | 'dial' | Clock dial or text input mode | | value | string | '' | Selected time (HH:MM 24-hour) | | open | boolean | false | Controls visibility | | hour24 | boolean | false | Uses 24-hour format (no AM/PM) | | aria-label | string | 'Time picker' | Accessible label |

Events: change (detail: string) — selected time in HH:MM 24-hour format; close.

<md-time-picker
  :model="${meetingTime}"
  :model:open="${pickerOpen}"
></md-time-picker>

<md-tooltip>

MD3 tooltip shown on hover/focus — plain (small floating label) or rich (card with title, body, and optional action).

| Prop | Type | Default | Description | |---|---|---|---| | text | string | '' | Tooltip body text | | variant | 'plain' \| 'rich' | 'plain' | Plain label or rich card | | title | string | '' | Rich tooltip title | | action | string | '' | Rich tooltip action button label |

Slots: default — the anchor element that triggers the tooltip.

Events: action — rich tooltip action button clicked.

<!-- Plain tooltip -->
<md-tooltip text="Delete permanently">
  <md-icon-button icon="delete"></md-icon-button>
</md-tooltip>

<!-- Rich tooltip -->
<md-tooltip variant="rich" title="Formatting" text="Adjust text size, weight, and alignment." action="Learn more" @action="${openDocs}">
  <md-icon-button icon="format_size"></md-icon-button>
</md-tooltip>

Composables

Advanced consumers can re-use the internal composable utilities directly. All are exported from cer-material:

| Export | Description | |---|---| | useControlledValue(getProp) | Syncs an internal reactive value to a prop, returning a ReactiveState. | | useEscapeKey(guard, onEscape) | Registers a global Escape key listener with an active-guard callback. Cleans up automatically. | | createFocusReturn() | Captures the current focus target and restores it when .return() is called. | | createFocusTrap() | Traps keyboard focus within a DOM element; call .activate(el) and .deactivate(). | | useListKeyNav(options) | Adds keyboard arrow-key navigation for a list of DOM items. | | useScrollLock() | Returns { lock(), unlock() } to prevent <body> scrolling while an overlay is open. |


Browser support

All evergreen browsers supporting:


License

MIT