Home >Web Front-end >CSS Tutorial >A Guide to HTML & CSS Forms (No Hacks!)

A Guide to HTML & CSS Forms (No Hacks!)

尊渡假赌尊渡假赌尊渡假赌
尊渡假赌尊渡假赌尊渡假赌Original
2025-02-10 09:34:11967browse

A Guide to HTML & CSS Forms (No Hacks!)

Historically, HTML forms have been quite tricky — firstly, because at least a little bit of JavaScript was required, and secondly, because no amount of CSS could ever make them behave.

However, this isn’t necessarily true in the case of the modern web, so let’s learn how to mark up forms using only HTML and CSS.

Key Takeaways

  • HTML forms can be created using only HTML and CSS, without the need for JavaScript. The form structure is created using the
    element and data is submitted using the action attribute. Additional attributes, such as enctype and target, can be used to define the data encoding type and where the output is displayed.
  • Labels are essential for usability and accessibility, describing what an input is for. There are three ways to declare a label: adjacent labels, ARIA labels, and wrapping labels. The most efficient method is wrapping inputs within labels. Placeholders are also useful to provide examples of what is expected in the input field.
  • There are various input types to choose from, such as button, checkbox, color, date, email, file, etc. Styling inputs can be challenging due to browser defaults, but the appearance attribute can be used to override these. Input validation is crucial to ensure user input meets certain criteria, and can be achieved using native-HTML validation or JavaScript.

Form-ing the basic structure

A Guide to HTML & CSS Forms (No Hacks!)

Start off with the

element.

Nothing fancy here. Just covering the basic structure.

<span><span><span><form</span>></span>
</span>    ...
<span><span><span></form</span>></span>
</span>

If you’re submitting the form data naturally (that is, without JavaScript), you’ll need to include the action attribute, where the value is the URL you’ll be sending the form data to. The method should be GET or POST depending on what you’re trying to achieve (don’t send sensitive data with GET).

Additionally, there’s also the lesser-used enctype attribute which defines the encoding type of the data being sent. Also, the target attribute, although not necessarily an attribute unique to forms, can be used to show the output in a new tab.

JavaScript-based forms don’t necessarily need these attributes.

<span><span><span><form</span> method<span>="POST"</span> action<span>="/subscribe"</span> enctype<span>="application/x-www-form-urlencoded"</span> target<span>="_blank"</span>></span>
</span>    ...
<span><span><span></form</span>></span>
</span>

Forms are made up of inputs, which expect data values.

<span><span><span><form</span>></span>
</span>    <span><span><span><input</span> type<span>="text"</span>></span><!-- text input -->
</span>    <span><span><span><input</span> type<span>="text"</span> value<span>="Prefilled value"</span>></span>
</span><span><span><span></form</span>></span>
</span>

See the Pen
Form 1 by SitePoint (@SitePoint)
on CodePen.

Including Labels for Better Usability & Accessibility

Every input needs a label.

A label is a text descriptor that describes what an input is for. There are three ways to declare a label, but one of them is superior to the other two. Let’s dive into these now.

Adjacent labels

Adjacent labels require the most code because we need to explicitly declare which input the label describes. To most, this is counterintuitive because we can instead wrap inputs inside labels to achieve the same effect with less code.

That being said, the adjacent method may be necessary in extenuating circumstances, so here’s what that would look like:

<span><span><span><form</span>></span>
</span>    ...
<span><span><span></form</span>></span>
</span>

As you can see from the example above, the for attribute of the

ARIA labels

While semantic HTML is better, ARIA (Accessible Rich Internet Applications) labels can compensate in their absence. In this case, here’s what a label might look like in the absence of an actual HTML

<span><span><span><form</span> method<span>="POST"</span> action<span>="/subscribe"</span> enctype<span>="application/x-www-form-urlencoded"</span> target<span>="_blank"</span>></span>
</span>    ...
<span><span><span></form</span>></span>
</span>

Unfortunately, the downside of this approach is the lack of a visual label. However, this might be fine with some markups (for example, a single-input form with a heading and placeholder):

