18 CSS Forms

18 Light JSPublished

18 CSS forms for contact pages, SaaS signups, e-commerce checkout, healthcare intake, and lead-gen quote flows — spanning character-counting textareas, live password-strength meters, multi-step wizards, autocomplete address fields, file-upload pickers, segmented controls, and radio-card selectors. Scoped under .frm-NN for no-collision pasting, prefers-reduced-motion guarded, framework-agnostic.

Related18 Tailwind CSS Forms30 CSS Login Forms16 CSS Multi-Step Form UI20 CSS Form Validation

Course pickPartner: Zero to Mastery

Affiliate link

Want to build production-ready CSS layouts, animations and UI from scratch?

You just browsed 18 forms. Learn the Flexbox, Grid and animation thinking behind them, so the next component starts in your head, not in a copy-paste.

10% off at checkout with code
Check ZTM Course

Every ZTM course in one membership: $25/month, paid yearly at $299 · 30‑day money-back guarantee

The CSS Bootcamp. We may earn a commission, at no extra cost to you.

01. Contact Form with Character Counter (opens in a new tab)

Published

Light JS
A contact form that respects the person typing: a textarea that grows with field-sizing:content, a circular counter ring driven by a registered @property that shifts from calm to amber to red as the limit approaches, screen-reader announcements only at meaningful thresholds (not every keystroke), topic chips built from real radios, and :user-invalid errors that wait until the user has actually left a field.

02. Registration Form with Password Strength (opens in a new tab)

Published

Light JS
A sign-up screen on a slow-drifting aurora: a four-segment strength meter whose fill colour is interpolated through oklch, a live requirements checklist that ticks itself off, an accessible show/hide toggle using aria-pressed, real-time confirm-password matching, and an entropy-style scorer that rewards length over symbol soup — exactly what NIST SP 800-63B recommends.

03. Checkout Payment Form with Order Summary (opens in a new tab)

Published

Light JS
A two-column checkout that collapses with a container query, not a viewport query: a live 3D card preview that mirrors what you type and flips to show the CVC when that field is focused (pure :has(), no JS for the flip), automatic card-number spacing with Visa/Mastercard/Amex detection, a payment-method switcher built from radios, quantity steppers, a working promo code, and a Pay button that always shows the real total.

04. Shipping Address Form with Autocomplete (opens in a new tab)

Published

Light JS
An address form that does the typing for you twice over: correct HTML autocomplete tokens so browsers and password managers fill every field in one tap, plus an accessible WAI-ARIA combobox that suggests full addresses as you type, navigable with arrow keys and Enter. The postcode label, placeholder and pattern adapt to the selected Tier-1 country (US ZIP, UK postcode, Canadian postal code, AU postcode), and delivery speed is a set of radio cards with live arrival dates.

05. Newsletter Signup Form, Inline to Stacked (opens in a new tab)

Published

Light JS
The newsletter form that adapts to whatever slot you drop it into — a hero, a sidebar, a footer column — using container queries instead of viewport media queries. It morphs through three layouts: a single pill (input and button fused), a split row, and a full-width stack. Drag the handle on the frame to watch it reflow live, with a width readout showing which layout is active.

06. Support Ticket Form with File Attachments (opens in a new tab)

Published

Light JS
A help-desk ticket form in a focused dark developer-tool style: priority as a colour-coded segmented control, category and subject fields, a description with markdown hints, and a drag-and-drop attachment zone that is a real <input type="file" multiple> underneath — keyboard-operable, screen-reader-labelled, with per-file progress, image thumbnails, type and size validation, and remove buttons.
Newsletter

Enjoying these demos? Get new ones by email.

A short email when a new collection ships. Unsubscribe any time.

at

Almost there: check your inbox to confirm.Manage

07. NPS Survey Form with Follow-Up Question (opens in a new tab)

Published

Light JS
A Net Promoter Score survey with zero JavaScript. The 0–10 scale is eleven real radio buttons tinted along a red-amber-green ramp; choosing a score instantly reveals a different follow-up question for detractors (0–6), passives (7–8) and promoters (9–10) using :has(). A progress bar fills, the band label updates, and the thank-you state is driven by a checkbox — all declarative CSS.

08. Job Application Form with Resume Upload (opens in a new tab)

Published

