npm package: @fastwork/xosmoz-theme
Description: Xosmoz Theme - Design tokens and theming system for Xosmoz
Latest version: 1.0.1

# @fastwork/xosmoz-theme

Design tokens and theming system for the Xosmoz design system. Uses OKLCH color space for perceptually uniform colors. Supports light and dark themes via CSS custom properties with `data-theme` attribute switching.

## Installation

```bash
npm install @fastwork/xosmoz-theme
```

## Quick Start

```css
/* Base styles: CSS reset, font imports, typography/spacing/shadow tokens */
@import '@fastwork/xosmoz-theme/base.css';

/* All themes (light + dark) */
@import '@fastwork/xosmoz-theme/themes.css';

/* OR import a specific theme only */
@import '@fastwork/xosmoz-theme/themes/light.css';
/* @import '@fastwork/xosmoz-theme/themes/dark.css'; */
```

## Theme Switching

Set the theme via the `data-theme` attribute on the `<html>` element. Light theme is the default (no attribute needed).

```html
<!-- Light theme (default) -->
<html>...</html>

<!-- Dark theme -->
<html data-theme="dark">...</html>
```

```javascript
// Switch theme at runtime
document.documentElement.setAttribute('data-theme', 'dark');

// Remove attribute to return to light (default)
document.documentElement.removeAttribute('data-theme');

// Persist across sessions
function setTheme(name) {
  document.documentElement.setAttribute('data-theme', name);
  localStorage.setItem('theme', name);
}

const savedTheme = localStorage.getItem('theme') || 'light';
document.documentElement.setAttribute('data-theme', savedTheme);
```

## CSS Variables Reference

All variables use the `--xz-` prefix.

### Base Colors

Surface and layout colors that adapt between light and dark themes.

| Variable | Purpose |
|----------|---------|
| `--xz-color-surface-50` | Base white surface |
| `--xz-color-surface-100` | Primary page background |
| `--xz-color-surface-200` | Elevated surface (cards, modals) |
| `--xz-color-surface-300` | Table headers, sidebars |
| `--xz-color-surface-400` | Popovers, tooltips |
| `--xz-color-text-100` | Primary text |
| `--xz-color-text-200` | Secondary/muted text (70% opacity) |
| `--xz-color-text-300` | Tertiary/disabled text (60% opacity) |
| `--xz-color-bg-100` | Extreme base background (near-black in light, near-white in dark) |
| `--xz-color-bg-200` | Darker base background |
| `--xz-color-text-fg` | Text on `bg-100` (white in light, black in dark) |
| `--xz-color-line-50` | Subtle borders |
| `--xz-color-line-100` | Default borders |
| `--xz-color-line-200` | Strong/emphasized borders |
| `--xz-color-line-300` | Strongest borders |

### Semantic Colors

Eight semantic categories: `primary`, `danger`, `success`, `warning`, `info`, `neutral`, `orange`, `purple`.

Each category follows the same token structure — replace `{name}` with the category:

| Variable | Purpose |
|----------|---------|
| `--xz-color-{name}-soft-100` | Lightest tinted background (badges, highlights) |
| `--xz-color-{name}-soft-200` | Stronger tinted background (hover states) |
| `--xz-color-{name}-soft-solid-100` | Light color-mixed background (solid, no transparency) |
| `--xz-color-{name}-soft-solid-200` | Stronger color-mixed background (solid, no transparency) |
| `--xz-color-{name}-line-100` | Subtle colored border |
| `--xz-color-{name}-line-200` | Default colored border (input focus) |
| `--xz-color-{name}-line-300` | Strong colored border (active states) |
| `--xz-color-{name}-bg-100` | Solid fill (buttons, tags) |
| `--xz-color-{name}-bg-200` | Darker solid fill (hover state) |
| `--xz-color-{name}-text-fg` | Text on solid backgrounds — pair with `bg-100`/`bg-200` (typically white) |
| `--xz-color-{name}-text-100` | Colored text on surfaces (links, labels) |
| `--xz-color-{name}-text-200` | Darker colored text on surfaces |

**Usage example:**

```css
/* Primary button */
.btn-primary {
  background: var(--xz-color-primary-bg-100);
  color: var(--xz-color-primary-text-fg);  /* white text on solid bg */
}
.btn-primary:hover {
  background: var(--xz-color-primary-bg-200);
}

/* Soft badge */
.badge-success {
  background: var(--xz-color-success-soft-100);
  color: var(--xz-color-success-text-100);
  border: 1px solid var(--xz-color-success-line-100);
}
```

### Alpha Overlays

Three alpha overlay scales with steps from 100 (10% opacity) to 1000 (100% opacity):

