Attention Banners
Why a screen needs a person, most severe first — one alert per cause with the action that owns the fix; renders nothing at all when the list is empty.
Preview
Stock claim failed
Payment sync delayed
Duplicated from #1042
<div class="w-full">
<x-ui.admin.attention-banners />
</div>
Installation
php artisan ui:add admin-attention-banners
Registry contract
php artisan ui:add admin-attention-banners
writes only the files below. The CLI validates each file hash before writing and asks before it replaces a local change, unless you pass --force.
-
resources/views/components/ui/admin/attention-banners.blade.php
Use with AI
A brief for your coding agent: install command, usage, props, guidance and the rules. Copy it, or open a prompt about this component in an assistant.
# Brok UI: Attention Banners (`admin-attention-banners`)
Why a screen needs a person, most severe first — one alert per cause with the action that owns the fix; renders nothing at all when the list is empty.
Brok UI is a Laravel Blade component registry. Installed components are plain Blade files the app owns.
## Install
```bash
php artisan ui:add admin-attention-banners
```
## Usage
```blade
<div class="w-full">
<x-ui.admin.attention-banners />
</div>
```
## Props
- `banners` (array|null, default `null`) — [['tone' => 'negative'|'attention'|'info'|'positive','title','body' => null,'actionLabel' => null,'actionHref' => null,'id' => null]]. `null` renders a sample set; `[]` renders nothing.
- `heading` (string|null, default `null`) — Accessible name of the region.
## Use when
- Use for important messages that should stay visible long enough to read and act on.
- A record or list has causes a person must act on, each with a screen that fixes it.
- A form was duplicated from another record and must say what was not copied.
## Avoid when
- Do not downgrade important guidance into transient messaging users can miss.
- A single inline field error — use `field`.
- Ambient system status — use `system-banner`.
## Anti-patterns
- Downgrading important outcomes to transient feedback
## Rules
- Use the `<brok:admin-attention-banners>` tag (or `<x-ui.admin-attention-banners>`) in Blade; do not rewrite the component.
- Prefer the documented props and variants over utility-class overrides; when a utility must win, use the `!` important modifier.
- Use semantic design tokens (`bg-primary`, `text-muted-foreground`), never raw colour utilities.
- Keep the `data-slot` attributes; they are the styling and test hooks.
## Links
- Docs: https://brokui.dev/docs/components/admin-attention-banners
- Registry JSON (files, props, contract): https://brokui.dev/r/open/admin-attention-banners.json
Working in Claude Code, Cursor or Codex? Give the agent the whole registry through the MCP server or the Brok UI skill.
API
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| banners | array | null | null | [['tone' => 'negative'|'attention'|'info'|'positive','title','body' => null,'actionLabel' => null,'actionHref' => null,'id' => null]]. `null` renders a sample set; `[]` renders nothing. |
| heading | string | null | null | Accessible name of the region. |
Slots
Default Blade slot only.
Data slots
Stable hooks for CSS overrides and browser tests.
Behavior
- Sorted here: negative, then attention, then info, then positive; stable within a tone.
- An empty array renders no wrapper, so callers never guard the include.
- Declares registry capability flags: a11y, responsive, rtl, darkMode, localized.
Guidance
Keep an important message visible near its context.
Use when
- Use for important messages that should stay visible long enough to read and act on.
- A record or list has causes a person must act on, each with a screen that fixes it.
- A form was duplicated from another record and must say what was not copied.
Avoid when
- Do not downgrade important guidance into transient messaging users can miss.
- A single inline field error — use `field`.
- Ambient system status — use `system-banner`.
Use instead
- Toast for low-priority confirmation
Anti-patterns
- Downgrading important outcomes to transient feedback
- Anatomy
- Theming hooks
Accessibility
- Keyboard
- No component-owned keyboard interaction; native element behavior applies.
- Focus
none
- Each banner is an `alert` primitive; the action is a real link, never a colour cue alone.
- Semantic HTML and a stable
data-slotattribute for styling and scripting hooks. - Focus-visible rings use the
ringtoken, so keyboard focus is always visible. - Disabled and invalid states are conveyed to assistive tech, not by color alone.
- Targets WCAG 2.2 AA; verify contrast in light, dark, admin and customer surfaces in the preview.
- Labels go through
__()and layout uses logical properties (ms-*,text-start), so it mirrors underdir="rtl"— flip the preview to RTL to confirm. - Dark mode uses the same semantic tokens under the
darkclass; high contrast follows forced-color system tokens.
Livewire
Livewire can update this component through forwarded wire:* attributes.
Source
The exact, editable file ui:add writes
into your app. Previews render this same code; there are no preview-only components.
{{--
Attention Banners — why this screen needs a person, most severe first.
One banner per cause. Each names what is wrong in the operator's words
and offers the screen that owns the fix, so a banner is a route to the
work rather than a report about it. A banner without a cause is
decoration: the component renders nothing at all for a healthy record, and
the caller never has to guard the include.
Ordering is decided here, not by the caller: negative before attention
before info, stable within a tone. A read model can emit its causes in
whatever order it finds them and the screen still leads with the one
that costs the most.
Role gating is the caller's job. A banner whose conclusion is about
margin is cost data even when it states no figure, so the page filters
the list before passing it; this component renders what it is given and asks
no questions about who is reading.
Generalised from the Noord-C admin's `record/banners.blade.php` (five
module copies) and the orders list's `operational-alerts.blade.php`.
--}}
@props([
// null (absent) renders the sample set so the docs preview shows the
// component; an explicit [] renders nothing, which is the promise above.
'banners' => null,
'heading' => null,
])
@php
// Shapes documented in item.json knowledge.props:
// banners: [['tone' => 'negative'|'attention'|'info'|'positive', 'title', 'body' => null, 'actionLabel' => null, 'actionHref' => null, 'id' => null]]
$banners ??= [
[
'tone' => 'attention',
'title' => __('Payment sync delayed'),
'body' => __('The payment provider has not confirmed a status for 6 orders in the last 15 minutes. Figures for those orders may be out of date.'),
'actionLabel' => __('View affected orders'),
'actionHref' => '#',
],
[
'tone' => 'negative',
'title' => __('Stock claim failed'),
'body' => __('Two lines on this order could not reserve stock. Fulfilment will stall until the warehouse resolves them.'),
'actionLabel' => __('Open warehouse queue'),
'actionHref' => '#',
],
[
'tone' => 'info',
'title' => __('Duplicated from #1042'),
'body' => __('Everything was copied except the customer credit, which belongs to the original order.'),
'actionLabel' => __('Open #1042'),
'actionHref' => '#',
],
];
$severity = ['negative' => 0, 'attention' => 1, 'info' => 2, 'positive' => 3];
$variants = ['negative' => 'destructive', 'attention' => 'warning', 'info' => 'info', 'positive' => 'success'];
$banners = collect((array) $banners)
->filter(fn ($entry) => is_array($entry))
->sortBy(fn (array $banner): int => $severity[$banner['tone'] ?? 'info'] ?? 2, SORT_NUMERIC, false)
->values();
$heading ??= __('Needs attention');
@endphp
@if ($banners->isNotEmpty())
<section
data-slot="attention-banners"
data-surface="admin"
aria-label="{{ $heading }}"
{{ $attributes->merge(['class' => 'flex flex-col gap-2']) }}
>
@foreach ($banners as $banner)
@php $tone = $banner['tone'] ?? 'info'; @endphp
<x-ui.alert
:variant="$variants[$tone] ?? 'info'"
:data-tone="$tone"
:id="$banner['id'] ?? null"
class="flex flex-wrap items-start justify-between gap-x-6 gap-y-2"
>
<div class="min-w-0 flex-1 basis-64">
<x-ui.alert.title>{{ $banner['title'] ?? '' }}</x-ui.alert.title>
@if (! empty($banner['body']))
<x-ui.alert.description class="max-w-prose text-pretty">{{ $banner['body'] }}</x-ui.alert.description>
@endif
</div>
@if (! empty($banner['actionLabel']))
<x-ui.button variant="outline" size="sm" :href="$banner['actionHref'] ?? '#'" class="shrink-0">
{{ $banner['actionLabel'] }}
</x-ui.button>
@endif
</x-ui.alert>
@endforeach
</section>
@endif
Ownership & lifecycle
Owner, release state, review evidence and adoption for this item.
- Owner
- Platform UI (@JoshJML)
- Current version
-
1.0.2 - Status
- Stable
- License
-
open - Accessibility reviewed
- No review date recorded
- Last breaking change
- No date recorded
- Deprecation
- Not deprecated
- Contract
-
v6 - Foundation
-
≥ 1.0.0