• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

clippingkk / web / #1178

21 Aug 2025 10:49AM UTC coverage: 0.523% (-0.02%) from 0.546%
#1178

push

web-flow
Merge c060881fd into 4eea96e38

30 of 448 branches covered (6.7%)

Branch coverage included in aggregate %.

12 of 7134 new or added lines in 368 files covered. (0.17%)

3 existing lines in 3 files now uncovered.

147 of 33423 relevant lines covered (0.44%)

8.69 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/components/button/button.tsx
1
'use client'
×
2

×
3
import { Loader2 } from 'lucide-react'
×
NEW
4
import type React from 'react'
×
NEW
5
import { cn } from '@/lib/utils'
×
6

×
7
// Common button classes
×
8
const baseButtonClasses =
×
9
  'relative z-10 inline-flex items-center justify-center gap-2 rounded-lg font-medium transition-all duration-300 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 active:scale-95 disabled:pointer-events-none disabled:opacity-70'
×
10

×
11
type ButtonVariant =
×
12
  | 'primary'
×
13
  | 'secondary'
×
14
  | 'outline'
×
15
  | 'ghost'
×
16
  | 'danger'
×
17
  | 'link'
×
18
type ButtonSize = 'sm' | 'md' | 'lg' | 'xl' | 'hero'
×
19

×
20
export interface ButtonProps
×
21
  extends React.ButtonHTMLAttributes<HTMLButtonElement> {
×
22
  /**
×
23
   * The visual style of the button
×
24
   * @default 'primary'
×
25
   */
×
26
  variant?: ButtonVariant
×
27
  /**
×
28
   * The size of the button
×
29
   * @default 'md'
×
30
   */
×
31
  size?: ButtonSize
×
32
  /**
×
33
   * Whether the button should take up the full width of its container
×
34
   * @default false
×
35
   */
×
36
  fullWidth?: boolean
×
37
  /**
×
38
   * Whether the button is in a loading state
×
39
   */
×
40
  isLoading?: boolean
×
41
  /**
×
42
   * Icon to display before the button text
×
43
   */
×
44
  leftIcon?: React.ReactNode
×
45
  /**
×
46
   * Icon to display after the button text
×
47
   */
×
48
  rightIcon?: React.ReactNode
×
49
  /**
×
50
   * Children elements
×
51
   */
×
52
  children: React.ReactNode
×
53
}
×
54

×
55
/**
×
56
 * Universal button component for ClippingKK with multiple variants and states
×
57
 *
×
58
 * @example
×
59
 * ```tsx
×
60
 * <Button variant="primary" size="lg" onClick={handleClick}>
×
61
 *   Click me
×
62
 * </Button>
×
63
 *
×
64
 * <Button variant="secondary" isLoading>
×
65
 *   Loading...
×
66
 * </Button>
×
67
 * ```
×
68
 */
