Datepicker

Datepicker

Turn on a screen reader to experience this example in action.

HTML Source Code

<div id="myDatepicker" class="datepicker">
    <div class="date">
      <label for="id-textbox-1">Date</label>
  
      <div class="group">
        <input type="text" placeholder="mm/dd/yyyy" id="id-textbox-1" aria-label="Enter or Choose date">
        <!--span class="desc" id="id-description-1">(<span class="sr-only">date format: </span>mm/dd/yyyy)</span-->
        <button type="button" class="icon red" aria-label="Choose Date">
          <span class="fa fa-calendar-alt fa-2x"></span>
        </button>
      </div>
    </div>
  
    <div id="id-datepicker-1" class="datepicker-dialog" role="dialog" aria-modal="true" aria-label="Choose Date">
      <div class="header">
        <button type="button" class="prev-year" aria-label="previous year">
          <span class="fas fa-angle-double-left fa-lg"></span>
        </button>
  
        <button type="button" class="prev-month" aria-label="previous month">
          <span class="fas fa-angle-left fa-lg"></span>
        </button>
  
        <h2 id="id-grid-label" class="month-year" aria-live="polite">February 2020</h2>
  
        <button type="button" class="next-month" aria-label="next month">
          <span class="fas fa-angle-right fa-lg"></span>
        </button>
  
        <button type="button" class="next-year" aria-label="next year">
          <span class="fas fa-angle-double-right fa-lg"></span>
        </button>
      </div>
  
      <div class="table-wrap"><table class="dates" role="grid" aria-labelledby="id-grid-label">
        <thead>
          <tr>
            <th scope="col" abbr="Sunday">Su</th>
            <th scope="col" abbr="Monday">Mo</th>
            <th scope="col" abbr="Tuesday">Tu</th>
            <th scope="col" abbr="Wednesday">We</th>
            <th scope="col" abbr="Thursday">Th</th>
            <th scope="col" abbr="Friday">Fr</th>
            <th scope="col" abbr="Saturday">Sa</th>
          </tr>
        </thead>
  
        <tbody>
          <tr>
            <td class="disabled" tabindex="-1"></td>
            <td class="disabled" tabindex="-1"></td>
            <td class="disabled" tabindex="-1"></td>
            <td class="disabled" tabindex="-1"></td>
            <td class="disabled" tabindex="-1"></td>
            <td class="disabled" tabindex="-1"></td>
            <td tabindex="-1" data-date="2020-02-01">1</td>
          </tr>
          <tr>
            <td tabindex="-1" data-date="2020-02-02">2</td>
            <td tabindex="-1" data-date="2020-02-03">3</td>
            <td tabindex="-1" data-date="2020-02-04">4</td>
            <td tabindex="-1" data-date="2020-02-05">5</td>
            <td tabindex="-1" data-date="2020-02-06">6</td>
            <td tabindex="-1" data-date="2020-02-07">7</td>
            <td tabindex="-1" data-date="2020-02-08">8</td>
          </tr>
          <tr>
            <td tabindex="-1" data-date="2020-02-09">9</td>
            <td tabindex="-1" data-date="2020-02-10">10</td>
            <td tabindex="-1" data-date="2020-02-11">11</td>
            <td tabindex="-1" data-date="2020-02-12">12</td>
            <td tabindex="-1" data-date="2020-02-13">13</td>
            <td tabindex="0" data-date="2020-02-14" role="gridcell" aria-selected="true">14</td>
            <td tabindex="-1" data-date="2020-02-15">15</td>
          </tr>
          <tr>
            <td tabindex="-1" data-date="2020-02-16">16</td>
            <td tabindex="-1" data-date="2020-02-17">17</td>
            <td tabindex="-1" data-date="2020-02-18">18</td>
            <td tabindex="-1" data-date="2020-02-19">19</td>
            <td tabindex="-1" data-date="2020-02-20">20</td>
            <td tabindex="-1" data-date="2020-02-21">21</td>
            <td tabindex="-1" data-date="2020-02-22">22</td>
          </tr>
          <tr>
            <td tabindex="-1" data-date="2020-02-23">23</td>
            <td tabindex="-1" data-date="2020-02-24">24</td>
            <td tabindex="-1" data-date="2020-02-25">25</td>
            <td tabindex="-1" data-date="2020-02-26">26</td>
            <td tabindex="-1" data-date="2020-02-27">27</td>
            <td tabindex="-1" data-date="2020-02-28">28</td>
            <td tabindex="-1" data-date="2020-02-29">29</td>
          </tr>
          <tr>
            <td tabindex="-1" data-date="2020-02-30">30</td>
            <td tabindex="-1" data-date="2020-02-31">31</td>
            <td class="disabled" tabindex="-1"></td>
            <td class="disabled" tabindex="-1"></td>
            <td class="disabled" tabindex="-1"></td>
            <td class="disabled" tabindex="-1"></td>
            <td class="disabled" tabindex="-1"></td>
          </tr>
        </tbody>
      </table></div>
  
      <div class="dialog-ok-cancel-group">
        <button class="dialog-button" value="cancel" id="btnCancel" >Cancel</button>
        <button class="dialog-button" value="ok">OK</button>
      </div>
      <div class="dialog-message" aria-live="polite"></div>
    </div>
  </div>
  <!---
