Welcome to Stackazon, your one-stop shop for great solutions to tricky coding problems! Your search query, "How do I style a label when its input is focused?", has returned five results.
Our ratings for this query consider browser compatibility, accessibility, and complexity. Be sure to read our reviewers' opinions for additional resources and justification for the rating.
The best part about Stackazon? Everything is free, and delivery is instant! No prime subscription needed either!
Disclaimer: The people, ratings, and reviews are fictional and based on my opinion and knowledge of key factors. What solution is best always depends on context and your use case!
This is mostly my wacky way of demonstrating how to evaluate important factors when coding for the web 😊
- The Problem
-
Solutions
- Using Adjacent Siblings (🌕🌕🌕🌗🌑)
- Using Javascript (🌕🌕🌕🌘🌑)
- Using :focus-within with Explicit Labels (🌕🌕🌕🌕🌘)
- Using :focus-within with Implicit Labels (🌕🌕🌕🌕🌑)
- Using :has (🌖🌑🌑🌑🌑)
- Codepen
Here at Stackazon, we rate using moons instead of stars because emoji sets support different moon phases, which can indicate fractional rating! Thank you, Moon, for having such convenient phases ❤️Why do you use moons instead of stars?
The Problem
Most of the time, an HTML input element should be properly labeled.
<label for="name">Name</label>
<input id="name" type="text" />
But let's say you want to highlight the "Name" label if someone is typing stuff into its text field. As web developers, we can use CSS to customize how the input looks when focused:
input:focus {
border-color: red;
}
But... how do you do the same for the label? Turns out it's tricky!
Solutions
Our Stackazon reviewers reference the following resources (and we recommend that you do too when evaluating solutions for other issues!):
- Can I Use - Tracks the browser compatibility of various web features
- Accessibility Support - Tracks to what degree assistive technologies implement accessibility specifications
- Web Content Accessibility Guidelines - Essentially a specification for maximizing web accessibility, also called WCAG
- MDN Web Docs - Curated documentation of web features including best practices (and recently got an upgrade!)
Using Adjacent Siblings
Rating: 🌕🌕🌕🌗🌑
CSS provides an adjacent sibling combinator which lets you match an element that is directly after another element.
If you structure your HTML such that the label is directly after the input:
<div class="input-container">
<input id="name" type="text" />
<label for="name">Name</label>
</div>
Then you can style the label with the adjacent sibling combinator:
input:focus + label {
color: red;
}
However, for a text field, conventionally the label is supposed to come before the input.
Unfortunately, the fancy adjacent sibling plus sign only works top-to-bottom! If you try to use label + input:focus
, then the styles will only apply to the input directly and not the label. If the label comes first in the markup, then you can't use this combinator to select the label when the input is focused.
That said, CSS to the rescue again! CSS can visually reorder elements on the page:
.input-container {
display: flex;
}
input { order: 2; }
label { order: 1; }
Reviews
Our reviewers gave this solution 3.5 out of 5 moons, citing excellent browser compatibility but with some accessibility concerns.
Ivan Eaton says:
Of the solutions presented, this has the best browser compatibility, as the adjacent sibling combinator has been implemented in all major browsers, included Internet Explorer, for many years.
Flexbox, though newer, is also widely supported.
Serena Redmon says:
I am concerned about how screen readers announce the text field and its label. Visually, it looks like the label comes first since we used the
order
property, but the screen reader announces the input first instead since it is first in the HTML code.The Web Content Accessibility Guidelines (WCAG) stipulate that
order of content in the source code [should be] the same as the visual presentation of the contentin order to minimize confusion when using accessibility tools. Therefore, I've rated this solution lower as the adjacency combinator requires the elements be in unconventional order.
Using Javascript
Rating: 🌕🌕🌕🌘🌑
When an input is focused or unfocused, it emits an event which we can listen to with Javascript. Additionally, the element has a reference to all of its labels via the labels property. Therefore, we can leverage Javascript with the following strategy:
- When the input receives focus, add a
focused
class to its label. - When the input loses focus (called blurring), remove the
focused
class.
document.querySelectorAll('input').forEach(input => {
input.addEventListener('focus', () => {
input.labels.forEach(label => {
label.classList.add('focused')
})
})
input.addEventListener('blur', () => {
input.labels.forEach(label => {
label.classList.remove('focused')
})
})
})
And now with CSS, you just need to apply styles to the focused
class!
.focused {
color: red;
}
Reviews
Our reviewers gave this solution 3.25 out of 5 moons, citing some browser compatibility issues and complexity as a result of using Javascript.
Ivan Eaton says:
The strategy of leveraging Javascript bypasses all the potential problems with browser compatibility for CSS features, but exchanges that for problems in Javascript feature support.
More specifically, the
labels
property with this solution uses is not supported in IE 11 and can result in errors if not handled. To maximize compatibility while still using this general approach, I recommend reading how to find an input's associated label.
Jose Scott says:
Introducing Javascript when there are decent CSS-only solutions can feel like overkill. The Javascript here introduces the need to properly manage when and how listeners are added to inputs, which requires some care for sufficiently dynamic pages and forms.
That said, this solution may be preferred if an input has many labels or the input and its label are far apart in the HTML hierarchy such that it is difficult to use CSS selectors to style them.
Using :focus-within with Explicit Labels
Rating: 🌕🌕🌕🌕🌘
CSS offers a relatively new pseudo-class called :focus-within
which matches an element if it or any of its descendants have focus. Therefore, as long as the label and its input share a common parent element in HTML, we can use this to style the label:
<div class="input-container">
<label for="name-field">Name</label>
<input id="name-field" type="text" />
</div>
.input-container:focus-within label {
color: red;
}
This works since the focused input is within the input-container
div. The selector is essentially saying, "find an input container with focus somewhere inside of it, then find all the container's child labels."
Of note, we are using the label's for
attribute to associate it to an input. If the input has an id
defined, setting for
to equal that id creates the relationship. Notice that in the HTML code above, both for
and id
are "name-field". This is often called an explicit label.
For more on explicit labels and their counterpart, implicit labels, read Labeling Controls.
Reviews
Our reviewers gave this solution 4.25 out of 5 moons, citing lack of support in old browsers but having the best accessibility of solutions present.
Ivan Eaton says:
Although a simple solution,
:focus-within
is not supported in IE 11. In a world that is moving away from that old browser, this solution becomes better and better, but unfortunately I cannot recommend it to everyone.
Serena Redmon says:
Of solutions presented, this supports accessibility tools the best: it properly assigns a label to an input and keeps code-order and visual-order the same, both being WCAG requirements.
Using :focus-within with Implicit Labels
Rating: 🌕🌕🌕🌕🌑
Instead of having a containing div, the label is able to contain its associated input directly.
<label>Name
<input type="text" />
</label>
With this structure, the :focus-within
pseudo-class, which matches an element if it or any of its descendants have focus, can be applied directly on the label. When the input inside of it has focus, then the label receives the styling we want.
label:focus-within {
color: red;
}
Associating a label and input via hierarchy like this, as opposed to using the input's id
, creates what's called an implicit label, in contrast to explicit labels in the other solutions.
Reviews
Our reviewers gave this solution 4 out of 5 moons, citing minor accessibility issues and lack of support in old browsers.
Serena Redmon says:
Implicit labels are best used when the id of the input is not known and hence an explicit label cannot be created. This is because, generally,
explicit labels are better supported by assistive technology(Ref: Labeling Controls). That said, support has gotten better with time.For example, as of today, most screen readers now equally support implicit labels and explicit labels. However, voice control software, which can be used by people with movement disabilities preventing them from using a keyboard or mouse, does not always adequately support implicit labels.
Ivan Eaton says:
Although a simple solution,
:focus-within
is not supported in IE 11. In a world that is moving away from that old browser, this solution becomes better and better, but unfortunately I cannot recommend it to everyone.
Using :has
Rating: 🌖🌑🌑🌑🌑
A notorious fact about CSS is there is no way to style an element based on elements after it in HTML. So for example, you can't style a quote that contains a link differently than a quote that doesn't, and more relevantly you can't style a label whose input directly after it is focused.
At least, that's true for now: Introducing the :has() pseudo-class!
This pseudo-class matches the element if the selector provided as a parameter also matches. The inner selector is relative to the element with the :has
on it.
label:has(+ input:focus) {
color: red;
}
This says "select labels that have an adjacent sibling which is a focused input". Unlike the Using Adjacent Siblings solution, this does not style the input. This styles the label, exactly what we want.
What's neat about this solution is that the label gets to come before its input and it doesn't require a containing div.
<label for="name">Name</label>
<input id="name" type="text" />
Reviews
Our reviewers gave this solution 0.75 out of 5 moons, citing its experimental status as rendering it unusable for now.
Ivan Eaton says:
Currently, nothing except the newest version of Safari supports this selector, making it impossible to use. Thankfully, Chrome has a proposal out, so maybe someday in the future we'll see this added more universally!
See: Can I Use :has
Codepen
Top comments (1)
Interesting problem and cool solution. Thanks for the sharing.