Skip to content

AccessDeniedPanel

<caos-access-denied-panel>

the blocked state that states which grant is missing without leaking what was hidden

May be placed on a record page, an app’s home page, a page inside an app and a step of a guided flow.

Name Attribute Description Type Default Required
heading heading, markup only The line at the top. Left unset it reads “You don’t have access”, which is the right sentence often enough to be the default. text You don’t have access Optional
grant grant, markup only The permission the caller would need, drawn as a mono line reading “Requires ”. Name the grant, never the content that was withheld. Unset, the line is not drawn at all. text none declared Optional

This piece raises no events.

Name What goes in it
the default slot The explanation — why this is blocked, in a sentence.
action The one way forward: a request-access control, or a way back.
Design token What it controls
--caos-color-surface The panel fill.
--caos-color-border The 1px panel edge and the edge of the grant line.
--caos-radius-lg The panel corner radius.
--caos-color-info-bg The disc behind the lock glyph.
--caos-color-info The lock glyph itself.
--caos-radius-pill The round lock disc.
--caos-color-text The heading, and the grant name.
--caos-color-text-muted The explanation, and the word “Requires”.
--caos-color-surface-2 The fill behind the grant line.
--caos-radius-sm The corners of the grant line.
--caos-font-display The typeface the heading is set in.
--caos-font The typeface of the word “Requires”.
--caos-font-mono The typeface the grant name is set in, so it can be quoted exactly.
--caos-space-7 The vertical padding of the panel.
--caos-space-5 The horizontal padding of the panel.
--caos-space-3 The gap between the disc, heading, explanation and grant line.
--caos-space-2 The gap between controls in the action slot.
--caos-space-1 The vertical padding of the grant line.

Nothing else drives this piece by calling it.

Nothing on this piece can be read back.

Part Which piece of it When it is there
::part(panel) the bordered panel the whole refusal sits in Always
::part(icon) the lock glyph Always
::part(title) the heading line Always
::part(message) the explanatory text Always
::part(grant) the named permission a person would have to be given Always
::part(actions) the row holding whatever action was slotted in Always

A surface that loaded correctly and is being withheld: the caller is missing a grant. Reach for it whenever the honest answer is “you are not allowed”, so a person stops looking for the button they think they have lost.

  • ErrorPanel — The surface FAILED rather than being refused. A failure has a code and a correlation id and may work on a retry; a refusal has neither and will not change until somebody is granted something. Drawing a refusal as an error sends a person to support for a ticket nobody can close, and drawing a failure as a refusal sends them to their administrator instead of the on-call engineer.
  • EmptyState — The caller IS allowed and there is genuinely nothing here yet. These two are the pair most often confused, and confusing them is the more expensive mistake in this direction: a person shown an empty list when they were actually blocked concludes the data is missing and goes looking for who deleted it.
  • ValidationSummary — Nothing is being withheld — a save was rejected because of what was typed. That is a list of things the same person can fix now, not a grant somebody else has to give them.
  • NoticeBanner — Most of the surface IS granted and only part of it is restricted. The banner sits above the content and says what is limited; this panel is for when there is no content to sit above.

The panel is a labelled region — role="region" pointed at the heading by id — so it is reachable as a landmark and announces what it is, and the lock disc is hidden from assistive tech because the heading already carries the meaning. Deliberately NOT role="alert": a refusal is a state the page is in, not something that just happened, and interrupting a screen reader for it would be wrong. What is left to the author is the copy — the explanation and the grant name are the whole accessible content, so a panel with a heading and nothing else announces almost nothing — and making sure whatever goes in the action slot is a real focusable control.

The blocked state as it is normally drawn: the default heading, a sentence of explanation, and the permission named in mono so it can be quoted to an administrator verbatim.

{
"id": "example",
"section": "The grant it needs",
"columns": 1,
"items": [
{
"id": "access_denied_panel_1",
"type": "component",
"key": "access_denied_panel",
"inputs": {
"grant": "Orders · View amounts"
},
"children": [
"Amounts on this order are restricted to the sales management team. Ask your administrator to grant the permission below."
]
}
]
}
<caos-access-denied-panel grant="Orders · View amounts">Amounts on this order are restricted to the sales management team. Ask your administrator to grant the permission below.</caos-access-denied-panel>

The same refusal with the one action that resolves it, so the person is not left to work out who to ask — and with a heading written for the specific surface rather than the default.

{
"id": "example",
"section": "With a way forward",
"columns": 1,
"items": [
{
"id": "access_denied_panel_1",
"type": "component",
"key": "access_denied_panel",
"inputs": {
"heading": "Amounts are restricted",
"grant": "Orders · View amounts"
},
"children": [
"Your role can open this order and read its line items, but not the amounts on them.",
{
"id": "button_2",
"type": "component",
"key": "button",
"inputs": {
"variant": "primary"
},
"slot": "action",
"children": [
"Request access"
]
},
{
"id": "button_3",
"type": "component",
"key": "button",
"inputs": {
"variant": "ghost"
},
"slot": "action",
"children": [
"Back to the order"
]
}
]
}
]
}
<caos-access-denied-panel heading="Amounts are restricted" grant="Orders · View amounts">
Your role can open this order and read its line items, but not the amounts on them.
<caos-button slot="action" variant="primary">Request access</caos-button>
<caos-button slot="action" variant="ghost">Back to the order</caos-button>
</caos-access-denied-panel>