@pnx-mixtape/mxds 0.1.0-rc.4 → 0.1.0-rc.5
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/.claude/skills/create-mxds-component/SKILL.md +77 -0
- package/DESIGN.md +595 -0
- package/README.md +1 -2
- package/dist/build/accordion.entry.js +2 -1
- package/dist/build/autocomplete.entry.js +1 -1
- package/dist/build/back-to-top.css +1 -0
- package/dist/build/back-to-top.entry.js +1 -0
- package/dist/build/button.css +1 -1
- package/dist/build/card.css +1 -1
- package/dist/build/chunks/Accordion-D2wp8hBG.js +44 -0
- package/dist/build/chunks/AccordionGroup-BYtoAFxg.js +47 -0
- package/dist/build/chunks/AccordionMobile-D3sTeiM5.js +26 -0
- package/dist/build/chunks/Autocomplete-kRo8RNbC.js +135 -0
- package/dist/build/chunks/BackToTop-4M-Wl7z3.js +64 -0
- package/dist/build/chunks/ClosableAlert-yy84gae6.js +55 -0
- package/dist/build/chunks/CookieConsent-wd8gQ2Oe.js +18 -0
- package/dist/build/chunks/Dialog-DWoXW-7Z.js +48 -0
- package/dist/build/chunks/DropMenu-CSTZq0Zt.js +68 -0
- package/dist/build/chunks/FeedbackForm-BochXEDx.js +64 -0
- package/dist/build/chunks/Gallery-Rkfv7Mbb.js +165 -0
- package/dist/build/chunks/GlobalToggle-CzlkpLpc.js +82 -0
- package/dist/build/chunks/InPageNavigation-D1Kk-rCm.js +86 -0
- package/dist/build/chunks/Navigation-CDQnN61h.js +147 -0
- package/dist/build/chunks/Popover-CiTAftNU.js +30 -0
- package/dist/build/chunks/Sticky-DoVndENm.js +51 -0
- package/dist/build/chunks/Tabs-BDlxCDNh.js +160 -0
- package/dist/build/chunks/Translate-bmM38j6z.js +46 -0
- package/dist/build/chunks/cookie-compliance-CG6x-TcV.js +119 -0
- package/dist/build/chunks/enums-BrHxPZZ_.js +36 -0
- package/dist/build/chunks/polyfills-D-qh1a_2.js +33 -0
- package/dist/build/chunks/utilities-Dd-08SW6.js +255 -0
- package/dist/build/content-block.css +1 -1
- package/dist/build/cookie-consent.entry.js +1 -1
- package/dist/build/dialog.entry.js +1 -1
- package/dist/build/drop-menu.entry.js +1 -1
- package/dist/build/elements.js +147 -1
- package/dist/build/feedback-form.css +1 -0
- package/dist/build/feedback-form.entry.js +1 -0
- package/dist/build/filters.entry.js +145 -1
- package/dist/build/gallery.entry.js +1 -1
- package/dist/build/global-alert.entry.js +1 -1
- package/dist/build/header.css +1 -1
- package/dist/build/header.entry.js +1 -1
- package/dist/build/icon.css +1 -1
- package/dist/build/in-page-navigation.entry.js +1 -1
- package/dist/build/navigation.entry.js +1 -1
- package/dist/build/pagination.css +1 -1
- package/dist/build/popover.entry.js +1 -1
- package/dist/build/react.js +2270 -1
- package/dist/build/sticky.entry.js +1 -1
- package/dist/build/tabs.entry.js +1 -1
- package/dist/build/translate.css +1 -0
- package/dist/build/translate.entry.js +1 -0
- package/dist/build/utility-list.entry.js +53 -1
- package/dist/types/Component/BackToTop/BackToTop.d.ts +20 -0
- package/dist/types/Component/BackToTop/BackToTop.d.ts.map +1 -0
- package/dist/types/Component/BackToTop/Elements/BackToTop.d.ts +27 -0
- package/dist/types/Component/BackToTop/Elements/BackToTop.d.ts.map +1 -0
- package/dist/types/Component/BackToTop/back-to-top.entry.d.ts +2 -0
- package/dist/types/Component/BackToTop/back-to-top.entry.d.ts.map +1 -0
- package/dist/types/Component/DropMenu/DropMenu.d.ts +2 -1
- package/dist/types/Component/DropMenu/DropMenu.d.ts.map +1 -1
- package/dist/types/Component/Tabs/Components/TabDropMenu.d.ts.map +1 -1
- package/dist/types/Component/Tabs/Elements/Tabs.d.ts.map +1 -1
- package/dist/types/Component/Translate/Elements/Translate.d.ts +23 -0
- package/dist/types/Component/Translate/Elements/Translate.d.ts.map +1 -0
- package/dist/types/Component/Translate/Translate.d.ts +37 -0
- package/dist/types/Component/Translate/Translate.d.ts.map +1 -0
- package/dist/types/Component/Translate/languages.d.ts +11 -0
- package/dist/types/Component/Translate/languages.d.ts.map +1 -0
- package/dist/types/Component/Translate/translate.entry.d.ts +2 -0
- package/dist/types/Component/Translate/translate.entry.d.ts.map +1 -0
- package/dist/types/Form/FeedbackForm/Elements/FeedbackForm.d.ts +37 -0
- package/dist/types/Form/FeedbackForm/Elements/FeedbackForm.d.ts.map +1 -0
- package/dist/types/Form/FeedbackForm/FeedbackForm.d.ts +36 -0
- package/dist/types/Form/FeedbackForm/FeedbackForm.d.ts.map +1 -0
- package/dist/types/Form/FeedbackForm/feedback-form.entry.d.ts +2 -0
- package/dist/types/Form/FeedbackForm/feedback-form.entry.d.ts.map +1 -0
- package/dist/types/Utility/Elements/google-translate-widget.d.ts +91 -0
- package/dist/types/Utility/Elements/google-translate-widget.d.ts.map +1 -0
- package/dist/types/Utility/utilities.d.ts +2 -0
- package/dist/types/Utility/utilities.d.ts.map +1 -1
- package/dist/types/elements.d.ts +3 -0
- package/dist/types/elements.d.ts.map +1 -1
- package/dist/types/enums.d.ts +3 -1
- package/dist/types/enums.d.ts.map +1 -1
- package/dist/types/react.d.ts +3 -0
- package/dist/types/react.d.ts.map +1 -1
- package/docs/API.md +1215 -0
- package/docs/CSSGuidelines.md +891 -0
- package/docs/CodeQuality.md +940 -0
- package/docs/ComponentArchitecture.md +714 -0
- package/docs/Contributing.md +626 -0
- package/docs/MemoryManagement.md +598 -0
- package/docs/Migrations.md +6 -0
- package/docs/README.md +100 -0
- package/docs/Security.md +444 -0
- package/docs/Testing.md +739 -0
- package/docs/Troubleshooting.md +674 -0
- package/package.json +6 -3
- package/src/Atom/Button/Button.tsx +1 -1
- package/src/Atom/Button/__snapshots__/Button.stories.tsx.snap +2 -2
- package/src/Atom/Button/_buttons.css +1 -0
- package/src/Atom/Icon/README.md +1 -1
- package/src/Atom/Icon/_icon.css +1 -0
- package/src/Atom/Icon/images/thumb.svg +1 -0
- package/src/Atom/Media/__snapshots__/Media.stories.ts.snap +4 -9
- package/src/Atom/Media/media.twig +4 -5
- package/src/Component/BackToTop/BackToTop.stories.ts +127 -0
- package/src/Component/BackToTop/BackToTop.stories.tsx +53 -0
- package/src/Component/BackToTop/BackToTop.tsx +120 -0
- package/src/Component/BackToTop/Elements/BackToTop.ts +119 -0
- package/src/Component/BackToTop/README.md +164 -0
- package/src/Component/BackToTop/__snapshots__/BackToTop.stories.ts.snap +24 -0
- package/src/Component/BackToTop/__snapshots__/BackToTop.stories.tsx.snap +3 -0
- package/src/Component/BackToTop/back-to-top.css +70 -0
- package/src/Component/BackToTop/back-to-top.entry.js +1 -0
- package/src/Component/BackToTop/back-to-top.twig +26 -0
- package/src/Component/Card/Components/CardContent.tsx +1 -1
- package/src/Component/Card/Components/CardMedia.tsx +1 -1
- package/src/Component/Card/__snapshots__/Card.stories.tsx.snap +3 -3
- package/src/Component/Card/card.css +1 -1
- package/src/Component/Carousel/Carousel.stories.ts +66 -36
- package/src/Component/Carousel/__snapshots__/Carousel.stories.tsx.snap +5 -5
- package/src/Component/ContentBlock/Components/ContentBlockMedia.tsx +1 -1
- package/src/Component/ContentBlock/content-block.css +1 -1
- package/src/Component/DropMenu/DropMenu.stories.ts +21 -2
- package/src/Component/DropMenu/DropMenu.tsx +14 -4
- package/src/Component/DropMenu/__snapshots__/DropMenu.stories.ts.snap +3 -3
- package/src/Component/DropMenu/__snapshots__/DropMenu.stories.tsx.snap +2 -2
- package/src/Component/DropMenu/drop-menu.twig +7 -1
- package/src/Component/HeroBanner/__snapshots__/HeroBanner.stories.tsx.snap +1 -1
- package/src/Component/InPageNavigation/__snapshots__/InPageNavigation.stories.ts.snap +48 -13
- package/src/Component/InPageNavigation/in-page-navigation.twig +4 -6
- package/src/Component/ListItem/Components/ListItemMedia.tsx +1 -1
- package/src/Component/Pagination/Pagination.stories.ts +57 -27
- package/src/Component/Pagination/pagination.css +16 -7
- package/src/Component/Popover/__snapshots__/Popover.stories.tsx.snap +3 -3
- package/src/Component/Tabs/Components/TabDropMenu.tsx +7 -1
- package/src/Component/Tabs/Elements/Tabs.ts +13 -3
- package/src/Component/Tabs/Tabs.stories.ts +33 -12
- package/src/Component/Tabs/Tabs.stories.tsx +28 -14
- package/src/Component/Tabs/__snapshots__/Tabs.stories.ts.snap +3 -3
- package/src/Component/Tabs/__snapshots__/Tabs.stories.tsx.snap +2 -2
- package/src/Component/Translate/Elements/Translate.ts +76 -0
- package/src/Component/Translate/README.md +283 -0
- package/src/Component/Translate/Translate.stories.ts +87 -0
- package/src/Component/Translate/Translate.stories.tsx +77 -0
- package/src/Component/Translate/Translate.tsx +213 -0
- package/src/Component/Translate/__snapshots__/Translate.stories.ts.snap +18 -0
- package/src/Component/Translate/__snapshots__/Translate.stories.tsx.snap +3 -0
- package/src/Component/Translate/languages.ts +39 -0
- package/src/Component/Translate/translate.css +62 -0
- package/src/Component/Translate/translate.entry.js +1 -0
- package/src/Component/Translate/translate.twig +44 -0
- package/src/Form/Autocomplete/Autocomplete.stories.tsx +2 -1
- package/src/Form/FeedbackForm/Elements/FeedbackForm.ts +122 -0
- package/src/Form/FeedbackForm/FeedbackForm.stories.ts +99 -0
- package/src/Form/FeedbackForm/FeedbackForm.stories.tsx +73 -0
- package/src/Form/FeedbackForm/FeedbackForm.tsx +196 -0
- package/src/Form/FeedbackForm/README.md +233 -0
- package/src/Form/FeedbackForm/__snapshots__/FeedbackForm.stories.ts.snap +125 -0
- package/src/Form/FeedbackForm/__snapshots__/FeedbackForm.stories.tsx.snap +5 -0
- package/src/Form/FeedbackForm/feedback-form.css +74 -0
- package/src/Form/FeedbackForm/feedback-form.entry.js +1 -0
- package/src/Form/FeedbackForm/feedback-form.twig +80 -0
- package/src/Layout/Header/Components/GlobalToggle.tsx +1 -1
- package/src/Layout/Header/Header.stories.ts +53 -48
- package/src/Layout/Header/Header.stories.tsx +35 -22
- package/src/Layout/Header/Header.tsx +2 -2
- package/src/Layout/Header/__snapshots__/Header.stories.ts.snap +14 -10
- package/src/Layout/Header/__snapshots__/Header.stories.tsx.snap +2 -2
- package/src/Layout/Header/_toggles.css +7 -7
- package/src/Layout/Page/__snapshots__/Page.stories.tsx.snap +1 -1
- package/src/Utility/Elements/google-translate-widget.ts +293 -0
- package/src/Utility/global.d.ts +4 -0
- package/src/Utility/utilities.ts +2 -0
- package/src/elements.ts +3 -0
- package/src/enums.ts +2 -0
- package/src/react.ts +3 -0
- package/dist/build/chunks/Accordion-GN0sME4W.js +0 -1
- package/dist/build/chunks/AccordionGroup-Bzqp5__q.js +0 -1
- package/dist/build/chunks/AccordionMobile-DG5jSe8Q.js +0 -1
- package/dist/build/chunks/Autocomplete-B4RtkAyh.js +0 -1
- package/dist/build/chunks/ClosableAlert-DEhj8Nxm.js +0 -1
- package/dist/build/chunks/CookieConsent-BkCMRVST.js +0 -1
- package/dist/build/chunks/Dialog-Haahn-Yn.js +0 -1
- package/dist/build/chunks/DropMenu-1baQc8Yc.js +0 -1
- package/dist/build/chunks/Gallery-D8NGXy2Q.js +0 -1
- package/dist/build/chunks/GlobalToggle-B52fdBQu.js +0 -1
- package/dist/build/chunks/InPageNavigation-Dyn2TR-N.js +0 -1
- package/dist/build/chunks/Navigation-hOrfLq7h.js +0 -1
- package/dist/build/chunks/Popover-Cxg-s5Jn.js +0 -1
- package/dist/build/chunks/Sticky-BGCuuEJK.js +0 -1
- package/dist/build/chunks/Tabs-DYy1YTdU.js +0 -15
- package/dist/build/chunks/cookie-compliance-Bbm-0vpj.js +0 -1
- package/dist/build/chunks/enums-DLV_1NAf.js +0 -1
- package/dist/build/chunks/polyfills-C7pRt54M.js +0 -1
- package/dist/build/chunks/utilities-_Bq3TxAQ.js +0 -1
|
@@ -0,0 +1,891 @@
|
|
|
1
|
+
# CSS Guidelines
|
|
2
|
+
|
|
3
|
+
CSS/Styling conventions for the Mixtape Design System.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [PostCSS Pipeline](#postcss-pipeline)
|
|
8
|
+
- [BEM Naming](#bem-naming)
|
|
9
|
+
- [File Organization](#file-organization)
|
|
10
|
+
- [Custom Media](#custom-media)
|
|
11
|
+
- [Design Tokens](#design-tokens)
|
|
12
|
+
- [Custom Property Fallbacks](#custom-property-fallbacks)
|
|
13
|
+
- [Color Scheme (light-dark)](#color-scheme-light-dark)
|
|
14
|
+
- [Logical Properties](#logical-properties)
|
|
15
|
+
- [Cascade Layers](#cascade-layers)
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## PostCSS Pipeline
|
|
20
|
+
|
|
21
|
+
The build process transforms non-standard CSS syntax into valid browser CSS. Understanding these transforms is essential for reading and writing component styles.
|
|
22
|
+
|
|
23
|
+
### Plugins & What They Do
|
|
24
|
+
|
|
25
|
+
| Plugin | Syntax it handles | Output |
|
|
26
|
+
|---|---|---|
|
|
27
|
+
| `postcss-design-tokens` | `token("spacing.m")` | CSS custom property value |
|
|
28
|
+
| `@csstools/postcss-global-data` | `@media (--medium-up)` | Custom media available globally |
|
|
29
|
+
| `postcss-mixins` | `@define-mixin`, `@add-mixin` | Inline CSS |
|
|
30
|
+
| `postcss-preset-env` (stage 0) | Modern CSS syntax | Cross-browser CSS |
|
|
31
|
+
| `postcss-prepend-layer` | (automatic) | Wraps all CSS in `@layer design-system.*` |
|
|
32
|
+
|
|
33
|
+
### token() Function
|
|
34
|
+
|
|
35
|
+
The `token()` function is provided by `postcss-design-tokens` and resolves values from `src/tokens.js` at build time:
|
|
36
|
+
|
|
37
|
+
```css
|
|
38
|
+
/* Source */
|
|
39
|
+
@custom-media --medium-up (width >= token("breakpoint.m"));
|
|
40
|
+
padding: token("spacing.m");
|
|
41
|
+
|
|
42
|
+
/* Compiled output */
|
|
43
|
+
@custom-media --medium-up (width >= 900px);
|
|
44
|
+
padding: 1rem;
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Token paths mirror the structure of `src/tokens.js`. See the [Design Tokens](#design-tokens) section for available categories.
|
|
48
|
+
|
|
49
|
+
### SVG Images
|
|
50
|
+
|
|
51
|
+
SVG files are referenced with a plain `url()` — Vite inlines them as a data URI (or hashes them as an asset) at build time:
|
|
52
|
+
|
|
53
|
+
```css
|
|
54
|
+
background-image: url("../images/icon-close.svg");
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Custom Media Queries
|
|
58
|
+
|
|
59
|
+
Custom media queries (defined in `src/_custom-media.css`) use `@csstools/postcss-global-data` to make them available to every file without needing an explicit `@import`. At build time they compile to standard `@media` queries:
|
|
60
|
+
|
|
61
|
+
```css
|
|
62
|
+
/* Source */
|
|
63
|
+
@media (--medium-up) { ... }
|
|
64
|
+
|
|
65
|
+
/* Compiled output */
|
|
66
|
+
@media (width >= 900px) { ... }
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The full list of available custom media names is in the [Custom Media](#custom-media) section.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## BEM Naming
|
|
74
|
+
|
|
75
|
+
### Structure
|
|
76
|
+
|
|
77
|
+
Block Element Modifier (BEM) methodology is **strictly enforced** by Stylelint.
|
|
78
|
+
|
|
79
|
+
```css
|
|
80
|
+
/* Block: Component/standalone entity */
|
|
81
|
+
.mx-card {
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/* Element: Part of block */
|
|
85
|
+
.mx-card__header {
|
|
86
|
+
}
|
|
87
|
+
.mx-card__body {
|
|
88
|
+
}
|
|
89
|
+
.mx-card__footer {
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/* Modifier: Variation of block or element */
|
|
93
|
+
.mx-card--reversed {
|
|
94
|
+
}
|
|
95
|
+
.mx-card--block {
|
|
96
|
+
}
|
|
97
|
+
.mx-card__header--large {
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Naming Rules
|
|
102
|
+
|
|
103
|
+
✅ **Allowed:**
|
|
104
|
+
|
|
105
|
+
```css
|
|
106
|
+
.mx-component /* Block */
|
|
107
|
+
.mx-component__element /* Element */
|
|
108
|
+
.mx-component--modifier /* Modifier */
|
|
109
|
+
.mx-component__element--modifier /* Element with modifier */
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
❌ **Not Allowed:**
|
|
113
|
+
|
|
114
|
+
```css
|
|
115
|
+
.mx-component-element /* Wrong separator */
|
|
116
|
+
.mx-component.modifier /* Not BEM */
|
|
117
|
+
.mx-componentElement /* camelCase not allowed */
|
|
118
|
+
.component /* Missing mx- prefix */
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Prefix Convention
|
|
122
|
+
|
|
123
|
+
All design system classes use the `mx-` prefix:
|
|
124
|
+
|
|
125
|
+
- **mx** = Mixtape
|
|
126
|
+
- Prevents conflicts with consumer styles
|
|
127
|
+
- Clear namespace for design system components
|
|
128
|
+
|
|
129
|
+
### Nesting with &
|
|
130
|
+
|
|
131
|
+
```css
|
|
132
|
+
/* ✅ GOOD - Use & for nesting, reduce specificity */
|
|
133
|
+
.mx-card {
|
|
134
|
+
display: flex;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.mx-card--reversed {
|
|
138
|
+
flex-direction: row-reverse;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.mx-card__media {
|
|
142
|
+
& img {
|
|
143
|
+
aspect-ratio: var(--ratio);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Stylelint Enforcement
|
|
149
|
+
|
|
150
|
+
Your `.stylelintrc.json` enforces BEM:
|
|
151
|
+
|
|
152
|
+
```javascript
|
|
153
|
+
"selector-class-pattern": [
|
|
154
|
+
"^([a-z])([a-z0-9]+)((-[a-z0-9]+)+)?(((--)?(__)?)([a-z0-9]+)((-[a-z0-9]+)+)?)?(((--)?(__)?)([a-z0-9]+)((-[a-z0-9]+)+)?)?$",
|
|
155
|
+
{
|
|
156
|
+
message: "Expected class selector to be BEM selector matching either .block__element or .block--modifier"
|
|
157
|
+
}
|
|
158
|
+
]
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## File Organization
|
|
164
|
+
|
|
165
|
+
### Component Structure
|
|
166
|
+
|
|
167
|
+
Components provide multiple CSS files for flexibility:
|
|
168
|
+
|
|
169
|
+
```
|
|
170
|
+
Component/
|
|
171
|
+
├── component.css # Public API - imports all partials
|
|
172
|
+
├── _component-base.css # Base styles (partial)
|
|
173
|
+
├── _component-modifiers.css # Modifier variations (partial)
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
#### Example: Button component
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
Button/
|
|
180
|
+
├── button.css # Full button styles (imports both partials)
|
|
181
|
+
├── _buttons.css # Base button styles
|
|
182
|
+
└── _buttons-styles.css # Button style variations
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Import Patterns
|
|
186
|
+
|
|
187
|
+
You can import the **full component** or **individual partials** depending on your needs:
|
|
188
|
+
|
|
189
|
+
#### Option 1: Import Full Component (Recommended)
|
|
190
|
+
|
|
191
|
+
```css
|
|
192
|
+
/* Gets all button styles */
|
|
193
|
+
@import "@pnx-mixtape/mxds/src/Atom/Button/button.css";
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
This is the simplest approach and includes all styles for the component.
|
|
197
|
+
|
|
198
|
+
#### Option 2: Import Partials (Granular Control)
|
|
199
|
+
|
|
200
|
+
```css
|
|
201
|
+
/* Import only what you need */
|
|
202
|
+
@import "@pnx-mixtape/mxds/src/Atom/Button/_buttons.css";
|
|
203
|
+
@import "@pnx-mixtape/mxds/src/Atom/Button/_buttons-styles.css";
|
|
204
|
+
|
|
205
|
+
/* Then override specific styles */
|
|
206
|
+
.mx-button--primary {
|
|
207
|
+
--button-background: var(--colour-brand-primary);
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Use this when you:
|
|
212
|
+
|
|
213
|
+
- Only need specific parts of a component
|
|
214
|
+
- Want to customize before other styles load
|
|
215
|
+
- Need finer control over what's included
|
|
216
|
+
|
|
217
|
+
### Partial Files Convention
|
|
218
|
+
|
|
219
|
+
Files prefixed with underscore are **partials**:
|
|
220
|
+
|
|
221
|
+
- `_component-base.css` - Internal partial, can be imported separately
|
|
222
|
+
- `component.css` - Public API, imports all partials
|
|
223
|
+
|
|
224
|
+
**Why use partials?**
|
|
225
|
+
|
|
226
|
+
- Modular - Import only what you need
|
|
227
|
+
- Customizable - Override between partial imports
|
|
228
|
+
- Flexible - Mix and match for your use case
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## Custom Media
|
|
233
|
+
|
|
234
|
+
### Always Import
|
|
235
|
+
|
|
236
|
+
This is only required if you're not using something like [@csstools/postcss-global-data](https://www.npmjs.com/package/@csstools/postcss-global-data).
|
|
237
|
+
|
|
238
|
+
Import custom media in every CSS file that uses them:
|
|
239
|
+
|
|
240
|
+
```css
|
|
241
|
+
/* MUST be first import in every file */
|
|
242
|
+
@import "../../_custom-media.css";
|
|
243
|
+
|
|
244
|
+
.mx-component {
|
|
245
|
+
display: block;
|
|
246
|
+
|
|
247
|
+
@media (--medium-up) {
|
|
248
|
+
display: flex;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### Available Breakpoints
|
|
254
|
+
|
|
255
|
+
```css
|
|
256
|
+
/* Mobile first breakpoints */
|
|
257
|
+
@media (--x-small-up) /* >= 375px */
|
|
258
|
+
@media (--small-up) /* >= 600px */
|
|
259
|
+
@media (--medium-up) /* >= 900px */
|
|
260
|
+
@media (--large-up) /* >= 1200px */
|
|
261
|
+
@media (--x-large-up) /* >= 1800px */
|
|
262
|
+
|
|
263
|
+
/* Desktop first breakpoints */
|
|
264
|
+
@media (--x-small-down) /* < 375px */
|
|
265
|
+
@media (--small-down) /* < 600px */
|
|
266
|
+
@media (--medium-down) /* < 900px */
|
|
267
|
+
@media (--large-down) /* < 1200px */
|
|
268
|
+
@media (--x-large-down) /* < 1800px */
|
|
269
|
+
|
|
270
|
+
/* Between breakpoints */
|
|
271
|
+
@media (--x-small-to-small) /* 375px to 600px */
|
|
272
|
+
@media (--small-to-medium) /* 600px to 900px */
|
|
273
|
+
@media (--medium-to-large) /* 900px to 1200px */
|
|
274
|
+
@media (--large-to-x-large) /* 1200px to 1800px */
|
|
275
|
+
|
|
276
|
+
/* Special breakpoints */
|
|
277
|
+
@media (--global-nav-up) /* >= 720px - Navigation breakpoint */
|
|
278
|
+
@media (--global-nav-down) /* < 720px */
|
|
279
|
+
@media (--collapse-card-up) /* >= 400px - Card collapse */
|
|
280
|
+
@media (--collapse-card-down) /* < 400px */
|
|
281
|
+
|
|
282
|
+
/* Interaction */
|
|
283
|
+
@media (--touch) /* Touch devices (no hover) */
|
|
284
|
+
@media (--mouse) /* Mouse devices (has hover) */
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### Mobile First Approach
|
|
288
|
+
|
|
289
|
+
```css
|
|
290
|
+
/* ✅ GOOD - Mobile first */
|
|
291
|
+
.mx-component {
|
|
292
|
+
/* Mobile styles (default) */
|
|
293
|
+
display: block;
|
|
294
|
+
padding: 1rem;
|
|
295
|
+
|
|
296
|
+
/* Tablet */
|
|
297
|
+
@media (--medium-up) {
|
|
298
|
+
display: flex;
|
|
299
|
+
padding: 2rem;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/* Desktop */
|
|
303
|
+
@media (--large-up) {
|
|
304
|
+
padding: 3rem;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/* ❌ BAD - Desktop first */
|
|
309
|
+
.mx-component {
|
|
310
|
+
display: flex; /* Desktop default */
|
|
311
|
+
|
|
312
|
+
@media (--medium-down) {
|
|
313
|
+
display: block; /* Override for mobile */
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## Design Tokens
|
|
321
|
+
|
|
322
|
+
### How Tokens Work
|
|
323
|
+
|
|
324
|
+
Design tokens in `src/tokens.js` are used during the build process to generate `src/constants.css`, which defines all CSS custom properties. Load this file once at the page level to make all custom properties available globally. Components can then use these custom properties for consistent styling.
|
|
325
|
+
|
|
326
|
+
**Workflow:**
|
|
327
|
+
|
|
328
|
+
1. Define/override tokens in `src/tokens.js`
|
|
329
|
+
2. Build process generates `src/constants.css` with CSS custom properties
|
|
330
|
+
3. Components use the custom properties
|
|
331
|
+
|
|
332
|
+
### Using Custom Properties
|
|
333
|
+
|
|
334
|
+
```css
|
|
335
|
+
.mx-component {
|
|
336
|
+
/* Colors */
|
|
337
|
+
color: var(--colour-foreground);
|
|
338
|
+
background: var(--colour-background);
|
|
339
|
+
|
|
340
|
+
/* Spacing */
|
|
341
|
+
padding: var(--spacing-m);
|
|
342
|
+
margin-block-start: var(--spacing-l);
|
|
343
|
+
|
|
344
|
+
/* Typography */
|
|
345
|
+
font-size: var(--font-size-m);
|
|
346
|
+
line-height: var(--line-height);
|
|
347
|
+
|
|
348
|
+
/* Layout */
|
|
349
|
+
max-inline-size: var(--container-max-width);
|
|
350
|
+
gap: var(--spacing-s);
|
|
351
|
+
|
|
352
|
+
/* Borders */
|
|
353
|
+
border-radius: var(--border-radius);
|
|
354
|
+
}
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### Available Token Categories
|
|
358
|
+
|
|
359
|
+
The `src/tokens.js` file defines tokens in these categories:
|
|
360
|
+
|
|
361
|
+
- `colour.*` - Colors (text, background, brand, etc.)
|
|
362
|
+
- `spacing.*` - Spacing scale (xxs, xs, s, m, l, xl, xxl)
|
|
363
|
+
- `font.*` - Typography (size, family, weight, lineHeight)
|
|
364
|
+
- `layout.*` - Layout values (measure, gutter, etc.)
|
|
365
|
+
- `radius.*` - Border radius (s, m, l)
|
|
366
|
+
- `border.*` - Border properties
|
|
367
|
+
- `breakpoint.*` - Breakpoint values
|
|
368
|
+
- `shadow.*` - Box shadows
|
|
369
|
+
- `transition.*` - Transition values
|
|
370
|
+
|
|
371
|
+
These are converted to CSS custom properties like:
|
|
372
|
+
|
|
373
|
+
- `colour.text.primary` → `--colour-text-primary`
|
|
374
|
+
- `spacing.m` → `--spacing-m`
|
|
375
|
+
- `font.size.body` → `--font-size-m`
|
|
376
|
+
|
|
377
|
+
### Overriding Tokens
|
|
378
|
+
|
|
379
|
+
In your project, override tokens before the build process:
|
|
380
|
+
|
|
381
|
+
```javascript
|
|
382
|
+
// project-tokens.js
|
|
383
|
+
import tokens from "@pnx-mixtape/mxds/tokens"
|
|
384
|
+
|
|
385
|
+
// Override existing tokens
|
|
386
|
+
tokens.colour.brand.primary = "rgb(0, 92, 250)"
|
|
387
|
+
tokens.spacing.m = "1.5rem"
|
|
388
|
+
|
|
389
|
+
// Add new tokens
|
|
390
|
+
tokens.colour.brand.tertiary = "rgb(100, 200, 50)"
|
|
391
|
+
|
|
392
|
+
export default tokens
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
Then configure your build tool (Vite, Webpack, etc.) with [postcss-design-tokens](https://www.npmjs.com/package/postcss-design-tokens) to use your project tokens instead of the default ones.
|
|
396
|
+
|
|
397
|
+
The build process will generate custom properties based on your overridden tokens.
|
|
398
|
+
|
|
399
|
+
#### Swapping the type scale
|
|
400
|
+
|
|
401
|
+
Every `--font-size-*` (other than `--font-size-m`, which is the anchor) is derived from a single ratio via `pow()`, so swapping `font.size.ratio` reshapes the whole scale at once. The `global.typeScales` table exposes the eight standard musical scales — pick the named ratio rather than hard-coding a number:
|
|
402
|
+
|
|
403
|
+
```javascript
|
|
404
|
+
import tokens, { global } from "@pnx-mixtape/mxds/tokens"
|
|
405
|
+
|
|
406
|
+
// Tighter UI-style scale (less display-y, denser)
|
|
407
|
+
tokens.font.size.ratio = global.typeScales.perfectFourth // 1.333
|
|
408
|
+
tokens.font.size.ratioSmall = global.typeScales.majorSecond // 1.125
|
|
409
|
+
|
|
410
|
+
// Or editorial / display-heavy
|
|
411
|
+
tokens.font.size.ratio = global.typeScales.goldenRatio // 1.618
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
Available scales:
|
|
415
|
+
|
|
416
|
+
| Name | Ratio | Feel |
|
|
417
|
+
| ----------------- | ----- | -------------------------------------------- |
|
|
418
|
+
| `minorSecond` | 1.067 | Very tight — UI/dense data |
|
|
419
|
+
| `majorSecond` | 1.125 | Tight |
|
|
420
|
+
| `minorThird` | 1.2 | Compact (current mobile default) |
|
|
421
|
+
| `majorThird` | 1.25 | Balanced |
|
|
422
|
+
| `perfectFourth` | 1.333 | Generous body / UI hybrid |
|
|
423
|
+
| `augmentedFourth` | 1.414 | Display-leaning (current desktop default) |
|
|
424
|
+
| `perfectFifth` | 1.5 | Punchy display |
|
|
425
|
+
| `goldenRatio` | 1.618 | Editorial / magazine |
|
|
426
|
+
|
|
427
|
+
`ratio` controls the desktop scale; `ratioSmall` is applied automatically below the `--medium-down` breakpoint, so you can pick a more dramatic ratio for desktop and a tighter one for mobile without writing any media queries.
|
|
428
|
+
|
|
429
|
+
### Generated Custom Properties
|
|
430
|
+
|
|
431
|
+
The `src/constants.css` file (generated from tokens by PostCSS) looks like this:
|
|
432
|
+
|
|
433
|
+
```css
|
|
434
|
+
:root {
|
|
435
|
+
--colour-text-primary: rgb(0, 0, 0);
|
|
436
|
+
--colour-background-secondary: rgb(245, 245, 245);
|
|
437
|
+
--spacing-m: 1rem;
|
|
438
|
+
--font-size-m: 1rem;
|
|
439
|
+
/* ... many more custom properties ... */
|
|
440
|
+
}
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
**Load this file once at the page level** - it defines all custom properties in `:root`, making them available globally to all components without duplication.
|
|
444
|
+
|
|
445
|
+
---
|
|
446
|
+
|
|
447
|
+
## Custom Property Fallbacks
|
|
448
|
+
|
|
449
|
+
### Cascading Fallback Pattern
|
|
450
|
+
|
|
451
|
+
Components use a **cascading fallback pattern** with custom properties, allowing consumers to override values at different levels without modifying component CSS.
|
|
452
|
+
|
|
453
|
+
**Pattern:**
|
|
454
|
+
|
|
455
|
+
```css
|
|
456
|
+
property: var(--override, var(--default));
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
This creates an override point that consumers can set, falling back to the design system default if not set.
|
|
460
|
+
|
|
461
|
+
### Example: Background Component
|
|
462
|
+
|
|
463
|
+
```css
|
|
464
|
+
.mx-background--primary {
|
|
465
|
+
/* Allows consumer override, falls back to design system default */
|
|
466
|
+
background-color: var(--background, var(--colour-background));
|
|
467
|
+
color: var(--foreground, var(--colour-foreground));
|
|
468
|
+
|
|
469
|
+
/* Multiple fallback levels for flexibility */
|
|
470
|
+
--scheme: var(--primary-scheme, var(--colour-primary-scheme, var(--colour-scheme)));
|
|
471
|
+
}
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
### How It Works
|
|
475
|
+
|
|
476
|
+
**1. Component defines fallback chain:**
|
|
477
|
+
|
|
478
|
+
```css
|
|
479
|
+
.mx-card {
|
|
480
|
+
padding: var(--card-padding, var(--spacing-m));
|
|
481
|
+
background: var(--card-background, var(--colour-background));
|
|
482
|
+
border: var(--line-width, 2px) solid var(--line-colour, var(--colour-border));
|
|
483
|
+
}
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
**2. Consumer overrides at any level:**
|
|
487
|
+
|
|
488
|
+
```css
|
|
489
|
+
/* Override globally */
|
|
490
|
+
:root {
|
|
491
|
+
--line-colour: blue; /* Changes all components using --line-colour */
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/* Override specific component */
|
|
495
|
+
.mx-card {
|
|
496
|
+
--card-padding: 2rem; /* Only affects cards */
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/* Override specific instance */
|
|
500
|
+
.hero-card {
|
|
501
|
+
--card-background: var(--colour-primary);
|
|
502
|
+
}
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
### Common Override Points
|
|
506
|
+
|
|
507
|
+
Components expose these common override variables:
|
|
508
|
+
|
|
509
|
+
**Spacing:**
|
|
510
|
+
|
|
511
|
+
- `--card-padding` → `--spacing-m`
|
|
512
|
+
- `--section-gap` → `--spacing-xl`
|
|
513
|
+
- `--gap` → `--spacing-s`
|
|
514
|
+
|
|
515
|
+
**Colors:**
|
|
516
|
+
|
|
517
|
+
- `--background` → `--colour-background`
|
|
518
|
+
- `--foreground` → `--colour-foreground`
|
|
519
|
+
- `--line-colour` → `--colour-border`
|
|
520
|
+
|
|
521
|
+
**Borders:**
|
|
522
|
+
|
|
523
|
+
- `--line-width` → `1px` or `2px`
|
|
524
|
+
- `--border-radius` → design system default
|
|
525
|
+
|
|
526
|
+
**Typography:**
|
|
527
|
+
|
|
528
|
+
- `--font-weight` → `--font-weight-bold`
|
|
529
|
+
- `--font-size` → size-specific default
|
|
530
|
+
|
|
531
|
+
### Benefits
|
|
532
|
+
|
|
533
|
+
✅ **No component CSS modification** - Override via custom properties
|
|
534
|
+
✅ **Scoped changes** - Target specific components or instances
|
|
535
|
+
✅ **Global changes** - Set at `:root` for site-wide updates
|
|
536
|
+
✅ **Graceful fallbacks** - Always falls back to design system defaults
|
|
537
|
+
✅ **Inheritance** - Child elements inherit parent overrides
|
|
538
|
+
|
|
539
|
+
### Real Example
|
|
540
|
+
|
|
541
|
+
```css
|
|
542
|
+
/* Component CSS (in design system) */
|
|
543
|
+
.mx-blockquote {
|
|
544
|
+
padding-inline-start: var(--line-gap, var(--gap));
|
|
545
|
+
border-inline-start: var(--line-width, 6px) solid var(--line-colour, var(--colour-border));
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/* Consumer override (in your project) */
|
|
549
|
+
.special-quote {
|
|
550
|
+
--line-colour: var(--colour-accent);
|
|
551
|
+
--line-width: 10px;
|
|
552
|
+
}
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
**Result:** The blockquote uses custom colors and width without touching component CSS.
|
|
556
|
+
|
|
557
|
+
---
|
|
558
|
+
|
|
559
|
+
## Color Scheme (light-dark)
|
|
560
|
+
|
|
561
|
+
### Automatic Light/Dark Mode
|
|
562
|
+
|
|
563
|
+
The design system uses the CSS `light-dark()` function and `color-scheme` property for automatic light/dark mode support.
|
|
564
|
+
|
|
565
|
+
### How It Works
|
|
566
|
+
|
|
567
|
+
**1. Set color-scheme:**
|
|
568
|
+
|
|
569
|
+
```css
|
|
570
|
+
.mx-background--primary {
|
|
571
|
+
color-scheme: var(--scheme, var(--colour-scheme));
|
|
572
|
+
}
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
The `color-scheme` property tells the browser which color scheme is in use (light or dark), affecting system UI and enabling `light-dark()`.
|
|
576
|
+
|
|
577
|
+
**2. Use light-dark() for colors:**
|
|
578
|
+
|
|
579
|
+
```css
|
|
580
|
+
.mx-background--primary {
|
|
581
|
+
--line-colour: light-dark(var(--colour-primary-darker), var(--colour-primary-lighter));
|
|
582
|
+
}
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
The `light-dark()` function automatically picks the first value in light mode, second value in dark mode.
|
|
586
|
+
|
|
587
|
+
### Background Component Example
|
|
588
|
+
|
|
589
|
+
```css
|
|
590
|
+
:where([class*="mx-background--"]) {
|
|
591
|
+
background-color: var(--background, var(--colour-background));
|
|
592
|
+
color: var(--foreground, var(--colour-foreground));
|
|
593
|
+
color-scheme: var(--scheme, var(--colour-scheme));
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
.mx-background--primary {
|
|
597
|
+
--scheme: var(--primary-scheme, var(--colour-primary-scheme, var(--colour-scheme)));
|
|
598
|
+
--background: var(--colour-primary);
|
|
599
|
+
--line-colour: light-dark(var(--colour-primary-darker), var(--colour-primary-lighter));
|
|
600
|
+
--colour-foreground-alt: var(--colour-primary-light);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
.mx-background--reverse {
|
|
604
|
+
--scheme: var(--reverse-scheme, var(--colour-reverse-scheme, dark));
|
|
605
|
+
}
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
### Setting Color Scheme
|
|
609
|
+
|
|
610
|
+
**Global (page level):**
|
|
611
|
+
|
|
612
|
+
```css
|
|
613
|
+
:root {
|
|
614
|
+
--colour-scheme: light; /* or dark */
|
|
615
|
+
}
|
|
616
|
+
```
|
|
617
|
+
|
|
618
|
+
**Per section:**
|
|
619
|
+
|
|
620
|
+
```css
|
|
621
|
+
.dark-section {
|
|
622
|
+
--scheme: dark;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
.light-section {
|
|
626
|
+
--scheme: light;
|
|
627
|
+
}
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
**Automatic (user preference):**
|
|
631
|
+
|
|
632
|
+
```css
|
|
633
|
+
:root {
|
|
634
|
+
color-scheme: light dark; /* Supports both */
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
@media (prefers-color-scheme: dark) {
|
|
638
|
+
:root {
|
|
639
|
+
--colour-scheme: dark;
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
### Using light-dark()
|
|
645
|
+
|
|
646
|
+
**Syntax:**
|
|
647
|
+
|
|
648
|
+
```css
|
|
649
|
+
property: light-dark(<light-value>, <dark-value>);
|
|
650
|
+
```
|
|
651
|
+
|
|
652
|
+
**Examples:**
|
|
653
|
+
|
|
654
|
+
```css
|
|
655
|
+
/* Text colors */
|
|
656
|
+
color: light-dark(var(--colour-text-dark), var(--colour-text-light));
|
|
657
|
+
|
|
658
|
+
/* Backgrounds */
|
|
659
|
+
background: light-dark(#ffffff, #000000);
|
|
660
|
+
|
|
661
|
+
/* Borders */
|
|
662
|
+
border-color: light-dark(var(--colour-primary), var(--colour-primary-light));
|
|
663
|
+
|
|
664
|
+
/* Shadows */
|
|
665
|
+
box-shadow: light-dark(0 2px 8px rgba(0, 0, 0, 0.1), 0 2px 8px rgba(255, 255, 255, 0.1));
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
### When to Use
|
|
669
|
+
|
|
670
|
+
**Use `light-dark()` for:**
|
|
671
|
+
|
|
672
|
+
- Colors that need to adapt to light/dark mode
|
|
673
|
+
- UI elements with different appearances per mode
|
|
674
|
+
- Borders, shadows, and subtle UI details
|
|
675
|
+
|
|
676
|
+
**Use `color-scheme` for:**
|
|
677
|
+
|
|
678
|
+
- Declaring which mode a section uses
|
|
679
|
+
- Enabling `light-dark()` function
|
|
680
|
+
- Affecting browser chrome (scrollbars, form controls)
|
|
681
|
+
|
|
682
|
+
### Benefits
|
|
683
|
+
|
|
684
|
+
✅ **Automatic switching** - Browser handles light/dark logic
|
|
685
|
+
✅ **No JavaScript required** - Pure CSS solution
|
|
686
|
+
✅ **Respects user preference** - Works with `prefers-color-scheme`
|
|
687
|
+
✅ **Scoped modes** - Different sections can use different schemes
|
|
688
|
+
✅ **System integration** - Browser chrome matches color scheme
|
|
689
|
+
|
|
690
|
+
### Complete Example
|
|
691
|
+
|
|
692
|
+
```css
|
|
693
|
+
/* Define color scheme modes in constants.css */
|
|
694
|
+
:root {
|
|
695
|
+
--colour-scheme: light;
|
|
696
|
+
--colour-primary: #003366;
|
|
697
|
+
--colour-primary-light: #6699cc;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
/* Component uses light-dark() */
|
|
701
|
+
.mx-component {
|
|
702
|
+
color-scheme: var(--colour-scheme);
|
|
703
|
+
border-color: light-dark(var(--colour-primary), var(--colour-primary-light));
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
/* Consumer changes scheme for a section */
|
|
707
|
+
.dark-hero {
|
|
708
|
+
--scheme: dark;
|
|
709
|
+
}
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
**Result:** The hero section automatically uses dark mode colors while rest of page uses light mode.
|
|
713
|
+
|
|
714
|
+
---
|
|
715
|
+
|
|
716
|
+
## Logical Properties
|
|
717
|
+
|
|
718
|
+
### Use Logical Properties
|
|
719
|
+
|
|
720
|
+
**ENFORCED** by stylelint-use-logical-spec plugin.
|
|
721
|
+
|
|
722
|
+
```css
|
|
723
|
+
/* ✅ GOOD - Logical properties (RTL-friendly) */
|
|
724
|
+
.mx-component {
|
|
725
|
+
padding-inline: 1rem; /* left/right in LTR, right/left in RTL */
|
|
726
|
+
padding-block: 2rem; /* top/bottom always */
|
|
727
|
+
margin-inline-start: 1rem; /* left in LTR, right in RTL */
|
|
728
|
+
margin-block-start: 1rem; /* top always */
|
|
729
|
+
border-inline-end: 1px solid; /* right in LTR, left in RTL */
|
|
730
|
+
inset-inline-start: 0; /* left in LTR, right in RTL */
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
/* ❌ BAD - Physical properties */
|
|
734
|
+
.mx-component {
|
|
735
|
+
padding-left: 1rem; /* Doesn't flip for RTL */
|
|
736
|
+
padding-right: 1rem;
|
|
737
|
+
margin-top: 1rem; /* OK but inconsistent */
|
|
738
|
+
border-right: 1px; /* Doesn't flip for RTL */
|
|
739
|
+
left: 0; /* Doesn't flip for RTL */
|
|
740
|
+
}
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
### Property Mapping
|
|
744
|
+
|
|
745
|
+
| Physical | Logical |
|
|
746
|
+
| ---------------- | ---------------------- |
|
|
747
|
+
| `padding-left` | `padding-inline-start` |
|
|
748
|
+
| `padding-right` | `padding-inline-end` |
|
|
749
|
+
| `padding-top` | `padding-block-start` |
|
|
750
|
+
| `padding-bottom` | `padding-block-end` |
|
|
751
|
+
| `margin-left` | `margin-inline-start` |
|
|
752
|
+
| `margin-right` | `margin-inline-end` |
|
|
753
|
+
| `border-left` | `border-inline-start` |
|
|
754
|
+
| `border-right` | `border-inline-end` |
|
|
755
|
+
| `left` | `inset-inline-start` |
|
|
756
|
+
| `right` | `inset-inline-end` |
|
|
757
|
+
| `top` | `inset-block-start` |
|
|
758
|
+
| `bottom` | `inset-block-end` |
|
|
759
|
+
| `width` | `inline-size` |
|
|
760
|
+
| `height` | `block-size` |
|
|
761
|
+
| `max-width` | `max-inline-size` |
|
|
762
|
+
| `min-height` | `min-block-size` |
|
|
763
|
+
|
|
764
|
+
### Shorthand Logical Properties
|
|
765
|
+
|
|
766
|
+
```css
|
|
767
|
+
/* Inline (horizontal in LTR) */
|
|
768
|
+
padding-inline: 1rem; /* Both sides */
|
|
769
|
+
margin-inline: 1rem 2rem; /* Start and end */
|
|
770
|
+
|
|
771
|
+
/* Block (vertical) */
|
|
772
|
+
padding-block: 1rem; /* Top and bottom */
|
|
773
|
+
margin-block: 1rem 2rem; /* Start and end */
|
|
774
|
+
|
|
775
|
+
/* Inset (position) */
|
|
776
|
+
inset: 0; /* All sides */
|
|
777
|
+
inset-inline: 0; /* Left and right in LTR */
|
|
778
|
+
inset-block: 0; /* Top and bottom */
|
|
779
|
+
```
|
|
780
|
+
|
|
781
|
+
### Exception: Float
|
|
782
|
+
|
|
783
|
+
`float` is exempt from logical properties (no logical equivalent):
|
|
784
|
+
|
|
785
|
+
```css
|
|
786
|
+
.mx-component {
|
|
787
|
+
float: left; /* OK - Exception defined in config */
|
|
788
|
+
}
|
|
789
|
+
```
|
|
790
|
+
|
|
791
|
+
---
|
|
792
|
+
|
|
793
|
+
## Cascade Layers
|
|
794
|
+
|
|
795
|
+
All design system CSS is wrapped in cascade layers for **proper specificity control**.
|
|
796
|
+
|
|
797
|
+
### Layer Order
|
|
798
|
+
|
|
799
|
+
```css
|
|
800
|
+
@layer popover-polyfill, design-system.defaults, design-system.atoms, design-system.layouts, design-system.utilities, design-system.components;
|
|
801
|
+
```
|
|
802
|
+
|
|
803
|
+
### Using Layers
|
|
804
|
+
|
|
805
|
+
```css
|
|
806
|
+
/* Automatically handled by build process */
|
|
807
|
+
@layer design-system.components {
|
|
808
|
+
.mx-card {
|
|
809
|
+
/* Component styles */
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
@layer design-system.utilities {
|
|
814
|
+
.mx-text--center {
|
|
815
|
+
/* Utility styles */
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
```
|
|
819
|
+
|
|
820
|
+
### Layer Benefits
|
|
821
|
+
|
|
822
|
+
- ✅ **Predictable specificity** - Layers override regardless of selector specificity
|
|
823
|
+
- ✅ **Easy overrides** - Consumer styles automatically override design system
|
|
824
|
+
- ✅ **No !important needed** - Layer order handles precedence
|
|
825
|
+
- ✅ **Better organization** - Clear separation of concerns
|
|
826
|
+
|
|
827
|
+
### Consumer Overrides
|
|
828
|
+
|
|
829
|
+
Consumers can easily override design system styles:
|
|
830
|
+
|
|
831
|
+
```css
|
|
832
|
+
/* Consumer styles (unlayered) always win */
|
|
833
|
+
.mx-card {
|
|
834
|
+
padding: 3rem; /* Overrides design system */
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
/* Or use their own layers */
|
|
838
|
+
@layer my-app {
|
|
839
|
+
.mx-card {
|
|
840
|
+
padding: 3rem; /* Also overrides */
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
```
|
|
844
|
+
|
|
845
|
+
---
|
|
846
|
+
|
|
847
|
+
## CSS Checklist
|
|
848
|
+
|
|
849
|
+
Before shipping styles:
|
|
850
|
+
|
|
851
|
+
- [ ] BEM naming convention followed
|
|
852
|
+
- [ ] `mx-` prefix on all classes
|
|
853
|
+
- [ ] Custom media imported at top of file (if needed)
|
|
854
|
+
- [ ] Mobile-first responsive approach
|
|
855
|
+
- [ ] Custom properties used (not hardcoded values)
|
|
856
|
+
- [ ] Logical properties used (not physical)
|
|
857
|
+
- [ ] Cascade layers applied
|
|
858
|
+
- [ ] Stylelint passes (`pnpm lint-css`)
|
|
859
|
+
- [ ] Styles work in RTL mode
|
|
860
|
+
- [ ] Tested across breakpoints
|
|
861
|
+
- [ ] No descending specificity issues
|
|
862
|
+
- [ ] Performance checked (no expensive selectors)
|
|
863
|
+
|
|
864
|
+
---
|
|
865
|
+
|
|
866
|
+
## Stylelint Configuration
|
|
867
|
+
|
|
868
|
+
Key rules from `.stylelintrc.json`:
|
|
869
|
+
|
|
870
|
+
```javascript
|
|
871
|
+
{
|
|
872
|
+
"extends": ["stylelint-config-standard"],
|
|
873
|
+
"plugins": ["stylelint-use-logical-spec"],
|
|
874
|
+
"rules": {
|
|
875
|
+
"selector-class-pattern": "...", // BEM enforcement
|
|
876
|
+
"selector-nested-pattern": "^&", // Must nest with &
|
|
877
|
+
"liberty/use-logical-spec": [true, { "except": ["float"] }],
|
|
878
|
+
"no-descending-specificity": null, // Disabled for flexibility
|
|
879
|
+
"import-notation": "string"
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
```
|
|
883
|
+
|
|
884
|
+
---
|
|
885
|
+
|
|
886
|
+
## Related Documentation
|
|
887
|
+
|
|
888
|
+
- [Code Quality Guidelines](./CodeQuality.md)
|
|
889
|
+
- [Component Architecture](./ComponentArchitecture.md)
|
|
890
|
+
- [Design Tokens](../src/tokens.js)
|
|
891
|
+
- [Custom Media](../src/_custom-media.css)
|