npm package: @snowcone-app/ui
Description: React components for merchandise visualization and customization
Author: Snowcone
Latest version: 0.1.15

# @snowcone-app/ui

**React components for merchandise visualization and customization**

Drop-in React components for displaying products, generating mockups, and building print-on-demand experiences. Works with the `@snowcone-app/sdk` SDK.

---

## 🤖 FOR AI ASSISTANTS - READ THIS FIRST!

**If you are an AI assistant (Claude, ChatGPT, Copilot, etc.) helping implement @snowcone-app/ui:**

### ALWAYS fetch and read the complete implementation guide:
```
/api/ai-guide
```
(Fetch from the same domain as the docs site you're viewing)

**This guide contains:**
- ✅ Complete installation (including critical Tailwind CSS setup)
- ✅ Common patterns and working code examples
- ✅ Component API reference
- ✅ Common mistakes and how to avoid them

**DO NOT implement @snowcone-app/ui without reading AI_GUIDE.md first!** Many issues (especially Tailwind CSS configuration) are documented there.

---

## 🤖 Quick Reference for LLMs

| Task | Pattern | Components (in order!) |
|------|---------|------------------------|
| **Display product with artwork** | Context-based (REQUIRED) | `<ArtSelector />` → `<ProductImage />` (ArtSelector MUST come first!) |
| **Artwork customization** | Context-based | `<ArtSelector />` → `<ProductImage />` + `<ArtworkCustomizer />` |
| **Variant selection** | Wrap in Product | `<Product><ProductOptions /></Product>` |
| **Add to cart** | Wrap in Product | `<Product><AddToCart /></Product>` |

**🚨 Critical Rules:**
1. **ProductImage REQUIRES ArtSelector**: `<ProductImage />` by itself shows placeholder - always use `<ArtSelector />` first!
2. **Context-only architecture**: `<ProductImage />` does NOT accept artwork as a prop - use `<ArtSelector />` to set artwork in context
3. `<ArtworkCustomizer />` reads from context (set by `<ArtSelector>`), NOT from props
4. `<ArtAlignment>` and `<TileCount>` are NOT exported - use `<ArtworkCustomizer>` instead
5. Most components require `<Product>` wrapper - check JSDoc for context requirements
6. Seamless patterns REQUIRE `tileCount` (TypeScript enforces this)

**❌ Most common mistake:**
```tsx
// WRONG - ProductImage without ArtSelector shows placeholder!
<Product productId="BEEB77">
  <ProductImage />  {/* ❌ No artwork = shows "Select artwork to preview" */}
</Product>

// CORRECT - ArtSelector provides artwork
<Product productId="BEEB77">
  <ArtSelector artworks={['https://example.com/art.png']} />  {/* ✅ Sets artwork */}
  <ProductImage />  {/* ✅ Reads artwork from context */}
</Product>
```

## Installation

### ⚡ One-Command Setup (Recommended)

**Run this single command to install and configure everything:**

```bash
npx @snowcone-app/cli init
```

**That's it!** The CLI will:
- ✅ Install `@snowcone-app/ui` and `zod` packages automatically
- ✅ Auto-detect your project structure
- ✅ Configure Tailwind CSS v4 `@source` paths correctly
- ✅ Verify your setup works

**Then restart your dev server:**

```bash
npm run dev  # or pnpm dev, yarn dev, bun dev
```

---

### Manual Installation (Advanced)

If you prefer manual setup:

```bash
npm install @snowcone-app/ui zod
# or
yarn add @snowcone-app/ui zod
# or
pnpm add @snowcone-app/ui zod
```

**Peer Dependencies:**
- React 18+ or 19+
- React DOM 18+ or 19+
- Zod 3+ or 4+

**Important:** You'll need to manually configure Tailwind CSS `@source` paths (see "Styling" section below).

## Quick Start

**⚠️ IMPORTANT:** ProductImage REQUIRES artwork via `ArtSelector` - they work as a pair!

```tsx
import { Shop, Product, ProductImage, AddToCart, ArtSelector } from '@snowcone-app/ui';

function MyStore() {
  return (
    <Shop mode="mock">
      <Product productId="BEEB77">
        {/* Step 1: ArtSelector provides artwork (REQUIRED!) */}
        <ArtSelector artworks={['https://example.com/art.png']} />

        {/* Step 2: ProductImage reads artwork from context */}
        <ProductImage />

        {/* Step 3: Add to cart */}
        <AddToCart onAddToCart={(item) => console.log(item)} />
      </Product>
    </Shop>
  );
}
```

**What this does:**
1. `<ArtSelector>` - Sets artwork in Design context (required for ProductImage to work)
2. `<ProductImage>` - Reads artwork from context and displays product mockup
3. `<AddToCart>` - Handles cart actions

**❌ Common mistake:**
```tsx
// This WON'T work - ProductImage needs artwork from ArtSelector!
<Product productId="BEEB77">
  <ProductImage />  {/* Shows "Select artwork to preview" placeholder */}
</Product>
```

## Core Components

### `<Shop>` - Root Provider

Wraps your app and provides configuration for all child components.

```tsx
<Shop 
  mode="mock"              // 'mock' | 'live' | 'meilisearch'
  endpoint="https://..."   // Optional: API endpoint
  mockupUrl="https://..."  // Optional: Mockup service URL
  accountId="acc_123"      // Optional: Your account ID
>
  {/* Your app */}
</Shop>
```

**Environment Variables (Next.js):**
```bash
NEXT_PUBLIC_MERCH_ENDPOINT=https://api.snowcone.app
NEXT_PUBLIC_MERCH_MOCKUP_URL=https://i.snowcone.app
NEXT_PUBLIC_MERCH_ACCOUNT_ID=your-account-id
```

### `<Product>` - Product Context

Provides product data to child components. Fetches product automatically.

```tsx
<Product productId="BEEB77">
  <ProductImage />
  <ProductOptions />
  <ProductPrice />
</Product>
```

### `<ProductImage>` - Mockup Visualization

**🚨 REQUIRES ARTSELECTOR:** ProductImage will NOT work alone - you MUST use `<ArtSelector>` first!

**What happens without ArtSelector:**
- ❌ Shows "Select artwork to preview" placeholder
- ❌ No product mockup is generated
- ❌ Component is essentially non-functional

**How to use it correctly:**

```tsx
// ✅ CORRECT - ArtSelector first, then ProductImage
<Product productId="BEEB77">
  {/* Step 1: ArtSelector provides artwork (REQUIRED!) */}
  <ArtSelector artworks={[
    { type: 'regular', src: 'https://example.com/art.jpg' },
    { type: 'pattern', src: 'https://example.com/pattern.jpg', tileCount: 2 }
  ]} />

  {/* Step 2: ProductImage reads artwork from context */}
  <ProductImage className="w-full" />
</Product>

// ❌ WRONG - ProductImage alone shows placeholder!
<Product productId="BEEB77">
  <ProductImage />  {/* ❌ No artwork = placeholder */}
</Product>

// ❌ WRONG - Artwork prop doesn't exist!
<ProductImage artwork={artwork} />  {/* ❌ Not supported */}
```

**Why this design?**
ProductImage uses context-based architecture - `ArtSelector` sets artwork in Design context, and `ProductImage` reads from that context. This allows multiple components to share the same artwork state.

### `<ProductOptions>` - Variant Selector

Shows color and size options for the product.

```tsx
<ProductOptions 
  variant="inline"  // 'inline' | 'dropdown'
  showPrice={true}  // Show price changes
/>
```

### `<ArtworkCustomizer>` - Smart Artwork Controls

Automatically renders the correct customization UI based on artwork type:
- Regular artwork → Positioning controls (drag to align)
- Seamless patterns → Tile density slider

⚠️ **Reads artwork from context** (set by `<ArtSelector>`), not from props!

```tsx
// Use with ArtSelector to manage artwork via context
<Product productId="BEEB77">
  <ArtSelector artworks={[
    { type: 'regular', src: 'https://example.com/photo.jpg' },
    { type: 'pattern', src: 'https://example.com/pattern.jpg', tileCount: 1 }
  ]} />
  <ArtworkCustomizer maxHeight={200} />  {/* Automatically adapts to artwork type */}
</Product>
```

### `<AddToCart>` - Purchase Button

```tsx
<AddToCart 
  onAddToCart={(item) => console.log('Added:', item)}
  className="btn-primary"
/>
```

## All 21 Components

**Primitives:**
- `ColorSwatch` - Color selection
- `ProductPrice` - Formatted pricing
- `ThemeToggle` - Dark/light mode
- `DragHintAnimation` - Drag interaction hint
- `FloatingActionGroup` - FAB button group

**Composed:**
- `ProductCard` - Complete product card (multiple variants)
- `ProductImage` - Mockup display
- `ProductOptions` - Variant selectors
- `ProductList` - Product grid/list
- `ProductGallery` - Image gallery
- `ArtworkCustomizer` - Smart artwork controls (positioning/tile density)
- `ArtSelector` - Artwork picker gallery
- `AddToCart` - Cart button
- `Lightbox` - Full-screen image viewer
- `RealtimeMockup` - Live mockup generation
- `CurrentSelectionDisplay` - Show selected options
- `TileCount` - Tile density control (internal - use ArtworkCustomizer instead)

**Patterns (Context Providers):**
- `Shop` - Root shop provider
- `Product` - Product data provider

**Hooks:**
- `useProduct` - Access product context
- `useShop` - Access shop context
- `useProductGallery` - Gallery state management
- `usePlacementsProcessor` - Placement data processing

## Complete Example

```tsx
import {
  Shop,
  Product,
  ProductImage,
  ProductOptions,
  ProductPrice,
  AddToCart,
  ArtSelector,
  ArtworkCustomizer
} from '@snowcone-app/ui';

function MerchStore() {
  return (
    <Shop mode="mock">
      <div className="container mx-auto p-4">
        <h1>My Merch Store</h1>

        <Product productId="BEEB77">
          <div className="grid md:grid-cols-2 gap-8">
            {/* Product Image */}
            <div>
              <ProductImage className="w-full rounded-lg" />
            </div>

            {/* Product Details */}
            <div className="space-y-4">
              <h2 className="text-2xl font-bold">Custom T-Shirt</h2>
              <ProductPrice className="text-xl" />

              {/* Artwork Selection */}
              <ArtSelector artworks={[
                { type: 'regular', src: 'https://example.com/photo.jpg' },
                { type: 'regular', src: 'https://example.com/logo.png' },
                { type: 'pattern', src: 'https://example.com/pattern.jpg', tileCount: 1 }
              ]} />

              {/* Artwork Customization - automatically shows right controls! */}
              <ArtworkCustomizer maxHeight={200} />

              <ProductOptions variant="inline" />

              <AddToCart
                onAddToCart={(item) => {
                  console.log('Added to cart:', item);
                }}
              />
            </div>
          </div>
        </Product>
      </div>
    </Shop>
  );
}
```

## Common Mistakes & How to Fix Them

### ❌ #1 Most Common Mistake: ProductImage shows "Select artwork to preview"

**Problem:** ProductImage REQUIRES ArtSelector - they are a pair, not optional!

This is the **#1 mistake** both LLMs and developers make. ProductImage is not a standalone component.

```tsx
// ❌ WRONG - ProductImage alone doesn't work!
<Product productId="BEEB77">
  <ProductImage />  {/* ❌ Shows placeholder - no artwork! */}
</Product>
```

**Solution:** ALWAYS use ArtSelector before ProductImage:

```tsx
// ✅ CORRECT - ArtSelector + ProductImage together
<Product productId="BEEB77">
  {/* ArtSelector comes FIRST (required!) */}
  <ArtSelector artworks={[
    'https://example.com/art1.jpg',
    'https://example.com/art2.jpg'
  ]} />

  {/* ProductImage comes SECOND (reads from ArtSelector) */}
  <ProductImage />
</Product>
```

**Remember:** Think of ArtSelector and ProductImage as a package deal - you need both!

### ❌ Trying to use ArtAlignment or TileCount directly

**Problem:** `ArtAlignment` and `TileCount` are internal components and NOT exported.

```tsx
// ❌ WRONG - These components are not exported!
import { ArtAlignment, TileCount } from '@snowcone-app/ui'; // ❌ Will fail!
```

**Solution:** Use `ArtworkCustomizer` instead - it handles both types automatically:

```tsx
// ✅ CORRECT - Use ArtworkCustomizer (works with context)
import { Product, ArtSelector, ArtworkCustomizer, ProductImage } from '@snowcone-app/ui';

<Product productId="BEEB77">
  <ArtSelector artworks={[
    { type: 'regular', src: 'https://example.com/photo.jpg' },
    { type: 'pattern', src: 'https://example.com/pattern.jpg', tileCount: 1 }
  ]} />
  <ProductImage />
  <ArtworkCustomizer maxHeight={200} />  {/* Automatically shows right controls! */}
</Product>
```

**How it works:**
- `ArtworkCustomizer` reads artwork from Design context (set by `ArtSelector`)
- Automatically renders positioning controls for regular artwork
- Automatically renders tile density controls for seamless patterns
- No manual conditionals needed - it switches UI automatically!

## 🎯 Common Patterns for LLMs & Developers

### Pattern 1: Context-Based Artwork Management

**The Right Way:** Use ArtSelector + ProductImage + ArtworkCustomizer

```tsx
import { Shop, Product, ArtSelector, ProductImage, ArtworkCustomizer } from '@snowcone-app/ui';

// ✅ CORRECT - Context-based pattern
<Shop>
  <Product productId="BEEB77">
    {/* Step 1: ArtSelector sets artwork in context */}
    <ArtSelector artworks={[
      { type: 'regular', src: 'https://example.com/photo.jpg' },
      { type: 'pattern', src: 'https://example.com/pattern.jpg', tileCount: 1 }
    ]} />

    {/* Step 2: ProductImage reads artwork from context */}
    <ProductImage />

    {/* Step 3: ArtworkCustomizer reads artwork from context and shows right controls */}
    <ArtworkCustomizer maxHeight={200} />
  </Product>
</Shop>
```

**Why this works:**
1. `ArtSelector` → Sets `selectedArtwork` in Design context
2. `ProductImage` → Reads from context, shows mockup
3. `ArtworkCustomizer` → Reads from context, shows positioning (regular) or tile density (pattern) controls

### Pattern 2: Multi-Placement Products (Advanced)

**For products with multiple placements needing different colors/images:**

```tsx
import { Product, ProductImage } from '@snowcone-app/ui';

// ✅ CORRECT - Use placements prop for multi-placement products
<Product productId="shirt-123">
  <ProductImage placements={{
    Front: '#FF0000',           // Red color on front
    Back: 'https://logo.jpg',   // Image on back
    Sleeve: 'rgb(0, 255, 0)'    // Green on sleeve
  }} />
</Product>
```

**When to use:** Products with multiple placements needing different colors or images per placement.

### Pattern 3: Product Context Components

**Components that require `<Product>` wrapper:**
- `<ProductOptions />` - Reads product options
- `<ProductPrice />` - Can work standalone, but best in context
- `<AddToCart />` - Requires product context
- `<ArtworkCustomizer />` - Requires Design context (from Product)

```tsx
// ✅ CORRECT - All context components wrapped
<Product productId="BEEB77">
  <ProductImage />
  <ProductOptions />
  <ProductPrice />
  <AddToCart onAddToCart={(item) => console.log(item)} />
</Product>

// ❌ WRONG - Missing Product wrapper
<ProductOptions />  // Will throw error!
```

### Pattern 4: Type-Safe Artwork with TypeScript

**Use discriminated unions for type safety:**

```tsx
import type { RegularArtwork, SeamlessPattern, Artwork } from '@snowcone-app/ui';

// Type 1: Regular artwork
const photo: RegularArtwork = {
  type: 'regular',
  src: 'https://example.com/photo.jpg'
};

// Type 2: Seamless pattern (tileCount REQUIRED!)
const pattern: SeamlessPattern = {
  type: 'pattern',
  src: 'https://example.com/pattern.jpg',
  tileCount: 1  // ✅ TypeScript enforces this!
};

// Union type
const artwork: Artwork = Math.random() > 0.5 ? photo : pattern;
```

**TypeScript will catch mistakes:**
```tsx
// ❌ TypeScript error - missing tileCount
const pattern: SeamlessPattern = {
  type: 'pattern',
  src: 'https://...'
  // Error: Property 'tileCount' is missing
};
```

### Pattern 5: Shop-Wide State Sharing

**Share artwork across multiple products:**

```tsx
<Shop>
  {/* Artwork selector shared across all products */}
  <ArtSelector artworks={artworkCollection} />

  <div className="grid grid-cols-2 gap-4">
    {/* Both products use the same selected artwork */}
    <Product productId="shirt-123">
      <ProductImage />
    </Product>
    <Product productId="mug-456">
      <ProductImage />
    </Product>
  </div>
</Shop>
```

### Pattern 6: Error Handling & Edge Cases

**Handle missing artwork gracefully:**

```tsx
<Product productId="BEEB77">
  {/* ProductImage without artwork shows "Select artwork to preview" after 2s */}
  <ProductImage />
</Product>
```

**Better: Provide artwork upfront via ArtSelector**

```tsx
<Product productId="BEEB77">
  {/* ✅ Always provide artwork for immediate display */}
  <ArtSelector artworks={[{ type: 'regular', src: 'https://...' }]} />
  <ProductImage />
</Product>
```

## TypeScript Support

Full TypeScript definitions included with **type-safe discriminated unions**:

```typescript
import type {
  Artwork,           // Union type: RegularArtwork | SeamlessPattern
  RegularArtwork,    // { type: 'regular', src, aspectRatio? }
  SeamlessPattern,   // { type: 'pattern', src, aspectRatio?, tileCount }
  ImageAlignment,
  ProductCardVariant
} from '@snowcone-app/ui';

// TypeScript enforces tileCount for patterns!
const pattern: SeamlessPattern = {
  type: 'pattern',
  src: 'https://...',
  tileCount: 2  // ✅ Required - won't compile without it
};

// Regular artwork doesn't need tileCount
const artwork: RegularArtwork = {
  type: 'regular',
  src: 'https://...',
  aspectRatio: 1
};
```

## Styling

Components use Tailwind CSS with semantic theme tokens:

```tsx
// Components respect your theme
<ProductImage className="bg-background text-foreground" />

// Theme tokens:
// - bg-background, text-foreground
// - bg-card, text-card-foreground  
// - bg-primary, text-primary-foreground
// - bg-muted, text-muted-foreground
// - border-border
```

**Tailwind v4 Users:** Make sure to configure `@source` directives:

```css
/* app/globals.css */
@import "tailwindcss";
@source "../app/**/*.{js,ts,jsx,tsx}";
@source "../node_modules/@snowcone-app/ui/src/**/*.{js,ts,jsx,tsx}";
```

## Development Mode

Use mock mode to develop without API calls:

```tsx
<Shop mode="mock">
  {/* Returns mock data - no API needed */}
</Shop>
```

## Shadcn-style Usage

Components ship with source code in the `src/` directory. You can:

1. **Use as npm package** (recommended)
2. **Copy source** - Copy components from `node_modules/@snowcone-app/ui/src/` to your project and customize freely

## Examples

- [Next.js Full Example](https://github.com/snowcone-app/ui-components/tree/main/examples/next-ecommerce)
- [Product Card Demo](https://github.com/snowcone-app/ui-components/tree/main/examples/next-ecommerce/app/demos/product-card)
- [Art Alignment Demo](https://github.com/snowcone-app/ui-components/tree/main/examples/next-ecommerce/app/demos/art-alignment)

## API Reference

See full component docs at: [https://developers.snowcone.app/components](https://developers.snowcone.app/components)

## Support

- **Issues:** [GitHub Issues](https://github.com/snowcone-app/ui-components/issues)
- **Discussions:** [GitHub Discussions](https://github.com/snowcone-app/ui-components/discussions)
- **Docs:** [developers.snowcone.app](https://developers.snowcone.app)

## License

MIT © [Snowcone](https://snowcone.app)

---

**⚠️ Alpha Release**

This is an alpha release (v0.1.0). The API may change as we gather feedback and iterate. We're shipping early to learn from real usage.

**Breaking changes are expected pre-1.0.** Use semver (`npm install @snowcone-app/ui@latest`) to get updates.

---

**Built for developers. Optimized for LLMs.** 🤖

> This component library is designed to work seamlessly with AI coding assistants. Each component has:
> - Clear, predictable props
> - Explicit TypeScript types
> - Complete usage examples
> - No magic or hidden behavior
>
> LLMs like Claude, GitHub Copilot, and Cursor can reliably generate correct code using these components.