Home / lwc / lwc-css-and-styling
lwc-css-and-styling
Styling LWC components — SLDS design tokens, --slds-c-* styling hooks, scoped CSS via shadow DOM, ::part() for restyling base components, light DOM (lwc:render-mode='light'), why CSS doesn't pierce the shadow boundary, !important vs specificity, and SLDS upgrades breaking hand-rolled selectors. NOT for app-scope theming or SLDS 2 hook migration — use lwc/lwc-styling-hooks. NOT for picking shadow vs light DOM — use lwc/lwc-shadow-vs-light-dom-decision.
Open the skill folder on GitHub
LWC CSS and Styling
LWC components render inside a shadow DOM by default. Component CSS is scoped to the component — it does not bleed in or out. SLDS classes work everywhere because the framework injects them globally; everything else stops at the shadow boundary. This is the single fact that determines every styling decision.
The typical confusion: a developer wants to change the background color of a lightning-button. They write lightning-button .slds-button { background: red; } and see no effect. The reason is the .slds-button class lives inside the button's shadow DOM. Five years ago the answer was a /deep/ selector or ::shadow, both since deprecated. The current answer is SLDS styling hooks — --slds-c-button-color-background: red — which the base components consume from the consumer's CSS custom property cascade.
For cases where there is no styling hook for the property you need, the toolbox has three more options: ::part() (where the base component exposes a part), light DOM (static renderMode = 'light'), or a slot (let the consumer render their own element). Each has tradeoffs. Reaching for !important or for the SLDS internal class name is the wrong answer almost always — the next SLDS upgrade renames the class and the styling vanishes.
Recommended Workflow
- **Read the component's "Styling Hooks" tab in the Component
Library before writing CSS.** Most base components document a list of
--slds-c-*-color-*,--slds-c-*-spacing-*,--slds-c-*-radius-*properties they consume. If the property you need has a hook, set it and stop. - If no hook exists, check
::part()exposure. Some basecomponents (newer ones especially) expose internal elements via
::part(name). Style them withlightning-foo::part(bar) { ... }. - **For your own components, prefer SLDS classes over hand-rolled
styles.**
class="slds-card"is admin-portable, theme-aware, and survives SLDS upgrades. Custom CSS is a maintenance ratchet. - **Use design tokens (
--lwc-colorBrand,--slds-g-color-*)instead of literal hex codes.** The user might have High Contrast mode on, the org might be themed via
LightningExperienceTheme, the page might be in a community with a different brand color. - Reach for light DOM only when shadow DOM is the bottleneck.
static renderMode = 'light'removes the shadow boundary — the component participates in global CSS. It also forfeits encapsulation; another component's.buttonrule will hit yours. Use for small layout components, never for shared base. - Never use
!importantin a base-component override. Itbinds the override to the current SLDS internal-class name. When SLDS updates, your override fails silently and the user sees the default style.
- Write CSS in
*.cssnext to the JS, not inlinestyle=.Inline styles bypass the design-token cascade and are harder to override per-theme.
What This Skill Does Not Cover
| Topic | See instead |
|---|---|
SLDS icon usage and lightning-icon | lwc/lwc-slds-icons |
| Aura styling | Aura is deprecated for new development |
| Custom Lightning App theming | admin/branding-themes |
| Experience Cloud / Community CSS | lwc/lwc-community-styling |