Light JS
A careers-page application broken into three calm steps — About you, Résumé, Final questions — with a step indicator that is a real ordered list (aria-current="step"), per-step native validation, a résumé drop zone restricted to PDF/DOC/DOCX up to 5 MB that turns into a file card with replace/remove, an optional cover-letter, work-authorisation radios, and a review screen before submit.

09. Profile Settings Form with Avatar Upload (opens in a new tab)

Published

Light JS
An account-settings panel that behaves like a modern app: an avatar you can replace (instant local preview via URL.createObjectURL), zoom with a range slider and remove; a username field with a fused @ prefix and a live availability check; a bio with counter; accessible role="switch" toggles; and a dirty-state save bar that slides up only when something actually changed — with Discard to restore the original values.

10. Restaurant Table Reservation Form (opens in a new tab)

Published

Light JS
A fine-dining booking widget in candle-lit black and gold: a party-size stepper, a horizontally scrolling date strip built from radios with scroll-snap, a time-slot grid where fully-booked slots are genuinely disabled (and say so), seating-area cards with photography, an occasion selector, and a live sentence summary — “Table for 4 · Fri 9 Oct · 8:00 pm · Terrace” — that updates as you choose.

11. Donation Form with Preset Amounts (opens in a new tab)

Published

Light JS
A nonprofit donation form designed around the psychology that actually moves people: a one-time/monthly toggle with a sliding pill indicator (pure :has()), four preset amounts each paired with a concrete impact line, an “Other” amount that auto-selects as you type, an optional fee-cover that shows the exact extra, a tribute (in honour of) reveal, and a Donate button that always states the real total and frequency.

12. Insurance Quote Request Form (opens in a new tab)

Published

Light JS
A three-step lead-gen quote form built for conversion: big icon radio cards to pick coverage (Auto, Home, Life, Renters), a details step whose questions change based on that choice, a coverage-level slider, and a sticky live estimate that recalculates a monthly price range as you go. A segmented progress bar, Back/Next with per-step validation, and a clear privacy promise right where people hesitate.

13. Event RSVP Form with Guest Count (opens in a new tab)

Published

Light JS
An elegant invitation-style RSVP: accept, decline or maybe as large radio cards; a guest-count stepper that generates a named row (with meal choice and child toggle) for each extra guest and removes them cleanly; dietary chips; a song request for the dance floor; and a decline path that hides everything irrelevant and offers a note to the hosts — powered by :has() so the layout always matches the answer.

14. Product Return and Refund Request Form (opens in a new tab)

Published

Light JS
A self-serve returns portal: select items from the order as checkbox cards with product photos, and each selected card expands to ask its own quantity and reason (pure :has()); final-sale items are genuinely disabled with an explanation; choose refund to the original card or store credit with a 10% bonus; pick drop-off or courier pickup; and watch an itemised refund total update live before generating a return label.
Newsletter

Enjoying these demos? Get new ones by email.

A short email when a new collection ships. Unsubscribe any time.

at

Almost there: check your inbox to confirm.Manage

Published

Light JS
A new-patient intake designed for accessibility first — set in Atkinson Hyperlegible, the typeface created for low-vision readers. Numbered sections with a live completion meter, a 0–10 pain slider that describes the level in words, allergy and insurance questions that reveal follow-ups only when relevant, and a consent block where the checkbox unlocks only after the full text has been scrolled, followed by a typed e-signature preview.

16. Mortgage Pre-Qualification Form (opens in a new tab)

Published

Light JS
A pre-qualification form that earns the lead by being useful first: linked slider + number inputs for home price and down payment (in % and $), a loan-term segmented control, credit band chips, income and debts — and a live results panel with the monthly payment, an animated conic-gradient donut splitting principal & interest, tax, insurance and PMI, a colour-coded debt-to-income gauge, and a plain-English pre-qualification verdict.

17. Law Firm Consultation Request Form (opens in a new tab)

Published

Light JS
A consultation request with the gravitas a law firm needs: an attorney profile with credentials beside a calm, editorial form. Practice-area cards, preferred contact method, an urgent-deadline toggle that reveals a court-date field, an opposing-party field for conflict checks, availability chips, a confidentiality note placed exactly where people hesitate, and the required “no attorney–client relationship” acknowledgement.

18. SaaS Demo Request Form (opens in a new tab)

