Tailwind + JSMIT licensed

Tailwind Rotating Placeholder Input

The native placeholder attribute itself cycles through four sample queries every 2.5 seconds, fading between them. The script swaps one attribute and flips one data attribute; every visual is a utility, and rotation pauses the instant the user focuses.

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-11 grid min-h-svh w-full place-items-center bg-tif11-bg px-6 py-14 font-tif11-sans" aria-label="Rotating placeholder input demo">
  <label class="grid w-full max-w-70 gap-1.5">
    <span class="font-tif11-mono text-[10px] tracking-[0.12em] text-tif11-mut uppercase">Search</span>
    <input type="search" name="tif11-q" inputmode="search" placeholder="Search products…" aria-label="Search"
      data-tif-rotate='["Search products…","Search docs…","Search people…","Search settings…"]'
      class="w-full rounded-[10px] border border-white/12 bg-tif11-field px-3.5 py-3 text-sm font-medium text-tif11-ink outline-none transition-[border-color,background-color] duration-250 placeholder:text-tif11-mut placeholder:transition-opacity placeholder:duration-250 focus:border-tif11-acc focus:bg-tif11-field2 data-fading:placeholder:opacity-0 motion-reduce:placeholder:transition-none">
  </label>
</div>
@import "tailwindcss";

@theme {
  --color-tif11-bg: #12121a;
  --color-tif11-bg: oklch(0.19 0.015 285);
  --color-tif11-field: #1a1a22;
  --color-tif11-field: oklch(0.23 0.015 285);
  --color-tif11-field2: #1f1f2a;
  --color-tif11-field2: oklch(0.26 0.018 285);
  --color-tif11-ink: #f0eeff;
  --color-tif11-ink: oklch(0.95 0.02 290);
  --color-tif11-mut: #b8b6d4;
  --color-tif11-mut: oklch(0.77 0.03 290);
  --color-tif11-acc: #c084fc;
  --color-tif11-acc: oklch(0.75 0.17 305);
  --font-tif11-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
  --font-tif11-mono: 'JetBrains Mono', ui-monospace, monospace;
}

.tif-11 {
  width: 100%;
}

.tif-11 ::selection {
  background: var(--color-tif11-acc);
  color: #12121a;
}
(function () {
  document.querySelectorAll('.tif-11').forEach(function (root) {
    if (root.dataset.bound) return; root.dataset.bound = '1';
    root.querySelectorAll('[data-tif-rotate]').forEach(function (input) {
      var phrases = [];
      try { phrases = JSON.parse(input.getAttribute('data-tif-rotate') || '[]'); } catch (e) {}
      if (!phrases.length) return;
      var i = 0, paused = false;
      var reduce = window.matchMedia('(prefers-reduced-motion: reduce)');
      setInterval(function () {
        if (paused || document.activeElement === input || input.value) return;
        input.dataset.fading = '';
        setTimeout(function () {
          i = (i + 1) % phrases.length;
          input.setAttribute('placeholder', phrases[i]);
          delete input.dataset.fading;
        }, reduce.matches ? 0 : 250);
      }, 2500);
      input.addEventListener('focus', function () { paused = true; });
      input.addEventListener('blur', function () { paused = !!input.value; });
    });
  });
})();

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-tif11-bg: #12121a;
  --color-tif11-bg: oklch(0.19 0.015 285);
  --color-tif11-field: #1a1a22;
  --color-tif11-field: oklch(0.23 0.015 285);
  --color-tif11-field2: #1f1f2a;
  --color-tif11-field2: oklch(0.26 0.018 285);
  --color-tif11-ink: #f0eeff;
  --color-tif11-ink: oklch(0.95 0.02 290);
  --color-tif11-mut: #b8b6d4;
  --color-tif11-mut: oklch(0.77 0.03 290);
  --color-tif11-acc: #c084fc;
  --color-tif11-acc: oklch(0.75 0.17 305);
  --font-tif11-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
  --font-tif11-mono: 'JetBrains Mono', ui-monospace, monospace;
}

.tif-11 {
  width: 100%;
}