This component has been adapted from an example provided by the W3C, in accordance with the W3C Software and Document License https://www.w3.org/copyright/software-license-2023/
-->

          

JavaScript Source Code

 //Language object to be modified as per the language
 var langText = {
    "chooseDate" : "Choose Date",
    "changeDate" : "Change Date",
    "messageCursorKeys": "Cursor keys can navigate dates",
    "dayLabels":[
        'Sunday',
        'Monday',
        'Tuesday',
        'Wednesday',
        'Thursday',
        'Friday',
        'Saturday',
    ],
    "monthLabels": [
        'January',
        'February',
        'March',
        'April',
        'May',
        'June',
        'July',
        'August',
        'September',
        'October',
        'November',
        'December',
    ],
       
}

class DatePickerDialog {
constructor(cdp) {
this.buttonLabelChoose = langText["chooseDate"];
this.buttonLabelChange = langText["changeDate"];
this.dayLabels = langText["dayLabels"];
this.monthLabels = langText["monthLabels"];

this.messageCursorKeys = langText["messageCursorKeys"];
this.lastMessage = '';

this.textboxNode = cdp.querySelector('input[type="text"');
this.buttonNode = cdp.querySelector('.group button');
this.dialogNode = cdp.querySelector('[role="dialog"]');
this.messageNode = this.dialogNode.querySelector('.dialog-message');

this.monthYearNode = this.dialogNode.querySelector('.month-year');

this.prevYearNode = this.dialogNode.querySelector('.prev-year');
this.prevMonthNode = this.dialogNode.querySelector('.prev-month');
this.nextMonthNode = this.dialogNode.querySelector('.next-month');
this.nextYearNode = this.dialogNode.querySelector('.next-year');

this.okButtonNode = this.dialogNode.querySelector('button[value="ok"]');
this.cancelButtonNode = this.dialogNode.querySelector("#btnCancel");

this.tbodyNode = this.dialogNode.querySelector('table.dates tbody');

this.lastRowNode = null;

this.days = [];

this.focusDay = new Date();
this.selectedDay = new Date(0, 0, 1);

this.lastDate = -1;

this.isMouseDownOnBackground = false;

this.textboxNode.addEventListener(
  'blur',
  this.setDateForButtonLabel.bind(this)
);

this.buttonNode.addEventListener(
  'keydown',
  this.handleButtonKeydown.bind(this)
);
this.buttonNode.addEventListener(
  'click',
  this.handleButtonClick.bind(this)
);

this.okButtonNode.addEventListener('click', this.handleOkButton.bind(this));
this.okButtonNode.addEventListener(
  'keydown',
  this.handleOkButton.bind(this)
);

this.cancelButtonNode.addEventListener(
  'click',
  this.handleCancelButton.bind(this)
);
this.cancelButtonNode.addEventListener(
  'keydown',
  this.handleCancelButton.bind(this)
);

this.prevMonthNode.addEventListener(
  'click',
  this.handlePreviousMonthButton.bind(this)
);
this.nextMonthNode.addEventListener(
  'click',
  this.handleNextMonthButton.bind(this)
);
this.prevYearNode.addEventListener(
  'click',
  this.handlePreviousYearButton.bind(this)
);
this.nextYearNode.addEventListener(
  'click',
  this.handleNextYearButton.bind(this)
);

this.prevMonthNode.addEventListener(
  'keydown',
  this.handlePreviousMonthButton.bind(this)
);
this.nextMonthNode.addEventListener(
  'keydown',
  this.handleNextMonthButton.bind(this)
);
this.prevYearNode.addEventListener(
  'keydown',
  this.handlePreviousYearButton.bind(this)
);
this.nextYearNode.addEventListener(
  'keydown',
  this.handleNextYearButton.bind(this)
);

document.body.addEventListener(
  'pointerup',
  this.handleBackgroundMouseUp.bind(this),
  true
);

// Create Grid of Dates

this.tbodyNode.innerHTML = '';
for (let i = 0; i < 6; i++) {
  const row = this.tbodyNode.insertRow(i);
  this.lastRowNode = row;
  for (let j = 0; j < 7; j++) {
    const cell = document.createElement('td');

    cell.tabIndex = -1;
    cell.addEventListener('click', this.handleDayClick.bind(this));
    cell.addEventListener('keydown', this.handleDayKeyDown.bind(this));
    cell.addEventListener('focus', this.handleDayFocus.bind(this));

    cell.textContent = '-1';

    row.appendChild(cell);
    this.days.push(cell);
  }
}

this.updateGrid();
this.close(false);
this.setDateForButtonLabel();
}

isSameDay(day1, day2) {
return (
  day1.getFullYear() == day2.getFullYear() &&
  day1.getMonth() == day2.getMonth() &&
  day1.getDate() == day2.getDate()
);
}

isNotSameMonth(day1, day2) {
return (
  day1.getFullYear() != day2.getFullYear() ||
  day1.getMonth() != day2.getMonth()
);
}

updateGrid() {
const fd = this.focusDay;

this.monthYearNode.textContent =
  this.monthLabels[fd.getMonth()] + ' ' + fd.getFullYear();

let firstDayOfMonth = new Date(fd.getFullYear(), fd.getMonth(), 1);
let dayOfWeek = firstDayOfMonth.getDay();

firstDayOfMonth.setDate(firstDayOfMonth.getDate() - dayOfWeek);

const d = new Date(firstDayOfMonth);

for (let i = 0; i < this.days.length; i++) {
  const flag = d.getMonth() != fd.getMonth();
  this.updateDate(
    this.days[i],
    flag,
    d,
    this.isSameDay(d, this.selectedDay)
  );
  d.setDate(d.getDate() + 1);

  // Hide last row if all dates are disabled (e.g. in next month)
  if (i === 35) {
    if (flag) {
      this.lastRowNode.style.display = 'none';
    } else {
      this.lastRowNode.style.display = 'table-row';
    
    }
  }
}
}

updateDate(domNode, disable, day, selected) {
let d = day.getDate().toString();
if (day.getDate() <= 9) {
  d = '0' + d;
}

let m = day.getMonth() + 1;
if (day.getMonth() < 9) {
  m = '0' + m;
}

domNode.tabIndex = -1;
domNode.removeAttribute('aria-selected');
domNode.setAttribute('data-date', day.getFullYear() + '-' + m + '-' + d);

if (disable) {
  domNode.classList.add('disabled');
  domNode.textContent = '';
} else {
  domNode.classList.remove('disabled');
  domNode.textContent = day.getDate();
  if (selected) {
    domNode.setAttribute('aria-selected', 'true');
    domNode.tabIndex = 0;
  }
}
}

moveFocusToDay(day) {
const d = this.focusDay;

this.focusDay = day;

if (
  d.getMonth() != this.focusDay.getMonth() ||
  d.getFullYear() != this.focusDay.getFullYear()
) {
  this.updateGrid();
}
this.setFocusDay();
}

setFocusDay(flag) {
if (typeof flag !== 'boolean') {
  flag = true;
}

for (let i = 0; i < this.days.length; i++) {
  const dayNode = this.days[i];
  const day = this.getDayFromDataDateAttribute(dayNode);

  dayNode.tabIndex = -1;
  if (this.isSameDay(day, this.focusDay)) {
    dayNode.tabIndex = 0;
    if (flag) {
      dayNode.focus();
    }
  }
}
}

open() {
this.dialogNode.style.display = 'block';
this.dialogNode.style.zIndex = 2;

this.getDateFromTextbox();
this.updateGrid();
this.lastDate = this.focusDay.getDate();
}

isOpen() {
return window.getComputedStyle(this.dialogNode).display !== 'none';
}

close(flag) {
if (typeof flag !== 'boolean') {
  // Default is to move focus to combobox
  flag = true;
}

this.setMessage('');
this.dialogNode.style.display = 'none';

if (flag) {
  this.buttonNode.focus();
}
}

changeMonth(currentDate, numMonths) {
const getDays = (year, month) => new Date(year, month, 0).getDate();

const isPrev = numMonths < 0;
const numYears = Math.trunc(Math.abs(numMonths) / 12);
numMonths = Math.abs(numMonths) % 12;

const newYear = isPrev
  ? currentDate.getFullYear() - numYears
  : currentDate.getFullYear() + numYears;

const newMonth = isPrev
  ? currentDate.getMonth() - numMonths
  : currentDate.getMonth() + numMonths;

const newDate = new Date(newYear, newMonth, 1);

const daysInMonth = getDays(newDate.getFullYear(), newDate.getMonth() + 1);

// If lastDat is not initialized set to current date
this.lastDate = this.lastDate ? this.lastDate : currentDate.getDate();

if (this.lastDate > daysInMonth) {
  newDate.setDate(daysInMonth);
} else {
  newDate.setDate(this.lastDate);
}

return newDate;
}

moveToNextYear() {
this.focusDay = this.changeMonth(this.focusDay, 12);
this.updateGrid();
}

moveToPreviousYear() {
this.focusDay = this.changeMonth(this.focusDay, -12);
this.updateGrid();
}

moveToNextMonth() {
this.focusDay = this.changeMonth(this.focusDay, 1);
this.updateGrid();
}

moveToPreviousMonth() {
this.focusDay = this.changeMonth(this.focusDay, -1);
this.updateGrid();
}

moveFocusToNextDay() {
const d = new Date(this.focusDay);
d.setDate(d.getDate() + 1);
this.lastDate = d.getDate();
this.moveFocusToDay(d);
}

moveFocusToNextWeek() {
const d = new Date(this.focusDay);
d.setDate(d.getDate() + 7);
this.lastDate = d.getDate();
this.moveFocusToDay(d);
}

moveFocusToPreviousDay() {
const d = new Date(this.focusDay);
d.setDate(d.getDate() - 1);
this.lastDate = d.getDate();
this.moveFocusToDay(d);
}

moveFocusToPreviousWeek() {
const d = new Date(this.focusDay);
d.setDate(d.getDate() - 7);
this.lastDate = d.getDate();
this.moveFocusToDay(d);
}

moveFocusToFirstDayOfWeek() {
const d = new Date(this.focusDay);
d.setDate(d.getDate() - d.getDay());
this.lastDate = d.getDate();
this.moveFocusToDay(d);
}

moveFocusToLastDayOfWeek() {
const d = new Date(this.focusDay);
d.setDate(d.getDate() + (6 - d.getDay()));
this.lastDate = d.getDate();
this.moveFocusToDay(d);
}

// Day methods

isDayDisabled(domNode) {
return domNode.classList.contains('disabled');
}

getDayFromDataDateAttribute(domNode) {
const parts = domNode.getAttribute('data-date').split('-');
return new Date(parts[0], parseInt(parts[1]) - 1, parts[2]);
}

// Textbox methods

setTextboxDate(domNode) {
let d = this.focusDay;

if (domNode) {
  d = this.getDayFromDataDateAttribute(domNode);
  // updated aria-selected
  this.days.forEach((day) =>
    day === domNode
      ? day.setAttribute('aria-selected', 'true')
      : day.removeAttribute('aria-selected')
  );
}

this.textboxNode.value =
  d.getMonth() + 1 + '/' + d.getDate() + '/' + d.getFullYear();
this.setDateForButtonLabel();
}

getDateFromTextbox() {
const parts = this.textboxNode.value.split('/');
const month = parseInt(parts[0]);
const day = parseInt(parts[1]);
let year = parseInt(parts[2]);

if (
  parts.length === 3 &&
  Number.isInteger(month) &&
  Number.isInteger(day) &&
  Number.isInteger(year)
) {
  if (year < 100) {
    year = 2000 + year;
  }
  this.focusDay = new Date(year, month - 1, day);
  this.selectedDay = new Date(this.focusDay);
} else {
  // If not a valid date (MM/DD/YY) initialize with todays date
  this.focusDay = new Date();
  this.selectedDay = new Date(0, 0, 1);
}
}

setDateForButtonLabel() {
const parts = this.textboxNode.value.split('/');

if (
  parts.length === 3 &&
  Number.isInteger(parseInt(parts[0])) &&
  Number.isInteger(parseInt(parts[1])) &&
  Number.isInteger(parseInt(parts[2]))
) {
  const day = new Date(
    parseInt(parts[2]),
    parseInt(parts[0]) - 1,
    parseInt(parts[1])
  );

  let label = this.buttonLabelChange;
  label += ', ' + this.dayLabels[day.getDay()];
  label += ' ' + this.monthLabels[day.getMonth()];
  label += ' ' + day.getDate();
  label += ', ' + day.getFullYear();
  this.buttonNode.setAttribute('aria-label', label);
} else {
  // If not a valid date, initialize with "Choose Date"
  this.buttonNode.setAttribute('aria-label', this.buttonLabelChoose);
}
}

setMessage(str) {
function setMessageDelayed() {
  this.messageNode.textContent = str;
}

if (str !== this.lastMessage) {
  setTimeout(setMessageDelayed.bind(this), 200);
  this.lastMessage = str;
}
}

// Event handlers

handleOkButton(event) {
let flag = false;

switch (event.type) {
  case 'keydown':
    switch (event.key) {
      case 'Tab':
        if (!event.shiftKey) {
          this.prevYearNode.focus();
          flag = true;
        }
        break;

      case 'Esc':
      case 'Escape':
        this.close();
        flag = true;
        break;

      default:
        break;
    }
    break;

  case 'click':
    this.setTextboxDate();
    this.close();
    flag = true;
    break;

  default:
    break;
}

if (flag) {
  event.stopPropagation();
  event.preventDefault();
}
}

handleCancelButton(event) {
let flag = false;

switch (event.type) {
  case 'keydown':
    switch (event.key) {
      case 'Esc':
      case 'Escape':
        this.close();
        flag = true;
        break;

      default:
        break;
    }
    break;

  case 'click':
    this.close();
    flag = true;
    break;

  default:
    break;
}

if (flag) {
  event.stopPropagation();
  event.preventDefault();
}
}

handleNextYearButton(event) {
let flag = false;

switch (event.type) {
  case 'keydown':
    switch (event.key) {
      case 'Esc':
      case 'Escape':
        this.close();
        flag = true;
        break;

      case 'Enter':
        this.moveToNextYear();
        this.setFocusDay(false);
        flag = true;
        break;
    }

    break;

  case 'click':
    this.moveToNextYear();
    this.setFocusDay(false);
    break;

  default:
    break;
}

if (flag) {
  event.stopPropagation();
  event.preventDefault();
}
}

handlePreviousYearButton(event) {
let flag = false;

switch (event.type) {
  case 'keydown':
    switch (event.key) {
      case 'Enter':
        this.moveToPreviousYear();
        this.setFocusDay(false);
        flag = true;
        break;

      case 'Tab':
        if (event.shiftKey) {
          this.okButtonNode.focus();
          flag = true;
        }
        break;

      case 'Esc':
      case 'Escape':
        this.close();
        flag = true;
        break;

      default:
        break;
    }

    break;

  case 'click':
    this.moveToPreviousYear();
    this.setFocusDay(false);
    break;

  default:
    break;
}

if (flag) {
  event.stopPropagation();
  event.preventDefault();
}
}

handleNextMonthButton(event) {
let flag = false;

switch (event.type) {
  case 'keydown':
    switch (event.key) {
      case 'Esc':
      case 'Escape':
        this.close();
        flag = true;
        break;

      case 'Enter':
        this.moveToNextMonth();
        this.setFocusDay(false);
        flag = true;
        break;
    }

    break;

  case 'click':
    this.moveToNextMonth();
    this.setFocusDay(false);
    break;

  default:
    break;
}

if (flag) {
  event.stopPropagation();
  event.preventDefault();
}
}

handlePreviousMonthButton(event) {
let flag = false;

switch (event.type) {
  case 'keydown':
    switch (event.key) {
      case 'Esc':
      case 'Escape':
        this.close();
        flag = true;
        break;

      case 'Enter':
        this.moveToPreviousMonth();
        this.setFocusDay(false);
        flag = true;
        break;
    }

    break;

  case 'click':
    this.moveToPreviousMonth();
    this.setFocusDay(false);
    flag = true;
    break;

  default:
    break;
}

if (flag) {
  event.stopPropagation();
  event.preventDefault();
}
}

handleDayKeyDown(event) {
let flag = false;

switch (event.key) {
  case 'Esc':
  case 'Escape':
    this.close();
    break;

  case ' ':
    this.setTextboxDate(event.currentTarget);
    flag = true;
    break;

  case 'Enter':
    this.setTextboxDate(event.currentTarget);
    this.close();
    flag = true;
    break;

  case 'Tab':
    this.cancelButtonNode.focus();
    if (event.shiftKey) {
      this.nextYearNode.focus();
    }
    this.setMessage('');
    flag = true;
    break;

  case 'Right':
  case 'ArrowRight':
    this.moveFocusToNextDay();
    flag = true;
    break;

  case 'Left':
  case 'ArrowLeft':
    this.moveFocusToPreviousDay();
    flag = true;
    break;

  case 'Down':
  case 'ArrowDown':
    this.moveFocusToNextWeek();
    flag = true;
    break;

  case 'Up':
  case 'ArrowUp':
    this.moveFocusToPreviousWeek();
    flag = true;
    break;

  case 'PageUp':
    if (event.shiftKey) {
      this.moveToPreviousYear();
    } else {
      this.moveToPreviousMonth();
    }
    this.setFocusDay();
    flag = true;
    break;

  case 'PageDown':
    if (event.shiftKey) {
      this.moveToNextYear();
    } else {
      this.moveToNextMonth();
    }
    this.setFocusDay();
    flag = true;
    break;

  case 'Home':
    this.moveFocusToFirstDayOfWeek();
    flag = true;
    break;

  case 'End':
    this.moveFocusToLastDayOfWeek();
    flag = true;
    break;
}

if (flag) {
  event.stopPropagation();
  event.preventDefault();
}
}

handleDayClick(event) {
if (!this.isDayDisabled(event.currentTarget) && event.which !== 3) {
  this.setTextboxDate(event.currentTarget);
  this.close();
}

event.stopPropagation();
event.preventDefault();
}

handleDayFocus() {
this.setMessage(this.messageCursorKeys);
}

handleButtonKeydown(event) {
if (event.key === 'Enter' || event.key === ' ') {
  this.open();
  this.setFocusDay();

  event.stopPropagation();
  event.preventDefault();
}
}

handleButtonClick(event) {
if (this.isOpen()) {
  this.close();
} else {
  this.open();
  this.setFocusDay();
}

event.stopPropagation();
event.preventDefault();
}

handleBackgroundMouseUp(event) {
if (
  !this.buttonNode.contains(event.target) &&
  !this.dialogNode.contains(event.target)
) {
  if (this.isOpen()) {
    this.close(false);
    event.stopPropagation();
    event.preventDefault();
  }
}
}
}

