A two-row tile showing a month and a day.
import { CalendarTile } from '@oztix/roadie-components/calendar-tile'
<CalendarTile month='NOV' day={27} />
Leads with the day of week instead of the month, for schedule lists where a heading already establishes the month.
<div className='flex flex-wrap gap-3'> <CalendarTile month='NOV' day={27} /> <CalendarTile weekday='FRI' day={27} /> </div>
Defaults to rounded-xl, matching Card. Override with any Tailwind radius class. Use rounded-none for a tile that bleeds to a card edge rather than floating inside it.
<div className='flex flex-wrap items-end gap-3'> <CalendarTile month='NOV' day={27} size='sm' /> <CalendarTile month='NOV' day={27} size='md' /> <CalendarTile month='NOV' day={27} size='lg' /> </div>
The tile abbreviates hard. It is paired with a line that carries the full date. Where the tile is the only date on a card, that line is required, not optional.
Strings come from formatGlyph in @oztix/roadie-core/datetime. It resolves them in the venue's timezone. Building them from new Date() at the call site renders the wrong day for anyone not standing at the venue. See Date and time.
import { formatGlyph } from '@oztix/roadie-core/datetime'const glyph = formatGlyph(event.startsAt, {timeZone: event.venue.timeZone})
The tile carries a month and a day and never a year. A three-row tile is cramped at 48px. The year is not what anyone scans a tile for. Whatever sits beside it owns the full date.
This is the one component with a CSS twin. A server-rendered template uses the
matching calendar-tile utility from @oztix/roadie-core/css, so both render
an identical tile.
<div class="calendar-tile emphasis-subtle intent-accent" aria-hidden="true"><span class="calendar-tile-top">NOV</span><span class="calendar-tile-day">27</span></div>
The strings still come from formatGlyph, or from a server-side formatter
following the same rules. The markup is the easy half. Resolving the day in the
venue's timezone is the half that goes wrong.
The rows are abbreviations, so they are never the accessible name. Without a label the tile is marked aria-hidden and announces nothing at all, which is right when a date line beside it already says the date.
Pass dateTime and the tile renders as a time element instead of a plain box. Do that only when the tile is the only date in its region. Where a date line sits beside it, that line owns the time element. Two of them publish the same instant twice, and a screen reader reads it twice.
Take month and day off the glyph rather than spreading it. It also carries
weekday, and the tile prefers a weekday over a month when both arrive, so a
spread renders FRI 27 where NOV 27 was meant.
Guard all three together. Every formatter returns string | null for a zone it
cannot resolve, and a guard on one does not narrow the others.
label with the full date when the tile is the only date in its region. It renders as role='img' with that accessible name.label when a neighbouring line already states the date. The tile is then marked aria-hidden, so the date is announced once rather than twice.Day of the month, e.g. `27`.
The full date, for assistive tech. The tile abbreviates hard, so without this a labelless tile is `aria-hidden` and announces nothing.
Machine-readable value from `formatMachine`. Setting it renders a `<time>` element instead of a plain box, so the rest of the props are typed against `time` rather than `div`. Set it only when the tile is the **only** date in its region. If a date line sits beside it, that line owns the `<time>`, and two of them would publish the same instant twice.
Short month, e.g. `NOV`. Ignored when `weekday` is set.
Short weekday, e.g. `FRI`. Replaces the month in the top row.