@brightweblabs/ui 1.0.2 → 1.1.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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@brightweblabs/ui",
3
3
  "private": false,
4
- "version": "1.0.2",
4
+ "version": "1.1.0",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
7
7
  "files": [
@@ -29,6 +29,7 @@
29
29
  "./calendar": "./src/components/calendar.tsx",
30
30
  "./card": "./src/components/card.tsx",
31
31
  "./chart": "./src/components/chart.tsx",
32
+ "./checkbox": "./src/components/checkbox.tsx",
32
33
  "./dropdown-menu": "./src/components/dropdown-menu.tsx",
33
34
  "./empty-state": "./src/components/empty-state.tsx",
34
35
  "./field": "./src/components/field.tsx",
@@ -58,30 +59,32 @@
58
59
  "./tooltip": "./src/components/tooltip.tsx"
59
60
  },
60
61
  "dependencies": {
61
- "@radix-ui/react-alert-dialog": "^1.1.15",
62
- "@radix-ui/react-avatar": "^1.1.10",
63
- "@radix-ui/react-dialog": "^1.1.15",
64
- "@radix-ui/react-dropdown-menu": "^2.1.16",
65
- "@radix-ui/react-label": "^2.1.8",
66
- "@radix-ui/react-popover": "^1.1.15",
67
- "@radix-ui/react-separator": "^1.1.8",
68
- "@radix-ui/react-slot": "^1.2.4",
69
- "@radix-ui/react-tooltip": "^1.2.8",
62
+ "@radix-ui/react-alert-dialog": "^1.1.21",
63
+ "@radix-ui/react-avatar": "^1.2.4",
64
+ "@radix-ui/react-dialog": "^1.1.21",
65
+ "@radix-ui/react-dropdown-menu": "^2.1.22",
66
+ "@radix-ui/react-label": "^2.1.13",
67
+ "@radix-ui/react-popover": "^1.1.21",
68
+ "@radix-ui/react-separator": "^1.1.13",
69
+ "@radix-ui/react-slot": "^1.3.1",
70
+ "@radix-ui/react-tooltip": "^1.2.14",
70
71
  "class-variance-authority": "^0.7.1",
71
72
  "clsx": "^2.1.1",
72
- "date-fns": "^4.1.0",
73
- "radix-ui": "^1.4.3",
74
- "react-day-picker": "^9.13.2",
73
+ "react-day-picker": "^9.14.0",
75
74
  "react-international-phone": "^4.8.0",
76
75
  "recharts": "^2.15.4",
77
76
  "sonner": "^2.0.7",
78
- "tailwind-merge": "^3.4.0",
79
- "@brightweblabs/theme": "0.3.0"
77
+ "tailwind-merge": "^3.6.0",
78
+ "@brightweblabs/theme": "0.4.0"
80
79
  },
81
80
  "peerDependencies": {
82
81
  "lucide-react": "^1.8.0",
83
82
  "next": "^16.0.0",
84
83
  "react": "^19.0.0",
85
84
  "react-dom": "^19.0.0"
85
+ },
86
+ "devDependencies": {
87
+ "react": "19.2.4",
88
+ "react-dom": "19.2.4"
86
89
  }
87
90
  }
@@ -1,9 +1,10 @@
1
1
  import type { ButtonHTMLAttributes } from "react";
2
2
 
3
+ import { buttonVariants } from "./button-variants";
3
4
  import { cn } from "../lib/utils";
4
5
 
5
6
  export const actionClassName =
6
- "inline-flex shrink-0 items-center justify-center gap-1.5 rounded-full border border-[color:var(--brand-accent)] bg-[color:var(--brand-accent)] px-3.5 py-1.5 text-ui-meta font-semibold text-[color:var(--accent-foreground)] transition-colors hover:bg-[color:var(--brand-accent-soft)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:size-3.5";
7
+ buttonVariants({ variant: "brand", size: "sm", className: "gap-1.5 rounded-full px-3.5 text-ui-meta [&_svg]:size-3.5" });
7
8
 
8
9
  export type ActionButtonProps = ButtonHTMLAttributes<HTMLButtonElement>;
9
10
 
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import * as React from "react";
2
4
  import { cva, type VariantProps } from "class-variance-authority";
