@hanzo/design 0.2.0 → 0.3.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/LICENSE CHANGED
@@ -1,21 +1,28 @@
1
- MIT License
1
+ BSD 3-Clause License
2
2
 
3
3
  Copyright (c) 2026 Hanzo AI, Inc.
4
4
 
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
11
7
 
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
14
10
 
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package/README.md CHANGED
@@ -6,18 +6,38 @@
6
6
 
7
7
  The single source of truth for how every Hanzo surface looks — tokens, components, brand assets, and the guidelines that hold them together.
8
8
 
9
- `@hanzoai/design`
9
+ `@hanzo/design`
10
10
 
11
11
  </div>
12
12
 
13
+ ## If you are an agent
14
+
15
+ Read [`skills/design-system/SKILL.md`](skills/design-system/SKILL.md). It is the
16
+ **one** way an AI surface learns this design system — chat, hanzo.app and Claude
17
+ Code all load the same file, so there is a single author and nothing to drift.
18
+ It ends by running the gate below, which is what makes the rules enforceable
19
+ rather than advisory:
20
+
21
+ ```sh
22
+ npx hanzo-design-lint <paths…>
23
+ ```
24
+
25
+ `prompts/` holds the longer material the skill points at. It is reference, not a
26
+ second entry point.
27
+
13
28
  ## Use it
14
29
 
15
- One import pulls in the whole token layer (fonts, color, type, spacing, radius, elevation, motion):
30
+ One import pulls in the whole token layer fonts, colour, type, spacing, radius,
31
+ elevation, motion, **z**, base:
16
32
 
17
33
  ```css
18
34
  @import "@hanzo/design/styles.css";
19
35
  ```
20
36
 
37
+ Import the **whole** file. Cherry-picking `tokens/*.css` is how surfaces ended up
38
+ without the z ladder or the elevation ramp. The fonts are self-hosted, so this
39
+ makes **no third-party request** — there is no longer a reason to skip a part of it.
40
+
21
41
  ### Programmatic tokens (control look & feel from code)
22
42
 
23
43
  The same tokens are exposed to TypeScript — **generated from the CSS**, so the two
@@ -44,8 +64,8 @@ Groups: `colors`, `typography`, `spacing`, `radius`, `elevation`, `motion`, `zIn
44
64
  Everything below is expressed as CSS custom properties, so code copies over 1:1 — the semantic names match `hanzo.ai`'s variables exactly.
45
65
 
46
66
  ```jsx
47
- import { Button } from "@hanzoai/design/components/core/Button.jsx";
48
- import { HanzoLogo } from "@hanzoai/design/components/core/HanzoLogo.jsx";
67
+ import { Button } from "@hanzo/design/components/core/Button.jsx";
68
+ import { HanzoLogo } from "@hanzo/design/components/core/HanzoLogo.jsx";
49
69
 
50
70
  <Button pill>Try Hanzo</Button>
