Open demo

CSS code

HTML
<div class="rec">
  <h2>Repeat this event</h2>
  <p class="sub">The preview shows the dates the rule produces this month.</p>
  <fieldset class="opts">
    <legend class="sr-only">Repeat pattern</legend>
    <label><input type="radio" name="repeat" id="rr-d" value="weekdays"> Every weekday</label>
    <label><input type="radio" name="repeat" id="rr-w" value="weekly-tuesday" checked> Every Tuesday</label>
    <label><input type="radio" name="repeat" id="rr-m" value="monthly-14"> Monthly, on the 14th</label>
  </fieldset>
  <div class="prev">
    <table class="g">
      <caption class="sr-only">Preview, September 2026</caption>
      <thead><tr><th scope="col"><span aria-hidden="true">M</span><span class="sr-only">Monday</span></th><th scope="col"><span aria-hidden="true">T</span><span class="sr-only">Tuesday</span></th><th scope="col"><span aria-hidden="true">W</span><span class="sr-only">Wednesday</span></th><th scope="col"><span aria-hidden="true">T</span><span class="sr-only">Thursday</span></th><th scope="col"><span aria-hidden="true">F</span><span class="sr-only">Friday</span></th><th scope="col"><span aria-hidden="true">S</span><span class="sr-only">Saturday</span></th><th scope="col"><span aria-hidden="true">S</span><span class="sr-only">Sunday</span></th></tr></thead>
      <tbody>
        <tr><td><span class="out">31</span></td><td><span class="hitD hitW">1<span class="tag sr-only">, repeats</span></span></td><td><span class="hitD">2<span class="tag sr-only">, repeats</span></span></td><td><span class="hitD">3<span class="tag sr-only">, repeats</span></span></td><td><span class="hitD">4<span class="tag sr-only">, repeats</span></span></td><td><span>5</span></td><td><span>6</span></td></tr>
        <tr><td><span class="hitD">7<span class="tag sr-only">, repeats</span></span></td><td><span class="hitD hitW">8<span class="tag sr-only">, repeats</span></span></td><td><span class="hitD">9<span class="tag sr-only">, repeats</span></span></td><td><span class="hitD">10<span class="tag sr-only">, repeats</span></span></td><td><span class="hitD">11<span class="tag sr-only">, repeats</span></span></td><td><span>12</span></td><td><span>13</span></td></tr>
        <tr><td><span class="hitD hitM">14<span class="tag sr-only">, repeats</span></span></td><td><span class="hitD hitW">15<span class="tag sr-only">, repeats</span></span></td><td><span class="hitD">16<span class="tag sr-only">, repeats</span></span></td><td><span class="hitD">17<span class="tag sr-only">, repeats</span></span></td><td><span class="hitD">18<span class="tag sr-only">, repeats</span></span></td><td><span>19</span></td><td><span>20</span></td></tr>
        <tr><td><span class="hitD">21<span class="tag sr-only">, repeats</span></span></td><td><span class="hitD hitW">22<span class="tag sr-only">, repeats</span></span></td><td><span class="hitD">23<span class="tag sr-only">, repeats</span></span></td><td><span class="hitD">24<span class="tag sr-only">, repeats</span></span></td><td><span class="hitD">25<span class="tag sr-only">, repeats</span></span></td><td><span>26</span></td><td><span>27</span></td></tr>
        <tr><td><span class="hitD">28<span class="tag sr-only">, repeats</span></span></td><td><span class="hitD hitW">29<span class="tag sr-only">, repeats</span></span></td><td><span class="hitD">30<span class="tag sr-only">, repeats</span></span></td><td><span class="out">1</span></td><td><span class="out">2</span></td><td><span class="out">3</span></td><td><span class="out">4</span></td></tr>
      </tbody>
    </table>
    <p class="sum" aria-live="polite"><span data-rule="d">22 dates: every Monday to Friday in September</span><span data-rule="w">5 dates: 1, 8, 15, 22 and 29 September</span><span data-rule="m">1 date: Monday 14 September</span></p>
  </div>
  <p class="note">Showing September 2026. A rule that produces no dates in the visible month is the single most common recurrence bug — a preview makes it obvious before anyone saves.</p>
