@times-design-system/components-wordpress 1.2.2-alpha.0 → 1.2.2-alpha.10
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/BLOCK_CREATION_CHECKLIST.md +132 -19
- package/README.md +10 -4
- package/SCSS_VARIABLES_REFERENCE.md +316 -0
- package/STYLING_SYNC_GUIDE.md +306 -0
- package/TRANSFORMATION_GUIDE.md +286 -13
- package/dist/blocks/ad-container/block.json +1 -0
- package/dist/blocks/ad-container/render.php +27 -0
- package/dist/blocks/ad-container/style.css +23 -20
- package/dist/blocks/button/block.json +1 -0
- package/dist/blocks/button/render.php +71 -0
- package/dist/blocks/button/style-resolved.css +153 -0
- package/dist/blocks/button/style.css +138 -18
- package/dist/blocks/chip/block.json +1 -0
- package/dist/blocks/chip/render.php +40 -0
- package/dist/blocks/chip/style.css +68 -29
- package/dist/blocks/divider/block.json +1 -0
- package/dist/blocks/divider/render.php +31 -0
- package/dist/blocks/divider/style.css +25 -13
- package/dist/blocks/flag/block.json +1 -0
- package/dist/blocks/flag/render.php +34 -0
- package/dist/blocks/flag/style.css +39 -30
- package/dist/blocks/icon-button/block.json +1 -0
- package/dist/blocks/icon-button/render.php +46 -0
- package/dist/blocks/icon-button/style.css +21 -22
- package/dist/blocks/input/block.json +1 -0
- package/dist/blocks/input/render.php +39 -0
- package/dist/blocks/input/style.css +40 -17
- package/dist/blocks/link/block.json +1 -0
- package/dist/blocks/link/render.php +44 -0
- package/dist/blocks/link/style.css +87 -34
- package/dist/blocks/text/block.json +1 -0
- package/dist/blocks/text/render.php +26 -0
- package/dist/blocks/text/style.css +5 -18
- package/dist/blocks/toast/block.json +1 -0
- package/dist/blocks/toast/render.php +37 -0
- package/dist/blocks/toast/style.css +50 -34
- package/dist/index.cjs +20 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/blocks/ad-container/block.json +1 -0
- package/src/blocks/ad-container/render.php +27 -0
- package/src/blocks/ad-container/style.css +23 -20
- package/src/blocks/button/block.json +1 -0
- package/src/blocks/button/render.php +71 -0
- package/src/blocks/button/style-resolved.css +153 -0
- package/src/blocks/button/style.css +138 -18
- package/src/blocks/chip/block.json +1 -0
- package/src/blocks/chip/render.php +40 -0
- package/src/blocks/chip/style.css +68 -29
- package/src/blocks/divider/block.json +1 -0
- package/src/blocks/divider/render.php +31 -0
- package/src/blocks/divider/style.css +25 -13
- package/src/blocks/flag/block.json +1 -0
- package/src/blocks/flag/render.php +34 -0
- package/src/blocks/flag/style.css +39 -30
- package/src/blocks/icon-button/block.json +1 -0
- package/src/blocks/icon-button/render.php +46 -0
- package/src/blocks/icon-button/style.css +21 -22
- package/src/blocks/input/block.json +1 -0
- package/src/blocks/input/render.php +39 -0
- package/src/blocks/input/style.css +40 -17
- package/src/blocks/link/block.json +1 -0
- package/src/blocks/link/render.php +44 -0
- package/src/blocks/link/style.css +87 -34
- package/src/blocks/text/block.json +1 -0
- package/src/blocks/text/render.php +26 -0
- package/src/blocks/text/style.css +5 -18
- package/src/blocks/toast/block.json +1 -0
- package/src/blocks/toast/render.php +37 -0
- package/src/blocks/toast/style.css +50 -34
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
# WordPress Block Styling Synchronization Guide
|
|
2
|
+
|
|
3
|
+
This guide ensures that WordPress block styles remain synchronized with React component styles, maintaining feature parity across implementations.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
All WordPress blocks must have equivalent CSS to their React counterparts. This includes:
|
|
8
|
+
|
|
9
|
+
- All variant combinations (intent, size, behaviour)
|
|
10
|
+
- All state handlers (hover, focus, active, disabled)
|
|
11
|
+
- All design token references
|
|
12
|
+
- Identical visual appearance in patterns and editor
|
|
13
|
+
|
|
14
|
+
## Process: React to WordPress Style Synchronization
|
|
15
|
+
|
|
16
|
+
### Step 1: Identify React Component Styles
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# Locate React component styles
|
|
20
|
+
ls packages/components-react/src/<ComponentName>/
|
|
21
|
+
# Look for: <ComponentName>.scss or <ComponentName>.css
|
|
22
|
+
|
|
23
|
+
# Count lines to understand complexity
|
|
24
|
+
wc -l packages/components-react/src/<ComponentName>/<ComponentName>.scss
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Step 2: Extract Style Segments
|
|
28
|
+
|
|
29
|
+
Read the React component stylesheet and identify:
|
|
30
|
+
|
|
31
|
+
1. **Base styles** (`.tds-<component>` class)
|
|
32
|
+
- Display, flex properties, gaps
|
|
33
|
+
- Basic colors, borders, padding
|
|
34
|
+
- Font properties (family, weight, size, line-height)
|
|
35
|
+
- Transitions and animations
|
|
36
|
+
|
|
37
|
+
2. **Variant modifiers** (`.tds-<component>--<variant>`)
|
|
38
|
+
- Intent: primary, secondary, negative
|
|
39
|
+
- Size: small, medium, large
|
|
40
|
+
- Behaviour: full, hug
|
|
41
|
+
- Channel: on, off
|
|
42
|
+
- Toggle: on, off
|
|
43
|
+
|
|
44
|
+
3. **State handlers** (pseudo-classes)
|
|
45
|
+
- `:hover` - hover states
|
|
46
|
+
- `:focus-visible` - focus states with focus ring
|
|
47
|
+
- `:active` - pressed states
|
|
48
|
+
- `:disabled` - disabled states
|
|
49
|
+
- `:visited` - visited states (for links)
|
|
50
|
+
|
|
51
|
+
4. **Nested selectors** (children and pseudo-elements)
|
|
52
|
+
- `.tds-<component>__<child>` - nested elements
|
|
53
|
+
- `::after` - focus rings, decorative elements
|
|
54
|
+
- `::before` - pseudo-elements
|
|
55
|
+
|
|
56
|
+
### Step 3: Copy and Adapt to WordPress
|
|
57
|
+
|
|
58
|
+
Create `style.css` in the block directory:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
cp packages/components-react/src/<ComponentName>/<ComponentName>.scss \
|
|
62
|
+
packages/components-wordpress/src/blocks/<component>/style-temp.css
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Then:
|
|
66
|
+
|
|
67
|
+
1. Convert SCSS to CSS (remove variables, nesting, mixins)
|
|
68
|
+
2. Replace any SCSS variables with CSS custom properties
|
|
69
|
+
3. Ensure all color/spacing/typography use design tokens
|
|
70
|
+
4. Verify BEM naming convention (hyphens, no underscores)
|
|
71
|
+
5. Save as `style.css`
|
|
72
|
+
|
|
73
|
+
### Step 4: Design Token Mapping
|
|
74
|
+
|
|
75
|
+
All style values must use CSS custom properties from `@times-design-system/theme-scss`.
|
|
76
|
+
|
|
77
|
+
**Common Design Token Categories**:
|
|
78
|
+
|
|
79
|
+
| Category | Tokens | Example |
|
|
80
|
+
| -------------- | -------------------------------------------------------------------- | -------------------------------------------- |
|
|
81
|
+
| **Colors** | `--interactive-*`, `--text-*`, `--border-*`, `--flag-*`, `--toast-*` | `var(--interactive-primary-fill-default)` |
|
|
82
|
+
| **Spacing** | `--spacing-01` through `--spacing-20` | `var(--spacing-03)` |
|
|
83
|
+
| **Typography** | `--fontFamily*`, `--fontWeight*`, `--fontSize*`, `--fontLineHeight*` | `var(--fontFamily040)`, `var(--fontSize030)` |
|
|
84
|
+
| **Borders** | `--border-radius-*` | `var(--border-radius-full)` |
|
|
85
|
+
| **Effects** | `--focus-border`, `--shadow-*` | `var(--focus-border)` |
|
|
86
|
+
|
|
87
|
+
**Validation**:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# Check for hardcoded hex colors (should be 0 results)
|
|
91
|
+
grep -E '#[0-9a-fA-F]{6}' packages/components-wordpress/src/blocks/<component>/style.css
|
|
92
|
+
|
|
93
|
+
# Check for hardcoded values (should use variables)
|
|
94
|
+
grep -E '[0-9]+px|[0-9]+rem' packages/components-wordpress/src/blocks/<component>/style.css | grep -v 'var('
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Step 5: Variant Coverage
|
|
98
|
+
|
|
99
|
+
Ensure **all** variant combinations are styled:
|
|
100
|
+
|
|
101
|
+
**Button Example**:
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
Intents: --intent-primary ✓, --intent-secondary ✓, --intent-negative ✓
|
|
105
|
+
Sizes: --size-small ✓, --size-medium ✓, --size-large ✓
|
|
106
|
+
Behaviour: --behaviour-full ✓, --behaviour-hug ✓
|
|
107
|
+
States: :hover ✓, :focus-visible ✓, :active ✓, :disabled ✓
|
|
108
|
+
Focus rings: ::after with --focus-border ✓
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**Link Example**:
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
Intents: --intent-primary ✓, --intent-secondary ✓
|
|
115
|
+
Sentiments: --sentiment-brand ✓, --sentiment-utility ✓
|
|
116
|
+
Sizes: --size-xsmall ✓, --size-small ✓, --size-medium ✓, --size-large ✓
|
|
117
|
+
States: :hover ✓, :focus-visible ✓, :active ✓, :visited ✓, :disabled ✓
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**Chip Example**:
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
Intents: --intent-primary ✓ (on/off variants)
|
|
124
|
+
Channels: --channel-on ✓, --channel-off ✓
|
|
125
|
+
Sizes: --size-small ✓, --size-large ✓
|
|
126
|
+
States: :hover ✓, :focus ✓, :active ✓, :disabled ✓
|
|
127
|
+
Toggle: on state (bg-dark) ✓, off state (bg-light) ✓
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Step 6: Test for Feature Parity
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
# 1. Line count comparison
|
|
134
|
+
echo "React component:"
|
|
135
|
+
wc -l packages/components-react/src/<ComponentName>/<ComponentName>.scss
|
|
136
|
+
echo "WordPress block:"
|
|
137
|
+
wc -l packages/components-wordpress/src/blocks/<component>/style.css
|
|
138
|
+
|
|
139
|
+
# Acceptable difference: ±20% (accounting for SCSS nesting/mixins vs flat CSS)
|
|
140
|
+
# If WordPress is <50% of React, styles are likely incomplete
|
|
141
|
+
|
|
142
|
+
# 2. Variant count comparison
|
|
143
|
+
echo "React variants:"
|
|
144
|
+
grep -o '\.[a-z-]*--[a-z-]*' packages/components-react/src/<ComponentName>/<ComponentName>.scss | sort -u | wc -l
|
|
145
|
+
echo "WordPress variants:"
|
|
146
|
+
grep -o '\.[a-z-]*--[a-z-]*' packages/components-wordpress/src/blocks/<component>/style.css | sort -u | wc -l
|
|
147
|
+
|
|
148
|
+
# 3. Visual comparison
|
|
149
|
+
# - Open React Storybook: packages/components-react (npm run storybook)
|
|
150
|
+
# - Open WordPress block in editor: packages/components-wordpress
|
|
151
|
+
# - Compare side-by-side for each variant
|
|
152
|
+
# - Verify colors match exactly (CSS custom properties should be identical)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Style Structure Template
|
|
156
|
+
|
|
157
|
+
Use this template as a reference for comprehensive block styling:
|
|
158
|
+
|
|
159
|
+
```css
|
|
160
|
+
/* Base component styles */
|
|
161
|
+
.tds-<component > {
|
|
162
|
+
--tds-<component>-bg: var(--interactive-primary-fill-default);
|
|
163
|
+
--tds-<component>-fg: var(--interactive-primary-text-default);
|
|
164
|
+
--tds-<component>-border: var(--interactive-primary-border-default);
|
|
165
|
+
|
|
166
|
+
display: flex; /* or block, grid, etc. */
|
|
167
|
+
align-items: center;
|
|
168
|
+
justify-content: center;
|
|
169
|
+
gap: var(--spacing-03);
|
|
170
|
+
|
|
171
|
+
border: 1px solid var(--tds-<component>-border);
|
|
172
|
+
background-color: var(--tds-<component>-bg);
|
|
173
|
+
color: var(--tds-<component>-fg);
|
|
174
|
+
|
|
175
|
+
font-family: var(--fontFamily040);
|
|
176
|
+
font-weight: var(--fontWeight050);
|
|
177
|
+
font-size: var(--fontSize030);
|
|
178
|
+
line-height: var(--fontLineHeight040);
|
|
179
|
+
|
|
180
|
+
border-radius: 0;
|
|
181
|
+
transition:
|
|
182
|
+
background-color 0.15s ease,
|
|
183
|
+
border-color 0.15s ease,
|
|
184
|
+
color 0.15s ease;
|
|
185
|
+
cursor: pointer;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/* Intent variants */
|
|
189
|
+
.tds-<component > --intent-primary {
|
|
190
|
+
--tds-<component>-bg: var(--interactive-primary-fill-default);
|
|
191
|
+
--tds-<component>-fg: var(--interactive-primary-text-default);
|
|
192
|
+
--tds-<component>-border: var(--interactive-primary-border-default);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.tds-<component > --intent-secondary {
|
|
196
|
+
--tds-<component>-bg: var(--interactive-secondary-fill-default);
|
|
197
|
+
--tds-<component>-fg: var(--interactive-secondary-text-default);
|
|
198
|
+
--tds-<component>-border: var(--interactive-secondary-border-default);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/* Size variants */
|
|
202
|
+
.tds-<component > --size-small {
|
|
203
|
+
min-height: var(--spacing-15);
|
|
204
|
+
padding: var(--spacing-03) var(--spacing-05);
|
|
205
|
+
font-size: var(--fontSize020);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.tds-<component > --size-large {
|
|
209
|
+
min-height: var(--spacing-20);
|
|
210
|
+
padding: var(--spacing-07) var(--spacing-11);
|
|
211
|
+
font-size: var(--fontSize040);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/* Behaviour variants */
|
|
215
|
+
.tds-<component > --behaviour-full {
|
|
216
|
+
width: 100%;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/* State handlers */
|
|
220
|
+
.tds-<component > :hover:not(:disabled) {
|
|
221
|
+
background-color: var(--interactive-primary-fill-hover);
|
|
222
|
+
border-color: var(--interactive-primary-border-hover);
|
|
223
|
+
color: var(--interactive-primary-text-hover);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.tds-<component > :active:not(:disabled) {
|
|
227
|
+
background-color: var(--interactive-primary-fill-pressed);
|
|
228
|
+
border-color: var(--interactive-primary-border-pressed);
|
|
229
|
+
color: var(--interactive-primary-text-pressed);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.tds-<component > :focus-visible {
|
|
233
|
+
outline: none;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.tds-<component > :focus-visible::after {
|
|
237
|
+
content: '';
|
|
238
|
+
position: absolute;
|
|
239
|
+
inset: calc(var(--spacing-03) * -1);
|
|
240
|
+
border: var(--spacing-01) solid var(--focus-border);
|
|
241
|
+
border-radius: inherit;
|
|
242
|
+
pointer-events: none;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.tds-<component > :disabled {
|
|
246
|
+
background-color: var(--interactive-disabled-b);
|
|
247
|
+
border-color: transparent;
|
|
248
|
+
color: var(--text-disabled);
|
|
249
|
+
cursor: not-allowed;
|
|
250
|
+
opacity: 0.5;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/* Nested elements */
|
|
254
|
+
.tds-<component > __label {
|
|
255
|
+
display: inline-flex;
|
|
256
|
+
align-items: center;
|
|
257
|
+
gap: var(--spacing-03);
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## Build and Verification
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
cd packages/components-wordpress
|
|
265
|
+
|
|
266
|
+
# Build the package
|
|
267
|
+
npm run build
|
|
268
|
+
|
|
269
|
+
# Verify styles compiled correctly
|
|
270
|
+
wc -l dist/blocks/<component>/style.css
|
|
271
|
+
|
|
272
|
+
# Test in WordPress
|
|
273
|
+
# 1. Copy dist/ to WordPress installation
|
|
274
|
+
# 2. Activate plugin or install NPM package
|
|
275
|
+
# 3. Add block to page
|
|
276
|
+
# 4. Test each variant combination
|
|
277
|
+
# 5. Test in patterns (server-side rendering)
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
## Maintenance
|
|
281
|
+
|
|
282
|
+
When React component styles change:
|
|
283
|
+
|
|
284
|
+
1. **Identify the change**: What CSS properties, variants, or states changed?
|
|
285
|
+
2. **Update React component**: Make the change in `packages/components-react/src/<ComponentName>/<ComponentName>.scss`
|
|
286
|
+
3. **Sync to WordPress**: Update `packages/components-wordpress/src/blocks/<component>/style.css` with the same change
|
|
287
|
+
4. **Build and test**: `npm run build` in both packages and test in WordPress
|
|
288
|
+
5. **Document the change**: Update CHANGELOG.md in both packages
|
|
289
|
+
|
|
290
|
+
## Common Styling Issues
|
|
291
|
+
|
|
292
|
+
| Issue | Cause | Solution |
|
|
293
|
+
| -------------------------------- | -------------------------------------- | --------------------------------------------------------------- |
|
|
294
|
+
| Block renders but styles missing | CSS not linked in `block.json` | Verify `"style": "file:./style.css"` in block.json |
|
|
295
|
+
| Colors don't match React | Hardcoded hex colors or wrong token | Replace with `var(--design-token)` from design system |
|
|
296
|
+
| Variants not styling | Modifier CSS not written | Compare to React, copy missing `.tds-<component>--*` selectors |
|
|
297
|
+
| Focus ring not visible | Missing `::after` pseudo-element | Add focus-visible handler with border and `var(--focus-border)` |
|
|
298
|
+
| Styles different in patterns | render.php classes don't match save.js | Verify class names exactly match between save.js and render.php |
|
|
299
|
+
| Line count too low | Styles incomplete or too minimal | Compare to React line count, should be similar (±20%) |
|
|
300
|
+
|
|
301
|
+
## Related Documentation
|
|
302
|
+
|
|
303
|
+
- [BLOCK_CREATION_CHECKLIST.md](./BLOCK_CREATION_CHECKLIST.md) - Step-by-step checklist for creating new blocks
|
|
304
|
+
- [TRANSFORMATION_GUIDE.md](./TRANSFORMATION_GUIDE.md) - Complete transformation process documentation
|
|
305
|
+
- Design tokens: `packages/tokens/README.md`
|
|
306
|
+
- Component React source: `packages/components-react/src/`
|
package/TRANSFORMATION_GUIDE.md
CHANGED
|
@@ -263,36 +263,180 @@ export default function Save({ attributes }) {
|
|
|
263
263
|
- No event handlers or dynamic behavior (it's static HTML)
|
|
264
264
|
- Can be PHP or JavaScript-rendered depending on config
|
|
265
265
|
|
|
266
|
-
### 7. **Create CSS Files**
|
|
266
|
+
### 7. **Create CSS Files — Full Parity with React**
|
|
267
267
|
|
|
268
|
-
#### `style.css` (Frontend Styles)
|
|
268
|
+
#### `style.css` (Frontend Styles) — Critical for Feature Parity
|
|
269
269
|
|
|
270
|
-
|
|
271
|
-
- Contains production CSS for the component
|
|
272
|
-
- References SCSS classes from `theme-scss` package
|
|
273
|
-
- Use CSS variables for tokens
|
|
270
|
+
The WordPress block's `style.css` **must mirror the React component's styles exactly**. This ensures visual and behavioral consistency across all implementations.
|
|
274
271
|
|
|
275
|
-
**
|
|
272
|
+
**Process**:
|
|
273
|
+
|
|
274
|
+
1. **Copy React component styles** from `packages/components-react/src/<ComponentName>/<ComponentName>.scss`
|
|
275
|
+
2. **Map all className modifiers** to CSS selectors (e.g., React `className="tds-button--size-small"` → CSS `.tds-button--size-small { ... }`)
|
|
276
|
+
3. **Preserve all CSS custom properties** for design tokens
|
|
277
|
+
4. **Include all state handlers**: hover, focus-visible, active, disabled
|
|
278
|
+
5. **Include all variants**: intent (primary, secondary, negative), size (small, medium, large), behaviour (full, hug)
|
|
279
|
+
|
|
280
|
+
**Key Principles**:
|
|
281
|
+
|
|
282
|
+
- Use **resolved design token values** for ALL properties: actual hex colors (e.g., `#005c8a`), pixel sizes (e.g., `8px`), and font names (e.g., `Roboto`)
|
|
283
|
+
- Follow BEM naming: `.tds-<component>`, `.tds-<component>--<modifier>`
|
|
284
|
+
- Include transitions for smooth state changes (usually `0.15s ease`)
|
|
285
|
+
- Focus rings should use concrete colors (e.g., `#737373`) with box-shadow pseudo-element
|
|
286
|
+
- Disabled states need opacity reduction and cursor change
|
|
287
|
+
- All pseudo-classes (`:hover`, `:focus-visible`, `:active`, `:disabled`) must match React behavior
|
|
288
|
+
|
|
289
|
+
**Example Structure** (from Button component):
|
|
276
290
|
|
|
277
291
|
```css
|
|
278
|
-
.tds
|
|
279
|
-
|
|
292
|
+
.tds-button {
|
|
293
|
+
--tds-button-bg: var(--interactive-primary-fill-default);
|
|
294
|
+
--tds-button-fg: var(--interactive-primary-text-default);
|
|
295
|
+
--tds-button-border: var(--interactive-primary-border-default);
|
|
296
|
+
|
|
297
|
+
display: flex;
|
|
298
|
+
align-items: center;
|
|
299
|
+
gap: var(--spacing-03);
|
|
300
|
+
border: 1px solid var(--tds-button-border);
|
|
301
|
+
background-color: var(--tds-button-bg);
|
|
302
|
+
color: var(--tds-button-fg);
|
|
303
|
+
font-family: Roboto;
|
|
304
|
+
font-weight: 500;
|
|
305
|
+
transition: 0.15s ease;
|
|
306
|
+
cursor: pointer;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/* Intent variants */
|
|
310
|
+
.tds-button--intent-primary {
|
|
311
|
+
--tds-button-bg: var(--interactive-primary-fill-default);
|
|
312
|
+
--tds-button-fg: var(--interactive-primary-text-default);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
.tds-button--intent-secondary {
|
|
316
|
+
--tds-button-bg: var(--interactive-secondary-fill-default);
|
|
317
|
+
--tds-button-fg: var(--interactive-secondary-text-default);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/* Size variants — RESOLVED values */
|
|
321
|
+
.tds-button--size-small {
|
|
322
|
+
min-height: 40px;
|
|
323
|
+
padding: 4px 8px;
|
|
324
|
+
font-size: 1.4rem;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
.tds-button--size-medium {
|
|
328
|
+
min-height: 48px;
|
|
329
|
+
padding: 8px 12px;
|
|
330
|
+
font-size: 1.6rem;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
.tds-button--size-large {
|
|
334
|
+
min-height: 64px;
|
|
335
|
+
padding: 12px 20px;
|
|
336
|
+
font-size: 1.8rem;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/* State handlers */
|
|
340
|
+
.tds-button:hover:not(:disabled) {
|
|
341
|
+
background-color: var(--interactive-primary-fill-hover);
|
|
342
|
+
border-color: var(--interactive-primary-border-hover);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
.tds-button:focus-visible {
|
|
346
|
+
outline: none;
|
|
280
347
|
}
|
|
281
348
|
|
|
282
|
-
.tds
|
|
283
|
-
|
|
349
|
+
.tds-button:focus-visible::after {
|
|
350
|
+
content: '';
|
|
351
|
+
position: absolute;
|
|
352
|
+
inset: calc(var(--spacing-03) * -1);
|
|
353
|
+
border: var(--spacing-01) solid var(--focus-border);
|
|
354
|
+
pointer-events: none;
|
|
284
355
|
}
|
|
285
356
|
|
|
286
|
-
.tds
|
|
357
|
+
.tds-button:disabled {
|
|
358
|
+
background-color: var(--interactive-disabled-b);
|
|
359
|
+
color: var(--text-disabled);
|
|
360
|
+
cursor: not-allowed;
|
|
361
|
+
opacity: 0.5;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/* Behaviour variants */
|
|
365
|
+
.tds-button--behaviour-full {
|
|
287
366
|
width: 100%;
|
|
288
367
|
}
|
|
289
368
|
```
|
|
290
369
|
|
|
291
|
-
|
|
370
|
+
**Validation Checklist**:
|
|
371
|
+
|
|
372
|
+
✓ All CSS custom properties match design tokens
|
|
373
|
+
✓ BEM naming is consistent (no underscores, only hyphens)
|
|
374
|
+
✓ All variant combinations present (intent × size × behaviour)
|
|
375
|
+
✓ State handlers for hover, focus, active, disabled
|
|
376
|
+
✓ Line count similar to React component (indicates parity)
|
|
377
|
+
✓ Transitions preserved
|
|
378
|
+
✓ No hardcoded hex colors (all use CSS variables)
|
|
379
|
+
|
|
380
|
+
#### `style-editor.css` (Editor Styles) — Optional
|
|
292
381
|
|
|
293
382
|
- Only loaded in WordPress editor
|
|
294
383
|
- Can override styles for better editing experience
|
|
295
384
|
- Optional — only add if editor preview differs significantly from frontend
|
|
385
|
+
- Example: Adjust padding/margins for better visual feedback in editor
|
|
386
|
+
- Example: Add borders to clarify block boundaries during editing
|
|
387
|
+
|
|
388
|
+
#### CSS Variables and theme-scss Integration
|
|
389
|
+
|
|
390
|
+
**IMPORTANT**: WordPress blocks use **resolved design token values** — actual hex colors, pixel measurements, and font definitions are compiled directly into the CSS. This means blocks are completely self-contained and do not depend on CSS custom properties from theme-scss.
|
|
391
|
+
|
|
392
|
+
**Key Approach**:
|
|
393
|
+
|
|
394
|
+
```
|
|
395
|
+
Design Tokens (theme-scss)
|
|
396
|
+
↓
|
|
397
|
+
Resolved to concrete values
|
|
398
|
+
↓
|
|
399
|
+
Hardcoded into WordPress block CSS
|
|
400
|
+
↓
|
|
401
|
+
No runtime CSS variable dependencies needed
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
**Why Resolved Values**:
|
|
405
|
+
|
|
406
|
+
- Blocks work reliably in any WordPress theme context
|
|
407
|
+
- No need for theme-scss CSS variables to be loaded
|
|
408
|
+
- Consistent appearance across installations
|
|
409
|
+
- Simpler debugging (actual colors/sizes visible in DevTools)
|
|
410
|
+
|
|
411
|
+
**Value Reference**:
|
|
412
|
+
|
|
413
|
+
| Value Type | Examples | Notes |
|
|
414
|
+
| ----------------- | ----------------------------------------------- | ------------------------------------- |
|
|
415
|
+
| **Colors (hex)** | `#005c8a`, `#ffffff`, `#737373` | All interactive colors use hex values |
|
|
416
|
+
| **Spacing (px)** | `4px`, `8px`, `12px`, `16px`, `24px` | All spacing uses pixel units |
|
|
417
|
+
| **Typography** | `-apple-system, BlinkMacSystemFont, "Segoe UI"` | System fonts specified directly |
|
|
418
|
+
| **Font weight** | `400`, `500`, `700` | Numeric weight values |
|
|
419
|
+
| **Font size** | `0.75rem`, `1rem`, `1.125rem` | REMs for scalability |
|
|
420
|
+
| **Border radius** | `0`, `2px`, `4px`, `9999px` | Explicit pixel or full values |
|
|
421
|
+
|
|
422
|
+
**Example Block CSS** (resolved values):
|
|
423
|
+
|
|
424
|
+
```css
|
|
425
|
+
.tds-button {
|
|
426
|
+
/* NO CSS variables — all values are concrete */
|
|
427
|
+
--tds-button-bg: #005c8a; /* Direct hex, not var(--color-*) */
|
|
428
|
+
--tds-button-fg: #ffffff; /* No fallbacks needed */
|
|
429
|
+
|
|
430
|
+
gap: 4px; /* Direct pixel, not var(--spacing-03) */
|
|
431
|
+
padding: 8px 12px; /* Resolved from design tokens */
|
|
432
|
+
|
|
433
|
+
font-family: Roboto;
|
|
434
|
+
font-weight: 500; /* Direct numeric value */
|
|
435
|
+
font-size: 1.6rem; /* Direct resolved size from theme-scss */
|
|
436
|
+
}
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
**See Also**: [SCSS_VARIABLES_REFERENCE.md](./SCSS_VARIABLES_REFERENCE.md) for complete mapping of all resolved values used across all blocks.
|
|
296
440
|
|
|
297
441
|
### 8. **Create/Update Utility Functions**
|
|
298
442
|
|
|
@@ -343,6 +487,32 @@ import './blocks/button/index.js';
|
|
|
343
487
|
import './blocks/<new-component>/index.js';
|
|
344
488
|
```
|
|
345
489
|
|
|
490
|
+
### 10. **Build and Verify**
|
|
491
|
+
|
|
492
|
+
**After completing all files**:
|
|
493
|
+
|
|
494
|
+
```bash
|
|
495
|
+
# Build the package
|
|
496
|
+
cd packages/components-wordpress
|
|
497
|
+
npm run build
|
|
498
|
+
|
|
499
|
+
# Verify block appears in dist/ with all styles
|
|
500
|
+
ls dist/blocks/<new-component>/
|
|
501
|
+
# Expected: block.json, index.js, edit.js, save.js, render.php, style.css, style-editor.css
|
|
502
|
+
|
|
503
|
+
# Check line count of compiled style.css (should be substantial)
|
|
504
|
+
wc -l dist/blocks/<new-component>/style.css
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
**Build verification checklist**:
|
|
508
|
+
|
|
509
|
+
✓ `dist/blocks/<new-component>/render.php` exists (server-side rendering)
|
|
510
|
+
✓ `dist/blocks/<new-component>/style.css` contains all styles (50+ lines for complex components)
|
|
511
|
+
✓ `block.json` has `"render": "file:./render.php"` reference
|
|
512
|
+
✓ No TypeScript/build errors
|
|
513
|
+
✓ Block registrations all successful
|
|
514
|
+
✓ CSS custom properties properly compiled
|
|
515
|
+
|
|
346
516
|
---
|
|
347
517
|
|
|
348
518
|
## Prop-to-Attribute Conversion Reference
|
|
@@ -593,6 +763,97 @@ Suggested order for consistent, dependency-aware transformation:
|
|
|
593
763
|
|
|
594
764
|
---
|
|
595
765
|
|
|
766
|
+
## Testing & Validation Checklist
|
|
767
|
+
|
|
768
|
+
### Pre-Build Testing
|
|
769
|
+
|
|
770
|
+
- [ ] **Styling Parity**: Run visual comparison of React component vs WordPress block
|
|
771
|
+
- Open React component in Storybook: `packages/components-react/src/<ComponentName>`
|
|
772
|
+
- Compare all variants side-by-side (intent, size, behaviour)
|
|
773
|
+
- Verify colors match exactly (should be identical CSS custom properties)
|
|
774
|
+
- Check spacing and padding are correct
|
|
775
|
+
- Test state transitions (hover, focus, active, disabled)
|
|
776
|
+
|
|
777
|
+
- [ ] **CSS Validation**:
|
|
778
|
+
- Line count check: `wc -l src/blocks/<component>/style.css` — should be substantial (50+ for complex components like Button/Link, 20+ for simple components)
|
|
779
|
+
- Compare to React component: `wc -l packages/components-react/src/<ComponentName>/<ComponentName>.scss`
|
|
780
|
+
- Should be similar complexity (±20% acceptable difference)
|
|
781
|
+
- No hardcoded hex colors: `grep -E '#[0-9a-fA-F]{6}' src/blocks/<component>/style.css` — should be zero results (all colors use CSS variables)
|
|
782
|
+
- Validate JSON: `npm run test`
|
|
783
|
+
|
|
784
|
+
- [ ] **Render Callback Testing**:
|
|
785
|
+
- Verify `render.php` exists for server-side pattern rendering
|
|
786
|
+
- Test in WordPress patterns: create pattern with block variants
|
|
787
|
+
- Confirm HTML output matches `save.js` structure exactly
|
|
788
|
+
- Test with different attribute combinations in patterns
|
|
789
|
+
|
|
790
|
+
### Post-Build Testing
|
|
791
|
+
|
|
792
|
+
```bash
|
|
793
|
+
# Navigate to packages/components-wordpress
|
|
794
|
+
cd packages/components-wordpress
|
|
795
|
+
|
|
796
|
+
# Run build
|
|
797
|
+
npm run build
|
|
798
|
+
|
|
799
|
+
# Verify compiled styles
|
|
800
|
+
wc -l dist/blocks/<component>/style.css # Check line count (should match src/)
|
|
801
|
+
grep -c 'tds-<component>--' dist/blocks/<component>/style.css # Count modifiers
|
|
802
|
+
|
|
803
|
+
# Verify render.php exists
|
|
804
|
+
ls dist/blocks/<component>/render.php
|
|
805
|
+
```
|
|
806
|
+
|
|
807
|
+
- [ ] **Compiled Output**:
|
|
808
|
+
- All variant CSS is present in `dist/blocks/<component>/style.css`
|
|
809
|
+
- `render.php` file exists and is copied to dist
|
|
810
|
+
- `block.json` contains `"render": "file:./render.php"`
|
|
811
|
+
|
|
812
|
+
- [ ] **WordPress Installation Testing**:
|
|
813
|
+
1. Build package: `npm run build`
|
|
814
|
+
2. Copy `dist/` to WordPress installation
|
|
815
|
+
3. Activate plugin or install NPM package
|
|
816
|
+
4. Add block to page in editor
|
|
817
|
+
5. Test each variant combination:
|
|
818
|
+
- Different intents (primary, secondary, negative)
|
|
819
|
+
- Different sizes (small, medium, large)
|
|
820
|
+
- Different states (hover by mouse, focus by keyboard, active by click, disabled)
|
|
821
|
+
6. Test in patterns (server-side rendering):
|
|
822
|
+
- Create pattern with block variants
|
|
823
|
+
- Confirm HTML renders correctly before editor JS loads
|
|
824
|
+
- Verify CSS loads properly
|
|
825
|
+
7. Test responsive breakpoints (if applicable):
|
|
826
|
+
- Resize browser window
|
|
827
|
+
- Verify layout adapts correctly
|
|
828
|
+
8. Test accessibility:
|
|
829
|
+
- Keyboard navigation through interactive elements
|
|
830
|
+
- Screen reader announcements
|
|
831
|
+
- Focus ring visibility
|
|
832
|
+
|
|
833
|
+
### Styling Feature Parity Validation
|
|
834
|
+
|
|
835
|
+
For components with variants (Button, Link, Chip, etc.), verify complete coverage:
|
|
836
|
+
|
|
837
|
+
```
|
|
838
|
+
Component: Button
|
|
839
|
+
Intents: primary ✓, secondary ✓, negative ✓
|
|
840
|
+
Sizes: small ✓, medium ✓, large ✓
|
|
841
|
+
Behaviour: hug ✓, full ✓
|
|
842
|
+
States: base ✓, hover ✓, focus ✓, active ✓, disabled ✓
|
|
843
|
+
CSS Classes: .tds-button--intent-primary ✓, .tds-button--size-small ✓, etc.
|
|
844
|
+
```
|
|
845
|
+
|
|
846
|
+
- [ ] All intent variants styled (must match React exactly)
|
|
847
|
+
- [ ] All size variants styled (must match React exactly)
|
|
848
|
+
- [ ] All behaviour variants styled (must match React exactly)
|
|
849
|
+
- [ ] All state handlers present (hover, focus-visible, active, disabled)
|
|
850
|
+
- [ ] CSS custom properties for all tokens (colors, spacing, typography)
|
|
851
|
+
- [ ] Focus rings use consistent `var(--focus-border)`
|
|
852
|
+
- [ ] Transitions preserved from React component
|
|
853
|
+
- [ ] No visual regressions compared to React
|
|
854
|
+
|
|
855
|
+
---
|
|
856
|
+
|
|
596
857
|
## Debugging & Troubleshooting
|
|
597
858
|
|
|
598
859
|
### Block not appearing in Gutenberg?
|
|
@@ -607,6 +868,10 @@ Suggested order for consistent, dependency-aware transformation:
|
|
|
607
868
|
1. Verify CSS files in `block.json` point to correct paths
|
|
608
869
|
2. Check class names match SCSS in `theme-scss`
|
|
609
870
|
3. Ensure theme-scss is installed: `npm install @times-design-system/theme-scss`
|
|
871
|
+
4. **Styling gap**: Compare `style.css` line count to React component — if significantly lower, styles may be incomplete
|
|
872
|
+
- Check for all variant modifiers: grep `.tds-<component>--` src/blocks/<component>/style.css
|
|
873
|
+
- Verify state handlers (hover, focus, active, disabled) are present
|
|
874
|
+
- Copy missing styles from React component
|
|
610
875
|
|
|
611
876
|
### Attributes not saving?
|
|
612
877
|
|
|
@@ -614,6 +879,14 @@ Suggested order for consistent, dependency-aware transformation:
|
|
|
614
879
|
2. Ensure `setAttributes({ key: value })` is called correctly
|
|
615
880
|
3. Check attribute types match intended use
|
|
616
881
|
|
|
882
|
+
### Block doesn't render in patterns?
|
|
883
|
+
|
|
884
|
+
1. Verify `render.php` exists in block directory
|
|
885
|
+
2. Check `block.json` has `"render": "file:./render.php"` reference
|
|
886
|
+
3. Rebuild with `npm run build` to copy render.php to dist/
|
|
887
|
+
4. Verify render.php HTML structure matches save.js exactly
|
|
888
|
+
5. Test in WordPress patterns (not just editor)
|
|
889
|
+
|
|
617
890
|
---
|
|
618
891
|
|
|
619
892
|
## References
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
/**
|
|
3
|
+
* Ad Container Block Render Callback
|
|
4
|
+
*
|
|
5
|
+
* Renders the Ad Container block on the frontend.
|
|
6
|
+
*
|
|
7
|
+
* @package TimesDesignSystem\ComponentsWordPress
|
|
8
|
+
* @var array $attributes Block attributes
|
|
9
|
+
* @var string $content Block content
|
|
10
|
+
* @var WP_Block $block Block instance
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
$type = isset($attributes['type']) ? sanitize_text_field($attributes['type']) : '';
|
|
14
|
+
$slot_id = isset($attributes['slotID']) ? sanitize_text_field($attributes['slotID']) : '';
|
|
15
|
+
|
|
16
|
+
$class_name = 'tds-ad-container';
|
|
17
|
+
if ($type) {
|
|
18
|
+
$class_name .= ' tds-ad-container--' . sanitize_html_class($type);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
$wrapper_attrs = get_block_wrapper_attributes(['class' => $class_name]);
|
|
22
|
+
?>
|
|
23
|
+
|
|
24
|
+
<div
|
|
25
|
+
<?php echo wp_kses_post($wrapper_attrs); ?>
|
|
26
|
+
<?php echo $slot_id ? 'data-slot-id="' . esc_attr($slot_id) . '"' : ''; ?>
|
|
27
|
+
></div>
|