Skip to content

Style date placeholders to match standard placeholders #38

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
hailwood opened this issue Jan 25, 2021 · 6 comments
Closed

Style date placeholders to match standard placeholders #38

hailwood opened this issue Jan 25, 2021 · 6 comments
Assignees

Comments

@hailwood
Copy link

hailwood commented Jan 25, 2021

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)

@adamwathan
Copy link
Member

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

@thecrypticace
Copy link
Contributor

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

@reinink
Copy link
Member

reinink commented Aug 18, 2023

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 reinink closed this as completed Aug 18, 2023
@hailwood
Copy link
Author

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!

@leevigraham
Copy link

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.

@adamwathan
Copy link
Member

Sooo, what's the best practice for this use case now?

Just deal with it looking bad or use a JS-based date picker :/ The web sucks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants