# CSS Patterns

This document outlines CSS variable naming conventions, styling patterns, and CSS module organization for componentLibrary.

## CSS Variable Naming Convention

**Pattern:** `--hscl-componentName-[elementName]-cssProperty-[state]`

- **Prefix:** Always start with `--hscl-` (HubSpot Component Library)
- **Component name:** Lowercase component name in camelCase, with name taken from the index.tsx file (e.g., `button`, `icon`, `divider`, `image`, `accordion`, `card`)
- **Element name (optional):** For sub-elements within a component, specify the element name in camelCase (e.g., `icon`, `body`, `title`, `header`, `overlay`, `submenu`)
  - Omit for properties that apply to the main component element itself
  - Include for properties that apply to specific sub-elements
- **CSS Property:** CSS property name in camelCase (e.g., `backgroundColor`, `fontSize`, `borderColor`, `paddingBlock`)
- **State (optional):** State suffix for interactive states (e.g., `-hover`, `-focus`, `-disabled`, `-active`)

**Examples for main component element:**
```css
--hscl-button-backgroundColor
--hscl-button-backgroundColor-hover
--hscl-button-backgroundColor-focus
--hscl-divider-borderColor
--hscl-icon-fill
--hscl-card-padding
```

**Examples for sub-elements:**
```css
--hscl-button-icon-fill
--hscl-accordion-body-color
--hscl-accordion-body-paddingBlock
--hscl-accordion-title-backgroundColor
--hscl-accordion-title-fontSize
--hscl-drawer-overlay-backgroundColor
--hscl-navigationMenu-submenu-linkColor
--hscl-navigationMenu-submenu-backgroundColor-hover
```

**Naming Best Practices:**
- Use camelCase throughout (never use underscores or hyphens within segments)
- Property names should match CSS property names when possible (e.g., `backgroundColor` not `bgColor`, `fill` not `fillColor`)
- Element names should be descriptive and singular (e.g., `icon`, `body`, `header`)
- State suffixes should match CSS pseudo-classes (e.g., `hover`, `focus`, `active`, `disabled`)
- Avoid redundant words (e.g., use `--hscl-list-item-color` not `--hscl-list-item-text-color`)

**Nested/Dynamic Variables:**
```typescript
// Components can use template variables to dynamically reference nested variables
'--hscl-component-font': `var(--hscl-component-${variantValue}-font)`
'--hscl-component-fontSize': `var(--hscl-component-${variantValue}-fontSize)`
```

**Note:** When using dynamic variants, use numbers without underscores (e.g., `variant1` not `variant_1`).

## CSS Variables Application

Components apply CSS variables through the `style` prop using the `CSSVariables` type:

```typescript
// Import the CSSVariables type
import type { CSSVariables } from '../utils/types.js';

// ALWAYS define CSS variables as typed constants
const cssVariables: CSSVariables = {
  '--hscl-component-property': value,
  '--hscl-component-property2': `${numericValue}px`, // Include units when needed
};

// Then use in JSX
<Element style={{ ...cssVariables, ...style }} />
<Element style={cssVariables} />
```

**The CSSVariables Type:**

The `CSSVariables` type extends `React.CSSProperties` to support CSS custom properties:

```typescript
export type CSSVariables = React.CSSProperties & {
  [key: `--${string}`]: string | undefined;
};
```

**Important: Always Define Variables, Never Inline**

❌ **Don't** inline CSS variable objects directly in JSX (causes TypeScript errors):
```typescript
// This will cause TypeScript errors
<Element style={{ '--hscl-property': value }} />
```

✅ **Do** define them as typed constants:
```typescript
// This works correctly
const elementStyle: CSSVariables = {
  '--hscl-property': value,
};
<Element style={elementStyle} />
```

**Examples:**

