Interactions
Interaction is a conversation. Details are the vocabulary. We build for the felt experience — not just what works, but what feels right.
Design principles
These principles guide how we handle interaction across Oztix applications. Follow them when building custom interactive UI.
Keyboard and focus
Keyboard works everywhere
The mouse is optional. Power users and assistive technologies rely on the keyboard.
Do
Don’t
divs that cannot be focused or activated with Enter/Space.Clear focus indicators
Navigation requires visibility. Users must always know where they are on the page.
Do
:focus-visible to avoid distracting mouse users. Roadie's is-interactive provides this automatically.Don’t
outline: none) without replacing them with a high-contrast alternative.Manage focus
Context changes require focus changes. Don't leave the user's focus behind when the UI changes.
Do
Don’t
Hit targets and touch
Match visual and hit targets
Fitts' Law applies to code. What looks clickable must be clickable — at a comfortable size.
Do
Don’t
Mobile input hygiene
Prevent disorienting zooms. Inputs must be legible by default on touch devices.
Do
<input> font size to ≥ 16px on mobile.Don’t
No dead zones
Proximity implies relationship. There should be no gap between a label and its control.
Do
Don’t
Respect user agency
The browser belongs to the user. Never disable native capabilities like zooming or pasting.
Do
<input> and <textarea> elements.Don’t
user-scalable=no) or block paste events for “security.”Forms
Labels are mandatory
Every control needs a name, even if visually hidden.
Do
<label> or aria-label. Clicking a visible label should focus its control.Don’t
Placeholders are examples
Placeholders should show how to answer, not what to answer.
Do
Don’t
Enter submits
The Enter key is the universal "Done" signal.
Do
Don’t
Keep submit active
Validation should be educational, not preventative. Disabled buttons hide the "why."
Do
Don’t
Don't block typing
Inputs accept input. Let the user type, then correct them.
Do
Don’t
Inline errors
Contextualise failure. Show the error where the problem is.
Do
Don’t
Autocomplete everywhere
Browsers are smart; let them help. Correct metadata enables one-click filling.
Do
autocomplete attributes and meaningful name values on inputs.Don’t
input_1 that confuse password managers.Unsaved changes protection
Data loss is a critical failure. Warn the user before destroying their work.
Do
Don’t
Feedback and state
Honest loading states
For fast, reversible operations, skip the loader entirely and use optimistic UI instead.
Do
Don’t
Optimistic UI
The interface should move as fast as the user's thought. Reserve loading states for high-stakes or slow operations.
Do
Don’t
Ellipsis implies continuation
An ellipsis indicates that an action is not yet complete or requires more input.
Do
Don’t
Destructive friction
Regret is expensive. Make irreversible actions hard to do by accident.
Do
Don’t
Forgiving interactions
Users are imprecise. The interface should anticipate intent, not punish inaccuracy.
Do
Don’t
Interaction utilities
Roadie provides two CSS utilities that encode many of these principles automatically. Apply them to elements that respond to user input.
is-interactive
For clickable elements — buttons, cards, links, toggles. Provides cursor, transitions, active press, focus ring, and disabled state. Pair with an emphasis-* class for visual styling.
Hover, click, and tab through these buttons to see transitions, active press, and focus rings.
| Behaviour | How |
|---|---|
| Cursor | cursor: pointer |
| Transitions | background, border, color, box-shadow, outline, transform — 0.2s ease |
| Active press | scale(0.99) |
| Focus ring | :focus-visible outline, intent-coloured with transparency |
| Disabled | opacity: 0.5, pointer-events: none, grayscale(0.3) |
Intent-coloured focus rings
The focus ring colour follows the nearest intent-* ancestor. Tab through these to see the ring change.
is-interactive-field
For form inputs — text fields, textareas, selects. Provides state-based colour transitions: neutral at rest, accent on focus, danger when invalid. Pair with emphasis-sunken or emphasis-raised.
Focus each input to see the accent border and ring. The invalid field shows danger styling.
| State | Background | Border | Outline |
|---|---|---|---|
| Rest | from emphasis | from emphasis | none |
| Hover | neutral-2 | neutral-7 | — |
| Focus | accent-2 | accent-9 | accent ring |
| Invalid | danger-2 | danger-9 | danger ring |
| Disabled | opacity: 0.5, pointer-events: none, grayscale(0.3) | ||
is-interactive-field-group
For composite form controls where multiple inputs share a single visual container — like the Combobox input group (text input + trigger button). Provides the same state transitions as is-interactive-field but scoped to the group wrapper instead of an individual input.
Use on the wrapping element that contains the child inputs. Focus within any child triggers the accent transition on the group. Pair with emphasis-raised or emphasis-sunken.
Emphasis + interaction layering
Emphasis presets and interaction utilities compose together. The emphasis sets appearance and hover/active colour shifts. The interaction utility adds mechanical behaviours. Together they form a complete interactive element.
Static vs interactive — hover to see the difference
emphasis-strong (static)
emphasis-strong + is-interactive
Hover, click, or tab to strong
emphasis-normal (static)
emphasis-normal + is-interactive
Hover, click, or tab to normal
emphasis-raised (static)
emphasis-raised + is-interactive
Hover, click, or tab to raised
Note: is-interactive-field provides its own hover, focus, and invalid logic — it does not use emphasis hover states. This is why form inputs use emphasis-sunken (which has no interactive states) paired with is-interactive-field (which provides all of them).
Component recipes
Common patterns from the component library.
| Component | Classes |
|---|---|
| Button (primary) | emphasis-strong is-interactive rounded-full |
| Button (secondary) | emphasis-normal is-interactive rounded-full |
| Button (subtler) | emphasis-subtler is-interactive rounded-full |
| Text input | emphasis-sunken border border-subtle is-interactive-field |
| Select | emphasis-raised border border-normal is-interactive-field |
| Clickable card | emphasis-raised is-interactive rounded-xl |
| Radio (inline) | emphasis-subtler is-interactive |
Quick reference
| Element | Utility | Emphasis | Notes |
|---|---|---|---|
| Button | is-interactive | strong / default / subtle / subtler | Add rounded-full |
| Text input | is-interactive-field | sunken | Add border border-subtle |
| Select | is-interactive-field | raised | Add border border-normal |
| Textarea | is-interactive-field | sunken | Add border border-subtle |
| Clickable card | is-interactive | raised | Add rounded-xl |
| Toggle / radio | is-interactive | subtler or default | — |
| Link | none (native) | — | Use underline underline-offset-2 |