Pure TailwindMIT licensed

Tailwind Time Range Input

Two type=&quot;time&quot; fields grouped in a <fieldset> with an arrow between them: one visual unit, two independently submittable values. Quiet hours, shift windows, appointment ranges.

Live Demo
Plain CSSTailwind v4
Try it

Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time

Untitled UI React · Affiliate link

Get Untitled UI Pro

The code

<div class="tif-18 grid min-h-svh w-full place-items-center bg-tif18-bg px-6 py-14 font-tif18-sans" aria-label="Time range input demo">
  <fieldset class="w-full max-w-80 rounded-xl border border-white/10 bg-tif18-field px-3.5 pb-3.5">
    <legend class="px-1.5 font-tif18-mono text-[10px] tracking-[0.12em] text-tif18-mut uppercase">Quiet hours</legend>
    <div class="flex items-end gap-3">
      <label class="grid flex-1 gap-1">
        <span class="text-[10px] tracking-[0.08em] text-tif18-mut uppercase">From</span>
        <input type="time" name="tif18-from" value="22:00" step="900"
          class="w-full scheme-dark rounded-lg border border-white/10 bg-tif18-deep px-2.5 py-2 font-tif18-mono text-[13px] font-semibold text-tif18-ink outline-none transition-colors duration-200 focus:border-tif18-acc">
      </label>
      <span class="pb-2 text-sm text-tif18-acc" aria-hidden="true">→</span>
      <label class="grid flex-1 gap-1">
        <span class="text-[10px] tracking-[0.08em] text-tif18-mut uppercase">To</span>
        <input type="time" name="tif18-to" value="07:00" step="900"
          class="w-full scheme-dark rounded-lg border border-white/10 bg-tif18-deep px-2.5 py-2 font-tif18-mono text-[13px] font-semibold text-tif18-ink outline-none transition-colors duration-200 focus:border-tif18-acc">
      </label>
    </div>
  </fieldset>
</div>
@import "tailwindcss";

@theme {
  --color-tif18-bg: #12121a;
  --color-tif18-bg: oklch(0.19 0.015 285);
  --color-tif18-field: #1a1a22;
  --color-tif18-field: oklch(0.23 0.015 285);
  --color-tif18-deep: #0c0c12;
  --color-tif18-deep: oklch(0.16 0.012 285);
  --color-tif18-ink: #f0eeff;
  --color-tif18-ink: oklch(0.95 0.02 290);
  --color-tif18-mut: #b8b6d4;
  --color-tif18-mut: oklch(0.77 0.03 290);
  --color-tif18-acc: #a78bfa;
  --color-tif18-acc: oklch(0.74 0.15 295);
  --font-tif18-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
  --font-tif18-mono: 'JetBrains Mono', ui-monospace, monospace;
}

.tif-18 {
  width: 100%;
}

.tif-18 ::selection {
  background: var(--color-tif18-acc);
  color: #12121a;
}

Setup — do this once per project

Skip this if your project already runs Tailwind v4.

1Load Tailwind
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>

Compiles in the browser — perfect for a CodePen or a quick test, not for production.

2Add the stylesheetRequiredthe @theme block is what makes the custom utilities exist
@import "tailwindcss";

@theme {
  --color-tif18-bg: #12121a;
  --color-tif18-bg: oklch(0.19 0.015 285);
  --color-tif18-field: #1a1a22;
  --color-tif18-field: oklch(0.23 0.015 285);
  --color-tif18-deep: #0c0c12;
  --color-tif18-deep: oklch(0.16 0.012 285);
  --color-tif18-ink: #f0eeff;
  --color-tif18-ink: oklch(0.95 0.02 290);
  --color-tif18-mut: #b8b6d4;
  --color-tif18-mut: oklch(0.77 0.03 290);
  --color-tif18-acc: #a78bfa;
  --color-tif18-acc: oklch(0.74 0.15 295);
  --font-tif18-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
  --font-tif18-mono: 'JetBrains Mono', ui-monospace, monospace;
}

.tif-18 {
  width: 100%;
}

.tif-18 ::selection {
  background: var(--color-tif18-acc);
  color: #12121a;
}
3Fonts used by this demooptional — skip if you have your own type stack
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&family=Plus+Jakarta+Sans:wght@400;500;600&display=swap">

What changed in the port

