Calendar Event Card
A single calendar event card with a tone-coloured accent bar, time chip, location, attendee avatars and an optional join action.
Preview
Design critique
Wed, 17 Jun
Studio B
<div class="mx-auto w-full max-w-sm space-y-3">
<x-ui.calendar-event-card
title="Product sync"
date="Tue, 16 Jun"
start="10:00"
end="10:45"
location="Google Meet"
tone="primary"
href="#"
:attendees="[['name' => 'Ada'], ['name' => 'Lin'], ['name' => 'Sam'], ['name' => 'Rae'], ['name' => 'Joel']]"
/>
<x-ui.calendar-event-card
title="Design critique"
date="Wed, 17 Jun"
start="14:00"
end="15:00"
location="Studio B"
tone="warning"
:attendees="[['name' => 'Mara'], ['name' => 'Ken']]"
/>
</div>
Installation
php artisan ui:add calendar-event-card
Registry contract
php artisan ui:add calendar-event-card
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/calendar-event-card.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: Calendar Event Card (`calendar-event-card`)
A single calendar event card with a tone-coloured accent bar, time chip, location, attendee avatars and an optional join action.
Brok UI is a Laravel Blade component registry. Installed components are plain Blade files the app owns.
## Install
```bash
php artisan ui:add calendar-event-card
```
## Usage
```blade
<div class="mx-auto w-full max-w-sm space-y-3">
<x-ui.calendar-event-card
title="Product sync"
date="Tue, 16 Jun"
start="10:00"
end="10:45"
location="Google Meet"
tone="primary"
href="#"
:attendees="[['name' => 'Ada'], ['name' => 'Lin'], ['name' => 'Sam'], ['name' => 'Rae'], ['name' => 'Joel']]"
/>
<x-ui.calendar-event-card
title="Design critique"
date="Wed, 17 Jun"
start="14:00"
end="15:00"
location="Studio B"
tone="warning"
:attendees="[['name' => 'Mara'], ['name' => 'Ken']]"
/>
</div>
```
## Props
- `title` (string, default `Product sync`)
- `date` (string, default `Tue, 16 Jun`)
- `start` (string, default `10:00`)
- `end` (string, default `10:45`)
- `location` (string, default `Google Meet`)
- `tone` (string, default `primary`)
- `attendees` (array, default `[]`)
- `href` (mixed|null, default `null`)
- `joinLabel` (string, default `Join`)
## Use when
- Use when self-contained content objects benefit from a recognizable card-like presentation with local actions or metadata.
- Showing one upcoming event with its time, place and attendees in an agenda or dashboard.
- Offering a join link beside the event details.
## Avoid when
- Do not use card widgets where a simpler list, table, or plain text block would make comparison faster.
- Rendering a whole calendar; calendar and mini-calendar lay out dates.
- Listing many events compactly, where a table or list-panel reads faster.
## Anti-patterns
- Using cards for dense cross-item comparison
## Rules
- Use the `<brok:calendar-event-card>` tag (or `<x-ui.calendar-event-card>`) 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/calendar-event-card
- Registry JSON (files, props, contract): https://brokui.dev/r/open/calendar-event-card.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 |
|---|---|---|---|
| title | string | Product sync | |
| date | string | Tue, 16 Jun | |
| start | string | 10:00 | |
| end | string | 10:45 | |
| location | string | Google Meet | |
| tone | string | primary | |
| attendees | array | [] | |
| href | mixed | null | null | |
| joinLabel | string | Join |
Slots
default—
Data slots
Stable hooks for CSS overrides and browser tests.
Behavior
- tone colours the accent bar and time chip; date, start and end render as given, with no parsing.
- attendees renders as an avatar-group; href adds the join action labelled by joinLabel.
- Declares registry capability flags: a11y, responsive, rtl, darkMode, localized.
Guidance
Present a self-contained content object with local context.
Use when
- Use when self-contained content objects benefit from a recognizable card-like presentation with local actions or metadata.
- Showing one upcoming event with its time, place and attendees in an agenda or dashboard.
- Offering a join link beside the event details.
Avoid when
- Do not use card widgets where a simpler list, table, or plain text block would make comparison faster.
- Rendering a whole calendar; calendar and mini-calendar lay out dates.
- Listing many events compactly, where a table or list-panel reads faster.
Use instead
- List for compact scanning
- Table for exact comparison
Anti-patterns
- Using cards for dense cross-item comparison
- Anatomy
- Theming hooks
Accessibility
- Keyboard
- No component-owned keyboard interaction; native element behavior applies.
- Focus
none
- The attendee group is labelled, so avatars are announced as attendees rather than as unnamed images.
- The tone accent is decorative; the time and location are text.
- 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.
@props([
'title' => 'Product sync',
'date' => 'Tue, 16 Jun',
'start' => '10:00',
'end' => '10:45',
'location' => 'Google Meet',
'tone' => 'primary',
'attendees' => [],
'href' => null,
'joinLabel' => 'Join',
])
@php
$attendees = is_array($attendees) ? $attendees : [];
// Tone → accent bar + soft time chip. Full static class strings so the JIT
// sees every utility; falls back to primary for unknown tones.
$tones = [
'primary' => ['bar' => 'bg-primary', 'chip' => 'bg-primary/10 text-primary'],
'success' => ['bar' => 'bg-success', 'chip' => 'bg-success/10 text-success'],
'warning' => ['bar' => 'bg-warning', 'chip' => 'bg-warning/10 text-warning'],
'info' => ['bar' => 'bg-info', 'chip' => 'bg-info/10 text-info'],
'destructive' => ['bar' => 'bg-destructive', 'chip' => 'bg-destructive/10 text-destructive'],
];
$t = $tones[$tone] ?? $tones['primary'];
$time = trim(($start ?? '').($end ? ' – '.$end : ''));
@endphp
{{--
Calendar event card — a single scheduled event. A tone-coloured accent bar
runs down the start edge (logical inset, so it mirrors under RTL); the title,
time chip, date and location stack alongside, with an optional attendee
avatar stack and a join action. All text is auto-escaped.
--}}
<div
data-slot="calendar-event-card"
data-tone="{{ $tone }}"
{{ $attributes->merge(['class' => 'relative overflow-hidden rounded-lg border border-border bg-card p-6 ps-6 text-foreground shadow-sm']) }}
>
<span class="absolute inset-y-0 start-0 w-2 {{ $t['bar'] }}" aria-hidden="true"></span>
<div class="flex items-start justify-between gap-3">
<div class="min-w-0">
<h3 class="truncate text-sm font-semibold">{{ $title }}</h3>
@if ($date)
<p class="mt-0.5 text-xs text-muted-foreground">{{ $date }}</p>
@endif
</div>
@if ($time)
<span class="inline-flex shrink-0 items-center rounded-md px-2 py-0.5 text-xs font-medium {{ $t['chip'] }}">{{ $time }}</span>
@endif
</div>
@if ($location)
<p class="mt-3 inline-flex items-center gap-2 text-xs text-muted-foreground">
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" class="size-4 shrink-0">
<path d="M20 10c0 6-8 12-8 12s-8-6-8-12a8 8 0 0 1 16 0Z" />
<circle cx="12" cy="10" r="3" />
</svg>
<span class="truncate">{{ $location }}</span>
</p>
@endif
@if (count($attendees) || $href)
<div class="mt-4 flex items-center justify-between gap-3">
@if (count($attendees))
<div class="flex -space-x-2" aria-label="{{ __('Attendees') }}">
@foreach (array_slice($attendees, 0, 4) as $person)
<x-ui.avatar
size="sm"
:src="$person['src'] ?? null"
:alt="$person['name'] ?? ''"
:fallback="$person['name'] ?? null"
class="ring-2 ring-card"
/>
@endforeach
@if (count($attendees) > 4)
<span class="z-10 inline-flex size-8 items-center justify-center rounded-full bg-muted text-xs font-medium text-muted-foreground ring-2 ring-card">+{{ count($attendees) - 4 }}</span>
@endif
</div>
@else
<span></span>
@endif
@if ($href)
<x-ui.button :href="$href" size="sm" variant="outline" class="shrink-0">{{ __($joinLabel) }}</x-ui.button>
@endif
</div>
@endif
</div>
Ownership & lifecycle
Owner, release state, review evidence and adoption for this item.
- Owner
- Platform UI (@JoshJML)
- Current version
-
1.0.4 - 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