A search field that types example queries into its placeholder, deletes each one and moves to the next. Three stacked phrases share one caret that sits after whichever is showing.
Also known as: animated placeholder text, typing search bar css, placeholder typewriter.
<div class="txt-search-placeholder-typing"> <svg class="txt-search-placeholder-typing-icon" viewBox="0 0 24 24" aria-hidden="true"><circle cx="11" cy="11" r="6.5"/><path d="m16 16 4.5 4.5"/></svg> <span class="txt-search-placeholder-typing-label">Search for</span> <span class="txt-search-placeholder-typing-words" aria-hidden="true"><span>rain jackets</span><span>hiking boots</span><span>wool beanies</span></span><span class="txt-search-placeholder-typing-caret" aria-hidden="true"></span> </div>
.txt-search-placeholder-typing {
--c: var(--txt-color, #3b82f6);
font-size: var(--txt-size, 32px);
font-family: system-ui, sans-serif;
font-weight: 700;
line-height: 1.2;
display: inline-flex;
align-items: center;
box-sizing: content-box;
min-width: 26ch;
padding: 0.6em 1.1em 0.6em 0.9em;
border-radius: 99em;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-size: calc(var(--txt-size, 32px) * 0.6);
font-weight: 500;
white-space: pre;
background: color-mix(in srgb, currentColor 5%, transparent);
box-shadow: inset 0 0 0 1px color-mix(in srgb, currentColor 16%, transparent);}
.txt-search-placeholder-typing-icon {
display: block;
width: 1.2em;
height: 1.2em;
margin-right: 0.6em;
fill: none;
stroke: color-mix(in srgb, currentColor 55%, transparent);
stroke-width: 2.2;
stroke-linecap: round;
}
.txt-search-placeholder-typing-label {
margin-right: 1ch;
color: color-mix(in srgb, currentColor 50%, transparent);
}
/* the phrases overlap, so this box is as wide as the one being typed */
.txt-search-placeholder-typing-words {
display: inline-grid;
color: var(--c);
}
.txt-search-placeholder-typing-words span {
grid-area: 1 / 1;
width: 0;
overflow: hidden;
/* every phrase is 12 characters; each gets a 3s slot of the 9s loop */
animation: txt-search-placeholder-typing-type 9s linear infinite;
}
/* the first phrase rests fully typed, which is what reduced motion shows */
.txt-search-placeholder-typing-words span:nth-child(1) { width: 12ch; }
/* negative delays start the others mid-cycle, already empty */
.txt-search-placeholder-typing-words span:nth-child(2) { animation-delay: -6s; }
.txt-search-placeholder-typing-words span:nth-child(3) { animation-delay: -3s; }
.txt-search-placeholder-typing-caret {
width: 0.1em;
height: 1.15em;
background: var(--c);
animation: txt-search-placeholder-typing-blink 1s step-end infinite;
}
@keyframes txt-search-placeholder-typing-type {
0% { width: 0; animation-timing-function: steps(12); }
13%, 25% { width: 12ch; animation-timing-function: steps(12); }
30%, 100% { width: 0; }
}
@keyframes txt-search-placeholder-typing-blink {
50% { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
.txt-search-placeholder-typing, .txt-search-placeholder-typing *, .txt-search-placeholder-typing::before, .txt-search-placeholder-typing::after, .txt-search-placeholder-typing *::before, .txt-search-placeholder-typing *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}--txt-color on the element or a parent.
Currently previewing #3b82f6.--txt-size. Every other dimension is in
em, so the whole thing scales together. Currently previewing 40px.transition or animation
shorthand. Staggered delays are relative, so they keep their rhythm.