@hanzo/ui 8.17.0 → 8.17.1

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/gallery.html CHANGED
@@ -1,4 +1,4 @@
1
- <!doctype html><html lang="en" class="t_dark"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>@hanzo/ui 8.17.0</title><style>/* @hanzo/ui — generated by scripts/gen-css.mjs. Do not edit. */
1
+ <!doctype html><html lang="en" class="t_dark"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>@hanzo/ui 8.17.1</title><style>/* @hanzo/ui — generated by scripts/gen-css.mjs. Do not edit. */
2
2
  :root { --hanzo-ui-styles: 1; }
3
3
  /* Hanzo Design System — the entry point. Import THIS one file.
4
4
  *
@@ -1725,6 +1725,73 @@
1725
1725
  border-bottom-right-radius: 0 !important;
1726
1726
  }
1727
1727
 
1728
+ /* ── The crease — the valley where one sheet lies over another ───────────────
1729
+ The ladder lifts a sheet with a drop that falls STRAIGHT DOWN: design's rule
1730
+ is one light, above, and `check-tokens.mjs` fails the build on a sideways
1731
+ offset. That is right, and it means the ladder has nothing to say about a
1732
+ VERTICAL seam — and an app shell is nothing but vertical seams. A rail beside
1733
+ a sidebar beside a pane, each a step apart on the ladder, still reads as three
1734
+ abutting rectangles with lines ruled between them, because the only mark at
1735
+ the seam is the hairline.
1736
+
1737
+ What a real sheet lays on the one beneath it, side by side under a light from
1738
+ above, is not a cast shadow — it is the valley going dark. Ambient occlusion
1739
+ has no direction of its own, which is both why it is honest here and why it is
1740
+ NOT spelled as a --shadow-*: those are directional by definition and this is
1741
+ not one.
1742
+
1743
+ A PSEUDO-ELEMENT, at z-index -1 inside an isolated stacking context, so it
1744
+ paints above the sheet's own fill and below every child. That ordering is the
1745
+ whole reason it is not a `background-image` (which `.fold` already claims on
1746
+ this same element, !important, so the two would silently fight) and not an
1747
+ inset `box-shadow` (which `.elevation-*` claims, likewise).
1748
+
1749
+ No side named -> both edges, which is what a pane with a neighbour on each
1750
+ hand needs. Name one and the other stops being drawn. */
1751
+ :root {
1752
+ --crease-ink: rgb(0 0 0 / .28);
1753
+ --crease-depth: 20px;
1754
+ }
1755
+
1756
+ .light, :root.t_light, .t_light {
1757
+ /* On white the valley is a smudge, not a shadow — the same redistribution the
1758
+ drops make, and for the same reason. */
1759
+ --crease-ink: rgb(0 0 0 / .05);
1760
+ }
1761
+
1762
+ .hz-crease {
1763
+ position: relative;
1764
+ /* Without this the -1 escapes and paints behind an ancestor's fill, which is
1765
+ indistinguishable from the crease not being there at all. */
1766
+ isolation: isolate;
1767
+ }
1768
+
1769
+ .hz-crease::before,
1770
+ .hz-crease::after {
1771
+ content: "";
1772
+ position: absolute;
1773
+ top: 0;
1774
+ bottom: 0;
1775
+ width: var(--crease-depth);
1776
+ z-index: -1;
1777
+ pointer-events: none;
1778
+ }
1779
+
1780
+ .hz-crease::before {
1781
+ left: 0;
1782
+ background-image: linear-gradient(to right, var(--crease-ink), transparent);
1783
+ }
1784
+
1785
+ .hz-crease::after {
1786
+ right: 0;
1787
+ background-image: linear-gradient(to left, var(--crease-ink), transparent);
1788
+ }
1789
+
1790
+ .hz-crease[data-crease="left"]::after,
1791
+ .hz-crease[data-crease="right"]::before {
1792
+ content: none;
1793
+ }
1794
+
1728
1795
  /* ── The scrim ──────────────────────────────────────────────────────────────
1729
1796
  The dim that makes a floating panel read as floating rather than as more
1730
1797
  page. gui's overlay dims TWICE — a translucent ground under a further
@@ -2370,6 +2437,52 @@ body {
2370
2437
  }
2371
2438
  }
2372
2439
 
2440
+ /* ── Unfold — how a sheet arrives ─────────────────────────────────────────────
2441
+ A panel that appears by fading is a rectangle switching on. A sheet of paper
2442
+ arrives by TURNING OPEN about the edge it is hinged on, so its near edge swings
2443
+ toward the reader and its far edge stays put — which is the one motion that
2444
+ says "this was already here, folded" rather than "this was drawn just now".
2445
+
2446
+ The hinge is the ONLY thing that varies, so it is the only knob: a drawer
2447
+ coming off the left wall turns about its left edge, a pane folding in from the
2448
+ right about its right. The angle follows the hinge — a sheet swings toward you
2449
+ from whichever side it is held — so naming the hinge sets both, and there is no
2450
+ way to ask for a turn that opens the wrong way.
2451
+
2452
+ `perspective()` inside the transform rather than on a parent: the vanishing
2453
+ point is then the sheet's own centre, so the turn reads at any size and the
2454
+ caller does not have to own a 3D context it did not ask for.
2455
+
2456
+ Seven degrees over 220ms. Enough to see the plane; short of the swinging-door
2457
+ effect that makes a workspace feel like a slideshow. */
2458
+ @keyframes hz-unfold {
2459
+ from {
2460
+ opacity: 0;
2461
+ transform: perspective(1600px) rotateY(var(--hz-turn, -7deg));
2462
+ }
2463
+ to {
2464
+ opacity: 1;
2465
+ transform: perspective(1600px) rotateY(0deg);
2466
+ }
2467
+ }
2468
+
2469
+ .hz-unfold {
2470
+ animation: hz-unfold 220ms cubic-bezier(0.2, 0.7, 0.3, 1) both;
2471
+ transform-origin: left center;
2472
+ will-change: transform, opacity;
2473
+ }
2474
+
2475
+ .hz-unfold[data-hinge="right"] {
2476
+ transform-origin: right center;
2477
+ --hz-turn: 7deg;
2478
+ }
2479
+
2480
+ @media (prefers-reduced-motion: reduce) {
2481
+ .hz-unfold {
2482
+ animation: none;
2483
+ }
2484
+ }
2485
+
2373
2486
  /* ── Layout guarantees ─────────────────────────────────────────────────────────
2374
2487
  Two rules, both structural. They are attached to `data-slot` rather than to a
2375
2488
  class because they are not styling a caller can opt out of — they are the
package/dist/glass.cjs CHANGED
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.screen = exports.accent = exports.selected = exports.row = exports.rows = exports.panel = exports.scrim = exports.fold = exports.sheet = exports.glass = void 0;
3
+ exports.screen = exports.accent = exports.selected = exports.row = exports.rows = exports.panel = exports.scrim = exports.fold = exports.unfold = exports.crease = exports.sheet = exports.glass = void 0;
4
+ exports.paper = paper;
4
5
  /**
5
6
  * The chrome, as values — `@hanzo/ui/glass`.
6
7
  *
@@ -107,6 +108,51 @@ const SHEET_FILL = {
107
108
  1: 'var(--sheet-1, rgb(38 38 38 / .5))',
108
109
  2: 'var(--sheet-2, rgb(38 38 38 / .75))',
109
110
  };
111
+ /**
112
+ * The crease — the valley where a neighbouring sheet lies over this one.
113
+ *
114
+ * <YStack {...paper(sheet(0), crease('right'))}>
115
+ *
116
+ * The ladder's drops fall straight down (design's rule is one light, above), so
117
+ * it has nothing to say about a VERTICAL seam — and an app shell is nothing but
118
+ * vertical seams. Three sheets a rung apart, side by side, still read as three
119
+ * abutting rectangles. This is the mark that makes them read as paper: the
120
+ * ground going dark in the valley, which is what a sheet actually lays on the
121
+ * one beside it.
122
+ *
123
+ * It goes on the sheet that is COVERED, on the edge the neighbour lies over —
124
+ * a rail with a sidebar to its right takes `crease('right')`. Nothing can paint
125
+ * outside its own box, so the receiver draws it; the caster only has to be
126
+ * higher up the ladder.
127
+ *
128
+ * Spend it at a seam and nowhere else. A crease down the side of a card with
129
+ * nothing beside it is a gradient somebody liked.
130
+ */
131
+ const crease = (edge) => ({ className: 'hz-crease', ...(edge ? { 'data-crease': edge } : {}) });
132
+ exports.crease = crease;
133
+ /**
134
+ * Unfold — how a sheet ARRIVES: turning open about the edge it is hinged on,
135
+ * rather than fading up as a rectangle.
136
+ *
137
+ * <YStack {...paper(sheet(2), unfold('right'))}>
138
+ *
139
+ * The hinge is the only knob, and it sets the direction of the turn with it, so
140
+ * a sheet cannot be asked to swing the wrong way. A drawer off the left wall is
141
+ * `unfold()`; a pane folding in from the right edge is `unfold('right')`.
142
+ *
143
+ * Motion only — it says nothing about what the surface is made of, so it
144
+ * composes with `sheet`, `glass` or neither. Off under reduced motion, in
145
+ * `styles/motion.css` beside every other keyframe this package ships.
146
+ */
147
+ const unfold = (hinge = 'left') => ({ className: 'hz-unfold', ...(hinge === 'right' ? { 'data-hinge': 'right' } : {}) });
148
+ exports.unfold = unfold;
149
+ function paper(...marks) {
150
+ const className = marks
151
+ .map((mark) => mark.className)
152
+ .filter(Boolean)
153
+ .join(' ');
154
+ return { ...Object.assign({}, ...marks), ...(className ? { className } : {}) };
155
+ }
110
156
  /**
111
157
  * The fold — a corner turned back, and the ONE mark that says an item opens.
112
158
  *
package/dist/glass.css CHANGED
@@ -244,6 +244,73 @@
244
244
  border-bottom-right-radius: 0 !important;
245
245
  }
246
246
 
247
+ /* ── The crease — the valley where one sheet lies over another ───────────────
248
+ The ladder lifts a sheet with a drop that falls STRAIGHT DOWN: design's rule
249
+ is one light, above, and `check-tokens.mjs` fails the build on a sideways
250
+ offset. That is right, and it means the ladder has nothing to say about a
251
+ VERTICAL seam — and an app shell is nothing but vertical seams. A rail beside
252
+ a sidebar beside a pane, each a step apart on the ladder, still reads as three
253
+ abutting rectangles with lines ruled between them, because the only mark at
254
+ the seam is the hairline.
255
+
256
+ What a real sheet lays on the one beneath it, side by side under a light from
257
+ above, is not a cast shadow — it is the valley going dark. Ambient occlusion
258
+ has no direction of its own, which is both why it is honest here and why it is
259
+ NOT spelled as a --shadow-*: those are directional by definition and this is
260
+ not one.
261
+
262
+ A PSEUDO-ELEMENT, at z-index -1 inside an isolated stacking context, so it
263
+ paints above the sheet's own fill and below every child. That ordering is the
264
+ whole reason it is not a `background-image` (which `.fold` already claims on
265
+ this same element, !important, so the two would silently fight) and not an
266
+ inset `box-shadow` (which `.elevation-*` claims, likewise).
267
+
268
+ No side named -> both edges, which is what a pane with a neighbour on each
269
+ hand needs. Name one and the other stops being drawn. */
270
+ :root {
271
+ --crease-ink: rgb(0 0 0 / .28);
272
+ --crease-depth: 20px;
273
+ }
274
+
275
+ .light, :root.t_light, .t_light {
276
+ /* On white the valley is a smudge, not a shadow — the same redistribution the
277
+ drops make, and for the same reason. */
278
+ --crease-ink: rgb(0 0 0 / .05);
279
+ }
280
+
281
+ .hz-crease {
282
+ position: relative;
283
+ /* Without this the -1 escapes and paints behind an ancestor's fill, which is
284
+ indistinguishable from the crease not being there at all. */
285
+ isolation: isolate;
286
+ }
287
+
288
+ .hz-crease::before,
289
+ .hz-crease::after {
290
+ content: "";
291
+ position: absolute;
292
+ top: 0;
293
+ bottom: 0;
294
+ width: var(--crease-depth);
295
+ z-index: -1;
296
+ pointer-events: none;
297
+ }
298
+
299
+ .hz-crease::before {
300
+ left: 0;
301
+ background-image: linear-gradient(to right, var(--crease-ink), transparent);
302
+ }
303
+
304
+ .hz-crease::after {
305
+ right: 0;
306
+ background-image: linear-gradient(to left, var(--crease-ink), transparent);
307
+ }
308
+
309
+ .hz-crease[data-crease="left"]::after,
310
+ .hz-crease[data-crease="right"]::before {
311
+ content: none;
312
+ }
313
+
247
314
  /* ── The scrim ──────────────────────────────────────────────────────────────
248
315
  The dim that makes a floating panel read as floating rather than as more
249
316
  page. gui's overlay dims TWICE — a translucent ground under a further
package/dist/glass.d.ts CHANGED
@@ -75,6 +75,76 @@ export declare const sheet: (level?: 0 | 1 | 2) => {
75
75
  readonly backgroundColor: "var(--sheet-0, #0a0a0a)" | "var(--sheet-1, rgb(38 38 38 / .5))" | "var(--sheet-2, rgb(38 38 38 / .75))";
76
76
  readonly className?: "elevation-1" | "elevation-2" | undefined;
77
77
  };
78
+ /**
79
+ * Which of a surface's edges lies UNDER a neighbour. Omit for both, which is
80
+ * what a pane between two other panes needs.
81
+ */
82
+ export type Edge = 'left' | 'right';
83
+ /**
84
+ * The crease — the valley where a neighbouring sheet lies over this one.
85
+ *
86
+ * <YStack {...paper(sheet(0), crease('right'))}>
87
+ *
88
+ * The ladder's drops fall straight down (design's rule is one light, above), so
89
+ * it has nothing to say about a VERTICAL seam — and an app shell is nothing but
90
+ * vertical seams. Three sheets a rung apart, side by side, still read as three
91
+ * abutting rectangles. This is the mark that makes them read as paper: the
92
+ * ground going dark in the valley, which is what a sheet actually lays on the
93
+ * one beside it.
94
+ *
95
+ * It goes on the sheet that is COVERED, on the edge the neighbour lies over —
96
+ * a rail with a sidebar to its right takes `crease('right')`. Nothing can paint
97
+ * outside its own box, so the receiver draws it; the caster only has to be
98
+ * higher up the ladder.
99
+ *
100
+ * Spend it at a seam and nowhere else. A crease down the side of a card with
101
+ * nothing beside it is a gradient somebody liked.
102
+ */
103
+ export declare const crease: (edge?: Edge) => {
104
+ readonly className: 'hz-crease';
105
+ readonly 'data-crease'?: Edge | undefined;
106
+ };
107
+ /**
108
+ * Unfold — how a sheet ARRIVES: turning open about the edge it is hinged on,
109
+ * rather than fading up as a rectangle.
110
+ *
111
+ * <YStack {...paper(sheet(2), unfold('right'))}>
112
+ *
113
+ * The hinge is the only knob, and it sets the direction of the turn with it, so
114
+ * a sheet cannot be asked to swing the wrong way. A drawer off the left wall is
115
+ * `unfold()`; a pane folding in from the right edge is `unfold('right')`.
116
+ *
117
+ * Motion only — it says nothing about what the surface is made of, so it
118
+ * composes with `sheet`, `glass` or neither. Off under reduced motion, in
119
+ * `styles/motion.css` beside every other keyframe this package ships.
120
+ */
121
+ export declare const unfold: (hinge?: Edge) => {
122
+ readonly className: 'hz-unfold';
123
+ readonly 'data-hinge'?: string | undefined;
124
+ };
125
+ interface Mark {
126
+ className?: string;
127
+ }
128
+ /**
129
+ * Compose marks onto one surface, without losing a class.
130
+ *
131
+ * <YStack {...paper(sheet(1), crease('left'), fold)}>
132
+ *
133
+ * SPREADING TWO RECIPES SILENTLY DROPS ONE. `{...sheet(1)} {...fold}` — the
134
+ * composition `fold`'s own documentation shows — keeps only the last
135
+ * `className`, so the sheet arrives with its fill and its fold and WITHOUT
136
+ * `elevation-1`: the surface is a shade lighter than the page and flat, which
137
+ * looks like a design decision rather than a lost property. JSX assigns whole
138
+ * keys, and `className` is one key; this repo already records the same shape
139
+ * costing a breakpoint object.
140
+ *
141
+ * So marks are composed through here rather than beside each other. Every other
142
+ * key still takes the last writer, which is what a caller means by ordering
143
+ * them; only the class list accumulates.
144
+ */
145
+ export declare function paper<A extends Mark>(a: A): A;
146
+ export declare function paper<A extends Mark, B extends Mark>(a: A, b: B): A & B;
147
+ export declare function paper<A extends Mark, B extends Mark, C extends Mark>(a: A, b: B, c: C): A & B & C;
78
148
  /**
79
149
  * The fold — a corner turned back, and the ONE mark that says an item opens.
80
150
  *
@@ -289,4 +359,5 @@ export declare const screen: {
289
359
  readonly alignItems: 'center';
290
360
  readonly justifyContent: 'center';
291
361
  };
362
+ export {};
292
363
  //# sourceMappingURL=glass.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"glass.d.ts","sourceRoot":"","sources":["../src/glass.ts"],"names":[],"mappings":"AA8BA;;;;;GAKG;AACH,MAAM,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,CAAA;AAExB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,eAAO,MAAM,KAAK,WAAW,IAAI;aAE7B,SAAS;aACT,eAAe,EAAE,aAAa;aAC9B,WAAW,EAAE,cAAc;CAClB,CAAA;AAEb;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,KAAK,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC;;;CAIzB,CAAA;AAQb;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,IAAI;aAAK,SAAS,EAAE,MAAM;CAAW,CAAA;AAElD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,KAAK;aAAK,eAAe,EAAE,uCAAuC;CAAW,CAAA;AAE1F;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,KAAK;aAChB,eAAe,EAAE,SAAS;aAC1B,WAAW,EAAE,CAAC;aACd,WAAW,EAAE,cAAc;aAC3B,YAAY,EAAE,IAAI;aAClB,SAAS,EAAE,aAAa;CAChB,CAAA;AAEV;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,IAAI;8BAxBE,SAAS;0BACb,CAAC;0BACD,cAAc;2BACb,IAAI;wBACP,aAAa;uBAuBd,QAAQ;CACV,CAAA;AAEV;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,GAAG;aACd,aAAa,EAAE,KAAK;aACpB,UAAU,EAAE,QAAQ;aACpB,cAAc,EAAE,eAAe;aAC/B,GAAG,EAAE,IAAI;aACT,iBAAiB,EAAE,IAAI;aACvB,eAAe,EAAE,IAAI;CACb,CAAA;AAEV;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,QAAQ,OAAQ,OAAO;8BAEV,SAAS;oBAAS,UAAU;;8BAC5B,aAAa;oBAAS,UAAU;CAAY,CAAA;AAEtE;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,MAAM;aACjB,eAAe,EAAE,SAAS;aAC1B,WAAW,EAAE,CAAC;aACd,WAAW,EAAE,SAAS;IACtB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAiCG;aACH,KAAK,EAAE,UAAU;aACjB,UAAU;iBAAI,eAAe,EAAE,SAAS;iBAAE,KAAK,EAAE,UAAU;;CACnD,CAAA;AAEV;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,MAAM;aACjB,SAAS,EAAE,QAAQ;aACnB,UAAU,EAAE,QAAQ;aACpB,cAAc,EAAE,QAAQ;CAChB,CAAA"}
1
+ {"version":3,"file":"glass.d.ts","sourceRoot":"","sources":["../src/glass.ts"],"names":[],"mappings":"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"}
package/dist/glass.js CHANGED
@@ -102,6 +102,49 @@ const SHEET_FILL = {
102
102
  1: 'var(--sheet-1, rgb(38 38 38 / .5))',
103
103
  2: 'var(--sheet-2, rgb(38 38 38 / .75))',
104
104
  };
105
+ /**
106
+ * The crease — the valley where a neighbouring sheet lies over this one.
107
+ *
108
+ * <YStack {...paper(sheet(0), crease('right'))}>
109
+ *
110
+ * The ladder's drops fall straight down (design's rule is one light, above), so
111
+ * it has nothing to say about a VERTICAL seam — and an app shell is nothing but
112
+ * vertical seams. Three sheets a rung apart, side by side, still read as three
113
+ * abutting rectangles. This is the mark that makes them read as paper: the
114
+ * ground going dark in the valley, which is what a sheet actually lays on the
115
+ * one beside it.
116
+ *
117
+ * It goes on the sheet that is COVERED, on the edge the neighbour lies over —
118
+ * a rail with a sidebar to its right takes `crease('right')`. Nothing can paint
119
+ * outside its own box, so the receiver draws it; the caster only has to be
120
+ * higher up the ladder.
121
+ *
122
+ * Spend it at a seam and nowhere else. A crease down the side of a card with
123
+ * nothing beside it is a gradient somebody liked.
124
+ */
125
+ export const crease = (edge) => ({ className: 'hz-crease', ...(edge ? { 'data-crease': edge } : {}) });
126
+ /**
127
+ * Unfold — how a sheet ARRIVES: turning open about the edge it is hinged on,
128
+ * rather than fading up as a rectangle.
129
+ *
130
+ * <YStack {...paper(sheet(2), unfold('right'))}>
131
+ *
132
+ * The hinge is the only knob, and it sets the direction of the turn with it, so
133
+ * a sheet cannot be asked to swing the wrong way. A drawer off the left wall is
134
+ * `unfold()`; a pane folding in from the right edge is `unfold('right')`.
135
+ *
136
+ * Motion only — it says nothing about what the surface is made of, so it
137
+ * composes with `sheet`, `glass` or neither. Off under reduced motion, in
138
+ * `styles/motion.css` beside every other keyframe this package ships.
139
+ */
140
+ export const unfold = (hinge = 'left') => ({ className: 'hz-unfold', ...(hinge === 'right' ? { 'data-hinge': 'right' } : {}) });
141
+ export function paper(...marks) {
142
+ const className = marks
143
+ .map((mark) => mark.className)
144
+ .filter(Boolean)
145
+ .join(' ');
146
+ return { ...Object.assign({}, ...marks), ...(className ? { className } : {}) };
147
+ }
105
148
  /**
106
149
  * The fold — a corner turned back, and the ONE mark that says an item opens.
107
150
  *
package/dist/glass.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"glass.js","sourceRoot":"","sources":["../src/glass.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAA;AAU1C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,KAAK,GAAS,CAAC,EAAE,EAAE,CACvC,CAAC;IACC,SAAS,EAAE,mBAAmB,KAAK,EAAE;IACrC,eAAe,EAAE,aAAa;IAC9B,WAAW,EAAE,cAAc;CAC5B,CAAU,CAAA;AAEb;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,KAAK,GAAc,CAAC,EAAE,EAAE,CAC5C,CAAC;IACC,eAAe,EAAE,UAAU,CAAC,KAAK,CAAC;IAClC,GAAG,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,aAAa,KAAK,EAAW,EAAE,CAAC;CACrE,CAAU,CAAA;AAEb,MAAM,UAAU,GAAG;IACjB,CAAC,EAAE,yBAAyB;IAC5B,CAAC,EAAE,oCAAoC;IACvC,CAAC,EAAE,qCAAqC;CAChC,CAAA;AAEV;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,EAAE,SAAS,EAAE,MAAM,EAAW,CAAA;AAElD;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,EAAE,eAAe,EAAE,uCAAuC,EAAW,CAAA;AAE1F;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG;IACnB,eAAe,EAAE,SAAS;IAC1B,WAAW,EAAE,CAAC;IACd,WAAW,EAAE,cAAc;IAC3B,YAAY,EAAE,IAAI;IAClB,SAAS,EAAE,aAAa;CAChB,CAAA;AAEV;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG;IAClB,GAAG,KAAK;IACR,GAAG,IAAI,CAAC,MAAM,CAAC;IACf,QAAQ,EAAE,QAAQ;CACV,CAAA;AAEV;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,GAAG,GAAG;IACjB,aAAa,EAAE,KAAK;IACpB,UAAU,EAAE,QAAQ;IACpB,cAAc,EAAE,eAAe;IAC/B,GAAG,EAAE,IAAI;IACT,iBAAiB,EAAE,IAAI;IACvB,eAAe,EAAE,IAAI;CACb,CAAA;AAEV;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,EAAW,EAAE,EAAE,CACtC,EAAE;IACA,CAAC,CAAE,EAAE,eAAe,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,EAAY;IAC9D,CAAC,CAAE,EAAE,eAAe,EAAE,aAAa,EAAE,KAAK,EAAE,UAAU,EAAY,CAAA;AAEtE;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,eAAe,EAAE,SAAS;IAC1B,WAAW,EAAE,CAAC;IACd,WAAW,EAAE,SAAS;IACtB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAiCG;IACH,KAAK,EAAE,UAAU;IACjB,UAAU,EAAE,EAAE,eAAe,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE;CACrD,CAAA;AAEV;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,SAAS,EAAE,QAAQ;IACnB,UAAU,EAAE,QAAQ;IACpB,cAAc,EAAE,QAAQ;CAChB,CAAA"}
1
+ {"version":3,"file":"glass.js","sourceRoot":"","sources":["../src/glass.ts"],"names":[],"mappings":"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"}
@@ -217,6 +217,52 @@
217
217
  }
218
218
  }
219
219
 
220
+ /* ── Unfold — how a sheet arrives ─────────────────────────────────────────────
221
+ A panel that appears by fading is a rectangle switching on. A sheet of paper
222
+ arrives by TURNING OPEN about the edge it is hinged on, so its near edge swings
223
+ toward the reader and its far edge stays put — which is the one motion that
224
+ says "this was already here, folded" rather than "this was drawn just now".
225
+
226
+ The hinge is the ONLY thing that varies, so it is the only knob: a drawer
227
+ coming off the left wall turns about its left edge, a pane folding in from the
228
+ right about its right. The angle follows the hinge — a sheet swings toward you
229
+ from whichever side it is held — so naming the hinge sets both, and there is no
230
+ way to ask for a turn that opens the wrong way.
231
+
232
+ `perspective()` inside the transform rather than on a parent: the vanishing
233
+ point is then the sheet's own centre, so the turn reads at any size and the
234
+ caller does not have to own a 3D context it did not ask for.
235
+
236
+ Seven degrees over 220ms. Enough to see the plane; short of the swinging-door
237
+ effect that makes a workspace feel like a slideshow. */
238
+ @keyframes hz-unfold {
239
+ from {
240
+ opacity: 0;
241
+ transform: perspective(1600px) rotateY(var(--hz-turn, -7deg));
242
+ }
243
+ to {
244
+ opacity: 1;
245
+ transform: perspective(1600px) rotateY(0deg);
246
+ }
247
+ }
248
+
249
+ .hz-unfold {
250
+ animation: hz-unfold 220ms cubic-bezier(0.2, 0.7, 0.3, 1) both;
251
+ transform-origin: left center;
252
+ will-change: transform, opacity;
253
+ }
254
+
255
+ .hz-unfold[data-hinge="right"] {
256
+ transform-origin: right center;
257
+ --hz-turn: 7deg;
258
+ }
259
+
260
+ @media (prefers-reduced-motion: reduce) {
261
+ .hz-unfold {
262
+ animation: none;
263
+ }
264
+ }
265
+
220
266
  /* ── Layout guarantees ─────────────────────────────────────────────────────────
221
267
  Two rules, both structural. They are attached to `data-slot` rather than to a
222
268
  class because they are not styling a caller can opt out of — they are the
package/dist/styles.css CHANGED
@@ -1725,6 +1725,73 @@
1725
1725
  border-bottom-right-radius: 0 !important;
1726
1726
  }
1727
1727
 
1728
+ /* ── The crease — the valley where one sheet lies over another ───────────────
1729
+ The ladder lifts a sheet with a drop that falls STRAIGHT DOWN: design's rule
1730
+ is one light, above, and `check-tokens.mjs` fails the build on a sideways
1731
+ offset. That is right, and it means the ladder has nothing to say about a
1732
+ VERTICAL seam — and an app shell is nothing but vertical seams. A rail beside
1733
+ a sidebar beside a pane, each a step apart on the ladder, still reads as three
1734
+ abutting rectangles with lines ruled between them, because the only mark at
1735
+ the seam is the hairline.
1736
+
1737
+ What a real sheet lays on the one beneath it, side by side under a light from
1738
+ above, is not a cast shadow — it is the valley going dark. Ambient occlusion
1739
+ has no direction of its own, which is both why it is honest here and why it is
1740
+ NOT spelled as a --shadow-*: those are directional by definition and this is
1741
+ not one.
1742
+
1743
+ A PSEUDO-ELEMENT, at z-index -1 inside an isolated stacking context, so it
1744
+ paints above the sheet's own fill and below every child. That ordering is the
1745
+ whole reason it is not a `background-image` (which `.fold` already claims on
1746
+ this same element, !important, so the two would silently fight) and not an
1747
+ inset `box-shadow` (which `.elevation-*` claims, likewise).
1748
+
1749
+ No side named -> both edges, which is what a pane with a neighbour on each
1750
+ hand needs. Name one and the other stops being drawn. */
1751
+ :root {
1752
+ --crease-ink: rgb(0 0 0 / .28);
1753
+ --crease-depth: 20px;
1754
+ }
1755
+
1756
+ .light, :root.t_light, .t_light {
1757
+ /* On white the valley is a smudge, not a shadow — the same redistribution the
1758
+ drops make, and for the same reason. */
1759
+ --crease-ink: rgb(0 0 0 / .05);
1760
+ }
1761
+
1762
+ .hz-crease {
1763
+ position: relative;
1764
+ /* Without this the -1 escapes and paints behind an ancestor's fill, which is
1765
+ indistinguishable from the crease not being there at all. */
1766
+ isolation: isolate;
1767
+ }
1768
+
1769
+ .hz-crease::before,
1770
+ .hz-crease::after {
1771
+ content: "";
1772
+ position: absolute;
1773
+ top: 0;
1774
+ bottom: 0;
1775
+ width: var(--crease-depth);
1776
+ z-index: -1;
1777
+ pointer-events: none;
1778
+ }
1779
+
1780
+ .hz-crease::before {
1781
+ left: 0;
1782
+ background-image: linear-gradient(to right, var(--crease-ink), transparent);
1783
+ }
1784
+
1785
+ .hz-crease::after {
1786
+ right: 0;
1787
+ background-image: linear-gradient(to left, var(--crease-ink), transparent);
1788
+ }
1789
+
1790
+ .hz-crease[data-crease="left"]::after,
1791
+ .hz-crease[data-crease="right"]::before {
1792
+ content: none;
1793
+ }
1794
+
1728
1795
  /* ── The scrim ──────────────────────────────────────────────────────────────
1729
1796
  The dim that makes a floating panel read as floating rather than as more
1730
1797
  page. gui's overlay dims TWICE — a translucent ground under a further
@@ -2370,6 +2437,52 @@ body {
2370
2437
  }
2371
2438
  }
2372
2439
 
2440
+ /* ── Unfold — how a sheet arrives ─────────────────────────────────────────────
2441
+ A panel that appears by fading is a rectangle switching on. A sheet of paper
2442
+ arrives by TURNING OPEN about the edge it is hinged on, so its near edge swings
2443
+ toward the reader and its far edge stays put — which is the one motion that
2444
+ says "this was already here, folded" rather than "this was drawn just now".
2445
+
2446
+ The hinge is the ONLY thing that varies, so it is the only knob: a drawer
2447
+ coming off the left wall turns about its left edge, a pane folding in from the
2448
+ right about its right. The angle follows the hinge — a sheet swings toward you
2449
+ from whichever side it is held — so naming the hinge sets both, and there is no
2450
+ way to ask for a turn that opens the wrong way.
2451
+
2452
+ `perspective()` inside the transform rather than on a parent: the vanishing
2453
+ point is then the sheet's own centre, so the turn reads at any size and the
2454
+ caller does not have to own a 3D context it did not ask for.
2455
+
2456
+ Seven degrees over 220ms. Enough to see the plane; short of the swinging-door
2457
+ effect that makes a workspace feel like a slideshow. */
2458
+ @keyframes hz-unfold {
2459
+ from {
2460
+ opacity: 0;
2461
+ transform: perspective(1600px) rotateY(var(--hz-turn, -7deg));
2462
+ }
2463
+ to {
2464
+ opacity: 1;
2465
+ transform: perspective(1600px) rotateY(0deg);
2466
+ }
2467
+ }
2468
+
2469
+ .hz-unfold {
2470
+ animation: hz-unfold 220ms cubic-bezier(0.2, 0.7, 0.3, 1) both;
2471
+ transform-origin: left center;
2472
+ will-change: transform, opacity;
2473
+ }
2474
+
2475
+ .hz-unfold[data-hinge="right"] {
2476
+ transform-origin: right center;
2477
+ --hz-turn: 7deg;
2478
+ }
2479
+
2480
+ @media (prefers-reduced-motion: reduce) {
2481
+ .hz-unfold {
2482
+ animation: none;
2483
+ }
2484
+ }
2485
+
2373
2486
  /* ── Layout guarantees ─────────────────────────────────────────────────────────
2374
2487
  Two rules, both structural. They are attached to `data-slot` rather than to a
2375
2488
  class because they are not styling a caller can opt out of — they are the
package/dist/theme.css CHANGED
@@ -1723,6 +1723,73 @@
1723
1723
  border-bottom-right-radius: 0 !important;
1724
1724
  }
1725
1725
 
1726
+ /* ── The crease — the valley where one sheet lies over another ───────────────
1727
+ The ladder lifts a sheet with a drop that falls STRAIGHT DOWN: design's rule
1728
+ is one light, above, and `check-tokens.mjs` fails the build on a sideways
1729
+ offset. That is right, and it means the ladder has nothing to say about a
1730
+ VERTICAL seam — and an app shell is nothing but vertical seams. A rail beside
1731
+ a sidebar beside a pane, each a step apart on the ladder, still reads as three
1732
+ abutting rectangles with lines ruled between them, because the only mark at
1733
+ the seam is the hairline.
1734
+
1735
+ What a real sheet lays on the one beneath it, side by side under a light from
1736
+ above, is not a cast shadow — it is the valley going dark. Ambient occlusion
1737
+ has no direction of its own, which is both why it is honest here and why it is
1738
+ NOT spelled as a --shadow-*: those are directional by definition and this is
1739
+ not one.
1740
+
1741
+ A PSEUDO-ELEMENT, at z-index -1 inside an isolated stacking context, so it
1742
+ paints above the sheet's own fill and below every child. That ordering is the
1743
+ whole reason it is not a `background-image` (which `.fold` already claims on
1744
+ this same element, !important, so the two would silently fight) and not an
1745
+ inset `box-shadow` (which `.elevation-*` claims, likewise).
1746
+
1747
+ No side named -> both edges, which is what a pane with a neighbour on each
1748
+ hand needs. Name one and the other stops being drawn. */
1749
+ :root {
1750
+ --crease-ink: rgb(0 0 0 / .28);
1751
+ --crease-depth: 20px;
1752
+ }
1753
+
1754
+ .light, :root.t_light, .t_light {
1755
+ /* On white the valley is a smudge, not a shadow — the same redistribution the
1756
+ drops make, and for the same reason. */
1757
+ --crease-ink: rgb(0 0 0 / .05);
1758
+ }
1759
+
1760
+ .hz-crease {
1761
+ position: relative;
1762
+ /* Without this the -1 escapes and paints behind an ancestor's fill, which is
1763
+ indistinguishable from the crease not being there at all. */
1764
+ isolation: isolate;
1765
+ }
1766
+
1767
+ .hz-crease::before,
1768
+ .hz-crease::after {
1769
+ content: "";
1770
+ position: absolute;
1771
+ top: 0;
1772
+ bottom: 0;
1773
+ width: var(--crease-depth);
1774
+ z-index: -1;
1775
+ pointer-events: none;
1776
+ }
1777
+
1778
+ .hz-crease::before {
1779
+ left: 0;
1780
+ background-image: linear-gradient(to right, var(--crease-ink), transparent);
1781
+ }
1782
+
1783
+ .hz-crease::after {
1784
+ right: 0;
1785
+ background-image: linear-gradient(to left, var(--crease-ink), transparent);
1786
+ }
1787
+
1788
+ .hz-crease[data-crease="left"]::after,
1789
+ .hz-crease[data-crease="right"]::before {
1790
+ content: none;
1791
+ }
1792
+
1726
1793
  /* ── The scrim ──────────────────────────────────────────────────────────────
1727
1794
  The dim that makes a floating panel read as floating rather than as more
1728
1795
  page. gui's overlay dims TWICE — a translucent ground under a further
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hanzo/ui",
3
- "version": "8.17.0",
3
+ "version": "8.17.1",
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": {