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.

Visibility

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.

AttributeOnWhat it does
data-descoptionA second line under the label.
data-badgeoptionA short tag on the right: “Signed in”, “72% used”.
data-stateoptionok · warn · bad · off · new: a dot before the label, and the badge’s tone.
data-iconoptionOne or two characters before the label: an emoji, a letter.
data-href · data-href-labeloptionA link on the row (“Set up”), kept on a disabled choice: it says how to make it ready.
data-ov-searchselecton or off: a search box (on by itself from 9 choices).
data-ov-titleselectThe phone sheet’s title; otherwise the select’s own label.
data-ov-select="bare"selectNo box of its own: for a select inside a chip or a toolbar.
multipleselectBoxes on the rows; the list stays open; the button says “a, c” or “3 chosen”.
data-ov-nativeanyKeeps 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>