본문으로 건너뛰기
입력 / calendar

Calendar

단일 날짜 선택 그리드(월 단위) — 외부 dep 0, 순수 날짜 산술 + role="grid".
입력calendarStatic specimencalendar달력날짜date
01 · Specimen

먼저 보고, 그다음 계약을 읽습니다

단일 날짜 선택 그리드(월 단위) — 외부 dep 0, 순수 날짜 산술 + role="grid".

Workbench 불러오는 중…
Component intent의도와 경계 읽기

한 달을 네이티브 <button> 셀 그리드로 그려 날짜 하나(value: Date | null)를 고르는 controlled 컴포넌트. Radix·date 라이브러리 없이 순수 날짜 산술로 동작하고, role="grid"/"row"/"gridcell" + 셀별 aria-selected 로 접근성을 표현한다(서버가 같은 markup 을 계산해 넣으면 maud/jinja 소비자도 동일 모양). weekStartsOn 으로 주 시작 요일을, minDate/maxDate/isDateDisabled 로 선택 범위를 제한한다. 경계 — 단일 날짜 선택만 한다: 기간(range) 선택·시각(시·분) 선택·input 옆 팝오버 DatePicker 합성은 범위 밖(이 컴포넌트를 내부에 합성해 별도 제공).

02 · Use

예제

SSOT에 등록된 실제 API 기준 snippet입니다. standalone 배지만 독립 실행 단위이며, fragment는 주변 state·handler 문맥을 생략합니다.

01기본 (controlled)tsxfragment
기본 (controlled)
import { Calendar } from "@axe/ui";import { useState } from "react";function DueDatePicker() {  const [due, setDue] = useState<Date | null>(null);  return <Calendar value={due} onValueChange={setDue} />;}
02월 시작·범위 제한tsxfragment
월 시작·범위 제한
import { Calendar } from "@axe/ui";<Calendar  value={due}  onValueChange={setDue}  weekStartsOn={1}  minDate={new Date(2026, 0, 1)}  maxDate={new Date(2026, 11, 31)}  isDateDisabled={(d) => d.getDay() === 0}/>
03CSS-only 정적 스냅샷 (비-React)htmlfragment
CSS-only 정적 스냅샷 (비-React)
<div class="axe-calendar">  <div class="axe-calendar__header">    <button type="button" class="axe-calendar__nav" aria-label="이전 달">‹</button>    <div class="axe-calendar__title" aria-live="polite">2026년 5월</div>    <button type="button" class="axe-calendar__nav" aria-label="다음 달">›</button>  </div>  <div class="axe-calendar__grid" role="grid" aria-label="달력">    <div class="axe-calendar__weekdays" role="row">      <div class="axe-calendar__weekday" role="columnheader" aria-label="일">일</div>      <!-- 월~토 columnheader 반복 -->    </div>    <div class="axe-calendar__week" role="row">      <div class="axe-calendar__day axe-calendar__day--outside" role="gridcell" aria-disabled="true"></div>      <div role="gridcell" aria-selected="true">          <button type="button" class="axe-calendar__day axe-calendar__day--selected" aria-label="14일, 2026년 7월 화요일" tabindex="0">14</button>      </div>      <div role="gridcell" aria-selected="false">          <button type="button" class="axe-calendar__day axe-calendar__day--today" aria-current="date" tabindex="-1">15</button>      </div>      <!-- 나머지 날짜 gridcell 반복 -->    </div>  </div></div>
03 · React

Props

TSX 소스가 진실입니다. 주요 export 컴포넌트의 public API만 노출합니다.

이름타입필수기본값설명
valueDate | null필수선택된 날짜 또는 미선택(null) — controlled.
onValueChange(value: Date) => void필수날짜 셀 클릭 시 그 Date 로 호출.
weekStartsOnWeekday0주 시작 요일(0=일 … 6=토). 요일 헤더와 리드 빈칸 계산에 반영.
minDateDate선택 가능한 최소 날짜(포함). 이전 날짜 셀은 disabled.
maxDateDate선택 가능한 최대 날짜(포함). 이후 날짜 셀은 disabled.
isDateDisabled(date: Date) => boolean개별 날짜 비활성 술어(min/max 위에 추가 적용). 예: 일요일 비활성.
weekdayLabels[string, string, string, string, string, string, string]["일","월","화","수","목","금","토"]요일 라벨(일요일 기준 7개). weekStartsOn 만큼 회전되어 헤더에 표시.
formatMonth(year: number, month: number) => string(y, m) => `${y}년 ${m + 1}월`헤더 월 표제 포맷(month 는 0-based).
disabledbooleanfalse전체 비활성 — 모든 셀·내비 버튼 disabled + --disabled 클래스.
ariaLabelstring"달력"role="grid" 요소의 aria-label.
...restOmit<React.HTMLAttributes<HTMLDivElement>, "onChange" | "defaultValue">className 병합, 나머지는 루트 div 로 전파. ref 는 루트 div 로 forward.
04 · Any stack