```typescript
import type { CSSVariables } from '../utils/types.js';

// Divider - direct values with units
const cssVariables: CSSVariables = {
  '--hscl-divider-alignment': getAlignmentCSSVar(alignment),
  '--hscl-divider-spacing': spacing,
  '--hscl-divider-borderStyle': borderStyle,
  '--hscl-divider-length': `${length}%`,
  '--hscl-divider-thickness': `${thickness}px`,
  ...style,
};

// In stories - define within render function
render: () => {
  const blueColor: CSSVariables = {
    '--hscl-divider-borderColor': '#3b82f6',
  };

  return <Divider thickness={2} style={blueColor} />;
}
```

## CSS Module Patterns

### Base Component Styles
```scss
.componentName {
  // Use CSS variables for all themeable properties
  property: var(--hscl-componentName-property);

  // Pseudo-states use state suffix
  &:hover {
    property: var(--hscl-componentName-property-hover);
  }

  // Focus-visible reuses hover styles + browser default outline
  &:focus-visible {
    property: var(--hscl-componentName-property-hover);
    outline: 2px solid Highlight;
    outline-offset: 2px;
  }

  &:disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }
}
```

### Variant Classes
```scss
// Not currently in use but pattern exists:
.hscl-componentName-primary {
  // Primary variant overrides
}

.hscl-componentName-secondary {
  // Secondary variant overrides
}
```

### Modifier Classes
```scss
.horizontal {
  // Modifier-specific styles that use pre-defined CSS vars wherever possible
}

.vertical {
  // Modifier-specific styles that use pre-defined CSS vars wherever possible
}

.responsive {
  max-inline-size: 100%;
  block-size: auto;
}
```

### State-Based Variants
Use `&` for pseudo-classes and nested selectors:

```scss
.button {
  transition: all 0.2s ease;

  &:hover {
    background-color: var(--hscl-button-backgroundColor-hover);
  }

  &:focus-visible {
    background-color: var(--hscl-button-backgroundColor-hover);
    color: var(--hscl-button-color-hover);
    outline: 2px solid Highlight;
    outline-offset: 2px;
  }

  &:disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }

  svg {
    fill: var(--hscl-button-icon-fill);
  }
}
```

## CSS Variables Advanced Patterns

### Fallback Values
Always provide fallback values for CSS variables:

```scss
.divider {
  border-color: var(--hscl-divider-borderColor, currentColor);
  border-style: var(--hscl-divider-borderStyle, solid);
}

.image {
  display: var(--hscl-image-display, inline-block);
  border-radius: var(--hscl-image-borderRadius, 0);
}
```

### Per-Side Border Width
Use individual per-side vars for `border-width` instead of a single combined var. This allows consumers to customize each side independently without needing to override all four sides at once.

```scss
.card {
  // Each side var falls back to a hardcoded default
  border-width: var(--hs-card-borderTopWidth, 0) var(--hs-card-borderRightWidth, 0) var(--hs-card-borderBottomWidth, 0) var(--hs-card-borderLeftWidth, 0);
}

.button {
  // Hover/active per-side vars fall back to the base per-side var
  &:hover {
    border-width: var(--hs-button-borderTopWidth-hover, var(--hs-button-borderTopWidth)) var(--hs-button-borderRightWidth-hover, var(--hs-button-borderRightWidth)) var(--hs-button-borderBottomWidth-hover, var(--hs-button-borderBottomWidth)) var(--hs-button-borderLeftWidth-hover, var(--hs-button-borderLeftWidth));
  }
}
```