</div>
CSS
/* Recurrence preview: three radio inputs pick a rule, and :has() on the wrapper highlights the dates
   that rule produces. Highlighted dates also get visually hidden text, and a summary line spells them out. */
.rec{--rc-accent:#6d28d9;--rc-muted:#5f6b7c;--rc-ring:#8692a5;
  box-sizing:border-box;width:100%;max-width:600px;padding:22px 24px 24px;background:#fff;border-radius:16px;
  box-shadow:0 14px 34px -18px rgba(18,24,40,.25);display:grid;grid-template-columns:210px 1fr;gap:24px}
.rec h2{grid-column:1 / -1;font-size:.98rem;margin:0 0 -22px}
.rec .sub{grid-column:1 / -1;font-size:.79rem;color:var(--rc-muted);margin:0 0 -8px}
.rec fieldset{border:0;margin:0;padding:0;min-width:0}
.rec .opts label{display:flex;align-items:center;gap:10px;padding:10px 12px;border-radius:10px;font-size:.84rem;font-weight:600;
  cursor:pointer;border:1px solid #e4e8ef;margin-bottom:8px}
.rec .opts label:hover{border-color:#c2cbd8}
.rec .opts input{appearance:none;width:15px;height:15px;margin:0;border-radius:50%;border:2px solid var(--rc-ring);flex-shrink:0;background:#fff}
.rec .opts input:checked{border:5px solid var(--rc-accent)}
.rec .opts label:has(:checked){border-color:var(--rc-accent);background:#f7f4ff}
.rec .opts label:has(:focus-visible){outline:2px solid var(--rc-accent);outline-offset:2px}
.rec table{width:100%;border-collapse:separate;border-spacing:2px;table-layout:fixed}
.rec th{font-size:.62rem;font-weight:800;color:var(--rc-muted);padding:6px 0;text-align:center}
.rec td{padding:0}
.rec td > span{aspect-ratio:1;display:grid;place-items:center;font-size:.74rem;font-weight:600;color:#2c3546;border-radius:7px}
.rec .out{color:#667284;font-weight:400}
.rec .tag,.rec .sum span{display:none}
.rec:has(#rr-d:checked) .hitD{background:var(--rc-accent);color:#fff}
.rec:has(#rr-d:checked) .hitD .tag,.rec:has(#rr-d:checked) .sum [data-rule="d"]{display:inline}
.rec:has(#rr-w:checked) .hitW{background:var(--rc-accent);color:#fff}
.rec:has(#rr-w:checked) .hitW .tag,.rec:has(#rr-w:checked) .sum [data-rule="w"]{display:inline}
.rec:has(#rr-m:checked) .hitM{background:var(--rc-accent);color:#fff}
.rec:has(#rr-m:checked) .hitM .tag,.rec:has(#rr-m:checked) .sum [data-rule="m"]{display:inline}
.rec .sum{font-size:.76rem;font-weight:600;color:var(--rc-accent);margin:10px 0 0}
.rec .note{grid-column:1 / -1;font-size:.78rem;color:var(--rc-muted);border-top:1px solid #eef1f5;padding-top:14px;margin:4px 0 0}
@media (max-width:620px){.rec{grid-template-columns:1fr}}
.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}

Bootstrap 5 code

Requires: Bootstrap 5.3.8 CSS

HTML
<div class="rec card w-100">
  <h2 class="fw-bold" style="font-size:.98rem">Repeat this event</h2>
  <p class="sub" style="font-size:.79rem;color:var(--rc-muted)">The preview shows the dates the rule produces this month.</p>
  <fieldset>
    <legend class="visually-hidden">Repeat pattern</legend>
    <label class="form-check"><input class="form-check-input" type="radio" name="repeat" id="rr-d" value="weekdays"> Every weekday</label>
    <label class="form-check"><input class="form-check-input" type="radio" name="repeat" id="rr-w" value="weekly-tuesday" checked> Every Tuesday</label>
    <label class="form-check"><input class="form-check-input" type="radio" name="repeat" id="rr-m" value="monthly-14"> Monthly, on the 14th</label>
  </fieldset>
  <div>
    <table class="table text-center mb-0">
      <caption class="visually-hidden">Preview, September 2026</caption>
      <thead><tr><th scope="col"><span aria-hidden="true">M</span><span class="visually-hidden">Monday</span></th><th scope="col"><span aria-hidden="true">T</span><span class="visually-hidden">Tuesday</span></th><th scope="col"><span aria-hidden="true">W</span><span class="visually-hidden">Wednesday</span></th><th scope="col"><span aria-hidden="true">T</span><span class="visually-hidden">Thursday</span></th><th scope="col"><span aria-hidden="true">F</span><span class="visually-hidden">Friday</span></th><th scope="col"><span aria-hidden="true">S</span><span class="visually-hidden">Saturday</span></th><th scope="col"><span aria-hidden="true">S</span><span class="visually-hidden">Sunday</span></th></tr></thead>
      <tbody>
        <tr><td><span class="out">31</span></td><td><span class="hitD hitW">1<span class="tag visually-hidden">, repeats</span></span></td><td><span class="hitD">2<span class="tag visually-hidden">, repeats</span></span></td><td><span class="hitD">3<span class="tag visually-hidden">, repeats</span></span></td><td><span class="hitD">4<span class="tag visually-hidden">, repeats</span></span></td><td><span>5</span></td><td><span>6</span></td></tr>
        <tr><td><span class="hitD">7<span class="tag visually-hidden">, repeats</span></span></td><td><span class="hitD hitW">8<span class="tag visually-hidden">, repeats</span></span></td><td><span class="hitD">9<span class="tag visually-hidden">, repeats</span></span></td><td><span class="hitD">10<span class="tag visually-hidden">, repeats</span></span></td><td><span class="hitD">11<span class="tag visually-hidden">, repeats</span></span></td><td><span>12</span></td><td><span>13</span></td></tr>
        <tr><td><span class="hitD hitM">14<span class="tag visually-hidden">, repeats</span></span></td><td><span class="hitD hitW">15<span class="tag visually-hidden">, repeats</span></span></td><td><span class="hitD">16<span class="tag visually-hidden">, repeats</span></span></td><td><span class="hitD">17<span class="tag visually-hidden">, repeats</span></span></td><td><span class="hitD">18<span class="tag visually-hidden">, repeats</span></span></td><td><span>19</span></td><td><span>20</span></td></tr>
        <tr><td><span class="hitD">21<span class="tag visually-hidden">, repeats</span></span></td><td><span class="hitD hitW">22<span class="tag visually-hidden">, repeats</span></span></td><td><span class="hitD">23<span class="tag visually-hidden">, repeats</span></span></td><td><span class="hitD">24<span class="tag visually-hidden">, repeats</span></span></td><td><span class="hitD">25<span class="tag visually-hidden">, repeats</span></span></td><td><span>26</span></td><td><span>27</span></td></tr>
        <tr><td><span class="hitD">28<span class="tag visually-hidden">, repeats</span></span></td><td><span class="hitD hitW">29<span class="tag visually-hidden">, repeats</span></span></td><td><span class="hitD">30<span class="tag visually-hidden">, repeats</span></span></td><td><span class="out">1</span></td><td><span class="out">2</span></td><td><span class="out">3</span></td><td><span class="out">4</span></td></tr>
      </tbody>
    </table>
    <p class="sum fw-semibold mb-0" style="font-size:.76rem;margin-top:10px;color:var(--rc-accent)" aria-live="polite"><span data-rule="d">22 dates: every Monday to Friday in September</span><span data-rule="w">5 dates: 1, 8, 15, 22 and 29 September</span><span data-rule="m">1 date: Monday 14 September</span></p>
  </div>
  <p class="border-top mb-0" style="grid-column:1 / -1;font-size:.78rem;color:var(--rc-muted);border-color:#eef1f5!important;padding-top:14px;margin-top:4px">Showing September 2026. A rule that produces no dates in the visible month is the single most common recurrence bug — a preview makes it obvious before anyone saves.</p>
</div>
CSS
/* Bootstrap card, .form-check radios and .table; :has() on the wrapper highlights the produced dates. */
.rec{--rc-accent:#6d28d9;--rc-muted:#5f6b7c;--rc-ring:#8692a5;
  max-width:600px;padding:22px 24px 24px;border:0;border-radius:16px;box-shadow:0 14px 34px -18px rgba(18,24,40,.25);
  display:grid;grid-template-columns:210px 1fr;gap:24px}
.rec h2{grid-column:1 / -1;margin-bottom:-22px}
.rec .sub{grid-column:1 / -1;margin-bottom:-8px}
.rec .form-check{display:flex;align-items:center;gap:10px;padding:10px 12px;margin:0 0 8px;min-height:0;border-radius:10px;
  font-size:.84rem;font-weight:600;cursor:pointer;border:1px solid #e4e8ef}
.rec .form-check:hover{border-color:#c2cbd8}
.rec .form-check-input{float:none;margin:0;width:15px;height:15px;border:2px solid var(--rc-ring);flex-shrink:0;cursor:pointer}
.rec .form-check-input:checked{background:#fff;border:5px solid var(--rc-accent)}
.rec .form-check-input:focus{box-shadow:none}
.rec .form-check:has(:checked){border-color:var(--rc-accent);background:#f7f4ff}
.rec .form-check:has(:focus-visible){outline:2px solid var(--rc-accent);outline-offset:2px}
.rec .table{--bs-table-bg:transparent;--bs-table-color:#2c3546;border-collapse:separate;border-spacing:2px;table-layout:fixed}
.rec .table>:not(caption)>*>*{padding:0;border:0}
.rec .table th{font-size:.62rem;font-weight:800;color:var(--rc-muted);padding:6px 0}
.rec td > span{aspect-ratio:1;display:grid;place-items:center;font-size:.74rem;font-weight:600;border-radius:7px}
.rec .out{color:#667284;font-weight:400}
.rec .tag,.rec .sum span{display:none}
.rec:has(#rr-d:checked) .hitD{background:var(--rc-accent);color:#fff}
.rec:has(#rr-d:checked) .hitD .tag,.rec:has(#rr-d:checked) .sum [data-rule="d"]{display:inline}
.rec:has(#rr-w:checked) .hitW{background:var(--rc-accent);color:#fff}
.rec:has(#rr-w:checked) .hitW .tag,.rec:has(#rr-w:checked) .sum [data-rule="w"]{display:inline}
.rec:has(#rr-m:checked) .hitM{background:var(--rc-accent);color:#fff}
.rec:has(#rr-m:checked) .hitM .tag,.rec:has(#rr-m:checked) .sum [data-rule="m"]{display:inline}
@media (max-width:620px){.rec{grid-template-columns:1fr}}

Tailwind 4 code

Requires: Tailwind CSS 4

HTML
<div class="rec grid w-full max-w-[600px] grid-cols-[210px_1fr] gap-6 rounded-2xl bg-white px-6 pt-[22px] pb-6 shadow-[0_14px_34px_-18px_rgba(18,24,40,.25)] max-[620px]:grid-cols-1">
  <h2 class="col-span-full -mb-[22px] text-[.98rem] font-bold">Repeat this event</h2>
  <p class="col-span-full -mb-2 text-[.79rem] text-rc-muted">The preview shows the dates the rule produces this month.</p>
  <fieldset class="min-w-0">
    <legend class="sr-only">Repeat pattern</legend>
    <label class="mb-2 flex cursor-pointer items-center gap-2.5 rounded-[10px] border border-[#e4e8ef] px-3 py-2.5 text-[.84rem] font-semibold hover:border-[#c2cbd8] has-checked:border-rc-accent has-checked:bg-[#f7f4ff] has-focus-visible:outline-2 has-focus-visible:outline-offset-2 has-focus-visible:outline-rc-accent"><input class="size-[15px] shrink-0 cursor-pointer appearance-none rounded-full border-2 border-rc-ring bg-white checked:border-[5px] checked:border-rc-accent focus-visible:outline-none" type="radio" name="repeat" id="rr-d" value="weekdays"> Every weekday</label>
    <label class="mb-2 flex cursor-pointer items-center gap-2.5 rounded-[10px] border border-[#e4e8ef] px-3 py-2.5 text-[.84rem] font-semibold hover:border-[#c2cbd8] has-checked:border-rc-accent has-checked:bg-[#f7f4ff] has-focus-visible:outline-2 has-focus-visible:outline-offset-2 has-focus-visible:outline-rc-accent"><input class="size-[15px] shrink-0 cursor-pointer appearance-none rounded-full border-2 border-rc-ring bg-white checked:border-[5px] checked:border-rc-accent focus-visible:outline-none" type="radio" name="repeat" id="rr-w" value="weekly-tuesday" checked> Every Tuesday</label>
    <label class="mb-2 flex cursor-pointer items-center gap-2.5 rounded-[10px] border border-[#e4e8ef] px-3 py-2.5 text-[.84rem] font-semibold hover:border-[#c2cbd8] has-checked:border-rc-accent has-checked:bg-[#f7f4ff] has-focus-visible:outline-2 has-focus-visible:outline-offset-2 has-focus-visible:outline-rc-accent"><input class="size-[15px] shrink-0 cursor-pointer appearance-none rounded-full border-2 border-rc-ring bg-white checked:border-[5px] checked:border-rc-accent focus-visible:outline-none" type="radio" name="repeat" id="rr-m" value="monthly-14"> Monthly, on the 14th</label>
  </fieldset>
  <div>
    <table class="w-full table-fixed border-separate border-spacing-0.5">
      <caption class="sr-only">Preview, September 2026</caption>
      <thead><tr><th scope="col" class="py-1.5 text-center text-[.62rem] font-extrabold text-rc-muted"><span aria-hidden="true">M</span><span class="sr-only">Monday</span></th><th scope="col" class="py-1.5 text-center text-[.62rem] font-extrabold text-rc-muted"><span aria-hidden="true">T</span><span class="sr-only">Tuesday</span></th><th scope="col" class="py-1.5 text-center text-[.62rem] font-extrabold text-rc-muted"><span aria-hidden="true">W</span><span class="sr-only">Wednesday</span></th><th scope="col" class="py-1.5 text-center text-[.62rem] font-extrabold text-rc-muted"><span aria-hidden="true">T</span><span class="sr-only">Thursday</span></th><th scope="col" class="py-1.5 text-center text-[.62rem] font-extrabold text-rc-muted"><span aria-hidden="true">F</span><span class="sr-only">Friday</span></th><th scope="col" class="py-1.5 text-center text-[.62rem] font-extrabold text-rc-muted"><span aria-hidden="true">S</span><span class="sr-only">Saturday</span></th><th scope="col" class="py-1.5 text-center text-[.62rem] font-extrabold text-rc-muted"><span aria-hidden="true">S</span><span class="sr-only">Sunday</span></th></tr></thead>
      <tbody>
        <tr><td class="p-0"><span class="grid aspect-square place-items-center rounded-[7px] text-[.74rem] font-normal text-[#667284]">31</span></td><td class="p-0"><span class="grid aspect-square place-items-center rounded-[7px] text-[.74rem] font-semibold text-[#2c3546] [.rec:has(#rr-d:checked)_&]:bg-rc-accent [.rec:has(#rr-d:checked)_&]:text-white [.rec:has(#rr-w:checked)_&]:bg-rc-accent [.rec:has(#rr-w:checked)_&]:text-white">1<span class="sr-only hidden [.rec:has(#rr-d:checked)_&]:inline [.rec:has(#rr-w:checked)_&]:inline">, repeats</span></span></td><td class="p-0"><span class="grid aspect-square place-items-center rounded-[7px] text-[.74rem] font-semibold text-[#2c3546] [.rec:has(#rr-d:checked)_&]:bg-rc-accent [.rec:has(#rr-d:checked)_&]:text-white">2<span class="sr-only hidden [.rec:has(#rr-d:checked)_&]:inline">, repeats</span></span></td><td class="p-0"><span class="grid aspect-square place-items-center rounded-[7px] text-[.74rem] font-semibold text-[#2c3546] [.rec:has(#rr-d:checked)_&]:bg-rc-accent [.rec:has(#rr-d:checked)_&]:text-white">3<span class="sr-only hidden [.rec:has(#rr-d:checked)_&]:inline">, repeats</span></span></td><td class="p-0"><span class="grid aspect-square place-items-center rounded-[7px] text-[.74rem] font-semibold text-[#2c3546] [.rec:has(#rr-d:checked)_&]:bg-rc-accent [.rec:has(#rr-d:checked)_&]:text-white">4<span class="sr-only hidden [.rec:has(#rr-d:checked)_&]:inline">, repeats</span></span></td><td class="p-0"><span class="grid aspect-square place-items-center rounded-[7px] text-[.74rem] font-semibold text-[#2c3546]">5</span></td><td class="p-0"><span class="grid aspect-square place-items-center rounded-[7px] text-[.74rem] font-semibold text-[#2c3546]">6</span></td></tr>
        <tr><td class="p-0"><span class="grid aspect-square place-items-center rounded-[7px] text-[.74rem] font-semibold text-[#2c3546] [.rec:has(#rr-d:checked)_&]:bg-rc-accent [.rec:has(#rr-d:checked)_&]:text-white">7<span class="sr-only hidden [.rec:has(#rr-d:checked)_&]:inline">, repeats</span></span></td><td class="p-0"><span class="grid aspect-square place-items-center rounded-[7px] text-[.74rem] font-semibold text-[#2c3546] [.rec:has(#rr-d:checked)_&]:bg-rc-accent [.rec:has(#rr-d:checked)_&]:text-white [.rec:has(#rr-w:checked)_&]:bg-rc-accent [.rec:has(#rr-w:checked)_&]:text-white">8<span class="sr-only hidden [.rec:has(#rr-d:checked)_&]:inline [.rec:has(#rr-w:checked)_&]:inline">, repeats</span></span></td><td class="p-0"><span class="grid aspect-square place-items-center rounded-[7px] text-[.74rem] font-semibold text-[#2c3546] [.rec:has(#rr-d:checked)_&]:bg-rc-accent [.rec:has(#rr-d:checked)_&]:text-white">9<span class="sr-only hidden [.rec:has(#rr-d:checked)_&]:inline">, repeats</span></span></td><td class="p-0"><span class="grid aspect-square place-items-center rounded-[7px] text-[.74rem] font-semibold text-[#2c3546] [.rec:has(#rr-d:checked)_&]:bg-rc-accent [.rec:has(#rr-d:checked)_&]:text-white">10<span class="sr-only hidden [.rec:has(#rr-d:checked)_&]:inline">, repeats</span></span></td><td class="p-0"><span class="grid aspect-square place-items-center rounded-[7px] text-[.74rem] font-semibold text-[#2c3546] [.rec:has(#rr-d:checked)_&]:bg-rc-accent [.rec:has(#rr-d:checked)_&]:text-white">11<span class="sr-only hidden [.rec:has(#rr-d:checked)_&]:inline">, repeats</span></span></td><td class="p-0"><span class="grid aspect-square place-items-center rounded-[7px] text-[.74rem] font-semibold text-[#2c3546]">12</span></td><td class="p-0"><span class="grid aspect-square place-items-center rounded-[7px] text-[.74rem] font-semibold text-[#2c3546]">13</span></td></tr>
        <tr><td class="p-0"><span class="grid aspect-square place-items-center rounded-[7px] text-[.74rem] font-semibold text-[#2c3546] [.rec:has(#rr-d:checked)_&]:bg-rc-accent [.rec:has(#rr-d:checked)_&]:text-white [.rec:has(#rr-m:checked)_&]:bg-rc-accent [.rec:has(#rr-m:checked)_&]:text-white">14<span class="sr-only hidden [.rec:has(#rr-d:checked)_&]:inline [.rec:has(#rr-m:checked)_&]:inline">, repeats</span></span></td><td class="p-0"><span class="grid aspect-square place-items-center rounded-[7px] text-[.74rem] font-semibold text-[#2c3546] [.rec:has(#rr-d:checked)_&]:bg-rc-accent [.rec:has(#rr-d:checked)_&]:text-white [.rec:has(#rr-w:checked)_&]:bg-rc-accent [.rec:has(#rr-w:checked)_&]:text-white">15<span class="sr-only hidden [.rec:has(#rr-d:checked)_&]:inline [.rec:has(#rr-w:checked)_&]:inline">, repeats</span></span></td><td class="p-0"><span class="grid aspect-square place-items-center rounded-[7px] text-[.74rem] font-semibold text-[#2c3546] [.rec:has(#rr-d:checked)_&]:bg-rc-accent [.rec:has(#rr-d:checked)_&]:text-white">16<span class="sr-only hidden [.rec:has(#rr-d:checked)_&]:inline">, repeats</span></span></td><td class="p-0"><span class="grid aspect-square place-items-center rounded-[7px] text-[.74rem] font-semibold text-[#2c3546] [.rec:has(#rr-d:checked)_&]:bg-rc-accent [.rec:has(#rr-d:checked)_&]:text-white">17<span class="sr-only hidden [.rec:has(#rr-d:checked)_&]:inline">, repeats</span></span></td><td class="p-0"><span class="grid aspect-square place-items-center rounded-[7px] text-[.74rem] font-semibold text-[#2c3546] [.rec:has(#rr-d:checked)_&]:bg-rc-accent [.rec:has(#rr-d:checked)_&]:text-white">18<span class="sr-only hidden [.rec:has(#rr-d:checked)_&]:inline">, repeats</span></span></td><td class="p-0"><span class="grid aspect-square place-items-center rounded-[7px] text-[.74rem] font-semibold text-[#2c3546]">19</span></td><td class="p-0"><span class="grid aspect-square place-items-center rounded-[7px] text-[.74rem] font-semibold text-[#2c3546]">20</span></td></tr>
        <tr><td class="p-0"><span class="grid aspect-square place-items-center rounded-[7px] text-[.74rem] font-semibold text-[#2c3546] [.rec:has(#rr-d:checked)_&]:bg-rc-accent [.rec:has(#rr-d:checked)_&]:text-white">21<span class="sr-only hidden [.rec:has(#rr-d:checked)_&]:inline">, repeats</span></span></td><td class="p-0"><span class="grid aspect-square place-items-center rounded-[7px] text-[.74rem] font-semibold text-[#2c3546] [.rec:has(#rr-d:checked)_&]:bg-rc-accent [.rec:has(#rr-d:checked)_&]:text-white [.rec:has(#rr-w:checked)_&]:bg-rc-accent [.rec:has(#rr-w:checked)_&]:text-white">22<span class="sr-only hidden [.rec:has(#rr-d:checked)_&]:inline [.rec:has(#rr-w:checked)_&]:inline">, repeats</span></span></td><td class="p-0"><span class="grid aspect-square place-items-center rounded-[7px] text-[.74rem] font-semibold text-[#2c3546] [.rec:has(#rr-d:checked)_&]:bg-rc-accent [.rec:has(#rr-d:checked)_&]:text-white">23<span class="sr-only hidden [.rec:has(#rr-d:checked)_&]:inline">, repeats</span></span></td><td class="p-0"><span class="grid aspect-square place-items-center rounded-[7px] text-[.74rem] font-semibold text-[#2c3546] [.rec:has(#rr-d:checked)_&]:bg-rc-accent [.rec:has(#rr-d:checked)_&]:text-white">24<span class="sr-only hidden [.rec:has(#rr-d:checked)_&]:inline">, repeats</span></span></td><td class="p-0"><span class="grid aspect-square place-items-center rounded-[7px] text-[.74rem] font-semibold text-[#2c3546] [.rec:has(#rr-d:checked)_&]:bg-rc-accent [.rec:has(#rr-d:checked)_&]:text-white">25<span class="sr-only hidden [.rec:has(#rr-d:checked)_&]:inline">, repeats</span></span></td><td class="p-0"><span class="grid aspect-square place-items-center rounded-[7px] text-[.74rem] font-semibold text-[#2c3546]">26</span></td><td class="p-0"><span class="grid aspect-square place-items-center rounded-[7px] text-[.74rem] font-semibold text-[#2c3546]">27</span></td></tr>
        <tr><td class="p-0"><span class="grid aspect-square place-items-center rounded-[7px] text-[.74rem] font-semibold text-[#2c3546] [.rec:has(#rr-d:checked)_&]:bg-rc-accent [.rec:has(#rr-d:checked)_&]:text-white">28<span class="sr-only hidden [.rec:has(#rr-d:checked)_&]:inline">, repeats</span></span></td><td class="p-0"><span class="grid aspect-square place-items-center rounded-[7px] text-[.74rem] font-semibold text-[#2c3546] [.rec:has(#rr-d:checked)_&]:bg-rc-accent [.rec:has(#rr-d:checked)_&]:text-white [.rec:has(#rr-w:checked)_&]:bg-rc-accent [.rec:has(#rr-w:checked)_&]:text-white">29<span class="sr-only hidden [.rec:has(#rr-d:checked)_&]:inline [.rec:has(#rr-w:checked)_&]:inline">, repeats</span></span></td><td class="p-0"><span class="grid aspect-square place-items-center rounded-[7px] text-[.74rem] font-semibold text-[#2c3546] [.rec:has(#rr-d:checked)_&]:bg-rc-accent [.rec:has(#rr-d:checked)_&]:text-white">30<span class="sr-only hidden [.rec:has(#rr-d:checked)_&]:inline">, repeats</span></span></td><td class="p-0"><span class="grid aspect-square place-items-center rounded-[7px] text-[.74rem] font-normal text-[#667284]">1</span></td><td class="p-0"><span class="grid aspect-square place-items-center rounded-[7px] text-[.74rem] font-normal text-[#667284]">2</span></td><td class="p-0"><span class="grid aspect-square place-items-center rounded-[7px] text-[.74rem] font-normal text-[#667284]">3</span></td><td class="p-0"><span class="grid aspect-square place-items-center rounded-[7px] text-[.74rem] font-normal text-[#667284]">4</span></td></tr>
      </tbody>
    </table>
    <p class="mt-2.5 text-[.76rem] font-semibold text-rc-accent" aria-live="polite"><span class="hidden [.rec:has(#rr-d:checked)_&]:inline" data-rule="d">22 dates: every Monday to Friday in September</span><span class="hidden [.rec:has(#rr-w:checked)_&]:inline" data-rule="w">5 dates: 1, 8, 15, 22 and 29 September</span><span class="hidden [.rec:has(#rr-m:checked)_&]:inline" data-rule="m">1 date: Monday 14 September</span></p>
  </div>
  <p class="col-span-full mt-1 border-t border-[#eef1f5] pt-3.5 text-[.78rem] text-rc-muted">Showing September 2026. A rule that produces no dates in the visible month is the single most common recurrence bug — a preview makes it obvious before anyone saves.</p>
</div>
CSS
@theme {
  --color-rc-accent: #6d28d9;
  --color-rc-muted: #5f6b7c;
  --color-rc-ring: #8692a5;
}

About this set

A recurrence picker with a live preview. Three options, every weekday, every Tuesday and monthly on the 14th, sit beside a September 2026 month, and choosing one highlights the dates that rule produces, with a line under the month spelling them out, for example 5 dates: 1, 8, 15, 22 and 29 September. Use it in event and booking forms, reminders and any settings screen where a repeat rule is easy to get wrong without seeing its result. There is no JavaScript. The options are native radio inputs in a fieldset with a legend, drawn as rings with appearance: none, and a :has() rule on the wrapper highlights the matching dates and shows the matching summary. The preview is a table with weekday headers, and each highlighted date also gets visually hidden text saying it repeats, so the result is not carried by colour alone. The ring outline reaches 3:1 contrast. Below 620 pixels the options stack above the preview. The Bootstrap version uses .form-check radios and .table, and the Tailwind version uses has-checked and arbitrary :has() variants on each date.

What’s included

  • Three repeat rules as native radio inputs, no JavaScript
  • :has() highlights the dates each rule produces
  • Summary line lists the dates in text
  • Hidden "repeats" text on every highlighted date
  • Stacks below 620px

Accessibility

The options are radio inputs in a fieldset with a legend, reachable with Tab and changed with the arrow keys, and focus draws a 2px violet outline around the whole option. The preview is a table with full weekday headers; highlighted dates include hidden "repeats" text and the summary line is a polite live region. Nothing animates.

Customise it

Change --rc-accent, --rc-muted and --rc-ring on .rec in plain CSS and Bootstrap, or the --color-rc-* theme colours in Tailwind. Each rule needs a radio id, a hit class on the matching dates and a summary span.