npm package: @lovince/ui
Description: Cross-framework UI components built with Web Components
Author: Steve
Latest version: 5.1.7

# @lovince/ui
Lit-based Web Component library with typed exports, design tokens, and 42 reusable components.
Full usage guide: docs/PACKAGE_USAGE.md · API reference: docs/api/ · Changelog: CHANGELOG.md

## What this package provides
- Framework-agnostic custom elements (tag names prefixed with lovince-)
- TypeScript types for all variants and event detail interfaces
- Auto-registration on import - no manual setup required
- Foundation design tokens via lovince-theme-provider

## Installation
```bash
npm install @lovince/ui lit
```

Note: lit is a required peer dependency. If your project already depends on it, you do not need to install it again.

Working inside this repository:
```bash
npm install
npx tsc --noEmit   # type-check only
npx tsup           # build
```

## Quick start

### Import and use immediately

```ts
import '@lovince/ui/button';
import '@lovince/ui/card';
```

```html
<lovince-button variant="primary">Save</lovince-button>
<lovince-card variant="elevated">Card content</lovince-card>
```

### Component-specific imports

```ts
import { Button, Card } from '@lovince/ui';
// or
import { Button } from '@lovince/ui/button';
import { Card } from '@lovince/ui/card';
```

Components auto-register when imported - no setup required.

## Use in HTML
```html
<lovince-button variant="primary">Save</lovince-button>
<lovince-checkbox variant="outlined">Remember me</lovince-checkbox>
```

## Composition example
```html
<lovince-card variant="elevated">
  <div slot="header">User Profile</div>
  <div slot="body">
    <p>Welcome to your dashboard</p>
    <lovince-accordion>
      <lovince-accordion-item open>
        <span slot="summary">Account Settings</span>
        <p>Manage your account preferences and security settings.</p>
      </lovince-accordion-item>
      <lovince-accordion-item>
        <span slot="summary">Notification Preferences</span>
        <p>Configure how you receive updates and alerts.</p>
      </lovince-accordion-item>
    </lovince-accordion>
  </div>
</lovince-card>
```

## Theming and design tokens
Wrap your app with lovince-theme-provider, or set data-theme on the document root:

```html
<!-- Via theme provider -->
<lovince-theme-provider theme="dark">
  <lovince-card variant="default">Dark themed card</lovince-card>
</lovince-theme-provider>

<!-- Or globally -->
<script>
  document.documentElement.setAttribute('data-theme', 'dark');
</script>
```

