================================================================================
Source: llm.txt
================================================================================

# HubSpot CMS Component Library

A collection of React components designed exclusively for building HubSpot CMS modules. These components provide pre-built UI elements with integrated HubSpot field definitions, CSS module styling, and TypeScript support.

## Installation

```bash
npm install @hubspot/cms-component-library
```

## Important Context

**HubSpot CMS Only**: These components are designed specifically for use within HubSpot's CMS environment when building modules. They depend on HubSpot-specific packages (`@hubspot/cms-components`) and will not work in standard React applications outside of the HubSpot CMS.

## Component Architecture

Each component follows a consistent structure:

```
ComponentName/
├── index.tsx                 # Main component (default export)
├── types.ts                  # TypeScript type definitions
├── ContentFields.tsx         # HubSpot content field definitions
├── StyleFields.tsx           # HubSpot style field definitions
├── index.module.scss         # CSS module styles with design tokens
└── stories/                  # Storybook documentation
```

## Importing Components

### Default Component Export

All components are exported as default exports and imported using the component name as the path:

```tsx
import Component from '@hubspot/cms-component-library/ComponentName'
```

### Components with Sub-Components

Some higher-order components export named sub-components for composition patterns:

```tsx
import Component, {
  SubComponent1,
  SubComponent2,
  SubComponent3
} from '@hubspot/cms-component-library/CompoundComponent';
```

## Field Definitions

Most components attach HubSpot CMS field definitions as static properties for easy integration with modules.

### ContentFields

Define the content-related fields (text, images, links, etc.) that editors can configure:

```tsx
import Button from '@hubspot/cms-component-library/Button';

// In your module's fields definition:
export function fields() {
  return (
    <>
      <Button.ContentFields
        buttonTextLabel="CTA text"
        buttonTextName="ctaText"
        buttonTextDefault="Get Started"
      />
    </>
  );
}
```

### StyleFields

Define styling-related fields (variants, colors, spacing) that editors can configure:

```tsx
import Button from '@hubspot/cms-component-library/Button';

// In your module's fields definition:
export function fields() {
  return (
    <>
      <Button.StyleFields
        buttonVariantLabel="Button style"
        buttonVariantName="buttonStyle"
        buttonVariantDefault="primary"
      />
    </>
  );
}
```

### Combined Usage

Typically, you'll use both ContentFields and StyleFields together:

```tsx
import { FieldGroup, ModuleFields } from '@hubspot/cms-components/fields';
import Button from '@hubspot/cms-component-library/Button';
import Text from '@hubspot/cms-component-library/Text';

export const fields = (
  <ModuleFields>
    <Text.ContentFields textFeatureSet="heading" />
    <Button.ContentFields />
    <FieldGroup label="Style" name="style" tab="STYLE">
      <Button.StyleFields />
    </FieldGroup>
  </ModuleFields>
);
```

## Consuming Components in Modules

### Basic Module Example

```tsx
import Button from '@hubspot/cms-component-library/Button';
import Text from '@hubspot/cms-component-library/Text';

export const Component = ({ buttonVariant, buttonLink, buttonText }) => {
  return (
    <>
      <Text fieldPath="text" />

      <Button
        buttonType="link"
        href={buttonLink?.url?.href}
        variant={buttonVariant}
      >
        {buttonText}
      </Button>
    </>
  );
};

export { fields } from './fields';

export const meta: ModuleMeta = {
  label: 'Button',
  content_types: [],
  icon: '',
  categories: ['content'],
};
```

### Using Layout Components

Layout components like Flex and Grid help organize other components:

```tsx
import Flex from '@hubspot/cms-component-library/Flex';
import Button from '@hubspot/cms-component-library/Button';

export const Component = () => {
  return (
    <Flex
      direction="column"
      gap="1rem"
      alignItems="center"
    >
      <Button buttonType="link" href="/page1">
        Page 1
      </Button>
      <Button buttonType="link" href="/page2">
        Page 2
      </Button>
    </Flex>
  );
};
```

## Interactive Components and Islands

Interactive components that require client-side JavaScript are pre-wrapped with the necessary island context. You can use them directly without additional island wrapping:

```tsx
import InteractiveComponent from '@hubspot/cms-component-library/InteractiveComponent';

// No need to wrap in an island - the component is already exported with island context
export const Component = () => {
  return (
    <InteractiveComponent
      onClick={() => console.log('clicked')}
    />
  );
};
```

## Component Composition Patterns

## Field Configuration Props

ContentFields and StyleFields accept configuration props to customize field labels, names, and defaults:

```tsx
import Button from '@hubspot/cms-component-library/Button';

export function fields() {
  return (
    <>
      <Button.ContentFields
        buttonTextLabel="Call to Action text"
        buttonTextName="ctaText"
        buttonTextDefault="Start Free Trial"
        buttonLinkLabel="CTA destination"
        buttonLinkName="ctaLink"
        showButtonLink={true}
        iconLabel="CTA icon"
        showIconLabel="Display icon?"
        showIconDefault={false}
      />

      <Button.StyleFields
        buttonVariantLabel="CTA style"
        buttonVariantName="ctaStyle"
        buttonVariantDefault="primary"
      />
    </>
  );
}
```

## Best Practices

1. **Import only what you need**: Import specific components rather than the entire library
2. **Use field definitions**: Leverage ContentFields and StyleFields for editor-friendly modules
3. **CSS Variables over inline styles**: Prefer CSS custom properties for styling customization
4. **Semantic HTML**: Components render semantic HTML elements for accessibility
5. **TypeScript types**: Use provided TypeScript types for type safety
6. **Layout composition**: Use Flex and Grid for responsive layouts rather than custom CSS


================================================================================
Source: components/componentLibrary/Accordion/llm.txt
================================================================================

# Accordion Component

A collapsible content component that creates expandable/collapsible sections using native HTML `<details>`/`<summary>` elements. It supports multiple style variants, icon options, and flexible content.

## Import path
```tsx
import Accordion, { AccordionItem, AccordionTitle, AccordionContent } from '@hubspot/cms-component-library/Accordion';
```

## Purpose

The Accordion component provides a consistent way to render collapsible content sections in HubSpot CMS projects. It leverages native HTML `<details>` and `<summary>` elements for built-in expand/collapse functionality without requiring JavaScript. The component supports multiple style variants, customizable expand/collapse icons, and can contain any content within the expandable panels.

## Compositional Component Pattern

The Accordion component follows a **parent/child compositional pattern**, consisting of four separate but related components:

| Component | Role | Renders |
|-----------|------|---------|
| `Accordion` | Container | Flex column layout |
| `AccordionItem` | Item wrapper | `<details>` |
| `AccordionTitle` | Clickable trigger | `<summary>` |
| `AccordionContent` | Expandable panel | `<div>` |

**Why this pattern?**
- **Separation of concerns**: Each component handles a specific responsibility (layout, state, trigger, content)
- **Native HTML semantics**: Uses `<details>`/`<summary>` for built-in accessibility and keyboard support
- **Flexibility**: Each subcomponent can be styled and customized independently
- **HubSpot CMS integration**: Each component has its own field definitions, allowing RepeatedGroup usage for dynamic accordions
- **Type safety**: Props are scoped to their respective components, preventing invalid configurations

**Key relationships:**
- `AccordionItem` should **always** contain both `AccordionTitle` and `AccordionContent`
- `AccordionItem` should **always** be used inside an `Accordion` component
- `AccordionItem` controls variant styling via CSS variables that cascade to child components
- `AccordionTitle` renders the clickable summary with configurable expand/collapse icons
- `AccordionContent` contains the expandable content that shows/hides

This pattern is distinct from the "Compound Component Pattern" (e.g., `AccordionItem.StyleFields`) which attaches field definitions—Accordion uses *both* patterns together.

## Component Structure

```
Accordion/
├── index.tsx                     # Main Accordion container component
├── types.ts                      # TypeScript type definitions for Accordion
├── AccordionItem/
│   ├── index.tsx                 # AccordionItem wrapper component
│   ├── types.ts                  # AccordionItem TypeScript types
│   ├── StyleFields.tsx           # HubSpot field definitions for variant
│   └── index.module.scss         # CSS module for item styling
├── AccordionTitle/
│   ├── index.tsx                 # AccordionTitle component (with island wiring)
│   ├── AccordionTitleBase.tsx    # Base title rendering (shared by static and island)
│   ├── types.ts                  # AccordionTitle TypeScript types
│   ├── ContentFields.tsx         # HubSpot field definitions for title text
│   ├── StyleFields.tsx           # HubSpot field definitions for icon
│   ├── icons.tsx                 # SVG icon components (Caret, Chevron, Plus)
│   ├── index.module.scss         # CSS module for title styling
│   └── islands/
│       └── AccordionTitleIsland.tsx  # Island for CMS inline editing support
├── AccordionContent/
│   ├── index.tsx                 # AccordionContent component
│   ├── types.ts                  # AccordionContent TypeScript types
│   ├── ContentFields.tsx         # HubSpot field definitions for content
│   └── index.module.scss         # CSS module for content styling
└── stories/
    └── Accordion.stories.tsx     # Storybook examples
```

## Components

### Accordion (Main Container)

**Purpose:** Container component that provides vertical layout with configurable gap spacing between accordion items.

**Props:**
```tsx
{
  gap?: string;                    // Space between accordion items (default: '24px')
  className?: string;              // Additional CSS classes
  style?: React.CSSProperties;     // Inline styles
  children: React.ReactNode;       // AccordionItem components
}
```

### AccordionItem (Item Wrapper)

**Purpose:** Wrapper component that renders a `<details>` element, providing the expand/collapse container with variant styling.

**Props:**
```tsx
{
  variant?: 'card1' | 'card2' | 'card3' | 'card4';  // Visual style variant (default: 'card1')
  className?: string;                                           // Additional CSS classes
  style?: React.CSSProperties;                                  // Inline styles
  children: React.ReactNode;                                    // Should contain AccordionTitle and AccordionContent
}
```

**Expected children:** Always include both `AccordionTitle` and `AccordionContent` as children.

### AccordionTitle (Trigger)

**Purpose:** Clickable trigger component that renders a `<summary>` element with rich text and an expand/collapse icon. Uses the `Text` component internally for CMS inline editing support.

**Props:**
```tsx
{
  icon?: 'chevron' | 'plus' | 'caret';  // Icon type (default: 'chevron')
  fieldPath?: string;                   // Path to the RichText field in HubSpot CMS (preferred for CMS modules)
  className?: string;                   // Additional CSS classes
  style?: React.CSSProperties;          // Inline styles
  children?: React.ReactNode;           // Fallback title content (used when fieldPath is not provided, e.g. in Storybook)
}
```

### AccordionContent (Panel)

**Purpose:** Content panel component that renders a `<div>` containing the expandable content. Uses the `Text` component internally when `fieldPath` is provided for CMS inline editing support.

**Props:**
```tsx
{
  fieldPath?: string;              // Path to the RichText field in HubSpot CMS (preferred for CMS modules)
  className?: string;              // Additional CSS classes
  style?: React.CSSProperties;     // Inline styles
  children?: React.ReactNode;      // Fallback content (used when fieldPath is not provided, e.g. in Storybook)
}
```

## Usage Examples

### Basic Accordion

```tsx
import Accordion, { AccordionItem, AccordionTitle, AccordionContent } from '@hubspot/cms-component-library/Accordion';

<Accordion>
  <AccordionItem>
    <AccordionTitle>What is HubSpot?</AccordionTitle>
    <AccordionContent>
      <p>HubSpot is a leading CRM platform that provides software and support to help businesses grow better.</p>
    </AccordionContent>
  </AccordionItem>
  <AccordionItem>
    <AccordionTitle>How do I get started?</AccordionTitle>
    <AccordionContent>
      <p>Sign up for a free account and explore our comprehensive onboarding guides and tutorials.</p>
    </AccordionContent>
  </AccordionItem>
</Accordion>
```

### With Plus/Minus Icon

```tsx
import Accordion, { AccordionItem, AccordionTitle, AccordionContent } from '@hubspot/cms-component-library/Accordion';

<Accordion>
  <AccordionItem>
    <AccordionTitle icon="plus">Plus becomes minus when expanded</AccordionTitle>
    <AccordionContent>
      <p>The plus icon switches to a minus icon when expanded, clearly indicating toggle state.</p>
    </AccordionContent>
  </AccordionItem>
</Accordion>
```

### Dynamic Rendering from Array

```tsx
import Accordion, { AccordionItem, AccordionTitle, AccordionContent } from '@hubspot/cms-component-library/Accordion';

const faqs = [
  { question: 'What payment methods do you accept?', answer: 'We accept all major credit cards and PayPal.' },
  { question: 'Can I cancel anytime?', answer: 'Yes, you can cancel your subscription at any time.' },
  { question: 'Do you offer refunds?', answer: 'We offer a 30-day money-back guarantee.' },
];

<Accordion>
  {faqs.map(({ question, answer }, index) => (
    <AccordionItem key={index}>
      <AccordionTitle icon="plus">{question}</AccordionTitle>
      <AccordionContent>{answer}</AccordionContent>
    </AccordionItem>
  ))}
</Accordion>
```

### With Rich Content

```tsx
import Accordion, { AccordionItem, AccordionTitle, AccordionContent } from '@hubspot/cms-component-library/Accordion';

<Accordion>
  <AccordionItem>
    <AccordionTitle>Getting Started Guide</AccordionTitle>
    <AccordionContent>
      <p>Welcome to HubSpot! Here are the steps to get started:</p>
      <ol>
        <li>Complete your profile setup</li>
        <li>Connect your email and calendar</li>
        <li>Import your existing contacts</li>
        <li>Set up your first campaign</li>
      </ol>
      <p><strong>Pro tip:</strong> Start with our interactive tutorials.</p>
    </AccordionContent>
  </AccordionItem>
</Accordion>
```

## HubSpot CMS Integration

### Field Definitions

The Accordion component provides field definitions for easy integration with HubSpot CMS modules.

#### AccordionItem.StyleFields

Configurable props for variant selection:

```tsx
<AccordionItem.StyleFields
  variantName="accordionVariant"
  variantLabel="Accordion style"
  variantDefault={{ variant_name: 'card1' }}
/>
```

**Fields:**
- `accordionVariant`: VariantSelectionField for selecting visual style using the card variant definition (card1, card2, card3, card4)

#### AccordionTitle.ContentFields

Configurable props for title text. Uses `Text.ContentFields` internally. Defaults to the `simpleText` feature set, configurable via `titleTextFeatureSet`:

```tsx
<AccordionTitle.ContentFields
  titleName="title"
  titleLabel="Title"
  titleDefault="<p>Accordion title</p>"
  titleTextFeatureSet="simpleText"
/>
```

**Props:**
- `titleLabel?: string` — label for the field (default: `'Title'`)
- `titleName?: string` — field name (default: `'title'`)
- `titleDefault?: string` — default HTML string (default: `'<p>Accordion title</p>'`)
- `titleTextFeatureSet?: TextFeatureSet` — feature set passed to `Text.ContentFields` (default: `'simpleText'`)

**Fields:**
- `title`: RichTextField for accordion title text (simpleText feature set by default)

#### AccordionTitle.StyleFields

Configurable props for icon style:

```tsx
<AccordionTitle.StyleFields
  iconName="icon"
  iconLabel="Icon"
  iconDefault="chevron"
/>
```

**Fields:**
- `icon`: ChoiceField for selecting icon type (caret, chevron, plus)

#### AccordionContent.ContentFields

Configurable props for content. Uses `Text.ContentFields` internally. Defaults to the `bodyContent` feature set, configurable via `contentFeatureSet`:

```tsx
<AccordionContent.ContentFields
  contentName="content"
  contentLabel="Content"
  contentDefault="<p>Add a short description</p>"
  contentFeatureSet="bodyContent"
/>
```

**Props:**
- `contentLabel?: string` — label for the field (default: `'Content'`)
- `contentName?: string` — field name (default: `'content'`)
- `contentDefault?: string` — default HTML string (default: `'<p>Add a short description</p>'`)
- `contentFeatureSet?: TextFeatureSet` — feature set passed to `Text.ContentFields` (default: `'bodyContent'`)

**Fields:**
- `content`: RichTextField for accordion content (bodyContent feature set by default)

### Module Usage Example

```tsx
import { ModuleMeta } from '../types/modules.js';
import Accordion, {
  AccordionItem,
  AccordionTitle,
  AccordionContent,
} from '@hubspot/cms-component-library/Accordion';
import { AccordionVariant } from '@hubspot/cms-component-library/Accordion/AccordionItem/types';
import { AccordionIconType } from '@hubspot/cms-component-library/Accordion/AccordionTitle/types';

type FAQModuleProps = {
  style?: {
    accordionVariant?: { variant_name: AccordionVariant };
    icon?: AccordionIconType;
  };
  accordionItems?: Array<{
    title?: string;
    content?: string;
  }>;
};

export const Component = ({
  style,
  accordionItems = [],
}: FAQModuleProps) => {
  const variant = style?.accordionVariant?.variant_name;
  const icon = style?.icon;

  return (
    <Accordion>
      {accordionItems.map((_item, index) => (
        <AccordionItem key={index} variant={variant}>
          <AccordionTitle
            icon={icon}
            fieldPath={`accordionItems[${index}].title`}
          />
          <AccordionContent
            fieldPath={`accordionItems[${index}].content`}
          />
        </AccordionItem>
      ))}
    </Accordion>
  );
};

export const meta: ModuleMeta = {
  label: 'FAQ Accordion',
  content_types: [],
  icon: '',
  categories: ['body_content'],
};
```

### Module Fields Example

```tsx
import {
  FieldGroup,
  ModuleFields,
  RepeatedFieldGroup,
} from '@hubspot/cms-components/fields';
import {
  AccordionContent,
  AccordionItem,
  AccordionTitle,
} from '@hubspot/cms-component-library/Accordion';

const defaultItem = {
  title: '<p>Accordion title</p>',
  content: '<p>Add a short description</p>',
};

export const fields = (
  <ModuleFields>
    <RepeatedFieldGroup
      label="Accordion Items"
      name="accordionItems"
      occurrence={{ min: 1, max: 20, default: 3 }}
      default={[defaultItem, defaultItem, defaultItem]}
    >
      <AccordionTitle.ContentFields />
      <AccordionContent.ContentFields />
    </RepeatedFieldGroup>
    <FieldGroup label="Style" name="style" tab="STYLE">
      <AccordionItem.StyleFields />
      <AccordionTitle.StyleFields />
    </FieldGroup>
  </ModuleFields>
);
```

## Styling

### CSS Variables

The Accordion component uses the shared card variant system for theming. Variant styles are applied via `data-card-variant` data attribute on `AccordionItem`, using the card variant definition from `VariantSelectionField`. The component consumes `--hs-card-*` theme CSS variables (e.g., `--hs-card-borderColor`, `--hs-card-borderRadius`, `--hs-card-backgroundColor`, `--hs-card-color`).

Padding and gap values are hardcoded (title: `24px` block / `32px` inline; content: `0 24px` block / `32px` inline). The accordion title text is styled to match `h4` via the theme's `--hs-heading4-*` CSS variables (`--hs-heading4-fontSize`, `--hs-heading4-fontFamily`, `--hs-heading4-fontStyle`, `--hs-heading4-fontWeight`). The accordion icon uses `currentColor` for its fill, inheriting from the card's text color.

### CSS Module Classes

**AccordionItem (`AccordionItem/index.module.scss`):**
- `.accordionItem`: Base details element styling with border, radius, background

**AccordionTitle (`AccordionTitle/index.module.scss`):**
- `.accordionTitle`: Flex container for title and icon
- `.accordionTitleText`: Title text with flex grow
- `.accordionIcon`: Base icon styling
- `.accordionIconChevron`: Chevron-specific sizing (rotates 180° when open)
- `.accordionIconPlus`: Plus icon (hidden when open)
- `.accordionIconMinus`: Minus icon (shown when open)
- `.accordionIconCaret`: Caret icon (rotates 180° when open)

**AccordionContent (`AccordionContent/index.module.scss`):**
- `.accordionContent`: Content area with padding and typography

## Accessibility

The Accordion component follows accessibility best practices:

- **Semantic HTML**: Uses native `<details>` and `<summary>` elements for built-in accessibility support
- **Keyboard Navigation**:
  - `Enter` or `Space` on summary toggles the accordion
  - Tab navigation works natively between accordion items
- **Screen Reader Support**:
  - `<details>` element announces expanded/collapsed state
  - `<summary>` element is announced as a button
- **Icon Accessibility**:
  - All icons have `aria-hidden="true"` since they are decorative
  - Icons have `role="img"` for proper semantics
- **Focus States**: Native focus styles are preserved for keyboard users
- **No JavaScript Required**: Expand/collapse functionality works without JavaScript

## Best Practices

- **Choose appropriate icons**: Use `caret` for a compact filled triangle indicator, `chevron` for subtle indication, `plus` for clearer expand/collapse semantics (common in FAQs)
- **Consistent variants**: Apply the same variant to all items within an accordion for visual consistency
- **Gap selection**: Use any valid CSS length value (e.g., '8px', '16px', '24px', '48px') for spacing between items
- **Dynamic rendering**: Always provide unique `key` props when mapping arrays to AccordionItems
- **Rich content**: AccordionContent supports any HTML content including lists, paragraphs, images, and nested components
- **Prefer library components**: When adding content inside AccordionContent, prefer using library components (e.g., `List`, `Text` with `textFeatureSet="heading"` for headings) over raw HTML elements for consistent theming and styling
- **Theme Variables**: Styling is controlled by the card variant system via `--hs-card-*` theme variables
- **Single responsibility**: Keep each accordion item focused on one topic for better UX

## Related Components

- **Flex**: Used internally for Accordion container layout. The Accordion component wraps Flex with a column direction.



================================================================================
Source: components/componentLibrary/Button/llm.txt
================================================================================

# Button Component

A polymorphic button component that can render as either a native `<button>` element for interactive actions or an `<a>` element for navigation, with full TypeScript type safety through discriminated unions.

## Import path
```tsx
import Button from '@hubspot/cms-component-library/Button';
```

## Purpose

The Button component provides a unified interface for creating interactive buttons and styled links in HubSpot CMS projects. It solves the common challenge of maintaining visual consistency between clickable elements that perform different functions (navigation vs. actions). `buttonType="link"` should be used most of the time and is default. `buttonType="button"` should only be used when an onClick handler is needed.

## Component Structure

```
Button/
├── index.tsx                     # Main component with render logic
├── types.ts                      # TypeScript type definitions (discriminated unions)
├── ContentFields.tsx             # HubSpot field definitions for content
├── StyleFields.tsx               # HubSpot field definitions for styling
├── index.module.scss             # CSS module with design tokens
└── stories/
    ├── Button.AsButton.stories.tsx  # Interactive button examples
    ├── Button.AsLink.stories.tsx    # Link navigation examples
    ├── ButtonDecorator.tsx           # Storybook decorator
    └── ButtonDecorator.module.scss   # Decorator styles
```

## Components

### Button (Main Component)

**Purpose:** Polymorphic component that renders either a `<button>` or `<a>` element based on the `buttonType` prop.

**Base Props (shared by both types):**
```tsx
{
  variant?: 'primaryButton' | 'secondaryButton' | 'tertiaryButton' | 'accentButton';  // Visual style variant (connected to theme settings)
  className?: string;                               // Additional CSS classes
  style?: React.CSSProperties;                      // Inline styles
  children?: React.ReactNode;                       // Button text/content
  iconFieldPath?: string;                           // Path to icon in HubSpot fields
  iconPosition?: 'left' | 'right';                  // Icon placement
  showIcon?: boolean;                               // Toggle icon visibility
  iconPurpose?: 'SEMANTIC' | 'DECORATIVE';         // Icon accessibility role
  iconTitle?: string;                               // Icon title for screen readers
}
```

**Button Type (`buttonType="button"`) - Additional Props:**
```tsx
{
  buttonType: 'button';           // Renders as <button> element
  onClick?: () => void;           // Click handler (requires JS islands)
  disabled?: boolean;             // Disables button interaction
}
```

**Link Type (`buttonType="link"`) - Additional Props:**
```tsx
{
  buttonType: 'link';                              // Renders as <a> element
  href?: string;                                   // Link destination URL
  target?: '_self' | '_blank' | '_parent' | '_top'; // Link target behavior
  rel?: string;                                    // Link relationship (e.g., 'noopener noreferrer')
}
```

## Usage Examples

### Basic Interactive Button

Custom Component wrapper
```tsx
import Button from '@hubspot/cms-component-library/Button';

export default function CustomButtonIsland({buttonType, ...rest}){

  // AI / User can now define interactive state or click handlers
  const onClickHandler = () => {
    alert('Button Clicked');
  }

  return <Button buttonType="button" {...rest} onClick={onClickHandler} />
}
```

Module file
```tsx
import CustomButtonIsland from './path/to/created/CustomButtonIsland?island';
import { Island } from '@hubspot/cms-components'

...

<Island module={CustomButtonIsland} {...props}>

...

```



### Basic Link Button

```tsx
<Button
  buttonType="link"
  variant="primaryButton"
  href="https://www.hubspot.com"
  target="_self"
>
  Visit HubSpot
</Button>
```

### External Link (New Tab)

```tsx
<Button
  buttonType="link"
  variant="primaryButton"
  href="https://www.hubspot.com"
  target="_blank"
  rel="noopener noreferrer"
>
  Open in New Tab
</Button>
```

### Button with Icon (Right Position)

```tsx
<Button
  buttonType="button"
  variant="primaryButton"
  onClick={handleClick}
  showIcon={true}
  iconFieldPath="icon"
  iconPosition="right"
  iconPurpose="DECORATIVE"
>
  Next Step
</Button>
```

### Button with Icon (Left Position)

```tsx
<Button
  buttonType="link"
  variant="secondaryButton"
  href="/back"
  showIcon={true}
  iconFieldPath="icon"
  iconPosition="left"
  iconPurpose="SEMANTIC"
  iconTitle="Go back"
>
  Back
</Button>
```

### Disabled Button

```tsx
<Button
  buttonType="button"
  variant="primaryButton"
  disabled={true}
  onClick={handleClick}
>
  Submit (disabled)
</Button>
```

### With Custom Styling

```tsx
<Button
  buttonType="link"
  variant="primaryButton"
  href="/custom"
  className="custom-button-class"
  style={{ maxWidth: '300px' }}
>
  Custom Styled Button
</Button>
```

## HubSpot CMS Integration

### Field Definitions

The Button component provides field definitions for easy integration with HubSpot CMS modules.

#### ContentFields.tsx

Configurable props for customizing field labels, names, and defaults:

```tsx
<Button.ContentFields
  buttonTextLabel="Button text"
  buttonTextName="buttonText"
  buttonTextDefault="Button"
  buttonTextPlaceholder="Add text"
  buttonLinkLabel="Button link"
  buttonLinkName="buttonLink"
  buttonLinkDefault={{
    url: {
      type: 'EXTERNAL',
      content_id: null,
      href: 'https://www.hubspot.com',
    },
  }}
  showButtonLink={true}
  iconLabel="Button icon"
  iconName="icon"
  iconDefault={/* icon default */}
  showIconLabel="Show button icon"
  showIconName="showIcon"
  showIconDefault={false}
  buttonIconPositionLabel="Icon position"
  buttonIconPositionName="buttonIconPosition"
  buttonIconPositionDefault="right"
/>
```

