This is impossible to Google for because every article talking about the :before
and :after
pseudo-elements seems to use the word 'content'.
I heard about it in this CSS-Tricks article, explaining how to implement an image slider as an example use-case for web components. The code example it appears inside is thus:
CSS
#slides ::content img { width: 25%; float: left; }
HTML
<template> ... <div class="inner"> <content select="img"></content> </div> </template>
It seems to be referring to this <content>
tag, which is used to allow the user to include Web Components, but I would love to understand this more deeply.
EDIT:
After reading further, in the aforementioned article, I discovered a link the author's "Shadow DOM CSS Cheatsheet" which includes a passage that explains what the ::content
pseudo-element is:
Selects distributed nodes inside of an element. Needs to be paired with polyfill-next-selector for browsers that do not support the native selector.
::content h1 { color: red; }
Source: http://robdodson.me/blog/2014/04/10/shadow-dom-css-cheat-sheet/
This is helpful, but I still find the whole affair rather opaque. Any additional insights?
::before (:before) In CSS, ::before creates a pseudo-element that is the first child of the selected element. It is often used to add cosmetic content to an element with the content property. It is inline by default.
A CSS pseudo-element is used to style specified parts of an element. For example, it can be used to: Style the first letter, or line, of an element. Insert content before, or after, the content of an element.
A CSS pseudo-element is a keyword added to a selector that lets you style a specific part of the selected element(s). For example, ::first-line can be used to change the font of the first line of a paragraph. Note: In contrast to pseudo-elements, pseudo-classes can be used to style an element based on its state.
The ::slotted() CSS pseudo-element represents any element that has been placed into a slot inside an HTML template (see Using templates and slots for more information). This only works when used inside CSS placed within a shadow DOM.
The ::content
pseudo-element is being replaced in future implementations of Web Components / Shadow DOM with the ::slotted
pseudo-element. Likewise, the element targeted by this pseudo-element has changed from <content
to <slot
> in the latest version of the Shadow DOM specification. You can see related discussion about that change here.
Currently browsers still support <content>
and ::content
.
Summary:
::content
is essentially a way to dig deeper and style descendants of the ShadowHost
, which normally aren't available to be styled, because your CSS doesn't know to look for the ShadowDOM fragment without ::content
.
This answer assumes you are at least somewhat familiar with the <template>
element and Web Components, specifically the ShadowDOM, which deals with ShadowTree
s and their two main elements, ShadowHost
and ShadowRoot
.
Note - As of this writing, there is less than 50% support (even prefixed, off-by-default support) for Web Components across the five major browsers. While all modern browsers support <template>
, only recent versions of Chrome and Opera support the ShadowDOM fully; with Firefox supporting parts of it after you toggle the requisite feature in about:config
(dom.webcomponents.enabled
) to true.
The goal of using the ShadowDOM
is similar to MVC's separation of concerns. That is, we want to separate our content from our presentation and allow for encapsulated templates in our code to help make it more manageable. We have this already in various programming languages, but it's remained a problem for some time in HTML and CSS. Further, there can be conflicts with class names when styling elements in web apps.
Normally, we interact with the LightDOM
(a sort of "Light Realm"), but sometimes it would be helpful to take advantage of encapsulation. Crossing into this sort of "Shadow Realm" (part of Web Components) is a new method to prevent the problems mentioned above by allowing encapsulation. Any styles applied to markup in your ShadowTree
won't apply to markup outside of your ShadowTree
, even if the exact same classes or selectors are used.
When the ShadowTree
(which lives in the ShadowDOM
) has a tree from the LightDOM
distributed within it, and/or when the ShadowTree
is rendered, the result is converted by the browser into what is called a composed tree.
When the browser renders your code, content is being distributed and inserted at new locations other than where it was physically typed. This distributed output is what you see (and what the browser sees), and is called the composed tree
. In reality, the content is not originally typed in the order that it now appears, but you won't know this, and neither will the browser. This separation between "end result" and "original code", if you will, is one of the main benefits of encapsulation.
Web Components & the Future of CSS is a great 40-minute video on Web Components and specifically the ShadowDOM, pointed out to me by ZachSaucier.
Specific to your question, the ::content
pseudo element applies to what are called distributed nodes. A distributed node is another term for whatever you put within the <content></content>
tags. The content is distributed from its place in the original markup to wherever you have placed your <content>
tags in the template.
So, when you need specificity in CSS, one way you can handle selectors normally is that you go to the parent element and add that in as part of the selector. Ex: if .container {}
is not specific enough, you might use div .container {}
or .main .container {}
in order to make your selector work.
Thinking about the point of the ShadowDOM, which is scoping and encapsulation, you have to realize that this new ShadowTree you've created is a completely new (discrete) DOM fragment. It's not in the same "Light Realm" as the rest of your content; it's in a "Shadow Realm". So, how does the CSS know to target this "Shadow Realm"? By using the ::content
pseudo-element!
::content
pseudo-element selector acts as the parent element of distributed nodes.HTML5Rocks has a great sequence of tutorials here, here, and here which cover more information and give some great examples (be sure to visit with Chrome or Opera until more browsers support these features).
For example, see this modified and improved (by Leo) version of the code from HTML5Rocks:
var div = document.querySelector('div'); var root = div.createShadowRoot(); var template = document.querySelector('template'); root.appendChild(template.content);
<template> <style> h3 { color: red; } content[select="h3"]::content > h3 { color: green; } ::content section p { text-decoration: underline; } </style> <h3>Shadow DOM</h3> <content select="h3"></content> <content select="section"></content> </template> <div> <h3>Light DOM</h3> <section> <div>I'm not underlined</div> <p>I'm underlined in Shadow DOM!</p> </section> </div>
Also available on JSFiddle (Remember to visit in a WebKit-based browser like Chrome or Opera)
Here you can see that the ::content
section p
pseudo element is first selecting the content of the ShadowRoot
, which is the contents of the div
element in your markup, and then specifying further by adding section p
.
This may seem unnecessary when compared to normal CSS selector usage (for example, why not just use section p {}
?), until you recall that, when traversing a ShadowTree
, you cannot normally select descendants of host
elements (which distributed nodes are), because they are in the "Shadow Realm" I mentioned earlier.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With