CSS content generation before or after 'input' elements [duplicate]

In Firefox 3 and Google Chrome 8.0 the following works as expected:

<style type="text/css">
    span:before { content: 'span: '; }
</style>

<span>Test</span> <!-- produces: "span: Test" -->

But it doesn't when the element is <input>:

<style type="text/css">
    input:before { content: 'input: '; }
</style>

<input type="text"></input> <!-- produces only the textbox; the generated content
                                 is nowhere to be seen in both FF3 and Chrome 8 -->

Why is it not working like I expected?


With :before and :after you specify which content should be inserted before (or after) the content inside of that element. input elements have no content.

E.g. if you write <input type="text">Test</input> (which is wrong) the browser will correct this and put the text after the input element.

The only thing you could do is to wrap every input element in a span or div and apply the CSS on these.

See the examples in the specification:

For example, the following document fragment and style sheet:

<h2> Header </h2>               h2 { display: run-in; }
<p> Text </p>                   p:before { display: block; content: 'Some'; }

...would render in exactly the same way as the following document fragment and style sheet:

<h2> Header </h2>            h2 { display: run-in; }
<p><span>Some</span> Text </p>  span { display: block }

This is the same reason why it does not work for <br>, <img>, etc. (<textarea> seems to be special).


This is not due to input tags not having any content per-se, but that their content is outside the scope of CSS.

input elements are a special type called replaced elements, these do not support :pseudo selectors like :before and :after.

In CSS, a replaced element is an element whose representation is outside the scope of CSS. These are kind of external objects whose representation is independent of the CSS. Typical replaced elements are <img>, <object>, <video> or form elements like <textarea> and <input>. Some elements, like <audio> or <canvas> are replaced elements only in specific cases. Objects inserted using the CSS content properties are anonymous replaced elements.

Note that this is even referred to in the spec:

This specification does not fully define the interaction of :before and :after with replaced elements (such as IMG in HTML).

And more explicitly:

Replaced elements do not have ::before and ::after pseudo-elements


Something like this works:

input + label::after {
  content: 'click my input';
  color: black;
}

input:focus + label::after {
  content: 'not valid yet';
  color: red;
}

input:valid + label::after {
  content: 'looks good';
  color: green;
}
<input id="input" type="number" required />
<label for="input"></label>

Then add some floats or positioning to order stuff.


fyi <form> supports :before / :after as well, might be of help if you wrap your <input> element with it... (got myself a design issue with that too)