<span><span><span><form</span>></span>
</span>    <span><span><span><input</span> type<span>="text"</span>></span><!-- text input -->
</span>    <span><span><span><input</span> type<span>="text"</span> value<span>="Prefilled value"</span>></span>
</span><span><span><span></form</span>></span>
</span>

(I’ll explain what placeholders are for in a moment.)

Wrapping labels

Wrapping inputs within labels is the cleanest approach. Also, thanks to CSS’s :focus-within, we can even style labels while their child inputs receive focus, but we’ll discuss that later.

<span><span><span><label</span> for<span>="firstName"</span>></span>First name<span><span></label</span>></span>
</span><span><span><span><input</span> id<span>="firstName"</span>></span>
</span>

Placeholders vs labels

A Guide to HTML & CSS Forms (No Hacks!)

A brief comparison:

  • Labels state what the input expects
  • Placeholders show examples of said expectations

Placeholders aren’t designed to be the alternative to labels, although as we saw in the ARIA example above, they can add back some of the context that’s lost in the absence of visual labels.

Ideally, though, we should use both:

<span><span><span><input</span> aria-label<span>="First name"</span>></span>
</span>

See the Pen
Form 2 by SitePoint (@SitePoint)
on CodePen.

Choosing Input Types

Placeholders only apply to text-based inputs, but there are actually a whole range of different input types, which include:

<span><span><span><h1</span>></span>Subscribe<span><span></h1</span>></span>
</span><span><span><span><form</span>></span>
</span>    <span><span><span><input</span> aria-label<span>="Email address"</span> placeholder<span>="bruce@wayneenterpris.es"</span>></span>
</span><span><span><span></form</span>></span>
</span>

Semantic input types are useful during form validation, especially when relying on native validation, which we’ll take a look at shortly. First, let’s learn how to style these inputs.

Styling inputs

Arguably the most infuriating aspect of coding forms is overriding the browser’s default styling. Thankfully, today, appearance: none; has 96.06% browser support according to caniuse.com.

After resetting the web browser’s default styling with the following CSS code, we can then style inputs however we want, and this even includes both the radio and checkbox input types:

<span><span><span><form</span>></span>
</span>    ...
<span><span><span></form</span>></span>
</span>

However, some of these inputs might come with quirks that are difficult or even impossible to overcome (depending on the web browser). For this reason, many developers tend to fall back to the default type="text" attribute=value if they find these quirks undesirable (for example, the “caret” on input type="number").

However, there is a silver lining …

Specifying an inputmode

With 82.3% web browser support according to caniuse.com, the new inputmode attribute specifies which keyboard layout will be revealed on handheld devices irrespective of the input type being used.

Better than nothing, right?

<span><span><span><form</span> method<span>="POST"</span> action<span>="/subscribe"</span> enctype<span>="application/x-www-form-urlencoded"</span> target<span>="_blank"</span>></span>
</span>    ...
<span><span><span></form</span>></span>
</span>

Validating User Input

Should you choose native-HTML validation over a JavaScript solution, remember that inputmode achieves nothing in this regard. inputmode="email" won’t validate an email address, whereas input type="email" will. That’s the difference.

Putting this aside, let’s discuss what does trigger validation:

<span><span><span><form</span>></span>
</span>    <span><span><span><input</span> type<span>="text"</span>></span><!-- text input -->
</span>    <span><span><span><input</span> type<span>="text"</span> value<span>="Prefilled value"</span>></span>
</span><span><span><span></form</span>></span>
</span>

Creating custom rules

Custom rules require knowledge of JavaScript regular expressions, as used by the RegExp object (but, without wrapping slashes or quotes). Here’s an example that enforces lowercase characters (a–z) and minlength/maxlength in one rule:

<span><span><span><label</span> for<span>="firstName"</span>></span>First name<span><span></label</span>></span>
</span><span><span><span><input</span> id<span>="firstName"</span>></span>
</span>

More info here.

Note: front-end validation (native-HTML or otherwise) should never, ever be used as a substitute for server-side validation!