// Initialize menu button date picker

window.addEventListener('load', function () {
const datePickers = document.querySelectorAll('.datepicker');

datePickers.forEach(function (dp) {
new DatePickerDialog(dp);
});
});

//This component has been adapted from an example provided by the W3C, in accordance with the W3C Software and Document License https://www.w3.org/copyright/software-license-2023/

CSS Source Code

/*
  Calendar/Date Picker — Restyled
  Deque University ARIA Component

  This component has been adapted from an example provided by the W3C,
  in accordance with the W3C Software and Document License
  https://www.w3.org/copyright/software-license-2023/
*/

:root {
  --dqu-interactive: #2e5f7a;
  --dqu-interactive-hover: #3a7a9a;
  --dqu-interactive-light: rgba(46, 95, 122, 0.08);
  --dqu-bg-primary: #fcfaf8;
  --dqu-bg-secondary: #f6f3ed;
  --dqu-border-secondary: #8c827d;
  --dqu-text-primary: #21201e;
  --dqu-font-family: "Noto Sans", sans-serif;
}

.sr-only {
  position: absolute;
  top: -2000em;
  left: -3000em;
}

.datepicker {
  margin-top: 1em;
  position: relative;
  font-family: var(--dqu-font-family);
}

.datepicker .group {
  display: inline-block;
  position: relative;
}

