@epa-wg/custom-element-dist 0.0.33 → 0.0.35
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/settings.local.json +18 -0
- package/.gitattributes +4 -0
- package/.github/workflows/deploy.yml +59 -0
- package/.idea/copilot.data.migration.agent.xml +6 -0
- package/.idea/copilot.data.migration.ask.xml +6 -0
- package/.idea/copilot.data.migration.ask2agent.xml +6 -0
- package/.idea/copilot.data.migration.edit.xml +6 -0
- package/.idea/custom-element-dist.iml +2 -0
- package/.storybook/main.ts +20 -17
- package/.storybook/preview.ts +23 -23
- package/.yarn/install-state.gz +0 -0
- package/.yarnrc.yml +1 -0
- package/README.md +6 -4
- package/coverage/block-navigation.js +1 -1
- package/coverage/coverage-final.json +4 -3
- package/coverage/index.html +34 -19
- package/coverage/sorter.js +21 -7
- package/coverage/src/custom-element/coverage.svg +1 -1
- package/coverage/src/custom-element/custom-element.js/coverage.svg +1 -1
- package/coverage/src/custom-element/custom-element.js.html +448 -391
- package/coverage/src/custom-element/http-request.js/coverage.svg +1 -1
- package/coverage/src/custom-element/http-request.js.html +38 -17
- package/coverage/src/custom-element/index.html +26 -26
- package/coverage/src/custom-element/local-storage.js.html +1 -1
- package/coverage/src/custom-element/location-element.js.html +1 -1
- package/coverage/src/custom-element/module-url.js.html +1 -1
- package/coverage/src/index.html +1 -1
- package/coverage/src/material/theme/colors.js/coverage.svg +10 -0
- package/coverage/src/material/theme/colors.js.html +217 -0
- package/coverage/src/material/theme/coverage.svg +10 -0
- package/coverage/src/material/theme/index.html +116 -0
- package/coverage/src/mocks/handlers.ts.html +1 -1
- package/coverage/src/mocks/index.html +1 -1
- package/coverage/src/stories/frame.canvas.ts.html +1 -1
- package/coverage/src/stories/http-request.stories.ts.html +1 -1
- package/coverage/src/stories/index.html +1 -1
- package/coverage/src/stories/testStoryBook.ts.html +12 -12
- package/coverage/src/sum.ts.html +1 -1
- package/dist/custom-element-Bssk9jRy.cjs +97 -0
- package/dist/{custom-element-WnOqmEOe.js → custom-element-BzDjIYMe.js} +193 -183
- package/dist/custom-element-bundle.cjs +1 -1
- package/dist/custom-element-bundle.js +3 -3
- package/dist/demo/a.html +10 -3
- package/dist/demo/a.svg +26 -26
- package/dist/demo/html-template.html +4 -3
- package/dist/demo/s.xml +1 -75
- package/dist/demo/s.xslt +351 -72
- package/dist/demo/s1.xml +3706 -0
- package/dist/http-request-DSaowcG1.cjs +1 -0
- package/dist/{http-request-BOvP4KTl.js → http-request-DTCzZ1gc.js} +15 -9
- package/dist/mockServiceWorker.js +105 -63
- package/package.json +5 -4
- package/public/demo/a.html +10 -3
- package/public/demo/a.svg +26 -26
- package/public/demo/html-template.html +4 -3
- package/public/demo/s.xml +1 -75
- package/public/demo/s.xslt +351 -72
- package/public/demo/s1.xml +3706 -0
- package/public/mockServiceWorker.js +105 -63
- package/src/custom-element/custom-element.js +28 -9
- package/src/custom-element/demo/a.html +10 -3
- package/src/custom-element/demo/a.svg +26 -26
- package/src/custom-element/demo/html-template.html +4 -3
- package/src/custom-element/demo/s.xml +1 -75
- package/src/custom-element/demo/s.xslt +351 -72
- package/src/custom-element/demo/s1.xml +3706 -0
- package/src/custom-element/http-request.js +7 -0
- package/src/custom-element/ide/customData-dce.json +123 -0
- package/src/custom-element/ide/web-types-dce.json +128 -1
- package/src/custom-element/ide/web-types-xsl.json +1 -1
- package/src/material/angular.css +987 -987
- package/src/material/components/action.html +262 -0
- package/src/material/components/autocomplete.html +167 -239
- package/src/material/components/badge.html +238 -239
- package/src/material/components/dropdown.html +0 -1
- package/src/material/components/icon-link.html +160 -161
- package/src/material/components/icon.html +251 -252
- package/src/material/components/input.html +569 -570
- package/src/material/components/menu.html +235 -236
- package/src/material/components.html +157 -158
- package/src/material/demo.css +36 -36
- package/src/material/index.html +109 -110
- package/src/material/material.css +356 -356
- package/src/material/theme/Base-Principles.md +339 -0
- package/src/material/theme/README.md +298 -18
- package/src/material/theme/UI Domain Model in web applications.svg +1 -0
- package/src/material/theme/User Semantic Theme tokens.svg +1 -0
- package/src/material/theme/action-pending-poc.html +62 -0
- package/src/material/theme/actions-color.html +141 -0
- package/src/material/theme/colors-light.html +631 -0
- package/src/material/theme/colors-native.html +51 -0
- package/src/material/theme/colors-poc.html +66 -0
- package/src/material/theme/colors.html +297 -0
- package/src/material/theme/colors.js +44 -0
- package/src/material/theme/consumer-theme.css +745 -0
- package/src/material/theme/semantic.css +132 -132
- package/src/material/theme/style-bug.html +123 -0
- package/src/material/theme/theme-data.css +43 -0
- package/src/material/theme/theme-data.xhtml +2926 -0
- package/src/material/theme/todo.md +274 -0
- package/src/material/theme/tokens/action-colors.png +0 -0
- package/src/material/theme/tokens/cem-article-illustration-4x1-letterbox-2000x500.png +0 -0
- package/src/material/theme/tokens/cem-breakpoints.md +519 -0
- package/src/material/theme/tokens/cem-colors.md +715 -0
- package/src/material/theme/tokens/cem-consumerflow-typography-matrix.svg +198 -0
- package/src/material/theme/tokens/cem-coupling.md +372 -0
- package/src/material/theme/tokens/cem-data-vs-reading-numerals.svg +164 -0
- package/src/material/theme/tokens/cem-dimension.md +625 -0
- package/src/material/theme/tokens/cem-layering.md +562 -0
- package/src/material/theme/tokens/cem-m3-parity.md +343 -0
- package/src/material/theme/tokens/cem-responsive.md +238 -0
- package/src/material/theme/tokens/cem-shape.md +691 -0
- package/src/material/theme/tokens/cem-stroke-density-illustration-4to1-v3.svg +102 -0
- package/src/material/theme/tokens/cem-stroke.md +480 -0
- package/src/material/theme/tokens/cem-timing.md +198 -0
- package/src/material/theme/tokens/cem-typography-model-stack.svg +64 -0
- package/src/material/theme/tokens/cem-voice-fonts-typography.md +718 -0
- package/src/material/theme/tokens/cem-voice-ladder.svg +91 -0
- package/src/material/theme/tokens/chips.png +0 -0
- package/src/material/theme/tokens/columns-page.png +0 -0
- package/src/material/theme/tokens/initials.png +0 -0
- package/src/material/theme/tokens/nav-buttons.png +0 -0
- package/src/material/theme/tokens/script.png +0 -0
- package/src/material/theme/tokens/sufler.png +0 -0
- package/src/material/theme/tokens/typography-icons.png +0 -0
- package/src/mocks/versions.mock.ts +1 -1
- package/src/stories/__screenshots__/dom-merge.test.stories.ts/dom-merge-dom-merge-OrderPreservingOn2ndTransform-1.png +0 -0
- package/src/stories/__screenshots__/xslt-conditionals.test.stories.ts/xslt-conditionals-xslt-conditionals-MultipleIfOrderingWorkaround-1.png +0 -0
- package/src/stories/dom-merge.test.stories.ts +25 -1
- package/src/stories/xslt-conditionals.test.stories.ts +492 -0
- package/src/stories/xslt-for-each.test.stories.ts +336 -0
- package/src/stories/xslt-if.test.stories.ts +89 -0
- package/storybook-static/assets/{Color-F6OSRLHC-Cbp293x2.js → Color-F6OSRLHC-DeDlDLjU.js} +1 -1
- package/storybook-static/assets/{Configure-BrFr4SLE.js → Configure-CH_tIP5N.js} +1 -1
- package/storybook-static/assets/{DocsRenderer-CFRXHY34-DhHzJiIO.js → DocsRenderer-CFRXHY34-Bc9EPsUI.js} +2 -2
- package/storybook-static/assets/{attributes.test.stories-Gg9LQTEK.js → attributes.test.stories-BtamFQkF.js} +1 -1
- package/storybook-static/assets/{css.test.stories-B_3ltOrx.js → css.test.stories-BfNxLgwr.js} +1 -1
- package/storybook-static/assets/{custom-element-CPnvJnn8.js → custom-element-CnmjNo0g.js} +6 -6
- package/storybook-static/assets/{dom-merge.test.stories-nQxcgLoM.js → dom-merge.test.stories-DxnitrLK.js} +47 -6
- package/storybook-static/assets/entry-preview-BNCt9WBs.js +26 -0
- package/storybook-static/assets/{entry-preview-docs-Dwczwtsc.js → entry-preview-docs-CbF8-81D.js} +2 -2
- package/storybook-static/assets/{external-template.test.stories-DZ-rjnfd.js → external-template.test.stories-BTsww7B0.js} +1 -1
- package/storybook-static/assets/{form.test.stories-DQhPYtMj.js → form.test.stories-DNJFtPJb.js} +1 -1
- package/storybook-static/assets/{frame.canvas-ClTqYyMN.js → frame.canvas-E5n9h6j1.js} +1 -1
- package/storybook-static/assets/{handlers-CLkps6Nz.js → handlers-Dvg8CAeR.js} +1 -1
- package/storybook-static/assets/http-request-BWeEEBkP.js +1 -0
- package/storybook-static/assets/{http-request.stories-jo0f73nw.js → http-request.stories-DgrBNle8.js} +1 -1
- package/storybook-static/assets/{iframe-CZwRpnn9.js → iframe-DiVWehoI.js} +11 -11
- package/storybook-static/assets/{index-Dr4PwNfd.js → index-CGuyH0k-.js} +87 -87
- package/storybook-static/assets/{index-CJQtnF9V.js → index-CdEbhcV9.js} +1 -1
- package/storybook-static/assets/index-DO1nmyvI.js +11 -0
- package/storybook-static/assets/{index-B68YUdzy.js → index-w6iX3YlR.js} +3 -3
- package/storybook-static/assets/{local-storage.test.stories-uA5EKRPf.js → local-storage.test.stories-Hwq80yUr.js} +1 -1
- package/storybook-static/assets/{location-element.test.stories-Cu-6Elcg.js → location-element.test.stories-mEhZzm7x.js} +1 -1
- package/storybook-static/assets/{module-url.test.stories-CD_wusXQ.js → module-url.test.stories-Bj46iT0V.js} +1 -1
- package/storybook-static/assets/{preview-CuCH40jj.js → preview-BjbXcJci.js} +2 -2
- package/storybook-static/assets/{preview-BFlNN3Wj.js → preview-Bn8igYMp.js} +1 -1
- package/storybook-static/assets/{preview-Cm4PPhHS.js → preview-CfuT8gak.js} +1 -1
- package/storybook-static/assets/{set-url.test.stories-CY7B9BVZ.js → set-url.test.stories-hzxLcqmm.js} +1 -1
- package/storybook-static/assets/{slice-events.test.stories-BVnPXm6e.js → slice-events.test.stories-DVyXFRU1.js} +1 -1
- package/storybook-static/assets/{slots.test.stories-Dxsa9KdA.js → slots.test.stories-CS544nS4.js} +1 -1
- package/storybook-static/assets/{version-select.test.stories-Buga1PAa.js → version-select.test.stories-D36nfYBq.js} +1 -1
- package/storybook-static/assets/xslt-conditionals.test.stories-BS1PTIHe.js +633 -0
- package/storybook-static/assets/xslt-for-each.test.stories-CtPS20RK.js +329 -0
- package/storybook-static/assets/xslt-if.test.stories-DcHrAMSY.js +71 -0
- package/storybook-static/demo/a.html +10 -3
- package/storybook-static/demo/a.svg +26 -26
- package/storybook-static/demo/html-template.html +4 -3
- package/storybook-static/demo/s.xml +1 -75
- package/storybook-static/demo/s.xslt +351 -72
- package/storybook-static/demo/s1.xml +3706 -0
- package/storybook-static/iframe.html +2 -2
- package/storybook-static/index.json +1 -1
- package/storybook-static/mockServiceWorker.js +105 -63
- package/storybook-static/project.json +1 -1
- package/storybook-static/sb-addons/essentials-controls-1/manager-bundle.js +69 -66
- package/storybook-static/sb-addons/essentials-docs-3/manager-bundle.js +62 -59
- package/dist/custom-element-6slVaFEs.cjs +0 -97
- package/dist/http-request-DPrY7mGh.cjs +0 -1
- package/storybook-static/assets/attributes.test.stories-CzWkKw0e.js +0 -1
- package/storybook-static/assets/entry-preview-DHVXbf3x.js +0 -26
- package/storybook-static/assets/external-template.test.stories-BivZqBTp.js +0 -1
- package/storybook-static/assets/http-request-DNq59pnj.js +0 -1
- package/storybook-static/assets/index-BwkS7JH_.js +0 -8
- package/storybook-static/assets/module-url.test.stories-CTjUAk3J.js +0 -1
|
@@ -0,0 +1,519 @@
|
|
|
1
|
+
# CEM D1x Breakpoints & Responsive Ranges — Window Size Classes (CEM × M3 × MUI)
|
|
2
|
+
**Status:** Canonical (v1.0)
|
|
3
|
+
**Last updated:** 2025-12-23
|
|
4
|
+
**Taxonomy placement:** D1x. Layout Framing (Space & Rhythm extension)
|
|
5
|
+
**Audience:** Design Systems, Product Design, Front-End Engineering
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
**CEM first rule:** If an adapter’s breakpoint names or defaults conflict with CEM semantics, **CEM semantics win**. Adapters translate to their own keyspaces; the consumer-facing vocabulary remains `compact/medium/expanded/large/xlarge`.
|
|
10
|
+
|
|
11
|
+
CEM uses **breakpoints** as a layout framing primitive that selects canonical layout archetypes (single-pane, two-pane, multi-pane) based on available space. Breakpoints express **semantic meaning about available UI space**, not device categories.
|
|
12
|
+
|
|
13
|
+
**Companion specs:**
|
|
14
|
+
- **D0. Color** ([`cem-colors.md`](./cem-colors.md)) — emotional palette and action states
|
|
15
|
+
- **D1. Space & Rhythm** ([`cem-dimension.md`](./cem-dimension.md)) — spacing scale, density modes, layout gaps
|
|
16
|
+
- **D1y. Responsiveness Strategy** ([`cem-responsive.md`](./cem-responsive.md)) — intrinsic vs container vs breakpoint adaptation
|
|
17
|
+
- **D2. Coupling & Compactness** ([`cem-coupling.md`](./cem-coupling.md)) — zone/guard/halo invariants preserved across ranges
|
|
18
|
+
- **D4. Layering** ([`cem-layering.md`](./cem-layering.md)) — depth/surface hierarchy
|
|
19
|
+
- **D5. Stroke & Separation** ([`cem-stroke.md`](./cem-stroke.md)) — separability may need reinforcement in dense layouts
|
|
20
|
+
- **D6. Typography** ([`cem-voice-fonts-typography.md`](./cem-voice-fonts-typography.md)) — reading constraints validated in large widths
|
|
21
|
+
- **D7. Time & Motion** ([`cem-timing.md`](./cem-timing.md)) — layout transition timing
|
|
22
|
+
|
|
23
|
+
## Table of contents
|
|
24
|
+
|
|
25
|
+
1. [Scope](#1-scope)
|
|
26
|
+
2. [Design principles](#2-design-principles)
|
|
27
|
+
3. [Token taxonomy](#3-token-taxonomy)
|
|
28
|
+
4. [Window size classes](#4-window-size-classes)
|
|
29
|
+
5. [Token surface (CSS)](#5-token-surface-css)
|
|
30
|
+
6. [Adapter mapping](#6-adapter-mapping)
|
|
31
|
+
7. [Usage guidance](#7-usage-guidance)
|
|
32
|
+
8. [Container queries extension](#8-container-queries-extension)
|
|
33
|
+
9. [Governance and versioning](#9-governance-and-versioning)
|
|
34
|
+
10. [Canonical token summary](#10-canonical-token-summary)
|
|
35
|
+
11. [References](#11-references)
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## 1. Scope
|
|
40
|
+
|
|
41
|
+
### 1.1 What D1x controls
|
|
42
|
+
|
|
43
|
+
D1x (Breakpoints) defines:
|
|
44
|
+
|
|
45
|
+
- **Semantic range vocabulary:** `compact`, `medium`, `expanded`, `large`, `xlarge` — stable names for layout bands.
|
|
46
|
+
- **Numeric basis bounds:** CSS custom properties for range boundaries.
|
|
47
|
+
- **Adapter guidance:** mapping to Material UI, Material Design 3, and other framework breakpoints.
|
|
48
|
+
|
|
49
|
+
### 1.2 What D1x does not define
|
|
50
|
+
|
|
51
|
+
D1x does **not** define:
|
|
52
|
+
|
|
53
|
+
- Column counts, grid systems, or gutter sizes (see **D1 Space & Rhythm**).
|
|
54
|
+
- Component-level responsive props (Grid, Stack, etc.).
|
|
55
|
+
- Container query values (optional extension in §8).
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## 2. Design principles
|
|
60
|
+
|
|
61
|
+
### 2.0 CEM first rule: semantics before adapters
|
|
62
|
+
|
|
63
|
+
CEM breakpoint semantics are defined by **available layout space** and **consumer intent** (one-pane vs two-pane vs multi-pane), not by framework defaults (for example, `md` meaning “900px” in MUI). Treat framework breakpoints as an **adapter layer**.
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
### 2.1 Semantics express available space, not devices
|
|
67
|
+
|
|
68
|
+
A breakpoint expresses **available UI space** (window/container), not "tablet vs desktop".
|
|
69
|
+
Do not implement `isTablet`-style logic based on breakpoint names.
|
|
70
|
+
|
|
71
|
+
### 2.2 Bounded variation
|
|
72
|
+
|
|
73
|
+
Breakpoints are a small, stable vocabulary. Prefer 3–5 ranges, not dozens of product-specific micro-breakpoints.
|
|
74
|
+
|
|
75
|
+
### 2.3 Breakpoints select layouts; details are separate
|
|
76
|
+
|
|
77
|
+
Breakpoints choose **canonical layout archetypes** (one-pane → two-pane → multi-pane).
|
|
78
|
+
Spacing, coupling, and stroke rules stay governed by their own dimensions (D1/D2/D5).
|
|
79
|
+
|
|
80
|
+
### 2.4 Cross-dimension invariants
|
|
81
|
+
|
|
82
|
+
- **D2 coupling invariants** (zone/guard/halo) must remain enforceable in every size class.
|
|
83
|
+
- **D6 reading constraints** should be validated in large widths (line length, paragraph rhythm).
|
|
84
|
+
- **D5 separability** may need reinforcement in dense layouts regardless of width.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## 3. Token taxonomy
|
|
89
|
+
|
|
90
|
+
### 3.1 Two-layer model
|
|
91
|
+
|
|
92
|
+
CEM breakpoints use a two-layer model:
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
Basis bounds (numeric boundaries)
|
|
96
|
+
--cem-bp-{axis}-{range}-{min|max}
|
|
97
|
+
│
|
|
98
|
+
▼
|
|
99
|
+
Semantic ranges (consumer vocabulary)
|
|
100
|
+
compact | medium | expanded | large | xlarge
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### 3.2 Why two layers?
|
|
104
|
+
|
|
105
|
+
- **Adapter flexibility:** numeric values can be tuned without changing semantic contracts.
|
|
106
|
+
- **Consumer stability:** product code depends on range names, not pixel values.
|
|
107
|
+
- **Cross-framework mapping:** different frameworks can adjust bounds while preserving intent.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## 4. Window size classes
|
|
112
|
+
|
|
113
|
+
CEM adopts the **Material Design 3 window size class vocabulary** as the primary reference because it is intentionally device-agnostic and defined on available width/height.
|
|
114
|
+
|
|
115
|
+
### 4.1 Width ranges (semantic)
|
|
116
|
+
|
|
117
|
+
| CEM range | Meaning (consumer) | Width — reference |
|
|
118
|
+
|------------|---------------------------------------------------------------|------------------------|
|
|
119
|
+
| `compact` | single-pane default; tight navigation | `< 600dp` |
|
|
120
|
+
| `medium` | two-pane *possible*; avoid over-wide lines | `600dp ≤ w < 840dp` |
|
|
121
|
+
| `expanded` | two-pane comfortable; navigation rail viable | `840dp ≤ w < 1200dp` |
|
|
122
|
+
| `large` | multi-pane viable; guard against sparse "stretch" | `1200dp ≤ w < 1600dp` |
|
|
123
|
+
| `xlarge` | desktop-class; constrain reading width, increase density | `≥ 1600dp` |
|
|
124
|
+
|
|
125
|
+
### 4.2 Height ranges (semantic)
|
|
126
|
+
|
|
127
|
+
Height is usually secondary due to vertical scrolling, but matters for landscape phones and split-screen.
|
|
128
|
+
|
|
129
|
+
| CEM range | Height — reference | Device representation (informative) |
|
|
130
|
+
|------------|------------------------|--------------------------------------------|
|
|
131
|
+
| `compact` | `< 480dp` | 99.78% of phones in landscape |
|
|
132
|
+
| `medium` | `480dp ≤ h < 900dp` | 96.56% of tablets in landscape, 97.59% of phones in portrait |
|
|
133
|
+
| `expanded` | `≥ 900dp` | 94.25% of tablets in portrait |
|
|
134
|
+
|
|
135
|
+
> **Note:** Most apps adapt based on width alone. Consider height when width is `medium` while height is `compact` (e.g., phones in landscape), where two-pane layouts are often impractical.
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## 5. Token surface (CSS)
|
|
140
|
+
|
|
141
|
+
### 5.1 Basis tokens (bounds)
|
|
142
|
+
|
|
143
|
+
These tokens are numeric bounds for ranges, consumed by build-time systems (PostCSS/@custom-media), JS breakpoints, and runtime CSS systems.
|
|
144
|
+
|
|
145
|
+
```css
|
|
146
|
+
:root {
|
|
147
|
+
/*
|
|
148
|
+
* Epsilon is used only when expressing half-open intervals `[min, nextMin)`
|
|
149
|
+
* as `max-width`/`max-height` (exclusive upper bounds).
|
|
150
|
+
*
|
|
151
|
+
* Choose epsilon per adapter/toolchain:
|
|
152
|
+
* - Generic CSS: 0.01px is typically sufficient
|
|
153
|
+
* - MUI default: theme.breakpoints.step = 5 => epsilon 0.05px for down()/between()
|
|
154
|
+
*/
|
|
155
|
+
--cem-bp-epsilon: 0.01px;
|
|
156
|
+
|
|
157
|
+
/* Width mins (M3 reference lattice; dp thresholds reused as CSS px by convention) */
|
|
158
|
+
--cem-bp-width-compact-min: 0px;
|
|
159
|
+
--cem-bp-width-medium-min: 600px;
|
|
160
|
+
--cem-bp-width-expanded-min: 840px;
|
|
161
|
+
--cem-bp-width-large-min: 1200px;
|
|
162
|
+
--cem-bp-width-xlarge-min: 1600px;
|
|
163
|
+
|
|
164
|
+
/* Derived width max (exclusive) */
|
|
165
|
+
--cem-bp-width-compact-max: calc(var(--cem-bp-width-medium-min) - var(--cem-bp-epsilon));
|
|
166
|
+
--cem-bp-width-medium-max: calc(var(--cem-bp-width-expanded-min) - var(--cem-bp-epsilon));
|
|
167
|
+
--cem-bp-width-expanded-max: calc(var(--cem-bp-width-large-min) - var(--cem-bp-epsilon));
|
|
168
|
+
--cem-bp-width-large-max: calc(var(--cem-bp-width-xlarge-min) - var(--cem-bp-epsilon));
|
|
169
|
+
|
|
170
|
+
/* Height mins */
|
|
171
|
+
--cem-bp-height-compact-min: 0px;
|
|
172
|
+
--cem-bp-height-medium-min: 480px;
|
|
173
|
+
--cem-bp-height-expanded-min: 900px;
|
|
174
|
+
|
|
175
|
+
/* Derived height max (exclusive) */
|
|
176
|
+
--cem-bp-height-compact-max: calc(var(--cem-bp-height-medium-min) - var(--cem-bp-epsilon));
|
|
177
|
+
--cem-bp-height-medium-max: calc(var(--cem-bp-height-expanded-min) - var(--cem-bp-epsilon));
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### 5.2 Normative rules
|
|
182
|
+
|
|
183
|
+
- Bounds MUST be strictly increasing.
|
|
184
|
+
- Range names MUST remain stable (`compact`, `medium`, `expanded`, optionally `large`, `xlarge`).
|
|
185
|
+
- Ranges MUST be interpreted as **half-open intervals**: `[min, nextMin)` (lower bound inclusive, upper bound exclusive).
|
|
186
|
+
- If you must express an exclusive upper bound as a CSS `max-width`/`max-height`, subtract a small **epsilon**.
|
|
187
|
+
- The epsilon value is **adapter/toolchain-specific**, but MUST be applied consistently to avoid overlap.
|
|
188
|
+
- In Material UI, prefer the built-in mechanism: `theme.breakpoints.step` (default `5`, i.e., `0.05px`) which is used to implement exclusive `down()`/`between()` upper bounds.
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
### 5.3 @custom-media (where supported)
|
|
192
|
+
|
|
193
|
+
```css
|
|
194
|
+
@custom-media --cem-compact (max-width: calc(600px - 0.01px));
|
|
195
|
+
@custom-media --cem-medium (min-width: 600px) and (max-width: calc(840px - 0.01px));
|
|
196
|
+
@custom-media --cem-expanded (min-width: 840px) and (max-width: calc(1200px - 0.01px));
|
|
197
|
+
@custom-media --cem-large (min-width: 1200px) and (max-width: calc(1600px - 0.01px));
|
|
198
|
+
@custom-media --cem-xlarge (min-width: 1600px);
|
|
199
|
+
|
|
200
|
+
/* Height classes */
|
|
201
|
+
@custom-media --cem-height-compact (max-height: calc(480px - 0.01px));
|
|
202
|
+
@custom-media --cem-height-medium (min-height: 480px) and (max-height: calc(900px - 0.01px));
|
|
203
|
+
@custom-media --cem-height-expanded (min-height: 900px);
|
|
204
|
+
|
|
205
|
+
/*
|
|
206
|
+
* NOTE:
|
|
207
|
+
* The `0.01px` epsilon is illustrative. Align epsilon to your toolchain:
|
|
208
|
+
* - If you use MUI default step=5, prefer 0.05px for exclusive upper bounds.
|
|
209
|
+
*/
|
|
210
|
+
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## 6. Adapter mapping
|
|
216
|
+
|
|
217
|
+
### 6.1 Material Design 3 (reference)
|
|
218
|
+
|
|
219
|
+
M3 window size classes provide the reference lattice for this spec. CEM directly adopts M3's width/height boundaries.
|
|
220
|
+
|
|
221
|
+
| M3 class | Width boundary |
|
|
222
|
+
|--------------|----------------|
|
|
223
|
+
| Compact | < 600dp |
|
|
224
|
+
| Medium | 600–839dp |
|
|
225
|
+
| Expanded | 840–1199dp |
|
|
226
|
+
| Large | 1200–1599dp |
|
|
227
|
+
| Extra-large | ≥ 1600dp |
|
|
228
|
+
|
|
229
|
+
### 6.2 Material UI (MUI) default breakpoints
|
|
230
|
+
|
|
231
|
+
| MUI key | Default value |
|
|
232
|
+
|---------|---------------|
|
|
233
|
+
| `xs` | 0px |
|
|
234
|
+
| `sm` | 600px |
|
|
235
|
+
| `md` | 900px |
|
|
236
|
+
| `lg` | 1200px |
|
|
237
|
+
| `xl` | 1536px |
|
|
238
|
+
|
|
239
|
+
**Gap analysis:** MUI lacks the **840px** boundary (M3 `expanded`) and uses 1536px instead of 1600px for `xl`.
|
|
240
|
+
|
|
241
|
+
### 6.3 Recommended MUI → CEM strategies
|
|
242
|
+
|
|
243
|
+
CEM semantics are stable; your MUI integration chooses an adapter strategy.
|
|
244
|
+
|
|
245
|
+
**Strategy A (recommended for most MUI codebases): keep MUI keys, align values to the CEM lattice.**
|
|
246
|
+
This avoids custom keys and minimizes TypeScript/module-augmentation friction while matching CEM/M3 boundaries.
|
|
247
|
+
|
|
248
|
+
```ts
|
|
249
|
+
const theme = createTheme({
|
|
250
|
+
breakpoints: {
|
|
251
|
+
values: {
|
|
252
|
+
xs: 0,
|
|
253
|
+
sm: 600, // medium lower bound
|
|
254
|
+
md: 840, // expanded lower bound (repurposes MUI `md`)
|
|
255
|
+
lg: 1200, // large lower bound
|
|
256
|
+
xl: 1600, // xlarge lower bound (repurposes MUI `xl`)
|
|
257
|
+
},
|
|
258
|
+
},
|
|
259
|
+
});
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
**Strategy B (semantic purity): replace MUI keys with CEM keys (`compact/medium/expanded/large/xlarge`).**
|
|
263
|
+
This yields the cleanest vocabulary, but requires TypeScript module augmentation and a broader migration.
|
|
264
|
+
|
|
265
|
+
**Strategy C (bridge / mixed keyspace): add `expanded`/`xlarge` alongside the defaults.**
|
|
266
|
+
Use only for incremental migration. This introduces two “large-ish” keys (`xl` vs `xlarge`)—treat the mixed period as transitional.
|
|
267
|
+
|
|
268
|
+
### 6.4 Mapping tables
|
|
269
|
+
|
|
270
|
+
**Strategy A mapping (aligned values, default keys):**
|
|
271
|
+
|
|
272
|
+
| CEM range | MUI binding |
|
|
273
|
+
|------------|-------------|
|
|
274
|
+
| `compact` | `down('sm')` |
|
|
275
|
+
| `medium` | `between('sm', 'md')` |
|
|
276
|
+
| `expanded` | `between('md', 'lg')` |
|
|
277
|
+
| `large` | `between('lg', 'xl')` |
|
|
278
|
+
| `xlarge` | `up('xl')` |
|
|
279
|
+
|
|
280
|
+
**Strategy B mapping (CEM keys as MUI keys):**
|
|
281
|
+
|
|
282
|
+
| CEM range | MUI binding |
|
|
283
|
+
|------------|-------------|
|
|
284
|
+
| `compact` | `down('medium')` *(or `only('compact')` if you define it explicitly)* |
|
|
285
|
+
| `medium` | `between('medium', 'expanded')` |
|
|
286
|
+
| `expanded` | `between('expanded', 'large')` |
|
|
287
|
+
| `large` | `between('large', 'xlarge')` |
|
|
288
|
+
| `xlarge` | `up('xlarge')` |
|
|
289
|
+
|
|
290
|
+
**Strategy C mapping (mixed keyspace, transitional):**
|
|
291
|
+
|
|
292
|
+
| CEM range | MUI binding |
|
|
293
|
+
|------------|-------------|
|
|
294
|
+
| `compact` | `down('sm')` |
|
|
295
|
+
| `medium` | `between('sm', 'expanded')` |
|
|
296
|
+
| `expanded` | `between('expanded', 'lg')` |
|
|
297
|
+
| `large` | `between('lg', 'xlarge')` |
|
|
298
|
+
| `xlarge` | `up('xlarge')` |
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
## 7. Usage guidance
|
|
303
|
+
|
|
304
|
+
### 7.1 What SHOULD change at a breakpoint
|
|
305
|
+
|
|
306
|
+
Breakpoints trigger **layout archetype changes**:
|
|
307
|
+
|
|
308
|
+
- **Navigation:** bottom bar → rail → drawer
|
|
309
|
+
- **Content:** one-pane → two-pane → three-pane
|
|
310
|
+
- **Density strategy:** introduce secondary panes rather than shrinking controls below D2 invariants
|
|
311
|
+
|
|
312
|
+
### 7.2 What SHOULD NOT change at a breakpoint
|
|
313
|
+
|
|
314
|
+
- Do not reduce D2 coupling minimums.
|
|
315
|
+
- Do not rely on breakpoints to "fix" separability; use D5 stroke reinforcement where needed.
|
|
316
|
+
- Avoid large typography scale jumps; prefer continuous scaling and validate reading metrics (D6).
|
|
317
|
+
|
|
318
|
+
### 7.3 Responsive design principles
|
|
319
|
+
|
|
320
|
+
1. **Mobile-first:** start with `compact` layout, progressively enhance.
|
|
321
|
+
2. **Content-driven:** let content needs determine when to introduce panes.
|
|
322
|
+
3. **Density-aware:** larger screens can support higher information density (D2).
|
|
323
|
+
4. **Line-length conscious:** constrain reading width in `xlarge` to preserve D6 readability (45–75 characters optimal).
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
### 7.4 Orthogonal responsiveness strategy
|
|
327
|
+
|
|
328
|
+
Breakpoints (D1x) classify **available space**. Responsiveness strategy (D1y) classifies **how layout adapts**. Treat these as orthogonal axes:
|
|
329
|
+
|
|
330
|
+
- `intrinsic` — continuous adaptation via Flex wrap / intrinsic Grid / fluid constraints.
|
|
331
|
+
- `container` — adaptation based on container size (`@container`), for portable components.
|
|
332
|
+
- `breakpoint` — adaptation based on viewport/window size classes, for top-level IA shifts.
|
|
333
|
+
- `hybrid` — intrinsic-first, with breakpoint/container steps only where meaningfully needed.
|
|
334
|
+
|
|
335
|
+
In CEM, the default posture is **intrinsic-first**; introduce breakpoint-driven behavior primarily when the **meaning** of layout changes (e.g., one-pane → two-pane), not to compensate for rigid mechanics. See [`cem-responsive.md`](./cem-responsive.md).
|
|
336
|
+
|
|
337
|
+
### 7.5 Component mapping checklist
|
|
338
|
+
|
|
339
|
+
Use this table to choose *where* D1x breakpoints apply and *which* D1y strategy to prefer. The “Responsiveness strategy” column is normative for component recipes; other columns are guidance.
|
|
340
|
+
|
|
341
|
+
| Component family | Breakpoint role (D1x) | Preferred query context | Responsiveness strategy (D1y) | Notes |
|
|
342
|
+
|---------------------------------------------------|---------------------------------|--------------------------------------|---------------------------------|-------------------------------------------------------------------------------------|
|
|
343
|
+
| App shell navigation (bottom bar / rail / drawer) | **Yes** (archetype switch) | Viewport/window | `breakpoint` / `hybrid` | Breakpoints change navigation meaning; keep D2 invariants. |
|
|
344
|
+
| Master–detail / list–detail panes | **Yes** (introduce second pane) | Viewport/window, sometimes container | `hybrid` | Use intrinsic sizing within panes; breakpoint decides when the second pane exists. |
|
|
345
|
+
| Card listings / galleries | Usually **No** | Container first | `intrinsic` / `container` | Prefer wrap or `auto-fit/minmax()`; avoid fixed column counts. |
|
|
346
|
+
| Forms (field groups) | Rare | Container first | `intrinsic` | Prefer flowing groups and `min()`/`max()` widths; breakpoint only for major reflow. |
|
|
347
|
+
| Data tables / dense comparison grids | Sometimes | Container first | `container` / `hybrid` | Container size often determines column visibility / stacking. |
|
|
348
|
+
| Side panels, filters, inspector panes | Sometimes | Container first | `container` / `hybrid` | The panel width (not viewport) is the driver in split UI. |
|
|
349
|
+
| Overlays (dialogs, popovers, tooltips) | No (typically) | Container/anchor | `intrinsic` | Size to content and coupling; do not key to viewport breakpoints. |
|
|
350
|
+
|
|
351
|
+
### 7.6 Intrinsic layouts disclaimer (flex/grid, wrapping, and container-first responsiveness)
|
|
352
|
+
|
|
353
|
+
CEM breakpoints define **semantic ranges of available space**. They do **not** mandate a fixed grid, fixed column count, or device-class assumptions.
|
|
354
|
+
|
|
355
|
+
- **Avoid fixed-column contracts on wide screens:** ultra-wide and uncommon aspect ratios (including very wide displays such as ~8K×1K) can invalidate “N columns” assumptions even when total pixels are high.
|
|
356
|
+
- **Prefer intrinsic, continuously-adaptive layouts first:** use Flexbox with `flex-wrap`, and/or Grid patterns like `repeat(auto-fit, minmax(...))` so layout adapts smoothly to the *actual* space available.
|
|
357
|
+
- **Prefer container width over viewport width for components:** in split panes, sidebars, cards, and embedded widgets, container queries produce more reliable behavior than viewport media queries.
|
|
358
|
+
|
|
359
|
+
A well-designed intrinsic layout can span **small mobile → large desktop → ultra-wide** without introducing additional breakpoint classes. Use breakpoints primarily when the **meaning** of layout changes (e.g., introducing a second pane), not to compensate for rigid layout mechanics.
|
|
360
|
+
|
|
361
|
+
### 7.7 Content-first, component-first responsiveness patterns (informative)
|
|
362
|
+
|
|
363
|
+
- **Content-driven breakpoints:** introduce a breakpoint only when content “breaks” (wrapping, truncation, unusable density), not to match a device list.
|
|
364
|
+
- **Component breakpoints via container queries:** make components portable; they should adapt to the size of the region they are placed into.
|
|
365
|
+
- **Continuous scaling where possible:** use modern CSS constraints (e.g., `clamp()`) to avoid hard jumps in spacing/typography.
|
|
366
|
+
|
|
367
|
+
### 7.8 Should CEM introduce a token value like `responsive`?
|
|
368
|
+
|
|
369
|
+
Generally **no**: “responsive” is an implementation property (intrinsic vs breakpoint-driven), not a breakpoint range.
|
|
370
|
+
|
|
371
|
+
If you need to mark that a layout is intended to be **intrinsic-responsive** (wrapping/container-first), prefer a **documentation tag** in component mapping (e.g., `strategy: intrinsic`) or an adapter/build-time flag. Avoid encoding “responsive” as an extra breakpoint value because it conflates *method* with *available space semantics*.
|
|
372
|
+
|
|
373
|
+
---
|
|
374
|
+
|
|
375
|
+
## 8. Container queries extension
|
|
376
|
+
|
|
377
|
+
Some UIs (split panes, sidebars, embedded widgets) need size classes based on **container width**, not viewport.
|
|
378
|
+
|
|
379
|
+
### 8.1 Container bounds (optional)
|
|
380
|
+
|
|
381
|
+
```css
|
|
382
|
+
:root {
|
|
383
|
+
/* Reuse the same semantic lattice; container queries are about measurement context. */
|
|
384
|
+
--cem-cq-epsilon: var(--cem-bp-epsilon);
|
|
385
|
+
|
|
386
|
+
--cem-cq-width-medium-min: 600px;
|
|
387
|
+
--cem-cq-width-expanded-min: 840px;
|
|
388
|
+
--cem-cq-width-large-min: 1200px;
|
|
389
|
+
|
|
390
|
+
/* Derived max (exclusive) */
|
|
391
|
+
--cem-cq-width-compact-max: calc(var(--cem-cq-width-medium-min) - var(--cem-cq-epsilon));
|
|
392
|
+
--cem-cq-width-medium-max: calc(var(--cem-cq-width-expanded-min) - var(--cem-cq-epsilon));
|
|
393
|
+
--cem-cq-width-expanded-max: calc(var(--cem-cq-width-large-min) - var(--cem-cq-epsilon));
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### 8.2 Container query usage
|
|
399
|
+
|
|
400
|
+
```css
|
|
401
|
+
.container {
|
|
402
|
+
container-type: inline-size;
|
|
403
|
+
container-name: card;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
@container card (max-width: 599.98px) {
|
|
407
|
+
/* compact container layout */
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
@container card (min-width: 600px) and (max-width: 839.98px) {
|
|
411
|
+
/* medium container layout */
|
|
412
|
+
}
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
**Material UI (optional): container query adapter and shorthand**
|
|
417
|
+
|
|
418
|
+
Material UI exposes `theme.containerQueries` and an `sx` shorthand using `@<size>` / `@<size>/<name>`.
|
|
419
|
+
|
|
420
|
+
```ts
|
|
421
|
+
// Theme API (breakpoint keys or unitless widths)
|
|
422
|
+
theme.containerQueries.up('sm'); // => '@container (min-width: 600px)'
|
|
423
|
+
theme.containerQueries('card').up(600); // => '@container card (min-width: 600px)'
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
```tsx
|
|
427
|
+
// sx shorthand (unitless sizes render as px; `@500px` is invalid syntax)
|
|
428
|
+
<Box
|
|
429
|
+
sx={{
|
|
430
|
+
'@': { p: 1 }, // 0px
|
|
431
|
+
'@600': { p: 2 }, // 600px
|
|
432
|
+
'@840': { p: 3 }, // 840px
|
|
433
|
+
}}
|
|
434
|
+
/>
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
### 8.3 Normative requirements
|
|
439
|
+
|
|
440
|
+
- Container bounds MUST map to the same semantic vocabulary (`compact/medium/expanded/...`).
|
|
441
|
+
- Consumer meaning MUST remain consistent between viewport and container contexts.
|
|
442
|
+
|
|
443
|
+
---
|
|
444
|
+
|
|
445
|
+
## 9. Governance and versioning
|
|
446
|
+
|
|
447
|
+
### 9.1 Breaking (major)
|
|
448
|
+
|
|
449
|
+
Treat as breaking if you:
|
|
450
|
+
|
|
451
|
+
- Rename/remove any canonical range name (`compact`, `medium`, `expanded`, `large`, `xlarge`).
|
|
452
|
+
- Reorder ranges or violate monotonic bounds.
|
|
453
|
+
- Change the meaning of a range (e.g., `medium` stops being the "two-pane possible" band).
|
|
454
|
+
|
|
455
|
+
### 9.2 Non-breaking (minor/patch)
|
|
456
|
+
|
|
457
|
+
Treat as non-breaking if you:
|
|
458
|
+
|
|
459
|
+
- Tune numeric bounds while preserving semantics and ordering.
|
|
460
|
+
- Add optional container-query bounds that map to the same range vocabulary.
|
|
461
|
+
- Clarify mapping guidance and examples.
|
|
462
|
+
|
|
463
|
+
---
|
|
464
|
+
|
|
465
|
+
## 10. Canonical token summary
|
|
466
|
+
|
|
467
|
+
### 10.1 Required (width basis)
|
|
468
|
+
|
|
469
|
+
| Token | Category | Required | Meaning |
|
|
470
|
+
|-------------------------------|----------|----------|--------------------------------------|
|
|
471
|
+
| `--cem-bp-width-compact-max` | Basis | Yes | upper bound for `compact` width |
|
|
472
|
+
| `--cem-bp-width-medium-min` | Basis | Yes | lower bound for `medium` width |
|
|
473
|
+
| `--cem-bp-width-medium-max` | Basis | Yes | upper bound for `medium` width |
|
|
474
|
+
| `--cem-bp-width-expanded-min` | Basis | Yes | lower bound for `expanded` width |
|
|
475
|
+
|
|
476
|
+
### 10.2 Recommended (extended width)
|
|
477
|
+
|
|
478
|
+
| Token | Category | Required | Meaning |
|
|
479
|
+
|--------------------------------|----------|----------|--------------------------------------|
|
|
480
|
+
| `--cem-bp-width-expanded-max` | Basis | Optional | upper bound for `expanded` width |
|
|
481
|
+
| `--cem-bp-width-large-min` | Basis | Optional | lower bound for `large` width |
|
|
482
|
+
| `--cem-bp-width-large-max` | Basis | Optional | upper bound for `large` width |
|
|
483
|
+
| `--cem-bp-width-xlarge-min` | Basis | Optional | lower bound for `xlarge` width |
|
|
484
|
+
|
|
485
|
+
### 10.3 Recommended (height basis)
|
|
486
|
+
|
|
487
|
+
| Token | Category | Required | Meaning |
|
|
488
|
+
|--------------------------------|----------|----------|--------------------------------------|
|
|
489
|
+
| `--cem-bp-height-compact-max` | Basis | Optional | upper bound for `compact` height |
|
|
490
|
+
| `--cem-bp-height-medium-min` | Basis | Optional | lower bound for `medium` height |
|
|
491
|
+
| `--cem-bp-height-medium-max` | Basis | Optional | upper bound for `medium` height |
|
|
492
|
+
| `--cem-bp-height-expanded-min` | Basis | Optional | lower bound for `expanded` height |
|
|
493
|
+
|
|
494
|
+
---
|
|
495
|
+
|
|
496
|
+
## 11. References
|
|
497
|
+
|
|
498
|
+
### Internal
|
|
499
|
+
|
|
500
|
+
- **D1. Space & Rhythm** (`cem-dimension.md`) — spacing scale and layout gaps
|
|
501
|
+
- **D2. Coupling & Compactness** (`cem-coupling.md`) — density invariants across ranges
|
|
502
|
+
- **D5. Stroke & Separation** (`cem-stroke.md`) — separability reinforcement under density pressure
|
|
503
|
+
- **D6. Typography (Voice & Reading)** (`cem-voice-fonts-typography.md`) — reading constraints and scanning patterns
|
|
504
|
+
- **D1y. Responsiveness Strategy** (`cem-responsive.md`) — intrinsic/container/breakpoint strategy vocabulary
|
|
505
|
+
|
|
506
|
+
### External
|
|
507
|
+
|
|
508
|
+
- [Material Design 3 — Window size classes](https://m3.material.io/foundations/layout/applying-layout/window-size-classes)
|
|
509
|
+
- [Android Developers — Use window size classes](https://developer.android.com/develop/ui/compose/layouts/adaptive/use-window-size-classes)
|
|
510
|
+
- [Material UI — Breakpoints](https://mui.com/material-ui/customization/breakpoints/)
|
|
511
|
+
- [Material UI — Container queries](https://mui.com/material-ui/customization/container-queries/)
|
|
512
|
+
- [MDN — CSS container queries](https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Containment/Container_queries)
|
|
513
|
+
- [web.dev — Responsive web design basics (content-driven breakpoints)](https://web.dev/articles/responsive-web-design-basics)
|
|
514
|
+
- [Jen Simmons — Designing Intrinsic Layouts (intrinsic design patterns)](https://talks.jensimmons.com/15TjNW/designing-intrinsic-layouts)
|
|
515
|
+
- [ModernCSS — Contextual Spacing for Intrinsic Web Design (`clamp()`)](https://moderncss.dev/contextual-spacing-for-intrinsic-web-design/)
|
|
516
|
+
|
|
517
|
+
---
|
|
518
|
+
|
|
519
|
+
*This spec is the canonical D1x contract for CEM responsive breakpoints and adapter mapping.*
|