@nextclaw/ui 0.5.7 → 0.5.9

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.
@@ -142,8 +142,8 @@ function matchInstalledSearch(
142
142
 
143
143
  function getAvatarColor(text: string) {
144
144
  const colors = [
145
- 'bg-blue-500', 'bg-indigo-500', 'bg-purple-500', 'bg-pink-500',
146
- 'bg-rose-500', 'bg-orange-500', 'bg-emerald-500', 'bg-teal-500', 'bg-cyan-500'
145
+ 'bg-amber-600', 'bg-orange-500', 'bg-yellow-600', 'bg-emerald-600',
146
+ 'bg-teal-600', 'bg-cyan-600', 'bg-stone-600', 'bg-rose-500', 'bg-violet-500'
147
147
  ];
148
148
  let hash = 0;
149
149
  for (let i = 0; i < text.length; i++) {
@@ -157,7 +157,7 @@ function ItemIcon({ name, fallback }: { name?: string; fallback: string }) {
157
157
  const letters = displayName.substring(0, 2).toUpperCase();
158
158
  const colorClass = getAvatarColor(displayName);
159
159
  return (
160
- <div className={cn("flex items-center justify-center w-11 h-11 rounded-2xl text-white font-bold text-base shrink-0 shadow-sm", colorClass)}>
160
+ <div className={cn("flex items-center justify-center w-10 h-10 rounded-xl text-white font-semibold text-sm shrink-0", colorClass)}>
161
161
  {letters}
162
162
  </div>
163
163
  );
@@ -173,7 +173,7 @@ function FilterPanel(props: {
173
173
  onSortChange: (value: MarketplaceSort) => void;
174
174
  }) {
175
175
  return (
176
- <div className="bg-white border border-gray-200 rounded-xl p-4 mb-4">
176
+ <div className="mb-4">
177
177
  <div className="flex gap-3 items-center">
178
178
  <div className="flex-1 min-w-0 relative">
179
179
  <PackageSearch className="h-4 w-4 text-gray-400 absolute left-3 top-1/2 -translate-y-1/2" />
@@ -181,11 +181,11 @@ function FilterPanel(props: {
181
181
  value={props.searchText}
182
182
  onChange={(event) => props.onSearchTextChange(event.target.value)}
183
183
  placeholder="Search extensions..."
184
- className="w-full h-9 border border-gray-200 rounded-lg pl-9 pr-3 text-sm focus:outline-none focus:ring-2 focus:ring-primary/30"
184
+ className="w-full h-9 border border-gray-200/80 rounded-xl pl-9 pr-3 text-sm focus:outline-none focus:ring-1 focus:ring-primary/40"
185
185
  />
186
186
  </div>
187
187
 
188
- <div className="inline-flex h-9 rounded-lg bg-gray-100 p-0.5 shrink-0">
188
+ <div className="inline-flex h-9 rounded-xl bg-gray-100/80 p-1 shrink-0">
189
189
  {([
190
190
  { value: 'all', label: 'All' },
191
191
  { value: 'plugin', label: 'Plugins' },
@@ -196,7 +196,7 @@ function FilterPanel(props: {
196
196
  type="button"
197
197
  onClick={() => props.onTypeFilterChange(opt.value)}
198
198
  className={cn(
199
- 'px-3 rounded-md text-sm font-medium transition-all whitespace-nowrap',
199
+ 'px-3 rounded-lg text-sm font-medium transition-all whitespace-nowrap',
200
200
  props.typeFilter === opt.value
201
201
  ? 'bg-white text-gray-900 shadow-sm'
202
202
  : 'text-gray-500 hover:text-gray-700'
@@ -252,7 +252,7 @@ function MarketplaceListCard(props: {
252
252
  const displayType = type === 'plugin' ? 'Plugin' : type === 'skill' ? 'Skill' : 'Extension';
253
253
 
254
254
  return (
255
- <article className="group bg-white border border-transparent hover:border-gray-200 rounded-xl px-4 py-3 hover:shadow-sm transition-all flex items-start gap-4 justify-between cursor-default h-[90px]">
255
+ <article className="group bg-white border border-gray-200/40 hover:border-gray-200/80 rounded-2xl px-5 py-4 hover:shadow-md shadow-sm transition-all flex items-start gap-3.5 justify-between cursor-default">
256
256
  <div className="flex gap-3 min-w-0 flex-1 h-full items-start">
257
257
  <ItemIcon name={title} fallback={spec || 'Ext'} />
258
258
  <div className="min-w-0 flex-1 flex flex-col justify-center h-full">
@@ -302,7 +302,7 @@ function MarketplaceListCard(props: {
302
302
  <button
303
303
  onClick={() => props.onInstall(props.item as MarketplaceItemSummary)}
304
304
  disabled={props.installState.isPending}
305
- className="inline-flex items-center gap-1.5 h-8 px-4 rounded-full text-xs font-semibold bg-gray-900 text-white hover:bg-black disabled:opacity-50 transition-colors"
305
+ className="inline-flex items-center gap-1.5 h-8 px-4 rounded-xl text-xs font-medium bg-primary text-white hover:bg-primary-600 disabled:opacity-50 transition-colors"
306
306
  >
307
307
  {isInstalling ? 'Installing...' : 'Install'}
308
308
  </button>
@@ -312,7 +312,7 @@ function MarketplaceListCard(props: {
312
312
  <button
313
313
  disabled={props.manageState.isPending}
314
314
  onClick={() => props.onManage(isDisabled ? 'enable' : 'disable', pluginRecord)}
315
- className="inline-flex items-center h-8 px-4 rounded-full text-xs font-semibold border border-gray-200 text-gray-700 bg-white hover:bg-gray-50 hover:border-gray-300 disabled:opacity-50 transition-colors"
315
+ className="inline-flex items-center h-8 px-4 rounded-xl text-xs font-medium border border-gray-200/80 text-gray-600 bg-white hover:bg-gray-50 hover:border-gray-300 disabled:opacity-50 transition-colors"
316
316
  >
317
317
  {busyForRecord && props.manageState.action !== 'uninstall'
318
318
  ? (props.manageState.action === 'enable' ? 'Enabling...' : 'Disabling...')
@@ -324,7 +324,7 @@ function MarketplaceListCard(props: {
324
324
  <button
325
325
  disabled={props.manageState.isPending}
326
326
  onClick={() => props.onManage('uninstall', record)}
327
- className="inline-flex items-center h-8 px-4 rounded-full text-xs font-semibold border border-rose-100 text-rose-600 bg-white hover:bg-rose-50 hover:border-rose-200 disabled:opacity-50 transition-colors"
327
+ className="inline-flex items-center h-8 px-4 rounded-xl text-xs font-medium border border-rose-100 text-rose-500 bg-white hover:bg-rose-50 hover:border-rose-200 disabled:opacity-50 transition-colors"
328
328
  >
329
329
  {busyForRecord && props.manageState.action === 'uninstall' ? 'Removing...' : 'Uninstall'}
330
330
  </button>
@@ -344,7 +344,7 @@ function PaginationBar(props: {
344
344
  return (
345
345
  <div className="mt-4 flex items-center justify-end gap-2">
346
346
  <button
347
- className="h-8 px-3 rounded-lg border border-gray-200 text-sm text-gray-700 disabled:opacity-40"
347
+ className="h-8 px-3 rounded-xl border border-gray-200/80 text-sm text-gray-600 disabled:opacity-40"
348
348
  onClick={props.onPrev}
349
349
  disabled={props.page <= 1 || props.busy}
350
350
  >
@@ -354,7 +354,7 @@ function PaginationBar(props: {
354
354
  {props.totalPages === 0 ? '0 / 0' : `${props.page} / ${props.totalPages}`}
355
355
  </div>
356
356
  <button
357
- className="h-8 px-3 rounded-lg border border-gray-200 text-sm text-gray-700 disabled:opacity-40"
357
+ className="h-8 px-3 rounded-xl border border-gray-200/80 text-sm text-gray-600 disabled:opacity-40"
358
358
  onClick={props.onNext}
359
359
  disabled={props.totalPages === 0 || props.page >= props.totalPages || props.busy}
360
360
  >
@@ -513,9 +513,9 @@ export function MarketplacePage() {
513
513
 
514
514
  return (
515
515
  <div className="animate-fade-in pb-20">
516
- <div className="mb-5">
517
- <h2 className="text-2xl font-bold text-gray-900">Marketplace</h2>
518
- <p className="text-[13px] text-gray-500 mt-1">A cleaner extension list focused on install / enable / disable.</p>
516
+ <div className="mb-4">
517
+ <h2 className="text-xl font-semibold text-gray-900">Marketplace</h2>
518
+ <p className="text-[12px] text-gray-400 mt-0.5">A cleaner extension list focused on install / enable / disable.</p>
519
519
  </div>
520
520
 
521
521
  <Tabs
@@ -546,7 +546,7 @@ export function MarketplacePage() {
546
546
 
547
547
  <section>
548
548
  <div className="flex items-center justify-between mb-3">
549
- <h3 className="text-[15px] font-bold text-gray-900">{scope === 'installed' ? 'Installed' : 'Extensions'}</h3>
549
+ <h3 className="text-[14px] font-semibold text-gray-900">{scope === 'installed' ? 'Installed' : 'Extensions'}</h3>
550
550
  <span className="text-[12px] text-gray-500">{listSummary}</span>
551
551
  </div>
552
552
 
@@ -0,0 +1,27 @@
1
+ import { cn } from '@/lib/utils';
2
+ import { ArrowRight } from 'lucide-react';
3
+
4
+ interface ActionLinkProps {
5
+ label: string;
6
+ className?: string;
7
+ onClick?: () => void;
8
+ }
9
+
10
+ /**
11
+ * Unified action link with arrow indicator.
12
+ * Used in card footers for "Configure →", "Enable →", etc.
13
+ */
14
+ export function ActionLink({ label, className, onClick }: ActionLinkProps) {
15
+ return (
16
+ <span
17
+ onClick={onClick}
18
+ className={cn(
19
+ 'inline-flex items-center gap-1 text-[13px] font-medium text-gray-600 hover:text-primary transition-colors cursor-pointer group/action',
20
+ className
21
+ )}
22
+ >
23
+ {label}
24
+ <ArrowRight className="h-3 w-3 transition-transform group-hover/action:translate-x-0.5" />
25
+ </span>
26
+ );
27
+ }
@@ -3,27 +3,26 @@ import { cva, type VariantProps } from 'class-variance-authority';
3
3
  import { cn } from '@/lib/utils';
4
4
 
5
5
  const buttonVariants = cva(
6
- 'inline-flex items-center justify-center whitespace-nowrap rounded-lg text-sm font-medium ring-offset-background transition-all duration-fast focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
6
+ 'inline-flex items-center justify-center whitespace-nowrap rounded-xl text-sm font-medium ring-offset-background transition-all duration-fast focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
7
7
  {
8
8
  variants: {
9
9
  variant: {
10
10
  default: 'bg-primary text-primary-foreground hover:bg-primary-600 active:bg-primary-700 shadow-sm',
11
11
  destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
12
- outline: 'border border-input bg-background hover:bg-secondary hover:text-secondary-foreground',
13
- secondary: 'bg-secondary text-secondary-foreground hover:bg-gray-200',
14
- ghost: 'hover:bg-secondary hover:text-secondary-foreground',
12
+ outline: 'border border-gray-200 bg-white hover:bg-gray-50 hover:text-gray-800 text-gray-600',
13
+ secondary: 'bg-gray-100 text-gray-700 hover:bg-gray-200/80',
14
+ ghost: 'hover:bg-gray-100/80 hover:text-gray-800',
15
15
  link: 'text-primary underline-offset-4 hover:underline',
16
- // New variants matching the design
17
16
  primary: 'bg-primary text-primary-foreground hover:bg-primary-600 active:bg-primary-700 shadow-sm',
18
- subtle: 'bg-secondary text-secondary-foreground hover:bg-gray-200',
19
- 'primary-outline': 'border-2 border-primary text-primary hover:bg-primary hover:text-primary-foreground'
17
+ subtle: 'bg-gray-100 text-gray-600 hover:bg-gray-200/80',
18
+ 'primary-outline': 'border border-primary/30 text-primary hover:bg-primary hover:text-primary-foreground'
20
19
  },
21
20
  size: {
22
- default: 'h-10 px-4 py-2',
23
- sm: 'h-8 rounded-md px-3 text-xs',
24
- lg: 'h-12 rounded-xl px-6 text-base',
25
- xl: 'h-14 rounded-xl px-8 text-base',
26
- icon: 'h-10 w-10'
21
+ default: 'h-9 px-4 py-2',
22
+ sm: 'h-8 rounded-lg px-3 text-xs',
23
+ lg: 'h-11 rounded-xl px-5 text-[14px]',
24
+ xl: 'h-12 rounded-2xl px-6 text-[15px]',
25
+ icon: 'h-9 w-9 rounded-xl'
27
26
  }
28
27
  },
29
28
  defaultVariants: {
@@ -35,7 +34,7 @@ const buttonVariants = cva(
35
34
 
36
35
  export interface ButtonProps
37
36
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
38
- VariantProps<typeof buttonVariants> {
37
+ VariantProps<typeof buttonVariants> {
39
38
  asChild?: boolean;
40
39
  }
41
40
 
@@ -8,8 +8,8 @@ const Card = React.forwardRef<
8
8
  <div
9
9
  ref={ref}
10
10
  className={cn(
11
- 'rounded-xl border border-gray-200 bg-white text-card-foreground shadow-card transition-all duration-base',
12
- hover && 'hover:shadow-card-hover hover:border-gray-300',
11
+ 'rounded-2xl border border-gray-200/50 bg-white text-card-foreground shadow-sm transition-all duration-base',
12
+ hover && 'hover:shadow-md hover:border-gray-200',
13
13
  className
14
14
  )}
15
15
  {...props}
@@ -23,7 +23,7 @@ const CardHeader = React.forwardRef<
23
23
  >(({ className, ...props }, ref) => (
24
24
  <div
25
25
  ref={ref}
26
- className={cn('flex flex-col space-y-2 p-6', className)}
26
+ className={cn('flex flex-col space-y-1.5 p-6', className)}
27
27
  {...props}
28
28
  />
29
29
  ));
@@ -36,7 +36,7 @@ const CardTitle = React.forwardRef<
36
36
  <h3
37
37
  ref={ref}
38
38
  className={cn(
39
- 'text-lg font-semibold leading-tight tracking-tight text-gray-900',
39
+ 'text-[15px] font-semibold leading-tight tracking-tight text-gray-900',
40
40
  className
41
41
  )}
42
42
  {...props}
@@ -50,7 +50,7 @@ const CardDescription = React.forwardRef<
50
50
  >(({ className, ...props }, ref) => (
51
51
  <p
52
52
  ref={ref}
53
- className={cn('text-sm text-gray-500 leading-relaxed', className)}
53
+ className={cn('text-[13px] text-gray-400 leading-relaxed', className)}
54
54
  {...props}
55
55
  />
56
56
  ));
@@ -0,0 +1,71 @@
1
+ import * as React from 'react';
2
+ import { cn } from '@/lib/utils';
3
+
4
+ interface ConfigCardProps {
5
+ children: React.ReactNode;
6
+ onClick?: () => void;
7
+ className?: string;
8
+ }
9
+
10
+ /**
11
+ * Unified config card used for Channels, Providers, etc.
12
+ * Style follows YouMind: generous padding, large radius, soft shadow.
13
+ */
14
+ export function ConfigCard({ children, onClick, className }: ConfigCardProps) {
15
+ return (
16
+ <div
17
+ onClick={onClick}
18
+ className={cn(
19
+ 'group relative flex flex-col p-6 rounded-2xl border border-gray-200/50 bg-white shadow-sm',
20
+ 'transition-all duration-base cursor-pointer',
21
+ 'hover:shadow-md hover:border-gray-200',
22
+ className
23
+ )}
24
+ >
25
+ {children}
26
+ </div>
27
+ );
28
+ }
29
+
30
+ interface ConfigCardHeaderProps {
31
+ children: React.ReactNode;
32
+ className?: string;
33
+ }
34
+
35
+ export function ConfigCardHeader({ children, className }: ConfigCardHeaderProps) {
36
+ return (
37
+ <div className={cn('flex items-start justify-between mb-4', className)}>
38
+ {children}
39
+ </div>
40
+ );
41
+ }
42
+
43
+ interface ConfigCardBodyProps {
44
+ title: string;
45
+ description?: string;
46
+ className?: string;
47
+ }
48
+
49
+ export function ConfigCardBody({ title, description, className }: ConfigCardBodyProps) {
50
+ return (
51
+ <div className={cn('flex-1', className)}>
52
+ <h3 className="text-[14px] font-bold text-gray-900 mb-0.5">{title}</h3>
53
+ {description && (
54
+ <p className="text-[12px] text-gray-400 leading-relaxed line-clamp-2">{description}</p>
55
+ )}
56
+ </div>
57
+ );
58
+ }
59
+
60
+ interface ConfigCardFooterProps {
61
+ children: React.ReactNode;
62
+ className?: string;
63
+ }
64
+
65
+ export function ConfigCardFooter({ children, className }: ConfigCardFooterProps) {
66
+ return (
67
+ <div className={cn('mt-4 pt-3 flex items-center justify-between', className)}>
68
+ {children}
69
+ </div>
70
+ );
71
+ }
@@ -36,7 +36,7 @@ const DialogContent = React.forwardRef<
36
36
  <DialogPrimitive.Content
37
37
  ref={ref}
38
38
  className={cn(
39
- "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-gray-200 bg-white p-6 shadow-xl duration-base data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] rounded-xl",
39
+ "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-gray-200/50 bg-white p-6 shadow-xl duration-base data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] rounded-2xl",
40
40
  className
41
41
  )}
42
42
  {...props}
@@ -1,7 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import { cn } from '@/lib/utils';
3
3
 
4
- export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {}
4
+ export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> { }
5
5
 
6
6
  const Input = React.forwardRef<HTMLInputElement, InputProps>(
7
7
  ({ className, type, ...props }, ref) => {
@@ -9,7 +9,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
9
9
  <input
10
10
  type={type}
11
11
  className={cn(
12
- 'flex h-10 w-full rounded-lg border border-gray-200 bg-white px-3 py-2 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-gray-400 focus:outline-none disabled:cursor-not-allowed disabled:opacity-50',
12
+ 'flex h-9 w-full rounded-xl border border-gray-200/80 bg-white px-3.5 py-2 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-gray-400 focus:outline-none focus:ring-1 focus:ring-primary/40 focus:border-primary/40 transition-colors disabled:cursor-not-allowed disabled:opacity-50',
13
13
  className
14
14
  )}
15
15
  ref={ref}
@@ -14,7 +14,7 @@ const SelectTrigger = React.forwardRef<
14
14
  <SelectPrimitive.Trigger
15
15
  ref={ref}
16
16
  className={cn(
17
- "flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-gray-200 bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-primary disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1 bg-white",
17
+ "flex h-9 w-full items-center justify-between whitespace-nowrap rounded-xl border border-gray-200/80 bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-primary/40 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1 bg-white",
18
18
  className
19
19
  )}
20
20
  {...props}
@@ -0,0 +1,51 @@
1
+ import { cn } from '@/lib/utils';
2
+
3
+ type StatusType = 'active' | 'inactive' | 'ready' | 'setup' | 'warning';
4
+
5
+ interface StatusDotProps {
6
+ status: StatusType;
7
+ label: string;
8
+ className?: string;
9
+ }
10
+
11
+ const statusStyles: Record<StatusType, { dot: string; text: string; bg: string }> = {
12
+ active: {
13
+ dot: 'bg-emerald-500',
14
+ text: 'text-emerald-600',
15
+ bg: 'bg-emerald-50',
16
+ },
17
+ ready: {
18
+ dot: 'bg-emerald-500',
19
+ text: 'text-emerald-600',
20
+ bg: 'bg-emerald-50',
21
+ },
22
+ inactive: {
23
+ dot: 'bg-gray-300',
24
+ text: 'text-gray-400',
25
+ bg: 'bg-gray-100/80',
26
+ },
27
+ setup: {
28
+ dot: 'bg-gray-300',
29
+ text: 'text-gray-400',
30
+ bg: 'bg-gray-100/80',
31
+ },
32
+ warning: {
33
+ dot: 'bg-amber-400',
34
+ text: 'text-amber-600',
35
+ bg: 'bg-amber-50',
36
+ },
37
+ };
38
+
39
+ /**
40
+ * Unified status indicator with dot + label.
41
+ * Used consistently across Channels, Providers, etc.
42
+ */
43
+ export function StatusDot({ status, label, className }: StatusDotProps) {
44
+ const style = statusStyles[status];
45
+ return (
46
+ <div className={cn('flex items-center gap-1.5 px-2 py-0.5 rounded-full', style.bg, className)}>
47
+ <span className={cn('h-1.5 w-1.5 rounded-full', style.dot)} />
48
+ <span className={cn('text-[11px] font-medium', style.text)}>{label}</span>
49
+ </div>
50
+ );
51
+ }
@@ -15,8 +15,8 @@ const Switch = React.forwardRef<HTMLButtonElement, SwitchProps>(
15
15
  aria-checked={checked}
16
16
  ref={ref}
17
17
  className={cn(
18
- 'peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors duration-fast focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-white disabled:cursor-not-allowed disabled:opacity-50',
19
- checked ? 'bg-primary' : 'bg-gray-200 hover:bg-gray-300',
18
+ 'peer inline-flex h-[22px] w-10 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors duration-fast focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-white disabled:cursor-not-allowed disabled:opacity-50',
19
+ checked ? 'bg-primary' : 'bg-gray-200/80 hover:bg-gray-300/80',
20
20
  className
21
21
  )}
22
22
  onClick={() => onCheckedChange?.(!checked)}
@@ -16,7 +16,7 @@ interface TabsProps {
16
16
 
17
17
  export function Tabs({ tabs, activeTab, onChange, className }: TabsProps) {
18
18
  return (
19
- <div className={cn('flex items-center gap-8 border-b border-gray-200 mb-8', className)}>
19
+ <div className={cn('flex items-center gap-6 border-b border-gray-200/60 mb-6', className)}>
20
20
  {tabs.map((tab) => {
21
21
  const isActive = activeTab === tab.id;
22
22
  return (
@@ -24,18 +24,21 @@ export function Tabs({ tabs, activeTab, onChange, className }: TabsProps) {
24
24
  key={tab.id}
25
25
  onClick={() => onChange(tab.id)}
26
26
  className={cn(
27
- 'relative pb-4 text-[15px] font-semibold transition-all duration-fast flex items-center gap-2',
27
+ 'relative pb-3 text-[14px] font-medium transition-all duration-fast flex items-center gap-1.5',
28
28
  isActive
29
- ? 'text-primary'
30
- : 'text-gray-500 hover:text-gray-700'
29
+ ? 'text-gray-900'
30
+ : 'text-gray-600 hover:text-gray-900'
31
31
  )}
32
32
  >
33
33
  {tab.label}
34
34
  {tab.count !== undefined && (
35
- <span className="text-[11px] font-medium text-gray-400">{tab.count.toLocaleString()}</span>
35
+ <span className={cn(
36
+ 'text-[11px] font-medium',
37
+ isActive ? 'text-gray-500' : 'text-gray-500'
38
+ )}>{tab.count.toLocaleString()}</span>
36
39
  )}
37
40
  {isActive && (
38
- <div className="absolute bottom-0 left-0 right-0 h-0.5 bg-primary animate-in fade-in slide-in-from-left-2 duration-300" />
41
+ <div className="absolute bottom-0 left-0 right-0 h-[2px] bg-primary rounded-full" />
39
42
  )}
40
43
  </button>
41
44
  );
@@ -42,7 +42,7 @@ export function TabsList({ children, className }: TabsListProps) {
42
42
  return (
43
43
  <div
44
44
  className={cn(
45
- 'inline-flex h-10 items-center justify-center rounded-lg bg-gray-100 p-1 text-gray-600',
45
+ 'inline-flex h-9 items-center justify-center rounded-xl bg-gray-100/80 p-1 text-gray-500',
46
46
  className
47
47
  )}
48
48
  >
@@ -62,10 +62,10 @@ export function TabsTrigger({ value, children, className }: TabsTriggerProps) {
62
62
  type="button"
63
63
  onClick={() => context.onValueChange(value)}
64
64
  className={cn(
65
- 'inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1.5 text-sm font-medium ring-offset-white transition-all duration-fast focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
65
+ 'inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-[13px] font-medium ring-offset-white transition-all duration-fast focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
66
66
  isActive
67
67
  ? 'bg-white text-gray-900 shadow-sm'
68
- : 'hover:bg-white/50 hover:text-gray-700',
68
+ : 'hover:bg-white/50 hover:text-gray-800 text-gray-600',
69
69
  className
70
70
  )}
71
71
  >
package/src/index.css CHANGED
@@ -19,43 +19,31 @@
19
19
  --popover: 0 0% 100%;
20
20
  --popover-foreground: 221 39% 11%;
21
21
 
22
- /* Primary: Brand Blue */
22
+ /* Primary: Blue */
23
23
  --primary: 217 80% 55%;
24
24
  --primary-foreground: 0 0% 100%;
25
25
 
26
- /* Secondary: Light Gray */
27
- --secondary: 220 14% 96%;
28
- --secondary-foreground: 215 28% 17%;
26
+ /* Secondary */
27
+ --secondary: 214 18% 96%;
28
+ --secondary-foreground: 219 18% 18%;
29
29
 
30
30
  /* Muted */
31
- --muted: 220 14% 96%;
32
- --muted-foreground: 220 9% 46%;
31
+ --muted: 214 18% 96%;
32
+ --muted-foreground: 216 6% 46%;
33
33
 
34
34
  /* Accent */
35
35
  --accent: 217 100% 97%;
36
- --accent-foreground: 217 70% 40%;
36
+ --accent-foreground: 217 60% 35%;
37
37
 
38
38
  /* Destructive */
39
39
  --destructive: 0 84% 60%;
40
40
  --destructive-foreground: 0 0% 98%;
41
41
 
42
42
  /* UI Elements */
43
- --border: 220 13% 91%;
44
- --input: 220 13% 91%;
43
+ --border: 213 14% 90%;
44
+ --input: 213 14% 90%;
45
45
  --ring: 217 80% 55%;
46
46
  --radius: 0.75rem;
47
-
48
- /* Legacy compatibility */
49
- --milk-50: 210 20% 98%;
50
- --milk-100: 220 14% 96%;
51
- --milk-200: 220 13% 91%;
52
- --milk-300: 216 12% 84%;
53
- --milk-400: 218 11% 65%;
54
- --milk-500: 220 9% 46%;
55
- --milk-600: 215 14% 34%;
56
- --milk-700: 217 19% 27%;
57
- --milk-800: 215 28% 17%;
58
- --milk-900: 221 39% 11%;
59
47
  }
60
48
  }
61
49
 
@@ -89,8 +77,8 @@
89
77
  SCROLLBAR
90
78
  ======================================== */
91
79
  .custom-scrollbar::-webkit-scrollbar {
92
- width: 6px;
93
- height: 6px;
80
+ width: 5px;
81
+ height: 5px;
94
82
  }
95
83
 
96
84
  .custom-scrollbar::-webkit-scrollbar-track {
@@ -110,14 +98,14 @@
110
98
  GLASSMORPHISM
111
99
  ======================================== */
112
100
  .glass {
113
- background: rgba(255, 255, 255, 0.8);
101
+ background: rgba(255, 255, 255, 0.85);
114
102
  backdrop-filter: blur(12px);
115
103
  -webkit-backdrop-filter: blur(12px);
116
- border: 1px solid rgba(255, 255, 255, 0.2);
104
+ border: 1px solid rgba(255, 255, 255, 0.3);
117
105
  }
118
106
 
119
107
  .glass-dark {
120
- background: rgba(0, 0, 0, 0.1);
108
+ background: rgba(0, 0, 0, 0.06);
121
109
  backdrop-filter: blur(12px);
122
110
  -webkit-backdrop-filter: blur(12px);
123
111
  border: 1px solid rgba(255, 255, 255, 0.1);
@@ -127,19 +115,19 @@
127
115
  SHADOWS
128
116
  ======================================== */
129
117
  .shadow-card {
130
- box-shadow: 0 2px 8px -2px rgba(0, 0, 0, 0.04), 0 1px 2px -1px rgba(0, 0, 0, 0.02);
118
+ box-shadow: 0 1px 3px -1px rgba(30, 20, 10, 0.03), 0 1px 2px -1px rgba(30, 20, 10, 0.02);
131
119
  }
132
120
 
133
121
  .shadow-card-hover {
134
- box-shadow: 0 12px 24px -4px rgba(0, 0, 0, 0.06), 0 4px 8px -4px rgba(0, 0, 0, 0.03);
122
+ box-shadow: 0 8px 20px -4px rgba(30, 20, 10, 0.06), 0 4px 8px -4px rgba(30, 20, 10, 0.03);
135
123
  }
136
124
 
137
125
  .shadow-premium {
138
- box-shadow: 0 10px 30px -10px rgba(0, 0, 0, 0.08), 0 4px 10px -4px rgba(0, 0, 0, 0.04);
126
+ box-shadow: 0 8px 24px -8px rgba(30, 20, 10, 0.06), 0 4px 10px -4px rgba(30, 20, 10, 0.03);
139
127
  }
140
128
 
141
129
  .shadow-premium-hover {
142
- box-shadow: 0 20px 40px -15px rgba(0, 0, 0, 0.12), 0 8px 15px -6px rgba(0, 0, 0, 0.06);
130
+ box-shadow: 0 16px 32px -12px rgba(30, 20, 10, 0.1), 0 6px 12px -4px rgba(30, 20, 10, 0.04);
143
131
  }
144
132
 
145
133
  /* ========================================
@@ -169,7 +157,7 @@
169
157
  @keyframes fadeIn {
170
158
  from {
171
159
  opacity: 0;
172
- transform: translateY(12px);
160
+ transform: translateY(8px);
173
161
  }
174
162
 
175
163
  to {
@@ -181,7 +169,7 @@
181
169
  @keyframes slideIn {
182
170
  from {
183
171
  opacity: 0;
184
- transform: translateX(-12px);
172
+ transform: translateX(-8px);
185
173
  }
186
174
 
187
175
  to {
@@ -193,7 +181,7 @@
193
181
  @keyframes scaleIn {
194
182
  from {
195
183
  opacity: 0;
196
- transform: scale(0.97);
184
+ transform: scale(0.98);
197
185
  }
198
186
 
199
187
  to {
@@ -210,20 +198,20 @@
210
198
  }
211
199
 
212
200
  50% {
213
- opacity: 0.8;
201
+ opacity: 0.85;
214
202
  }
215
203
  }
216
204
 
217
205
  .animate-fade-in {
218
- animation: fadeIn 0.4s cubic-bezier(0.16, 1, 0.3, 1) forwards;
206
+ animation: fadeIn 0.35s cubic-bezier(0.16, 1, 0.3, 1) forwards;
219
207
  }
220
208
 
221
209
  .animate-slide-in {
222
- animation: slideIn 0.35s cubic-bezier(0.16, 1, 0.3, 1) forwards;
210
+ animation: slideIn 0.3s cubic-bezier(0.16, 1, 0.3, 1) forwards;
223
211
  }
224
212
 
225
213
  .animate-scale-in {
226
- animation: scaleIn 0.35s cubic-bezier(0.16, 1, 0.3, 1) forwards;
214
+ animation: scaleIn 0.3s cubic-bezier(0.16, 1, 0.3, 1) forwards;
227
215
  }
228
216
 
229
217
  .animate-pulse-soft {