SolutionsCase StudiesAI ToolsResourcesAboutContact
Get Your Free AI Automation Audit Book a Free 30-Minute Strategy Call
Playground~/frontend Open editor

~/css/input-fields

8 CSS Input Fields

5 Pure CSS3 with JSPublished 2026-10-04Free · no sign-up

Eight text input patterns for forms: a floating label, a search box with a clear button, an underline field, validation states, one-time code boxes, a password field with a Show button, prefix and suffix add-ons, and a textarea with a character counter.

01. Floating Label Input

FormLabel

The label sits inside the field like a placeholder and floats above it once the field has focus or a value. It relies on :placeholder-shown with a single-space placeholder, so the real label is always present for screen readers and autofill.

View code
<div class="fl">
  <input id="fl-email" type="email" placeholder=" " autocomplete="email">
  <label for="fl-email">Email address</label>
</div>
.fl { position: relative; width: min(340px, 90vw); font-family: Inter, system-ui, sans-serif; }
.fl input { width: 100%; box-sizing: border-box; padding: 22px 14px 8px; border: 1.5px solid #cdd3de; border-radius: 12px; background: #fff; color: #101828; font-size: 16px; outline: none; transition: border-color .15s, box-shadow .15s; }
.fl label { position: absolute; left: 15px; top: 16px; color: #667085; font-size: 16px; pointer-events: none; transform-origin: left top; transition: transform .15s, color .15s; }
.fl input:focus { border-color: #2563eb; box-shadow: 0 0 0 4px rgba(37,99,235,.15); }
.fl input:focus + label, .fl input:not(:placeholder-shown) + label { transform: translateY(-9px) scale(.78); color: #2563eb; }
.fl input:not(:focus):not(:placeholder-shown) + label { color: #475467; }

02. Search Input with Icon and Clear

SearchIcon

A search field with a magnifier on the left and a clear button on the right that only appears when there is text. The icon is decorative SVG, the clear button has its own accessible name, and type="search" brings the right mobile keyboard.

View code
<form class="srch" role="search" onsubmit="event.preventDefault()">
  <label for="q" class="sr">Search products</label>
  <svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="M20 20l-3.5-3.5"/></svg>
  <input id="q" type="search" placeholder="Search products" value="linen shirt">
  <button type="button" aria-label="Clear search" onclick="var i=this.form.q; i.value=''; i.focus();">&times;</button>
</form>
.srch { position: relative; width: min(360px, 90vw); font-family: Inter, system-ui, sans-serif; }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
.srch svg { position: absolute; left: 14px; top: 50%; width: 18px; height: 18px; transform: translateY(-50%); fill: none; stroke: #8a94a8; stroke-width: 2; stroke-linecap: round; }
.srch input { width: 100%; box-sizing: border-box; padding: 13px 42px; border: 1px solid #2d3447; border-radius: 999px; background: #151a26; color: #e7ebf5; font-size: 15px; outline: none; }
.srch input::-webkit-search-cancel-button { display: none; }
.srch input:focus { border-color: #7c9cff; box-shadow: 0 0 0 3px rgba(124,156,255,.25); }
.srch button { position: absolute; right: 8px; top: 50%; width: 30px; height: 30px; transform: translateY(-50%); border: 0; border-radius: 50%; background: #262d3e; color: #c9d1e2; font-size: 18px; cursor: pointer; }
.srch:has(input:placeholder-shown) button { display: none; }
.srch button:focus-visible { outline: 2px solid #7c9cff; }

03. Underline Input with Animated Bar

MinimalAnimation

A minimal field with only a bottom border. On focus a coloured bar grows outward from the centre, drawn with a background gradient whose size animates, so there are no extra elements and the effect is skipped when reduced motion is set.

View code
<label class="ul">
  <span>Full name</span>
  <input type="text" autocomplete="name" placeholder="Ada Lovelace">
</label>
.ul { display: grid; gap: 6px; width: min(320px, 88vw); color: #475467; font: 600 13px Inter, system-ui, sans-serif; }
.ul input { padding: 10px 2px; border: 0; border-bottom: 1.5px solid #c4cad6; background: linear-gradient(#7c3aed, #7c3aed) center bottom / 0 2px no-repeat; color: #101828; font-size: 17px; outline: none; transition: background-size .3s; }
.ul input:focus { background-size: 100% 2px; }
.ul input::placeholder { color: #8a93a6; }
@media (prefers-reduced-motion: reduce) { .ul input { transition: none; } }

04. Input Validation States

ValidationFeedback

The three states every form needs, side by side: default with a hint, an error with a message tied to the field through aria-describedby and aria-invalid, and a success state. Each state is shown with an icon and text as well as colour.

View code
<div class="st">
  <div class="f"><label for="a1">Username</label><input id="a1" value="sam" aria-describedby="a1h"><p id="a1h" class="h">3 to 20 letters or numbers.</p></div>
  <div class="f bad"><label for="a2">Email</label><input id="a2" value="sam@" aria-invalid="true" aria-describedby="a2h"><p id="a2h" class="h">&#9888; Enter a full email address, like name@example.com.</p></div>
  <div class="f good"><label for="a3">Store URL</label><input id="a3" value="sams-shop" aria-describedby="a3h"><p id="a3h" class="h">&#10003; That name is available.</p></div>
</div>
.st { display: grid; gap: 14px; width: min(360px, 90vw); font-family: Inter, system-ui, sans-serif; }
.f label { display: block; margin-bottom: 6px; color: #344054; font-size: 14px; font-weight: 600; }
.f input { width: 100%; box-sizing: border-box; padding: 10px 12px; border: 1.5px solid #d0d5dd; border-radius: 10px; color: #101828; font-size: 15px; outline: none; }
.f input:focus { border-color: #2563eb; box-shadow: 0 0 0 3px rgba(37,99,235,.15); }
.h { margin: 6px 0 0; color: #5d6679; font-size: 13px; }
.bad input { border-color: #d92d20; }
.bad .h { color: #b42318; }
.good input { border-color: #079455; }
.good .h { color: #067647; }

05. One-Time Code Boxes

AuthJavaScript

Six boxes for a verification code. Typing moves to the next box, Backspace moves back and pasting a whole code fills every box. Each box is labelled for screen readers, and autocomplete="one-time-code" lets phones offer the code from a text message.

View code
<fieldset class="otp">
  <legend>Enter the 6-digit code we sent you</legend>
  <div class="boxes">
    <input inputmode="numeric" maxlength="1" autocomplete="one-time-code" aria-label="Digit 1">
    <input inputmode="numeric" maxlength="1" aria-label="Digit 2">
    <input inputmode="numeric" maxlength="1" aria-label="Digit 3">
    <input inputmode="numeric" maxlength="1" aria-label="Digit 4">
    <input inputmode="numeric" maxlength="1" aria-label="Digit 5">
    <input inputmode="numeric" maxlength="1" aria-label="Digit 6">
  </div>
</fieldset>
.otp { box-sizing: border-box; max-width: 100%; margin: 0; padding: 0; border: 0; color: #c9d1e2; font: 500 14px Inter, system-ui, sans-serif; text-align: center; }
.otp legend { margin: 0 auto 14px; }
.boxes { display: flex; gap: 8px; justify-content: center; }
.otp input { width: 40px; height: 56px; border: 1.5px solid #343c51; border-radius: 12px; background: #151a26; color: #fff; font: 700 24px "JetBrains Mono", monospace; text-align: center; outline: none; }
.otp input:focus { border-color: #34d399; box-shadow: 0 0 0 3px rgba(52,211,153,.25); }
const boxes = [...document.querySelectorAll('.otp input')];
boxes.forEach((b, i) => {
  b.addEventListener('input', () => { b.value = b.value.replace(/\D/g, ''); if (b.value && boxes[i + 1]) boxes[i + 1].focus(); });
  b.addEventListener('keydown', e => { if (e.key === 'Backspace' && !b.value && boxes[i - 1]) boxes[i - 1].focus(); });
  b.addEventListener('paste', e => {
    const digits = (e.clipboardData.getData('text').match(/\d/g) || []).slice(0, 6);
    if (!digits.length) return;
    e.preventDefault();
    digits.forEach((d, k) => { if (boxes[k]) boxes[k].value = d; });
    boxes[Math.min(digits.length, 5)].focus();
  });
});

06. Password with Show Toggle

AuthForm

A password field with a Show and Hide button inside it. The button swaps the input type and updates aria-pressed so screen readers announce the state, and autocomplete="current-password" keeps password managers working.

View code
<div class="pw">
  <label for="pw-in">Password</label>
  <div class="pw-box">
    <input id="pw-in" type="password" value="correct horse" autocomplete="current-password">
    <button type="button" aria-pressed="false" aria-controls="pw-in">Show</button>
  </div>
</div>
.pw { width: min(340px, 90vw); font-family: Inter, system-ui, sans-serif; }
.pw label { display: block; margin-bottom: 6px; color: #344054; font-size: 14px; font-weight: 600; }
.pw-box { display: flex; align-items: center; border: 1.5px solid #d0d5dd; border-radius: 10px; background: #fff; }
.pw-box:focus-within { border-color: #2563eb; box-shadow: 0 0 0 3px rgba(37,99,235,.15); }
.pw-box input { flex: 1; min-width: 0; padding: 11px 12px; border: 0; background: none; color: #101828; font-size: 15px; outline: none; }
.pw-box button { margin-right: 6px; padding: 6px 10px; border: 0; border-radius: 7px; background: #f2f4f7; color: #344054; font: 700 13px Inter, sans-serif; cursor: pointer; }
.pw-box button:focus-visible { outline: 2px solid #2563eb; }
const btn = document.querySelector('.pw-box button'), field = document.getElementById('pw-in');
btn.addEventListener('click', () => {
  const show = field.type === 'password';
  field.type = show ? 'text' : 'password';
  btn.textContent = show ? 'Hide' : 'Show';
  btn.setAttribute('aria-pressed', String(show));
});

07. Input with Prefix and Suffix

FormAddon

Fields with fixed text attached, such as a currency before a price or a domain after a subdomain. The add-ons are plain spans inside a flex wrapper, focus styling moves to the wrapper with :focus-within, and the unit is also included in the label.

View code
<div class="pf">
  <label for="price">Price (USD)</label>
  <div class="grp"><span>$</span><input id="price" inputmode="decimal" value="49.00"></div>
  <label for="sub">Shop address</label>
  <div class="grp"><input id="sub" value="sunrise-ceramics"><span>.myshop.com</span></div>
</div>
.pf { display: grid; gap: 6px; width: min(360px, 90vw); color: #c9d1e2; font: 600 13px Inter, system-ui, sans-serif; }
.pf label:not(:first-child) { margin-top: 10px; }
.grp { display: flex; align-items: stretch; overflow: hidden; border: 1px solid #343c51; border-radius: 10px; background: #151a26; }
.grp:focus-within { border-color: #fbbf24; box-shadow: 0 0 0 3px rgba(251,191,36,.2); }
.grp span { display: grid; place-items: center; padding: 0 12px; background: #1e2433; color: #9aa4b8; font-weight: 500; }
.grp input { flex: 1; min-width: 0; padding: 11px 12px; border: 0; background: none; color: #fff; font: 500 15px Inter, sans-serif; outline: none; }

08. Textarea with Character Counter

FormJavaScript

A review box that counts characters as you type and warns as you approach the limit. The counter is a polite live region that only announces at key thresholds, and the field grows with its content using field-sizing where the browser supports it.

View code
<div class="ta">
  <label for="rev">Your review</label>
  <textarea id="rev" maxlength="200" aria-describedby="rev-c" rows="3">Lovely glaze, arrived well packed.</textarea>
  <p id="rev-c" class="cnt" aria-live="polite"></p>
</div>
.ta { width: min(380px, 90vw); font-family: Inter, system-ui, sans-serif; }
.ta label { display: block; margin-bottom: 6px; color: #344054; font-size: 14px; font-weight: 600; }
.ta textarea { width: 100%; box-sizing: border-box; min-height: 90px; padding: 11px 12px; border: 1.5px solid #d0d5dd; border-radius: 10px; color: #101828; font: 15px/1.5 Inter, sans-serif; resize: vertical; field-sizing: content; outline: none; }
.ta textarea:focus { border-color: #0d9488; box-shadow: 0 0 0 3px rgba(13,148,136,.18); }
.cnt { margin: 6px 0 0; color: #5d6679; font-size: 13px; text-align: right; }
.cnt.warn { color: #b54708; font-weight: 600; }
const t = document.getElementById('rev'), c = document.getElementById('rev-c');
function upd() {
  const left = t.maxLength - t.value.length;
  c.textContent = left + ' characters left';
  c.classList.toggle('warn', left <= 20);
}
t.addEventListener('input', upd);
upd();

About these CSS Input Fields

Always keep a real label

Placeholders disappear as soon as you type, so they cannot replace a label. Every field here has a <label> linked by for and id. The floating label trick keeps that real label and only moves it, using :placeholder-shown with a single-space placeholder.

Use the right type and autocomplete

The input type chooses the mobile keyboard: email, tel, search or numeric through inputmode. The autocomplete attribute, such as email, current-password or one-time-code, lets browsers and password managers fill fields correctly, which saves people time and mistakes.

Errors people can act on

An error message should say how to fix the problem, sit next to the field and be linked with aria-describedby. Add aria-invalid="true" so screen readers announce the field as invalid, and show the state with an icon and text as well as a red border.

Focus styles

Removing the default outline without a replacement makes a form unusable with a keyboard. These demos replace it with a coloured border and a soft ring, or a thicker underline, that is clearly visible on the page background.

Frequently asked questions

Which fields use JavaScript?

The one-time code, password toggle and character counter use a few lines of JavaScript. The others are pure CSS.

Does the floating label work with autofill?

Yes. Autofilled values make :placeholder-shown false, so the label floats above the value.

How do I add an icon inside an input?

Position the icon absolutely inside a relative wrapper and add padding to the input, as in the search demo.