A role and permission grid that enforces the implications between permissions instead of allowing impossible states.
Written by
Ahmed Amr
Year
2026
Status
Exploration
Built with
React, TypeScript, ARIA
Should an interface silently correct an invalid permission combination, or refuse it, or apply the correction and say so?
In this article3
Loading lab note
Approval permissions matrix
Permissions granted to each role. Approval above 10,000 requires approval up to 10,000.
Permission
Viewer
Initiator
Approver
Administrator
View balances
Create payment
Approve up to 10k
Approve above 10k
Manage users
What I was testing
Permissions in a payments product are not independent. The right to approve a
payment above a threshold necessarily includes the right to approve one below
it. Modelled as two free checkboxes, the interface can express a state the
backend will reject or, worse, accept and then behave unpredictably.
There are three ways to handle it, and picking between them is the actual design
work:
Allow the invalid state and fail on save. Honest but hostile; the user
discovers the rule only after doing the work.
Disable the dependent checkbox. Prevents the error, explains nothing. The
user is left guessing why a control is dead.
Apply the implication and announce it. The grid stays in a valid state
and the rule is taught at the moment it becomes relevant.
Apply the implication, then say what happened
Chose
Granting approval above the threshold also grants approval below it, and a live region states the change in words.
Trade-off
The interface changes something the user did not directly click, which is normally a thing to avoid. Doing it silently would be genuinely bad. This is a permissions grid, and someone who believes they revoked an authority they did not revoke has a real problem. Saying it out loud is what makes the automatic change acceptable.
The same logic runs in reverse: removing the lower authority removes the higher
one, with the same announcement.
Accessibility notes
A real <table>, with roles as column headers and permissions as row headers.
Each checkbox has an accessible name composed from its row and column
("Approve above 10k for Approver"), so a cell is never just an unlabelled box.
Cells are at least 44px tall, so the tap target is usable.
Implied changes are announced through a polite live region.
A visually hidden <caption> states the dependency rule up front.
Still open
This is an exploration rather than a settled pattern. Real approval chains add
dimensions this grid does not model, including per-counterparty limits, dual control
where two approvers are required, and the case where the initiator may not also
approve. Those probably do not belong in a matrix at all; a matrix is a good
shape for independent facts and a poor one for rules about sequence.