Salesforce · · 14 min read

How to Style Your LWC

A complete guide to styling Lightning Web Components — the Salesforce Lightning Design System, SLDS custom variables and global styling hooks, using SLDS in your LWC, creating CSS files, reusable CSS, and using static resources for styling.

Part 82: How to Style Your LWC

Welcome back to the Salesforce blog series. In Part 75, we covered the fundamentals of CSS and how stylesheets work within the context of Lightning Web Components. We talked about selectors, the cascade, and how Shadow DOM scoping (which we explored back in Part 73) creates an encapsulation boundary around every component. That was the theory. Now it is time to get practical.

In this post, we are going to talk about how to actually style your LWC in the real world. That means working with the Salesforce Lightning Design System (SLDS), understanding styling hooks and custom properties, creating and sharing CSS across components, and even pulling in external styles from static resources. By the end of this post, you will have a thorough understanding of every tool available to you when it comes to making your components look polished and consistent with the rest of the Salesforce platform.


What is the Salesforce Lightning Design System (SLDS)?

If you have spent any time in the Salesforce ecosystem, you have probably noticed that the Lightning Experience has a very consistent look and feel. Buttons look the same everywhere. Cards have the same padding and border radius. Fonts, colors, and spacing all follow the same pattern. That consistency does not happen by accident. It is the result of the Salesforce Lightning Design System, or SLDS.

SLDS is a CSS framework built and maintained by Salesforce specifically for the Lightning platform. Think of it like Bootstrap or Tailwind, but designed exclusively for Salesforce applications. It provides a comprehensive library of CSS classes, design tokens, and component blueprints that define how things should look across the entire Lightning Experience.

The core idea behind SLDS is simple: instead of writing custom CSS for every button, card, modal, and form you build, you apply predefined SLDS classes to your markup. This gives you two major benefits. First, your components automatically look consistent with the rest of Salesforce. Second, when Salesforce updates their design language (which they do periodically), your components update along with the platform because you are using their classes rather than hardcoded values.

SLDS covers a huge range of UI patterns. Here are a few categories you will use constantly:

  • Utilities — spacing, alignment, text sizing, visibility, truncation
  • Components — buttons, cards, modals, tables, badges, pills, menus
  • Grid system — a flexible 12-column grid for building responsive layouts
  • Icons — the standard Salesforce icon library
  • Design tokens — named values for colors, fonts, spacing, and more

You can explore the full SLDS documentation at the official Salesforce Lightning Design System site. It is worth bookmarking because you will refer to it constantly as you build components.


What Are SLDS Custom Variables and Global Styling Hooks?

Back in Part 75, we talked about how Shadow DOM creates a boundary around each component. Styles from outside cannot reach in, and styles from inside cannot leak out. This is great for encapsulation, but it creates a challenge: how do you apply consistent theming across dozens of encapsulated components?

The answer is CSS custom properties, also known as CSS variables. Unlike regular CSS properties, custom properties can cross the Shadow DOM boundary. A custom property defined on a parent element is available inside the shadow tree of its child components. This is the mechanism that SLDS uses to provide platform-wide theming.

Salesforce exposes a large set of global styling hooks through CSS custom properties. These hooks follow a naming convention that starts with --slds- and they cover everything from colors and fonts to spacing and border radii. Here are some common examples:

