@ultimat3/ui 28.0.0 → 29.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +1 -1
- package/package.json +6 -6
- package/src/global.scss +89 -2
- package/src/tokens/reset.scss +7 -0
package/CLAUDE.md
CHANGED
|
@@ -71,7 +71,7 @@ Tier 4. Imports `@ultimat3/core`, `i18n`, `money`, `time` — **not `schema`**,
|
|
|
71
71
|
| `src/theme/ambient-slot.ts` | the module-scope slot holding the reader `useUi()` falls back to on the server — imports nothing, so a browser chunk pays nothing for it |
|
|
72
72
|
| `src/theme/ambient.ts` | the server's reader (`ambientUiContext`: request locale, zone, direction, translator), registered at import; `index.ts` imports it bare, and the `browser` field maps it to `ambient.browser.ts`, which answers the defaults and registers nothing |
|
|
73
73
|
| `src/error-registry.ts` | the `registerErrorCodes()` call, alone, so `errors.ts` is pure and the barrel's re-export of it costs a chunk nothing |
|
|
74
|
-
| `src/components/` |
|
|
74
|
+
| `src/components/` | See [CATALOG.md](CATALOG.md)'s generated header for the component inventory and total; `PascalCase.tsx` (component convention overrides the repo's kebab-case) |
|
|
75
75
|
| `src/icons/glyphs/` | GENERATED: 1767 per-icon modules, `@ultimat3/ui/icons/<name>` → `icon<Name>` |
|
|
76
76
|
| `src/icons/build-icons.ts` | the generator + the pinned `LUCIDE_VERSION`; `LICENSE.lucide` is upstream's ISC text |
|
|
77
77
|
| `src/theme/brand.ts` | `defineTheme()` — the ONE brand-override seam; there is no SCSS `@use ... with ()` path. Merges a preset under the overrides, measures, emits at every level `theme.scss` does |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ultimat3/ui",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "29.1.0",
|
|
4
4
|
"description": "SolidJS design system: semantic design tokens, dark/RTL-ready SCSS modules, a11y primitives",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"LICENSE"
|
|
40
40
|
],
|
|
41
41
|
"engines": {
|
|
42
|
-
"bun": ">=1.4.
|
|
42
|
+
"bun": ">=1.4.4"
|
|
43
43
|
},
|
|
44
44
|
"scripts": {
|
|
45
45
|
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
@@ -48,10 +48,10 @@
|
|
|
48
48
|
"icons": "bun run src/icons/build-icons.ts"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@ultimat3/core": "
|
|
52
|
-
"@ultimat3/i18n": "
|
|
53
|
-
"@ultimat3/money": "
|
|
54
|
-
"@ultimat3/time": "
|
|
51
|
+
"@ultimat3/core": "29.1.0",
|
|
52
|
+
"@ultimat3/i18n": "29.1.0",
|
|
53
|
+
"@ultimat3/money": "29.1.0",
|
|
54
|
+
"@ultimat3/time": "29.1.0"
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
57
|
"solid-js": "^1.9.0"
|
package/src/global.scss
CHANGED
|
@@ -30,11 +30,17 @@
|
|
|
30
30
|
// The client router's route-presented modal (`@ultimat3/render`'s `NAVIGATION_MODAL_ATTRIBUTE`, a
|
|
31
31
|
// route declaring `navigation: 'modal'`): a native <dialog> holding the page's `<main>`, styled as
|
|
32
32
|
// `Dialog` is. Global because no component renders it — the router does, and `render` cannot
|
|
33
|
-
// import `ui` (one tier) — so the
|
|
34
|
-
// `:where()`: an app restyles it without out-shouting a selector.
|
|
33
|
+
// import `ui` (one tier) — so the names are held equal by render's `navigation-modal-style.test.ts`.
|
|
34
|
+
// `:where()`: an app restyles it without out-shouting a selector. Opened as a top-layer popover
|
|
35
|
+
// (`show()` + `showPopover()`, never `showModal()`, whose inert document recalculates the style of
|
|
36
|
+
// every element beneath), so it is placed here as `showModal()` would have placed it.
|
|
35
37
|
:where(dialog[data-x-modal]) {
|
|
38
|
+
position: fixed;
|
|
39
|
+
inset: 0;
|
|
36
40
|
inline-size: min(100% - #{t.space(8)}, 40rem);
|
|
41
|
+
block-size: fit-content;
|
|
37
42
|
max-block-size: min(88dvh, 60rem);
|
|
43
|
+
margin: auto;
|
|
38
44
|
padding: t.space(5);
|
|
39
45
|
border: 1px solid t.role('line');
|
|
40
46
|
border-radius: t.radius(lg);
|
|
@@ -51,6 +57,87 @@
|
|
|
51
57
|
}
|
|
52
58
|
}
|
|
53
59
|
|
|
60
|
+
// The frame's close button (`NAVIGATION_MODAL_CLOSE_ATTRIBUTE`): after the content in the DOM, at
|
|
61
|
+
// the dialog's top corner on screen. Its name is its `aria-label`; the cross is drawn, not written.
|
|
62
|
+
:where([data-x-modal-close]) {
|
|
63
|
+
position: absolute;
|
|
64
|
+
inset-block-start: t.space(3);
|
|
65
|
+
inset-inline-end: t.space(3);
|
|
66
|
+
inline-size: t.space(8);
|
|
67
|
+
block-size: t.space(8);
|
|
68
|
+
padding: 0;
|
|
69
|
+
border: 0;
|
|
70
|
+
border-radius: t.radius(md);
|
|
71
|
+
background: transparent;
|
|
72
|
+
color: t.role('fg-muted');
|
|
73
|
+
cursor: pointer;
|
|
74
|
+
|
|
75
|
+
&::before,
|
|
76
|
+
&::after {
|
|
77
|
+
content: '';
|
|
78
|
+
position: absolute;
|
|
79
|
+
inset-block-start: 50%;
|
|
80
|
+
inset-inline-start: 25%;
|
|
81
|
+
inline-size: 50%;
|
|
82
|
+
block-size: t.rem(2px);
|
|
83
|
+
border-radius: t.radius(full);
|
|
84
|
+
background: currentcolor;
|
|
85
|
+
rotate: 45deg;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
&::after {
|
|
89
|
+
rotate: -45deg;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
&:hover {
|
|
93
|
+
background: t.role('bg-soft');
|
|
94
|
+
color: t.role('fg');
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// The modal's own heading clears the close button.
|
|
99
|
+
:where([data-x-modal-body] > :is(h1, h2):first-child) {
|
|
100
|
+
padding-inline-end: t.space(8);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// The body while the modal's document is on its way (`aria-busy`): a skeleton the size of a short
|
|
104
|
+
// form, built from surface roles as `Skeleton` is, so the dialog does not jump far when it fills.
|
|
105
|
+
:where([data-x-modal-skeleton]) {
|
|
106
|
+
display: grid;
|
|
107
|
+
gap: t.space(3);
|
|
108
|
+
min-block-size: 12rem;
|
|
109
|
+
|
|
110
|
+
&::before,
|
|
111
|
+
&::after {
|
|
112
|
+
content: '';
|
|
113
|
+
display: block;
|
|
114
|
+
border-radius: t.radius(md);
|
|
115
|
+
background: linear-gradient(
|
|
116
|
+
90deg,
|
|
117
|
+
t.role('bg-soft') 25%,
|
|
118
|
+
t.role('line') 37%,
|
|
119
|
+
t.role('bg-soft') 63%
|
|
120
|
+
)
|
|
121
|
+
0 0 / 200% 100%;
|
|
122
|
+
animation: ultimate-modal-shimmer calc(#{t.duration(slower)} * 2) linear infinite;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
&::before {
|
|
126
|
+
inline-size: 60%;
|
|
127
|
+
block-size: t.space(6);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
&::after {
|
|
131
|
+
block-size: 8rem;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
@keyframes ultimate-modal-shimmer {
|
|
136
|
+
to {
|
|
137
|
+
background-position: -200% 0;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
54
141
|
@keyframes ultimate-modal-in {
|
|
55
142
|
from {
|
|
56
143
|
opacity: 0;
|
package/src/tokens/reset.scss
CHANGED
|
@@ -135,6 +135,13 @@ html:has(dialog:modal) {
|
|
|
135
135
|
overflow: hidden;
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
+
// The router's route-presented modal is a top-layer popover, never `:modal` (`global.scss` says
|
|
139
|
+
// why), so it locks the scroll by its own name. A child combinator from <body>: the one place the
|
|
140
|
+
// router appends it, so the `:has()` is answered without walking the page.
|
|
141
|
+
:where(html:has(> body > dialog[data-x-modal][open])) {
|
|
142
|
+
overflow: hidden;
|
|
143
|
+
}
|
|
144
|
+
|
|
138
145
|
// The live region `announce()` (`../a11y.ts`) appends to <body>, hidden from here rather than
|
|
139
146
|
// through `element.style`: a style a script applies at runtime is not among the inline styles the
|
|
140
147
|
// CSP the framework itself sends has hashed, so it logged a `style-src` violation on every page
|