@eli_tabrisov/web-components-kit
v1.0.0
Published
Minimal wk-* web components library with TypeScript, Shadow DOM, and themable --wk-* design tokens
Maintainers
Readme
Web Components Kit
Minimal, framework-agnostic web components built with TypeScript, Shadow DOM, and zero runtime dependencies. Every component is a wk-* custom element styled through --wk-* design tokens.
Features
- 🎯 Zero runtime dependencies — plain custom elements, no framework
- 🎨 Shadow DOM — style encapsulation with themable
--wk-*custom properties - 📦 TypeScript — full typings, including
HTMLElementTagNameMapfor every tag - ♿ Accessible — ARIA attributes, semantic HTML,
prefers-reduced-motionsupport - 🌗 Dark mode — follows the system preference, overridable via
data-theme - 🚀 Framework agnostic — works with any framework or vanilla JS
Installation
pnpm add @eli_tabrisov/web-components-kitimport '@eli_tabrisov/web-components-kit'
import '@eli_tabrisov/web-components-kit/style.css'Both imports are required: the first registers all wk-* elements, the second loads the design tokens (colors, spacing, typography) that the components consume. Without style.css the components render unthemed.
<wk-button>Click me</wk-button>Components
<wk-alert>
Status message with semantic color variants and an animated dismiss.
<wk-alert type="success" dismissible>
<span slot="icon">✓</span>
<span slot="title">Saved</span>
Your changes have been saved.
</wk-alert>| Attribute | Values | Default |
| --- | --- | --- |
| type | info | success | warning | error | info |
| dismissible | boolean attribute — renders a dismiss button | off |
Slots: default (message), title, icon. Event: dismiss (fired after the exit transition, before removal).
Custom properties: --wk-alert-bg, --wk-alert-border-color, --wk-alert-color, --wk-alert-icon-color, --wk-alert-padding, --wk-alert-border-radius, --wk-alert-border-width, --wk-alert-font-size, --wk-alert-line-height, --wk-alert-motion-duration, --wk-alert-motion-easing.
<wk-button>
<wk-button>Click me</wk-button>
<wk-button type="submit">Submit</wk-button>
<wk-button disabled>Disabled</wk-button>
<wk-button><span slot="icon-start">↓</span>Download</wk-button>| Attribute | Values | Default |
| --- | --- | --- |
| type | button | submit | reset | button |
| disabled | boolean attribute | off |
aria-label, aria-describedby, and id are forwarded to the inner native <button>. Slots: default, icon-start, icon-end.
Custom properties: --wk-button-bg, --wk-button-color, --wk-button-padding, --wk-button-border-radius, --wk-button-border, --wk-button-font-size, --wk-button-font-weight, --wk-button-cursor, --wk-button-transition.
wk-button {
--wk-button-bg: var(--wk-color-success);
--wk-button-border-radius: var(--wk-radius-full);
}<wk-card>
Surface container with optional header and footer that collapse automatically when their slots are empty.
<wk-card>
<h3 slot="header">Title</h3>
Body content
<div slot="footer"><wk-button>Action</wk-button></div>
</wk-card>Slots: default (body), header, footer.
Custom properties: --wk-card-bg, --wk-card-border-color, --wk-card-border, --wk-card-border-radius, --wk-card-padding, --wk-card-shadow, --wk-card-header-padding, --wk-card-footer-padding, --wk-card-header-border, --wk-card-footer-border.
<wk-dialog>
Modal dialog built on the native <dialog> element — focus trapping, Escape, and backdrop come from the platform.
<wk-dialog id="confirm">
<span slot="header">Confirm</span>
Are you sure?
<div slot="footer"><wk-button>OK</wk-button></div>
</wk-dialog>
<script>
document.querySelector('#confirm').open()
</script>| Attribute | Effect |
| --- | --- |
| open | reflects the open state; can be set to open declaratively |
| no-header | hides the header even when header content is slotted |
| no-footer | hides the footer |
| fullscreen | dialog fills the viewport |
Methods: open(), close(). Events: open, close. Slots: default (body), header, footer.
Custom properties: --wk-dialog-bg, --wk-dialog-overlay-bg, --wk-dialog-border-color, --wk-dialog-border-radius, --wk-dialog-padding, --wk-dialog-max-width, --wk-dialog-max-height, --wk-dialog-shadow, --wk-dialog-header-padding, --wk-dialog-footer-padding, --wk-dialog-z-index, --wk-dialog-motion-duration, --wk-dialog-motion-easing.
<wk-form-group>
Wraps native <label> + <input>/<textarea> elements and progressively enhances them with custom validation messages and automatic required-field indicators.
<wk-form-group
value-missing-message="This field is required"
too-short-message="Must be at least 2 characters"
>
<label for="name">Name</label>
<input type="text" id="name" name="name" required minlength="2" />
</wk-form-group>Each attribute maps to a ValidityState flag:
| Attribute | Shown when |
| --- | --- |
| value-missing-message | field is required but empty |
| too-long-message | value exceeds maxlength |
| too-short-message | value is shorter than minlength |
| range-overflow-message | number exceeds max |
| range-underflow-message | number is less than min |
| type-mismatch-message | value doesn't match the input type |
| pattern-mismatch-message | value doesn't match pattern |
| step-mismatch-message | number doesn't match step |
| bad-input-message | input can't be parsed |
| custom-error-message | setCustomValidity() was used |
Styling — the error message is exposed as a part, and the required indicator (* appended to labels) is colored from the light DOM:
wk-form-group {
--wk-form-group-error-color: var(--wk-color-error);
--wk-form-group-required-color: var(--wk-color-error);
}
wk-form-group::part(error-message) {
font-style: italic;
}<wk-toast>
Transient notification. Use it declaratively or through ToastManager.
import { ToastManager } from '@eli_tabrisov/web-components-kit'
ToastManager.success('Saved!')
ToastManager.error('Something went wrong', { position: 'bottom-center' })
ToastManager.show({
message: 'Custom toast',
title: 'Heads up',
type: 'warning',
duration: 8000,
dismissible: true,
icon: '⚠️',
})
ToastManager.closeAll()| Attribute | Values | Default |
| --- | --- | --- |
| type | info | success | warning | error | info |
| position | top-left | top-center | top-right | bottom-left | bottom-center | bottom-right | top-right |
| duration | auto-dismiss delay in ms, 0 disables | 5000 |
| dismissible | boolean attribute | off |
Method: close(). Event: dismiss. Slots: default (message), title, icon. ToastManager also exposes setDefaultPosition() and setDefaultDuration().
Custom properties: --wk-toast-bg, --wk-toast-border-color, --wk-toast-color, --wk-toast-icon-color, --wk-toast-padding, --wk-toast-border-radius, --wk-toast-border-width, --wk-toast-font-size, --wk-toast-line-height, --wk-toast-max-width, --wk-toast-min-width, --wk-toast-box-shadow, --wk-toast-motion-duration, --wk-toast-motion-easing.
<wk-tooltip>
<wk-tooltip text="More info" position="bottom">
<button>Hover me</button>
</wk-tooltip>| Attribute | Values | Default |
| --- | --- | --- |
| text | tooltip content | — |
| position | top | bottom | left | right | top |
| show-delay | delay in ms before showing | 0 |
| hide-delay | delay in ms before hiding | 0 |
| disabled | boolean attribute | off |
Shows on hover and on keyboard focus of the slotted trigger, and wires aria-describedby automatically. The bubble is exposed as ::part(tooltip).
Custom properties: --wk-tooltip-bg, --wk-tooltip-color, --wk-tooltip-padding, --wk-tooltip-border-radius, --wk-tooltip-font-size, --wk-tooltip-max-width, --wk-tooltip-z-index, --wk-tooltip-arrow-size, --wk-tooltip-spacing, --wk-tooltip-transition.
Theming
All design tokens live in style.css on :root, prefixed --wk- so they never collide with your own variables. Override any of them globally or per subtree:
:root {
--wk-color-primary: oklch(55% 0.2 260);
--wk-radius-md: 0.5rem;
}Token groups: --wk-color-* (primary, semantic success/error/warning/info with -bg/-border/-text shades, neutral scale 0–1000, overlay), --wk-font-family-*, --wk-font-size--2…6 (fluid, clamp-based), --wk-font-weight-*, --wk-line-height-*, --wk-space-0…24, --wk-radius-*, --wk-shadow-*, --wk-transition-{fast,base,slow}, --wk-z-*.
Dark mode follows prefers-color-scheme automatically. Force a theme with data-theme on the root element: <html data-theme="dark"> or <html data-theme="light">.
Reduced motion: with prefers-reduced-motion: reduce, all transitions collapse to near-zero durations while dismissal events keep working.
TypeScript
Elements register themselves on import, and every tag is declared in HTMLElementTagNameMap, so DOM APIs are fully typed:
const button = document.createElement('wk-button')
const dialog = document.querySelector('wk-dialog')
dialog?.open()Component classes (WkButton, WkDialog, …), ToastManager, and the ToastOptions type are exported from the package root.
Migration from 0.x
Version 1.0.0 renames every public identifier. No behavior changed; update names:
| 0.x | 1.0.0 |
| --- | --- |
| <my-button>, <my-card>, … | <wk-button>, <wk-card>, … |
| MyButton, MyCard, … | WkButton, WkCard, … |
| --color-*, --space-*, --radius-*, --shadow-*, --font-*, --line-height-*, --transition-*, --z-* | --wk-color-*, --wk-space-*, … (same names, --wk- prefix) |
| --button-*, --card-*, --alert-*, --dialog-*, --toast-*, --tooltip-* | --wk-button-*, --wk-card-*, … |
| --error-message-color | --wk-form-group-error-color |
| --required-indicator-color | --wk-form-group-required-color (set it on wk-form-group or an ancestor in your page's CSS) |
| MyWebComponents UMD-style global name | WebComponentsKit |
| #my-form-group-required-styles injected style id | #wk-form-group-required-styles |
Also note: importing @eli_tabrisov/web-components-kit/style.css is required for the components to be themed — this was true before but undocumented.
Development
pnpm dev # dev server with the demo page
pnpm build # build the library (dist/ + types/)
pnpm build:demo # build the demo site
pnpm test # run tests in watch mode
pnpm run test:run # run tests once
pnpm lint # lint
pnpm type-check # type checkProject Structure
web-components-kit/
├── src/
│ ├── components/
│ │ ├── alert/
│ │ ├── button/
│ │ ├── card/
│ │ ├── dialog/
│ │ ├── form-group/
│ │ ├── toast/
│ │ └── tooltip/
│ ├── design-tokens.css
│ └── index.ts
├── tests/
├── demo/
├── dist/ # build output
└── types/ # TypeScript definitionsLicense
MIT