.axe-* 클래스 계약

React 밖에서도 같은 표면을 그리는 공개 계약입니다. stable은 minor 버전 안에서 이름을 바꾸지 않습니다.

클래스안정성용도
.axe-calendarstable루트 div.
.axe-calendar--disabledstable전체 비활성 상태.
.axe-calendar__headerstable이전/제목/다음 3열 헤더.
.axe-calendar__titlestable월 표제(aria-live="polite").
.axe-calendar__navstable이전/다음 달 버튼.
.axe-calendar__gridstablerole="grid" 날짜 그리드 컨테이너.
.axe-calendar__weekdaysstable요일 헤더 row(role="row").
.axe-calendar__weekdaystable요일 셀(role="columnheader").
.axe-calendar__weekstable한 주 row(role="row", 7칸).
.axe-calendar__daystable날짜 셀 <button>.
.axe-calendar__day--outsidestable이달 밖 빈칸(격자 정렬용, aria-disabled).
.axe-calendar__day--selectedstable선택된 날짜(accent 채움).
.axe-calendar__day--todaystable오늘 표시(테두리/링).
비-React 소비 노트
스타일(.axe-calendar*)은 순수 CSS 고 aria 구조도 서버가 그대로 계산 가능하다 — 서버(maud/jinja)가 한 달치 셀·요일 헤더·aria-selected/aria-current 를 산출해 같은 markup 을 그리면 CSS-only 로 동일 재현된다. 단 달 이동(이전/다음)과 선택 반영은 JS 몫이라 정적 렌더는 한 달의 스냅샷이다.
05 · Inclusive

접근성

키보드, ARIA, 구현 노트를 함께 검토합니다.

Keyboard
  • ←/→는 하루, ↑/↓는 한 주 단위로 roving focus를 이동한다.
  • Home/End는 현재 주의 첫날/마지막 날로 이동한다.
  • PageUp/PageDown은 같은 날짜를 기준으로 이전/다음 달로 이동한다.
  • Enter/Space는 포커스된 날짜를 선택한다.
ARIA

그리드는 role="grid" + aria-label. 요일 헤더 줄 role="row" 안에 각 요일 role="columnheader"(aria-label). 각 주 role="row", 셀 래퍼 role="gridcell" + 선택 셀에 aria-selected. 날짜 <button>의 접근성 이름은 보이는 일자 숫자를 포함한 전체 연월일·요일이고, 오늘이면 aria-current="date". 이전/다음 버튼은 aria-label "이전 달"/"다음 달", 월 표제는 aria-live="polite" 로 이동 시 낭독. 이달 밖 빈칸은 aria-disabled="true".

Notes

날짜 버튼은 roving tabindex로 한 셀만 Tab 순서에 놓인다. 키보드 이동이 다른 달을 넘으면 월 표제와 그리드를 함께 갱신하고 새 날짜로 포커스를 복원한다. min/max/isDateDisabled/disabled로 막힌 날짜는 건너뛰며 native disabled도 유지한다. prefers-reduced-motion은 CSS에서 처리한다.

06 · Judgment

권장 · 지양

권장
  • value 를 부모 state 로 소유(controlled) — 미선택은 null.
  • 선택 가능 구간이 있으면 minDate/maxDate 로 명시(빠진 날짜는 native disabled).
  • 다른 로케일/주 시작이 필요하면 weekdayLabels·weekStartsOn·formatMonth 로 오버라이드.
지양
  • 기간(range)·시각 선택을 기대하지 말 것 — 단일 날짜 전용, 상위 DatePicker 로 합성.
  • value 에 시·분이 담긴 Date 를 넣어도 동치 비교는 날짜(자정)만 본다 — 시각은 무시됨.
검색창을 열면 컴포넌트 인덱스를 불러옵니다.