Forms & Selection
The input vocabulary. Every control shares the structural radius and the hairline border; the focused label and ring take the live accent. Build fields with the .across-field scaffold.
Field
.across-field · .across-inputThe label + control + helper scaffold. The label colors to the accent on focus; required and optional markers and an error state are built in.
<div class="across-field">
<label class="across-field__label">Display name <span class="across-field__req">*</span></label>
<input class="across-input" placeholder="e.g. Across Team">
<span class="across-field__help">Shown on shared boards.</span>
</div>
Select & Input Group
.across-select · .across-input-groupA styled native <select> with a custom chevron, and joined inputs/addons that share one hairline border.
<div class="across-select">
<select class="across-select__field">…</select>
<i class="ph-light ph-caret-down across-select__chevron"></i>
</div>
<div class="across-input-group">
<span class="across-input-group__addon">https://</span>
<input class="across-input" value="orbitacross.app">
<button class="across-btn">Verify</button>
</div>
Choice Controls
.across-check · .across-radio · .across-st-toggle · .across-st-segCheckbox, radio, switch, and segmented — all take the accent when on. Tabs and segmented auto-wire through primitives.js.
<label class="across-check"><input type="checkbox" checked><span></span> Sync</label>
<button class="across-st-toggle is-on"><span class="across-st-toggle__track"><span class="across-st-toggle__thumb"></span></span> Live</button>
<div class="across-st-seg"><button class="across-st-seg__btn is-active">24h</button>…</div>
Combobox
AcrossUI.comboboxFilterable single- or multi-select, fully keyboard-navigable. Created imperatively from a data array — no markup to maintain.
AcrossUI.combobox(el, {
options: ["London", "Tokyo", "Berlin", "Paris"],
multiple: true,
value: ["london"],
placeholder: "Select cities…",
onChange: function (v) { console.log(v); }
});
Fieldset
.across-fieldsetGroups related fields under a legend on a hairline — structure without a box. An optional hint sits opposite the legend.
<fieldset class="across-fieldset">
<legend class="across-fieldset__legend">Notifications
<span class="across-fieldset__hint">Applies to this workspace</span></legend>
<div class="across-fieldset__body">…</div>
</fieldset>
Toggle Group
[data-toggle-group]A segmented set of buttons — single-select by default, or data-toggle-group="multi" for many. The active member takes the accent; auto-wired by primitives.js.
<div class="across-toggle-group" data-toggle-group> <!-- single -->
<button class="across-toggle-group__btn is-on">Day</button>
<button class="across-toggle-group__btn">Week</button>
</div>
<div class="across-toggle-group" data-toggle-group="multi">…</div>
File Upload
.across-dropzoneClick or drag files onto the dropzone; each lands in the file list with size and a remove control. Auto-wired — point it at a hidden <input type="file">.
<div class="across-dropzone" data-dropzone>
<input type="file" multiple hidden>
<div class="across-dropzone__icon"><i class="ph-light ph-upload-simple"></i></div>
<div class="across-dropzone__title">Drop files here or <b>browse</b></div>
<div class="across-dropzone__hint">PNG, JPG, or PDF</div>
</div>
<div class="across-file-list"></div>
Tag / Chip Input
.across-chip-inputType and press Enter (or comma) to add a chip; Backspace removes the last. With data-validate="email", malformed tokens flag as invalid.
<div class="across-chip-input" data-taginput data-validate="email">
<span class="across-chip-input__chip"><span class="across-chip-input__chip-label">[email protected]</span>
<button class="across-chip-input__chip-x"><i class="ph-light ph-x"></i></button></span>
<input class="across-chip-input__field" placeholder="Add teammate email…">
</div>
Color Input
.across-color-inputCurated brand swatches plus an optional custom picker — not a free-for-all wheel. The selected hex reads out in mono.
<div class="across-color-input" data-color-input>
<div class="across-color-swatches">
<button class="across-color-swatch is-selected" style="background:var(--track)" data-color="#1589FF"></button>
<label class="across-color-swatch across-color-swatch--custom"><i class="ph-light ph-plus"></i>
<input type="color"></label>
</div>
<div class="across-color-value"><span class="across-color-value__hex">#1589FF</span></div>
</div>
Input OTP
.across-otpSegmented one-time-code entry — auto-advances on type, steps back on Backspace, and distributes a pasted code across the slots. Fires a complete event when full.
<div class="across-otp" data-otp>
<input class="across-otp__slot" inputmode="numeric" aria-label="Digit 1">
<input class="across-otp__slot" inputmode="numeric" aria-label="Digit 2">
<span class="across-otp__sep"></span>
<input class="across-otp__slot" inputmode="numeric" aria-label="Digit 3">
</div>
<!-- listen: el.addEventListener("complete", e => e.detail.value) -->
Date Field
.across-datepicker · .across-calendarA trigger that opens a Monday-start month grid (the shared .across-calendar) in a popover — auto-wired by primitives.js. Pick a day to set the value; Esc or outside-click closes. Sets data-value (YYYY-MM-DD) and fires change.
<div class="across-datepicker" data-datefield>
<button class="across-datepicker__trigger">
<i class="ph-light ph-calendar-blank across-datepicker__icon"></i>
<span class="across-datepicker__value is-empty">Select a date</span>
<i class="ph-light ph-caret-down across-datepicker__caret"></i>
</button>
</div>
<!-- listen: el.addEventListener("change", e => e.detail.value) -->

