Views

Sizing & Mobile

The scheduler fills the height it is given and scrolls inside it, and it measures its own width to decide when to switch to a layout made for a phone. Neither needs a media query from you.

Three Layouts

Three arrangements cover every page, and the common one needs no number at all. The frame below stands in for a page: a header, and a scheduler that takes the rest.

Your app a header of its own height

Oct 19 – 25, 2026

GMT+7
Mon 19
Tue 20
Wed 21
Thu 22
Fri 23
Sat 24
Sun 25
All day
Lunch break
<!-- The rest of the page: a flex column, and the scheduler takes what is left.
     min-h-0 lets it shrink below its content so it scrolls inside instead of
     stretching the page. -->
<div class="flex h-dvh flex-col">
  <header class="h-14 shrink-0 border-b">App header</header>
  <Scheduler bind:events class="min-h-0 flex-1" />
</div>

Which one

LayoutHow
The rest of the pageFlex column; the scheduler gets min-h-0 flex-1
Grows with its contentNothing: no height on it or any parent
A fixed sizeheight={640}, height="70dvh", or a parent with a height

Growing With Content

Without a bounded height the scheduler is as tall as its content. For the month and the agenda that is a few hundred pixels; for the week view it is the whole day, about 1150px plus headers, because the grid has no reason to hide any hour. Fine in a long article, rarely what a page wants for its main calendar.

<!-- No height anywhere: each view grows to its content. The week view is the
     full day at 48 slots of 24px, about 1150px plus headers. -->
<article>
  <h1>Season programme</h1>
  <p>...</p>
  <Scheduler bind:events view="month" />
  <p>...</p>
</article>

A Fixed Size

height takes pixels or any CSS length. Sizing the parent works the same way, since the scheduler fills it. Every demo on these pages uses one or the other.

October 2026

Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
28
29
30
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1
<!-- A card, a tile, a modal: pixels or any CSS length -->
<Scheduler bind:events height={640} />
<Scheduler bind:events height="70dvh" />

<!-- Or size the parent and let the scheduler fill it -->
<div class="h-[32rem]">
  <Scheduler bind:events />
</div>

The Compact Layout

Below compactBreakpoint, 640px of the scheduler's own width, the calendar rearranges itself for touch. The number is the scheduler's width rather than the viewport's, so a calendar in a narrow panel of a wide page gets the same treatment as one on a phone. Drag the slider to watch it switch.

compact

Oct 19 – 25, 2026

GMT+7
Mon 19
Tue 20
Wed 21
Thu 22
Fri 23
Sat 24
Sun 25
All day
Lunch break
<!-- Below 640px of scheduler width the compact layout switches on by itself.
     The number is the scheduler's own width, not the viewport's. -->
<Scheduler bind:events />

<!-- Show the whole week on phones anyway -->
<Scheduler bind:events compactDays={null} />

<!-- Switch earlier or later -->
<Scheduler bind:events compactBreakpoint={720} />

<!-- Never switch -->
<Scheduler bind:events compactBreakpoint={0} />

What changes

PartCompact
ToolbarToday as an icon, arrows, short title, a view menu; every control at least 36px
Week viewcompactDays days from date, 3 by default; the arrows step by that many
Event detailsA slide-over inside the calendar
Month cellsDay numbers only
Everything elseUnchanged: the same gestures, the same keys

Four Thresholds

Four widths decide the layout, and only two of them are props. The month grid has a density threshold of its own, measured on the grid rather than the scheduler, so a wide scheduler with a docked sidebar can still get the dense month. The popover rule is the one exception that reads the viewport: on a narrow screen there is no room beside an event, so the card opens below it and never pushes the page sideways.

Thresholds

WhatMeasured against
Compact layoutcompactBreakpoint, 640px of the scheduler
Month cell density768px of the month grid
SidebarsidebarBreakpoint, 1024px of the scheduler
Popover side768px of the viewport

Touch

A tap does what a click does. A drag starts after a press of 300 milliseconds, so a finger that swipes across the grid scrolls the page as it should, and one that rests on an event picks it up. While a drag is in progress the context menu is suppressed and the grid scrolls itself when the finger nears an edge. Escape from an attached keyboard cancels.

The Sidebar Below Its Breakpoint

The sidebar docks beside the view while the scheduler is at least sidebarBreakpoint wide, 1024px by default, and becomes a slide-over behind the toolbar's menu button below that. Picking a date in the slide-over closes it. This docs column is 884px wide, so the demos here lower the breakpoint to keep the sidebar docked; drag the slider under it to see the switch. See Sidebar.

Oct 19 – 25, 2026

GMT+7
Mon 19
Tue 20
Wed 21
Thu 22
Fri 23
Sat 24
Sun 25
All day
Lunch break

A Toolbar of Your Own on Phones

A toolbar snippet receives compact and can rearrange itself on the same switch. A registered view can offer a shortTitle for the narrow bar, the way the built-in week view shortens Sep 14 – 20, 2026 to Sep 14 – 16.

<!-- A toolbar snippet receives the same switch, so it can follow the built-in one -->
<Scheduler bind:events toolbar={header} />

{#snippet header({ title, step, today, view, views, setView, compact })}
  <div class="flex items-center gap-2 px-3 py-2">
    <Button icon="lucide:chevron-left" size={compact ? 'md' : 'sm'} onclick={() => step(-1)} />
    <Button icon="lucide:chevron-right" size={compact ? 'md' : 'sm'} onclick={() => step(1)} />
    <h2 class={compact ? 'text-sm' : 'text-lg'}>{title}</h2>
    {#if compact}
      <Select items={views.map(({ name, label }) => ({ value: name, label }))} value={view} onValueChange={setView} />
    {:else}
      <ToggleGroup items={views.map(({ name, label }) => ({ value: name, label }))} value={view} onValueChange={setView} />
    {/if}
  </div>
{/snippet}

<!-- A view of your own can offer a shorter title for the compact toolbar -->
const workWeek: ViewDefinition = {
  name: 'workweek',
  title: (anchor, range, ctx) => `Week of ${format(range.start)}`,
  shortTitle: (anchor, range) => format(range.start),
  /* ... */
};

Props

The props that decide how the scheduler fits its box.

PropTypeDefault
heightnumber | string-
compactBreakpointnumber640
compactDaysnumber | null3
sidebarBreakpointnumber1024
detail'popover' | 'slideover' | false'popover'
toolbarboolean | Snippet<[ToolbarSnippetProps]>true

Snippets

What the snippets on this page receive. Every type is exported from @sv5ui/scheduler.

TypeFields
ToolbarSnippetPropstitle, date, range, view, views, scheduler, step, today, navigate, setView, toggleSidebar, sidebarOpen, compact