Date picker

날짜 선택기

A form control that opens a calendar popup on click and fills the field with the date you pick, then closes.

Also known as: DatepickerDate inputCalendar picker
···
html
<div class="dpw">
  <span class="dpf">약속일</span>
  <button class="dpin" id="dpin" aria-haspopup="dialog" aria-expanded="true">2026년 9월 26일 ▾</button>
  <div class="dpop" role="dialog" aria-label="날짜 선택">
    <div class="dphead"><button aria-label="이전 달" tabindex="-1">‹</button><b>2026년 9월</b><button aria-label="다음 달" tabindex="-1">›</button></div>
    <div class="dpgrid" role="grid" aria-label="9월" id="dpgrid">
      <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>
</div>
css
.dpw{width:min(230px,92%);display:flex;flex-direction:column;gap:6px;font-size:11px}
.dpf{color:var(--muted);font-size:10px;margin-left:4px}
.dpin{width:100%;text-align:left;padding:7px 10px;border:1px solid var(--line);border-radius:8px;background:var(--surface);color:var(--fg);font-size:12px;font-weight:600;cursor:default}
.dpop{width:100%;background:var(--surface);border:1px solid var(--line);border-radius:10px;padding:8px;display:flex;flex-direction:column;gap:6px}
.dphead{display:flex;align-items:center;justify-content:space-between;font-size:11px;font-weight:700;color:var(--fg)}
.dphead button{border:0;background:none;color:var(--muted);font-size:14px;cursor:default;width:20px}
.dpgrid{display:grid;grid-template-columns:repeat(7,1fr);gap:2px}
.dow{text-align:center;color:var(--muted);font-size:9px;padding:2px 0}
.dcell{border:0;background:none;color:var(--fg);font-size:10px;border-radius:6px;cursor:default;aspect-ratio:1;display:grid;place-items:center}
.dcell.today{color:var(--accent);font-weight:700}
.dcell[aria-selected=true]{background:var(--accent);color:#fff;font-weight:700}
js
const grid = document.getElementById('dpgrid');
const label = document.getElementById('dpin');
const FIRST_DOW = 2, DAYS = 30;
const cells = [];
for (let i = 0; i < FIRST_DOW; i++) grid.appendChild(document.createElement('span'));
for (let d = 1; d <= DAYS; d++) {
  const b = document.createElement('button');
  b.className = 'dcell'; b.type = 'button'; b.setAttribute('role', 'gridcell'); b.tabIndex = -1;
  b.textContent = String(d);
  if (d === 26) b.classList.add('today');
  grid.appendChild(b);
  cells.push(b);
}
function select(d) {
  cells.forEach((c, i) => { if (i + 1 === d) c.setAttribute('aria-selected', 'true'); else c.removeAttribute('aria-selected'); });
  label.textContent = '2026년 9월 ' + d + '일 ▾';
}
let auto = true, sel = 26, dir = 1;
select(sel);
cells.forEach((c, i) => c.addEventListener('click', () => { auto = false; select(i + 1); }));
setInterval(() => {
  if (!auto) return;
  sel += dir;
  if (sel >= 30) dir = -1;
  if (sel <= 1) dir = 1;
  select(sel);
}, 900);

The trigger carries aria-haspopup="dialog" and aria-expanded, while the calendar grid inside uses role="grid" with each date button as a role="gridcell". Arrow keys conventionally move by one day (up/down by seven), and PageUp/PageDown jump a month back or forward.

It's easy to confuse with a calendar view, but the purpose differs. A calendar view is a persistent screen for browsing a whole month of events, while a date picker is a transient popup that opens just long enough to fill one form field. Picking a value should close it immediately — there's no need to scan several dates at once.

Focus should land on the already-selected date (or today, if none) when the popup opens, and return to the input when it closes. Esc closes it without changing the value.