CSS Custom Properties in Web Components — Complete Guide
In this tutorial, you will learn about CSS Custom Properties in Web Components. We cover key concepts, practical examples, and best practices to help you master this topic.
CSS custom properties cross the Shadow Dom boundary, enabling host pages to theme encapsulated components without breaking style isolation.
What You'll Learn
- How CSS custom properties inherit into shadow trees
- How to define theming APIs with custom properties
- How to provide default values
- How to create component variants with property sets
Why It Matters
Style Encapsulation prevents CSS from leaking in, but it also prevents external styling. CSS custom properties are the sanctioned way to provide theming hooks for shadow DOM components.
flowchart LR A[Host Page] --> B[--primary: blue] B --> C[Shadow DOM inherits] C --> D[var(--primary)] D --> E[Component uses value]
Theme API Pattern
class ThemedAlert extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML = `
<style>
:host {
--alert-bg: #f8d7da;
--alert-border: #f5c6cb;
--alert-color: #721c24;
--alert-padding: 12px;
--alert-radius: 4px;
}
.alert {
background: var(--alert-bg);
border: 1px solid var(--alert-border);
color: var(--alert-color);
padding: var(--alert-padding);
border-radius: var(--alert-radius);
font-family: sans-serif;
}
.alert strong { color: var(--alert-color); }
</style>
<div class="alert"><slot></slot></div>
`;
}
static get observedAttributes() {
return ['type'];
}
attributeChangedCallback(name, oldValue, newValue) {
if (name === 'type') {
const themes = {
info: { '--alert-bg': '#d1ecf1', '--alert-border': '#bee5eb', '--alert-color': '#0c5460' },
success: { '--alert-bg': '#d4edda', '--alert-border': '#c3e6cb', '--alert-color': '#155724' },
warning: { '--alert-bg': '#fff3cd', '--alert-border': '#ffeeba', '--alert-color': '#856404' },
error: { '--alert-bg': '#f8d7da', '--alert-border': '#f5c6cb', '--alert-color': '#721c24' }
};
const theme = themes[newValue] || themes.error;
Object.entries(theme).forEach(([prop, value]) => {
this.style.setProperty(prop, value);
});
}
}
}
customElements.define('themed-alert', ThemedAlert);
Expected output: Users set the type attribute for built-in themes or override individual properties via CSS.
Providing Defaults
Always provide fallback values with var() to ensure the component works without theming.
// Good: default provided
color: var(--text-color, #333);
// Bad: no default, inherits or uses initial
color: var(--text-color);
Common Mistakes
- Not providing defaults for custom properties
- Using custom properties that do not inherit correctly
- Overriding custom properties inside the shadow tree instead of on the host
Practice Questions
- Do CSS custom properties cross the shadow boundary? Yes, they inherit into the shadow tree from the host.
- How do you provide a default for var()? var(--property-name, default-value).
Mini Project
Build a button component with a complete theming API via CSS custom properties. Support at least 8 properties: --btn-bg, --btn-color, --btn-border, --btn-radius, --btn-padding, --btn-font-size, --btn-hover-bg, --btn-active-transform.
What's Next
Lesson 12: CSS Parts
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro