@hanzo/design 0.4.3 → 0.4.4

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.
@@ -3,18 +3,24 @@ import React from 'react'
3
3
  /** Native-select build of the @hanzo/ui Select — same chrome, no portal. */
4
4
  export function Select({ options = [], value, onChange, placeholder, style, children, ...rest }) {
5
5
  const [focus, setFocus] = React.useState(false)
6
+ const [hover, setHover] = React.useState(false)
6
7
  return (
7
8
  <span style={{position:'relative',display:'inline-flex',alignItems:'center',width:'100%'}}>
9
+ {/* Same chrome as Input and Textarea — radius, hover lift, focus halo. */}
8
10
  <select
9
11
  value={value}
10
12
  onChange={(e)=>onChange && onChange(e.target.value)}
11
13
  onFocus={()=>setFocus(true)} onBlur={()=>setFocus(false)}
14
+ onMouseEnter={()=>setHover(true)} onMouseLeave={()=>setHover(false)}
12
15
  style={{
13
16
  appearance:'none',width:'100%',height:36,padding:'0 32px 0 12px',
14
17
  fontFamily:'var(--font-sans)',fontSize:'var(--text-control)',color:'var(--foreground)',
15
- background:'var(--surface-2)',border:'1px solid ' + (focus ? 'var(--border-focus)' : 'var(--border-control)'),
16
- borderRadius:'var(--radius-sm)',cursor:'pointer',
17
- transition:'border-color var(--duration-fast) var(--ease-out)',
18
+ background: hover && !focus ? 'var(--surface-3)' : 'var(--surface-2)',
19
+ border:'1px solid ' + (focus ? 'var(--border-focus)' : 'var(--border-control)'),
20
+ borderRadius:'var(--radius-md)',cursor:'pointer',
21
+ outline:'none',
22
+ boxShadow: focus ? 'var(--ring-focus)' : 'none',
23
+ transition:'border-color var(--duration-fast) var(--ease-out), background-color var(--duration-fast) var(--ease-out), box-shadow var(--duration-fast) var(--ease-out)',
18
24
  ...style,
19
25
  }}
20
26
  {...rest}
@@ -1,18 +1,27 @@
1
1
  import React from 'react'
2
2
 
3
+ // Same chrome as Input, to the pixel — radius, hover lift, focus halo. A
4
+ // textarea that rounds one step tighter than the field above it and lights up
5
+ // differently on focus is the kind of drift nobody reports and everybody feels.
3
6
  export function Textarea({ rows = 4, style, ...rest }) {
4
7
  const [focus, setFocus] = React.useState(false)
8
+ const [hover, setHover] = React.useState(false)
5
9
  return (
6
10
  <textarea
7
11
  rows={rows}
8
12
  onFocus={(e)=>{setFocus(true);rest.onFocus&&rest.onFocus(e)}}
9
13
  onBlur={(e)=>{setFocus(false);rest.onBlur&&rest.onBlur(e)}}
14
+ onMouseEnter={()=>setHover(true)}
15
+ onMouseLeave={()=>setHover(false)}
10
16
  style={{
11
17
  width:'100%',padding:'8px 12px',resize:'vertical',
12
18
  fontFamily:'var(--font-sans)',fontSize:'var(--text-control)',lineHeight:'var(--leading-relaxed)',color:'var(--foreground)',
13
- background:'var(--surface-2)',border:'1px solid ' + (focus ? 'var(--border-focus)' : 'var(--border-control)'),
14
- borderRadius:'var(--radius-sm)',
15
- transition:'border-color var(--duration-fast) var(--ease-out)',
19
+ background: hover && !focus ? 'var(--surface-3)' : 'var(--surface-2)',
20
+ border:'1px solid ' + (focus ? 'var(--border-focus)' : 'var(--border-control)'),
21
+ borderRadius:'var(--radius-md)',
22
+ outline:'none',
23
+ boxShadow: focus ? 'var(--ring-focus)' : 'none',
24
+ transition:'border-color var(--duration-fast) var(--ease-out), background-color var(--duration-fast) var(--ease-out), box-shadow var(--duration-fast) var(--ease-out)',
16
25
  ...style,
17
26
  }}
18
27
  {...rest}
@@ -1,7 +1,7 @@
1
- <!-- @dsCard group="Colors" viewport="700x150" name="Borders" subtitle="One alpha ladder, graded by duty — only the focus ring owes a ratio" -->
1
+ <!-- @dsCard group="Colors" viewport="820x160" name="Borders" subtitle="One alpha ladder, graded by duty — only the focus ring owes a ratio" -->
2
2
  <!doctype html><html class="dark"><head><meta charset="utf-8"><title>Borders</title><link rel="stylesheet" href="../styles.css">
3
3
  <style>body{margin:0;padding:16px 20px;background:#000;font-family:var(--font-sans);color:var(--foreground)}
4
- .g{display:flex;gap:10px;flex-wrap:wrap}.sw{flex:1;min-width:64px}.chip{height:56px;border-radius:8px;border:1px solid var(--white-10)}
4
+ .g{display:flex;gap:10px;flex-wrap:wrap}.sw{flex:1;min-width:110px}.chip{height:56px;border-radius:8px;border:1px solid var(--white-10)}
5
5
  .n{margin-top:6px;font-size:10px;font-weight:600;letter-spacing:.06em;color:var(--neutral-300);font-family:var(--font-mono)}
6
6
  .v{font-size:9px;color:var(--neutral-600);font-family:var(--font-mono)}
7
7
  .row{display:flex;align-items:baseline;gap:14px}.mono{font-family:var(--font-mono)}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hanzo/design",
3
- "version": "0.4.3",
3
+ "version": "0.4.4",
4
4
  "packageManager": "pnpm@11.17.0",
5
5
  "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.",
6
6
  "license": "BSD-3-Clause",
package/styles.css CHANGED
@@ -818,7 +818,9 @@
818
818
  UA's border, which puts two typefaces and a foreign blue focus ring inside
819
819
  Hanzo cards on every surface that has not styled its own fields yet. The
820
820
  rules below are what a control looks like here. --border-control, NOT
821
- --border: the boundary IS the affordance, so it is the rung that owes 3:1. */
821
+ --border: a field's edge is its own duty and its own rung, so a surface can
822
+ retune fields without touching every divider on the page. It owes no
823
+ contrast ratio — the whole budget goes to --ring, below. */
822
824
  :where(input,select,textarea,button){font:inherit;color:inherit}
823
825
  :where(input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]),select,textarea){
824
826
  background:var(--surface-2);
@@ -896,7 +898,7 @@
896
898
  every scrolling surface in the product. 6px, no track, thumb on the same
897
899
  hairline ladder as everything else. Both syntaxes ship because they are
898
900
  disjoint — `scrollbar-*` is Firefox and the `::-webkit-*` pseudos are
899
- Chromium/Safari; neither engine reads the other. */
901
+ Chromium/Safari; neither engine reads the other.
900
902
  The thumb is a --white-* rung, so it does not invert; the `.light`
901
903
  restatements sit here rather than in colors.css because a scrollbar is an
902
904
  element default and this file is where element defaults live. Everything
package/tailwind.css CHANGED
@@ -841,7 +841,9 @@
841
841
  UA's border, which puts two typefaces and a foreign blue focus ring inside
842
842
  Hanzo cards on every surface that has not styled its own fields yet. The
843
843
  rules below are what a control looks like here. --border-control, NOT
844
- --border: the boundary IS the affordance, so it is the rung that owes 3:1. */
844
+ --border: a field's edge is its own duty and its own rung, so a surface can
845
+ retune fields without touching every divider on the page. It owes no
846
+ contrast ratio — the whole budget goes to --ring, below. */
845
847
  :where(input,select,textarea,button){font:inherit;color:inherit}
846
848
  :where(input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]),select,textarea){
847
849
  background:var(--surface-2);
@@ -919,7 +921,7 @@
919
921
  every scrolling surface in the product. 6px, no track, thumb on the same
920
922
  hairline ladder as everything else. Both syntaxes ship because they are
921
923
  disjoint — `scrollbar-*` is Firefox and the `::-webkit-*` pseudos are
922
- Chromium/Safari; neither engine reads the other. */
924
+ Chromium/Safari; neither engine reads the other.
923
925
  The thumb is a --white-* rung, so it does not invert; the `.light`
924
926
  restatements sit here rather than in colors.css because a scrollbar is an
925
927
  element default and this file is where element defaults live. Everything
package/tokens/base.css CHANGED
@@ -83,7 +83,9 @@
83
83
  UA's border, which puts two typefaces and a foreign blue focus ring inside
84
84
  Hanzo cards on every surface that has not styled its own fields yet. The
85
85
  rules below are what a control looks like here. --border-control, NOT
86
- --border: the boundary IS the affordance, so it is the rung that owes 3:1. */
86
+ --border: a field's edge is its own duty and its own rung, so a surface can
87
+ retune fields without touching every divider on the page. It owes no
88
+ contrast ratio — the whole budget goes to --ring, below. */
87
89
  :where(input,select,textarea,button){font:inherit;color:inherit}
88
90
  :where(input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]),select,textarea){
89
91
  background:var(--surface-2);
@@ -161,7 +163,7 @@
161
163
  every scrolling surface in the product. 6px, no track, thumb on the same
162
164
  hairline ladder as everything else. Both syntaxes ship because they are
163
165
  disjoint — `scrollbar-*` is Firefox and the `::-webkit-*` pseudos are
164
- Chromium/Safari; neither engine reads the other. */
166
+ Chromium/Safari; neither engine reads the other.
165
167
  The thumb is a --white-* rung, so it does not invert; the `.light`
166
168
  restatements sit here rather than in colors.css because a scrollbar is an
167
169
  element default and this file is where element defaults live. Everything