diff --git a/.changeset/hover-border-strong.md b/.changeset/hover-border-strong.md new file mode 100644 index 00000000..7beeaf40 --- /dev/null +++ b/.changeset/hover-border-strong.md @@ -0,0 +1,6 @@ +--- +'@surfnet/curve-react': minor +'@surfnet/curve-angular': minor +--- + +Form fields now show a `--border-strong` border on hover: Combobox (chips), Input, Input Group, Input OTP, Select and Textarea. Focus, active and invalid states still take precedence, and disabled fields don't react to hover. diff --git a/packages/angular/src/lib/ui/combobox/src/lib/hlm-combobox.css b/packages/angular/src/lib/ui/combobox/src/lib/hlm-combobox.css index 78bc0ac8..59cdabc8 100644 --- a/packages/angular/src/lib/ui/combobox/src/lib/hlm-combobox.css +++ b/packages/angular/src/lib/ui/combobox/src/lib/hlm-combobox.css @@ -80,6 +80,10 @@ background-color: color-mix(in oklab, var(--input) 30%, transparent); } +.curve-combobox-chips:hover:where(:not(:has(:disabled))) { + border-color: var(--border-strong); +} + .curve-combobox-chips:focus-within { border-color: var(--ring); box-shadow: diff --git a/packages/angular/src/lib/ui/input-group/src/lib/hlm-input-group.css b/packages/angular/src/lib/ui/input-group/src/lib/hlm-input-group.css index 335bd00e..47f1f40b 100644 --- a/packages/angular/src/lib/ui/input-group/src/lib/hlm-input-group.css +++ b/packages/angular/src/lib/ui/input-group/src/lib/hlm-input-group.css @@ -24,6 +24,10 @@ background-color: color-mix(in oklab, var(--input) 30%, transparent); } +.curve-input-group:hover:where(:not(:has(:disabled))) { + border-color: var(--border-strong); +} + .curve-input-group:has([data-slot='input-group-control']:focus-visible) { border-color: var(--ring); box-shadow: @@ -45,6 +49,7 @@ } /* Inside a combobox popup the search field shows no focus ring. */ +[data-slot='combobox-content'] .curve-input-group:hover, [data-slot='combobox-content'] .curve-input-group:focus-within { border-color: inherit; box-shadow: var(--shadow-xs, 0 1px 2px 0 rgb(0 0 0 / 0.05)); diff --git a/packages/angular/src/lib/ui/input-otp/src/lib/hlm-input-otp.css b/packages/angular/src/lib/ui/input-otp/src/lib/hlm-input-otp.css index 1e8d0602..bf83559b 100644 --- a/packages/angular/src/lib/ui/input-otp/src/lib/hlm-input-otp.css +++ b/packages/angular/src/lib/ui/input-otp/src/lib/hlm-input-otp.css @@ -42,6 +42,10 @@ transition: all 150ms cubic-bezier(0.4, 0, 0.2, 1); } +.curve-input-otp-slot:disabled { + cursor: not-allowed; +} + .curve-input-otp-slot:first-child { border-inline-start-width: 1px; border-start-start-radius: var(--radius-md, calc(var(--radius, 0.625rem) * 0.8)); @@ -61,6 +65,11 @@ border-color: var(--destructive); } +/* Hover; the active state below overrides it. */ +.curve-input-otp-slot:hover:not([data-matches-spartan-invalid='true']) { + border-color: var(--border-strong); +} + /* The slot the next character goes into. */ .curve-input-otp-slot:has(brn-input-otp-slot[data-active='true']) { z-index: 10; diff --git a/packages/angular/src/lib/ui/input/src/lib/hlm-input.css b/packages/angular/src/lib/ui/input/src/lib/hlm-input.css index fb987bff..479f947a 100644 --- a/packages/angular/src/lib/ui/input/src/lib/hlm-input.css +++ b/packages/angular/src/lib/ui/input/src/lib/hlm-input.css @@ -24,6 +24,10 @@ color: var(--muted-foreground); } +.curve-input:hover:where(:not(:disabled)) { + border-color: var(--border-strong); +} + .curve-input:focus { border-color: var(--ring); } diff --git a/packages/angular/src/lib/ui/select/src/lib/hlm-select.css b/packages/angular/src/lib/ui/select/src/lib/hlm-select.css index de83ee5c..8a5adb99 100644 --- a/packages/angular/src/lib/ui/select/src/lib/hlm-select.css +++ b/packages/angular/src/lib/ui/select/src/lib/hlm-select.css @@ -45,6 +45,10 @@ background-color: color-mix(in oklab, var(--input) 50%, transparent); } +.curve-select-trigger:hover:where(:not(:disabled)) { + border-color: var(--border-strong); +} + .curve-select-trigger:focus { border-color: var(--ring); } diff --git a/packages/angular/src/lib/ui/textarea/src/lib/hlm-textarea.css b/packages/angular/src/lib/ui/textarea/src/lib/hlm-textarea.css index 5157849c..ff2e2812 100644 --- a/packages/angular/src/lib/ui/textarea/src/lib/hlm-textarea.css +++ b/packages/angular/src/lib/ui/textarea/src/lib/hlm-textarea.css @@ -21,6 +21,10 @@ color: var(--muted-foreground); } +.curve-textarea:hover:where(:not(:disabled)) { + border-color: var(--border-strong); +} + .curve-textarea:focus { border-color: var(--ring); } diff --git a/packages/react/src/components/ui/combobox/combobox.module.css b/packages/react/src/components/ui/combobox/combobox.module.css index 7968a137..a7b578a6 100644 --- a/packages/react/src/components/ui/combobox/combobox.module.css +++ b/packages/react/src/components/ui/combobox/combobox.module.css @@ -158,6 +158,10 @@ box-shadow 150ms ease; } +.chips:hover:where(:not(:has(:disabled))) { + border-color: var(--border-strong); +} + .chips:focus-within { border-color: var(--ring); box-shadow: 0 0 0 3px color-mix(in oklch, var(--ring) 32%, transparent); diff --git a/packages/react/src/components/ui/input-group/input-group.module.css b/packages/react/src/components/ui/input-group/input-group.module.css index 6b760c1e..4b6e0a74 100644 --- a/packages/react/src/components/ui/input-group/input-group.module.css +++ b/packages/react/src/components/ui/input-group/input-group.module.css @@ -18,6 +18,10 @@ background-color: color-mix(in oklch, var(--input) 30%, transparent); } +.group:hover:where(:not(:has(:disabled))) { + border-color: var(--border-strong); +} + .group:has([data-slot='input-group-control']:focus-visible) { border-color: var(--ring); box-shadow: 0 0 0 3px color-mix(in oklch, var(--ring) 32%, transparent); @@ -42,6 +46,7 @@ height: auto; } +:global([data-slot='combobox-content']) .group:hover, :global([data-slot='combobox-content']) .group:focus-within { border-color: inherit; box-shadow: none; diff --git a/packages/react/src/components/ui/input-otp/input-otp.module.css b/packages/react/src/components/ui/input-otp/input-otp.module.css index 71d67d92..0287a412 100644 --- a/packages/react/src/components/ui/input-otp/input-otp.module.css +++ b/packages/react/src/components/ui/input-otp/input-otp.module.css @@ -20,10 +20,6 @@ opacity: 0.5; } -.input { - cursor: not-allowed; -} - .input:disabled { cursor: not-allowed; } @@ -77,6 +73,10 @@ background-color: color-mix(in oklch, var(--input) 30%, transparent); } +.slot:hover:not([aria-invalid='true']) { + border-color: var(--border-strong); +} + .slot[data-active='true'] { z-index: 10; border-color: var(--ring); diff --git a/packages/react/src/components/ui/input/input.module.css b/packages/react/src/components/ui/input/input.module.css index 0c5e3927..e63ed331 100644 --- a/packages/react/src/components/ui/input/input.module.css +++ b/packages/react/src/components/ui/input/input.module.css @@ -19,6 +19,10 @@ color: var(--muted-foreground); } +.input:hover:where(:not(:disabled)) { + border-color: var(--border-strong); +} + .input:focus { border-color: var(--ring); } diff --git a/packages/react/src/components/ui/select/select.module.css b/packages/react/src/components/ui/select/select.module.css index bd02b6dc..ce76c968 100644 --- a/packages/react/src/components/ui/select/select.module.css +++ b/packages/react/src/components/ui/select/select.module.css @@ -38,6 +38,10 @@ height: calc(var(--spacing, 0.25rem) * 8); } +.trigger:hover:where(:not(:disabled)) { + border-color: var(--border-strong); +} + .trigger:focus { border-color: var(--ring); } diff --git a/packages/react/src/components/ui/textarea/textarea.module.css b/packages/react/src/components/ui/textarea/textarea.module.css index 9c940e02..b1cc7b5a 100644 --- a/packages/react/src/components/ui/textarea/textarea.module.css +++ b/packages/react/src/components/ui/textarea/textarea.module.css @@ -20,6 +20,10 @@ color: var(--muted-foreground); } +.textarea:hover:where(:not(:disabled)) { + border-color: var(--border-strong); +} + .textarea:focus { border-color: var(--ring); }