@clipboard-health/ai-rules 2.27.0 → 2.29.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/package.json +1 -1
- package/rules/frontend/styling.md +91 -0
package/package.json
CHANGED
|
@@ -44,3 +44,94 @@ Use `rem` for fonts/heights (scales with user zoom), spacing indices for padding
|
|
|
44
44
|
|
|
45
45
|
- ✅ Use full names: `padding`, `paddingX`, `marginY`
|
|
46
46
|
- ❌ Avoid abbreviations: `p`, `px`, `my`
|
|
47
|
+
|
|
48
|
+
## Dimensions
|
|
49
|
+
|
|
50
|
+
Always specify explicit `width`/`height`/`minWidth`/`maxWidth`/`minHeight`/`maxHeight` on elements whose layout should not depend on their content's intrinsic size (images, illustrations, fixed cards, skeletons). Use unit choices in this order:
|
|
51
|
+
|
|
52
|
+
1. **Theme-spacing tokens** (`theme.spacing(n)`) — preferred for any dimension within the spacing scale. The CBH theme uses an **array-based** spacing scale, so `n` is an **index, not a multiplier**:
|
|
53
|
+
|
|
54
|
+
| n | px |
|
|
55
|
+
| --- | --- |
|
|
56
|
+
| 0 | 0 |
|
|
57
|
+
| 1 | 4 |
|
|
58
|
+
| 2 | 6 |
|
|
59
|
+
| 3 | 8 |
|
|
60
|
+
| 4 | 12 |
|
|
61
|
+
| 5 | 16 |
|
|
62
|
+
| 6 | 20 |
|
|
63
|
+
| 7 | 24 |
|
|
64
|
+
| 8 | 32 |
|
|
65
|
+
| 9 | 40 |
|
|
66
|
+
| 10 | 48 |
|
|
67
|
+
| 11 | 56 |
|
|
68
|
+
| 12 | 64 |
|
|
69
|
+
|
|
70
|
+
Indices > 12 are out of range and resolve to `undefined` (which serializes to `0`/empty — usually rendered as `0%`). Max representable size is **64 px**.
|
|
71
|
+
|
|
72
|
+
2. **`rem`** — for any dimension > 64 px (e.g. hero illustrations, full-page graphics). Scales with user zoom. Examples: `"16rem"` (≈256px), `"11rem"` (≈176px).
|
|
73
|
+
|
|
74
|
+
3. **Percentages / `vw` / `vh`** — for responsive layouts that should track the viewport or parent container.
|
|
75
|
+
|
|
76
|
+
4. **Raw pixel strings** (`"258px"`) — only as a last resort with justification.
|
|
77
|
+
|
|
78
|
+
**MUI gotcha:** in `sx`, numeric `width`/`height` values are treated as **raw pixels**, not `theme.spacing(n)`. The spacing shortcut only applies to `padding`/`margin`/`gap`. To get theme spacing on a dimension, call `theme.spacing()` explicitly via the `sx` callback.
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
// ✅ Correct — within the spacing scale, via callback
|
|
82
|
+
<Box sx={(theme) => ({ width: theme.spacing(8), height: theme.spacing(8) })} />
|
|
83
|
+
|
|
84
|
+
// ✅ Correct — larger than the spacing scale, use rem
|
|
85
|
+
<Box sx={{ width: "16rem", height: "11rem" }} />
|
|
86
|
+
|
|
87
|
+
// ❌ Wrong — out of range (max index is 12). theme.spacing(32) returns undefined → renders as 0
|
|
88
|
+
<Box sx={(theme) => ({ width: theme.spacing(32), height: theme.spacing(22) })} />
|
|
89
|
+
|
|
90
|
+
// ❌ Wrong — renders as 8×8 px (raw pixels), not theme.spacing(8) = 32px
|
|
91
|
+
<Box sx={{ width: 8, height: 8 }} />
|
|
92
|
+
|
|
93
|
+
// ❌ Wrong — raw pixel string without justification
|
|
94
|
+
<Box sx={{ width: "258px", height: "176px" }} />
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
For `padding`/`margin`/`gap`, numeric values pass through the spacing scale automatically — `padding: 4` is `12px`. That auto-resolution does **not** happen for `width`/`height`. The asymmetry is a MUI quirk, not a CBH choice.
|
|
98
|
+
|
|
99
|
+
## Images
|
|
100
|
+
|
|
101
|
+
Images (`<img>`, `<Box component="img">`, `<Image>`, MUI `<Avatar>` with an image `src`, SVG components rendered from asset files) **must specify both an explicit `width` and `height`** per the Dimensions rule above. Pair with `objectFit: "contain"` (or `"cover"`) so a swapped asset stays inside the fixed box.
|
|
102
|
+
|
|
103
|
+
**Why:** without explicit dimensions, the rendered box collapses to the source asset's intrinsic aspect ratio. Swapping the asset (e.g. a redesigned PNG/SVG with different dimensions) then shifts surrounding layout — in the worst case, pushing content off the viewport.
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
// ✅ Correct — small icon, theme spacing fits
|
|
107
|
+
<Image
|
|
108
|
+
src="/assets/icons/check.svg"
|
|
109
|
+
alt="Verified"
|
|
110
|
+
sx={(theme) => ({
|
|
111
|
+
width: theme.spacing(6),
|
|
112
|
+
height: theme.spacing(6),
|
|
113
|
+
objectFit: "contain",
|
|
114
|
+
})}
|
|
115
|
+
/>
|
|
116
|
+
|
|
117
|
+
// ✅ Correct — hero illustration, larger than spacing scale, use rem
|
|
118
|
+
<Image
|
|
119
|
+
src="/assets/images/work-badge.svg"
|
|
120
|
+
alt="Create Work Badge"
|
|
121
|
+
width="16rem"
|
|
122
|
+
height="11rem"
|
|
123
|
+
sx={{ objectFit: "contain" }}
|
|
124
|
+
/>
|
|
125
|
+
|
|
126
|
+
// ❌ Wrong — width/height props on Image flow into sx as raw pixels (8×8, not theme.spacing(8))
|
|
127
|
+
<Image src="..." alt="..." width={8} height={8} />
|
|
128
|
+
|
|
129
|
+
// ❌ Wrong — theme.spacing(32) out of range, renders as 0
|
|
130
|
+
<Image src="..." alt="..." sx={(theme) => ({ width: theme.spacing(32) })} />
|
|
131
|
+
|
|
132
|
+
// ❌ Wrong — height tracks the source asset's aspect ratio
|
|
133
|
+
<Image src="..." alt="..." width="258px" height="auto" />
|
|
134
|
+
|
|
135
|
+
// ❌ Wrong — no dimensions; layout depends entirely on the asset's intrinsic size
|
|
136
|
+
<Image src="/assets/images/work-badge.png" alt="Work Badge" />
|
|
137
|
+
```
|