defuss-shadcn 0.8.1 → 0.8.2
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/AGENTS.md +10 -3
- package/Makefile +4 -1
- package/README.md +3 -2
- package/dist/SKILL.md +1 -1
- package/dist/SKILL_tpl.md +1 -1
- package/dist/architecture_tpl.html +1 -2
- package/dist/components/accordion/accordion.css +12 -5
- package/dist/components/accordion/accordion.js +16 -0
- package/dist/components/accordion/component-skill.md +1 -0
- package/dist/components/alert-dialog/alert-dialog.css +8 -0
- package/dist/components/alert-dialog/component-skill.md +1 -0
- package/dist/components/collapsible/collapsible.css +11 -3
- package/dist/components/combobox/combobox.css +40 -0
- package/dist/components/combobox/combobox.js +24 -0
- package/dist/components/combobox/component-skill.md +2 -0
- package/dist/components/command/command.css +11 -0
- package/dist/components/command/component-skill.md +1 -0
- package/dist/components/dialog/component-skill.md +1 -0
- package/dist/components/dialog/dialog.css +12 -0
- package/dist/components/image/component-skill.md +1 -0
- package/dist/components/image/image.css +8 -0
- package/dist/components/sheet/component-skill.md +1 -0
- package/dist/components/sheet/sheet.css +8 -0
- package/dist/components/sidebar/component-skill.md +1 -1
- package/dist/components/sidebar/sidebar.css +8 -0
- package/dist/components/tree-view/tree-view.css +11 -3
- package/dist/components/typography/component-skill.md +2 -2
- package/dist/components/typography/typography.css +3 -3
- package/dist/documentation/accessibility.html +1 -2
- package/dist/documentation/accordion.html +28 -163
- package/dist/documentation/alert-dialog.html +11 -48
- package/dist/documentation/alert.html +1 -59
- package/dist/documentation/animations.html +1 -2
- package/dist/documentation/architecture.html +18 -24
- package/dist/documentation/avatar.html +1 -62
- package/dist/documentation/badge.html +1 -43
- package/dist/documentation/blog.html +1 -87
- package/dist/documentation/brand-logos.html +1 -60
- package/dist/documentation/breadcrumb.html +1 -114
- package/dist/documentation/button-group.html +1 -70
- package/dist/documentation/button.html +2 -86
- package/dist/documentation/calendar.html +1 -63
- package/dist/documentation/card.html +1 -124
- package/dist/documentation/carousel.html +1 -156
- package/dist/documentation/cascade-layers.html +1 -2
- package/dist/documentation/changelog.html +20 -2
- package/dist/documentation/checkbox.html +1 -103
- package/dist/documentation/collapsible.html +12 -38
- package/dist/documentation/color-picker.html +1 -32
- package/dist/documentation/combobox.html +69 -158
- package/dist/documentation/command.html +16 -95
- package/dist/documentation/component-skills.html +1 -2
- package/dist/documentation/context-menu.html +1 -24
- package/dist/documentation/css/layout.css +23 -33
- package/dist/documentation/dark-mode.html +1 -2
- package/dist/documentation/data-attribute-api.html +1 -2
- package/dist/documentation/date-picker.html +1 -43
- package/dist/documentation/dialog.html +14 -102
- package/dist/documentation/dropdown.html +1 -153
- package/dist/documentation/es-modules.html +1 -2
- package/dist/documentation/faq.html +2 -70
- package/dist/documentation/feature-details.html +1 -85
- package/dist/documentation/file-input.html +1 -40
- package/dist/documentation/form.html +1 -137
- package/dist/documentation/get-in-touch.html +1 -65
- package/dist/documentation/hero.html +1 -57
- package/dist/documentation/icon.html +1 -162
- package/dist/documentation/image.html +13 -175
- package/dist/documentation/index.html +2 -3
- package/dist/documentation/input.html +1 -140
- package/dist/documentation/installation.html +1 -2
- package/dist/documentation/js/layout.js +46 -3
- package/dist/documentation/js/search-index.js +1 -1
- package/dist/documentation/js/site.js +74 -72
- package/dist/documentation/label.html +1 -101
- package/dist/documentation/native-web-apis.html +1 -2
- package/dist/documentation/navigation-menu.html +1 -79
- package/dist/documentation/newsletter.html +1 -64
- package/dist/documentation/number-input.html +1 -30
- package/dist/documentation/pagination.html +1 -112
- package/dist/documentation/popover.html +1 -81
- package/dist/documentation/pricing.html +1 -109
- package/dist/documentation/product-showcase.html +1 -91
- package/dist/documentation/progress.html +1 -31
- package/dist/documentation/radio.html +1 -143
- package/dist/documentation/scroll-area.html +1 -47
- package/dist/documentation/select.html +1 -58
- package/dist/documentation/separator.html +1 -96
- package/dist/documentation/sheet.html +10 -112
- package/dist/documentation/sidebar.html +12 -177
- package/dist/documentation/site-footer.html +1 -82
- package/dist/documentation/site-header.html +1 -98
- package/dist/documentation/skeleton.html +1 -37
- package/dist/documentation/slider.html +1 -112
- package/dist/documentation/sortable.html +1 -126
- package/dist/documentation/spinner.html +1 -37
- package/dist/documentation/statistic.html +1 -48
- package/dist/documentation/stats.html +1 -70
- package/dist/documentation/steps.html +1 -186
- package/dist/documentation/switch.html +1 -80
- package/dist/documentation/table.html +1 -54
- package/dist/documentation/tabs.html +1 -143
- package/dist/documentation/testimonials.html +1 -81
- package/dist/documentation/textarea.html +1 -44
- package/dist/documentation/theming.html +1 -2
- package/dist/documentation/timeline.html +1 -48
- package/dist/documentation/toast.html +1 -114
- package/dist/documentation/toggle-group.html +1 -154
- package/dist/documentation/toggle.html +1 -90
- package/dist/documentation/toolbar.html +1 -71
- package/dist/documentation/tooltip.html +1 -99
- package/dist/documentation/tree-view.html +12 -55
- package/dist/documentation/typography.html +7 -120
- package/docs/404.html +340 -0
- package/docs/accessibility.html +1 -2
- package/docs/accordion.html +28 -163
- package/docs/alert-dialog.html +11 -48
- package/docs/alert.html +1 -59
- package/docs/animations.html +1 -2
- package/docs/architecture.html +18 -24
- package/docs/avatar.html +1 -62
- package/docs/badge.html +1 -43
- package/docs/blog.html +1 -87
- package/docs/brand-logos.html +1 -60
- package/docs/breadcrumb.html +1 -114
- package/docs/button-group.html +1 -70
- package/docs/button.html +2 -86
- package/docs/calendar.html +1 -63
- package/docs/card.html +1 -124
- package/docs/carousel.html +1 -156
- package/docs/cascade-layers.html +1 -2
- package/docs/changelog.html +20 -2
- package/docs/checkbox.html +1 -103
- package/docs/collapsible.html +12 -38
- package/docs/color-picker.html +1 -32
- package/docs/combobox.html +69 -158
- package/docs/command.html +16 -95
- package/docs/component-skills.html +1 -2
- package/docs/context-menu.html +1 -24
- package/docs/css/layout.css +23 -33
- package/docs/dark-mode.html +1 -2
- package/docs/data-attribute-api.html +1 -2
- package/docs/date-picker.html +1 -43
- package/docs/dialog.html +14 -102
- package/docs/dropdown.html +1 -153
- package/docs/es-modules.html +1 -2
- package/docs/faq.html +2 -70
- package/docs/feature-details.html +1 -85
- package/docs/file-input.html +1 -40
- package/docs/form.html +1 -137
- package/docs/get-in-touch.html +1 -65
- package/docs/hero.html +1 -57
- package/docs/icon.html +1 -162
- package/docs/image.html +13 -175
- package/docs/index.html +2 -3
- package/docs/input.html +1 -140
- package/docs/installation.html +1 -2
- package/docs/js/layout.js +46 -3
- package/docs/js/search-index.js +1 -1
- package/docs/js/site.js +74 -72
- package/docs/label.html +1 -101
- package/docs/native-web-apis.html +1 -2
- package/docs/navigation-menu.html +1 -79
- package/docs/newsletter.html +1 -64
- package/docs/number-input.html +1 -30
- package/docs/pagination.html +1 -112
- package/docs/popover.html +1 -81
- package/docs/pricing.html +1 -109
- package/docs/product-showcase.html +1 -91
- package/docs/progress.html +1 -31
- package/docs/radio.html +1 -143
- package/docs/scroll-area.html +1 -47
- package/docs/select.html +1 -58
- package/docs/separator.html +1 -96
- package/docs/sheet.html +10 -112
- package/docs/sidebar.html +12 -177
- package/docs/site-footer.html +1 -82
- package/docs/site-header.html +1 -98
- package/docs/skeleton.html +1 -37
- package/docs/slider.html +1 -112
- package/docs/sortable.html +1 -126
- package/docs/spinner.html +1 -37
- package/docs/statistic.html +1 -48
- package/docs/stats.html +1 -70
- package/docs/steps.html +1 -186
- package/docs/switch.html +1 -80
- package/docs/table.html +1 -54
- package/docs/tabs.html +1 -143
- package/docs/testimonials.html +1 -81
- package/docs/textarea.html +1 -44
- package/docs/theming.html +1 -2
- package/docs/timeline.html +1 -48
- package/docs/toast.html +1 -114
- package/docs/toggle-group.html +1 -154
- package/docs/toggle.html +1 -90
- package/docs/toolbar.html +1 -71
- package/docs/tooltip.html +1 -99
- package/docs/tree-view.html +12 -55
- package/docs/typography.html +7 -120
- package/package.json +2 -1
- package/scripts/lib/arch-page.ts +32 -9
- package/scripts/lib/mirror.ts +8 -0
- package/scripts/purge-cdn.ts +71 -0
- package/scripts/sync-docs.ts +1 -2
- package/scripts/verify.ts +4 -3
- package/src/SKILL.md +1 -1
- package/src/SKILL_tpl.md +1 -1
- package/src/architecture_tpl.html +1 -2
- package/src/components/accordion/accordion.css +12 -5
- package/src/components/accordion/accordion.ts +13 -0
- package/src/components/accordion/component-skill.md +1 -0
- package/src/components/alert-dialog/alert-dialog.css +8 -0
- package/src/components/alert-dialog/component-skill.md +1 -0
- package/src/components/collapsible/collapsible.css +11 -3
- package/src/components/combobox/combobox.css +40 -0
- package/src/components/combobox/combobox.ts +25 -0
- package/src/components/combobox/component-skill.md +2 -0
- package/src/components/command/command.css +11 -0
- package/src/components/command/component-skill.md +1 -0
- package/src/components/dialog/component-skill.md +1 -0
- package/src/components/dialog/dialog.css +12 -0
- package/src/components/image/component-skill.md +1 -0
- package/src/components/image/image.css +8 -0
- package/src/components/sheet/component-skill.md +1 -0
- package/src/components/sheet/sheet.css +8 -0
- package/src/components/sidebar/component-skill.md +1 -1
- package/src/components/sidebar/sidebar.css +8 -0
- package/src/components/tree-view/tree-view.css +11 -3
- package/src/components/typography/component-skill.md +2 -2
- package/src/components/typography/typography.css +3 -3
- package/src/documentation/accessibility.html +1 -2
- package/src/documentation/accordion.html +28 -163
- package/src/documentation/alert-dialog.html +11 -48
- package/src/documentation/alert.html +1 -59
- package/src/documentation/animations.html +1 -2
- package/src/documentation/architecture.html +18 -24
- package/src/documentation/avatar.html +1 -62
- package/src/documentation/badge.html +1 -43
- package/src/documentation/blog.html +1 -87
- package/src/documentation/brand-logos.html +1 -60
- package/src/documentation/breadcrumb.html +1 -114
- package/src/documentation/button-group.html +1 -70
- package/src/documentation/button.html +2 -86
- package/src/documentation/calendar.html +1 -63
- package/src/documentation/card.html +1 -124
- package/src/documentation/carousel.html +1 -156
- package/src/documentation/cascade-layers.html +1 -2
- package/src/documentation/changelog.html +20 -2
- package/src/documentation/checkbox.html +1 -103
- package/src/documentation/collapsible.html +12 -38
- package/src/documentation/color-picker.html +1 -32
- package/src/documentation/combobox.html +69 -158
- package/src/documentation/command.html +16 -95
- package/src/documentation/component-skills.html +1 -2
- package/src/documentation/context-menu.html +1 -24
- package/src/documentation/css/layout.css +23 -33
- package/src/documentation/dark-mode.html +1 -2
- package/src/documentation/data-attribute-api.html +1 -2
- package/src/documentation/date-picker.html +1 -43
- package/src/documentation/dialog.html +14 -102
- package/src/documentation/dropdown.html +1 -153
- package/src/documentation/es-modules.html +1 -2
- package/src/documentation/faq.html +2 -70
- package/src/documentation/feature-details.html +1 -85
- package/src/documentation/file-input.html +1 -40
- package/src/documentation/form.html +1 -137
- package/src/documentation/get-in-touch.html +1 -65
- package/src/documentation/hero.html +1 -57
- package/src/documentation/icon.html +1 -162
- package/src/documentation/image.html +13 -175
- package/src/documentation/index.html +2 -3
- package/src/documentation/input.html +1 -140
- package/src/documentation/installation.html +1 -2
- package/src/documentation/js/layout.ts +48 -4
- package/src/documentation/js/search-index.js +1 -1
- package/src/documentation/js/site.ts +70 -75
- package/src/documentation/label.html +1 -101
- package/src/documentation/native-web-apis.html +1 -2
- package/src/documentation/navigation-menu.html +1 -79
- package/src/documentation/newsletter.html +1 -64
- package/src/documentation/number-input.html +1 -30
- package/src/documentation/pagination.html +1 -112
- package/src/documentation/popover.html +1 -81
- package/src/documentation/pricing.html +1 -109
- package/src/documentation/product-showcase.html +1 -91
- package/src/documentation/progress.html +1 -31
- package/src/documentation/radio.html +1 -143
- package/src/documentation/scroll-area.html +1 -47
- package/src/documentation/select.html +1 -58
- package/src/documentation/separator.html +1 -96
- package/src/documentation/sheet.html +10 -112
- package/src/documentation/sidebar.html +12 -177
- package/src/documentation/site-footer.html +1 -82
- package/src/documentation/site-header.html +1 -98
- package/src/documentation/skeleton.html +1 -37
- package/src/documentation/slider.html +1 -112
- package/src/documentation/sortable.html +1 -126
- package/src/documentation/spinner.html +1 -37
- package/src/documentation/statistic.html +1 -48
- package/src/documentation/stats.html +1 -70
- package/src/documentation/steps.html +1 -186
- package/src/documentation/switch.html +1 -80
- package/src/documentation/table.html +1 -54
- package/src/documentation/tabs.html +1 -143
- package/src/documentation/testimonials.html +1 -81
- package/src/documentation/textarea.html +1 -44
- package/src/documentation/theming.html +1 -2
- package/src/documentation/timeline.html +1 -48
- package/src/documentation/toast.html +1 -114
- package/src/documentation/toggle-group.html +1 -154
- package/src/documentation/toggle.html +1 -90
- package/src/documentation/toolbar.html +1 -71
- package/src/documentation/tooltip.html +1 -99
- package/src/documentation/tree-view.html +12 -55
- package/src/documentation/typography.html +7 -120
- package/tests/e2e/accordion.e2e.ts +47 -3
- package/tests/e2e/alert-dialog.e2e-fixture.html +6 -1
- package/tests/e2e/alert-dialog.e2e.ts +28 -0
- package/tests/e2e/combobox.e2e.ts +54 -0
- package/tests/e2e/command.e2e-fixture.html +3 -1
- package/tests/e2e/command.e2e.ts +24 -0
- package/tests/e2e/dialog.e2e-fixture.html +6 -1
- package/tests/e2e/dialog.e2e.ts +25 -0
- package/tests/e2e/image.e2e-fixture.html +3 -1
- package/tests/e2e/image.e2e.ts +27 -0
- package/tests/e2e/sheet.e2e-fixture.html +6 -1
- package/tests/e2e/sheet.e2e.ts +34 -0
- package/tests/e2e/sidebar.e2e-fixture.html +3 -0
- package/tests/e2e/sidebar.e2e.ts +31 -0
- package/tests/e2e/spinner.e2e.ts +5 -1
- package/tests/e2e/tabs.e2e.ts +5 -0
- package/tests/e2e/typography.e2e-fixture.html +1 -1
- package/tests/e2e/typography.e2e.ts +1 -1
- package/tests/ui.test.ts +84 -0
package/AGENTS.md
CHANGED
|
@@ -17,12 +17,18 @@ You are working on the **defuss-shadcn** design system repo.
|
|
|
17
17
|
The consumer-facing system lives in `dist/` — **it is generated**: edit sources in
|
|
18
18
|
`src/` (`bun run build` compiles `.ts` → `.js` and copies everything else 1:1).
|
|
19
19
|
`docs/` is also generated: **only the documentation site** (`dist/documentation/*` +
|
|
20
|
-
`robots.txt`/`sitemap.xml`
|
|
20
|
+
`robots.txt`/`sitemap.xml` + a `404.html` copy of `index.html` so GitHub Pages never
|
|
21
|
+
serves an empty page for dead links) published by GitHub Pages — its pages' `../components/…` and
|
|
21
22
|
`../theme/…` references are rewritten to the jsDelivr GitHub CDN (shared transform in
|
|
22
23
|
`scripts/lib/mirror.ts`), so the mirror carries no copies of the component assets.
|
|
23
24
|
Refresh it with `bun run docs` (`make build` does this automatically and `verify`
|
|
24
25
|
fails if the mirror drifts). Never edit `docs/` directly — like `dist/`, it is
|
|
25
26
|
deleted and rebuilt on every `bun run docs`.
|
|
27
|
+
Because the docs pages load their assets from jsDelivr `@latest` (newest git tag),
|
|
28
|
+
a fresh release keeps serving the **previous** release's CSS/JS until jsDelivr's
|
|
29
|
+
cache expires (12h edge, 7d browser). After `bun run deploy`, run
|
|
30
|
+
`bun run purge-cdn` (`make purge-cdn`) to force `@latest` to re-resolve to the
|
|
31
|
+
new tag immediately.
|
|
26
32
|
Never edit `dist/` directly; it is deleted and rebuilt on every build.
|
|
27
33
|
|
|
28
34
|
---
|
|
@@ -45,7 +51,7 @@ defuss-shadcn/
|
|
|
45
51
|
│ ├── css/docs-theme.css ← doc-site font overrides (not part of the system)
|
|
46
52
|
│ ├── css/layout.css ← doc-site layout (not part of the system)
|
|
47
53
|
│ ├── js/layout.js ← SPA router, <site-header>/<site-nav> web components
|
|
48
|
-
│ ├── js/site.js ← doc-site-only JS (tabs, copy buttons,
|
|
54
|
+
│ ├── js/site.js ← doc-site-only JS (tabs, copy buttons, code collapse)
|
|
49
55
|
│ ├── js/shiki-highlight.js ← Shiki-based syntax highlighting (ES module, CDN)
|
|
50
56
|
│ ├── js/themes.js ← tweakcn color theme presets (global THEMES array)
|
|
51
57
|
│ └── js/theme-switcher.js ← applies theme overrides to CSS custom properties
|
|
@@ -73,7 +79,8 @@ defuss-shadcn/
|
|
|
73
79
|
│ ├── sync-css-snippets.ts ← re-embed component CSS into doc pages after edits
|
|
74
80
|
│ ├── sync-js-snippets.ts ← re-embed component JS into doc pages after edits
|
|
75
81
|
│ ├── push.sh ← commit + push dev → main (non-release)
|
|
76
|
-
│
|
|
82
|
+
│ ├── deploy.sh ← release: version bump, changelog, tag, GitHub release
|
|
83
|
+
│ └── purge-cdn.ts ← purge jsDelivr @latest cache for all dist assets (run after deploy)
|
|
77
84
|
├── tests/ ← UI tests (Vitest browser mode + Playwright)
|
|
78
85
|
│ ├── helpers.ts ← loads real doc pages in a same-origin iframe
|
|
79
86
|
│ ├── ui.test.ts ← end-to-end tests of the actual site UI
|
package/Makefile
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# KISS: every target delegates to package.json so there is one source of truth.
|
|
3
3
|
|
|
4
4
|
.DEFAULT_GOAL := help
|
|
5
|
-
.PHONY: help setup dev test test-run coverage e2e lint verify screenshots build docs
|
|
5
|
+
.PHONY: help setup dev test test-run coverage e2e lint verify screenshots build docs purge-cdn
|
|
6
6
|
|
|
7
7
|
help: ## Show available targets
|
|
8
8
|
@grep -E '^[a-zA-Z0-9_-]+:.*## ' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*## "}; {printf " \033[36m%-12s\033[0m %s\n", $$1, $$2}'
|
|
@@ -35,6 +35,9 @@ typecheck: ## Strict tsc type-check of tests/ and scripts/ (no emit)
|
|
|
35
35
|
docs: ## Build, then mirror dist/ → docs/ for GitHub Pages
|
|
36
36
|
bun run docs
|
|
37
37
|
|
|
38
|
+
purge-cdn: ## Purge jsDelivr @latest cache for all dist assets (run after deploy)
|
|
39
|
+
bun run purge-cdn
|
|
40
|
+
|
|
38
41
|
# Full pipeline: fast checks first, compile, refresh screenshots (the verify
|
|
39
42
|
# gate requires them fresh vs. the new dist/), then gate + tests. Calls
|
|
40
43
|
# scripts/build.ts directly because `bun run build` would run verify BEFORE
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
[](https://www.npmjs.com/package/defuss-shadcn)
|
|
10
10
|
[](https://socket.dev/npm/package/defuss-shadcn)
|
|
11
11
|
|
|
12
|
-
**A UI component system that scales with AI.** Themeable components built on semantic HTML, modern CSS, and vanilla JavaScript. No framework. No build step for consumers — `dist/` is committed and ready to use as-is. The simplest possible foundation for AI-driven prototyping.
|
|
12
|
+
**A UI component system that scales with _local_ AI.** Themeable components built on semantic HTML, modern CSS, and vanilla JavaScript. No framework. No build step for consumers — `dist/` is committed and ready to use as-is. The simplest possible foundation for AI-driven prototyping.
|
|
13
13
|
|
|
14
14
|
41 of 68 components need no JavaScript — native HTML and modern CSS cover them entirely.
|
|
15
15
|
|
|
@@ -141,7 +141,8 @@ bun run test:run # run the UI test suite (headless Chromium)
|
|
|
141
141
|
|
|
142
142
|
`src/` is the authoring tree (`.ts` + html/css/md/fonts); `dist/` is its compiled, 1:1
|
|
143
143
|
mirror, committed and the only thing that ships. `docs/` is the generated **documentation
|
|
144
|
-
site** (only `dist/documentation/` + the SEO files
|
|
144
|
+
site** (only `dist/documentation/` + the SEO files + a `404.html` copy of `index.html` so
|
|
145
|
+
GitHub Pages never serves an empty page for dead links) that GitHub Pages publishes — the
|
|
145
146
|
pages' `../components/…` / `../theme/…` references are rewritten to the jsDelivr GitHub
|
|
146
147
|
CDN by the mirror ([scripts/lib/mirror.ts](scripts/lib/mirror.ts)), so docs/ carries no
|
|
147
148
|
copies of the component assets. Refresh with `bun run docs`, never edit it directly.
|
package/dist/SKILL.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
# defuss-shadcn — Agent Skill
|
|
6
6
|
|
|
7
|
-
**A UI component system that scales with AI.** Themeable components built on semantic HTML, modern CSS, and vanilla JavaScript. No framework, zero dependencies — `dist/` is committed and ready to use as-is. This file is the entry point for an AI agent integrating the library: read it once, then read only the component skills you need.
|
|
7
|
+
**A UI component system that scales with _local_ AI.** Themeable components built on semantic HTML, modern CSS, and vanilla JavaScript. No framework, zero dependencies — `dist/` is committed and ready to use as-is. This file is the entry point for an AI agent integrating the library: read it once, then read only the component skills you need.
|
|
8
8
|
|
|
9
9
|
## Integrate
|
|
10
10
|
|
package/dist/SKILL_tpl.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
# defuss-shadcn — Agent Skill
|
|
6
6
|
|
|
7
|
-
**A UI component system that scales with AI.** Themeable components built on semantic HTML, modern CSS, and vanilla JavaScript. No framework, zero dependencies — `dist/` is committed and ready to use as-is. This file is the entry point for an AI agent integrating the library: read it once, then read only the component skills you need.
|
|
7
|
+
**A UI component system that scales with _local_ AI.** Themeable components built on semantic HTML, modern CSS, and vanilla JavaScript. No framework, zero dependencies — `dist/` is committed and ready to use as-is. This file is the entry point for an AI agent integrating the library: read it once, then read only the component skills you need.
|
|
8
8
|
|
|
9
9
|
## Integrate
|
|
10
10
|
|
|
@@ -23,8 +23,7 @@
|
|
|
23
23
|
<link rel="stylesheet" href="css/docs-theme.css">
|
|
24
24
|
<link rel="stylesheet" href="css/docs-utilities.css">
|
|
25
25
|
<link rel="stylesheet" href="css/layout.css">
|
|
26
|
-
|
|
27
|
-
<link rel="stylesheet" href="../components/button/button.css">
|
|
26
|
+
<link rel="stylesheet" href="../components/button/button.css">
|
|
28
27
|
<link rel="stylesheet" href="../components/card/card.css">
|
|
29
28
|
<link rel="stylesheet" href="../components/dialog/dialog.css">
|
|
30
29
|
<link rel="stylesheet" href="../components/badge/badge.css">
|
|
@@ -11,20 +11,27 @@
|
|
|
11
11
|
|
|
12
12
|
&:last-child { border-bottom: none; }
|
|
13
13
|
|
|
14
|
-
/* -- Content animation (CSS-only) -------------------------
|
|
15
|
-
|
|
14
|
+
/* -- Content animation (CSS-only) -------------------------
|
|
15
|
+
Two gotchas, both required for the panel to glide:
|
|
16
|
+
1. `::details-content` must attach to the compound (`&::details-content`,
|
|
17
|
+
NOT `& ::details-content` — the descendant form never matches, the
|
|
18
|
+
pseudo's originating element is the subject itself).
|
|
19
|
+
2. `block-size: auto` is a keyword — without interpolate-size the
|
|
20
|
+
0→auto pair is non-interpolable and the transition silently snaps. */
|
|
21
|
+
&::details-content {
|
|
16
22
|
block-size: 0;
|
|
17
23
|
overflow-y: clip;
|
|
24
|
+
interpolate-size: allow-keywords;
|
|
18
25
|
transition: block-size 200ms ease, content-visibility 200ms allow-discrete;
|
|
19
26
|
}
|
|
20
27
|
|
|
21
|
-
&[open]
|
|
28
|
+
&[open]::details-content {
|
|
22
29
|
block-size: auto;
|
|
23
30
|
}
|
|
24
31
|
}
|
|
25
32
|
|
|
26
33
|
@starting-style {
|
|
27
|
-
.accordion-item[open]
|
|
34
|
+
.accordion-item[open]::details-content {
|
|
28
35
|
block-size: 0;
|
|
29
36
|
}
|
|
30
37
|
}
|
|
@@ -76,7 +83,7 @@
|
|
|
76
83
|
(REQUIRED for all components — AGENTS.md "Accessibility CSS"). */
|
|
77
84
|
@media (prefers-reduced-motion: reduce) {
|
|
78
85
|
@layer components {
|
|
79
|
-
.accordion-item
|
|
86
|
+
.accordion-item::details-content { transition: none; }
|
|
80
87
|
.accordion-trigger { transition: none; }
|
|
81
88
|
}
|
|
82
89
|
}
|
|
@@ -116,6 +116,18 @@ function init() {
|
|
|
116
116
|
getState: () => accordionApi.getState(accordion),
|
|
117
117
|
};
|
|
118
118
|
items.forEach((item) => {
|
|
119
|
+
// Cancellable pre-event: closing the LAST open item of a non-collapsible
|
|
120
|
+
// single accordion is denied here, before the DOM changes. Reopening it
|
|
121
|
+
// in the `toggle` handler instead would visibly flicker close→open now
|
|
122
|
+
// that the ::details-content height transition animates.
|
|
123
|
+
item.addEventListener('beforetoggle', (e) => {
|
|
124
|
+
if (accordion._applying)
|
|
125
|
+
return; // programmatic state change in progress
|
|
126
|
+
if (e.newState !== 'closed' || collapsible)
|
|
127
|
+
return;
|
|
128
|
+
if (!Array.from(items).some((i) => i !== item && i.open))
|
|
129
|
+
e.preventDefault();
|
|
130
|
+
});
|
|
119
131
|
item.addEventListener('toggle', () => {
|
|
120
132
|
if (accordion._applying)
|
|
121
133
|
return; // programmatic state change in progress
|
|
@@ -126,6 +138,10 @@ function init() {
|
|
|
126
138
|
});
|
|
127
139
|
}
|
|
128
140
|
else if (!collapsible) {
|
|
141
|
+
// fallback for browsers without beforetoggle (which the deny above
|
|
142
|
+
// needs): reopen, accepting the flicker — better than losing the
|
|
143
|
+
// single-open guarantee. Modern browsers never reach this branch
|
|
144
|
+
// because a denied beforetoggle fires no toggle event at all.
|
|
129
145
|
const anyOpen = Array.from(items).some((i) => i.open);
|
|
130
146
|
if (!anyOpen)
|
|
131
147
|
item.open = true;
|
|
@@ -171,3 +171,4 @@ These are all browser-native — no JS needed.
|
|
|
171
171
|
- Content height animation uses `::details-content` pseudo-element with `block-size` transition and `@starting-style` for the enter animation — fully CSS-only, no JS measurement needed
|
|
172
172
|
- Set `data-type="single"` for accordion behavior (only one open); omit for disclosure list (any number open)
|
|
173
173
|
- Set `data-collapsible` alongside `data-type="single"` to allow all items to be closed
|
|
174
|
+
- A non-collapsible single accordion never closes its last open item: the attempt is denied in the cancellable `beforetoggle` event (`preventDefault()`), so the click is a silent no-op. Reopening in `toggle` instead would visibly flicker close-then-open
|
|
@@ -50,6 +50,14 @@
|
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
/* -- Scroll lock ------------------------------------------- */
|
|
54
|
+
/* Page behind stays put while the alert is modal (see dialog.css):
|
|
55
|
+
`:modal` + overflow:hidden freezes the viewport at its current offset. */
|
|
56
|
+
html:has(dialog.alert-dialog:modal) {
|
|
57
|
+
overflow: hidden;
|
|
58
|
+
scrollbar-gutter: stable;
|
|
59
|
+
}
|
|
60
|
+
|
|
53
61
|
.alert-dialog-content {
|
|
54
62
|
padding: 1.5rem;
|
|
55
63
|
}
|
|
@@ -62,3 +62,4 @@ The api is bound per dialog; the registry global is
|
|
|
62
62
|
- No backdrop click dismiss — user must make an explicit choice
|
|
63
63
|
- Escape key is disabled — user must use the action buttons
|
|
64
64
|
- Focus is trapped inside the dialog via native `showModal()`
|
|
65
|
+
- While the alert is modal, `html:has(dialog.alert-dialog:modal)` sets `overflow: hidden` + `scrollbar-gutter: stable` — the page behind cannot scroll and its position is preserved for when the alert closes (no JS scroll-lock).
|
|
@@ -4,19 +4,26 @@
|
|
|
4
4
|
border-radius: var(--radius-lg);
|
|
5
5
|
overflow: hidden;
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
/* Two gotchas, both required for the panel to glide:
|
|
8
|
+
1. `::details-content` must attach to the compound (`&::details-content`,
|
|
9
|
+
NOT `& ::details-content` — the descendant form never matches, the
|
|
10
|
+
pseudo's originating element is the subject itself).
|
|
11
|
+
2. `block-size: auto` is a keyword — without interpolate-size the
|
|
12
|
+
0→auto pair is non-interpolable and the transition silently snaps. */
|
|
13
|
+
&::details-content {
|
|
8
14
|
block-size: 0;
|
|
9
15
|
overflow-y: clip;
|
|
16
|
+
interpolate-size: allow-keywords;
|
|
10
17
|
transition: block-size 200ms ease, content-visibility 200ms allow-discrete;
|
|
11
18
|
}
|
|
12
19
|
|
|
13
|
-
&[open]
|
|
20
|
+
&[open]::details-content {
|
|
14
21
|
block-size: auto;
|
|
15
22
|
}
|
|
16
23
|
}
|
|
17
24
|
|
|
18
25
|
@starting-style {
|
|
19
|
-
.collapsible[open]
|
|
26
|
+
.collapsible[open]::details-content {
|
|
20
27
|
block-size: 0;
|
|
21
28
|
}
|
|
22
29
|
}
|
|
@@ -80,6 +87,7 @@
|
|
|
80
87
|
.collapsible *::before,
|
|
81
88
|
.collapsible *::after,
|
|
82
89
|
.collapsible::backdrop,
|
|
90
|
+
.collapsible::details-content,
|
|
83
91
|
.collapsible-chevron,
|
|
84
92
|
.collapsible-chevron *,
|
|
85
93
|
.collapsible-chevron::before,
|
|
@@ -29,6 +29,39 @@
|
|
|
29
29
|
opacity: 0.5;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
/* Clear button (injected by combobox.js after the trigger). Anchor-positioned
|
|
33
|
+
over the trigger's chevron slot; anchored via the same per-instance anchor
|
|
34
|
+
name the popover uses. `data-placeholder` presence is the single
|
|
35
|
+
selection marker — the :has() rules below flip chevron and clear in sync,
|
|
36
|
+
so no JS toggles visibility. */
|
|
37
|
+
.combobox-clear {
|
|
38
|
+
position: fixed;
|
|
39
|
+
inset: auto;
|
|
40
|
+
margin: 0;
|
|
41
|
+
top: anchor(top);
|
|
42
|
+
bottom: anchor(bottom);
|
|
43
|
+
right: anchor(right);
|
|
44
|
+
/* center the 20px box exactly over the 16px chevron (1rem trigger padding) */
|
|
45
|
+
margin-inline-end: 0.875rem;
|
|
46
|
+
width: 1.25rem;
|
|
47
|
+
display: none;
|
|
48
|
+
place-items: center;
|
|
49
|
+
padding: 0;
|
|
50
|
+
border: none;
|
|
51
|
+
border-radius: var(--radius-sm);
|
|
52
|
+
background: transparent;
|
|
53
|
+
color: var(--muted-foreground);
|
|
54
|
+
cursor: pointer;
|
|
55
|
+
|
|
56
|
+
&:hover { color: var(--foreground); }
|
|
57
|
+
&:focus-visible { outline: 2px solid var(--ring); outline-offset: 1px; }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.combobox:has(.combobox-value:not([data-placeholder])) {
|
|
61
|
+
& .combobox-clear { display: grid; }
|
|
62
|
+
& .combobox-chevron { display: none; }
|
|
63
|
+
}
|
|
64
|
+
|
|
32
65
|
.combobox-content {
|
|
33
66
|
position: fixed;
|
|
34
67
|
inset: auto;
|
|
@@ -142,6 +175,13 @@
|
|
|
142
175
|
pointer-events: none;
|
|
143
176
|
opacity: 0.5;
|
|
144
177
|
}
|
|
178
|
+
|
|
179
|
+
/* Author `display: flex` overrides the UA's [hidden] { display: none }
|
|
180
|
+
(author origin always wins), so filter()'s item.hidden = true left
|
|
181
|
+
non-matching options rendered — and clickable — in the list. */
|
|
182
|
+
&[hidden] {
|
|
183
|
+
display: none;
|
|
184
|
+
}
|
|
145
185
|
}
|
|
146
186
|
|
|
147
187
|
.combobox-empty {
|
|
@@ -108,6 +108,30 @@ function init() {
|
|
|
108
108
|
const anchorId = `--combobox-${popover.id}`;
|
|
109
109
|
trigger.style.anchorName = anchorId;
|
|
110
110
|
popover.style.positionAnchor = anchorId;
|
|
111
|
+
// Clear button - injected so consumer markup stays minimal (and the
|
|
112
|
+
// button can't be nested in the trigger's <button>). Visibility is pure
|
|
113
|
+
// CSS: .combobox-clear shows exactly while data-placeholder is absent
|
|
114
|
+
// (combobox.css :has() rule); JS only wires the click and focus.
|
|
115
|
+
const placeholder = valueEl?.dataset.placeholder ?? '';
|
|
116
|
+
const clearBtn = document.createElement('button');
|
|
117
|
+
clearBtn.type = 'button';
|
|
118
|
+
clearBtn.className = 'combobox-clear';
|
|
119
|
+
clearBtn.setAttribute('aria-label', 'Clear selection');
|
|
120
|
+
clearBtn.innerHTML =
|
|
121
|
+
'<svg aria-hidden="true" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>';
|
|
122
|
+
clearBtn.style.positionAnchor = anchorId;
|
|
123
|
+
trigger.after(clearBtn);
|
|
124
|
+
clearBtn.addEventListener('click', () => {
|
|
125
|
+
allItems.forEach((i) => { i.setAttribute('aria-selected', 'false'); });
|
|
126
|
+
if (valueEl) {
|
|
127
|
+
valueEl.textContent = placeholder;
|
|
128
|
+
// re-declare data-placeholder: selectItem removed it, and it is the
|
|
129
|
+
// very marker the CSS :has() rule keys off to hide this button again
|
|
130
|
+
valueEl.setAttribute('data-placeholder', placeholder);
|
|
131
|
+
}
|
|
132
|
+
// the button goes display:none with the selection - keep focus usable
|
|
133
|
+
trigger.focus();
|
|
134
|
+
});
|
|
111
135
|
const getVisibleItems = () => allItems.filter((item) => !item.hidden && item.getAttribute('aria-disabled') !== 'true');
|
|
112
136
|
const open = () => {
|
|
113
137
|
// deferred show (safeShowPopover): showPopover() mid-exit crashes the
|
|
@@ -22,6 +22,7 @@ the search input when the popover opens.
|
|
|
22
22
|
|
|
23
23
|
## Native Web APIs
|
|
24
24
|
- [Popover API](https://developer.mozilla.org/en-US/docs/Web/API/Popover_API) — top-layer rendering and light-dismiss for the dropdown list
|
|
25
|
+
- [`:has()`](https://developer.mozilla.org/en-US/docs/Web/CSS/:has) — reveals the injected clear button exactly while a selection exists (`data-placeholder` absent)
|
|
25
26
|
- [CSS Anchor Positioning](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_anchor_positioning) — positions the popover relative to the trigger without JS
|
|
26
27
|
- [`@starting-style`](https://developer.mozilla.org/en-US/docs/Web/CSS/@starting-style) — entry animation starting values for popover appearance
|
|
27
28
|
- [`overscroll-behavior`](https://developer.mozilla.org/en-US/docs/Web/CSS/overscroll-behavior) — prevents scroll chaining from the listbox to the page
|
|
@@ -169,6 +170,7 @@ The registry global is `_defussShadcn.comboboxApi` / `_defussShadcn.comboboxStat
|
|
|
169
170
|
- The popover animates in via `@starting-style` + `transition-behavior: allow-discrete`
|
|
170
171
|
- The check icon for selected items uses a CSS `::before` pseudo-element
|
|
171
172
|
- Filter matching is case-insensitive and supports substring matching
|
|
173
|
+
- The clear button (`.combobox-clear`, ✕) is **injected by the JS after the trigger** — consumer markup never contains it. It replaces the chevron whenever a value is selected (pure CSS via `:has()`), and clicking it restores the placeholder and deselects every option
|
|
172
174
|
- The empty state element is shown when no items match the filter query
|
|
173
175
|
- Group labels and separators auto-hide when their group has no visible items
|
|
174
176
|
- `overscroll-behavior: contain` prevents scroll chaining from the listbox
|
|
@@ -18,6 +18,14 @@
|
|
|
18
18
|
dialog.command[open]::backdrop { background: oklch(0 0 0 / 0); }
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
/* -- Scroll lock ------------------------------------------- */
|
|
22
|
+
/* Page behind stays put while the palette is modal (see dialog.css):
|
|
23
|
+
`:modal` + overflow:hidden freezes the viewport at its current offset. */
|
|
24
|
+
html:has(dialog.command:modal) {
|
|
25
|
+
overflow: hidden;
|
|
26
|
+
scrollbar-gutter: stable;
|
|
27
|
+
}
|
|
28
|
+
|
|
21
29
|
.command-input-wrapper {
|
|
22
30
|
display: flex; align-items: center; gap: 0.5rem;
|
|
23
31
|
padding: 0.75rem 1rem; border-bottom: 1px solid var(--border);
|
|
@@ -43,6 +51,9 @@
|
|
|
43
51
|
&:focus-visible { outline: 2px solid var(--ring); outline-offset: -2px; }
|
|
44
52
|
&[aria-disabled="true"] { pointer-events: none; opacity: 0.5; }
|
|
45
53
|
& svg { width: 1rem; height: 1rem; color: var(--muted-foreground); flex-shrink: 0; }
|
|
54
|
+
/* Same as combobox-item: `display: flex` beats the UA [hidden] rule, and
|
|
55
|
+
command.ts filter() hides non-matches via the hidden attribute. */
|
|
56
|
+
&[hidden] { display: none; }
|
|
46
57
|
}
|
|
47
58
|
|
|
48
59
|
.command-separator { height: 1px; background-color: var(--border); margin: 0.25rem -0.25rem; }
|
|
@@ -109,3 +109,4 @@ The api is bound per palette dialog; the registry global is
|
|
|
109
109
|
- The dialog uses `showModal()` — focus is trapped inside and Escape closes it natively.
|
|
110
110
|
- On close, the search input is cleared and all items are restored.
|
|
111
111
|
- No JavaScript positioning is needed — the dialog uses CSS `position: fixed` with `top: 15%`.
|
|
112
|
+
- While the palette is modal, `html:has(dialog.command:modal)` sets `overflow: hidden` + `scrollbar-gutter: stable` — the page behind cannot scroll and its position is preserved for when the palette closes (no JS scroll-lock).
|
|
@@ -114,5 +114,6 @@ Requires minimal JavaScript — only for trigger wiring and backdrop-click-to-cl
|
|
|
114
114
|
## Notes
|
|
115
115
|
|
|
116
116
|
- Animation uses CSS-only enter via `@starting-style` and exit via `transition` + `allow-discrete`.
|
|
117
|
+
- While the dialog is modal, `html:has(dialog.dialog:modal)` sets `overflow: hidden` + `scrollbar-gutter: stable` — the page behind cannot scroll and its position is preserved for when the dialog closes (no JS scroll-lock).
|
|
117
118
|
- The selector is `dialog.dialog` (element + class) to avoid styling native `<dialog>` elements used elsewhere.
|
|
118
119
|
- For forms inside dialogs, use the `dialog-body` wrapper for the form content.
|
|
@@ -54,6 +54,18 @@
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
/* -- Scroll lock ------------------------------------------- */
|
|
58
|
+
/* While the dialog is modal the page behind must stay put — closing
|
|
59
|
+
returns you to exactly the scroll offset you had. `:modal` matches only
|
|
60
|
+
while opened via showModal(); overflow:hidden freezes the viewport
|
|
61
|
+
without losing the scroll position (no JS position:fixed hack), and
|
|
62
|
+
scrollbar-gutter:stable keeps the scrollbar's space reserved so its
|
|
63
|
+
removal can't shift the layout underneath. */
|
|
64
|
+
html:has(dialog.dialog:modal) {
|
|
65
|
+
overflow: hidden;
|
|
66
|
+
scrollbar-gutter: stable;
|
|
67
|
+
}
|
|
68
|
+
|
|
57
69
|
/* -- Content sections ------------------------------------- */
|
|
58
70
|
.dialog-content { padding: 1.5rem; }
|
|
59
71
|
.dialog-header { margin-bottom: 1rem; }
|
|
@@ -144,6 +144,7 @@ The registry global is `_defussShadcn.imageApi` / `_defussShadcn.imageStates`.
|
|
|
144
144
|
- Use `loading="lazy"` on images below the fold for performance.
|
|
145
145
|
- Lightbox supports keyboard: Escape closes, Tab navigates controls.
|
|
146
146
|
- Multiple images with `data-preview` share a single dialog instance.
|
|
147
|
+
- While the lightbox is modal, `html:has(dialog.image-lightbox:modal)` sets `overflow: hidden` + `scrollbar-gutter: stable` — the page behind cannot scroll and its position is preserved for when the lightbox closes (no JS scroll-lock).
|
|
147
148
|
# Image
|
|
148
149
|
|
|
149
150
|
## Native basis
|
|
@@ -108,6 +108,14 @@
|
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
/* -- Scroll lock ------------------------------------------- */
|
|
112
|
+
/* Page behind stays put while the lightbox is modal (see dialog.css):
|
|
113
|
+
`:modal` + overflow:hidden freezes the viewport at its current offset. */
|
|
114
|
+
html:has(dialog.image-lightbox:modal) {
|
|
115
|
+
overflow: hidden;
|
|
116
|
+
scrollbar-gutter: stable;
|
|
117
|
+
}
|
|
118
|
+
|
|
111
119
|
.image-lightbox-content {
|
|
112
120
|
display: flex;
|
|
113
121
|
align-items: center;
|
|
@@ -123,6 +123,7 @@ The api is bound per sheet element; the registry global is
|
|
|
123
123
|
|
|
124
124
|
## Notes
|
|
125
125
|
|
|
126
|
+
- While a sheet is modal, `html:has(dialog.sheet:modal)` sets `overflow: hidden` + `scrollbar-gutter: stable` — the page behind cannot scroll and its position is preserved for when the sheet closes (no JS scroll-lock).
|
|
126
127
|
- Right/left sheets have a fixed width of `24rem` with `max-width: 100vw` for small screens.
|
|
127
128
|
- Top/bottom sheets are full width with `height: auto` — they size to their content. Their `.sheet-content` column caps at `48rem` and centers, so content doesn't stretch to the viewport edges.
|
|
128
129
|
- The selector is `dialog.sheet` (element + class) to avoid conflicts with `dialog.dialog`.
|
|
@@ -122,6 +122,14 @@
|
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
+
/* -- Scroll lock ------------------------------------------- */
|
|
126
|
+
/* Page behind stays put while a sheet is modal (see dialog.css):
|
|
127
|
+
`:modal` + overflow:hidden freezes the viewport at its current offset. */
|
|
128
|
+
html:has(dialog.sheet:modal) {
|
|
129
|
+
overflow: hidden;
|
|
130
|
+
scrollbar-gutter: stable;
|
|
131
|
+
}
|
|
132
|
+
|
|
125
133
|
/* -- Content sections -------------------------------------- */
|
|
126
134
|
.sheet-content { padding: 1.5rem; position: relative; }
|
|
127
135
|
|
|
@@ -126,7 +126,7 @@ The registry global is `_defussShadcn.sidebarApi` / `_defussShadcn.sidebarStates
|
|
|
126
126
|
|
|
127
127
|
## Notes
|
|
128
128
|
|
|
129
|
-
- **Mobile**: Desktop sidebar hidden below 768px. Use `<dialog class="sidebar-mobile">` for slide-in sheet.
|
|
129
|
+
- **Mobile**: Desktop sidebar hidden below 768px. Use `<dialog class="sidebar-mobile">` for slide-in sheet. While it is modal, `html:has(.sidebar-mobile:modal)` sets `overflow: hidden` + `scrollbar-gutter: stable` — the page behind cannot scroll and keeps its position (no JS scroll-lock).
|
|
130
130
|
- **Collapsible groups**: `<details class="sidebar-group">` — native toggle, no JS.
|
|
131
131
|
- **Submenus**: `<details class="sidebar-submenu">` for nested nav with left border.
|
|
132
132
|
- **Badges**: `<span class="sidebar-badge">` for notification counts.
|
|
@@ -356,6 +356,14 @@
|
|
|
356
356
|
.sidebar-mobile[data-side="right"][open] { opacity: 0; translate: 100% 0; }
|
|
357
357
|
}
|
|
358
358
|
|
|
359
|
+
/* ── Scroll lock ──────────────────────────────────────────── */
|
|
360
|
+
/* Page behind stays put while the mobile sheet is modal (see dialog.css):
|
|
361
|
+
`:modal` + overflow:hidden freezes the viewport at its current offset. */
|
|
362
|
+
html:has(.sidebar-mobile:modal) {
|
|
363
|
+
overflow: hidden;
|
|
364
|
+
scrollbar-gutter: stable;
|
|
365
|
+
}
|
|
366
|
+
|
|
359
367
|
/* ── Reduced motion ───────────────────────────────────────── */
|
|
360
368
|
@media (prefers-reduced-motion: reduce) {
|
|
361
369
|
.app-sidebar { transition: none; }
|
|
@@ -19,19 +19,26 @@
|
|
|
19
19
|
.tree-branch {
|
|
20
20
|
border: none;
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
/* Two gotchas, both required for the branch to glide:
|
|
23
|
+
1. `::details-content` must attach to the compound (`&::details-content`,
|
|
24
|
+
NOT `& ::details-content` — the descendant form never matches, the
|
|
25
|
+
pseudo's originating element is the subject itself).
|
|
26
|
+
2. `block-size: auto` is a keyword — without interpolate-size the
|
|
27
|
+
0→auto pair is non-interpolable and the transition silently snaps. */
|
|
28
|
+
&::details-content {
|
|
23
29
|
block-size: 0;
|
|
24
30
|
overflow-y: clip;
|
|
31
|
+
interpolate-size: allow-keywords;
|
|
25
32
|
transition: block-size 200ms ease, content-visibility 200ms allow-discrete;
|
|
26
33
|
}
|
|
27
34
|
|
|
28
|
-
&[open]
|
|
35
|
+
&[open]::details-content {
|
|
29
36
|
block-size: auto;
|
|
30
37
|
}
|
|
31
38
|
}
|
|
32
39
|
|
|
33
40
|
@starting-style {
|
|
34
|
-
.tree-branch[open]
|
|
41
|
+
.tree-branch[open]::details-content {
|
|
35
42
|
block-size: 0;
|
|
36
43
|
}
|
|
37
44
|
}
|
|
@@ -120,6 +127,7 @@
|
|
|
120
127
|
.tree-branch *::before,
|
|
121
128
|
.tree-branch *::after,
|
|
122
129
|
.tree-branch::backdrop,
|
|
130
|
+
.tree-branch::details-content,
|
|
123
131
|
.tree-branch-trigger,
|
|
124
132
|
.tree-branch-trigger *,
|
|
125
133
|
.tree-branch-trigger::before,
|
|
@@ -72,7 +72,7 @@ Pure CSS — no JavaScript or ARIA required.
|
|
|
72
72
|
|
|
73
73
|
### Inline code
|
|
74
74
|
```html
|
|
75
|
-
<code class="code">default-semantic-tokens.css</code>
|
|
75
|
+
<code class="inline-code">default-semantic-tokens.css</code>
|
|
76
76
|
```
|
|
77
77
|
|
|
78
78
|
---
|
|
@@ -91,7 +91,7 @@ Pure CSS — no JavaScript or ARIA required.
|
|
|
91
91
|
| `.small` | `<small>` or any | 0.875rem medium text, line-height 1 |
|
|
92
92
|
| `.muted` | `<p>` or any | 0.875rem muted-foreground text |
|
|
93
93
|
| `.blockquote` | `<blockquote>` | Italic block with inline-start border, hanging punctuation |
|
|
94
|
-
| `.code` | `<code>` | Monospace inline code with muted background |
|
|
94
|
+
| `.inline-code` | `<code>` | Monospace inline code with muted background |
|
|
95
95
|
|
|
96
96
|
---
|
|
97
97
|
|
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
/* -- Inline code -------------------------------------------- */
|
|
110
|
-
.code {
|
|
110
|
+
.inline-code {
|
|
111
111
|
position: relative;
|
|
112
112
|
border-radius: var(--radius-sm);
|
|
113
113
|
background: var(--muted);
|
|
@@ -129,7 +129,7 @@
|
|
|
129
129
|
border-inline-start-width: 3px;
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
-
.code {
|
|
132
|
+
.inline-code {
|
|
133
133
|
outline: 1px solid var(--border);
|
|
134
134
|
}
|
|
135
135
|
|
|
@@ -143,7 +143,7 @@
|
|
|
143
143
|
border-inline-start-color: CanvasText;
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
-
.code {
|
|
146
|
+
.inline-code {
|
|
147
147
|
background: Canvas;
|
|
148
148
|
outline: 1px solid CanvasText;
|
|
149
149
|
color: CanvasText;
|
|
@@ -23,8 +23,7 @@
|
|
|
23
23
|
<link rel="stylesheet" href="css/docs-theme.css">
|
|
24
24
|
<link rel="stylesheet" href="css/docs-utilities.css">
|
|
25
25
|
<link rel="stylesheet" href="css/layout.css">
|
|
26
|
-
|
|
27
|
-
<link rel="stylesheet" href="../components/button/button.css">
|
|
26
|
+
<link rel="stylesheet" href="../components/button/button.css">
|
|
28
27
|
<link rel="stylesheet" href="../components/card/card.css">
|
|
29
28
|
<link rel="stylesheet" href="../components/dialog/dialog.css">
|
|
30
29
|
<link rel="stylesheet" href="../components/badge/badge.css">
|