{{-- A badge: M3's count or dot on an icon, or a short status label. M3's two badges (BadgeTokens, androidx Compose Material 3, Apache-2.0): - `` — the small badge, a 6px dot: something new, no number. - `` — the large badge, 16px tall, label-small: a count. `max` caps what is shown ("99+"). Both are `error` by default, as M3 draws them, and an `outline` status label draws its edge in `outline`, not `outline-variant`: M3 asks a badge for 3:1 against what is behind it, and `outline-variant` is the decorative role dividers use. `floating` pins one to the top-end corner of a `position: relative` parent, on M3's anchor geometry — the dot flush in the corner, the count 2px above it — an icon or an icon button: The status label is not an M3 badge but every app needs one: `tonal` draws the value in the colour's container ("Expired" in error-container), `solid` in the colour itself (a label that has to stand out, "Built in" in primary), `outline` in a neutral edge. `color` (alias `tone`): `error` (the default), `primary`, `secondary`, `tertiary`, `success`, `warning`, `info`, and two without a hue of their own: - `neutral` — neutral ink on every variant: on-surface-variant with surface text as a dot or count (the ink an outline badge already writes in), surface-container-high with on-surface-variant text when `tonal`, the outline edge when `outline`. - `plain` — no background, text or border colour at all, only shape, size and type, so the caller's CSS paints it. The value is `value`, or the slot, which renders as HTML — an icon beside the word: ` Pro`. With neither it is a dot. A count or dot says nothing to a screen reader on its own: give the icon's control a label that includes it ("Notifications, 4 new"), or pass `label` here. Drawn by resources/css/components/badge.css from `data-md-dot` (the small badge), `data-md-variant` (a status label: `solid`, `tonal` or `outline`, the first of them that is set, in that order), `data-md-color` and `data-md-floating`. --}} @props([ 'value' => null, 'max' => null, 'color' => null, 'tone' => null, 'tonal' => false, 'solid' => false, 'outline' => false, 'floating' => false, 'label' => null, ]) @php $color = in_array($color ?? $tone, ['primary', 'secondary', 'tertiary', 'error', 'success', 'warning', 'info', 'neutral', 'plain'], true) ? ($color ?? $tone) : 'error'; // hasActualContent(): a slot holding only a comment, or an empty @foreach, is still a dot. $markup = $value === null && $slot->hasActualContent(); $text = $value ?? ($markup ? trim((string) $slot) : null); $dot = blank($text); $status = ($tonal || $solid || $outline) && ! $dot; if (! $dot && $max !== null && is_numeric($text) && (int) $text > (int) $max) { $text = $max.'+'; $markup = false; } // `solid` wins over `tonal`, and either over `outline`, as the three have always resolved. $variant = match (true) { ! $status => null, (bool) $solid => 'solid', (bool) $tonal => 'tonal', default => 'outline', }; $attributes = $attributes->merge(array_filter([ 'data-md-badge' => true, 'data-md-dot' => $dot ? true : null, 'data-md-variant' => $variant, 'data-md-color' => $color, 'data-md-floating' => $floating ? true : null, 'aria-label' => $label, 'aria-hidden' => $label === null && ! $status ? 'true' : null, ], fn ($value): bool => $value !== null)); @endphp @unless ($dot)@if ($markup){{ $slot }}@else{{ $text }}@endif@endunless