.tif-11 ::selection {
  background: var(--color-tif11-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 is-fading class becomes a data-fading attribute read by the data-fading: variant — the script now sets state, never style, which is the rule the whole collection follows.
  • The fade duration is honoured in JS too: under prefers-reduced-motion the swap happens immediately rather than waiting out a fade that is not running.
  • Wrapped per-root with a data-bound guard, so two copies on one page do not double-tick.
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 Rotating Placeholder Input
Source: https://codefronts.com/components/tailwind-css-input-fields/tailwind-rotating-placeholder-input/

The native placeholder attribute itself cycles through four sample queries every 2.5 seconds, fading between them. The script swaps one attribute and flips one data attribute; every visual is a utility, and rotation pauses the instant the user focuses.
## HTML
```html
<div class="tif-11 grid min-h-svh w-full place-items-center bg-tif11-bg px-6 py-14 font-tif11-sans" aria-label="Rotating placeholder input demo">
  <label class="grid w-full max-w-70 gap-1.5">
    <span class="font-tif11-mono text-[10px] tracking-[0.12em] text-tif11-mut uppercase">Search</span>
    <input type="search" name="tif11-q" inputmode="search" placeholder="Search products…" aria-label="Search"
      data-tif-rotate='["Search products…","Search docs…","Search people…","Search settings…"]'
      class="w-full rounded-[10px] border border-white/12 bg-tif11-field px-3.5 py-3 text-sm font-medium text-tif11-ink outline-none transition-[border-color,background-color] duration-250 placeholder:text-tif11-mut placeholder:transition-opacity placeholder:duration-250 focus:border-tif11-acc focus:bg-tif11-field2 data-fading:placeholder:opacity-0 motion-reduce:placeholder:transition-none">
  </label>
</div>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-tif11-bg: #12121a;
  --color-tif11-bg: oklch(0.19 0.015 285);
  --color-tif11-field: #1a1a22;
  --color-tif11-field: oklch(0.23 0.015 285);
  --color-tif11-field2: #1f1f2a;
  --color-tif11-field2: oklch(0.26 0.018 285);
  --color-tif11-ink: #f0eeff;
  --color-tif11-ink: oklch(0.95 0.02 290);
  --color-tif11-mut: #b8b6d4;
  --color-tif11-mut: oklch(0.77 0.03 290);
  --color-tif11-acc: #c084fc;
  --color-tif11-acc: oklch(0.75 0.17 305);
  --font-tif11-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
  --font-tif11-mono: 'JetBrains Mono', ui-monospace, monospace;
}

.tif-11 {
  width: 100%;
}

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

## JavaScript
```js
(function () {
  document.querySelectorAll('.tif-11').forEach(function (root) {
    if (root.dataset.bound) return; root.dataset.bound = '1';
    root.querySelectorAll('[data-tif-rotate]').forEach(function (input) {
      var phrases = [];
      try { phrases = JSON.parse(input.getAttribute('data-tif-rotate') || '[]'); } catch (e) {}
      if (!phrases.length) return;
      var i = 0, paused = false;
      var reduce = window.matchMedia('(prefers-reduced-motion: reduce)');
      setInterval(function () {
        if (paused || document.activeElement === input || input.value) return;
        input.dataset.fading = '';
        setTimeout(function () {
          i = (i + 1) % phrases.length;
          input.setAttribute('placeholder', phrases[i]);
          delete input.dataset.fading;
        }, reduce.matches ? 0 : 250);
      }, 2500);
      input.addEventListener('focus', function () { paused = true; });
      input.addEventListener('blur', function () { paused = !!input.value; });
    });
  });
})();
```

How this works

The script owns state, the class list owns style. Each tick sets data-fading, waits out the fade, swaps the placeholder attribute and clears the flag:

input.dataset.fading = '';
setTimeout(function () {
  input.setAttribute('placeholder', phrases[i]);
  delete input.dataset.fading;
}, 250);

Two guards make it compliant. WCAG 2.2.2 requires that moving content can be paused; a focus listener stops rotation, and every tick short-circuits if the input is focused or already holds a value. A placeholder that mutates while someone is reading it is a defect, not a flourish.

Make it yours

  • Phrases: edit the data-tif-rotate JSON. Any length works; the script cycles modulo length.
  • Cadence: the 2500 in setInterval. Keep the 250ms fade well below it.
  • Fade length: change placeholder:duration-250 and the setTimeout together — they are one number in two places.
  • Opt out: drop the data attribute from any input and the loop skips it.

Gotchas — read before shipping

  • A placeholder is not a label. The visible caption above the field is what a screen reader announces, and it must stay.
  • Rotation without a pause mechanism fails WCAG 2.2.2 — the focus guard is the compliance, not a nicety.
  • Malformed JSON falls back to an empty array and the input keeps its static placeholder, silently.

Browser support

ChromeSafariFirefoxEdge
111+16.4+128+111+

setAttribute, setInterval and ::placeholder opacity are universal; the floor is Tailwind v4's sheet.

Techniques used in this demo

Utilitiesfirst-class utility or variant

Search CodeFronts

Loading…