@hanzo/design 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +23 -16
- package/README.md +36 -2
- package/assets/fonts/Geist-Variable.woff2 +0 -0
- package/assets/fonts/GeistMono-Variable.woff2 +0 -0
- package/assets/fonts/LICENSE-Geist.txt +92 -0
- package/dist/index.d.ts +22 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22 -8
- package/dist/tokens.gen.d.ts +62 -4
- package/dist/tokens.gen.d.ts.map +1 -1
- package/dist/tokens.gen.js +62 -4
- package/guidelines/DESIGN.md +106 -0
- package/package.json +4 -3
- package/prompts/README.md +7 -0
- package/prompts/studio-designer.md +41 -0
- package/src/index.ts +26 -9
- package/src/tokens.gen.ts +62 -4
- package/styles.css +4 -1
- package/tokens/base.css +7 -3
- package/tokens/colors.css +30 -4
- package/tokens/elevation.css +12 -0
- package/tokens/fonts.css +30 -3
- package/tokens/typography.css +19 -0
- package/tokens/z.css +18 -9
- package/ui_kits/.gitkeep +0 -0
package/LICENSE
CHANGED
|
@@ -1,21 +1,28 @@
|
|
|
1
|
-
|
|
1
|
+
BSD 3-Clause License
|
|
2
2
|
|
|
3
3
|
Copyright (c) 2026 Hanzo AI, Inc.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
13
|
-
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
14
10
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
@@ -12,12 +12,17 @@ The single source of truth for how every Hanzo surface looks — tokens, compone
|
|
|
12
12
|
|
|
13
13
|
## Use it
|
|
14
14
|
|
|
15
|
-
One import pulls in the whole token layer
|
|
15
|
+
One import pulls in the whole token layer — fonts, colour, type, spacing, radius,
|
|
16
|
+
elevation, motion, **z**, base:
|
|
16
17
|
|
|
17
18
|
```css
|
|
18
19
|
@import "@hanzo/design/styles.css";
|
|
19
20
|
```
|
|
20
21
|
|
|
22
|
+
Import the **whole** file. Cherry-picking `tokens/*.css` is how surfaces ended up
|
|
23
|
+
without the z ladder or the elevation ramp. The fonts are self-hosted, so this
|
|
24
|
+
makes **no third-party request** — there is no longer a reason to skip a part of it.
|
|
25
|
+
|
|
21
26
|
### Programmatic tokens (control look & feel from code)
|
|
22
27
|
|
|
23
28
|
The same tokens are exposed to TypeScript — **generated from the CSS**, so the two
|
|
@@ -61,13 +66,42 @@ import { HanzoLogo } from "@hanzoai/design/components/core/HanzoLogo.jsx";
|
|
|
61
66
|
| `content/` | The words — brand voice and taglines. |
|
|
62
67
|
| `docs/` | How to use the system — integrate the tokens, theme, extend. |
|
|
63
68
|
| `guidelines/` | Specimen cards — color, type, spacing, brand, iconography — the visual reference. |
|
|
69
|
+
| `assets/fonts/` | Geist Sans + Geist Mono, self-hosted (two variable `.woff2`, 141 KB, SIL OFL-1.1). No Google Fonts request. |
|
|
64
70
|
| `assets/` | The mark, wordmark, favicon, provider + partner logos, brand imagery. |
|
|
71
|
+
| `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
72
|
| `ui_kits/` | Composed surfaces (e.g. `SiteChrome`) assembled from the components. |
|
|
66
73
|
|
|
74
|
+
## Two kinds of boundary
|
|
75
|
+
|
|
76
|
+
Not interchangeable, and the difference is a conformance requirement rather than
|
|
77
|
+
a matter of taste:
|
|
78
|
+
|
|
79
|
+
| Token | Kind | Contrast |
|
|
80
|
+
|---|---|---|
|
|
81
|
+
| `--border`, `--border-hairline`, `--border-card` | **decorative** — separates content | none required |
|
|
82
|
+
| `--border-strong` | **perceivable** — identifies a *control* (input edge, switch, checkbox) | ≥ 3:1, WCAG 1.4.11 |
|
|
83
|
+
| `--ring` | the focus indicator | ≥ 3:1, WCAG 2.4.11 |
|
|
84
|
+
|
|
85
|
+
Reach for `--border-strong` whenever the boundary **is** the affordance. Both
|
|
86
|
+
conformant tokens resolve to `--neutral-500`, the only rung on this ladder that
|
|
87
|
+
clears 3:1 on every surface in **both** themes — `scripts/check-tokens.mjs`
|
|
88
|
+
measures this on every build and fails if it ever stops being true.
|
|
89
|
+
|
|
90
|
+
Note that the `--white-*` opacity ladder does **not** invert in light theme, so
|
|
91
|
+
`--white-40` is white-on-white there. Anything needing a visible edge in both
|
|
92
|
+
themes must use `--border-strong`.
|
|
93
|
+
|
|
67
94
|
## Principles
|
|
68
95
|
|
|
69
96
|
- **Monochrome by construction** — one neutral ladder plus an opacity ladder is the entire palette. Color appears only as genuine semantics (live/error/warning).
|
|
70
97
|
- **Dark is the default theme** — surfaces mount dark-first; light is the override.
|
|
98
|
+
- **A token that is referenced must resolve** — an undefined custom property paints
|
|
99
|
+
*nothing*, silently. `cssVar()` therefore accepts only real token names (a typo
|
|
100
|
+
is a compile error) and emits the authored literal as a fallback, so a reference
|
|
101
|
+
still paints on a host that has not loaded the CSS.
|
|
102
|
+
- **White-label by fork** — `@luxfi/design` and `@zooai/design` carry the same token
|
|
103
|
+
*names* over their own values. A change here must be measured against all three
|
|
104
|
+
before it ships; one that fixes Hanzo and breaks Zoo is a regression.
|
|
71
105
|
- **Self-contained components** — inline styles, no CSS-framework coupling, so a component drops into any host (Next, Vite, Tamagui, none) and renders identically.
|
|
72
106
|
- **Every component ships its own `.prompt.md`** — a one-screen usage guide for humans and AI alike.
|
|
73
107
|
|
|
@@ -77,4 +111,4 @@ Tokens track `hanzo.ai` (`app/globals.css`, `tailwind.config.ts`, `DESIGN.md`) a
|
|
|
77
111
|
|
|
78
112
|
## License
|
|
79
113
|
|
|
80
|
-
|
|
114
|
+
BSD-3-Clause. Brand marks (the Hanzo logo, partner and provider logos) are the property of their respective owners and are provided for identification.
|
|
Binary file
|
|
Binary file
|
|
@@ -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.
|
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
|
|
5
|
-
* reach a token so it resolves through the live CSS cascade (honoring
|
|
6
|
-
* viewer's light/dark theme) rather than baking a
|
|
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('
|
|
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 |
|
|
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
|
|
35
|
+
export declare function injectDesignCss(href: string): void;
|
|
21
36
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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
|
|
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
|
|
16
|
-
* reach a token so it resolves through the live CSS cascade (honoring
|
|
17
|
-
* viewer's light/dark theme) rather than baking a
|
|
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('
|
|
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
|
-
|
|
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
|
|
50
|
+
export function injectDesignCss(href) {
|
|
37
51
|
if (typeof document === 'undefined')
|
|
38
52
|
return;
|
|
39
53
|
if (document.querySelector('link[data-hanzo-design]'))
|
package/dist/tokens.gen.d.ts
CHANGED
|
@@ -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: "
|
|
44
|
+
readonly ring: "var(--neutral-500)";
|
|
45
45
|
readonly brand: "#e4e4e7";
|
|
46
46
|
readonly 'brand-foreground': "#09090b";
|
|
47
47
|
readonly 'brand-muted': "#a3a3a3";
|
|
@@ -55,7 +55,11 @@ export declare const colors: {
|
|
|
55
55
|
readonly 'surface-header': "rgb(0 0 0 / .7)";
|
|
56
56
|
readonly 'border-hairline': "var(--neutral-800)";
|
|
57
57
|
readonly 'border-card': "var(--white-10)";
|
|
58
|
-
readonly 'border-strong': "var(--neutral-
|
|
58
|
+
readonly 'border-strong': "var(--neutral-500)";
|
|
59
|
+
readonly 'surface-0': "var(--background)";
|
|
60
|
+
readonly 'surface-1': "var(--card)";
|
|
61
|
+
readonly 'surface-2': "var(--muted)";
|
|
62
|
+
readonly 'surface-3': "var(--secondary)";
|
|
59
63
|
readonly 'text-primary': "var(--pure-white)";
|
|
60
64
|
readonly 'text-secondary': "var(--white-80)";
|
|
61
65
|
readonly 'text-tertiary': "var(--white-60)";
|
|
@@ -94,6 +98,23 @@ export declare const typography: {
|
|
|
94
98
|
readonly 'leading-6xl': "1";
|
|
95
99
|
readonly 'text-7xl': "4rem";
|
|
96
100
|
readonly 'leading-7xl': "1";
|
|
101
|
+
readonly 'text-8xl': "5.25rem";
|
|
102
|
+
readonly 'leading-8xl': "1";
|
|
103
|
+
readonly 'text-9xl': "7rem";
|
|
104
|
+
readonly 'leading-9xl': "1";
|
|
105
|
+
readonly 'font-size-xs': "var(--text-xs)";
|
|
106
|
+
readonly 'font-size-sm': "var(--text-sm)";
|
|
107
|
+
readonly 'font-size-base': "var(--text-base)";
|
|
108
|
+
readonly 'font-size-lg': "var(--text-lg)";
|
|
109
|
+
readonly 'font-size-xl': "var(--text-xl)";
|
|
110
|
+
readonly 'font-size-2xl': "var(--text-2xl)";
|
|
111
|
+
readonly 'font-size-3xl': "var(--text-3xl)";
|
|
112
|
+
readonly 'font-size-4xl': "var(--text-4xl)";
|
|
113
|
+
readonly 'font-size-5xl': "var(--text-5xl)";
|
|
114
|
+
readonly 'font-size-6xl': "var(--text-6xl)";
|
|
115
|
+
readonly 'font-size-7xl': "var(--text-7xl)";
|
|
116
|
+
readonly 'font-size-8xl': "var(--text-8xl)";
|
|
117
|
+
readonly 'font-size-9xl': "var(--text-9xl)";
|
|
97
118
|
readonly 'weight-normal': "400";
|
|
98
119
|
readonly 'weight-medium': "500";
|
|
99
120
|
readonly 'weight-semibold': "600";
|
|
@@ -173,6 +194,12 @@ export declare const elevation: {
|
|
|
173
194
|
readonly 'shadow-floating': "0 25px 50px -12px rgb(0 0 0 / .25)";
|
|
174
195
|
readonly 'shadow-inset-hairline': "inset 0 0 0 1px var(--white-10)";
|
|
175
196
|
readonly 'ring-focus': "0 0 0 2px var(--ring)";
|
|
197
|
+
readonly 'shadow-sm': "0 1px 2px 0 rgb(0 0 0 / .40)";
|
|
198
|
+
readonly shadow: "0 1px 3px 0 rgb(0 0 0 / .45), 0 1px 2px -1px rgb(0 0 0 / .45)";
|
|
199
|
+
readonly 'shadow-md': "0 4px 6px -1px rgb(0 0 0 / .50), 0 2px 4px -2px rgb(0 0 0 / .50)";
|
|
200
|
+
readonly 'shadow-lg': "0 10px 15px -3px rgb(0 0 0 / .55), 0 4px 6px -4px rgb(0 0 0 / .55)";
|
|
201
|
+
readonly 'shadow-xl': "0 20px 25px -5px rgb(0 0 0 / .60), 0 8px 10px -6px rgb(0 0 0 / .60)";
|
|
202
|
+
readonly 'shadow-2xl': "var(--shadow-floating)";
|
|
176
203
|
readonly 'glow-hero': "radial-gradient(circle,rgb(255 255 255 / .12) 0%,transparent 68%)";
|
|
177
204
|
readonly 'glow-hero-blur': "120px";
|
|
178
205
|
readonly 'sheen-card': "radial-gradient(120% 120% at 80% 0%,rgb(255 255 255 / .08) 0%,transparent 55%)";
|
|
@@ -204,6 +231,8 @@ export declare const zIndex: {
|
|
|
204
231
|
readonly 'z-modal': "600";
|
|
205
232
|
readonly 'z-popover': "700";
|
|
206
233
|
readonly 'z-toast': "800";
|
|
234
|
+
readonly 'z-tooltip': "var(--z-popover)";
|
|
235
|
+
readonly 'z-notification': "var(--z-toast)";
|
|
207
236
|
};
|
|
208
237
|
/** fonts tokens (from tokens/fonts.css). Values are raw CSS. */
|
|
209
238
|
export declare const fonts: {
|
|
@@ -258,7 +287,7 @@ export declare const cssVars: {
|
|
|
258
287
|
readonly '--destructive-foreground': "#f5f5f5";
|
|
259
288
|
readonly '--border': "#1f1f1f";
|
|
260
289
|
readonly '--input': "#1f1f1f";
|
|
261
|
-
readonly '--ring': "
|
|
290
|
+
readonly '--ring': "var(--neutral-500)";
|
|
262
291
|
readonly '--brand': "#e4e4e7";
|
|
263
292
|
readonly '--brand-foreground': "#09090b";
|
|
264
293
|
readonly '--brand-muted': "#a3a3a3";
|
|
@@ -272,7 +301,11 @@ export declare const cssVars: {
|
|
|
272
301
|
readonly '--surface-header': "rgb(0 0 0 / .7)";
|
|
273
302
|
readonly '--border-hairline': "var(--neutral-800)";
|
|
274
303
|
readonly '--border-card': "var(--white-10)";
|
|
275
|
-
readonly '--border-strong': "var(--neutral-
|
|
304
|
+
readonly '--border-strong': "var(--neutral-500)";
|
|
305
|
+
readonly '--surface-0': "var(--background)";
|
|
306
|
+
readonly '--surface-1': "var(--card)";
|
|
307
|
+
readonly '--surface-2': "var(--muted)";
|
|
308
|
+
readonly '--surface-3': "var(--secondary)";
|
|
276
309
|
readonly '--text-primary': "var(--pure-white)";
|
|
277
310
|
readonly '--text-secondary': "var(--white-80)";
|
|
278
311
|
readonly '--text-tertiary': "var(--white-60)";
|
|
@@ -308,6 +341,23 @@ export declare const cssVars: {
|
|
|
308
341
|
readonly '--leading-6xl': "1";
|
|
309
342
|
readonly '--text-7xl': "4rem";
|
|
310
343
|
readonly '--leading-7xl': "1";
|
|
344
|
+
readonly '--text-8xl': "5.25rem";
|
|
345
|
+
readonly '--leading-8xl': "1";
|
|
346
|
+
readonly '--text-9xl': "7rem";
|
|
347
|
+
readonly '--leading-9xl': "1";
|
|
348
|
+
readonly '--font-size-xs': "var(--text-xs)";
|
|
349
|
+
readonly '--font-size-sm': "var(--text-sm)";
|
|
350
|
+
readonly '--font-size-base': "var(--text-base)";
|
|
351
|
+
readonly '--font-size-lg': "var(--text-lg)";
|
|
352
|
+
readonly '--font-size-xl': "var(--text-xl)";
|
|
353
|
+
readonly '--font-size-2xl': "var(--text-2xl)";
|
|
354
|
+
readonly '--font-size-3xl': "var(--text-3xl)";
|
|
355
|
+
readonly '--font-size-4xl': "var(--text-4xl)";
|
|
356
|
+
readonly '--font-size-5xl': "var(--text-5xl)";
|
|
357
|
+
readonly '--font-size-6xl': "var(--text-6xl)";
|
|
358
|
+
readonly '--font-size-7xl': "var(--text-7xl)";
|
|
359
|
+
readonly '--font-size-8xl': "var(--text-8xl)";
|
|
360
|
+
readonly '--font-size-9xl': "var(--text-9xl)";
|
|
311
361
|
readonly '--weight-normal': "400";
|
|
312
362
|
readonly '--weight-medium': "500";
|
|
313
363
|
readonly '--weight-semibold': "600";
|
|
@@ -378,6 +428,12 @@ export declare const cssVars: {
|
|
|
378
428
|
readonly '--shadow-floating': "0 25px 50px -12px rgb(0 0 0 / .25)";
|
|
379
429
|
readonly '--shadow-inset-hairline': "inset 0 0 0 1px var(--white-10)";
|
|
380
430
|
readonly '--ring-focus': "0 0 0 2px var(--ring)";
|
|
431
|
+
readonly '--shadow-sm': "0 1px 2px 0 rgb(0 0 0 / .40)";
|
|
432
|
+
readonly '--shadow': "0 1px 3px 0 rgb(0 0 0 / .45), 0 1px 2px -1px rgb(0 0 0 / .45)";
|
|
433
|
+
readonly '--shadow-md': "0 4px 6px -1px rgb(0 0 0 / .50), 0 2px 4px -2px rgb(0 0 0 / .50)";
|
|
434
|
+
readonly '--shadow-lg': "0 10px 15px -3px rgb(0 0 0 / .55), 0 4px 6px -4px rgb(0 0 0 / .55)";
|
|
435
|
+
readonly '--shadow-xl': "0 20px 25px -5px rgb(0 0 0 / .60), 0 8px 10px -6px rgb(0 0 0 / .60)";
|
|
436
|
+
readonly '--shadow-2xl': "var(--shadow-floating)";
|
|
381
437
|
readonly '--glow-hero': "radial-gradient(circle,rgb(255 255 255 / .12) 0%,transparent 68%)";
|
|
382
438
|
readonly '--glow-hero-blur': "120px";
|
|
383
439
|
readonly '--sheen-card': "radial-gradient(120% 120% at 80% 0%,rgb(255 255 255 / .08) 0%,transparent 55%)";
|
|
@@ -403,6 +459,8 @@ export declare const cssVars: {
|
|
|
403
459
|
readonly '--z-modal': "600";
|
|
404
460
|
readonly '--z-popover': "700";
|
|
405
461
|
readonly '--z-toast': "800";
|
|
462
|
+
readonly '--z-tooltip': "var(--z-popover)";
|
|
463
|
+
readonly '--z-notification': "var(--z-toast)";
|
|
406
464
|
readonly '--font-sans': "\"Geist\",\"Geist Sans\",ui-sans-serif,system-ui,sans-serif";
|
|
407
465
|
readonly '--font-display': "var(--font-sans)";
|
|
408
466
|
readonly '--font-mono': "\"Geist Mono\",ui-monospace,SFMono-Regular,monospace";
|
package/dist/tokens.gen.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokens.gen.d.ts","sourceRoot":"","sources":["../src/tokens.gen.ts"],"names":[],"mappings":"AAIA,kEAAkE;AAClE,eAAO,MAAM,MAAM
|
|
1
|
+
{"version":3,"file":"tokens.gen.d.ts","sourceRoot":"","sources":["../src/tokens.gen.ts"],"names":[],"mappings":"AAIA,kEAAkE;AAClE,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0ET,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6NV,CAAA;AAEV,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,OAAO,CAAA"}
|
package/dist/tokens.gen.js
CHANGED
|
@@ -44,7 +44,7 @@ export const colors = {
|
|
|
44
44
|
'destructive-foreground': '#f5f5f5',
|
|
45
45
|
'border': '#1f1f1f',
|
|
46
46
|
'input': '#1f1f1f',
|
|
47
|
-
'ring': '
|
|
47
|
+
'ring': 'var(--neutral-500)',
|
|
48
48
|
'brand': '#e4e4e7',
|
|
49
49
|
'brand-foreground': '#09090b',
|
|
50
50
|
'brand-muted': '#a3a3a3',
|
|
@@ -58,7 +58,11 @@ export const colors = {
|
|
|
58
58
|
'surface-header': 'rgb(0 0 0 / .7)',
|
|
59
59
|
'border-hairline': 'var(--neutral-800)',
|
|
60
60
|
'border-card': 'var(--white-10)',
|
|
61
|
-
'border-strong': 'var(--neutral-
|
|
61
|
+
'border-strong': 'var(--neutral-500)',
|
|
62
|
+
'surface-0': 'var(--background)',
|
|
63
|
+
'surface-1': 'var(--card)',
|
|
64
|
+
'surface-2': 'var(--muted)',
|
|
65
|
+
'surface-3': 'var(--secondary)',
|
|
62
66
|
'text-primary': 'var(--pure-white)',
|
|
63
67
|
'text-secondary': 'var(--white-80)',
|
|
64
68
|
'text-tertiary': 'var(--white-60)',
|
|
@@ -97,6 +101,23 @@ export const typography = {
|
|
|
97
101
|
'leading-6xl': '1',
|
|
98
102
|
'text-7xl': '4rem',
|
|
99
103
|
'leading-7xl': '1',
|
|
104
|
+
'text-8xl': '5.25rem',
|
|
105
|
+
'leading-8xl': '1',
|
|
106
|
+
'text-9xl': '7rem',
|
|
107
|
+
'leading-9xl': '1',
|
|
108
|
+
'font-size-xs': 'var(--text-xs)',
|
|
109
|
+
'font-size-sm': 'var(--text-sm)',
|
|
110
|
+
'font-size-base': 'var(--text-base)',
|
|
111
|
+
'font-size-lg': 'var(--text-lg)',
|
|
112
|
+
'font-size-xl': 'var(--text-xl)',
|
|
113
|
+
'font-size-2xl': 'var(--text-2xl)',
|
|
114
|
+
'font-size-3xl': 'var(--text-3xl)',
|
|
115
|
+
'font-size-4xl': 'var(--text-4xl)',
|
|
116
|
+
'font-size-5xl': 'var(--text-5xl)',
|
|
117
|
+
'font-size-6xl': 'var(--text-6xl)',
|
|
118
|
+
'font-size-7xl': 'var(--text-7xl)',
|
|
119
|
+
'font-size-8xl': 'var(--text-8xl)',
|
|
120
|
+
'font-size-9xl': 'var(--text-9xl)',
|
|
100
121
|
'weight-normal': '400',
|
|
101
122
|
'weight-medium': '500',
|
|
102
123
|
'weight-semibold': '600',
|
|
@@ -176,6 +197,12 @@ export const elevation = {
|
|
|
176
197
|
'shadow-floating': '0 25px 50px -12px rgb(0 0 0 / .25)',
|
|
177
198
|
'shadow-inset-hairline': 'inset 0 0 0 1px var(--white-10)',
|
|
178
199
|
'ring-focus': '0 0 0 2px var(--ring)',
|
|
200
|
+
'shadow-sm': '0 1px 2px 0 rgb(0 0 0 / .40)',
|
|
201
|
+
'shadow': '0 1px 3px 0 rgb(0 0 0 / .45), 0 1px 2px -1px rgb(0 0 0 / .45)',
|
|
202
|
+
'shadow-md': '0 4px 6px -1px rgb(0 0 0 / .50), 0 2px 4px -2px rgb(0 0 0 / .50)',
|
|
203
|
+
'shadow-lg': '0 10px 15px -3px rgb(0 0 0 / .55), 0 4px 6px -4px rgb(0 0 0 / .55)',
|
|
204
|
+
'shadow-xl': '0 20px 25px -5px rgb(0 0 0 / .60), 0 8px 10px -6px rgb(0 0 0 / .60)',
|
|
205
|
+
'shadow-2xl': 'var(--shadow-floating)',
|
|
179
206
|
'glow-hero': 'radial-gradient(circle,rgb(255 255 255 / .12) 0%,transparent 68%)',
|
|
180
207
|
'glow-hero-blur': '120px',
|
|
181
208
|
'sheen-card': 'radial-gradient(120% 120% at 80% 0%,rgb(255 255 255 / .08) 0%,transparent 55%)',
|
|
@@ -207,6 +234,8 @@ export const zIndex = {
|
|
|
207
234
|
'z-modal': '600',
|
|
208
235
|
'z-popover': '700',
|
|
209
236
|
'z-toast': '800',
|
|
237
|
+
'z-tooltip': 'var(--z-popover)',
|
|
238
|
+
'z-notification': 'var(--z-toast)',
|
|
210
239
|
};
|
|
211
240
|
/** fonts tokens (from tokens/fonts.css). Values are raw CSS. */
|
|
212
241
|
export const fonts = {
|
|
@@ -261,7 +290,7 @@ export const cssVars = {
|
|
|
261
290
|
'--destructive-foreground': '#f5f5f5',
|
|
262
291
|
'--border': '#1f1f1f',
|
|
263
292
|
'--input': '#1f1f1f',
|
|
264
|
-
'--ring': '
|
|
293
|
+
'--ring': 'var(--neutral-500)',
|
|
265
294
|
'--brand': '#e4e4e7',
|
|
266
295
|
'--brand-foreground': '#09090b',
|
|
267
296
|
'--brand-muted': '#a3a3a3',
|
|
@@ -275,7 +304,11 @@ export const cssVars = {
|
|
|
275
304
|
'--surface-header': 'rgb(0 0 0 / .7)',
|
|
276
305
|
'--border-hairline': 'var(--neutral-800)',
|
|
277
306
|
'--border-card': 'var(--white-10)',
|
|
278
|
-
'--border-strong': 'var(--neutral-
|
|
307
|
+
'--border-strong': 'var(--neutral-500)',
|
|
308
|
+
'--surface-0': 'var(--background)',
|
|
309
|
+
'--surface-1': 'var(--card)',
|
|
310
|
+
'--surface-2': 'var(--muted)',
|
|
311
|
+
'--surface-3': 'var(--secondary)',
|
|
279
312
|
'--text-primary': 'var(--pure-white)',
|
|
280
313
|
'--text-secondary': 'var(--white-80)',
|
|
281
314
|
'--text-tertiary': 'var(--white-60)',
|
|
@@ -311,6 +344,23 @@ export const cssVars = {
|
|
|
311
344
|
'--leading-6xl': '1',
|
|
312
345
|
'--text-7xl': '4rem',
|
|
313
346
|
'--leading-7xl': '1',
|
|
347
|
+
'--text-8xl': '5.25rem',
|
|
348
|
+
'--leading-8xl': '1',
|
|
349
|
+
'--text-9xl': '7rem',
|
|
350
|
+
'--leading-9xl': '1',
|
|
351
|
+
'--font-size-xs': 'var(--text-xs)',
|
|
352
|
+
'--font-size-sm': 'var(--text-sm)',
|
|
353
|
+
'--font-size-base': 'var(--text-base)',
|
|
354
|
+
'--font-size-lg': 'var(--text-lg)',
|
|
355
|
+
'--font-size-xl': 'var(--text-xl)',
|
|
356
|
+
'--font-size-2xl': 'var(--text-2xl)',
|
|
357
|
+
'--font-size-3xl': 'var(--text-3xl)',
|
|
358
|
+
'--font-size-4xl': 'var(--text-4xl)',
|
|
359
|
+
'--font-size-5xl': 'var(--text-5xl)',
|
|
360
|
+
'--font-size-6xl': 'var(--text-6xl)',
|
|
361
|
+
'--font-size-7xl': 'var(--text-7xl)',
|
|
362
|
+
'--font-size-8xl': 'var(--text-8xl)',
|
|
363
|
+
'--font-size-9xl': 'var(--text-9xl)',
|
|
314
364
|
'--weight-normal': '400',
|
|
315
365
|
'--weight-medium': '500',
|
|
316
366
|
'--weight-semibold': '600',
|
|
@@ -381,6 +431,12 @@ export const cssVars = {
|
|
|
381
431
|
'--shadow-floating': '0 25px 50px -12px rgb(0 0 0 / .25)',
|
|
382
432
|
'--shadow-inset-hairline': 'inset 0 0 0 1px var(--white-10)',
|
|
383
433
|
'--ring-focus': '0 0 0 2px var(--ring)',
|
|
434
|
+
'--shadow-sm': '0 1px 2px 0 rgb(0 0 0 / .40)',
|
|
435
|
+
'--shadow': '0 1px 3px 0 rgb(0 0 0 / .45), 0 1px 2px -1px rgb(0 0 0 / .45)',
|
|
436
|
+
'--shadow-md': '0 4px 6px -1px rgb(0 0 0 / .50), 0 2px 4px -2px rgb(0 0 0 / .50)',
|
|
437
|
+
'--shadow-lg': '0 10px 15px -3px rgb(0 0 0 / .55), 0 4px 6px -4px rgb(0 0 0 / .55)',
|
|
438
|
+
'--shadow-xl': '0 20px 25px -5px rgb(0 0 0 / .60), 0 8px 10px -6px rgb(0 0 0 / .60)',
|
|
439
|
+
'--shadow-2xl': 'var(--shadow-floating)',
|
|
384
440
|
'--glow-hero': 'radial-gradient(circle,rgb(255 255 255 / .12) 0%,transparent 68%)',
|
|
385
441
|
'--glow-hero-blur': '120px',
|
|
386
442
|
'--sheen-card': 'radial-gradient(120% 120% at 80% 0%,rgb(255 255 255 / .08) 0%,transparent 55%)',
|
|
@@ -406,6 +462,8 @@ export const cssVars = {
|
|
|
406
462
|
'--z-modal': '600',
|
|
407
463
|
'--z-popover': '700',
|
|
408
464
|
'--z-toast': '800',
|
|
465
|
+
'--z-tooltip': 'var(--z-popover)',
|
|
466
|
+
'--z-notification': 'var(--z-toast)',
|
|
409
467
|
'--font-sans': '"Geist","Geist Sans",ui-sans-serif,system-ui,sans-serif',
|
|
410
468
|
'--font-display': 'var(--font-sans)',
|
|
411
469
|
'--font-mono': '"Geist Mono",ui-monospace,SFMono-Regular,monospace',
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# Hanzo Design System — Canonical Tokens
|
|
2
|
+
|
|
3
|
+
`@hanzo/ui` is the single source of truth for the shared Hanzo product look across
|
|
4
|
+
the **Tailwind** apps (hanzo.chat, hanzo.app, hanzo console, commerce, hanzo-desktop).
|
|
5
|
+
Change a value here; apps converge on it. This file is that source of truth for the
|
|
6
|
+
three things that must read as **one product**: typography, the sidebar/panel system,
|
|
7
|
+
and the dark-black palette.
|
|
8
|
+
|
|
9
|
+
> One library: **`@hanzo/ui@8`** (`pkg/ui`, on **`@hanzo/gui`**) IS the component
|
|
10
|
+
> library — the cross-platform product/record layer every surface consumes.
|
|
11
|
+
> **`@hanzo/ui-shadcn`** (`pkgs/ui`) is the legacy shadcn/Tailwind/Radix kit, kept
|
|
12
|
+
> only for existing v5 consumers (pin `@hanzo/ui-shadcn@^5`; no new adoptions).
|
|
13
|
+
> This file stays the source of truth for the *token values* (fonts, dark palette,
|
|
14
|
+
> the sidebar glyph) both render.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 1. Typography — Basel Grotesk + Geist Mono
|
|
19
|
+
|
|
20
|
+
| Role | Family | Notes |
|
|
21
|
+
|------|--------|-------|
|
|
22
|
+
| UI / body / display / heading (`sans`) | **Basel Grotesk** | Self-hosted. Book = weight **400**, Medium = weight **500**. |
|
|
23
|
+
| code / data / mono (`mono`) | **Geist Mono** | `next/font/google` (`Geist_Mono`) or the geist CDN. |
|
|
24
|
+
| Arabic / Hebrew (`--font-ar` / `--font-he`) | unchanged | i18n only — keep. |
|
|
25
|
+
|
|
26
|
+
**Dropped as defaults:** Geist Sans, DM Sans, Figtree, Inter, PT Sans, Roboto Mono.
|
|
27
|
+
|
|
28
|
+
Basel is a **licensed, non-Google** face — **self-host** the woff2/woff, do NOT use
|
|
29
|
+
`next/font/google` for it. Canonical files (mirror lux.exchange):
|
|
30
|
+
`Basel-Grotesk-Book.woff2/.woff` (400), `Basel-Grotesk-Medium.woff2/.woff` (500).
|
|
31
|
+
|
|
32
|
+
`@font-face` (weights 400/500, `font-display: swap`, `font-style: normal`):
|
|
33
|
+
|
|
34
|
+
```css
|
|
35
|
+
@font-face {
|
|
36
|
+
font-family: 'Basel';
|
|
37
|
+
font-style: normal;
|
|
38
|
+
font-weight: 400; /* Book; 500 = Medium */
|
|
39
|
+
font-display: swap;
|
|
40
|
+
src: url('.../Basel-Grotesk-Book.woff2') format('woff2'),
|
|
41
|
+
url('.../Basel-Grotesk-Book.woff') format('woff');
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Per-app adoption (converge the value, keep each app's own mechanism):
|
|
46
|
+
- **@hanzo/ui / Next apps** → `next/font/local` for Basel (`--font-basel-sans`) +
|
|
47
|
+
`next/font/google` `Geist_Mono` (`--font-geist-mono`). See `app/lib/fonts.ts`;
|
|
48
|
+
tailwind `sans → var(--font-basel-sans)`, `mono → var(--font-geist-mono)`.
|
|
49
|
+
- **Vite + Tailwind apps** (chat, launcher, desktop) → self-host Basel `@font-face`
|
|
50
|
+
+ geist-mono CDN import; tailwind `fontFamily.sans = ['Basel', …]`,
|
|
51
|
+
`mono = ['Geist Mono', …]`.
|
|
52
|
+
- **Tamagui (console)** → Basel `@font-face` in globals + override the Tamagui
|
|
53
|
+
`body`/`heading` font `family` to Basel; Geist Mono for `code`/`pre`.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## 2. Sidebar toggle icon — lucide `PanelLeft`
|
|
58
|
+
|
|
59
|
+
One glyph everywhere: lucide **`PanelLeft`** (the shadcn `SidebarTrigger` default).
|
|
60
|
+
Where a directional open/close affordance is wanted, use the pair
|
|
61
|
+
**`PanelLeftClose`** (expanded) / **`PanelLeft`** (collapsed). Never a hamburger,
|
|
62
|
+
a magnifier, a directional arrow, or a bespoke panel SVG for the sidebar toggle.
|
|
63
|
+
|
|
64
|
+
- Icon: stroke-2, ~16–20px, `currentColor`.
|
|
65
|
+
- Button: ghost/outline, square (`size-7`/`h-6 w-6`), subtle hover.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## 3. Sidebar + panels
|
|
70
|
+
|
|
71
|
+
| Spec | Value |
|
|
72
|
+
|------|-------|
|
|
73
|
+
| Sidebar width (expanded) | **16rem / 256px** (`SIDEBAR_WIDTH`) |
|
|
74
|
+
| Sidebar width (collapsed / icon rail) | **3rem / 48px** (apps vary 48–70px) |
|
|
75
|
+
| Sidebar / panel surface | resting **#0a0a0a** over the true-black page |
|
|
76
|
+
| Border / separation | `border-border` — subtle white-alpha ~10% in dark (`border-r` / `border-l`) |
|
|
77
|
+
| Item hover | subtle `white/5` |
|
|
78
|
+
| Item active | monochrome `white/10` — **no colored accent** (the house style is monochrome) |
|
|
79
|
+
| Right panel rail | `border-l border-border`, same surface, collapsible |
|
|
80
|
+
|
|
81
|
+
Prefer the `@hanzo/ui` `Sidebar` primitive (`pkgs/ui/primitives/sidebar.tsx`,
|
|
82
|
+
`SidebarTrigger` → `PanelLeft`) where the app can consume it; otherwise match these
|
|
83
|
+
classes/tokens.
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## 4. Dark-black palette (true-black OLED)
|
|
88
|
+
|
|
89
|
+
The house dark theme is a **true-black** canvas (matches hanzo.ai marketing +
|
|
90
|
+
hanzo.chat OLED), with a shallow surface-depth ladder for cards/panels and quiet
|
|
91
|
+
hairline borders — never harsh pure-white on pure-black.
|
|
92
|
+
|
|
93
|
+
| Token | Value | Use |
|
|
94
|
+
|-------|-------|-----|
|
|
95
|
+
| Page background | **#000000** (`oklch(0 0 0)`) | body / canvas |
|
|
96
|
+
| Surface / sidebar / panel (resting) | **#0a0a0a** | sidebars, panels, cards |
|
|
97
|
+
| Press | **#050505** | pressed surface |
|
|
98
|
+
| Elevated / hover | **#171717** | hover, raised card |
|
|
99
|
+
| Border / divider | **rgba(255,255,255,0.10)** (≈ `#171717` opaque on black) | hairlines |
|
|
100
|
+
| Foreground (primary text) | near-white **#ededf1** (`oklch(0.985)`) — not pure `#fff` | body text |
|
|
101
|
+
| Muted / secondary text | `white/70` (≈ `#a1a1aa`) | secondary |
|
|
102
|
+
|
|
103
|
+
Keep each app's theme-token engine (CSS vars / Tailwind tokens / Tamagui `$color*`);
|
|
104
|
+
converge the **values/usage** to the table above, don't rip the engine.
|
|
105
|
+
|
|
106
|
+
Reference: `pkgs/ui/style/hanzo-default-colors.css` (`.dark` / `.hanzo-ui-dark-theme`).
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanzo/design",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Hanzo Design System \u2014 monochrome, dark-default tokens + components + brand assets, the single source of truth for every Hanzo surface. CSS + typed programmatic tokens.",
|
|
5
|
-
"license": "
|
|
5
|
+
"license": "BSD-3-Clause",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "https://github.com/hanzoai/design.git"
|
|
@@ -29,7 +29,8 @@
|
|
|
29
29
|
},
|
|
30
30
|
"scripts": {
|
|
31
31
|
"gen": "node scripts/gen-tokens.mjs",
|
|
32
|
-
"
|
|
32
|
+
"test": "node scripts/check-tokens.mjs",
|
|
33
|
+
"build": "npm run gen && npm test && tsc -p tsconfig.json",
|
|
33
34
|
"prepublishOnly": "npm run build"
|
|
34
35
|
},
|
|
35
36
|
"devDependencies": {
|
package/prompts/README.md
CHANGED
|
@@ -24,3 +24,10 @@ Everything here assumes one import gives you the tokens:
|
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
So every rule below can be obeyed with a CSS variable, never a hardcoded hex.
|
|
27
|
+
|
|
28
|
+
## The two prompts (one and one way)
|
|
29
|
+
- **`system.md`** — the design LANGUAGE prompt: paste as a system prompt to make any
|
|
30
|
+
model generate Hanzo-looking output (monochrome, true-black, token-only).
|
|
31
|
+
- **`studio-designer.md`** — the AGENTIC prompt Hanzo Studio's Design System designer
|
|
32
|
+
hands the Hanzo agent stack to GENERATE/EDIT this repo's file tree with its file
|
|
33
|
+
tools, grounded in `guidelines/DESIGN.md`. Uses `system.md`'s language rules.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Hanzo Studio — Design System Designer (agentic prompt)
|
|
2
|
+
|
|
3
|
+
The system prompt the Studio "Design System" designer hands to the Hanzo agent
|
|
4
|
+
stack. The agent generates/edits the design-system file tree in **hanzoai/design**
|
|
5
|
+
using its file tools (write/edit/tree), grounded in the canon.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
You are the **Hanzo Design System designer**, an agent operating on the
|
|
10
|
+
`hanzoai/design` repository. You produce and maintain a complete, coherent design
|
|
11
|
+
system as real files, using your file tools (read, write, edit, tree).
|
|
12
|
+
|
|
13
|
+
## The canon (never contradict)
|
|
14
|
+
- **Typography:** Basel Grotesk (`sans`, weights 400 Book / 500 Medium, self-hosted
|
|
15
|
+
woff2) + Geist Mono (`mono`). No Inter/Roboto/DM Sans as defaults.
|
|
16
|
+
- **Palette:** true-black OLED. Page `#000`, surface `#0a0a0a`, press `#050505`,
|
|
17
|
+
elevated `#171717`, border `rgba(255,255,255,.10)`, text `#ededf1`. **Monochrome —
|
|
18
|
+
no colored accents.** Semantic color only for live/error/warning.
|
|
19
|
+
- **Sidebar:** 256px expanded / 48px rail; lucide `PanelLeft` toggle; `white/5` hover,
|
|
20
|
+
`white/10` active.
|
|
21
|
+
- Full source: `guidelines/DESIGN.md`.
|
|
22
|
+
|
|
23
|
+
## Structure you own
|
|
24
|
+
`tokens/` (base·colors·fonts·spacing·radius·elevation·motion.css + TS modules) ·
|
|
25
|
+
`guidelines/` · `assets/{brand,logos,providers,fonts}` · `components/` · `ui_kits/`.
|
|
26
|
+
|
|
27
|
+
## How you work
|
|
28
|
+
1. **Read before writing.** `tree` the repo + read `guidelines/DESIGN.md` and the
|
|
29
|
+
relevant `tokens/*` so every new value stays consistent with what exists.
|
|
30
|
+
2. **Tokens are the source.** A component reads token vars (`var(--hz-*)`), never a
|
|
31
|
+
hardcoded hex/px. If a needed token is missing, add it to `tokens/` first, then use it.
|
|
32
|
+
3. **One and one way.** One place per value. Don't duplicate a color/space/font.
|
|
33
|
+
4. **Generate real, coherent files** — a request like "add a Card component" writes
|
|
34
|
+
`components/card.{css,tsx,md}` referencing the tokens + a usage note, not a mock.
|
|
35
|
+
5. **Honor the request scope.** "Generate a design system" → the full tree from the
|
|
36
|
+
canon. "Add elevation tokens" → just `tokens/elevation.css` + wire into `base.css`.
|
|
37
|
+
6. **Never fabricate brand assets.** Reference real files in `assets/`; if a logo/font
|
|
38
|
+
is missing, note it as a TODO, don't invent bytes.
|
|
39
|
+
7. **Explain what you wrote** (files + why) so the review is legible.
|
|
40
|
+
|
|
41
|
+
Output: the files, written to the repo, plus a one-paragraph summary of the change.
|
package/src/index.ts
CHANGED
|
@@ -12,17 +12,30 @@
|
|
|
12
12
|
export * from './tokens.gen.js'
|
|
13
13
|
import { cssVars, type CssVarName } from './tokens.gen.js'
|
|
14
14
|
|
|
15
|
+
/** A token name with the leading `--` omitted: `'background'` for `'--background'`. */
|
|
16
|
+
export type TokenName = CssVarName extends `--${infer N}` ? N : never
|
|
17
|
+
|
|
15
18
|
/**
|
|
16
|
-
* A `var(--name
|
|
17
|
-
* reach a token so it resolves through the live CSS cascade (honoring
|
|
18
|
-
* viewer's light/dark theme) rather than baking a
|
|
19
|
+
* A `var(--name, <authored literal>)` reference to a token — the ONE way code
|
|
20
|
+
* should reach a token, so it resolves through the live CSS cascade (honoring
|
|
21
|
+
* the viewer's light/dark theme and any brand fork) rather than baking a value.
|
|
22
|
+
*
|
|
23
|
+
* background: cssVar('--background') // → "var(--background, #000000)"
|
|
24
|
+
* color: cssVar('foreground', '#fff')// → "var(--foreground, #fff)"
|
|
19
25
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
26
|
+
* The name is checked AGAINST THE STYLESHEET at compile time. That check used to
|
|
27
|
+
* be opted out of with `| (string & {})`, which is how `cssVar('surface-1')`
|
|
28
|
+
* shipped: the token did not exist, `var(--surface-1)` resolved to nothing, and
|
|
29
|
+
* a menu painted transparent with no error anywhere. An undefined custom
|
|
30
|
+
* property fails SILENTLY, so the type is the only place it can be caught.
|
|
31
|
+
*
|
|
32
|
+
* When no explicit fallback is given the token's own authored literal is used,
|
|
33
|
+
* so the reference still paints on a host that has not loaded the CSS layer.
|
|
22
34
|
*/
|
|
23
|
-
export function cssVar(name: CssVarName |
|
|
24
|
-
const n = name.startsWith('--') ? name : `--${name}`
|
|
25
|
-
|
|
35
|
+
export function cssVar(name: CssVarName | TokenName, fallback?: string): string {
|
|
36
|
+
const n = (name.startsWith('--') ? name : `--${name}`) as CssVarName
|
|
37
|
+
const lit = fallback ?? (cssVars as Record<string, string>)[n]
|
|
38
|
+
return lit ? `var(${n}, ${lit})` : `var(${n})`
|
|
26
39
|
}
|
|
27
40
|
|
|
28
41
|
/** The raw authored value of a token (the literal from the CSS), or `undefined`. */
|
|
@@ -35,8 +48,12 @@ export function tokenValue(name: CssVarName): string | undefined {
|
|
|
35
48
|
* cannot use a bundler CSS import (e.g. a runtime-mounted island). Prefer the
|
|
36
49
|
* static `import '@hanzo/design/styles.css'` where a bundler is available.
|
|
37
50
|
* No-op outside the browser.
|
|
51
|
+
*
|
|
52
|
+
* `href` is REQUIRED: this used to default to esm.sh, which silently made a
|
|
53
|
+
* third-party CDN the origin of the entire token layer for anyone who called it
|
|
54
|
+
* bare. Pass a URL you serve.
|
|
38
55
|
*/
|
|
39
|
-
export function injectDesignCss(href
|
|
56
|
+
export function injectDesignCss(href: string): void {
|
|
40
57
|
if (typeof document === 'undefined') return
|
|
41
58
|
if (document.querySelector('link[data-hanzo-design]')) return
|
|
42
59
|
const l = document.createElement('link')
|
package/src/tokens.gen.ts
CHANGED
|
@@ -45,7 +45,7 @@ export const colors = {
|
|
|
45
45
|
'destructive-foreground': '#f5f5f5',
|
|
46
46
|
'border': '#1f1f1f',
|
|
47
47
|
'input': '#1f1f1f',
|
|
48
|
-
'ring': '
|
|
48
|
+
'ring': 'var(--neutral-500)',
|
|
49
49
|
'brand': '#e4e4e7',
|
|
50
50
|
'brand-foreground': '#09090b',
|
|
51
51
|
'brand-muted': '#a3a3a3',
|
|
@@ -59,7 +59,11 @@ export const colors = {
|
|
|
59
59
|
'surface-header': 'rgb(0 0 0 / .7)',
|
|
60
60
|
'border-hairline': 'var(--neutral-800)',
|
|
61
61
|
'border-card': 'var(--white-10)',
|
|
62
|
-
'border-strong': 'var(--neutral-
|
|
62
|
+
'border-strong': 'var(--neutral-500)',
|
|
63
|
+
'surface-0': 'var(--background)',
|
|
64
|
+
'surface-1': 'var(--card)',
|
|
65
|
+
'surface-2': 'var(--muted)',
|
|
66
|
+
'surface-3': 'var(--secondary)',
|
|
63
67
|
'text-primary': 'var(--pure-white)',
|
|
64
68
|
'text-secondary': 'var(--white-80)',
|
|
65
69
|
'text-tertiary': 'var(--white-60)',
|
|
@@ -99,6 +103,23 @@ export const typography = {
|
|
|
99
103
|
'leading-6xl': '1',
|
|
100
104
|
'text-7xl': '4rem',
|
|
101
105
|
'leading-7xl': '1',
|
|
106
|
+
'text-8xl': '5.25rem',
|
|
107
|
+
'leading-8xl': '1',
|
|
108
|
+
'text-9xl': '7rem',
|
|
109
|
+
'leading-9xl': '1',
|
|
110
|
+
'font-size-xs': 'var(--text-xs)',
|
|
111
|
+
'font-size-sm': 'var(--text-sm)',
|
|
112
|
+
'font-size-base': 'var(--text-base)',
|
|
113
|
+
'font-size-lg': 'var(--text-lg)',
|
|
114
|
+
'font-size-xl': 'var(--text-xl)',
|
|
115
|
+
'font-size-2xl': 'var(--text-2xl)',
|
|
116
|
+
'font-size-3xl': 'var(--text-3xl)',
|
|
117
|
+
'font-size-4xl': 'var(--text-4xl)',
|
|
118
|
+
'font-size-5xl': 'var(--text-5xl)',
|
|
119
|
+
'font-size-6xl': 'var(--text-6xl)',
|
|
120
|
+
'font-size-7xl': 'var(--text-7xl)',
|
|
121
|
+
'font-size-8xl': 'var(--text-8xl)',
|
|
122
|
+
'font-size-9xl': 'var(--text-9xl)',
|
|
102
123
|
'weight-normal': '400',
|
|
103
124
|
'weight-medium': '500',
|
|
104
125
|
'weight-semibold': '600',
|
|
@@ -181,6 +202,12 @@ export const elevation = {
|
|
|
181
202
|
'shadow-floating': '0 25px 50px -12px rgb(0 0 0 / .25)',
|
|
182
203
|
'shadow-inset-hairline': 'inset 0 0 0 1px var(--white-10)',
|
|
183
204
|
'ring-focus': '0 0 0 2px var(--ring)',
|
|
205
|
+
'shadow-sm': '0 1px 2px 0 rgb(0 0 0 / .40)',
|
|
206
|
+
'shadow': '0 1px 3px 0 rgb(0 0 0 / .45), 0 1px 2px -1px rgb(0 0 0 / .45)',
|
|
207
|
+
'shadow-md': '0 4px 6px -1px rgb(0 0 0 / .50), 0 2px 4px -2px rgb(0 0 0 / .50)',
|
|
208
|
+
'shadow-lg': '0 10px 15px -3px rgb(0 0 0 / .55), 0 4px 6px -4px rgb(0 0 0 / .55)',
|
|
209
|
+
'shadow-xl': '0 20px 25px -5px rgb(0 0 0 / .60), 0 8px 10px -6px rgb(0 0 0 / .60)',
|
|
210
|
+
'shadow-2xl': 'var(--shadow-floating)',
|
|
184
211
|
'glow-hero': 'radial-gradient(circle,rgb(255 255 255 / .12) 0%,transparent 68%)',
|
|
185
212
|
'glow-hero-blur': '120px',
|
|
186
213
|
'sheen-card': 'radial-gradient(120% 120% at 80% 0%,rgb(255 255 255 / .08) 0%,transparent 55%)',
|
|
@@ -214,6 +241,8 @@ export const zIndex = {
|
|
|
214
241
|
'z-modal': '600',
|
|
215
242
|
'z-popover': '700',
|
|
216
243
|
'z-toast': '800',
|
|
244
|
+
'z-tooltip': 'var(--z-popover)',
|
|
245
|
+
'z-notification': 'var(--z-toast)',
|
|
217
246
|
} as const
|
|
218
247
|
|
|
219
248
|
/** fonts tokens (from tokens/fonts.css). Values are raw CSS. */
|
|
@@ -273,7 +302,7 @@ export const cssVars = {
|
|
|
273
302
|
'--destructive-foreground': '#f5f5f5',
|
|
274
303
|
'--border': '#1f1f1f',
|
|
275
304
|
'--input': '#1f1f1f',
|
|
276
|
-
'--ring': '
|
|
305
|
+
'--ring': 'var(--neutral-500)',
|
|
277
306
|
'--brand': '#e4e4e7',
|
|
278
307
|
'--brand-foreground': '#09090b',
|
|
279
308
|
'--brand-muted': '#a3a3a3',
|
|
@@ -287,7 +316,11 @@ export const cssVars = {
|
|
|
287
316
|
'--surface-header': 'rgb(0 0 0 / .7)',
|
|
288
317
|
'--border-hairline': 'var(--neutral-800)',
|
|
289
318
|
'--border-card': 'var(--white-10)',
|
|
290
|
-
'--border-strong': 'var(--neutral-
|
|
319
|
+
'--border-strong': 'var(--neutral-500)',
|
|
320
|
+
'--surface-0': 'var(--background)',
|
|
321
|
+
'--surface-1': 'var(--card)',
|
|
322
|
+
'--surface-2': 'var(--muted)',
|
|
323
|
+
'--surface-3': 'var(--secondary)',
|
|
291
324
|
'--text-primary': 'var(--pure-white)',
|
|
292
325
|
'--text-secondary': 'var(--white-80)',
|
|
293
326
|
'--text-tertiary': 'var(--white-60)',
|
|
@@ -323,6 +356,23 @@ export const cssVars = {
|
|
|
323
356
|
'--leading-6xl': '1',
|
|
324
357
|
'--text-7xl': '4rem',
|
|
325
358
|
'--leading-7xl': '1',
|
|
359
|
+
'--text-8xl': '5.25rem',
|
|
360
|
+
'--leading-8xl': '1',
|
|
361
|
+
'--text-9xl': '7rem',
|
|
362
|
+
'--leading-9xl': '1',
|
|
363
|
+
'--font-size-xs': 'var(--text-xs)',
|
|
364
|
+
'--font-size-sm': 'var(--text-sm)',
|
|
365
|
+
'--font-size-base': 'var(--text-base)',
|
|
366
|
+
'--font-size-lg': 'var(--text-lg)',
|
|
367
|
+
'--font-size-xl': 'var(--text-xl)',
|
|
368
|
+
'--font-size-2xl': 'var(--text-2xl)',
|
|
369
|
+
'--font-size-3xl': 'var(--text-3xl)',
|
|
370
|
+
'--font-size-4xl': 'var(--text-4xl)',
|
|
371
|
+
'--font-size-5xl': 'var(--text-5xl)',
|
|
372
|
+
'--font-size-6xl': 'var(--text-6xl)',
|
|
373
|
+
'--font-size-7xl': 'var(--text-7xl)',
|
|
374
|
+
'--font-size-8xl': 'var(--text-8xl)',
|
|
375
|
+
'--font-size-9xl': 'var(--text-9xl)',
|
|
326
376
|
'--weight-normal': '400',
|
|
327
377
|
'--weight-medium': '500',
|
|
328
378
|
'--weight-semibold': '600',
|
|
@@ -393,6 +443,12 @@ export const cssVars = {
|
|
|
393
443
|
'--shadow-floating': '0 25px 50px -12px rgb(0 0 0 / .25)',
|
|
394
444
|
'--shadow-inset-hairline': 'inset 0 0 0 1px var(--white-10)',
|
|
395
445
|
'--ring-focus': '0 0 0 2px var(--ring)',
|
|
446
|
+
'--shadow-sm': '0 1px 2px 0 rgb(0 0 0 / .40)',
|
|
447
|
+
'--shadow': '0 1px 3px 0 rgb(0 0 0 / .45), 0 1px 2px -1px rgb(0 0 0 / .45)',
|
|
448
|
+
'--shadow-md': '0 4px 6px -1px rgb(0 0 0 / .50), 0 2px 4px -2px rgb(0 0 0 / .50)',
|
|
449
|
+
'--shadow-lg': '0 10px 15px -3px rgb(0 0 0 / .55), 0 4px 6px -4px rgb(0 0 0 / .55)',
|
|
450
|
+
'--shadow-xl': '0 20px 25px -5px rgb(0 0 0 / .60), 0 8px 10px -6px rgb(0 0 0 / .60)',
|
|
451
|
+
'--shadow-2xl': 'var(--shadow-floating)',
|
|
396
452
|
'--glow-hero': 'radial-gradient(circle,rgb(255 255 255 / .12) 0%,transparent 68%)',
|
|
397
453
|
'--glow-hero-blur': '120px',
|
|
398
454
|
'--sheen-card': 'radial-gradient(120% 120% at 80% 0%,rgb(255 255 255 / .08) 0%,transparent 55%)',
|
|
@@ -418,6 +474,8 @@ export const cssVars = {
|
|
|
418
474
|
'--z-modal': '600',
|
|
419
475
|
'--z-popover': '700',
|
|
420
476
|
'--z-toast': '800',
|
|
477
|
+
'--z-tooltip': 'var(--z-popover)',
|
|
478
|
+
'--z-notification': 'var(--z-toast)',
|
|
421
479
|
'--font-sans': '"Geist","Geist Sans",ui-sans-serif,system-ui,sans-serif',
|
|
422
480
|
'--font-display': 'var(--font-sans)',
|
|
423
481
|
'--font-mono': '"Geist Mono",ui-monospace,SFMono-Regular,monospace',
|
package/styles.css
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
/* Hanzo Design System — global entry point. Import THIS one file.
|
|
2
|
-
|
|
2
|
+
Every token group is served here; nothing is authored but left unimported.
|
|
3
|
+
(`tokens/z.css` went missing from this list once and the whole ladder stopped
|
|
4
|
+
resolving while still type-checking — see scripts/check-tokens.mjs, which now
|
|
5
|
+
fails the build if any tokens/*.css is not imported below.) */
|
|
3
6
|
@import url("tokens/fonts.css");
|
|
4
7
|
@import url("tokens/colors.css");
|
|
5
8
|
@import url("tokens/typography.css");
|
package/tokens/base.css
CHANGED
|
@@ -2,10 +2,14 @@
|
|
|
2
2
|
a utility framework. Components carry their own styles inline. */
|
|
3
3
|
*{box-sizing:border-box;border-color:var(--border)}
|
|
4
4
|
html{-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;scroll-behavior:smooth}
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
/* Each font-family carries the stack as a literal fallback. --font-sans lives in
|
|
6
|
+
tokens/fonts.css, which a surface may legitimately import separately; without
|
|
7
|
+
the fallback an unresolved var() makes font-family invalid and the UA drops to
|
|
8
|
+
its SERIF default — the whole console silently rendered in Times. */
|
|
9
|
+
body{margin:0;background:var(--background);color:var(--foreground);font-family:var(--font-sans,ui-sans-serif,system-ui,sans-serif);font-size:var(--text-base);line-height:var(--leading-base);font-feature-settings:var(--font-feature-settings)}
|
|
10
|
+
h1,h2,h3,h4{margin:0;font-family:var(--font-display,var(--font-sans,ui-sans-serif,system-ui,sans-serif));letter-spacing:var(--tracking-tight);color:var(--text-primary)}
|
|
7
11
|
p{margin:0;text-wrap:pretty}
|
|
8
|
-
code,pre,kbd{font-family:var(--font-mono)}
|
|
12
|
+
code,pre,kbd{font-family:var(--font-mono,ui-monospace,SFMono-Regular,monospace)}
|
|
9
13
|
a{color:var(--text-primary);text-decoration:none;text-underline-offset:4px;transition:color var(--duration-fast) var(--ease-out)}
|
|
10
14
|
a:hover{color:var(--text-primary);text-decoration:underline}
|
|
11
15
|
:focus-visible{outline:2px solid var(--ring);outline-offset:2px}
|
package/tokens/colors.css
CHANGED
|
@@ -51,7 +51,12 @@
|
|
|
51
51
|
--destructive-foreground:#f5f5f5;
|
|
52
52
|
--border:#1f1f1f;
|
|
53
53
|
--input:#1f1f1f;
|
|
54
|
-
|
|
54
|
+
/* A focus indicator is a NON-TEXT CONTRAST target: WCAG 2.4.11/1.4.11 require
|
|
55
|
+
3:1 against every surface it can land on. --neutral-500 is the only rung on
|
|
56
|
+
this ladder that clears 3:1 on all of them — #000000, #0a0a0a, #101010,
|
|
57
|
+
#1a1a1a, AND the light theme's #ffffff/#f5f5f5 — so one value serves both
|
|
58
|
+
themes. (Was #333333 = 1.66:1 on --background: not a focus indicator.) */
|
|
59
|
+
--ring:var(--neutral-500);
|
|
55
60
|
--brand:#e4e4e7;
|
|
56
61
|
--brand-foreground:#09090b;
|
|
57
62
|
--brand-muted:#a3a3a3;
|
|
@@ -65,9 +70,24 @@
|
|
|
65
70
|
--surface-card-quiet:rgb(23 23 23 / .4); /* bg-neutral-900/40 — story cards */
|
|
66
71
|
--surface-overlay:rgb(10 10 10 / .95); /* dropdown / popover panels */
|
|
67
72
|
--surface-header:rgb(0 0 0 / .7); /* fixed nav, with backdrop blur */
|
|
73
|
+
/* Boundaries come in two kinds and they are NOT interchangeable.
|
|
74
|
+
DECORATIVE (--border, --border-hairline, --border-card): separates content;
|
|
75
|
+
WCAG imposes no ratio. Keep them quiet.
|
|
76
|
+
PERCEIVABLE (--border-strong): identifies a CONTROL — an input edge, a
|
|
77
|
+
switch, a checkbox — and must clear 3:1 (WCAG 1.4.11) on every surface.
|
|
78
|
+
Reach for --border-strong whenever the boundary IS the affordance. */
|
|
68
79
|
--border-hairline:var(--neutral-800);
|
|
69
80
|
--border-card:var(--white-10);
|
|
70
|
-
--border-strong:var(--neutral-
|
|
81
|
+
--border-strong:var(--neutral-500); /* 3.59:1 worst case — see --ring */
|
|
82
|
+
|
|
83
|
+
/* ——— the numeric surface ladder ——— */
|
|
84
|
+
/* Aliases onto the semantic canvases above, so a brand fork that retunes
|
|
85
|
+
--card/--muted/--secondary retunes the ladder with it and the light theme
|
|
86
|
+
inverts for free. Ascending lift: 0 is the page, 3 is a hovered control. */
|
|
87
|
+
--surface-0:var(--background);
|
|
88
|
+
--surface-1:var(--card);
|
|
89
|
+
--surface-2:var(--muted);
|
|
90
|
+
--surface-3:var(--secondary);
|
|
71
91
|
|
|
72
92
|
/* ——— text ranks ——— */
|
|
73
93
|
--text-primary:var(--pure-white);
|
|
@@ -107,7 +127,10 @@
|
|
|
107
127
|
--destructive-foreground:#ffffff;
|
|
108
128
|
--border:#e5e5e5;
|
|
109
129
|
--input:#e5e5e5;
|
|
110
|
-
|
|
130
|
+
/* Same rung as dark: #d4d4d4 measured 1.48:1 on white and could not carry a
|
|
131
|
+
focus indicator either. --neutral-500 is 4.74:1 on #ffffff / 4.38:1 on
|
|
132
|
+
#f5f5f5, so ONE value is conformant in both themes. */
|
|
133
|
+
--ring:var(--neutral-500);
|
|
111
134
|
--black:#0a0a0a;
|
|
112
135
|
--white:#ffffff;
|
|
113
136
|
--surface-card:#f5f5f5;
|
|
@@ -117,7 +140,10 @@
|
|
|
117
140
|
--surface-header:rgb(255 255 255 / .8);
|
|
118
141
|
--border-hairline:var(--neutral-200);
|
|
119
142
|
--border-card:rgb(0 0 0 / .1);
|
|
120
|
-
--border-strong:var(--neutral-
|
|
143
|
+
--border-strong:var(--neutral-500); /* was --neutral-300 = 1.48:1 on white */
|
|
144
|
+
/* The white-opacity ladder does NOT invert, so --white-40 is white-on-white
|
|
145
|
+
here (1.00:1). Anything that needs a visible edge in BOTH themes must use
|
|
146
|
+
--border-strong, never a --white-* rung. */
|
|
121
147
|
--text-primary:var(--neutral-950);
|
|
122
148
|
--text-secondary:rgb(10 10 10 / .8);
|
|
123
149
|
--text-tertiary:rgb(10 10 10 / .6);
|
package/tokens/elevation.css
CHANGED
|
@@ -6,6 +6,18 @@
|
|
|
6
6
|
--shadow-floating:0 25px 50px -12px rgb(0 0 0 / .25); /* shadow-2xl: composer, dropdowns, mega panel */
|
|
7
7
|
--shadow-inset-hairline:inset 0 0 0 1px var(--white-10);
|
|
8
8
|
--ring-focus:0 0 0 2px var(--ring);
|
|
9
|
+
|
|
10
|
+
/* The t-shirt ramp. Named by size rather than by role, because that is how
|
|
11
|
+
every component library already asks for a shadow (and how @hanzo/brand
|
|
12
|
+
spells it). Alphas are heavier than Tailwind's defaults: on a near-black
|
|
13
|
+
ground a 10% black drop is invisible, so each rung is tuned to read on
|
|
14
|
+
--background. --shadow-2xl and --shadow-floating are the same rung. */
|
|
15
|
+
--shadow-sm:0 1px 2px 0 rgb(0 0 0 / .40);
|
|
16
|
+
--shadow:0 1px 3px 0 rgb(0 0 0 / .45), 0 1px 2px -1px rgb(0 0 0 / .45);
|
|
17
|
+
--shadow-md:0 4px 6px -1px rgb(0 0 0 / .50), 0 2px 4px -2px rgb(0 0 0 / .50);
|
|
18
|
+
--shadow-lg:0 10px 15px -3px rgb(0 0 0 / .55), 0 4px 6px -4px rgb(0 0 0 / .55);
|
|
19
|
+
--shadow-xl:0 20px 25px -5px rgb(0 0 0 / .60), 0 8px 10px -6px rgb(0 0 0 / .60);
|
|
20
|
+
--shadow-2xl:var(--shadow-floating);
|
|
9
21
|
/* Ambient hero glow — a single white radial, blurred 120px, low opacity. */
|
|
10
22
|
--glow-hero:radial-gradient(circle,rgb(255 255 255 / .12) 0%,transparent 68%); /* @kind color */
|
|
11
23
|
--glow-hero-blur:120px;
|
package/tokens/fonts.css
CHANGED
|
@@ -1,7 +1,34 @@
|
|
|
1
1
|
/* Geist Sans + Geist Mono — the only two faces on Hanzo surfaces.
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
SELF-HOSTED. The faces ship inside this package (assets/fonts/*.woff2, two
|
|
3
|
+
variable files, 141 KB total, SIL OFL-1.1 — see assets/fonts/LICENSE-Geist.txt).
|
|
4
|
+
|
|
5
|
+
Why self-hosted rather than @import from fonts.googleapis.com:
|
|
6
|
+
- A sign-in page must not make a third-party request. hanzoai/id refused to
|
|
7
|
+
import this file for exactly that reason, which split the token layer: id
|
|
8
|
+
took the colours and not the typeface. Self-hosting removes the reason, so
|
|
9
|
+
every surface can import styles.css unchanged.
|
|
10
|
+
- The @import was a render-blocking request to a host we do not control, on
|
|
11
|
+
the critical path of every surface, and it broke offline/air-gapped dev.
|
|
12
|
+
- One variable file per family replaces nine static weights, and it is fewer
|
|
13
|
+
bytes than the CSS-then-woff2 round trip Google served.
|
|
14
|
+
|
|
15
|
+
The url()s are relative to THIS file, so they resolve wherever the package is
|
|
16
|
+
mounted — node_modules, a CDN, a copied dist — with no configuration. */
|
|
17
|
+
|
|
18
|
+
@font-face{
|
|
19
|
+
font-family:"Geist";
|
|
20
|
+
src:url("../assets/fonts/Geist-Variable.woff2") format("woff2");
|
|
21
|
+
font-weight:100 900;
|
|
22
|
+
font-style:normal;
|
|
23
|
+
font-display:swap;
|
|
24
|
+
}
|
|
25
|
+
@font-face{
|
|
26
|
+
font-family:"Geist Mono";
|
|
27
|
+
src:url("../assets/fonts/GeistMono-Variable.woff2") format("woff2");
|
|
28
|
+
font-weight:100 900;
|
|
29
|
+
font-style:normal;
|
|
30
|
+
font-display:swap;
|
|
31
|
+
}
|
|
5
32
|
|
|
6
33
|
:root{
|
|
7
34
|
--font-sans:"Geist","Geist Sans",ui-sans-serif,system-ui,sans-serif;
|
package/tokens/typography.css
CHANGED
|
@@ -16,6 +16,25 @@
|
|
|
16
16
|
--text-5xl:2.5rem; --leading-5xl:1.05; /* 40px */
|
|
17
17
|
--text-6xl:3.25rem; --leading-6xl:1; /* 52px */
|
|
18
18
|
--text-7xl:4rem; --leading-7xl:1; /* 64px */
|
|
19
|
+
--text-8xl:5.25rem; --leading-8xl:1; /* 84px */
|
|
20
|
+
--text-9xl:7rem; --leading-9xl:1; /* 112px */
|
|
21
|
+
|
|
22
|
+
/* The SAME scale under @hanzo/brand's spelling. @hanzo/gui's shell theme and
|
|
23
|
+
Hanzo Studio address the ramp as --font-size-*; both names are one value, so
|
|
24
|
+
a component written against either resolves here. --text-* is canonical. */
|
|
25
|
+
--font-size-xs:var(--text-xs);
|
|
26
|
+
--font-size-sm:var(--text-sm);
|
|
27
|
+
--font-size-base:var(--text-base);
|
|
28
|
+
--font-size-lg:var(--text-lg);
|
|
29
|
+
--font-size-xl:var(--text-xl);
|
|
30
|
+
--font-size-2xl:var(--text-2xl);
|
|
31
|
+
--font-size-3xl:var(--text-3xl);
|
|
32
|
+
--font-size-4xl:var(--text-4xl);
|
|
33
|
+
--font-size-5xl:var(--text-5xl);
|
|
34
|
+
--font-size-6xl:var(--text-6xl);
|
|
35
|
+
--font-size-7xl:var(--text-7xl);
|
|
36
|
+
--font-size-8xl:var(--text-8xl);
|
|
37
|
+
--font-size-9xl:var(--text-9xl);
|
|
19
38
|
|
|
20
39
|
--weight-normal:400;
|
|
21
40
|
--weight-medium:500;
|
package/tokens/z.css
CHANGED
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
/* Stacking order — the one z-index ladder. Layers are named by role, never by a
|
|
2
2
|
magic number, so a dropdown opened from the fixed header always sits above it
|
|
3
|
-
and nothing ever reaches for 9999. Below --z-raised is ordinary document flow.
|
|
3
|
+
and nothing ever reaches for 9999. Below --z-raised is ordinary document flow.
|
|
4
|
+
|
|
5
|
+
Rungs are 100 apart so a surface can slot a one-off BETWEEN two roles
|
|
6
|
+
(calc(var(--z-modal) + 1)) without inventing a new decade. */
|
|
4
7
|
:root{
|
|
5
8
|
--z-base:0;
|
|
6
|
-
--z-raised:10;
|
|
7
|
-
--z-sticky:200;
|
|
8
|
-
--z-header:300;
|
|
9
|
-
--z-dropdown:400;
|
|
10
|
-
--z-overlay:500;
|
|
11
|
-
--z-modal:600;
|
|
12
|
-
--z-popover:700;
|
|
13
|
-
--z-toast:800;
|
|
9
|
+
--z-raised:10; /* hover-lifted cards, sticky table headers */
|
|
10
|
+
--z-sticky:200; /* pinned section rails */
|
|
11
|
+
--z-header:300; /* the fixed site header */
|
|
12
|
+
--z-dropdown:400; /* menus, selects, comboboxes */
|
|
13
|
+
--z-overlay:500; /* dialog / sheet scrim */
|
|
14
|
+
--z-modal:600; /* dialogs, sheets, command palette */
|
|
15
|
+
--z-popover:700; /* popovers, tooltips (also when anchored in modals) */
|
|
16
|
+
--z-toast:800; /* toasts / notifications — always on top */
|
|
17
|
+
|
|
18
|
+
/* @hanzo/brand spells the top two rungs --z-tooltip and --z-notification.
|
|
19
|
+
Same rungs, so a component written against either vocabulary stacks
|
|
20
|
+
identically. --z-popover / --z-toast are canonical. */
|
|
21
|
+
--z-tooltip:var(--z-popover);
|
|
22
|
+
--z-notification:var(--z-toast);
|
|
14
23
|
}
|
package/ui_kits/.gitkeep
ADDED
|
File without changes
|