.datepicker label {
  display: block;
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--dqu-text-primary);
  margin-bottom: 4px;
}

.datepicker input {
  font-family: var(--dqu-font-family);
  font-size: 1rem;
  padding: 8px 12px;
  margin: 0;
  background-color: var(--dqu-bg-primary);
  color: var(--dqu-text-primary);
  border: 1px solid var(--dqu-border-secondary);
  border-radius: 8px;
}

.datepicker button.icon {
  position: relative;
  top: 0.25em;
  margin: 0 0 0 4px;
  padding: 6px;
  border: 1px solid var(--dqu-border-secondary);
  background-color: var(--dqu-bg-primary);
  border-radius: 8px;
  cursor: pointer;
}

.datepicker .desc {
  position: absolute;
  left: 0;
  top: 2em;
}

.datepicker .fa-calendar-alt {
  color: var(--dqu-interactive);
}

.datepicker button.icon:hover {
  background-color: var(--dqu-interactive-light);
  border-color: var(--dqu-interactive) !important;
  outline: none !important;
}

.datepicker button.icon:focus {
  outline: none !important;
  border: 2px solid var(--dqu-interactive) !important;
  padding: 5px;
  background-color: var(--dqu-interactive-light);
}

.datepicker input:focus {
  background-color: var(--dqu-bg-primary) !important;
  color: var(--dqu-text-primary);
  outline: 2px solid var(--dqu-interactive) !important;
  outline-offset: 2px;
  border-color: var(--dqu-interactive);
}

