Open demo

CSS code

HTML
<div class="dayv">
  <h2>Tuesday 15 September</h2>
  <p class="sub">Four events · 2h 30m free before lunch</p>
  <div class="grid">
    <span class="hr" aria-hidden="true">08:00</span>
    <span class="hr" aria-hidden="true">09:00</span>
    <span class="hr" aria-hidden="true">10:00</span>
    <span class="hr" aria-hidden="true">11:00</span>
    <span class="hr" aria-hidden="true">12:00</span>
    <span class="hr" aria-hidden="true">13:00</span>
    <span class="hr" aria-hidden="true">14:00</span>
    <span class="hr" aria-hidden="true">15:00</span>
    <span class="hr" aria-hidden="true">16:00</span>
    <span class="hr" aria-hidden="true">17:00</span>
    <span class="hr" aria-hidden="true">18:00</span>
    <div class="lane">
      <ol aria-label="Events on Tuesday 15 September">
        <li class="ev" style="--from:1;--dur:1;--col:0;--w:2;--c:#6d28d9"><b>Standup</b> <small>09:00 – 10:00</small></li>
        <li class="ev" style="--from:3;--dur:2;--col:0;--w:1;--c:#0369a1"><b>Design review</b> <small>11:00 – 13:00</small></li>
        <li class="ev" style="--from:3.5;--dur:1;--col:1;--w:1;--c:#b45309"><b>1:1 with Rosa</b> <small>11:30 – 12:30</small></li>
        <li class="ev" style="--from:6;--dur:1.5;--col:0;--w:2;--c:#15803d"><b>Q4 planning</b> <small>14:00 – 15:30</small></li>
      </ol>
      <p class="now" style="top:233px"><span><span class="sr-only">Now </span><time datetime="13:20">13:20</time></span></p>
    </div>
  </div>
</div>
CSS
/* Day view: hour rows on a CSS grid, events absolutely placed from --from (hours after 08:00) and --dur.
   Overlapping events share the lane through --col and --w (in halves). */
.dayv{--dv-row:44px;--dv-muted:#5f6b7c;--dv-now:#e11d48;
  box-sizing:border-box;width:100%;max-width:520px;padding:20px 22px 24px;background:#fff;border-radius:16px;
  box-shadow:0 14px 34px -18px rgba(18,24,40,.25)}
