Open demo

CSS code

HTML
<div><span class="lbl">Large display with date</span>
<div class="card" data-digital="24"><span class="sr-only" data-say>Current time</span>
  <div class="big" aria-hidden="true"><span data-show="hms">--:--:--</span><small data-show="long">—</small></div></div></div>
<div><span class="lbl">12-hour with meridiem</span>
<div class="card ampm" data-digital="12"><span class="sr-only" data-say>Current time</span>
  <span aria-hidden="true" data-show="h12">--:--</span><span class="mer" aria-hidden="true" data-show="ampm">--</span></div></div>
<div><span class="lbl">Terminal readout</span>
<div class="term" data-digital="24"><span class="sr-only" data-say>Current time</span>
  <span aria-hidden="true" data-show="hms">--:--:--</span><em aria-hidden="true" data-show="iso">—</em></div></div>
CSS
/* Digital clocks: the script writes each [data-show] element (hms, h12, ampm, long, iso) inside a
   [data-digital] root. The ticking digits are aria-hidden; screen readers get the [data-say] text instead. */
.card{background:#fff;border-radius:16px;padding:26px 30px;box-shadow:0 2px 14px rgba(15,23,42,.1)}
.big{font-size:4.2rem;font-weight:200;letter-spacing:-.02em;line-height:1;font-variant-numeric:tabular-nums}
.big small{display:block;font-size:.92rem;font-weight:500;color:#64748b;letter-spacing:.04em;margin-top:10px}
.ampm{display:flex;align-items:baseline;gap:9px;font-size:2.3rem;font-weight:600;font-variant-numeric:tabular-nums}
.ampm .mer{font-size:.85rem;font-weight:700;color:#2563eb;letter-spacing:.1em}
.term{background:#0b0f16;color:#4ade80;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
  padding:20px 24px;border-radius:12px;font-size:1.9rem;letter-spacing:.06em;font-variant-numeric:tabular-nums}
.term em{display:block;font-size:.72rem;color:#8b95a7;font-style:normal;margin-top:7px;letter-spacing:.08em}
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
@media (max-width:420px){.big{font-size:3.3rem}.card{padding:22px 22px}}
JavaScript
// Digital clocks. Fills every [data-show] element inside a [data-digital] root with the local time,
// re-timed to each second boundary. The visually hidden [data-say] text changes once a minute only.
(function () {
  var roots = document.querySelectorAll('[data-digital]');
  if (!roots.length) return;
  var lastMinute = -1;
  function p2(n) { return String(n).padStart(2, '0'); }
  function tick() {
    var d = new Date(), H = d.getHours(), M = d.getMinutes(), S = d.getSeconds();
    var out = {
      hms: p2(H) + ':' + p2(M) + ':' + p2(S),
      h12: (H % 12 || 12) + ':' + p2(M),
      ampm: H < 12 ? 'AM' : 'PM',
      long: d.toLocaleDateString(undefined, { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' }),
      iso: d.getFullYear() + '-' + p2(d.getMonth() + 1) + '-' + p2(d.getDate()) + ' // local'
    };
    var newMinute = M !== lastMinute; lastMinute = M;
    roots.forEach(function (root) {
      root.querySelectorAll('[data-show]').forEach(function (el) { el.textContent = out[el.getAttribute('data-show')]; });
      var say = root.querySelector('[data-say]');
      if (say && newMinute) say.textContent = 'Current time ' +
        (root.getAttribute('data-digital') === '12' ? out.h12 + ' ' + out.ampm : p2(H) + ':' + p2(M)) + ', ' +
        d.toLocaleDateString(undefined, { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' });
    });
    setTimeout(tick, 1000 - Date.now() % 1000);
  }
  tick();
})();

Bootstrap 5 code

Requires: Bootstrap 5.3.8 CSS

HTML
<div><span class="lbl">Large display with date</span>
<div class="card clock-card" data-digital="24"><div class="card-body"><span class="visually-hidden" data-say>Current time</span>
  <div class="clock-big" aria-hidden="true"><span data-show="hms">--:--:--</span><small class="d-block fw-medium text-body-secondary" data-show="long">—</small></div></div></div></div>
<div><span class="lbl">12-hour with meridiem</span>
<div class="card clock-card" data-digital="12"><div class="card-body clock-ampm d-flex align-items-baseline fw-semibold"><span class="visually-hidden" data-say>Current time</span>
  <span aria-hidden="true" data-show="h12">--:--</span><span class="text-primary fw-bold" aria-hidden="true" data-show="ampm">--</span></div></div></div>
<div><span class="lbl">Terminal readout</span>
<div class="card clock-card clock-term font-monospace" data-digital="24"><div class="card-body"><span class="visually-hidden" data-say>Current time</span>
  <span aria-hidden="true" data-show="hms">--:--:--</span><em class="d-block fst-normal" aria-hidden="true" data-show="iso">—</em></div></div></div>
CSS
/* Digital clocks on Bootstrap .card: the card is restyled through --bs-card-* variables, the readouts
   use .fw-light, .font-monospace and .text-body-secondary. The script fills every [data-show] element. */
.clock-card{--bs-card-border-width:0;--bs-card-border-radius:16px;--bs-card-spacer-y:26px;--bs-card-spacer-x:30px;
  --bs-card-color:#1e293b;--bs-secondary-color:#64748b;box-shadow:0 2px 14px rgba(15,23,42,.1)}
.clock-big{font-size:4.2rem;font-weight:200;letter-spacing:-.02em;line-height:1;font-variant-numeric:tabular-nums}
.clock-big small{font-size:.92rem;letter-spacing:.04em;margin-top:10px}
.clock-ampm{gap:9px;font-size:2.3rem;font-variant-numeric:tabular-nums}
.clock-ampm .text-primary{--bs-primary-rgb:37,99,235;font-size:.85rem;letter-spacing:.1em}
.clock-term{--bs-card-bg:#0b0f16;--bs-card-color:#4ade80;--bs-card-border-radius:12px;--bs-card-spacer-y:20px;--bs-card-spacer-x:24px;
  --bs-font-monospace:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:1.9rem;letter-spacing:.06em;font-variant-numeric:tabular-nums;box-shadow:none}
.clock-term em{font-size:.72rem;color:#8b95a7;margin-top:7px;letter-spacing:.08em}
@media (max-width:420px){.clock-big{font-size:3.3rem}.clock-card:not(.clock-term){--bs-card-spacer-y:22px;--bs-card-spacer-x:22px}}
JavaScript
// Digital clocks. Fills every [data-show] element inside a [data-digital] root with the local time,
// re-timed to each second boundary. The visually hidden [data-say] text changes once a minute only.
(function () {
  var roots = document.querySelectorAll('[data-digital]');
  if (!roots.length) return;
  var lastMinute = -1;
  function p2(n) { return String(n).padStart(2, '0'); }
  function tick() {
    var d = new Date(), H = d.getHours(), M = d.getMinutes(), S = d.getSeconds();
    var out = {
      hms: p2(H) + ':' + p2(M) + ':' + p2(S),
      h12: (H % 12 || 12) + ':' + p2(M),
      ampm: H < 12 ? 'AM' : 'PM',
      long: d.toLocaleDateString(undefined, { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' }),
      iso: d.getFullYear() + '-' + p2(d.getMonth() + 1) + '-' + p2(d.getDate()) + ' // local'
    };
    var newMinute = M !== lastMinute; lastMinute = M;
    roots.forEach(function (root) {
      root.querySelectorAll('[data-show]').forEach(function (el) { el.textContent = out[el.getAttribute('data-show')]; });
      var say = root.querySelector('[data-say]');
      if (say && newMinute) say.textContent = 'Current time ' +
        (root.getAttribute('data-digital') === '12' ? out.h12 + ' ' + out.ampm : p2(H) + ':' + p2(M)) + ', ' +
        d.toLocaleDateString(undefined, { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' });
    });
    setTimeout(tick, 1000 - Date.now() % 1000);
  }
  tick();
})();

Tailwind 4 code

Requires: Tailwind CSS 4

HTML
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#64748b] uppercase">Large display with date</span>
<div class="rounded-2xl bg-white px-[22px] py-[22px] shadow-[0_2px_14px_rgba(15,23,42,.1)] min-[421px]:px-[30px] min-[421px]:py-[26px]" data-digital="24"><span class="sr-only" data-say>Current time</span>
  <div class="text-[3.3rem] leading-none font-extralight tracking-[-.02em] tabular-nums min-[421px]:text-[4.2rem]" aria-hidden="true"><span data-show="hms">--:--:--</span><small class="mt-2.5 block text-[.92rem] font-medium tracking-[.04em] text-[#64748b]" data-show="long">—</small></div></div></div>
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#64748b] uppercase">12-hour with meridiem</span>
<div class="flex items-baseline gap-[9px] rounded-2xl bg-white px-[22px] py-[22px] text-[2.3rem] min-[421px]:px-[30px] min-[421px]:py-[26px] font-semibold tabular-nums shadow-[0_2px_14px_rgba(15,23,42,.1)]" data-digital="12"><span class="sr-only" data-say>Current time</span>
  <span aria-hidden="true" data-show="h12">--:--</span><span class="text-[.85rem] font-bold tracking-[.1em] text-[#2563eb]" aria-hidden="true" data-show="ampm">--</span></div></div>
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#64748b] uppercase">Terminal readout</span>
<div class="rounded-xl bg-[#0b0f16] px-6 py-5 font-[ui-monospace,SFMono-Regular,Menlo,monospace] text-[1.9rem] tracking-[.06em] text-[#4ade80] tabular-nums" data-digital="24"><span class="sr-only" data-say>Current time</span>
  <span aria-hidden="true" data-show="hms">--:--:--</span><em class="mt-[7px] block text-[.72rem] tracking-[.08em] text-[#8b95a7] not-italic" aria-hidden="true" data-show="iso">—</em></div></div>
JavaScript
// Digital clocks. Fills every [data-show] element inside a [data-digital] root with the local time,
// re-timed to each second boundary. The visually hidden [data-say] text changes once a minute only.
(function () {
  var roots = document.querySelectorAll('[data-digital]');
  if (!roots.length) return;
  var lastMinute = -1;
  function p2(n) { return String(n).padStart(2, '0'); }
  function tick() {
    var d = new Date(), H = d.getHours(), M = d.getMinutes(), S = d.getSeconds();
    var out = {
      hms: p2(H) + ':' + p2(M) + ':' + p2(S),
      h12: (H % 12 || 12) + ':' + p2(M),
      ampm: H < 12 ? 'AM' : 'PM',
      long: d.toLocaleDateString(undefined, { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' }),
      iso: d.getFullYear() + '-' + p2(d.getMonth() + 1) + '-' + p2(d.getDate()) + ' // local'
    };
    var newMinute = M !== lastMinute; lastMinute = M;
    roots.forEach(function (root) {
      root.querySelectorAll('[data-show]').forEach(function (el) { el.textContent = out[el.getAttribute('data-show')]; });
      var say = root.querySelector('[data-say]');
      if (say && newMinute) say.textContent = 'Current time ' +
        (root.getAttribute('data-digital') === '12' ? out.h12 + ' ' + out.ampm : p2(H) + ':' + p2(M)) + ', ' +
        d.toLocaleDateString(undefined, { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' });
    });
    setTimeout(tick, 1000 - Date.now() % 1000);
  }
  tick();
})();

About this set

Three digital clock readouts that show the local time from the browser. The large display pairs a thin 24-hour time with seconds and the full date in the visitor’s locale, for a dashboard header or a kiosk screen. The 12-hour card shows hours and minutes in a heavy weight with a small blue AM or PM marker, for interfaces aimed at audiences who read time that way. The terminal readout uses a monospaced green-on-black style with the local date in ISO format, for developer tools and status pages. Every number uses tabular figures, so the digits do not shift sideways as they change. The markup carries no IDs: each clock is a container with data-digital, and each value is an element with data-show naming its format, so you can place several clocks on one page and one short vanilla script fills them all, re-timed to each second boundary. The Bootstrap version builds each readout on a .card restyled through its CSS variables, and the Tailwind version uses only utility classes.

What’s included

  • Large 24-hour time with the full localised date
  • 12-hour time with an AM or PM marker
  • Monospaced terminal readout with the local ISO date
  • Tabular figures, so digits never jump sideways
  • No IDs: data-show attributes let one script drive any number of clocks

Accessibility

The ticking digits are aria-hidden. Each clock has visually hidden text such as "Current time 9:41 PM, Thursday, October 15, 2026" that changes once a minute and is not a live region, so screen readers read the time on request and are never interrupted. Captions and the date reach 4.5:1 contrast, including the grey date in the terminal readout.

Customise it

Edit font sizes and colours on .big, .ampm, .mer and .term in plain CSS. In Bootstrap change the --bs-card-* variables on .clock-card and .clock-term; in Tailwind edit the text-, bg- and font- utilities on each readout.