.datepicker input:hover {
  background-color: var(--dqu-bg-primary) !important;
  color: var(--dqu-text-primary);
  outline: inherit !important;
}

/* Dialog popup */
.datepicker-dialog {
  position: absolute;
  width: 320px;
  clear: both;
  border: 2px solid var(--dqu-interactive);
  margin-top: 0.15em;
  border-radius: 8px;
  padding: 0;
  background-color: var(--dqu-bg-primary);
  display: none;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
  overflow: clip;
}

.datepicker-dialog .header {
  cursor: default;
  background-color: var(--dqu-interactive);
  padding: 10px !important;
  font-size: 1rem !important;
  font-weight: 600 !important;
  text-transform: uppercase;
  color: white !important;
  display: flex !important;
  align-items: center !important;
  justify-content: space-around !important;
  font-family: var(--dqu-font-family);
}

.datepicker-dialog h2 {
  margin: 0 !important;
  padding: 0 !important;
  display: inline-block;
  font-size: 1rem !important;
  color: white;
  text-transform: none;
  font-weight: 600;
  border: none;
  font-family: var(--dqu-font-family);
}

.datepicker-dialog button {
  border-style: none;
  background: transparent;
}

.datepicker-dialog button::-moz-focus-inner {
  border: 0;
}

.datepicker-dialog .dates {
  width: 320px;
}

