Chcesz dodać do formularza kalendarz, który będzie ładny, użyteczny i dostępny? Świetnie — trafiłeś we właściwe miejsce. W tym artykule w prosty, konwersacyjny sposób pokażę Ci kilka podejść: od najłatwiejszego (wbudowany input[type="date"]) po lekki, własny kalendarz w JavaScript. Dodatkowo dostaniesz praktyczny kod do wklejenia i podrasowania pod swoje potrzeby. Zaczynamy!
Dlaczego w ogóle warto mieć kalendarz w formularzu?
Kalendarz w formularzu to nie tylko ładny gadżet. To przede wszystkim:
- szybsze i bardziej pewne wprowadzanie dat przez użytkownika,
- mniejsza liczba błędów formatu (np.
2025-10-08zamiast08.10.25), - lepsze UX na urządzeniach mobilnych,
- możliwość zaimplementowania ograniczeń (np. daty minimalnej/maksymalnej),
- łatwiejsza walidacja po stronie klienta i serwera.
Jeśli chcesz, by użytkownik wybrał datę np. terminu wizyty, datę urodzenia czy datę rezerwacji — kalendarz znacząco podnosi konwersję i zmniejsza frustrację.
Szybkie i proste rozwiązanie: input type="date"
Jeśli zależy Ci na szybkości, kompatybilności i minimalnej ilości kodu — użyj wbudowanego pola daty:
<form> <label for="visit-date">Wybierz datę wizyty:</label> <input id="visit-date" name="visitDate" type="date" min="2025-01-01" max="2030-12-31" required> <button type="submit">Wyślij</button> </form>
Zalety:
- Natychmiast działa na przeglądarkach, które wspierają HTML5.
- Na telefonach pokazuje natywny picker (bardzo wygodne).
- Obsługuje atrybuty
min,max,stepi walidację HTML.
Wady:
- Wygląd i funkcje zależą od przeglądarki i systemu — nie masz pełnej kontroli nad UI.
- Nie wszystkie przeglądarki traktują
type="date"tak samo (np. starsze desktopowe).
Jeżeli to wystarczy — super. Jeśli chcesz pełnej kontroli nad wyglądem i dodatkowymi funkcjami (zakresy godzinowe, wielokrotne wybory, blokowanie dni tygodnia), czytaj dalej.
Kiedy warto zrobić własny kalendarz?
Zastanów się nad własnym rozwiązaniem, gdy chcesz:
- w pełni dopasować wygląd do designu strony,
- dodać logikę blokowania dni (np. weekendy, święta),
- umożliwić wybór zakresu dat (od — do),
- zintegrować kalendarz z innymi komponentami (godziny, serwisy zewnętrzne),
- obsłużyć niestandardowe formaty dat.
Poniżej znajdziesz przykładowy, lekki kalendarz napisany w HTML/CSS/JS. Ma podstawowe funkcje: pokazuje miesiąc, umożliwia przechodzenie między miesiącami, zaznaczanie daty i wypełnianie pola tekstowego w czytelnym formacie.
Przykład: lekki, własny kalendarz w JavaScript
Skopiuj i wklej poniższy kod do pliku index.html — to kompletny przykład działający bez zewnętrznych bibliotek.
<!doctype html>
<html lang="pl">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Formularz z kalendarzem</title>
<style>
body { font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial; padding: 20px; }
.field { margin-bottom: 12px; }
.calendar-wrapper { position: relative; display: inline-block; }
.calendar { position: absolute; top: 44px; left: 0; background: white; border: 1px solid #ddd; box-shadow: 0 6px 18px rgba(0,0,0,0.08); padding: 8px; z-index: 100; width: 260px; }
.cal-head { display:flex; justify-content:space-between; align-items:center; margin-bottom: 6px; }
.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
.cal-cell { padding: 6px; text-align:center; border-radius: 4px; cursor:pointer; user-select:none; }
.cal-cell:hover { background:#f0f0f0; }
.cal-cell.disabled { color:#bbb; pointer-events:none; background:transparent; }
.cal-cell.selected { background:#0b79ff; color:white; }
.weekday { font-weight:600; color:#666; font-size: 13px; }
button.icon { background:none;border:none;cursor:pointer;padding:6px;font-size:16px; }
</style>
</head>
<body>
<h1>Rezerwacja — wybierz datę</h1>
<form id="bookingForm">
<div class="field">
<label for="dateInput">Data:</label>
<div class="calendar-wrapper">
<input id="dateInput" name="date" type="text" placeholder="Kliknij, aby wybrać datę" readonly aria-haspopup="dialog" aria-expanded="false" />
<div id="calendar" class="calendar" hidden role="dialog" aria-label="Wybór daty">
<div class="cal-head">
<button type="button" id="prev" class="icon" aria-label="Poprzedni miesiąc"><</button>
<div id="monthLabel"></div>
<button type="button" id="next" class="icon" aria-label="Następny miesiąc">></button>
</div>
<div class="cal-grid" id="weekdays"></div>
<div class="cal-grid" id="days"></div>
</div>
</div>
</div>
<button type="submit">Zarezerwuj</button>
</form>
<script>
(function(){
// Konfiguracja: min i max daty
const minDate = new Date(2025, 0, 1); // 1 stycznia 2025
const maxDate = new Date(2030, 11, 31); // 31 grudnia 2030
const input = document.getElementById('dateInput');
const calendar = document.getElementById('calendar');
const monthLabel = document.getElementById('monthLabel');
const weekdaysEl = document.getElementById('weekdays');
const daysEl = document.getElementById('days');
const prevBtn = document.getElementById('prev');
const nextBtn = document.getElementById('next');
// Aktualny widoczny miesiąc
let viewDate = new Date();
viewDate.setDate(1);
// Funkcja formatująca datę (YYYY-MM-DD)
function formatDate(d){
const y = d.getFullYear();
const m = String(d.getMonth()+1).padStart(2,'0');
const day = String(d.getDate()).padStart(2,'0');
return `${y}-${m}-${day}`;
}
// Dni tygodnia (polskie skróty)
const weekdays = ['Pn','Wt','Śr','Cz','Pt','Sb','Nd'];
weekdays.forEach(w => {
const el = document.createElement('div');
el.className = 'weekday';
el.textContent = w;
weekdaysEl.appendChild(el);
});
function render(){
// Nagłówek miesiąca
const monthName = viewDate.toLocaleString('pl', { month: 'long', year: 'numeric' });
monthLabel.textContent = monthName;
// Kasujemy poprzednie dni
daysEl.innerHTML = '';
const year = viewDate.getFullYear();
const month = viewDate.getMonth();
// Dzień tygodnia pierwszego dnia (0=Sunday in JS) -> chcemy Monday-first
const firstDay = new Date(year, month, 1);
let startIndex = firstDay.getDay(); // 0 (niedziela) ... 6 (sobota)
// Przesunięcie: chcemy, żeby poniedziałek był pierwszy (index 0)
startIndex = (startIndex + 6) % 7;
// Ile dni w miesiącu?
const daysInMonth = new Date(year, month+1, 0).getDate();
// Dodaj puste komórki przed pierwszym dniem
for(let i=0;i<startIndex;i++){
const empty = document.createElement('div');
empty.className = 'cal-cell disabled';
daysEl.appendChild(empty);
}
for(let d=1; d<=daysInMonth; d++){
const cell = document.createElement('div');
const thisDate = new Date(year, month, d);
cell.className = 'cal-cell';
cell.tabIndex = 0;
cell.textContent = d;
// Sprawdź ograniczenia min/max
if(thisDate < minDate || thisDate > maxDate){
cell.classList.add('disabled');
} else {
cell.addEventListener('click', () => {
input.value = formatDate(thisDate);
closeCalendar();
});
// keyboard accessibility
cell.addEventListener('keydown', (e) => {
if(e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
input.value = formatDate(thisDate);
closeCalendar();
}
});
}
// Zaznacz jeśli to aktualnie wybrana data
if(input.value && input.value === formatDate(thisDate)) {
cell.classList.add('selected');
}
daysEl.appendChild(cell);
}
}
function openCalendar(){
calendar.hidden = false;
input.setAttribute('aria-expanded', 'true');
render();
}
function closeCalendar(){
calendar.hidden = true;
input.setAttribute('aria-expanded', 'false');
}
input.addEventListener('click', (e) => {
if(calendar.hidden) openCalendar();
else closeCalendar();
});
// Kliknięcie poza kalendarzem zamyka go
document.addEventListener('click', (e) => {
if(!calendar.contains(e.target) && e.target !== input){
closeCalendar();
}
});
prevBtn.addEventListener('click', () => {
viewDate.setMonth(viewDate.getMonth() - 1);
render();
});
nextBtn.addEventListener('click', () => {
viewDate.setMonth(viewDate.getMonth() + 1);
render();
});
// Zamknij na Esc
document.addEventListener('keydown', (e) => {
if(e.key === 'Escape') closeCalendar();
});
// Przy pierwszym załadowaniu ustaw widok na miesiąc zawierający minDate jeśli current poza zakresem
(function initView() {
const today = new Date();
if(today < minDate) viewDate = new Date(minDate.getFullYear(), minDate.getMonth(), 1);
else if(today > maxDate) viewDate = new Date(maxDate.getFullYear(), maxDate.getMonth(), 1);
else viewDate = new Date(today.getFullYear(), today.getMonth(), 1);
})();
})();
</script>
</body>
</html>
Co robi powyższy kod?
- Renderuje popupowy kalendarz z polami na dni i nagłówkiem miesiąca.
- Pozwala przechodzić między miesiącami.
- Blokuje daty poza zakresem
minDate/maxDate. - Wypełnia pole w formacie
YYYY-MM-DD. - Obsługuje klawiaturę (Enter / Space / Esc) i podstawową dostępność (aria).
To prosty, gotowy punkt startowy. Możesz rozbudować go o:
- wybór zakresu dat (start/end),
- zaznaczanie wykrywania świąt,
- integrację z backendem (np. sprawdzanie dostępności terminu),
- formatowanie w lokalnym stylu (np.
dd.mm.yyyy) — pamiętaj, by trzymać wartośćinput.valuew stabilnym formacie do walidacji.
Dodanie kalendarza do formularza może być proste (HTML5 input[type="date"]) lub bardziej zaawansowane (własny komponent JS). W większości przypadków zacznij od natywnego inputu — to szybko i bezpiecznie. Gdy potrzebujesz więcej kontroli nad wyglądem i logiką, stwórz własny kalendarz, korzystając z przedstawionego przykładu jako bazy.