# Component Structure Patterns

This document outlines the standard file structure and implementation patterns for components.

## File Structure

All components follow a consistent file structure:

```
ComponentName/
├── index.tsx              # Main component implementation (or island wrapper — see below)
├── index.module.scss      # Component styles
├── ContentFields.tsx      # Content field definitions (optional)
├── StyleFields.tsx        # Style field definitions (optional)
├── types.ts              # TypeScript type definitions
├── islands/              # Client-side interactive implementations (optional — see Island Patterns)
│   └── ComponentNameIsland.tsx
└── stories/              # Storybook stories (these can be created after the component is reviewed and merged)
    ├── ComponentName.stories.tsx
    └── ComponentNameDecorator.tsx
```

**Note:** If a component requires client-side interactivity (state, event handlers, browser APIs), the interactive logic lives in an `islands/` subdirectory and the `index.tsx` becomes a wrapper that renders `<Island module={...} />`. See [Island Patterns](./island-patterns.md) for full details.

**Note:** Complex components with multiple variants (like Button) should have separate story files per variant (e.g., `Button.AsButton.stories.tsx`, `Button.AsLink.stories.tsx`).

## Class Name Building Pattern

Components follow a consistent pattern for building class names using CSS modules only (no static classes):

### Pattern 1: defaultClasses + combinedClasses (Most Common)

```typescript
// 1. Build defaultClasses with CSS module classes (use cx if there are multiple default classes)
const defaultClasses = styles.componentName;

// 2. Combine with user's className prop
const combinedClasses = cx(defaultClasses, className);

// 3. Apply to element
<Element className={combinedClasses} />
```

**Examples:**
```typescript
// Icon component
const defaultClasses = styles.icon;
const combinedClasses = cx(defaultClasses, className);

// Image component
const defaultClasses = cx(styles.image, variantClass, {
  [styles.responsive]: responsive,
});
const combinedClasses = cx(defaultClasses, className);

// Divider component
const defaultClasses = cx(styles.divider, variantClass, orientationClass);
const combinedClasses = cx(defaultClasses, className);
```

### Pattern 2: sharedProps (for multiple related elements)

```typescript
// When you need to share props across conditional renders
const variantClass = styles[`hscl-button-${variant}`];
const defaultClasses = cx(styles.button, variantClass);

const sharedProps = {
  className: cx(defaultClasses, className),
  style: style,
};

// Use in multiple places
<button {...sharedProps}>...</button>
<a {...sharedProps}>...</a>
```

**Key Points:**
- Always include the CSS module class (`styles.componentName`)
- Always merge with user's `className` prop
- Use `cx()` utility for conditional classes
- Variant classes follow pattern: `styles[`hscl-componentName-${variant}`]`
- **No static classes** - we rely on CSS modules to prevent developers from breaking the design system

## Props Destructuring Pattern

Components destructure props inline in the function signature with defaults:

```typescript
const ComponentName = ({
  // Required props first (no defaults)
  requiredProp,

  // Optional props with defaults
  variant = 'primary',
  className = '',
  style = {},
  children = 'Default content',

  // Spread remaining props
  ...rest
}: ComponentProps) => {
```

**Standard props every component should accept:**
- `className?: string` - defaults to `''`
- `style?: React.CSSProperties` - defaults to `{}`
- `variant?: string` - defaults to `'primary'` (when applicable)
- `...rest` - spread to underlying HTML element

## Variant System Pattern

Components should support a variant system which is currently planned but not fully implemented:

```typescript
// 1. Accept variant prop with default
const { variant = 'primary', ... } = props;

// 2. Generate variant class name
const variantClass = styles[`hscl-componentName-${variant}`];

// 3. Include in defaultClasses
const defaultClasses = cx(styles.componentName, variantClass, 'hscl-componentName');
```

**Common variants:** `'primary'`, `'secondary'`, `'tertiary'`

**Note:** Make sure you add a comment in the code to follow-up: `// !todo: not used atm but keeping for when we need to add variant system`

## Compound Component Pattern

All components with fields use a compound component pattern:

```typescript
// 1. Create main component
const ComponentNameComponent = (props: ComponentProps) => {
  // ... implementation
};

// 2. Create typed version with static properties
type ComponentNameComponent = typeof ComponentNameComponent & {
  ContentFields: typeof ContentFields;
  StyleFields?: typeof StyleFields; // Optional
};

// 3. Cast and attach field components
const ComponentName = ComponentNameComponent as ComponentNameComponent;
ComponentName.ContentFields = ContentFields;
ComponentName.StyleFields = StyleFields; // If applicable

// 4. Export as default
export default ComponentName;
```

**Usage inside a module's fields.ts file:**
```tsx
<Button.ContentFields />
<Button.StyleFields />
```

## Props Spreading Patterns

### Shared Props Pattern
Extract shared props for reuse across conditional renders:

```typescript
const sharedProps = {
  className: cx(defaultClasses, className),
  style: customStyle,
};

// Reuse in multiple places
<button {...sharedProps} onClick={onClick}>...</button>
<a {...sharedProps} href={href}>...</a>
```

## Accessibility Patterns

### Semantic HTML Selection
Choose appropriate HTML elements:

```typescript
// Use semantic <hr> for horizontal dividers
if (orientation === 'horizontal') {
  return <hr {...sharedProps} />;
}

// Use <div> with role for vertical dividers
return <div {...sharedProps} role="separator" />;
```

## Common Utilities

### Classname Utility (`cx`)
```typescript
import cx from '../utils/classname.js';

// Merge class names
cx(class1, class2, class3);

// Conditional classes
cx(baseClass, {
  [styles.active]: isActive,
  [styles.disabled]: isDisabled,
});
```

## Helper Function Patterns

### Value Transformation Helpers
Helper functions should be defined outside the component for reusability and readability:

```typescript
const getAlignmentCSSVar = (alignment: DividerAlignment) => {
  switch (alignment) {
    case 'start':
      return `flex-${alignment}`;
    case 'end':
      return `flex-${alignment}`;
    case 'center':
      return alignment;
    default:
      return 'stretch';
  }
};
```

## Complete Component Example

```typescript
// index.tsx
import styles from './index.module.scss';
import ContentFields from './ContentFields.js';
import StyleFields from './StyleFields.js';
import cx from '../utils/classname.js';
import { ComponentProps } from './types.js';

const ComponentNameComponent = ({
  variant = 'primary',
  className = '',
  style = {},
  children,
  ...rest
}: ComponentProps) => {

  const cssVariables: React.CSSProperties & {
    [key: `--${string}`]: string | undefined;
  } = {
    '--hscl-componentName-property': 'value',
  };

  const variantClass = styles[`hscl-componentName-${variant}`];
  const defaultClasses = cx(styles.componentName, variantClass);
  const combinedClasses = cx(defaultClasses, className);

  return (
    <div
      className={combinedClasses}
      style={{ ...cssVariables, ...style }}
      {...rest}
    >
      {children}
    </div>
  );
};

type ComponentNameComponent = typeof ComponentNameComponent & {
  ContentFields: typeof ContentFields;
  StyleFields: typeof StyleFields;
};

const ComponentName = ComponentNameComponent as ComponentNameComponent;
ComponentName.ContentFields = ContentFields;
ComponentName.StyleFields = StyleFields;

export default ComponentName;
```
