R1.03.5overlapping state prioritydesign

Overlapping states need an explicit priority rule

Aliases: state stacking · compound state · state precedence · priority rule

What it is

Loading, disabled, error, selected, and focus can all be true at once: a request is in flight, the field is business-disabled, local validation has already failed. The answer is not another artboard titled “loading and disabled and error.” It is an overlapping state priority: which layer is allowed to show, which is suppressed, and which one assistive technology reads.

Without that rule, CSS specificity or the order of JSX becomes the referee. Compound states are a stacking operation, not a second pass over the Cartesian product.

Why it happens

States are not a mutually exclusive enum. Input channels, async requests, and validators flip bits independently, and the truths intersect. If the visual layer paints “last matching class wins,” different files with different class order pick different winners: one spinner covers the red text, another lets the red text cover the spinner and still receive clicks. Behavior needs a winner too: if disabled is true, may the user cancel the request; if error is true, does the focus ring remain.

Priority is an ordered list, not a new matrix. A common order puts unavailability above interactive feedback (disabled suppresses hover), in-flight above submittable, and failure above empty — products may differ, but the order must be written down. Unwritten priority is re-judged every time class names are refactored. The defect looks like “sometimes it looks wrong,” not like a missing frame.

Where it stops holding

A control modeled as mutually exclusive and guaranteed so by a single state machine (the current step of a stepper) does not need a stacking rule; adding one implies illegal combinations exist. A page-level blocking overlay already decides priority for every control beneath it; the component need not stack a second “globally busy.” Print and static export have no runtime intersection; emit one final snapshot. Motion that interpolates two states into a mid-color is a transition, not a third stable compound state, and must stay out of the priority table.

Applying it

  • List states allowed to be true together as a high-to-low priority, and note whether each layer suppresses appearance, interaction, or both.
  • In Storybook, toggle the most common pairs at once (loading+disabled, error+focus, selected+disabled) and read the winner against the table.
  • Expose the same winner to assistive technology: the announced name must not fight the visual winner (still saying “Edit” while a spinner shows).
  • How to check: screenshot every allowed pair and record one keyboard pass. Visual winner, whether it can activate, and the screen-reader utterance must point at the same layer. Any mismatch means specificity is standing in for priority.

Related

  • Same group: R1.03.1 Every component needs a complete interactive state set · R1.03.2 Loading, empty, and error are the states most often left unspecified · R1.03.3 Missing states get improvised during implementation · R1.03.4 Transitions between states fail more often than the states themselves · R1.03.6 States must be controllable and observable from outside the component
  • Nearby: R2.05 Completeness of edge-case delivery · R3.09 Semantic structure and accessibility implementation
  • Search terms: overlapping state priority · state precedence · compound state

Cards in the same group

Quick Actions

Share

Share this page

ios_share

https://hci.top/en/handbook/R1.03.5