```css
--xz-color-black-alpha-{100-1000}  /* black with increasing opacity */
--xz-color-white-alpha-{100-1000}  /* white with increasing opacity */
--xz-color-overlay-{100-1000}      /* overlay (black-based) with increasing opacity */
```

### Raw Color Palettes

Single base color per palette. Available palettes: `fastwork`, `gray`, `green`, `yellow`, `red`, `orange`, `purple`, `cyan`.

```css
--xz-color-{palette}   /* palette base color */
```

Each palette color is defined using OKLCH

### Typography

**Font sizes** (50–1600 numeric scale):

| Variable | Value |
|----------|-------|
| `--xz-font-size-50` | 0.5rem / 8px |
| `--xz-font-size-100` | 0.625rem / 10px |
| `--xz-font-size-200` | 0.75rem / 12px |
| `--xz-font-size-300` | 0.875rem / 14px |
| `--xz-font-size-400` | 1rem / 16px |
| `--xz-font-size-500` | 1.125rem / 18px |
| `--xz-font-size-600` | 1.25rem / 20px |
| `--xz-font-size-700` | 1.5rem / 24px |
| `--xz-font-size-800` | 1.625rem / 26px |
| `--xz-font-size-900` | 2rem / 32px |
| `--xz-font-size-1000` | 2.375rem / 38px |
| `--xz-font-size-1100` | 2.5rem / 40px |
| `--xz-font-size-1200` | 2.6875rem / 43px |
| `--xz-font-size-1300` | 3rem / 48px |
| `--xz-font-size-1400` | 3.5rem / 56px |
| `--xz-font-size-1500` | 4rem / 64px |
| `--xz-font-size-1600` | 4.5rem / 72px |

**Font families:**

```css
--xz-font-family-primary    /* "Google Sans" + system sans-serif stack */
--xz-font-family-secondary  /* "Google Sans" + "Fastwork" + "Noto Sans Thai" + system fallbacks */
```

**Line heights:**

```css
--xz-line-height-100pct  /* 1 */
--xz-line-height-125pct  /* 1.25 */
--xz-line-height-135pct  /* 1.35 */
--xz-line-height-150pct  /* 1.5 */
--xz-line-height-165pct  /* 1.65 */
--xz-line-height-200pct  /* 2 */
```

**Font shorthand tokens** — headings are responsive (scale down on mobile ≤768px), other tokens use fixed sizes:

```css
/* Headings — secondary font, bold, responsive */
--xz-font-heading1  /* 72px desktop / 48px mobile */
--xz-font-heading2  /* 64px desktop / 43px mobile */
--xz-font-heading3  /* 56px desktop / 38px mobile */
--xz-font-heading4  /* 48px desktop / 32px mobile */
--xz-font-heading5  /* 40px desktop / 26px mobile */
--xz-font-heading6  /* 32px desktop / 24px mobile */

/* Titles — primary font, bold */
--xz-font-title1    /* 24px */
--xz-font-title2    /* 20px */
--xz-font-title3    /* 18px */
--xz-font-title4    /* 16px */

/* Subtitles — primary font, bold or regular */
--xz-font-subtitle1-bold
--xz-font-subtitle1-regular  /* 16px */
--xz-font-subtitle2-bold
--xz-font-subtitle2-regular  /* 14px */
--xz-font-subtitle3-bold
--xz-font-subtitle3-regular  /* 12px */

/* Body — primary font, regular */
--xz-font-body1  /* 16px */
--xz-font-body2  /* 14px */
--xz-font-body3  /* 12px */
--xz-font-body4  /* 10px */
```

Usage: `font: var(--xz-font-heading1);`

### Box Shadows

```css
--xz-box-shadow-100  /* 0 1px 2px  — subtle */
--xz-box-shadow-200  /* 0 2px 6px  — small */
--xz-box-shadow-300  /* 0 4px 16px — medium */
--xz-box-shadow-400  /* 0 8px 20px — large */
```

## JavaScript/TypeScript API

### Theme Objects

```typescript
import { lightTheme, darkTheme, themes, getTheme, getThemeNames } from '@fastwork/xosmoz-theme';

// Access raw palette colors
lightTheme.palette.fastwork.value          // 'oklch(0.57 0.237 260.608)'

// Access semantic colors
lightTheme.colors.primary.bg[100]     // solid fill color
lightTheme.colors.primary.fg[100]     // 'oklch(1 0 0 / 1)' — white for text on solid bg
lightTheme.colors.primary.text[100]   // colored text
lightTheme.colors.orange.bg[100]      // orange solid fill
lightTheme.colors.purple.soft[100]    // purple tinted background

// Access base tokens
lightTheme.colors.surface[200]        // card background
lightTheme.colors.text[100]           // primary text

// Access box shadows
lightTheme.boxShadows[200]            // '0 2px 6px 0 rgba(0, 0, 0, 0.10)'

// Get theme by name
const theme = getTheme('dark');

// List available themes
const names = getThemeNames(); // ['light', 'dark']
```

