# CSS :placeholder-shown

Use this pseudo-class to style an input that is currently displaying the placeholder text -- in other words, the user has not typed anything in the textbox 📭

It's great to apply some dynamic styling depending if your input is empty or not 👏

input:placeholder-shown {
  border-color: pink;

# How does it work?

The :placeholder-show is a CSS pseudo-class that allows you to apply styling to <input> or <textarea> that has a placeholder text.

<input placeholder="placeholder text" />
<textarea placeholder="placeholder text"></textarea>

  • Pink if placeholder is shown, user has not typed anything
  • Black if NO placeholder is shown, user has typed something

# :placeholder-shown must have placeholder

This selector will not work if the element does NOT have a placeholder text.

<input /><!-- no placeholder -->

<!-- This is also considered no placeholder text -->
<input placeholder="" />
input:placeholder-shown {
  border-color: pink;

No placeholder text displayed

# :placeholder-shown vs ::placeholder

So we can use :placeholder-shown to style the input element.

input:placeholder-shown {
  border: 1px solid pink;
  background: yellow;
  color: green;

☝️Hmmm...notice something odd 🤔 -- We set color: green but it didn't work. Well, that's because :placeholder-shown will only target the input itself. But for the actual placeholder text, you have to use the pseudo-element ::placeholder.

input::placeholder {
  color: green;

⚠️ BUT! When I was playing around with this, I noticed there are some other properties that if applied at the :placeholder-shown level will affect the placeholder text.

input:placeholder-shown {
  font-style: italic;
  text-transform: uppercase;
  letter-spacing: 5px;

Now, I don't really know why this is the case 🤷‍♀️ Maybe because those properties get inherited by the placeholder. Anyways, if you know the real reason, please do submit a pull request in the "Community Input" section 😄

# :placeholder-shown vs :empty

Although :placeholder-shown is specifically made to target if an element is displaying the placeholder or not. We can essentially use it to check if the input is empty or not (of course, assuming, all your input has a placeholder). So maybe your next question, can't we use CSS empty? Well, let's check 👩‍🔬

<input value="not empty">
<input><!-- empty -->
input:empty {
  border: 1px solid pink;

input {
  border: 1px solid black;

❌Expect this to be black

  • Pink if empty
  • Black if NOT empty

Hmmm...from here you might assume that :empty seem to be working because we are seeing the Pink border. But it actually isn't working 😔

The reason the pink is displaying because pseudo-class increases the specificity. Similar to how class selectors (ie. .form-input) has a higher specificity than a type selector (ie. input). Higher specificity selectors will always override the styles of those set at a lower specificity.

Here's the verdict! Don't use :empty to check if an input element is empty or not 🙅‍♀️

# How to check input emptiness without a placeholder?

Okay, so our only way of checking if an input is empty or not is using :placeholder-shown. But what happens if our input element doesn't have a placeholder. Well here's a clever way! Pass in an empty string, " ".

<input placeholder=" "><!-- 👈 pass empty string -->
input:placeholder-shown {
  border-color: pink;

No placeholder text displayed

Thanks @jeltehomminga for sharing this tip!

# Combined with other selectors

So it's cool we can target the input elements that are displaying the placeholder text. In other words, if the placeholder text is shown, then it must mean that element is empty. Using this knowledge, we can combine this pseudo-class with other selectors and do some really neat things! Let's have a look 🤩

# Inverse :placeholder-shown with :not

We can do the inverse of something using the :not pseudo-class. Here, we can target when the input is NOT empty.

<input placeholder="placeholder" value="not empty" />
input:not(:placeholder) {
  border-color: green;
  • Green if NOT empty, user has typed something
  • Black if empty

# Floating Label

One of the problems of using a placeholder and not using a label is accessibility. Because once you're typing the placeholder text is gone. This can lead to confusion for the user. A really good solution is the floating label. Initially, the placeholder text is displayed without the label. And once the user starts typing, the label will appear. This way you can still maintain the cleanliness of your form without compromising the user experience or accessibility. Double win 🥳

And this is possible with pure CSS. We just have to combine placeholder-shown with :not and +. Here's a super simplified version of the floating label.

<input name="name" placeholder="Type name..." />
<label for="name">NAME</label>
label {
  display: none;
  position: absolute;
  top: 0;

input:not(:placeholder-shown) + label {
  display: block;

And if you start typing, this is the result you will get:

# Browser Support

Support for :placeholder-shown is excellent! This includes Internet Explorer (yes, I'm as surprised as you are 😆). But (of course there's a "but"), for IE, you will need to use the non-standard name :-ms-input-placeholder.

Browser placeholder-shown
Chrome ✅
Firefox ✅
Safari ✅
Edge ✅
Internet Explorer ✅ (:-ms-input-placeholder)

# Community Input

  • @cilly_boloe: This works really well with adjacent labels you can move or highlight with "input:placeholder-shown + label" instead of having to add a custom class with JS on focus events like I had been doing 🙃

  • @xirclebox: You can take this a step further by adding an explicit or implicit label to the input. That way the users will always know what the field is for. This is great for UX and accessibility.

  • @robertgroves: I put up a quick CodePen to show how this could be used to include a visual for indicating that input from the user is still needed.

  • @jibijohndavid: CSS Only Floating Label: CodePen

  • @codedgar_dev: I use that along :valid and :invalid to validate natively inputs in my framework Puppertino

  • @nachocoloma: You can make it required and use :invalid, but that would be triggered by any failing validation, not just a missing value (and maybe the case is not an empty required field). Better use an empty placeholder instead.

# Resources

Related Tidbits