@hanzo/ui 8.0.46 → 8.0.48
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/dist/backends/gui/glass.cjs +22 -13
- package/dist/backends/gui/glass.d.ts +37 -6
- package/dist/backends/gui/glass.d.ts.map +1 -1
- package/dist/backends/gui/glass.js +23 -14
- package/dist/backends/gui/glass.js.map +1 -1
- package/dist/glass.cjs +256 -0
- package/dist/glass.css +140 -0
- package/dist/glass.d.ts +243 -0
- package/dist/glass.d.ts.map +1 -0
- package/dist/glass.js +252 -0
- package/dist/glass.js.map +1 -0
- package/dist/product/SlideOver.cjs +6 -1
- package/dist/product/SlideOver.d.ts.map +1 -1
- package/dist/product/SlideOver.js +6 -1
- package/dist/product/SlideOver.js.map +1 -1
- package/dist/styles/motion.css +10 -3
- package/dist/styles.css +157 -27
- package/dist/theme.css +147 -24
- package/package.json +11 -3
package/dist/styles.css
CHANGED
|
@@ -710,20 +710,8 @@
|
|
|
710
710
|
The url()s are relative to THIS file, so they resolve wherever the package is
|
|
711
711
|
mounted — node_modules, a CDN, a copied dist — with no configuration. */
|
|
712
712
|
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
src:url("./assets/fonts/Geist-Variable.woff2") format("woff2");
|
|
716
|
-
font-weight:100 900;
|
|
717
|
-
font-style:normal;
|
|
718
|
-
font-display:swap;
|
|
719
|
-
}
|
|
720
|
-
@font-face{
|
|
721
|
-
font-family:"Geist Mono";
|
|
722
|
-
src:url("./assets/fonts/GeistMono-Variable.woff2") format("woff2");
|
|
723
|
-
font-weight:100 900;
|
|
724
|
-
font-style:normal;
|
|
725
|
-
font-display:swap;
|
|
726
|
-
}
|
|
713
|
+
|
|
714
|
+
|
|
727
715
|
|
|
728
716
|
:root{
|
|
729
717
|
--font-sans:"Geist","Geist Sans",ui-sans-serif,system-ui,sans-serif;
|
|
@@ -1076,24 +1064,159 @@
|
|
|
1076
1064
|
[data-touch-y="23"]::after { top: -23px; bottom: -23px; }
|
|
1077
1065
|
[data-touch-y="24"]::after { top: -24px; bottom: -24px; }
|
|
1078
1066
|
|
|
1079
|
-
/*
|
|
1080
|
-
.
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1067
|
+
/* The material, the elevation ladder, the scrim and the row separators live in
|
|
1068
|
+
src/glass.css and are inlined here by scripts/compose-theme.mjs — one home,
|
|
1069
|
+
two entry points (`@hanzo/ui/theme.css` and `@hanzo/ui/glass.css`), so a host
|
|
1070
|
+
that wants only the chrome material does not have to take the token layer
|
|
1071
|
+
with it.
|
|
1072
|
+
|
|
1073
|
+
They used to be four `.elevation-*` rules reading `var(--hz-elevation-N, …)`.
|
|
1074
|
+
Nothing in this repo, in @hanzo/design or in any consumer ever DEFINED an
|
|
1075
|
+
`--hz-elevation-*` variable, so every one of those resolved to its fallback:
|
|
1076
|
+
an indirection through a name that does not exist, with a hardcoded shadow
|
|
1077
|
+
behind it, next to a design system that publishes real ones. */
|
|
1078
|
+
|
|
1079
|
+
/* Glass — the material floating chrome is made of, and the ladder it stands on.
|
|
1080
|
+
*
|
|
1081
|
+
* import '@hanzo/ui/glass.css' // this slice alone
|
|
1082
|
+
* import '@hanzo/ui/theme.css' // the whole sheet; this is inlined in it
|
|
1083
|
+
*
|
|
1084
|
+
* Menus, dialogs, popovers, toasts, sticky bars: things with page UNDER them.
|
|
1085
|
+
* That last clause is the whole rule. Vibrancy is a lens — translucent ground
|
|
1086
|
+
* plus a backdrop blur — so it shows a softened version of whatever it covers,
|
|
1087
|
+
* and covering nothing but the flat canvas it has nothing to soften. An in-flow
|
|
1088
|
+
* card wearing this is not glass; it is the page with extra steps. So: over
|
|
1089
|
+
* content -> glass, in the flow -> the surface ladder. There is no third case.
|
|
1090
|
+
*
|
|
1091
|
+
* ── Values come from @hanzo/design ──────────────────────────────────────────
|
|
1092
|
+
* Every colour, shadow and light below is a design token. The fallbacks are
|
|
1093
|
+
* design's OWN published values, present so a host that imports this file
|
|
1094
|
+
* without design's sheet still gets a ladder instead of a silently-dropped
|
|
1095
|
+
* declaration (an undefined var() invalidates the whole property). They are a
|
|
1096
|
+
* mirror, not a second opinion — glass.test.ts reads @hanzo/design/styles.css
|
|
1097
|
+
* and fails if any of them stops matching, so the copy cannot drift.
|
|
1098
|
+
*/
|
|
1084
1099
|
|
|
1085
|
-
/*
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1100
|
+
/* ── The material ───────────────────────────────────────────────────────────
|
|
1101
|
+
Applied BY SLOT, not by call site. Every surface below is floating chrome by
|
|
1102
|
+
construction — a popover is over the page or it is nothing — so which of them
|
|
1103
|
+
is glass was never a decision a call site should get to make. It was making
|
|
1104
|
+
it anyway: in the console, of 135 floating surfaces exactly ONE had reached
|
|
1105
|
+
for the material by hand and a dozen dialogs had gone the other way and
|
|
1106
|
+
pinned an opaque fill. Reaching them one by one is 134 edits that agree today
|
|
1107
|
+
and drift apart on the next one somebody adds. A new dialog is glass because
|
|
1108
|
+
it is a dialog.
|
|
1109
|
+
|
|
1110
|
+
`.glass` is here for the same reason `glass()` exists in the recipes module:
|
|
1111
|
+
a hand-rolled bar or a custom menu is floating chrome that no slot names.
|
|
1112
|
+
|
|
1113
|
+
The ground is derived from --background rather than fixed, so the material
|
|
1114
|
+
follows the theme instead of assuming a dark one.
|
|
1115
|
+
|
|
1116
|
+
The opaque `background-color` outside the @supports block is load-bearing
|
|
1117
|
+
rather than belt-and-braces: a browser that cannot blur gets no rule at all
|
|
1118
|
+
from inside it, and a menu with no background is a menu you read the page
|
|
1119
|
+
through. !important beats the compiled atomic background class, which lands
|
|
1120
|
+
in an inline <style> a bundler orders after this sheet, and it beats the call
|
|
1121
|
+
sites that pin a fill by hand — those become no-ops instead of holes. */
|
|
1089
1122
|
@supports (backdrop-filter: blur(8px)) {
|
|
1090
|
-
|
|
1123
|
+
.glass,
|
|
1124
|
+
[data-slot="glass"],
|
|
1125
|
+
[data-slot="dialog-content"],
|
|
1126
|
+
[data-slot="popover-content"],
|
|
1127
|
+
[data-slot="select-content"],
|
|
1128
|
+
[data-slot="dropdown-menu-content"],
|
|
1129
|
+
[data-slot="dropdown-menu-sub-content"],
|
|
1130
|
+
[data-slot="tooltip-content"] {
|
|
1091
1131
|
background-color: color-mix(in oklab, var(--background) 72%, transparent) !important;
|
|
1092
1132
|
-webkit-backdrop-filter: blur(20px) saturate(1.8);
|
|
1093
1133
|
backdrop-filter: blur(20px) saturate(1.8);
|
|
1094
1134
|
}
|
|
1095
1135
|
}
|
|
1096
1136
|
|
|
1137
|
+
/* ── Depth — three rungs, and the lit edge is the load-bearing half ──────────
|
|
1138
|
+
A cast shadow is how a light page shows lift. On a near-black canvas it is
|
|
1139
|
+
nearly free of information: black on near-black moves no pixels, so a raised
|
|
1140
|
+
panel wearing only a shadow reads exactly as flat as one wearing none. What
|
|
1141
|
+
says "this surface is above that one" in a dark UI is the LIT EDGE — the top
|
|
1142
|
+
lip catching light, which is why every macOS/iOS glass panel has one. Design
|
|
1143
|
+
spends the same two tokens for the same reason and zeroes --edge-highlight in
|
|
1144
|
+
`.light`, where a white line on a white card is nothing and the drop does all
|
|
1145
|
+
the work: one composition, both themes, no branch here.
|
|
1146
|
+
|
|
1147
|
+
Levels are a LADDER, not a palette — 1 rests in the flow, 2 lifts off it, 3
|
|
1148
|
+
floats over the page. Nothing needs a fourth; if something seems to, it is
|
|
1149
|
+
sitting at the wrong level.
|
|
1150
|
+
|
|
1151
|
+
Anchored chrome — a menu pinned to the control that opened it, a tooltip to
|
|
1152
|
+
its target — is rung 2. A modal has no anchor and a scrim of its own, so it
|
|
1153
|
+
floats free at 3. Those slots share the rung's rule rather than restating its
|
|
1154
|
+
value, so the ladder cannot come apart from the surfaces standing on it.
|
|
1155
|
+
|
|
1156
|
+
Glass over glass (a select opened inside a dialog) is deliberately left to
|
|
1157
|
+
stack: two 72% grounds land near 92%, so the nested surface reads as MORE
|
|
1158
|
+
solid than its parent, which is the right answer and the one macOS gives. */
|
|
1159
|
+
.elevation-1 {
|
|
1160
|
+
box-shadow:
|
|
1161
|
+
var(--edge-highlight, inset 0 1px 0 0 rgb(255 255 255 / .10)),
|
|
1162
|
+
var(--shadow-sm, 0 1px 2px 0 rgb(0 0 0 / .40)) !important;
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
.elevation-2,
|
|
1166
|
+
[data-slot="popover-content"],
|
|
1167
|
+
[data-slot="select-content"],
|
|
1168
|
+
[data-slot="dropdown-menu-content"],
|
|
1169
|
+
[data-slot="dropdown-menu-sub-content"],
|
|
1170
|
+
[data-slot="tooltip-content"] {
|
|
1171
|
+
box-shadow:
|
|
1172
|
+
var(--edge-highlight, inset 0 1px 0 0 rgb(255 255 255 / .10)),
|
|
1173
|
+
var(--shadow-lg, 0 10px 15px -3px rgb(0 0 0 / .55), 0 4px 6px -4px rgb(0 0 0 / .55)) !important;
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
.elevation-3,
|
|
1177
|
+
[data-slot="dialog-content"] {
|
|
1178
|
+
box-shadow:
|
|
1179
|
+
var(--edge-highlight, inset 0 1px 0 0 rgb(255 255 255 / .10)),
|
|
1180
|
+
var(--shadow-floating, 0 24px 60px -16px rgb(0 0 0 / .75)) !important;
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
/* ── The scrim ──────────────────────────────────────────────────────────────
|
|
1184
|
+
The dim that makes a floating panel read as floating rather than as more
|
|
1185
|
+
page. gui's overlay dims TWICE — a translucent ground under a further
|
|
1186
|
+
`opacity` — landing at about a quarter black. Opaque chrome gets away with
|
|
1187
|
+
it; glass does not, because the blur behind a translucent panel only reads as
|
|
1188
|
+
depth when the ground behind it is genuinely darker.
|
|
1189
|
+
|
|
1190
|
+
One value, in one place: the recipes module reads the same token, so the dim
|
|
1191
|
+
behind a component dialog and the dim behind a hand-rolled one are the same
|
|
1192
|
+
dim by construction rather than by two people remembering a number. */
|
|
1193
|
+
[data-slot="dialog-overlay"] {
|
|
1194
|
+
opacity: 1;
|
|
1195
|
+
background-color: var(--surface-scrim, rgb(0 0 0 / .8));
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
/* ── Grouped rows ───────────────────────────────────────────────────────────
|
|
1199
|
+
One card, hairlines between its rows — the macOS settings group. Four
|
|
1200
|
+
separate cards for four toggles reads as four unrelated decisions, each with
|
|
1201
|
+
its own four edges; the same four inside one card reads as one set and drops
|
|
1202
|
+
twelve edges of noise.
|
|
1203
|
+
|
|
1204
|
+
The separator is drawn by the PARENT, because only the parent knows which
|
|
1205
|
+
child is last. Every by-hand version either left a trailing hairline sitting
|
|
1206
|
+
against the card's own bottom border (two parallel lines, 1px apart) or made
|
|
1207
|
+
each row take a `first` flag it should never have needed. `> * + *` has
|
|
1208
|
+
neither failure mode and needs nothing from the call site.
|
|
1209
|
+
|
|
1210
|
+
Pair with `overflow: hidden` on the card (the `rows` recipe does) so the
|
|
1211
|
+
separators stop at the radius instead of poking through the rounded corner.
|
|
1212
|
+
|
|
1213
|
+
--borderColor first: that is the variable the gui theme scope re-bases, and
|
|
1214
|
+
it is what a card's own edge is drawn with. Falling through to --border keeps
|
|
1215
|
+
a host that has design's tokens but no gui scope from losing the line. */
|
|
1216
|
+
[data-slot="rows"] > * + * {
|
|
1217
|
+
border-top: 1px solid var(--borderColor, var(--border));
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1097
1220
|
/* @hanzo/ui motion primitives — the calm, restrained animation vocabulary the
|
|
1098
1221
|
Hanzo surfaces share. One place defines the easing; components apply the class.
|
|
1099
1222
|
`className` forwards to the underlying DOM node on web, so the browser
|
|
@@ -1253,10 +1376,17 @@
|
|
|
1253
1376
|
letter-spacing: -0.01em;
|
|
1254
1377
|
}
|
|
1255
1378
|
|
|
1379
|
+
/* Paper — a ringed sheet: the ladder's lit edge and drop, plus a hairline all
|
|
1380
|
+
the way round. The three values are @hanzo/design's, reached by their real
|
|
1381
|
+
names. They used to be `--hz-ring` / `--hz-paper-highlight` /
|
|
1382
|
+
`--hz-elevation-3`, none of which is defined in this repo, in @hanzo/design
|
|
1383
|
+
or in any consumer — so all three resolved to hardcoded fallbacks, and the
|
|
1384
|
+
comment above promising that "a host that defines its own --hz-* tokens still
|
|
1385
|
+
wins" described a hook nobody could reach. */
|
|
1256
1386
|
.paper {
|
|
1257
|
-
box-shadow: var(--
|
|
1258
|
-
var(--
|
|
1259
|
-
var(--
|
|
1387
|
+
box-shadow: var(--shadow-inset-hairline, inset 0 0 0 1px rgb(255 255 255 / .10)),
|
|
1388
|
+
var(--edge-highlight, inset 0 1px 0 0 rgb(255 255 255 / .10)),
|
|
1389
|
+
var(--shadow-lg, 0 10px 15px -3px rgb(0 0 0 / .55), 0 4px 6px -4px rgb(0 0 0 / .55));
|
|
1260
1390
|
}
|
|
1261
1391
|
|
|
1262
1392
|
@keyframes menu-in {
|
package/dist/theme.css
CHANGED
|
@@ -708,20 +708,8 @@
|
|
|
708
708
|
The url()s are relative to THIS file, so they resolve wherever the package is
|
|
709
709
|
mounted — node_modules, a CDN, a copied dist — with no configuration. */
|
|
710
710
|
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
src:url("./assets/fonts/Geist-Variable.woff2") format("woff2");
|
|
714
|
-
font-weight:100 900;
|
|
715
|
-
font-style:normal;
|
|
716
|
-
font-display:swap;
|
|
717
|
-
}
|
|
718
|
-
@font-face{
|
|
719
|
-
font-family:"Geist Mono";
|
|
720
|
-
src:url("./assets/fonts/GeistMono-Variable.woff2") format("woff2");
|
|
721
|
-
font-weight:100 900;
|
|
722
|
-
font-style:normal;
|
|
723
|
-
font-display:swap;
|
|
724
|
-
}
|
|
711
|
+
|
|
712
|
+
|
|
725
713
|
|
|
726
714
|
:root{
|
|
727
715
|
--font-sans:"Geist","Geist Sans",ui-sans-serif,system-ui,sans-serif;
|
|
@@ -1074,20 +1062,155 @@
|
|
|
1074
1062
|
[data-touch-y="23"]::after { top: -23px; bottom: -23px; }
|
|
1075
1063
|
[data-touch-y="24"]::after { top: -24px; bottom: -24px; }
|
|
1076
1064
|
|
|
1077
|
-
/*
|
|
1078
|
-
.
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1065
|
+
/* The material, the elevation ladder, the scrim and the row separators live in
|
|
1066
|
+
src/glass.css and are inlined here by scripts/compose-theme.mjs — one home,
|
|
1067
|
+
two entry points (`@hanzo/ui/theme.css` and `@hanzo/ui/glass.css`), so a host
|
|
1068
|
+
that wants only the chrome material does not have to take the token layer
|
|
1069
|
+
with it.
|
|
1070
|
+
|
|
1071
|
+
They used to be four `.elevation-*` rules reading `var(--hz-elevation-N, …)`.
|
|
1072
|
+
Nothing in this repo, in @hanzo/design or in any consumer ever DEFINED an
|
|
1073
|
+
`--hz-elevation-*` variable, so every one of those resolved to its fallback:
|
|
1074
|
+
an indirection through a name that does not exist, with a hardcoded shadow
|
|
1075
|
+
behind it, next to a design system that publishes real ones. */
|
|
1076
|
+
|
|
1077
|
+
/* Glass — the material floating chrome is made of, and the ladder it stands on.
|
|
1078
|
+
*
|
|
1079
|
+
* import '@hanzo/ui/glass.css' // this slice alone
|
|
1080
|
+
* import '@hanzo/ui/theme.css' // the whole sheet; this is inlined in it
|
|
1081
|
+
*
|
|
1082
|
+
* Menus, dialogs, popovers, toasts, sticky bars: things with page UNDER them.
|
|
1083
|
+
* That last clause is the whole rule. Vibrancy is a lens — translucent ground
|
|
1084
|
+
* plus a backdrop blur — so it shows a softened version of whatever it covers,
|
|
1085
|
+
* and covering nothing but the flat canvas it has nothing to soften. An in-flow
|
|
1086
|
+
* card wearing this is not glass; it is the page with extra steps. So: over
|
|
1087
|
+
* content -> glass, in the flow -> the surface ladder. There is no third case.
|
|
1088
|
+
*
|
|
1089
|
+
* ── Values come from @hanzo/design ──────────────────────────────────────────
|
|
1090
|
+
* Every colour, shadow and light below is a design token. The fallbacks are
|
|
1091
|
+
* design's OWN published values, present so a host that imports this file
|
|
1092
|
+
* without design's sheet still gets a ladder instead of a silently-dropped
|
|
1093
|
+
* declaration (an undefined var() invalidates the whole property). They are a
|
|
1094
|
+
* mirror, not a second opinion — glass.test.ts reads @hanzo/design/styles.css
|
|
1095
|
+
* and fails if any of them stops matching, so the copy cannot drift.
|
|
1096
|
+
*/
|
|
1082
1097
|
|
|
1083
|
-
/*
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1098
|
+
/* ── The material ───────────────────────────────────────────────────────────
|
|
1099
|
+
Applied BY SLOT, not by call site. Every surface below is floating chrome by
|
|
1100
|
+
construction — a popover is over the page or it is nothing — so which of them
|
|
1101
|
+
is glass was never a decision a call site should get to make. It was making
|
|
1102
|
+
it anyway: in the console, of 135 floating surfaces exactly ONE had reached
|
|
1103
|
+
for the material by hand and a dozen dialogs had gone the other way and
|
|
1104
|
+
pinned an opaque fill. Reaching them one by one is 134 edits that agree today
|
|
1105
|
+
and drift apart on the next one somebody adds. A new dialog is glass because
|
|
1106
|
+
it is a dialog.
|
|
1107
|
+
|
|
1108
|
+
`.glass` is here for the same reason `glass()` exists in the recipes module:
|
|
1109
|
+
a hand-rolled bar or a custom menu is floating chrome that no slot names.
|
|
1110
|
+
|
|
1111
|
+
The ground is derived from --background rather than fixed, so the material
|
|
1112
|
+
follows the theme instead of assuming a dark one.
|
|
1113
|
+
|
|
1114
|
+
The opaque `background-color` outside the @supports block is load-bearing
|
|
1115
|
+
rather than belt-and-braces: a browser that cannot blur gets no rule at all
|
|
1116
|
+
from inside it, and a menu with no background is a menu you read the page
|
|
1117
|
+
through. !important beats the compiled atomic background class, which lands
|
|
1118
|
+
in an inline <style> a bundler orders after this sheet, and it beats the call
|
|
1119
|
+
sites that pin a fill by hand — those become no-ops instead of holes. */
|
|
1087
1120
|
@supports (backdrop-filter: blur(8px)) {
|
|
1088
|
-
|
|
1121
|
+
.glass,
|
|
1122
|
+
[data-slot="glass"],
|
|
1123
|
+
[data-slot="dialog-content"],
|
|
1124
|
+
[data-slot="popover-content"],
|
|
1125
|
+
[data-slot="select-content"],
|
|
1126
|
+
[data-slot="dropdown-menu-content"],
|
|
1127
|
+
[data-slot="dropdown-menu-sub-content"],
|
|
1128
|
+
[data-slot="tooltip-content"] {
|
|
1089
1129
|
background-color: color-mix(in oklab, var(--background) 72%, transparent) !important;
|
|
1090
1130
|
-webkit-backdrop-filter: blur(20px) saturate(1.8);
|
|
1091
1131
|
backdrop-filter: blur(20px) saturate(1.8);
|
|
1092
1132
|
}
|
|
1093
1133
|
}
|
|
1134
|
+
|
|
1135
|
+
/* ── Depth — three rungs, and the lit edge is the load-bearing half ──────────
|
|
1136
|
+
A cast shadow is how a light page shows lift. On a near-black canvas it is
|
|
1137
|
+
nearly free of information: black on near-black moves no pixels, so a raised
|
|
1138
|
+
panel wearing only a shadow reads exactly as flat as one wearing none. What
|
|
1139
|
+
says "this surface is above that one" in a dark UI is the LIT EDGE — the top
|
|
1140
|
+
lip catching light, which is why every macOS/iOS glass panel has one. Design
|
|
1141
|
+
spends the same two tokens for the same reason and zeroes --edge-highlight in
|
|
1142
|
+
`.light`, where a white line on a white card is nothing and the drop does all
|
|
1143
|
+
the work: one composition, both themes, no branch here.
|
|
1144
|
+
|
|
1145
|
+
Levels are a LADDER, not a palette — 1 rests in the flow, 2 lifts off it, 3
|
|
1146
|
+
floats over the page. Nothing needs a fourth; if something seems to, it is
|
|
1147
|
+
sitting at the wrong level.
|
|
1148
|
+
|
|
1149
|
+
Anchored chrome — a menu pinned to the control that opened it, a tooltip to
|
|
1150
|
+
its target — is rung 2. A modal has no anchor and a scrim of its own, so it
|
|
1151
|
+
floats free at 3. Those slots share the rung's rule rather than restating its
|
|
1152
|
+
value, so the ladder cannot come apart from the surfaces standing on it.
|
|
1153
|
+
|
|
1154
|
+
Glass over glass (a select opened inside a dialog) is deliberately left to
|
|
1155
|
+
stack: two 72% grounds land near 92%, so the nested surface reads as MORE
|
|
1156
|
+
solid than its parent, which is the right answer and the one macOS gives. */
|
|
1157
|
+
.elevation-1 {
|
|
1158
|
+
box-shadow:
|
|
1159
|
+
var(--edge-highlight, inset 0 1px 0 0 rgb(255 255 255 / .10)),
|
|
1160
|
+
var(--shadow-sm, 0 1px 2px 0 rgb(0 0 0 / .40)) !important;
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
.elevation-2,
|
|
1164
|
+
[data-slot="popover-content"],
|
|
1165
|
+
[data-slot="select-content"],
|
|
1166
|
+
[data-slot="dropdown-menu-content"],
|
|
1167
|
+
[data-slot="dropdown-menu-sub-content"],
|
|
1168
|
+
[data-slot="tooltip-content"] {
|
|
1169
|
+
box-shadow:
|
|
1170
|
+
var(--edge-highlight, inset 0 1px 0 0 rgb(255 255 255 / .10)),
|
|
1171
|
+
var(--shadow-lg, 0 10px 15px -3px rgb(0 0 0 / .55), 0 4px 6px -4px rgb(0 0 0 / .55)) !important;
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
.elevation-3,
|
|
1175
|
+
[data-slot="dialog-content"] {
|
|
1176
|
+
box-shadow:
|
|
1177
|
+
var(--edge-highlight, inset 0 1px 0 0 rgb(255 255 255 / .10)),
|
|
1178
|
+
var(--shadow-floating, 0 24px 60px -16px rgb(0 0 0 / .75)) !important;
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
/* ── The scrim ──────────────────────────────────────────────────────────────
|
|
1182
|
+
The dim that makes a floating panel read as floating rather than as more
|
|
1183
|
+
page. gui's overlay dims TWICE — a translucent ground under a further
|
|
1184
|
+
`opacity` — landing at about a quarter black. Opaque chrome gets away with
|
|
1185
|
+
it; glass does not, because the blur behind a translucent panel only reads as
|
|
1186
|
+
depth when the ground behind it is genuinely darker.
|
|
1187
|
+
|
|
1188
|
+
One value, in one place: the recipes module reads the same token, so the dim
|
|
1189
|
+
behind a component dialog and the dim behind a hand-rolled one are the same
|
|
1190
|
+
dim by construction rather than by two people remembering a number. */
|
|
1191
|
+
[data-slot="dialog-overlay"] {
|
|
1192
|
+
opacity: 1;
|
|
1193
|
+
background-color: var(--surface-scrim, rgb(0 0 0 / .8));
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
/* ── Grouped rows ───────────────────────────────────────────────────────────
|
|
1197
|
+
One card, hairlines between its rows — the macOS settings group. Four
|
|
1198
|
+
separate cards for four toggles reads as four unrelated decisions, each with
|
|
1199
|
+
its own four edges; the same four inside one card reads as one set and drops
|
|
1200
|
+
twelve edges of noise.
|
|
1201
|
+
|
|
1202
|
+
The separator is drawn by the PARENT, because only the parent knows which
|
|
1203
|
+
child is last. Every by-hand version either left a trailing hairline sitting
|
|
1204
|
+
against the card's own bottom border (two parallel lines, 1px apart) or made
|
|
1205
|
+
each row take a `first` flag it should never have needed. `> * + *` has
|
|
1206
|
+
neither failure mode and needs nothing from the call site.
|
|
1207
|
+
|
|
1208
|
+
Pair with `overflow: hidden` on the card (the `rows` recipe does) so the
|
|
1209
|
+
separators stop at the radius instead of poking through the rounded corner.
|
|
1210
|
+
|
|
1211
|
+
--borderColor first: that is the variable the gui theme scope re-bases, and
|
|
1212
|
+
it is what a card's own edge is drawn with. Falling through to --border keeps
|
|
1213
|
+
a host that has design's tokens but no gui scope from losing the line. */
|
|
1214
|
+
[data-slot="rows"] > * + * {
|
|
1215
|
+
border-top: 1px solid var(--borderColor, var(--border));
|
|
1216
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanzo/ui",
|
|
3
|
-
"version": "8.0.
|
|
3
|
+
"version": "8.0.48",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Hanzo UI — the one canonical Hanzo component library, on @hanzo/gui + @hanzo/design. ONE substrate: every component renders through @hanzo/gui primitives, so the same import works on web, native (expo) and desktop (Tauri). Self-contained (theme.css), presentational, host-agnostic, clean-room.",
|
|
6
6
|
"exports": {
|
|
@@ -38,6 +38,13 @@
|
|
|
38
38
|
"import": "./dist/core/tokens.js"
|
|
39
39
|
},
|
|
40
40
|
"./theme.css": "./dist/theme.css",
|
|
41
|
+
"./glass.css": "./dist/glass.css",
|
|
42
|
+
"./glass": {
|
|
43
|
+
"types": "./dist/glass.d.ts",
|
|
44
|
+
"import": "./dist/glass.js",
|
|
45
|
+
"require": "./dist/glass.cjs",
|
|
46
|
+
"default": "./dist/glass.js"
|
|
47
|
+
},
|
|
41
48
|
"./product": {
|
|
42
49
|
"types": "./dist/product/index.d.ts",
|
|
43
50
|
"import": "./dist/product/index.js",
|
|
@@ -232,9 +239,10 @@
|
|
|
232
239
|
"tc": "tsc --noEmit",
|
|
233
240
|
"typecheck": "tsc --noEmit",
|
|
234
241
|
"typecheck:ui": "tsc -p tsconfig.check.json",
|
|
235
|
-
"test": "
|
|
242
|
+
"test": "pnpm run test:unit",
|
|
236
243
|
"test:watch": "vitest",
|
|
237
244
|
"test:consumer": "node scripts/consumer-test.mjs",
|
|
238
|
-
"test:unit": "vitest run"
|
|
245
|
+
"test:unit": "vitest run",
|
|
246
|
+
"test:consumer:webpack": "node scripts/consumer-webpack.mjs"
|
|
239
247
|
}
|
|
240
248
|
}
|