.dayv h2{font-size:.98rem;margin:0 0 2px}
.dayv .sub{font-size:.79rem;color:var(--dv-muted);margin:0 0 16px}
.dayv .grid{display:grid;grid-template-columns:52px 1fr;grid-template-rows:repeat(11,var(--dv-row));position:relative}
.dayv .hr{grid-column:1;font-size:.72rem;color:var(--dv-muted);text-align:right;padding-right:10px;transform:translateY(-7px)}
.dayv .lane{grid-column:2;grid-row:1 / -1;position:relative;border-left:1px solid #e7ebf1}
.dayv .lane::before{content:"";position:absolute;inset:0;background:repeating-linear-gradient(180deg,#eef1f5 0 1px,transparent 1px var(--dv-row))}
.dayv ol{list-style:none;margin:0;padding:0}
.dayv .ev{position:absolute;box-sizing:border-box;left:calc(var(--col,0) * 50% + 6px);width:calc(var(--w,2) * 50% - 12px);
  top:calc(var(--from) * var(--dv-row));height:calc(var(--dur) * var(--dv-row) - 5px);border-radius:8px;padding:6px 9px;
  background:var(--c);font-size:.75rem;font-weight:700;color:#fff;overflow:hidden;line-height:1.35}
.dayv .ev small{display:block;font-weight:500;font-size:.68rem;margin-top:1px}
.dayv .now{position:absolute;left:0;right:0;height:2px;margin:0;background:var(--dv-now);z-index:3}
.dayv .now::before{content:"";position:absolute;left:-4px;top:-3px;width:8px;height:8px;border-radius:50%;background:var(--dv-now)}
.dayv .now span{position:absolute;right:0;top:-18px;font-size:.65rem;font-weight:800;color:var(--dv-now);background:#fff;padding:0 4px}
.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}
JavaScript
/* Place the now-line from the real clock, clamped to the 08:00-19:00 window the grid renders.
   Outside the working day the line is hidden. */
document.querySelectorAll('.dayv').forEach(root => {
  const ROW = 44, START = 8, HOURS = 11;
  const now = root.querySelector('.now'), d = new Date();
  const pos = d.getHours() + d.getMinutes() / 60 - START;
  if (pos < 0 || pos > HOURS) { now.hidden = true; return; }
  now.style.top = pos * ROW + 'px';
  const t = String(d.getHours()).padStart(2, '0') + ':' + String(d.getMinutes()).padStart(2, '0');
  now.querySelector('time').textContent = t;
  now.querySelector('time').dateTime = t;
});

Bootstrap 5 code

Requires: Bootstrap 5.3.8 CSS

HTML
<div class="dayv card w-100">
  <h2 class="fw-bold mb-1" style="font-size:.98rem">Tuesday 15 September</h2>
  <p class="mb-3" style="font-size:.79rem;color:var(--dv-muted)">Four events · 2h 30m free before lunch</p>
  <div class="hours position-relative">
    <span class="hr text-end" aria-hidden="true">08:00</span>
    <span class="hr text-end" aria-hidden="true">09:00</span>
    <span class="hr text-end" aria-hidden="true">10:00</span>
    <span class="hr text-end" aria-hidden="true">11:00</span>
    <span class="hr text-end" aria-hidden="true">12:00</span>
    <span class="hr text-end" aria-hidden="true">13:00</span>
    <span class="hr text-end" aria-hidden="true">14:00</span>
    <span class="hr text-end" aria-hidden="true">15:00</span>
    <span class="hr text-end" aria-hidden="true">16:00</span>
    <span class="hr text-end" aria-hidden="true">17:00</span>
    <span class="hr text-end" aria-hidden="true">18:00</span>
    <div class="lane position-relative">
      <ol class="list-unstyled m-0" aria-label="Events on Tuesday 15 September">
        <li class="ev position-absolute rounded-3 overflow-hidden text-white fw-bold" style="--from:1;--dur:1;--col:0;--w:2;--c:#6d28d9"><b>Standup</b> <small class="d-block fw-medium">09:00 – 10:00</small></li>
        <li class="ev position-absolute rounded-3 overflow-hidden text-white fw-bold" style="--from:3;--dur:2;--col:0;--w:1;--c:#0369a1"><b>Design review</b> <small class="d-block fw-medium">11:00 – 13:00</small></li>
        <li class="ev position-absolute rounded-3 overflow-hidden text-white fw-bold" style="--from:3.5;--dur:1;--col:1;--w:1;--c:#b45309"><b>1:1 with Rosa</b> <small class="d-block fw-medium">11:30 – 12:30</small></li>
        <li class="ev position-absolute rounded-3 overflow-hidden text-white fw-bold" style="--from:6;--dur:1.5;--col:0;--w:2;--c:#15803d"><b>Q4 planning</b> <small class="d-block fw-medium">14:00 – 15:30</small></li>
      </ol>
      <p class="now position-absolute start-0 end-0 m-0" style="top:233px"><span class="position-absolute end-0 fw-bolder bg-white"><span class="visually-hidden">Now </span><time datetime="13:20">13:20</time></span></p>
    </div>
  </div>
</div>
CSS
/* Bootstrap card, list and utilities. Positioning the events needs a few custom rules. */
.dayv{--dv-row:44px;--dv-muted:#5f6b7c;--dv-now:#e11d48;
  max-width:520px;padding:20px 22px 24px;border:0;border-radius:16px;box-shadow:0 14px 34px -18px rgba(18,24,40,.25)}
.dayv .hours{display:grid;grid-template-columns:52px 1fr;grid-template-rows:repeat(11,var(--dv-row))}
.dayv .hr{grid-column:1;font-size:.72rem;color:var(--dv-muted);padding-right:10px;transform:translateY(-7px)}
.dayv .lane{grid-column:2;grid-row:1 / -1;border-left:1px solid #e7ebf1}
.dayv .lane::before{content:"";position:absolute;inset:0;background:repeating-linear-gradient(180deg,#eef1f5 0 1px,transparent 1px var(--dv-row))}
.dayv .ev{left:calc(var(--col,0) * 50% + 6px);width:calc(var(--w,2) * 50% - 12px);
  top:calc(var(--from) * var(--dv-row));height:calc(var(--dur) * var(--dv-row) - 5px);padding:6px 9px;background:var(--c);font-size:.75rem;line-height:1.35}
.dayv .ev small{font-size:.68rem;margin-top:1px}
.dayv .now{height:2px;background:var(--dv-now);z-index:3}
.dayv .now::before{content:"";position:absolute;left:-4px;top:-3px;width:8px;height:8px;border-radius:50%;background:var(--dv-now)}
.dayv .now span{top:-18px;font-size:.65rem;color:var(--dv-now);padding:0 4px}
JavaScript
/* Place the now-line from the real clock, clamped to the 08:00-19:00 window the grid renders.
   Outside the working day the line is hidden. */
document.querySelectorAll('.dayv').forEach(root => {
  const ROW = 44, START = 8, HOURS = 11;
  const now = root.querySelector('.now'), d = new Date();
  const pos = d.getHours() + d.getMinutes() / 60 - START;
  if (pos < 0 || pos > HOURS) { now.hidden = true; return; }
  now.style.top = pos * ROW + 'px';
  const t = String(d.getHours()).padStart(2, '0') + ':' + String(d.getMinutes()).padStart(2, '0');
  now.querySelector('time').textContent = t;
  now.querySelector('time').dateTime = t;
});

Tailwind 4 code

Requires: Tailwind CSS 4

HTML
<div class="dayv w-full max-w-[520px] rounded-2xl bg-white px-[22px] pt-5 pb-6 shadow-[0_14px_34px_-18px_rgba(18,24,40,.25)]">
  <h2 class="mb-0.5 text-[.98rem] font-bold">Tuesday 15 September</h2>
  <p class="mb-4 text-[.79rem] text-dv-muted">Four events · 2h 30m free before lunch</p>
  <div class="relative grid grid-cols-[52px_1fr] grid-rows-[repeat(11,44px)]">
    <span class="col-start-1 -translate-y-[7px] pr-2.5 text-right text-[.72rem] text-dv-muted" aria-hidden="true">08:00</span>
    <span class="col-start-1 -translate-y-[7px] pr-2.5 text-right text-[.72rem] text-dv-muted" aria-hidden="true">09:00</span>
    <span class="col-start-1 -translate-y-[7px] pr-2.5 text-right text-[.72rem] text-dv-muted" aria-hidden="true">10:00</span>
    <span class="col-start-1 -translate-y-[7px] pr-2.5 text-right text-[.72rem] text-dv-muted" aria-hidden="true">11:00</span>
    <span class="col-start-1 -translate-y-[7px] pr-2.5 text-right text-[.72rem] text-dv-muted" aria-hidden="true">12:00</span>
    <span class="col-start-1 -translate-y-[7px] pr-2.5 text-right text-[.72rem] text-dv-muted" aria-hidden="true">13:00</span>
    <span class="col-start-1 -translate-y-[7px] pr-2.5 text-right text-[.72rem] text-dv-muted" aria-hidden="true">14:00</span>
    <span class="col-start-1 -translate-y-[7px] pr-2.5 text-right text-[.72rem] text-dv-muted" aria-hidden="true">15:00</span>
    <span class="col-start-1 -translate-y-[7px] pr-2.5 text-right text-[.72rem] text-dv-muted" aria-hidden="true">16:00</span>
    <span class="col-start-1 -translate-y-[7px] pr-2.5 text-right text-[.72rem] text-dv-muted" aria-hidden="true">17:00</span>
    <span class="col-start-1 -translate-y-[7px] pr-2.5 text-right text-[.72rem] text-dv-muted" aria-hidden="true">18:00</span>
    <div class="relative col-start-2 row-span-full border-l border-[#e7ebf1] before:absolute before:inset-0 before:bg-[repeating-linear-gradient(180deg,#eef1f5_0_1px,transparent_1px_44px)] before:content-['']">
      <ol aria-label="Events on Tuesday 15 September">
        <li class="absolute top-[calc(var(--from)*44px)] left-[calc(var(--col)*50%+6px)] h-[calc(var(--dur)*44px-5px)] w-[calc(var(--w)*50%-12px)] overflow-hidden rounded-lg bg-(--c) px-[9px] py-1.5 text-[.75rem] leading-[1.35] font-bold text-white" style="--from:1;--dur:1;--col:0;--w:2;--c:#6d28d9"><b>Standup</b> <small class="mt-px block text-[.68rem] font-medium">09:00 – 10:00</small></li>
        <li class="absolute top-[calc(var(--from)*44px)] left-[calc(var(--col)*50%+6px)] h-[calc(var(--dur)*44px-5px)] w-[calc(var(--w)*50%-12px)] overflow-hidden rounded-lg bg-(--c) px-[9px] py-1.5 text-[.75rem] leading-[1.35] font-bold text-white" style="--from:3;--dur:2;--col:0;--w:1;--c:#0369a1"><b>Design review</b> <small class="mt-px block text-[.68rem] font-medium">11:00 – 13:00</small></li>
        <li class="absolute top-[calc(var(--from)*44px)] left-[calc(var(--col)*50%+6px)] h-[calc(var(--dur)*44px-5px)] w-[calc(var(--w)*50%-12px)] overflow-hidden rounded-lg bg-(--c) px-[9px] py-1.5 text-[.75rem] leading-[1.35] font-bold text-white" style="--from:3.5;--dur:1;--col:1;--w:1;--c:#b45309"><b>1:1 with Rosa</b> <small class="mt-px block text-[.68rem] font-medium">11:30 – 12:30</small></li>
        <li class="absolute top-[calc(var(--from)*44px)] left-[calc(var(--col)*50%+6px)] h-[calc(var(--dur)*44px-5px)] w-[calc(var(--w)*50%-12px)] overflow-hidden rounded-lg bg-(--c) px-[9px] py-1.5 text-[.75rem] leading-[1.35] font-bold text-white" style="--from:6;--dur:1.5;--col:0;--w:2;--c:#15803d"><b>Q4 planning</b> <small class="mt-px block text-[.68rem] font-medium">14:00 – 15:30</small></li>
      </ol>
      <p class="now absolute right-0 left-0 z-3 h-0.5 bg-dv-now before:absolute before:-top-[3px] before:-left-1 before:size-2 before:rounded-full before:bg-dv-now before:content-['']" style="top:233px"><span class="absolute -top-[18px] right-0 bg-white px-1 text-[.65rem] font-extrabold text-dv-now"><span class="sr-only">Now </span><time datetime="13:20">13:20</time></span></p>
    </div>
  </div>
</div>
CSS
@theme {
  --color-dv-muted: #5f6b7c;
  --color-dv-now: #e11d48;
}
JavaScript
/* Place the now-line from the real clock, clamped to the 08:00-19:00 window the grid renders.
   Outside the working day the line is hidden. */
document.querySelectorAll('.dayv').forEach(root => {
  const ROW = 44, START = 8, HOURS = 11;
  const now = root.querySelector('.now'), d = new Date();
  const pos = d.getHours() + d.getMinutes() / 60 - START;
  if (pos < 0 || pos > HOURS) { now.hidden = true; return; }
  now.style.top = pos * ROW + 'px';
  const t = String(d.getHours()).padStart(2, '0') + ':' + String(d.getMinutes()).padStart(2, '0');
  now.querySelector('time').textContent = t;
  now.querySelector('time').dateTime = t;
});

About this set

A single-day calendar from 08:00 to 19:00 with hour rows, four events and a red line marking the current time. Two of the events overlap, a design review and a one-to-one, and sit side by side in half-width columns, which is the case most day views get wrong. Use it for a personal planner, a booking back office or the day panel of a larger calendar. Each event is a list item positioned from two custom properties, –from (hours after 08:00) and –dur (length in hours), plus –col and –w for overlaps, so moving an event means changing numbers rather than pixels. The events form an ordered list named after the day, and each one states its start and end time in text, so the schedule reads correctly without the grid. A few lines of script read the real clock to place the now-line and update its label, and hide it outside the hours the grid shows. Event colours were darkened so the white text on them reaches 4.5:1. The Bootstrap version uses a card, list and position utilities; the Tailwind version positions events with calc() arbitrary values reading the same properties.

What’s included

  • Events placed by --from and --dur custom properties
  • Side-by-side layout for overlapping events
  • Live now-line placed from the real clock, hidden out of hours
  • Event colours darkened for 4.5:1 white text
  • Plain CSS, Bootstrap and Tailwind versions of the same design

Accessibility

Events are an ordered list labelled with the date, and each item includes its time range as text; the hour labels are decorative and hidden from assistive tech. The now-line carries visually hidden "Now" text before its time. There are no interactive controls and nothing animates.

Customise it

Change --dv-row (hour height), --dv-muted and --dv-now on .dayv, and the --c colour on each event. If you change the hour range, update START and HOURS in the script and the number of grid rows. In Tailwind edit the --color-dv-* theme colours.