**Props:**
- `buttonTextLabel?: string` — label for the TextField (default: `'Button text'`)
- `buttonTextName?: TButtonTextName` — field name (default: `'buttonText'`). Generic `TButtonTextName extends string` lets consumers narrow the field-name literal for compile-time key matching on `fieldVisibility`.
- `buttonTextDefault?` — default value for the TextField
- `buttonTextPlaceholder?: string` — placeholder text for the TextField (default: `'Add text'`)
- `buttonLinkLabel?: string` — label for the LinkField (default: `'Button link'`)
- `buttonLinkName?: TButtonLinkName` — field name (default: `'buttonLink'`). Generic `TButtonLinkName extends string` lets consumers narrow the field-name literal for compile-time key matching on `fieldVisibility`.
- `buttonLinkDefault?` — default value for the LinkField
- `showButtonLink?: boolean` — hide the LinkField when `false` (default: `true`)
- `iconLabel?: string` — label for the icon field
- `iconName?: string` — field name for the icon
- `iconDefault?` — default value for the icon field
- `showIconLabel?: string` — label for the show-icon toggle (default: `'Show button icon'`)
- `showIconName?: string` — field name for the show-icon toggle
- `showIconDefault?: boolean` — default value for the show-icon toggle
- `buttonIconPositionLabel?: string` — label for the icon position field (default: `'Icon position'`)
- `buttonIconPositionName?: string` — field name for the icon position (default: `'buttonIconPosition'`)
- `buttonIconPositionDefault?: 'left' | 'right'` — default icon position (default: `'right'`)
- `fieldVisibility?: Partial<Record<TButtonTextName | TButtonLinkName, Visibility>>` — visibility options keyed by field name. Keys must match the `buttonTextName` or `buttonLinkName` props at compile time. Values match the cms-components Visibility schema (e.g. `hidden_subfields`, `controlling_field_path`, `operator`).

**Example — hide the nofollow subfield on the default `buttonLink`:**
```tsx
<Button.ContentFields
  fieldVisibility={{ buttonLink: { hidden_subfields: { no_follow: true } } }}
/>
```

**Example — custom field names (keys must match):**
```tsx
<Button.ContentFields
  buttonTextName="ctaText"
  buttonLinkName="ctaLink"
  fieldVisibility={{ ctaLink: { hidden_subfields: { no_follow: true } } }}
/>
```

**Fields:**
- `buttonText`: TextField for button text content
- `buttonLink`: LinkField for href destination (can be hidden with `showButtonLink={false}`)
- `icon`: Icon field using Icon.ContentFields
- `showIcon`: Toggle for icon visibility
- `buttonIconPosition`: ChoiceField for selecting icon placement (left, right). This field is only shown when the icon toggle is enabled. Its visibility uses `controlling_field_path` resolved via `useFieldPath(showIconName ?? 'showIcon')`, so it binds to the show-icon toggle's field at its actual path — including when the Button is nested in a repeater or field group.

#### StyleFields.tsx

Configurable props for variant:

```tsx
<Button.StyleFields
  buttonVariantLabel="Button style"
  buttonVariantName="buttonVariant"
  buttonVariantDefault={{ variant_name: 'primaryButton' }}
/>
```

**Fields:**
- `buttonVariant`: VariantSelectionField connected to theme settings (primaryButton, secondaryButton, tertiaryButton, accentButton)

### Module Usage Example

```tsx
import Button from '@hubspot/cms-component-library/Button';

export default function CTAModule({ fieldValues }) {
  return (
    <Button
      buttonType="link"
      variant={fieldValues.buttonVariant}
      href={fieldValues.buttonLink?.url?.href}
      target="_blank"
      rel="noopener noreferrer"
      showIcon={fieldValues.showIcon}
      iconFieldPath="icon"
      iconPosition="right"
    >
      {fieldValues.buttonText}
    </Button>
  );
}
```

## Styling

### Theme CSS Variables

The Button component is themed via `--hs-button-*` CSS variables, which are provided by the theme settings variant system (scoped via the variant prop and `data-button-variant` attribute).

**Base Styles (from theme):**
- `--hs-button-backgroundColor`: Background color
- `--hs-button-color`: Text color
- `--hs-button-borderRadius`: Border radius
- `--hs-button-borderStyle`: Border style
- `--hs-button-borderTopWidth`: Top border width
- `--hs-button-borderRightWidth`: Right border width
- `--hs-button-borderBottomWidth`: Bottom border width
- `--hs-button-borderLeftWidth`: Left border width
- `--hs-button-borderColor`: Border color
- `--hs-button-fontSize`: Font size
- `--hs-button-fontFamily`: Font family
- `--hs-button-fontStyle`: Font style
- `--hs-button-fontWeight`: Font weight
- `--hs-button-textDecoration`: Text decoration
- `--hs-button-padding`: Padding (falls back to `12px 24px`)

**Hover States (from theme):**
- `--hs-button-backgroundColor-hover`: Hover background (falls back to base)
- `--hs-button-color-hover`: Hover text color (falls back to base)
- `--hs-button-borderStyle-hover`: Hover border style (falls back to base)
- `--hs-button-borderTopWidth-hover`: Hover top border width (falls back to base)
- `--hs-button-borderRightWidth-hover`: Hover right border width (falls back to base)
- `--hs-button-borderBottomWidth-hover`: Hover bottom border width (falls back to base)
- `--hs-button-borderLeftWidth-hover`: Hover left border width (falls back to base)
- `--hs-button-borderColor-hover`: Hover border color (falls back to base)

**Focus States:**
The `:focus-visible` state reuses the hover CSS variables — there are no separate `*-focus` variables:
- `--hs-button-backgroundColor-hover` (also used for focus)
- `--hs-button-color-hover` (also used for focus)
- `--hs-button-borderStyle-hover` (also used for focus)
- `--hs-button-borderTopWidth-hover` (also used for focus)
- `--hs-button-borderRightWidth-hover` (also used for focus)
- `--hs-button-borderBottomWidth-hover` (also used for focus)
- `--hs-button-borderLeftWidth-hover` (also used for focus)
- `--hs-button-borderColor-hover` (also used for focus)

**Active States (from theme):**
- `--hs-button-backgroundColor-active`: Active background (falls back to base)
- `--hs-button-color-active`: Active text color (falls back to base)
- `--hs-button-borderStyle-active`: Active border style (falls back to base)
- `--hs-button-borderTopWidth-active`: Active top border width (falls back to base)
- `--hs-button-borderRightWidth-active`: Active right border width (falls back to base)
- `--hs-button-borderBottomWidth-active`: Active bottom border width (falls back to base)
- `--hs-button-borderLeftWidth-active`: Active left border width (falls back to base)
- `--hs-button-borderColor-active`: Active border color (falls back to base)

### Hardcoded Layout Values

The following properties use fixed values and are not configurable via CSS variables:

- **Gap:** 8px (space between text and icon)
- **Icon fill:** currentColor

The component itself does not declare a focus outline — the keyboard focus indicator is provided globally by the consuming theme's stylesheet (e.g. the focus-visible rule in `web-default-templates`'s `main.css`).

## Accessibility

The Button component follows accessibility best practices:

- **Semantic HTML**: Renders appropriate elements (`<button>` or `<a>`) based on context
- **Keyboard Navigation**: Native keyboard support for both button and link variants
  - Buttons: Space and Enter keys activate
  - Links: Enter key navigates
- **Focus Management**: CSS-based focus styles with outline for keyboard users
- **Disabled State**:
  - Only available for `buttonType="button"`
  - Sets `disabled` attribute and reduces opacity
  - Prevents click events and shows not-allowed cursor
- **Icon Accessibility**:
  - `iconPurpose="SEMANTIC"`: Icon conveys meaning (includes accessible title)
  - `iconPurpose="DECORATIVE"`: Icon is visual only (aria-hidden)
  - `iconTitle` provides screen reader description for semantic icons
- **Link Security**:
  - Use `rel="noopener noreferrer"` with `target="_blank"` to prevent security vulnerabilities


## Best Practices

- **Choose the right type**: Use `buttonType="button"` for actions (submit, toggle, open modal) and `buttonType="link"` for navigation
- **Never use `<a>` for actions**: If it's not navigating to a URL, use `buttonType="button"`
- **Always use `rel="noopener noreferrer"` with `target="_blank"`**: Prevents security vulnerabilities with external links
- **Button type requires JavaScript islands**: Interactive buttons with `onClick` handlers only work in JavaScript islands, not in static HubSpot modules
- **Icon positioning**: Place icons at the end (right) for forward actions, at the start (left) for back/previous actions
- **Icon purpose**: Use `SEMANTIC` when the icon adds meaning, `DECORATIVE` when it's purely visual
- **Disabled state**: Only use with `buttonType="button"`, not applicable to links
- **CSS Variables**: Override design tokens using CSS variables rather than hardcoding values
- **Variant system**: Variants are connected to theme settings via `VariantSelectionField` and the `data-button-variant` attribute. CSS variables are scoped automatically by the ContentUILib variant system.

## Common Patterns

### Call-to-Action (CTA) Button

```tsx
<Button
  buttonType="link"
  variant="primaryButton"
  href="/signup"
  showIcon={true}
  iconFieldPath="icon"
  iconPosition="right"
>
  Get Started
</Button>
```

### Form Submit Button

```tsx
<Button
  buttonType="button"
  variant="primaryButton"
  onClick={handleSubmit}
  disabled={isSubmitting}
>
  {isSubmitting ? 'Submitting...' : 'Submit Form'}
</Button>
```

### Navigation Button with Icon

```tsx
<Button
  buttonType="link"
  variant="secondaryButton"
  href="/dashboard"
  showIcon={true}
  iconFieldPath="icon"
  iconPosition="left"
>
  Back to Dashboard
</Button>
```

### External Resource Link

```tsx
<Button
  buttonType="link"
  variant="tertiaryButton"
  href="https://docs.hubspot.com"
  target="_blank"
  rel="noopener noreferrer"
  showIcon={true}
  iconFieldPath="icon"
  iconPosition="right"
>
  View Documentation
</Button>
```

## Related Components

- **Icon**: Used internally for icon rendering. Can be used standalone for custom icon implementations.



================================================================================
Source: components/componentLibrary/Card/llm.txt
================================================================================

# Card Component

A flexible, polymorphic container component that provides a consistent visual structure for grouping related content with theme variant styling.

## Import path
```tsx
import Card from '@hubspot/cms-component-library/Card';
```

## Purpose

The Card component provides a reusable container for grouping related content in HubSpot CMS projects. It uses the theme variant system (via `data-card-variant` attribute) to apply visual styles like background color, text color, border radius, and borders. Use it when you need to visually separate and organize content into distinct sections with theme-consistent styling.

## Component Structure

```
Card/
├── index.tsx                    # Main component with render logic
├── types.ts                     # TypeScript type definitions
├── StyleFields.tsx              # HubSpot field definitions for styling (VariantSelectionField)
├── index.module.scss            # CSS module consuming theme variant CSS variables
└── stories/
    ├── Card.stories.tsx         # Storybook examples
    ├── CardDecorator.tsx        # Storybook decorator
    └── CardDecorator.module.scss # Decorator styles with variant variable definitions
```

## Components

### Card (Main Component)

**Purpose:** Polymorphic container component that renders as a `<div>`, `<article>`, or `<section>` element with theme variant styling applied via the `data-card-variant` attribute.

**Props:**
```tsx
{
  variant?: 'card1' | 'card2' | 'card3' | 'card4';  // Theme variant (default: 'card1')
  as?: 'div' | 'article' | 'section';             // HTML element to render (default: 'div')
  children?: React.ReactNode;                     // Card content
  className?: string;                             // Additional CSS classes
  style?: React.CSSProperties;                    // Inline styles
}
```

## Usage Examples

### Basic Card with Content

```tsx
import Card from '@hubspot/cms-component-library/Card';

<Card>
  <h3>Card Title</h3>
  <p>This is the card content.</p>
</Card>
```

### Semantic Article Card

```tsx
<Card as="article" variant="card3">
  <h3>Blog Post Title</h3>
  <p>Article content goes here...</p>
</Card>
```

### Dynamic Cards from Data

```tsx
const features = [
  { title: 'Analytics', description: 'Track performance metrics' },
  { title: 'Integration', description: 'Connect with existing tools' },
  { title: 'Support', description: '24/7 customer assistance' }
];

<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '16px' }}>
  {features.map((feature) => (
    <Card key={feature.title}>
      <h3>{feature.title}</h3>
      <p>{feature.description}</p>
    </Card>
  ))}
</div>
```

## HubSpot CMS Integration

### Field Definitions

The Card component uses `VariantSelectionField` from `@hubspot/cms-components/fields` to allow theme variant selection in the CMS editor.

#### StyleFields.tsx

Configurable props for customizing field labels, names, and defaults:

```tsx
<Card.StyleFields
  cardVariantLabel="Card style"
  cardVariantName="cardVariant"
  cardVariantDefault={{ variant_name: 'card1' }}
/>
```

**Fields:**
- `cardVariant`: VariantSelectionField for selecting theme variant (variantDefinitionName: "card")

### Module Usage Example

```tsx
import Card from '@hubspot/cms-component-library/Card';

export default function FeatureCardModule({ fieldValues }) {
  return (
    <Card
      variant={fieldValues.cardVariant.variant_name}
      as="article"
    >
      <h3>{fieldValues.title}</h3>
      <p>{fieldValues.description}</p>
    </Card>
  );
}

FeatureCardModule.fields = (
  <>
    <Card.StyleFields />
    <TextField label="Title" name="title" default="Feature title" />
    <TextField label="Description" name="description" default="Feature description" />
  </>
);
```

## Styling

### Theme Variant CSS Variables (provided by the variant system via `data-card-variant`)

- `--hs-card-borderRadius`: Border radius
- `--hs-card-backgroundColor`: Background color
- `--hs-card-color`: Text color
- `--hs-card-borderStyle`: Border style (e.g. `solid`, `none`)
- `--hs-card-borderTopWidth`: Top border width (e.g. `1px`)
- `--hs-card-borderRightWidth`: Right border width (e.g. `1px`)
- `--hs-card-borderBottomWidth`: Bottom border width (e.g. `1px`)
- `--hs-card-borderLeftWidth`: Left border width (e.g. `1px`)
- `--hs-card-borderColor`: Border color
- `--hs-card-padding`: Padding (falls back to `24px`)


## Accessibility

The Card component follows accessibility best practices:

- **Semantic HTML**: Use the `as` prop to render appropriate semantic elements (`article` for blog posts, `section` for page sections, `div` for generic containers)
- **Content Structure**: Ensure proper heading hierarchy within card content (e.g., if cards are in an h2 section, use h3 for card titles)
- **Color Contrast**: Theme variants should meet WCAG AA contrast requirements; verify custom colors maintain sufficient contrast
- **Focus Management**: Cards themselves are not interactive, but ensure interactive elements within cards (buttons, links) have proper focus styles

## Best Practices

- **Choose semantic elements**: Use `as="article"` for self-contained content, `as="section"` for thematic groupings, and `as="div"` (default) for generic containers
- **Border radius via theme variants**: Border radius is controlled by the variant system, not a direct prop.
- **Override CSS variables**: Customize appearance using CSS variables rather than overriding class styles
- **Grid layouts**: Use CSS Grid or Flexbox for card layouts rather than relying on card margins
- **Content flexibility**: Cards are content-agnostic containers; structure internal content using appropriate semantic elements

## Related Components

- **Flex**: Use for arranging cards in flexible layouts
- **Grid**: Use for creating responsive card grids
- **Text**: Use with `textFeatureSet="heading"` for editable card titles


================================================================================
Source: components/componentLibrary/Carousel/llm.txt
================================================================================

# Carousel Component

