@hanzo/design 0.1.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/tokens/colors.css CHANGED
@@ -51,7 +51,12 @@
51
51
  --destructive-foreground:#f5f5f5;
52
52
  --border:#1f1f1f;
53
53
  --input:#1f1f1f;
54
- --ring:#333333;
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-700);
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
- --ring:#d4d4d4;
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-300);
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);
@@ -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
- hanzo.ai loads them via next/font/google (Geist, Geist_Mono); here they come
3
- from the same Google Fonts source, so the rendered face is identical. */
4
- @import url("https://fonts.googleapis.com/css2?family=Geist:wght@100..900&family=Geist+Mono:wght@100..900&display=swap");
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;
@@ -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; /* hover-lifted cards, sticky table headers */
7
- --z-sticky:200; /* pinned section rails */
8
- --z-header:300; /* the fixed site header */
9
- --z-dropdown:400; /* menus, selects, comboboxes */
10
- --z-overlay:500; /* dialog / sheet scrim */
11
- --z-modal:600; /* dialogs, sheets, command palette */
12
- --z-popover:700; /* popovers, tooltips (also when anchored in modals)*/
13
- --z-toast:800; /* toasts / notifications — always on top */
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
  }
File without changes