# ren-otp Component Contract

One-time-passcode segmented input composite.

Load this file after `ren-design.md` and before generating, editing, or reviewing `ren-otp` UI.

## Purpose

- Provide the agent-facing public contract for the `ren-otp` composite.
- Keep generated markup aligned with the colocated CSS/JS source.
- Route theming through semantic tokens and the component token API instead of ad hoc styles.

## Use When

- You need the Otp composite behavior or visual role.
- The UI should stay inside RenDS' vanilla HTML/CSS/JS conventions.
- The implementation can use the public selectors, states, and imports listed here.

## Do Not Use When

- Native HTML plus `base/primitive-zero.md` is enough.
- A simpler primitive can express the UI without this composite.
- You would need to invent undocumented selectors, states, or JavaScript APIs.

## aiHints

```yaml
selectionCriteria:
  useWhen:
    - "User must enter a short fixed-length code split across N single-character slots (SMS OTP, 2FA, email PIN)."
    - "Need auto-advance to the next slot, Backspace to previous slot, Arrow keys to navigate, Home/End to jump to first/last."
    - "Need clipboard paste to distribute the pasted string across consecutive slots and focus the next empty one."
    - "Need numeric vs alphanumeric filtering (inputmode=\"numeric\" or \"text\") and a per-slot data-filled state."
    - "Need explicit data-valid / data-invalid states on the wrapper plus a ren-complete event when all slots fill."
  avoidWhen:
    - "User pastes a long password / token in one go — use a single <input type=\"password\"> with autocomplete=\"one-time-code\" if it is a single field."
    - "Free-form numeric value with unit / decimals — use ren-number-field."
    - "Code length is variable or unknown — use a single text input."
    - "Code is alphanumeric AND case-sensitive in a way the slot UI cannot make obvious — fall back to a single masked input."

canonicalImports:
  css:
    - "rends/components/composites/ren-otp/ren-otp.css"
  js:
    - "rends/components/composites/ren-otp/ren-otp.js"
  notes:
    - "Public Token API has no --ren-otp-* tokens; theme through the semantic input tokens and size scale listed below."
    - "If the page already imports rends/components/index.css, do not import the CSS again."

requiredMarkup:
  - "Use <ren-otp length=\"6\" type=\"numeric\"> as the host element — slots are generated by JS, do not author them by hand."
  - "If you must author slots, each must be a real <input class=\"ren-otp-slot\" type=\"text\" maxlength=\"1\" inputmode=\"numeric|text\" aria-label=\"Code digit N of M\" data-index=\"N\">."
  - "Optional separators are <div class=\"ren-otp-separator\"> placed between slot groups; they are pointer-events: none and decorative."
  - "Size variants .ren-otp-sm and .ren-otp-lg go on the wrapper; do not size individual .ren-otp-slot."
  - "Validation state goes on the wrapper as [data-valid] or [data-invalid]; the per-slot [data-filled] is set automatically by the component."

forbiddenPatterns:
  - "Using a single <input maxlength=\"6\"> styled to look segmented — keyboard caret control and paste-distribute won't work."
  - "Removing maxlength=\"1\" / inputmode — without them mobile keyboards switch back to letters and the auto-advance breaks."
  - "Manually wiring focus on slot blur / change — let handleSlotInput / handleSlotKeydown manage focus transitions to keep Backspace and paste flows correct."
  - "Hardcoding the slot size — use --touch-min for the default, --size-sm via .ren-otp-sm, --size-lg via .ren-otp-lg."
  - "Mixing data-valid and data-invalid on the wrapper simultaneously — setValid() removes the opposite attribute by design."

tokenPolicy:
  allowed:
    - "Semantic input tokens: --color-input-bg, --color-input-bg-hover, --color-input-border, --color-input-border-focus, --color-input-focus-ring, --color-input-placeholder, --color-disabled-bg, --color-disabled-text."
    - "Semantic state / text tokens: --color-text, --color-text-muted, --color-border, --color-fill, --color-accent, --color-danger, --color-success."
    - "Layout / type / motion tokens: --space-0-5, --space-1, --space-2, --space-3, --stroke-1, --radius-sm, --radius-md, --radius-lg, --touch-min, --size-sm, --size-lg, --text-sm, --text-lg, --body-size, --font-mono, --transition-tactile."
  forbidden:
    - "Primitive palette tokens (--blue-*, --gray-*, --red-*, --green-*, --orange-*, --yellow-*, --teal-*, --purple-*, --pink-*) in consumer overrides."
    - "Hardcoded hex / rgb() colors for focus rings or borders; the current CSS still uses rgb() for the danger / success ring tints — do not replicate, prefer tokens."
    - "Inventing --ren-otp-* custom properties not present in the source until they ship in the Public Token API."

accessibility:
  required:
    - "Slot dimensions default to var(--touch-min) (44px) for thumb accuracy; --size-sm only for non-touch surfaces."
    - "Each slot exposes aria-label=\"Code digit N of M\" so screen readers announce position; do not strip these labels."
    - "Backspace, ArrowLeft/Right, Home, End move focus across slots — keep the keydown handler intact when extending the component."
    - "Paste handler must run on every slot so users can paste from any slot, not only the first."
    - "data-valid and data-invalid are paired with text feedback elsewhere on the form (do not communicate validation through color alone)."
    - ":focus-visible draws --color-accent at 2px outline; do not remove it without restoring an equivalent ring."
```

## Required Imports

```html
<link rel="stylesheet" href="rends/components/composites/ren-otp/ren-otp.css">
<script type="module" src="rends/components/composites/ren-otp/ren-otp.js"></script>
```

If the page already imports `rends/components/index.css`, do not import the CSS twice.

## Canonical Markup

```html
<ren-otp length="6" type="numeric" aria-label="Verification code"></ren-otp>

```

Use the docs page and source files listed below for full examples before adding production markup.

## Variants And Public Selectors

- `.ren-otp`
- `.ren-otp-lg`
- `.ren-otp-separator`
- `.ren-otp-slot`
- `.ren-otp-sm`

## States And Attributes

- `[data-filled]`
- `[data-invalid]`
- `[data-valid]`
- `:disabled`
- `:focus-visible`
- `:hover`

## Public Token API

- `None detected in the colocated CSS/JS. Check related files before inventing one.`

If no `--ren-*` token is detected here, theme through semantic tokens from `tokens/tokens.md` and avoid selector overrides.

## Accessibility Contract

- Preserve native semantics first; add ARIA only when semantic HTML is insufficient.
- Keep visible keyboard focus via RenDS focus tokens and `:focus-visible`.
- Keep interactive hit areas at 44px minimum unless this file's source clearly defines a smaller non-touch target.
- Respect reduced motion by using RenDS duration/easing tokens only.
- Do not communicate state through color alone.
- Keep JS behavior progressive: the visual structure should remain understandable before enhancement.

## Related Files

- `components/composites/ren-otp/ren-otp.css`
- `components/composites/ren-otp/ren-otp.js`
- `docs/components/ren-otp.html`
- `ren-design.md`
- `tokens/tokens.md`
- `base/layouts.md`

## Test Expectations

- Run component or docs a11y coverage when markup, states, or ARIA change.
- Run CSS lint when selectors, tokens, or visual states change.
- Manually verify light/dark themes when color, surface, border, or shadow behavior changes.