51
71
  ```
@@ -61,13 +81,42 @@ import { HanzoLogo } from "@hanzoai/design/components/core/HanzoLogo.jsx";
61
81
  | `content/` | The words — brand voice and taglines. |
62
82
  | `docs/` | How to use the system — integrate the tokens, theme, extend. |
63
83
  | `guidelines/` | Specimen cards — color, type, spacing, brand, iconography — the visual reference. |
84
+ | `assets/fonts/` | Geist Sans + Geist Mono, self-hosted (two variable `.woff2`, 141 KB, SIL OFL-1.1). No Google Fonts request. |
64
85
  | `assets/` | The mark, wordmark, favicon, provider + partner logos, brand imagery. |
86
+ | `scripts/check-tokens.mjs` | The gate: every token file is served, every internal `var()` resolves, and the contrast floors hold. Runs on `npm run build`. |
65
87
  | `ui_kits/` | Composed surfaces (e.g. `SiteChrome`) assembled from the components. |
66
88
 
89
+ ## Two kinds of boundary
90
+
91
+ Not interchangeable, and the difference is a conformance requirement rather than
92
+ a matter of taste:
93
+
94
+ | Token | Kind | Contrast |
95
+ |---|---|---|
96
+ | `--border`, `--border-hairline`, `--border-card` | **decorative** — separates content | none required |
97
+ | `--border-strong` | **perceivable** — identifies a *control* (input edge, switch, checkbox) | ≥ 3:1, WCAG 1.4.11 |
98
+ | `--ring` | the focus indicator | ≥ 3:1, WCAG 2.4.11 |
99
+
100
+ Reach for `--border-strong` whenever the boundary **is** the affordance. Both
101
+ conformant tokens resolve to `--neutral-500`, the only rung on this ladder that
102
+ clears 3:1 on every surface in **both** themes — `scripts/check-tokens.mjs`
103
+ measures this on every build and fails if it ever stops being true.
104
+
105
+ Note that the `--white-*` opacity ladder does **not** invert in light theme, so
106
+ `--white-40` is white-on-white there. Anything needing a visible edge in both
107
+ themes must use `--border-strong`.
108
+
67
109
  ## Principles
68
110
 
69
111
  - **Monochrome by construction** — one neutral ladder plus an opacity ladder is the entire palette. Color appears only as genuine semantics (live/error/warning).
70
112
  - **Dark is the default theme** — surfaces mount dark-first; light is the override.
113
+ - **A token that is referenced must resolve** — an undefined custom property paints
114
+ *nothing*, silently. `cssVar()` therefore accepts only real token names (a typo
115
+ is a compile error) and emits the authored literal as a fallback, so a reference
116
+ still paints on a host that has not loaded the CSS.
117
+ - **White-label by fork** — `@luxfi/design` and `@zooai/design` carry the same token
118
+ *names* over their own values. A change here must be measured against all three
119
+ before it ships; one that fixes Hanzo and breaks Zoo is a regression.
71
120
  - **Self-contained components** — inline styles, no CSS-framework coupling, so a component drops into any host (Next, Vite, Tamagui, none) and renders identically.
72
121
  - **Every component ships its own `.prompt.md`** — a one-screen usage guide for humans and AI alike.
73
122
 
@@ -77,4 +126,4 @@ Tokens track `hanzo.ai` (`app/globals.css`, `tailwind.config.ts`, `DESIGN.md`) a
77
126
 
78
127
  ## License
79
128
 
80
- MIT. Brand marks (the Hanzo logo, partner and provider logos) are the property of their respective owners and are provided for identification.
129
+ BSD-3-Clause. Brand marks (the Hanzo logo, partner and provider logos) are the property of their respective owners and are provided for identification.
@@ -0,0 +1,92 @@
1
+ Copyright (c) 2023 Vercel, in collaboration with basement.studio
2
+
3
+ This Font Software is licensed under the SIL Open Font License, Version 1.1.
4
+ This license is copied below, and is also available with a FAQ at:
5
+ http://scripts.sil.org/OFL
6
+
7
+ -----------------------------------------------------------
8
+ SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
9
+ -----------------------------------------------------------
10
+
11
+ PREAMBLE
12
+ The goals of the Open Font License (OFL) are to stimulate worldwide
13
+ development of collaborative font projects, to support the font creation
14
+ efforts of academic and linguistic communities, and to provide a free and
15
+ open framework in which fonts may be shared and improved in partnership
16
+ with others.
17
+
18
+ The OFL allows the licensed fonts to be used, studied, modified and
19
+ redistributed freely as long as they are not sold by themselves. The
20
+ fonts, including any derivative works, can be bundled, embedded,
21
+ redistributed and/or sold with any software provided that any reserved
22
+ names are not used by derivative works. The fonts and derivatives,
23
+ however, cannot be released under any other type of license. The
24
+ requirement for fonts to remain under this license does not apply
25
+ to any document created using the fonts or their derivatives.
26
+
27
+ DEFINITIONS
28
+ "Font Software" refers to the set of files released by the Copyright
29
+ Holder(s) under this license and clearly marked as such. This may
30
+ include source files, build scripts and documentation.
31
+
32
+ "Reserved Font Name" refers to any names specified as such after the
33
+ copyright statement(s).
34
+
35
+ "Original Version" refers to the collection of Font Software components as
36
+ distributed by the Copyright Holder(s).
37
+
38
+ "Modified Version" refers to any derivative made by adding to, deleting,
39
+ or substituting -- in part or in whole -- any of the components of the
40
+ Original Version, by changing formats or by porting the Font Software to a
41
+ new environment.
42
+
43
+ "Author" refers to any designer, engineer, programmer, technical
44
+ writer or other person who contributed to the Font Software.
45
+
46
+ PERMISSION AND CONDITIONS
47
+ Permission is hereby granted, free of charge, to any person obtaining
48
+ a copy of the Font Software, to use, study, copy, merge, embed, modify,
49
+ redistribute, and sell modified and unmodified copies of the Font
50
+ Software, subject to the following conditions:
51
+
52
+ 1) Neither the Font Software nor any of its individual components,
53
+ in Original or Modified Versions, may be sold by itself.
54
+
55
+ 2) Original or Modified Versions of the Font Software may be bundled,
56
+ redistributed and/or sold with any software, provided that each copy
57
+ contains the above copyright notice and this license. These can be
58
+ included either as stand-alone text files, human-readable headers or
59
+ in the appropriate machine-readable metadata fields within text or
60
+ binary files as long as those fields can be easily viewed by the user.
61
+
62
+ 3) No Modified Version of the Font Software may use the Reserved Font
63
+ Name(s) unless explicit written permission is granted by the corresponding
64
+ Copyright Holder. This restriction only applies to the primary font name as
65
+ presented to the users.
66
+
67
+ 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
68
+ Software shall not be used to promote, endorse or advertise any
69
+ Modified Version, except to acknowledge the contribution(s) of the
70
+ Copyright Holder(s) and the Author(s) or with their explicit written
71
+ permission.
72
+
73
+ 5) The Font Software, modified or unmodified, in part or in whole,
74
+ must be distributed entirely under this license, and must not be
75
+ distributed under any other license. The requirement for fonts to
76
+ remain under this license does not apply to any document created
77
+ using the Font Software.
78
+
79
+ TERMINATION
80
+ This license becomes null and void if any of the above conditions are
81
+ not met.
82
+
83
+ DISCLAIMER
84
+ THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
85
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
86
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
87
+ OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
88
+ COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
89
+ INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
90
+ DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
91
+ FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
92
+ OTHER DEALINGS IN THE FONT SOFTWARE.
@@ -22,7 +22,7 @@ export function HanzoWordmark({ size = 22, label = 'Hanzo AI', variant = 'white'
22
22
  return (
23
23
  <span style={{ display: 'inline-flex', alignItems: 'center', gap: 8, ...style }}>
24
24
  <HanzoLogo size={size} variant={variant} />
25
- <span style={{ fontSize: 15, fontWeight: 600, letterSpacing: 'var(--tracking-tight)', color: 'var(--text-primary)' }}>{label}</span>
25
+ <span style={{ fontSize: 'var(--text-sm)', fontWeight: 600, letterSpacing: 'var(--tracking-tight)', color: 'var(--text-primary)' }}>{label}</span>
26
26
  </span>
27
27
  )
28
28
  }
@@ -11,7 +11,7 @@ export function Dialog({ open, onOpenChange, children }) {
11
11
  return (
12
12
  <div
13
13
  onClick={() => onOpenChange && onOpenChange(false)}
14
- style={{position:'fixed',inset:0,zIndex:80,display:'flex',alignItems:'center',justifyContent:'center',padding:24,background:'rgb(0 0 0 / .8)',backdropFilter:'blur(4px)',animation:'hanzo-fade-up var(--duration-fast) var(--ease-out)'}}
14
+ style={{position:'fixed',inset:0,zIndex:'var(--z-modal)',display:'flex',alignItems:'center',justifyContent:'center',padding:24,background:'var(--surface-scrim)',backdropFilter:'blur(4px)',animation:'hanzo-fade-up var(--duration-fast) var(--ease-out)'}}
15
15
  >
16
16
  {children}
17
17
  </div>
@@ -18,7 +18,7 @@ export function DropdownMenu({ align = 'right', hover = false, trigger, style, c
18
18
  >
19
19
  <span onClick={() => setOpen((v) => !v)} style={{display:'inline-flex'}}>{trigger}</span>
20
20
  {open && (
21
- <div style={{position:'absolute',top:'100%',[align]:0,paddingTop:12,zIndex:70,minWidth:208}}>
21
+ <div style={{position:'absolute',top:'100%',[align]:0,paddingTop:12,zIndex:'var(--z-dropdown)',minWidth:208}}>
22
22
  <div style={{display:'flex',flexDirection:'column',padding:8,background:'var(--surface-overlay)',border:'1px solid var(--border-hairline)',borderRadius:'var(--radius-xl)',boxShadow:'var(--shadow-floating)',backdropFilter:'blur(16px)',animation:'hanzo-fade-down var(--duration-fast) var(--ease-out)'}}>
23
23
  {children}
24
24
  </div>
@@ -12,7 +12,7 @@ export function Sheet({ open, onOpenChange, side = 'right', width = 380, childre
12
12
  ? { left: 0, top: 0, bottom: 0, width, borderRight: '1px solid var(--border-hairline)' }
13
13
  : { right: 0, top: 0, bottom: 0, width, borderLeft: '1px solid var(--border-hairline)' }
14
14
  return (
15
- <div onClick={() => onOpenChange && onOpenChange(false)} style={{position:'fixed',inset:0,zIndex:80,background:'rgb(0 0 0 / .7)'}}>
15
+ <div onClick={() => onOpenChange && onOpenChange(false)} style={{position:'fixed',inset:0,zIndex:'var(--z-modal)',background:'var(--surface-scrim)'}}>
16
16
  <div
17
17
  onClick={(e) => e.stopPropagation()}
18
18
  style={{position:'absolute',display:'flex',flexDirection:'column',background:'var(--popover)',boxShadow:'var(--shadow-floating)',animation:'hanzo-fade-up var(--duration-base) var(--ease-out)',...pos}}
@@ -29,7 +29,7 @@ export function Toaster({ duration = 3500, position = 'bottom-right' }) {
29
29
  }, [duration])
30
30
  const anchor = position === 'top-right' ? { top: 24, right: 24 } : { bottom: 24, right: 24 }
31
31
  return (
32
- <div style={{position:'fixed',zIndex:90,display:'flex',flexDirection:'column',gap:8,...anchor}}>
32
+ <div style={{position:'fixed',zIndex:'var(--z-toast)',display:'flex',flexDirection:'column',gap:8,...anchor}}>
33
33
  {items.map((i) => (
34
34
  <div key={i.id} style={{
35
35
  minWidth:260,maxWidth:380,padding:'12px 14px',display:'flex',alignItems:'flex-start',gap:10,
package/dist/index.d.ts CHANGED
@@ -1,14 +1,25 @@
1
1
  export * from './tokens.gen.js';
2
2
  import { type CssVarName } from './tokens.gen.js';
3
+ /** A token name with the leading `--` omitted: `'background'` for `'--background'`. */
4
+ export type TokenName = CssVarName extends `--${infer N}` ? N : never;
3
5
  /**
4
- * A `var(--name[, fallback])` reference to a token — the ONE way code should
5
- * reach a token so it resolves through the live CSS cascade (honoring the
6
- * viewer's light/dark theme) rather than baking a fixed value.
6
+ * A `var(--name, <authored literal>)` reference to a token — the ONE way code
7
+ * should reach a token, so it resolves through the live CSS cascade (honoring
8
+ * the viewer's light/dark theme and any brand fork) rather than baking a value.
7
9
  *
8
- * background: cssVar('--background') // → "var(--background)"
9
- * color: cssVar('--foreground', '#fff')
10
+ * background: cssVar('--background') // → "var(--background, #000000)"
11
+ * color: cssVar('foreground', '#fff')// → "var(--foreground, #fff)"
12
+ *
13
+ * The name is checked AGAINST THE STYLESHEET at compile time. That check used to
14
+ * be opted out of with `| (string & {})`, which is how `cssVar('surface-1')`
15
+ * shipped: the token did not exist, `var(--surface-1)` resolved to nothing, and
16
+ * a menu painted transparent with no error anywhere. An undefined custom
17
+ * property fails SILENTLY, so the type is the only place it can be caught.
18
+ *
19
+ * When no explicit fallback is given the token's own authored literal is used,
20
+ * so the reference still paints on a host that has not loaded the CSS layer.
10
21
  */
11
- export declare function cssVar(name: CssVarName | (string & {}), fallback?: string): string;
22
+ export declare function cssVar(name: CssVarName | TokenName, fallback?: string): string;
12
23
  /** The raw authored value of a token (the literal from the CSS), or `undefined`. */
13
24
  export declare function tokenValue(name: CssVarName): string | undefined;
14
25
  /**
@@ -16,6 +27,10 @@ export declare function tokenValue(name: CssVarName): string | undefined;
16
27
  * cannot use a bundler CSS import (e.g. a runtime-mounted island). Prefer the
17
28
  * static `import '@hanzo/design/styles.css'` where a bundler is available.
18
29
  * No-op outside the browser.
30
+ *
31
+ * `href` is REQUIRED: this used to default to esm.sh, which silently made a
32
+ * third-party CDN the origin of the entire token layer for anyone who called it
33
+ * bare. Pass a URL you serve.
19
34
  */
20
- export declare function injectDesignCss(href?: string): void;
35
+ export declare function injectDesignCss(href: string): void;
21
36
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAWA,cAAc,iBAAiB,CAAA;AAC/B,OAAO,EAAW,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAE1D;;;;;;;GAOG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAGlF;AAED,oFAAoF;AACpF,wBAAgB,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,GAAG,SAAS,CAE/D;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,SAA4C,GAAG,IAAI,CAQtF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAWA,cAAc,iBAAiB,CAAA;AAC/B,OAAO,EAAW,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAE1D,uFAAuF;AACvF,MAAM,MAAM,SAAS,GAAG,UAAU,SAAS,KAAK,MAAM,CAAC,EAAE,GAAG,CAAC,GAAG,KAAK,CAAA;AAErE;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,SAAS,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAI9E;AAED,oFAAoF;AACpF,wBAAgB,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,GAAG,SAAS,CAE/D;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAQlD"}
package/dist/index.js CHANGED
@@ -12,16 +12,26 @@
12
12
  export * from './tokens.gen.js';
13
13
  import { cssVars } from './tokens.gen.js';
14
14
  /**
15
- * A `var(--name[, fallback])` reference to a token — the ONE way code should
16
- * reach a token so it resolves through the live CSS cascade (honoring the
17
- * viewer's light/dark theme) rather than baking a fixed value.
15
+ * A `var(--name, <authored literal>)` reference to a token — the ONE way code
16
+ * should reach a token, so it resolves through the live CSS cascade (honoring
17
+ * the viewer's light/dark theme and any brand fork) rather than baking a value.
18
18
  *
19
- * background: cssVar('--background') // → "var(--background)"
20
- * color: cssVar('--foreground', '#fff')
19
+ * background: cssVar('--background') // → "var(--background, #000000)"
20
+ * color: cssVar('foreground', '#fff')// → "var(--foreground, #fff)"
21
+ *
22
+ * The name is checked AGAINST THE STYLESHEET at compile time. That check used to
23
+ * be opted out of with `| (string & {})`, which is how `cssVar('surface-1')`
24
+ * shipped: the token did not exist, `var(--surface-1)` resolved to nothing, and
25
+ * a menu painted transparent with no error anywhere. An undefined custom
26
+ * property fails SILENTLY, so the type is the only place it can be caught.
27
+ *
28
+ * When no explicit fallback is given the token's own authored literal is used,
29
+ * so the reference still paints on a host that has not loaded the CSS layer.
21
30
  */
22
31
  export function cssVar(name, fallback) {
23
- const n = name.startsWith('--') ? name : `--${name}`;
24
- return fallback ? `var(${n}, ${fallback})` : `var(${n})`;
32
+ const n = (name.startsWith('--') ? name : `--${name}`);
33
+ const lit = fallback ?? cssVars[n];
34
+ return lit ? `var(${n}, ${lit})` : `var(${n})`;
25
35
  }
26
36
  /** The raw authored value of a token (the literal from the CSS), or `undefined`. */
27
37
  export function tokenValue(name) {
@@ -32,8 +42,12 @@ export function tokenValue(name) {
32
42
  * cannot use a bundler CSS import (e.g. a runtime-mounted island). Prefer the
33
43
  * static `import '@hanzo/design/styles.css'` where a bundler is available.
34
44
  * No-op outside the browser.
45
+ *
46
+ * `href` is REQUIRED: this used to default to esm.sh, which silently made a
47
+ * third-party CDN the origin of the entire token layer for anyone who called it
48
+ * bare. Pass a URL you serve.
35
49
  */
36
- export function injectDesignCss(href = 'https://esm.sh/@hanzo/design/styles.css') {
50
+ export function injectDesignCss(href) {
37
51
  if (typeof document === 'undefined')
38
52
  return;
39
53
  if (document.querySelector('link[data-hanzo-design]'))
@@ -41,7 +41,7 @@ export declare const colors: {
41
41
  readonly 'destructive-foreground': "#f5f5f5";
42
42
  readonly border: "#1f1f1f";
43
43
  readonly input: "#1f1f1f";
44
- readonly ring: "#333333";
44
+ readonly ring: "var(--neutral-500)";
45
45
  readonly brand: "#e4e4e7";
46
46
  readonly 'brand-foreground': "#09090b";
47
47
  readonly 'brand-muted': "#a3a3a3";
@@ -53,9 +53,14 @@ export declare const colors: {
53
53
  readonly 'surface-card-quiet': "rgb(23 23 23 / .4)";
54
54
  readonly 'surface-overlay': "rgb(10 10 10 / .95)";
55
55
  readonly 'surface-header': "rgb(0 0 0 / .7)";
56
+ readonly 'surface-scrim': "rgb(0 0 0 / .8)";
56
57
  readonly 'border-hairline': "var(--neutral-800)";
57
58
  readonly 'border-card': "var(--white-10)";
58
- readonly 'border-strong': "var(--neutral-700)";
59
+ readonly 'border-strong': "var(--neutral-500)";
60
+ readonly 'surface-0': "var(--background)";
61
+ readonly 'surface-1': "var(--card)";
62
+ readonly 'surface-2': "var(--muted)";
63
+ readonly 'surface-3': "var(--secondary)";
59
64
  readonly 'text-primary': "var(--pure-white)";
60
65
  readonly 'text-secondary': "var(--white-80)";
61
66
  readonly 'text-tertiary': "var(--white-60)";
@@ -94,6 +99,23 @@ export declare const typography: {
94
99
  readonly 'leading-6xl': "1";
95
100
  readonly 'text-7xl': "4rem";
96
101
  readonly 'leading-7xl': "1";
102
+ readonly 'text-8xl': "5.25rem";
103
+ readonly 'leading-8xl': "1";
104
+ readonly 'text-9xl': "7rem";
105
+ readonly 'leading-9xl': "1";
106
+ readonly 'font-size-xs': "var(--text-xs)";
107
+ readonly 'font-size-sm': "var(--text-sm)";
108
+ readonly 'font-size-base': "var(--text-base)";
109
+ readonly 'font-size-lg': "var(--text-lg)";
110
+ readonly 'font-size-xl': "var(--text-xl)";
111
+ readonly 'font-size-2xl': "var(--text-2xl)";
112
+ readonly 'font-size-3xl': "var(--text-3xl)";
113
+ readonly 'font-size-4xl': "var(--text-4xl)";
114
+ readonly 'font-size-5xl': "var(--text-5xl)";
115
+ readonly 'font-size-6xl': "var(--text-6xl)";
116
+ readonly 'font-size-7xl': "var(--text-7xl)";
117
+ readonly 'font-size-8xl': "var(--text-8xl)";
118
+ readonly 'font-size-9xl': "var(--text-9xl)";
97
119
  readonly 'weight-normal': "400";
98
120
  readonly 'weight-medium': "500";
99
121
  readonly 'weight-semibold': "600";
@@ -173,6 +195,12 @@ export declare const elevation: {
173
195
  readonly 'shadow-floating': "0 25px 50px -12px rgb(0 0 0 / .25)";
174
196
  readonly 'shadow-inset-hairline': "inset 0 0 0 1px var(--white-10)";
175
197
  readonly 'ring-focus': "0 0 0 2px var(--ring)";
198
+ readonly 'shadow-sm': "0 1px 2px 0 rgb(0 0 0 / .40)";
199
+ readonly shadow: "0 1px 3px 0 rgb(0 0 0 / .45), 0 1px 2px -1px rgb(0 0 0 / .45)";
200
+ readonly 'shadow-md': "0 4px 6px -1px rgb(0 0 0 / .50), 0 2px 4px -2px rgb(0 0 0 / .50)";
201
+ readonly 'shadow-lg': "0 10px 15px -3px rgb(0 0 0 / .55), 0 4px 6px -4px rgb(0 0 0 / .55)";
202
+ readonly 'shadow-xl': "0 20px 25px -5px rgb(0 0 0 / .60), 0 8px 10px -6px rgb(0 0 0 / .60)";
203
+ readonly 'shadow-2xl': "var(--shadow-floating)";
176
204
  readonly 'glow-hero': "radial-gradient(circle,rgb(255 255 255 / .12) 0%,transparent 68%)";
177
205
  readonly 'glow-hero-blur': "120px";
178
206
  readonly 'sheen-card': "radial-gradient(120% 120% at 80% 0%,rgb(255 255 255 / .08) 0%,transparent 55%)";
@@ -204,6 +232,8 @@ export declare const zIndex: {
204
232
  readonly 'z-modal': "600";
205
233
  readonly 'z-popover': "700";
206
234
  readonly 'z-toast': "800";
235
+ readonly 'z-tooltip': "var(--z-popover)";
236
+ readonly 'z-notification': "var(--z-toast)";
207
237
  };
208
238
  /** fonts tokens (from tokens/fonts.css). Values are raw CSS. */
209
239
  export declare const fonts: {
@@ -258,7 +288,7 @@ export declare const cssVars: {
258
288
  readonly '--destructive-foreground': "#f5f5f5";
259
289
  readonly '--border': "#1f1f1f";
260
290
  readonly '--input': "#1f1f1f";
261
- readonly '--ring': "#333333";
291
+ readonly '--ring': "var(--neutral-500)";
262
292
  readonly '--brand': "#e4e4e7";
263
293
  readonly '--brand-foreground': "#09090b";
264
294
  readonly '--brand-muted': "#a3a3a3";
@@ -270,9 +300,14 @@ export declare const cssVars: {
270
300
  readonly '--surface-card-quiet': "rgb(23 23 23 / .4)";
271
301
  readonly '--surface-overlay': "rgb(10 10 10 / .95)";
272
302
  readonly '--surface-header': "rgb(0 0 0 / .7)";
303
+ readonly '--surface-scrim': "rgb(0 0 0 / .8)";
273
304
  readonly '--border-hairline': "var(--neutral-800)";
274
305
  readonly '--border-card': "var(--white-10)";
275
- readonly '--border-strong': "var(--neutral-700)";
306
+ readonly '--border-strong': "var(--neutral-500)";
307
+ readonly '--surface-0': "var(--background)";
308
+ readonly '--surface-1': "var(--card)";
309
+ readonly '--surface-2': "var(--muted)";
310
+ readonly '--surface-3': "var(--secondary)";
276
311
  readonly '--text-primary': "var(--pure-white)";
277
312
  readonly '--text-secondary': "var(--white-80)";
278
313
  readonly '--text-tertiary': "var(--white-60)";
@@ -308,6 +343,23 @@ export declare const cssVars: {
308
343
  readonly '--leading-6xl': "1";
309
344
  readonly '--text-7xl': "4rem";
310
345
  readonly '--leading-7xl': "1";
346
+ readonly '--text-8xl': "5.25rem";
347
+ readonly '--leading-8xl': "1";
348
+ readonly '--text-9xl': "7rem";
349
+ readonly '--leading-9xl': "1";
350
+ readonly '--font-size-xs': "var(--text-xs)";
351
+ readonly '--font-size-sm': "var(--text-sm)";
352
+ readonly '--font-size-base': "var(--text-base)";
353
+ readonly '--font-size-lg': "var(--text-lg)";
354
+ readonly '--font-size-xl': "var(--text-xl)";
355
+ readonly '--font-size-2xl': "var(--text-2xl)";
356
+ readonly '--font-size-3xl': "var(--text-3xl)";
357
+ readonly '--font-size-4xl': "var(--text-4xl)";
358
+ readonly '--font-size-5xl': "var(--text-5xl)";
359
+ readonly '--font-size-6xl': "var(--text-6xl)";
360
+ readonly '--font-size-7xl': "var(--text-7xl)";
361
+ readonly '--font-size-8xl': "var(--text-8xl)";
362
+ readonly '--font-size-9xl': "var(--text-9xl)";
311
363
  readonly '--weight-normal': "400";
312
364
  readonly '--weight-medium': "500";
313
365
  readonly '--weight-semibold': "600";
@@ -378,6 +430,12 @@ export declare const cssVars: {
378
430
  readonly '--shadow-floating': "0 25px 50px -12px rgb(0 0 0 / .25)";
379
431
  readonly '--shadow-inset-hairline': "inset 0 0 0 1px var(--white-10)";
380
432
  readonly '--ring-focus': "0 0 0 2px var(--ring)";
433
+ readonly '--shadow-sm': "0 1px 2px 0 rgb(0 0 0 / .40)";
434
+ readonly '--shadow': "0 1px 3px 0 rgb(0 0 0 / .45), 0 1px 2px -1px rgb(0 0 0 / .45)";
435
+ readonly '--shadow-md': "0 4px 6px -1px rgb(0 0 0 / .50), 0 2px 4px -2px rgb(0 0 0 / .50)";
436
+ readonly '--shadow-lg': "0 10px 15px -3px rgb(0 0 0 / .55), 0 4px 6px -4px rgb(0 0 0 / .55)";
437
+ readonly '--shadow-xl': "0 20px 25px -5px rgb(0 0 0 / .60), 0 8px 10px -6px rgb(0 0 0 / .60)";
438
+ readonly '--shadow-2xl': "var(--shadow-floating)";
381
439
  readonly '--glow-hero': "radial-gradient(circle,rgb(255 255 255 / .12) 0%,transparent 68%)";
382
440
  readonly '--glow-hero-blur': "120px";
383
441
  readonly '--sheen-card': "radial-gradient(120% 120% at 80% 0%,rgb(255 255 255 / .08) 0%,transparent 55%)";
@@ -403,6 +461,8 @@ export declare const cssVars: {
403
461
  readonly '--z-modal': "600";
404
462
  readonly '--z-popover': "700";
405
463
  readonly '--z-toast': "800";
464
+ readonly '--z-tooltip': "var(--z-popover)";
465
+ readonly '--z-notification': "var(--z-toast)";
406
466
  readonly '--font-sans': "\"Geist\",\"Geist Sans\",ui-sans-serif,system-ui,sans-serif";
407
467
  readonly '--font-display': "var(--font-sans)";
408
468
  readonly '--font-mono': "\"Geist Mono\",ui-monospace,SFMono-Regular,monospace";
@@ -1 +1 @@
1
- {"version":3,"file":"tokens.gen.d.ts","sourceRoot":"","sources":["../src/tokens.gen.ts"],"names":[],"mappings":"AAIA,kEAAkE;AAClE,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsET,CAAA;AAEV,0EAA0E;AAC1E,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6Cb,CAAA;AAEV,oEAAoE;AACpE,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqCV,CAAA;AAEV,kEAAkE;AAClE,eAAO,MAAM,MAAM;;;;;;;;;CAST,CAAA;AAEV,wEAAwE;AACxE,eAAO,MAAM,SAAS;;;;;;;;;;;CAWZ,CAAA;AAEV,kEAAkE;AAClE,eAAO,MAAM,MAAM;;;;;;;;;;;CAWT,CAAA;AAEV,6DAA6D;AAC7D,eAAO,MAAM,MAAM;;;;;;;;;;CAUT,CAAA;AAEV,gEAAgE;AAChE,eAAO,MAAM,KAAK;;;;;;CAMR,CAAA;AAEV,8DAA8D;AAC9D,eAAO,MAAM,IAAI,IAEP,CAAA;AAEV,sFAAsF;AACtF,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgMV,CAAA;AAEV,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,OAAO,CAAA"}
1
+ {"version":3,"file":"tokens.gen.d.ts","sourceRoot":"","sources":["../src/tokens.gen.ts"],"names":[],"mappings":"AAIA,kEAAkE;AAClE,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2ET,CAAA;AAEV,0EAA0E;AAC1E,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8Db,CAAA;AAEV,oEAAoE;AACpE,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqCV,CAAA;AAEV,kEAAkE;AAClE,eAAO,MAAM,MAAM;;;;;;;;;CAST,CAAA;AAEV,wEAAwE;AACxE,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;CAiBZ,CAAA;AAEV,kEAAkE;AAClE,eAAO,MAAM,MAAM;;;;;;;;;;;CAWT,CAAA;AAEV,6DAA6D;AAC7D,eAAO,MAAM,MAAM;;;;;;;;;;;;CAYT,CAAA;AAEV,gEAAgE;AAChE,eAAO,MAAM,KAAK;;;;;;CAMR,CAAA;AAEV,8DAA8D;AAC9D,eAAO,MAAM,IAAI,IAEP,CAAA;AAEV,sFAAsF;AACtF,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8NV,CAAA;AAEV,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,OAAO,CAAA"}