### Token families
Global foundation tokens (defined in src/foundations/*) are the source of truth. Component tokens consume them:

| Family | Prefix | Example |
|---|---|---|
| Colors | --ui-color-* | --ui-color-surface-default |
| Spacing | --ui-space-* | --ui-space-4 |
| Radius | --ui-radius-* | --ui-radius-md |
| Shadows | --ui-shadow-* | --ui-shadow-elevated |
| Z-index | --ui-z-* | --ui-z-overlay |
| Motion | --ui-motion-* | --ui-motion-duration-fast |

Each component also exposes its own scoped tokens (e.g. --card-bg, --card-padding) that fall back to the global token values. Override at the component level without touching globals:

```css
lovince-card {
  --card-bg: #1a1a2e;
  --card-radius: 1rem;
}
```

## Tree-shaking
```ts
// ✅ Optimal — only imports what you use
import { Card, Button } from '@lovince/ui';

// ✅ Also optimal — component subpath
import { Card } from '@lovince/ui/card';

// ❌ Avoid — pulls the entire library into your bundle
import * as UI from '@lovince/ui';
```

Verified bundle sizes (gzipped, measured with tsup + Rollup):

| Pattern | Approx. gzip size |
|---|---|
| Single component (direct import) | 6–12 KB |
| Full library (barrel import) | ~85 KB |

Run npm run test:treeshake to verify tree-shaking is working in your build.

## Public API
Top-level exports from src/index.ts:

| Export | Description |
|---|---|
| Button, Card, Checkbox, … | Direct component classes (tree-shakable) |
| export * from './events' | Shared event utilities |

### Subpath exports:
| Path | Description |
|---|---|
| @lovince/ui/button, /card, … | Component-specific entry points |
| @lovince/ui/foundations | Theme provider and design tokens |

## Component catalog (42)
This table is auto-generated from src/components by npm run readme:components. Do not edit manually.

<!-- COMPONENT_CATALOG_START -->

## Component catalog (42)

This section is auto-generated from `src/components` by `npm run readme:components`.

| # | Component | Tag |
|---|---|---|
| 1 | `accordion` | `<lovince-accordion>` |
| 2 | `accordion-group` | `<lovince-accordion-group>` |
| 3 | `activity-feed` | `<lovince-activity-feed>` |
| 4 | `activity-indicator` | `<lovince-activity-indicator>` |
| 5 | `activity-item` | `<lovince-activity-item>` |
| 6 | `alert` | `<lovince-alert>` |
| 7 | `alert-dialog` | `<lovince-alert-dialog>` |
| 8 | `analytics-card` | `<lovince-analytics-card>` |
| 9 | `anchor` | `<lovince-anchor>` |
| 10 | `announcement-bar` | `<lovince-announcement-bar>` |
| 11 | `app-bar` | `<lovince-app-bar>` |
| 12 | `aria-announcer` | `<lovince-aria-announcer>` |
| 13 | `aria-live-region` | `<lovince-aria-live-region>` |
| 14 | `aspect-ratio` | `<lovince-aspect-ratio>` |
| 15 | `audio-player` | `<lovince-audio-player>` |
| 16 | `auto-layout` | `<lovince-auto-layout>` |
| 17 | `autocomplete` | `<lovince-autocomplete>` |
| 18 | `avatar` | `<lovince-avatar>` |
| 19 | `avatar-group` | `<lovince-avatar-group>` |
| 20 | `background-video` | `<lovince-background-video>` |
| 21 | `badge` | `<lovince-badge>` |
| 22 | `badge-group` | `<lovince-badge-group>` |
| 23 | `banner` | `<lovince-banner>` |
| 24 | `bar-chart` | `<lovince-bar-chart>` |
| 25 | `bottom-navigation` | `<lovince-bottom-navigation>` |
| 26 | `box` | `<lovince-box>` |
| 27 | `breadcrumb` | `<lovince-breadcrumb>` |
| 28 | `breadcrumb-item` | `<lovince-breadcrumb-item>` |
| 29 | `breakpoint-provider` | `<lovince-breakpoint-provider>` |
| 30 | `button` | `<lovince-button>` |
| 31 | `button-base` | `<lovince-button-base>` |
| 32 | `calendar` | `<lovince-calendar>` |
| 33 | `calendar-range` | `<lovince-calendar-range>` |
| 34 | `caption` | `<lovince-caption>` |
| 35 | `card` | `<lovince-card>` |
| 36 | `carousel` | `<lovince-carousel>` |
| 37 | `carousel-item` | `<lovince-carousel-item>` |
| 38 | `chart` | `<lovince-chart>` |
| 39 | `checkbox` | `<lovince-checkbox>` |
| 40 | `checkbox-base` | `<lovince-checkbox-base>` |
| 41 | `chip` | `<lovince-chip>` |
| 42 | `chips-input` | `<lovince-chips-input>` |

<!-- COMPONENT_CATALOG_END -->

## Custom events
Events fired by components in this catalog. All events bubble and are composed (cross shadow DOM boundaries).

| Component | Event | Detail type |
|---|---|---|
| most interactive components | ui-change | { value: string } — normalized contract |
| accordion | accordion-change | { activeIndex: number } |
| alert | alert-close | — |
| alert-dialog | dialog-open | — |
| alert-dialog | dialog-close | { trigger: string } |
| alert-dialog | dialog-confirm | { value: string } |
| card | card-click | { sourceEvent: MouseEvent | KeyboardEvent } |
| card | card-select | { selected: boolean } |
| card | card-expand | { expanded: boolean } |
| carousel | carousel-change | { index: number } |
| checkbox | checkbox-change | { checked: boolean } |

### Example — listening to a normalized change event:
```ts
document.querySelector('lovince-checkbox')
  ?.addEventListener('ui-change', (e: Event) => {
    const { value } = (e as CustomEvent<{ value: string }>).detail;
    console.log(value);
  });
```

### Example — listening to card events:
```ts
const card = document.querySelector('lovince-card');

card?.addEventListener('card-click', (e: Event) => {
  const { sourceEvent } = (e as CustomEvent<{ sourceEvent: MouseEvent | KeyboardEvent }>).detail;
  console.log('activated by', sourceEvent.type);
});

card?.addEventListener('card-select', (e: Event) => {
  const { selected } = (e as CustomEvent<{ selected: boolean }>).detail;
  console.log('selected:', selected);
});
```

## Common patterns

### Form card
```html
<lovince-card variant="outlined">
  <div slot="header">User Information</div>
  <div slot="body">
    <lovince-checkbox variant="outlined">Subscribe to newsletter</lovince-checkbox>
    <lovince-button variant="primary" style="margin-top: 16px">Submit</lovince-button>
  </div>
</lovince-card>
```

### Status feedback
```html
<lovince-alert variant="warning" dismissible>Please check your entries.</lovince-alert>
<lovince-badge variant="success">Complete</lovince-badge>
```

### Selectable card grid
```ts
import { defineCard } from '@lovince/ui';
defineCard();

document.querySelectorAll('lovince-card[interaction="clickable"]').forEach(card => {
  card.addEventListener('card-click', () => {
    // toggle selected state
    (card as any).selected = !(card as any).selected;
  });
});
```

```html
<lovince-card interaction="clickable" variant="outlined">Option A</lovince-card>
<lovince-card interaction="clickable" variant="outlined">Option B</lovince-card>
```

## Typed imports
```ts
import type {
  CardVariant,
  CardSize,
  CardInteraction,
  CardDivider,
  CardLoading,
  CardRole,
  CardClickDetail,
  CardSelectDetail,
  CardExpandDetail,
  ButtonVariant,
  CheckboxVariant,
  AlertVariant,
} from '@lovince/ui';
```

## Generating API docs
TypeDoc generates a full API reference from the TSDoc comments in source:

```bash
npm install -D typedoc     # one-time setup
npm run docs               # outputs to docs/api/
```

typedoc.json:
```json
{
  "entryPoints": ["src/index.ts"],
  "out": "docs/api",
  "tsconfig": "tsconfig.json"
}
```

Publish docs/api/ via GitHub Pages or any static host. The generated site covers all exported classes, types, slots, CSS parts, and custom properties.

## Release workflow
This repo uses Changesets for semantic versioning.

```bash
npm run changeset    # record a patch / minor / major change
npm run version      # apply version bumps, update CHANGELOG.md
npm run release      # build, typecheck, publish to npm
```

Other useful commands:
```bash
npm run lint:layers      # enforce cross-layer import boundaries
npm run test:treeshake   # smoke-test single-component tree-shaking
npm run typecheck        # tsc --noEmit
```

## Notes
- Components are not auto-registered. Always call defineX() before using a custom element.
- Use Option 1 or 2 (direct or subpath imports) for optimal bundle size.
- Use Option 3 (scoped registry) for production apps, SSR, and micro-frontends.
- Option 4 (defineAllComponents) is deprecated for production — it registers everything globally and cannot be undone.
- Do not import from src/* directly. Use only the documented entry points above.