Anne van Kesteren

Generated content and replaced elements

How should the following work:

img::before { content: "Image: " }

If it should work at all. Note that ::before and ::after create elements inside the elements they are applied upon. For example, when applies on a span element it would look like:

<span><::before/>... actual content ...<::after/></span>

For the img element, which is technically a replaced element, this raises some questions. The most logical way to do it when pseudo-elements would apply to replaced elements seems to be that an anonymous box is created inside the actual element. However, the height and width properties are traditionally used to scale the image, which would in this case be the anonymous box. That seems inconsistent. Especially as you expect img { border:solid } to go around the generated content...

The CSS WG is looking for input on this issue. Comments are open for those who have anything to say about it. (Feel free to try out versions of Opera that have support for it to see how it works there...)


  1. My understanding was that CSS3 was going to introduce a new pseudo-element (::outside IIRC) which would also work with replaced elements. Although I certainly wouldn't mind if CSS2.1 were to let :before/after work with <img> as shown by that infamous CSS2 example...

    Posted by Doug Wright at

  2. I have been wondering myself for some time. I eventually concluded that it'd probably be best if the generated content applied (and only applied) to the alt tekst. My thoughts on this are not quite organized enough to bother explaining as of why, but it "feels" right to me so far. I'll probably get back on the subject if it were to rise on www-html.

    Posted by ACJ at

  3. ACJ: I think you are right. And it is going to make much more sense in XHTML 2.0 where the alternative tekst is the content of the img element. Another reason why the alt attribute was a mistake. But otherwise, elements that do not allow child nodes can not support ::before and ::after.

    Posted by Sjoerd Visscher at

  4. How about the br tag?

    Posted by namegh at

  5. I agree with ACJ. By the way, for the object element, the generated content can be added inside the object element. e.g.

    <object type="image/svg+xml" data="example.svg"><::before/>... nested content ...<::after/></object>

    Posted by minghong at

  6. Especially as you expect img { border:solid } to go around the generated content...

    Do I? I don't think I do. Just as I would want height and width to only apply to the image and not the generated content, so too would I want border to only apply to the image. From a design standpoint, it is part of the image's decorations. If the border is unrelated, it would be around an appropriate wrapper.

    If you choose to advocate for before and after to only apply to the alternative text, then there is a need for two pseudo-classes that allow external text if you wish to avoid a proliferation of useless divs.

    Does generated content go inside an inline-block? In that case, this matter is certainly a headache.

    Posted by Leons Petrazickis at

  7. I'm going to go against the grain and say it should do what you expect... "Image: [image data]". Here's why. I see the content as the data model - this is after the xml processing - as something like:

    <img> [image data] </img>

    Likewise, I see the model for a paragraph as:

    <p> [text data] <em> [text data] </em> [text data] </p>

    So adding content in the above creates:

    <img> <::before> [text data] </::before> [image data] </img>

    It is the same when adding image/* as the ::before content (if that is allowed... don't know).

    P.S. My well-formed XHTML fragment doesn't work... it uses CDATA sections. Please Fix!

    Posted by Jimmy Cerra at

  8. And that breaks width/height... Should have paid more attention to the comments. The fix may be that the width and height apply to the replaced content, the [image data], rather than the element's CSS width/height itself. Then the element's width/height expand to fill it as specified by the spec. But that may cause more problems than just adding an implied box around the image data. I guess I don't have a strong opinion any way... Just food for thought.

    Posted by Jimmy Cerra at

  9. ACJ, then it is no longer a replaced element anymore. Most browsers treat that as an inline box to which generated content should apply. (At least, in my opinion.) Same for the object element by the way. You could assume that inside the object element there is an anonymous box for the image so ::before and ::after can apply, but what about height, width and border?

    Posted by Anne at

  10. I've tested around with this some time ago. Opera indeed works quite weird (or sense making, dunno) with the image border if the image is displayed inline (it doesn't go around the image anymore). I think generated content for empty elements should just be ignored (like Mozilla does right now, apart from the HR).

    Posted by Krijn Hoetmer at

  11. I think that it should generate:

    <img alt="foo" src="bar"><::before>Image: </::before></img>

    If the image is available then the generated content should be ignored because the element is replaced. If the image is not available then it should display the alt text as normal, and ignore the generated content since the HTML spec says the IMG element must be empty.

    Making special cases and messing with the alt attribute seems very messy to me.

    Posted by zcorpan at

  12. What happens if the generated content is also an image?

    Posted by Jimmy Cerra at

  13. Agree with zcorpan. The CSS should generate <img alt="foo" src="bar">Image: </img>, and that text should be ignored by UAs (just like Gecko and MSIE do right now). Opera's implementation—in my opinion at least—is screwy. It's a simple rule, let's not mess it up.

    Posted by Craig Anderson at

  14. The problem is that there is no rule. Please pay attention. Also, is the following a replaced element:

    foo { content:"bar" }

    (According to the editor of CSS 3 Generated Content it is not. According to me that is confusing, but whatever.)

    Posted by Anne at

  15. Personally, I don't see a conceptual problem with an <img> tag having content, the spec is just arbitrarily not allowing it. What does it look like? Perhaps the inverse of other elements: The image would be in the foreground and the contained content would essentially be a background, showing through transparent portions.

    It would be nice to be able to raise the z-order value of the inner content to force it above the image (overlaid image labels), but it seems like interpreting the inner content as background means that no z-index value could put it above the actual image content.

    Should I talk to my Senator about allowing xhtml <img> tags to have content?

    Posted by Eric Everman at

  16. I liked the foreground/background idea. I suppose it would be possible to use normal z-index values: img::before { content: "Image: "; } creates background text for transparent areas, img::before { content: "Image: "; z-index: 1 } creates foreground text. I'd imagine inline boxes in both cases. Border would obviously apply to image only (how weird ideas like positioning the generated content outside the image might work out I won't get started on..)

    Posted by Hallvord at