# @onlynative/core — Theme System for React Native

> Version: 0.0.0-alpha.8
> Peer deps: react >=18, react-native >=0.72
> Optional: @material/material-color-utilities >=0.4.0 (for createMaterialTheme)

## Quick Start

```tsx
import { ThemeProvider } from '@onlynative/core'

export default function App() {
  return (
    <ThemeProvider>
      {/* Your app */}
    </ThemeProvider>
  )
}
```

## API

### ThemeProvider

Wrap your app root to supply the theme to all components. Works with any design system — Material Design 3 or custom themes. Defaults to the MD3 light theme when no theme is provided.

```tsx
import { ThemeProvider, darkTheme } from '@onlynative/core'

// Light theme (default)
<ThemeProvider>{children}</ThemeProvider>

// Dark theme
<ThemeProvider theme={darkTheme}>{children}</ThemeProvider>

// Custom theme
import { lightTheme } from '@onlynative/core'
import type { Theme } from '@onlynative/core'

const custom: Theme = {
  ...lightTheme,
  colors: { ...lightTheme.colors, primary: '#006A6A', onPrimary: '#FFFFFF' },
}
<ThemeProvider theme={custom}>{children}</ThemeProvider>
```

Props:
- `theme?: BaseTheme` — Theme object. Default: `lightTheme` (MD3)
- `children: ReactNode`

### useTheme()

Returns the current theme from the nearest `ThemeProvider`.

Without a type parameter, returns the Material Design 3 `Theme`. Pass a custom theme type for typed access to your design system's tokens.

```tsx
import { useTheme } from '@onlynative/core'

// Material Design 3 (default)
const theme = useTheme()
// theme.colors.primary, theme.typography.bodyMedium, theme.spacing.md, etc.

// Custom design system
const theme = useTheme<MyTheme>()
// theme.colors.brand, theme.typography.heading, etc.
```

### defineTheme(theme)

Identity function that validates a custom theme object against `BaseTheme`. Provides type-checking and autocompletion.

```tsx
import { defineTheme } from '@onlynative/core'
import type { BaseTheme, TextStyle } from '@onlynative/core'

interface MyColors { [key: string]: string; brand: string; background: string; text: string }
interface MyTypography { [key: string]: TextStyle; heading: TextStyle; body: TextStyle }
interface MyTheme extends BaseTheme { colors: MyColors; typography: MyTypography }

const myTheme = defineTheme<MyTheme>({
  colors: { brand: '#FF6B00', background: '#FFFFFF', text: '#1A1A1A' },
  typography: {
    heading: { fontFamily: 'Inter', fontSize: 24, fontWeight: '700', lineHeight: 32, letterSpacing: 0 },
    body: { fontFamily: 'Inter', fontSize: 16, fontWeight: '400', lineHeight: 24, letterSpacing: 0.5 },
  },
  shape: { roundness: 1, cornerNone: 0, cornerExtraSmall: 4, cornerSmall: 8, cornerMedium: 12, cornerLarge: 16, cornerExtraLarge: 28, cornerFull: 9999 },
  spacing: { xs: 4, sm: 8, md: 16, lg: 24, xl: 32 },
  stateLayer: { pressedOpacity: 0.12, focusedOpacity: 0.12, hoveredOpacity: 0.08, disabledOpacity: 0.38 },
  elevation: { ... },
  motion: { ... },
})
```

### createMaterialTheme(seedColor, options?)

Generates a complete MD3 light and dark theme from a single seed color. Uses Google's HCT color space via `@material/material-color-utilities` for spec-compliant palette generation.

Defaults are byte-identical to the upstream MD3 `material-color-utilities` library — pure spec output.

**Spec-aligned options:**
- `variant?: 'tonalSpot' | 'neutral' | 'vibrant' | 'expressive' | 'fidelity' | 'content' | 'monochrome' | 'rainbow' | 'fruitSalad'` — MD3 scheme variant (default `'tonalSpot'`, the Material You default on Android 12+). Each is a spec-defined recipe for deriving the palette. Use `'monochrome'` for spec-legal pure-grey themes, `'vibrant'` for high-colorfulness, etc.
- `contrastLevel?: 'standard' | 'medium' | 'high'` — MD3 contrast preset (default `'standard'`). Maps to MD3 contrast values `0 / 0.5 / 1.0`. Use `'medium'` or `'high'` for WCAG AAA / low-vision modes.
- `fontFamily?: string` — Custom font family applied to all 15 typography styles. When omitted, platform defaults are used (Roboto on Android, System on iOS).
- `roundness?: number` — Global corner-radius multiplier. `0` = sharp corners, `1` = default MD3 (default), `2` = double rounding. Does not affect `cornerNone` or `cornerFull`.

**Explicit overrides (NOT part of MD3 spec — use only when no built-in `variant` covers your case):**
- `surfaceTone?: 'spec' | 'neutral'` — Default `'spec'` keeps the variant's neutral palette as-is. `'neutral'` rebuilds the neutral and neutralVariant palettes with chroma `0` while leaving primary/secondary/tertiary untouched. Use this for a colorful brand + OLED-near-black surfaces. For a fully spec-legal monochrome theme prefer `variant: 'monochrome'`.
- `seedAdjustments?: { primary?: number, secondary?: number }` — Per-palette HCT chroma overrides. Same hue, fresh chroma. The variant defaults are spec-defined (TonalSpot uses `primary: 36` / `secondary: 16`); only override when no `variant` matches your brand. Try `variant: 'vibrant'` first.

