Calendar
A month grid for one date, a range or several, with min and max, blocked days, two-month view and full keyboard paging.
Rendering Calendar…
Installation
Copy the source into your project with the CLI. It lands under @/components/ui and is yours to edit.
npx mlola-ui add calendarUsage
import { Calendar } from "@/components/ui/calendar";
export default function Example() {
return <Calendar defaultValue="2026-09-18" defaultMonth="2026-09-01" today="2026-09-14" label="Delivery date" />;
}Styling comes from the engine stylesheet and the nearest theme. Set it once at the root:
<html data-theme="graphite" data-mode="light">HTML
The markup the example above renders, at rest. The interaction lives in the React component; from another framework, set the attributes below as the state changes.
<div class="ml-calendar" role="group" aria-label="Delivery date" data-months="1">
<div class="ml-calendar-months">
<div class="ml-calendar-month">
<div class="ml-calendar-caption">
<button class="ml-calendar-nav" type="button" aria-label="Previous month"><svg class="mlola-glyph" width="1em" height="1em" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="var(--mlola-glyph-stroke, var(--ml-icon-stroke, 1.75))" stroke-linecap="round" stroke-linejoin="round" focusable="false" aria-hidden="true" data-optical-size="default" style="color:var(--mlola-glyph-color, currentColor);scale:var(--mlola-glyph-optical-scale, 1);transform-origin:center"><path d="m15 18-6-6 6-6" pathLength="1"></path></svg></button>
<p class="ml-calendar-title" aria-live="polite">September 2026</p>
<button class="ml-calendar-nav" type="button" aria-label="Next month"><svg class="mlola-glyph" width="1em" height="1em" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="var(--mlola-glyph-stroke, var(--ml-icon-stroke, 1.75))" stroke-linecap="round" stroke-linejoin="round" focusable="false" aria-hidden="true" data-optical-size="default" style="color:var(--mlola-glyph-color, currentColor);scale:var(--mlola-glyph-optical-scale, 1);transform-origin:center"><path d="m9 18 6-6-6-6" pathLength="1"></path></svg></button>
</div>
<div class="ml-calendar-grid" role="grid" aria-label="September 2026">
<div class="ml-calendar-row" role="row">
<span class="ml-calendar-weekday" role="columnheader" aria-label="Monday">Mo</span>
<span class="ml-calendar-weekday" role="columnheader" aria-label="Tuesday">Tu</span>
<span class="ml-calendar-weekday" role="columnheader" aria-label="Wednesday">We</span>
<span class="ml-calendar-weekday" role="columnheader" aria-label="Thursday">Th</span>
<span class="ml-calendar-weekday" role="columnheader" aria-label="Friday">Fr</span>
<span class="ml-calendar-weekday" role="columnheader" aria-label="Saturday">Sa</span>
<span class="ml-calendar-weekday" role="columnheader" aria-label="Sunday">Su</span>
</div>
<div class="ml-calendar-row" role="row">
<span class="ml-calendar-cell" role="gridcell"><button class="ml-calendar-day" type="button" tabindex="-1" aria-label="Monday, August 31, 2026" data-outside="true">31</button></span>
<span class="ml-calendar-cell" role="gridcell"><button class="ml-calendar-day" type="button" tabindex="-1" aria-label="Tuesday, September 1, 2026">1</button></span>
<span class="ml-calendar-cell" role="gridcell"><button class="ml-calendar-day" type="button" tabindex="-1" aria-label="Wednesday, September 2, 2026">2</button></span>
<span class="ml-calendar-cell" role="gridcell"><button class="ml-calendar-day" type="button" tabindex="-1" aria-label="Thursday, September 3, 2026">3</button></span>
<span class="ml-calendar-cell" role="gridcell"><button class="ml-calendar-day" type="button" tabindex="-1" aria-label="Friday, September 4, 2026">4</button></span>
<span class="ml-calendar-cell" role="gridcell"><button class="ml-calendar-day" type="button" tabindex="-1" aria-label="Saturday, September 5, 2026">5</button></span>
<span class="ml-calendar-cell" role="gridcell"><button class="ml-calendar-day" type="button" tabindex="-1" aria-label="Sunday, September 6, 2026">6</button></span>
</div>
<div class="ml-calendar-row" role="row">
<span class="ml-calendar-cell" role="gridcell"><button class="ml-calendar-day" type="button" tabindex="-1" aria-label="Monday, September 7, 2026">7</button></span>
<span class="ml-calendar-cell" role="gridcell"><button class="ml-calendar-day" type="button" tabindex="-1" aria-label="Tuesday, September 8, 2026">8</button></span>
<span class="ml-calendar-cell" role="gridcell"><button class="ml-calendar-day" type="button" tabindex="-1" aria-label="Wednesday, September 9, 2026">9</button></span>
<span class="ml-calendar-cell" role="gridcell"><button class="ml-calendar-day" type="button" tabindex="-1" aria-label="Thursday, September 10, 2026">10</button></span>
<span class="ml-calendar-cell" role="gridcell"><button class="ml-calendar-day" type="button" tabindex="-1" aria-label="Friday, September 11, 2026">11</button></span>
<span class="ml-calendar-cell" role="gridcell"><button class="ml-calendar-day" type="button" tabindex="-1" aria-label="Saturday, September 12, 2026">12</button></span>
<span class="ml-calendar-cell" role="gridcell"><button class="ml-calendar-day" type="button" tabindex="-1" aria-label="Sunday, September 13, 2026">13</button></span>
</div>
<div class="ml-calendar-row" role="row">
<span class="ml-calendar-cell" role="gridcell"><button class="ml-calendar-day" type="button" tabindex="-1" aria-label="Monday, September 14, 2026" aria-current="date" data-today="true">14</button></span>
<span class="ml-calendar-cell" role="gridcell"><button class="ml-calendar-day" type="button" tabindex="-1" aria-label="Tuesday, September 15, 2026">15</button></span>
<span class="ml-calendar-cell" role="gridcell"><button class="ml-calendar-day" type="button" tabindex="-1" aria-label="Wednesday, September 16, 2026">16</button></span>
<span class="ml-calendar-cell" role="gridcell"><button class="ml-calendar-day" type="button" tabindex="-1" aria-label="Thursday, September 17, 2026">17</button></span>
<span class="ml-calendar-cell" role="gridcell" aria-selected="true"><button class="ml-calendar-day" type="button" tabindex="0" aria-label="Friday, September 18, 2026" data-selected="true">18</button></span>
<span class="ml-calendar-cell" role="gridcell"><button class="ml-calendar-day" type="button" tabindex="-1" aria-label="Saturday, September 19, 2026">19</button></span>
<span class="ml-calendar-cell" role="gridcell"><button class="ml-calendar-day" type="button" tabindex="-1" aria-label="Sunday, September 20, 2026">20</button></span>
</div>
<div class="ml-calendar-row" role="row">
<span class="ml-calendar-cell" role="gridcell"><button class="ml-calendar-day" type="button" tabindex="-1" aria-label="Monday, September 21, 2026">21</button></span>
<span class="ml-calendar-cell" role="gridcell"><button class="ml-calendar-day" type="button" tabindex="-1" aria-label="Tuesday, September 22, 2026">22</button></span>
<span class="ml-calendar-cell" role="gridcell"><button class="ml-calendar-day" type="button" tabindex="-1" aria-label="Wednesday, September 23, 2026">23</button></span>
<span class="ml-calendar-cell" role="gridcell"><button class="ml-calendar-day" type="button" tabindex="-1" aria-label="Thursday, September 24, 2026">24</button></span>
<span class="ml-calendar-cell" role="gridcell"><button class="ml-calendar-day" type="button" tabindex="-1" aria-label="Friday, September 25, 2026">25</button></span>
<span class="ml-calendar-cell" role="gridcell"><button class="ml-calendar-day" type="button" tabindex="-1" aria-label="Saturday, September 26, 2026">26</button></span>
<span class="ml-calendar-cell" role="gridcell"><button class="ml-calendar-day" type="button" tabindex="-1" aria-label="Sunday, September 27, 2026">27</button></span>
</div>
<div class="ml-calendar-row" role="row">
<span class="ml-calendar-cell" role="gridcell"><button class="ml-calendar-day" type="button" tabindex="-1" aria-label="Monday, September 28, 2026">28</button></span>
<span class="ml-calendar-cell" role="gridcell"><button class="ml-calendar-day" type="button" tabindex="-1" aria-label="Tuesday, September 29, 2026">29</button></span>
<span class="ml-calendar-cell" role="gridcell"><button class="ml-calendar-day" type="button" tabindex="-1" aria-label="Wednesday, September 30, 2026">30</button></span>
<span class="ml-calendar-cell" role="gridcell"><button class="ml-calendar-day" type="button" tabindex="-1" aria-label="Thursday, October 1, 2026" data-outside="true">1</button></span>
<span class="ml-calendar-cell" role="gridcell"><button class="ml-calendar-day" type="button" tabindex="-1" aria-label="Friday, October 2, 2026" data-outside="true">2</button></span>
<span class="ml-calendar-cell" role="gridcell"><button class="ml-calendar-day" type="button" tabindex="-1" aria-label="Saturday, October 3, 2026" data-outside="true">3</button></span>
<span class="ml-calendar-cell" role="gridcell"><button class="ml-calendar-day" type="button" tabindex="-1" aria-label="Sunday, October 4, 2026" data-outside="true">4</button></span>
</div>
<div class="ml-calendar-row" role="row">
<span class="ml-calendar-cell" role="gridcell"><button class="ml-calendar-day" type="button" tabindex="-1" aria-label="Monday, October 5, 2026" data-outside="true">5</button></span>
<span class="ml-calendar-cell" role="gridcell"><button class="ml-calendar-day" type="button" tabindex="-1" aria-label="Tuesday, October 6, 2026" data-outside="true">6</button></span>
<span class="ml-calendar-cell" role="gridcell"><button class="ml-calendar-day" type="button" tabindex="-1" aria-label="Wednesday, October 7, 2026" data-outside="true">7</button></span>
<span class="ml-calendar-cell" role="gridcell"><button class="ml-calendar-day" type="button" tabindex="-1" aria-label="Thursday, October 8, 2026" data-outside="true">8</button></span>
<span class="ml-calendar-cell" role="gridcell"><button class="ml-calendar-day" type="button" tabindex="-1" aria-label="Friday, October 9, 2026" data-outside="true">9</button></span>
<span class="ml-calendar-cell" role="gridcell"><button class="ml-calendar-day" type="button" tabindex="-1" aria-label="Saturday, October 10, 2026" data-outside="true">10</button></span>
<span class="ml-calendar-cell" role="gridcell"><button class="ml-calendar-day" type="button" tabindex="-1" aria-label="Sunday, October 11, 2026" data-outside="true">11</button></span>
</div>
</div>
</div>
</div>
</div>Options
| Prop | Values | Default |
|---|---|---|
mode | singlemultiplerange | — |
Every prop with a fixed set of values, read from the exported TypeScript types with its doc comment. Open the Code tab above for the rest of the API.
Classes and attributes
A class names each element; data-* and aria-* attributes carry its variant and state. The values listed are the ones the stylesheet draws; the default needs no attribute.
| Class | Attributes |
|---|---|
ml-calendar | — |
ml-calendar-month | — |
ml-calendar-caption | — |
ml-calendar-title | — |
ml-calendar-nav | — |
ml-calendar-months | — |
ml-calendar-grid | — |
ml-calendar-row | — |
ml-calendar-weekday | — |
ml-calendar-cell | data-previewdata-range="end | start" |
ml-calendar-day | aria-disabled="true"data-outsidedata-selecteddata-todaydata-unreachable |
Accessibility and theming
- Semantic elements, accessible names and state relationships are part of the contract.
- Keyboard, pointer and touch paths are operable; focus stays visible in every theme.
- Light and dark modes pass WCAG AA contrast in all 5 canonical themes.
- Reduced motion and forced colors keep meaning and operation.
Dependencies
Installed alongside this component. No third-party UI runtime is ever added.
@mlola-ui/icons