fe-lwc-skill 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -0
- package/bin/index.js +145 -0
- package/package.json +23 -0
- package/skill/SKILL.md +88 -0
- package/skill/rules/data-error-handling.md +297 -0
- package/skill/rules/data-lds-first.md +261 -0
- package/skill/rules/data-wire-vs-imperative.md +248 -0
- package/skill/rules/design-token-dxp.md +230 -0
- package/skill/rules/design-token-slds.md +258 -0
- package/skill/rules/events-custom-event.md +227 -0
- package/skill/rules/events-lms.md +193 -0
- package/skill/rules/template-directives.md +191 -0
- package/skill/rules/template-dom-querying.md +202 -0
- package/skill/rules/template-form-pattern.md +285 -0
- package/skill/rules/template-track-immutable.md +206 -0
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
# design-token-dxp
|
|
2
|
+
|
|
3
|
+
**Impact: HIGH**
|
|
4
|
+
|
|
5
|
+
> ⚠️ **Runtime:** `--dxp` hooks are available in **LWR runtime only** (Experience Cloud LWR sites). They do not work in Aura runtime or standard Lightning pages.
|
|
6
|
+
|
|
7
|
+
Experience Cloud LWR sites expose `--dxp` CSS custom properties (styling hooks) that map directly to the Theme panel in Experience Builder. Using these hooks means a site admin can retheme the entire portal — colors, fonts, spacing — without a developer touching component CSS. Hardcoding values bypasses this system and causes visual regressions whenever the theme changes.
|
|
8
|
+
|
|
9
|
+
> **Rule:**
|
|
10
|
+
>
|
|
11
|
+
> - For typography in HTML templates, use `dxp-text-*` CSS classes (e.g. `dxp-text-heading-xlarge`).
|
|
12
|
+
> - For typography in component CSS, use `--dxp-s-text-*` hooks (e.g. `var(--dxp-s-text-heading-extra-large-font-size)`).
|
|
13
|
+
> - For colors, use `--dxp-g-*` global color hooks — never hardcode hex values.
|
|
14
|
+
> - For font-family and base font size overrides, use `--dxp-g-root-font-family` / `--dxp-g-heading-font-family`.
|
|
15
|
+
> - Define a `:root` block in **Head Markup** (Experience Builder → Settings → Advanced → Head Markup) for site-wide token overrides. Do not repeat overrides inside individual component CSS files.
|
|
16
|
+
> - Never use `--dxp-g-destructive` for brand colors — it is reserved for error/invalid states only.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Typography — CSS Classes vs CSS Variables
|
|
21
|
+
|
|
22
|
+
Two ways to apply text styles. Use the class approach in HTML templates; use the variable approach when overriding in CSS.
|
|
23
|
+
|
|
24
|
+
### Heading classes & hooks
|
|
25
|
+
|
|
26
|
+
| Level | HTML class | CSS variable (font-size) | CSS variable (font-family) |
|
|
27
|
+
| ----------- | ------------------------- | -------------------------------------------- | ---------------------------------------------- |
|
|
28
|
+
| Extra Large | `dxp-text-heading-xlarge` | `--dxp-s-text-heading-extra-large-font-size` | `--dxp-s-text-heading-extra-large-font-family` |
|
|
29
|
+
| Large | `dxp-text-heading-large` | `--dxp-s-text-heading-large-font-size` | `--dxp-s-text-heading-large-font-family` |
|
|
30
|
+
| Medium | `dxp-text-heading-medium` | `--dxp-s-text-heading-medium-font-size` | `--dxp-s-text-heading-medium-font-family` |
|
|
31
|
+
| Small | `dxp-text-heading-small` | `--dxp-s-text-heading-small-font-size` | `--dxp-s-text-heading-small-font-family` |
|
|
32
|
+
| X-Small | `dxp-text-heading-xsmall` | `--dxp-s-text-heading-extra-small-font-size` | `--dxp-s-text-heading-extra-small-font-family` |
|
|
33
|
+
|
|
34
|
+
Each heading level also supports:
|
|
35
|
+
|
|
36
|
+
- `--dxp-s-text-heading-[level]-font-weight`
|
|
37
|
+
- `--dxp-s-text-heading-[level]-line-height`
|
|
38
|
+
- `--dxp-s-text-heading-[level]-color`
|
|
39
|
+
|
|
40
|
+
### Body / paragraph classes & hooks
|
|
41
|
+
|
|
42
|
+
| Level | HTML class | CSS variable (font-size) |
|
|
43
|
+
| --------------- | ---------------------- | ------------------------------------ |
|
|
44
|
+
| Paragraph 1 | `dxp-text-body-large` | `--dxp-s-text-body-large-font-size` |
|
|
45
|
+
| Paragraph 2 | `dxp-text-body-medium` | `--dxp-s-text-body-medium-font-size` |
|
|
46
|
+
| Caption / small | `dxp-text-body-small` | `--dxp-s-text-body-small-font-size` |
|
|
47
|
+
|
|
48
|
+
Each body level also supports `--dxp-s-text-body-[level]-font-family` and `--dxp-s-text-body-[level]-line-height`.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Color hooks
|
|
53
|
+
|
|
54
|
+
| Family | Hook | Use case |
|
|
55
|
+
| ----------------- | ----------------------------------------------- | ------------------------------------------- |
|
|
56
|
+
| Root | `--dxp-g-root` | Page / section background |
|
|
57
|
+
| Root contrast | `--dxp-g-root-contrast` | Text/icons on root background |
|
|
58
|
+
| Root interaction | `--dxp-g-root-1` | Hover state on root background |
|
|
59
|
+
| Brand | `--dxp-g-brand` | Primary brand color (buttons, links, focus) |
|
|
60
|
+
| Brand contrast | `--dxp-g-brand-contrast` | Text/icons on brand background |
|
|
61
|
+
| Brand interaction | `--dxp-g-brand-1` | Hover state of brand elements |
|
|
62
|
+
| Success | `--dxp-g-success` | Success badges, alerts, toasts |
|
|
63
|
+
| Destructive | `--dxp-g-destructive` | Error / invalid states only |
|
|
64
|
+
| Warning | `--dxp-g-warning` | Warning badges, alerts |
|
|
65
|
+
| Info | `--dxp-g-info` | Tooltips, popovers |
|
|
66
|
+
| Neutral | `--dxp-g-neutral` | Borders, shadows, disabled inputs |
|
|
67
|
+
| Neutral scale | `--dxp-g-neutral-1` / `neutral-2` / `neutral-3` | Progressive neutral shades |
|
|
68
|
+
|
|
69
|
+
> ⚠️ `neutral`, `warning`, `info`, `success`, and `destructive` **cannot be configured in the Theme panel** — override them manually in Head Markup only.
|
|
70
|
+
|
|
71
|
+
> ⚠️ Always use the paired `*-contrast` hook for text/icons placed on a colored background to ensure accessible contrast ratios.
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Case 1: Hardcoded typography in HTML template
|
|
76
|
+
|
|
77
|
+
**Incorrect — custom class with hardcoded font-size breaks when theme changes:**
|
|
78
|
+
|
|
79
|
+
```html
|
|
80
|
+
<!-- appointmentCard.html -->
|
|
81
|
+
<!-- ❌ Custom class with hardcoded font-size — not tied to the site theme -->
|
|
82
|
+
<template>
|
|
83
|
+
<h2 class="card-title">{appointment.Name}</h2>
|
|
84
|
+
<p class="card-body">{appointment.Summary__c}</p>
|
|
85
|
+
</template>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
```css
|
|
89
|
+
/* appointmentCard.css */
|
|
90
|
+
/* ❌ Hardcoded values — admin cannot update these from Theme panel */
|
|
91
|
+
.card-title {
|
|
92
|
+
font-size: 18px;
|
|
93
|
+
font-family: "Inter", sans-serif;
|
|
94
|
+
}
|
|
95
|
+
.card-body {
|
|
96
|
+
font-size: 14px;
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**Correct — use `dxp-text-*` classes in the template:**
|
|
101
|
+
|
|
102
|
+
```html
|
|
103
|
+
<!-- appointmentCard.html -->
|
|
104
|
+
<!-- ✅ dxp classes inherit from Theme panel — no CSS needed -->
|
|
105
|
+
<template>
|
|
106
|
+
<h2 class="dxp-text-heading-medium">{appointment.Name}</h2>
|
|
107
|
+
<p class="dxp-text-body-large">{appointment.Summary__c}</p>
|
|
108
|
+
</template>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
```css
|
|
112
|
+
/* appointmentCard.css — ✅ empty for typography, only add non-text custom styles */
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## Case 2: Typography override in component CSS
|
|
118
|
+
|
|
119
|
+
When a custom CSS class needs to match a text style (e.g. a dynamically styled element):
|
|
120
|
+
|
|
121
|
+
**Correct — use `--dxp-s-text-*` variables in CSS:**
|
|
122
|
+
|
|
123
|
+
```css
|
|
124
|
+
/* portalHero.css */
|
|
125
|
+
.hero-title {
|
|
126
|
+
font-size: var(--dxp-s-text-heading-extra-large-font-size);
|
|
127
|
+
font-family: var(--dxp-s-text-heading-extra-large-font-family);
|
|
128
|
+
font-weight: var(--dxp-s-text-heading-extra-large-font-weight);
|
|
129
|
+
color: var(--dxp-s-text-heading-extra-large-color);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.hero-subtitle {
|
|
133
|
+
font-size: var(--dxp-s-text-body-large-font-size);
|
|
134
|
+
line-height: var(--dxp-s-text-body-large-line-height);
|
|
135
|
+
color: var(--dxp-g-root-contrast); /* ✅ contrast pair for root background */
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## Case 3: Hardcoded color values
|
|
142
|
+
|
|
143
|
+
**Incorrect:**
|
|
144
|
+
|
|
145
|
+
```css
|
|
146
|
+
/* ❌ Hardcoded hex — breaks when brand color changes in Theme panel */
|
|
147
|
+
.cta-button {
|
|
148
|
+
background-color: #0070d2;
|
|
149
|
+
color: #ffffff;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/* ❌ Using destructive color for brand — wrong semantic */
|
|
153
|
+
.brand-badge {
|
|
154
|
+
background-color: var(--dxp-g-destructive);
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**Correct:**
|
|
159
|
+
|
|
160
|
+
```css
|
|
161
|
+
/* ✅ Uses brand hooks — updates automatically when admin changes theme */
|
|
162
|
+
.cta-button {
|
|
163
|
+
background-color: var(--dxp-g-brand);
|
|
164
|
+
color: var(--dxp-g-brand-contrast); /* ✅ always accessible on brand bg */
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/* ✅ Correct semantic — destructive only for errors */
|
|
168
|
+
.error-badge {
|
|
169
|
+
background-color: var(--dxp-g-destructive);
|
|
170
|
+
color: var(--dxp-g-destructive-contrast);
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Case 4: Global `:root` override in Head Markup
|
|
177
|
+
|
|
178
|
+
Define site-wide token overrides once in Head Markup — not scattered across component CSS files.
|
|
179
|
+
|
|
180
|
+
**Incorrect — overriding tokens inside a component:**
|
|
181
|
+
|
|
182
|
+
```css
|
|
183
|
+
/* appointmentCard.css */
|
|
184
|
+
/* ❌ Only affects this component — leads to duplicated overrides everywhere */
|
|
185
|
+
:host {
|
|
186
|
+
--dxp-g-heading-font-family: "Nunito", sans-serif;
|
|
187
|
+
--dxp-g-brand: #005fbb;
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
**Correct — one `:root` block in Experience Builder → Settings → Advanced → Head Markup:**
|
|
192
|
+
|
|
193
|
+
```html
|
|
194
|
+
<!-- Head Markup -->
|
|
195
|
+
<style>
|
|
196
|
+
:root {
|
|
197
|
+
/* Base font */
|
|
198
|
+
--dxp-g-root-font-family: "Nunito", "Salesforce Sans", arial, sans-serif;
|
|
199
|
+
--dxp-g-heading-font-family: "Nunito", "Salesforce Sans", arial, sans-serif;
|
|
200
|
+
|
|
201
|
+
/* Brand colors — configurable via Theme panel */
|
|
202
|
+
--dxp-g-brand: #005fbb;
|
|
203
|
+
--dxp-g-brand-contrast: #ffffff;
|
|
204
|
+
|
|
205
|
+
/* Semantic colors — must be set here, not configurable in Theme panel */
|
|
206
|
+
--dxp-g-success: #2e7d32;
|
|
207
|
+
--dxp-g-warning: #e65100;
|
|
208
|
+
--dxp-g-destructive: #c62828;
|
|
209
|
+
--dxp-g-neutral: #e5e5e5;
|
|
210
|
+
}
|
|
211
|
+
</style>
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## Quick Decision Guide
|
|
217
|
+
|
|
218
|
+
| Need | Approach |
|
|
219
|
+
| ---------------------------- | ----------------------------------------------------------------- |
|
|
220
|
+
| Heading style in HTML | `class="dxp-text-heading-{xlarge\|large\|medium\|small\|xsmall}"` |
|
|
221
|
+
| Body text in HTML | `class="dxp-text-body-{large\|medium\|small}"` |
|
|
222
|
+
| Typography in component CSS | `var(--dxp-s-text-heading-[level]-font-size)` etc. |
|
|
223
|
+
| Brand / interaction color | `var(--dxp-g-brand)` + `var(--dxp-g-brand-contrast)` |
|
|
224
|
+
| Error state color | `var(--dxp-g-destructive)` — not for brand use |
|
|
225
|
+
| Global font / color override | `:root {}` in Head Markup — not in component CSS |
|
|
226
|
+
| Hardcode a hex value | ❌ Never |
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
**Reference:** [--dxp Styling Hooks](https://developer.salesforce.com/docs/atlas.en-us.exp_cloud_lwr.meta/exp_cloud_lwr/brand_hooks.htm) · [Text Hooks](https://developer.salesforce.com/docs/atlas.en-us.exp_cloud_lwr.meta/exp_cloud_lwr/brand_hooks_text.htm) · [Color Hooks](https://developer.salesforce.com/docs/atlas.en-us.exp_cloud_lwr.meta/exp_cloud_lwr/brand_hooks_color.htm)
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
# design-token-slds
|
|
2
|
+
|
|
3
|
+
**Impact: MEDIUM**
|
|
4
|
+
|
|
5
|
+
SLDS (Salesforce Lightning Design System) provides a comprehensive set of utility classes for layout, spacing, typography, and borders. Using SLDS utilities keeps component CSS files minimal and ensures visual consistency with the rest of the Salesforce platform. Writing custom CSS for things SLDS already handles wastes time and creates maintenance debt.
|
|
6
|
+
|
|
7
|
+
> **Rule:**
|
|
8
|
+
>
|
|
9
|
+
> - Reach for SLDS utility classes before writing any custom CSS.
|
|
10
|
+
> - **Never write `display: flex`, `flex-direction`, `align-items`, or `gap` in CSS** — use `slds-grid` and its modifier classes instead. If you find yourself writing flexbox CSS, stop and look up the SLDS equivalent first.
|
|
11
|
+
> - Use `slds-m-*` and `slds-p-*` for margins and padding before setting them in CSS.
|
|
12
|
+
> - Use `slds-border_*` for borders; `slds-text-*` for typography utilities.
|
|
13
|
+
> - Write custom CSS only when SLDS utilities genuinely cannot cover the requirement.
|
|
14
|
+
> - Never override SLDS utility classes with `!important` — this creates specificity debt.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Project CSS Variables
|
|
19
|
+
|
|
20
|
+
Before writing any custom CSS value (color, font, spacing), read **both** project config files below. Replace `<site-name>` with the actual portal folder name found under `digitalExperiences/site/`.
|
|
21
|
+
|
|
22
|
+
**1. Branding tokens** — colors, fonts, and spacing variables:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
force-app/main/default/digitalExperiences/site/<site-name>/sfdc_cms__brandingSet/Build_Your_Own_LWR/content.json
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**2. App page layout config** — page-level CSS variables and region settings:
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
force-app/main/default/digitalExperiences/site/<site-name>/sfdc_cms__appPage/mainAppPage/content.json
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
> ⚠️ **Important — read `headMarkup` key first.** This file contains a `headMarkup` key that holds a `<style>` block injected into the site's `<head>`. This is where site-wide CSS variable overrides live — values here take precedence over component-level CSS and branding tokens. Before writing any CSS override, check `headMarkup` to see if the variable is already defined there. If you need to override a CSS variable globally, add it to `headMarkup`, not inside a component's `.css` file.
|
|
35
|
+
|
|
36
|
+
The `contentBody.values` node in the branding file contains all design tokens for the site. Keys map to `--dxp-*` CSS custom properties at runtime. Always use the corresponding `--dxp-*` variable in component CSS — never hardcode the raw value.
|
|
37
|
+
|
|
38
|
+
Example — given this branding file structure:
|
|
39
|
+
|
|
40
|
+
```json
|
|
41
|
+
{
|
|
42
|
+
"contentBody": {
|
|
43
|
+
"values": {
|
|
44
|
+
"ButtonColor": "var(--dxp-g-brand)",
|
|
45
|
+
"ButtonBorderRadius": "0px",
|
|
46
|
+
"ButtonFontWeight": "500",
|
|
47
|
+
"FormElementBorderColor": "rgb(155, 155, 155)",
|
|
48
|
+
"FormElementBorderRadius": "8px",
|
|
49
|
+
"HeadingLargeFontSize": "1.625rem",
|
|
50
|
+
"HeadingLargeFontWeight": "700",
|
|
51
|
+
"BodyFontSize": "1rem",
|
|
52
|
+
"BodyLineHeight": "1.4",
|
|
53
|
+
"BackgroundColor": "rgb(255, 255, 255)"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Then in component CSS use the corresponding `--dxp-*` hooks — **never** the raw values:
|
|
60
|
+
|
|
61
|
+
```css
|
|
62
|
+
/* ✅ correct — uses dxp hooks that map to the branding values above */
|
|
63
|
+
.cta-button {
|
|
64
|
+
background-color: var(--dxp-g-brand); /* ButtonColor */
|
|
65
|
+
border-radius: var(--dxp-s-button-sizing-border-radius); /* ButtonBorderRadius */
|
|
66
|
+
font-weight: var(--dxp-s-button-text-font-weight); /* ButtonFontWeight */
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.form-input {
|
|
70
|
+
border-color: var(--dxp-s-form-element-color-border); /* FormElementBorderColor */
|
|
71
|
+
border-radius: var(--dxp-s-form-element-sizing-border-radius); /* FormElementBorderRadius */
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.section-heading {
|
|
75
|
+
font-size: var(--dxp-s-text-heading-large-font-size); /* HeadingLargeFontSize */
|
|
76
|
+
font-weight: var(--dxp-s-text-heading-large-font-weight); /* HeadingLargeFontWeight */
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/* ❌ never hardcode raw values from contentBody.values */
|
|
80
|
+
.cta-button {
|
|
81
|
+
background-color: #005fbb; /* ❌ */
|
|
82
|
+
border-radius: 0px; /* ❌ */
|
|
83
|
+
}
|
|
84
|
+
.form-input {
|
|
85
|
+
border-color: rgb(155, 155, 155); /* ❌ */
|
|
86
|
+
border-radius: 8px; /* ❌ */
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Case 1: Custom layout instead of `slds-grid`
|
|
93
|
+
|
|
94
|
+
**Incorrect — custom flexbox reimplements what SLDS already provides:**
|
|
95
|
+
|
|
96
|
+
```html
|
|
97
|
+
<!-- appointmentList.html -->
|
|
98
|
+
<!-- ❌ Custom CSS class for a standard flex row layout -->
|
|
99
|
+
<template>
|
|
100
|
+
<div class="card-row">
|
|
101
|
+
<div class="card-col-left">
|
|
102
|
+
<p>{appointment.Name}</p>
|
|
103
|
+
</div>
|
|
104
|
+
<div class="card-col-right">
|
|
105
|
+
<p>{appointment.Date__c}</p>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
</template>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
```css
|
|
112
|
+
/* appointmentList.css — ❌ NEVER write layout CSS like this */
|
|
113
|
+
.card-row {
|
|
114
|
+
display: flex; /* ❌ use slds-grid */
|
|
115
|
+
flex-direction: row; /* ❌ default for slds-grid */
|
|
116
|
+
align-items: center; /* ❌ use slds-grid_vertical-align-center */
|
|
117
|
+
gap: 1rem; /* ❌ use slds-m-* on children instead */
|
|
118
|
+
}
|
|
119
|
+
.card-col-left {
|
|
120
|
+
flex: 1; /* ❌ use slds-col */
|
|
121
|
+
}
|
|
122
|
+
.card-col-right {
|
|
123
|
+
flex: 0 0 auto; /* ❌ use slds-shrink-none */
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
**Correct — use `slds-grid` utilities:**
|
|
128
|
+
|
|
129
|
+
```html
|
|
130
|
+
<!-- appointmentList.html -->
|
|
131
|
+
<!-- ✅ SLDS handles the layout — no custom CSS needed -->
|
|
132
|
+
<template>
|
|
133
|
+
<div class="slds-grid slds-grid_align-spread slds-grid_vertical-align-center">
|
|
134
|
+
<div class="slds-col">
|
|
135
|
+
<p>{appointment.Name}</p>
|
|
136
|
+
</div>
|
|
137
|
+
<div class="slds-col slds-shrink-none">
|
|
138
|
+
<p>{appointment.Date__c}</p>
|
|
139
|
+
</div>
|
|
140
|
+
</div>
|
|
141
|
+
</template>
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
```css
|
|
145
|
+
/* appointmentList.css — ✅ empty, or contains only non-layout custom styles */
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Common layout patterns → SLDS translation
|
|
149
|
+
|
|
150
|
+
| What Figma shows | CSS you would write ❌ | SLDS equivalent ✅ |
|
|
151
|
+
| -------------------------- | --------------------------------------------- | ------------------------------------------- |
|
|
152
|
+
| Row, space between | `display:flex; justify-content:space-between` | `slds-grid slds-grid_align-spread` |
|
|
153
|
+
| Row, centered vertically | `display:flex; align-items:center` | `slds-grid slds-grid_vertical-align-center` |
|
|
154
|
+
| Column stack | `display:flex; flex-direction:column` | `slds-grid slds-grid_vertical` |
|
|
155
|
+
| Item takes remaining space | `flex: 1` | `slds-col` |
|
|
156
|
+
| Item fixed width | `flex: 0 0 auto` | `slds-shrink-none` |
|
|
157
|
+
| Gap between items | `gap: 0.5rem` | `slds-m-right_small` on each child |
|
|
158
|
+
| Wrap to next line | `flex-wrap: wrap` | `slds-wrap` on `slds-grid` |
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Case 2: Custom spacing instead of `slds-m-*` / `slds-p-*`
|
|
163
|
+
|
|
164
|
+
**Incorrect — hardcoded margin/padding in CSS:**
|
|
165
|
+
|
|
166
|
+
```html
|
|
167
|
+
<!-- patientCard.html -->
|
|
168
|
+
<template>
|
|
169
|
+
<div class="card-wrapper">
|
|
170
|
+
<h2 class="card-heading">{patient.Name}</h2>
|
|
171
|
+
<p class="card-body">{patient.Summary__c}</p>
|
|
172
|
+
</div>
|
|
173
|
+
</template>
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
```css
|
|
177
|
+
/* patientCard.css — ❌ custom spacing that duplicates SLDS scale */
|
|
178
|
+
.card-wrapper {
|
|
179
|
+
padding: 16px;
|
|
180
|
+
margin-bottom: 12px;
|
|
181
|
+
}
|
|
182
|
+
.card-heading {
|
|
183
|
+
margin-bottom: 8px;
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
**Correct — SLDS spacing utilities on the element directly:**
|
|
188
|
+
|
|
189
|
+
```html
|
|
190
|
+
<!-- patientCard.html -->
|
|
191
|
+
<!-- ✅ Spacing handled in markup — CSS file stays clean -->
|
|
192
|
+
<template>
|
|
193
|
+
<div class="slds-p-around_medium slds-m-bottom_small">
|
|
194
|
+
<h2 class="slds-m-bottom_x-small">{patient.Name}</h2>
|
|
195
|
+
<p>{patient.Summary__c}</p>
|
|
196
|
+
</div>
|
|
197
|
+
</template>
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## Case 3: Custom border instead of `slds-border_*`
|
|
203
|
+
|
|
204
|
+
**Incorrect:**
|
|
205
|
+
|
|
206
|
+
```css
|
|
207
|
+
/* ❌ Custom border — not consistent with SLDS visual language */
|
|
208
|
+
.status-badge {
|
|
209
|
+
border: 1px solid #dddbda;
|
|
210
|
+
border-radius: 4px;
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
**Correct:**
|
|
215
|
+
|
|
216
|
+
```html
|
|
217
|
+
<!-- ✅ SLDS utility class -->
|
|
218
|
+
<span class="slds-border_bottom slds-p-bottom_xxx-small">{status}</span>
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## SLDS utilities quick reference
|
|
224
|
+
|
|
225
|
+
| Need | SLDS class |
|
|
226
|
+
| --------------------- | ------------------------------------------------- |
|
|
227
|
+
| Flex row | `slds-grid` |
|
|
228
|
+
| Flex column | `slds-grid slds-grid_vertical` |
|
|
229
|
+
| Grow column | `slds-col` |
|
|
230
|
+
| Fixed-size column | `slds-shrink-none` |
|
|
231
|
+
| Align items center | `slds-grid_vertical-align-center` |
|
|
232
|
+
| Justify space-between | `slds-grid_align-spread` |
|
|
233
|
+
| Margin (all sides) | `slds-m-around_{size}` |
|
|
234
|
+
| Margin (directional) | `slds-m-top_{size}`, `slds-m-bottom_{size}`, etc. |
|
|
235
|
+
| Padding (all sides) | `slds-p-around_{size}` |
|
|
236
|
+
| Padding (directional) | `slds-p-top_{size}`, `slds-p-bottom_{size}`, etc. |
|
|
237
|
+
| Border | `slds-border_{top\|bottom\|left\|right}` |
|
|
238
|
+
| Truncate text | `slds-truncate` |
|
|
239
|
+
| Screen reader only | `slds-assistive-text` |
|
|
240
|
+
|
|
241
|
+
> **Size tokens:** `xxx-small`, `xx-small`, `x-small`, `small`, `medium`, `large`, `x-large`, `xx-large`
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## When to write custom CSS
|
|
246
|
+
|
|
247
|
+
Write custom CSS only when SLDS and `--dxp` hooks cannot cover the requirement:
|
|
248
|
+
|
|
249
|
+
- Component-specific visual states not in SLDS (e.g. custom progress indicator)
|
|
250
|
+
- Animations and transitions
|
|
251
|
+
- CSS Grid layouts beyond what `slds-grid` supports
|
|
252
|
+
- Overriding a third-party component's style via `::part()` or `:host`
|
|
253
|
+
|
|
254
|
+
In all other cases, default to SLDS utilities first.
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
**Reference:** [SLDS Utility Classes](https://www.lightningdesignsystem.com/utilities/alignment/) · [SLDS Grid System](https://www.lightningdesignsystem.com/utilities/grid/)
|