ars-web-components
v3.1.0
Published
A collection of reusable web components built with TypeScript and ES modules
Maintainers
Readme
ARS Web Components
A collection of reusable web components built with TypeScript and ES modules.
⚠️ Breaking change in v3.0.0: component modules no longer register custom elements at import time. You must call
registerArsWebComponents()before using any<ars-*>tag. See Registration below.
🚀 Live Demo
Check out the live demo.
Alternative: Download the repository and open index.html in a local web server.
Features
- 🎯 TypeScript: Full type safety with generated
.d.tsdeclarations - 🎨 CSS-friendly: Components are styling-agnostic, use your own CSS classes
- ⚡ ES Modules: Modern module system with proper imports/exports
- 🔧 Interactive Effects: Built-in pressed effects and animations
- 📱 Touch Support: Full mobile and desktop interaction support
- 🧪 Vitest Testing: 785 tests with jsdom environment
- 🧹 ESLint: Flat-config linting for source, tests, and tool configs
- 🔒 Proper Encapsulation: Private methods and static utilities for clean API design
- 🤝 Mixin Coordination: Smart pointer coordination system for multiple gesture mixins
- 📜 Mobile Scroll Management: Intelligent scroll prevention during gesture interactions
Architecture
Project Structure
ars-web-components/
├── src/ # TypeScript source files
│ ├── components/ # Web components
│ │ ├── ars-button/
│ │ ├── ars-calendar/
│ │ ├── ars-candlestick-chart/
│ │ ├── ars-color-select/
│ │ ├── ars-data-roller/
│ │ ├── ars-dialog/
│ │ ├── ars-info-tile/
│ │ ├── ars-input/
│ │ ├── ars-line-chart/
│ │ ├── ars-page/
│ │ ├── ars-select/
│ │ ├── ars-table/
│ │ ├── ars-tabs/
│ │ ├── ars-toast/
│ │ ├── ars-toggle/
│ │ ├── chart-base/
│ │ └── web-component-base/
│ ├── mixins/ # Reusable mixins
│ │ ├── common/
│ │ ├── draggable-mixin/
│ │ ├── localized-mixin/
│ │ ├── pressed-effect-mixin/
│ │ ├── remote-call-mixin/
│ │ ├── roll-mixin/
│ │ ├── show-if-property-true-mixin/
│ │ └── swipeable-mixin/
├── demos/ # Demo entry points and demo-only assets
│ └── css/ # Shared CSS for demos
├── dist/ # Compiled JavaScript output (generated)
├── test/ # Shared Vitest setup and mocks
├── tsconfig.json
├── vitest.config.ts
└── server.js # Express dev serverTypeScript + Web Components
All components extend WebComponentBase (which extends HTMLElement) and are compiled from TypeScript to ES modules with full type declarations.
Chart components (ars-line-chart, ars-candlestick-chart) use WebGPU instanced rendering for high-performance visualization. Each frame submits 3 GPU draw calls instead of hundreds of sequential Canvas 2D method calls. A shared GPUDevice is created lazily on first use, or can be injected externally via ChartGPUContext.setDevice(device) for integration with engines like brainiac-engine.
import WebComponentBase from "../web-component-base/web-component-base.js";
class MyComponent extends WebComponentBase {
static get observedAttributes() {
return ["attr1", "attr2"];
}
connectedCallback() { /* lifecycle */ }
attributeChangedCallback(name: string, oldVal: string, newVal: string) { /* ... */ }
publicMethod() { /* component API */ }
}Installation
npm install ars-web-componentsDevelopment Workflow
Live-reload demo development (recommended while editing)
Use Vite to serve the demos with automatic reload from src/ (no rebuild required on each edit):
npm run devThen open the printed local URL (for example http://127.0.0.1:8080/ or http://127.0.0.1:8081/).
Notes:
- The demo pages still reference
/dist/...paths, but the Vite dev server rewrites them to/src/...during development. - This keeps the demo HTML stable while enabling fast iteration.
Dist build verification (release/package path)
When you want to verify the published package output, rebuild the package:
npm run buildThis regenerates dist/, which is what external consumers import.
Tests
Run the test suite:
npm testTest files live alongside the source they cover (for easier navigation while editing components/mixins). Shared Vitest setup and mocks remain in test/.
Lint
Run the lint suite:
npm run lintThe lint configuration covers source files, colocated tests, and TypeScript tool config files through tsconfig.eslint.json.
Registration
Because the package is marked sideEffects: false, importing a component
class does not register its custom element. Call
registerArsWebComponents() once at application startup before any
<ars-*> tag is rendered:
import { registerArsWebComponents } from "ars-web-components";
registerArsWebComponents();For apps that only use a few components, register them manually instead to keep bundles smaller:
import { ArsButton } from "ars-web-components";
customElements.define("ars-button", ArsButton);Design System Compatibility (Design-Agnostic by Default)
ars-web-components is intended to be design-system agnostic.
- It should work with your own CSS/theme variables.
- In the Dark Factory ecosystem (internal usage), the default pairing is the private
ars-design-system. - If no design system is used, you can start from one of the lightweight fallback theme templates shipped in this package.
Recommended pairing (Dark Factory / internal usage)
Import order:
import "ars-design-system/styles.css";
import "ars-web-components/styles.css";Then initialize the component library with your design adapter:
import {
initializeArsWebComponents,
registerArsWebComponents,
} from "ars-web-components";
registerArsWebComponents();
initializeArsWebComponents({
designAdapter: {
rootAttributes: { "data-theme": "my-app-theme" },
cssVariables: {
"--arswc-color-accent": "#2563eb"
}
}
});The adapter contract is intentionally small and generic so applications can supply any design system.
Explicit default adapter (no external design system)
If you want an explicit non-Dark-Factory setup without custom adapters:
import {
getArsWebComponentsDefaultAdapter,
initializeArsWebComponents,
registerArsWebComponents,
} from "ars-web-components";
registerArsWebComponents();
initializeArsWebComponents({
designAdapter: getArsWebComponentsDefaultAdapter("dark"),
});This keeps initialization explicit while still providing a simple starter theme.
Brainiac Engine Embedding
ars-web-components can be mounted inside brainiac-engine DOM overlays as agent presentations.
Embedding guidelines:
- Prefer components that can operate inside a provided host container instead of assuming
document.body. - Prefer local/internal routing when embedding page containers inside an engine overlay.
- Prefer owner-document scoped listeners over hard-coded global
windowreferences. - Prefer declarative event wiring and direct template rendering over dynamic string evaluation.
Current component support for engine embedding:
ars-calendar- supports
global-events-enabled="false"so embedded hosts can opt out of global window events
- supports
ars-dialog- static
notify(...)anddialog(...)accept an optional mount-target options object
- static
ars-page- supports
routing-mode="internal"for local navigation without browser history ownership
- supports
Hardening notes for embedded hosts:
web-component-baseno longer usesevalto wire DOM eventsars-dialogandars-color-selectnow render templates directly instead of evaluating template strings- embedded integrations should prefer explicit host references and typed data over raw HTML/script injection patterns
For comprehensive embedding guidance including mount/unmount lifecycle, event forwarding, and design adapter usage in iframes/shadow roots, see docs/EMBEDDING.md.
For the release process checklist, see docs/PUBLISH_CHECKLIST.md.
Custom Design System Contract (--arswc-*)
ars-web-components components read library-level design variables (--arswc-*) set by the active design adapter.
When a custom design system initializes the library, it should set some or all of these variables on the document root (or another shared scope):
Recommended core variables:
--arswc-color-bg--arswc-color-surface--arswc-color-border--arswc-color-text--arswc-color-muted--arswc-color-accent--arswc-color-accent-contrast--arswc-radius-sm--arswc-radius-md--arswc-shadow-sm--arswc-font-family-sans--arswc-font-family-mono
Resolution priority in components:
- component-specific CSS variables (for example
--ars-calendar-*) - library design variables (
--arswc-*) from the active adapter - hardcoded component fallback values
This makes the library design-system agnostic while keeping integration predictable.
Design Contract Coverage
All components resolve their default styling through the library design contract (--arswc-*) before falling back to hardcoded values.
Core tokens (used by most components):
--arswc-color-bg,--arswc-color-surface,--arswc-color-border--arswc-color-text,--arswc-color-muted,--arswc-color-accent,--arswc-color-accent-contrast--arswc-radius-sm,--arswc-radius-md,--arswc-shadow-sm--arswc-font-family-sans,--arswc-font-family-mono
Semantic color tokens (added in 1.0.0):
--arswc-color-danger— destructive actions, error states (button, toast, input validation)--arswc-color-success— success confirmation (toast, toggle on state)--arswc-color-warning— warning states (toast)--arswc-color-disabled,--arswc-color-disabled-bg— disabled element foreground/background
Typography and spacing tokens (added in 1.0.0):
--arswc-font-size-sm,--arswc-font-size-md,--arswc-font-size-lg--arswc-spacing-xsthrough--arswc-spacing-xl--arswc-transition-duration,--arswc-focus-ring
Components remain independently overridable through their own component-specific CSS variables (for example --ars-calendar-*).
Fallback templates (no design system)
Choose one small generic template and then override values in your app:
import "ars-web-components/theme-default-dark.css";
import "ars-web-components/styles.css";or
import "ars-web-components/theme-default-light.css";
import "ars-web-components/styles.css";These fallback templates are intentionally minimal. They exist to make adoption easy, not to define a complete visual identity.
Usage
Method 1: ES Module Import (Recommended)
import { ArsCalendar } from "ars-web-components";Method 2: Direct Script Import
For projects that need to load components directly without bundling:
<script type="module" src="./node_modules/ars-web-components/dist/index.js"></script>Components
Component Inventory
| Component | Tag | Status | Description |
|---|---|---|---|
| Button | <ars-button> | Stable | Interactive button with variant, size, disabled, loading |
| Toggle | <ars-toggle> | Stable | Boolean switch control with keyboard support |
| Input | <ars-input> | Stable | Text input with label, validation, clearable |
| Color Select | <ars-color-select> | Stable | Carousel color picker with keyboard navigation |
| Toast | <ars-toast> | Stable | Notification toast with stacking and auto-dismiss |
| Tabs | <ars-tabs> | Stable | Tabbed navigation with 4 placement options |
| Select | <ars-select> | Stable | Dropdown select with search and multi-select |
| Table | <ars-table> | Stable | Data table with sorting, selection, virtual scroll |
| Calendar | <ars-calendar> | Stable | Date selection and event management |
| Dialog | <ars-dialog> | Stable | Modal dialog with confirmation and notification modes |
| Data Roller | <ars-data-roller> | Stable | Animated cycling data display |
| Info Tile | <ars-info-tile> | Stable | Structured information card for dashboards |
| Line Chart | <ars-line-chart> | Stable | WebGPU-rendered line chart |
| Candlestick Chart | <ars-candlestick-chart> | Stable | WebGPU-rendered financial OHLC candlestick chart |
| Page Router | <ars-page> | Stable | Component-based routing |
| Relational Node | <ars-relational-node> | Deprecated | Use <ars-info-tile> instead |
ArsButton
Styled, accessible button with variant, size, and state support.
<ars-button variant="primary" size="md">Click Me</ars-button>
<ars-button variant="danger" disabled>Disabled</ars-button>
<ars-button loading>Loading...</ars-button>Attributes: variant (primary/secondary/danger/ghost), size (sm/md/lg), disabled, loading, type (button/submit/reset)
Slots: default (label), prefix, suffix
Events: ars-button:click — detail: { variant }
ArsToggle
Boolean switch control with keyboard support.
<ars-toggle label="Dark mode" checked></ars-toggle>Attributes: checked, disabled, label, label-position (start/end)
Events: ars-toggle:change — detail: { checked }
ArsInput
Text input with label, validation, and clearable state.
<ars-input label="Email" type="email" placeholder="[email protected]" required></ars-input>
<ars-input label="Search" type="search" clearable></ars-input>Attributes: type, value, placeholder, label, error, disabled, readonly, clearable, min, max, step, pattern, required
Slots: prefix, suffix
Events: ars-input:input, ars-input:change, ars-input:clear
ArsToast
Notification toast with severity levels and stacking.
import { ArsToast } from "ars-web-components";
ArsToast.show("Operation complete!", {
severity: "success",
duration: 4000,
position: "top-right",
});Static API: ArsToast.show(message, options) — options: severity, duration, dismissible, position, progress, mountTarget, targetDocument
Attributes: message, severity (info/success/warning/error), duration, dismissible, progress, open
Events: ars-toast:dismiss — detail: { reason }
ArsTabs
Tabbed navigation with companion <ars-tab-panel>.
<ars-tabs>
<ars-tab-panel tab-id="overview" label="Overview">Content here</ars-tab-panel>
<ars-tab-panel tab-id="settings" label="Settings">Settings here</ars-tab-panel>
</ars-tabs>Attributes: active-tab, placement (top/bottom/start/end)
Events: ars-tabs:change — detail: { activeTab, previousTab }
ArsSelect
Dropdown select with search, groups, and multi-select.
const select = document.querySelector("ars-select");
select.options = [
{ value: "apple", label: "Apple" },
{ value: "banana", label: "Banana", group: "Tropical" },
];Attributes: value, placeholder, label, disabled, searchable, multiple, error
Events: ars-select:change, ars-select:open, ars-select:close
ArsTable
Data table with sorting, selection, and virtual scrolling.
const table = document.querySelector("ars-table");
table.columns = [
{ key: "name", label: "Name" },
{ key: "age", label: "Age", align: "end" },
];
table.data = [
{ name: "Alice", age: 30 },
{ name: "Bob", age: 25 },
];Attributes: selectable (none/single/multiple), sortable, striped, compact, virtual-scroll, auto-sort
Events: ars-table:sort, ars-table:select, ars-table:row-click
ArsCalendar
Interactive calendar component with full customization support.
<ars-calendar
id="myCalendar"
localized_abbreviated_days='["Sun","Mon","Tue","Wed","Thu","Fri","Sat"]'
localized_months='["January","February","March",...,"December"]'
localized_today="Today"
css-vars='{"ars-calendar-bg": "#f0f0f0", "ars-calendar-header-bg": "#333"}'
></ars-calendar>Attributes:
localized_abbreviated_days: JSON array of localized day abbreviationslocalized_months: JSON array of localized month nameslocalized_today: Localized text for "Today" buttoncustom-css: Custom CSS string to add to component stylescss-vars: JSON object of CSS custom properties for theming
Methods:
addEvent(event): Add an event to the calendarremoveEvent(event): Remove an event from the calendarchangeEvent(date, newText, newColor): Modify an existing eventselectDate(day, month, year): Programmatically select a datesetSelectedDateToToday(): Select today's datesetCustomTemplate(templateFunction): Provide custom HTML templatesetCSSVars(cssVarsObject): Set CSS variables programmaticallygetCSSVars(): Get current CSS variables
Events:
ars-calendar:daySelected: Fired when a date is selected
CSS Customization:
The calendar supports extensive theming through CSS variables:
ars-calendar {
--ars-calendar-bg: #ffffff;
--ars-calendar-shadow: 0px 3px 3px rgba(0, 0, 0, 0.25);
--ars-calendar-border-radius: 5px;
--ars-calendar-header-bg: linear-gradient(to bottom, #b32b0c, #cd310d);
--ars-calendar-header-height: 34px;
--ars-calendar-header-color: #fff;
--ars-calendar-cell-width: 30px;
--ars-calendar-cell-height: 30px;
/* ... and many more */
}ArsDialog
Modal dialog component for user interactions. Supports custom content, styling with backdrop management, and both confirmation and notification modes.
// Open a dialog with confirmation buttons and custom content
const result = await ArsDialog.dialog(
`<form><label>Name: <input id='name'></label></form>`,
"Dialog Title"
);
if (result) {
const name = result.querySelector("#name").value;
// ...
}
// Show a notification dialog
await ArsDialog.notify("Operation complete!", "Success");Attributes:
custom-css: Custom CSS string to add to dialog stylescss-vars: JSON object of CSS custom properties for theming
Methods:
setCSSVars(cssVarsObject): Set CSS variables programmaticallygetCSSVars(): Get current CSS variables
Styling & Theming:
- Dialog supports extensive theming via CSS variables (see
ars-dialog-css.js) - Form elements (
input,select,textarea) are styled by default, even for light DOM content injected as HTML - Content area is fully responsive and prevents overflow
Light DOM Form Styling:
If you inject form elements as raw HTML (light DOM) into the dialog, ARS Dialog will automatically inject a <style> tag with form element CSS into the content area, ensuring consistent styling for input, select, and textarea.
Events:
- Dialog returns the content DOM element (for confirmation dialogs) or
null/falseif cancelled
Example:
const result = await ArsDialog.dialog('<input id="myInput">', "Enter Value");
if (result) {
alert(result.querySelector("#myInput").value);
}ArsColorSelect
Carousel-based color picker with spectrum-ordered swatches and keyboard navigation.
<ars-color-select color="#3B82F6" visible-count="7"></ars-color-select>Features:
- Horizontal carousel strip of circular swatches in spectrum order (red → orange → yellow → green → blue → purple → neutrals)
- Navigation buttons and position track indicator
- Keyboard navigation: Arrow Left/Right, Home, End
- ARIA listbox semantics with
role="option"andaria-selected - Custom palette via
paletteproperty, swatch sizes (sm/md/lg) prefers-reduced-motionsupport
Attributes: color, palette (JSON array), swatch-size (sm/md/lg), disabled, visible-count
Events: ars-color-select:change — detail: { id, color, previousColor }
Usage Example:
const picker = document.querySelector("ars-color-select");
picker.palette = ["#FF0000", "#00FF00", "#0000FF"];
picker.addEventListener("ars-color-select:change", (e) => {
console.log(`Selected: ${e.detail.color}`);
});ArsPage & ArsPageController
Component-based router and navigation controls for web applications, using the new remote-call API for inter-component communication.
ArsPage
<ars-page
id="my-router"
default-page="home"
routes='{"home":"/","about":"/about","contact":"/contact"}'
>
<div id="home">Home page content</div>
<div id="about">About page content</div>
<div id="contact">Contact page content</div>
</ars-page>Attributes:
default-page: The page to show initially
ArsPageController
<ars-page-controller target-page="my-router">
<nav>
<button data-page="home">Home</button>
<button data-page="about">About</button>
<button data-page="contact">Contact</button>
</nav>
</ars-page-controller>Attributes:
target-page: The ID of thears-pagecomponent to control
Features:
- Component-based routing
- Custom navigation markup via child elements with
data-page - Event-driven architecture
- Easy to integrate
ArsRelationalNode
Reusable DOM node card for graph-based UIs that need browser-native interaction instead of canvas-drawn text.
import { ArsRelationalNode } from "ars-web-components";
const node = new ArsRelationalNode();
node.data = {
id: "event_alpha",
title: "Event Alpha",
subtitle: "is a: event",
properties: {
start_date: "2026-03-11",
owner: "andre",
},
};
node.setSelected(true);This component is intended for projected overlays in tools like Nexus or Brainiac Engine where the spatial runtime owns positioning and zoom while the browser owns the card UI.
Mixins
ARS Web Components includes a collection of reusable behavior classes. Each mixin is a concrete custom element class that can be used either as a standalone wrapper element or as the base class for your own element.
Register the mixin tags before using them. The easiest way is to call the central registration helper once at application startup:
import { registerArsWebComponents } from "ars-web-components"; registerArsWebComponents();If you only need a specific mixin, import the class and register it explicitly:
import { DraggableMixin } from "ars-web-components"; if (!customElements.get("draggable-mixin")) { customElements.define("draggable-mixin", DraggableMixin); }
Using a mixin as a wrapper element
<pressed-effect-mixin>
<button>Press me</button>
</pressed-effect-mixin>Using a mixin as a base class
import { PressedEffectMixin } from "ars-web-components";
class MyButton extends PressedEffectMixin {
constructor() {
super();
// Your component logic
}
}
customElements.define("my-button", MyButton);PressedEffect Mixin
Adds pressed animation effects to components with solid background colors.
<pressed-effect-mixin>
<button>Press me</button>
</pressed-effect-mixin>Features:
- Automatic color detection from background
- Smooth pressed animation effects
- Works with solid background colors
- Touch and mouse support
Demo: http://localhost:8080/demos/mixins/pressed-effect-mixin/
Localized Mixin
Provides localization capabilities for components with dynamic language switching.
<localized-mixin id="greeting" locale="en">
<div>Hello World</div>
</localized-mixin>import { LocalizedMixin } from "ars-web-components";
class LocalizedComponent extends LocalizedMixin {
constructor() {
super();
this.addTranslations("en", { greeting: "Hello World" });
this.addTranslations("es", { greeting: "Hola Mundo" });
this.addTranslations("fr", { greeting: "Bonjour le Monde" });
this.setLocale("en");
}
}
customElements.define("localized-component", LocalizedComponent);Features:
- Dynamic language switching
- Text localization support
- Event-driven updates
- Mock localization system for testing
Demo: http://localhost:8080/demos/mixins/localized-mixin/
RemoteCall Mixins
Enables inter-component communication through DOM events and component IDs.
<remote-call-caller-mixin receiver="my-receiver" method="greet" args='["World"]'>
<button>Send greeting</button>
</remote-call-caller-mixin>
<remote-call-receiver-mixin id="my-receiver" allow="greet">
<greeter-element></greeter-element>
</remote-call-receiver-mixin>class GreeterElement extends HTMLElement {
greet(name) {
console.log(`Hello ${name}`);
}
}
customElements.define("greeter-element", GreeterElement);Important: Receiver elements must have a unique id attribute, and the wrapped target must expose public methods.
Features:
- Component ID-based targeting
- Method call with parameters
- Error handling and validation
- Support for multiple receiver instances
Demo: http://localhost:8080/demos/mixins/remote-call-mixin/
ShowIfPropertyTrue Mixin
Conditionally shows/hides the first child element based on a property, data attribute, or regular attribute value.
<show-if-property-true-mixin show-if-property="isVisible">
<div class="bar">isVisible Bar</div>
</show-if-property-true-mixin>import { ShowIfPropertyTrueMixin } from "ars-web-components";
// Register once
if (!customElements.get("show-if-property-true-mixin")) {
customElements.define("show-if-property-true-mixin", ShowIfPropertyTrueMixin);
}
// Toggle visibility by setting the property on the wrapped element
const mixin = document.querySelector("show-if-property-true-mixin");
const target = mixin.firstElementChild;
target.isVisible = true; // shows the element
target.isVisible = false; // hides the elementAdd the keep-space-when-hidden attribute to keep the element's space in the layout when hidden (uses visibility: hidden instead of display: none).
<show-if-property-true-mixin show-if-property="isVisible" keep-space-when-hidden>
<div class="bar">isVisible Bar</div>
</show-if-property-true-mixin>Demo: http://localhost:8080/demos/mixins/show-if-property-true-mixin/
Swipeable Mixin
Adds swipe gesture support to components.
<swipeable-mixin min-swipe-distance="30" max-swipe-time="800">
<div>Swipe me</div>
</swipeable-mixin>import { SwipeableMixin } from "ars-web-components";
class SwipeableComponent extends SwipeableMixin {
constructor() {
super();
this.onSwipeLeft = () => console.log("Swiped left!");
this.onSwipeRight = () => console.log("Swiped right!");
}
}
customElements.define("swipeable-component", SwipeableComponent);Features:
- Swipe detection with customizable distance and time thresholds
- Direction detection (left, right, up, down)
- Configurable via
min-swipe-distanceandmax-swipe-timeattributes - Works seamlessly with other mixins using PointerCoordinator
- Order-independent: Can be nested in any order with other gesture mixins
Demo: http://localhost:8080/demos/mixins/swipeable-mixin/
Draggable Mixin
Adds drag gesture detection to components with customizable thresholds and real-time feedback.
<draggable-mixin drag-threshold="10">
<div>Drag me</div>
</draggable-mixin>import { DraggableMixin } from "ars-web-components";
class DraggableComponent extends DraggableMixin {
constructor() {
super();
this.addEventListener('dragstart', (e) => console.log('Drag started:', e.detail));
this.addEventListener('dragmove', (e) => console.log('Dragging:', e.detail));
this.addEventListener('dragend', (e) => console.log('Drag ended:', e.detail));
}
}
customElements.define("draggable-component", DraggableComponent);Features:
- Drag start, move, and end events with detailed coordinate data
- Direction detection (left, right, up, down) with distance tracking
- Configurable drag threshold via
drag-thresholdattribute - Real-time drag feedback with
dragmoveevents - Works seamlessly with other mixins using PointerCoordinator
- Order-independent: Can be nested in any order with other gesture mixins
Demo: http://localhost:8080/demos/mixins/draggable-mixin/
Roll Mixin
Adds roll animation effects to components.
<roll-mixin>
<div>Roll me</div>
</roll-mixin>import { RollMixin } from "ars-web-components";
class RollableComponent extends RollMixin {
constructor() {
super();
this.onRollStart = () => console.log("Roll started!");
this.onRollEnd = () => console.log("Roll ended!");
}
}
customElements.define("rollable-component", RollableComponent);Demo: http://localhost:8080/demos/mixins/roll-mixin/
Mixin Coordination
ARS Web Components includes a sophisticated coordination system that allows multiple gesture mixins to work together seamlessly on the same element.
Pointer Coordination
Gesture mixins in this package share an internal PointerCoordinator utility to manage pointer capture and redispatching.
That coordination is what allows drag and swipe behaviors to coexist on the same interaction surface without fighting over the same pointer stream.
Features:
- Prevents conflicts when multiple mixins try to capture the same pointer
- Smart scroll prevention that only activates during active gestures
- Event redispatching system to prevent infinite loops
- Early gesture detection for responsive touch interactions
- Order-independent coordination: Works regardless of mixin nesting order
Using Multiple Mixins Together
You can combine multiple gesture mixins on the same element in any order:
<!-- DraggableMixin as parent, SwipeableMixin as child -->
<draggable-mixin drag-threshold="10">
<swipeable-mixin min-swipe-distance="30" max-swipe-time="800">
<div>Drag for movement, swipe for quick actions</div>
</swipeable-mixin>
</draggable-mixin>
<!-- SwipeableMixin as parent, DraggableMixin as child -->
<swipeable-mixin min-swipe-distance="30" max-swipe-time="800">
<draggable-mixin drag-threshold="10">
<div>Swipe for quick actions, drag for movement</div>
</draggable-mixin>
</swipeable-mixin>In both examples:
- Dragging will trigger drag events
- Quick swipes will trigger swipe events
- Both mixins coordinate through PointerCoordinator
- Scroll prevention only activates when gestures are detected
- Order-independent: Works regardless of which mixin is parent/child
Development
Quick Start
cd ars-web-components
npm install
npm run build # Compile TypeScript → dist/
npm test # Run all 785 tests
npm start # Start dev server on port 8080Scripts
| Script | Description |
|---|---|
| npm run build | Compile TypeScript to dist/ via tsc |
| npm test | Run all tests (Vitest, single run) |
| npm run test:watch | Run tests in watch mode |
| npm run test:coverage | Run tests with coverage report |
| npm run dev | Start the Vite dev server for live demo development |
| npm start | Start Express dev server on port 8080 |
| npm stop | Stop the dev server |
Experimenting with Components and Mixins
Once the server is running, you can access:
- Main Demo Gallery: http://localhost:8080/
- Individual Components:
- form-primitives (button + toggle): http://localhost:8080/demos/components/form-primitives/
- ars-input: http://localhost:8080/demos/components/ars-input/
- ars-color-select: http://localhost:8080/demos/components/ars-color-select/
- ars-toast: http://localhost:8080/demos/components/ars-toast/
- ars-tabs: http://localhost:8080/demos/components/ars-tabs/
- ars-select: http://localhost:8080/demos/components/ars-select/
- ars-table: http://localhost:8080/demos/components/ars-table/
- ars-info-tile: http://localhost:8080/demos/components/ars-info-tile/
- ars-calendar: http://localhost:8080/demos/components/ars-calendar/
- ars-dialog: http://localhost:8080/demos/components/ars-dialog/
- ars-page: http://localhost:8080/demos/components/ars-page/
- ars-data-roller: http://localhost:8080/demos/components/ars-data-roller/
- ars-line-chart: http://localhost:8080/demos/components/ars-line-chart/
- ars-candlestick-chart: http://localhost:8080/demos/components/ars-candlestick-chart/
- Individual Mixins:
- pressed-effect: http://localhost:8080/demos/mixins/pressed-effect-mixin/
- localized: http://localhost:8080/demos/mixins/localized-mixin/
- remote-call: http://localhost:8080/demos/mixins/remote-call-mixin/
- swipeable: http://localhost:8080/demos/mixins/swipeable-mixin/
- draggable: http://localhost:8080/demos/mixins/draggable-mixin/
- show-if-property-true: http://localhost:8080/demos/mixins/show-if-property-true-mixin/
- roll: http://localhost:8080/demos/mixins/roll-mixin/
Co-development Setup
For projects that need to develop alongside ars-web-components, you can set up symlinks:
# In your project's node_modules
ln -s ../../../../ars-web-components ars-web-components-devAvailable Exports
Check src/index.ts for all available imports:
// Components
export { ArsButton } from "./components/ars-button/ars-button.js";
export { ArsCalendar } from "./components/ars-calendar/ars-calendar.js";
export { ArsCandlestickChart } from "./components/ars-candlestick-chart/ars-candlestick-chart.js";
export { ArsColorSelect } from "./components/ars-color-select/ars-color-select.js";
export { ArsDataRoller } from "./components/ars-data-roller/ars-data-roller.js";
export { ArsDialog } from "./components/ars-dialog/ars-dialog.js";
export { ArsInfoTile } from "./components/ars-info-tile/ars-info-tile.js";
export { ArsInput } from "./components/ars-input/ars-input.js";
export { ArsLineChart } from "./components/ars-line-chart/ars-line-chart.js";
export { ArsPage } from "./components/ars-page/ars-page.js";
export { ArsPageController } from "./components/ars-page/ars-page-controller.js";
export { ArsSelect } from "./components/ars-select/ars-select.js";
export { ArsTable } from "./components/ars-table/ars-table.js";
export { ArsTabs, ArsTabPanel } from "./components/ars-tabs/ars-tabs.js";
export { ArsToast } from "./components/ars-toast/ars-toast.js";
export { ArsToggle } from "./components/ars-toggle/ars-toggle.js";
export { ChartBase } from "./components/chart-base/chart-base.js";
export { ChartGPUContext } from "./components/chart-base/gpu/chart-gpu-context.js";
export { WebComponentBase } from "./components/web-component-base/web-component-base.js";
// Design System
export { getArsWebComponentsDefaultAdapter, initializeArsWebComponents } from "./design-system.js";
// Registration
export { registerArsWebComponents } from "./register.js";
// Mixins
export { DraggableMixin } from "./mixins/draggable-mixin/draggable-mixin.js";
export { LocalizedMixin } from "./mixins/localized-mixin/localized-mixin.js";
export { PressedEffectMixin } from "./mixins/pressed-effect-mixin/pressed-effect-mixin.js";
export { RemoteCallCallerMixin } from "./mixins/remote-call-mixin/remote-call-caller-mixin.js";
export { RemoteCallReceiverMixin } from "./mixins/remote-call-mixin/remote-call-receiver-mixin.js";
export { RollMixin } from "./mixins/roll-mixin/roll-mixin.js";
export { ShowIfPropertyTrueMixin } from "./mixins/show-if-property-true-mixin/show-if-property-true-mixin.js";
export { SwipeableMixin } from "./mixins/swipeable-mixin/swipeable-mixin.js";Requirements
- WebGPU-capable browser for chart components (Chrome 113+, Safari 17+, Firefox 141+)
- Modern browser with ES module support for all other components
- Node.js (for building and testing)
arslibdependency (automatically installed)
License
MIT
