캘린더 뷰

Calendar view

한 달치 날짜를 격자로 펼쳐두고, 날짜마다 있는 일정을 점이나 라벨로 보여주는 화면.

다른 이름: Month viewEvent calendarDate grid
···
html
<div class="cvw">
  <div class="cvhead"><button aria-label="이전 달" tabindex="-1">‹</button><b>2026년 9월</b><button aria-label="다음 달" tabindex="-1">›</button></div>
  <div class="cvgrid" role="grid" aria-label="9월 일정" id="cvgrid">
    <span class="dow">일</span><span class="dow">월</span><span class="dow">화</span><span class="dow">수</span><span class="dow">목</span><span class="dow">금</span><span class="dow">토</span>
  </div>
</div>
css
.cvw{position:absolute;inset:10px;display:flex;flex-direction:column;gap:6px}
.cvhead{display:flex;align-items:center;justify-content:space-between;font-size:12px;font-weight:700;color:var(--fg)}
.cvhead button{border:0;background:none;color:var(--muted);font-size:15px;cursor:default;width:20px}
.cvgrid{display:grid;grid-template-columns:repeat(7,1fr);gap:3px;flex:1}
.dow{text-align:center;color:var(--muted);font-size:9px;padding:2px 0}
.ccell{position:relative;border:1px solid transparent;border-radius:7px;color:var(--fg);font-size:10px;display:flex;flex-direction:column;align-items:center;padding-top:2px;gap:2px}
.ccell.today{color:var(--accent);font-weight:700}
.ccell[data-focus]{border-color:var(--accent)}
.evt{width:4px;height:4px;border-radius:50%;background:var(--accent-2)}
js
const grid = document.getElementById('cvgrid');
const FIRST_DOW = 2, DAYS = 30, EVENTS = [3, 10, 10, 18, 26];
for (let i = 0; i < FIRST_DOW; i++) grid.appendChild(document.createElement('span'));
const cells = [];
for (let d = 1; d <= DAYS; d++) {
  const c = document.createElement('div');
  c.className = 'ccell'; c.setAttribute('role', 'gridcell'); c.tabIndex = -1;
  if (d === 26) c.classList.add('today');
  const n = document.createElement('span');
  n.textContent = String(d);
  c.appendChild(n);
  const dots = EVENTS.filter((e) => e === d).length;
  for (let k = 0; k < dots; k++) { const e = document.createElement('i'); e.className = 'evt'; c.appendChild(e); }
  grid.appendChild(c);
  cells.push(c);
}
let i = 25;
cells[i].tabIndex = 0;
cells[i].setAttribute('data-focus', '');
setInterval(() => {
  cells[i].removeAttribute('data-focus'); cells[i].tabIndex = -1;
  i = (i + 1) % cells.length;
  cells[i].setAttribute('data-focus', ''); cells[i].tabIndex = 0;
}, 700);

날짜 칸들은 role="grid" 안의 role="gridcell"로 구성하고, 한 번에 하나의 칸만 tabindex="0"을 갖는 로빙 탭인덱스로 방향키 이동을 지원합니다 — 좌우는 하루씩, 상하는 7일(한 주)씩 이동하고 PageUp/PageDown으로 달을 넘깁니다.

데이트 피커(이 사전의 다른 항목)와 같은 그리드 구조를 공유하지만 목적이 다릅니다. 데이트 피커는 폼 입력 하나를 채우기 위해 잠깐 열리는 팝업이라 날짜 하나를 고르면 바로 닫히지만, 캘린더 뷰는 그 자체가 화면의 주인공으로 계속 펼쳐져 있고 하루에 이벤트가 여러 개 쌓일 수도 있습니다(점 여러 개, 혹은 "+3개 더").

오늘 날짜는 반드시 색이 아닌 형태(테두리, 굵기)로도 구분해야 하고, 이벤트가 있는 날은 점 색깔만으로 종류를 구분하기보다 hover나 클릭 시 텍스트로 내용을 보여줘야 색맹 사용자도 무엇이 있는지 알 수 있습니다.