JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 1028
  • Score
    100M100P100Q95258F
  • License MIT

Framework-agnostic web components built with Lit: Material Symbols / Simple Icons / theSVG icons, declarative client-side router, drag-and-drop primitives, details accordion with FIFO open caps, a CSS-anchored popover, a form-associated selectbox with single/multi-select, and a form-associated button.

Package Exports

  • @telperion/elements
  • @telperion/elements/button
  • @telperion/elements/button/index
  • @telperion/elements/details-set
  • @telperion/elements/details-set/index
  • @telperion/elements/drag-drop
  • @telperion/elements/drag-drop/index
  • @telperion/elements/icon
  • @telperion/elements/icon/index
  • @telperion/elements/icon/material-symbols.css
  • @telperion/elements/popover
  • @telperion/elements/popover/index
  • @telperion/elements/router
  • @telperion/elements/router/index
  • @telperion/elements/select
  • @telperion/elements/select/index

Readme

@telperion/elements

npm version npm

A collection of custom web components built with Lit Elements. This library provides a suite of reusable, framework-independent web components for modern web applications.

✨ Features

  • 🌐 Framework Independent: Built as custom web elements, works with any framework (React, Vue, Angular, Vanilla JS)
  • 🔧 Custom Components: A growing collection of useful web components
  • 🎨 CSS Custom Properties: Rich CSS variable support for styling
  • 📦 Modular: Import only the components you need
  • 🔄 Reactive: Built on Lit's reactive properties system
  • 🎯 TypeScript: Full TypeScript support with type definitions

📦 Installation

npm install @telperion/elements
yarn add @telperion/elements
pnpm add @telperion/elements

Tailwind v4 consumers: some elements (e.g. <tp-icon>) render Tailwind utility classes from inside the published package. Add @source "@telperion/elements"; to your Tailwind entry CSS so the JIT scanner picks them up. See each module's README for details.

🚀 Usage

import '@telperion/elements';

// Use the components in your HTML

Loading via <script> tag (no bundler)

Every component module ships a self-contained, minified IIFE bundle that you can load directly from a CDN with a classic <script> tag — no bundler, no import, no npm install.

Use the explicit /iife/... path so the CDN serves the standalone bundle (subpath shorthand like /icon resolves to the ESM build via the package exports map and will not run as a classic script):

<!-- All elements registered (TelperionElements global) -->
<script src="https://unpkg.com/@telperion/elements/iife/elements.js"></script>

<!-- Or just the icon module (TelperionElements.Icon) -->
<script src="https://unpkg.com/@telperion/elements/iife/icon/index.js"></script>

<!-- Or just the router module (TelperionElements.Router) -->
<script src="https://unpkg.com/@telperion/elements/iife/router/index.js"></script>

<!-- Or just the drag-and-drop module (TelperionElements.DragDrop) -->
<script src="https://unpkg.com/@telperion/elements/iife/drag-drop/index.js"></script>

<!-- Or just the details-set module (TelperionElements.DetailsSet) -->
<script src="https://unpkg.com/@telperion/elements/iife/details-set/index.js"></script>

<!-- Or just the popover module (TelperionElements.Popover) -->
<script src="https://unpkg.com/@telperion/elements/iife/popover/index.js"></script>

<!-- Or just the select module (TelperionElements.Select) -->
<script src="https://unpkg.com/@telperion/elements/iife/select/index.js"></script>

<!-- Or just the button module (TelperionElements.Button) -->
<script src="https://unpkg.com/@telperion/elements/iife/button/index.js"></script>

