Easy on the eyes.Hard to break.
Every component works from the keyboard, renders on the server and follows the same two color tokens. There are 2,687 tests, so you don't find out the hard way.
You write the component.Prism writes the rest.
Prism imports each Lit class, reads the properties Lit resolved, and writes an idiomatic package for every framework.Here is one component, and what came out.
/**
* @tag arc-toggle
* @fires {CustomEvent<{ checked: boolean }>} arc-change
*/
export class ArcToggle extends
DeclaredPropsMixin(FormControlMixin(LitElement)) {
static properties = {
checked: flag(false),
disabled: { type: Boolean, reflect: true },
size: oneOf(['sm', 'md', 'lg'], { default: 'md' }),
label: { type: String },
name: { type: String, reflect: true },
};
}
// Auto-generated by @arclux/prism 3.4.0 — do not edit manually
import React from 'react';
import { createComponent, type EventName } from '@lit/react';
import { ArcToggle } from '@arclux/arc-ui/toggle';
export interface ToggleProps {
required?: boolean;
readonly?: boolean;
checked?: boolean;
disabled?: boolean;
size?: 'sm' | 'md' | 'lg';
label?: string;
name?: string;
className?: string;
onArcChange?: (e: CustomEvent) => void;
}
export const Toggle = createComponent({
tagName: 'arc-toggle',
elementClass: ArcToggle,
react: React,
events: {
onArcChange: 'arc-change' as EventName<CustomEvent>,
},
});
<!-- Auto-generated by @arclux/prism 3.4.0 — do not edit manually -->
<script setup lang="ts">
import '@arclux/arc-ui/toggle';
import { ref } from 'vue';
import type { ArcToggle } from '@arclux/arc-ui/toggle';
defineOptions({ name: 'Toggle' });
const props = withDefaults(defineProps<{
required?: boolean;
readonly?: boolean;
checked?: boolean;
disabled?: boolean;
size?: 'sm' | 'md' | 'lg';
label?: string;
name?: string;
}>(), {
required: false,
readonly: false,
checked: false,
disabled: false,
size: 'md',
label: '',
name: '',
});
const emit = defineEmits<{
'arc-change': [event: CustomEvent];
'update:checked': [value: boolean];
}>();
function onArcChange(payload: CustomEvent) {
emit('arc-change', payload);
const detail = payload.detail as Record<string, unknown> | null;
if (detail) {
if ('checked' in detail) emit('update:checked', detail.checked as boolean);
}
}
const __el = ref<ArcToggle | null>(null);
/** The element, for the methods it is driven by: checkValidity() and reportValidity(). */
defineExpose({ element: __el });
</script>
<template>
<arc-toggle
:required="props.required"
:readonly="props.readonly"
:checked="props.checked"
:disabled="props.disabled"
:size="props.size"
:label="props.label"
:name="props.name"
@arc-change="onArcChange"
ref="__el"
>
</arc-toggle>
</template>
<!-- Auto-generated by @arclux/prism 3.4.0 — do not edit manually -->
<script lang="ts">
import '@arclux/arc-ui/toggle';
import type { ArcToggle } from '@arclux/arc-ui/toggle';
interface Props {
required?: boolean;
readonly?: boolean;
checked?: boolean;
disabled?: boolean;
size?: 'sm' | 'md' | 'lg';
label?: string;
name?: string;
class?: string;
id?: string;
style?: string;
title?: string;
role?: string;
slot?: string;
part?: string;
exportparts?: string;
dir?: string;
lang?: string;
translate?: string;
accesskey?: string;
enterkeyhint?: string;
inputmode?: string;
popover?: string;
contenteditable?: boolean | string;
tabindex?: number;
hidden?: boolean;
inert?: boolean;
draggable?: boolean;
spellcheck?: boolean;
autofocus?: boolean;
[key: `data-${string}`]: unknown;
[key: `aria-${string}`]: unknown;
[key: `on${string}`]: unknown;
}
let { required = false, readonly = false, checked = $bindable(), disabled = false, size = 'md', label = '', name = '', ...rest }: Props = $props();
let __el: HTMLElement | undefined = $state();
/**
* The element, for the methods it is driven by: checkValidity() and reportValidity().
*
* A function rather than a property: an instance `export` in runes mode
* captures its binding once, which would be before the element exists.
* Reach it with `bind:this` on this component, then `element()`.
*/
export function element(): ArcToggle | undefined {
return __el as ArcToggle | undefined;
}
// Two-way binding — mirror the event detail back onto the prop, then
// forward to the consumer's own handler, which {...rest} would otherwise
// have attached. These are declared after {...rest} below so they win.
function __onArcChange(e: Event) {
const detail = (e as CustomEvent).detail as Record<string, unknown> | null;
if (detail) {
if ('checked' in detail) checked = detail.checked as boolean;
}
(rest['onarc-change'] as ((e: Event) => void) | undefined)?.(e);
}
</script>
<arc-toggle {required} {readonly} {checked} {disabled} {size} {label} {name} bind:this={__el} {...rest}
onarc-change={__onArcChange}
>
</arc-toggle>
// Auto-generated by @arclux/prism 3.4.0 — do not edit manually
import { Component, ElementRef, inject, Input, Output, EventEmitter, forwardRef } from '@angular/core';
import { NG_VALUE_ACCESSOR, type ControlValueAccessor } from '@angular/forms';
import '@arclux/arc-ui/toggle';
import type { ArcToggle } from '@arclux/arc-ui/toggle';
@Component({
selector: 'arc-toggle',
standalone: true,
template: ``,
host: {
'(arc-change)': '_onArcChange($event)',
},
providers: [
{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => Toggle), multi: true },
],
})
export class Toggle implements ControlValueAccessor {
private readonly _el: ArcToggle = inject(ElementRef).nativeElement;
/** The element, for the methods it is driven by: checkValidity() and reportValidity(). */
get element(): ArcToggle {
return this._el;
}
@Input() set required(value: boolean) {
this._el.required = value;
}
get required(): boolean {
return this._el.required;
}
@Input() set readonly(value: boolean) {
this._el.readonly = value;
}
get readonly(): boolean {
return this._el.readonly;
}
@Input() set checked(value: boolean) {
this._el.checked = value;
}
get checked(): boolean {
return this._el.checked;
}
@Input() set disabled(value: boolean) {
this._el.disabled = value;
}
get disabled(): boolean {
return this._el.disabled;
}
@Input() set size(value: 'sm' | 'md' | 'lg') {
this._el.size = value;
}
get size(): 'sm' | 'md' | 'lg' {
return this._el.size;
}
@Input() set label(value: string) {
this._el.label = value;
}
get label(): string {
return this._el.label;
}
@Input() set name(value: string) {
this._el.name = value;
}
get name(): string {
return this._el.name;
}
@Output() checkedChange = new EventEmitter<boolean>();
_onArcChange(event: CustomEvent) {
const detail = event.detail as Record<string, unknown> | null;
if (!detail) return;
if ('checked' in detail) {
this.checkedChange.emit(detail.checked as boolean);
}
}
/* ── ControlValueAccessor ──
Bound to `checked`, committed on `arc-change`.
The listener lives in the constructor because the host metadata already
maps that event to this component’s own @Output. writeValue deliberately
does not call _onChangeFn: it is the form writing into the element, and
echoing it back would mark the control dirty on every setValue. */
private _onChangeFn: (value: boolean) => void = () => {};
private _onTouchedFn: () => void = () => {};
constructor() {
this._el.addEventListener('arc-change', () => {
this._onChangeFn(this._el.checked);
this._onTouchedFn();
});
}
writeValue(value: boolean | null | undefined): void {
const next = value ?? false;
this._el.checked = next;
}
registerOnChange(fn: (value: boolean) => void): void {
this._onChangeFn = fn;
}
registerOnTouched(fn: () => void): void {
this._onTouchedFn = fn;
}
setDisabledState(isDisabled: boolean): void {
this._el.disabled = isDisabled;
}
}
// Auto-generated by @arclux/prism 3.4.0 — do not edit manually
import { splitProps, type Component } from 'solid-js';
import '@arclux/arc-ui/toggle';
import type { ArcToggle } from '@arclux/arc-ui/toggle';
declare module 'solid-js/jsx-runtime' {
namespace JSX {
interface IntrinsicElements {
'arc-toggle': Record<string, unknown>;
}
}
}
export interface ToggleProps {
required?: boolean;
readonly?: boolean;
checked?: boolean;
disabled?: boolean;
size?: 'sm' | 'md' | 'lg';
label?: string;
name?: string;
onArcChange?: (e: CustomEvent) => void;
/** The element, for the methods it is driven by: checkValidity() and reportValidity(). */
ref?: ArcToggle | ((el: ArcToggle) => void);
class?: string;
id?: string;
style?: string;
title?: string;
role?: string;
slot?: string;
part?: string;
exportparts?: string;
dir?: string;
lang?: string;
translate?: string;
accesskey?: string;
enterkeyhint?: string;
inputmode?: string;
popover?: string;
contenteditable?: boolean | string;
tabindex?: number;
hidden?: boolean;
inert?: boolean;
draggable?: boolean;
spellcheck?: boolean;
autofocus?: boolean;
[key: `data-${string}`]: unknown;
[key: `aria-${string}`]: unknown;
[key: `on${string}`]: unknown;
}
export const Toggle: Component<ToggleProps> = (props) => {
const [local, rest] = splitProps(props, ['required', 'readonly', 'checked', 'disabled', 'size', 'label', 'name', 'onArcChange', 'ref']);
return (
<arc-toggle required={local.required} readonly={local.readonly} checked={local.checked} disabled={local.disabled} size={local.size} label={local.label} name={local.name} on:arc-change={local.onArcChange} ref={(el: ArcToggle) => { if (typeof local.ref === 'function') local.ref(el); }} {...rest}>
</arc-toggle>
);
};
// Auto-generated by @arclux/prism 3.4.0 — do not edit manually
import { h } from 'preact';
import { forwardRef } from 'preact/compat';
import { useImperativeHandle, useLayoutEffect, useRef } from 'preact/hooks';
import '@arclux/arc-ui/toggle';
import type { ArcToggle } from '@arclux/arc-ui/toggle';
export interface ToggleProps {
required?: boolean;
readonly?: boolean;
checked?: boolean;
disabled?: boolean;
size?: 'sm' | 'md' | 'lg';
label?: string;
name?: string;
onArcChange?: (e: CustomEvent) => void;
class?: string;
id?: string;
style?: string;
title?: string;
role?: string;
slot?: string;
part?: string;
exportparts?: string;
dir?: string;
lang?: string;
translate?: string;
accesskey?: string;
enterkeyhint?: string;
inputmode?: string;
popover?: string;
contenteditable?: boolean | string;
tabindex?: number;
hidden?: boolean;
inert?: boolean;
draggable?: boolean;
spellcheck?: boolean;
autofocus?: boolean;
[key: `data-${string}`]: unknown;
[key: `aria-${string}`]: unknown;
[key: `on${string}`]: unknown;
}
/** Driven by its methods: checkValidity() and reportValidity(). A ref on it holds the element. */
export const Toggle = forwardRef<ArcToggle, ToggleProps>(({ required, readonly, checked, disabled, size, label, name, onArcChange, ...rest }, forwarded) => {
const ref = useRef<ArcToggle>(null);
useImperativeHandle(forwarded, () => ref.current as ArcToggle, []);
useLayoutEffect(() => {
const el = ref.current;
if (!el) return;
const listeners: Array<[string, EventListener]> = [];
if (onArcChange) {
const fn: EventListener = (e) => onArcChange(e as CustomEvent);
el.addEventListener('arc-change', fn);
listeners.push(['arc-change', fn]);
}
return () => listeners.forEach(([name, fn]) => el.removeEventListener(name, fn));
}, [onArcChange]);
return h('arc-toggle', { ref, required, readonly, checked, disabled, size, label, name, ...rest });
});
// Generated — do not edit
import { ArcToggle } from './toggle.js';
if (!customElements.get('arc-toggle')) customElements.define('arc-toggle', ArcToggle);
ArcToggle[Symbol.for('tagName')] = 'arc-toggle';
export { ArcToggle };
Across all 169 components that comes to 1,212 files and 59,148 lines of framework code, written by Prism 3.4.0. CI regenerates every one on each push and fails on a single changed character.Framework guides →
Not a mockup.A working app.
An issue tracker made of ARC parts and some layout. Click things, drag things, file a bug.It goes nowhere, which makes this the most honest tracker you'll use today.
All issues
Cycle 42, Sep 22 to Oct 5. You are signed in as Nina.
No issues match that filter.
Built from<arc-sidebar><arc-page-header><arc-breadcrumb><arc-search><arc-segmented-control><arc-list><arc-avatar><arc-tag><arc-kanban><arc-chart><arc-bar-list><arc-progress><arc-sheet><arc-description-list><arc-timeline><arc-dialog><arc-input><arc-select><arc-textarea><arc-button><arc-toast>
Yours,all the way down.
Set two colors, four tokens, and every gradient, glow and focus ring follows.The utility classes read the same tokens, so your layout follows too.
:root { --accent-primary: #4d7ef7; --accent-primary-rgb: 77, 126, 247; --accent-secondary: #9567ff; --accent-secondary-rgb: 149, 103, 255;}That file is the whole theme, and it is live: pick a swatch and this page repaints, the light behind the hero included.Build a theme in the Synthesizer →
272 utility classes read the same tokens as the components. These sliders scope their change to this card, the way an override on a container works in your app. Utility classes →
Every pushruns the gauntlet.
Straight from this repository's CI, under the names it uses.
- Generated files match source0 diffs
- Component tests2687 tests
- SSR render check195/195
- axe-core audit0 serious
- Typecheck generated declarationsevery subpath
- Smoke-test packed tarballsevery package
Keyboard patterns follow the ARIA Authoring Practices, and every component manages its own focus and honors reduced motion.Accessibility →
What changedin v4.9.1.
Hide the Box Too.Released September 29, 2026. Every package ships at the same version.
A docs fix for arc-top-bar, from getpulsar.dev.