# Titan Casket Component Library

A Nuxt 4 + Vue 3 component library that powers Titan Casket customer journeys. The project ships typed UI primitives styled with Tailwind, documented scenarios, and a comprehensive quality bar enforced by automated testing.

**Dual-purpose library:**

- 📦 **NPM Package**: Install in any Vue 3 project
- 🔌 **Nuxt Layer**: Extend in Nuxt 4 applications

## Installation

### As NPM Package (Vue 3 Projects)

```bash
# Using npm
npm install @titancasket/component-library

# Using yarn
yarn add @titancasket/component-library

# Using pnpm
pnpm add @titancasket/component-library
```

#### Setup Tailwind CSS

Add the Titan preset to your `tailwind.config.js`:

```js
export default {
    presets: [require('@titancasket/component-library/tailwind.preset')],
    content: ['./src/**/*.{vue,js,ts}', './node_modules/@titancasket/component-library/dist/**/*.{js,mjs}'],
};
```

#### Import Components

```vue
<script setup>
import { TitanButton, TitanInput, TitanSelect, TitanIconSelect } from '@titancasket/component-library';
</script>

<template>
    <TitanButton variant="default">Click me</TitanButton>
    <TitanInput v-model="email" label="Email" type="email" />
</template>
```

### As Nuxt Layer (Nuxt 4 Projects)

Add to your `nuxt.config.ts`:

```ts
export default defineNuxtConfig({
    extends: ['@titancasket/component-library'],
});
```

Components and composables are auto-imported.

## Available Components

### UI Components

#### `TitanButton`
Button component with two variants and customizable sizes.
- **Variants**: `default` (yellow fill), `outline` (transparent with yellow border)
- **Sizes**: `sm`, `md`, `lg`
- **Features**: Optional arrow, disabled state, full accessibility support

```vue
<TitanButton variant="default" size="md" @click="handleClick">
  Click me
</TitanButton>
```

#### `TitanIcon`

Dynamic icon component that renders SVG icons from the auto-generated icon registry.

The library includes an automated icon system with 12 SVG icons:

- **UI Icons** (8): casket, casket-open, casket-closed, family, fire, question-mark, tombstone, urn
- **Benefits Icons** (4): cart, truck, casket, heart

Icons are automatically generated from `public/icons/` and use `currentColor` for CSS-based styling.

```vue
<div class="w-6 h-6 text-titan-green-800">
  <TitanIcon name="ui/casket" aria-label="Casket selection" />
</div>
```

See [TitanIcon documentation](docs/components/TitanIcon.md) for details.

#### `TitanInput`
Floating label text input with validation support.
- **Types**: `text`, `email`, `password`, `number`, `tel`, `url`
- **Features**: Floating labels, error states, required field indicator, placeholder support
- **Accessibility**: Full ARIA support, keyboard navigation

```vue
<TitanInput
  v-model="email"
  label="Email Address"
  type="email"
  :required="true"
  :error="hasError"
  error-message="Please enter a valid email"
/>
```

#### `TitanTextarea`
Multi-line text input with floating labels.
- **Features**: Auto-resizing, character limits, floating labels, error states
- **Accessibility**: ARIA support with proper labeling and error announcements

```vue
<TitanTextarea
  v-model="message"
  label="Your Message"
  :rows="4"
  :max-length="500"
  :required="true"
/>
```

#### `TitanSelectContainer`
Flexible select component with three visual variants:
- **Inline**: Pill-based buttons (compact, horizontal)
- **Icon**: Icon cards with custom icon slots
- **Stacked**: Full-width buttons with title and description

Features: Single/multi-select, custom values, keyboard navigation, ARIA support

