Forms
Controls that look like OpenVibe, on every site
Every select is OpenVibe’s own: a menu on a computer, a sheet from the bottom on a phone. A choice can say whether it is ready, how much is used, and how to set it up. The form underneath is the browser’s, so everything that worked still works, and a page without JavaScript keeps its plain controls.
The select
Open it. On a phone it rises from the bottom with rows a finger tall; on a computer it opens under the button, or above it when there is more room. The keyboard works the whole way: arrows, Home and End, typing to jump, Enter, Escape.
Used when Claude Code nears its weekly limit.
Chosen: codex
<select id="fm-agent" name="agent" data-ov-title="Fallback agent">
<option value="">None</option>
<option value="codex" data-state="ok" data-badge="20% used"
data-desc="ChatGPT plan · signed in on ada-laptop" selected>Codex</option>
<option value="opencode" data-state="warn" data-badge="88% used"
data-desc="5-hour window nearly full">OpenCode</option>
<option value="minimax" disabled data-state="off" data-desc="Not signed in on this machine"
data-href="#fm-attrs" data-href-label="Sign in">MiniMax Code</option>
</select>Several, search, groups
A long list gets a search box. Groups become headings. A multiple select keeps its list open and counts what is chosen.
Switches and boxes
A checkbox with role="switch" is a switch. Checkboxes, radios and sliders take the site’s accent. Nothing here changes a size, so a site’s own layout never moves.
Switch rows and segmented choices
The kit builds the two rows a settings form is made of. A switch row is 56 px and the whole row is the target: an icon tile, a title, one line, and an iOS-style switch that is the native checkbox underneath, so it works without JavaScript. A segmented control puts three or four choices in one 44 px control, icons before the words.
Chosen: public
// The kit's builders, server or browser (openvibe-shared/kit, docs/kit.md)
kit.switchRow({
name: 'burn_after_read', icon: 'flame',
title: 'Burn after reading', line: 'Gone once it is opened',
})
kit.segmented({
name: 'visibility', value: 'public', label: 'Visibility',
items: [
{ value: 'public', label: 'Public', icon: 'globe' },
{ value: 'unlisted', label: 'Unlisted', icon: 'link' },
{ value: 'private', label: 'Private', icon: 'lock' },
],
})Fields and choices
Opt in with .ov-field for the whole field: a label, a control 44 px tall (16 px text on a phone, so it never zooms), a hint, an error. .ov-choice draws a choice as a card.
Sealed, never shown again.
<div class="ov-field">
<label for="key">API key</label>
<input id="key" name="key" autocomplete="off">
<p class="ov-field-hint">Sealed, never shown again.</p>
</div><div class="ov-choices">
<label class="ov-choice">
<input type="radio" name="plan" value="go">
<span class="ov-choice-body">
<b class="ov-choice-title">OpenCode Go</b>
<span class="ov-choice-desc">A month of open models</span>
</span>
</label>
…
</div>What it reads
Plain HTML attributes: a server that knows what a person has set up writes them, and every site draws them the same way.
| Attribute | On | What it does |
|---|---|---|
data-desc | option | A second line under the label. |
data-badge | option | A short tag on the right: “Signed in”, “72% used”. |
data-state | option | ok · warn · bad · off · new: a dot before the label, and the badge’s tone. |
data-icon | option | One or two characters before the label: an emoji, a letter. |
data-href · data-href-label | option | A link on the row (“Set up”), kept on a disabled choice: it says how to make it ready. |
data-ov-search | select | on or off: a search box (on by itself from 9 choices). |
data-ov-title | select | The phone sheet’s title; otherwise the select’s own label. |
data-ov-select="bare" | select | No box of its own: for a select inside a chip or a toolbar. |
multiple | select | Boxes on the rows; the list stays open; the button says “a, c” or “3 chosen”. |
data-ov-native | any | Keeps the browser’s own control, on it and everything inside it. |
Use it
On an OpenVibe site the navbar brings both files once the page settles. Anywhere else they are free (MIT). <html data-ov-forms="off"> keeps a whole site on the browser’s own.
<!-- On an OpenVibe site: nothing. The navbar loads both files for a page with a form control. -->
<!-- Anywhere else (MIT): -->
<link rel="stylesheet" href="https://openvibe.design/shared/ov-forms.css">
<script src="https://openvibe.design/shared/ov-forms.js" defer></script>