Do not use a single `borderWidth` var — it cannot express per-side customization. If an existing API exposes a single `border-width`-like CSS variable (e.g. a third-party form component's bridge var), set it to the four-value space-separated token sequence; CSS custom properties can hold multi-token values and `border-width` will expand them correctly.

### Computed Variables
Variables can reference other variables for dynamic behavior:

```typescript
const cssVariables = {
  '--hscl-component-font': `var(--hscl-component-${variantValue}-font)`,
  '--hscl-component-fontSize': `var(--hscl-component-${variantValue}-fontSize)`,
};
```

This allows runtime selection of which nested variable to use based on component props.

## CSS Module Advanced Patterns

### State-Based Variants
Use `&` for pseudo-classes and nested selectors:

```scss
.button {
  transition: all 0.2s ease;

  &:hover {
    background-color: var(--hscl-button-backgroundColor-hover);
  }

  &:focus-visible {
    background-color: var(--hscl-button-backgroundColor-hover);
    color: var(--hscl-button-color-hover);
    outline: 2px solid Highlight;
    outline-offset: 2px;
  }

  &:disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }

  svg {
    fill: var(--hscl-button-icon-fill);
  }
}
```

## Accessibility Patterns for Interactive Components

Interactive components (Button, Link, etc.) should implement all relevant accessibility states following HubSpot's CSS guidelines.

### Required Interactive States

All interactive components should style **hover, focus, active** states:

```scss
.button {
  // Base styles
  background-color: var(--hscl-button-backgroundColor);
  color: var(--hscl-button-color);
  transition: all 0.2s ease;

  // Respect user's motion preferences
  @media (prefers-reduced-motion: reduce) {
    transition: none;
  }

  // Hover - visual feedback on mouse over
  &:hover {
    background-color: var(--hscl-button-backgroundColor-hover, var(--hscl-button-backgroundColor));
    color: var(--hscl-button-color-hover, var(--hscl-button-color));
  }

  // Focus-visible - reuses hover styles + browser default outline
  &:focus-visible {
    background-color: var(--hscl-button-backgroundColor-hover, var(--hscl-button-backgroundColor));
    color: var(--hscl-button-color-hover, var(--hscl-button-color));
    outline: 2px solid Highlight;
    outline-offset: 2px;
  }

  // Active - pressed/clicked state
  &:active {
    background-color: var(--hscl-button-backgroundColor-active, var(--hscl-button-backgroundColor));
    color: var(--hscl-button-color-active, var(--hscl-button-color));
  }

  // Disabled - non-interactive state
  &:disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }
}
```

### Key Accessibility Principles

1. **Use `:focus-visible` instead of `:focus`**
   - Shows focus outline only for keyboard navigation
   - Better UX - no outline when clicking with mouse
   - Follows modern accessibility best practices

2. **Provide fallback values that inherit from base state**
   ```scss
   // Good - inherits from base if not customized
   background-color: var(--hscl-button-backgroundColor-hover, var(--hscl-button-backgroundColor));

   // Avoid - no fallback to base state
   background-color: var(--hscl-button-backgroundColor-hover);
   ```

3. **Reuse hover styles for focus, rely on browser default outline**
   - Focus-visible should apply the same visual treatment as hover (background, color, border)
   - Use `outline: 2px solid initial` for the browser's default focus ring color
   - No need for separate focus-specific CSS variables
   ```scss
   &:focus-visible {
     background-color: var(--hscl-button-backgroundColor-hover, var(--hscl-button-backgroundColor));
     color: var(--hscl-button-color-hover, var(--hscl-button-color));
     outline: 2px solid Highlight;
     outline-offset: 2px;
   }
   ```

4. **Respect reduced motion preferences**
   ```scss
   transition: all 0.2s ease;

   @media (prefers-reduced-motion: reduce) {
     transition: none;
   }
   ```

5. **All states should be customizable via CSS variables**
   - Enables theme customization
   - Maintains consistent API across components

### Pattern Reference

See these components for implementation examples:
- **Button** (`src/cms-assets/library/components/componentLibrary/Button/index.module.scss`) - Full implementation with all states and reduced motion support
- **Link** (`src/cms-assets/library/components/componentLibrary/Link/index.module.scss`) - Minimal interactive component with hover and focus-visible states
- **Logo** (`src/cms-assets/library/components/componentLibrary/Logo/index.tsx`) - Composition example: inherits focus styles from Link component when wrapped

## Complete CSS Module Example

```scss
// index.module.scss
.componentName {
  property: var(--hscl-componentName-property);

  &:hover {
    property: var(--hscl-componentName-property-hover);
  }

  &:focus-visible {
    property: var(--hscl-componentName-property-hover);
    outline: 2px solid Highlight;
    outline-offset: 2px;
  }
}

.hscl-componentName-primary {
  // Variant styles
}
```