**Separate entry point** — keeps the ~60 kB dependency out of the main bundle:

```tsx
import { createMaterialTheme } from '@onlynative/core/create-theme'
import { ThemeProvider } from '@onlynative/core'

// Pure MD3 default (TonalSpot variant)
const { lightTheme, darkTheme } = createMaterialTheme('#006A6A')

// Switch MD3 variant
createMaterialTheme('#006A6A', { variant: 'vibrant' })

// Spec-legal monochrome theme
createMaterialTheme('#006A6A', { variant: 'monochrome' })

// High-contrast accessibility preset
createMaterialTheme('#006A6A', { contrastLevel: 'high' })

// Custom font + sharp corners
createMaterialTheme('#006A6A', { fontFamily: 'Inter', roundness: 0 })

// Override: keep colorful primary/secondary, flatten surfaces to pure grey
createMaterialTheme('#006A6A', { surfaceTone: 'neutral' })

// Override: per-palette chroma
createMaterialTheme('#006A6A', { seedAdjustments: { primary: 60, secondary: 32 } })

// Use in provider
<ThemeProvider theme={lightTheme}>{children}</ThemeProvider>

// Or with dark mode
<ThemeProvider theme={isDark ? darkTheme : lightTheme}>{children}</ThemeProvider>
```

Returns: `{ lightTheme: Theme, darkTheme: Theme }`

The generated themes include all 69 MD3 color roles plus shared tokens (typography, shape, spacing, stateLayer, elevation, motion, topAppBar).

Requires `@material/material-color-utilities` as a peer dependency: `npm install @material/material-color-utilities`

### applyRoundness(roundness)

Scales the MD3 corner radius tokens by a multiplier. Returns a complete `Shape` object. `cornerNone` (0) and `cornerFull` (999) are never affected.

- `roundness: 0` — sharp corners (all intermediate tokens become 0)
- `roundness: 1` — default MD3 values
- `roundness: 2` — double the rounding

```tsx
import { applyRoundness, lightTheme } from '@onlynative/core'

// Use with spread to override shape on an existing theme
const sharpTheme = { ...lightTheme, shape: applyRoundness(0) }

// Or with defineTheme
import { defineTheme } from '@onlynative/core'
const theme = defineTheme({ ...lightTheme, shape: applyRoundness(0.5) })
```

### material preset

Grouped object containing all Material Design 3 theme values.

```tsx
import { material } from '@onlynative/core'

material.lightTheme  // MD3 light theme
material.darkTheme   // MD3 dark theme
material.defaultTopAppBarTokens
```

### Theme type hierarchy

- `BaseTheme` — Generic base interface. All design systems extend this. Has `colors: Record<string, string>`, `typography: Record<string, TextStyle>`, plus shape, spacing, stateLayer, elevation, motion.
- `Theme` — Material Design 3 theme. Extends `BaseTheme` with 69 MD3 color roles, 15 typography variants, and optional `topAppBar` tokens. `MaterialTheme` is an identical alias (same type) — use it to disambiguate in multi-design-system codebases.

### Theme structure

```
BaseTheme {
  colors: Record<string, string>
  typography: Record<string, TextStyle>
  shape: Shape           — roundness, cornerNone, cornerExtraSmall, cornerSmall, cornerMedium, cornerLarge, cornerExtraLarge, cornerFull
  spacing: Spacing       — xs, sm, md, lg, xl
  elevation: Elevation   — level0..level3 (shadow properties)
  stateLayer: StateLayer — pressedOpacity, focusedOpacity, hoveredOpacity, disabledOpacity
  motion: Motion         — duration and easing tokens
}

Theme extends BaseTheme {
  colors: Colors         — 69 MD3 color roles
  typography: Typography — 15 type scale variants (displayLarge..labelSmall)
  topAppBar?: TopAppBarTokens
}
```

Colors: primary, onPrimary, primaryContainer, onPrimaryContainer, primaryFixed, onPrimaryFixed, primaryFixedDim, onPrimaryFixedVariant, secondary (same pattern), tertiary (same pattern), error, onError, errorContainer, onErrorContainer, background, onBackground, surface, surfaceDim, surfaceBright, surfaceContainerLowest, surfaceContainerLow, surfaceContainer, surfaceContainerHigh, surfaceContainerHighest, onSurface, surfaceVariant, onSurfaceVariant, outline, outlineVariant, surfaceTint, shadow, scrim, inverseSurface, inverseOnSurface, inversePrimary

Typography variants: displayLarge, displayMedium, displaySmall, headlineLarge, headlineMedium, headlineSmall, titleLarge, titleMedium, titleSmall, bodyLarge, bodyMedium, bodySmall, labelLarge, labelMedium, labelSmall — each with fontFamily, fontSize, fontWeight, lineHeight, letterSpacing

### useBreakpoint()

Returns the current MD3 window size class. Reactively updates on resize.

```tsx
import { useBreakpoint } from '@onlynative/core'

const bp = useBreakpoint()
// 'compact' (0-599) | 'medium' (600-839) | 'expanded' (840-1199) | 'large' (1200-1599) | 'extraLarge' (1600+)
```

### useBreakpointValue(values)

Returns a value based on the current breakpoint with cascade fallback.

```tsx
import { useBreakpointValue } from '@onlynative/core'

const columns = useBreakpointValue({ compact: 1, medium: 2, expanded: 4 })
// compact → 1, medium → 2, expanded/large/extraLarge → 4
```

Type: `useBreakpointValue<T>(values: Partial<Record<Breakpoint, T>> & Record<'compact', T>): T`

