@clipboard-health/ai-rules 2.27.0 → 2.28.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clipboard-health/ai-rules",
3
- "version": "2.27.0",
3
+ "version": "2.28.0",
4
4
  "description": "Pre-built AI agent rules for consistent coding standards.",
5
5
  "keywords": [
6
6
  "ai",
@@ -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
+ ```