CSS Pseudo-classes: Styling Form Fields Based on Their Input
Let’s take a look at some pseudo-classes that are specific to form fields and form field input. These pseudo-classes can be used to style fields based on the validity of user input, whether the field is required or currently enabled.
All of the pseudo-classes that follow are specific to forms. As a result, there’s less of a need to limit the scope with a selector. Using :enabled won’t introduce side effects for span elements. Limiting the scope is helpful, however, when you want to syle various types of form controls differently.
:enabled and :disabled
As their name suggests, these pseudo-classes match elements that have (or lack) the disabled HTML5 attribute. This can be an input control such as input , select , or button element (seen shortly), or it can be a fieldset element:
button type="submit" disabled>Save draftbutton>
Form elements are enabled by default; that is, they only become disabled if the disabled attribute is set. Using input:enabled will match every input element that is without a disabled attribute set. Conversely, button:disabled would match all button elements with a disabled attribute:
button:disabled opacity: .5; >
The figure shows the :enabled and :disabled states for our button element.
:required and :optional
Required and optional states are determined by the presence or absence of the required attribute on the field. [6] For example:
Most browsers only indicate whether a field is required once the form is submitted. With the :required pseudo-class, we can indicate to the user that the field is required before submission. For example, the following CSS will add a yellow border to our email field from above, and is shown in the figure below:
input:required border: 1px solid #ffc107; >
The :optional class works similarly, by matching elements that do not have a required attribute. For example, the CSS that follows gives us the results seen below.
select:optional border: 1px solid #ccc; >
:checked
Unlike the other pseudo-classes that we’ve covered, :checked only applies to radio and checkbox form controls. As the name indicates, this pseudo-class lets us define separate styles for selected inputs.
Unfortunately, styling radio controls and checkboxes in most browsers is about as pleasant as a trip to the dentist for a filling. CSS Basic User Interface Module Level 4 attempts to address this with the appearance property, but this property is not yet supported. WebKit/Blink-based browsers and Firefox do, however, support nonstandard, vendor-prefixed versions of it.
In order to create custom radio button and checkbox inputs that work well across browsers, we need to become clever with our selectors. We’ll use a sibling combinator, a pseudo-element, and :checked to create custom radio button and checkbox controls. For example, to change the style of a label when its associated radio button is checked, we could use the following CSS:
[type=radio]:checked + label font-weight: bold; font-size: 1.1rem; >
This makes the label bold and increases its size when its associated control is checked. We can improve this, though, by using the ::before pseudo-element with our label element to inject a custom control:
[type=radio] opacity: 0; > [type=radio] + label::before background: #fff; content: ''; display: inline-block; border: 1px solid #444; height: 1.2rem; margin-right: 1em; vertical-align: middle; width: 1.2rem; > [type=radio]:checked + label::before background: #4caf50; >
This gives us the customized controls you see below.
In order for this technique to work, of course, our HTML needs to be structured appropriately:
- The label element must be immediately adjacent to its input control.
- The form control must have an id attribute in addition to the name attribute (for example, ).
- The label must have a for attribute, and its value must match the id of the form control (for example, ).
Associating the label using for with the input ensures that the form input will be selected when the user clicks or taps the label or its child pseudo-element ( ::before ).
:in-range and :out-of-range
The :in-range and :out-of-range pseudo-classes can be used with range , number , and date input controls. Using :in-range and :out-of-range requires setting min and/or max attribute values for the control. Here’s an example using the number input type:
p> label for="picknum">Enter a number from 1-100label> input type="number" min="1" max="100" id="picknum" name="picknum" step="1"> p>
Let’s add a little bit of CSS to change styles if the values are within or outside of our range of one to 100:
:out-of-range background: #ffeb3b; > :in-range background: #fff; >
Should the user enter -3 or 101, the background color of #picknum will change to yellow as defined in our :out-of-range rule set (see the figure below). Otherwise, it will remain white as defined in our :in-range rule set.
:valid and :invalid
With the :valid and :invalid pseudo-classes, we can set styles based on whether or not the form input meets our requirements. This will depend on the validation constraints imposed by the type or pattern attribute value. For example, an input with type=»email» will be invalid if the user input is “foo 123,” as represented in teh figure below.
A form control will have an invalid state under the following conditions:
- when a required field is an empty field
- when the user’s input does not match the type or pattern constraints
- when the field’s input falls outside of the range of its min and max attribute values
Optional fields with empty values are valid by default. Obviously, if user input satisfies the constraints of the field, it exists in a valid state.
Form controls can have multiple states at once. So you may find yourself managing specificity (discussed in the next section) and cascade conflicts. A way to mitigate this is by limiting which pseudo-classes you use in your projects. For example, don’t bother defining an :optional rule set if you’ll also define a :valid rule set.
It’s also possible, however, to chain pseudo-classes. For example, we can mix the :focus and :invalid pseudo-classes to style an element only while it has focus: input:focus:invalid . By chaining pseudo-classes, we can style an element that has more than one state.
[6] Remember that in HTML5, the presence or absence of the attribute determines its value. In other words, required=»false» has the same effect as required=»true» , required=»required» and required .
Share This Article
Tiffany B. Brown is a freelance web developer and technical writer based in Los Angeles. Brown offers web development and consulting services to larger agencies and small businesses. A former member of the Opera Software developer relations team, Brown is also co-author of SitePoint’s JumpStart HTML5 book. She sporadically writes about web development technology on her blog. You can follow her on Twitter at @webinista.