.datepicker-dialog .prev-year,
.datepicker-dialog .prev-month,
.datepicker-dialog .next-month,
.datepicker-dialog .next-year {
  padding: 4px;
  width: 24px;
  height: 24px;
  color: white;
  cursor: pointer;
}

.datepicker-dialog .prev-year:focus,
.datepicker-dialog .prev-month:focus,
.datepicker-dialog .next-month:focus,
.datepicker-dialog .next-year:focus {
  padding: 2px;
  border: 2px solid white;
  border-radius: 4px;
  outline: 0 !important;
}

.datepicker-dialog .prev-year:hover,
.datepicker-dialog .prev-month:hover,
.datepicker-dialog .next-month:hover,
.datepicker-dialog .next-year:hover {
  padding: 3px;
  border: 1px solid white;
  border-radius: 4px;
}

.datepicker-dialog .dialog-ok-cancel-group {
  text-align: right;
  margin-top: 1em;
  margin-bottom: 1em;
  margin-right: 1em;
}

.datepicker-dialog .dialog-ok-cancel-group button {
  padding: 8px 14px;
  margin-left: 0.5em;
  min-width: 5em;
  width: auto;
  background-color: var(--dqu-bg-secondary);
  font-family: var(--dqu-font-family);
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--dqu-text-primary);
  outline: none !important;
  border: 1px solid var(--dqu-border-secondary);
  border-radius: 9999px;
  cursor: pointer;
}

