Business Hours & Holidays
Shade the time outside working hours, name the days off, and draw the shape of a day behind the
events. All three are display only: they tell the reader what the day is, and leave what can be
done with it to onMutate.
Business Hours
businessHours takes an opening time, a closing time and the days they apply to; all three are required. In the
time grid the hours outside are shaded, and a day not in days is shaded in full. It also decides where the grid first scrolls to, and what isBusinessHours means in a cell snippet. It does not block anything: an event can still be created, dropped or
resized into the shaded time.
<!-- Time outside 9:00 to 18:00, and every hour of Saturday and Sunday, is shaded.
Nothing is blocked: events can still be created or dropped there. -->
<Scheduler
bind:events
businessHours={{ start: '09:00', end: '18:00', days: [1, 2, 3, 4, 5] }}
/>BusinessHours
| Field | Description |
|---|---|
start | Opening time, 24 hour 'HH:mm' |
end | Closing time, 24 hour 'HH:mm', exclusive |
days | Days the hours apply to; 0 is Sunday. Other days are shaded in full |
Holidays
holidays lists whole days. Each is tinted in every grid view and named wherever there is room, and a holiday
is always treated as outside business hours, whatever days says.
<!-- A holiday tints the whole day in every view and is named in the headers.
It always counts as outside business hours. Dates are read in timeZone. -->
<Scheduler
bind:events
holidays={[
{ date: '2026-09-15', title: 'Founders day' },
{ date: '2026-09-25' } // tinted, unnamed
]}
/>Holiday
| Field | Description |
|---|---|
date | 'YYYY-MM-DD', read in the scheduler's timeZone |
title | Optional. Shown in headers, cells and agenda headings, and read to screen readers |
Where a holiday shows
| View | What appears |
|---|---|
Week | Column tinted, name under the day number in the header |
Day | Title area tinted, name as a badge beside the date |
Month | Cell tinted, name beside the day number when the grid is wide enough |
Year | Day number in the tertiary colour |
Agenda | Name beside the day heading |
Screen readers | Part of the accessible name of month cells and year days |
Background Events
An event with background: true is a translucent block drawn behind the others with its title in small type. It takes no column
in the overlap layout, cannot be moved, resized or deleted, and opens no details. It is for the
shape of the day rather than an appointment: a maintenance window, lunch, an afternoon kept free
of meetings.
// A background event is a translucent block behind the others. It takes no
// column, cannot be moved or deleted, and opens no details. Use it for the
// shape of the day: a maintenance window, lunch, a block kept free of meetings.
{ id: 'lunch', title: 'Lunch', start: '2026-09-16T12:00', end: '2026-09-16T13:00', background: true }
{ id: 'focus', title: 'No meetings', start: '2026-09-18T13:00', end: '2026-09-18T17:00', background: true }A Working Week
Pick the opening hours and the working days, and switch the holidays and week numbers on and off. Tuesday is Founders day. The maintenance window on Wednesday and the free afternoon on Friday are background events.
Oct 19 – 25, 2026
<Scheduler
bind:events
businessHours={{ start: '09:00', end: '18:00', days: [1, 2, 3, 4, 5] }}
holidays={[{ date: '2026-09-15', title: 'Founders day' }]}
weekNumbers
/>Blocking Time
Shading is not a rule. If an event must not land outside the opening hours, decide that
where every change already passes: onMutate. Throwing rolls the change back with an animation and calls onError,
which is where a toast belongs. See Saving Changes. An interaction plugin can refuse earlier, during the drag, when that matters.
<!-- businessHours only shades. To refuse a change, say so from onMutate:
the event slides back and onError runs. -->
<Scheduler
bind:events
businessHours={hours}
onMutate={(mutation) => {
const event = mutation.after;
if (event && (event.start.hour < 9 || event.end.hour > 18)) {
throw new Error('Outside opening hours');
}
return api.save(mutation);
}}
onError={(mutation, error) => toast.error(String(error))}
/>Reading the Flags in a Cell
The cell snippet is drawn behind every month cell and time grid column and receives isWeekend, isHoliday, isBusinessHours, isToday, isAnchor and isOutside. isBusinessHours is a fact about the day, not the hour: true on a working day that is not a holiday. The hour by
hour shading is drawn by the grid itself. Stripes for the weekend, here in the month view; see Custom Rendering for the other snippets.
October 2026
<!-- The cell snippet is drawn behind each day. It gets the flags, so a stripe
for weekends or a tint for holidays needs no date maths. -->
<Scheduler bind:events {holidays} {businessHours} cell={backdrop} />
{#snippet backdrop({ isWeekend, isHoliday, isBusinessHours })}
{#if isHoliday}
<div class="absolute inset-0 bg-tertiary/5"></div>
{:else if isWeekend || !isBusinessHours}
<div class="absolute inset-0 bg-[repeating-linear-gradient(135deg,transparent_0_8px,var(--color-outline-variant)_8px_9px)] opacity-30"></div>
{/if}
{/snippet}Props
The props this page uses.
| Prop | Type | Default |
|---|---|---|
businessHours | BusinessHours | - |
holidays | Holiday[] | [] |
cell | Snippet<[CellSnippetProps]> | - |
onMutate | (m: Mutation<T>) => MutationResult<T> | Promise<...> | - |
Snippets
What the snippets on this page receive. Every type is exported from @sv5ui/scheduler.
| Type | Fields |
|---|---|
CellSnippetProps | date, view, isToday, isAnchor, isWeekend, isHoliday, isBusinessHours, isOutside |