Published

Light JS
A B2B ‘Book a demo’ that skips the back-and-forth email: work-email validation that politely flags Gmail/Outlook/Yahoo addresses and auto-fills the company from the domain, a company-size segmented control, role and interest chips, and an inline scheduler showing the next five business days with slots rendered in the visitor's own time zone (via Intl). Set on an animated mesh-gradient with social proof alongside.
FAQ

Frequently asked questions

How do I make a textarea grow with its content without JavaScript?
The autogrow in the contact form is one line: field-sizing: content on the <textarea>. The element measures its own text and sets its block size to fit, so the box grows as you type and shrinks on delete — no scrollHeight reads, no resize handler, no hidden mirror element. Pair it with min-height and max-height so it starts at a sensible size and stops before it eats the viewport, then let an inner scroll take over. The common bug: people still bind a keyup handler that sets style.height, which fights the native sizing and causes a visible jump on every keystroke — delete it. When not to use it: field-sizing is still rolling out, so treat the growth as progressive enhancement. A plain fixed-height textarea with a scrollbar is the baseline everyone gets, and that is a perfectly usable fallback while support fills in.
How do I switch a form from two columns to one without a media query?
The checkout and newsletter forms change layout with a container query, not a viewport @media rule. Set container-type: inline-size on the wrapper, then write @container (min-width: 32rem) to flip from a stacked single column to the two-column order-summary layout. The win is that the form responds to its own width, so the same component lays out correctly in a wide page body, a narrow sidebar, or a modal without knowing the viewport. The usual bug: writing the @container query to style the same element you set container-type on — a container can't query itself, so the rule has to target a descendant inside it. That is exactly why the newsletter form nests its fields in a child the query controls. Every current engine ships container queries; if you must support older browsers, keep a @media fallback behind it, but don't reach for it first.
How do I build a 0–10 NPS rating with no JavaScript?
The NPS survey uses eleven real <input type="radio"> controls sharing one name, each paired with a <label> styled as a numbered chip. Selection is pure CSS: input:checked + label paints the chosen score, and the red-to-green ramp is just a per-label background. The follow-up comment field appears with :has() — a rule such as form:has(input:checked) reveals it once any score is picked. The bug that breaks it: forgetting to connect each label to its input, either by wrapping the input in the label or matching for to id — without that, clicking a number does nothing and the control is unreachable by keyboard. Because these are native radios, Tab, arrow-key navigation and group semantics work for free. Wrap the set in a <fieldset> with a <legend> so a screen reader announces the question alongside each option.
Why won't the browser autofill my shipping address form?
Autofill is driven entirely by the autocomplete attribute, and the shipping form sets the exact tokens browsers expect: name, address-line1 and address-line2, address-level2 for city, address-level1 for state or region, postal-code, and country. Get the token right and the browser offers a saved address in one tap and fills every field. The usual reason it 'doesn't work': invented values like autocomplete="on" or autocomplete="address", or a missing name so the heuristic can't map the field. Set inputmode="numeric" on the postal code so mobile shows the number pad. For a page with both billing and shipping, prefix the tokens — shipping street-address, billing street-address — so the browser keeps them separate. Never switch autofill off on address or payment fields; it is the single biggest completion-rate lever you have.
How do I preview an uploaded image before the form is submitted?
The profile-settings and job-application forms preview the picked image locally using URL.createObjectURL(file) on the file from the <input type="file"> change event, assigning the result to an <img> source. It is instant and offline — no upload round-trip and no base64 FileReader work. The bug almost everyone ships: never calling URL.revokeObjectURL(), which leaks the blob in memory for the life of the page — revoke the previous URL each time a new file is chosen. Validate before previewing: check file.type against an allowed image list and file.size against a cap, so a 40 MB file or a PDF doesn't render as a broken image. For accessibility, keep the real file input focusable and labelled even when you hide it behind a styled button — a visually-hidden input paired with a <label> is still reachable, whereas display: none removes it from the tab order entirely.
How should I build the password strength meter in the registration form?
The registration form's meter is four segment bars plus a small script that scores the password and sets how many segments fill and the colour they interpolate toward. Keep the scoring honest: weight length first — it matters more than any single character class — then add points for distinct character sets, and cap the score so a short password can't reach 'strong' by tacking on one symbol. Drive the bars by setting a data attribute or a CSS custom property they read, so the meter stays pure presentation. The accessibility point that gets missed: a colour change alone is invisible to colour-blind and screen-reader users, so pair it with a visible text label — 'Weak', 'Fair', 'Strong' — inside an aria-live region. When not to lean on it: never block submission on the meter, it is guidance not a gate, and don't impose forced symbol-and-digit composition rules — length plus a blocklist of common passwords is the stronger policy.
How do I keep a slider and a number input in sync?
The mortgage pre-qualification form links a <input type="range"> to a <input type="number"> for home price and down payment. Wire both to the same input event and write each other's value inside the handler; use input, not change, so it fires on every drag and keystroke and the two stay in lockstep live. Clamp in one place — read the value, bound it to min and max, then assign — otherwise typing a huge figure in the number field shoves the slider past its track. Keep both as real fields with their own <label> so each value is announced and keyboard-adjustable; the slider alone is clumsy for precise entry and the number alone loses the at-a-glance feel, which is why pairing them earns its code. Format the displayed currency in a separate read-only element — injecting commas and a dollar sign into a type="number" field makes it invalid and the browser silently clears it.
What is the accessibility baseline these forms follow?
The baseline is consistent across the set: every control has a programmatic label, grouped controls sit in a <fieldset> with a <legend>, and the patient-intake form goes further by setting its whole surface in Atkinson Hyperlegible, a typeface built to keep similar letterforms distinct. Associate a label by pointing for at the input's id, or wrap the input inside the <label> — placeholder text is not a label and vanishes on focus. The consent checkbox is a real <input type="checkbox"> with its own label stating exactly what is agreed to, and required fields use the required attribute plus a visible cue, never colour alone. The bug that quietly excludes people: a custom control built from a <div> with a click handler — it has no role, no keyboard behaviour and no value, so Tab and screen-reader users can't operate it. Start from the native element and style it rather than rebuilding one from scratch.