.datepicker-dialog .dialog-button:focus {
  outline: 3px solid var(--dqu-interactive) !important;
  outline-offset: 2px;
  background-color: var(--dqu-interactive-light);
}

.datepicker-dialog .dialog-button:hover {
  outline: 2px solid var(--dqu-interactive) !important;
  outline-offset: 2px;
  background-color: var(--dqu-interactive-light) !important;
  border-color: var(--dqu-interactive);
}

.datepicker-dialog .fa-calendar-alt {
  color: var(--dqu-interactive);
}

.datepicker-dialog .month-year {
  display: inline-block;
  width: 12em;
  text-align: center !important;
  white-space: nowrap;
  font-weight: 600 !important;
}

.datepicker-dialog table.dates {
  padding-left: 1em;
  padding-right: 1em;
  padding-top: 1em;
  border: none;
  border-collapse: separate;
}

.datepicker-dialog table.dates th,
.datepicker-dialog table.dates td {
  text-align: center;
  background: var(--dqu-bg-primary);
  color: var(--dqu-text-primary);
  border: none;
  font-family: var(--dqu-font-family);
}

.datepicker-dialog table.dates tr {
  border: 1px solid var(--dqu-border-secondary);
}

.datepicker-dialog table.dates td {
  padding: 3px;
  margin: 0;
  line-height: inherit;
  height: 30px;
  width: 40px;
  border-radius: 5px;
  font-size: 15px;
  background: var(--dqu-bg-secondary);
}