.my-custom-card {
    background-color: var(--slds-g-color-surface-container-1, #ffffff);
    color: var(--slds-g-color-on-surface-1, #181818);
    border-radius: var(--slds-g-radius-border-2, 0.25rem);
    padding: var(--slds-g-spacing-4, 1rem);
    font-family: var(--slds-g-font-family, 'Salesforce Sans', Arial, sans-serif);
}

Notice the var() syntax. The first argument is the custom property name. The second argument after the comma is a fallback value that gets used if the custom property is not defined. Always include a fallback so your component still looks reasonable in contexts where SLDS hooks are not available.

The Difference Between Tokens and Hooks

You might see references to both “design tokens” and “styling hooks” in the Salesforce documentation. Here is the distinction. Design tokens are the older system. They are predefined named values like --lwc-colorTextDefault that the platform sets globally. Styling hooks are the newer, more flexible system that uses the --slds- prefix. Salesforce has been moving toward styling hooks as the primary approach, so when you are writing new components, prefer the --slds- hooks over the older --lwc- tokens.

Why Hooks Matter

Styling hooks are not just about convenience. They are about future-proofing. When Salesforce rolls out a design refresh, the values behind these hooks change, and every component that uses them updates automatically. If you hardcode color: #0070d2 instead of using var(--slds-g-color-brand-1, #0070d2), your component will look increasingly out of place over time as the platform evolves around it.


How to Use SLDS in Your LWC

One of the best things about building LWC inside the Salesforce platform is that SLDS is available automatically. You do not need to install it, import it, or configure it. When your component runs inside Lightning Experience, the SLDS stylesheet is already loaded on the page. You just apply the classes.

Here is a simple example. Let us say you want to build a card component with a header, some body text, and a button:

<template>
    <div class="slds-card">
        <div class="slds-card__header slds-grid">
            <header class="slds-media slds-media_center slds-has-flexi-truncate">
                <div class="slds-media__body">
                    <h2 class="slds-card__header-title">
                        <span>Account Details</span>
                    </h2>
                </div>
            </header>
        </div>
        <div class="slds-card__body slds-card__body_inner">
            <p>This is the card body content.</p>
        </div>
        <footer class="slds-card__footer">
            <button class="slds-button slds-button_brand">View All</button>
        </footer>
    </div>
</template>

Every class in that markup comes from SLDS. The slds-card class creates the card container. The slds-button slds-button_brand classes create a branded button. The slds-grid class activates the SLDS flexbox grid. You did not write a single line of CSS, and yet the component looks exactly like a native Salesforce card.

Combining SLDS Classes with Custom Styles

In practice, you will almost always combine SLDS utility classes with some custom CSS. SLDS handles the general look and feel, and your custom CSS handles component-specific tweaks. Here is how that works:

<template>
    <div class="slds-box slds-theme_default custom-container">
        <h3 class="slds-text-heading_medium slds-m-bottom_small">Status</h3>
        <p class="slds-text-body_regular status-text">All systems operational.</p>
    </div>
</template>
.custom-container {
    max-width: 400px;
    margin: 0 auto;
}

.status-text {
    color: var(--slds-g-color-success-1, #2e844a);
    font-weight: 600;
}

The SLDS classes handle the box styling, text sizing, and spacing. Your custom CSS adds a max width, centers the container, and gives the status text a green color using an SLDS styling hook. This combination is the pattern you will use most often.

The SLDS Grid System

SLDS includes a 12-column grid system that works just like grids in other CSS frameworks. You use slds-grid on the container and slds-col with size classes on the children:

<template>
    <div class="slds-grid slds-wrap slds-gutters">
        <div class="slds-col slds-size_1-of-2">
            <div class="slds-box">Left Column</div>
        </div>
        <div class="slds-col slds-size_1-of-2">
            <div class="slds-box">Right Column</div>
        </div>
    </div>
</template>

The slds-size_1-of-2 class makes each column take up half the width. You can use fractions like 1-of-3, 2-of-3, 1-of-4, and so on. The slds-wrap class allows columns to wrap to the next line on smaller screens, and slds-gutters adds standard spacing between columns.


How to Create a CSS File for Your LWC

As we covered briefly in Part 75, every LWC can have its own CSS file. The file must share the exact same name as the component and sit in the same directory. Here is the file structure:

myStatusCard/
    myStatusCard.html
    myStatusCard.js
    myStatusCard.css
    myStatusCard.js-meta.xml

The CSS file is automatically loaded and scoped to the component. You do not need an import statement or a <link> tag. The framework handles everything.

Inside this file, you write standard CSS with a few important caveats that follow from the Shadow DOM behavior we discussed in Part 73:

  1. You cannot use element selectors that target elements outside your component. Your styles are scoped. A p selector in your CSS targets only <p> elements inside your component.

  2. You cannot use the id selector. Since LWC transforms IDs to maintain uniqueness, ID-based selectors are unreliable.

  3. Use the :host selector to style the component itself. The :host pseudo-class targets the component’s outer element:

:host {
    display: block;
    padding: var(--slds-g-spacing-4, 1rem);
    background-color: var(--slds-g-color-surface-container-1, #ffffff);
    border: 1px solid var(--slds-g-color-border-1, #e5e5e5);
    border-radius: var(--slds-g-radius-border-2, 0.25rem);
}
  1. Use :host() with a selector to conditionally style based on attributes or classes on the host element:
:host(.highlighted) {
    border-left: 4px solid var(--slds-g-color-brand-1, #0070d2);
}
  1. Use custom properties for values you want parent components to be able to override:
:host {
    --my-card-bg: var(--slds-g-color-surface-container-1, #ffffff);
    background-color: var(--my-card-bg);
}

A parent component can then override --my-card-bg to theme your card without breaking encapsulation. This is the same mechanism SLDS styling hooks use, and it is the recommended approach for creating themeable components.


How to Create a Reusable CSS File for Your LWC’s

Sometimes you have styles that need to be shared across multiple components. Maybe you have a set of utility classes, a common layout pattern, or a brand color palette that several components use. Duplicating those styles across every component’s CSS file would be a maintenance nightmare. LWC solves this with CSS modules.

A CSS module is simply a standalone CSS file that lives in its own component bundle (without an HTML or JS file). You then import it into any component that needs those styles.

Here is how to set it up. First, create a new component bundle that contains only a CSS file:

sharedStyles/
    sharedStyles.css

Inside sharedStyles.css, write the styles you want to share:

.brand-header {
    font-size: 1.25rem;
    font-weight: 700;
    color: var(--slds-g-color-brand-1, #0070d2);
    margin-bottom: var(--slds-g-spacing-3, 0.75rem);
}

.subtle-text {
    color: var(--slds-g-color-on-surface-2, #706e6b);
    font-size: 0.875rem;
}

.action-bar {
    display: flex;
    justify-content: flex-end;
    gap: var(--slds-g-spacing-3, 0.75rem);
    padding-top: var(--slds-g-spacing-4, 1rem);
    border-top: 1px solid var(--slds-g-color-border-1, #e5e5e5);
}

Now, to use these styles in a component, import the CSS file in your component’s CSS file using the @import syntax:

/* myComponent.css */
@import 'c/sharedStyles';

.component-specific-style {
    max-width: 600px;
}

The c/ prefix is the default namespace for custom components. After this import, all the classes defined in sharedStyles.css are available in myComponent. You can use .brand-header, .subtle-text, and .action-bar in your HTML template as if they were defined locally.

A Few Rules for Shared CSS

  • The shared CSS bundle does not need an HTML file, a JS file, or a meta XML file. It only needs the CSS file.
  • You can import multiple shared CSS files into one component.
  • The imported styles are still scoped to the component that imports them. They do not become global.
  • If the same class name exists in both the imported file and the local CSS file, the local definition takes precedence.

This pattern is extremely useful for keeping a consistent look across a library of related components. You define your shared palette, typography, and layout patterns once, and every component that imports them stays in sync.


Using a Static Resource for Your LWC’s CSS

There are situations where you need to load an external CSS file that does not follow the LWC component bundle pattern. Maybe you are using a third-party CSS library, or you have a legacy stylesheet that you need to bring into a Lightning Web Component. For these cases, you can use Salesforce static resources.

A static resource is a file (or zip of files) that you upload to Salesforce and then reference in your code. To use a static resource CSS file in an LWC, you load it programmatically using the loadStyle function from the lightning/platformResourceLoader module.

First, upload your CSS file as a static resource in Salesforce Setup. Let us say you upload a file called customTheme.css as a static resource named customTheme.

Then, in your component’s JavaScript file:

import { LightningElement } from 'lwc';
import { loadStyle } from 'lightning/platformResourceLoader';
import customThemeResource from '@salesforce/resourceUrl/customTheme';

export default class MyThemedComponent extends LightningElement {
    isStyleLoaded = false;

    renderedCallback() {
        if (this.isStyleLoaded) {
            return;
        }
        this.isStyleLoaded = true;

        loadStyle(this, customThemeResource)
            .then(() => {
                console.log('Custom theme loaded successfully');
            })
            .catch(error => {
                console.error('Error loading custom theme', error);
            });
    }
}

Important Caveats

There are a few things to be aware of when using static resources for CSS:

  1. Styles loaded via loadStyle are not scoped by Shadow DOM. They are injected into the global page scope. This means they can potentially affect other components on the page. Use this approach carefully and make your selectors specific enough to avoid collisions.

  2. Use renderedCallback with a guard flag. The renderedCallback fires every time the component re-renders. Without the isStyleLoaded flag, you would load the stylesheet over and over again. The flag ensures it only loads once.

  3. loadStyle returns a Promise. The stylesheet loads asynchronously. If your component depends on certain styles being present before it renders correctly, you may need to handle that in your template with a loading state.

  4. You can load multiple stylesheets. If your static resource is a zip file containing several CSS files, you can reference specific files inside it:

loadStyle(this, customThemeResource + '/css/main.css')
  1. Prefer SLDS and component CSS when possible. Static resources should be your fallback, not your first choice. The scoped CSS approach is safer, more maintainable, and better aligned with the LWC architecture.

Section Notes

Let us recap what we covered in this section:

  • SLDS is the Salesforce Lightning Design System. It is a comprehensive CSS framework that provides consistent styling across the entire Lightning platform. It is automatically available in any LWC running inside Lightning Experience.

  • Styling hooks are CSS custom properties (using the --slds- prefix) that cross the Shadow DOM boundary. They allow you to use platform-defined colors, fonts, spacing, and other values that stay in sync with the Salesforce design language. Always include a fallback value.

  • Component CSS files are automatically scoped to the component via Shadow DOM. Use the :host selector to style the component element itself. Use custom properties to make your components themeable.

  • Shared CSS modules let you define styles once and import them into multiple components. The imported styles remain scoped to the importing component.

  • Static resources let you load external CSS files into your LWC. These styles are not scoped by Shadow DOM, so use them carefully and as a last resort.

The general priority for styling should be: SLDS classes first, then SLDS styling hooks for custom values, then component-scoped CSS, then shared CSS modules, and finally static resources only when none of the above options work.


PROJECT: Update Your Section 5 LWC to Use SLDS Classes and Styling Hooks

Time to put this into practice. Go back to the LWC you built during Section 5 and update its styling to use SLDS. Here is what you should do:

  1. Replace hardcoded layout styles with the SLDS grid. If your component has any kind of column layout, convert it to use slds-grid, slds-col, and the appropriate size classes.

  2. Apply SLDS component classes to your elements. If you have buttons, wrap them with slds-button and the appropriate variant class (slds-button_brand, slds-button_neutral, slds-button_destructive). If you have cards, use the slds-card pattern. If you have form elements, apply the slds-form-element structure.

  3. Replace hardcoded color and spacing values with SLDS styling hooks. Go through your CSS file and anywhere you see a hardcoded hex color, pixel value for spacing, or font declaration, replace it with the appropriate --slds- custom property. Use the SLDS documentation to find the right hook for each value.

  4. Create a shared CSS module. Identify at least two or three styles that could be shared across components. Create a sharedStyles component bundle with just a CSS file, move those shared styles into it, and import it into your component.

  5. Test your component. Deploy it to your org and verify that it looks consistent with the rest of the Lightning Experience. Try resizing your browser window to make sure the grid layout responds properly.

This exercise is about building the habit of reaching for SLDS first instead of writing everything from scratch. The more comfortable you get with the SLDS class library and styling hooks, the faster you will build components that look professional and stay consistent with the platform.

In the next section, we will move on from styling and start working with data inside our Lightning Web Components. See you there.