Skip to content

Commit 053f3a8

Browse files
authored
Merge pull request #66 from WebComponentsGuide/learn-components-styling
Add “Component - Styling” content
2 parents 78db0c5 + b322b75 commit 053f3a8

File tree

1 file changed

+305
-1
lines changed

1 file changed

+305
-1
lines changed

learn/components/styling.md

Lines changed: 305 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,308 @@ title: Styling
33
order: 5
44
---
55

6-
{% stub %}
6+
Web Components have powerful styling capabilities which make them portable and extensible. Styles declared within the
7+
Shadow DOM serve as a Web Component’s default styling. It’s similar to writing a user-agent stylesheet for your custom
8+
element and it works quite similarily as well.
9+
10+
## Shadow Encapsulation: Scoped Styles
11+
12+
Shadow DOM offers a boundary line between styles outside the tree, and styles inside the tree. Styles cannot cross
13+
this boundary unintentionally. This is different to regular CSS where a selector can effect every element on a page.
14+
15+
```html
16+
<style>
17+
p {
18+
color: deeppink;
19+
}
20+
</style>
21+
22+
<p>This text is deeppink and not teal because it is outside of the shadow root.</p>
23+
24+
<fancy-p>
25+
<template shadowroot="open">
26+
<style>
27+
p {
28+
color: teal;
29+
}
30+
</style>
31+
<p>This text is teal and not deeppink because it is inside of the shadow root.</p>
32+
</template>
33+
</fancy-p>
34+
```
35+
36+
The `<p>` element within the shadow tree is not effected by the styles declared outside of the shadow tree.
37+
38+
## Inheritance
39+
40+
Custom elements abide by the same rules of inheritance as other HTML elements. CSS properties whose default value is
41+
`inherit` will inherit from their parent element, for example `font-size`, `font-family`, and `color`. This `inherit`
42+
property crosses the Shadow DOM boundary. [CSS custom properties][] default to `inherit`, so they'll cross Shadow DOM
43+
boundaries too. Top-level elements within a shadow tree inherit properties from the custom element itself (also known
44+
as the shadow host).
45+
46+
```html
47+
<style>
48+
article {
49+
color: deeppink;
50+
}
51+
</style>
52+
53+
<article>
54+
<h1>This text is deeppink.</h1>
55+
<article-meta>
56+
<template shadowroot="open">
57+
<style>
58+
span {
59+
font-style: italic;
60+
}
61+
</style>
62+
<span>By Some Person</span>
63+
</template>
64+
</article-meta>
65+
</article>
66+
```
67+
68+
The `<article-meta>` custom element inherits its `color` from the `<article>` element where it is set to `deeppink`. The
69+
`<span>` element within the shadow tree inherits its `color` from the `<article-meta>` custom element which means the
70+
value will also be `deeppink`.
71+
72+
## Styling elements outside of a shadow tree
73+
74+
In order to be portable, Web Components can provide default styles for the element itself (also known as the shadow host).
75+
They can also style slotted elements with some default styles.
76+
77+
### Writing default styles for the shadow host with `:host` and `:host()`
78+
79+
There are two selectors which can be used to style the shadow host from within the shadow tree. They are the `:host`
80+
pseudo-class and the `:host()` pseudo-class function. The first will always select the shadow host. Here’s `:host` in
81+
action:
82+
83+
```html
84+
<fancy-p>
85+
<template shadowroot="open">
86+
<style>
87+
:host {
88+
display: inline-block;
89+
}
90+
</style>
91+
</template>
92+
</fancy-p>
93+
```
94+
95+
The `:host()` selector will select the shadow host if it matches a given selector. For example, it can be used to
96+
select for hosts that have a given attribute. While `:host` may apply to `<fancy-p>` component, `:host([extra])` would
97+
apply only to `<fancy-p extra>` elements:
98+
99+
```css
100+
:host([extra]) {
101+
font-style: italic;
102+
font-weight: bold;
103+
}
104+
```
105+
106+
```html
107+
<fancy-p>I not am extra</fancy-p>
108+
<fancy-p extra>I am extra</fancy-p>
109+
```
110+
111+
#### Chaining selectors after `:host`
112+
113+
While the `:host` selector refers to the shadow host element which is outside of the shadow tree, if you chain selectors
114+
it will select elements within the shadow tree.
115+
116+
```html
117+
<fancy-p>
118+
<template shadowroot="open">
119+
<style>
120+
:host > p {
121+
color: deeppink;
122+
}
123+
</style>
124+
<p>I am deeppink.</p>
125+
</template>
126+
<p>I am not deeppink.</p>
127+
</fancy-p>
128+
```
129+
130+
131+
### Writing default styles for slotted elements with `::slotted()`
132+
133+
The `::slotted()` pseudo-element selector allows you to write default styles for slotted elements that match the given
134+
selector. Specifying a selector can be useful for styling specific elements in particular ways.
135+
136+
```html
137+
<fancy-elements>
138+
<template shadowroot="open">
139+
<style>
140+
::slotted(button) {
141+
color: deeppink;
142+
}
143+
144+
::slotted(p) {
145+
color: teal;
146+
}
147+
</style>
148+
<slot></slot>
149+
</template>
150+
<button>I am a slotted button</button>
151+
<p>I am a slotted paragraph.</p>
152+
</fancy-elements>
153+
```
154+
155+
If you want to target elements in specific slots you can pass an attribute selector which matches the slot:
156+
157+
```html
158+
<fancy-article>
159+
<template shadowroot="open">
160+
<style>
161+
::slotted([slot="title"]) {
162+
font-size: 2rem;
163+
}
164+
::slotted([slot="subtitle"]) {
165+
font-size: 1.25rem;
166+
}
167+
/* The following will target elements going into the unnamed slot */
168+
::slotted(:not([slot])) {
169+
color: deeppink;
170+
}
171+
</style>
172+
<article>
173+
<hgroup>
174+
<slot name="title"></slot>
175+
<slot name="subtitle"></slot>
176+
</hgroup>
177+
<slot></slot>
178+
</article>
179+
</template>
180+
<h1 slot="title">I am the title</h1>
181+
<p slot="subtitle">I am the subtitle</p>
182+
<p>I am content.</p>
183+
</fancy-article>
184+
```
185+
186+
You cannot chain selectors with `::slotted()`, so the following will not work:
187+
188+
```css
189+
::slotted(h1) span {
190+
color: deeppink;
191+
}
192+
```
193+
194+
## Parts: styling a shadow tree from the outside
195+
196+
The CSS Shadow Part API allows elements within a shadow tree to be styled from outside of it. This allows Web Components
197+
to be very extensible.
198+
199+
```css
200+
fancy-article::part(header) {
201+
display: grid;
202+
gap: 0.25rem;
203+
padding: 0.5rem;
204+
border-block-end: 0.125rem solid deeppink;
205+
}
206+
207+
fancy-article::part(content) {
208+
display: grid;
209+
justify-content: start;
210+
gap: 0.5rem;
211+
}
212+
```
213+
214+
```html
215+
<fancy-article>
216+
<template shadowroot="open">
217+
<article part="article">
218+
<hgroup part="header">
219+
<slot name="title"></slot>
220+
<slot name="subtitle"></slot>
221+
</hgroup>
222+
<div part="content">
223+
<slot name="content"></slot>
224+
</div>
225+
</article>
226+
</template>
227+
<h1 slot="title">Hello, World!</h1>
228+
<p slot="subtitle">I am a subtitle...</p>
229+
<p>I am content</p>
230+
</fancy-article>
231+
```
232+
233+
Similar to `::slotted()`, you cannot chain selectors after `::part()` to select children or siblings. The following will
234+
not work:
235+
236+
```css
237+
fancy-article::part(header) slot {
238+
display: block;
239+
}
240+
```
241+
242+
## How to include default styles for a Web Component
243+
244+
There are a variety of methods to include styles for a Web Component. Some will be familiar, but others are newer.
245+
246+
### Using `<style>`
247+
248+
The `<style>` tag is the most simple way to write styles for a Web Component. Just include it in your shadow tree:
249+
250+
```html
251+
<style>
252+
:host {
253+
color: deeppink;
254+
}
255+
</style>
256+
```
257+
258+
### Using `<link rel="stylesheet">`
259+
260+
Using a `<link rel="stylesheet">` element in the shadow tree will allow you to write styles in an external stylesheet.
261+
262+
```html
263+
<link rel="stylesheet" href="/fancy-article-element.css">
264+
```
265+
266+
If you do this, the stylesheet will be loaded after the script is loaded. This will likely cause a “flash of unstyled
267+
content” (FOUC). To circumvent this, you can preload the stylesheet like this:
268+
269+
```html
270+
<link rel="preload" href="/fancy-article-element.css" as="style">
271+
```
272+
273+
### Using Constructable Stylesheets
274+
275+
Constructable Stylesheets are stylesheets which are programmatically created in JavaScript. You can create a new
276+
stylesheet using the `CSSStyleSheet` constructor and set styles with the `.replaceSync()` method:
277+
278+
```js
279+
const stylesheet = new CSSStyleSheet()
280+
281+
stylesheet.replaceSync(`
282+
:host {
283+
color: deeppink;
284+
}
285+
`)
286+
287+
class FancyArticleElement extends HTMLElement {
288+
connectedCallback() {
289+
this.attachShadow({mode: 'open'}).adoptedStyleSheets = [stylesheet]
290+
}
291+
}
292+
```
293+
294+
### Using CSS Module scripts
295+
296+
CSS Module scripts allow developers to import stylesheets as if they were a module script. To do so, use an import
297+
assertion where the `type` is `css` and then you can add the imported stylesheet to the `adoptedStyleSheets` array for
298+
the element’s shadow root.
299+
300+
```js
301+
import stylesheet from './fancy-article-element.css' assert { type: 'css' }
302+
303+
class FancyArticleElement extends HTMLElement {
304+
connectedCallback() {
305+
this.attachShadow({mode: 'open'}).adoptedStyleSheets = [stylesheet]
306+
}
307+
}
308+
```
309+
310+
[CSS custom properties]: https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties

0 commit comments

Comments
 (0)