28 CSS Input Fields07 / 28
Inline Validation
Live `:valid` / `:invalid` feedback. Green tick when correct, red cross when not — driven by `:has()` and pseudo-elements, no JavaScript required.
Published
The code
<label class="if-07">
<span class="if-07-label">Email</span>
<span class="if-07-wrap">
<input type="email" name="email" autocomplete="email" placeholder="name@company.com" required />
</span>
<span class="if-07-help">We'll never share your address.</span>
</label><label class="if-07">
<span class="if-07-label">Email</span>
<span class="if-07-wrap">
<input type="email" name="email" autocomplete="email" placeholder="name@company.com" required />
</span>
<span class="if-07-help">We'll never share your address.</span>
</label>.if-07 {
display: grid;
gap: 6px;
width: 100%;
max-width: 280px;
font-size: 11px;
color: #b8b6d4;
}
.if-07-label {
font-weight: 600;
letter-spacing: 0.04em;
}
.if-07-wrap {
position: relative;
display: block;
}
.if-07 input {
width: 100%;
box-sizing: border-box;
padding: 11px 38px 11px 14px;
background: #1a1a22;
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 8px;
color: #f0eeff;
font-size: 14px;
outline: none;
transition: border-color 0.2s, background 0.2s;
}
.if-07 input:focus {
border-color: #7c6cff;
}
.if-07-wrap::after {
content: "";
position: absolute;
right: 12px;
top: 50%;
transform: translateY(-50%) scale(0.5);
width: 18px;
height: 18px;
border-radius: 50%;
opacity: 0;
transition: transform 0.2s, opacity 0.2s, background 0.2s;
}
.if-07:has(input:not(:placeholder-shown):invalid) input {
border-color: rgba(239, 68, 68, 0.55);
}
.if-07:has(input:not(:placeholder-shown):invalid) .if-07-wrap::after {
opacity: 1;
transform: translateY(-50%) scale(1);
background: #ef4444 url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 10 10' fill='none' stroke='white' stroke-width='1.6' stroke-linecap='round'><path d='M2 2 L8 8 M8 2 L2 8'/></svg>") center/10px no-repeat;
}
.if-07:has(input:not(:placeholder-shown):valid) input {
border-color: rgba(34, 197, 94, 0.55);
}
.if-07:has(input:not(:placeholder-shown):valid) .if-07-wrap::after {
opacity: 1;
transform: translateY(-50%) scale(1);
background: #22c55e url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 10 10' fill='none' stroke='white' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'><path d='M2 5 L4.2 7.2 L8 3'/></svg>") center/10px no-repeat;
}
.if-07-help {
font-size: 10.5px;
color: #b8b6d4;
}.if-07 {
display: grid;
gap: 6px;
width: 100%;
max-width: 280px;
font-size: 11px;
color: #b8b6d4;
}
.if-07-label {
font-weight: 600;
letter-spacing: 0.04em;
}
.if-07-wrap {
position: relative;
display: block;
}
.if-07 input {
width: 100%;
box-sizing: border-box;
padding: 11px 38px 11px 14px;
background: #1a1a22;
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 8px;
color: #f0eeff;
font-size: 14px;
outline: none;
transition: border-color 0.2s, background 0.2s;
}
.if-07 input:focus {
border-color: #7c6cff;
}
.if-07-wrap::after {
content: "";
position: absolute;
right: 12px;
top: 50%;
transform: translateY(-50%) scale(0.5);
width: 18px;
height: 18px;
border-radius: 50%;
opacity: 0;
transition: transform 0.2s, opacity 0.2s, background 0.2s;
}
.if-07:has(input:not(:placeholder-shown):invalid) input {
border-color: rgba(239, 68, 68, 0.55);
}
.if-07:has(input:not(:placeholder-shown):invalid) .if-07-wrap::after {
opacity: 1;
transform: translateY(-50%) scale(1);
background: #ef4444 url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 10 10' fill='none' stroke='white' stroke-width='1.6' stroke-linecap='round'><path d='M2 2 L8 8 M8 2 L2 8'/></svg>") center/10px no-repeat;
}
.if-07:has(input:not(:placeholder-shown):valid) input {
border-color: rgba(34, 197, 94, 0.55);
}
.if-07:has(input:not(:placeholder-shown):valid) .if-07-wrap::after {
opacity: 1;
transform: translateY(-50%) scale(1);
background: #22c55e url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 10 10' fill='none' stroke='white' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'><path d='M2 5 L4.2 7.2 L8 3'/></svg>") center/10px no-repeat;
}
.if-07-help {
font-size: 10.5px;
color: #b8b6d4;
}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: Inline Validation
Source: https://codefronts.com/components/css-input-fields/inline-validation/
Live `:valid` / `:invalid` feedback. Green tick when correct, red cross when not — driven by `:has()` and pseudo-elements, no JavaScript required.
## HTML
```html
<label class="if-07">
<span class="if-07-label">Email</span>
<span class="if-07-wrap">
<input type="email" name="email" autocomplete="email" placeholder="name@company.com" required />
</span>
<span class="if-07-help">We'll never share your address.</span>
</label>
```
## CSS
```css
.if-07 {
display: grid;
gap: 6px;
width: 100%;
max-width: 280px;
font-size: 11px;
color: #b8b6d4;
}
.if-07-label {
font-weight: 600;
letter-spacing: 0.04em;
}
.if-07-wrap {
position: relative;
display: block;
}
.if-07 input {
width: 100%;
box-sizing: border-box;
padding: 11px 38px 11px 14px;
background: #1a1a22;
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 8px;
color: #f0eeff;
font-size: 14px;
outline: none;
transition: border-color 0.2s, background 0.2s;
}
.if-07 input:focus {
border-color: #7c6cff;
}
.if-07-wrap::after {
content: "";
position: absolute;
right: 12px;
top: 50%;
transform: translateY(-50%) scale(0.5);
width: 18px;
height: 18px;
border-radius: 50%;
opacity: 0;
transition: transform 0.2s, opacity 0.2s, background 0.2s;
}
.if-07:has(input:not(:placeholder-shown):invalid) input {
border-color: rgba(239, 68, 68, 0.55);
}
.if-07:has(input:not(:placeholder-shown):invalid) .if-07-wrap::after {
opacity: 1;
transform: translateY(-50%) scale(1);
background: #ef4444 url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 10 10' fill='none' stroke='white' stroke-width='1.6' stroke-linecap='round'><path d='M2 2 L8 8 M8 2 L2 8'/></svg>") center/10px no-repeat;
}
.if-07:has(input:not(:placeholder-shown):valid) input {
border-color: rgba(34, 197, 94, 0.55);
}
.if-07:has(input:not(:placeholder-shown):valid) .if-07-wrap::after {
opacity: 1;
transform: translateY(-50%) scale(1);
background: #22c55e url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 10 10' fill='none' stroke='white' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'><path d='M2 5 L4.2 7.2 L8 3'/></svg>") center/10px no-repeat;
}
.if-07-help {
font-size: 10.5px;
color: #b8b6d4;
}
```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: Inline Validation
Source: https://codefronts.com/components/css-input-fields/inline-validation/
Live `:valid` / `:invalid` feedback. Green tick when correct, red cross when not — driven by `:has()` and pseudo-elements, no JavaScript required.
## HTML
```html
<label class="if-07">
<span class="if-07-label">Email</span>
<span class="if-07-wrap">
<input type="email" name="email" autocomplete="email" placeholder="name@company.com" required />
</span>
<span class="if-07-help">We'll never share your address.</span>
</label>
```
## CSS
```css
.if-07 {
display: grid;
gap: 6px;
width: 100%;
max-width: 280px;
font-size: 11px;
color: #b8b6d4;
}
.if-07-label {
font-weight: 600;
letter-spacing: 0.04em;
}
.if-07-wrap {
position: relative;
display: block;
}
.if-07 input {
width: 100%;
box-sizing: border-box;
padding: 11px 38px 11px 14px;
background: #1a1a22;
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 8px;
color: #f0eeff;
font-size: 14px;
outline: none;
transition: border-color 0.2s, background 0.2s;
}
.if-07 input:focus {
border-color: #7c6cff;
}
.if-07-wrap::after {
content: "";
position: absolute;
right: 12px;
top: 50%;
transform: translateY(-50%) scale(0.5);
width: 18px;
height: 18px;
border-radius: 50%;
opacity: 0;
transition: transform 0.2s, opacity 0.2s, background 0.2s;
}
.if-07:has(input:not(:placeholder-shown):invalid) input {
border-color: rgba(239, 68, 68, 0.55);
}
.if-07:has(input:not(:placeholder-shown):invalid) .if-07-wrap::after {
opacity: 1;
transform: translateY(-50%) scale(1);
background: #ef4444 url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 10 10' fill='none' stroke='white' stroke-width='1.6' stroke-linecap='round'><path d='M2 2 L8 8 M8 2 L2 8'/></svg>") center/10px no-repeat;
}
.if-07:has(input:not(:placeholder-shown):valid) input {
border-color: rgba(34, 197, 94, 0.55);
}
.if-07:has(input:not(:placeholder-shown):valid) .if-07-wrap::after {
opacity: 1;
transform: translateY(-50%) scale(1);
background: #22c55e url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 10 10' fill='none' stroke='white' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'><path d='M2 5 L4.2 7.2 L8 3'/></svg>") center/10px no-repeat;
}
.if-07-help {
font-size: 10.5px;
color: #b8b6d4;
}
```How this works
Constraint validation runs natively against the type="email" and required attributes. :has(input:not(:placeholder-shown):valid) on the label matches once the user has typed something valid; the same pattern with :invalid flags the error state. The tick or cross is a single ::after pseudo-element with an inline SVG data-URI background.
Gating on :not(:placeholder-shown) keeps the indicator hidden while the field is empty, so pure-CSS form validation without JavaScript still feels forgiving — no red cross the moment the input mounts.
Make it yours
- Change the trigger rule by swapping
requiredfor apatternattribute — regex, min-length, andtype="url"all feed:valid. - Replace the SVG data-URI in the
::afterbackground to swap tick and cross for your own icons. - Retint the success and error states via the
#22c55eand#ef4444colours in both border and icon backgrounds. - Slow the reveal by adjusting the shared
transitionon::after(currently0.2son transform, opacity, background).
Gotchas — read before shipping
- Because the trigger is
:not(:placeholder-shown), theplaceholderattribute must be non-empty — a whitespace" "works if you want no visible placeholder. :invalidmatches before the user interacts; the:user-invalidpseudo-class fires only after a blur or submit and is a better UX signal if you can require Chrome 119+ and Safari 16.5+.:has()gates the whole effect — Chrome 105, Safari 15.4, Firefox 121. Older browsers show a plain field with no indicator, which degrades cleanly.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 105+ | 15.4+ | 121+ | 105+ |
:has() is the floor; the fallback is an unindicated but fully functional field.