1. Basic Form — clean labels, rounded inputs, primary button
<form> <label>Name</label> <input type="text" placeholder="Ada Lovelace"> <label>Email</label> <input type="email" placeholder="ada@example.com"> <label>Role</label> <select> <option>Designer</option> <option>Engineer</option> <option>Student</option> </select> <label>Message</label> <textarea rows="4" placeholder="Say hi..."></textarea> <button class="primary" type="submit">Submit</button> </form>
form { display: flex; flex-direction: column; gap: 14px; } input, textarea, select { padding: 12px 14px; border: 1px solid #d4d4d8; border-radius: 8px; } input:focus { outline: none; border-color: #111; }
2. Floating Label — label shrinks up on focus (peer selector trick)
<div class="field"> <!-- placeholder=" " is critical for :placeholder-shown --> <input type="text" id="fname" placeholder=" "> <label for="fname">Full Name</label> </div> <div class="field"> <input type="email" id="femail" placeholder=" "> <label for="femail">Email Address</label> </div> <div class="field"> <input type="tel" id="fphone" placeholder=" "> <label for="fphone">Phone Number</label> </div> <button class="primary" type="submit">Continue</button>
.field { position: relative; } .field input { padding: 22px 14px 10px; /* room for label inside */ } .field label { position: absolute; top: 14px; left: 14px; color: #888; pointer-events: none; transition: all .2s; } /* Shrink label when input is focused OR has content */ input:focus + label, input:not(:placeholder-shown) + label { top: 5px; font-size: 11px; color: #ec4899; }
3. Custom Radio & Checkbox — fully styled, keyboard-accessible
Pick one — Subscription plan
Select interests
<h4>Pick one — Subscription plan</h4> <label class="opt"> <!-- Real input is hidden; .box.round is the visual --> <input type="radio" name="plan"> <span class="box round"></span> Starter — free </label> <label class="opt"> <input type="radio" name="plan" checked> <span class="box round"></span> Pro — $9/mo </label> <label class="opt"> <input type="radio" name="plan"> <span class="box round"></span> Team — $29/mo </label> <h4>Select interests</h4> <label class="opt"> <!-- Real input is hidden; .box is the visual --> <input type="checkbox" checked> <span class="box"></span> Design </label> <label class="opt"> <input type="checkbox"> <span class="box"></span> Development </label> <label class="opt"> <input type="checkbox" checked> <span class="box"></span> Writing </label> <label class="opt"> <input type="checkbox"> <span class="box"></span> Photography </label>
/* Hide the real input */ input[type="checkbox"], input[type="radio"] { display: none; } .box { width: 20px; height: 20px; border: 2px solid #d4d4d8; border-radius: 6px; display: flex; align-items: center; justify-content: center; } /* Inner dot drawn with ::after, scaled to 0 by default */ .box::after { content: ""; width: 10px; height: 10px; background: #fff; border-radius: 3px; transform: scale(0); transition: transform .15s; } /* Adjacent sibling combinator: when input is checked, style .box */ input:checked + .box { background: #ec4899; border-color: #ec4899; } input:checked + .box::after { transform: scale(1); }
4. Validation States — success, error, and helper messages
<div class="field success"> <label>Username</label> <input type="text" value="ada_lovelace"> <i class="ph-fill ph-check-circle state-icon"></i> <div class="msg">✓ Looks good — username is available</div> </div> <div class="field error"> <label>Email</label> <input type="email" value="not-an-email"> <i class="ph-fill ph-warning-circle state-icon"></i> <div class="msg">⚠ Please enter a valid email address</div> </div> <div class="field"> <label>Website (optional)</label> <input type="url" placeholder="https://yoursite.com"> </div> <button class="primary" type="submit">Create account</button>
.field.success input { border-color: #10b981; } .field.success .msg { color: #10b981; } .field.success .state-icon { color: #10b981; } .field.error input { border-color: #ef4444; background: #fef2f2; } .field.error .msg { color: #ef4444; } /* Icon sits inside the input on the right */ .state-icon { position: absolute; right: 12px; top: 50%; transform: translateY(-50%); }
5. Range Slider + Toggle Switch — styled with custom thumb + sliding pill
<div> <div class="slider-row"><label>Budget</label><span class="amount">$2,500</span></div> <input type="range" min="0" max="10000" value="2500"> </div> <div> <div class="slider-row"><label>Quality</label><span class="amount">High</span></div> <input type="range" min="0" max="100" value="80"> </div> <div class="toggle"> <label>Email notifications</label> <span class="switch"> <input type="checkbox" checked> <span class="switch-slider"></span> </span> </div> <div class="toggle"> <label>Dark mode</label> <span class="switch"> <input type="checkbox"> <span class="switch-slider"></span> </span> </div> <div class="toggle"> <label>Auto-save</label> <span class="switch"> <input type="checkbox" checked> <span class="switch-slider"></span> </span> </div>
/* Style the range thumb (WebKit) */ input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; width: 22px; height: 22px; border-radius: 50%; background: linear-gradient(135deg, #ec4899, #8b5cf6); cursor: pointer; } /* Toggle switch — checkbox hack */ .switch { width: 46px; height: 26px; position: relative; } .switch input { display: none; } .switch-slider { position: absolute; inset: 0; background: #d4d4d8; border-radius: 999px; } .switch-slider::before { content: ""; position: absolute; width: 20px; height: 20px; left: 3px; top: 3px; background: #fff; border-radius: 50%; transition: transform .2s; } /* When checked, slide the knob + color the track */ input:checked + .switch-slider { background: #10b981; } input:checked + .switch-slider::before { transform: translateX(20px); }
6. Multi-Step Form — progress indicator + step-by-step fields
Step 3 of 4
About You
<div class="steps"> <div class="step done"></div> <div class="step done"></div> <div class="step current"></div> <div class="step"></div> </div> <div class="step-label"> <span>Step <strong>3</strong> of 4</span> <span>About You</span> </div> <form> <label>Company name</label> <input type="text" placeholder="Acme Inc."> <label>Team size</label> <select> <option>Just me</option> <option>2–10</option> <option>11–50</option> <option>50+</option> </select> <label>What brings you here?</label> <textarea rows="3"></textarea> <button type="button" class="primary">← Back</button> <button type="submit" class="primary">Next step →</button> </form>
.steps { display: flex; gap: 8px; } .step { flex: 1; height: 6px; background: #e5e7eb; border-radius: 999px; } .step.done { background: #ec4899; } /* Half-filled for current step */ .step.current { background: linear-gradient(90deg, #ec4899 50%, #e5e7eb 50%); }
7. Search with Suggestions — icon inside input + dropdown list
- Cards LabLab
- Icon TutorialTutorial
- Neumorphism LabLab
- SVG LabLab
<div class="search"> <i class="ph ph-magnifying-glass"></i> <input type="search" placeholder="Search components, labs, tutorials..."> </div> <ul class="suggestions"> <li><i class="ph ph-cube"></i> Cards Lab<span class="tag">Lab</span></li> <li><i class="ph ph-code"></i> Icon Tutorial<span class="tag">Tutorial</span></li> <li><i class="ph ph-palette"></i> Neumorphism Lab<span class="tag">Lab</span></li> <li><i class="ph ph-paint-brush"></i> SVG Lab<span class="tag">Lab</span></li> </ul>
.search { position: relative; } /* Push input text to the right of the icon */ .search input { padding-left: 44px; border-radius: 999px; background: #f4f4f5; border-color: transparent; } .search i { position: absolute; left: 16px; top: 50%; transform: translateY(-50%); color: #888; } .suggestions li { padding: 10px 16px; display: flex; gap: 10px; cursor: pointer; } .suggestions li:hover { background: #f9f9fa; }
8. File Upload — drag-and-drop zone with file preview list
sunset-draft.jpg 2.4 MB
brand-guide.pdf 8.1 MB
<!-- <label> wraps the input so whole area is clickable --> <label class="drop"> <div class="icon"><i class="ph-duotone ph-cloud-arrow-up"></i></div> <h4>Drop files here or click to browse</h4> <p>PNG, JPG, PDF up to 10MB</p> <input type="file" multiple> </label> <!-- preview list of selected files --> <div class="files"> <div class="file"><i class="ph-fill ph-file-image"></i> sunset-draft.jpg <span class="size">2.4 MB</span></div> <div class="file"><i class="ph-fill ph-file-pdf"></i> brand-guide.pdf <span class="size">8.1 MB</span></div> </div>
.drop { border: 2px dashed #d4d4d8; border-radius: 14px; padding: 38px 20px; text-align: center; background: #fafafa; cursor: pointer; transition: all .2s; } .drop:hover { border-color: #ec4899; background: #fdf2f8; } /* Hide the real input — the label is the UI */ .drop input { display: none; }
9. All Input Types — every HTML5 input, select, textarea, and button, each with a label
<!-- All HTML5 input types pair with a <label for=""> --> <label for="name">Name</label> <input id="name" type="text"> <!-- Numbers & range --> <input type="number" min="0" max="100"> <input type="range" min="0" max="100" value="50"> <!-- Date/time family --> <input type="date"> <input type="time"> <input type="datetime-local"> <input type="month"> <input type="week"> <!-- Pickers --> <input type="color" value="#ec4899"> <input type="file"> <!-- Autocomplete with datalist --> <input list="frameworks"> <datalist id="frameworks"> <option value="Astro"> <option value="Next.js"> </datalist> <!-- Choices --> <select><option>Single</option></select> <select multiple><option>Multi</option></select> <input type="radio" name="plan"> <input type="checkbox"> <!-- Buttons --> <button type="submit">Submit</button> <button type="reset">Reset</button> <button type="button">Plain</button> <!-- Group related inputs with fieldset + legend --> <fieldset> <legend>Contact info</legend> <!-- fields here --> </fieldset>
/* Fieldset = visual group for related fields */ fieldset { border: 1px solid #e5e7eb; border-radius: 12px; padding: 16px 18px; } /* Legend sits on the border — nice accent */ legend { font-size: 11px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: #ec4899; padding: 0 6px; } /* Range slider needs some special handling */ input[type="range"] { padding: 0; border: none; background: transparent; } /* Color input needs smaller padding */ input[type="color"] { padding: 2px; height: 36px; } /* <output> displays computed form values */ output { font-weight: 700; color: #ec4899; font-variant-numeric: tabular-nums; }
10. Social Sign-Up — one-tap auth cuts onboarding friction
<div class="social"> <button class="sbtn google"><i class="ph-fill ph-google-logo"></i> Continue with Google</button> <button class="sbtn apple"><i class="ph-fill ph-apple-logo"></i> Continue with Apple</button> <button class="sbtn fb"><i class="ph-fill ph-facebook-logo"></i> Continue with Facebook</button> </div> <div class="divider">or sign up with email</div> <label>Email</label> <input type="email" placeholder="you@example.com"> <button class="primary" type="submit">Create account</button>
.sbtn { display: flex; align-items: center; justify-content: center; gap: 10px; padding: 11px; border: 1px solid #d1d5db; border-radius: 10px; } /* "or" line with rules on both sides */ .divider { display: flex; align-items: center; gap: 12px; color: #9ca3af; } .divider::before, .divider::after { content: ''; flex: 1; height: 1px; background: #e5e7eb; }
11. Success Screen — confirm the submission worked (press Send)
Message sent!
Thanks — we'll get back to you within a day.
<!-- wrapper toggles between form and success panel --> <div class="signup" id="signup"> <form class="signup-form"> <p>Takes about a minute · we'll email you back.</p> <div class="field"> <label>Name</label> <input type="text" placeholder="Your name"> </div> <div class="field"> <label>Message</label> <textarea rows="3" placeholder="How can we help?"></textarea> </div> <button class="primary" type="submit">Send message</button> </form> <!-- hidden until the form is submitted --> <div class="success"> <div class="ring">✓</div> <h3>Message sent!</h3> <p>Thanks — we'll get back to you within a day.</p> <button class="again" type="button">Send another</button> </div> </div>
/* swap the form for the success panel after submit */ .signup-form { display: grid; gap: 12px; } .success { display: none; flex-direction: column; align-items: center; gap: 8px; text-align: center; } .signup.sent .signup-form { display: none; } .signup.sent .success { display: flex; } .ring { width: 48px; height: 48px; border-radius: 50%; display: grid; place-items: center; background: #dcfce7; color: #16a34a; font-size: 26px; } .primary { background: #ec4899; color: #fff; border: none; border-radius: 8px; padding: 10px 16px; cursor: pointer; } .again { background: none; border: 1px solid #d1d5db; border-radius: 8px; padding: 8px 14px; cursor: pointer; }
// Grab the wrapper, the form, and the "send another" button. const signup = document.getElementById('signup'); const form = signup.querySelector('.signup-form'); const again = signup.querySelector('.again'); // On submit: stop the page reload and reveal the success panel. form.addEventListener('submit', e => { e.preventDefault(); signup.classList.add('sent'); // CSS hides the form, shows .success }); // "Send another" flips it back to the form. again.addEventListener('click', () => { signup.classList.remove('sent'); });
12. Preset Suggestions — quick picks plus a custom option
<div class="chips"> <button class="chip">$10</button> <button class="chip on">$25</button> <!-- default --> <button class="chip">$50</button> <button class="chip">$100</button> </div> <div class="amount-wrap"> <span class="cur">$</span> <input type="number" placeholder="Custom amount"> </div>
.chip { padding: 9px 16px; border: 1px solid #d1d5db; border-radius: 999px; cursor: pointer; } /* highlight the chosen preset */ .chip.on { background: #111827; color: #fff; border-color: #111827; }
13. Accordion Sections — break a long form into collapsible steps
<!-- native <details> — collapsible, no JS needed --> <details class="acc" open> <summary><span class="ttl"><i class="ph-fill ph-check-circle done"></i> 1. Your details</span></summary> <div class="body"> … fields … </div> </details> <details class="acc"> <summary><span class="ttl">2. Shipping address</span></summary> <div class="body"> … fields … </div> </details> <details class="acc"> <summary><span class="ttl">3. Payment</span></summary> <div class="body"> … fields … </div> </details>
.acc summary { cursor: pointer; list-style: none; padding: 14px 16px; } /* +/− indicator that flips when open */ .acc summary::after { content: '+'; } .acc[open] summary::after { content: '\2212'; } /* green check marks a finished section */ .done { color: #16a34a; }
14. Review Screen — let people check & edit before submitting
<p>Almost done — please review your details.</p> <div class="rev-sec"> <div class="rev-head"> <h4>Contact</h4> <a href="#">Edit</a> <!-- jump back to that step --> </div> <div class="rev-row"><span class="k">Name</span><span class="v">Ada Lovelace</span></div> <div class="rev-row"><span class="k">Email</span><span class="v">ada@example.com</span></div> </div> <div class="rev-sec"> <div class="rev-head"><h4>Shipping</h4><a href="#">Edit</a></div> <div class="rev-row"><span class="k">Address</span><span class="v">123 Main St, Annandale, VA</span></div> <div class="rev-row"><span class="k">Method</span><span class="v">Standard (3–5 days)</span></div> </div> <div class="rev-sec"> <div class="rev-head"><h4>Payment</h4><a href="#">Edit</a></div> <div class="rev-row"><span class="k">Card</span><span class="v">•••• 4242</span></div> </div>
.rev-sec { border: 1px solid #e5e7eb; border-radius: 10px; padding: 14px 16px; } .rev-head { display: flex; justify-content: space-between; } .rev-row { display: flex; justify-content: space-between; padding: 4px 0; } .rev-row .k { color: #6b7280; } /* label muted, value bold */
15. Intro Microcopy — set expectations before the first field
<!-- tell people what they're in for, up front --> <div class="intro"> <i class="ph-fill ph-info"></i> <p>This quick survey helps us tailor your account…</p> </div> <div class="meta"> <span><i class="ph ph-clock"></i> ~2 minutes</span> <span><i class="ph ph-list-numbers"></i> 4 short questions</span> <span><i class="ph ph-lock-simple"></i> Private</span> </div>
/* a friendly, low-pressure intro banner */ .intro { display: flex; gap: 12px; background: #f0f7ff; border: 1px solid #cfe5fb; border-radius: 10px; padding: 14px 16px; } .meta { display: flex; gap: 18px; font-size: 12px; color: #6b7280; }