.datepicker-dialog table.dates td.disabled {
  padding: 2px;
  border: none;
  height: 31px;
  width: 41px;
}

.datepicker-dialog table.dates td:focus,
.datepicker-dialog table.dates td:hover {
  padding: 0;
  background-color: var(--dqu-interactive-light);
  color: var(--dqu-text-primary);
}

.datepicker-dialog table.dates td:focus {
  padding: 1px;
  border: 2px solid var(--dqu-interactive);
  outline: 0 !important;
}

.datepicker-dialog table.dates td:not(.disabled):hover {
  padding: 2px;
  border: 1px solid var(--dqu-interactive);
}

.datepicker-dialog table.dates td[aria-selected] {
  padding: 1px;
  border: 2px solid var(--dqu-interactive);
  background-color: var(--dqu-interactive-light);
}

/* Inset outline keeps focus indicator inside the cell so it doesn't
   overlap adjacent cells. White on selected (teal bg), teal on others. */
.datepicker-dialog table.dates td:focus {
  outline: 3px solid var(--dqu-interactive) !important;
  outline-offset: -4px;
}

.datepicker-dialog table.dates td[aria-selected]:focus,
.datepicker-dialog table.dates td[tabindex="0"]:focus {
  outline: 3px solid white !important;
  outline-offset: -4px;
}

.datepicker-dialog table.dates td[tabindex="0"] {
  background-color: var(--dqu-interactive);
  color: white;
}

.datepicker-dialog .dialog-message {
  padding: 0.4em 1em 0.2em;
  min-height: 2rem;
  background: var(--dqu-interactive);
  color: white;
  font-family: var(--dqu-font-family);
  font-size: 0.875rem;
}

.red {
  color: var(--dqu-interactive);
}

Copy and Paste Full Page Example