# iOS Calendar

> Month grid, day selection and the day's agenda.

- Author: Atelier UI
- License: MIT
- Page: https://chromotion.dev/en/templates/ios-calendar
- Site: Atelier UI (https://chromotion.dev)

## Install

```bash
npx shadcn@latest add https://chromotion.dev/r/ios-calendar.json
```

Adds the page at `app/ios-calendar/page.tsx` together with the components below. Stack: React + TypeScript + Tailwind CSS v4, shadcn/ui conventions. `cn` comes from `@/lib/utils`.

## Components

- [iOS Kit](https://chromotion.dev/r/ios-kit.md): `@/components/ios-kit`

## Dependencies

```bash
npm install lucide-react
```

## Source

```tsx title="app/ios-calendar/page.tsx"
"use client"

import * as React from "react"
import { ChevronLeft, Inbox, Plus, Search } from "lucide-react"

import { cn } from "@/lib/utils"
import { IOSDevice, IOSNavButton } from "@/components/ios-kit"

// November 2026 starts on a Sunday; today is the 12th.
const FIRST_WEEKDAY = 0
const DAYS = 30
const TODAY = 12
const weekdays = ["S", "M", "T", "W", "T", "F", "S"]
const weekdayNames = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]

type Event = { time: string; end: string; title: string; place: string; color: string }

const events: Record<number, Event[]> = {
  3: [{ time: "9:00 AM", end: "10:00 AM", title: "Stand-up", place: "Zoom", color: "bg-sky-500" }],
  5: [{ time: "2:00 PM", end: "3:30 PM", title: "Dentist", place: "Market St", color: "bg-red-500" }],
  12: [
    { time: "10:00 AM", end: "11:00 AM", title: "Design review", place: "Room 4B", color: "bg-sky-500" },
    { time: "12:30 PM", end: "1:30 PM", title: "Lunch with Mia", place: "Nopa", color: "bg-orange-500" },
    { time: "7:00 PM", end: "9:00 PM", title: "Climbing gym", place: "Mission Cliffs", color: "bg-green-500" },
  ],
  14: [{ time: "All-day", end: "", title: "Sam's birthday", place: "", color: "bg-pink-500" }],
  19: [
    { time: "11:00 AM", end: "12:00 PM", title: "Quarterly planning", place: "HQ", color: "bg-purple-500" },
    { time: "4:00 PM", end: "5:00 PM", title: "1:1 with Jordan", place: "Zoom", color: "bg-sky-500" },
  ],
  26: [{ time: "All-day", end: "", title: "Thanksgiving", place: "", color: "bg-orange-500" }],
}

export default function IOSCalendar() {
  const [selected, setSelected] = React.useState(TODAY)
  const cells = [...Array(FIRST_WEEKDAY).fill(null), ...Array.from({ length: DAYS }, (_, i) => i + 1)]
  const weeks = Array.from({ length: Math.ceil(cells.length / 7) }, (_, i) => cells.slice(i * 7, i * 7 + 7))
  const list = events[selected] ?? []

  return (
    <IOSDevice>
      <header className="shrink-0 px-3 pt-[54px]">
        <div className="flex h-11 items-center justify-between">
          <IOSNavButton className="-ml-1">
            <ChevronLeft className="size-[26px]" strokeWidth={2.5} />
            <span className="-ml-0.5 text-[17px]">2026</span>
          </IOSNavButton>
          <div className="flex">
            <IOSNavButton aria-label="Search" className="justify-center"><Search className="size-[22px]" /></IOSNavButton>
            <IOSNavButton aria-label="Add event" className="justify-center"><Plus className="size-[26px]" /></IOSNavButton>
          </div>
        </div>
        <h1 className="px-1 text-[34px] leading-[41px] font-bold tracking-tight text-(color:--ios-red)">November</h1>
        <div className="mt-2 grid grid-cols-7 border-b border-(color:--ios-sep) pb-1.5 text-center text-[12px] font-semibold text-(color:--ios-label2)">
          {weekdays.map((day, i) => <span key={i}>{day}</span>)}
        </div>
      </header>

      <div role="grid" aria-label="November 2026" className="grid shrink-0 grid-cols-7 px-1 pt-1">
        {weeks.map((week, weekIndex) => (
          <div key={weekIndex} role="row" className="contents">
            {week.map((day, index) => {
              if (day === null) return <span key={`blank-${index}`} role="gridcell" aria-hidden />
              const isToday = day === TODAY
              const isSelected = day === selected
              return (
                <button
                  key={day}
                  type="button"
                  role="gridcell"
                  aria-selected={isSelected}
                  aria-label={`November ${day}`}
                  onClick={() => setSelected(day)}
                  className="flex h-[52px] flex-col items-center gap-0.5 pt-1.5"
                >
                  <span
                    className={cn(
                      "flex size-[34px] items-center justify-center rounded-full text-[19px] transition-colors",
                      isSelected && isToday && "bg-[#d70015] font-semibold text-white",
                      isSelected && !isToday && "bg-(color:--ios-label) font-semibold text-(color:--ios-bg)",
                      !isSelected && isToday && "font-semibold text-(color:--ios-red)"
                    )}
                  >
                    {day}
                  </span>
                  <span className={cn("size-[5px] rounded-full", events[day] ? "bg-(color:--ios-label3)" : "bg-transparent")} />
                </button>
              )
            })}
          </div>
        ))}
      </div>

      <section className="mt-1 min-h-0 flex-1 overflow-y-auto border-t border-(color:--ios-sep) px-4 pt-3 pb-[90px] [scrollbar-width:none]">
        <h2 className="mb-2 text-[13px] font-semibold text-(color:--ios-label2) uppercase">
          {weekdayNames[(selected - 1 + FIRST_WEEKDAY) % 7]}, November {selected}
        </h2>
        {list.length === 0 ? (
          <p className="pt-8 text-center text-[17px] text-(color:--ios-label3)">No Events</p>
        ) : (
          <ul className="flex flex-col gap-3">
            {list.map((event) => (
              <li key={event.title} className="flex gap-3">
                <span className="w-[68px] shrink-0 text-right text-[13px] leading-[18px] text-(color:--ios-label2)">
                  {event.time}
                  <br />
                  {event.end}
                </span>
                <span className={cn("w-1 shrink-0 rounded-full", event.color)} />
                <span className="min-w-0 rounded-[8px] py-0.5">
                  <span className="block truncate text-[17px] leading-[22px] font-medium">{event.title}</span>
                  {event.place && <span className="block truncate text-[15px] text-(color:--ios-label2)">{event.place}</span>}
                </span>
              </li>
            ))}
          </ul>
        )}
      </section>

      <div className="absolute inset-x-0 bottom-0 z-30 flex h-[83px] items-start justify-between border-t border-(color:--ios-sep) bg-(color:--ios-bg)/80 px-6 pt-1 backdrop-blur-xl">
        <IOSNavButton onClick={() => setSelected(TODAY)}>Today</IOSNavButton>
        <IOSNavButton>Calendars</IOSNavButton>
        <IOSNavButton aria-label="Inbox" className="justify-end"><Inbox className="size-[22px]" /></IOSNavButton>
      </div>
    </IOSDevice>
  )
}
```