A compound component for user-navigated slide carousels. Built on [embla-carousel](https://www.embla-carousel.com/), it exposes a composable set of sub-components that all communicate via a shared React context.

## Import path

```tsx
import Carousel, {
  CarouselTrack,
  CarouselSlide,
  CarouselPrev,
  CarouselNext,
  CarouselDots,
  CarouselCounter,
} from '@hubspot/cms-component-library/Carousel';
```

## When to use Carousel vs Ticker

Use **Carousel** when:
- Content is finite and users need to navigate through individual slides (testimonials, product images, team member cards, hero variants)
- Users may want to pause on a specific slide to read or interact with it
- You need pagination controls (prev/next buttons, dot indicators, slide counter)
- The relationship between slides matters — each is a discrete piece of content

Use **Ticker** instead when:
- Content should scroll continuously with no user navigation (logo strips, announcement banners, partner lists)
- The animation is ambient / decorative and users don't need to interact with individual items

## Island Requirement

**Carousel must be rendered inside an island.** It uses `useState`, `useEffect`, `useCallback`, and the Embla Carousel browser API — none of which can run during server-side rendering. Placing `<Carousel>` directly in a module's `Component` export will fail.

The correct pattern is:
1. Create an `islands/` directory in your module folder
2. Put all `Carousel` usage inside an island file (e.g. `islands/CarouselIsland.tsx`) with a default export
3. In the module's `index.tsx`, import the island with the `?island` suffix and render it via `<Island module={...} />`

```tsx
// modules/MyModule/islands/CarouselIsland.tsx
import Carousel, { CarouselTrack, CarouselSlide, CarouselPrev, CarouselNext, CarouselDots } from '@hubspot/cms-component-library/Carousel';

const CarouselIsland = ({ slides }) => (
  <Carousel>
    <CarouselTrack>
      {slides.map((slide, i) => (
        <CarouselSlide key={i}>{slide.content}</CarouselSlide>
      ))}
    </CarouselTrack>
    <CarouselPrev />
    <CarouselNext />
    <CarouselDots />
  </Carousel>
);

export default CarouselIsland;
```

```tsx
// modules/MyModule/index.tsx
import { Island } from '@hubspot/cms-components';
// @ts-expect-error -- ?island not typed
import CarouselIsland from './islands/CarouselIsland.js?island';

export const Component = ({ slides }) => (
  <Island module={CarouselIsland} slides={slides} />
);
```

## Component Structure

```
Carousel/
├── index.tsx                  # Provider component + all named exports
├── types.ts                   # TypeScript type definitions
├── context.ts                 # CarouselContext + useCarouselContext hook
├── hooks/
│   └── useContainerBreakpoint.ts  # Container-width breakpoint hook (ResizeObserver)
├── Track/
│   ├── index.tsx              # Embla viewport — controls scroll behavior
│   └── index.module.scss
├── Slide/
│   ├── index.tsx              # Wrapper for a single slide
│   └── index.module.scss
├── Prev/
│   ├── index.tsx              # Previous button with default icon
│   └── index.module.scss
├── Next/
│   ├── index.tsx              # Next button with default icon
│   └── index.module.scss
├── Dots/
│   ├── index.tsx              # Dot indicators with click-to-navigate
│   └── index.module.scss
└── Counter/
    ├── index.tsx              # "1 / 5" style slide counter
    └── index.module.scss
```

## Components

### Carousel (Provider)

**Purpose:** Root context provider. Manages shared state (`emblaApi`, `currentIndex`, `slideCount`, `canScrollPrev`, `canScrollNext`). All other sub-components must be descendants of `<Carousel>`.

**Props:**
```tsx
{
  children: React.ReactNode;
}
```

### CarouselTrack

**Purpose:** The scrollable viewport. Initializes `embla-carousel-react`, sets up autoplay (disabled in the HubSpot editor), and syncs scroll state into context. This is the only required sub-component — all `CarouselSlide` elements go inside it.

**Props:**
```tsx
{
  loop?: boolean;                         // Whether to loop at the ends (default: true)
  perPage?: number;                       // Slides visible at once (default: 1)
  gap?: number | string;                  // Gap between slides — number = px, string = any CSS unit
  align?: 'start' | 'center' | 'end';    // Slide alignment within the viewport (default: 'start')
  slidesToScroll?: 'auto' | number;       // How many slides to advance per scroll (default: 1)
  dragFree?: boolean;                     // Freeform drag (disabled in editor) (default: false)
  autoplay?: boolean;                     // Auto-advance slides (disabled in editor) (default: false)
  autoplayInterval?: number;              // Milliseconds between auto-advances (default: 3000)
  breakpoints?: Array<{
    minWidth: number | string;            // Threshold in px — number or string e.g. 768 or '768px'
    options: {                            // Any subset of the props above (except breakpoints itself)
      loop?: boolean;
      perPage?: number;
      gap?: number | string;
      align?: 'start' | 'center' | 'end';
      slidesToScroll?: 'auto' | number;
      dragFree?: boolean;
      autoplay?: boolean;
      autoplayInterval?: number;
    };
  }>;
  className?: string;
  style?: React.CSSProperties;
  children: React.ReactNode;             // Should be <CarouselSlide> elements
}
```

**Editor behavior:** When rendered inside the HubSpot editor, `dragFree` and `watchDrag` are both forced to `false`, and autoplay plugins are not attached. This keeps slides stable for editing.

**Reduced motion:** When `prefers-reduced-motion: reduce` is set, slide transition duration is set to `0`.

**CSS container:** `CarouselTrack` wraps the Embla viewport in a `div` that declares `container-type: inline-size; container-name: carousel`. This is used internally by the breakpoint system (ResizeObserver reads the container's width). Consumers can also write `@container carousel (min-width: Npx) { ... }` rules in their own stylesheets to style slide content at different carousel widths — but slide sizing itself should be controlled through the `breakpoints` prop, not CSS variable overrides.

**CSS variables:** The Track component sets `--carousel-slide-size` and `--carousel-slide-spacing` as inline styles on the viewport element after the first ResizeObserver measurement. Slides reference them via `var(--carousel-slide-size, 100%)` and `var(--carousel-slide-spacing, 0px)`. Before the first measurement, no inline CSS vars are injected, so slides render at the CSS default (100% width, 0 spacing).

**Deferred initialization:** Embla is not initialized until the first ResizeObserver measurement fires. This ensures Embla sees the correct breakpoint-resolved options on its first init rather than initializing with base options and immediately reInit-ing.

## Responsive Breakpoints

`CarouselTrack` accepts a `breakpoints` prop that maps container-width thresholds to option overrides. All responsive behavior — including `perPage`, `gap`, and behavioral options like `loop` and `align` — flows through this prop.

Breakpoints are evaluated against the **carousel's own width** (not the viewport), so they work correctly inside HubSpot's Editor 2.0 side-by-side device-preview canvas where the viewport stays full-width but the carousel container is physically constrained to the preview width.

Breakpoints are applied mobile-first: all entries whose `minWidth` ≤ the current container width are matched and merged in ascending order, with later entries overriding earlier ones.

### Basic usage

```tsx
<CarouselTrack
  loop
  perPage={1}
  gap={16}
  align="center"
  breakpoints={[
    { minWidth: 768,  options: { perPage: 2, gap: 24 } },
    { minWidth: 1024, options: { perPage: 3, gap: 32, loop: false, align: 'start', slidesToScroll: 3 } },
  ]}
>
  {slides}
</CarouselTrack>
```

`minWidth` accepts a number (`768`) or a px string (`'768px'`). Options are the same fields as the base `CarouselTrack` props (excluding `breakpoints`, `className`, `style`, `children`).

### How it works

1. `useContainerBreakpoint` attaches a `ResizeObserver` to the viewport element and returns `null` until the first measurement fires.
2. While `null`, no CSS vars are injected (slides render at the CSS default 100%) and Embla is not initialized.
3. Once the observer fires, the matched breakpoints are merged over the base props and the resolved `perPage`/`gap` values are injected as `--carousel-slide-size` and `--carousel-slide-spacing` inline styles. Embla is then initialized with the resolved behavioral options.
4. On subsequent resizes that cross a threshold, the matched set updates, CSS vars are re-computed, and Embla reInits with the new options.

### Styling slide content at different widths

To style content inside slides responsively, use `@container carousel` rules in your module's CSS. The carousel wrapper already declares `container-name: carousel`, so no additional container declaration is needed:

```css
@container carousel (min-width: 768px) {
  .slideContent {
    background-color: #1a2b4a;
  }
  .slideHeading {
    font-size: 2rem;
  }
}
```

### `className` on CarouselSlide targets the inner content div

`className` and `style` on `<CarouselSlide>` are applied to the **inner content div**, not the outer Embla-managed div. Setting `height: 100%` on the inner div works because Embla's flex container stretches all outer wrappers to the height of the tallest slide (`align-items: stretch`). For image carousels, use `aspect-ratio` on the inner content div to reserve vertical space before images load and prevent layout shift.

---

### CarouselSlide

**Purpose:** Wrapper for a single slide's content. The outer `div` handles the Embla layout (flex-basis, min-width, padding). The inner `div` receives `className` and `style` for content styling.

**Props:**
```tsx
{
  className?: string;          // Applied to the inner content div
  style?: React.CSSProperties; // Applied to the inner content div
  children: React.ReactNode;
}
```

### CarouselPrev

**Purpose:** Button to scroll to the previous slide. Disabled when `canScrollPrev` is `false`. Renders a default left-arrow SVG icon; pass `children` to replace it with custom content.

**Props:**
```tsx
{
  className?: string;
  style?: React.CSSProperties;
  children?: React.ReactNode;  // Replaces the default arrow icon
}
```

### CarouselNext

**Purpose:** Button to scroll to the next slide. Disabled when `canScrollNext` is `false`. Renders a default right-arrow SVG icon; pass `children` to replace it with custom content.

**Props:**
```tsx
{
  className?: string;
  style?: React.CSSProperties;
  children?: React.ReactNode;  // Replaces the default arrow icon
}
```

### CarouselDots

**Purpose:** A row of dot buttons, one per slide. The active dot is the same size as inactive dots but filled with a darker color. Each dot scrolls directly to its corresponding slide on click.

**Props:**
```tsx
{
  className?: string;
  style?: React.CSSProperties;
}
```

**CSS variables (override on parent or via `style`):**
- `--carousel-dot-size`: dot diameter (default: `8px`) — applies to all dots
- `--carousel-dot-color`: inactive dot color (default: `#d1d5db`)
- `--carousel-dot-active-color`: active dot color, also used on hover (default: `#1a1a2e`)
- `--carousel-dot-active-width`: active dot width (default: `var(--carousel-dot-size, 8px)`) — override to create a wider pill shape
- `--carousel-dot-active-radius`: active dot border-radius (default: `50%`) — override to flatten the circle into a pill (e.g. `4px`)

### CarouselCounter

**Purpose:** Text display of the current slide position, e.g. `1 / 5`. Updates automatically as slides change.

**Props:**
```tsx
{
  separator?: string;          // Character(s) between current and total (default: '/')
  className?: string;
  style?: React.CSSProperties;
}
```

## Usage Examples

### Minimal Carousel

```tsx
import Carousel, { CarouselTrack, CarouselSlide, CarouselPrev, CarouselNext } from '@hubspot/cms-component-library/Carousel';

<Carousel>
  <CarouselTrack>
    <CarouselSlide><div>Slide 1</div></CarouselSlide>
    <CarouselSlide><div>Slide 2</div></CarouselSlide>
    <CarouselSlide><div>Slide 3</div></CarouselSlide>
  </CarouselTrack>
  <CarouselPrev />
  <CarouselNext />
</Carousel>
```

### Carousel with Dots and Counter

```tsx
<Carousel>
  <CarouselTrack loop={true}>
    <CarouselSlide><div>Slide 1</div></CarouselSlide>
    <CarouselSlide><div>Slide 2</div></CarouselSlide>
    <CarouselSlide><div>Slide 3</div></CarouselSlide>
  </CarouselTrack>
  <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', gap: '16px', marginTop: '16px' }}>
    <CarouselPrev />
    <CarouselDots />
    <CarouselNext />
  </div>
  <CarouselCounter separator="of" />
</Carousel>
```

### Multi-slide Carousel with Gap

```tsx
<Carousel>
  <CarouselTrack perPage={3} gap={24} loop={false} align="start">
    <CarouselSlide><div>Item 1</div></CarouselSlide>
    <CarouselSlide><div>Item 2</div></CarouselSlide>
    <CarouselSlide><div>Item 3</div></CarouselSlide>
    <CarouselSlide><div>Item 4</div></CarouselSlide>
    <CarouselSlide><div>Item 5</div></CarouselSlide>
  </CarouselTrack>
  <CarouselPrev />
  <CarouselNext />
</Carousel>
```

### Autoplay Carousel

```tsx
<Carousel>
  <CarouselTrack autoplay={true} autoplayInterval={5000} loop={true}>
    <CarouselSlide><div>Slide A</div></CarouselSlide>
    <CarouselSlide><div>Slide B</div></CarouselSlide>
    <CarouselSlide><div>Slide C</div></CarouselSlide>
  </CarouselTrack>
  <CarouselDots />
</Carousel>
```

### Custom Arrow Icons

```tsx
<Carousel>
  <CarouselTrack>
    <CarouselSlide><div>Slide 1</div></CarouselSlide>
    <CarouselSlide><div>Slide 2</div></CarouselSlide>
  </CarouselTrack>
  <CarouselPrev>← Back</CarouselPrev>
  <CarouselNext>Next →</CarouselNext>
</Carousel>
```

### Module Usage Example

Carousel must live inside an island. The module's `Component` export uses `<Island>` to load it; all Carousel logic lives in the island file.

```tsx
// modules/TestimonialsModule/islands/TestimonialsIsland.tsx
import Carousel, {
  CarouselTrack,
  CarouselSlide,
  CarouselPrev,
  CarouselNext,
  CarouselDots,
} from '@hubspot/cms-component-library/Carousel';

type Testimonial = { quote: string; author: string };

type Props = {
  testimonials: Testimonial[];
  autoplay: boolean;
  autoplayInterval: number;
};

const TestimonialsIsland = ({ testimonials, autoplay, autoplayInterval }: Props) => (
  <Carousel>
    <CarouselTrack
      loop={true}
      autoplay={autoplay}
      autoplayInterval={autoplayInterval}
      gap={16}
    >
      {testimonials.map((item, i) => (
        <CarouselSlide key={i}>
          <blockquote>{item.quote}</blockquote>
          <cite>{item.author}</cite>
        </CarouselSlide>
      ))}
    </CarouselTrack>
    <div style={{ display: 'flex', justifyContent: 'center', gap: '12px', marginTop: '24px' }}>
      <CarouselPrev />
      <CarouselDots />
      <CarouselNext />
    </div>
  </Carousel>
);

export default TestimonialsIsland;
```

```tsx
// modules/TestimonialsModule/index.tsx
import { Island } from '@hubspot/cms-components';
// @ts-expect-error -- ?island not typed
import TestimonialsIsland from './islands/TestimonialsIsland.js?island';

type ComponentProps = {
  testimonials: { quote: string; author: string }[];
  autoplay: boolean;
  autoplayInterval: number;
};

export const Component = ({ testimonials = [], autoplay = false, autoplayInterval = 3000 }: ComponentProps) => (
  <Island
    module={TestimonialsIsland}
    testimonials={testimonials}
    autoplay={autoplay}
    autoplayInterval={autoplayInterval}
  />
);
```

## Accessibility

- **`CarouselPrev` / `CarouselNext`**: Both have `aria-label` ("Previous slide" / "Next slide") and are `disabled` when scrolling is not possible — including before Embla has initialized, so they're never focusable while the carousel is fading in
- **`CarouselDots`**: The container has `aria-label="Slide navigation"`; each dot button has `aria-label="Go to slide N"` and `aria-current` on the active dot
- **`CarouselCounter`**: Has `aria-live="polite"` and `aria-atomic="true"` so screen readers announce slide changes
- **Reduced motion**: Transition duration is set to `0` when `prefers-reduced-motion: reduce` is active; autoplay does not start

## Slide Height — Making All Slides the Same Height

### Why slides can differ in height

`CarouselSlide` renders two nested divs:

```
<div>                  ← outer Embla wrapper (auto-stretches to tallest slide via flex align-items: stretch)
  <div className ...>  ← inner content div (receives your className / style)
    {children}
  </div>
</div>
```

The outer wrapper is automatically stretched to the height of the tallest slide because the track container is `display: flex` with `align-items: stretch` (the default). The inner content div is **not** — it only grows as tall as its content unless you explicitly set `height: 100%`. Without it, slides with less content will appear shorter than slides with more content.

**Default rule:** For text-based carousels, always apply `height: 100%` to the inner content div so all slides fill the stretched outer wrapper uniformly. Image-based carousels have layout trade-offs (see below) and the right strategy depends on the design intent.

---

### Text-based slides — equal height (default)

Apply `height: 100%` to the inner content div. Pair it with `min-height` to prevent the carousel from collapsing when content is very sparse, and `box-sizing: border-box` so padding doesn't cause overflow.

```css
/* carouselIsland.module.css */
.slide {
  height: 100%;             /* fills the auto-stretched Embla outer wrapper */
  min-height: 320px;        /* floor so the carousel is never too short */
  padding: 56px 64px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 16px;
  box-sizing: border-box;
  background: #1a1a2e;
  color: #fff;
}

.eyebrow {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.6);
}

.heading {
  margin: 0;
  font-size: 2rem;
  font-weight: 700;
  line-height: 1.2;
}

.body {
  margin: 0;
  font-size: 1rem;
  line-height: 1.65;
  color: rgba(255, 255, 255, 0.72);
}
```

```tsx
{slides.map((slide, i) => (
  <CarouselSlide key={i} className={styles.slide}>
    {slide.eyebrow && <span className={styles.eyebrow}>{slide.eyebrow}</span>}
    <h2 className={styles.heading}>{slide.heading}</h2>
    <p className={styles.body}>{slide.body}</p>
  </CarouselSlide>
))}
```

All slides grow to the height of the tallest slide. The shortest slides gain extra space at the bottom (since `justify-content: center` distributes it evenly above and below the content stack).

---

### Image-based slides — choose a strategy

Unlike text carousels, there is no universally correct layout for images. The right choice depends on the design. The two main strategies are **cover (clip)** and **contain (letterbox)**.

#### Strategy 1: Cover — image fills the box, excess is clipped

The image always fills the slide completely. Parts of the image that fall outside the fixed aspect ratio are cropped. This is the most common choice for hero and background-style image carousels.

**Effect:** Image is never distorted, never letterboxed. Edges may be cropped depending on the image's aspect ratio vs. the slide's fixed size.

```css
/* carouselIsland.module.css */
.slide {
  height: 480px;            /* fixed height — all slides are identical */
  position: relative;
  overflow: hidden;
}

.slideImage {
  width: 100%;
  height: 100%;
  object-fit: cover;        /* fills the box, crops the overflow */
  object-position: center;  /* crop anchor — adjust per design (e.g. 'top', '20% 80%') */
  display: block;
}

.slideCaption {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  padding: 24px 32px;
  background: linear-gradient(transparent, rgba(0, 0, 0, 0.55));
  color: #fff;
}
```

```tsx
{slides.map((slide, i) => (
  <CarouselSlide key={i} className={styles.slide}>
    <img src={slide.image.url} alt={slide.image.alt} className={styles.slideImage} />
    {slide.caption && <p className={styles.slideCaption}>{slide.caption}</p>}
  </CarouselSlide>
))}
```

#### Strategy 2: Contain — full image visible, gaps are letterboxed

The entire image is always visible. If the image's aspect ratio doesn't match the slide's aspect ratio, the remaining space is filled with a background color (letterbox). Use this when showing the full image matters more than filling the slide edge-to-edge (e.g., product photography on a white background).

**Effect:** Image is never distorted, never cropped. Letterbox bars appear when aspect ratios differ.

```css
/* carouselIsland.module.css */
.slide {
  height: 480px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #f5f5f5;      /* letterbox color — match your design background */
}

.slideImage {
  width: 100%;
  height: 100%;
  object-fit: contain;      /* full image visible, gaps filled with background */
  display: block;
}
```

```tsx
{slides.map((slide, i) => (
  <CarouselSlide key={i} className={styles.slide}>
    <img src={slide.image.url} alt={slide.image.alt} className={styles.slideImage} />
  </CarouselSlide>
))}
```

#### Summary

| Strategy | `object-fit` | Image distorted? | Image cropped? | Letterbox bars? | Best for |
|---|---|---|---|---|---|
| Cover | `cover` | No | Yes (edges) | No | Hero images, backgrounds, photography |
| Contain | `contain` | No | No | Yes (when AR differs) | Product shots, logos, images where full visibility matters |

For image carousels, do not apply `height: 100%` to the inner div — use a fixed `height` (or fixed aspect ratio via `aspect-ratio`) on the slide itself so all slides share an explicit shared size rather than inheriting from the tallest slide's content.

---

## Constraints

- All sub-components (`CarouselTrack`, `CarouselPrev`, etc.) must be rendered inside a `<Carousel>` provider; using them outside throws an error: `"Carousel sub-components must be used within <Carousel>"`
- There are no field definitions (`ContentFields` / `StyleFields`) for this component — the module author wires up fields directly in their module's fields file
- `perPage` and `gap` are applied as CSS variables on the track element, not as Embla options — responsive per-page and gap changes should be handled via the `breakpoints` prop


================================================================================
Source: components/componentLibrary/Divider/llm.txt
================================================================================

# Divider Component

A visual separator component that can be oriented horizontally or vertically to divide content sections. Creates semantic separation using native HTML elements with customizable styling.

## Import path
```tsx
import Divider from '@hubspot/cms-component-library/Divider';
```

## Purpose

The Divider component provides a consistent way to create visual separators in HubSpot CMS projects. It renders semantic HTML elements (`<hr>` for horizontal, `<div role="separator">` for vertical) with flexible styling options. Developers should use this component when they need to visually separate content sections, whether between stacked content (horizontal) or side-by-side content (vertical). The component supports customizable line type, thickness, length, alignment, color, and spacing through props and CSS variables.

## Component Structure

```
Divider/
├── index.tsx                     # Main component with render logic
├── types.ts                      # TypeScript type definitions
├── StyleFields.tsx               # HubSpot field definitions for styling
├── index.module.scss             # CSS module with design tokens
└── stories/
    ├── Divider.stories.tsx       # Component usage examples
    ├── DividerDecorator.tsx      # Storybook decorator
    └── DividerDecorator.module.scss  # Decorator styles
```

## Components

### Divider (Main Component)

**Purpose:** Renders a visual separator that adapts its HTML element and styling based on orientation.

**Props:**
```tsx
{
  orientation?: 'horizontal' | 'vertical';                     // Divider orientation (default: 'horizontal')
  alignment?: {                                                 // Alignment within parent container
    horizontal_align?: 'LEFT' | 'CENTER' | 'RIGHT';
    vertical_align?: 'TOP' | 'MIDDLE' | 'BOTTOM';
  };
  spacing?: `${number}px`;                                     // Margin spacing in pixel format (e.g., '16px', '24px')
  borderStyle?: 'solid' | 'dotted' | 'dashed' | 'double';     // Border line style (default: 'solid')
  length?: number;                                             // Length as percentage (1-100) of available space (default: 100)
  thickness?: number;                                          // Border thickness in pixels (default: 1)
  color?: { rgba?: string; color?: string; opacity?: number }; // Line color (from ColorField or rgba value)
  className?: string;                                          // Additional CSS classes
  style?: React.CSSProperties;                                 // Inline styles (including CSS variables)
}
```

**Rendering Behavior:**
- `orientation="horizontal"`: Renders as `<hr>` element with top border
- `orientation="vertical"`: Renders as `<div role="separator">` with left border

**Alignment Behavior:**

When no `alignment` is passed, the divider stretches to fill the available space (default).

For horizontal dividers, use `horizontal_align`:
- `'LEFT'`: Aligns to the left (`flex-start`)
- `'CENTER'`: Centers within the container
- `'RIGHT'`: Aligns to the right (`flex-end`)

For vertical dividers, use `vertical_align`:
- `'TOP'`: Aligns to the top (`flex-start`)
- `'MIDDLE'`: Centers vertically
- `'BOTTOM'`: Aligns to the bottom (`flex-end`)

## Usage Examples

### Basic Horizontal Divider

```tsx
import Divider from '@hubspot/cms-component-library/Divider';

// Default horizontal divider (full width, stretch)
<Divider />
```

### Basic Vertical Divider

```tsx
// Vertical separator between content
<Divider orientation="vertical" />
```

### Horizontal Divider with Custom Spacing

```tsx
<Divider spacing="32px" />
```

### Short Centered Divider

```tsx
<Divider length={50} alignment={{ horizontal_align: 'CENTER' }} spacing="16px" />
```

### Customized Divider

```tsx
<Divider
  borderStyle="dashed"
  thickness={2}
  length={75}
  alignment={{ horizontal_align: 'CENTER' }}
  spacing="24px"
/>
```

### Colored Divider

```tsx
// color.rgba is populated by the ColorField in a module context
<Divider color={{ rgba: 'rgba(59, 130, 246, 1)' }} thickness={2} />
```

### Vertical Divider with Alignment

```tsx
<Divider
  orientation="vertical"
  length={50}
  alignment={{ vertical_align: 'MIDDLE' }}
/>
```

### Vertical Divider in Flex Layout

```tsx
import Flex from '@hubspot/cms-component-library/Flex';
import Divider from '@hubspot/cms-component-library/Divider';

<Flex direction="row" gap="16px" alignItems="center">
  <div>Left Content</div>
  <Divider orientation="vertical" />
  <div>Right Content</div>
</Flex>
```

## HubSpot CMS Integration

### Field Definitions

All Divider fields are style fields. The Divider component exposes only `StyleFields` — there are no content fields.

#### StyleFields

All configurable style props for the divider:

```tsx
<Divider.StyleFields
  borderStyleLabel="Line type"
  borderStyleName="borderStyle"
  borderStyleDefault="solid"
  thicknessLabel="Line thickness"
  thicknessName="thickness"
  thicknessDefault={1}
  lengthLabel="Line length"
  lengthName="length"
  lengthDefault={100}
  alignmentLabel="Line alignment"
  alignmentName="alignment"
  alignmentDefault={{ horizontal_align: 'CENTER', vertical_align: 'MIDDLE' }}
  alignmentDirection="HORIZONTAL"
  colorLabel="Line color"
  colorName="color"
  colorDefault={{ color: '#000000', opacity: 100 }}
/>
```

**Fields:**
- `borderStyle`: ChoiceField for line type (solid, dotted, dashed, double)
- `thickness`: NumberField for thickness in pixels (minimum: 1, suffix: px)
- `length`: NumberField for length as a percentage (1–100, suffix: %)
- `alignment`: AlignmentField for alignment within the container
- `color`: ColorField for line color (opacity hidden — color only; bound to global theme colors via `{ context: 'global_styles', collection: 'theme_colors' }`)

**Developer-only props (not user-facing):**
- `alignmentDirection`: Controls whether the AlignmentField shows horizontal or vertical options. Accepts `'HORIZONTAL'` (default) or `'VERTICAL'`. Set this based on how the divider will be used — developers consuming the component set this, not end users.
- `hideFields`: An array of field names to opt out of rendering. All fields are shown by default (opt-out). Accepts an array of `StyleFieldName` values: `'borderStyle' | 'color' | 'thickness' | 'length' | 'alignment'`.

**Hiding fields example:**

```tsx
<FieldGroup label="Style" name="style" tab="STYLE">
  <Divider.StyleFields hideFields={['alignment', 'length']} />
</FieldGroup>
```

**Note:** The `orientation` and `spacing` props must be set directly on the Divider component — there are no field definitions for them.

### Module Usage Example

```tsx
import Divider from '@hubspot/cms-component-library/Divider';

export default function SectionDividerModule({ fieldValues }) {
  const { borderStyle, thickness, length, alignment, color } = fieldValues.style;

  return (
    <Divider
      borderStyle={borderStyle}
      thickness={thickness}
      length={length}
      alignment={alignment}
      color={color}
      spacing="24px"
    />
  );
}
```

### Module Fields Example

```tsx
import { FieldGroup, ModuleFields } from '@hubspot/cms-components/fields';
import Divider from '@hubspot/cms-component-library/Divider';

export const fields = (
  <ModuleFields>
    <FieldGroup label="Style" name="style" tab="STYLE">
      <Divider.StyleFields />
    </FieldGroup>
  </ModuleFields>
);
```

For a vertical divider module, pass `alignmentDirection` to expose vertical alignment options:

```tsx
<FieldGroup label="Style" name="style" tab="STYLE">
  <Divider.StyleFields alignmentDirection="VERTICAL" />
</FieldGroup>
```

## Styling

The Divider component applies all styling via inline styles derived from props. The CSS module provides hardcoded defaults (1px solid currentColor, stretch alignment, 16px margin) which are overridden by prop values when provided. No CSS variables are used.

## Accessibility

The Divider component follows accessibility best practices:

- **Semantic HTML**:
  - Horizontal: Uses native `<hr>` element, recognized by screen readers as thematic break
  - Vertical: Uses `<div role="separator">` for proper ARIA semantics
- **Keyboard Navigation**: Does not interfere with keyboard navigation as it's non-interactive
- **Screen Reader Support**: Properly announced by assistive technologies as content separators

## Best Practices

- **Choose the right orientation**:
  - Use `horizontal` for separating stacked content sections (most common)
  - Use `vertical` for separating side-by-side content within a Flex or Grid layout
- **Alignment considerations**:
  - Omit `alignment` for full-width/height stretch behavior (default)
  - Use `{ horizontal_align: 'CENTER' }` for decorative short horizontal dividers
  - Use `{ vertical_align: 'MIDDLE' }` for centered vertical dividers
  - Set `alignmentDirection` on `StyleFields` to match the intended orientation so users see the right alignment options
- **Spacing guidelines**:
  - Use `spacing` prop to control margin around the divider
  - For horizontal dividers, spacing controls top/bottom margin
  - For vertical dividers, spacing controls left/right margin
- **Length as percentage**: The `length` prop accepts 1–100 as a percentage, not pixel values
- **Thickness control**: Use the `thickness` prop for border thickness (in pixels). Double border style requires at least 3px
- **Color**: The `color` prop is populated from the `ColorField` in a module context. In non-module usage, pass `{ rgba: 'rgba(...)' }` directly
- **Line type**: Choose appropriate border style for visual hierarchy:
  - `solid`: Default, clear separation
  - `dashed`: Less prominent, informal
  - `dotted`: Subtle, minimal separation
  - `double`: Emphasis, formal


================================================================================
Source: components/componentLibrary/Drawer/llm.txt
================================================================================

# Drawer Component

A slide-out panel component that overlays content from the side (left/right), top, or bottom of the viewport. Provides a modal-like experience for displaying supplementary content, navigation menus, or forms without navigating away from the current page.

## Import path
```tsx
import Drawer, { useDrawer } from '@hubspot/cms-component-library/Drawer';
```

## Purpose

The Drawer component provides a consistent way to create slide-out panels. It renders a fixed-position container that slides in from any edge of the viewport with smooth animations. The component automatically manages body scroll locking, keyboard interactions (ESC to close), and accessibility attributes. Use this component when you need to display additional content without losing the current page context, such as navigation menus, filters, settings panels, or detailed forms.

## Component Structure

```
Drawer/
├── index.tsx                       # Main component with render logic
├── types.ts                        # TypeScript type definitions
├── drawerStack.ts                  # Shared drawer stack for nested drawer management
├── hooks/
│   ├── index.tsx                   # useDrawer hook for state management
│   └── useFocusTrap.ts             # Focus trap hook used internally by Drawer
├── index.module.scss               # CSS module with animations
└── stories/
    ├── Drawer.stories.tsx          # Component usage examples
    ├── DrawerDecorator.tsx         # Storybook decorator
    └── DrawerDecorator.module.scss # Decorator styles
```

## Components

### Drawer (Main Component)

**Purpose:** Renders a fixed-position panel that slides in from any viewport edge with overlay support and body scroll locking.

**Props:**
```tsx
{
  open: boolean;                                        // Controls drawer visibility (required)
  direction: 'left' | 'right' | 'top' | 'bottom';       // Edge from which drawer slides (required)
  className?: string;                                   // Additional CSS classes
  style?: React.CSSProperties;                          // Inline styles
  children?: React.ReactNode;                           // Drawer content
  showOverlay?: boolean;                                // Show dark backdrop overlay (default: true)
  onOverlayClick?: () => void;                          // Callback when overlay is clicked
  fullScreen?: boolean;                                 // Make drawer fill entire viewport (default: false)
  size?: string;                                        // Drawer width/height in CSS units (default: '300px')
  zIndex?: number;                                      // Drawer panel z-index (default: 1000 from CSS)
  overlayZIndex?: number;                               // Overlay z-index (default: 999 from CSS)
  'aria-label'?: string;                                // Accessible label for the drawer
  'aria-labelledby'?: string;                           // ID of element that labels the drawer
}
```

### useDrawer Hook

**Purpose:** Provides drawer state management with open, close, and toggle functions.

**Return Type:**
```tsx
{
  isOpen: boolean;      // Current open/closed state
  open: () => void;     // Opens the drawer
  close: () => void;    // Closes the drawer
  toggle: () => void;   // Toggles drawer state
}
```

## Usage Examples

### Island Usage

The Drawer component requires JavaScript for interactive state management and must be used within an Island component.

#### Island Component

```tsx
// islands/DrawerIsland.tsx
import Drawer, { useDrawer } from '@hubspot/cms-component-library/Drawer';

export default function DrawerIsland({ menuItems, direction = 'right', size = '320px' }) {
  const { isOpen, open, close } = useDrawer();

  return (
    <>
      <button onClick={open}>Open Menu</button>
      <Drawer
        open={isOpen}
        direction={direction}
        size={size}
        onOverlayClick={close}
        aria-label="Navigation menu"
      >
        <nav style={{ padding: '24px' }}>
          <h2>Menu</h2>
          <ul>
            {menuItems?.map((item, index) => (
              <li key={index}>
                <a href={item.url}>{item.label}</a>
              </li>
            ))}
          </ul>
          <button onClick={close}>Close</button>
        </nav>
      </Drawer>
    </>
  );
}
```

#### Module File

```tsx
import { Island } from '@hubspot/cms-components';
import DrawerIsland from './islands/DrawerIsland?island';

export const Component = ({ fieldValues }) => {
  return (
    <Island
      module={DrawerIsland}
      menuItems={fieldValues.menuItems}
      direction={fieldValues.direction}
      size={fieldValues.size}
    />
  );
};
```

### Basic Drawer (Right Side)

```tsx
import Drawer, { useDrawer } from '@hubspot/cms-component-library/Drawer';

export default function MyComponent() {
  const { isOpen, open, close } = useDrawer();

  return (
    <>
      <button onClick={open}>Open Menu</button>
      <Drawer
        open={isOpen}
        direction="right"
        onOverlayClick={close}
        aria-label="Navigation menu"
      >
        <div style={{ padding: '24px' }}>
          <h2>Menu</h2>
          <nav>
            {/* Navigation content */}
          </nav>
          <button onClick={close}>Close</button>
        </div>
      </Drawer>
    </>
  );
}
```

### Left Side Drawer

```tsx
<Drawer
  open={isOpen}
  direction="left"
  onOverlayClick={close}
  aria-label="Side navigation"
>
  <div style={{ padding: '24px' }}>
    <h2>Navigation</h2>
    {/* Drawer content */}
  </div>
</Drawer>
```

### Top Drawer

```tsx
<Drawer
  open={isOpen}
  direction="top"
  size="400px"
  onOverlayClick={close}
  aria-label="Details panel"
>
  <div style={{ padding: '24px' }}>
    <h2>Details</h2>
    {/* Content */}
  </div>
</Drawer>
```

### Bottom Drawer

```tsx
<Drawer
  open={isOpen}
  direction="bottom"
  size="50vh"
  onOverlayClick={close}
  aria-label="Details panel"
>
  <div style={{ padding: '24px' }}>
    <h2>Details</h2>
    {/* Content */}
  </div>
</Drawer>
```

### Custom Size Drawer

```tsx
<Drawer
  open={isOpen}
  direction="right"
  size="500px"
  onOverlayClick={close}
  aria-label="Details panel"
>
  <div style={{ padding: '24px' }}>
    <h2>Details</h2>
    {/* Content */}
  </div>
</Drawer>
```

### Full Screen Drawer

```tsx
<Drawer
  open={isOpen}
  direction="right"
  fullScreen
  onOverlayClick={close}
  aria-label="Full screen content"
>
  <div style={{ padding: '48px' }}>
    <h1>Full Screen Content</h1>
    <p>This drawer takes up the entire viewport.</p>
    <button onClick={close}>Close</button>
  </div>
</Drawer>
```

### Without Overlay

```tsx
<Drawer
  open={isOpen}
  direction="right"
  showOverlay={false}
  aria-label="Side panel"
>
  <div style={{ padding: '24px' }}>
    <h2>Panel</h2>
    <p>Background remains visible and interactive.</p>
    <button onClick={close}>Close</button>
  </div>
</Drawer>
```

### With Toggle Control

```tsx
const { isOpen, toggle } = useDrawer();

<>
  <button onClick={toggle}>
    {isOpen ? 'Close' : 'Open'} Drawer
  </button>
  <Drawer
    open={isOpen}
    direction="right"
    showOverlay={false}
    aria-label="Toggleable drawer"
  >
    <div style={{ padding: '24px' }}>
      {/* Content */}
    </div>
  </Drawer>
</>
```

## HubSpot CMS Integration

### Field Definitions

The Drawer component is primarily interactive and does not provide pre-built field definitions. Since drawers are typically controlled programmatically (open/close state, callbacks), the component focuses on behavior rather than static content configuration. You can define your own fields based on the content you want to display inside the drawer (such as menu items, settings options, or form data).

### Module Usage Example

When integrating Drawer into a HubSpot module, create an Island component that manages drawer state and pass any necessary data from field values.

```tsx
import { Island } from '@hubspot/cms-components';
import DrawerIsland from './islands/DrawerIsland?island';

export default function NavigationModule({ fieldValues }) {
  return (
    <Island
      module={DrawerIsland}
      menuItems={fieldValues.menuItems}
      direction={fieldValues.direction || 'left'}
      size={fieldValues.size || '320px'}
    />
  );
}
```

## Styling

All styling uses hardcoded defaults. The `size` prop controls drawer width (for left/right) or height (for top/bottom) and is applied as an inline style. Background color defaults to `var(--hs-section-backgroundColor, white)` — it inherits the section's background color when the `--hs-section-backgroundColor` CSS custom property is set, and falls back to `white` otherwise. Box shadow and z-index (`1000`) are hardcoded in the CSS module. The overlay uses hardcoded defaults for background (`rgba(0, 0, 0, 0.5)`), backdrop blur (`blur(3px)`), and z-index (`999`). Use the `style` prop to pass standard `React.CSSProperties` overrides when needed.

## Accessibility

The Drawer component follows accessibility best practices:

- **Semantic HTML**: Uses `role="dialog"` and `aria-modal="true"` for proper semantics
- **Keyboard Navigation**:
  - `ESC` key closes the topmost open drawer
  - `Tab` and `Shift+Tab` cycle focus through focusable elements inside the open drawer and wrap at the ends, so keyboard users cannot escape into the background page
- **Screen Reader Support**:
  - `aria-label` or `aria-labelledby` should be provided to describe drawer purpose
  - `aria-hidden` attribute toggles based on open state
  - Proper ARIA roles announce drawer as a modal dialog
- **Body Scroll Locking**: Background content scroll is disabled when drawer is open
- **Focus Management**: When the drawer opens, focus moves to the first focusable element inside (or the drawer container if it has none). When the drawer closes, focus returns to whatever element had focus before it opened
- **Focus Trap**: While a drawer is open, `Tab`/`Shift+Tab` are constrained to focusable elements inside that drawer. With nested drawers, only the topmost drawer traps focus; closing it returns the trap to the next drawer in the stack
- **Visual Indicators**: Overlay provides clear visual feedback that drawer is modal
- **Reduced Motion**: Respects `prefers-reduced-motion` for users with motion sensitivity

## Best Practices

- **Use Islands for interactivity**: The Drawer component is interactive and requires JavaScript, so it must be used within an Island component in modules
- **Provide aria-label**: Always include `aria-label` or `aria-labelledby` to describe the drawer's purpose
- **Choose appropriate direction**:
  - `left` or `right`: Best for navigation menus, settings panels, or supplementary content
  - `top`: Good for notification panels or collapsible headers
  - `bottom`: Ideal for mobile-style action sheets or details panels
- **Size considerations**:
  - Consider `fullScreen` for complex workflows or extensive forms
- **Handle close actions**: Always provide a way to close the drawer (overlay click, close button)
- **Overlay usage**: Keep `showOverlay={true}` (default) for modal behavior, use `false` only for persistent sidebars
- **Body scroll locking**: The component automatically prevents background scrolling when open
- **ESC key support**: The drawer closes automatically when ESC is pressed (built-in)
- **Content structure**: Include a clear heading and close button within drawer content for usability
- **Avoid nested drawers**: Don't open multiple non-full-screen drawers simultaneously as it creates confusing UX
- **State management**: Use the `useDrawer` hook for consistent state management
- **Loading states**: Consider showing loading indicators within drawer content during async operations

## Common Patterns

### Navigation Menu Drawer

```tsx
import Drawer, { useDrawer } from '@hubspot/cms-component-library/Drawer';

export default function NavigationDrawer({ menuItems }) {
  const { isOpen, open, close } = useDrawer();

  return (
    <>
      <button onClick={open}>Menu</button>
      <Drawer
        open={isOpen}
        direction="left"
        size="320px"
        onOverlayClick={close}
        aria-label="Navigation menu"
      >
        <nav style={{ padding: '24px' }}>
          <h2>Navigation</h2>
          <ul>
            {menuItems.map((item, i) => (
              <li key={i}>
                <a href={item.url}>{item.label}</a>
              </li>
            ))}
          </ul>
          <button onClick={close}>Close</button>
        </nav>
      </Drawer>
    </>
  );
}
```


## Related Components

- **useDrawer**: Hook for managing drawer open/close state. Always use this hook rather than managing state manually.


================================================================================
Source: components/componentLibrary/Flex/llm.txt
================================================================================

# Flex Component

A layout container component that implements the CSS Flexbox API for creating flexible, responsive layouts.

## Import path
```tsx
import Flex from '@hubspot/cms-component-library/Flex';
```

## Purpose

The Flex component provides a consistent interface for creating flexbox layouts in HubSpot CMS projects. It solves the common challenge of building responsive layouts by abstracting CSS flexbox properties into a declarative component API. Developers should use this component when they need to arrange child elements with flexible sizing, alignment, and spacing controls. The component supports all standard flexbox properties through props and allows customization via semantic HTML elements.

## Component Structure

```
Flex/
├── index.tsx                     # Main component with flexbox logic
├── types.ts                      # TypeScript type definitions for props
├── index.module.scss             # CSS module with design tokens
└── stories/
    ├── Flex.stories.tsx          # Component usage examples
    ├── FlexDecorator.tsx         # Storybook decorator
    └── FlexDecorator.module.scss # Storybook decorator styles
```

## Components

### Flex (Main Component)

**Purpose:** Creates a flexbox container that controls the layout, alignment, and spacing of child elements.

**Props:**
```tsx
{
  as?: 'div' | 'span' | 'section' | 'article' | 'aside' | 'nav' | 'header' | 'footer' | 'main' | 'ul' | 'ol' | 'li';  // HTML element type (default: 'div')
  direction?: 'row' | 'row-reverse' | 'column' | 'column-reverse';  // Flex direction (default: 'row')
  wrap?: 'nowrap' | 'wrap' | 'wrap-reverse';  // Flex wrap behavior (default: 'nowrap')
  justifyContent?: 'flex-start' | 'flex-end' | 'center' | 'space-between' | 'space-around' | 'space-evenly';  // Main axis alignment (default: 'flex-start')
  alignItems?: 'flex-start' | 'flex-end' | 'center' | 'baseline' | 'stretch';  // Cross axis alignment (default: 'stretch')
  alignContent?: 'flex-start' | 'flex-end' | 'center' | 'space-between' | 'space-around' | 'stretch';  // Multi-line alignment (default: 'stretch')
  gap?: string;  // Spacing between flex items (e.g., '16px', '1rem')
  paddingInline?: string;  // Horizontal padding
  paddingBlock?: string;  // Vertical padding
  marginInline?: string;  // Horizontal margin
  marginBlock?: string;  // Vertical margin
  maxWidth?: string;  // Maximum width constraint
  maxHeight?: string;  // Maximum height constraint
  inline?: boolean;  // Use inline-flex instead of flex (default: false)
  className?: string;  // Additional CSS classes
  style?: React.CSSProperties;  // Inline style overrides
  children?: React.ReactNode;  // Child elements
}
```

## Usage Examples

### Basic Horizontal Layout

```tsx
import Flex from '@hubspot/cms-component-library/Flex';

<Flex direction="row" gap="16px">
  <div>Item 1</div>
  <div>Item 2</div>
  <div>Item 3</div>
</Flex>
```

### Vertical Stack

```tsx
<Flex direction="column" gap="24px">
  <div>Header</div>
  <div>Content</div>
  <div>Footer</div>
</Flex>
```

### Centered Content (With custom style)

```tsx
<Flex
  direction="row"
  justifyContent="center"
  alignItems="center"
  style={{ minHeight: '300px' }} // added for illustrative purposes
>
  <div>Centered Content</div>
</Flex>
```

### Space Between Layout

```tsx
<Flex
  direction="row"
  justifyContent="space-between"
  alignItems="center"
>
  <div>Left Item</div>
  <div>Right Item</div>
</Flex>
```

### Wrapped Grid Layout

```tsx
<Flex
  direction="row"
  wrap="wrap"
  gap="16px"
  maxWidth="800px"
>
  <div>Card 1</div>
  <div>Card 2</div>
  <div>Card 3</div>
  <div>Card 4</div>
  <div>Card 5</div>
</Flex>
```


### Semantic Article Layout

```tsx
<Flex
  as="article"
  direction="column"
  gap="32px"
  maxWidth="800px"
  marginInline="auto"
>
  <header>Article Title</header>
  <section>Article Content</section>
  <footer>Author Info</footer>
</Flex>
```

### Inline Flex Container

```tsx
<p>
  This is text with{' '}
  <Flex
    inline
    direction="row"
    gap="4px"
    alignItems="center"
  >
    <span>inline</span>
    <span>flex</span>
    <span>items</span>
  </Flex>
  {' '}in the flow.
</p>
```

## Styling

All layout properties (display, direction, wrap, alignment, gap, padding, margin, max dimensions) are applied as inline styles directly from the component props. The CSS module provides only a minimal base class (`box-sizing: border-box`). Use the `style` prop to pass standard `React.CSSProperties` overrides when needed.


## Accessibility

The Flex component supports accessibility best practices:

- **Semantic HTML**: The `as` prop allows rendering with appropriate semantic elements (nav, article, section, etc.) for better document structure
- **Keyboard Navigation**: Does not interfere with native keyboard navigation of child elements
- **Responsive Design**: Supports `wrap` property for responsive layouts that adapt to screen size

## Best Practices

- **Choose semantic elements**: Use the `as` prop to render the most appropriate HTML element (e.g., `as="nav"` for navigation, `as="article"` for article content)
- **Use gap instead of margins**: The `gap` property provides consistent spacing between flex items without margin collapsing issues
- **Set wrap for responsive layouts**: Use `wrap="wrap"` when items should flow to multiple lines on smaller screens
- **Use stretch for equal heights**: The default `alignItems="stretch"` creates equal-height columns
- **Combine with maxWidth**: Set `maxWidth` with `marginInline="auto"` for centered, constrained layouts
- **Use inline sparingly**: Only use `inline={true}` when the flex container needs to flow inline with text

## Grid vs Flex: When to Use Which

**Use Flex when:**
- You need a one-dimensional layout (either a row or column)
- Content size should determine item dimensions
- You want simple alignment and spacing between items
- Items should automatically adjust based on available space

**Use Grid when:**
- You need a two-dimensional layout (rows and columns together)
- You want precise control over item placement in both dimensions
- Creating structured layouts like galleries, dashboards, or forms
- You need items to align both horizontally and vertically

**Quick rule:** If your layout is primarily linear (items in a row or stacked vertically), use Flex. If you need both rows and columns working together, use Grid.

## Related Components

- **Button**: Often used as a child element within Flex layouts for action areas
- **Divider**: Can be used between Flex items to create visual separation


================================================================================
Source: components/componentLibrary/Form/llm.txt
================================================================================

# Form Component

A form component that dynamically renders either a v4 form or a legacy form based on the form field's `embed_version` value. The component uses JavaScript islands to load and embed HubSpot forms client-side.

## Import path
```tsx
import Form from '@hubspot/cms-component-library/Form';
```

## Purpose

The Form component provides a unified interface for embedding HubSpot forms in CMS pages. It automatically determines whether to render a v4 form (using the new embed SDK) or a legacy form (using the classic `hbspt.forms.create` API) based on the `embed_version` returned by the form field. This allows module authors to simply pass the form field value through without needing to know which form version they're working with.

## Component Structure

```
Form/
├── index.tsx                          # Main component with version resolution logic
├── types.ts                           # TypeScript type definitions (discriminated union)
├── ContentFields.tsx                  # HubSpot field definitions for content
├── StyleFields.tsx                    # HubSpot field definitions for styling
├── islands/
│   ├── FormIsland.tsx                 # V4 form island (embed SDK)
│   ├── LegacyFormIsland.tsx           # Legacy form island (hbspt.forms.create)
│   ├── PlaceholderFormIsland.tsx      # Editor-only placeholder form
│   ├── index.module.scss              # Shared form styles
│   ├── v4Form.module.scss             # V4 form CSS variables
│   ├── legacyForm.module.scss         # Legacy form styles
│   └── PlaceholderForm.module.scss    # Placeholder form styles
└── llm.txt
```

## Props

The Form component uses a discriminated union — you must use one of two prop patterns:

### With form field (preferred)
```tsx
{
  formField: typeof FormFieldDefaults;  // The form field value from module props
  variant?: string;                     // Variant name passed to the island (default: 'form1')
  className?: string;                   // CSS class applied to the outer Island wrapper
}
```

### With explicit form ID and version
```tsx
{
  formId: string;                        // The HubSpot form ID
  formVersion: 'v4' | 'v3' | 'v2' | ''; // The form embed version
  variant?: string;                      // Variant name passed to the island (default: 'form1')
  className?: string;                    // CSS class applied to the outer Island wrapper
}
```

## Dynamic Version Resolution

When using the `formField` prop, the component reads `formField.embed_version` to decide which island to render:
- If `embed_version === 'v4'` → renders `FormIsland` (v4 embed SDK)
- Otherwise (missing or any other value) → renders `LegacyFormIsland` (classic embed)

When using `formId` + `formVersion` props, the `formVersion` value is used directly.

## Usage Examples

### Using form field (recommended)

This is the preferred approach. Pass the form field value directly and let the component resolve the version automatically.

Module file:
```tsx
import Form from '@hubspot/cms-component-library/Form';
import { FormFieldDefaults } from '@hubspot/cms-components/fields';

type ComponentProps = {
  form: typeof FormFieldDefaults;
};

export const Component = (props: ComponentProps) => {
  return <Form formField={props.form} />;
};
```

### Using explicit form ID and version

Use this when you have a form ID from a source other than a form field.

```tsx
import Form from '@hubspot/cms-component-library/Form';

<Form formId="53e5b258-4526-4012-9274-8bbe23ab2d09" formVersion="v4" />
```

## HubSpot CMS Integration

### Field Definitions

The Form component provides field definitions for easy integration with HubSpot CMS modules.

#### ContentFields

Configurable props for customizing field labels, names, and defaults:

```tsx
<Form.ContentFields
  formIdLabel="Form"
  formIdName="form"
  formIdDefault={{}}
/>
```

**Fields:**
- `form`: FormField that allows the user to select a HubSpot form in the editor. Supports embed versions v4, v3, and v2.

**ContentFields Props:**
```tsx
{
  formIdLabel?: string;              // Label shown in the editor (default: "Form")
  formIdName?: string;               // Field name (default: "form")
  formIdDefault?: FormFieldDefaults; // Default field value (default: {})
}
```

#### StyleFields

Configurable props for variant selection:

```tsx
<Form.StyleFields
  formVariantLabel="Form style"
  formVariantName="formVariant"
  formVariantDefault={{ variant_name: 'form1' }}
/>
```

**Fields:**
- `formVariant`: VariantSelectionField for selecting a form style variant from the `form` variant definition.

**StyleFields Props:**
```tsx
{
  formVariantLabel?: string;                     // Label shown in the editor (default: "Form style")
  formVariantName?: string;                      // Field name (default: "formVariant")
  formVariantDefault?: { variant_name: string }; // Default variant (default: { variant_name: 'form1' })
}
```

### Module Usage Example

```tsx
// Component file (index.tsx)
import Form from '@hubspot/cms-component-library/Form';
import { FormFieldDefaults } from '@hubspot/cms-components/fields';

type ComponentProps = {
  form: typeof FormFieldDefaults;
};

export const Component = (props: ComponentProps) => {
  return <Form formField={props.form} />;
};

// Fields file (fields.tsx)
import { ModuleFields, FieldGroup } from '@hubspot/cms-components/fields';
import Form from '@hubspot/cms-component-library/Form';

export const fields = (
  <ModuleFields>
    <Form.ContentFields />
    <FieldGroup label="Design" name="groupDesign" tab="STYLE">
      <Form.StyleFields />
    </FieldGroup>
  </ModuleFields>
);
```

## Islands

The Form component renders as a JavaScript island. Both form types require client-side JavaScript:

- **FormIsland (v4)**: Loads the v4 embed SDK script (`js.hsforms.net/forms/embed/developer/{portalId}.js`) and renders a `div` with `data-form-id`, `data-portal-id`, `data-env`, and `data-form-variant` attributes.
- **LegacyFormIsland**: Loads the legacy embed script (`js.hsforms.net/forms/embed/v2.js`) and calls `window.hbspt.forms.create()` to render the form into a target container. The container div also has `data-form-variant`.
- **PlaceholderFormIsland**: Renders a static, disabled placeholder form (with dummy first name, last name, email fields and a submit button). It is only ever reached when the user is in the editor and no form ID is present — `FormComponent` itself returns `null` in that same situation outside the editor.

All islands automatically resolve the portal ID and environment (QA vs prod) from the CMS context.

## Styling

### Theme CSS Variables

Form styling is driven by `--hs-form-*` and `--hs-form-field-*` CSS variables set by the variant system.

**Form container:**
- `--hs-form-backgroundColor`: Background color
- `--hs-form-borderRadius`: Border radius
- `--hs-form-borderStyle`: Border style
- `--hs-form-borderTopWidth` / `--hs-form-borderRightWidth` / `--hs-form-borderBottomWidth` / `--hs-form-borderLeftWidth`: Border widths
- `--hs-form-borderColor`: Border color
- `--hs-form-padding`: Padding (falls back to `32px`)

**Form fields:**
- `--hs-form-field-backgroundColor`: Field background color
- `--hs-form-field-borderRadius`: Field border radius
- `--hs-form-field-borderStyle`: Field border style
- `--hs-form-field-borderTopWidth` / `--hs-form-field-borderRightWidth` / `--hs-form-field-borderBottomWidth` / `--hs-form-field-borderLeftWidth`: Field border widths
- `--hs-form-field-borderColor`: Field border color
- `--hs-form-field-backgroundColor-focus` / `--hs-form-field-border*-focus`: Focus-state overrides (fall back to default field values)

**Labels & input text:**
- `--hs-form-label-color`, `--hs-form-label-fontFamily`, `--hs-form-label-fontSize`, `--hs-form-label-fontStyle`, `--hs-form-label-fontWeight`, `--hs-form-label-textDecoration`
- `--hs-form-input-color`, `--hs-form-input-fontFamily`, `--hs-form-input-fontSize`, `--hs-form-input-fontStyle`, `--hs-form-input-fontWeight`, `--hs-form-input-textDecoration`
- `--hs-form-placeholder-color`

**Submit button** — inherits `--hs-button-*` variables from the selected button variant (see Button component).

## Best Practices

- **Prefer `formField` over `formId` + `formVersion`**: Passing the form field value directly ensures the version is always correct and keeps the module code simple.
- **Form version is determined by the form itself**: The `embed_version` property on the form field value is set by HubSpot based on the form's configuration. Module authors should not hardcode a version.
- **Styling via variant definitions**: Use `Form.StyleFields` with variant definitions to allow theme-level form styling rather than hardcoding CSS values.


================================================================================
Source: components/componentLibrary/Grid/llm.txt
================================================================================

# Grid Component

A layout container component that implements the CSS Grid API for creating structured, two-dimensional layouts. Works with GridItem children for precise grid placement control.

## Import path
```tsx
import Grid, { GridItem } from '@hubspot/cms-component-library/Grid';
```

## Purpose

The Grid component provides a consistent interface for creating grid-based layouts in HubSpot CMS projects. It solves the challenge of building complex two-dimensional layouts by abstracting CSS grid properties into a declarative component API. Developers should use this component when they need precise control over both rows and columns, such as dashboards, card galleries, magazine layouts, or any structured grid system. The component supports all essential grid properties through props, includes responsive breakpoints, and allows customization via semantic HTML elements.

## Quick Start Guide

### Basic Usage Pattern
```tsx
import Grid, { GridItem } from '@hubspot/cms-component-library/Grid';

// Simple grid - plain children
<Grid templateColumns="repeat(3, 1fr)" gap="16px">
  <div>Auto-placed item</div>
  <div>Auto-placed item</div>
</Grid>

// Advanced grid - GridItem for positioning
<Grid templateColumns="repeat(3, 1fr)" gap="16px">
  <GridItem gridColumn="span 2">Spans 2 columns</GridItem>
  <GridItem>Normal item</GridItem>
</Grid>

// GridItem with components
<Grid templateColumns="repeat(2, 1fr)" gap="16px">
  <GridItem as={Button} buttonType="primary">
    Button in grid
  </GridItem>
</Grid>
```

### Critical Rules
1. **Use props, not CSS**: `<GridItem gridColumn="1 / 3">` instead of `<div style={{ gridColumn: '1 / 3' }}>`
2. **GridItem for positioning**: Use GridItem when you need `gridColumn`, `gridRow`, `alignSelf`, `justifySelf`, or component rendering
3. **Mobile-first responsive**: Base props for mobile, then `*Md` (768px+) and `*Lg` (1024px+) variants
4. **`as` prop for components**: `<GridItem as={Button}>` passes through all Button props while adding grid positioning

## Component Structure

```
Grid/
├── index.tsx                     # Main Grid component with grid logic
├── types.ts                      # TypeScript type definitions for Grid props
├── index.module.scss             # CSS module with responsive design tokens
├── GridItem/
│   ├── index.tsx                 # GridItem component for child positioning
│   ├── types.ts                  # TypeScript type definitions for GridItem props
│   └── index.module.scss         # GridItem CSS module
├── _variables.scss               # Shared breakpoint variables ($breakpoint-md, $breakpoint-lg)
└── stories/
    ├── Grid.stories.tsx          # Component usage examples
    ├── GridDecorator.tsx         # Storybook decorator
    └── GridDecorator.module.scss # Decorator styles
```

## Components

### Grid (Main Component)

**Purpose:** Creates a grid container that controls the two-dimensional layout, alignment, and spacing of child elements across rows and columns.

**Key Feature:** The `as` prop allows Grid to render as any semantic HTML element, maintaining grid functionality while improving document structure and accessibility.

**Props:**
```tsx
{
  as?: 'div' | 'span' | 'section' | 'article' | 'aside' | 'nav' | 'header' | 'footer' | 'main' | 'ul' | 'ol';  // HTML element type (default: 'div')
  templateColumns?: string;  // Grid template columns (e.g., 'repeat(3, 1fr)', '200px 1fr 1fr')
  templateColumnsMd?: string;  // Grid template columns at tablet breakpoint (768px+)
  templateColumnsLg?: string;  // Grid template columns at desktop breakpoint (1024px+)
  templateRows?: string;  // Grid template rows (e.g., 'repeat(2, 100px)', 'auto 1fr auto')
  templateRowsMd?: string;  // Grid template rows at tablet breakpoint (768px+)
  templateRowsLg?: string;  // Grid template rows at desktop breakpoint (1024px+)
  autoFlow?: 'row' | 'column' | 'row dense' | 'column dense';  // Grid auto flow direction (default: 'row')
  autoFlowMd?: 'row' | 'column' | 'row dense' | 'column dense';  // Grid auto flow at tablet breakpoint
  autoFlowLg?: 'row' | 'column' | 'row dense' | 'column dense';  // Grid auto flow at desktop breakpoint
  autoColumns?: string;  // Size of auto-generated columns (e.g., 'minmax(100px, auto)')
  autoRows?: string;  // Size of auto-generated rows (e.g., 'minmax(100px, auto)')
  justifyContent?: 'start' | 'end' | 'center' | 'stretch' | 'space-between' | 'space-around' | 'space-evenly';  // Grid horizontal alignment (default: 'stretch')
  justifyContentMd?: 'start' | 'end' | 'center' | 'stretch' | 'space-between' | 'space-around' | 'space-evenly';  // Grid horizontal alignment at tablet breakpoint
  justifyContentLg?: 'start' | 'end' | 'center' | 'stretch' | 'space-between' | 'space-around' | 'space-evenly';  // Grid horizontal alignment at desktop breakpoint
  alignItems?: 'start' | 'end' | 'center' | 'stretch' | 'baseline';  // Grid vertical alignment (default: 'stretch')
  alignItemsMd?: 'start' | 'end' | 'center' | 'stretch' | 'baseline';  // Grid vertical alignment at tablet breakpoint
  alignItemsLg?: 'start' | 'end' | 'center' | 'stretch' | 'baseline';  // Grid vertical alignment at desktop breakpoint
  gap?: string;  // Spacing between grid items (e.g., '16px', '1rem')
  gapMd?: string;  // Spacing between grid items at tablet breakpoint
  gapLg?: string;  // Spacing between grid items at desktop breakpoint
  paddingInline?: string;  // Horizontal padding
  paddingBlock?: string;  // Vertical padding
  marginInline?: string;  // Horizontal margin
  marginBlock?: string;  // Vertical margin
  maxWidth?: string;  // Maximum width constraint
  maxHeight?: string;  // Maximum height constraint
  inline?: boolean;  // Use inline-grid instead of grid (default: false)
  className?: string;  // Additional CSS classes
  style?: React.CSSProperties;  // Inline styles (including grid-specific child styles)
  children?: React.ReactNode;  // Child elements
}
```

### GridItem (Child Component)

**Purpose:** Wraps grid children to control their precise placement, spanning, and alignment within the Grid container. Can render as any HTML element or custom React component while maintaining grid positioning control.

**Key Feature:** The `as` prop is polymorphic - it accepts both HTML element strings ('div', 'section', etc.) and React component references (Button, Text, custom components). GridItem handles grid placement while passing through all other props to the underlying component.

**Props:**
```tsx
{
  as?: React.ElementType;  // Any HTML element or React component (default: 'div')
                            // Examples: 'div', 'article', Button, Text, CustomComponent
  gridColumn?: string;  // Grid column placement (e.g., '1 / 3', 'span 2', '2')
  gridColumnMd?: string;  // Grid column placement at tablet breakpoint (768px+)
  gridColumnLg?: string;  // Grid column placement at desktop breakpoint (1024px+)
  gridRow?: string;  // Grid row placement (e.g., '1 / 3', 'span 2', '2')
  gridRowMd?: string;  // Grid row placement at tablet breakpoint (768px+)
  gridRowLg?: string;  // Grid row placement at desktop breakpoint (1024px+)
  justifySelf?: 'start' | 'end' | 'center' | 'stretch' | 'baseline';  // Horizontal self-alignment
  justifySelfMd?: 'start' | 'end' | 'center' | 'stretch' | 'baseline';  // Horizontal self-alignment at tablet
  justifySelfLg?: 'start' | 'end' | 'center' | 'stretch' | 'baseline';  // Horizontal self-alignment at desktop
  alignSelf?: 'start' | 'end' | 'center' | 'stretch' | 'baseline';  // Vertical self-alignment
  alignSelfMd?: 'start' | 'end' | 'center' | 'stretch' | 'baseline';  // Vertical self-alignment at tablet
  alignSelfLg?: 'start' | 'end' | 'center' | 'stretch' | 'baseline';  // Vertical self-alignment at desktop
  className?: string;  // Additional CSS classes
  style?: React.CSSProperties;  // Inline styles
  children?: React.ReactNode;  // Child elements
  ...rest  // All props of the component specified in 'as' are passed through
}
```

**How the `as` Prop Works:**

GridItem's `as` prop provides flexibility in rendering while maintaining grid control:

1. **HTML Elements**: Pass element names as strings
   ```tsx
   <GridItem as="article">Content</GridItem>
   <GridItem as="section">Section content</GridItem>
   ```

2. **React Components**: Pass component references directly
   ```tsx
   <GridItem as={Button} buttonType="primary">Button</GridItem>
   <GridItem as={Text} fieldPath="title" />
   ```

3. **Prop Pass-Through**: All props beyond GridItem's own props are forwarded to the underlying component
   ```tsx
   {/* Button-specific props are passed through */}
   <GridItem as={Button} href="https://example.com" buttonType="link">
     Link Button
   </GridItem>

   {/* Text-specific props are passed through */}
   <GridItem as={Text} fieldPath="sectionTitle" className="custom-text" />
   ```

4. **Grid Positioning**: GridItem handles all grid-specific positioning regardless of the underlying component
   ```tsx
   <GridItem
     as={Button}
     gridColumn="1 / 3"
     gridRow="2"
     buttonType="primary"
   >
     Positioned Button
   </GridItem>
   ```

## Usage Examples

**IMPORTANT: Always use Grid and GridItem props for layout control. Custom CSS should be a last resort only when props cannot achieve the desired layout.**

### Basic Grid with GridItem Placement

```tsx
import Grid, { GridItem } from '@hubspot/cms-component-library/Grid';

<Grid templateColumns="repeat(3, 1fr)" gap="16px">
  <GridItem>Item 1</GridItem>
  <GridItem>Item 2</GridItem>
  <GridItem gridColumn="span 2">Item 3 (spans 2 columns)</GridItem>
  <GridItem>Item 4</GridItem>
  <GridItem>Item 5</GridItem>
</Grid>
```

### GridItem with Components (using `as` prop)

```tsx
import Grid, { GridItem } from '@hubspot/cms-component-library/Grid';
import Button from '@hubspot/cms-component-library/Button';
import Text from '@hubspot/cms-component-library/Text';

<Grid templateColumns="repeat(2, 1fr)" gap="16px">
  {/* GridItem rendering as Button */}
  <GridItem
    as={Button}
    buttonType="primary"
    href="https://example.com"
  >
    Click Me
  </GridItem>

  {/* GridItem rendering as Text (heading feature set) */}
  <GridItem>
    <Text fieldPath="sectionTitle" />
  </GridItem>

  {/* Regular content */}
  <GridItem gridColumn="span 2">
    <p>Content spanning both columns</p>
  </GridItem>
</Grid>
```

### Responsive Grid Layout with GridItem

```tsx
{/* Use props for responsive behavior - NO custom CSS needed */}
<Grid
  templateColumns="1fr"
  templateColumnsMd="repeat(2, 1fr)"
  templateColumnsLg="repeat(3, 1fr)"
  gap="16px"
>
  <GridItem>Item 1</GridItem>
  <GridItem>Item 2</GridItem>
  <GridItem>Item 3</GridItem>
  <GridItem
    gridColumn="1"
    gridColumnMd="span 2"
    gridColumnLg="1"
  >
    Item 4 (spans 2 cols on tablet only)
  </GridItem>
  <GridItem>Item 5</GridItem>
  <GridItem>Item 6</GridItem>
</Grid>
```

### Complex Layout Using Props (NO custom CSS)

```tsx
import Grid, { GridItem } from '@hubspot/cms-component-library/Grid';
import Text from '@hubspot/cms-component-library/Text';

{/* Dashboard layout - all positioning via props */}
<Grid
  templateColumns="1fr"
  templateColumnsMd="200px 1fr"
  templateRows="auto 1fr auto"
  gap="16px"
>
  {/* Header - full width on all breakpoints */}
  <GridItem
    gridColumn="1"
    gridColumnMd="1 / -1"
  >
    <Text fieldPath="dashboardTitle" />
  </GridItem>

  {/* Sidebar - hidden on mobile, shown on tablet+ */}
  <GridItem
    gridColumn="1"
    gridRow="2"
    style={{ display: 'none' }}
    className="show-md"
  >
    Sidebar Navigation
  </GridItem>

  {/* Main content */}
  <GridItem
    gridColumn="1"
    gridColumnMd="2"
    gridRow="2"
  >
    Main Content Area
  </GridItem>

  {/* Footer - full width */}
  <GridItem
    gridColumn="1"
    gridColumnMd="1 / -1"
  >
    Footer Content
  </GridItem>
</Grid>
```

### Overlapping Items with GridItem

```tsx
{/* Create overlapping grid items using props */}
<Grid
  templateColumns="repeat(3, 1fr)"
  templateRows="repeat(2, 200px)"
  gap="16px"
>
  <GridItem
    gridColumn="1 / 3"
    gridRow="1 / 2"
  >
    Item 1 (columns 1-2)
  </GridItem>

  <GridItem
    gridColumn="2 / 4"
    gridRow="1 / 2"
  >
    Item 2 (columns 2-3, overlaps with Item 1)
  </GridItem>

  <GridItem
    as={Button}
    buttonType="link"
    href="https://example.com"
    gridColumn="3 / 4"
    gridRow="1 / 3"
  >
    Button spanning full height
  </GridItem>
</Grid>
```

### Auto-Fit Responsive Cards

```tsx
{/* GridItem optional for simple auto-placement */}
<Grid
  templateColumns="repeat(auto-fit, minmax(250px, 1fr))"
  gap="24px"
>
  <GridItem>Card 1</GridItem>
  <GridItem>Card 2</GridItem>
  <GridItem>Card 3</GridItem>
  <GridItem>Card 4</GridItem>
</Grid>
```

### Magazine Layout with GridItem Spanning

```tsx
{/* Use GridItem props instead of inline styles */}
<Grid
  templateColumns="repeat(4, 1fr)"
  templateRows="repeat(2, 150px)"
  gap="16px"
>
  <GridItem gridColumn="span 2" gridRow="span 2">
    Featured Article
  </GridItem>
  <GridItem>Article 2</GridItem>
  <GridItem>Article 3</GridItem>
  <GridItem>Article 4</GridItem>
  <GridItem>Article 5</GridItem>
</Grid>
```

### Semantic Section Layout with GridItem

```tsx
<Grid
  as="section"
  templateColumns="1fr 2fr"
  gap="32px"
  maxWidth="1200px"
  marginInline="auto"
>
  <GridItem as="aside">Sidebar Content</GridItem>
  <GridItem as="article">Main Article Content</GridItem>
</Grid>
```


### Item-Specific Alignment with GridItem

```tsx
<Grid
  templateColumns="repeat(3, 1fr)"
  templateRows="repeat(2, 150px)"
  gap="16px"
  alignItems="stretch"
>
  {/* Override grid-level alignment per item using props */}
  <GridItem alignSelf="start">
    Aligned to start
  </GridItem>

  <GridItem alignSelf="center" justifySelf="center">
    Centered both ways
  </GridItem>

  <GridItem alignSelf="end" justifySelf="end">
    Aligned to end
  </GridItem>

  <GridItem gridColumn="span 3" alignSelf="center">
    Spans all columns, centered vertically
  </GridItem>
</Grid>
```

## Accessibility

The Grid component supports accessibility best practices:

- **Semantic HTML**: The `as` prop allows rendering with appropriate semantic elements (nav, article, section, etc.) for better document structure and screen reader support
- **Keyboard Navigation**: Does not interfere with native keyboard navigation of child elements
- **Responsive Design**: Built-in responsive breakpoints ensure layouts adapt to different screen sizes and devices
- **Visual Order**: Grid placement allows visual reordering without changing DOM order, but be cautious that visual order should match reading order for accessibility

## Best Practices

### Layout Control Strategy
- **ALWAYS prefer props over custom CSS**: Grid and GridItem provide props for nearly all layout needs. Only resort to custom CSS when props truly cannot achieve the desired result.
- **Use GridItem for positioning**: Wrap children in GridItem when you need to control placement (`gridColumn`, `gridRow`), spanning, or individual alignment (`alignSelf`, `justifySelf`)
- **Direct children for simple grids**: Plain children are fine when items auto-flow without special positioning needs
- **Avoid inline styles for grid properties**: Instead of `style={{ gridColumn: '1 / 3' }}`, use `<GridItem gridColumn="1 / 3">`

### Component Usage
- **Choose semantic elements**: Use the `as` prop on both Grid and GridItem to render appropriate HTML elements (e.g., `as="section"` for content sections, `as="article"` for articles)
- **Leverage GridItem's `as` prop for components**: Use `<GridItem as={Button}>` instead of wrapping: `<GridItem><Button /></GridItem>`
- **Pass through component props**: When using `as={Component}`, GridItem forwards all non-grid props to the component

### Spacing and Sizing
- **Use gap instead of margins**: The `gap` property provides consistent spacing between grid items without margin collapsing issues
- **Leverage responsive props**: Use breakpoint variants (`templateColumnsMd`, `gridColumnMd`, etc.) to create mobile-first responsive layouts
- **Use fr units for flexibility**: The `fr` unit distributes available space proportionally (e.g., `1fr 2fr` creates a 1:2 ratio)
- **Combine with maxWidth**: Set `maxWidth` with `marginInline="auto"` for centered, constrained layouts

### Responsive Design
- **Mobile-first approach**: Define base props for mobile, then override with `*Md` and `*Lg` variants
- **Use auto-fit for fluid grids**: The pattern `repeat(auto-fit, minmax(250px, 1fr))` creates grids that automatically adjust column count
- **Responsive item placement**: Use `gridColumnMd` and `gridColumnLg` on GridItem to change placement at different breakpoints

### Alignment
- **Start with stretch alignment**: The default `alignItems="stretch"` and `justifyContent="stretch"` create consistent sizing
- **Override per item when needed**: Use GridItem's `alignSelf` and `justifySelf` props to override grid-level alignment for specific items

### Performance and Maintenance
- **Be mindful of dense packing**: Use `autoFlow="row dense"` or `autoFlow="column dense"` carefully, as it can create confusing visual order
- **Keep it simple**: Don't over-engineer grid structures. Use the simplest prop combination that achieves your layout

## Flex vs Grid: When to Use Which

**Use Grid when:**
- You need a two-dimensional layout (rows and columns together)
- You want precise control over item placement in both dimensions
- Creating structured layouts like galleries, dashboards, card grids, or forms
- You need items to align both horizontally and vertically in a predictable grid structure

**Use Flex when:**
- You need a one-dimensional layout (either a row or column)
- Content size should determine item dimensions
- You want simple alignment and spacing between items
- Items should automatically adjust based on available space

**Quick rule:** If you need both rows and columns working together in a structured layout, use Grid. If your layout is primarily linear (items in a row or stacked vertically), use Flex.

## Summary: Key Takeaways

1. **Props Over CSS**: Always use Grid and GridItem props for layout control. Custom CSS is a last resort.

2. **GridItem for Control**: Wrap children in GridItem when you need positioning, spanning, or per-item alignment.

3. **Polymorphic `as` Prop**: Both Grid and GridItem can render as any element or component while maintaining grid functionality.

4. **Prop Pass-Through**: GridItem forwards all non-grid props to the underlying component, enabling clean integration with library components.

5. **Mobile-First Responsive**: Define base props for mobile, then use `*Md` and `*Lg` variants for larger screens.

6. **Semantic HTML**: Use the `as` prop to improve document structure and accessibility.

7. **Keep It Simple**: Don't over-engineer. Use the simplest prop combination that achieves your layout.

## Internal Styling Architecture

Grid uses a hybrid styling approach to support responsive breakpoints while minimizing CSS variable usage.

### Non-Responsive Props (Direct Inline Styles)
These props are applied as standard inline CSS properties with no CSS variables:
- `display` (via `inline` prop), `gridAutoColumns` (`autoColumns`), `gridAutoRows` (`autoRows`)
- `paddingInline`, `paddingBlock`, `marginInline`, `marginBlock`, `maxWidth`, `maxHeight`

### Responsive Props (CSS Variables + Media Queries)
Props with `*Md` and `*Lg` breakpoint variants require CSS variables because inline styles cannot respond to media queries. The SCSS file reads these variables inside `@media` blocks to apply breakpoint-specific values.

**Grid CSS variables** (`--hs-grid-*`):
- `--hs-grid-templateColumns`, `--hs-grid-templateColumns-md`, `--hs-grid-templateColumns-lg`
- `--hs-grid-templateRows`, `--hs-grid-templateRows-md`, `--hs-grid-templateRows-lg`
- `--hs-grid-autoFlow`, `--hs-grid-autoFlow-md`, `--hs-grid-autoFlow-lg`
- `--hs-grid-justifyContent`, `--hs-grid-justifyContent-md`, `--hs-grid-justifyContent-lg`
- `--hs-grid-alignItems`, `--hs-grid-alignItems-md`, `--hs-grid-alignItems-lg`
- `--hs-grid-gap`, `--hs-grid-gap-md`, `--hs-grid-gap-lg`

**GridItem CSS variables** (`--hs-gridItem-*`):
- `--hs-gridItem-gridColumn`, `--hs-gridItem-gridColumn-md`, `--hs-gridItem-gridColumn-lg`
- `--hs-gridItem-gridRow`, `--hs-gridItem-gridRow-md`, `--hs-gridItem-gridRow-lg`
- `--hs-gridItem-justifySelf`, `--hs-gridItem-justifySelf-md`, `--hs-gridItem-justifySelf-lg`
- `--hs-gridItem-alignSelf`, `--hs-gridItem-alignSelf-md`, `--hs-gridItem-alignSelf-lg`

### Naming Convention
CSS variable names are intentionally scoped with `--hs-grid-` and `--hs-gridItem-` prefixes to avoid conflicts with generic `--hs-*` variables that may be used elsewhere in theme settings or section-level styling. Do not introduce generic names like `--hs-gap` or `--hs-padding-inline`.

### Breakpoints
- Base: default (mobile-first)
- `md`: 768px+ (tablet)
- `lg`: 1024px+ (desktop)

Breakpoint values are defined in `_variables.scss` and shared between Grid and GridItem SCSS files.

## Related Components

- **Flex**: Use for one-dimensional layouts when you don't need both row and column control
- **GridItem**: Child component for precise grid placement and positioning


================================================================================
Source: components/componentLibrary/Icon/llm.txt
================================================================================

# Icon Component

A wrapper around the HubSpot CMS Icon component that provides flexible sizing, theme-driven styling via CSS variables, and accessibility options.

## Import path
```tsx
import Icon from '@hubspot/cms-component-library/Icon';
```

## Purpose

The Icon component wraps the native HubSpot CMS Icon component to provide a consistent interface for rendering icons with theme-driven styling and accessibility options. Styling (color, background, border) is controlled by CSS variables injected by the HubSpot theme system based on the selected icon variant. The `data-icon-variant` attribute on the wrapper element is what scopes those CSS variables.

## Component Structure

```
Icon/
├── index.tsx                     # Main component with render logic
├── types.ts                      # TypeScript type definitions
├── ContentFields.tsx             # HubSpot field definitions for icon content
├── StyleFields.tsx               # HubSpot field definitions for icon styles
├── index.module.scss             # CSS module consuming theme CSS variables
└── stories/
    ├── Icon.stories.tsx          # Component usage examples
    ├── IconDecorator.tsx         # Storybook decorator
    └── IconDecorator.module.scss # Decorator styles
```

## Components

### Icon (Main Component)

**Purpose:** Renders an icon with configurable size, variant-based theme styling, and accessibility properties.

**Props:**
```tsx
{
  fieldPath?: string;                              // Path to icon in HubSpot fields
  variant?: string;                                // Icon variant name, sets data-icon-variant for CSS var scoping. Omit to allow parent CSS variables to cascade.
  size?: 'small' | 'medium' | 'large';             // Icon size (small=16px, medium=24px, large=32px). Omit to use CSS default (16px)
  className?: string;                              // Additional CSS classes
  style?: React.CSSProperties;                     // Inline styles (merged with size styles)
  showIcon?: boolean;                              // Toggle icon visibility (default: true)
  purpose?: 'SEMANTIC' | 'DECORATIVE';            // Icon accessibility role (default: 'DECORATIVE')
  title?: string;                                  // Icon title for screen readers (use with SEMANTIC)
}
```

## CSS Variables

Styling is driven by the following CSS variables, injected by the theme system and scoped via `data-icon-variant`:

| Variable | Property | Fallback |
|---|---|---|
| `--hs-icon-color` | SVG fill | `currentColor` |
| `--hs-icon-backgroundColor` | Background color | `transparent` |
| `--hs-icon-borderRadius` | Border radius | `0` |
| `--hs-icon-borderStyle` | Border style | `solid` |
| `--hs-icon-borderTopWidth` | Top border width | `0` |
| `--hs-icon-borderRightWidth` | Right border width | `0` |
| `--hs-icon-borderBottomWidth` | Bottom border width | `0` |
| `--hs-icon-borderLeftWidth` | Left border width | `0` |
| `--hs-icon-borderColor` | Border color | `transparent` |

## Usage Examples

### Basic Icon

```tsx
import Icon from '@hubspot/cms-component-library/Icon';

<Icon
  fieldPath="icon"
  size="medium"
/>
```

### Icon with Variant

```tsx
<Icon
  fieldPath="icon"
  size="medium"
  variant="icon2"
/>
```

### Semantic Icon with Accessibility

```tsx
<Icon
  fieldPath="icon"
  size="medium"
  purpose="SEMANTIC"
  title="Download file"
/>
```

### Conditional Icon Display

```tsx
<Icon
  fieldPath="icon"
  size="small"
  showIcon={shouldShowIcon}
/>
```

## HubSpot CMS Integration

### Field Definitions

The Icon component provides field definitions for easy integration with HubSpot CMS modules.

#### ContentFields

Configurable props for icon selection and visibility:

```tsx
<Icon.ContentFields
  iconLabel="Icon"
  iconName="icon"
  iconDefault={{ name: 'check-circle', unicode: 'f058', type: 'REGULAR' }}
  addIconToggle={true}
  showIconLabel="Show icon"
  showIconName="showIcon"
  showIconDefault={false}
/>
```

**Fields:**
- `icon`: IconField for selecting the icon
- `showIcon`: BooleanField toggle for icon visibility (optional, enabled with `addIconToggle={true}`)

#### StyleFields

Configurable props for icon appearance:

```tsx
<Icon.StyleFields />
```

**Fields:**
- `iconVariant`: VariantSelectionField for choosing the icon theme variant (controls color, background, border via CSS vars)
- `iconSize`: ChoiceField for icon size (`'small'` 16px, `'medium'` 24px, `'large'` 32px)

All fields support `label`, `name`, and `default` customization props, and can be hidden via `hideFields`.

### Module Usage Example

```tsx
import Icon from '@hubspot/cms-component-library/Icon';

export default function IconModule({ fieldValues }) {
  return (
    <Icon
      fieldPath="icon"
      variant={fieldValues.style?.iconVariant?.variant_name}
      size={fieldValues.style?.iconSize}
      showIcon={fieldValues.showIcon}
      purpose="DECORATIVE"
    />
  );
}
```

### Module Fields Example

```tsx
import { ModuleFields, FieldGroup } from '@hubspot/cms-components/fields';
import Icon from '@hubspot/cms-component-library/Icon';

export const fields = (
  <ModuleFields>
    <Icon.ContentFields addIconToggle={true} />
    <FieldGroup name="style" label="Style" tab="STYLE">
      <Icon.StyleFields />
    </FieldGroup>
  </ModuleFields>
);
```

## Styling

When `showIconBackground` is true, the Icon component renders a `<div data-icon-variant={variant}>` wrapper that the HubSpot theme system uses to scope CSS variables. The `data-icon-variant` attribute is only present when `variant` is explicitly provided — omitting it allows CSS variables from a parent element (e.g. a card's `data-card-variant`) to cascade through unimpeded. All color, background, and border properties come from the theme — no inline styles are used for these.

The only inline styles applied are width and height when the `size` prop is provided.

## Accessibility

The Icon component follows accessibility best practices:

- **Icon Purpose**:
  - `DECORATIVE` (default): Icon is purely visual, hidden from screen readers with `aria-hidden`
  - `SEMANTIC`: Icon conveys meaning, includes accessible title for screen readers
- **Title Attribute**: Use `title` prop with `purpose="SEMANTIC"` to provide screen reader description
- **Conditional Rendering**: The `showIcon` prop allows dynamic icon visibility without affecting layout

## Best Practices

- **Choose the right purpose**: Use `SEMANTIC` when the icon conveys important meaning, `DECORATIVE` when it's purely visual
- **Always provide title with SEMANTIC**: When using `purpose="SEMANTIC"`, always include a meaningful `title` for screen readers
- **Use named sizes**: Pass `'small'` (16px), `'medium'` (24px), or `'large'` (32px) to `size`. Omit `size` entirely to use the CSS default of `16px`
- **Field path**: The `fieldPath` prop should match the field name defined in ContentFields
- **Variant drives all visual styling**: Do not use inline styles for color or background — configure those through the theme variant system


================================================================================
Source: components/componentLibrary/Image/llm.txt
================================================================================

# Image Component

A semantic image component for displaying images with built-in responsive behavior, lazy loading, and accessibility features.

## Import path
```tsx
import Image from '@hubspot/cms-component-library/Image';
```

## Purpose

The Image component provides a standardized way to display images in HubSpot CMS projects with proper accessibility, responsive behavior, and performance optimization. It handles common image rendering concerns like lazy loading, responsive sizing, and semantic HTML attributes.

## Component Structure

```
Image/
├── index.tsx                     # Main component with render logic
├── types.ts                      # TypeScript type definitions
├── ContentFields.tsx             # HubSpot field definitions for content
├── index.module.scss             # CSS module with design tokens
└── stories/
    ├── Image.stories.tsx         # Usage examples in Storybook
    ├── ImageDecorator.tsx        # Storybook decorator
    └── ImageDecorator.module.scss # Decorator styles
```

## Props

```tsx
{
  src: string;                                   // Image source URL (required)
  alt: string;                                   // Alternative text for accessibility (required)
  title?: string;                                // Tooltip text on hover
  width?: number;                                // Image width in pixels
  height?: number;                               // Image height in pixels
  loading?: 'lazy' | 'eager' | 'disabled';       // Loading strategy (default: 'lazy'). 'disabled' maps to 'eager' in the rendered <img> — it does not remove the loading attribute
  responsive?: boolean;                          // Enable responsive scaling (default: true)
  className?: string;                            // Additional CSS classes
  style?: React.CSSProperties;                   // Inline styles
  variant?: 'primary' | 'secondary' | 'tertiary'; // Visual style variant
}
```

## Usage Examples

### Basic Image

```tsx
import Image from '@hubspot/cms-component-library/Image';

<Image
  src="https://example.com/image.jpg"
  alt="Description of the image"
  width={800}
  height={600}
/>
```

### Responsive Image

```tsx
<Image
  src="https://example.com/hero-image.jpg"
  alt="Hero banner showcasing product features"
  width={1200}
  height={600}
  responsive={true}
/>
```

### With Title Tooltip

```tsx
<Image
  src="https://example.com/product.jpg"
  alt="Product showcase"
  title="Click to view product details"
  width={600}
  height={400}
/>
```

### Eager Loading (Above the Fold)

```tsx
<Image
  src="https://example.com/hero.jpg"
  alt="Hero image"
  width={1920}
  height={1080}
  loading="eager"
  responsive={true}
/>
```

### With Custom Styling

```tsx
<Image
  src="https://example.com/profile.jpg"
  alt="User profile picture"
  width={200}
  height={200}
  style={{
    borderRadius: '50%',
    border: '4px solid #0066cc',
  }}
/>
```

## HubSpot CMS Integration

### Field Definitions

The Image component provides field definitions for easy integration with HubSpot CMS modules.

#### ContentFields

Configurable props for customizing field labels, names, and defaults:

```tsx
<Image.ContentFields
  imageLabel="Hero image"
  imageName="heroImage"
  imageDefault={{
    src: '',
    alt: '',
    width: 800,
    height: 600,
    loading: 'lazy',
  }}
  showLoading={true}
  resizable={true}
  responsive={true}
  imageEnabledFeatures={['replace', 'crop']}
/>
```

**Fields:**
- `image`: ImageField for image source, alt text, dimensions, and loading strategy
- `showLoading`: Toggle to display loading strategy options in the editor
- `resizable`: Enable image resizing in the editor
- `responsive`: Enable responsive scaling by default
- `imageEnabledFeatures`: Optional array of enabled inline-editing features ('replace', 'crop')

#### Style Fields

**Note:** There are no style fields included in this component. Do not try to consume or import style fields from the image component.

### Module Usage Example

```tsx
import Image from '@hubspot/cms-component-library/Image';

export default function HeroModule({ fieldValues }) {
  return (
    <Image
      src={fieldValues.heroImage?.src}
      alt={fieldValues.heroImage?.alt}
      width={fieldValues.heroImage?.width}
      height={fieldValues.heroImage?.height}
      loading={fieldValues.heroImage?.loading || 'lazy'}
      responsive={true}
    />
  );
}
```

## Styling

The Image component uses hardcoded base styles with the `style` prop accepting standard `React.CSSProperties` for customization.

**Base defaults (from CSS module):**
- `display: inline-block`
- `max-inline-size: max-content`
- `max-block-size: max-content`
- `border-radius: 0`

Use the `style` prop to customize border, border-radius, box-shadow, or other visual properties:

```tsx
<Image
  src="photo.jpg"
  alt="Example"
  style={{ borderRadius: '8px', border: '2px solid #ccc', boxShadow: '0 2px 8px rgba(0,0,0,0.1)' }}
/>
```

### Responsive Behavior

When `responsive={true}` (default), the image will:
- Scale down to fit container width while maintaining aspect ratio
- Never exceed its natural dimensions
- Use `max-inline-size: 100%` and `block-size: auto`


## Accessibility

The Image component follows accessibility best practices:

- **Required Alt Text**: The `alt` prop is required for screen reader accessibility
  - Descriptive alt text should explain what the image shows
  - For decorative images, use an empty string: `alt=""`
- **Semantic HTML**: Renders native `<img>` elements with proper attributes
- **Title Attribute**: Optional `title` prop provides additional context on hover
- **Dimensions**: `width` and `height` props help browsers allocate space and prevent layout shift

## Best Practices

- **Always provide meaningful alt text**: Describe the content and purpose of the image for screen readers
- **Use lazy loading for below-the-fold images**: Keep the default `loading="lazy"` for images not immediately visible
- **Use eager loading for above-the-fold images**: Set `loading="eager"` for hero images and critical content
- **Specify dimensions**: Always provide `width` and `height` to prevent cumulative layout shift (CLS)
- **Enable responsive behavior**: Keep `responsive={true}` (default) unless you need fixed dimensions
- **Use the `style` prop for custom styling**: Pass standard `React.CSSProperties` for border, shadow, and other visual customizations
- **Style fields**: The image component doesn't include style fields. Do not try to import them.

## Related Components

- **Link**: Wrap Image with Link component to create clickable images


================================================================================
Source: components/componentLibrary/LanguageSwitcher/llm.txt
================================================================================

# LanguageSwitcher Component

A multilingual navigation component that displays the current page language and provides a slide-out drawer interface for selecting available language variants. Integrates with HubSpot's multi-language content system to enable seamless language switching across translated pages.

## Import path
```tsx
import LanguageSwitcher from '@hubspot/cms-component-library/LanguageSwitcher';
```

## Purpose

The LanguageSwitcher component provides an accessible interface for users to view and switch between available language variants of the current page. It displays a text-link trigger showing the current language (with an optional globe icon), and opens a slide-out drawer panel listing all available translations. The component automatically fetches language variants from HubSpot's CMS using the `useLanguageVariants` hook. Visual styling (text color, background color) is inherited from the section the component lives in via `--hs-section-color` and `--hs-section-backgroundColor` CSS custom properties — no manual color configuration is needed.

## Component Structure

```
LanguageSwitcher/
├── index.tsx                                 # Main component (wrapper that delegates to Island)
├── types.ts                                  # TypeScript type definitions
├── ContentFields.tsx                         # HubSpot field definitions for content
├── LanguageOptions.tsx                       # Language option list component
├── LanguageOptions.module.scss               # CSS module for language options
├── utils.tsx                                 # Utility functions for language display
├── _dummyData.tsx                            # Dummy translation data for development
├── index.module.scss                         # CSS module with design tokens
├── assets/
│   └── Globe.tsx                             # Globe SVG icon component
├── islands/
│   ├── LanguageSwitcherIsland.tsx            # Client-side island with interactive UI
│   └── LanguageSwitcherDrawer.tsx            # Portaled drawer (mobile full-screen, desktop side panel)
└── stories/
    ├── LanguageSwitcher.stories.tsx          # Component usage examples
    ├── LanguageSwitcherDecorator.tsx         # Storybook decorator
    └── LanguageSwitcherDecorator.module.css  # Decorator styles
```

## Components

### LanguageSwitcher (Main Component)

**Purpose:** Renders a text-link trigger displaying the current page language that opens a drawer panel with all available language options. Colors are inherited from the surrounding section.

**Props:**
```tsx
{
  translations?: LanguageVariant[];             // Language variants (if not provided, uses useLanguageVariants hook)
  className?: string;                           // Additional CSS classes
  style?: React.CSSProperties;                  // Inline styles
  showGlobeIcon?: boolean;                      // Show/hide globe icon (default: true)
  display?: 'fullName' | 'countryCode';         // How to display the current language (default: 'fullName')
  dropdownPosition?: 'left' | 'right';          // Which side the drawer slides in from on desktop (default: 'right')
  languageSwitcherSelectText?: string;          // Header text in drawer (default: 'Select a language')
}
```

**Mobile behavior:**
On mobile viewports (≤767px), the drawer slides in from the right as a full-screen panel (matching the NavigationMenu mobile slideout), positioned below the header section. The trigger button shows only the globe icon (no language text) on mobile. `dropdownPosition` only applies on desktop. Mobile detection uses `ResizeObserver` on the nearest `<section>` element so it works correctly inside the HubSpot editor's preview canvas. The LanguageSwitcher and NavigationMenu coordinate via `useMobileDrawerGroup` — opening one automatically closes the other.

**Portal behavior:**
The drawer is portaled into the nearest `<section>` ancestor of the trigger element. This means section CSS custom properties (`--hs-section-backgroundColor`, `--hs-section-color`) cascade naturally into the drawer without any JavaScript color bridging. The component returns `null` until the section is located (after first mount), which is safe since the drawer starts closed.

## Usage Examples

### Basic Usage

The LanguageSwitcher component automatically handles Island-ification internally. Simply import and use it:

```tsx
import LanguageSwitcher from '@hubspot/cms-component-library/LanguageSwitcher';

export default function MyComponent() {
  return <LanguageSwitcher />;
}
```

When used without a `translations` prop, it automatically calls `useLanguageVariants()` to fetch language data.

### With Custom Header Text

```tsx
<LanguageSwitcher languageSwitcherSelectText="Choose your language" />
```

### With Country Code Display

```tsx
<LanguageSwitcher display="countryCode" />
```

### With Custom Translations

```tsx
<LanguageSwitcher translations={customLanguageVariants} />
```

### In Header Navigation

```tsx
<header style={{ display: 'flex', justifyContent: 'space-between', padding: '16px' }}>
  <div>
    <h1>My Website</h1>
  </div>
  <nav style={{ display: 'flex', gap: '24px', alignItems: 'center' }}>
    <a href="/about">About</a>
    <a href="/contact">Contact</a>
    <LanguageSwitcher />
  </nav>
</header>
```

## HubSpot CMS Integration

### Field Definitions

The LanguageSwitcher component provides field definitions for easy integration with HubSpot CMS modules.

#### ContentFields

Configurable props for content and behavior:

```tsx
<LanguageSwitcher.ContentFields />
```

**Fields:**
- `showGlobeIcon`: Toggle to show/hide the globe icon
- `display`: Pill toggle between `'fullName'` and `'countryCode'`
- `dropdownPosition`: Pill toggle between `'left'` and `'right'` drawer direction

### Module Usage Example

```tsx
import LanguageSwitcher from '@hubspot/cms-component-library/LanguageSwitcher';

export default function HeaderModule({ fieldValues }) {
  return (
    <header>
      <nav>
        {/* Other navigation items */}
        <LanguageSwitcher
          showGlobeIcon={fieldValues.showGlobeIcon}
          display={fieldValues.display}
          dropdownPosition={fieldValues.dropdownPosition}
          languageSwitcherSelectText={fieldValues.languageSwitcherSelectText}
        />
      </nav>
    </header>
  );
}
```

## Styling

Colors are **not configurable via props**. Instead, the component inherits colors from the section it lives in via CSS custom properties:

- `--hs-section-color` — controls text color for the trigger, drawer title, close button, and language links
- `--hs-section-backgroundColor` — controls background color for the drawer panel and sticky drawer header

These variables are set by the section module wrapping the component. No additional color configuration is needed on the LanguageSwitcher itself.

**Trigger Styles:**
- Plain text-link appearance (no background, no border)
- Padding: 8px 12px with 8px gap between icon and text
- Globe icon size: 20px height
- Font size: 18px
- Focus: `outline: 2px solid AccentColor; outline-offset: 2px` on `:focus-visible`

**Drawer Styles:**
- Desktop: drawer width 400px, slides in from `dropdownPosition` side
- Mobile (≤767px): full-screen right-slide panel (100vw), positioned below the header section, matching NavigationMenu
- Header padding: 24px (sticky-positioned)
- Header font size: 24px, font weight 600
- Options container padding: 24px

**Language Options Styles:**
- Link font size: 18px
- Link padding: 12px 16px
- Active language item has a subtle background highlight

## Accessibility

The LanguageSwitcher component follows accessibility best practices:

- **Semantic HTML**: Uses native `<button>` elements and a drawer with `role="dialog"`
- **Keyboard Navigation**:
  - Trigger activates with Space or Enter keys
  - ESC key closes the drawer
  - Tab navigation works through language options
- **ARIA Attributes**:
  - `aria-expanded` on trigger indicates drawer state
  - `aria-label="Language selector"` on drawer provides context
  - `aria-label="Close language selector"` on close button
- **Focus Indicators**: `:focus-visible` outline on both trigger and close button using the browser's `AccentColor`
- **Screen Reader Support**:
  - Current language is announced on trigger
  - Language options are properly labeled with native names

## Best Practices

- **Island-ification is handled internally**: The LanguageSwitcher component automatically handles Island-ification for interactive state
- **Colors come from the section**: Place the component inside a section that sets `--hs-section-color` and `--hs-section-backgroundColor` — the component will inherit them automatically
- **Position prominently**: Place in global navigation (header or footer) where users expect language controls
- **Use native language names**: The component automatically displays language names in their native script (e.g., "Español" for Spanish, "Français" for French)
- **Customize header text**: Set `languageSwitcherSelectText` to match your site's primary language for consistency
- **Responsive design**: On mobile (≤767px) the drawer slides in from the right as a full-screen panel below the header, matching the NavigationMenu. The trigger shows only the globe icon. `dropdownPosition` only applies on desktop. Opening the LanguageSwitcher automatically closes the NavigationMenu and vice versa.
- **Test with real translations**: Pass real HubSpot language variants via the `translations` prop, or rely on the default `useLanguageVariants()` hook for production

## Common Patterns

### Header Navigation Integration

```tsx
import LanguageSwitcher from '@hubspot/cms-component-library/LanguageSwitcher';

export default function SiteHeader({ fieldValues }) {
  return (
    <header style={{
      display: 'flex',
      justifyContent: 'space-between',
      padding: '16px 24px',
      alignItems: 'center'
    }}>
      <div>
        <h1>Website Name</h1>
      </div>
      <nav style={{ display: 'flex', gap: '24px', alignItems: 'center' }}>
        <a href="/about">About</a>
        <a href="/services">Services</a>
        <a href="/contact">Contact</a>
        <LanguageSwitcher
          showGlobeIcon={fieldValues.showGlobeIcon}
          display={fieldValues.display}
          languageSwitcherSelectText={fieldValues.languageSwitcherSelectText}
        />
      </nav>
    </header>
  );
}
```

### Footer Placement

```tsx
import LanguageSwitcher from '@hubspot/cms-component-library/LanguageSwitcher';

export default function SiteFooter({ fieldValues }) {
  return (
    <footer style={{ padding: '48px 24px', textAlign: 'center' }}>
      <div style={{ marginBottom: '24px' }}>
        <LanguageSwitcher
          languageSwitcherSelectText={fieldValues.languageSwitcherSelectText}
        />
      </div>
      <p>© 2024 Company Name. All rights reserved.</p>
    </footer>
  );
}
```

### Mobile-First Navigation

```tsx
import LanguageSwitcher from '@hubspot/cms-component-library/LanguageSwitcher';

export default function MobileNav({ fieldValues }) {
  return (
    <nav style={{
      display: 'flex',
      flexDirection: 'column',
      gap: '16px',
      padding: '16px'
    }}>
      <a href="/">Home</a>
      <a href="/about">About</a>
      <a href="/contact">Contact</a>
      <div style={{ borderTop: '1px solid #eee', paddingTop: '16px' }}>
        <LanguageSwitcher dropdownPosition="left" />
      </div>
    </nav>
  );
}
```

## Related Components

- **Drawer**: Used internally for the language selection panel. The LanguageSwitcher uses Drawer with a configurable direction and handles open/close state.
- **Divider**: Used to separate the drawer header from language options.


================================================================================
Source: components/componentLibrary/Link/llm.txt
================================================================================

# Link Component

A simple, semantic link component for wrapping content (text, icons, images) with clickable links. Designed for minimal visual styling while maintaining accessibility and flexibility.

## Import path
```tsx
import Link from '@hubspot/cms-component-library/Link';
```

## Purpose

The Link component provides a lightweight wrapper for making content clickable without the visual weight of a button. It's distinct from the Button component in both purpose and appearance:

- **Link**: Simple content wrapper with minimal styling (this component)
- **Button**: Visual UI button with prominent styling (use even when the button functions as a link)

## When to Use Link vs Button

**Use Link when:**
- Wrapping icons or images with links
- Creating navigation menu items
- Inline text links within content
- Clickable content that doesn't need button-like appearance

**Use Button when:**
- Creating call-to-action elements
- Prominent navigation that should look like a button
- Form submissions or actions
- Any element that needs button-like visual emphasis

## Component Structure

```
Link/
├── index.tsx                     # Main component
├── types.ts                      # TypeScript type definitions
├── ContentFields.tsx             # HubSpot field definitions
├── index.module.scss             # SCSS module with design tokens
└── stories/
    ├── Link.stories.tsx           # Storybook examples
    ├── LinkDecorator.tsx          # Storybook decorator
    └── LinkDecorator.module.scss  # Decorator styles
```

## Props

The Link component supports **two mutually exclusive usage patterns**. You must use one approach or the other, but **NOT BOTH**.

### Pattern 1: Field-based (Recommended for HubSpot CMS)

Pass a `linkField` prop containing HubSpot link field data. The component automatically extracts href, target, and rel values.

```tsx
{
  linkField: {                                       // HubSpot link field value
    url?: { href?: string; type?: string };          // Link URL object
    open_in_new_tab?: boolean;                       // Open in new tab
    no_follow?: boolean;                             // Add nofollow rel
  };
  className?: string;                                // Additional CSS classes
  style?: React.CSSProperties;                       // Inline styles
  children?: React.ReactNode;                        // Link text/content
}
```

### Pattern 2: Manual (For Custom/Programmatic Usage)

Pass individual `href`, `target`, and `rel` props directly.

```tsx
{
  href: string;                                      // Link destination URL (required)
  target?: '_self' | '_blank' | '_parent' | '_top';  // Link target behavior
  rel?: string;                                      // Link relationship (e.g., 'noopener noreferrer')
  className?: string;                                // Additional CSS classes
  style?: React.CSSProperties;                       // Inline styles
  children?: React.ReactNode;                        // Link text/content
}
```

**TypeScript enforces this pattern** - you cannot use `linkField` together with `href`/`target`/`rel`.

## Usage Examples

### Pattern 1: Field-based (Recommended for HubSpot CMS)

#### Basic Field-based Link

```tsx
import Link from '@hubspot/cms-component-library/Link';

// In a HubSpot module:
export default function MyModule({ fieldValues }) {
  return (
    <Link linkField={fieldValues.link}>
      {fieldValues.linkText}
    </Link>
  );
}
```

### Pattern 2: Manual (For Custom/Programmatic Usage)

#### Basic Manual Link

```tsx
import Link from '@hubspot/cms-component-library/Link';

<Link href="https://www.hubspot.com">
  Visit HubSpot
</Link>
```

#### External Link (New Tab)

```tsx
<Link
  href="https://www.hubspot.com"
  target="_blank"
>
  Open in New Tab
</Link>
```

#### Navigation Menu

```tsx
<nav>
  <Link href="/home">Home</Link>
  <Link href="/about">About</Link>
  <Link href="/contact">Contact</Link>
</nav>
```

## HubSpot CMS Integration

### Field Definitions

The Link component provides field definitions for easy integration with HubSpot CMS modules.

#### ContentFields

```tsx
<Link.ContentFields
  linkLabel="Link URL"
  linkName="link"
  linkDefault={{
    url: {
      type: 'EXTERNAL',
      content_id: null,
      href: 'https://www.hubspot.com',
    },
  }}
/>
```

**Props:**
- `linkLabel?: string` — label for the LinkField (default: `'Link'`)
- `linkName?: TLinkName` — field name (default: `'link'`). Generic `TLinkName extends string` lets consumers narrow the field-name literal for compile-time key matching on `fieldVisibility`.
- `linkDefault?` — default value for the LinkField
- `fieldVisibility?: Partial<Record<TLinkName, Visibility>>` — visibility options keyed by field name. Keys must match the `linkName` prop at compile time. Values match the cms-components Visibility schema (e.g. `hidden_subfields`, `controlling_field_path`, `operator`).

**Example — hide the nofollow option (default `linkName`):**
```tsx
<Link.ContentFields
  fieldVisibility={{ link: { hidden_subfields: { no_follow: true } } }}
/>
```

**Example — custom `linkName` (keys must match):**
```tsx
<Link.ContentFields
  linkName="footerLink"
  fieldVisibility={{ footerLink: { hidden_subfields: { no_follow: true } } }}
/>
```

**Fields:**
- `link`: LinkField for href destination

### Module Usage Example

**Recommended (Field-based):**

```tsx
import Link from '@hubspot/cms-component-library/Link';

export default function NavigationModule({ fieldValues }) {
  return (
    <Link linkField={fieldValues.link}>
      {fieldValues.linkText}
    </Link>
  );
}
```

**Manual (only when needed):**

```tsx
export default function NavigationModule({ fieldValues }) {
  return (
    <Link
      href="https://www.hubspot.com"
      target="_blank"
      rel="nofollow"
    >
      Go to HubSpot
    </Link>
  );
}
```

Note: The field-based approach is simpler and less error-prone.

## Styling

### CSS Variables (Theme-provided)

The component reads these CSS variables, which the theme is expected to provide:

**Base Styles:**
- `--hs-section-link-color`: Text color
- `--hs-link-textDecoration`: Text decoration

**Hover / Focus States:**
- `--hs-section-link-color-hover`: Hover/focus text color
- `--hs-link-textDecoration-hover`: Hover/focus text decoration

**Hardcoded Styles (not theme-configurable):**
- `display: inline-block`
- `cursor: pointer`

The component itself does not declare a focus outline — the keyboard focus indicator is provided globally by the consuming theme's stylesheet (e.g. the focus-visible rule in `web-default-templates`'s `main.css`).

### Overriding link styles from consuming components

Because the component reads its styles from CSS custom properties, consumers can override them locally by setting the same vars on an ancestor element (or on the same element — e.g. `Logo` passes its class to `Link` so `.logo` ends up on the `<a>`). This is preferred over adding one-off selectors in the global theme.

```scss
.myWrapper {
  --hs-link-textDecoration: none;
  --hs-link-textDecoration-hover: none;
}
```

## Accessibility

The Link component follows accessibility best practices:

- **Semantic HTML**: Renders native `<a>` elements for proper browser behavior
- **Keyboard Navigation**: Full keyboard support via native link functionality
  - Enter key activates the link
  - Tab key moves focus to/from link
- **Link Security**:
  - Automatically uses `rel="noopener noreferrer"` with `target="_blank"` to prevent security vulnerabilities
  - Prevents new page from accessing `window.opener` object

## Best Practices

- **Use linkField prop when available**: If you have HubSpot field data, use the `linkField` prop instead of manually extracting href/target/rel
- **Do NOT mix approaches**: TypeScript will prevent you from using both `linkField` and `href` props together
- **Security**: The component automatically adds `rel="noopener noreferrer"` when target is `_blank`
- **Minimal styling**: Keep link styling simple; use Button component for prominent CTAs

## Common Patterns

### Navigation Menu Item

```tsx
<Link href="/dashboard">
  Dashboard
</Link>
```

### External Resource

```tsx
<Link
  href="https://developers.hubspot.com"
  target="_blank"
>
  API Documentation
</Link>
```

### Image Wrapper

```tsx
<Link href="/product">
  <img src="product.jpg" alt="Product name" />
</Link>
```

## Related Components

- **Button**: Use for prominent CTAs and elements that need button-like visual styling

### Link vs Button Decision Tree

```
Need to make something clickable?
│
├─ Should it look like a button (prominent, styled)?
│  └─ Use Button component (even if it links to a URL)
│
└─ Should it be a simple link (minimal styling)?
   └─ Use Link component
```


================================================================================
Source: components/componentLibrary/List/llm.txt
================================================================================

# List Component

A flexible list component that renders ordered (`<ol>`) or unordered (`<ul>`) lists with customizable spacing and marker visibility.

## Import path
```tsx
import List, { ListItem } from '@hubspot/cms-component-library/List';
```

## Purpose

The List component provides a consistent way to render lists in HubSpot CMS projects. It combines the semantic correctness of native HTML list elements with the flexibility of the Flex component for layout control. The component supports both ordered (numbered) and unordered (bulleted) lists, with configurable marker visibility.

## Compositional Component Pattern

The List component follows a **parent/child compositional pattern**, consisting of two separate but related components:

| Component | Role | Renders |
|-----------|------|---------|
| `List` | Container | `<ol>` or `<ul>` |
| `ListItem` | Child | `<li>` |

**Why this pattern?**
- **Separation of concerns**: List handles container semantics (ordered vs unordered, gap, marker visibility), while ListItem handles individual item presentation
- **Flexibility**: Modules can pass any content as children to ListItem, including custom icons
- **HubSpot CMS integration**: Each component has its own field definitions, allowing RepeatedGroup usage for dynamic lists
- **Type safety**: Props are scoped to their respective components, preventing invalid configurations

**Key relationships:**
- ListItem should **always** be used inside a List component
- List passes layout context (gap, marker visibility) to children via CSS
- ListItem renders children directly in the `<li>` element

This pattern is distinct from the "Compound Component Pattern" (e.g., `Button.ContentFields`) which attaches field definitions—List uses *both* patterns together.

## Component Structure

```
List/
├── index.tsx                     # Main List component with render logic
├── types.ts                      # TypeScript type definitions
├── ContentFields.tsx             # HubSpot field definitions for list type
├── index.module.scss             # CSS module for list styling
├── ListItem/
│   ├── index.tsx                 # ListItem component
│   ├── types.ts                  # ListItem TypeScript types
│   └── ContentFields.tsx         # HubSpot field definitions for list item text
└── stories/
    ├── List.stories.tsx          # Storybook examples
    └── ListDecorator.tsx         # Storybook decorator
```

## Components

### List (Main Component)

**Purpose:** Container component that renders either an `<ol>` or `<ul>` element based on the `listType` prop, with Flex-based layout control.

**Props:**
```tsx
{
  listType?: 'ordered' | 'unordered';                     // List type - 'ordered' renders <ol>, 'unordered' renders <ul>
  gap?: string;                                           // Space between list items (any valid CSS length value, e.g., '16px', '1rem', '0')
  showMarker?: boolean;                                   // Show list markers (bullets/numbers) - default: true
  className?: string;                                     // Additional CSS classes
  style?: React.CSSProperties;                            // Inline styles
  children?: React.ReactNode;                             // ListItem components
}
```

### ListItem (Child Component)

**Purpose:** Individual list item component that renders an `<li>` element. Children are rendered directly inside the `<li>`.

**Props:**
```tsx
{
  children?: React.ReactNode;                 // List item content (text, elements, icons, etc.)
  className?: string;                         // Additional CSS classes
  style?: React.CSSProperties;                // Inline styles
}
```

## Usage Examples

### Basic Unordered List

```tsx
import List, { ListItem } from '@hubspot/cms-component-library/List';

<List listType="unordered">
  <ListItem>First item</ListItem>
  <ListItem>Second item</ListItem>
  <ListItem>Third item</ListItem>
</List>
```

### Basic Ordered List

```tsx
import List, { ListItem } from '@hubspot/cms-component-library/List';

<List listType="ordered">
  <ListItem>Step one</ListItem>
  <ListItem>Step two</ListItem>
  <ListItem>Step three</ListItem>
</List>
```

### List without Markers

```tsx
import List, { ListItem } from '@hubspot/cms-component-library/List';

<List listType="unordered" showMarker={false}>
  <ListItem>Item without bullet</ListItem>
  <ListItem>Another item without bullet</ListItem>
  <ListItem>Third item without bullet</ListItem>
</List>
```

### List with Custom Gap

```tsx
import List, { ListItem } from '@hubspot/cms-component-library/List';

<List listType="unordered" gap="1rem">
  <ListItem>Spaced item one</ListItem>
  <ListItem>Spaced item two</ListItem>
  <ListItem>Spaced item three</ListItem>
</List>
```

### List Items with Custom Content (Icons)

When you need icons or custom content in list items, set `showMarker={false}` to hide bullets/numbers, then handle your own layout:

```tsx
import List, { ListItem } from '@hubspot/cms-component-library/List';
import Icon from '@hubspot/cms-component-library/Icon';

<List listType="unordered" showMarker={false} gap="0.5rem">
  <ListItem style={{ display: 'flex', gap: '8px', alignItems: 'flex-start' }}>
    <Icon name="check" size={16} />
    <span>Item with custom icon</span>
  </ListItem>
  <ListItem style={{ display: 'flex', gap: '8px', alignItems: 'flex-start' }}>
    <Icon name="check" size={16} />
    <span>Another item with icon</span>
  </ListItem>
</List>
```

### With Custom Styling

```tsx
import List, { ListItem } from '@hubspot/cms-component-library/List';

<List
  listType="unordered"
   gap="8px"
  className="custom-list-class"
  style={{ maxWidth: '500px' }}
>
  <ListItem>Custom styled item</ListItem>
</List>
```

## HubSpot CMS Integration

### Field Definitions

The List component provides field definitions for easy integration with HubSpot CMS modules.

#### List.ContentFields

Configurable props for list type selection:

```tsx
<List.ContentFields
  listTypeName="listType"
  listTypeLabel="List type"
  listTypeDefault="unordered"
/>
```

**Fields:**
- `listType`: ChoiceField for selecting ordered or unordered list

#### ListItem.ContentFields

Configurable props for list item content:

```tsx
<ListItem.ContentFields
  textName="text"
  textLabel="Item text"
  textDefault="Add a list item here."
/>
```

**Fields:**
- `text`: TextField for list item text content

### Module Usage Example

```tsx
import { ModuleMeta } from '../types/modules.js';
import List, { ListItem } from '@hubspot/cms-component-library/List';
import { GapValue } from '@hubspot/cms-component-library/List/types';

type FeatureListModuleProps = {
  listType?: 'ordered' | 'unordered';
  style?: {
    gap?: GapValue;
    showMarker?: boolean;
  };
  listItems?: Array<{
    text?: string;
  }>;
};

export const Component = ({
  listType = 'unordered',
  style,
  listItems = [],
}: FeatureListModuleProps) => {
  const gap = style?.gap ?? '0px';
  const showMarker = style?.showMarker ?? true;

  return (
    <List
      listType={listType}
      gap={gap}
      showMarker={showMarker}
    >
      {listItems.map(({ text }, index) => (
        <ListItem key={index}>{text}</ListItem>
      ))}
    </List>
  );
};

export const meta: ModuleMeta = {
  label: 'Feature List',
  content_types: [],
  icon: '',
  categories: ['text'],
};
```

### Module Fields Example

```tsx
import {
  FieldGroup,
  ModuleFields,
  RepeatedFieldGroup,
  TextField,
} from '@hubspot/cms-components/fields';
import List, { ListItem } from '@hubspot/cms-component-library/List';

const defaultItem = {
  text: 'Add a list item here.',
};

export const fields = (
  <ModuleFields>
    <List.ContentFields />
    <RepeatedFieldGroup
      label="List items"
      name="listItems"
      occurrence={{ min: 1, max: 20, default: 4 }}
      default={[defaultItem, defaultItem, defaultItem, defaultItem]}
    >
      <ListItem.ContentFields />
    </RepeatedFieldGroup>
    <FieldGroup label="Style" name="style" tab="STYLE">
      <TextField
        label="Gap"
        name="gap"
        default="0px"
        helpText="Space between list items (e.g., '0', '0.5rem', '16px')"
      />
    </FieldGroup>
  </ModuleFields>
);
```

## Styling

### CSS Module Classes

**List (`index.module.scss`):**
- `.list`: Base list styles (removes default padding/margin, positions bullets inside)
- `.noMarker`: Applies `list-style: none` (hides markers—modules handle their own internal layout)
- Nested `.list`: Indented styling for nested lists

**ListItem:**
- ListItem renders a plain `<li>` element with no default styles. Use `className` or `style` props for custom styling.

## Accessibility

The List component follows accessibility best practices:

- **Semantic HTML**: Renders appropriate elements (`<ol>` for ordered, `<ul>` for unordered)
- **List Semantics**: Proper `<li>` elements maintain screen reader list navigation
- **Keyboard Navigation**: Native list keyboard navigation is preserved
- **Content Structure**: Lists help screen reader users understand grouped content

## Best Practices

- **Choose the right list type**: Use `ordered` for sequential steps or rankings, `unordered` for non-sequential items
- **Marker visibility**: Use `showMarker={true}` (default) for standard lists, `showMarker={false}` when providing custom markers or icons as children
- **Gap selection**: Use any valid CSS length value (e.g., '0', '0.5rem', '1rem', '16px') for spacing between items
- **Dynamic rendering**: Always provide unique `key` props when mapping arrays to ListItems
- **Custom icons**: When using custom icons, set `showMarker={false}` and pass icons as part of ListItem children
- **Custom content layout**: When using `showMarker={false}`, markers are hidden but no layout is imposed. Modules are responsible for their own internal layout—use `style={{ display: 'flex', gap: '8px', alignItems: 'flex-start' }}` on ListItem or wrap content in a styled container

## Common Patterns

### Feature List with Custom Icons

```tsx
import List, { ListItem } from '@hubspot/cms-component-library/List';
import Icon from '@hubspot/cms-component-library/Icon';

const itemStyle = { display: 'flex', gap: '8px', alignItems: 'flex-start' };

const FeatureList = ({ features }) => (
  <List listType="unordered" showMarker={false} gap="0.5rem">
    {features.map((feature, index) => (
      <ListItem key={index} style={itemStyle}>
        <Icon name="check" size={20} />
        <span>{feature.text}</span>
      </ListItem>
    ))}
  </List>
);
```

### Step-by-Step Instructions

```tsx
import List, { ListItem } from '@hubspot/cms-component-library/List';

const StepList = ({ steps }) => (
  <List listType="ordered" gap="1rem">
    {steps.map((step, index) => (
      <ListItem key={index}>{step.text}</ListItem>
    ))}
  </List>
);
```

### Simple Text List

```tsx
import List, { ListItem } from '@hubspot/cms-component-library/List';

<List listType="unordered">
  <ListItem>Simple item one</ListItem>
  <ListItem>Simple item two</ListItem>
  <ListItem>Simple item three</ListItem>
</List>
```

## Related Components

- **Flex**: Used internally for list layout control. The List component extends Flex capabilities.
- **Icon**: Can be used as children in ListItem for custom icon lists.


================================================================================
Source: components/componentLibrary/Logo/llm.txt
================================================================================

# Logo Component

A responsive logo component that displays brand logos from HubSpot brand settings with optional link wrapping, size constraints, and loading optimization.

## Import path
```tsx
import Logo from '@hubspot/cms-component-library/Logo';
```

## Purpose

The Logo component automatically integrates with HubSpot's brand settings to display the primary brand logo. It handles responsive image rendering, optional link wrapping for navigation, size constraints for different layout contexts (navigation bars, footers, headers), and loading optimization. This component eliminates the need to manually configure logo images across modules and ensures brand consistency throughout the site.

## Component Structure

```
Logo/
├── index.tsx                   # Main component with render logic
├── types.tsx                   # TypeScript type definitions
├── ContentFields.tsx           # HubSpot CMS field definitions
├── index.module.scss           # CSS module with design tokens
├── _dummyLogoData.ts           # Test data for development
└── assets/
    └── hubspot-logo.png        # Default dummy logo asset
```

## Props

```tsx
{
  useDummyLogo?: boolean;       // Use test logo instead of brand settings (development only)
  companyName?: string;         // Fallback text displayed when no brand logo src is available
  className?: string;           // Additional CSS classes
  style?: React.CSSProperties;  // Inline styles
  maxWidth?: string;            // Maximum width constraint (e.g., "150px", "10rem", "50%")
  maxHeight?: string;           // Maximum height constraint (e.g., "50px", "5rem", "10vh")
  loading?: 'lazy' | 'eager' | 'disabled';  // Image loading behavior (defaults to 'eager')
}
```

**Fallback Behavior:** When no brand logo `src` is available and `companyName` is provided, the component renders the company name as styled text instead of returning nothing. If a logo `src` is present, the logo image is always displayed and `companyName` is ignored. If neither a logo `src` nor `companyName` is provided, the component renders nothing.

**Note:** The Logo component does not accept `width` or `height` props directly. The intrinsic dimensions come from the brand settings logo data (`LogoData.width` and `LogoData.height`), which are used to set the image's intrinsic size via the `width` and `height` attributes on the `<img>` element. If no `maxWidth` or `maxHeight` props are provided, the logo will display at its intrinsic size from brand settings (constrained by responsive CSS to a maximum of 100% of the container width). Use `maxWidth` and `maxHeight` to constrain the display size while maintaining the logo's aspect ratio. Both props accept any valid CSS dimension value (pixels, rem, percentages, viewport units, etc.).

**Brand Settings Data Structure:**
The component uses `useBrandSettings()` which returns logo data in this format:
```tsx
{
  src: string;        // Logo image URL
  alt?: string;       // Alt text for accessibility
  width?: number;     // Intrinsic width in pixels (sets image width attribute; establishes initial size when no maxWidth/maxHeight provided)
  height?: number;    // Intrinsic height in pixels (sets image height attribute; establishes initial size when no maxWidth/maxHeight provided)
  link?: string;      // Optional URL for logo link
}
```

## Usage Examples

### Basic Logo

```tsx
import Logo from '@hubspot/cms-component-library/Logo';

<Logo />
```

### Logo in Navigation Bar

When you need to constrain logo size in a navigation header:

```tsx
<nav>
  <Logo maxWidth="120px" />
  {/* Navigation items */}
</nav>
```

### Company Name Fallback

When no brand logo is configured, display the company name instead:

```tsx
<Logo companyName="Acme Corporation" />
```

### Logo with Lazy Loading

For logos that appear below the fold:

```tsx
<Logo
  loading="lazy"
  maxWidth="150px"
/>
```

### Height-Only Constraint

When you only need to constrain the height (e.g., in a navigation bar with fixed height), use `maxHeight` alone. The component automatically maintains the aspect ratio:

```tsx
<nav>
  <Logo maxHeight="50px" />
  {/* Navigation items */}
</nav>
```

**Special Behavior:** When only `maxHeight` is provided (without `maxWidth`), the component automatically:
- Sets `maxInlineSize` to `fit-content` via inline style
- Sets the image's `inlineSize` and `blockSize` to `auto`
- This ensures the logo maintains its natural aspect ratio while respecting the height constraint, allowing the width to scale proportionally

### Custom Sizing

Constrain logo size using both maxWidth and maxHeight while maintaining aspect ratio:

```tsx
<Logo
  maxWidth="250px"
  maxHeight="80px"
/>
```

### Local Development Testing

When testing locally on the dev server where brand settings aren't available, use test data:

```tsx
<Logo useDummyLogo={true} maxWidth="150px" />
```

## HubSpot CMS Integration

### Brand Settings Integration

The Logo component automatically retrieves logo data from HubSpot brand settings using the `useBrandSettings()` hook from `@hubspot/cms-components`. While this hook provides access to multiple logos, this component specifically uses only the primary logo configured in the HubSpot brand kit settings.

### Module Usage Example

```tsx
import Logo from '@hubspot/cms-component-library/Logo';

export default function HeaderModule({ fieldValues }) {
  return (
    <header>
      <Logo
        maxWidth={fieldValues.logoMaxWidth}
        loading="eager"
      />
      <nav>{/* Navigation items */}</nav>
    </header>
  );
}
```

### Field Configuration

The Logo component exposes a `ContentFields` sub-component (`Logo.ContentFields`) that adds a HubSpot CMS `LogoField` to a module. Use this when you want editors to override the brand-settings logo on a per-module basis.

```tsx
import Logo from '@hubspot/cms-component-library/Logo';

// Inside your module's Fields export:
<Logo.ContentFields />
```

`Logo.ContentFields` accepts the following props (all optional):

```tsx
{
  logoLabel?: string;   // Field label shown in editor (default: 'Logo')
  logoName?: string;    // Field name used for fieldValues lookup (default: 'img')
  logoDefault?: {       // Default field values
    override_inherited_src?: boolean;  // default: false
    src?: string | null;               // default: null
    alt?: string | null;               // default: null
    loading?: string;                  // default: 'disabled'
  };
  showLoading?: boolean;  // Show loading selector in editor (default: true)
}
```

When `ContentFields` is not used, all logo data comes from HubSpot brand settings via the `useBrandSettings()` hook automatically.

## Styling

The Logo component applies layout properties as inline styles via props. The `style` prop accepts standard `React.CSSProperties` for additional customization.

**Base defaults (from CSS module):**
- `display: inline-block`
- `inline-size: fit-content`
- `max-inline-size: 100%`
- `max-block-size: none`

The `.logo` wrapper also sets `--hs-link-textDecoration: none` and `--hs-link-textDecoration-hover: none` to suppress underlines when the logo is wrapped in a link.

These defaults are overridden when `maxWidth` or `maxHeight` props are provided.

**Company Name Fallback:**
- The company name fallback text uses `font-size: 28px`, `font-weight: 700`, `color: currentColor`, `white-space: nowrap`, and inherits the heading font family via `var(--hs-headings-fontFamily, sans-serif)`.

## Accessibility

The Logo component follows accessibility best practices:

- **Alt Text**: Uses alt text from brand settings for screen readers. Always provide meaningful alt text in your brand settings (e.g., "Company Name Logo" rather than just "Logo")
- **Semantic Links**: When a link is configured in brand settings, the logo automatically wraps in a semantic `<Link>` component with proper accessibility attributes
- **Keyboard Navigation**: Focus states are clearly visible with customizable outline styles for keyboard users
- **Image Loading**: The `loading` prop allows control over image loading behavior, preventing layout shifts that can confuse assistive technology users
- **Responsive Images**: Uses the Image component internally to provide responsive image handling for better performance across devices

## Best Practices

### Brand Settings Configuration
- **Data source**: Logo data comes from HubSpot brand settings via the `useBrandSettings()` hook. If logo data is not showing up correctly, check the brand settings configuration first
- **Alt text**: The component uses alt text from brand settings. If accessibility issues arise, verify that meaningful alt text (e.g., "Acme Corporation Logo") is configured in brand settings rather than generic text
- **Logo linking**: The component automatically wraps the logo in a link if a link URL is configured in brand settings. If linking behavior is unexpected, check the brand settings link configuration
- **Image quality**: Logo display quality depends on the image uploaded to brand settings. For debugging display issues, verify that appropriately sized images (recommended 2x resolution for retina displays) are configured

### Size Constraints
- **Use maxWidth in constrained layouts**: Navigation bars and footers often need size limits to maintain layout integrity
- **Use maxHeight for height-only constraints**: When you only need to constrain height, the component automatically handles width sizing to maintain aspect ratio
- **Maintain aspect ratio**: The component preserves the logo's aspect ratio from brand settings. Use `maxWidth` and `maxHeight` together to constrain both dimensions, or use one alone to constrain a single dimension
- **Consider responsive breakpoints**: Use the style prop with media queries for responsive sizing

### Performance
- **Use eager loading for above-the-fold logos**: Navigation logos should load immediately (`loading="eager"`)
- **Use lazy loading for below-the-fold logos**: Footer or secondary logos can use `loading="lazy"` to improve initial page load
- **Optimize source images**: Ensure logos in brand settings are appropriately sized and compressed

### Development
- **Use useDummyLogo only in development**: This prop is for testing and should not be used in production code
- **Test with real brand settings**: Always test your modules with actual brand settings before deployment

### Layout Integration
- **Combine with navigation components**: Logos commonly appear alongside navigation menus in headers
- **Provide adequate spacing**: Use CSS margins or padding to ensure the logo has breathing room
- **Consider dark/light themes**: If your site has theme variations, test logo visibility in both contexts

## Integration with Other Components

The Logo component internally uses:

- **Image Component**: For responsive image rendering with proper attributes
- **Link Component**: For accessible link wrapping when logo.link is configured

This ensures consistency with other components in the library and leverages shared functionality for images and navigation.

## Performance Considerations

### Loading Behavior
- `loading="eager"`: Image loads immediately (default) - use for critical above-the-fold logos
- `loading="lazy"`: Image loads when near viewport - use for below-the-fold logos to improve initial page load

### Responsive Images
The component uses the Image component internally, which handles:
- Responsive image sizing based on container
- Proper aspect ratio maintenance
- Efficient loading strategies

## Common Use Cases

### Navigation Header
```tsx
<header style={{ display: 'flex', alignItems: 'center', padding: '1rem' }}>
  <Logo maxHeight="50px" loading="eager" />
  <nav>{/* Navigation menu */}</nav>
</header>
```

### Footer Branding
```tsx
<footer style={{ padding: '2rem', backgroundColor: '#f5f5f5' }}>
  <Logo maxWidth="180px" loading="lazy" />
  <p>© 2026 Company Name. All rights reserved.</p>
</footer>
```

### Centered Hero Logo
```tsx
<section style={{ textAlign: 'center', padding: '4rem' }}>
  <Logo
    maxWidth="300px"
    style={{ display: 'block', margin: '0 auto' }}
  />
  <h1>Welcome to Our Site</h1>
</section>
```

### Sidebar Logo
```tsx
<aside>
  <Logo
    maxWidth="100px"
    style={{ display: 'block' }}
  />
  {/* Sidebar content */}
</aside>
```


================================================================================
Source: components/componentLibrary/Menu/NavigationMenu/llm.txt
================================================================================

# NavigationMenu Component

A horizontal navigation menu component that renders a top-level menu bar with dropdown submenus, keyboard navigation support, and a mobile drawer menu that slides in from the right.

## Import path
```tsx
import NavigationMenu from '@hubspot/cms-component-library/NavigationMenu';
```

## Purpose

The NavigationMenu component provides a horizontal navigation menu for HubSpot CMS projects. It automatically integrates with HubSpot's site menu system, supporting multi-level dropdown menus with keyboard navigation and flexible horizontal alignment options. On mobile viewports, it displays a hamburger button that opens a full-width drawer with a vertical list of top-level menu items. Colors inherit from the section's color scheme (`--hs-section-color` and `--hs-section-backgroundColor`).

## Component Structure

```
NavigationMenu/
├── index.tsx                      # Main component wrapper (Island integration)
├── ContentFields.tsx              # HubSpot field definitions for menu selection
├── islands/
│   ├── NavigationMenuIsland.tsx   # Island component with desktop nav and hamburger
│   ├── MobileMenu.tsx             # Mobile drawer with vertical menu items
│   ├── types.ts                   # TypeScript type definitions
│   └── index.module.scss          # CSS module styles
└── stories/
    ├── NavigationMenu.stories.tsx  # Storybook examples
    └── NavigationMenuDecorator.tsx # Storybook decorator
```

## Components

### NavigationMenu (Main Component)

**Purpose:** Renders a horizontal navigation menu from HubSpot site menu data with dropdown submenus on desktop and a slide-out drawer menu on mobile.

**Props:**
```tsx
{
  menuData: SiteMenu;                    // Menu data from useMenu hook (required)
  justifyMenu?: FlexJustifyContent;      // Horizontal alignment (default: 'flex-start')
  navAriaLabel?: string;                 // ARIA label for navigation (default: 'Navigation')
  hamburgerAriaLabel?: string;           // ARIA label for hamburger button (default: 'Open menu')
  className?: string;                    // Additional CSS classes
  style?: React.CSSProperties;           // Inline styles
}
```

### MobileMenu (Internal Component)

**Purpose:** Renders the mobile drawer with a recursive tree of navigation items and drilldown behavior. Items with children display a right-pointing chevron; clicking them slides in the submenu from the right. A "< Back" button at the top of the drilled-in view returns to the previous level.

**Props:**
```tsx
{
  isOpen: boolean;                       // Whether the drawer is open
  close: () => void;                     // Callback to close the drawer
  menuItems: EnrichedMenuDataItem[];     // Menu items to display
  drawerTop: number;                     // Top position in pixels (below header)
  navAriaLabel: string;                  // ARIA label for mobile nav
  portalTarget?: HTMLElement | null;     // Section element to portal into (set by NavigationMenuIsland)
}
```

## Consuming the Component with useMenu Hook

The NavigationMenu component requires menu data from HubSpot's `useMenu` hook. This hook fetches the site menu structure based on a menu ID defined in your module fields.

### Basic Module Implementation

```tsx
import { ModuleMeta } from '../types/modules.js';
import NavigationMenu from '@hubspot/cms-component-library/NavigationMenu';
import { useMenu, fieldPath } from '@hubspot/cms-components';

export const Component = () => {
  const menuData = useMenu(fieldPath('menu'));

  return (
    <NavigationMenu
      menuData={menuData}
      justifyMenu="flex-start"
      navAriaLabel="Main navigation"
    />
  );
};

export { fields } from './fields.js';

export const meta: ModuleMeta = {
  label: 'Main Navigation Menu',
  content_types: [],
  icon: '',
  categories: ['navigation'],
};
```

### Module Fields Definition

```tsx
import { ModuleFields } from '@hubspot/cms-components/fields';
import NavigationMenu from '@hubspot/cms-component-library/NavigationMenu';

export const fields = (
  <ModuleFields>
    <NavigationMenu.ContentFields />
  </ModuleFields>
);
```

**What this does:**
1. `NavigationMenu.ContentFields` creates a HubSpot MenuField in your module
2. This field allows content editors to select which site menu to display
3. The `useMenu(fieldPath('menu'))` hook fetches the selected menu's data
4. The menu data is passed to the NavigationMenu component via the `menuData` prop

## Usage Examples

### Basic Navigation Menu

```tsx
import NavigationMenu from '@hubspot/cms-component-library/NavigationMenu';
import { useMenu, fieldPath } from '@hubspot/cms-components';

export const Component = () => {
  const menuData = useMenu(fieldPath('menu'));

  return <NavigationMenu menuData={menuData} />;
};
```

## Mobile Behavior

On viewports below the `md` breakpoint (or when `data-hs-breakpoint-name="mobile"` is set):

- The desktop horizontal nav is hidden
- A hamburger button appears, centered in the nav area
- Clicking the hamburger opens a full-width drawer from the right, positioned below the header section
- The hamburger bars animate to an X while open
- The drawer contains a vertical list of menu items with drilldown navigation
- Items with children show a right-pointing chevron; clicking them slides in the submenu from the right (CSS `left` transition, 0.3s ease)
- A "< Back" button appears at the top of the menu when drilled into a submenu, returning to the previous level
- Clicking a leaf nav link closes the drawer
- Pressing ESC closes the drawer at any level (handled by the Drawer component)
- ArrowRight on an item with children drills in; ArrowLeft goes back
- ArrowDown/ArrowUp navigate between siblings
- Closing and reopening the drawer resets to the top-level menu
- Respects `prefers-reduced-motion` (transitions disabled)

## Styling

Colors inherit from the containing section's CSS variables:
- `--hs-section-color`: Text color for links and hamburger bars
- `--hs-section-backgroundColor`: Background color for menu items

The mobile drawer is portaled into the nearest `<section>` ancestor, so these variables cascade naturally without any JavaScript color bridging. The component renders `null` on first render (before mount), which is safe since the drawer starts closed.

Layout values like padding and max-width are defined in the CSS module. The mobile menu uses 10px block padding on the list, 8px block / 32px inline padding on each item, and 12px wide chevron icons.

## HubSpot CMS Integration

### Field Definitions

The NavigationMenu component provides field definitions for menu selection in HubSpot CMS modules.

#### ContentFields

Configurable props for customizing field labels, names, and defaults:

```tsx
<NavigationMenu.ContentFields
  menuLabel="Menu"
  menuName="menu"
  menuDefault="default"
/>
```

**Fields:**
- `menu`: MenuField for selecting which site menu to display (default: 'default')

#### StyleFields

Configurable props for customizing color field labels, names, and defaults. Opacity is not exposed on any of these color fields (`showOpacity={false}`).

```tsx
<NavigationMenu.StyleFields
  linkColorLabel="Text color (default)"
  linkColorName="linkColor"
  linkColorDefault={{ color: '#141414', opacity: 100 }}
  linkColorHoverLabel="Text color (hover)"
  linkColorHoverName="linkColorHover"
  linkColorHoverDefault={{ color: '#141414', opacity: 100 }}
  accentColorHoverLabel="Accent color (hover)"
  accentColorHoverName="accentColorHover"
  accentColorHoverDefault={{ color: '#F9F9F9', opacity: 100 }}
/>
```

**Fields:**
- `linkColor`: ColorField for the default text color of navigation links and the hamburger menu icon
- `linkColorHover`: ColorField for the text color of navigation links on hover
- `accentColorHover`: ColorField for menu item hover state backgrounds and flyout menu borders

## Accessibility

The NavigationMenu component follows accessibility best practices:

- **Semantic HTML**: Renders as a `<nav>` element with an unordered list (`<ul>`)
- **ARIA Labels**: Customizable `aria-label` via the `navAriaLabel` prop; mobile nav gets `Mobile ${navAriaLabel}`
- **ARIA Roles**: List items have `role="menuitem"` for proper screen reader support
- **Hamburger Button**: Uses `aria-expanded` to indicate drawer state, toggles `aria-label` between "Open menu" and "Close menu"
- **Keyboard Navigation**: Full keyboard support with arrow keys
  - `ArrowDown`: Opens submenu or moves to next item
  - `ArrowUp`: Closes submenu or moves to previous item
  - `ArrowRight`: Moves to next top-level item or opens nested submenu
  - `ArrowLeft`: Moves to previous top-level item or returns to parent
  - `Enter`/`Space`: Activates link
  - `Escape`: Closes submenu or mobile drawer
  - `Home`/`End`: Jump to first/last item
- **Focus Management**: Keyboard focus properly managed across menu levels
- **External Links**: Automatically adds `target="_blank"` and `rel="noopener noreferrer"` for external links

## Best Practices

- **Always use useMenu hook**: The NavigationMenu component requires menu data from the `useMenu` hook—never pass hardcoded menu structures
- **Consistent alignment**: Use the same `justifyMenu` value across navigation instances for visual consistency
- **ARIA labels**: Provide descriptive `navAriaLabel` values (e.g., "Main navigation", "Footer navigation")
- **Null checking**: Always check if menuData exists before rendering, as useMenu can return null

## Related Components

- **VerticalMenu**: Vertical navigation menu component for sidebar navigation
- **MenuItem**: Used internally to render individual menu items with support for nested children and keyboard navigation
- **Drawer**: Used internally for the mobile slide-out menu


================================================================================
Source: components/componentLibrary/Menu/VerticalMenu/llm.txt
================================================================================

# VerticalMenu Component

A vertical navigation menu component that renders a hierarchical list of links with support for nested submenus, customizable depth, and flexible styling options.

## Import path
```tsx
import VerticalMenu from '@hubspot/cms-component-library/VerticalMenu';
```

## Purpose

The VerticalMenu component provides a consistent way to render vertical navigation menus in HubSpot CMS projects. It automatically integrates with HubSpot's site menu system, supporting multi-level navigation with configurable maximum depth, customizable colors for links and backgrounds, and adjustable indentation for submenu items.

## Component Structure

```
VerticalMenu/
├── index.tsx                      # Main component wrapper (Island integration)
├── types.ts                       # TypeScript type definitions
├── ContentFields.tsx              # HubSpot field definitions for menu selection
├── islands/
│   ├── verticalMenuIsland.tsx    # Island component with rendering logic
│   └── index.module.scss         # CSS module with design tokens
└── stories/
    ├── VerticalMenu.stories.tsx  # Storybook examples
    └── VerticalMenuDecorator.tsx # Storybook decorator
```

## Components

### VerticalMenu (Main Component)

**Purpose:** Renders a vertical navigation menu from HubSpot site menu data with support for nested submenus.

**Props:**
```tsx
{
  menuData: SiteMenu;                    // Menu data from useMenu hook (required)
  maxDepth?: number;                     // Maximum nesting depth (default: unlimited)
  indentation?: string;                  // Submenu indentation (default: '0px')
  linkColor?: string;                    // Top-level link text color
  linkColorHover?: string;               // Top-level link hover text color
  backgroundColor?: string;              // Top-level link background color
  backgroundColorHover?: string;         // Top-level link hover background color
  submenuLinkColor?: string;             // Submenu link text color
  submenuLinkColorHover?: string;        // Submenu link hover text color
  submenuBackgroundColor?: string;       // Submenu link background color
  submenuBackgroundColorHover?: string;  // Submenu link hover background color
  className?: string;                    // Additional CSS classes
  style?: React.CSSProperties;           // Inline styles with CSS variables
}
```

## Consuming the Component with useMenu Hook

The VerticalMenu component requires menu data from HubSpot's `useMenu` hook. This hook fetches the site menu structure based on a menu ID defined in your module fields.

### Basic Module Implementation

```tsx
import { ModuleMeta } from '../types/modules.js';
import VerticalMenu from '@hubspot/cms-component-library/VerticalMenu';
import { useMenu, fieldPath } from '@hubspot/cms-components';

export const Component = () => {
  // Fetch menu data using the useMenu hook
  // fieldPath('menu') references the menu field defined in fields.tsx
  const menuData = useMenu(fieldPath('menu'));

  return (
    <VerticalMenu
      menuData={menuData}
      maxDepth={2}
      indentation="20px"
    />
  );
};

export { fields } from './fields.js';

export const meta: ModuleMeta = {
  label: 'Vertical Navigation Menu',
  content_types: [],
  icon: '',
  categories: ['navigation'],
};
```

### Module Fields Definition

```tsx
import { ModuleFields } from '@hubspot/cms-components/fields';
import VerticalMenu from '@hubspot/cms-component-library/VerticalMenu';

export const fields = (
  <ModuleFields>
    <VerticalMenu.ContentFields />
  </ModuleFields>
);
```

**What this does:**
1. `VerticalMenu.ContentFields` creates a HubSpot MenuField in your module
2. This field allows content editors to select which site menu to display
3. The `useMenu(fieldPath('menu'))` hook fetches the selected menu's data
4. The menu data is passed to the VerticalMenu component via the `menuData` prop

## HubSpot CMS Integration

### Field Definitions

The VerticalMenu component provides field definitions for menu selection in HubSpot CMS modules.

#### ContentFields

Configurable props for customizing field labels, names, and defaults:

```tsx
<VerticalMenu.ContentFields
  menuLabel="Menu"
  menuName="menu"
  menuDefault="default"
/>
```

**Fields:**
- `menu`: MenuField for selecting which site menu to display (default: 'default')

#### StyleFields

Configurable props for customizing color field labels, names, and defaults. Opacity is not exposed on any of these color fields (`showOpacity={false}`).

```tsx
<VerticalMenu.StyleFields
  linkColorLabel="Text color (default)"
  linkColorName="linkColor"
  linkColorDefault={{ color: '#141414', opacity: 100 }}
  linkColorHoverLabel="Text color (hover)"
  linkColorHoverName="linkColorHover"
  linkColorHoverDefault={{ color: '#141414', opacity: 100 }}
  accentColorHoverLabel="Accent color (hover)"
  accentColorHoverName="accentColorHover"
  accentColorHoverDefault={{ color: '#F9F9F9', opacity: 100 }}
/>
```

**Fields:**
- `linkColor`: ColorField for the default text color of navigation links
- `linkColorHover`: ColorField for the text color of navigation links on hover
- `accentColorHover`: ColorField for menu item hover state backgrounds and flyout menu borders

## Styling

Colors and hover states are applied via `--hs-verticalMenu-*` CSS variables, set internally by the component from the color props (`linkColor`, `linkColorHover`, `backgroundColor`, `backgroundColorHover`, `submenuLinkColor`, `submenuLinkColorHover`, `submenuBackgroundColor`, `submenuBackgroundColorHover`). Layout values like width (`100%`), gap (`0`), padding-inline (`10px`), and padding-block (`8px`) are hardcoded in the CSS module. The `indentation` prop is applied via the `--hs-verticalMenu-paddingInlineStart` CSS variable.

**CSS Variables (set from props):**
- `--hs-verticalMenu-color`: Link text color (default: currentColor)
- `--hs-verticalMenu-backgroundColor`: Link background color (default: transparent)
- `--hs-verticalMenu-color-hover`: Link hover text color
- `--hs-verticalMenu-backgroundColor-hover`: Link hover background color
- `--hs-verticalMenu-paddingInlineStart`: Indentation for submenu items (default: 0px)
- `--hs-verticalMenu-submenu-color`: Submenu link text color
- `--hs-verticalMenu-submenu-backgroundColor`: Submenu link background color
- `--hs-verticalMenu-submenu-color-hover`: Submenu link hover text color
- `--hs-verticalMenu-submenu-backgroundColor-hover`: Submenu link hover background color


## Accessibility

The VerticalMenu component follows accessibility best practices:

- **Semantic HTML**: Renders as a `<nav>` element with nested unordered lists (`<ul>`)
- **ARIA Roles**: List items have `role="menuitem"` for proper screen reader support
- **Keyboard Navigation**: Full keyboard support provided by the MenuItem component
- **Link Semantics**: Uses proper `<a>` elements for links with `href` attributes
- **External Links**: Automatically adds `target="_blank"` and `rel="noopener noreferrer"` for external links
- **Focus Management**: Native focus styles preserved for keyboard users

## Best Practices

- **Always use useMenu hook**: The VerticalMenu component requires menu data from the `useMenu` hook—never pass hardcoded menu structures
- **Control depth wisely**: Use `maxDepth` to limit nesting levels for better UX (1-3 levels recommended)
- **Consistent indentation**: Use consistent indentation values across your site (typically 16px, 20px, or 24px)
- **Color contrast**: Ensure sufficient contrast between link colors and backgrounds (WCAG AA minimum)
- **Hover states**: Always define hover colors for better visual feedback
- **Submenu styling**: Use distinct colors for submenu items to create visual hierarchy


## Related Components

- **NavigationMenu**: Horizontal navigation menu component for top-level navigation bars
- **MenuItem**: Used internally to render individual menu items with support for nested children


================================================================================
Source: components/componentLibrary/Text/llm.txt
================================================================================

# Text Component

A component for rendering HubSpot CMS rich text field content.

## Import path
```tsx
import Text from '@hubspot/cms-component-library/Text';
```

## Purpose

The Text component wraps HubSpot's native `RichText` component to provide a consistent interface for rendering editable rich text content in HubSpot CMS modules. It handles the connection between a CMS field and the rendered output, while exposing a single CSS variable for text color theming. Use this component whenever a module needs a rich text area managed through the HubSpot page editor.

## Component Structure

```
Text/
├── index.tsx                     # Main component wrapping HubSpot RichText
├── types.ts                      # TypeScript type definitions
├── ContentFields.tsx             # HubSpot field definitions for rich text content
└── index.module.scss             # CSS module with design tokens
```

## Components

### Text (Main Component)

**Purpose:** Renders a HubSpot CMS rich text field at the given `fieldPath`, applying CSS module class and CSS variable theming for text color.

**Props:**
```tsx
{
  fieldPath?: string;      // Path to the RichText field in HubSpot CMS (e.g., 'text', 'bodyContent')
  className?: string;      // Additional CSS classes (default: '')
  style?: CSSVariables;    // Inline styles, supports CSS custom properties (default: {})
}
```

## Usage Examples

### Basic Text

```tsx
import Text from '@hubspot/cms-component-library/Text';

<Text fieldPath="text" />
```

## HubSpot CMS Integration

### Field Definitions

The Text component provides `ContentFields` for defining a `RichTextField` inside a HubSpot CMS module.

#### ContentFields

Configurable props for customizing the text field label, name, default value, and editor features:

```tsx
<Text.ContentFields
  textLabel="Body text"
  textName="bodyText"
  textDefault="<p>Enter your content here.</p>"
  textFeatureSet="bodyContent"
  textPlaceholder="Enter body text"
/>
```

**Props:**
- `textLabel` (default: `'Text'`): Label shown in the HubSpot editor
- `textName` (default: `'text'`): Field name used as the `fieldPath` reference in the component
- `textDefault` (default: `'<p>Add text here</p>'`): Default HTML content for the field
- `textFeatureSet` (`'bodyContent' | 'heading' | 'simpleText'`, default: `'bodyContent'`): Controls the editor toolbar
  - Use `'bodyContent'` for body editing (block, alignment, indents, lists, standard/advanced emphasis, link)
  - Use `'heading'` for a reduced feature set suited to titles and captions (heading, alignment, standard emphasis, link)
  - Use `'simpleText'` for minimal editing (icon only)
- `textPlaceholder` (default: `'Add text'`): Placeholder text shown in the inline editor when the field is empty. Forwarded to `RichTextField`'s `placeholder` prop

**Fields:**
- `text` (name set by `textName`): RichTextField for the rich text content

#### Style Fields

**Note:** There are no style fields included with this component. Do not try to import them.

### Module Usage Example

```tsx
import Text from '@hubspot/cms-component-library/Text';

export default function ArticleModule() {
  return (
    <Text fieldPath="bodyText" />
  );
}
```

### Module Fields Example

```tsx
import { ModuleFields } from '@hubspot/cms-components/fields';
import Text from '@hubspot/cms-component-library/Text';

export const fields = (
  <ModuleFields>
    <Text.ContentFields
      textLabel="Body text"
      textName="bodyText"
      textDefault="<p>Enter your content here.</p>"
      textFeatureSet="bodyContent"
    />
  </ModuleFields>
);
```

### Multiple Text Fields in One Module

```tsx
import { ModuleFields } from '@hubspot/cms-components/fields';
import Text from '@hubspot/cms-component-library/Text';

export const fields = (
  <ModuleFields>
    <Text.ContentFields
      textLabel="Heading text"
      textName="headingText"
      textDefault="<p>Enter your heading.</p>"
      textFeatureSet="heading"
    />
    <Text.ContentFields
      textLabel="Body text"
      textName="bodyText"
      textDefault="<p>Enter your body content.</p>"
      textFeatureSet="bodyContent"
    />
  </ModuleFields>
);

export default function ArticleModule() {
  return (
    <div>
      <Text fieldPath="headingText" />
      <Text fieldPath="bodyText" />
    </div>
  );
}
```

## Styling

### CSS Variables

The Text component uses HubSpot theme section variables for all styling. There are no `--hscl-` override variables for this component.

**Used variables:**
- `--hs-section-color`: Text color (falls back to `currentColor`)
- `--hs-section-link-color`: Link color
- `--hs-section-link-color-hover`: Link hover and focus-visible color
- `--hs-section-blockquote-accentColor`: Blockquote left border color
- `--hs-section-blockquote-backgroundColor`: Blockquote background color
- `--hs-section-blockquote-color`: Blockquote text color

## Accessibility

- **Semantic HTML**: Content is rendered directly from the HubSpot CMS editor output, which produces standard HTML elements (`<p>`, `<h1>`–`<h6>`, `<ul>`, `<ol>`, etc.)
- **Structured content**: Encourage editors to use proper heading hierarchy and list elements within the rich text editor for screen reader compatibility
- **Link accessibility**: Links created in the rich text editor will render as native `<a>` elements

## Best Practices

- **Match `fieldPath` to `textName`**: The `fieldPath` prop on the component must match the `textName` prop on `ContentFields` — they reference the same CMS field
- **Choose the right feature set**: Use `'heading'` for simpler text areas (titles, captions) to keep the editor toolbar uncluttered; use `'bodyContent'` for full article or section content
- **Style fields**: There are no style fields for this component. Do not try to import them
- **CSS Variables for theming**: This component uses `--hs-section-*` variables from the theme. There are no `--hscl-` overrides; styling is controlled via the theme's section settings
- **Multiple fields per module**: You can render multiple `Text` components in one module by giving each a unique `textName` / `fieldPath` pair

## Related Components

- **Button**: Use for call-to-action elements within or adjacent to text content
- **Link**: Use for standalone clickable text or wrapped content that requires a link without rich text editing


================================================================================
Source: components/componentLibrary/Ticker/llm.txt
================================================================================

# Ticker Component

A continuously scrolling marquee/ticker component for ambient, looping content. Renders children twice (one visible set, one aria-hidden duplicate) and uses a CSS `@keyframes` animation to create a seamless infinite scroll. Animation speed and timing are calculated dynamically from the content width and the `speed` prop.

## Import path

```tsx
import Ticker from '@hubspot/cms-component-library/Ticker';
```

## When to use Ticker vs Carousel

Use **Ticker** when:
- Content should scroll continuously with no user navigation (logo strips, partner logos, announcement banners, social proof marquees, brand strips)
- The animation is ambient or decorative — users do not need to stop on a specific item
- There is no meaningful "slide 1 of N" structure; items flow past like a news ticker

Use **Carousel** instead when:
- Content is finite and users need to navigate to a specific slide
- Users need to pause, read, or interact with individual items (testimonials, product cards, hero images)
- Prev/next buttons, dots, or a counter are needed

## Island Requirement

**Ticker must be rendered inside an island.** It uses `useState`, `useEffect`, and `ResizeObserver` — none of which can run during server-side rendering. Placing `<Ticker>` directly in a module's `Component` export will fail.

The correct pattern is:
1. Create an `islands/` directory in your module folder
2. Put all `Ticker` usage inside an island file (e.g. `islands/TickerIsland.tsx`) with a default export
3. In the module's `index.tsx`, import the island with the `?island` suffix and render it via `<Island module={...} />`

```tsx
// modules/MyModule/islands/TickerIsland.tsx
import Ticker from '@hubspot/cms-component-library/Ticker';

type Props = { items: string[]; speed: number; pauseOnHover: boolean };

const TickerIsland = ({ items, speed, pauseOnHover }: Props) => (
  <Ticker speed={speed} pauseOnHover={pauseOnHover} gap={40}>
    {items.map((item, i) => <span key={i}>{item}</span>)}
  </Ticker>
);

export default TickerIsland;
```

```tsx
// modules/MyModule/index.tsx
import { Island } from '@hubspot/cms-components';
// @ts-expect-error -- ?island not typed
import TickerIsland from './islands/TickerIsland.js?island';

export const Component = ({ items = [], speed = 50, pauseOnHover = true }) => (
  <Island module={TickerIsland} items={items} speed={speed} pauseOnHover={pauseOnHover} />
);
```

## Component Structure

```
Ticker/
├── index.tsx              # Main component implementation
├── types.ts               # TypeScript type definitions
└── index.module.scss      # CSS module — keyframe animation and layout
```

## Component

### Ticker

**Purpose:** Wraps `children` in a horizontally scrolling container. Children are rendered twice — the first set is visible; the second is aria-hidden to prevent duplicate screen reader announcements. A `ResizeObserver` recalculates the animation duration whenever the content size changes so the scroll speed stays constant regardless of viewport size.

**Props:**
```tsx
{
  speed?: number;              // Scroll speed in pixels per second (default: 50)
  pauseOnHover?: boolean;      // Pause animation when the user hovers (default: true)
  gap?: number | string;       // Gap between items — number = px, string = any CSS unit
  className?: string;          // Applied to the outer wrapper div
  style?: React.CSSProperties; // Applied to the outer wrapper div
  children: React.ReactNode;   // Items to scroll — rendered twice internally
}
```

**How speed works:** The component measures the pixel distance between the two content sets and divides it by `speed` to compute the CSS animation duration in seconds. A higher `speed` value = faster scroll. If `speed` is `0` or the content has zero width, the animation stops.

**Reduced motion:** When `prefers-reduced-motion: reduce` is active, the animation duration is set to `0s` (effectively stopped). The `ResizeObserver` also listens for media query changes and updates the duration live.

## CSS Variables

These CSS variables are set dynamically by the component at runtime and are not intended to be overridden from outside:

- `--ticker-duration` — animation duration in seconds, derived from content width ÷ speed
- `--ticker-shift` — the translateX target in pixels, calculated from the offset between the two content sets

## CSS Classes

- `.ticker` — `overflow: hidden`, full width; the visible clipping container
- `.list` — `display: flex`, `width: max-content`; the animating element
- `.list.running` — applies `animation: tickerScroll var(--ticker-duration) linear infinite`
- `.set` — `display: flex`; one copy of the children; applied to both the visible and the aria-hidden set
- `.ticker.pauseOnHover:hover .list` — sets `animation-play-state: paused` when `pauseOnHover={true}`

## Usage Examples

### Basic Logo Ticker

```tsx
import Ticker from '@hubspot/cms-component-library/Ticker';

<Ticker>
  <img src="/logo-a.svg" alt="Company A" />
  <img src="/logo-b.svg" alt="Company B" />
  <img src="/logo-c.svg" alt="Company C" />
  <img src="/logo-d.svg" alt="Company D" />
</Ticker>
```

### Faster Ticker with Gap

```tsx
<Ticker speed={80} gap={40}>
  <img src="/logo-a.svg" alt="Company A" />
  <img src="/logo-b.svg" alt="Company B" />
  <img src="/logo-c.svg" alt="Company C" />
</Ticker>
```

### Ticker that Does Not Pause on Hover

```tsx
<Ticker pauseOnHover={false} speed={30}>
  <span>Breaking news item one</span>
  <span>Breaking news item two</span>
  <span>Breaking news item three</span>
</Ticker>
```

### Ticker with CSS Gap String

```tsx
<Ticker gap="3rem">
  <div className="logo-item"><img src="/a.svg" alt="A" /></div>
  <div className="logo-item"><img src="/b.svg" alt="B" /></div>
  <div className="logo-item"><img src="/c.svg" alt="C" /></div>
</Ticker>
```

### Module Usage Example

Ticker must live inside an island. The module's `Component` export uses `<Island>` to load it; all Ticker logic lives in the island file.

```tsx
// modules/LogoStripModule/islands/LogoStripIsland.tsx
import Ticker from '@hubspot/cms-component-library/Ticker';

type Logo = { src: string; alt: string };

type Props = { logos: Logo[]; speed: number; gap: number };

const LogoStripIsland = ({ logos, speed, gap }: Props) => (
  <Ticker speed={speed} gap={gap} pauseOnHover={true}>
    {logos.map((logo, i) => (
      <img key={i} src={logo.src} alt={logo.alt} style={{ height: '40px', width: 'auto' }} />
    ))}
  </Ticker>
);

export default LogoStripIsland;
```

```tsx
// modules/LogoStripModule/index.tsx
import { Island } from '@hubspot/cms-components';
// @ts-expect-error -- ?island not typed
import LogoStripIsland from './islands/LogoStripIsland.js?island';

type Logo = { src: string; alt: string };

type ComponentProps = { logos?: Logo[]; speed?: number; gap?: number };

export const Component = ({ logos = [], speed = 50, gap = 40 }: ComponentProps) => (
  <Island module={LogoStripIsland} logos={logos} speed={speed} gap={gap} />
);
```

## Accessibility

- The second copy of children is rendered with `aria-hidden="true"` so screen readers do not announce the content twice
- When `prefers-reduced-motion: reduce` is set, the animation is stopped (`duration: 0`) — content is still visible but static
- The component does not trap focus or intercept keyboard events

## Constraints

- The Ticker component is a **single component** — there are no sub-components or compound patterns
- There are no field definitions (`ContentFields` / `StyleFields`) — the module author wires up fields directly
- `gap` is applied via the CSS `gap` property on both `.set` divs and the `.list` div. It does not affect spacing between the two duplicate sets — that gap is determined by the natural width of the content
- The animation only starts once `duration > 0` (i.e., after the `ResizeObserver` fires in the browser). During SSR, no animation plays
- Items passed as `children` must have fixed or deterministic dimensions for the speed calculation to be accurate. Lazy-loaded images without explicit width/height may cause a brief speed miscalculation until they load


================================================================================
Source: components/componentLibrary/utilityFields/Shadow/llm.txt
================================================================================

# Shadow Utility Field

A standalone, reusable field definition that adds a consistent drop shadow control to any HubSpot CMS module. Part of the `utilityFields` category — not a UI component, but a composable field + CSS utility pair that can be attached to any element.

## Import path
```tsx
import Shadow from '@hubspot/cms-component-library/utilityFields/Shadow';
```

## Purpose

The Shadow utility field provides a consistent editor experience for drop shadow depth across modules and elements. It exposes a four-choice field (`none`, `subtle`, `medium`, `bold`) along with a CSS utility function (`getShadowClass`) that maps the field value to a co-located CSS module class. Use it when you want to give editors control over shadow depth without each module independently defining its own shadow values.

For non-rectangular elements (e.g. SVG-based shapes), use only the field for consistent UI and implement the CSS yourself using `filter: drop-shadow()`.

## Structure

```
utilityFields/Shadow/
├── StyleFields.tsx       # ChoiceField definition (none/subtle/medium/bold)
├── index.module.css      # Co-located box-shadow CSS classes
├── index.ts              # Exports { StyleFields, getShadowClass }
└── types.ts              # ShadowDepth type and StyleFieldsProps
```

## StyleFields

Adds a `Shadow` button-group choice field to the module editor.

**Configurable props:**
```tsx
<Shadow.StyleFields
  shadowLabel="Shadow"         // Field label (default: 'Shadow')
  shadowName="shadow"          // Field name in HubSpot data (default: 'shadow')
  shadowDefault="none"         // Default value (default: 'none')
  shadowAgentContext="..."     // AI context string
/>
```

**Field choices:** `none` | `subtle` | `medium` | `bold`

## getShadowClass

Maps a `ShadowDepth` value to the corresponding CSS module class from `index.module.css`. Returns `undefined` for `'none'` (no class applied).

```tsx
Shadow.getShadowClass('none')    // undefined
Shadow.getShadowClass('subtle')  // styles.shadowSubtle → box-shadow: 0px 2px 8px rgba(0,0,0,0.08)
Shadow.getShadowClass('medium')  // styles.shadowMedium → box-shadow: 0px 4px 16px rgba(0,0,0,0.12)
Shadow.getShadowClass('bold')    // styles.shadowBold   → box-shadow: 0px 8px 32px rgba(0,0,0,0.18)
```

## Shadow Values Reference

| Depth  | box-shadow                              |
|--------|-----------------------------------------|
| subtle | `0px 2px 8px rgba(0, 0, 0, 0.08)`      |
| medium | `0px 4px 16px rgba(0, 0, 0, 0.12)`     |
| bold   | `0px 8px 32px rgba(0, 0, 0, 0.18)`     |

## Usage Examples

### Full usage: field + CSS utility (most common)

```tsx
// fields.tsx
import { FieldGroup, ModuleFields } from '@hubspot/cms-components/fields';
import Shadow from '@hubspot/cms-component-library/utilityFields/Shadow';

export const fields = (
  <ModuleFields>
    <FieldGroup label="Style" name="style" tab="STYLE">
      <Shadow.StyleFields />
    </FieldGroup>
  </ModuleFields>
);
```

```tsx
// index.tsx
import Shadow from '@hubspot/cms-component-library/utilityFields/Shadow';
import { ShadowDepth } from '@hubspot/cms-component-library/utilityFields/Shadow/types';
import cx from '@hubspot/cms-component-library/utils/classname';

type Props = {
  style: { shadow: ShadowDepth };
};

export const Component = ({ style: { shadow } }: Props) => (
  <div className={cx(styles.wrapper, Shadow.getShadowClass(shadow))}>
    {/* content */}
  </div>
);
```

### Field-only usage (for SVG/non-rectangular elements)

Use `Shadow.StyleFields` for consistent field UI, but apply the shadow yourself via `filter`:

```tsx
// fields.tsx — same as above

// index.tsx
const SHADOW_FILTER = {
  subtle: 'drop-shadow(0px 2px 8px rgba(0, 0, 0, 0.08))',
  medium: 'drop-shadow(0px 4px 16px rgba(0, 0, 0, 0.12))',
  bold:   'drop-shadow(0px 8px 32px rgba(0, 0, 0, 0.18))',
};

const filter = shadow !== 'none' ? SHADOW_FILTER[shadow] : undefined;

<div style={{ ...(filter && { filter }) }}>
  <svg>...</svg>
</div>
```

### Custom field name (when shadow is nested in a field group)

```tsx
<Shadow.StyleFields shadowName="cardShadow" />

// Props type reflects the custom name
type Props = {
  style: { cardShadow: ShadowDepth };
};
```

## When to use utilityFields vs component StyleFields

- **Use `utilityFields/Shadow`** when shadow depth is an opt-in concern for the module author — it's not intrinsic to any one component and should apply to the module's primary element.
- **Do not** add shadow directly to a component's `StyleFields` — shadow is an element-level concern, not a component-level one. A `Card` component shouldn't own the shadow; the module composing the card decides whether to attach shadow.


================================================================================
Source: components/componentLibrary/Video/llm.txt
================================================================================

# Video Component

The Video component supports HubSpot-hosted video (via `@hubspot/video-player-core`) and embedded video (oEmbed URLs or raw HTML embeds). Both modes are fully client-side interactive via Islands. HubSpot video includes play analytics tracking to populate key performance metrics.

## Import path

```tsx
import Video from '@hubspot/cms-component-library/Video';
```

## Purpose

The Video component provides a unified interface for rendering video content in HubSpot CMS modules. It handles two distinct video types, each rendered through a dedicated client-side Island:

- **`hubspot_video`**: Renders a HubSpot-hosted video via Mux player with a custom theme (from `@hubspot/video-player-core`). Supports server-side video data prefetching to avoid a client-side fetch on load. Includes play analytics tracking (initial play, seconds viewed, completed play).
- **`embed`**: Renders third-party video via oEmbed URL or raw HTML embed. For oEmbed, supports an optional custom thumbnail with a play button overlay that defers iframe load until clicked.

## Component Structure

```
Video/
├── index.tsx                          # Main component — dispatches to HSVideoIsland or EmbedVideoIsland
├── index.module.scss                  # CSS module for the video wrapper
├── types.ts                           # TypeScript type definitions for all Video types
├── ContentFields.tsx                  # HubSpot field definitions for content
├── StyleFields.tsx                    # HubSpot field definitions for styling
├── serverUtils.ts                     # Server-side video fetch utility (fetchHSVideoServerSide)
├── TrackHSVideoAnalytics.tsx          # Analytics tracking component (used inside HSVideoIsland)
├── hooks/
│   ├── usePageMeta.tsx                # Hook to collect page metadata for analytics
│   ├── useQueryParam.tsx              # Hook to read URL query params client-side
│   └── useUserToken.tsx               # Hook to read HubSpot contact UTK with privacy consent
├── utils/
│   └── videoAnalytics.ts              # Analytics event tracking APIs
└── islands/
    ├── HSVideoIsland.tsx              # Client Island: HubSpot video player + analytics
    ├── EmbedVideoIsland.tsx           # Client Island: oEmbed/HTML embed video
    └── index.module.scss              # CSS module for embed video island styles
```

## Props

```tsx
{
  videoType: 'hubspot_video' | 'embed';            // Which video mode to render
  hubspotVideoParams?: HubSpotVideoParams;         // Required when videoType is 'hubspot_video'
  embedVideoParams?: EmbedVideoParams;             // Required when videoType is 'embed'
  oembedThumbnail?: { src: string; alt?: string }; // Optional custom thumbnail for oEmbed videos
  playButtonColor?: ColorFieldValue;               // Play button color (HubSpot video, and oEmbed with custom thumbnail)
  video?: VideoCrmObject;                          // Server-prefetched video data (optional; falls back to client-side fetch)
  className?: string;                              // CSS class applied to the outer video wrapper div
}
```

## Server-Side Data Fetching

For HubSpot video, the component supports prefetching video data server-side to avoid a client fetch on page load. Use `fetchHSVideoServerSide` from `serverUtils.ts` inside your module's `getServerSideProps`. Note that `getServerSideProps` is only available in Professional and Enterprise portals.

If `video` is not passed (or `undefined`), `HSVideoIsland` automatically falls back to fetching it client-side via `useVideo` from `@hubspot/video-player-core`.

```tsx
import { fetchHSVideoServerSide } from '../../componentLibrary/Video/serverUtils.js';

export const getServerSideProps = withModuleProps(async ({ fieldValues }) => {
  const video = await fetchHSVideoServerSide(
    fieldValues.videoType,
    fieldValues.hubspotVideo
  );
  return { serverSideProps: { video }, caching: {} };
});
```

## HubSpot CMS Integration

### ContentFields

```tsx
<Video.ContentFields
  videoTypeLabel="Video type"
  videoTypeName="videoType"
  hsVideoLabel="HubSpot video"
  hsVideoName="hubspotVideo"
  embedVideoLabel="Embed using"
  embedVideoName="embedVideo"
  includeOembedThumbnailLabel="Include custom thumbnail"
  includeOembedThumbnailName="includeOembedThumbnail"
  oembedThumbnailLabel="Custom thumbnail"
  oembedThumbnailName="oembedThumbnail"
/>
```

**Fields:**
- `videoType`: ChoiceField (pill display) — choices are `['embed', 'Embedded']` and `['hubspot_video', 'HubSpot hosted']`. Defaults to `'embed'`. Only shown to portals with the `marketing-video` scope (Pro+ tier), or when currently set to `hubspot_video`.
- `hubspotVideo`: VideoField — shown only when `videoType === 'hubspot_video'`. Default overridable via `hsVideoDefault` prop.
- `embedVideo`: EmbedField — supports `oembed` and `html` source types; shown only when `videoType === 'embed'`. Default overridable via `embedVideoDefault` prop.
- `includeOembedThumbnail`: BooleanField (toggle) — shown only for oEmbed videos with a URL; controls visibility of the custom thumbnail image field. Defaults to `false`. Default overridable via `includeOembedThumbnailDefault` prop.
- `oembedThumbnail`: ImageField — shown only when `includeOembedThumbnail` is true. Default overridable via `oembedThumbnailDefault` prop.

**Important:** The `videoType` field defaults to `'embed'` because the field is hidden from portals without the `marketing-video` scope.

### StyleFields

```tsx
<Video.StyleFields
  playButtonColorLabel="Play button color"
  playButtonColorName="playButtonColor"
  playButtonColorDefault={{ color: '#F7761F', opacity: 100 }}
/>
```

**Fields:**
- `playButtonColor`: ColorField — shown for HubSpot video, and for oEmbed video when a custom thumbnail with a URL is set

### Module Usage Example

```tsx
// fields.tsx
import { FieldGroup, ModuleFields } from '@hubspot/cms-components/fields';
import Video from '../../componentLibrary/Video/index.js';

export const fields = (
  <ModuleFields>
    <Video.ContentFields />
    <FieldGroup label="Design" name="groupDesign" tab="STYLE">
      <Video.StyleFields />
    </FieldGroup>
  </ModuleFields>
);
```

```tsx
// index.tsx
import { withModuleProps } from '@hubspot/cms-components';
import { fetchHSVideoServerSide } from '../../componentLibrary/Video/serverUtils.js';
import Video from '../../componentLibrary/Video/index.js';

type ComponentProps = {
  fieldValues: {
    videoType: 'hubspot_video' | 'embed';
    hubspotVideo?: HubSpotVideoParams;
    embedVideo?: EmbedVideoParams;
    oembedThumbnail?: { src: string; alt?: string };
    groupDesign: { playButtonColor: typeof ColorFieldDefaults };
  };
  serverSideProps: {
    video?: VideoCrmObject;
  };
};

export const Component = ({ fieldValues, serverSideProps }: ComponentProps) => {
  const { videoType, hubspotVideo, embedVideo, oembedThumbnail, groupDesign } = fieldValues;

  return (
    <Video
      videoType={videoType}
      hubspotVideoParams={hubspotVideo}
      embedVideoParams={embedVideo}
      oembedThumbnail={oembedThumbnail}
      playButtonColor={groupDesign.playButtonColor}
      video={serverSideProps.video}
    />
  );
};

export const getServerSideProps = withModuleProps(async ({ fieldValues }) => {
  const video = await fetchHSVideoServerSide(
    fieldValues.videoType,
    fieldValues.hubspotVideo
  );
  return { serverSideProps: { video }, caching: {} };
});
```

## Embed Video Behavior

### oEmbed

When `source_type === 'oembed'` and an `oembed_url` is present:

- If the `oembed_response` is pre-populated by HubSpot (typical in production), it is used directly without a client-side fetch.
- If `oembed_response` is absent, `EmbedVideoIsland` fetches it client-side from `/_hcms/oembed`.
- If `oembedThumbnail.src` is provided, the iframe is deferred: a thumbnail + play button is shown, and the iframe (with `autoplay=1`) is only injected on click.  Note: the play button click is a no-op if `oembed_response` has not yet resolved (i.e., `iframeRef.current` is null) — this can occur briefly when `oembed_response` is absent and the client-side fetch is still in progress.
- `playButtonColor` controls the play button overlay SVG fill color via an inline style.

### HTML Embed

When `source_type === 'html'` and `embed_html` is present, the HTML is rendered directly via `dangerouslySetInnerHTML`.

## Play Analytics (HubSpot Video Only)

`HSVideoIsland` renders `TrackHSVideoAnalytics` when video data is resolved. It tracks:

- **Initial play** (`trackPlayEvent`): Fired once on first playback start
- **Seconds viewed** (`trackSecondsViewed`): Reported in 10-second intervals and on page visibility change, using 1-second chunk tracking
- **Completed play** (`trackCompletedPlay`): Fired on video end, visibility change, or when all chunks are viewed (for looped videos)

Analytics require HubSpot tracking code (`_hsq`) and privacy consent to be present. Events will not be tracked if the contact UTK or page metadata is unavailable.

## Best Practices

- **Always use `getServerSideProps` (in Pro/Enterprise accounts) with `fetchHSVideoServerSide`** for HubSpot video modules. This avoids a visible client-side fetch delay on page load. If the server fetch fails, `HSVideoIsland` falls back to fetching client-side automatically.
- **Pass `video` from `serverSideProps`** through to the `Video` component prop — this is how the server-fetched data reaches the Island.
- **Field name alignment**: The `VideoField` field name (default `hubspotVideo`) maps to `hubspotVideo` in `fieldValues`, but the `Video` component prop is `hubspotVideoParams`. Pass `fieldValues.hubspotVideo` as `hubspotVideoParams`.
- **`playButtonColor` visibility**: The play button color field is conditionally shown — only for HubSpot video or oEmbed with a custom thumbnail. Do not expect it to always be visible in the editor.
- **`videoType` default**: The `videoType` field defaults to `'embed'` for all portals. This cannot currently be changed per-scope at field definition time.
