tailwindcss-forms icon indicating copy to clipboard operation
tailwindcss-forms copied to clipboard

Style date placeholders to match standard placeholders

Open hailwood opened this issue 4 years ago • 2 comments

The placeholders on date inputs don't respect the standard placeholder attribute. It would be great if we could have the pseudo elements styled so they match the placeholder e.g. changing

'input::placeholder, textarea::placeholder'

to

'input::placeholder, textarea::placeholder, ::-webkit-datetime-edit'

There's a bit more to it (e.g. the firefox placeholders)

hailwood avatar Jan 25 '21 02:01 hailwood

Noticed this myself today, just adding this screenshot (Safari) to help make it clear for when someone gets a chance to look into it:

image

adamwathan avatar Aug 27 '21 11:08 adamwathan

I spent an absolutely ridiculous amount of time looking into this many months ago for a personal project. I came to the conclusion that it's not simple. There are solutions but they all have problems. :/

I created a Tailwind Play with comments in the CSS and a preview of what works and what does not: https://play.tailwindcss.com/IV31S1G6iv?file=css

thecrypticace avatar Mar 01 '22 22:03 thecrypticace

Hey! So I just spent more time that I'd like to admit trying to figure this out, and I've ultimately come to the unfortunate conclusion that this can't be fixed in Safari. Let me walk you through what I've learned...

What I've come to realize is that browsers don't consider the date input empty state "value" (which is "YYYY-MM-DD" in Chrome and Firefox, and the current date in Safari) to be a placeholder, even though it very much has a placeholder vibe to it. Because it's not a placeholder, it can't be styled using the ::placeholder pseudo selector in CSS.

So, what can we do?

Well, before we get into Safari, let's start by looking at Chrome and Firefox. These browsers always show the "YYYY-MM-DD" empty value in the same color as the date input's text color. So, applying a text color like text-red-500 results in this:

<input type="date" class="text-red-500">
image

Trying to apply a placeholder color results in no changes to the "YYYY-MM-DD" empty value:

<input type="date" class="placeholder:text-red-500">
image

There really isn't any concept of a date input placeholder in Chrome and Firefox.

Where this gets complicated is in Safari. Safari doesn't show "YYYY-MM-DD" for the empty state, but instead shows today's date, but in a muted placeholder style:

image

Like in Chrome and Firefox, adding a placeholder is not possible but adding a text color is:

<input type="date" class="text-red-500">
image

However, when you set a text color, Safari automatically adjusts the text brightness to make sure that the default date input (today's date) is muted enough, making sure to distinguish it from a real value. However, you have no control over the exact color that Safari chooses here. It has some type of algorithm and metrics it uses to set this, and despite all my best efforts I was unable to get it to use the placeholder color of my choosing.

Basically Safari takes whatever color you set for the date input text color, and then reduces it by about 50%. Even if you set it to straight up black, it's still pretty muted:

image

And what's really weird is that, depending on the color you choose, it might even just straight up throw that away and use blue instead:

<input type="date" class="text-slate-950">
image

Using the -webkit-datetime-* pseudo classes

Now Safari does provide the -webkit-datetime-* pseudo classes, allowing you to style each individual part of the date, and what's interesting is that this only seems to impact the date input when there is an actual value, not the empty state. So, at first I thought that we could potentially use this to style the empty "placeholder" color independently of the text color:

<input type="date" value="1983-06-02" />
<input type="date" />
/* set the default date text color */
input[type="date"]::-webkit-datetime-edit-year-field,
input[type="date"]::-webkit-datetime-edit-month-field,
input[type="date"]::-webkit-datetime-edit-day-field {
  color: theme(colors.gray.700);
}

/* set the slashes to gray 700 */
input[type="date"]::-webkit-datetime-edit-text {
  color: theme(colors.gray.700);
}

/* adjust the placeholder style to be as dark as possible */
input[type="date"] {
  color: black;
}
image

And honestly, when you first see the results here it actually looks like we've accomplished something. Unfortunately, in reality Safari has chosen an empty "placeholder" state color that doesn't really match what we might use for our other placeholder colors. For example, imagine we want a placeholder color of text-gray-500 for our inputs:

image

Notice here how the date input placeholder doesn't match with the text input (gray-500) placeholder, and of that makes sense since we're just relying on whatever Safari is converting the black text color to. Meaning, if we wanted to get more specific/exact with the particular gray we want to use for the date input placeholder (like we are with the text input placeholder), we basically can't.

And if you compare this to how things look today, you can see that it's hardly any different — we haven't meaningfully improved things despite all our best efforts:

image

Hacking validation

Also, just in case anyone looks at this issue in the future, some folks suggested using client-side validation in conjunction with the -webkit-text-fill-color property to solve this. The -webkit-text-fill-color property is interesting because it cannot have an opacity, so it forces the date input color. For example:

<div><input type="date" required /></div>
<div><input type="date" value="1983-06-02" required /></div>
input[type="date"] {
  color: theme(colors.gray.900);
}

input[type="date"]:invalid {
  -webkit-text-fill-color: theme(colors.gray.500);
}
image

While this kind of works, it's really hacking the :invalid psuedo selector for something it's not intended to be used with. For example, if the date input is invalid for a different reason — maybe it has a max validation rule on it — you'll end up seeing the fake placeholder styles.

(Oh and if you're wondering if you can use the -webkit-text-fill-color property in my first solution to get around this, unfortunately nope, as this property takes precedence over the color, meaning there is no way to target the two date input states independently and you'll just get the -webkit-text-fill-color in both states.)

Conclusion

So ultimately, while it is possible to slightly affect the color of Safari's empty value "placeholder" color, it's hardly a perfect science and doesn't lead to super great results. Given all that, I am inclined to just leave things as they are and close this issue for now. Who knows, maybe Safari improves the APIs here in the future and gives us more control, but I am not holding my breath 😅

@hailwood Thanks either way for sharing — it would have been nice if we could find a reliable way to solve this, but some things on the web just are what they are.

reinink avatar Aug 18 '23 17:08 reinink

This has been a wild ride, I could feel the "I've got it!" excitement rise and fall reading through all that. Thanks for looking into this and sharing @reinink!

hailwood avatar Aug 20 '23 21:08 hailwood

My solution based on previous work here and more experiments.

-webkit-text-fill-color: supports opacity when using rgba which means we can use a consistent colour. The downside here is that the input with the value is also transparent.

image

The next part requires toggling a 'js' and no-value class on the input. We rely on the .js class to add our css overrides without effecting the input for non-js users. The final trick is to add a change event listener to the input which toggles the no-value class. If the no-value and js classes are applied set the -webkit-text-fill-color: to a transparent version.

image

Here's the pen https://codepen.io/leevigraham/pen/VwRpRpv

This is non tailwind example but I'm sure toggling tailwind classes could achieve the same thing.

leevigraham avatar Jan 18 '24 05:01 leevigraham