
Uplifting Components To Slds2
- 2.2k installs
- 763 repo stars
- Updated July 24, 2026
- forcedotcom/sf-skills
uplifting-components-to-slds2 is a Salesforce skill for migrating LWC and Aura components from SLDS 1 to SLDS 2 using the SLDS linter.
About
The uplifting-components-to-slds2 skill from Salesforce sf-skills systematizes Lightning Web Component migration from SLDS 1 to SLDS 2. Workflow always starts with npx @salesforce-ux/slds-linter@latest lint --fix ., then fixes remaining violations across color, spacing, sizing, typography, border, radius, and shadow hook categories. Four violation types have dedicated references: no-hardcoded-values-slds2, lwc-token-to-slds-hook, no-slds-class-overrides, and no-deprecated-tokens-slds1. Class overrides require renaming CSS selectors to component-prefixed classes and adding them alongside SLDS classes in HTML markup. Color hooks demand context-based selection via the color-hooks decision guide; non-color hooks map to numbered scales. All replacements use var(--slds-g-hook, originalValue) fallbacks. Layout values like 100%, auto, and 0 stay unchanged. Validation loops the linter until zero errors and checks light, dark, and density rendering. Advanced patterns cover color-mix for transparency and calc expressions with tokens. Prerequisites include Node.js 14.x and git backup before bulk uplift work.
- Requires SLDS linter --fix as the mandatory first migration step.
- Covers four violation types with per-rule reference guides.
- Class overrides need both CSS rename and markup class additions.
- Color hooks use context-first selection; non-color hooks use numbered scales.
- Validation loops until zero linter errors with fallback values preserved.
Uplifting Components To Slds2 by the numbers
- 2,164 all-time installs (skills.sh)
- +7 installs in the week ending Jul 28, 2026 (Skillselion tracking)
- Ranked #213 of 2,277 Frontend Development skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Jul 28, 2026 (Skillselion catalog sync)
uplifting-components-to-slds2 capabilities & compatibility
- Capabilities
- mandatory slds linter auto fix workflow · per rule violation remediation guides · color and non color hook selection decision tree · class override css and markup paired updates · migration validation checklist and linter feedba
- Works with
- salesforce
- Use cases
- frontend · refactoring
npx skills add https://github.com/forcedotcom/sf-skills --skill uplifting-components-to-slds2Add your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 2.2k |
|---|---|
| repo stars | ★ 763 |
| Security audit | 2 / 3 scanners passed |
| Last updated | July 24, 2026 |
| Repository | forcedotcom/sf-skills ↗ |
How do I fix SLDS 2 linter violations for hardcoded values, deprecated tokens, and class overrides in LWC CSS?
Migrate Lightning Web Components from SLDS 1 to SLDS 2 using the SLDS linter and per-rule styling hook replacement guides.
Who is it for?
SLDS 2 uplift, linter violation fixes, LWC token migration, and class override remediation.
Skip if: Skip for new greenfield LWC scaffolding without SLDS 1 debt or backend Apex refactoring.
When should I use this skill?
User mentions SLDS 2, SLDS uplift, linter violations, LWC token migration, or no-hardcoded-values rules.
What you get
Migrated CSS and updated markup with zero SLDS linter errors and fallback-preserving styling hooks.
- SLDS 2 color hook mappings
- Markup and JS context analysis notes
Files
Goal
Systematically migrate Lightning Web Components from SLDS 1 to SLDS 2 using the SLDS linter and structured guidance for fixing violations across all styling hook categories.
SLDS 2 Styling Hook Categories
| Category | Hook Prefix | What It Replaces |
|---|---|---|
| Color | --slds-g-color-* | Hardcoded colors, --lwc-color* tokens |
| Spacing | --slds-g-spacing-* | Hardcoded margins, padding, gaps |
| Sizing | --slds-g-sizing-* | Hardcoded widths, heights, dimensions |
| Typography | --slds-g-font-* | Hardcoded font sizes, weights, line heights |
| Border/Radius | --slds-g-radius-border-*, --slds-g-sizing-border-* | Hardcoded border-radius, border-width |
| Shadow | --slds-g-shadow-* | Hardcoded box-shadow values |
Color hooks require the most judgment (context-dependent selection). Non-color hooks are mostly numbered scales with straightforward mappings.
Prerequisites
- Node.js 14.x or higher installed
- Access to component CSS and markup files (
.htmlfor LWC,.cmpfor Aura) - Terminal/command line access to run linter
- Git repository for backup (recommended)
---
Workflow
1. **REQUIRED — ALWAYS run first:** npx @salesforce-ux/slds-linter@latest lint --fix . — NEVER skip this step. This handles simple violations automatically.
2. Review linter output -> Identify remaining manual fixes needed
3. Fix by violation type -> Use per-rule reference guides
4. Choose the right hook -> Context-first, inspect HTML before deciding
5. Validate -> Re-run linter and confirm zero errorsStep 1: Run SLDS Linter
MANDATORY: This step is NOT optional.
npx @salesforce-ux/slds-linter@latest lint --fix .The linter analyzes all CSS and markup files (.html for LWC, .cmp for Aura), auto-fixes simple violations, and reports remaining issues requiring manual intervention.
Step 2: Analyze Linter Output
The linter reports violations in this format:
componentName.css
15:3 warning Overriding slds-button isn't supported. To differentiate SLDS and
custom classes, create a CSS class in your namespace.
Examples: myapp-input, myapp-button. slds/no-slds-class-overrides
23:5 error The '--lwc-colorBackground' design token is deprecated. Replace it with
the SLDS 2 styling hook and set the fallback to '--lwc-colorBackground'.
1. --slds-g-color-surface-2
2. --slds-g-color-surface-container-2 slds/lwc-token-to-slds-hook
30:8 warning Consider replacing the #ffffff static value with an SLDS 2 styling hook
that has a similar value:
1. --slds-g-color-surface-1
2. --slds-g-color-surface-container-1
3. --slds-g-color-on-accent-1
4. --slds-g-color-on-accent-2
5. --slds-g-color-on-accent-3 slds/no-hardcoded-values-slds2
31:15 error Consider removing t(fontSizeMedium) or replacing it with
var(--slds-g-font-size-base, var(--lwc-fontSizeMedium, 0.8125rem)).
Set the fallback to t(fontSizeMedium). For more info, see
Styling Hooks on lightningdesignsystem.com. slds/no-deprecated-tokens-slds1Four violation types, each with its own fix approach (see Step 3).
Important: The linter flags all hardcoded values. Fix color, spacing, sizing, typography, border, and shadow values — but skip layout values (100%, auto, 0, inherit, none). See rule-no-hardcoded-values.md for the full fix-vs-skip triage table.
Step 3: Fix Violations by Type
Each rule has a dedicated reference guide with full examples and decision logic:
| Violation Rule | Quick Summary | Reference |
|---|---|---|
slds/no-hardcoded-values-slds2 | Replace hardcoded values with SLDS hook + original as fallback | rule-no-hardcoded-values.md |
slds/lwc-token-to-slds-hook | Replace --lwc-* tokens with SLDS 2 hook, keep LWC token as fallback | rule-lwc-token-to-slds-hook.md |
slds/no-slds-class-overrides | Create component-prefixed class, add to markup alongside SLDS class | rule-no-slds-class-overrides.md |
slds/no-deprecated-tokens-slds1 | Replace legacy t()/token() syntax with SLDS 2 hook + LWC fallback | rule-no-deprecated-tokens-slds1.md |
Always include fallback values — var(--slds-g-hook, originalValue) where originalValue is the exact original from the source CSS.
Class Override Quick Reference
Class overrides require changes to both CSS and markup (.html or .cmp). This is the most commonly missed step:
1. CSS: Rename .slds-* selector → {componentName}-{sldsElementPart} (camelCase) 2. Markup: Add the new class alongside the SLDS class — never remove the SLDS class
/* Before */ .slds-button { border-radius: 8px; }
/* After */ .myComponent-button { border-radius: 8px; }<!-- Markup: both classes --> <button class="slds-button myComponent-button">Click</button>See rule-no-slds-class-overrides.md for descendant selectors, multi-class selectors, and naming conventions.
Step 4: Choose the Right Hook
Color hooks require context-based selection. REQUIRED: When any violation involves a color property (`color`, `background-color`, `background`, `fill`, `border-color`), you MUST read [color-hooks-decision-guide.md](references/color-hooks-decision-guide.md) BEFORE choosing a hook. The linter lists possible hooks in no particular order — do NOT pick the first suggestion. The guide contains property-based rules that determine the correct hook.
Non-color hooks are simpler — match the CSS value to the numbered scale. See [non-color-hooks-decision-guide.md](references/non-color-hooks-decision-guide.md) for value-to-hook lookup tables covering spacing, sizing, typography, borders, radius, and shadows.
Step 5: Validate and Verify
Linter feedback loop — repeat until zero errors:
1. npx @salesforce-ux/slds-linter@latest lint .
2. Review errors -> fix by type (Step 3)
3. Re-run linter
4. Repeat until output shows: 0 errors---
Validation
- [ ] No
.slds-*classes in CSS selectors - [ ] No
var(--lwc-*)tokens without SLDS 2 replacements - [ ] All hooks include fallback values
- [ ] Background/foreground color hooks from same family
- [ ] Original SLDS classes preserved in HTML
- [ ] Spacing uses numbered hooks (not named like
spacing-medium) - [ ] Typography uses numbered hooks (not named like
font-weight-bold) - [ ] Component renders correctly in light/dark mode and density settings
See [migration-checklist.md](references/migration-checklist.md) for the full validation checklist.
---
Output
Return the fully migrated CSS (and updated HTML markup where class overrides were fixed) with zero SLDS linter violations. All styling hooks must include fallback values preserving the original CSS values.
---
Advanced Patterns
Color-Mix for Transparency
When a hardcoded value uses rgba() or transparency, use color-mix() with the SLDS hook to preserve opacity:
/* Before */
border-color: rgba(186, 5, 23, 0.7);
/* After — use oklab color space for perceptual consistency */
border-color: color-mix(in oklab, var(--slds-g-color-palette-red-40, rgb(181,54,45)), transparent 30%);Formula: To achieve X% opacity, use (100 - X)% transparent in color-mix.
- 70% opacity →
transparent 30% - 50% opacity →
transparent 50%
Use opaque rgb() as fallback (not rgba()) — color-mix handles the transparency.
calc() Expressions with Tokens
When migrating t('calc(...)') or calc() with deprecated tokens:
/* Before — Aura t() with calc */
height: t('calc(' + lineHeightButton + ' + 2px)');
/* After — if calc is still needed */
height: calc(var(--lwc-lineHeightButton) + 2px);
/* After — if calc was unnecessary, simplify */
height: var(--lwc-lineHeightButton);For calc() with --lwc-* tokens being replaced:
/* Before */
padding: calc(var(--lwc-spacingMedium) + 4px);
/* After */
padding: calc(var(--slds-g-spacing-4, var(--lwc-spacingMedium)) + 4px);Tip: Often the calc() is unnecessary and can be simplified. Check if the result matches an existing hook value.
---
Key Constraints
- Never invent hook names — only use hooks documented in the SLDS design system
- Always include fallback values — the fallback must be the exact original value from the source CSS
- Never change hardcoded numerical values — values like
100%,50%,200px,1.5,auto,0,inherit,none,flex: 1are structural/layout values. Do not replace them with hooks and do not remove them — they are not styling hook candidates - No exact match? Leave as-is — if a hardcoded value doesn't closely correspond to any hook's rendered value, leave it unchanged rather than force-fitting
- Match hook number to original value intensity — don't default to
-1. Pick the variant closest to the original. See color-hooks-decision-guide.md - Only numbered scales — named hooks like
spacing-medium,font-weight-bold,radius-largedo NOT exist
Troubleshooting
| Issue | Solution |
|---|---|
| Linter suggests 2+ color hook options | Inspect HTML context to determine element's semantic role — see color-hooks-decision-guide.md |
| Visual appearance changed after migration | Verify fallback values match originals; check surface vs container family |
| No hook available for hardcoded value | Leave unchanged; do not invent custom hook names |
Linter says "Remove the static value" for 100%, auto, etc. | Leave unchanged — these are layout values. Removing them breaks rendering. |
| CSS class naming errors | Use exact camelCase component name: myComponent-button, not MyComponent-button |
| Spacing/sizing doesn't match | Check value-to-hook mapping in non-color-hooks-decision-guide.md; verify spacing vs sizing usage |
Named hook not working (e.g., spacing-medium) | Named hooks don't exist — use numbered scale: spacing-4 for 16px, font-weight-7 for inline bold emphasis (not headings) |
| Component looks different in compact density | Use density-aware hooks (--slds-g-spacing-var-*) for components that adapt to density |
---
References
- [Color Hooks Decision Guide](references/color-hooks-decision-guide.md) — All 5 color hook families, decision trees, background-foreground pairing, palette accessibility
- [Non-Color Hooks Decision Guide](references/non-color-hooks-decision-guide.md) — Spacing, sizing, typography, borders, radius, and shadow hooks with lookup tables
- [Rule: No Hardcoded Values](references/rule-no-hardcoded-values.md) — Linter behavior, fix-vs-skip triage, replacement pattern, utility class workflow
- [Rule: LWC Token to SLDS Hook](references/rule-lwc-token-to-slds-hook.md) — Deprecated
--lwc-*token replacement patterns - [Rule: No Deprecated Tokens SLDS1](references/rule-no-deprecated-tokens-slds1.md) — Legacy
t()/token()Aura syntax replacement patterns - [Rule: No SLDS Class Overrides](references/rule-no-slds-class-overrides.md) — Class renaming and HTML updates
- [Migration Examples](references/examples.md) — Before/after examples by scenario and complexity
- [Common Patterns](references/common-patterns.md) — Classes never to override, deprecated SLDS 2 classes, palette fallbacks, tokens with no SLDS 2 equivalent
- [Migration Checklist](references/migration-checklist.md) — Full validation checklist
Color Hooks Decision Guide
This guide helps you choose the correct SLDS 2 color hook. Use it when replacing hardcoded colors or when the linter suggests multiple options.
BEFORE choosing any hook: Always inspect the element's context in this order:
1. Markup (.htmlfor LWC,.cmpfor Aura) — search for the CSS class. Check parent containers, nesting depth, ARIA attributes, interactive role.
2. JavaScript — if not in markup, search the JS/TS files for dynamic class insertion (classList.add, template literals, conditional class bindings).>
CSS alone is never enough — always determine context from markup or JS before choosing a hook.
Table of Contents
- Hook Selection Priority
- Surface Family
- Accent Family
- Feedback Family
- Palette Family
- System Family
- Choosing the Numbered Variant
- Background-Foreground Pairing Rules
- Applied Examples
Hook Selection Priority
Always try semantic hooks first. ~85-90% of color decisions should use semantic hooks. System and palette hooks are last resorts, not alternatives.
1. SEMANTIC HOOKS (try first, ~85-90% of decisions)
surface-*, accent-*, error/warning/success/info/disabled-*
• Accessibility built-in • Theme-aware • Dark mode ready
2. SYSTEM HOOKS (5-10%, only when no semantic family fits)
*-base-*
• Manual accessibility required • No semantic meaning
3. PALETTE (data viz & decorative only, <5%)
palette-*
• Manual accessibility • Non-semantic color onlyIf you find yourself reaching for a system hook, re-check whether a semantic hook applies — surface, accent, or feedback families cover most cases.
Decision flow: 1. Page/overlay/container background? → Surface hooks 2. Brand/interactive? → Accent hooks 3. Error/warning/success/info/disabled state? → Feedback hooks 4. Edge case with no semantic fit? → System hooks 5. Data viz or decorative? → Palette hooks
Visual color density rule (85-5-10): ~85% of UI surface area should be neutral grays/whites (surface hooks), ~5% accent/feedback colors, ~10% maximum expressive colors.
"Color ≠ Semantic Meaning"
| Scenario | Wrong Choice | Right Choice | Why |
|---|---|---|---|
| Red text but no error class | --slds-g-color-error-1 | --slds-g-color-palette-red-50 | Color for emphasis, not error state |
| Blue that's not clickable | --slds-g-color-accent-2 | --slds-g-color-palette-cloud-blue-50 | Decorative blue, not interactive |
| Green in data chart | --slds-g-color-success-1 | --slds-g-color-palette-green-50 | Data point, not success state |
| Orange highlight span | --slds-g-color-warning-1 | --slds-g-color-palette-orange-90 | Visual emphasis, not warning |
Use system hooks if the element matches a semantic meaning. If it explicitly does not, use the palette equivalent (e.g., --slds-g-color-palette-red-40). Evaluate background-color first, as it informs the correct foreground color pair.
---
Surface Family
Core Question
Is this element a page foundation, an overlay (modal/popover/dropdown), or does it sit within the content flow on an existing surface?
| Characteristic | Use surface-* | Use surface-container-* |
|---|---|---|
| Creates new stacking context | Yes | No |
| Elevated/overlays other content | Yes | No |
| Exists within page's content flow | No | Yes |
| Sits on top of an existing surface | No | Yes |
Linter Lists Both? Use Context
The linter lists hooks with similar color values in no particular order. When it suggests both surface-N and surface-container-N, check the element's DOM role:
| Question | Answer | Hook |
|---|---|---|
| Does it sit on top of another visible surface? | Yes | surface-container-* |
| Is it the page/modal/overlay/component root? | Yes | surface-* |
Hook Types
| Hook Type | Pattern | Use For |
|---|---|---|
| Surface | --slds-g-color-surface-1 / -2 / -3 | Pages, modals, popovers, overlays |
| Container | --slds-g-color-surface-container-1 / -2 / -3 | Cards, buttons, panels on existing surfaces |
| On Surface | --slds-g-color-on-surface-1 / -2 / -3 | Foreground (text, icons) on any surface or container |
| Inverse | --slds-g-color-surface-inverse-1 / -2 | Dark backgrounds on light themes (hero banners, inverted headers) |
Surface Numbering (Light → Dark, NOT States)
Surface variants are an aesthetic progression, not interaction states:
| Variant | Description | Typical Use |
|---|---|---|
surface-1 | Lightest (white) | Clean base; cards/containers stand out against it |
surface-2 | Light gray | Softer separation; avoids harsh white backgrounds |
surface-3 | Medium gray | Additional depth; rare in practice |
The same numbering applies to surface-container-* (1=lightest, 3=darkest).
On-Surface Emphasis Levels
On-surface variants represent content emphasis, not surface pairing:
| Variant | Emphasis | Use For |
|---|---|---|
on-surface-1 | Low (de-emphasized) | Captions, placeholder text, secondary content |
on-surface-2 | Medium (standard) | Body text, labels, filled input fields |
on-surface-3 | High (maximum weight) | Page titles, component headings, primary content |
All three on-surface variants can appear on the same surface background. Choose by content importance, not by matching the surface number.
Markup Nesting Depth → Hook Choice
| DOM Position | Hook | Why |
|---|---|---|
Page/app wrapper (<body> or root) | surface-1 | Foundation canvas |
| Card/panel directly on page | surface-container-1 | First container on a surface |
| Sub-panel inside a card | surface-container-2 | Nested container |
| Modal / popover / dropdown | surface-1 | Creates NEW stacking context — resets depth |
Exception: If a card/panel IS the component's entire visual footprint (notification bar, banner, standalone section with no visible parent surface), treat it as a surface, not a container.
State Progression
The starting variant depends on which one matches the original default color — don't assume -1:
| Default | Hover | When |
|---|---|---|
surface-container-1 | surface-container-2 | Default bg is white/near-white |
surface-container-2 | surface-container-3 | Default bg is light gray (~#f4f4f4) |
Edge Cases
| Scenario | Hook | Why |
|---|---|---|
| Full-page card (IS the page background) | surface-* | Acts as page surface, not a container |
| Slide-out panel overlaying content | surface-* | Creates new stacking context |
| Slide-out panel in page flow (no overlay) | surface-container-* | Sits within existing surface |
| Nested modals | surface-1 for each | Each modal creates its own stacking context |
| Dropdown over page content | surface-1 | Overlay, not a container |
Inverse Hooks
Use when hardcoded dark-blue backgrounds (#032d60, #03234d) appear on light themes. Pair with --slds-g-color-on-surface-inverse-* — do NOT pair with regular on-surface-*.
Warning: CSS Class Names Are Irrelevant
An element named .card-container might use surface-* if it's the page-level background, or surface-container-* if it's a card. Always base the decision on structural DOM position, not naming conventions.
Surface variants (1-2-3) are a light-to-dark aesthetic progression, NOT functional states. The linter's suggestion list is NOT ranked -- use element context, not list position.
---
Accent Family
Core Question
Is this element interactive or expressing brand identity?
Hook Types
| Hook Type | Pattern | Use For |
|---|---|---|
| Accent | accent-1 / -2 / -3 | Links, clickable text, interactive icons |
| Container | accent-container-1 / -2 / -3 | Brand button backgrounds |
| Border | border-accent-1 / -2 / -3 | High-emphasis brand borders |
| On Accent | on-accent-1 / -2 / -3 | Text on brand backgrounds |
Context Determines Hook Type
| Element Context | Hook | Example |
|---|---|---|
| Interactive container/button background | accent-container-* | Brand button background-color |
| Link text, icon fill (NOT on accent bg) | accent-* | Link color, icon fill |
| Text/icon ON an accent-container background | on-accent-* | Button label color |
| Border of an accent element | border-accent-* | Button border-color |
accent-* is for foreground (text, icons). accent-container-* is for background fills of interactive containers. Determine what the element IS first, then the CSS property follows naturally.
Accent Container State Progression
| Default | Hover/Active |
|---|---|
accent-container-1 | accent-container-2 |
accent-container-2 | accent-container-3 |
Why accent-2 is the default for links (not accent-1)
accent-1 may not meet 4.5:1 WCAG contrast on non-white backgrounds. accent-2 passes on all standard surfaces — use it as the safe default for text links. Go up one for hover: accent-2 → accent-3.
Quick Decision
1. Link or clickable text → accent-2 2. Brand button background → accent-container-1 3. Text on brand background → on-accent-1 4. Brand border → border-accent-1 (use only when design explicitly requires brand-colored outline — default to neutral border-2 for most borders)
Accent vs Surface
| Element Type | Surface | Accent |
|---|---|---|
| Non-interactive card | surface-container-* | Never |
| Clickable card | surface-container-* (bg) | accent-2 (text) |
| Primary button | Never | accent-container-* |
---
Feedback Family
Core Question
Does the markup/ARIA context indicate a specific state?
Hook Types
| Type | Use For |
|---|---|
error-* / error-container-* / on-error-* / border-error-* | Invalid inputs, errors, destructive actions |
warning-* / warning-container-* / on-warning-* / border-warning-* | Cautions, alerts |
success-* / success-container-* / on-success-* / border-success-* | Confirmations, valid states |
info-* / info-container-* / on-info-* | Tips, help badges |
disabled-* / disabled-container-* / on-disabled-* / border-disabled-* | Inactive elements |
Quick Decision
1. aria-invalid attribute → error-* 2. Class with "error"/"invalid" → error-* 3. Class with "success"/"valid" → success-* 4. [disabled] attribute → disabled-* 5. role="alert" → check alert type
Variant Availability
| Type | Has -2? | Why |
|---|---|---|
| Error | Yes | Destructive buttons need hover states |
| Success | Yes | Success buttons need hover states |
| Warning | No (only -1) | No warning buttons — only static alerts |
| Info | No (only -1) | No info buttons — only static badges |
| Disabled | Yes | Different visual weights |
---
Palette Family
Core Question
Is this color for data visualization or decoration without semantic meaning?
Use for: chart data series, decorative gradients, non-semantic colored elements — NOT standard UI.
Hook Pattern: --slds-g-color-palette-{color}-{grade}
Grade Scale: 0 (darkest) to 100 (lightest). Example: --slds-g-color-palette-cloud-blue-50.
Cool Tones (Recommended): cloud-blue, indigo, purple, violet Warm Tones (Use with caution): green, orange, hot-orange, red
Warm tones risk confusion with feedback colors — green looks like success, red looks like error, orange looks like warning. Only use palette hooks when the color explicitly does NOT carry semantic meaning.
Palette → Semantic Conversion
If existing code uses palette hooks but the element is actually interactive or semantic, convert to the appropriate semantic family:
| Element Role | Palette Hook (Before) | Semantic Hook (After) |
|---|---|---|
| Clickable icon/link | palette-*-blue-* | accent-* |
| Brand button bg | palette-*-* | accent-container-* |
| Error indicator | palette-red-* | error-* |
| Success indicator | palette-green-* | success-* |
| Chart data point | palette-* | Keep palette (correct usage) |
| Decorative element | palette-* | Keep palette (correct usage) |
Check markup context: if the element has click handlers, href, role="button", or brand intent, it belongs in a semantic family.
Accessibility
Palette hooks do NOT guarantee accessible contrast. Verify manually:
- 50-point rule: 50 grade points between bg and text (4.5:1 WCAG)
- 40-point rule: 40 grade points between bg and UI element (3:1 WCAG)
Color vision deficiency: Never use red+green or blue+purple as sole differentiators. Always pair with patterns, labels, or shapes.
---
System Family
Core Question
Have all semantic and palette options been exhausted?
| Need | Try First | System Fallback | Only If |
|---|---|---|---|
| Background | surface-* | neutral-base-95 | Custom component, no semantic fit |
| Interactive | accent-* | brand-base-50 | Legacy exact match required |
| Error state | error-* | error-base-50 | Special non-standard requirement |
System hooks require manual accessibility testing. Never use as a first choice.
---
Choosing the Numbered Variant
Do not default to `-1`. Numbered variants represent a light-to-dark progression. Pick the variant whose actual rendered value best matches the original hardcoded value.
1. Look at the original hardcoded value 2. Compare to the rendered values of available variants 3. Pick the closest match, not the lowest number
/* Original: #014486 — very dark navy */
/* WRONG: defaulting to -1 */
border-color: var(--slds-g-color-border-accent-1, #014486);
/* RIGHT: -3 is darkest, closest to #014486 */
border-color: var(--slds-g-color-border-accent-3, #014486);/* Original: rgb(243,242,242) — very light gray */
/* container-1 ≈ #fff, container-2 ≈ #f4f4f4, container-3 ≈ #f3f2f2 */
/* RIGHT: container-3 is closest */
background-color: var(--slds-g-color-surface-container-3, rgb(243,242,242));When an element has interactive states, variants progress sequentially from the starting match:
| Start At | Hover | Active |
|---|---|---|
-1 | -2 | -3 |
-2 | -3 | — |
---
Background-Foreground Pairing Rules
| If background uses... | Then text/fill MUST use... |
|---|---|
surface-* or surface-container-* | on-surface-* |
surface-inverse-* or surface-container-inverse-* | on-surface-inverse-* |
accent-container-* | on-accent-* |
error-container-* | on-error-* |
warning-container-* | on-warning-* |
success-container-* | on-success-* |
info-container-* | on-info-* |
disabled-container-* | on-disabled-* |
Never mix families — e.g., don't use on-accent-* on a surface-container-* background.
Border Color Decision
| Context | Hook |
|---|---|
| Interactive element (default) | border-2 (primary choice for most borders) |
| Decorative divider | border-1 |
| Validation state | border-[state]-1 |
| Disabled | border-disabled-1 |
---
Applied Examples — Color Context Investigation
Context from Class Name
/* Before — recordPage.css */
.headerBackground {
background: var(--lwc-colorBackgroundAlt);
}Decision: From class name headerBackground, this is a header container on another surface → surface-container-1.
/* After */
.headerBackground {
background: var(--slds-g-color-surface-container-1, var(--lwc-colorBackgroundAlt));
}Context from Component Name
/* Before — defaultOrgSharingSettingsPanelFooter.css */
.THIS {
background-color: t(colorBackground);
}Decision: .THIS alone isn't enough. Component name says panel footer → container on a panel surface → surface-container-2.
/* After */
.THIS {
background-color: var(--slds-g-color-surface-container-2, var(--lwc-colorBackground));
}Context from Deep Investigation (Markup + JS)
/* Before — floatingPanelContent.css */
.main-body {
background-color: var(--lwc-colorBackgroundAlt);
}Investigation:
- Component name
floatingPanelContentis ambiguous .main-bodynot found in markup — it's a computed class- JS shows the class is applied alongside
slds-popover*classes
Decision: Main background of a popover → surface element → surface-1.
/* After */
.main-body {
background-color: var(--slds-g-color-surface-1, var(--lwc-colorBackgroundAlt));
}Border Color — Semantic Fit Over Math
/* Before */
border: 1px solid #dddbda;Decision: Mathematically closest might be palette-neutral-90, but border-1 is still light grey, fits the design intention, and fits the semantic system better.
/* After */
border: var(--slds-g-sizing-border-1, 1px) solid var(--slds-g-color-border-1, #dddbda);Pairing Examples
Card on page surface:
.product-card {
background-color: var(--slds-g-color-surface-container-1, #ffffff);
color: var(--slds-g-color-on-surface-1, #2e2e2e);
}Error alert (role="alert" + error class):
.error-message {
background: var(--slds-g-color-error-container-1, #fddde3);
color: var(--slds-g-color-on-error-1, #b60554);
border: 1px solid var(--slds-g-color-border-error-1, #b60554);
}Linter vs Agent Decision-Making
Linters suggest hooks by color similarity — they match hex values regardless of meaning. Agents must choose by semantic family — inspect markup (.html/.cmp), ARIA attributes, and element purpose. Always prioritize semantic correctness over color matching.
Common Patterns
Frequently encountered patterns, class lists, and edge cases for SLDS 2 migration.
---
Deprecated SLDS 2 Classes
These classes are removed in SLDS 2. Remove them from markup (.html for LWC, .cmp for Aura):
| Deprecated Class | Action |
|---|---|
slds-icon-utility-error | Remove — icon component handles styling |
slds-icon-utility-* (all variants) | Remove — use icon component variants instead |
<!-- Before -->
<span class="slds-icon_container slds-icon-utility-error">
<!-- After -->
<span class="slds-icon_container">---
Color Palette Fallbacks
When using palette hooks for data visualization or decorative color, include RGB fallbacks:
| SLDS2 Hook | RGB Fallback | Common Use |
|---|---|---|
--slds-g-color-palette-red-40 | rgb(181,54,45) | Critical indicators, data viz |
--slds-g-color-palette-blue-50 | rgb(0,112,210) | Brand colors, primary actions |
--slds-g-color-palette-green-50 | rgb(4,132,75) | Positive indicators, data viz |
color: var(--slds-g-color-palette-red-40, rgb(181,54,45));
background: var(--slds-g-color-palette-blue-50, rgb(0,112,210));For transparency with palette hooks, use color-mix() — see Advanced Patterns in SKILL.md.
---
Tokens with No SLDS 2 Equivalent
Some legacy tokens have no direct SLDS 2 hook. Use --lwc-* or hardcoded values:
| Token | Action | Replacement |
|---|---|---|
lineHeightButton | Use --lwc-* directly | var(--lwc-lineHeightButton) |
durationInstantly | Use --lwc-* directly | var(--lwc-durationInstantly) |
durationPromptly | Use --lwc-* directly | var(--lwc-durationPromptly) |
durationSlowly | Use --lwc-* directly | var(--lwc-durationSlowly) |
zIndexSticky | Use hardcoded value | 9000 |
*Do not invent `--slds-g- hooks** for these tokens. The linter will flag invented hooks with slds/no-slds-namespace-for-custom-hooks`.
---
Custom Hook Namespace
When no SLDS 2 hook exists for an internal/custom value, use --lwc-* directly. Do not use the --slds-g-* namespace for custom purposes.
/* Bad — inventing SLDS namespace for internal values */
transition: var(--slds-g-duration-slowly, var(--lwc-durationSlowly));
/* Good — use --lwc-* when no official SLDS 2 equivalent */
transition: var(--lwc-durationSlowly);---
Font-Family Post-Linter Cleanup
After the linter runs, it may add verbose font-stack fallbacks to font-family. For font-family only, trim to just the hook tokens — remove the hardcoded font stack:
/* Linter output — verbose */
font-family: var(--slds-g-font-family, var(--lwc-fontFamily, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif));
/* Cleaned up — tokens only */
font-family: var(--slds-g-font-family, var(--lwc-fontFamily));For all other properties, keep the linter's rgb/rem/px fallbacks.
SLDS Migration Examples
Before/after examples organized by violation type.
Table of Contents
- Class Override Example
- LWC Token Example
- Hardcoded Value Examples
- Aura t() Token Migration
- Deprecated Class Removal
- Color-Mix with Transparency
- calc() with Legacy Tokens
- Tokens with No SLDS 2 Equivalent
- Multi-File End-to-End (Aura)
---
Class Override Example
Violation:
dataTable.css
5:1 warning Overriding slds-table isn't supported. To differentiate SLDS and
custom classes, create a CSS class in your namespace.
Examples: myapp-input, myapp-button. slds/no-slds-class-overrides
12:1 warning Overriding slds-button isn't supported... slds/no-slds-class-overridesBefore:
.slds-table {
border-width: var(--slds-g-sizing-border-1, 1px);
border-radius: var(--slds-g-radius-border-2, 0.25rem);
}
.slds-table .slds-button {
padding: var(--slds-g-spacing-2, 0.5rem) var(--slds-g-spacing-2, 1rem);
}<table class="slds-table">
<tr><td><button class="slds-button">Edit</button></td></tr>
</table>After:
.dataTable-table {
border-width: var(--slds-g-sizing-border-1, 1px);
border-radius: var(--slds-g-radius-border-2, 0.25rem);
}
.dataTable-table .dataTable-button {
padding: var(--slds-g-spacing-2, 0.5rem) var(--slds-g-spacing-2, 1rem);
}<table class="slds-table dataTable-table">
<tr><td><button class="slds-button dataTable-button">Edit</button></td></tr>
</table>---
LWC Token Example
Violation:
panelHeader.css
3:3 error The '--lwc-colorBackgroundAlt' design token is deprecated. Replace it with
the SLDS 2 styling hook and set the fallback to '--lwc-colorBackgroundAlt'.
1. --slds-g-color-surface-2
2. --slds-g-color-surface-container-2 slds/lwc-token-to-slds-hookBefore:
.header-bar {
background-color: var(--lwc-colorBackgroundAlt);
}<div class="THIS">
<div class="header-bar">Settings</div>
<div class="panel-content">...</div>
</div>Context: .header-bar is a section within the component → choose container.
After:
.header-bar {
background-color: var(--slds-g-color-surface-container-2, var(--lwc-colorBackgroundAlt));
}---
Hardcoded Value Examples
Tile with Border and Spacing
Violation:
tile.css
2:20 warning Consider replacing the #ffffff static value with an SLDS 2 styling hook
that has a similar value:
1. --slds-g-color-surface-1
2. --slds-g-color-surface-container-1
3. --slds-g-color-on-accent-1
4. --slds-g-color-on-accent-2
5. --slds-g-color-on-accent-3 slds/no-hardcoded-values-slds2Before:
.info-tile {
background-color: #ffffff;
border: 1px solid #e5e5e5;
padding: 1rem;
}Context: Tile is a container sitting on a page surface → choose surface-container-1 from the list.
After:
.info-tile {
background-color: var(--slds-g-color-surface-container-1, #ffffff);
border: var(--slds-g-sizing-border-1, 1px) solid var(--slds-g-color-border-1, #e5e5e5);
padding: var(--slds-g-spacing-4, 1rem);
}Modal Background
Before:
.modal-overlay {
background-color: #ffffff;
}Context: Modal creates a new stacking context (overlay) → choose surface-1, NOT surface-container-1.
After:
.modal-overlay {
background-color: var(--slds-g-color-surface-1, #ffffff);
}Link Colors
Before:
.nav-link {
color: #0176d3;
}
.nav-link:hover {
color: #014486;
}Context: Links are interactive → accent family. Default = accent-2 (accessible). Hover goes one up: accent-3.
After:
.nav-link {
color: var(--slds-g-color-accent-2, #0176d3);
}
.nav-link:hover {
color: var(--slds-g-color-accent-3, #014486);
}Alert Validation States
Before:
.alert-error {
border-color: #c23934;
background: #fddde3;
}
.alert-error-text {
color: #c23934;
font-size: 12px;
}
.alert-success {
border-color: #4bca81;
}After:
.alert-error {
border-color: var(--slds-g-color-border-error-1, #c23934);
background: var(--slds-g-color-error-container-1, #fddde3);
}
.alert-error-text {
color: var(--slds-g-color-on-error-1, #c23934);
font-size: var(--slds-g-font-scale-neg-1, 12px);
}
.alert-success {
border-color: var(--slds-g-color-border-success-1, #4bca81);
}Brand Icon with Accent Background
Before:
.icon-container {
width: 2rem;
height: 2rem;
background-color: #066AFE;
}
.account-icon {
fill: #FFFFFF;
}Context: Blue background with white icon = brand element → accent family. on-accent pairs with accent-container.
After:
.icon-container {
width: var(--slds-g-sizing-9, 2rem);
height: var(--slds-g-sizing-9, 2rem);
background-color: var(--slds-g-color-accent-container-1, #066AFE);
}
.account-icon {
fill: var(--slds-g-color-on-accent-1, #FFFFFF);
}Profile Header — Spacing, Typography, and Shadow
Before:
.profile-header {
padding: 1.5rem;
border-radius: 0.75rem;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.profile-name {
font-size: 18px;
font-weight: 700;
line-height: 1.25;
margin-bottom: 0.5rem;
}
.profile-role {
font-size: 14px;
font-weight: 400;
line-height: 1.5;
}After:
.profile-header {
padding: var(--slds-g-spacing-5, 1.5rem);
border-radius: var(--slds-g-radius-border-3, 0.75rem);
box-shadow: var(--slds-g-shadow-1, 0 2px 4px rgba(0,0,0,0.1));
}
.profile-name {
font-size: var(--slds-g-font-scale-4, 18px);
font-weight: var(--slds-g-font-weight-7, 700);
line-height: var(--slds-g-font-lineheight-2, 1.25);
margin-bottom: var(--slds-g-spacing-2, 0.5rem);
}
.profile-role {
font-size: var(--slds-g-font-scale-1, 14px);
font-weight: var(--slds-g-font-weight-4, 400);
line-height: var(--slds-g-font-lineheight-4, 1.5);
}Search Input — Border and Focus
Before:
.search-input {
border: 1px solid #ccc;
border-radius: 0.5rem;
padding: 0.5rem 0.75rem;
font-size: 14px;
}
.search-input:focus {
border-width: 3px;
}After:
.search-input {
border: var(--slds-g-sizing-border-1, 1px) solid var(--slds-g-color-border-2, #ccc);
border-radius: var(--slds-g-radius-border-2, 0.5rem);
padding: var(--slds-g-spacing-2, 0.5rem) var(--slds-g-spacing-3, 0.75rem);
font-size: var(--slds-g-font-scale-1, 14px);
}
.search-input:focus {
border-width: var(--slds-g-sizing-border-3, 3px);
}What NOT to Replace
Layout values (100%, auto, 0, flex: 1, none), animation properties, opacity, and overlay alpha values (rgba(0,0,0,0.5)) should remain unchanged. See rule-no-hardcoded-values.md for the full list.
---
Aura t() Token Migration
Before:
.THIS .stepTitle {
color: t(colorTextPlaceholder);
font-size: t(fontSizeMedium);
padding: t(spacingSmall);
}After:
.THIS .stepTitle {
color: var(--slds-g-color-on-surface-2, var(--lwc-colorTextPlaceholder));
font-size: var(--slds-g-font-scale-2, var(--lwc-fontSizeMedium));
padding: var(--slds-g-spacing-3, var(--lwc-spacingSmall));
}Why these hooks: colorTextPlaceholder maps to on-surface-2 (muted foreground text). fontSizeMedium maps to font-scale-2 (1rem). spacingSmall maps to spacing-3 (0.75rem). See rule-no-deprecated-tokens-slds1.md for all token mappings.
---
Deprecated Class Removal
Before (`.cmp`):
<span class="slds-icon_container slds-icon-utility-error">
<lightning:icon iconName="utility:error" />
</span>After:
<span class="slds-icon_container">
<lightning:icon iconName="utility:error" />
</span>Why remove: slds-icon-utility-* classes are removed in SLDS 2. The <lightning:icon> component applies its own styling via iconName. Keeping the deprecated class causes linter errors and has no visual effect.
---
Color-Mix with Transparency
Before:
.THIS .errorBorder { border: 2px solid rgba(186, 5, 23, 0.7); }After:
.THIS .errorBorder {
border: var(--slds-g-sizing-border-2, 2px) solid color-mix(in oklab, var(--slds-g-color-palette-red-40, rgb(181,54,45)), transparent 30%);
}Why color-mix: rgba() can't wrap a CSS variable for the color component alone. color-mix(in oklab, ...) lets you apply transparency to the hook's resolved value at runtime. Formula: X% opacity = (100-X)% transparent. Use opaque rgb() fallback to avoid double transparency when both color-mix and rgba are applied.
---
calc() with Legacy Tokens
Before:
height: t('calc(' + lineHeightButton + ' + 2px)');After (if calc needed):
height: calc(var(--lwc-lineHeightButton) + 2px);Simplified (try first):
height: var(--lwc-lineHeightButton);Why simplify: The + 2px in legacy t() calc expressions was often a workaround for rendering inconsistencies. Try without it first — if the component renders correctly, the simpler form is preferred.
---
Tokens with No SLDS 2 Equivalent
/* Before */
z-index: var(--lwc-zIndexSticky);
transition: opacity var(--slds-g-duration-slowly, var(--lwc-durationSlowly)) ease-in-out;
/* After — z-index: hardcoded, duration: --lwc-* directly */
z-index: 9000;
transition: opacity var(--lwc-durationSlowly) ease-in-out;Why: Not every legacy token has an SLDS 2 equivalent. Z-index and duration are internal layout/animation concerns — hardcode z-index, use --lwc-* for duration. Inventing --slds-g-duration-* triggers slds/no-slds-namespace-for-custom-hooks.
---
Multi-File End-to-End (Aura)
CSS before:
.THIS .slds-card__header {
background: var(--lwc-colorBackgroundAlt);
z-index: var(--lwc-zIndexSticky);
border-bottom: 1px solid #ddd;
}CSS after:
.THIS .relatedList-card-header {
background: var(--slds-g-color-surface-container-2, var(--lwc-colorBackgroundAlt));
z-index: 9000;
border-bottom: var(--slds-g-sizing-border-1, 1px) solid var(--slds-g-color-border-1, #ddd);
}Markup before (`.cmp`):
<h2 class="slds-card__header">Related Items</h2>Markup after:
<h2 class="slds-card__header relatedList-card-header">Related Items</h2>Three rules applied: token replacement, class override, hardcoded border. slds-card__header is a part within a card → choose container.
SLDS 2 Uplift Checklist
Validation checklist for each CSS file after applying SLDS 2 uplift fixes.
---
Per-Rule Checks
slds/no-slds-class-overrides
CSS:
- [ ] All
.slds-*overrides reported by the linter are renamed to{componentName}-{sldsElementPart} - [ ] Component name is camelCase
- [ ] SLDS element names preserved after prefix (e.g.,
.slds-button→myComponent-button) - [ ] Each SLDS class in a compound selector gets its own component class
HTML:
- [ ] Original SLDS classes preserved (never removed)
- [ ] Component classes added alongside SLDS classes
- [ ] Every element in CSS selector chain updated in HTML
slds/lwc-token-to-slds-hook
- [ ] All
var(--lwc-*)tokens replaced with SLDS 2 hooks - [ ] Hooks chosen from linter's numbered suggestion list (not invented)
- [ ] Surface vs container choice matches DOM context
- [ ] Fallback includes original token:
var(--slds-g-[hook], var(--lwc-[token]))
slds/no-hardcoded-values-slds2
- [ ] Color values replaced with context-appropriate hooks (surface, accent, feedback, etc.)
- [ ] Non-color values (spacing, sizing, typography, border, radius, shadow) replaced where exact match exists
- [ ] Hardcoded numerical values left unchanged — do not replace or remove values like
100%,50%,200px,1.5,auto,0,inherit,none,flex: 1 - [ ] All replacements include original value as fallback:
var(--slds-g-[hook], originalValue) - [ ] Only numbered hooks used (no
spacing-medium,font-weight-bold, etc.)
---
Cross-Cutting Checks
Hook Selection
- [ ] Background-foreground hooks paired from same family (e.g.,
surface-container-*withon-surface-*) - [ ] Spacing hooks for margin/padding/gap; sizing hooks for width/height
- [ ] Density-aware variants (
--slds-g-spacing-var-*) used where component adapts to comfy/compact
Linter Validation
- [ ]
npx @salesforce-ux/slds-linter@latest lint .— zero errors - [ ] Warnings reviewed — remaining warnings are for values with no available hook
---
Troubleshooting
Visual appearance changed
1. Check fallback values match original hardcoded values exactly 2. Verify correct hook family (surface vs container) 3. Ensure background-foreground hooks properly paired
Linter still shows violations
1. Verify hook names exactly match linter suggestions 2. Check for missed CSS files in subdirectories 3. Ensure all elements in selector chains updated in HTML
Functionality broken
1. Check original SLDS classes preserved in HTML 2. Verify JavaScript selectors updated if CSS classes changed (class overrides rule) 3. Update tests that use class-based query selectors
Non-Color Styling Hooks Guide
Reference for replacing hardcoded spacing, sizing, typography, border, radius, and shadow values with SLDS 2 styling hooks. These hooks use numbered scales with straightforward mappings — unlike color hooks, they rarely require context-based decisions.
Pattern for all replacements:
property: var(--slds-g-[hook], originalValue);Important: Only replace values that match a hook's actual rendered value. If a hardcoded value falls between two hooks (e.g., 3px when hooks offer 4px and 8px), leave it unchanged. The linter auto-fix handles exact matches; manual fixes should only apply when there's a clear correspondence. Forcing a non-matching value into the nearest hook changes the component's visual appearance.
---
Critical Rule: Never Invent Hooks
Only use hooks that actually exist in SLDS 2. The linter is the source of truth for which hooks exist and what values they map to. Do not:
- Guess hook names — Hooks like
--slds-g-spacing-medium,--slds-g-font-weight-bold,--slds-g-radius-largedo NOT exist. SLDS 2 uses numbered scales only (e.g.,--slds-g-spacing-4,--slds-g-font-weight-7). - Extrapolate patterns — If you see
--slds-g-spacing-1through--slds-g-spacing-12, do not assume--slds-g-spacing-13exists. Each category has a fixed scale defined by the design system. - Invent semantic names — There are no
--slds-g-spacing-page,--slds-g-font-heading, or--slds-g-shadow-modalhooks. Hooks are numeric, not semantic.
When unsure whether a hook exists: Run the linter (npx @salesforce-ux/slds-linter@latest lint --fix .). It will suggest valid hooks for flagged values. If the linter doesn't flag a value or doesn't suggest a hook, leave it hardcoded.
---
Table of Contents
- Spacing Hooks
- Sizing Hooks
- Typography Hooks
- Border Width Hooks
- Border Radius Hooks
- Shadow Hooks
- Uplift Decision Tree
- Common Mistakes
- Accessibility Notes
---
Spacing Hooks
Prefix: --slds-g-spacing-* Use for: margin, padding, gap, row-gap, column-gap Do NOT use for: width, height, or other dimension properties (use sizing hooks)
Scale range: 1–12. There is no --slds-g-spacing-13 or higher. If a value exceeds 5rem/80px, leave it hardcoded.
Density-Aware Spacing
For components that adapt between comfy and compact display density, use density-aware variants:
| Hook Pattern | Applies To |
|---|---|
--slds-g-spacing-var-* | All sides (margin, padding) |
--slds-g-spacing-var-block-* | Vertical only (top/bottom) |
--slds-g-spacing-var-inline-* | Horizontal only (left/right) |
Use density-aware hooks for data tables, forms, cards, tabs, and navigation components that need to respond to the user's density preference.
Examples
/* Before */
.card-body { padding: 1rem; }
.list-item { margin-bottom: 0.5rem; }
.grid { gap: 1.5rem; }
/* After — hook names come from the linter, not guesswork */
.card-body { padding: var(--slds-g-spacing-4, 1rem); }
.list-item { margin-bottom: var(--slds-g-spacing-2, 0.5rem); }
.grid { gap: var(--slds-g-spacing-5, 1.5rem); }
/* Multi-value shorthand */
.button { padding: var(--slds-g-spacing-2, 0.5rem) var(--slds-g-spacing-4, 1rem); }---
Sizing Hooks
Prefix: --slds-g-sizing-* Use for: width, height, min-width, max-width, min-height, max-height Do NOT use for: margin, padding, gap (use spacing hooks) Scale range: 1–16. There is no --slds-g-sizing-17 or higher.
Examples
/* Before */
.icon { width: 32px; height: 32px; }
/* After */
.icon { width: var(--slds-g-sizing-9, 32px); height: var(--slds-g-sizing-9, 32px); }---
Typography Hooks
Font Scale
Prefix: --slds-g-font-scale-* (and --slds-g-font-size-base for 13px) Use for: font-size Scale range: neg-4 through 10, plus base (0.8125rem/13px). Negative values (neg-1 to neg-4) are for small/caption text. There is no --slds-g-font-scale-11 or higher.
Density-aware variant: --slds-g-font-scale-var-* (also 1–10) — adapts between comfy and compact.
Font Weight
Prefix: --slds-g-font-weight-* Use for: font-weight Scale range: 1–7. Maps to CSS weight values 100–700. There is no --slds-g-font-weight-8 or higher.
Weight pairing guidance in SLDS 2:
- Display text (large scale) → lighter weight
- Titles/headings → regular weight
- Buttons/small body titles → semi-bold weight
- Inline emphasis within body → bold weight (sparingly)
Line Height
Prefix: --slds-g-font-lineheight-* Use for: line-height Scale range: 1–6. Values range from 1 to 2. There is no --slds-g-font-lineheight-7 or higher.
Font Family
| Hook | Use Case |
|---|---|
--slds-g-font-family | Default font family |
--slds-g-font-family-base | Base font family |
--slds-g-font-family-monospace | Code snippets |
Content Width
Prefix: --slds-g-sizing-content-* and --slds-g-sizing-heading-* Use for: max-width on text containers (uses ch units for readable line lengths) Scale range: content 1–3, heading 1–3.
Typography Examples
/* Before */
.title { font-size: 18px; font-weight: bold; line-height: 1.25; }
.body { font-size: 14px; font-weight: normal; line-height: 1.5; }
/* After — let the linter confirm the correct scale numbers */
.title {
font-size: var(--slds-g-font-scale-4, 18px);
font-weight: var(--slds-g-font-weight-4, bold);
line-height: var(--slds-g-font-lineheight-2, 1.25);
}
.body {
font-size: var(--slds-g-font-scale-1, 14px);
font-weight: var(--slds-g-font-weight-4, normal);
line-height: var(--slds-g-font-lineheight-4, 1.5);
}---
Border Width Hooks
Prefix: --slds-g-sizing-border-* Use for: border-width, border, border-top, etc. (the width component) Scale range: 1–4. Maps to 1px–4px. There is no --slds-g-sizing-border-5 or higher.
Border widths are NOT density-aware — they stay constant regardless of comfy/compact settings.
SLDS 2 philosophy: Use borders sparingly. Prefer spacing or shadows for visual separation. Use borders purposefully for structure, interactivity indication, and state communication.
Examples
/* Before */
.input { border: 1px solid #ccc; }
/* After */
.input { border: var(--slds-g-sizing-border-1, 1px) solid var(--slds-g-color-border-2, #ccc); }---
Border Radius Hooks
Prefix: --slds-g-radius-border-* Use for: border-radius Scale range: 1–4 plus special values circle and pill. There is no --slds-g-radius-border-5 or higher.
Choose radius by component type, not by matching px values. The hook resolves to the design-system value; the original value is preserved as fallback only.
| Hook | Components |
|---|---|
--slds-g-radius-border-1 | Badges, checkboxes |
--slds-g-radius-border-2 | Text inputs, comboboxes, text areas, tooltips |
--slds-g-radius-border-3 | Menus, popovers |
--slds-g-radius-border-4 | Cards, modals, docked composers |
--slds-g-radius-border-circle | Buttons, button icons, avatars, radios, pills |
--slds-g-radius-border-pill | Pill-shaped elements |
Border radius hooks are NOT density-aware.
Examples
/* Before */
.card { border-radius: 8px; }
.button { border-radius: 50%; }
/* After — chosen by component type, not px value */
.card { border-radius: var(--slds-g-radius-border-4, 8px); }
.button { border-radius: var(--slds-g-radius-border-circle, 50%); }---
Shadow Hooks
Prefix: --slds-g-shadow-* Use for: box-shadow Scale range: 1–6. Directional variants (-block-start-*, -block-end-*, -inline-start-*, -inline-end-*) use range 1–4. Focus variants (-outline-focus-*, -outset-focus-*, -inset-focus-*, -inset-inverse-focus-*) only have 1.
Match shadow depth to the element's stacking order — higher shadows for elements visually above others:
| Hook | Components |
|---|---|
--slds-g-shadow-1 | Page headers, joined tables, filter panels, dropdowns, inline edit, slider handles |
--slds-g-shadow-2 | Menus, docked form footer, docked utility bar, color picker, notifications |
--slds-g-shadow-3 | Panels, docked composer, tooltips, toasts |
--slds-g-shadow-4 | Modals, popovers, App Launcher |
Directional Shadows
For components positioned against screen edges:
| Hook Pattern | Direction |
|---|---|
--slds-g-shadow-block-start-* | Upward |
--slds-g-shadow-block-end-* | Downward (inherits from base) |
--slds-g-shadow-inline-start-* | Left |
--slds-g-shadow-inline-end-* | Right |
Focus Shadows
| Hook Pattern | Use Case |
|---|---|
--slds-g-shadow-outline-focus-* | Simple outline focus |
--slds-g-shadow-outset-focus-* | Double ring outset focus (white inner, brand outer) |
--slds-g-shadow-inset-focus-* | Single ring inset focus |
--slds-g-shadow-inset-inverse-focus-* | Double ring inset focus (brand inner, white outer) |
SLDS 2 philosophy: Don't apply shadows to base-level components that sit on a surface without covering other components.
Examples
/* Before */
.modal { box-shadow: 0 4px 8px rgba(0,0,0,0.1); }
.dropdown { box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
/* After */
.modal { box-shadow: var(--slds-g-shadow-4, 0 4px 8px rgba(0,0,0,0.1)); }
.dropdown { box-shadow: var(--slds-g-shadow-1, 0 2px 4px rgba(0,0,0,0.1)); }---
Uplift Decision Tree — Non-Color
When the linter flags a non-color hardcoded value, follow this process:
Step 1: Run the Linter First
Always run npx @salesforce-ux/slds-linter@latest lint --fix . before manual fixes. The linter handles exact value-to-hook matches automatically. Only proceed to manual fixes for values the linter flags but cannot auto-fix.
Step 2: Density-Aware or Standard?
Only use density-aware hooks if the original value was a variable density token:
--lwc-varSpacingMedium→--slds-g-spacing-var-4--lwc-spacingMedium→--slds-g-spacing-4(non-variable, use standard hook)
Step 3: No Exact Match — Should You Replace?
Shadow: If a shadow hook is nearly identical (e.g., offset differs by 1px), replace with the closest equivalent. Otherwise, leave as-is.
Border Radius: Replace by component type (see table above), not by px value. If the element doesn't match a known component type, leave hardcoded.
Typography: If between two scale values, use the closest. If far outside the available range, leave as-is.
Spacing/Sizing: If within 10% of a hook value, update to the closest hook. Otherwise leave as-is.
Hardcoded numerical/structural values: Never change or remove values like width: 100%, height: 50%, max-width: 200px, flex: 1, height: auto, display: none, line-height: 1.5, or 0. These are layout and structural values, not candidates for hooks. Leave them exactly as they are in the source CSS.
---
Common Mistakes
1. Inventing hooks that don't exist — This is the most common and damaging mistake. Named hooks (--slds-g-spacing-medium) and out-of-range numbered hooks do not exist. Always verify with the linter.
2. Confusing spacing and sizing — Spacing is for margins/padding/gaps. Sizing is for width/height/dimensions. Using the wrong one makes the value density-unaware or disrupts the grid system.
3. Replacing or removing hardcoded numerical values — Never change width: 100%, height: 50%, max-width: 200px, flex: 1, height: auto, display: none, line-height: 1.5, or 0. These are structural/layout values — do not replace them with hooks and do not remove them.
4. Missing fallback values — Always include the original value as fallback: var(--slds-g-spacing-4, 1rem). Without fallbacks, the component breaks if the hook is unavailable.
5. Ignoring density-aware variants — For data-dense components (tables, forms, lists), use --slds-g-spacing-var-* and --slds-g-font-scale-var-* so spacing and text adapt to comfy/compact settings.
6. *Using `--slds-c- or --slds-s-` hooks — Only `--slds-g-` (global) hooks are valid for migration. Component and scoped hooks are not for direct use in CSS.
---
Accessibility Notes
Touch Targets
- Minimum 24x24 CSS pixels for pointer inputs (WCAG 2.2 Level AA)
- Minimum 44x44 for touch inputs (industry standard)
- Use spacing hooks for padding to achieve target sizes on interactive elements
Typography Readability
- Use body-level font scale or larger for primary body text
- Default base font size (13px) is at the lower limit for comfortable reading
- Use a line height of 1.5 as default for body text (WCAG 1.4.12)
- Optimal line length: 45-75 characters (use content width hooks for max-width)
Focus Visibility
- Use appropriate border-width hooks for focus state borders
- Use
--slds-g-shadow-outset-focus-*for focus rings (double-ring pattern visible on any background) - Focus borders must maintain 3:1 minimum contrast with adjacent surfaces
Border Contrast
- Borders must maintain 3:1 contrast ratio with adjacent surfaces
- Use higher-contrast border color hooks for interactive elements
- Use lower-contrast border color hooks for decorative/non-interactive borders
Rule: LWC Token to SLDS Hook
Rule ID: slds/lwc-token-to-slds-hook Severity: Error Scope: Replaces deprecated --lwc-* design tokens with SLDS 2 styling hooks.
---
What the Linter Does
The linter detects deprecated --lwc-* tokens and reports them as errors. When there is only one suggestion, --fix auto-applies it. When there are multiple suggestions, manual selection is required. Here's real linter output for a multiple-suggestion case:
2:14 error The '--lwc-colorBackground' design token is deprecated. Replace it with
the SLDS 2 styling hook and set the fallback to '--lwc-colorBackground'.
1. --slds-g-color-surface-2
2. --slds-g-color-surface-container-2 slds/lwc-token-to-slds-hook
✖ 1 SLDS Violation (1 error, 0 warnings)---
CRITICAL: Two-Step Workflow
Step 1: Read Linter Suggestions First (Mandatory)
Before doing anything else, extract the numbered suggestions from the linter output.
ABSOLUTE RULE: You can ONLY use hooks from the linter's numbered list. You CANNOT use any other hooks.
- If linter suggests
1. --slds-g-color-surface-2and2. --slds-g-color-surface-container-2 - You can ONLY choose between those two
- Using
--slds-g-color-surface-1is FORBIDDEN (not in the list)
Step 2: Apply Context-Based Decision (Only After Step 1)
Now that you have the linter's suggestions, use context to choose the best option FROM THE LIST.
---
Decision Process
Single Suggestion
If the linter gives ONE option, --fix auto-applies it. The result looks like:
color: var(--slds-g-color-on-surface-2, var(--lwc-colorTextDefault));Multiple Suggestions
If the linter gives MULTIPLE options, apply pattern matching to choose.
Surface vs Container — Core Concept:
SURFACE = The overlay itself — the element that creates a new stacking context (pages, modals, popovers, dialogs)
- The modal/popover/dialog body background (e.g.,
.slds-modal,.slds-popover) - Main component backgrounds like
.main-body,.page-wrapper,.THIS
CONTAINER = Elements that sit on top of a surface (cards, tiles, headers, footers, list items)
- Parts within an overlay like
.slds-modal__header,.slds-modal__footer - Card components like
.card-header,.card-footer,.tile-body - Repeating items like
.list-item,.table-row
Pattern Matching
When the linter gives surface vs container options:
Choose SURFACE when:
.slds-modal,.slds-popover,.slds-dialog— the overlay itself (creates new stacking context)main-*,*-body,*-page,*-root,*-wrapper,*-background— primary/root elements.THIS— component root
Choose CONTAINER when:
.slds-modal__*,.slds-popover__*,.slds-dialog__*— parts within an overlay (header, footer, content)*-card-*,*-tile-*,*-item*,*-row*— nested elements within surfaces- Sections/panels nested within a card, tile, or item
Step-by-Step Decision Process
1. READ LINTER SUGGESTIONS — Extract the numbered list of hooks 2. IDENTIFY CLASS NAME — Look at the CSS selector being styled 3. PATTERN MATCH — Check surface patterns first, then container patterns 4. SELECT FROM LINTER OPTIONS — Choose the corresponding option 5. NEVER INVENT HOOKS — Only use hooks explicitly listed by the linter
For deeper context investigation (class usage in HTML/JS, component structure), see color-hooks-decision-guide.md.
---
Replacement Pattern
Always include the original LWC token as fallback:
property: var(--slds-g-[hook], var(--lwc-[originalToken]));The nested var() fallback ensures compatibility during migration.
---
Mandatory Rules
Rule 1: ONLY USE LINTER-SUGGESTED HOOKS
- Read the linter output first
- Only use hooks that appear in the linter's numbered list
- Cannot invent or use hooks not suggested by the linter
Rule 2: USE PATTERN RECOGNITION TO CHOOSE FROM LINTER OPTIONS
- One option → use that exact option
- Multiple options → apply pattern matching:
- Surface:
.slds-modal,.slds-popover,.slds-dialog,main-*,*-body,*-page,*-root,.THIS - Container:
.slds-modal__*,.slds-popover__*,.slds-dialog__*,*-card-*,*-tile-*,*-item*,*-row* - Apply patterns generically — don't memorize specific examples
Rule 3: ALWAYS INCLUDE FALLBACK
- Format:
var(--slds-g-[hook], var(--lwc-[originalToken]))
Rule 4: MINIMAL CHANGES
- Only fix actual
slds/lwc-token-to-slds-hookviolations - Do not remove any other code or styles
- Reference line numbers for all modifications
- If no violations found, return empty list
---
Validation Checklist
- [ ] All
var(--lwc-*)tokens have SLDS 2 replacements - [ ] Replacements are from the linter's suggested list (not invented)
- [ ] Original token included as fallback:
var(--slds-g-*, var(--lwc-*)) - [ ] Context-appropriate choice when multiple options given
Rule: No Deprecated Tokens SLDS1
Rule ID: slds/no-deprecated-tokens-slds1 Severity: Error Scope: Replaces legacy Aura t(tokenName) and token(tokenName) syntax with SLDS 2 styling hooks.
---
What the Linter Does
The linter detects t() and token() function calls in CSS, .cmp, and .html files. These are Aura-era token accessors that are deprecated in SLDS 2.
Two message types:
When a replacement exists:
Consider removing t(colorTextDefault) or replacing it with --slds-g-color-on-surface-3.
Set the fallback to t(colorTextDefault).When no replacement exists:
Update outdated design tokens to SLDS 2 styling hooks with similar values.---
Replacement Pattern
Always wrap in var() with the original LWC token as fallback:
/* Before — Aura t() syntax */
color: t(colorTextDefault);
background-color: t(colorBackgroundAlt);
/* After — SLDS 2 hook with LWC fallback */
color: var(--slds-g-color-on-surface-3, var(--lwc-colorTextDefault));
background-color: var(--slds-g-color-surface-container-1, var(--lwc-colorBackgroundAlt));The nested var() fallback ensures compatibility during migration — if the SLDS 2 hook isn't available, the LWC token still resolves.
---
Common Token Mappings
Text Colors
| Legacy Token | SLDS2 Hook | LWC Fallback |
|---|---|---|
t(colorTextPlaceholder) | --slds-g-color-on-surface-2 | --lwc-colorTextPlaceholder |
t(colorTextWeak) | --slds-g-color-on-surface-1 | --lwc-colorTextWeak |
t(colorTextDefault) | --slds-g-color-on-surface-3 | --lwc-colorTextDefault |
t(colorTextIconInverse) | --slds-g-color-on-surface-inverse-1 | --lwc-colorTextIconInverse |
Background Colors
| Legacy Token | SLDS2 Hook | LWC Fallback |
|---|---|---|
t(colorBackground) | --slds-g-color-surface-container-2 | --lwc-colorBackground |
t(colorBackgroundAlt) | --slds-g-color-surface-container-1 | --lwc-colorBackgroundAlt |
t(colorBackgroundAlt2) | --slds-g-color-surface-container-2 | --lwc-colorBackgroundAlt2 |
Spacing
| Legacy Token | SLDS2 Hook | LWC Fallback |
|---|---|---|
t(spacingXxSmall) | --slds-g-spacing-1 | --lwc-spacingXxSmall |
t(spacingXSmall) | --slds-g-spacing-2 | --lwc-spacingXSmall |
t(spacingSmall) | --slds-g-spacing-3 | --lwc-spacingSmall |
t(spacingMedium) | --slds-g-spacing-4 | --lwc-spacingMedium |
t(spacingLarge) | --slds-g-spacing-5 | --lwc-spacingLarge |
t(templateGutters) | --slds-g-spacing-3 | --lwc-templateGutters |
Border Radius
| Legacy Token | SLDS2 Hook | LWC Fallback |
|---|---|---|
t(borderRadiusSmall) | --slds-g-radius-border-1 | --lwc-borderRadiusSmall |
t(borderRadiusMedium) | --slds-g-radius-border-2 | --lwc-borderRadiusMedium |
t(borderRadiusLarge) | --slds-g-radius-border-3 | --lwc-borderRadiusLarge |
Font Sizes
| Legacy Token | SLDS2 Hook | LWC Fallback |
|---|---|---|
t(fontSizeSmall) | --slds-g-font-scale-1 | --lwc-fontSizeSmall |
t(fontSizeMedium) | --slds-g-font-scale-2 | --lwc-fontSizeMedium |
t(fontSizeLarge) | --slds-g-font-scale-3 | --lwc-fontSizeLarge |
t(fontSizeXLarge) | --slds-g-font-scale-4 | --lwc-fontSizeXLarge |
Font Weights
| Legacy Token | SLDS2 Hook | LWC Fallback |
|---|---|---|
t(fontWeightLight) | --slds-g-font-weight-3 | --lwc-fontWeightLight |
t(fontWeightRegular) | --slds-g-font-weight-4 | --lwc-fontWeightRegular |
t(fontWeightBold) | --slds-g-font-weight-7 | --lwc-fontWeightBold |
Line Heights
| Legacy Token | SLDS2 Hook | LWC Fallback |
|---|---|---|
t(lineHeightHeading) | --slds-g-font-lineheight-2 | --lwc-lineHeightHeading |
---
Tokens with No SLDS 2 Equivalent
Z-Index
Z-index tokens have no SLDS 2 hook. Use the hardcoded value directly:
/* Before */
z-index: t(zIndexSticky);
/* After — hardcoded, no hook available */
z-index: 9000;Duration
Duration tokens are for internal component transitions. Use --lwc-* directly — do NOT invent --slds-g-duration-* hooks:
/* Bad — inventing a non-existent hook */
transition: var(--slds-g-duration-slowly, var(--lwc-durationSlowly));
/* Good — use --lwc-* directly */
transition: var(--lwc-durationSlowly);Available duration tokens: --lwc-durationInstantly, --lwc-durationPromptly, --lwc-durationSlowly.
---
Wizard Token Mappings
oneDesktopSetupWizardTokens
From oneDesktopSetupWizardTokens.tokens — map wizard token → base token → SLDS 2:
| Wizard Token | SLDS2 Replacement |
|---|---|
wizardColorTextHeader | var(--slds-g-color-on-surface-3, var(--lwc-colorTextDefault)) |
wizardColorActiveMilestoneTracker | var(--lwc-colorBackgroundButtonBrand) |
wizardColorInactiveMilestoneTracker | var(--slds-g-color-surface-container-1, var(--lwc-colorBackgroundInputDisabled)) |
wizardColorTextMilestoneTracker | var(--slds-g-color-on-surface-3, var(--lwc-colorTextActionLabelActive)) |
wizardFontWeightMilestoneTracker | var(--slds-g-font-weight-4, var(--lwc-fontWeightRegular)) |
wizardFontSizeMilestoneTracker | var(--slds-g-font-scale-2, var(--lwc-fontSizeMedium)) |
wizardColorBackgroundError | var(--slds-g-color-surface-container-2, var(--lwc-colorBackgroundInput)) |
wizardBorderRadiusError | var(--slds-g-radius-border-2, var(--lwc-borderRadiusMedium)) |
s1wizardNamespace
From s1wizardNamespace.tokens:
| S1wizard Token | SLDS2 Replacement |
|---|---|
s1wizardContactFieldBackground | var(--slds-g-color-on-surface-1, var(--lwc-colorBackgroundActionbarIconUtility)) |
s1wizardComicHeadingFontFamily | var(--slds-g-font-family, var(--lwc-fontFamily)) |
s1wizardComicHeadingTextSize | var(--slds-g-font-scale-4, var(--lwc-fontSizeXLarge)) |
s1wizardComicHeadingTextColor | var(--slds-g-color-on-surface-3, var(--lwc-colorTextDefault)) |
---
Font-Family Cleanup
After the linter runs, it may add verbose font-stack fallbacks to font-family. For font-family only, trim to just the hook tokens — remove the hardcoded font stack:
/* Linter output — verbose */
font-family: var(--slds-g-font-family, var(--lwc-fontFamily, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif));
/* Cleaned up — tokens only */
font-family: var(--slds-g-font-family, var(--lwc-fontFamily));For all other properties, keep the linter's fallbacks (rgb, rem, px values).
---
Mandatory Rules
Rule 1: ONLY USE LINTER-SUGGESTED HOOKS
- Read the linter output first
- Only use hooks that the linter suggests as replacements
- If the linter says "no replacement", see "Tokens with No SLDS 2 Equivalent" above
Rule 2: ALWAYS INCLUDE FALLBACK
- Format:
var(--slds-g-[hook], var(--lwc-[token])) - The
--lwc-*fallback ensures the component still works if the SLDS 2 hook isn't available
Rule 3: MINIMAL CHANGES
- Only fix actual
slds/no-deprecated-tokens-slds1violations - Do not refactor surrounding code or styles
- Reference line numbers for all modifications
---
Validation Checklist
- [ ] All
t()andtoken()calls have SLDS 2 replacements (or documented as no-equivalent) - [ ] Replacements use linter-suggested hooks
- [ ] Original token included as LWC fallback:
var(--slds-g-*, var(--lwc-*)) - [ ] Duration tokens use
--lwc-*directly (not invented--slds-g-duration-*) - [ ] Z-index tokens use hardcoded values
- [ ] Font-family has no font-stack fallback (tokens only)
- [ ] Re-run linter shows zero errors for this rule
Rule: No Hardcoded Values
Rule ID: slds/no-hardcoded-values-slds2 Severity: Warning Scope: All CSS properties with hardcoded values that have SLDS 2 hook equivalents — colors, spacing, sizing, typography, borders, radius, and shadows.
---
What the Linter Does
The linter detects hardcoded values and reports them as warnings. Here's real output for an icon component:
3:10 warning Consider replacing the 32px static value with an SLDS 2 styling hook
that has a similar value: --slds-g-sizing-9. slds/no-hardcoded-values-slds2
5:21 warning Dynamic element with css-class "icon-container" using static value
"#066afe" for "background-color" css property. Consider replacing
the #066AFE static value with an SLDS 2 styling hook:
1. --slds-g-color-surface-inverse-1
2. --slds-g-color-surface-inverse-2
3. --slds-g-color-surface-container-inverse-1 slds/no-hardcoded-values-slds2
9:9 warning Dynamic element with css-class "account-icon" using static value
"#ffffff" for "fill" css property. Consider replacing:
1. --slds-g-color-on-accent-1
2. --slds-g-color-on-accent-2
3. --slds-g-color-error-base-95
4. --slds-g-color-warning-base-95 slds/no-hardcoded-values-slds2Non-color values get single suggestions — auto-fixable with --fix:
/* 32px → sizing-9 (single suggestion, auto-fixed) */
width: var(--slds-g-sizing-9, 32px);Color values get multiple suggestions — requires manual selection. The linter suggests hooks based on color-value similarity, not semantic context. You must inspect the HTML to choose correctly.
---
What to Fix vs What to Skip
| Property Type | Example Values | Action |
|---|---|---|
Color properties (color, fill, background, background-color, stroke, border-*-color, outline-color) | #fff, rgb(0,0,0) | Fix — replace with color hook + fallback |
Spacing properties (margin, padding, gap) | 16px, 1rem, 24px | Fix — replace with spacing hook + fallback |
Sizing properties (width, height, min-*, max-*) | 32px, 2rem | Fix — replace with sizing hook + fallback |
Font properties (font-size, font-weight, line-height) | 14px, bold, 1.5 | Fix — replace with typography hook + fallback |
Border properties (border-radius, border-width) | 8px, 1px | Fix — replace with radius/border hook + fallback |
Shadow properties (box-shadow) | 0 4px 8px rgba(…) | Fix — replace with shadow hook + fallback |
| Layout/structural values | 100%, auto, 0, inherit, none | Skip — leave unchanged, removing breaks rendering |
When the linter says "Remove the static value" for a layout value like width: 100% or height: auto, do not remove it.
---
Replacement Pattern
Always include the original value as fallback:
property: var(--slds-g-[hook], originalValue);The fallback must be the exact original value from the source CSS (e.g., #066AFE, not a converted equivalent).
---
Decision Tree
When examining a hardcoded value or deprecated token, follow this decision tree:
1. SLDS utility class available?
└─ Yes → Remove CSS, add utility class to HTML
└─ No ↓
2. At least one 1:1 styling hook mapping?
├─ Exactly one → Use it with fallback
├─ Multiple → Inspect HTML context to choose (see decision guides)
└─ None ↓
3. No exact match
└─ No close match → Leave hardcodedStep 1: Check for Utility Classes
If an SLDS utility class sets the exact property to the exact value, remove the CSS and replace with the class on the HTML element(s).
Best fit vs perfect fit: Use existing utilities that get you close enough, even if it means combining two or three classes. This keeps markup readable, styles consistent, and avoids unnecessary custom CSS. Don't write new CSS rules for edge cases unless absolutely necessary.
When modifying CSS classes:
- Before removing a CSS declaration, ensure you understand how it's used in HTML and in JS (computed classes)
- Update any tests after changing CSS classes — some tests use class query selectors that should be replaced with
data-tidattributes
Example: Replacing with Utility Classes
Common CSS like display: flex with alignment can often be replaced entirely:
/* Before — component.css */
.container {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}<!-- Before — component.html -->
<div class="container">These styles map to SLDS grid utility classes:
<!-- After — component.html (CSS removed entirely) -->
<div class="slds-grid slds-grid_align-center slds-grid_vertical-align-center">Note: flex-direction: row is the default for display: flex — it can be removed entirely.
Steps 2 & 3: Choose the Right Hook
For choosing between multiple hooks or finding the closest match, see the decision guides:
| Category | Decision Guide |
|---|---|
| Color (background, foreground, border color) | color-hooks-decision-guide.md — surface vs container, semantic vs palette, applied examples from real PRs |
| Spacing, sizing, typography, borders, radius, shadows | non-color-hooks-decision-guide.md — numbered scales, closest-match rules, density-aware variants |
---
Common Mistakes
1. Inventing hook names — Only use hooks documented in the SLDS design system. 2. Missing fallback — Always include the original value: var(--slds-g-hook, originalValue) 3. Confusing spacing and sizing — Spacing is for margins/padding/gaps. Sizing is for width/height/dimensions. 4. Using named hooks — --slds-g-spacing-medium, --slds-g-font-weight-bold, --slds-g-radius-large do NOT exist. Only numbered hooks exist. 5. Replacing layout values — Don't replace 100%, auto, flex: 1, none, or 0 with hooks. 6. Blindly trusting linter suggestions for colors — The linter matches by color value, not semantic context. Always inspect HTML before choosing.
What NOT to Replace
Leave these unchanged (no SLDS hooks apply):
width: 100%; /* layout values */
height: auto; /* layout values */
flex: 1; /* layout values */
display: none; /* layout values */
transition: color 0.3s ease; /* animation values */
opacity: 0.5; /* opacity */
background: linear-gradient(…); /* gradients */
left: 50%; /* positioning offsets */Rule: No SLDS Class Overrides
Rule ID: slds/no-slds-class-overrides Severity: Warning Scope: Detects CSS selectors that directly target .slds-* classes.
---
What the Linter Does
The linter detects CSS classes that directly override SLDS classes and reports them as warnings. It does not auto-fix — all changes require manual work in both CSS and HTML.
1:1 warning Overriding slds-button isn't supported. To differentiate SLDS and
custom classes, create a CSS class in your namespace.
Examples: myapp-input, myapp-button. slds/no-slds-class-overridesManual steps required: 1. Rename .slds-* selectors in CSS to {componentName}-{sldsElementPart} 2. Add the new component class to markup (.html for LWC, .cmp for Aura) alongside the original SLDS class 3. Never remove the original SLDS class from markup
---
Naming Convention
Format: {componentName}-{sldsElementPart} (camelCase component name)
| SLDS Class | Component: userProfile | Result |
|---|---|---|
.slds-button | userProfile | userProfile-button |
.slds-card | userProfile | userProfile-card |
.slds-modal__content | userProfile | userProfile-modal__content |
.slds-button_brand | userProfile | userProfile-button_brand |
---
Common Patterns
Pattern 1: Simple Selector
/* Before CSS */
.slds-button { border-radius: 8px; }
/* After CSS */
.myComponent-button { border-radius: 8px; }<!-- After HTML (manual) -->
<button class="slds-button myComponent-button">Click</button>Pattern 2: Descendant Selector
/* Before CSS */
.slds-card .slds-button { margin-top: 1rem; }
/* After CSS */
.myComponent-card .myComponent-button { margin-top: 1rem; }<!-- After HTML (manual) — each SLDS class gets a component class -->
<div class="slds-card myComponent-card">
<button class="slds-button myComponent-button">Click</button>
</div>Pattern 3: Multi-Class Selector
/* Before CSS */
.slds-card.slds-p-around_medium { border: 1px solid blue; }
/* After CSS */
.myComponent-card.myComponent-p-around_medium { border: 1px solid blue; }<!-- After HTML (manual) -->
<div class="slds-card slds-p-around_medium myComponent-card myComponent-p-around_medium">---
Core Rules
1. Never remove SLDS classes from markup (.html or .cmp) — only ADD component classes alongside them 2. One-to-one mapping — each SLDS class in a CSS selector gets exactly one component class 3. CamelCase component name — sampleComponent-button, not sample-component-button 4. Preserve SLDS element names — .slds-button becomes componentName-button (strip slds- prefix, keep the rest)
---
Interaction with Other Rules
If the overridden CSS properties include hardcoded colors, you must also apply rule-no-hardcoded-values.md to those properties:
/* Before */
.slds-icon-action-check { background: #4bca81; }
/* After — both rules applied */
.myComponent-icon-action-check { background: var(--slds-g-color-success-base-70, #4bca81); }---
---
Validation Checklist
CSS:
- [ ] All
.slds-*overrides reported by the linter are addressed - [ ] Component name is camelCase
- [ ] SLDS element names preserved after prefix
Markup (`.html` for LWC, `.cmp` for Aura):
- [ ] Original SLDS classes preserved
- [ ] Component classes added alongside SLDS classes
- [ ] Every element in CSS selector chain updated in markup
- [ ] One component class per SLDS class
Related skills
Forks & variants (1)
Uplifting Components To Slds2 has 1 known copy in the catalog totaling 1.5k installs. They canonicalize to this original listing.
- forcedotcom - 1.5k installs
How it compares
Use uplifting-components-to-slds2 for context-aware SLDS 2 hook decisions when a linter lists multiple semantic color options for the same hardcoded value.
FAQ
Is the linter step optional?
No. Always run npx @salesforce-ux/slds-linter@latest lint --fix . first; it auto-fixes simple violations.
How are class overrides fixed?
Rename .slds-* selectors to component-prefixed classes and add the new class alongside the SLDS class in markup.
Should layout values like 100% be replaced?
No. Leave structural layout values unchanged; they are not styling hook candidates.
Is Uplifting Components To Slds2 safe to install?
skills.sh reports 2 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.