Flux 2.0

Component

Input

Single-line text entry, with the label, help and error pattern that goes with it.

Plain-text source, for agents and clipboard use: /components/source/input.txt

When to use it

Collects a short, free-form value. The field, its label, and its help or error text are one unit and should be built together — a bare input is almost always a bug.

Not this
For a value from a known, closed set use Select. For a long-form value use Textarea. For a boolean use Switch or Checkbox.

Examples

Default

html
<div class="flux-field">
  <label class="flux-field__label" for="ref">Booking reference</label>
  <input class="flux-input" id="ref" type="text" placeholder="KB-000000" />
</div>

With an info bubble

Explanatory text goes behind the info button, not below the field. The bubble opens on hover, focus or tap, stays open while the pointer is over it, and closes on Escape. Reference it from the control with aria-describedby so a screen reader announces it with the field.

html
<div class="flux-field">
  <div class="flux-field__header">
    <label class="flux-field__label" for="invoice-ref">
      Supplier invoice reference <span class="flux-field__required" aria-hidden="true">*</span>
    </label>
    <span class="flux-info" data-flux-info>
      <button class="flux-info__trigger" type="button" aria-label="About supplier invoice reference" aria-describedby="invoice-ref-info">
        <svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
          <circle cx="8" cy="8" r="6.25" stroke="currentColor" stroke-width="1.5" />
          <path d="M8 7.25V11" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
          <circle cx="8" cy="5" r="0.875" fill="currentColor" />
        </svg>
      </button>
      <span class="flux-info__bubble" id="invoice-ref-info" role="tooltip" hidden>Enter it exactly as printed on the invoice. Kaptio and the AP extract keep it unchanged.</span>
    </span>
  </div>
  <input class="flux-input" id="invoice-ref" type="text" required aria-describedby="invoice-ref-info" />
</div>

Error

Bind the message to the field with aria-describedby so a screen reader announces it, and set aria-invalid so the state is not conveyed by colour alone.

Add the part after the @, for example kaptio.com.

html
<div class="flux-field">
  <label class="flux-field__label" for="email">
    Email <span class="flux-field__required" aria-hidden="true">*</span>
  </label>
  <input class="flux-input" id="email" type="email" value="orvar@" aria-invalid="true" aria-describedby="email-error" />
  <p class="flux-field__help" data-tone="error" id="email-error">Add the part after the @, for example kaptio.com.</p>
</div>

Disabled

html
<div class="flux-field">
  <div class="flux-field__header">
    <label class="flux-field__label" for="locked">Currency</label>
    <span class="flux-info" data-flux-info>
      <button class="flux-info__trigger" type="button" aria-label="About currency" aria-describedby="locked-info">
        <svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
          <circle cx="8" cy="8" r="6.25" stroke="currentColor" stroke-width="1.5" />
          <path d="M8 7.25V11" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
          <circle cx="8" cy="5" r="0.875" fill="currentColor" />
        </svg>
      </button>
      <span class="flux-info__bubble" id="locked-info" role="tooltip" hidden>Set by the departure, and not editable here.</span>
    </span>
  </div>
  <input class="flux-input" id="locked" type="text" value="ISK" disabled aria-describedby="locked-info" />
</div>

Anatomy

PartDescription
LabelAlways present and always visible, above the field. Bold, small.
Control36px tall, 4px radius, border-strong outline so the boundary clears 3:1.
Info bubbleAn info button beside the label. Explanation, format and constraints open in a bubble on hover, focus or tap, never as text below the field.
Error textThe only message below the field. Says what to do, not that something failed.

API

NameKindDescription
flux-fieldclassWrapper binding label, control and message into one unit.
flux-field__labelclassThe visible label. Required on every field.
flux-inputclassThe control. Also applies to type=email, number, search, tel, url.
flux-field__headerclassRow holding the label and its info button. Use it only when the field has an info bubble.
flux-infoclassWrapper positioning the info button and its bubble.
data-flux-infoattributeInitialises hover, focus, tap and Escape behaviour. Requires flux-components.js.
flux-info__triggerclassIcon-only button. Its aria-label names the field: "About <field label>".
flux-info__bubbleclassrole="tooltip", hidden by default. The explanation, in one or two sentences.
flux-field__helpclassError text below the field, with data-tone="error". Not for explanation.
aria-invalidattributeMarks the field invalid. Drives the error border.

States

StateTreatment
Defaultborder-strong outline on field-bg.
Hoverborder-interactive.
Focus2px teal outline at 2px offset, from the global focus rule.
Invalidborder-error, plus error text bound with aria-describedby.
Disabledsurface-disabled fill at 60% opacity.
Info openBubble on layer-01 with a subtle border and shadow-md, above the label. The info icon turns icon-brand.

Keyboard

KeysAction
TabMoves focus into and out of the field, and onto its info button. Focusing the info button opens the bubble.
EscapeCloses an open info bubble. Where the field is a search or filter, clears it.

Accessibility

  • Every input needs a programmatically associated label. A placeholder is not a label — it disappears on focus and fails 4.5:1 by design.
  • Error text must be referenced by aria-describedby, or a screen reader user will hear the field is invalid without hearing why.
  • Never signal validity with colour alone; pair the border with aria-invalid and a message.
  • Reference the info bubble from both its button and the control with aria-describedby, so the explanation is announced with the field. With an error, list the error first: aria-describedby="ref-error ref-info".
  • The bubble meets WCAG 1.4.13: it opens on focus as well as hover, stays open while the pointer is over it, and closes on Escape. A tap pins it open for touch.

Tokens

The tier-3 and tier-2 tokens this component binds to. Change the token, not the component.

  • --flux-field-height
  • --flux-field-radius
  • --flux-field-padding-x
  • --flux-field-border
  • --flux-field-border-hover
  • --flux-field-border-error
  • --flux-field-placeholder
  • --flux-field-info-size
  • --flux-field-info-icon
  • --flux-field-info-bg
  • --flux-field-info-text
  • --flux-field-info-max-width

Do and don’t

Do

  • Keep one field height and one radius across an entire form.
  • Put explanation, format and constraints in the info bubble beside the label, so the form keeps one rhythm.
  • Size the field to the expected content where it aids scanning — a postcode field need not be full width.

Don’t

  • Do not use the placeholder as the label.
  • Do not use a blue focus ring. Focus is Flux teal in every theme.
  • Do not mark a field required with colour alone; use the asterisk and the required attribute.
  • Do not put explanatory text below the field. Only an error message sits there.
  • Do not put an action, a link or the only copy of critical information inside the bubble. It holds explanation the user can do without once they know the form.