Each per-module bundle is fully self-contained: side-effect imports register the custom elements as soon as the script is evaluated. jsDelivr (https://cdn.jsdelivr.net/npm/@telperion/elements/iife/...) and other npm CDNs work the same way.

The package also declares a non-standard "script" export condition pointing at these files for tooling that wants to discover them programmatically, but no browser or major CDN currently resolves it — always use the full /iife/... path in <script src>.

📚 Components

Icon

A framework-agnostic icon element with two families: Material Symbols and Simple Icons (brand icons).

Includes:

  • <tp-icon> — renders a single glyph from the chosen family

Features:

  • Material Symbols: three variants (outlined, round, sharp) and all four variable-font axes exposed as attributes (filled, grade, weight, optical-size)
  • Simple Icons (family="simple-icons"): brand icons (YouTube, LinkedIn, GitHub, …) loaded on demand from cdn.simpleicons.org into a single shared <svg> sprite — one HTTP request per slug, ever; auto aria-label from the brand title; tp-icon-load / tp-icon-error events; configurable base URL for self-hosting
  • theSVG (family="thesvg"): multi-color, multi-variant brand icons from thesvg.org (~5,600 SVGs). Lazy-fetched into a separate <svg data-tp-thesvg> sprite with per-symbol id namespacing so multi-gradient logos coexist without collisions. variant attribute selects default / mono / wordmark / …; tp-icon-load / tp-icon-error events fire with { slug, variant, viewBox }; configurable base URL.
  • Light-DOM rendering so consumer styling (Tailwind, CSS variables, color/size) "just works"
  • Material Symbols fonts loaded via the Google Fonts CSS API — no broken hash URLs when Google rotates them; self-host friendly

JS/TS

import "@telperion/elements/icon";

CSS

@import "@telperion/elements/icon/material-symbols.css";

HTML

<tp-icon>home</tp-icon>
<tp-icon variant="round" filled weight="700">favorite</tp-icon>
<tp-icon family="simple-icons" slug="facebook" style="color:#1877F2"></tp-icon>
<tp-icon family="thesvg" slug="google" variant="wordmark"></tp-icon>

Icon Documentation →


Router Components

A complete client-side routing solution for single-page applications, built with declarative web components.

Includes:

  • <tha-router> - Main router component managing URL matching and navigation
  • <tha-route> - Route definitions with URLPattern syntax for path matching
  • <tha-router-outlet> - Content rendering outlet for active routes
  • <tha-router-link> - Navigation links with automatic active state detection
  • <tha-route-config> - Global router configuration

Features:

  • Template-based routing without JavaScript configuration
  • Multiple history strategies (browser, hash, memory)
  • Dynamic route parameters and wildcards
  • Relative path navigation
  • Automatic active link detection
  • Signal-based reactive updates

Router Documentation →


Details Set

A grouping element for native <details> that caps how many can be open at once, supports cloned summary markers, and offers marker-only toggling.

Includes:

  • <tp-details-set> — manages direct-child <details> elements

Features:

  • max-open-items attribute — FIFO eviction when the cap is exceeded (0 = unlimited)
  • Declarative summary markers via <template summary-marker index="…"> direct children — cloned into every direct-child <summary> at the requested splice index; multiple templates and dynamic add/remove supported
  • Cloned marker elements are auto-tagged with tp-summary-marker so the native disclosure triangle is hidden and CSS can style markers state-aware (details[open] > summary > [tp-summary-marker])
  • toggle-on="summary" | "marker" (set-level + per-<details> override) — restrict toggling to clicks on the marker (keyboard activation always works; falls back to summary-toggle when no marker is present)
  • tp-details-set-change event with { opened, closed } arrays
  • Light-DOM rendering; modern CSS (::details-content, interpolate-size) opt-in for animated open/close

JS/TS

import "@telperion/elements/details-set";

HTML

<tp-details-set max-open-items="1" toggle-on="marker">
  <template summary-marker index="0"><span class="chev"></span></template>
  <details><summary>One</summary>...</details>
  <details><summary>Two</summary>...</details>
</tp-details-set>

Details Set Documentation →


Popover

Framework-agnostic popover element built on the native Popover API and CSS Anchor Positioning. Placement is pure CSS — JavaScript only parses attributes, resolves the anchor, and (optionally) wires triggers.

Includes:

  • <tp-popover> — auto-applies the native popover attribute and anchors itself via CSS

Features:

  • Auto popover attribute (auto by default; switch with mode="manual")
  • Anchor resolved from a target querySelector string, falling back to parentElement
  • Declarative position syntax with three forms:
    • Full: <pop-inline> to <target-inline> / <pop-block> to <target-block>
    • Two-keyword: <inline> / <block> (e.g. center / top)
    • Single axis or single keyword (e.g. top to bottom, start, bottom)
  • Inline keywords: start | center | end; block keywords: top | middle | bottom
  • Optional trigger="click" | "hover" (hover also opens on focus)
  • Use plain CSS margin on the popover for a gap from its anchor
  • Automatic edge-flipping via the default position-try-fallbacks: flip-block, flip-inline, flip-block flip-inline (override with position-try-fallbacks: none)
  • Light-DOM rendering; no JS positioning loop

JS/TS

import "@telperion/elements/popover";

HTML

<div>
  <button>Anchor</button>
  <tp-popover position="top">Above the parent.</tp-popover>
</div>

<button id="trigger">Click me</button>
<tp-popover target="#trigger" position="start / bottom" trigger="click">
  Anchored via target="#trigger".
</tp-popover>

Popover Documentation →


Select

Form-associated, framework-agnostic selectbox built on <tp-popover>. Single-select by default; switches to multi-select with FIFO eviction via max. Selection state is signal-backed and discoverable across nested shadow DOM boundaries via @lit/context.

Includes:

  • <tp-select> — the selectbox itself
  • <tp-option> — a single selectable item
  • <tp-selected-content> — live mirror of the current selection (used as the trigger label by default)

Features:

  • Form-associated custom element with full <form> integration via ElementInternals (name, disabled, required, reset/restore callbacks, validity)
  • max attribute — 1 for single-select, any positive integer for multi-select with FIFO eviction at the cap, or infinite for unbounded selection
  • Single-select submits a string; multi-select submits a FormData with one entry per value (matching native <select multiple>)
  • Auto-closes the popover when a selection fills the quota; stays open while accumulating in multi-select
  • Three slots: slot="button" (replace trigger), slot="popover" (replace panel content while keeping <tp-option> children registered for selection / validation / submission), and the default slot (option list)
  • <tp-selected-content> works anywhere in the descendant composed tree — including across shadow DOM boundaries
  • Signal-driven re-rendering via the SignalWatcher mixin — no manual change listeners

JS/TS

import "@telperion/elements/select";

HTML

<tp-select name="fruit" required placeholder="Pick a fruit">
  <tp-option value="apple">Apple</tp-option>
  <tp-option value="banana">Banana</tp-option>
  <tp-option value="cherry">Cherry</tp-option>
</tp-select>

<!-- Multi-select with FIFO cap of 3 -->
<tp-select name="tags" max="3"></tp-select>

<!-- Unbounded multi-select -->
<tp-select name="tags" max="infinite"></tp-select>

Select Documentation →


Button

Framework-agnostic, form-associated button built with Lit. Behaves like the native <button> element — tab-selectable, activates on Space / Enter, and submits or resets its containing form via type="submit" / type="reset".

Includes:

  • <tp-button> — the button itself

Features:

  • Four variants: solid · outline · ghost · text (ghost is outline without the border)
  • Form-associated via ElementInternalstype="submit" calls form.requestSubmit(), type="reset" calls form.reset(), and .form mirrors HTMLButtonElement.form
  • Tab-selectable out of the box (tabindex="0"); disabled removes the button from the tab order, blocks click/keyboard activation, and sets aria-disabled
  • Space / Enter activation matching native button semantics
  • Announces role="button" to assistive technology through element internals
  • Palette-driven color via the shared ShadeMixerLitElement mixin: pick a color token (primary, secondary, success, …) and tune intensity with shade (01000, 500 is the base color); mixer (none / black / white) is auto-derived from shade
  • Themable design system: every palette token (--tp-color-primary, --tp-color-success, …) is overridable at :root or any ancestor; add brand-specific names with a single rule (tp-button[color="brand"] { --tp-element-color: …; })
  • Light-DOM rendering so consumer styling (Tailwind, CSS variables, design tokens) applies directly; pure-CSS hover, focus, and active transitions

JS/TS

import "@telperion/elements/button";

HTML

<tp-button>Click me</tp-button>
<tp-button variant="outline" color="success">Save</tp-button>
<tp-button variant="ghost" color="primary">Cancel</tp-button>
<tp-button variant="text" color="danger" disabled>Delete</tp-button>

<form>
  <input name="email" type="email" required />
  <tp-button type="submit" color="primary">Subscribe</tp-button>
  <tp-button type="reset" variant="outline">Reset</tp-button>
</form>

Button Documentation →


This library is actively being developed. More components will be added over time.

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

📄 License

MIT © Telperion Technology