You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+30Lines changed: 30 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -104,6 +104,33 @@ Content is centered at `720px` by default — no class needed. These opt-in util
104
104
|`.visually-hidden`| Hidden visually, accessible to screen readers |
105
105
|`.overflow-auto`| Scrollable container |
106
106
107
+
## Third-Party Component Isolation
108
+
109
+
nimble.css styles can conflict with third-party components (datatables, rich text editors, etc.) that expect unstyled elements. Add `.no-nimble` to opt out of nimble's component styles inside a subtree:
**What still applies:** Reset, colors/custom properties, body grid, layout utilities (`.fluid`, `.full-bleed`, `.wide`, `.container`), and print styles. This means layout classes work on `.no-nimble` elements.
125
+
126
+
This works via CSS `@scope` (Chrome 118+, Safari 17.4+, Firefox 128+). To disable scoping entirely (smaller output, no opt-out):
127
+
128
+
```scss
129
+
@use'@leftium/nimble.css/scss'with (
130
+
$exclude-selector: null
131
+
);
132
+
```
133
+
107
134
## Customization
108
135
109
136
### CSS Custom Properties
@@ -160,6 +187,9 @@ Build a CSS file with new defaults. SCSS-unique options listed first; the rest m
160
187
$enable-switch: true,
161
188
$enable-details: true,
162
189
190
+
// Scoping (set to null to disable @scope wrapping)
191
+
$exclude-selector: '.no-nimble',
192
+
163
193
// Surface fine-tuning
164
194
$surface-chroma: 0.002,
165
195
$surface-light-base: 0.985, // light mode base lightness
The `_scopeable.scss` module uses `meta.load-css()` to load all scopeable partials, allowing the entry point (`nimble.scss`) to conditionally wrap them in `@scope`:
149
+
150
+
```scss
151
+
@if$exclude-selector {
152
+
@scope (:root) to (#{$exclude-selector}) {
153
+
@includescopeable.load;
154
+
}
155
+
} @else {
156
+
@includescopeable.load;
157
+
}
158
+
```
159
+
160
+
Layout utilities are intentionally global because they interact with the body grid (e.g., `.full-bleed` sets `grid-column: 1 / -1`). An element with `class="no-nimble full-bleed"` should still participate in the body grid layout even though nimble's component styles (typography, forms, etc.) don't apply inside it.
161
+
162
+
See [Section 15.2](#152-third-party-component-isolation-no-nimble-opt-out) for usage details and design rationale.
163
+
164
+
### 3.6 Base Reset
133
165
134
166
nimble.css embeds a trimmed version of **sanitize.css** as its reset foundation because:
135
167
@@ -863,9 +895,13 @@ Basic print rules: force black-on-white, show link URLs, prevent orphaned headin
863
895
864
896
## 10. Utility Classes
865
897
866
-
nimble.css includes a**minimal**set of utility classes. These are the only classes nimble.css provides:
898
+
nimble.css includes a**minimal**set of utility classes. These are the only classes nimble.css provides.
899
+
900
+
Utilities are split into **global** (always apply, including on `.no-nimble` elements) and **scoped** (excluded inside `.no-nimble` subtrees). See [Section3.5](#35-scoping-architecture-global-vs-scopeable) for the architectural rationale.
867
901
868
-
### 10.1 Layout
902
+
### 10.1 Layout (Global)
903
+
904
+
These interact with the bodygrid and must work everywhere, including on `.no-nimble` elements:
$exclude-selector: null, // disable @scope wrapping (no .no-nimble opt-out)
1030
1084
$content-width: 800px,
1031
1085
);
1032
1086
```
@@ -1128,22 +1182,61 @@ Lessons from MVP.css, new.css, and HN discussions:
1128
1182
-**Pure classless is insufficient** for real-world use. You need at least: a way to distinguish primary/secondary buttons, striped tables, layout modes, and full-bleed content.
1129
1183
-**Minimum viable classes**: nimble.css uses ~8 classes total. Every class has a clear, non-overlapping purpose.
CSS cascade layers solve most specificity conflicts with third-party components (Svelte scoped styles, web components, etc.) because unlayered styles always beat layered styles. However, nimble.css's element styles can still "fill in" CSS properties that a component never explicitly sets, subtly changing its appearance.
1134
1188
1135
-
For the rare cases where this matters, use `revert-layer` to undo nimble's styles inside a component wrapper:
1189
+
nimble.css provides a built-in opt-out mechanism using CSS `@scope`. Component-level styles (typography, links, buttons, forms, tables, code, media, article, details, dialog, and non-layout utilities) are wrapped in:
1136
1190
1137
1191
```css
1138
-
/* Reset nimble styles inside a specific component */
1139
-
.datatable-wrapperth,
1140
-
.datatable-wrappertd,
1141
-
.datatable-wrapperinput {
1142
-
all: revert-layer;
1192
+
@scope (:root) to (.no-nimble) {
1193
+
/* component styles */
1143
1194
}
1144
1195
```
1145
1196
1146
-
`revert-layer` (supported in all browsers that support `@layer`) reverts properties to the value they'd have without the current layer. This is documented as a recommended pattern, not built into nimble.css itself.
1197
+
This means nimble's component styles apply everywhere **except** inside elements with `class="no-nimble"`. Document-level styles (reset, colors, body grid, layout utilities, print) remain global — they always apply.
1198
+
1199
+
**Usage:**
1200
+
1201
+
```html
1202
+
<!-- nimble styles apply here -->
1203
+
<mainclass="fluid full-bleed">
1204
+
<h1>Styled by nimble</h1>
1205
+
1206
+
<!-- nimble component styles do NOT apply inside this element -->
1207
+
<divclass="no-nimble full-bleed">
1208
+
<ThirdPartyDataTable />
1209
+
</div>
1210
+
</main>
1211
+
```
1212
+
1213
+
Note that layout utilities (`.fluid`, `.full-bleed`, `.wide`, `.container`) are global, so they work on `.no-nimble` elements — you can still control layout while opting out of nimble's component styling.
1214
+
1215
+
**SCSS configuration:**
1216
+
1217
+
The exclude selector is configurable via `$exclude-selector`:
Set to `null` to disable scoping entirely (all styles apply globally, no `@scope` wrapper emitted):
1226
+
1227
+
```scss
1228
+
@use'@leftium/nimble.css/scss'with (
1229
+
$exclude-selector: null // no @scope — smaller output, no opt-out
1230
+
);
1231
+
```
1232
+
1233
+
**Size overhead:** The `@scope` wrapper adds ~1.7 KB (~6%) to the minified output. After gzip/brotli compression, the overhead is negligible.
1234
+
1235
+
**Browser support:**`@scope` is supported in Chrome 118+, Safari 17.4+, and Firefox 128+ — the same modern browser targets nimble.css already requires for `light-dark()` and oklch.
1236
+
1237
+
**Why not `revert-layer`?** Early prototyping used `all: revert-layer` on wrapper elements, but this approach was either too aggressive (broke third-party component layout by reverting grid/flex properties) or lost specificity battles with scoped component styles. `@scope` cleanly prevents nimble's styles from entering the subtree at all.
1238
+
1239
+
**Why not opt-in (Pico's `$parent-selector` approach)?** Pico CSS supports `$parent-selector: '.pico'` so styles only apply inside a class. nimble.css's body grid requires rules on `body` itself, which can't be nested inside a class. Opt-out (default-on with `.no-nimble` escape hatch) avoids this architectural conflict.
1147
1240
1148
1241
### 15.3 Why SCSS?
1149
1242
@@ -1343,6 +1436,7 @@ Utilities, extended demo, and final validation.
1343
1436
- Refactor (date/time dedup, extract progress/meter/select to add-on sub-bundles): `nimble.css` 24,963 B / `nimble.min.css` 19,355 B / gzipped ~4.4 KB / brotli ~3.9 KB
1344
1437
- Flip model (nimble = core without extras, nimble-full = everything): `nimble.min.css` 15,796 B / gzipped ~3.8 KB / brotli ~3.3 KB
1345
1438
- Fix progress by excluding from reset `background-repeat` rule; progress add-on fully opt-in: `nimble.min.css` 15,809 B / gzipped ~3.8 KB / brotli ~3.3 KB
1439
+
- Add `@scope`-based `.no-nimble` opt-out: split SCSS into global vs scopeable modules; component styles wrapped in `@scope (:root) to (.no-nimble)`; layout utilities kept global: `nimble.min.css` 22,655 B / `nimble-core.min.css` 18,978 B
0 commit comments