```vue
<!-- Inline variant (pills) -->
<TitanSelectContainer
  v-model="selected"
  :options="['Option 1', 'Option 2', 'Option 3']"
  variant="inline"
  label="SELECT AN OPTION"
/>

<!-- Icon variant with slots -->
<TitanSelectContainer
  v-model="disposition"
  :options="[
    { value: 'buried', label: 'Buried' },
    { value: 'cremated', label: 'Cremated' }
  ]"
  variant="icon"
  label="CHOOSE A DISPOSITION"
>
  <template #icon-buried>🪦</template>
  <template #icon-cremated>⚱️</template>
</TitanSelectContainer>

<!-- Stacked variant with title/description -->
<TitanSelectContainer
  v-model="serviceType"
  :options="[
    {
      value: 'traditional',
      label: 'traditional',
      title: 'Traditional service',
      description: 'A ceremony at a place of worship.'
    }
  ]"
  variant="stacked"
  label="WITH A:"
/>

<!-- Multiple selection with custom values -->
<TitanSelectContainer
  v-model="selected"
  :options="['Flowers', 'Donation', 'Memorial']"
  variant="inline"
  multiple
  allow-custom
  show-checkmark
/>
```

See [TitanSelectContainer documentation](docs/components/TitanSelectContainer.md) for complete API and usage examples.

#### `TitanSelect` (Deprecated)
Pill-based single or multi-select component with custom value support.
- **Note**: This is now a wrapper around `TitanSelectContainer` for backward compatibility. New code should use `TitanSelectContainer` with `variant="inline"`.
- **Modes**: Single-select or multi-select
- **Features**: Keyboard navigation (Arrow keys, Home, End), custom value entry, disabled options, toggle deselect
- **Styling**: Selected items highlighted in Titan Purple, unselected in light green

```vue
<!-- Single select -->
<TitanSelect
  v-model="selectedOption"
  label="Choose one"
  :options="['Option 1', 'Option 2', 'Option 3']"
/>

<!-- Multi-select with custom values -->
<TitanSelect
  v-model="selectedItems"
  label="Choose multiple"
  :options="options"
  :multiple="true"
  :allow-custom="true"
/>
```

#### `TitanIconSelect` (Deprecated)
Icon-based single-select component with card layout for visual selection.
- **Note**: This is now a wrapper around `TitanSelectContainer` for backward compatibility. New code should use `TitanSelectContainer` with `variant="icon"`.
- **Layout**: 100px × 102px cards with icons and labels
- **Icons**: Provided via named slots (e.g., `#icon-buried`)
- **Features**: Single-select only, keyboard navigation (Arrow keys, Space, Enter), disabled state, required validation, optional checkmark
- **Styling**: Selected cards in Titan Purple with white text, unselected in light green with visible borders
- **Accessibility**: Full ARIA support with radiogroup/radio roles

```vue
<TitanIconSelect
  v-model="selectedOption"
  name="memorialization"
  :options="[
    { value: 'buried', label: 'Buried' },
    { value: 'cremated', label: 'Cremated' },
    { value: 'other', label: 'Other' }
  ]"
  :show-checkmark="false"
>
  <template #icon-buried>
    <svg><!-- Headstone icon --></svg>
  </template>
  <template #icon-cremated>
    <svg><!-- Urn icon --></svg>
  </template>
  <template #icon-other>
    <svg><!-- Flames icon --></svg>
  </template>
</TitanIconSelect>
```

#### `TitanDatePicker`
Date selection component with proper placeholder handling.
- **Features**: Date validation, required field support, error states, accessible labeling
- **Accessibility**: Proper ARIA attributes and keyboard support

```vue
<TitanDatePicker
  v-model="selectedDate"
  label="Select a date"
  :required="true"
/>
```

#### `TitanImageUpload`
Drag-and-drop image upload with preview.
- **Features**: Click or drag to upload, image preview, file validation, error handling
- **Accessibility**: Keyboard accessible with proper ARIA labels

```vue
<TitanImageUpload
  v-model="uploadedImage"
  label="Upload Image"
  :max-size="5242880"
/>
```

#### `TitanInfoPanel`
Informational panel component for displaying tips and guidance.
- **Variants**: `default` (light background), `compact` (minimal padding), `transparent` (no background)
- **Features**: Optional title, bullet list support, slot support for custom content

```vue
<TitanInfoPanel
  title="Tips for a great photo"
  :items="['Use high resolution', 'Ensure good lighting', 'Face the camera']"
  variant="default"
/>
```

#### `TitanSubheader`
Section heading component for organizing form content.
- **Typography**: 13px Work Sans Bold with uppercase styling (Figma-accurate)
- **Features**: Consistent spacing, Titan green color, custom class support
- **Use Cases**: Form section headers, content dividers, subsection titles

