@majkapp/plugin-theme
v1.0.1
Published
Shared theme system for Majk plugins
Maintainers
Readme
@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
Installation
npm install @majk/plugin-themeQuick Start
1. Wrap your plugin app with PluginThemeProvider
// 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
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:
// 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:
<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:
.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 componentsfallbackTheme?: Theme- Optional fallback theme if parent doesn't provide one
usePluginTheme()
Hook to access the current theme.
Returns:
{
theme: Theme | null; // Current theme object
isDark: boolean; // True if dark mode is active
isReady: boolean; // True when theme has been loaded
}Theme Type
interface Theme {
id: string;
name: string;
description: string;
isDark: boolean;
colors: {
textNormal: string;
textAccent: string;
backgroundPrimary: string;
// ... 50+ color properties
};
}How It Works
- Main App: Sends theme data to plugin iframes via
postMessagewhen theme changes - Plugin:
PluginThemeProviderlistens for theme messages - Auto-Apply: CSS variables are set on
:rootand dark class is toggled - React Context: Theme is available via
usePluginTheme()hook
Examples
Conditional Styling Based on Theme
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
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
