tailwindcss-forms
tailwindcss-forms copied to clipboard
Style date placeholders to match standard placeholders
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)
Noticed this myself today, just adding this screenshot (Safari) to help make it clear for when someone gets a chance to look into it:
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
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">
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">
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:
Like in Chrome and Firefox, adding a placeholder is not possible but adding a text color is:
<input type="date" class="text-red-500">
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:
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">
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;
}
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:
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:
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);
}
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.
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!
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.
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.
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.