@gluonjs/quarks
Typed native-element factories and headless interaction primitives. The package depends on Core but Core never imports it, so applications that only need the renderer do not install or bundle UI code.
@gluonjs/quarks at a glance
Runtime: browser · Release: 1.13.0
Documentation guide · npm · Source
Public API: @gluonjs/quarks
Install
npm install @gluonjs/quarksQuick start
import { Dialog, Listbox, createFocusScope, q } from '@gluonjs/quarks';Choose this package when
- Factory helpers for native elements and headless behavior primitives.
- Semantic building blocks without inventing roles or names.
- Low-level interaction helpers for higher UI packages.
Choose another boundary when:
- Does not create accessibility semantics on behalf of callers.
- Callers remain responsible for roles, names, and labels.
Related documentation
import { Dialog, Listbox, createFocusScope, q } from '@gluonjs/quarks';q.<tag>(), quark(), and fragment() preserve native HTML semantics. A factory never invents a role or accessible name; the caller owns every semantic requirement of the native element it selects. Void elements reject children. q.textarea() maps primitive children to the native defaultValue property because HTML parses textarea contents as raw text; use .value for controlled content. Template, Node, directive, and collection children are rejected. Direct template child interpolation inside raw-text and RCDATA elements (textarea, title, script, and style) is rejected with a runtime error that points to the supported complete binding form.
For stable, safe option-key shapes, a Quark factory caches a bounded explicit binding template per factory and sends class, style, data, ARIA, property, boolean, event, ref, URL, and child values to their dedicated Core Parts. This avoids repeating generic spread-key classification during commit while keeping open or unsupported prop bags on the generic spread path. Shape transitions remain correct but are not treated as stable template updates, so callers that need identity across changing key sets should keep the option-key set stable.
Quark factories do not add generic gluon or quark classes. Supply a component-specific class when styling a native element; headless primitives retain only their documented component classes such as gluon-overlay.
QuarkProps<ElementType> has no general string index signature. It derives native scalar values and explicit property/boolean bindings from the target DOM interface and types ARIA, data, class, style, event, and ref bindings. Use unsafeQuarkProps() only for a reviewed platform/vendor key that the typed contract does not yet contain. The complete component matrix and TypeScript diagnostic boundary are documented in docs/ui-extensibility.md.
Headless accessibility contracts
createFocusScope(container, options)focuses the requested initial target, contains Tab and Shift+Tab, and restores a connected trigger on deactivation. CallhandleKeydown()from the owning surface and always calldeactivate()when that surface closes.Overlayadds no semantic role. Pointer dismissal runs only when the pointer target is the overlay itself, so interaction inside its child is preserved.DialogrequireslabelorlabelledBy, emitsrole="dialog", exposesaria-modal, and supports Escape dismissal whenonDismissis supplied. The owner composes it with a focus scope and controls background inertness.Popoveruses the nativepopoverattribute. Its trigger must use the nativepopovertargetrelationship and retain an accessible name.TooltipandHoverCardare separate request-free anchored contracts. Tooltip is a non-interactiverole="tooltip"description. HoverCard is a labelled, focusablerole="dialog"surface. Their typedtriggerrenderer receives the ARIA, event, id, data, and ref properties that must be spread onto the actual native trigger; a wrapper is never presented as the control.
import { HoverCard, Tooltip, q } from '@gluonjs/quarks';
const help = Tooltip({
id: 'delivery-help',
trigger: ({ aria, ...owned }) => q.button({
...owned,
aria: { ...aria, label: 'How delivery timing works' },
type: 'button',
children: 'Delivery details',
}),
content: 'Timing is confirmed for the configured item.',
placement: 'block-end',
delay: 300,
contentAttributes: { class: 'delivery-tooltip' },
});
const details = HoverCard({
id: 'maker-details',
label: 'Maker details',
trigger: (owned) => q.button({ ...owned, type: 'button', children: 'Maker' }),
content: q.a({ href: '/makers/ada', children: 'Read the maker profile' }),
});Mouse hover honors delay; focus opens without moving focus; touch pointerdown and click form one deterministic toggle. Tooltip content is not a focus target and must not contain interactive descendants. HoverCard content can be entered with Arrow Down or Enter, or by ordinary pointer interaction. Escape closes the topmost overlay and restores its trigger when focus was inside the HoverCard. Document outside-interaction listeners, resize/scroll listeners, ResizeObserver, and timers exist only while needed and are removed on close or unmount. Reduced-motion preference removes the opening delay.
placement accepts block-start, block-end, inline-start, or inline-end. The opposite main-axis side is selected on collision and both viewport axes are clamped to an 8px edge. Logical inline placement follows the trigger's computed direction. The contract owns inline position, inset, margin, left, top, and the Tooltip's non-interactive pointer-events; consumers style appearance through contentAttributes.class and constructable stylesheets. hostAttributes and contentAttributes are separate, and owned semantics, behavior, ref, visibility, and position fields throw when supplied through the wrong extension point. IDs are unique per live document and use the HTML-safe [A-Za-z][A-Za-z0-9_-]* subset.
Listboxrequires a stableidand label. Arrow Up/Down, Home, and End select enabled options; disabled options are skipped. The owner persistsonChangeand rerenders the controlledvalue.Fielduses an implicit native label. Error text usesrole="alert"; callers must pass the corresponding invalid state to a custom child control.
The exported quarkManifest is the machine-readable stable-contract inventory. Browser behavior is covered in Chromium, Firefox, and WebKit by tests/ui-system.spec.ts. The interactive compiled example is docs-site/examples/ui-system.ts.
The package does not read or mutate document at import or SSR construction time. Anchored overlays install browser ownership only after a rendered trigger opens and release it on close or removal.
All visible strings and accessible names are caller inputs. The package performs no locale selection and supports either text direction through native semantics.