```vue
<TitanSubheader text="What are you envisioning for the parade elements?" />
```

#### `TitanProgressBar`
Progress indicator with step counter for multi-step processes.
- **Layout**: Full-width bar with label showing current/total (e.g., "3/8")
- **Design**: 6px height bar with Titan yellow fill, 8px gap between bar and label
- **Features**: Smooth 500ms transitions, automatic percentage calculation, validation warnings
- **Accessibility**: Full ARIA progressbar support with value announcements
- **Use Cases**: Multi-step forms, onboarding flows, task completion tracking

```vue
<TitanProgressBar :current="3" :total="8" />
```

### Layout Components

#### `TitanFormSection`
Schema-based dynamic form renderer for creating forms declaratively.
- **Features**: Dynamic component rendering, v-model integration, mixed component support (form + non-form)
- **Benefits**: Reduced boilerplate, consistent spacing, easy form structure changes
- **Schema Support**: TitanInput, TitanTextarea, TitanSelect, TitanDatePicker, TitanSubheader, and more

```vue
<script setup>
const formData = ref({
  paradeElements: '',
  colors: '',
});

const schema = [
  {
    component: 'TitanSubheader',
    props: { text: 'What are you envisioning for the parade elements?' }
  },
  {
    component: 'TitanTextarea',
    props: { label: 'Notes', rows: 3 },
    model: 'paradeElements'
  },
  {
    component: 'TitanSubheader',
    props: { text: 'Any particular colors you would like represented?' }
  },
  {
    component: 'TitanTextarea',
    props: { label: 'Notes', rows: 3 },
    model: 'colors'
  }
];
</script>

<template>
  <TitanFormSection v-model="formData" :schema="schema" />
</template>
```

**Composable**: The `useFormSchema()` composable provides utilities for schema validation, form data initialization, and field validation.

#### `TitanCard`
Flexible card container with header, content, and optional logo.
- **Variants**: `default` (white background), `system` (translucent with backdrop blur)
- **Features**: Optional title, header slot, logo display (top-right), responsive design
- **Styling**: Rounded corners, shadow, grid-based layout

```vue
<TitanCard variant="default" title="Welcome" :logo="true">
  <p>Your content goes here</p>
</TitanCard>
```

#### `TitanStepper`
Step indicator component for multi-step forms and wizards.
- **Features**: Current step tracking, completed step indicators, step labels
- **Accessibility**: Proper ARIA landmarks and step announcements

```vue
<TitanStepper
  :steps="['Personal Info', 'Upload Photo', 'Review']"
  :current-step="1"
/>
```

### Design System

The library includes a complete Titan Casket design system with scientifically-generated color scales:

- **Primary Green**: `#003822` (titan-green-800) - Main brand color
- **Secondary Yellow**: `#facf60` (titan-yellow-400) - Accent and CTAs
- **Tertiary Purple**: `#3F1038` (titan-purple-500) - Selection highlights

All components use these brand colors and are fully typed with TypeScript. Import the Tailwind preset to access the complete design token system.

## Development Setup

1. Install dependencies (Node 18+, Yarn 1.x):
    ```bash
    yarn install
    ```
2. Launch the playground at `http://localhost:3000`:
    ```bash
    yarn dev
    ```
3. Build the library for NPM publishing:
    ```bash
    yarn build          # Builds standalone Vue 3 library to dist/
    ```
4. Build Nuxt layer or preview:
    ```bash
    yarn build:nuxt     # Build Nuxt layer
    yarn preview        # Preview production build
    yarn generate       # Generate static site
    ```

## Scripts & Tooling

