AccessDeniedState
The panel shown when a signed-in user may not see what they asked for.
Being refused is an expected outcome of a permission model, not a failure. That distinction
drives the whole component: it carries a lock rather than a destructive icon, and it does not
take role="alert" — it is the page content, not an interruption to it.
No pricing role assigned
Your account can sign in but has not been given a pricing role, so there is nothing here to show yet. Contact IT to be added to the right group.
Your account has conflicting roles
Two pricing roles are assigned to your account, so we cannot tell which permissions apply. Access is blocked until one is removed.Assigned: rms-revenue-manager, rms-product-team
You don't have access to Pricing Engine Admin
This area is limited to Revenue Managers.
Props
Prop
Type
Choosing between this and ErrorState
The two are easy to conflate and should not be.
| Situation | Component | Why |
|---|---|---|
| We know who you are and you may not do this | AccessDeniedState | The system worked correctly. Nothing to retry. |
| We could not work out what you may do | ErrorState | A lookup failed or a claim was malformed — that is a fault, so it announces itself and offers a retry. |
| You are not signed in at all | neither | Redirect to sign-in. A panel telling someone they lack access when they simply have no session is a dead end. |
Reaching for ErrorState on a refusal interrupts a screen reader for a page that rendered
exactly as intended, and offers a retry button that cannot possibly help.
Writing the copy
title and description are props rather than baked in because the only useful sentence is the
one that says what to do next, and that differs by cause. "Ask your manager to add you to the
Revenue Manager group" and "your account has two conflicting roles" need different next steps.
- Do name the thing the user needs, not the thing that failed.
- Do use
detailfor facts support can act on — which roles conflict, which permission was required. It renders muted beneath the description, so it stays out of the way of the sentence that matters. - Don't ship a bare "Access denied" or "403". It is accurate and leaves the reader with nowhere to go.
- Don't name internal permission strings in
description. Put them indetail, where they read as diagnostics rather than as the message.
Accessibility
- No
role="alert", deliberately — see above. If you genuinely need to announce a permission lookup failure as it appears, that isErrorState. - The lock icon is decorative; the title carries the meaning. Never rely on the icon alone.
- Pass
actionwhen the user has somewhere else they can actually go. A panel with no exit in a full-page takeover leaves keyboard users stranded.
Do / Don't
- Do gate the action, not the page, when the permission model allows viewing. Hiding a whole page from someone who is allowed to read it is a worse outcome than a disabled button.
- Do pair it with the shown-but-disabled convention for individual controls — a permission-gated control stays visible and disabled with an explanatory tooltip, rather than disappearing.
- Don't use it for an empty result. An empty list is a normal response; that is
EmptyState.