Styling valid/invalid states

A Guide to HTML & CSS Forms (No Hacks!)

Just for extra clarity, this is how we’d style validity:

<span><span><span><input</span> aria-label<span>="First name"</span>></span>
</span>

Houston, we have a problem!

Inputs attempt to validate their values (or lack thereof) immediately, so the following code (which only shows the valid/invalid states while the input holds a value) might be better:

<span><span><span><h1</span>></span>Subscribe<span><span></h1</span>></span>
</span><span><span><span><form</span>></span>
</span>    <span><span><span><input</span> aria-label<span>="Email address"</span> placeholder<span>="bruce@wayneenterpris.es"</span>></span>
</span><span><span><span></form</span>></span>
</span>

This shows the valid/invalid styling but only when the placeholder isn’t shown (because the user typed something).

See the Pen
Form 3 by SitePoint (@SitePoint)
on CodePen.

Other Basic Things

Sending form data

Sending form data to the server usually requires that inputs have the name attribute. This also applies to hidden inputs:

<span><span><span><form</span>></span>
</span>    ...
<span><span><span></form</span>></span>
</span>

Accepting long-form input

Essentially, is the same thing as , except for the fact that textareas have multiline support. Yes, would certainly be more intuitive, but alas is the correct way to accept long-form input from users. Plus, it accepts most (if not all) of the attributes that inputs do.

Grouping inputs for better accessibility

Although shorter forms offer a much better user experience, sometimes longer ones are unavoidable. In such scenario, the

element can be used to contain related inputs, with a child being used as a title/heading for the
:
<span><span><span><form</span> method<span>="POST"</span> action<span>="/subscribe"</span> enctype<span>="application/x-www-form-urlencoded"</span> target<span>="_blank"</span>></span>
</span>    ...
<span><span><span></form</span>></span>
</span>

Nice-to-know things

Disabling inputs

Adding the disabled attribute can render an input (or any focusable element) defunct, although this would typically be applied/unapplied via JavaScript. Here’s how it works, though:

<span><span><span><form</span>></span>
</span>    <span><span><span><input</span> type<span>="text"</span>></span><!-- text input -->
</span>    <span><span><span><input</span> type<span>="text"</span> value<span>="Prefilled value"</span>></span>
</span><span><span><span></form</span>></span>
</span>

And the accompanying CSS, if needed:

<span><span><span><label</span> for<span>="firstName"</span>></span>First name<span><span></label</span>></span>
</span><span><span><span><input</span> id<span>="firstName"</span>></span>
</span>

However, if all that you want to do is add an extra visual cue hinting that the user’s input isn’t valid, you would most likely want to use the general sibling combinator (~). The following code basically means “the submit button that follows any element with invalid input”. This doesn’t alter any functionality, but when we’re leveraging native-HTML form validation (which handles disabling/enabling of submit-ability automatically), this is fine:

<span><span><span><input</span> aria-label<span>="First name"</span>></span>
</span>

Disabling an input, but sending the data anyway

A mix of and , the following example will ensure that the value cannot be changed. The difference is that, unlike disabled, readonly values are sent as form data; and unlike hidden, readonly is visible:

<span><span><span><h1</span>></span>Subscribe<span><span></h1</span>></span>
</span><span><span><span><form</span>></span>
</span>    <span><span><span><input</span> aria-label<span>="Email address"</span> placeholder<span>="bruce@wayneenterpris.es"</span>></span>
</span><span><span><span></form</span>></span>
</span>

Altering increments

Numeric-based inputs have a “spin button” to adjust the numerical value, and they also accept a step attribute which determines an alternative incremental value of each adjustment:

<span><span><span><label</span>></span>
</span>    First name<span><span><span><input</span>></span>
</span><span><span><span></label</span>></span>
</span>

Styling forms, labels, and fieldsets on focus

We can use focus-within: to style any parent of an input currently receiving focus. Most likely this element will be the input’s

,

The above is the detailed content of A Guide to HTML & CSS Forms (No Hacks!). For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn