Pular para o conteúdo

Interaction — States

interaction specs/interaction/states.kmd

Visual + behavioral contract for the 8 interaction states every Koder control passes through: enabled, hovered, focused, pressed, dragged, selected, activated, disabled. Material parity (`/foundations/interaction/states`). Token-level recipes for applying state overlays without per-control duplication.

Quando esta spec se aplica

Triggers primários

Todos os triggers

Corpo da especificação

Spec — Interaction: States

Facet Visual do Koder Design. Material parity: https://m3.material.io/foundations/interaction/states.

The 8 states

StateTriggerPersistence
EnabledDefaultUntil another state takes precedence
HoveredPointer overWhile hover
FocusedKeyboard nav lands; or explicit focus()Until blur
PressedPointer down OR Space/Enter on focusedWhile pressed
DraggedPointer down + move past thresholdWhile dragging
SelectedUser explicit selection (selection.kmd)Until deselected
ActivatedPersistent expanded/open state (nav item current)Until view changes
DisabledApp logicUntil logic changes

States are composable: a button can be focused AND hovered AND pressed simultaneously. Visuals layer additively.

R1 — Overlay opacity recipe

Every state visual is an overlay on top of the base control color. Opacity per state:

StateOverlay opacity
Hover8%
Focus12%
Pressed12% (+ ripple on touch)
Dragged16%
Selected12% (persistent)
Activated12% (persistent)

Combined states sum opacities (capped at 24%): focused+pressed = 24%.

Token: overlay color = var(--on-surface) for surface controls, var(--accent-on) for accent-filled controls. Applied as background-image: linear-gradient(<overlay>, <overlay>) so it composes on top of the base.

R2 — Focus ring contract

Focus indication must be always visible for keyboard navigation — never :focus { outline: none }.

.control:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}
  • focus-visible (not focus) — avoids ring on mouse click (per user agent heuristic), preserves it on keyboard nav
  • 2px stroke minimum (3px on TV per adaptive-design.kmd R7)
  • outline-offset so the ring sits OUTSIDE the control border
  • Color: var(--focus) — typically the accent at higher contrast

R3 — Disabled state

Disabled controls:

  • Reduced opacity: 38% of normal (opacity: 0.38)
  • No hover/focus/press response
  • cursor: not-allowed (web), no ripple (Flutter)
  • Reads as "dimmed, not interactive" — does NOT use error/warning red
  • aria-disabled="true" (NOT disabled HTML attribute when the control should still be focusable for keyboard nav announcement)
  • Tooltip explaining WHY disabled (best practice — not enforced)

R4 — Pressed state on touch

On touch surfaces, "pressed" is brief — the press ends when the finger lifts. Visual:

  • Ripple emanating from touch point (Material default)
  • OR scale-down (transform: scale(0.97) for ~100ms) — for variants that don't want ripple
  • Audio feedback: optional (per voice/wake-word.kmd if voice enabled)

Pressed visual ends on touch-up OR after a maximum 600ms (whichever first) to avoid stuck-pressed appearance.

R5 — Dragged state

For draggable elements (file list items, sortable rows, kanban cards):

  • Elevation boost (shadow +2 levels) during drag
  • Slight scale (1.04) so the dragged item visually "lifts"
  • Drop targets show 12% accent-tinted overlay (consistent with R1)
  • Drag preview shows the item content (not just a placeholder)

R6 — Selected vs activated

These are easily confused. Rule:

  • Selected — user picked this thing as part of an action they haven't completed (selection.kmd). Multi-select examples.
  • Activated — this thing represents the CURRENT view/section. Navigation item showing "you are here". Persists across the current view.

A nav item for the current page = activated. A list item picked for bulk-delete = selected.

R7 — State announcement (a11y)

Screen readers announce state changes. Requirements:

StateAnnouncement
FocusedElement role + label + state ("Button, Save, focused")
PressedImplicit in focus + native role
Selected"Selected" appended; via aria-selected="true"
Activated"Current" appended; via aria-current="page" (or "step", "true")
Disabled"Dimmed" / "Disabled" appended; via aria-disabled="true"
Indeterminate"Mixed" appended; via aria-checked="mixed"

R8 — Animation duration

State transitions use fast motion tokens (per future motion.kmd):

  • Hover → 100ms
  • Focus → 50ms (near-instant; aids keyboard speed)
  • Press → 50ms in, 100ms out
  • Drag start → 200ms (lift)
  • Drag end → 150ms (settle)

Reduced-motion users get all of these set to 0ms (instant transitions) via @media (prefers-reduced-motion: reduce).

  • interaction/selection.kmd — selection state details
  • foundations/elements.kmd — element families per control type
  • themes/color-schemes.kmd — overlay color tokens

Referências