Which parts became utilities, which stayed CSS, and why.

  • The .if-18 label and .if-18 input descendant rules become per-element class lists, which matters here because the two fields are otherwise identical and easy to let drift.
  • step=&quot;900&quot; added: a quiet-hours picker offering 1-minute precision is a worse control than one offering quarters.
  • The arrow's align-self: end; padding-bottom: 9px fudge becomes items-end on the row plus pb-2 on the arrow.
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS demo from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Tailwind Time Range Input
Source: https://codefronts.com/components/tailwind-css-input-fields/tailwind-time-range-input/

Two type="time" fields grouped in a <fieldset> with an arrow between them: one visual unit, two independently submittable values. Quiet hours, shift windows, appointment ranges.
## HTML
```html
<div class="tif-18 grid min-h-svh w-full place-items-center bg-tif18-bg px-6 py-14 font-tif18-sans" aria-label="Time range input demo">
  <fieldset class="w-full max-w-80 rounded-xl border border-white/10 bg-tif18-field px-3.5 pb-3.5">
    <legend class="px-1.5 font-tif18-mono text-[10px] tracking-[0.12em] text-tif18-mut uppercase">Quiet hours</legend>
    <div class="flex items-end gap-3">
      <label class="grid flex-1 gap-1">
        <span class="text-[10px] tracking-[0.08em] text-tif18-mut uppercase">From</span>
        <input type="time" name="tif18-from" value="22:00" step="900"
          class="w-full scheme-dark rounded-lg border border-white/10 bg-tif18-deep px-2.5 py-2 font-tif18-mono text-[13px] font-semibold text-tif18-ink outline-none transition-colors duration-200 focus:border-tif18-acc">
      </label>
      <span class="pb-2 text-sm text-tif18-acc" aria-hidden="true">→</span>
      <label class="grid flex-1 gap-1">
        <span class="text-[10px] tracking-[0.08em] text-tif18-mut uppercase">To</span>
        <input type="time" name="tif18-to" value="07:00" step="900"
          class="w-full scheme-dark rounded-lg border border-white/10 bg-tif18-deep px-2.5 py-2 font-tif18-mono text-[13px] font-semibold text-tif18-ink outline-none transition-colors duration-200 focus:border-tif18-acc">
      </label>
    </div>
  </fieldset>
</div>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-tif18-bg: #12121a;
  --color-tif18-bg: oklch(0.19 0.015 285);
  --color-tif18-field: #1a1a22;
  --color-tif18-field: oklch(0.23 0.015 285);
  --color-tif18-deep: #0c0c12;
  --color-tif18-deep: oklch(0.16 0.012 285);
  --color-tif18-ink: #f0eeff;
  --color-tif18-ink: oklch(0.95 0.02 290);
  --color-tif18-mut: #b8b6d4;
  --color-tif18-mut: oklch(0.77 0.03 290);
  --color-tif18-acc: #a78bfa;
  --color-tif18-acc: oklch(0.74 0.15 295);
  --font-tif18-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
  --font-tif18-mono: 'JetBrains Mono', ui-monospace, monospace;
}

.tif-18 {
  width: 100%;
}

.tif-18 ::selection {
  background: var(--color-tif18-acc);
  color: #12121a;
}
```

How this works

The group is semantic, the layout is three flex children. No JavaScript is involved in either:

<fieldset class="rounded-xl border p-3.5">
  <legend class="px-1.5 text-[10px] uppercase">Quiet hours</legend>
  <div class="flex items-end gap-3">…

Cross-field validation is still yours. Neither input knows the other exists, so "end after start" needs a submit handler — the browser will happily accept 22:00 → 07:00, which in a quiet-hours field is correct, and in a shift picker is not.

Make it yours

  • Granularity: step=&quot;900&quot; (15 min), 1800, 3600.
  • Label: the legend copy — Availability, Shift, Booking window.
  • Flat variant: drop the fieldset border and padding for an inline settings row.
  • Accent: --color-tif18-acc drives the arrow and both focus borders.

Gotchas — read before shipping

  • 12h vs 24h display follows the OS locale. No attribute and no CSS can force one.
  • The clock popover differs across engines and is unstyleable beyond color-scheme.
  • step changes what the picker offers and what validates — set it deliberately, not decoratively.

Browser support

ChromeSafariFirefoxEdge
111+16.4+128+111+

Time input is universal; only the dark popover needs a modern engine.

Techniques used in this demo

Utilitiesfirst-class utility or variant

Theme tokensset in @theme, used by a utility

Search CodeFronts

Loading…