@titancasket/component-library
Version:
Titan Casket Vue 3 component library with design system - usable as Nuxt layer or standalone NPM package
507 lines (411 loc) ⢠17.4 kB
Markdown
# 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/)