npm package: @shopkit/core
Description: Core storefront utilities including theme and context
Latest version: 0.1.0

# @shopkit/core

Core storefront utilities including theme management and authentication context.

## Features

- **Theme System**: Complete theming with CSS variables, React context, and SSR support
- **CSS Variable Generation**: Automatic CSS custom property generation from theme config
- **Theme Registry**: Caching and loading of merchant themes
- **Auth Context**: Customer authentication state management
- **Server/Client Separation**: Next.js App Router compatible architecture

## Installation

```bash
npm install @shopkit/core
# or
bun add @shopkit/core
```

### Peer Dependencies

```bash
npm install react react-dom next
```

## Quick Start

### Theme Setup (Next.js App Router)

```tsx
// app/layout.tsx (Server Component)
import { getActiveTheme } from '@shopkit/core/theme/server';
import { ThemeProvider } from '@shopkit/core/theme/client';

export default async function RootLayout({ children }) {
  const theme = await getActiveTheme('my-merchant');

  return (
    <html>
      <body>
        <ThemeProvider merchantName="my-merchant" initialTheme={theme}>
          {children}
        </ThemeProvider>
      </body>
    </html>
  );
}
```

```tsx
// components/MyComponent.tsx (Client Component)
'use client';

import { useTheme } from '@shopkit/core/theme/client';

export function MyComponent() {
  const { theme, getValue } = useTheme();

  const primaryColor = getValue('colors.primary', '#000000');

  return <div style={{ color: primaryColor }}>Themed content</div>;
}
```

## Theme System

### ThemeConfig Structure

```typescript
import type { ThemeConfig } from '@shopkit/core';

const themeConfig: ThemeConfig = {
  colors: {
    primary: '#1a1a1a',
    secondary: '#666666',
    accent: '#0066cc',
    background: '#ffffff',
    text: '#1a1a1a',
  },
  typography: {
    fontFamily: {
      heading: '"Inter", sans-serif',
      body: '"Inter", sans-serif',
    },
    fontSize: {
      xs: '0.75rem',
      sm: '0.875rem',
      base: '1rem',
      lg: '1.125rem',
      xl: '1.25rem',
      '2xl': '1.5rem',
      '3xl': '1.875rem',
      '4xl': '2.25rem',
    },
    fontWeight: {
      normal: '400',
      medium: '500',
      semibold: '600',
      bold: '700',
    },
  },
  spacing: {
    xs: '0.25rem',
    sm: '0.5rem',
    md: '1rem',
    lg: '1.5rem',
    xl: '2rem',
    '2xl': '3rem',
  },
  borderRadius: {
    none: '0',
    sm: '0.25rem',
    md: '0.375rem',
    lg: '0.5rem',
    full: '9999px',
  },
};
```

### CSS Variable Generation

```typescript
import { generateThemeCSS, getCSSVariableMap } from '@shopkit/core/theme';

// Generate CSS string
const css = generateThemeCSS(themeConfig);
// Output: :root { --colors-primary: #1a1a1a; --typography-font-family-heading: "Inter", sans-serif; ... }

// Get variable map
const varMap = getCSSVariableMap(themeConfig);
// { 'colors.primary': '--colors-primary', 'typography.fontFamily.heading': '--typography-font-family-heading', ... }
```

### Theme Registry

```typescript
import { createThemeRegistry } from '@shopkit/core/theme';

const registry = createThemeRegistry({
  basePath: './src/themes',
});

// Load theme for a merchant
const theme = await registry.loadTheme('my-merchant', 'light');
```

### useTheme Hook

```typescript
import { useTheme } from '@shopkit/core/theme/client';

function MyComponent() {
  const {
    theme,           // Current theme object
    getValue,        // Get a theme value by path
    getCSSVar,       // Get CSS variable reference
    merchantName,    // Current merchant name
  } = useTheme();

  // Get value with fallback
  const color = getValue('colors.accent', '#default');

  // Get CSS variable reference
  const cssVar = getCSSVar('colors.primary');
  // Returns: 'var(--colors-primary)'
}
```

## Auth Context

### Setup

```tsx
// app/layout.tsx
import { AuthProvider } from '@shopkit/core/context';

export default function RootLayout({ children }) {
  return (
    <AuthProvider>
      {children}
    </AuthProvider>
  );
}
```

### Usage

```tsx
'use client';

import { useAuth } from '@shopkit/core/context';

function AccountButton() {
  const { customer, isAuthenticated, login, logout } = useAuth();

  if (isAuthenticated) {
    return (
      <div>
        <span>Welcome, {customer.firstName}</span>
        <button onClick={logout}>Logout</button>
      </div>
    );
  }

  return <button onClick={() => login()}>Login</button>;
}
```

## Subpath Exports

```typescript
// Main entry (everything)
import { ThemeProvider, AuthProvider } from '@shopkit/core';

// Theme module
import { ThemeConfig, Theme } from '@shopkit/core/theme';

// Theme client (React components/hooks)
import { ThemeProvider, useTheme } from '@shopkit/core/theme/client';

// Theme server (SSR utilities)
import { getActiveTheme, configureThemeLoader } from '@shopkit/core/theme/server';

// Context module (Auth)
import { AuthProvider, useAuth } from '@shopkit/core/context';
```

## Type Reference

### Theme Types

```typescript
// Theme configuration (what you define)
interface ThemeConfig {
  colors?: Record<string, string>;
  typography?: {
    fontFamily?: Record<string, string>;
    fontSize?: Record<string, string>;
    fontWeight?: Record<string, string>;
    lineHeight?: Record<string, string>;
  };
  spacing?: Record<string, string>;
  borderRadius?: Record<string, string>;
  [key: string]: unknown;
}

// Resolved theme (what you use)
interface Theme {
  id: string;
  name: string;
  role: ThemeRole;
  config: ThemeConfig;
}

// Theme roles
type ThemeRole = 'light' | 'dark' | 'high-contrast';
```

### ThemeContextValue

```typescript
interface ThemeContextValue {
  theme: Theme | null;
  merchantName: string;
  isLoading: boolean;
  getValue: <T>(path: string, defaultValue?: T) => T;
  getCSSVar: (path: string) => string;
}
```

## Utility Functions

### Path to CSS Variable

```typescript
import { pathToCssVar, cssVarRef } from '@shopkit/core/theme';

pathToCssVar('colors.primary');  // '--colors-primary'
cssVarRef('colors.primary');     // 'var(--colors-primary)'
```

### Object Flattening

```typescript
import { flattenObject, unflattenObject } from '@shopkit/core/theme';

const flat = flattenObject({ colors: { primary: '#000' } });
// { 'colors.primary': '#000' }

const nested = unflattenObject({ 'colors.primary': '#000' });
// { colors: { primary: '#000' } }
```

### Value Resolution

```typescript
import { resolveThemeValue, hasThemeValue } from '@shopkit/core/theme';

const theme = { config: { colors: { primary: '#000' } } };

resolveThemeValue(theme, 'colors.primary');  // '#000'
hasThemeValue(theme, 'colors.primary');      // true
hasThemeValue(theme, 'colors.nonexistent'); // false
```

## Integration with @shopkit/builder

```typescript
import { createPageBuilder } from '@shopkit/builder';
import { createThemeRegistry } from '@shopkit/core/theme';

const themeRegistry = createThemeRegistry({
  basePath: './src/themes',
});

const pageBuilder = createPageBuilder({
  widgets: widgetRegistry,
  commerceClient: shopifyClient,
  themeLoader: themeRegistry, // Implements IThemeLoader
  templateLoader: templateLoader,
});
```

## License

MIT