| Command                   | Purpose                                                                            |
| ------------------------- | ---------------------------------------------------------------------------------- |
| `yarn dev`                | Run the Nuxt development server with HMR.                                          |
| `yarn build`              | **Build standalone Vue 3 library** to `dist/` (for NPM publishing).                |
| `yarn lib:build`          | Same as `yarn build` - builds NPM package.                                         |
| `yarn build:nuxt`         | Build Nuxt layer for production.                                                   |
| `yarn preview`            | Preview the production build locally.                                              |
| `yarn generate`           | Produce static output for Jamstack-style hosting.                                  |
| `yarn prepare`            | Prepare Nuxt layer (runs automatically after install in Nuxt projects).            |
| `yarn storybook`          | Launch Storybook at `http://localhost:6006` for interactive component development. |
| `yarn build-storybook`    | Build Storybook as a static site for deployment.                                   |
| `yarn test`               | Execute Vitest unit suites with Happy DOM.                                         |
| `yarn test:watch`         | Keep Vitest running for rapid feedback.                                            |
| `yarn test:coverage`      | Generate coverage reports (minimum 80%).                                           |
| `yarn test:visual`        | Run Playwright snapshot tests across browsers.                                     |
| `yarn test:visual:update` | Update Playwright baselines after approved design changes.                         |
| `yarn test:storybook`     | Run Storybook test runner (Playwright-based) for story validation.                 |
| `yarn test:all`           | Chain unit + visual + Storybook runs; required before PRs.                         |

## Directory Layout

```
app/
├─ components/
│  ├─ ui/          # Exported UI primitives (TitanButton, TitanInput, TitanSelect, TitanIconSelect, ...)
│  │              # Each component has a .stories.ts file for Storybook
│  └─ layout/      # Structural/layout wrappers
├─ composables/    # Shared logic (useFormField, etc.)
├─ pages/          # Playground demos used by visual tests
├─ types/          # Component-specific TypeScript contracts
├─ utils/          # Reusable helpers and formatters

.storybook/
├─ main.ts         # Storybook + Vite + Tailwind configuration
├─ preview.ts      # Global styles and preview settings
└─ test-runner.ts  # Playwright-based test runner config

docs/
├─ components/     # Consumer-facing component documentation
└─ templates/      # Documentation boilerplate for component docs

.agent/
└─ sop/           # Standard Operating Procedures (internal docs)
    ├─ component-creation-process.md
    ├─ testing-guide.md
    └─ npm-publishing.md

tests/
├─ unit/           # Vitest specs grouped by domain
├─ visual/         # Playwright visual regression suites
├─ fixtures/       # Shared datasets and mocks
└─ templates/      # Copyable test scaffolding
```

Nuxt, Tailwind, Vitest, and Playwright configurations live at the repo root (`nuxt.config.ts`, `tailwind.config.js`, `vitest.config.ts`, `playwright.config.ts`).

## Component Development Workflow

Follow the repeatable process captured in `.agent/sop/component-creation-process.md`. In short:

- Plan props, events, and accessibility before coding.
- Scaffold Vue SFCs with `<script setup lang="ts">` and two-space indentation.
- Use Tailwind tokens from `tailwind.config.js` rather than custom values.
- **Create Storybook stories** (`.stories.ts`) alongside each component for interactive development and documentation.
- Keep documentation, preview examples, and data-testids in sync with implementation.

### Storybook Integration

Every UI component must have a corresponding `.stories.ts` file that:

- Documents all component variants and states
- Provides interactive controls for props
- Includes comprehensive examples (default, error, disabled, etc.)
- Serves as living documentation with auto-generated props tables

Run `yarn storybook` during development to iterate on components with instant feedback.

## Testing Standards

- Unit tests live in `tests/unit/` and rely on Vitest + @vue/test-utils; name files `ComponentName.test.ts`.
- Visual snapshots live in `tests/visual/`; cover every interactive state and rebaseline only after reviewing diffs.
- Maintain ≥80% coverage by running `yarn test:coverage`; HTML reports are emitted to `coverage/index.html`.
- End each iteration with `yarn test:all` so regressions surface early.

For deeper guidance, reference `.agent/sop/testing-guide.md`.

## Contributing

Before opening a pull request:

- Review the contributor playbook in `AGENTS.md` (Repository Guidelines).
- Use Conventional Commit prefixes (`fix:`, `docs:`, `chore:`) with imperative subjects ≤72 characters.
- Fill PR descriptions with motivation, linked issues, screenshots for UI changes, and test evidence.

## Additional Resources

- [Component creation process](.agent/sop/component-creation-process.md)
- [Testing guide](.agent/sop/testing-guide.md)
- [NPM Publishing guide](.agent/sop/npm-publishing.md)
- [Nuxt documentation](https://nuxt.com/docs)
- [Playwright documentation](https://playwright.dev/)
