HTML Placeholder Text: Styling, Floating Labels and Accessibility
The HTML placeholder attribute works in every current browser, so you no longer need Modernizr, jQuery or a script that fakes hint text with value and onfocus. Write placeholder="…", style it with ::placeholder, and react to it in CSS with :placeholder-shown. What matters now is using it correctly, because a placeholder is a hint, not a label.
This guide covers the native attribute, how to style it so people can read it, how to build a floating label in pure CSS, and how to tidy the search input. Everything below was built and tested in a standalone demo.

The native placeholder attribute
The attribute shows short text inside an empty text-like input and removes it as soon as the user types. It applies to text, search, url, tel, email, password and number inputs, and to textarea.
<label for="email">Email</label>
<input id="email" name="email" type="email"
placeholder="[email protected]" autocomplete="email">
Notice the real <label>. Placeholders are not a replacement for one, for reasons covered further down.
What the old fallback did, and why you can delete it
Older browsers ignored the attribute, so tutorials (including the first version of this one) detected support with Modernizr and used jQuery to copy the placeholder into the field’s value, clearing it on focus and restoring it on blur. That approach had real bugs: the fake text was submitted with the form unless you stripped it, screen readers read it as user input, and autofill and validation treated it as data. Since the attribute is supported everywhere, remove that code entirely rather than keeping it as a safety net.
Style the placeholder with ::placeholder
The ::placeholder pseudo-element targets the hint text. Browsers default to a light grey that often fails contrast requirements, so set your own color.
:root {
--muted: #5b6475;
--accent: #2457d6; /* focus color, used by the floating label below */
}
input::placeholder,
textarea::placeholder {
color: var(--muted);
opacity: 1; /* Firefox historically dimmed placeholders */
}
Per MDN, only a limited set of properties applies to ::placeholder (font and text properties, color, opacity, backgrounds), so you cannot position or animate it reliably. That is why the floating label below animates a real <label> instead.
WCAG asks for a 4.5:1 contrast ratio for text, and placeholder text counts. #5b6475 on white clears it, but that figure only holds on a white field. On a dark theme, re-check the ratio against the dark background, or define the color as a custom property and override it per theme (for example inside @media (prefers-color-scheme: dark)). Also keep the hint visually different from typed text, or people assume the field is already filled in.
Why a placeholder is not a label
- It disappears. Once the user types, the instruction is gone. Anyone who is interrupted, or who needs to double-check what a field wants, has to clear the field.
- It is weak for assistive technology and autofill. A programmatic label is the dependable way to name a control. Placeholder-only fields are a common accessibility failure, and the HTML standard itself says the attribute should not be used as a substitute for a label.
- It looks like a value. Grey text in an empty box is easy to mistake for a prefilled answer, and easy to skip.
Use placeholders for short format examples ([email protected], DD/MM/YYYY) and keep the question in a label. If the layout is tight, use a floating label, which keeps the label visible at all times.
Build a floating label with :placeholder-shown
The :placeholder-shown pseudo-class matches an input while its placeholder is visible, which means while it is empty. You can use that to move the label out of the way only when there is something to protect. One requirement: the input must have a placeholder, otherwise the pseudo-class never matches. A single space does the job and shows nothing.
<div class="field float">
<input id="name" name="name" type="text" placeholder=" " autocomplete="name">
<label for="name">Full name</label>
</div>
The label comes after the input so the sibling combinator ~ can reach it from the input’s state.
.field { position: relative; }
.float input {
width: 100%;
padding: 1.45rem .9rem .5rem; /* room for the small label */
}
.float label {
position: absolute;
top: .95rem;
left: .9rem;
color: var(--muted);
pointer-events: none;
transform-origin: left top;
transition: transform .15s ease, color .15s;
}
/* Float when focused OR when the field has content */
.float input:focus ~ label,
.float input:not(:placeholder-shown) ~ label {
transform: translateY(-.6rem) scale(.78);
}
.float input:focus ~ label { color: var(--accent); }
@media (prefers-reduced-motion: reduce) {
.float label { transition: none; }
}
Two details are worth the extra lines. The rule :not(:placeholder-shown) keeps the label floated after the user types and moves focus away, and it also handles values restored by the browser or set by the server. Animating transform rather than top or font-size keeps the motion cheap and free of layout shifts, and the reduced-motion query respects people who turn animation off.
Because the label is a real <label for>, clicking it focuses the input and screen readers announce it. If you would rather not write this yourself, our Bootstrap 5 contact form with floating labels and registration form with floating labels use the same idea with Bootstrap’s built-in .form-floating component.
Autofill, textarea and select
Browser autofill is the usual way this pattern breaks. In Chrome and Safari an autofilled value can appear while the field still counts as placeholder-shown, so the label sits on top of the filled text until the user interacts with the page. Add the autofill state to the floating rule:
.float input:autofill ~ label,
.float input:-webkit-autofill ~ label {
transform: translateY(-.6rem) scale(.78);
}
The standard :autofill and the prefixed :-webkit-autofill go in separate rules in production code, because a browser that drops one selector in a list drops the whole rule. Test with a saved address in your target browsers, since autofill behavior varies by version.
The technique works on <textarea> with one change: keep the label at the top-left, give the textarea placeholder=" " and the same top padding, and use the same ~ label rules. It does not carry over to <select>, which has no placeholder to match. For selects, keep a normal visible label above the control.
Clean up the search input
WebKit and Blink browsers add their own magnifier and clear button to type="search". The old fix used -webkit-appearance. The unprefixed appearance property is now standard, and the vendor pseudo-elements are still how you hide the extras.
input[type="search"] {
appearance: none;
}
input[type="search"]::-webkit-search-decoration,
input[type="search"]::-webkit-search-cancel-button {
display: none;
}
Think before hiding the clear button: it is a useful control. If you remove it, offer your own. For more search patterns, see Beautiful CSS3 Search Form and the expanding search bar with suggestions.
Show errors without fighting the placeholder
Use :user-invalid rather than :invalid to mark problems. It matches only after the user has interacted with the field, so an empty required input does not turn red on page load.
input:user-invalid {
border-color: #c62828;
}
Pair the color with text, since color alone does not communicate an error to everyone. Link the message to the field with aria-describedby. The same attribute is the right home for hints that would otherwise tempt you to cram instructions into the placeholder, such as password rules: put the hint in an element below the field and reference it, so it stays visible and screen readers read it after the label.
Checklist
- Every field has a visible, programmatically associated
<label>. - Placeholders show only format examples, never the instructions.
- Placeholder color meets 4.5:1 against the field background, in light and dark themes.
- Floating labels use
placeholder=" ",:placeholder-shownand a reduced-motion fallback. - Use the right
typeandautocompletevalues so mobile keyboards and autofill help. - No JavaScript is needed, and no fake
valuetext.
The same thinking applies to other form controls. Our 20 pro CSS tips cover more selectors worth knowing, and The Power of CSS Selectors goes deeper on state pseudo-classes like these.
I posted something similar a while ago on my own blog
http://kamikazemusic.com/quick-tips/jquery-html5-placeholder-fix/
There were concerns raised in the comments about the accessibility of injecting text into the val attribute, personally I think used sensibly and as long as the text is injected with JavaScript then it should be fine, maybe some readers here have a different view?
I read your post Dave, but I think it’s fine to use it. Only thing I could think of is if you don’t have a seperate label, an older browser, and no Javascript enabled. You don’t see any hints about the field. But hey, who doesn’t have Javascript these days.
But I think it’s just great to use this new feature.
Regarding JavaScript: I am looking at the demo code and see jQuery, Modernizr, and a ton of extra markup, when all of the above could have been taken care of by jQuery alone. Just a thought :)
This is a useful resource for seeing what attributes are supported by the modern browsers when styling the placeholder.
It’s not possible to style the placeholder at all in IE9, Opera 10 (and below) or Firefox 3.6, and the rest also have mixed support.
Though my comment here doesn’t change the *technical* aspect of the post (providing a fallback for placeholder), I think there is a large problem with the use case of the placeholder.
There is a difference between the HTML5 placeholder, and what I call an in-field label (What you are actually showing in this post). The placeholder is supposed to show *how* the user should input data. An in-field label serves the same purpose as a real label, only it appears in (or over) the field.
Here are some examples of the difference:
Label: Email
Placeholder: [email protected]
Label: Phone number
Placeholder: 413-123-4567
Just because the HTML5 placeholder looks like in-field labels we see around, it doesn’t serve the same purpose. If you plan to use the placeholder attribute, you should probably also provide a label.
That’s a debate I’ve had with a few people, too many treat it like a label where as a placeholder should simply show the format of an input and not be it’s label.
I think it’s fine to use placeholders as a replacement of labels, it’s cleaner and more condensed. It’s not apart of yesterdays semantic web, but i think tomorrow it should be.
Well Jonathan, I think that’s different with every person.
Personally I really really hate it when my label is gone on focus. Because I tab all my forms, I don’t see what type of input is needed in the next inputfield.
So I have to make 2 extra tabs, to see what inputfield I have when there is no seperate label.
But hey, that’s me of course
I agree with Rene. For people who tab through forms, which is gaining popularity as forms grow more on the web, not being able to see what type of input is required is just bad coding. That being said, I think it is perfectly acceptable, and I do this all the time, to use placeholders as labels on text inputs that are easy to discern the required data. For example a search form on a website could use a placeholder. They are usually only one field and the submit button will actually help act as a label because it will say something search or just be a search icon.
Jonathan, but they serve completely different needs. Even though the HTML5 spec isn’t finalized, the goal of a placeholder is quite different than a label. A label also provides screen-readers with essential information about the field. I am not sure how they do with placeholders. Even though web standards have been widely adopted, mis-using an attribute or element is just as bad as it ever was.
I agree with Doug Neiner here, I think label is still required in web form. Label and placeholder serve two different purposes. Placeholder is not equal to label.
The entire point of my demo is just to show placeholder (not web standard form). Hence I didn’t even put a submit button in the form.
I fail to see how this is better than value.
2 reasons:
1) Value serves a different purpose. It can be used as the default form submission value when the visitor doesn’t input one, whereas placeholder text is meant to be replaced, and will not return a value if the visitor doesn’t supply one.
2) Without javascript, value doesn’t disappear when the input is clicked. The user has to manually delete it.
I see your point and it is cool but as Jake pointed out in a comment below : “That’s all very well, but why not just use the jQuery code and not bother with the HTML5? This would mean you don’t have to add modernizr to your page load”. Plus your form validation would catch the default value out before submitting the form.
Thanks for the helpfull article.
I believe it’s quite common to remove the ‘Chrome’ borders of input boxes with outline:none (on the :focus) state. What’s the difference between this method and input[type=”search”]::-webkit-search-decoration?
The JS could be better. In general, avoid to select twice the same DOM elements. Store stuff in variables if nesseserly. And you should use ‘===’ instead of ‘==’ and i wonder why you need a second .blur() after the .blur()…
That’s all very well, but why not just use the jQuery code and not bother with the HTML5? This would mean you don’t have to add modernizr to your page load.
Be *very* careful when doing this. It interacts very poorly with other jQuery plugins because they can’t tell whether the input’s value is legitimate or not. For example, this completely breaks most autocomplete and combobox plugins.
So, we’re importing two libraries to do something that was formally one line of JavaScript? I think that’s a bad idea. If you’re already using jQuery and Modernizr, thats OK. If you’re not and this is all you want to do, save all the extra HTTP requests / downloads, and roll your own. It’s not that hard to detect placeholder support and use the “old school” way (even though the jQuery is doing almost the exact same thing).
hey, i have a problem to see the demo, please put the right link please.
thanks.
Good,I like it,that is good way to placehold text!
If you wanna test just placeholder support you can use this instead Modernizr:
function placeholderSupport(){
return ‘placeholder’ in document.createElement(‘input’);
}
For a more robust solution, feel free to use my placeholder plugin for jQuery. It works for password inputs, textareas, and detects placeholder support for inputs and textareas separately.
If you’re not using jQuery and don’t need to test a bunch of features then you could try my standalone version in good old-fashioned Javascript:
Oh! My God! IT’ S very complexif and pepfull I was going to take when I need the instruction.Who it works?
Your focus() callback should apply the same check (check whether input.val() is empty or equal to placeholder value, not simply check whether equal to placeholder value) used in the blur() callback. Otherwise IE8 winds up never removing the placeholder class from the input.
This is good but I think it’s a little bit of over kill. My personal opinion is the placeholder should be used as an enhancement if the browser supports it if not then leave it.
What is the advantage of writing all these code instead of old method? I think it’s too early to introduce this new html5 tag. Isn’t it?
@Gleenk it can;t be too early… especially as most browsers now support HTML5
Great share.
It would be great if the input field would not remove the placeholder text on focus but rather as soon as a key event happens. That way you will still know what to type in the input field. The problem comes when you use [tab] to cycle through your input fields; there is a chance that without a label you won’t know what you’re supposed to fill in.
For our signup page (at shopify.com), we’re using transparent input fields with labels that emulate that functionality – even though not HTML5 – are more user friendly.
I want to take this moment to say that your post is really useful for the people who are working on HTMl5. I really like the work that has gone into making the post.
its a good language where any person can understand easily.web designer can modify but the person can also care the language and modify. skygatemedia is our site if any person have want to make web design then contact us
AddFrog.com is a free classifieds site to post ads to buy and sell products and services online. Post free classifieds for sale, purchase, rentals and services related to jobs, real estate, education, automotive, pets, travel, matrimonial, electronics, home appliances, health, machines etc. Find local classified advertisement ads for used and new products at best price in India.
I love the fact that you clear the input fields of the placeholder text when the form is submitted. This was one big advantage to using the HTML5 placeholder, and it’s great to see an exact implementation in JavaScript.
Recent surveys, children of depressed mothers’ negative patterns of activity occurring in different brain reveals. This is for children of mothers who take more risks in the future is going to have depression.
Unique Internet Marketing Services – the Marketing division of Uniqueweb Technologies in India. Explore the sections of SEO, PPC, Social media and avail the best services for your business now.
placeholder is a very nice addition to html , saves alot of js usage .
This is really useful HTML5 trick. Tks jQuery, tks Nick!
One thing is sure that HTML5 will really ease the pressure on css coders. You can code fast and make more money.
What is friendship?Mont Blanc PenWhy do we call a person our friend? When do we call someone a very good friend?
I just tried to tab through this page to get to the link to your demo (I cannot use a pointing device) and got taken straight to the comment form. Since I’m here…
That is a major nuisance to say the least and there is no good reason to mess about with the tab order in this case (or, indeed, in any but a very few specific cases). And aside from the form elements, there are no focus styles, so I cannot see where the focus is without constantly moving my attention between the page content and the browser’s status bar. That is another nuisance.
Still, the part of the article that I got to read, before being dumped down to the bottom of the page, was quite good…
It gets worse when you start mixing the charsets.
It’s easy to resent punctuation. Its purpose is to clarify sentences, so why are the rules governing it so complicated? There are so many exceptions, so many exceptions to exceptions — it’s enough to make you want forego punctuation altogether. Well, back when it was alive and kicking, the Latin language did just that — and it didn’t stop there. Written Latin also omitted spaces between words or lowercase letters.
I was searching this kind of blog on internet, after finding you my search has been finished. I have learnt many new ideas from this blog, which are very beneficial for me. Thanks for the nice information.
Part two of last week’s examination of the personality of punctuation. Click here to read Part 1. Em Dash and Hyphen Em dashes and hyphens are sisters, and whenever they go out together, they get stopped by strangers. “Are you twins? You all look so much alike!” At this, they roll their eyes. Sure, they look similar, but it’s obvious — to them, at least — that they’re each completely unique.
Its look like theres a issue with passwords fields
I think the best advantage of using HTML 5 is the fact that is can be rendered correctly very easily in multiple browsers which renders the need for different versions of a site to be no longer necessary.
Unfortunately you can’t simply make the value of a password field equal to ‘password’ because it will be obfuscated to ******* so the only reliable way is to have a background graphic of the word ‘Password’ instead.
Personally, I prefer to use the proper HTML5 placeholder text and leave nothing for passwords. If people can’t update their browsers, poor them! ;)
another method is cloning the entirely, but changing the type before appending it to the DOM, thereby circumventing the jQuery protection as well as the bug in older versions of IE. this is how we’ve solved it in our plugin:
Well, you can use a label and absolutely position it over the password input and then change its display state via JS. When user focus in the input and it’s empty, the label (false placeholder) will appear and so it will if the user focus out and the input is empty. If user press a key when focus in the input and value is null, then display it. If input is not empty in any of these cases, the false placeholder will disappear.
You have to use ID and FOR attributes so input will get focused when clicking the label.
I tweaked it to fix this issue, by cloning the input as a text input and hiding the old. Then removing the new one / switching focus to the old one on focus:
https://gist.github.com/jonraasch/2892148
I believe you are wrong, Matthew. If you use js to change the HTML5 input type from “password” to “text” when the user has not entered text, it will be displayed in plaintext while the password is obscured properly when entered.
I’m having an issue with IE8. I’m loading in forms through ajax and in IE8 all I get is empty inputs with no placeholders. Every other browser seems to handle this OK, and if the inputs are on the page initially, they work in IE8 too.
Thoughts?
Not sure why this is only a problem only in IE8, but to be sure that all your events are firing in the correct order you might want to place Nick’s code into a function then execute that function with a callback after you have loaded the form.
Or, as a second thought just hit me, you might want to change the .focus and .blur to .live(‘focus’ … and .live(‘blur’ … to make sure those events are bound to elements that get injected into the DOM after the .ready event fires.
Good luck, hope that helps. :)
Me like HTML 5 a lot, a whole lot!
One thing is that one of the most popular incentives for utilizing your card is a cash-back and also rebate present. Generally, you will get 1-5% back upon various expenditures. Depending on the credit card, you may get 1% in return on most expenses, and 5% again on expenses made at convenience stores, filling stations, grocery stores plus ‘member merchants’.
Thinking about where this could be used – perhaps the comment form wouldn’t be a good place. If for example you used the placeholder text “enter your website” for the website input field and the commenter decided not to enter a website – thereby leaving the placeholder text along – then “enter your website” would be submitted with the comments as the web address. This would basically mean lots of links to “enter your website” which would of course be a broken link.
thanks for tuts.. I like java and jquery.. good luck..
Erm… just downloaded the demo, opened it in Multiple IEs (6, 7, 8, or 9) and it doesn’t work.
Am I missing something here?
just downloaded the demo, opened it in Multiple IEs (6, 7, 8, or 9) and it doesn’t work.
I do not know if it’s just me or if everybody else encountering issues with your website. It seems like some of the written text in your posts are running off the screen. Can someone else please comment and let me know if this is happening to them too? This may be a issue with my web browser because I’ve had
this happen before. Appreciate it
What Modernizr test does this require if I’m doing a production build?
I’m amazed, I have to admit. Seldom do I come across a blog that’s equally educative and entertaining, and
let me tell you, you have hit the nail on the head. The issue is something that not enough men and women
are speaking intelligently about. Now i’m very happy I came across this in my hunt for something concerning this.
This is so good. It’s good when things work.