npm package: @majkapp/plugin-theme
Description: Shared theme system for Majk plugins
Author: Jules White
Latest version: 1.0.1

# @majk/plugin-theme

Shared theme system for Majk plugins, providing automatic theme synchronization between the main Majk app and plugin iframes.

## Features

- 🎨 **Automatic Theme Sync** - Plugins automatically receive theme updates from the main app
- 🌓 **Dark/Light Mode** - Seamless dark mode support with CSS class management
- 🎯 **Type-Safe** - Full TypeScript support with theme type definitions
- 🎨 **Tailwind Integration** - Pre-configured Tailwind preset for consistent styling
- ⚡ **Zero Config** - Just wrap your app in `PluginThemeProvider`
- ⚡ **Flash Prevention** - Optional template to eliminate flash of unstyled content (FOUC) - see [FLASH_PREVENTION.md](./FLASH_PREVENTION.md)

## Installation

```bash
npm install @majk/plugin-theme
```

## Quick Start

### 1. Wrap your plugin app with PluginThemeProvider

```jsx
// src/main.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { PluginThemeProvider } from '@majk/plugin-theme/react';
import { NextUIProvider } from '@nextui-org/react';
import App from './App';
import '@majk/plugin-theme/styles'; // Import CSS variables

ReactDOM.createRoot(document.getElementById('root')).render(
  <PluginThemeProvider>
    <NextUIProvider>
      <App />
    </NextUIProvider>
  </PluginThemeProvider>
);
```

### 2. Use the theme in your components

```jsx
import { usePluginTheme } from '@majk/plugin-theme';
import { Card, CardBody } from '@nextui-org/react';

function Dashboard() {
  const { theme, isDark, isReady } = usePluginTheme();

  if (!isReady) {
    return <div>Loading theme...</div>;
  }

  return (
    <div className="p-8" style={{
      background: theme.colors.backgroundPrimary,
      color: theme.colors.textNormal
    }}>
      <h1 style={{ color: theme.colors.textAccent }}>
        Current theme: {theme.name}
      </h1>
      <Card>
        <CardBody>
          <p>Dark mode: {isDark ? 'Yes' : 'No'}</p>
        </CardBody>
      </Card>
    </div>
  );
}
```

### 3. Configure Tailwind (Optional)

Use the provided Tailwind preset for consistent utilities:

```js
// tailwind.config.js
const majkTheme = require('@majk/plugin-theme/tailwind-preset');

module.exports = {
  presets: [majkTheme],
  content: ['./src/**/*.{js,jsx,ts,tsx}'],
};
```

Then use Majk theme utilities in your components:

```jsx
<div className="bg-app-bg-primary text-app-text-normal">
  <button className="bg-app-btn-primary hover:bg-app-btn-primary-hover">
    Click me
  </button>
</div>
```

## CSS Variables

All theme colors are available as CSS variables:

```css
.my-component {
  color: var(--text-normal);
  background: var(--background-primary);
  border-color: var(--background-modifier-border);
}

.my-button {
  background: var(--button-primary-bg);
}

.my-button:hover {
  background: var(--button-primary-hover);
}
```

### Available CSS Variables

**Text Colors:**
- `--text-normal`, `--text-muted`, `--text-faint`
- `--text-accent`, `--text-accent-hover`
- `--text-error`, `--text-warning`, `--text-success`

**Background Colors:**
- `--background-primary`, `--background-primary-alt`, `--background-secondary`
- `--background-modifier-border`, `--background-modifier-border-hover`, `--background-modifier-border-focus`

**Button Colors:**
- `--button-primary-bg`, `--button-primary-hover`
- `--button-secondary-bg`, `--button-secondary-hover`

**And many more...** See `styles/theme-variables.css` for the complete list.

## API Reference

### `PluginThemeProvider`

Provides theme context to all child components and handles theme synchronization.

**Props:**
- `children: ReactNode` - Your plugin components
- `fallbackTheme?: Theme` - Optional fallback theme if parent doesn't provide one

### `usePluginTheme()`

Hook to access the current theme.

**Returns:**
```typescript
{
  theme: Theme | null;      // Current theme object
  isDark: boolean;          // True if dark mode is active
  isReady: boolean;         // True when theme has been loaded
}
```

### `Theme` Type

```typescript
interface Theme {
  id: string;
  name: string;
  description: string;
  isDark: boolean;
  colors: {
    textNormal: string;
    textAccent: string;
    backgroundPrimary: string;
    // ... 50+ color properties
  };
}
```

## How It Works

1. **Main App**: Sends theme data to plugin iframes via `postMessage` when theme changes
2. **Plugin**: `PluginThemeProvider` listens for theme messages
3. **Auto-Apply**: CSS variables are set on `:root` and dark class is toggled
4. **React Context**: Theme is available via `usePluginTheme()` hook

## Examples

### Conditional Styling Based on Theme

```jsx
function MyComponent() {
  const { theme } = usePluginTheme();

  return (
    <div style={{
      padding: '20px',
      background: theme.colors.backgroundSecondary,
      border: `1px solid ${theme.colors.backgroundModifierBorder}`,
      borderRadius: '8px'
    }}>
      <h2 style={{ color: theme.colors.textAccent }}>
        Themed Component
      </h2>
    </div>
  );
}
```

### Using with Next UI Components

```jsx
import { Card, Button, Chip } from '@nextui-org/react';

function PluginCard() {
  const { isDark } = usePluginTheme();

  return (
    <Card className={isDark ? 'dark' : ''}>
      <CardBody>
        <Chip color="primary">Plugin Content</Chip>
        <Button color="primary">Action</Button>
      </CardBody>
    </Card>
  );
}
```

## License

MIT