All files / src/utils tailwindClassHelpers.ts

100% Statements 2/2
100% Branches 0/0
100% Functions 1/1
100% Lines 2/2

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296                                                                  26x                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             1763x              
/**
 * TailwindCSS Class Helpers
 * 
 * Standard class patterns for consistent widget styling across the application.
 * These utilities promote reusability and visual consistency while maintaining
 * accessibility standards.
 * 
 * @module tailwindClassHelpers
 */
 
/**
 * Standard class patterns for consistent widget styling
 * 
 * These patterns use Tailwind utility classes and design tokens from designTokens.ts
 * to ensure visual consistency across all widgets.
 * 
 * @example
 * ```tsx
 * import { WidgetClasses, cn } from '@/utils/tailwindClassHelpers';
 * 
 * function MyWidget() {
 *   return (
 *     <div className={cn(WidgetClasses.container, WidgetClasses.containerHover)}>
 *       <h2 className={WidgetClasses.heading}>Widget Title</h2>
 *       <div className={WidgetClasses.grid2Cols}>
 *         <div className={WidgetClasses.card}>Content 1</div>
 *         <div className={WidgetClasses.card}>Content 2</div>
 *       </div>
 *     </div>
 *   );
 * }
 * ```
 */
export const WidgetClasses = {
  // ========================================
  // Container patterns
  // ========================================
  
  /**
   * Standard widget container with rounded corners, border, shadow, and padding
   * Uses design tokens for consistent spacing and elevation
   */
  container: 'rounded-md border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 shadow-md p-lg',
  
  /**
   * Hover effect for interactive containers
   * Increases shadow on hover with smooth transition
   */
  containerHover: 'hover:shadow-lg transition-shadow duration-normal',
  
  // ========================================
  // Section patterns
  // ========================================
  
  /**
   * Standard section with bottom margin and vertical spacing
   * Use for major content divisions within widgets
   */
  section: 'mb-lg space-y-md',
  
  /**
   * Section with left border accent
   * Use for emphasized or highlighted sections
   */
  sectionBorder: 'border-l-4 border-primary pl-md',
  
  // ========================================
  // Typography patterns
  // ========================================
  
  /**
   * Primary heading style for widget titles
   * Uses design token typography and semantic colors
   */
  heading: 'text-subheading font-semibold text-gray-800 dark:text-gray-100 mb-md',
  
  /**
   * Secondary heading for subsections
   * Smaller and medium weight for content hierarchy
   */
  subheading: 'text-body-lg font-medium text-gray-700 dark:text-gray-200 mb-sm',
  
  /**
   * Standard body text style
   * Use for primary content and descriptions
   */
  body: 'text-body text-gray-600 dark:text-gray-400',
  
  /**
   * Small label text for form labels and metadata
   * Uppercase with tracking for emphasis
   */
  label: 'text-caption font-medium text-gray-500 dark:text-gray-500 uppercase tracking-wide',
  
  /**
   * Small label text for form labels and metadata in normal case
   * Use this when label text should preserve its original casing
   * instead of being forced to uppercase.
   */
  labelNormal: 'text-caption font-medium text-gray-500 dark:text-gray-500 tracking-wide',
  
  // ========================================
  // Card patterns
  // ========================================
  
  /**
   * Standard card container for nested content
   * Lighter background to distinguish from main container
   */
  card: 'rounded-md border border-gray-200 dark:border-gray-600 p-md bg-gray-50 dark:bg-gray-700',
  
  /**
   * Interactive card with hover effect
   * Use for clickable cards and list items
   */
  cardInteractive: 'hover:bg-gray-100 dark:hover:bg-gray-600 cursor-pointer transition-colors duration-fast',
  
  // ========================================
  // Button patterns
  // ========================================
  
  /**
   * Primary button style
   * High contrast for main actions
   */
  buttonPrimary: 'bg-primary hover:bg-primary-dark text-white font-medium py-sm px-md rounded-md transition-colors duration-fast focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2',
  
  /**
   * Secondary button style
   * Lower contrast for alternative actions
   */
  buttonSecondary: 'bg-gray-200 dark:bg-gray-600 hover:bg-gray-300 dark:hover:bg-gray-500 text-gray-800 dark:text-gray-100 font-medium py-sm px-md rounded-md transition-colors duration-fast focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2',
  
  // ========================================
  // Grid patterns
  // ========================================
  
  /**
   * Two-column grid layout
   * Stacks on mobile, 2 columns on medium+ screens
   */
  grid2Cols: 'grid grid-cols-1 md:grid-cols-2 gap-md',
  
  /**
   * Three-column grid layout
   * Stacks on mobile, 2 columns on tablet, 3 columns on desktop
   */
  grid3Cols: 'grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-md',
  
  /**
   * Flexible row layout with wrapping
   * Adjusts gap between items responsively
   */
  flexRow: 'flex flex-wrap gap-sm md:gap-md',
  
  // ========================================
  // Responsive patterns
  // ========================================
  
  /**
   * Hide on mobile, show on tablet and desktop
   * Use for non-essential content on small screens
   */
  hideMobile: 'hidden md:block',
  
  /**
   * Show on mobile only, hide on tablet and desktop
   * Use for mobile-optimized alternatives
   */
  hideDesktop: 'block md:hidden',
  
  /**
   * Responsive text sizing
   * Smaller on mobile, larger on desktop
   */
  textResponsive: 'text-body md:text-body-lg',
  
  // ========================================
  // State patterns
  // ========================================
  
  /**
   * Disabled state styling
   * Reduced opacity and no pointer events
   */
  disabled: 'opacity-50 cursor-not-allowed pointer-events-none',
  
  /**
   * Loading state with pulse animation
   * Use for skeleton screens and loading indicators
   */
  loading: 'animate-pulse',
  
  /**
   * Focus-visible ring for keyboard navigation
   * Ensures accessibility compliance
   */
  focusVisible: 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2',
  
  // ========================================
  // Badge patterns
  // ========================================
  
  /**
   * Standard badge styling
   * Use with specific color variants
   */
  badge: 'inline-flex items-center px-sm py-xs text-caption font-medium rounded-sm',
  
  /**
   * Success badge (green)
   */
  badgeSuccess: 'bg-success text-white',
  
  /**
   * Warning badge (yellow)
   */
  badgeWarning: 'bg-warning text-gray-900',
  
  /**
   * Error badge (red)
   */
  badgeError: 'bg-error text-white',
  
  /**
   * Info badge (blue)
   */
  badgeInfo: 'bg-info text-white',
  
  /**
   * Neutral badge (gray)
   */
  badgeNeutral: 'bg-neutral text-white',
  
  // ========================================
  // Divider patterns
  // ========================================
  
  /**
   * Horizontal divider
   */
  dividerHorizontal: 'border-t border-gray-200 dark:border-gray-700 my-md',
  
  /**
   * Vertical divider (for flex layouts)
   */
  dividerVertical: 'border-l border-gray-200 dark:border-gray-700 mx-md',
  
} as const;
 