×
69
export function Button({
×
70
  className,
×
71
  variant = 'primary',
×
72
  size = 'md',
×
73
  fullWidth = false,
×
74
  isLoading = false,
×
75
  disabled,
×
76
  leftIcon,
×
77
  rightIcon,
×
78
  children,
×
79
  ...props
×
80
}: ButtonProps) {
×
81
  const getVariantClasses = (variant: ButtonVariant) => {
×
82
    switch (variant) {
×
83
      case 'primary':
×
84
        return [
×
85
          'bg-gradient-to-br from-sky-400 via-blue-500 to-cyan-600 text-white shadow-lg shadow-blue-500/20',
×
86
          'hover:-translate-y-1 hover:shadow-[0_0_20px_3px] hover:shadow-blue-500/30',
×
87
          'after:absolute after:inset-0 after:z-0 after:rounded-lg after:bg-gradient-to-r after:from-blue-500 after:via-sky-600 after:to-cyan-600 after:opacity-0 after:transition-opacity after:duration-300 hover:after:opacity-100',
×
88
          'before:absolute before:-inset-1 before:z-0 before:scale-[1.05] before:rounded-lg before:bg-gradient-to-r before:from-sky-400 before:to-blue-500 before:opacity-0 before:blur-md before:transition-all before:duration-300 hover:before:opacity-50',
×
89
        ].join(' ')
×
90
      case 'secondary':
×
91
        return [
×
92
          'bg-gradient-to-br from-cyan-500/80 to-blue-600/80 text-white/90 backdrop-blur-sm',
×
93
          'hover:-translate-y-1 hover:text-white hover:shadow-lg hover:shadow-cyan-500/20',
×
94
          'after:absolute after:inset-0 after:z-0 after:rounded-lg after:bg-gradient-to-r after:from-blue-500/40 after:to-cyan-500/40 after:opacity-0 after:transition-opacity after:duration-300 hover:after:opacity-100',
×
95
        ].join(' ')
×
96
      case 'outline':
×
97
        return [
×
98
          'border-2 border-[#045fab] bg-transparent text-[#045fab] dark:text-white',
×
99
          'hover:-translate-y-1 hover:bg-[#045fab]/10 hover:shadow-lg hover:shadow-[#045fab]/20',
×
100
          'dark:border-white/70 dark:hover:bg-white/10 dark:hover:shadow-white/10',
×
101
        ].join(' ')
×
102
      case 'ghost':
×
103
        return [
×
104
          'bg-transparent text-slate-700 hover:bg-slate-100 dark:text-slate-200 dark:hover:bg-slate-800/60',
×
105
          'hover:-translate-y-1',
×
106
        ].join(' ')
×
107
      case 'danger':
×
108
        return [
×
109
          'bg-gradient-to-br from-red-500 to-rose-600 text-white shadow-lg shadow-red-500/20',
×
110
          'hover:-translate-y-1 hover:shadow-[0_0_20px_3px] hover:shadow-red-500/30',
×
111
          'after:absolute after:inset-0 after:z-0 after:rounded-lg after:bg-gradient-to-r after:from-rose-600 after:to-red-600 after:opacity-0 after:transition-opacity after:duration-300 hover:after:opacity-100',
×
112
        ].join(' ')
×
113
      case 'link':
×
114
        return 'bg-transparent p-0 text-[#045fab] underline-offset-4 hover:underline dark:text-blue-400'
×
115
      default:
×
116
        return ''
×
117
    }
×
118
  }
×
119

×
120
  const getSizeClasses = (size: ButtonSize) => {
×
121
    switch (size) {
×
122
      case 'sm':
×
123
        return 'px-3 py-1.5 text-sm'
×
124
      case 'md':
×
125
        return 'px-4 py-2 text-base'
×
126
      case 'lg':
×
127
        return 'px-6 py-3 text-lg'
×
128
      case 'xl':
×
129
        return 'px-8 py-4 text-xl'
×
130
      case 'hero':
×
131
        return 'px-14 py-6 text-3xl font-black'
×
132
      default:
×
133
        return ''
×
134
    }
×
135
  }
×
136

×
137
  const buttonClasses = cn(
×
138
    baseButtonClasses,
×
139
    getVariantClasses(variant),
×
140
    getSizeClasses(size),
×
141
    fullWidth ? 'w-full' : '',
×
142
    className
×
143
  )
×
144

×
145
  return (
×
146
    <button
×
147
      className={buttonClasses}
×
148
      disabled={disabled || isLoading}
×
149
      {...props}
×
150
    >
×
151
      {/* Create a container for content to position it above the pseudo-elements */}
×
NEW
152
      <span className='relative z-10 flex items-center justify-center gap-2'>
×
153
        {isLoading ? (
×
NEW
154
          <Loader2 className='h-4 w-4 animate-spin' />
×
155
        ) : (
×
NEW
156
          leftIcon && <span className='flex items-center'>{leftIcon}</span>
×
157
        )}
×
158
        {children}
×
159
        {rightIcon && !isLoading && (
×
NEW
160
          <span className='flex items-center'>{rightIcon}</span>
×
161
        )}
×
162
      </span>
×
163
    </button>
×
164
  )
×
165
}
×
166

×
167
export default Button
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc