CSS :optional Pseudo Class
Use the :optional CSS pseudo-class for selecting the elements that are optional. Read about the pseudo-class and practice with examples.
The :optional pseudo-class matches every form control that the user is not required to fill in. It is the exact opposite of :required: a control matches one or the other, never both.
A form control is "optional" simply because it lacks the required attribute. This page explains which elements :optional applies to, how it differs from related pseudo-classes, and how to use it to give users a visual hint about which fields they can safely skip.
Which elements does :optional match?
Only the three form controls that accept the required attribute can match :optional:
<input>(excepttype="hidden",type="range",type="color", buttons, and similar types that can never be required)<select><textarea>
Any of these without a required attribute is matched by :optional. Elements that cannot be required at all — such as <div>, <p>, or a submit <button> — are never matched.
:optional vs :required
These two pseudo-classes partition every requirable control:
| Control | Matched by :required | Matched by :optional |
|---|---|---|
<input required> | yes | no |
<input> | no | yes |
<select required> | yes | no |
<textarea> | no | yes |
Because the two never overlap, a common pattern is to leave optional fields neutral and highlight only required ones — or, as in the example below, fade optional fields so they recede visually.
Syntax
:optional {
/* CSS declarations */
}You can chain :optional with other selectors and pseudo-classes (e.g. :hover, :focus) and with pseudo-elements (e.g. ::after) to build richer states:
input:optional:focus {
outline: 2px solid #1c87c9;
}Example
The example below styles optional inputs with a muted, semi-transparent look that brightens on hover, while required inputs get a bold bottom border so the difference is obvious at a glance:
<!DOCTYPE html>
<html>
<head>
<title>Title of the document</title>
<style>
.example {
margin: 40px auto;
max-width: 400px;
}
label,
button {
display: block;
width: 100%;
margin-bottom: 1.5em;
}
input,
select,
button {
padding: .4em 1em;
}
input,
select {
border: 1px solid #666666;
}
input:optional,
select:optional {
background-color: #eeeeee;
color: #666666;
opacity: 0.5;
transition: .3s;
}
input:optional:hover,
select:optional:hover {
opacity: 1;
}
input:required,
textarea:required {
border-bottom: 3px solid #1c87c9;
}
</style>
</head>
<body>
<h2>:optional selector example</h2>
<div class="example">
<form action="#">
<label>
<input type="text" required />Name *
</label>
<label>
<input type="email" required />Email *
</label>
<label>
<input type="tel" />Phone (optional)
</label>
<label>
<input type="url" />Address (optional)
</label>
</form>
</div>
</body>
</html>Here both :optional and :required are used together so the two field types are easy to tell apart.
Gotchas
- Submit buttons are not optional. A
<button>or<input type="submit">cannot be required, so it never matches:optional. Style buttons with their own selector. - Validation states are separate.
:optionalonly reports whether a field is required — not whether its value is valid. For that, use:validand:invalid. - It updates live. If you toggle the
requiredattribute with JavaScript, the browser re-evaluates:optional/:requiredimmediately, so the styling follows.
Related pseudo-classes
:required— the inverse of:optional.:validand:invalid— match controls by whether their value passes validation.:enabledand:disabled— match by interactivity.:read-only— matches controls the user cannot edit.