suiss/uim · React Native Component
Calendar
A month grid for picking a single date, several dates or a date range.
Calendar is an open-source mobile component from suiss/uim for React Native and Expo, with SwiftUI and Jetpack Compose versions built from the same design tokens. The @esuiss/uim CLI copies its source into your project, so you own the code.


import * as React from "react"
import { Calendar } from "@/components/ui/calendar"
type CalendarProps = React.ComponentProps<typeof Calendar>
function SingleCalendar(
props: Omit<CalendarProps, "mode" | "selected" | "onSelect">
) {
const today = new Date()
const [date, setDate] = React.useState<Date | undefined>(
new Date(today.getFullYear(), today.getMonth(), today.getDate() + 3)
)
return (
<Calendar
{...props}
mode="single"
selected={date}
onSelect={setDate}
className="rounded-2xl border border-separator"
/>
)
}
export function CalendarExample() {
return (
<SingleCalendar />
)
}import EsuissUI
import SwiftUI
enum CalendarDates {
static func day(_ offset: Int) -> Date {
let calendar = Foundation.Calendar.current
let start = calendar.startOfDay(for: Date())
return calendar.date(byAdding: .day, value: offset, to: start) ?? start
}
static func dayOfMonth(_ day: Int) -> Date {
let calendar = Foundation.Calendar.current
var components = calendar.dateComponents([.year, .month], from: Date())
components.day = day
return calendar.date(from: components) ?? Date()
}
}
struct CalendarBordered: ViewModifier {
func body(content: Content) -> some View {
content
.clipShape(RoundedRectangle(cornerRadius: Radius.xl2, style: .continuous))
.overlay {
RoundedRectangle(cornerRadius: Radius.xl2, style: .continuous).strokeBorder(Color.separator)
}
}
}
struct CalendarSingleDemo: View {
var size: DateCalendar.Size = .default
var buttonVariant: VariantButtonStyle.Variant = .plain
var captionLayout: DateCalendar.CaptionLayout = .label
var showOutsideDays = false
var showWeekNumber = false
var weekendsDisabled = false
@State private var date: Date? = CalendarDates.day(3)
var body: some View {
DateCalendar(
selection: $date,
size: size,
buttonVariant: buttonVariant,
captionLayout: captionLayout,
showOutsideDays: showOutsideDays,
showWeekNumber: showWeekNumber,
isDisabled: { day in
guard weekendsDisabled else { return false }
let weekday = Foundation.Calendar.current.component(.weekday, from: day)
return weekday == 1 || weekday == 7
}
)
.modifier(CalendarBordered())
}
}
func day(_ offset: Int) -> Date {
let calendar = Foundation.Calendar.current
let start = calendar.startOfDay(for: Date())
return calendar.date(byAdding: .day, value: offset, to: start) ?? start
}
func dayOfMonth(_ day: Int) -> Date {
let calendar = Foundation.Calendar.current
var components = calendar.dateComponents([.year, .month], from: Date())
components.day = day
return calendar.date(from: components) ?? Date()
}import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.esuiss.ui.components.ButtonVariant
import com.esuiss.ui.components.Calendar
import com.esuiss.ui.components.CalendarCaptionLayout
import com.esuiss.ui.components.CalendarSize
import com.esuiss.ui.theme.EsuissTheme
import com.esuiss.ui.theme.Radius
import java.util.Date
import java.util.GregorianCalendar
import java.util.Locale
fun calendarDay(offsetDays: Int = 0, dayOfMonth: Int? = null): Date {
val now = GregorianCalendar()
val result = GregorianCalendar(now.get(GregorianCalendar.YEAR), now.get(GregorianCalendar.MONTH), now.get(GregorianCalendar.DAY_OF_MONTH))
if (dayOfMonth != null) result.set(GregorianCalendar.DAY_OF_MONTH, dayOfMonth)
result.add(GregorianCalendar.DAY_OF_MONTH, offsetDays)
return result.time
}
fun calendarIsWeekend(date: Date): Boolean {
val day = GregorianCalendar().apply { time = date }.get(GregorianCalendar.DAY_OF_WEEK)
return day == GregorianCalendar.SATURDAY || day == GregorianCalendar.SUNDAY
}
@Composable
fun CalendarFrame(content: @Composable () -> Unit) {
Box(
Modifier
.border(1.dp, EsuissTheme.colors.separator, RoundedCornerShape(Radius.xl2))
.padding(1.dp),
) {
content()
}
}
@Composable
fun CalendarSingle(
size: CalendarSize = CalendarSize.Default,
captionLayout: CalendarCaptionLayout = CalendarCaptionLayout.Label,
showWeekNumber: Boolean = false,
showOutsideDays: Boolean = false,
weekendsDisabled: Boolean = false,
buttonVariant: ButtonVariant = ButtonVariant.Plain,
locale: Locale = Locale.getDefault(),
key: String = "",
) {
var date by rememberSaveable(key) { mutableStateOf<Date?>(calendarDay(offsetDays = 3)) }
CalendarFrame {
Calendar(
selected = date,
onSelect = { date = it },
size = size,
captionLayout = captionLayout,
showWeekNumber = showWeekNumber,
showOutsideDays = showOutsideDays,
disabled = { weekendsDisabled && calendarIsWeekend(it) },
buttonVariant = buttonVariant,
locale = locale,
)
}
}Calendar renders one or more month grids with a caption, previous and next buttons, weekday headers and pressable day cells. Selection mode is set with mode: single, multiple or range. Ranges are drawn as a continuous shaded band with filled start and end days.
Three sizes are available: sm, default and lg. The caption can be a plain label or month and year dropdowns built on Select, which suits birth dates and other dates far from today.
Installation
$ pnpm dlx @esuiss/uim@latest add calendar$ pnpm dlx @esuiss/uim@latest add calendar --platform swift$ pnpm dlx @esuiss/uim@latest add calendar --platform kotlinThe CLI also adds what it builds on: Button, Icon, Select, Text.
Manual
- Install the dependencies:
npx expo install cn phosphor-react-native react-native-svg-
Add the pieces it builds on: Button, Icon, Select, Text, and the primitives
components/primitives/calendar.tsx,components/primitives/use-render.tsx. -
Copy the source into
components/ui/calendar.tsx:
import { cn } from "cn"
import { CaretLeftIcon, CaretRightIcon } from "phosphor-react-native"
import * as React from "react"
import { I18nManager, ScrollView, View } from "react-native"
import * as CalendarPrimitive from "@/components/primitives/calendar"
import { resolveClassName } from "@/components/primitives/use-render"
import { Button, type ButtonProps } from "@/components/ui/button"
import { Icon } from "@/components/ui/icon"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
import { Text, TextClassContext } from "@/components/ui/text"- Update the import paths to match your project.
- Install the dependencies:
npx expo install cn phosphor-react-native react-native-svg-
Add the pieces it builds on: Button, Icon, Select, Text, and the primitives
components/primitives/calendar.tsx,components/primitives/use-render.tsx. -
Copy the source into
components/ui/calendar.tsx:
import { cn } from "cn"
import { CaretLeftIcon, CaretRightIcon } from "phosphor-react-native"
import * as React from "react"
import { I18nManager, ScrollView, View } from "react-native"
import * as CalendarPrimitive from "@/components/primitives/calendar"
import { resolveClassName } from "@/components/primitives/use-render"
import { Button, type ButtonProps } from "@/components/ui/button"
import { Icon } from "@/components/ui/icon"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
import { Text, TextClassContext } from "@/components/ui/text"- Update the import paths to match your project.
- Install the dependencies:
npx expo install phosphor-react-native react-native-svg-
Add the pieces it builds on: Button, Icon, Select, Text, and the primitives
components/primitives/calendar.tsx,components/primitives/use-render.tsx. -
Copy the source into
components/ui/calendar.tsx:
import { CaretLeftIcon, CaretRightIcon } from "phosphor-react-native"
import * as React from "react"
import {
I18nManager,
Pressable,
ScrollView,
type StyleProp,
StyleSheet,
type TextStyle,
View,
type ViewStyle,
} from "react-native"
import * as CalendarPrimitive from "@/components/primitives/calendar"
import { resolveStyle } from "@/components/primitives/use-render"
import { Button, type ButtonProps } from "@/components/ui/button"
import { Icon } from "@/components/ui/icon"- Update the import paths to match your project.
-
Target iOS 17 or later.
-
Add the Swift package to your app:
.package(url: "https://github.com/phosphor-icons/swift", from: "2.1.0")- Copy the theme files every view uses:
import SwiftUI
import UIKit
struct RGBA: Sendable {
let red: CGFloat
let green: CGFloat
let blue: CGFloat
let alpha: CGFloat
init(_ hex: UInt32, _ alpha: CGFloat = 1) {
red = CGFloat((hex >> 16) & 0xFF) / 255
green = CGFloat((hex >> 8) & 0xFF) / 255
blue = CGFloat(hex & 0xFF) / 255
self.alpha = alpha
}import SwiftUI
extension Color {
public static let surface = Color.adaptive(light: .init(0xFFFFFF), dark: .init(0x000000))
public static let surfaceSecondary = Color.adaptive(light: .init(0xF5F5F7), dark: .init(0x161617))
public static let surfaceTertiary = Color.adaptive(light: .init(0xE8E8ED), dark: .init(0x2C2C2E))
public static let surfaceRaised = Color.adaptive(light: .init(0xFFFFFF), dark: .init(0x1D1D1F))
public static let label = Color.adaptive(light: .init(0x1D1D1F), dark: .init(0xF5F5F7))
public static let labelSecondary = Color.adaptive(light: .init(0x6E6E73), dark: .init(0xA1A1A6))
public static let labelTertiary = Color.adaptive(light: .init(0x86868B), dark: .init(0x86868B))
public static let labelQuaternary = Color.adaptive(light: .init(0xD2D2D7), dark: .init(0x424245))
public static let control = Color.adaptive(light: .init(0x000000, 0.04), dark: .init(0xFFFFFF, 0.1))
public static let controlPressed = Color.adaptive(light: .init(0x000000, 0.12), dark: .init(0xFFFFFF, 0.22))
public static let controlSelected = Color.adaptive(light: .init(0x1D1D1F), dark: .init(0xF5F5F7))
public static let controlDisabled = Color.adaptive(light: .init(0x000000, 0.03), dark: .init(0xFFFFFF, 0.05))
public static let onControlSelected = Color.adaptive(light: .init(0xFFFFFF), dark: .init(0x000000))- Copy the source and every file it uses:
import PhosphorSwift
import SwiftUI
public struct DateRange: Hashable, Sendable {
public var from: Date
public var to: Date?
public init(from: Date, to: Date? = nil) {
self.from = from
self.to = to
}
public static func adding(_ day: Date, to range: DateRange?, calendar: Foundation.Calendar) -> DateRange? {
guard let range else { return DateRange(from: day) }
let from = range.from
guard let to = range.to else {import SwiftUI
public struct VariantButtonStyle: ButtonStyle {
public enum Variant: CaseIterable, Sendable {
case `default`, outline, secondary, tinted, ghost, plain, destructive, link
}
public enum Size: CaseIterable, Sendable {
case `default`, xs, sm, lg, xl, icon, iconXs, iconSm, iconLg, iconXl
var height: CGFloat {
switch self {
case .xs, .iconXs: 24
case .sm, .iconSm: 32
case .default, .icon: 36
case .lg, .iconLg: 44import SwiftUI
extension EnvironmentValues {
@Entry var buttonGroup: Axis? = nil
@Entry var buttonGroupSize: CGSize = .zero
}
enum ButtonGroupSpace: Hashable {
case group
}
struct ButtonGroupFrame {
let bounds: Anchor<CGRect>
let outlined: Bool
}import PhosphorSwift
import SwiftUI
extension Shadow {
static let control = Shadow(color: .black.opacity(0.08), radius: 0.5, y: 0.5)
static let thumb = Shadow(color: .black.opacity(0.12), radius: 1.5, y: 1)
}
struct SelectionIndicator<Mark: View>: View {
let checked: Bool
let pressed: Bool
let cornerRadius: CGFloat
let mark: Mark
@Environment(\.isEnabled) private var isEnabled
@Environment(\.isInvalid) private var invalidimport SwiftUI
extension EnvironmentValues {
@Entry public var isInvalid = false
@Entry var fieldInset = false
@Entry var fieldGroupInset = false
@Entry var fieldSetInset = false
}
extension View {
public func invalid(_ isInvalid: Bool = true) -> some View {
environment(\.isInvalid, isInvalid)
}
}
public enum FieldOrientation: CaseIterable, Sendable {import SwiftUI
extension EnvironmentValues {
@Entry public var hapticsEnabled = true
}
extension View {
public func haptics(_ enabled: Bool) -> some View {
environment(\.hapticsEnabled, enabled)
}
func hapticFeedback<Trigger: Equatable>(_ feedback: SensoryFeedback, trigger: Trigger) -> some View {
modifier(HapticFeedbackModifier(feedback: feedback, trigger: trigger) { _, _ in true })
}
func hapticFeedback<Trigger: Equatable>(import SwiftUI
public struct Separator: View {
let orientation: Axis
public init(_ orientation: Axis = .horizontal) {
self.orientation = orientation
}
public var body: some View {
Rectangle()
.fill(Color.separator)
.frame(width: orientation == .vertical ? 1 : nil, height: orientation == .horizontal ? 1 : nil)
.frame(maxWidth: orientation == .horizontal ? .infinity : nil, maxHeight: orientation == .vertical ? .infinity : nil)
.accessibilityHidden(true)
}import SwiftUI
public enum TextVariant: CaseIterable, Sendable {
case `default`, h1, h2, h3, h4, p, blockquote, code, lead, large, small, muted
var style: TextStyle {
switch self {
case .h1: .xl3
case .h2: .xl2
case .h3: .xl
case .h4, .lead, .large: .lg
case .code, .small, .muted: .sm
default: .base
}
}- Add the files to your app target.
- Set
minSdkto 24 or higher and add the dependencies:
implementation(platform("androidx.compose:compose-bom:2026.09.00"))
implementation("androidx.compose.foundation:foundation")
implementation("androidx.compose.ui:ui")
implementation("com.adamglin:phosphor-icon-android:1.0.0")- Copy the theme files every view uses:
package com.esuiss.ui.theme
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.dropShadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.shadow.Shadow as DropShadow
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.LineHeightStyle
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpOffsetpackage com.esuiss.ui.theme
import androidx.compose.runtime.Immutable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
@Immutable
data class Colors(
val surface: Color,
val surfaceSecondary: Color,
val surfaceTertiary: Color,
val surfaceRaised: Color,
val label: Color,
val labelSecondary: Color,
val labelTertiary: Color,
val labelQuaternary: Color,- Copy the source and every file it uses:
package com.esuiss.ui.components
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideOutHorizontally
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.detectHorizontalDragGestures
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsPressedAsState
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Boxpackage com.esuiss.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsPressedAsState
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShapepackage com.esuiss.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.Immutablepackage com.esuiss.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsPressedAsState
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.selection.triStateToggleable
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Colorpackage com.esuiss.ui.components
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
val LocalContentStyle = compositionLocalOf { TextStyle.Default }
val LocalContentColor = compositionLocalOf { Color.Unspecified }
val LocalIconSize = compositionLocalOf { 20.dp }package com.esuiss.ui.components
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsPressedAsState
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightpackage com.esuiss.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.LayoutScopeMarker
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextpackage com.esuiss.ui.components
import android.provider.Settings
import androidx.compose.animation.core.CubicBezierEasing
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.text.selection.TextSelectionColors
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Sizepackage com.esuiss.ui.components
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.remember
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.hapticfeedback.HapticFeedback
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalHapticFeedback
val LocalHapticsEnabled = staticCompositionLocalOf { true }
@Composable
fun ProvideHaptics(enabled: Boolean, content: @Composable () -> Unit) {
CompositionLocalProvider(LocalHapticsEnabled provides enabled, content = content)
}package com.esuiss.ui.components
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.takeOrElse
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.unit.Dp
import com.esuiss.ui.theme.EsuissTheme
@Composable
fun Icon(
imageVector: ImageVector,package com.esuiss.ui.components
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.text.BasicText
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.esuiss.ui.theme.EsuissTheme
import com.esuiss.ui.theme.Typography
@Composablepackage com.esuiss.ui.components
import android.os.Build
import android.view.Window
import android.view.WindowManager
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.AnimatedVisibilityScope
import androidx.compose.animation.EnterExitState
import androidx.compose.animation.core.MutableTransitionState
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.scaleIn
import androidx.compose.animation.scaleOut
import androidx.compose.animation.slideInpackage com.esuiss.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.esuiss.ui.theme.EsuissTheme
@Composable
fun Separator(- Update the
com.esuiss.uipackage names to match your project.
Usage
import { CalendarDayButton, Calendar } from "@/components/ui/calendar"<SingleCalendar />Pass mode, selected and onSelect to control the selection; selected is a Date, a Date[] or a { from, to } range depending on the mode. Use month, defaultMonth and onMonthChange to control the visible month, and numberOfMonths to show several months.
Use disabled with date matchers, such as { dayOfWeek: [0, 6] }, to block days. Limit navigation with startMonth and endMonth; with a year dropdown they default to the last 100 years.
Set locale for month and weekday names, and weekStartsOn to change the first day of the week. CalendarDayButton is exported for custom day rendering.
Examples
Sizes
The sm, default and lg sizes.


import * as React from "react"
import { ScrollView } from "react-native"
import { Calendar } from "@/components/ui/calendar"
type CalendarProps = React.ComponentProps<typeof Calendar>
function SingleCalendar(
props: Omit<CalendarProps, "mode" | "selected" | "onSelect">
) {
const today = new Date()
const [date, setDate] = React.useState<Date | undefined>(
new Date(today.getFullYear(), today.getMonth(), today.getDate() + 3)
)
return (
<Calendar
{...props}
mode="single"
selected={date}
onSelect={setDate}
className="rounded-2xl border border-separator"
/>
)
}
export function CalendarSizesExample() {
return (
<ScrollView contentContainerClassName="items-start gap-6">
<SingleCalendar size="sm" />
<SingleCalendar size="default" />
<SingleCalendar size="lg" />
</ScrollView>
)
}import EsuissUI
import SwiftUI
struct CalendarSizesExample: View {
var body: some View {
VStack(alignment: .leading, spacing: 24) {
CalendarSingleDemo(size: .sm)
CalendarSingleDemo(size: .default)
CalendarSingleDemo(size: .lg)
}
}
}
enum CalendarDates {
static func day(_ offset: Int) -> Date {
let calendar = Foundation.Calendar.current
let start = calendar.startOfDay(for: Date())
return calendar.date(byAdding: .day, value: offset, to: start) ?? start
}
static func dayOfMonth(_ day: Int) -> Date {
let calendar = Foundation.Calendar.current
var components = calendar.dateComponents([.year, .month], from: Date())
components.day = day
return calendar.date(from: components) ?? Date()
}
}
struct CalendarBordered: ViewModifier {
func body(content: Content) -> some View {
content
.clipShape(RoundedRectangle(cornerRadius: Radius.xl2, style: .continuous))
.overlay {
RoundedRectangle(cornerRadius: Radius.xl2, style: .continuous).strokeBorder(Color.separator)
}
}
}
struct CalendarSingleDemo: View {
var size: DateCalendar.Size = .default
var buttonVariant: VariantButtonStyle.Variant = .plain
var captionLayout: DateCalendar.CaptionLayout = .label
var showOutsideDays = false
var showWeekNumber = false
var weekendsDisabled = false
@State private var date: Date? = CalendarDates.day(3)
var body: some View {
DateCalendar(
selection: $date,
size: size,
buttonVariant: buttonVariant,
captionLayout: captionLayout,
showOutsideDays: showOutsideDays,
showWeekNumber: showWeekNumber,
isDisabled: { day in
guard weekendsDisabled else { return false }
let weekday = Foundation.Calendar.current.component(.weekday, from: day)
return weekday == 1 || weekday == 7
}
)
.modifier(CalendarBordered())
}
}
func day(_ offset: Int) -> Date {
let calendar = Foundation.Calendar.current
let start = calendar.startOfDay(for: Date())
return calendar.date(byAdding: .day, value: offset, to: start) ?? start
}
func dayOfMonth(_ day: Int) -> Date {
let calendar = Foundation.Calendar.current
var components = calendar.dateComponents([.year, .month], from: Date())
components.day = day
return calendar.date(from: components) ?? Date()
}import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.esuiss.ui.components.ButtonVariant
import com.esuiss.ui.components.Calendar
import com.esuiss.ui.components.CalendarCaptionLayout
import com.esuiss.ui.components.CalendarSize
import com.esuiss.ui.theme.EsuissTheme
import com.esuiss.ui.theme.Radius
import java.util.Date
import java.util.GregorianCalendar
import java.util.Locale
fun calendarDay(offsetDays: Int = 0, dayOfMonth: Int? = null): Date {
val now = GregorianCalendar()
val result = GregorianCalendar(now.get(GregorianCalendar.YEAR), now.get(GregorianCalendar.MONTH), now.get(GregorianCalendar.DAY_OF_MONTH))
if (dayOfMonth != null) result.set(GregorianCalendar.DAY_OF_MONTH, dayOfMonth)
result.add(GregorianCalendar.DAY_OF_MONTH, offsetDays)
return result.time
}
fun calendarIsWeekend(date: Date): Boolean {
val day = GregorianCalendar().apply { time = date }.get(GregorianCalendar.DAY_OF_WEEK)
return day == GregorianCalendar.SATURDAY || day == GregorianCalendar.SUNDAY
}
@Composable
fun CalendarFrame(content: @Composable () -> Unit) {
Box(
Modifier
.border(1.dp, EsuissTheme.colors.separator, RoundedCornerShape(Radius.xl2))
.padding(1.dp),
) {
content()
}
}
@Composable
fun CalendarSingle(
size: CalendarSize = CalendarSize.Default,
captionLayout: CalendarCaptionLayout = CalendarCaptionLayout.Label,
showWeekNumber: Boolean = false,
showOutsideDays: Boolean = false,
weekendsDisabled: Boolean = false,
buttonVariant: ButtonVariant = ButtonVariant.Plain,
locale: Locale = Locale.getDefault(),
key: String = "",
) {
var date by rememberSaveable(key) { mutableStateOf<Date?>(calendarDay(offsetDays = 3)) }
CalendarFrame {
Calendar(
selected = date,
onSelect = { date = it },
size = size,
captionLayout = captionLayout,
showWeekNumber = showWeekNumber,
showOutsideDays = showOutsideDays,
disabled = { weekendsDisabled && calendarIsWeekend(it) },
buttonVariant = buttonVariant,
locale = locale,
)
}
}
@Composable
fun CalendarSizes() {
Column(verticalArrangement = Arrangement.spacedBy(24.dp)) {
CalendarSingle(size = CalendarSize.Sm, key = "sm")
CalendarSingle(size = CalendarSize.Default, key = "default")
CalendarSingle(size = CalendarSize.Lg, key = "lg")
}
}Range
Range selection across two months.


import * as React from "react"
import { ScrollView } from "react-native"
import { DateRange } from "@/components/primitives/calendar"
import { Calendar } from "@/components/ui/calendar"
function RangeCalendar() {
const today = new Date()
const [range, setRange] = React.useState<DateRange | undefined>({
from: today,
to: new Date(today.getFullYear(), today.getMonth(), today.getDate() + 6),
})
return (
<ScrollView>
<Calendar
mode="range"
selected={range}
onSelect={setRange}
numberOfMonths={2}
className="rounded-2xl border border-separator"
/>
</ScrollView>
)
}
export function CalendarRangeExample() {
return (
<RangeCalendar />
)
}import EsuissUI
import SwiftUI
enum CalendarDates {
static func day(_ offset: Int) -> Date {
let calendar = Foundation.Calendar.current
let start = calendar.startOfDay(for: Date())
return calendar.date(byAdding: .day, value: offset, to: start) ?? start
}
static func dayOfMonth(_ day: Int) -> Date {
let calendar = Foundation.Calendar.current
var components = calendar.dateComponents([.year, .month], from: Date())
components.day = day
return calendar.date(from: components) ?? Date()
}
}
struct CalendarBordered: ViewModifier {
func body(content: Content) -> some View {
content
.clipShape(RoundedRectangle(cornerRadius: Radius.xl2, style: .continuous))
.overlay {
RoundedRectangle(cornerRadius: Radius.xl2, style: .continuous).strokeBorder(Color.separator)
}
}
}
struct CalendarRangeDemo: View {
@State private var range: DateRange? = DateRange(from: CalendarDates.day(0), to: CalendarDates.day(6))
var body: some View {
DateCalendar(selection: $range, numberOfMonths: 2)
.modifier(CalendarBordered())
}
}
func day(_ offset: Int) -> Date {
let calendar = Foundation.Calendar.current
let start = calendar.startOfDay(for: Date())
return calendar.date(byAdding: .day, value: offset, to: start) ?? start
}
func dayOfMonth(_ day: Int) -> Date {
let calendar = Foundation.Calendar.current
var components = calendar.dateComponents([.year, .month], from: Date())
components.day = day
return calendar.date(from: components) ?? Date()
}import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.esuiss.ui.components.Calendar
import com.esuiss.ui.components.DateRange
import com.esuiss.ui.theme.EsuissTheme
import com.esuiss.ui.theme.Radius
import java.util.Date
import java.util.GregorianCalendar
fun calendarDay(offsetDays: Int = 0, dayOfMonth: Int? = null): Date {
val now = GregorianCalendar()
val result = GregorianCalendar(now.get(GregorianCalendar.YEAR), now.get(GregorianCalendar.MONTH), now.get(GregorianCalendar.DAY_OF_MONTH))
if (dayOfMonth != null) result.set(GregorianCalendar.DAY_OF_MONTH, dayOfMonth)
result.add(GregorianCalendar.DAY_OF_MONTH, offsetDays)
return result.time
}
@Composable
fun CalendarFrame(content: @Composable () -> Unit) {
Box(
Modifier
.border(1.dp, EsuissTheme.colors.separator, RoundedCornerShape(Radius.xl2))
.padding(1.dp),
) {
content()
}
}
@Composable
fun CalendarRange() {
var from by rememberSaveable { mutableStateOf<Date?>(calendarDay()) }
var to by rememberSaveable { mutableStateOf<Date?>(calendarDay(offsetDays = 6)) }
CalendarFrame {
Calendar(
selected = if (from == null) null else DateRange(from, to),
onSelect = { range: DateRange? ->
from = range?.from
to = range?.to
},
numberOfMonths = 2,
)
}
}Multiple
Several separate dates selected with mode="multiple".


import * as React from "react"
import { Calendar } from "@/components/ui/calendar"
function MultipleCalendar() {
const today = new Date()
const [dates, setDates] = React.useState<Date[] | undefined>([
new Date(today.getFullYear(), today.getMonth(), 8),
new Date(today.getFullYear(), today.getMonth(), 15),
new Date(today.getFullYear(), today.getMonth(), 22),
])
return (
<Calendar
mode="multiple"
selected={dates}
onSelect={setDates}
className="rounded-2xl border border-separator"
/>
)
}
export function CalendarMultipleExample() {
return (
<MultipleCalendar />
)
}import EsuissUI
import SwiftUI
enum CalendarDates {
static func day(_ offset: Int) -> Date {
let calendar = Foundation.Calendar.current
let start = calendar.startOfDay(for: Date())
return calendar.date(byAdding: .day, value: offset, to: start) ?? start
}
static func dayOfMonth(_ day: Int) -> Date {
let calendar = Foundation.Calendar.current
var components = calendar.dateComponents([.year, .month], from: Date())
components.day = day
return calendar.date(from: components) ?? Date()
}
}
struct CalendarBordered: ViewModifier {
func body(content: Content) -> some View {
content
.clipShape(RoundedRectangle(cornerRadius: Radius.xl2, style: .continuous))
.overlay {
RoundedRectangle(cornerRadius: Radius.xl2, style: .continuous).strokeBorder(Color.separator)
}
}
}
struct CalendarMultipleDemo: View {
@State private var dates: Set<Date> = [
CalendarDates.dayOfMonth(8),
CalendarDates.dayOfMonth(15),
CalendarDates.dayOfMonth(22),
]
var body: some View {
DateCalendar(selection: $dates)
.modifier(CalendarBordered())
}
}
func day(_ offset: Int) -> Date {
let calendar = Foundation.Calendar.current
let start = calendar.startOfDay(for: Date())
return calendar.date(byAdding: .day, value: offset, to: start) ?? start
}
func dayOfMonth(_ day: Int) -> Date {
let calendar = Foundation.Calendar.current
var components = calendar.dateComponents([.year, .month], from: Date())
components.day = day
return calendar.date(from: components) ?? Date()
}import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.esuiss.ui.components.Calendar
import com.esuiss.ui.theme.EsuissTheme
import com.esuiss.ui.theme.Radius
import java.util.Date
import java.util.GregorianCalendar
fun calendarDay(offsetDays: Int = 0, dayOfMonth: Int? = null): Date {
val now = GregorianCalendar()
val result = GregorianCalendar(now.get(GregorianCalendar.YEAR), now.get(GregorianCalendar.MONTH), now.get(GregorianCalendar.DAY_OF_MONTH))
if (dayOfMonth != null) result.set(GregorianCalendar.DAY_OF_MONTH, dayOfMonth)
result.add(GregorianCalendar.DAY_OF_MONTH, offsetDays)
return result.time
}
@Composable
fun CalendarFrame(content: @Composable () -> Unit) {
Box(
Modifier
.border(1.dp, EsuissTheme.colors.separator, RoundedCornerShape(Radius.xl2))
.padding(1.dp),
) {
content()
}
}
@Composable
fun CalendarMultiple() {
var dates by rememberSaveable {
mutableStateOf(listOf(calendarDay(dayOfMonth = 8), calendarDay(dayOfMonth = 15), calendarDay(dayOfMonth = 22)))
}
CalendarFrame {
Calendar(selected = dates, onSelect = { value: List<Date> -> dates = value })
}
}Dropdown caption
Month and year dropdowns in the caption.


import * as React from "react"
import { Calendar } from "@/components/ui/calendar"
type CalendarProps = React.ComponentProps<typeof Calendar>
function SingleCalendar(
props: Omit<CalendarProps, "mode" | "selected" | "onSelect">
) {
const today = new Date()
const [date, setDate] = React.useState<Date | undefined>(
new Date(today.getFullYear(), today.getMonth(), today.getDate() + 3)
)
return (
<Calendar
{...props}
mode="single"
selected={date}
onSelect={setDate}
className="rounded-2xl border border-separator"
/>
)
}
export function CalendarDropdownCaptionExample() {
return (
<SingleCalendar captionLayout="dropdown" />
)
}import EsuissUI
import SwiftUI
struct CalendarDropdownCaptionExample: View {
var body: some View {
CalendarSingleDemo(captionLayout: .dropdown)
}
}
enum CalendarDates {
static func day(_ offset: Int) -> Date {
let calendar = Foundation.Calendar.current
let start = calendar.startOfDay(for: Date())
return calendar.date(byAdding: .day, value: offset, to: start) ?? start
}
static func dayOfMonth(_ day: Int) -> Date {
let calendar = Foundation.Calendar.current
var components = calendar.dateComponents([.year, .month], from: Date())
components.day = day
return calendar.date(from: components) ?? Date()
}
}
struct CalendarBordered: ViewModifier {
func body(content: Content) -> some View {
content
.clipShape(RoundedRectangle(cornerRadius: Radius.xl2, style: .continuous))
.overlay {
RoundedRectangle(cornerRadius: Radius.xl2, style: .continuous).strokeBorder(Color.separator)
}
}
}
struct CalendarSingleDemo: View {
var size: DateCalendar.Size = .default
var buttonVariant: VariantButtonStyle.Variant = .plain
var captionLayout: DateCalendar.CaptionLayout = .label
var showOutsideDays = false
var showWeekNumber = false
var weekendsDisabled = false
@State private var date: Date? = CalendarDates.day(3)
var body: some View {
DateCalendar(
selection: $date,
size: size,
buttonVariant: buttonVariant,
captionLayout: captionLayout,
showOutsideDays: showOutsideDays,
showWeekNumber: showWeekNumber,
isDisabled: { day in
guard weekendsDisabled else { return false }
let weekday = Foundation.Calendar.current.component(.weekday, from: day)
return weekday == 1 || weekday == 7
}
)
.modifier(CalendarBordered())
}
}
func day(_ offset: Int) -> Date {
let calendar = Foundation.Calendar.current
let start = calendar.startOfDay(for: Date())
return calendar.date(byAdding: .day, value: offset, to: start) ?? start
}
func dayOfMonth(_ day: Int) -> Date {
let calendar = Foundation.Calendar.current
var components = calendar.dateComponents([.year, .month], from: Date())
components.day = day
return calendar.date(from: components) ?? Date()
}import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.esuiss.ui.components.ButtonVariant
import com.esuiss.ui.components.Calendar
import com.esuiss.ui.components.CalendarCaptionLayout
import com.esuiss.ui.components.CalendarSize
import com.esuiss.ui.theme.EsuissTheme
import com.esuiss.ui.theme.Radius
import java.util.Date
import java.util.GregorianCalendar
import java.util.Locale
@Composable
fun CalendarDropdownCaptionExample() {
CalendarSingle(captionLayout = CalendarCaptionLayout.Dropdown)
}
fun calendarDay(offsetDays: Int = 0, dayOfMonth: Int? = null): Date {
val now = GregorianCalendar()
val result = GregorianCalendar(now.get(GregorianCalendar.YEAR), now.get(GregorianCalendar.MONTH), now.get(GregorianCalendar.DAY_OF_MONTH))
if (dayOfMonth != null) result.set(GregorianCalendar.DAY_OF_MONTH, dayOfMonth)
result.add(GregorianCalendar.DAY_OF_MONTH, offsetDays)
return result.time
}
fun calendarIsWeekend(date: Date): Boolean {
val day = GregorianCalendar().apply { time = date }.get(GregorianCalendar.DAY_OF_WEEK)
return day == GregorianCalendar.SATURDAY || day == GregorianCalendar.SUNDAY
}
@Composable
fun CalendarFrame(content: @Composable () -> Unit) {
Box(
Modifier
.border(1.dp, EsuissTheme.colors.separator, RoundedCornerShape(Radius.xl2))
.padding(1.dp),
) {
content()
}
}
@Composable
fun CalendarSingle(
size: CalendarSize = CalendarSize.Default,
captionLayout: CalendarCaptionLayout = CalendarCaptionLayout.Label,
showWeekNumber: Boolean = false,
showOutsideDays: Boolean = false,
weekendsDisabled: Boolean = false,
buttonVariant: ButtonVariant = ButtonVariant.Plain,
locale: Locale = Locale.getDefault(),
key: String = "",
) {
var date by rememberSaveable(key) { mutableStateOf<Date?>(calendarDay(offsetDays = 3)) }
CalendarFrame {
Calendar(
selected = date,
onSelect = { date = it },
size = size,
captionLayout = captionLayout,
showWeekNumber = showWeekNumber,
showOutsideDays = showOutsideDays,
disabled = { weekendsDisabled && calendarIsWeekend(it) },
buttonVariant = buttonVariant,
locale = locale,
)
}
}Week numbers
Week numbers in a leading column.


import * as React from "react"
import { Calendar } from "@/components/ui/calendar"
type CalendarProps = React.ComponentProps<typeof Calendar>
function SingleCalendar(
props: Omit<CalendarProps, "mode" | "selected" | "onSelect">
) {
const today = new Date()
const [date, setDate] = React.useState<Date | undefined>(
new Date(today.getFullYear(), today.getMonth(), today.getDate() + 3)
)
return (
<Calendar
{...props}
mode="single"
selected={date}
onSelect={setDate}
className="rounded-2xl border border-separator"
/>
)
}
export function CalendarWeekNumbersExample() {
return (
<SingleCalendar showWeekNumber />
)
}import EsuissUI
import SwiftUI
struct CalendarWeekNumbersExample: View {
var body: some View {
CalendarSingleDemo(showWeekNumber: true)
}
}
enum CalendarDates {
static func day(_ offset: Int) -> Date {
let calendar = Foundation.Calendar.current
let start = calendar.startOfDay(for: Date())
return calendar.date(byAdding: .day, value: offset, to: start) ?? start
}
static func dayOfMonth(_ day: Int) -> Date {
let calendar = Foundation.Calendar.current
var components = calendar.dateComponents([.year, .month], from: Date())
components.day = day
return calendar.date(from: components) ?? Date()
}
}
struct CalendarBordered: ViewModifier {
func body(content: Content) -> some View {
content
.clipShape(RoundedRectangle(cornerRadius: Radius.xl2, style: .continuous))
.overlay {
RoundedRectangle(cornerRadius: Radius.xl2, style: .continuous).strokeBorder(Color.separator)
}
}
}
struct CalendarSingleDemo: View {
var size: DateCalendar.Size = .default
var buttonVariant: VariantButtonStyle.Variant = .plain
var captionLayout: DateCalendar.CaptionLayout = .label
var showOutsideDays = false
var showWeekNumber = false
var weekendsDisabled = false
@State private var date: Date? = CalendarDates.day(3)
var body: some View {
DateCalendar(
selection: $date,
size: size,
buttonVariant: buttonVariant,
captionLayout: captionLayout,
showOutsideDays: showOutsideDays,
showWeekNumber: showWeekNumber,
isDisabled: { day in
guard weekendsDisabled else { return false }
let weekday = Foundation.Calendar.current.component(.weekday, from: day)
return weekday == 1 || weekday == 7
}
)
.modifier(CalendarBordered())
}
}
func day(_ offset: Int) -> Date {
let calendar = Foundation.Calendar.current
let start = calendar.startOfDay(for: Date())
return calendar.date(byAdding: .day, value: offset, to: start) ?? start
}
func dayOfMonth(_ day: Int) -> Date {
let calendar = Foundation.Calendar.current
var components = calendar.dateComponents([.year, .month], from: Date())
components.day = day
return calendar.date(from: components) ?? Date()
}import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.esuiss.ui.components.ButtonVariant
import com.esuiss.ui.components.Calendar
import com.esuiss.ui.components.CalendarCaptionLayout
import com.esuiss.ui.components.CalendarSize
import com.esuiss.ui.theme.EsuissTheme
import com.esuiss.ui.theme.Radius
import java.util.Date
import java.util.GregorianCalendar
import java.util.Locale
@Composable
fun CalendarWeekNumbersExample() {
CalendarSingle(showWeekNumber = true)
}
fun calendarDay(offsetDays: Int = 0, dayOfMonth: Int? = null): Date {
val now = GregorianCalendar()
val result = GregorianCalendar(now.get(GregorianCalendar.YEAR), now.get(GregorianCalendar.MONTH), now.get(GregorianCalendar.DAY_OF_MONTH))
if (dayOfMonth != null) result.set(GregorianCalendar.DAY_OF_MONTH, dayOfMonth)
result.add(GregorianCalendar.DAY_OF_MONTH, offsetDays)
return result.time
}
fun calendarIsWeekend(date: Date): Boolean {
val day = GregorianCalendar().apply { time = date }.get(GregorianCalendar.DAY_OF_WEEK)
return day == GregorianCalendar.SATURDAY || day == GregorianCalendar.SUNDAY
}
@Composable
fun CalendarFrame(content: @Composable () -> Unit) {
Box(
Modifier
.border(1.dp, EsuissTheme.colors.separator, RoundedCornerShape(Radius.xl2))
.padding(1.dp),
) {
content()
}
}
@Composable
fun CalendarSingle(
size: CalendarSize = CalendarSize.Default,
captionLayout: CalendarCaptionLayout = CalendarCaptionLayout.Label,
showWeekNumber: Boolean = false,
showOutsideDays: Boolean = false,
weekendsDisabled: Boolean = false,
buttonVariant: ButtonVariant = ButtonVariant.Plain,
locale: Locale = Locale.getDefault(),
key: String = "",
) {
var date by rememberSaveable(key) { mutableStateOf<Date?>(calendarDay(offsetDays = 3)) }
CalendarFrame {
Calendar(
selected = date,
onSelect = { date = it },
size = size,
captionLayout = captionLayout,
showWeekNumber = showWeekNumber,
showOutsideDays = showOutsideDays,
disabled = { weekendsDisabled && calendarIsWeekend(it) },
buttonVariant = buttonVariant,
locale = locale,
)
}
}Disabled days
Weekends disabled with a dayOfWeek matcher.


import * as React from "react"
import { Calendar } from "@/components/ui/calendar"
type CalendarProps = React.ComponentProps<typeof Calendar>
function SingleCalendar(
props: Omit<CalendarProps, "mode" | "selected" | "onSelect">
) {
const today = new Date()
const [date, setDate] = React.useState<Date | undefined>(
new Date(today.getFullYear(), today.getMonth(), today.getDate() + 3)
)
return (
<Calendar
{...props}
mode="single"
selected={date}
onSelect={setDate}
className="rounded-2xl border border-separator"
/>
)
}
export function CalendarDisabledDaysExample() {
return (
<SingleCalendar disabled={{ dayOfWeek: [0, 6] }} />
)
}import EsuissUI
import SwiftUI
struct CalendarDisabledDaysExample: View {
var body: some View {
CalendarSingleDemo(weekendsDisabled: true)
}
}
enum CalendarDates {
static func day(_ offset: Int) -> Date {
let calendar = Foundation.Calendar.current
let start = calendar.startOfDay(for: Date())
return calendar.date(byAdding: .day, value: offset, to: start) ?? start
}
static func dayOfMonth(_ day: Int) -> Date {
let calendar = Foundation.Calendar.current
var components = calendar.dateComponents([.year, .month], from: Date())
components.day = day
return calendar.date(from: components) ?? Date()
}
}
struct CalendarBordered: ViewModifier {
func body(content: Content) -> some View {
content
.clipShape(RoundedRectangle(cornerRadius: Radius.xl2, style: .continuous))
.overlay {
RoundedRectangle(cornerRadius: Radius.xl2, style: .continuous).strokeBorder(Color.separator)
}
}
}
struct CalendarSingleDemo: View {
var size: DateCalendar.Size = .default
var buttonVariant: VariantButtonStyle.Variant = .plain
var captionLayout: DateCalendar.CaptionLayout = .label
var showOutsideDays = false
var showWeekNumber = false
var weekendsDisabled = false
@State private var date: Date? = CalendarDates.day(3)
var body: some View {
DateCalendar(
selection: $date,
size: size,
buttonVariant: buttonVariant,
captionLayout: captionLayout,
showOutsideDays: showOutsideDays,
showWeekNumber: showWeekNumber,
isDisabled: { day in
guard weekendsDisabled else { return false }
let weekday = Foundation.Calendar.current.component(.weekday, from: day)
return weekday == 1 || weekday == 7
}
)
.modifier(CalendarBordered())
}
}
func day(_ offset: Int) -> Date {
let calendar = Foundation.Calendar.current
let start = calendar.startOfDay(for: Date())
return calendar.date(byAdding: .day, value: offset, to: start) ?? start
}
func dayOfMonth(_ day: Int) -> Date {
let calendar = Foundation.Calendar.current
var components = calendar.dateComponents([.year, .month], from: Date())
components.day = day
return calendar.date(from: components) ?? Date()
}import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.esuiss.ui.components.ButtonVariant
import com.esuiss.ui.components.Calendar
import com.esuiss.ui.components.CalendarCaptionLayout
import com.esuiss.ui.components.CalendarSize
import com.esuiss.ui.theme.EsuissTheme
import com.esuiss.ui.theme.Radius
import java.util.Date
import java.util.GregorianCalendar
import java.util.Locale
@Composable
fun CalendarDisabledDaysExample() {
CalendarSingle(weekendsDisabled = true)
}
fun calendarDay(offsetDays: Int = 0, dayOfMonth: Int? = null): Date {
val now = GregorianCalendar()
val result = GregorianCalendar(now.get(GregorianCalendar.YEAR), now.get(GregorianCalendar.MONTH), now.get(GregorianCalendar.DAY_OF_MONTH))
if (dayOfMonth != null) result.set(GregorianCalendar.DAY_OF_MONTH, dayOfMonth)
result.add(GregorianCalendar.DAY_OF_MONTH, offsetDays)
return result.time
}
fun calendarIsWeekend(date: Date): Boolean {
val day = GregorianCalendar().apply { time = date }.get(GregorianCalendar.DAY_OF_WEEK)
return day == GregorianCalendar.SATURDAY || day == GregorianCalendar.SUNDAY
}
@Composable
fun CalendarFrame(content: @Composable () -> Unit) {
Box(
Modifier
.border(1.dp, EsuissTheme.colors.separator, RoundedCornerShape(Radius.xl2))
.padding(1.dp),
) {
content()
}
}
@Composable
fun CalendarSingle(
size: CalendarSize = CalendarSize.Default,
captionLayout: CalendarCaptionLayout = CalendarCaptionLayout.Label,
showWeekNumber: Boolean = false,
showOutsideDays: Boolean = false,
weekendsDisabled: Boolean = false,
buttonVariant: ButtonVariant = ButtonVariant.Plain,
locale: Locale = Locale.getDefault(),
key: String = "",
) {
var date by rememberSaveable(key) { mutableStateOf<Date?>(calendarDay(offsetDays = 3)) }
CalendarFrame {
Calendar(
selected = date,
onSelect = { date = it },
size = size,
captionLayout = captionLayout,
showWeekNumber = showWeekNumber,
showOutsideDays = showOutsideDays,
disabled = { weekendsDisabled && calendarIsWeekend(it) },
buttonVariant = buttonVariant,
locale = locale,
)
}
}Outside days
Days from adjacent months shown with showOutsideDays.


import * as React from "react"
import { Calendar } from "@/components/ui/calendar"
type CalendarProps = React.ComponentProps<typeof Calendar>
function SingleCalendar(
props: Omit<CalendarProps, "mode" | "selected" | "onSelect">
) {
const today = new Date()
const [date, setDate] = React.useState<Date | undefined>(
new Date(today.getFullYear(), today.getMonth(), today.getDate() + 3)
)
return (
<Calendar
{...props}
mode="single"
selected={date}
onSelect={setDate}
className="rounded-2xl border border-separator"
/>
)
}
export function CalendarOutsideDaysExample() {
return (
<SingleCalendar showOutsideDays />
)
}import EsuissUI
import SwiftUI
struct CalendarOutsideDaysExample: View {
var body: some View {
CalendarSingleDemo(showOutsideDays: true)
}
}
enum CalendarDates {
static func day(_ offset: Int) -> Date {
let calendar = Foundation.Calendar.current
let start = calendar.startOfDay(for: Date())
return calendar.date(byAdding: .day, value: offset, to: start) ?? start
}
static func dayOfMonth(_ day: Int) -> Date {
let calendar = Foundation.Calendar.current
var components = calendar.dateComponents([.year, .month], from: Date())
components.day = day
return calendar.date(from: components) ?? Date()
}
}
struct CalendarBordered: ViewModifier {
func body(content: Content) -> some View {
content
.clipShape(RoundedRectangle(cornerRadius: Radius.xl2, style: .continuous))
.overlay {
RoundedRectangle(cornerRadius: Radius.xl2, style: .continuous).strokeBorder(Color.separator)
}
}
}
struct CalendarSingleDemo: View {
var size: DateCalendar.Size = .default
var buttonVariant: VariantButtonStyle.Variant = .plain
var captionLayout: DateCalendar.CaptionLayout = .label
var showOutsideDays = false
var showWeekNumber = false
var weekendsDisabled = false
@State private var date: Date? = CalendarDates.day(3)
var body: some View {
DateCalendar(
selection: $date,
size: size,
buttonVariant: buttonVariant,
captionLayout: captionLayout,
showOutsideDays: showOutsideDays,
showWeekNumber: showWeekNumber,
isDisabled: { day in
guard weekendsDisabled else { return false }
let weekday = Foundation.Calendar.current.component(.weekday, from: day)
return weekday == 1 || weekday == 7
}
)
.modifier(CalendarBordered())
}
}
func day(_ offset: Int) -> Date {
let calendar = Foundation.Calendar.current
let start = calendar.startOfDay(for: Date())
return calendar.date(byAdding: .day, value: offset, to: start) ?? start
}
func dayOfMonth(_ day: Int) -> Date {
let calendar = Foundation.Calendar.current
var components = calendar.dateComponents([.year, .month], from: Date())
components.day = day
return calendar.date(from: components) ?? Date()
}import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.esuiss.ui.components.ButtonVariant
import com.esuiss.ui.components.Calendar
import com.esuiss.ui.components.CalendarCaptionLayout
import com.esuiss.ui.components.CalendarSize
import com.esuiss.ui.theme.EsuissTheme
import com.esuiss.ui.theme.Radius
import java.util.Date
import java.util.GregorianCalendar
import java.util.Locale
@Composable
fun CalendarOutsideDaysExample() {
CalendarSingle(showOutsideDays = true)
}
fun calendarDay(offsetDays: Int = 0, dayOfMonth: Int? = null): Date {
val now = GregorianCalendar()
val result = GregorianCalendar(now.get(GregorianCalendar.YEAR), now.get(GregorianCalendar.MONTH), now.get(GregorianCalendar.DAY_OF_MONTH))
if (dayOfMonth != null) result.set(GregorianCalendar.DAY_OF_MONTH, dayOfMonth)
result.add(GregorianCalendar.DAY_OF_MONTH, offsetDays)
return result.time
}
fun calendarIsWeekend(date: Date): Boolean {
val day = GregorianCalendar().apply { time = date }.get(GregorianCalendar.DAY_OF_WEEK)
return day == GregorianCalendar.SATURDAY || day == GregorianCalendar.SUNDAY
}
@Composable
fun CalendarFrame(content: @Composable () -> Unit) {
Box(
Modifier
.border(1.dp, EsuissTheme.colors.separator, RoundedCornerShape(Radius.xl2))
.padding(1.dp),
) {
content()
}
}
@Composable
fun CalendarSingle(
size: CalendarSize = CalendarSize.Default,
captionLayout: CalendarCaptionLayout = CalendarCaptionLayout.Label,
showWeekNumber: Boolean = false,
showOutsideDays: Boolean = false,
weekendsDisabled: Boolean = false,
buttonVariant: ButtonVariant = ButtonVariant.Plain,
locale: Locale = Locale.getDefault(),
key: String = "",
) {
var date by rememberSaveable(key) { mutableStateOf<Date?>(calendarDay(offsetDays = 3)) }
CalendarFrame {
Calendar(
selected = date,
onSelect = { date = it },
size = size,
captionLayout = captionLayout,
showWeekNumber = showWeekNumber,
showOutsideDays = showOutsideDays,
disabled = { weekendsDisabled && calendarIsWeekend(it) },
buttonVariant = buttonVariant,
locale = locale,
)
}
}Button variants
Navigation buttons in the ghost, secondary and tinted variants.


import * as React from "react"
import { ScrollView } from "react-native"
import { Calendar } from "@/components/ui/calendar"
type CalendarProps = React.ComponentProps<typeof Calendar>
function SingleCalendar(
props: Omit<CalendarProps, "mode" | "selected" | "onSelect">
) {
const today = new Date()
const [date, setDate] = React.useState<Date | undefined>(
new Date(today.getFullYear(), today.getMonth(), today.getDate() + 3)
)
return (
<Calendar
{...props}
mode="single"
selected={date}
onSelect={setDate}
className="rounded-2xl border border-separator"
/>
)
}
export function CalendarButtonVariantsExample() {
return (
<ScrollView contentContainerClassName="items-start gap-6">
<SingleCalendar buttonVariant="ghost" />
<SingleCalendar buttonVariant="secondary" />
<SingleCalendar buttonVariant="tinted" />
</ScrollView>
)
}import EsuissUI
import SwiftUI
struct CalendarButtonVariantsExample: View {
var body: some View {
VStack(alignment: .leading, spacing: 24) {
CalendarSingleDemo(buttonVariant: .ghost)
CalendarSingleDemo(buttonVariant: .secondary)
CalendarSingleDemo(buttonVariant: .tinted)
}
}
}
enum CalendarDates {
static func day(_ offset: Int) -> Date {
let calendar = Foundation.Calendar.current
let start = calendar.startOfDay(for: Date())
return calendar.date(byAdding: .day, value: offset, to: start) ?? start
}
static func dayOfMonth(_ day: Int) -> Date {
let calendar = Foundation.Calendar.current
var components = calendar.dateComponents([.year, .month], from: Date())
components.day = day
return calendar.date(from: components) ?? Date()
}
}
struct CalendarBordered: ViewModifier {
func body(content: Content) -> some View {
content
.clipShape(RoundedRectangle(cornerRadius: Radius.xl2, style: .continuous))
.overlay {
RoundedRectangle(cornerRadius: Radius.xl2, style: .continuous).strokeBorder(Color.separator)
}
}
}
struct CalendarSingleDemo: View {
var size: DateCalendar.Size = .default
var buttonVariant: VariantButtonStyle.Variant = .plain
var captionLayout: DateCalendar.CaptionLayout = .label
var showOutsideDays = false
var showWeekNumber = false
var weekendsDisabled = false
@State private var date: Date? = CalendarDates.day(3)
var body: some View {
DateCalendar(
selection: $date,
size: size,
buttonVariant: buttonVariant,
captionLayout: captionLayout,
showOutsideDays: showOutsideDays,
showWeekNumber: showWeekNumber,
isDisabled: { day in
guard weekendsDisabled else { return false }
let weekday = Foundation.Calendar.current.component(.weekday, from: day)
return weekday == 1 || weekday == 7
}
)
.modifier(CalendarBordered())
}
}
func day(_ offset: Int) -> Date {
let calendar = Foundation.Calendar.current
let start = calendar.startOfDay(for: Date())
return calendar.date(byAdding: .day, value: offset, to: start) ?? start
}
func dayOfMonth(_ day: Int) -> Date {
let calendar = Foundation.Calendar.current
var components = calendar.dateComponents([.year, .month], from: Date())
components.day = day
return calendar.date(from: components) ?? Date()
}import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.esuiss.ui.components.ButtonVariant
import com.esuiss.ui.components.Calendar
import com.esuiss.ui.components.CalendarCaptionLayout
import com.esuiss.ui.components.CalendarSize
import com.esuiss.ui.theme.EsuissTheme
import com.esuiss.ui.theme.Radius
import java.util.Date
import java.util.GregorianCalendar
import java.util.Locale
fun calendarDay(offsetDays: Int = 0, dayOfMonth: Int? = null): Date {
val now = GregorianCalendar()
val result = GregorianCalendar(now.get(GregorianCalendar.YEAR), now.get(GregorianCalendar.MONTH), now.get(GregorianCalendar.DAY_OF_MONTH))
if (dayOfMonth != null) result.set(GregorianCalendar.DAY_OF_MONTH, dayOfMonth)
result.add(GregorianCalendar.DAY_OF_MONTH, offsetDays)
return result.time
}
fun calendarIsWeekend(date: Date): Boolean {
val day = GregorianCalendar().apply { time = date }.get(GregorianCalendar.DAY_OF_WEEK)
return day == GregorianCalendar.SATURDAY || day == GregorianCalendar.SUNDAY
}
@Composable
fun CalendarFrame(content: @Composable () -> Unit) {
Box(
Modifier
.border(1.dp, EsuissTheme.colors.separator, RoundedCornerShape(Radius.xl2))
.padding(1.dp),
) {
content()
}
}
@Composable
fun CalendarSingle(
size: CalendarSize = CalendarSize.Default,
captionLayout: CalendarCaptionLayout = CalendarCaptionLayout.Label,
showWeekNumber: Boolean = false,
showOutsideDays: Boolean = false,
weekendsDisabled: Boolean = false,
buttonVariant: ButtonVariant = ButtonVariant.Plain,
locale: Locale = Locale.getDefault(),
key: String = "",
) {
var date by rememberSaveable(key) { mutableStateOf<Date?>(calendarDay(offsetDays = 3)) }
CalendarFrame {
Calendar(
selected = date,
onSelect = { date = it },
size = size,
captionLayout = captionLayout,
showWeekNumber = showWeekNumber,
showOutsideDays = showOutsideDays,
disabled = { weekendsDisabled && calendarIsWeekend(it) },
buttonVariant = buttonVariant,
locale = locale,
)
}
}
@Composable
fun CalendarButtonVariants() {
Column(verticalArrangement = Arrangement.spacedBy(24.dp)) {
CalendarSingle(buttonVariant = ButtonVariant.Ghost, key = "ghost")
CalendarSingle(buttonVariant = ButtonVariant.Secondary, key = "secondary")
CalendarSingle(buttonVariant = ButtonVariant.Tinted, key = "tinted")
}
}Locale
Turkish month and weekday names with locale="tr-TR".


import * as React from "react"
import { Calendar } from "@/components/ui/calendar"
type CalendarProps = React.ComponentProps<typeof Calendar>
function SingleCalendar(
props: Omit<CalendarProps, "mode" | "selected" | "onSelect">
) {
const today = new Date()
const [date, setDate] = React.useState<Date | undefined>(
new Date(today.getFullYear(), today.getMonth(), today.getDate() + 3)
)
return (
<Calendar
{...props}
mode="single"
selected={date}
onSelect={setDate}
className="rounded-2xl border border-separator"
/>
)
}
export function CalendarLocaleExample() {
return (
<SingleCalendar locale="tr-TR" />
)
}import EsuissUI
import SwiftUI
struct CalendarLocaleExample: View {
var body: some View {
CalendarSingleDemo()
.environment(\.locale, Locale(identifier: "tr_TR"))
}
}
enum CalendarDates {
static func day(_ offset: Int) -> Date {
let calendar = Foundation.Calendar.current
let start = calendar.startOfDay(for: Date())
return calendar.date(byAdding: .day, value: offset, to: start) ?? start
}
static func dayOfMonth(_ day: Int) -> Date {
let calendar = Foundation.Calendar.current
var components = calendar.dateComponents([.year, .month], from: Date())
components.day = day
return calendar.date(from: components) ?? Date()
}
}
struct CalendarBordered: ViewModifier {
func body(content: Content) -> some View {
content
.clipShape(RoundedRectangle(cornerRadius: Radius.xl2, style: .continuous))
.overlay {
RoundedRectangle(cornerRadius: Radius.xl2, style: .continuous).strokeBorder(Color.separator)
}
}
}
struct CalendarSingleDemo: View {
var size: DateCalendar.Size = .default
var buttonVariant: VariantButtonStyle.Variant = .plain
var captionLayout: DateCalendar.CaptionLayout = .label
var showOutsideDays = false
var showWeekNumber = false
var weekendsDisabled = false
@State private var date: Date? = CalendarDates.day(3)
var body: some View {
DateCalendar(
selection: $date,
size: size,
buttonVariant: buttonVariant,
captionLayout: captionLayout,
showOutsideDays: showOutsideDays,
showWeekNumber: showWeekNumber,
isDisabled: { day in
guard weekendsDisabled else { return false }
let weekday = Foundation.Calendar.current.component(.weekday, from: day)
return weekday == 1 || weekday == 7
}
)
.modifier(CalendarBordered())
}
}
func day(_ offset: Int) -> Date {
let calendar = Foundation.Calendar.current
let start = calendar.startOfDay(for: Date())
return calendar.date(byAdding: .day, value: offset, to: start) ?? start
}
func dayOfMonth(_ day: Int) -> Date {
let calendar = Foundation.Calendar.current
var components = calendar.dateComponents([.year, .month], from: Date())
components.day = day
return calendar.date(from: components) ?? Date()
}import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.esuiss.ui.components.ButtonVariant
import com.esuiss.ui.components.Calendar
import com.esuiss.ui.components.CalendarCaptionLayout
import com.esuiss.ui.components.CalendarSize
import com.esuiss.ui.theme.EsuissTheme
import com.esuiss.ui.theme.Radius
import java.util.Date
import java.util.GregorianCalendar
import java.util.Locale
@Composable
fun CalendarLocaleExample() {
CalendarSingle(locale = Locale.forLanguageTag("tr-TR"))
}
fun calendarDay(offsetDays: Int = 0, dayOfMonth: Int? = null): Date {
val now = GregorianCalendar()
val result = GregorianCalendar(now.get(GregorianCalendar.YEAR), now.get(GregorianCalendar.MONTH), now.get(GregorianCalendar.DAY_OF_MONTH))
if (dayOfMonth != null) result.set(GregorianCalendar.DAY_OF_MONTH, dayOfMonth)
result.add(GregorianCalendar.DAY_OF_MONTH, offsetDays)
return result.time
}
fun calendarIsWeekend(date: Date): Boolean {
val day = GregorianCalendar().apply { time = date }.get(GregorianCalendar.DAY_OF_WEEK)
return day == GregorianCalendar.SATURDAY || day == GregorianCalendar.SUNDAY
}
@Composable
fun CalendarFrame(content: @Composable () -> Unit) {
Box(
Modifier
.border(1.dp, EsuissTheme.colors.separator, RoundedCornerShape(Radius.xl2))
.padding(1.dp),
) {
content()
}
}
@Composable
fun CalendarSingle(
size: CalendarSize = CalendarSize.Default,
captionLayout: CalendarCaptionLayout = CalendarCaptionLayout.Label,
showWeekNumber: Boolean = false,
showOutsideDays: Boolean = false,
weekendsDisabled: Boolean = false,
buttonVariant: ButtonVariant = ButtonVariant.Plain,
locale: Locale = Locale.getDefault(),
key: String = "",
) {
var date by rememberSaveable(key) { mutableStateOf<Date?>(calendarDay(offsetDays = 3)) }
CalendarFrame {
Calendar(
selected = date,
onSelect = { date = it },
size = size,
captionLayout = captionLayout,
showWeekNumber = showWeekNumber,
showOutsideDays = showOutsideDays,
disabled = { weekendsDisabled && calendarIsWeekend(it) },
buttonVariant = buttonVariant,
locale = locale,
)
}
}API Reference
CalendarDayButton
Pressable day cell styled for each selection state.
React.ComponentProps<typeof CalendarPrimitive.DayButton>Calendar
Month grids with caption and navigation.
React.ComponentProps<typeof CalendarPrimitive.Root>- About
- Button variant for the navigation buttons. Defaults to
plain. - Type
ButtonProps["variant"]- Default
- "plain"
- About
sm,defaultorlg.- Type
CalendarSize- Default
- "default"
- About
label,dropdown,dropdown-monthsordropdown-years.- Type
CaptionLayout- Default
- "label"
- About
- Shows week numbers.
- Type
boolean- Default
- false
Accessibility
- Each month is a
gridlabelled with its caption. - Day cells have the
buttonrole, a full date label, andselectedanddisabledstate. - The previous and next buttons are labelled "Go to the Previous Month" and "Go to the Next Month".
- Caption dropdowns are labelled "Choose the Month" and "Choose the Year".
Notes
- Navigation arrows swap in right-to-left layouts.
- Uses
bg-accent,bg-accent-surface,bg-surface-secondaryandtext-linktokens; light and dark follow the system appearance.