/**
 * Combine Tailwind classes with proper handling of conditionals
 * 
 * This utility function merges multiple class strings, filtering out
 * falsy values (false, null, undefined) for conditional styling.
 * 
 * @param classes - Variable number of class strings or conditional values
 * @returns Combined class string with falsy values filtered out
 * 
 * @example
 * ```tsx
 * // Basic usage
 * cn('text-lg', 'font-bold') // 'text-lg font-bold'
 * 
 * // Conditional classes
 * cn('base-class', isActive && 'active-class', 'another-class')
 * // Result: 'base-class active-class another-class' (if isActive is true)
 * // Result: 'base-class another-class' (if isActive is false)
 * 
 * // With null/undefined
 * cn('text-lg', null, undefined, 'font-bold') // 'text-lg font-bold'
 * 
 * // Practical example
 * function Button({ primary, disabled }) {
 *   return (
 *     <button className={cn(
 *       'btn',
 *       primary && 'btn-primary',
 *       !primary && 'btn-secondary',
 *       disabled && 'opacity-50'
 *     )}>
 *       Click me
 *     </button>
 *   );
 * }
 * ```
 */
export function cn(...classes: (string | undefined | false | null)[]): string {
  return classes.filter(Boolean).join(' ');
}
 
/**
 * Type definitions for better TypeScript support
 */
export type WidgetClassKey = keyof typeof WidgetClasses;