| name | shadow-dom-styling |
| description | How to style rikka custom elements — `css\`` for Shadow DOM stylesheets, `inlineStyle\`` for inline style attributes, `adoptStyle`, the `:host` selector, and signal interpolation in styles. Load this when the task is about scoping CSS, theming via signals, or wiring a stylesheet into a custom element. |
Shadow DOM & Styling
Two CSS tagged templates from @takanashi/rikka-dom: css\`returns aCSSStyleSheet(for Shadow DOM),inlineStyle``returns a plain style object (for inlinestyle` attributes).
Imports
import { css, inlineStyle, adoptStyle } from "@takanashi/rikka-dom";
css\...`: CSSStyleSheet`
For Shadow DOM stylesheets. The result is a real CSSStyleSheet instance.
import { css } from "@takanashi/rikka-dom";
const styles = css`
:host {
display: block;
padding: 16px;
}
.card {
padding: 16px;
border-radius: 8px;
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
}
`;
Merging stylesheets
Interpolate another CSSStyleSheet to merge:
const base = css`
:host { display: block; font-family: system-ui; }
`;
const themed = css`
${base}
.card { color: red; }
`;
Signal interpolation
Both css\`andinlineStyle``` support signal interpolation; styles update automatically when signals change:
import { signal } from "@takanashi/rikka-signal";
import { css, inlineStyle } from "@takanashi/rikka-dom";
const theme = signal("dark");
const sheet = css`
:host { color: ${theme}; }
// theme change → stylesheet textContent updates
`;
const inline = inlineStyle`color: ${theme};`;
inlineStyle\...`: Record<string, string>`
For inline style attributes. CSS property names auto-convert to camelCase.
import { inlineStyle } from "@takanashi/rikka-dom";
const s = inlineStyle`
padding: 16px;
border-radius: 8px;
background: #1a1a2e;
`;
div({ style: s }, "Hello");
Usage distinction
| css\`` | inlineStyle\`` |
|---|
| Return type | CSSStyleSheet | Record<string, string> |
| Used in | defineElement config.styles / adoptedStyleSheets | style attribute of any element |
Injecting styles into Shadow DOM
Via defineElement styles option (preferred)
defineElement("my-card", {
styles: css`:host { display: block; padding: 16px; }`,
render() { return div({ class: "card" }, "Hello"); },
});
rikka uses adoptedStyleSheets to inject the sheet into the Shadow Root. The sheet is shared across all instances of the element, so memory cost is constant.
Via adoptStyle(root, sheet) (manual)
Useful for elements you don't own, or for adding stylesheets dynamically:
import { adoptStyle } from "@takanashi/rikka-dom";
const sheet = css``;
if (someEl.shadowRoot) {
adoptStyle(someEl.shadowRoot, sheet);
}
:host selector
Inside a Shadow DOM stylesheet, :host matches the host element. Use it for the element's own layout:
styles: css`
:host {
display: block;
padding: 16px;
}
:host([disabled]) {
opacity: 0.5;
}
:host(:hover) {
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
`
To target the host from outside the Shadow DOM, use the standard tag selector on the host:
my-card {
display: block;
margin: 8px;
}
Pitfalls
1. Using css\`` for inline styles
div({ style: css`color: red` });
div({ style: inlineStyle`color: red` });
2. Forgetting :host for element layout
styles: css`.card { display: block; }`;
styles: css`:host { display: block; }`;
3. Sharing CSSStyleSheet between Light DOM and Shadow DOM
adoptedStyleSheets only works on Document and ShadowRoot. For a defineElement with shadow: false, the styles won't apply. Either:
- Use
shadow: true (default), or
- Inject a
<style> element directly into the element via render():
defineElement("my-el", {
shadow: false,
render() {
const style = document.createElement("style");
style.textContent = `.my-el { display: block; }`;
return fragment(style, div({}, "content"));
},
});
4. Using :host in a Light DOM fallback
If the stylesheet is applied in a Light DOM fallback (browser without attachShadow), :host doesn't match. Provide a tag selector fallback:
const sheet = css`
:is(:host, my-card) { display: block; padding: 16px; }
`;
5. CSS values that don't round-trip through camelCase
inlineStyle converts -foo-bar → fooBar. If you need a custom property (CSS variable), keep the kebab-case form — it will pass through:
const s = inlineStyle`--my-color: red; color: var(--my-color);`;
See also