Related collections

15 CSS 3D Tilt Hover Cards preview

15 CSS 3D Tilt Hover Cards

15 mouse-aware 3D tilt hover cards — e-commerce product spotlight, glassmorphism parallax team card, interactive pricing tier, holographic NFT collectible, pop-out mascot, dark tech grid with border glow, media player album art, blog article preview, cyberpunk neon glow, dashboard KPI widget, minimalist real estate, flip-to-back tilt, mobile app showcase, course learning card, and a Pure CSS touch-friendly tilt. Vanilla JS writes --rx/--ry/--mx/--my; all rendering stays in CSS on the GPU.

24 CSS Animated Cards preview

24 CSS Animated Cards

24 hand-coded CSS animated card patterns organised by what triggers the motion, not by card style. Scroll and entrance triggers: staggered grid reveals, @starting-style mount-in, scroll-driven scale and fade on animation-timeline: view(), View Transitions API card-to-detail morphs, blur-to-sharp lazy image loading, and FLIP re-layout when a filter changes. State-change triggers: add-to-cart success morph, like and save particle burst, expand and collapse to intrinsic height with calc-size(), swipe-to-dismiss on Pointer Events, and skeleton-to-loaded crossfade. Data-driven triggers: count-up KPI metrics, SVG sparkline draw-on-enter, and progress ring fill on load. Ambient idle loops: breathing glow, floating drift, animated gradient mesh backgrounds, conic rotating borders, auto-cycling testimonial decks, and live status pulses. Plus four hover patterns where the effect is the topic itself: cursor spotlight, holographic foil glare, corner ribbon slide, and horizontal accordion expand. Every card is scoped under a .ac-NN prefix for no-collision pasting, guards prefers-reduced-motion, animates compositor-only properties, and ports unchanged to React, Vue, Svelte, Astro, Next.js and Tailwind.

22 CSS Avatars preview

22 CSS Avatars

22 hand-coded CSS avatars for chat apps, team dashboards, comment threads, account menus, and social profiles. Covers circular and squircle shapes, gradient and conic story rings, hexagon clip-path crops, online status dots, notification count badges, verified checkmarks, stacked facepiles with plus-N overflow, initials fallbacks for users without photos, broken-image recovery, avatar pickers, and a size scale system where the ring, badge, and status dot all scale from a single custom property.

Search CodeFronts

Loading…