28 CSS Input Fields11 / 28

Light JSMIT licensed

Rotating Placeholder

The native placeholder attribute itself rotates through four sample queries every 2.5 seconds — "Search products…" → "Search docs…" → "Search people…" → "Search settings…". Light JS swaps the attribute; the browser does the rest. Stops the moment the user focuses or starts typing.

Published

Live Demo
Try it

The code

<label class="if-11">
  <span class="if-11-cap">Search</span>
  <input
    type="search"
    name="q"
    inputmode="search"
    placeholder="Search products…"
    aria-label="Search"
    data-if-rotate='["Search products…","Search docs…","Search people…","Search settings…"]'
  />
</label>
.if-11 {
  display: grid;
  gap: 6px;
  width: 100%;
  max-width: 280px;
}

.if-11-cap {
  font-family: "JetBrains Mono", monospace;
  font-size: 10px;
  letter-spacing: 0.12em;
  color: #b8b6d4;
  text-transform: uppercase;
}

.if-11 input {
  width: 100%;
  box-sizing: border-box;
  padding: 12px 14px;
  background: #1a1a22;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 10px;
  color: #f0eeff;
  font: 500 14px/1 system-ui, sans-serif;
  outline: none;
  transition: border-color 0.25s, background 0.2s;
}

.if-11 input:focus {
  border-color: #c084fc;
  background: #1f1f2a;
}

.if-11 input::placeholder {
  color: #b8b6d4;
  transition: opacity 0.25s ease;
}

.if-11 input.is-fading::placeholder {
  opacity: 0;
}
// Rotating Placeholder — swap the input's placeholder attribute on a 2.5s loop
document.querySelectorAll("[data-if-rotate]").forEach(function (input) {
  var phrases;
  try {
    phrases = JSON.parse(input.getAttribute("data-if-rotate") || "[]");
  } catch (e) {
    phrases = [];
  }
  if (!phrases.length) return;
  var i = 0,
    paused = false;
  function tick() {
    if (paused || document.activeElement === input || input.value) return;
    input.classList.add("is-fading");
    setTimeout(function () {
      i = (i + 1) % phrases.length;
      input.setAttribute("placeholder", phrases[i]);
      input.classList.remove("is-fading");
    }, 250);
  }
  setInterval(tick, 2500);
  input.addEventListener("focus", function () {
    paused = true;
  });
  input.addEventListener("blur", function () {
    paused = !!input.value;
  });
});
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 Input Field 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: Rotating Placeholder
Source: https://codefronts.com/components/css-input-fields/rotating-placeholder/

The native placeholder attribute itself rotates through four sample queries every 2.5 seconds — "Search products…" → "Search docs…" → "Search people…" → "Search settings…". Light JS swaps the attribute; the browser does the rest. Stops the moment the user focuses or starts typing.
## HTML
```html
<label class="if-11">
  <span class="if-11-cap">Search</span>
  <input
    type="search"
    name="q"
    inputmode="search"
    placeholder="Search products…"
    aria-label="Search"
    data-if-rotate='["Search products…","Search docs…","Search people…","Search settings…"]'
  />
</label>
```
## CSS
```css
.if-11 {
  display: grid;
  gap: 6px;
  width: 100%;
  max-width: 280px;
}

.if-11-cap {
  font-family: "JetBrains Mono", monospace;
  font-size: 10px;
  letter-spacing: 0.12em;
  color: #b8b6d4;
  text-transform: uppercase;
}

.if-11 input {
  width: 100%;
  box-sizing: border-box;
  padding: 12px 14px;
  background: #1a1a22;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 10px;
  color: #f0eeff;
  font: 500 14px/1 system-ui, sans-serif;
  outline: none;
  transition: border-color 0.25s, background 0.2s;
}

.if-11 input:focus {
  border-color: #c084fc;
  background: #1f1f2a;
}

.if-11 input::placeholder {
  color: #b8b6d4;
  transition: opacity 0.25s ease;
}

.if-11 input.is-fading::placeholder {
  opacity: 0;
}
```

## JavaScript
```js
// Rotating Placeholder — swap the input's placeholder attribute on a 2.5s loop
document.querySelectorAll("[data-if-rotate]").forEach(function (input) {
  var phrases;
  try {
    phrases = JSON.parse(input.getAttribute("data-if-rotate") || "[]");
  } catch (e) {
    phrases = [];
  }
  if (!phrases.length) return;
  var i = 0,
    paused = false;
  function tick() {
    if (paused || document.activeElement === input || input.value) return;
    input.classList.add("is-fading");
    setTimeout(function () {
      i = (i + 1) % phrases.length;
      input.setAttribute("placeholder", phrases[i]);
      input.classList.remove("is-fading");
    }, 250);
  }
  setInterval(tick, 2500);
  input.addEventListener("focus", function () {
    paused = true;
  });
  input.addEventListener("blur", function () {
    paused = !!input.value;
  });
});
```

How this works

The input carries a data-if-rotate attribute holding a JSON array of phrases. On load, the script parses that array once per input, then runs setInterval every 2500ms. Each tick adds an is-fading class that transitions ::placeholder opacity to zero, waits 250ms, swaps the placeholder attribute via setAttribute, and removes the class so the new phrase fades in.

Two guards keep it well-behaved: a focus listener sets paused = true so rotation halts the moment the user taps the field, and the tick short-circuits if document.activeElement is the input or if the input already has a value. This makes the animated placeholder input pattern safe for accessibility — the placeholder never mutates while the user is reading or typing.

Make it yours

  • Edit the phrases in the data-if-rotate JSON array — any length works, the script cycles modulo length.
  • Change rotation cadence by swapping the 2500 in setInterval; keep the 250ms fade below your interval.
  • Match the fade duration to your brand by editing the ::placeholder transition and the setTimeout delay together.
  • Drop the data-if-rotate attribute on any input and it opts out — the querySelectorAll loop skips it.

Gotchas — read before shipping

  • Placeholder text is not an accessible label — keep the visible caption or aria-label so screen readers announce the field's purpose.
  • Rotating placeholders violate WCAG 2.2.2 if users cannot pause; the focus-pause listener is what keeps this pattern compliant.
  • Malformed JSON in data-if-rotate silently falls back to an empty array — the input just sits with its static placeholder.

Browser support

ChromeSafariFirefoxEdge
60+12+60+60+

Uses only setAttribute, setInterval, and ::placeholder opacity — universally supported across every modern engine including mobile.

Search CodeFronts

Loading…