3
5
  import { Slot } from "@radix-ui/react-slot";
@@ -1,6 +1,8 @@
1
+ "use client";
2
+
1
3
  import * as React from "react"
2
4
  import { ChevronRight, MoreHorizontal } from "lucide-react"
3
- import { Slot } from "radix-ui"
5
+ import { Slot } from "@radix-ui/react-slot"
4
6
 
5
7
  import { cn } from "../lib/utils"
6
8
 
@@ -38,7 +40,7 @@ function BreadcrumbLink({
38
40
  }: React.ComponentProps<"a"> & {
39
41
  asChild?: boolean
40
42
  }) {
41
- const Comp = asChild ? Slot.Root : "a"
43
+ const Comp = asChild ? Slot : "a"
42
44
 
43
45
  return (
44
46
  <Comp
@@ -1,25 +1,25 @@
1
1
  import { cva, type VariantProps } from "class-variance-authority";
2
2
 
3
3
  const buttonVariants = cva(
4
- "inline-flex shrink-0 items-center justify-center gap-2 whitespace-nowrap rounded-[var(--radius-control)] text-ui-body !font-semibold transition-all outline-none disabled:pointer-events-none disabled:opacity-50 focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
4
+ "inline-flex shrink-0 items-center justify-center gap-2 whitespace-nowrap rounded-[var(--radius-control)] text-ui-body !font-semibold transition-[color,background-color,border-color,box-shadow,transform,filter,text-decoration-color] motion-reduce:transition-none outline-none disabled:pointer-events-none disabled:opacity-50 focus-visible:border-current focus-visible:ring-[3px] focus-visible:ring-current aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
5
5
  {
6
6
  variants: {
7
7
  variant: {
8
8
  default: "bg-primary text-primary-foreground hover:bg-primary/90",
9
9
  accent:
10
- "relative isolate h-auto overflow-hidden !rounded-none px-0 py-0 font-display !font-bold text-[length:var(--text-ui-body)] leading-[1.6] text-foreground [padding:var(--space-button-y)_var(--space-button-x)] hover:text-foreground focus-visible:border-transparent focus-visible:ring-2 focus-visible:ring-[color:var(--brand-accent)] focus-visible:ring-offset-2",
10
+ "relative isolate h-auto overflow-hidden !rounded-none px-0 py-0 font-display !font-bold text-[length:var(--text-ui-body)] leading-[var(--type-leading-160)] text-foreground [padding:var(--space-button-y)_var(--space-button-x)] hover:text-foreground focus-visible:border-transparent focus-visible:ring-2 focus-visible:ring-current focus-visible:ring-offset-2",
11
11
  accentLink:
12
- "h-auto min-h-0 gap-[var(--space-sm)] !rounded-none px-0 py-0 font-display !font-bold text-[length:var(--text-ui-subhead)] leading-[1.66] text-[color:var(--brand-accent)] no-underline hover:opacity-70 hover:no-underline",
12
+ "h-auto min-h-0 gap-[var(--space-sm)] !rounded-none px-0 py-0 font-display !font-bold text-[length:var(--text-ui-subhead)] leading-[var(--type-leading-166)] text-[color:var(--foreground-accent-link)] no-underline hover:underline",
13
13
  destructive:
14
- "bg-destructive text-destructive-foreground hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40",
14
+ "bg-destructive text-destructive-foreground hover:brightness-[1.05]",
15
15
  brand:
16
- "bg-[color:var(--brand-accent)] text-[color:var(--accent-foreground)] !font-extrabold shadow-[var(--shadow-accent-control)] hover:brightness-[1.05]",
16
+ "bg-[color:var(--surface-button-brand)] text-[color:var(--accent-foreground)] !font-extrabold shadow-[var(--shadow-accent-control)] hover:brightness-[1.05]",
17
17
  soft:
18
18
  "border border-[color:var(--border)] bg-[color:var(--surface-button-soft)] text-foreground hover:border-[color:var(--border-button-soft-hover)] hover:bg-[color:var(--surface-button-soft-hover)]",
19
19
  outline:
20
- "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:hover:bg-input/50",
20
+ "border bg-background shadow-xs hover:bg-[color:var(--surface-button-soft-hover)] hover:text-foreground dark:bg-input/30 dark:hover:bg-[color:var(--surface-button-soft-hover)]",
21
21
  secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
22
- ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
22
+ ghost: "hover:bg-[color:var(--surface-button-soft-hover)] hover:text-foreground",
23
23
  link: "text-foreground underline-offset-4 transition-colors hover:text-primary hover:underline",
24
24
  },
25
25
  size: {
@@ -0,0 +1,10 @@
1
+ import * as React from "react";
2
+
3
+ type CheckboxProps = Omit<React.ComponentProps<"input">, "type">;
4
+
5
+ function Checkbox(props: CheckboxProps) {
6
+ return <input type="checkbox" {...props} />;
7
+ }
8
+
9
+ export { Checkbox };
10
+ export type { CheckboxProps };
@@ -17,8 +17,8 @@ export function EmptyState({ icon: Icon, title, hint, action, className, ...prop
17
17
  <Icon className="size-5 text-muted-foreground" aria-hidden="true" />
18
18
  </div>
19
19
  <div>
20
- <p className="text-ui-body font-semibold">{title}</p>
21
- {hint ? <p className="mt-1 text-ui-meta">{hint}</p> : null}
20
+ <p className="text-ui-body font-semibold text-foreground">{title}</p>
21
+ {hint ? <p className="mt-1 text-ui-meta text-muted-foreground">{hint}</p> : null}
22
22
  </div>
23
23
  {action ? <div className="mt-1">{action}</div> : null}
24
24
  </div>
@@ -8,7 +8,7 @@ function Input({ className, type, ...props }: React.ComponentProps<"input">) {
8
8
  type={type}
9
9
  data-slot="input"
10
10
  className={cn(
11
- "file:text-foreground placeholder:text-foreground/30 selection:bg-primary selection:text-primary-foreground h-11 w-full min-w-0 rounded-xl border border-foreground/15 bg-foreground/5 px-3 py-1 text-base text-foreground shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-input file:text-sm file:font-semibold disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
11
+ "file:text-foreground placeholder:text-foreground-muted-accessible selection:bg-primary selection:text-primary-foreground h-11 w-full min-w-0 rounded-xl border border-foreground/15 bg-foreground/5 px-3 py-1 text-base text-foreground shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-input file:text-sm file:font-semibold disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
12
12
  "focus:border-primary focus:ring-primary/40 focus:ring-[3px]",
13
13
  "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
14
14
  className,
@@ -8,10 +8,19 @@ import { Input } from "./input";
8
8
 
9
9
  interface PasswordInputProps extends Omit<React.ComponentProps<"input">, "type"> {
10
10
  showToggle?: boolean;
11
+ showPasswordLabel?: string;
12
+ hidePasswordLabel?: string;
11
13
  }
12
14
 
13
15
  const PasswordInput = React.forwardRef<HTMLInputElement, PasswordInputProps>(
14
- ({ className, disabled, showToggle = true, ...props }, ref) => {
16
+ ({
17
+ className,
18
+ disabled,
19
+ showToggle = true,
20
+ showPasswordLabel = "Show password",
21
+ hidePasswordLabel = "Hide password",
22
+ ...props
23
+ }, ref) => {
15
24
  const [showPassword, setShowPassword] = React.useState(false);
16
25
  return (
17
26
  <div className="relative w-full">
@@ -22,7 +31,7 @@ const PasswordInput = React.forwardRef<HTMLInputElement, PasswordInputProps>(
22
31
  onClick={() => setShowPassword((current) => !current)}
23
32
  className="absolute right-2 top-1/2 flex size-8 -translate-y-1/2 items-center justify-center rounded-[var(--radius)] text-foreground/40 transition-colors hover:text-foreground/70 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40 disabled:pointer-events-none disabled:opacity-50"
24
33
  disabled={disabled}
25
- aria-label={showPassword ? "Hide password" : "Show password"}
34
+ aria-label={showPassword ? hidePasswordLabel : showPasswordLabel}
26
35
  >
27
36
  {showPassword ? <EyeOff className="size-4" /> : <Eye className="size-4" />}
28
37
  </button>
@@ -1,17 +1,15 @@
1
1
  "use client";
2
2
 
3
- import { useEffect, useRef, useState } from "react";
3
+ import { useEffect, useId, useRef, useState, type InputHTMLAttributes, type KeyboardEvent } from "react";
4
4
  import { defaultCountries, parseCountry, usePhoneInput } from "react-international-phone";
5
5
  import type { CountryIso2 } from "react-international-phone";
6
6
 
7
7
  import { cn } from "../lib/utils";
8
8
 
9
- interface PhoneInputProps {
9
+ interface PhoneInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "className" | "defaultValue" | "onChange" | "type" | "value"> {
10
10
  value: string;
11
11
  onChange: (e164: string) => void;
12
- onBlur?: () => void;
13
12
  defaultCountry?: CountryIso2;
14
- disabled?: boolean;
15
13
  className?: string;
16
14
  }
17
15
 
@@ -25,12 +23,15 @@ function flagEmoji(iso2: string) {
25
23
  return iso2.toUpperCase().split("").map((character) => String.fromCodePoint(0x1f1e6 - 0x41 + character.charCodeAt(0))).join("");
26
24
  }
27
25
 
28
- export function PhoneInput({ value, onChange, onBlur, defaultCountry = "pt", disabled = false, className }: PhoneInputProps) {
26
+ export function PhoneInput({ value, onChange, defaultCountry = "pt", disabled = false, className, placeholder = "912 345 678", ...inputProps }: PhoneInputProps) {
29
27
  const inputRef = useRef<HTMLInputElement | null>(null);
30
28
  const triggerRef = useRef<HTMLButtonElement | null>(null);
31
29
  const dropdownRef = useRef<HTMLDivElement | null>(null);
30
+ const optionRefs = useRef<Array<HTMLButtonElement | null>>([]);
31
+ const countryListId = useId();
32
32
  const [open, setOpen] = useState(false);
33
33
  const [search, setSearch] = useState("");
34
+ const [activeIndex, setActiveIndex] = useState(0);
34
35
 
35
36
  const { inputValue, handlePhoneValueChange, country, setCountry } = usePhoneInput({
36
37
  defaultCountry,
@@ -52,19 +53,76 @@ export function PhoneInput({ value, onChange, onBlur, defaultCountry = "pt", dis
52
53
  return () => document.removeEventListener("mousedown", handleOutsideClick);
53
54
  }, [open]);
54
55
 
56
+ useEffect(() => {
57
+ if (open) optionRefs.current[activeIndex]?.scrollIntoView({ block: "nearest" });
58
+ }, [activeIndex, open]);
59
+
55
60
  const filteredCountries = search
56
61
  ? orderedCountries.filter((candidate) => candidate.name.toLowerCase().includes(search.toLowerCase()) || `+${candidate.dialCode}`.includes(search))
57
62
  : orderedCountries;
58
63
 
64
+ const closePicker = (restoreFocus = false) => {
65
+ setOpen(false);
66
+ setSearch("");
67
+ if (restoreFocus) triggerRef.current?.focus();
68
+ };
69
+
70
+ const openPicker = (direction: "first" | "last" | "selected" = "selected") => {
71
+ const selectedIndex = orderedCountries.findIndex((candidate) => candidate.iso2 === country.iso2);
72
+ setSearch("");
73
+ setActiveIndex(direction === "first" ? 0 : direction === "last" ? orderedCountries.length - 1 : Math.max(0, selectedIndex));
74
+ setOpen(true);
75
+ };
76
+
77
+ const selectCountry = (index: number) => {
78
+ const candidate = filteredCountries[index];
79
+ if (!candidate) return;
80
+ setCountry(candidate.iso2);
81
+ setOpen(false);
82
+ setSearch("");
83
+ inputRef.current?.focus();
84
+ };
85
+
86
+ const handlePickerKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
87
+ if (event.key === "Escape") {
88
+ event.preventDefault();
89
+ closePicker(true);
90
+ return;
91
+ }
92
+ if (event.key === "ArrowDown" || event.key === "ArrowUp") {
93
+ event.preventDefault();
94
+ const direction = event.key === "ArrowDown" ? 1 : -1;
95
+ setActiveIndex((current) => filteredCountries.length === 0
96
+ ? 0
97
+ : (current + direction + filteredCountries.length) % filteredCountries.length);
98
+ return;
99
+ }
100
+ if (event.key === "Enter") {
101
+ event.preventDefault();
102
+ selectCountry(activeIndex);
103
+ }
104
+ };
105
+
59
106
  return (
60
107
  <div className={cn("relative flex items-center", className)}>
61
108
  <button
62
109
  ref={triggerRef}
63
110
  type="button"
64
111
  disabled={disabled}
65
- onClick={() => setOpen((current) => !current)}
112
+ onClick={() => open ? closePicker() : openPicker()}
113
+ onKeyDown={(event) => {
114
+ if (event.key === "ArrowDown" || event.key === "ArrowUp") {
115
+ event.preventDefault();
116
+ openPicker(event.key === "ArrowDown" ? "first" : "last");
117
+ } else if (event.key === "Escape" && open) {
118
+ event.preventDefault();
119
+ closePicker(true);
120
+ }
121
+ }}
66
122
  className="flex h-7 items-center gap-1 border-0 bg-transparent pr-1.5 text-sm disabled:pointer-events-none disabled:opacity-100"
123
+ aria-controls={countryListId}
67
124
  aria-expanded={open}
125
+ aria-haspopup="listbox"
68
126
  aria-label="Choose country code"
69
127
  >
70
128
  <span className="text-base leading-none">{flagEmoji(country.iso2)}</span>
@@ -76,37 +134,54 @@ export function PhoneInput({ value, onChange, onBlur, defaultCountry = "pt", dis
76
134
  {open ? (
77
135
  <div ref={dropdownRef} className="absolute left-0 top-full z-50 mt-1.5 w-56 rounded-[var(--radius-card)] border border-[color:var(--hairline-strong)] bg-popover text-popover-foreground shadow-[var(--shadow-phone-dropdown)]">
78
136
  <div className="border-b border-[color:var(--hairline)] px-2.5 py-2">
79
- <input autoFocus value={search} onChange={(event) => setSearch(event.target.value)} placeholder="Search…" className="w-full border-0 bg-transparent text-ui-meta text-foreground outline-none placeholder:text-foreground/40" />
137
+ <input
138
+ autoFocus
139
+ role="combobox"
140
+ aria-autocomplete="list"
141
+ aria-controls={countryListId}
142
+ aria-expanded="true"
143
+ aria-label="Search countries"
144
+ aria-activedescendant={filteredCountries[activeIndex] ? `${countryListId}-${filteredCountries[activeIndex].iso2}` : undefined}
145
+ value={search}
146
+ onChange={(event) => {
147
+ setSearch(event.target.value);
148
+ setActiveIndex(0);
149
+ }}
150
+ onKeyDown={handlePickerKeyDown}
151
+ placeholder="Search…"
152
+ className="w-full border-0 bg-transparent text-ui-meta text-foreground outline-none placeholder:text-foreground-muted-accessible"
153
+ />
80
154
  </div>
81
- <div className="max-h-48 overflow-y-auto rounded-b-[var(--radius-card)] p-1">
82
- {filteredCountries.map((candidate) => (
155
+ <div id={countryListId} role="listbox" aria-label="Country calling codes" className="max-h-48 overflow-y-auto rounded-b-[var(--radius-card)] p-1">
156
+ {filteredCountries.map((candidate, index) => (
83
157
  <button
84
158
  key={candidate.iso2}
159
+ id={`${countryListId}-${candidate.iso2}`}
160
+ ref={(node) => { optionRefs.current[index] = node; }}
85
161
  type="button"
162
+ role="option"
163
+ aria-selected={country.iso2 === candidate.iso2}
164
+ tabIndex={-1}
86
165
  onMouseDown={(event) => event.preventDefault()}
87
- onClick={() => {
88
- setCountry(candidate.iso2);
89
- setOpen(false);
90
- setSearch("");
91
- inputRef.current?.focus();
92
- }}
166
+ onMouseEnter={() => setActiveIndex(index)}
167
+ onClick={() => selectCountry(index)}
93
168
  className={cn(
94
169
  "flex w-full items-center gap-2 rounded-[var(--radius)] px-2 py-1 text-left transition-colors hover:bg-[color:var(--elevate-2)]",
95
- country.iso2 === candidate.iso2 && "bg-[color:var(--elevate-3)] text-[color:var(--brand-accent)]",
170
+ (country.iso2 === candidate.iso2 || activeIndex === index) && "bg-[color:var(--elevate-3)] text-[color:var(--brand-accent)]",
96
171
  )}
97
172
  >
98
173
  <span className="text-sm leading-none">{flagEmoji(candidate.iso2)}</span>
99
174
  <span className="flex-1 truncate text-ui-label normal-case tracking-normal text-foreground">{candidate.name}</span>
100
- <span className="shrink-0 text-ui-micro text-foreground/40">+{candidate.dialCode}</span>
175
+ <span className="shrink-0 text-ui-micro text-foreground-muted-accessible">+{candidate.dialCode}</span>
101
176
  </button>
102
177
  ))}
103
- {filteredCountries.length === 0 ? <p className="px-2 py-2 text-ui-meta text-foreground/45">No results</p> : null}
178
+ {filteredCountries.length === 0 ? <p className="px-2 py-2 text-ui-meta text-foreground-muted-accessible">No results</p> : null}
104
179
  </div>
105
180
  </div>
106
181
  ) : null}
107
182
 
108
183
  <span className="h-4 w-px shrink-0 bg-foreground/15" />
109
- <input ref={inputRef} value={inputValue} onChange={handlePhoneValueChange} onBlur={onBlur} disabled={disabled} placeholder="912 345 678" type="tel" className="h-7 flex-1 border-0 bg-transparent pl-2 text-sm text-foreground outline-none placeholder:text-foreground/35 disabled:pointer-events-none disabled:opacity-100" />
184
+ <input {...inputProps} ref={inputRef} value={inputValue} onChange={handlePhoneValueChange} disabled={disabled} placeholder={placeholder} type="tel" className="h-7 flex-1 border-0 bg-transparent pl-2 text-sm text-foreground outline-none placeholder:text-foreground-muted-accessible disabled:pointer-events-none disabled:opacity-100" />
110
185
  </div>
111
186
  );
112
187
  }
@@ -65,7 +65,7 @@ function SheetOverlay({
65
65
  <SheetPrimitive.Overlay
66
66
  data-slot="sheet-overlay"
67
67
  className={cn(
68
- "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-[color:var(--surface-overlay)]",
68
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-[color:var(--surface-overlay)] motion-reduce:animate-none motion-reduce:transition-none",
69
69
  className
70
70
  )}
71
71
  {...props}
@@ -89,7 +89,7 @@ function SheetContent({
89
89
  <SheetPrimitive.Content
90
90
  data-slot="sheet-content"
91
91
  className={cn(
92
- "bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
92
+ "bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500 motion-reduce:animate-none motion-reduce:transform-none motion-reduce:transition-none",
93
93
  side === "right" &&
94
94
  "data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-[var(--sheet-width)]",
95
95
  side === "left" &&
@@ -9,7 +9,7 @@ export type StatTileProps = HTMLAttributes<HTMLDivElement> & {
9
9
  export function StatTile({ label, children, className, ...props }: StatTileProps) {
10
10
  return (
11
11
  <div className={cn("stat-cell", className)} {...props}>
12
- <p className="text-ui-label">{label}</p>
12
+ <p className="text-ui-label text-muted-foreground">{label}</p>
13
13
  <div className="mt-3">{children}</div>
14
14
  </div>
15
15
  );
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import { Slot } from "@radix-ui/react-slot";
2
4
  import { tintPill } from "@brightweblabs/theme/tint";
3
5
  import type { ComponentProps, CSSProperties, ReactNode } from "react";
package/src/index.ts CHANGED
@@ -29,6 +29,8 @@ export * from "./components/card";
29
29
  export * from "./components/chart";
30
30
  export * from "./components/breadcrumb";
31
31
  export * from "./components/calendar";
32
+ export { Checkbox } from "./components/checkbox";
33
+ export type { CheckboxProps } from "./components/checkbox";
32
34
  export { Label } from "./components/label";
33
35
  export * from "./components/pagination";
34
36
  export * from "./components/popover";