### Design Tokens

```typescript
import { fontSize, fontWeight, fontFamily, lineHeight, font, typography } from '@fastwork/xosmoz-theme/tokens';

fontSize[400]         // '1rem'
fontSize[700]         // '1.5rem'
fontWeight[700]       // 700
fontFamily.primary    // "Google Sans" + system sans-serif stack
fontFamily.secondary  // "Google Sans" + "Fastwork" + "Noto Sans Thai" + fallbacks
lineHeight['150pct']  // '1.5'

// Semantic font tokens
font.heading1         // { fontFamily, fontSize: { desktop, mobile }, fontWeight, lineHeight }
font.title2
font.subtitle1Bold
font.body1

// All typography combined
typography.fontSize
typography.fontFamily
typography.font
```

### Types

```typescript
import type {
  ThemeConfig,
  ThemeName,
  ThemeRegistry,
  ColorToken,
  TypographyToken,
  TypographyScale,
  FontWeights,
  HeadingTokens,
  TitleTokens,
  SubtitleTokens,
  BodyTokens,
  Theme,
} from '@fastwork/xosmoz-theme';
```

## Custom Themes

Override any CSS variable for a custom theme:

```css
[data-theme="brand"] {
  --xz-color-surface-100: oklch(1.00 0 0);
  --xz-color-surface-200: oklch(0.98 0.005 260);
  --xz-color-text-100: oklch(0.20 0.006 285);
  --xz-color-line-50: oklch(0.95 0 1 / 0.95);
  --xz-color-primary-bg-100: oklch(0.58 0.25 30);
  --xz-color-primary-text-fg: oklch(1 0 0 / 0.95);
  --xz-color-primary-text-100: oklch(0.58 0.25 30);
  /* override other variables as needed */
}
```

Then activate it:

```html
<html data-theme="brand">...</html>
```

## Common Patterns

### Card

```css
.card {
  background: var(--xz-color-surface-200);
  color: var(--xz-color-text-100);
  border: 1px solid var(--xz-color-line-50);
  border-radius: 0.75rem;
  box-shadow: var(--xz-box-shadow-200);
}
```

### Primary button

```css
.btn-primary {
  background: var(--xz-color-primary-bg-100);
  color: var(--xz-color-primary-text-fg);
  font: var(--xz-font-subtitle1-bold);
  border: none;
  border-radius: 0.5rem;
}
.btn-primary:hover {
  background: var(--xz-color-primary-bg-200);
}
```

### Outline button

```css
.btn-outline {
  background: transparent;
  color: var(--xz-color-primary-text-100);
  border: 1px solid var(--xz-color-primary-line-200);
}
```

### Alert

```css
.alert-success {
  background: var(--xz-color-success-soft-100);
  color: var(--xz-color-success-text-100);
  border: 1px solid var(--xz-color-success-line-100);
}

.alert-danger {
  background: var(--xz-color-danger-bg-100);
  color: var(--xz-color-danger-text-fg);
}
```

### Form input

```css
.input {
  background: var(--xz-color-surface-100);
  color: var(--xz-color-text-100);
  border: 1px solid var(--xz-color-line-100);
  font: var(--xz-font-body1);
}
.input:focus {
  border-color: var(--xz-color-primary-line-200);
  box-shadow: 0 0 0 3px var(--xz-color-primary-soft-100);
}
```

### Muted text

```css
.text-muted    { color: var(--xz-color-text-200); }
.text-disabled { color: var(--xz-color-text-300); }
```

## CSS Files

| Import path | Contents |
|-------------|----------|
| `@fastwork/xosmoz-theme/base.css` | Font imports, CSS reset, non-color tokens (typography, spacing, borders, shadows) |
| `@fastwork/xosmoz-theme/themes.css` | All themes (light + dark) |
| `@fastwork/xosmoz-theme/themes/light.css` | Light theme colors only |
| `@fastwork/xosmoz-theme/themes/dark.css` | Dark theme colors only |
| `@fastwork/xosmoz-theme/llms.txt` | LLM context file for AI tooling |

## Development

```bash
# Build the package (TypeScript + CSS)
npm run build

# Watch mode
npm run dev

# Lint
npm run lint

# Remove dist/
npm run clean
```

## License

MIT