mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 20:01:09 +02:00
feat: implement unified theme system across all web apps
SUMMARY: Create a unified theming architecture with two new shared packages (@manacore/shared-theme and @manacore/shared-theme-ui) that provides consistent theming across all 4 web applications while allowing app-specific primary color customization. NEW PACKAGES: @manacore/shared-theme: - Svelte 5 Runes-based theme store factory - 4 theme variants: Lume (Gold), Nature (Green), Stone (Blue Gray), Ocean (Blue) - 3 theme modes: Light, Dark, System (auto-detect) - HSL-based color system with 18 semantic tokens - localStorage persistence per app - System preference detection via matchMedia - Pre-defined configs for all apps (memoro, manacore, manadeck, maerchenzauber) @manacore/shared-theme-ui: - ThemeToggle: Light/dark mode toggle button - ThemeSelector: Visual theme variant selector with color dots - ThemeModeSelector: Segmented control for light/dark/system UPDATED PACKAGES: @manacore/shared-tailwind: - Converted from HEX to HSL-based CSS variables - Updated preset.js with hsl(var(--color-*)) syntax - themes.css now contains all 4 theme variants with light/dark modes APP MIGRATIONS: memoro/web: - Replaced 145 LOC theme store with 25 LOC shared implementation - Deleted local ThemeSelector.svelte and ThemeToggle.svelte - Primary color: Gold (47 95% 58%) manacore/web: - Replaced 80 LOC theme store with 25 LOC shared implementation - Deleted local ThemeToggle.svelte - Primary color: Indigo (239 84% 67%) manadeck/web: - Added new theme store using shared package - Primary color: Indigo (239 84% 67%) maerchenzauber/web: - Added new theme store using shared package - Primary color: Purple (280 60% 55%) All app layouts updated with theme.initialize() in onMount. BENEFITS: - ~225 LOC of app-specific code reduced to ~100 LOC total - Single source of truth for theme logic - Consistent theming experience across all apps - Easy to add new theme variants - App-specific primary colors preserved 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
ef70a1af0b
commit
96e0aceb93
31 changed files with 2993 additions and 1089 deletions
|
|
@ -1,107 +1,114 @@
|
|||
/**
|
||||
* Shared Tailwind CSS preset for all ManaCore apps
|
||||
*
|
||||
*
|
||||
* This preset uses HSL-based CSS variables for theming.
|
||||
* Colors are defined as HSL values (e.g., "47 95% 58%") and
|
||||
* wrapped with hsl() in the Tailwind config for flexibility.
|
||||
*
|
||||
* Usage in tailwind.config.js:
|
||||
* ```
|
||||
* import sharedPreset from '@manacore/shared-tailwind/preset';
|
||||
*
|
||||
* import preset from '@manacore/shared-tailwind/preset';
|
||||
*
|
||||
* export default {
|
||||
* presets: [sharedPreset],
|
||||
* presets: [preset],
|
||||
* content: ['./src/**\/*.{html,js,svelte,ts}'],
|
||||
* // app-specific overrides...
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
|
||||
import { colors } from './colors.js';
|
||||
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
const preset = {
|
||||
darkMode: 'class',
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
// Brand colors
|
||||
mana: colors.mana,
|
||||
|
||||
// Primary scale
|
||||
primary: colors.primary,
|
||||
|
||||
// Semantic colors using CSS custom properties
|
||||
// These can be changed at runtime via themes.css
|
||||
background: 'var(--color-background)',
|
||||
foreground: 'var(--color-foreground)',
|
||||
|
||||
// Content areas
|
||||
content: {
|
||||
DEFAULT: 'var(--color-content)',
|
||||
hover: 'var(--color-content-hover)',
|
||||
page: 'var(--color-content-page)',
|
||||
// Brand color (consistent across all apps)
|
||||
mana: '#4287f5',
|
||||
|
||||
// ===== HSL-Based Semantic Colors =====
|
||||
// These use CSS variables set by @manacore/shared-theme
|
||||
// Format: hsl(var(--color-name)) where --color-name is "H S% L%"
|
||||
|
||||
// Page background
|
||||
background: 'hsl(var(--color-background))',
|
||||
|
||||
// Main text color
|
||||
foreground: 'hsl(var(--color-foreground))',
|
||||
|
||||
// Primary brand color (customizable per app)
|
||||
primary: {
|
||||
DEFAULT: 'hsl(var(--color-primary))',
|
||||
foreground: 'hsl(var(--color-primary-foreground))',
|
||||
},
|
||||
|
||||
// Menu/sidebar
|
||||
menu: {
|
||||
DEFAULT: 'var(--color-menu)',
|
||||
hover: 'var(--color-menu-hover)',
|
||||
|
||||
// Secondary accent
|
||||
secondary: {
|
||||
DEFAULT: 'hsl(var(--color-secondary))',
|
||||
foreground: 'hsl(var(--color-secondary-foreground))',
|
||||
},
|
||||
|
||||
// Text
|
||||
theme: {
|
||||
DEFAULT: 'var(--color-text)',
|
||||
secondary: 'var(--color-text-secondary)',
|
||||
|
||||
// Card/content surfaces
|
||||
surface: {
|
||||
DEFAULT: 'hsl(var(--color-surface))',
|
||||
hover: 'hsl(var(--color-surface-hover))',
|
||||
elevated: 'hsl(var(--color-surface-elevated))',
|
||||
},
|
||||
|
||||
|
||||
// Muted/disabled elements
|
||||
muted: {
|
||||
DEFAULT: 'hsl(var(--color-muted))',
|
||||
foreground: 'hsl(var(--color-muted-foreground))',
|
||||
},
|
||||
|
||||
// Borders
|
||||
border: {
|
||||
light: 'var(--color-border-light)',
|
||||
DEFAULT: 'var(--color-border)',
|
||||
strong: 'var(--color-border-strong)',
|
||||
DEFAULT: 'hsl(var(--color-border))',
|
||||
strong: 'hsl(var(--color-border-strong))',
|
||||
},
|
||||
|
||||
// Semantic/feedback colors
|
||||
error: 'hsl(var(--color-error))',
|
||||
success: 'hsl(var(--color-success))',
|
||||
warning: 'hsl(var(--color-warning))',
|
||||
|
||||
// Form elements
|
||||
input: 'hsl(var(--color-input))',
|
||||
ring: 'hsl(var(--color-ring))',
|
||||
|
||||
// ===== Legacy aliases (for backwards compatibility) =====
|
||||
content: {
|
||||
DEFAULT: 'hsl(var(--color-surface))',
|
||||
hover: 'hsl(var(--color-surface-hover))',
|
||||
page: 'hsl(var(--color-background))',
|
||||
},
|
||||
menu: {
|
||||
DEFAULT: 'hsl(var(--color-muted))',
|
||||
hover: 'hsl(var(--color-surface-hover))',
|
||||
},
|
||||
theme: {
|
||||
DEFAULT: 'hsl(var(--color-foreground))',
|
||||
secondary: 'hsl(var(--color-muted-foreground))',
|
||||
},
|
||||
|
||||
// Buttons
|
||||
'primary-btn': {
|
||||
DEFAULT: 'var(--color-primary-button)',
|
||||
text: 'var(--color-primary-button-text)',
|
||||
},
|
||||
'secondary-btn': 'var(--color-secondary-button)',
|
||||
|
||||
// Feedback colors
|
||||
error: 'var(--color-error)',
|
||||
success: 'var(--color-success)',
|
||||
warning: 'var(--color-warning)',
|
||||
|
||||
// Direct theme colors (for apps that don't use CSS vars)
|
||||
lume: {
|
||||
...colors.lume.light,
|
||||
dark: colors.lume.dark,
|
||||
},
|
||||
nature: {
|
||||
...colors.nature.light,
|
||||
dark: colors.nature.dark,
|
||||
},
|
||||
stone: {
|
||||
...colors.stone.light,
|
||||
dark: colors.stone.dark,
|
||||
},
|
||||
ocean: {
|
||||
...colors.ocean.light,
|
||||
dark: colors.ocean.dark,
|
||||
DEFAULT: 'hsl(var(--color-primary))',
|
||||
text: 'hsl(var(--color-primary-foreground))',
|
||||
},
|
||||
},
|
||||
|
||||
// Border radius tokens
|
||||
|
||||
// Border radius tokens (CSS variable support)
|
||||
borderRadius: {
|
||||
'none': '0',
|
||||
'sm': '0.25rem',
|
||||
DEFAULT: '0.375rem',
|
||||
'md': '0.5rem',
|
||||
'lg': '0.75rem',
|
||||
'xl': '1rem',
|
||||
'2xl': '1.5rem',
|
||||
'3xl': '2rem',
|
||||
'sm': 'var(--radius-sm, 0.25rem)',
|
||||
DEFAULT: 'var(--radius, 0.375rem)',
|
||||
'md': 'var(--radius-md, 0.5rem)',
|
||||
'lg': 'var(--radius-lg, 0.75rem)',
|
||||
'xl': 'var(--radius-xl, 1rem)',
|
||||
'2xl': 'var(--radius-2xl, 1.5rem)',
|
||||
'3xl': 'var(--radius-3xl, 2rem)',
|
||||
'full': '9999px',
|
||||
},
|
||||
|
||||
|
||||
// Box shadow tokens
|
||||
boxShadow: {
|
||||
'sm': '0 1px 2px 0 rgb(0 0 0 / 0.05)',
|
||||
|
|
@ -113,7 +120,7 @@ const preset = {
|
|||
'inner': 'inset 0 2px 4px 0 rgb(0 0 0 / 0.05)',
|
||||
'none': 'none',
|
||||
},
|
||||
|
||||
|
||||
// Font family
|
||||
fontFamily: {
|
||||
sans: [
|
||||
|
|
@ -136,14 +143,37 @@ const preset = {
|
|||
'monospace',
|
||||
],
|
||||
},
|
||||
|
||||
|
||||
// Animation
|
||||
animation: {
|
||||
'spin-slow': 'spin 3s linear infinite',
|
||||
'pulse-slow': 'pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite',
|
||||
'bounce-slow': 'bounce 2s infinite',
|
||||
'fade-in': 'fadeIn 0.2s ease-out',
|
||||
'fade-out': 'fadeOut 0.2s ease-in',
|
||||
'slide-in': 'slideIn 0.2s ease-out',
|
||||
'slide-out': 'slideOut 0.2s ease-in',
|
||||
},
|
||||
|
||||
|
||||
keyframes: {
|
||||
fadeIn: {
|
||||
'0%': { opacity: '0' },
|
||||
'100%': { opacity: '1' },
|
||||
},
|
||||
fadeOut: {
|
||||
'0%': { opacity: '1' },
|
||||
'100%': { opacity: '0' },
|
||||
},
|
||||
slideIn: {
|
||||
'0%': { transform: 'translateY(-10px)', opacity: '0' },
|
||||
'100%': { transform: 'translateY(0)', opacity: '1' },
|
||||
},
|
||||
slideOut: {
|
||||
'0%': { transform: 'translateY(0)', opacity: '1' },
|
||||
'100%': { transform: 'translateY(-10px)', opacity: '0' },
|
||||
},
|
||||
},
|
||||
|
||||
// Transition
|
||||
transitionDuration: {
|
||||
'250': '250ms',
|
||||
|
|
|
|||
|
|
@ -1,265 +1,434 @@
|
|||
/**
|
||||
* CSS Custom Properties for ManaCore themes
|
||||
*
|
||||
* Usage:
|
||||
* 1. Import in your app.css: @import '@manacore/shared-tailwind/themes.css';
|
||||
* 2. Set theme with data-theme attribute: <html data-theme="lume">
|
||||
* 3. Dark mode is automatic with .dark class or prefers-color-scheme
|
||||
* Shared Theme CSS Variables (HSL-based)
|
||||
*
|
||||
* This file defines HSL-based CSS custom properties for all theme variants.
|
||||
* Variables are set by @manacore/shared-theme's createThemeStore() at runtime,
|
||||
* but this file provides sensible defaults for static rendering.
|
||||
*
|
||||
* Usage in app.css:
|
||||
* ```css
|
||||
* @import '@manacore/shared-tailwind/themes.css';
|
||||
* @tailwind base;
|
||||
* @tailwind components;
|
||||
* @tailwind utilities;
|
||||
* ```
|
||||
*
|
||||
* Color format: HSL values without hsl() wrapper
|
||||
* Example: --color-primary: 47 95% 58%;
|
||||
* Used as: hsl(var(--color-primary))
|
||||
*/
|
||||
|
||||
/* Default: Lume Light Theme */
|
||||
:root,
|
||||
[data-theme="lume"] {
|
||||
--color-primary: #f8d62b;
|
||||
--color-primary-button: #f8d62b;
|
||||
--color-primary-button-text: #000000;
|
||||
--color-secondary: #D4B200;
|
||||
--color-secondary-button: #FFE9A3;
|
||||
|
||||
--color-background: #dddddd;
|
||||
--color-foreground: #2c2c2c;
|
||||
--color-content: #ffffff;
|
||||
--color-content-hover: #f5f5f5;
|
||||
--color-content-page: #ffffff;
|
||||
--color-menu: #dddddd;
|
||||
--color-menu-hover: #cccccc;
|
||||
|
||||
--color-text: #2c2c2c;
|
||||
--color-text-secondary: #666666;
|
||||
|
||||
--color-border-light: #f2f2f2;
|
||||
--color-border: #e6e6e6;
|
||||
--color-border-strong: #cccccc;
|
||||
|
||||
--color-error: #e74c3c;
|
||||
--color-success: #27ae60;
|
||||
--color-warning: #f39c12;
|
||||
/* ===== Default Theme (Lume Light) ===== */
|
||||
:root {
|
||||
/* Primary brand color */
|
||||
--color-primary: 47 95% 58%;
|
||||
--color-primary-foreground: 0 0% 0%;
|
||||
|
||||
/* Secondary accent */
|
||||
--color-secondary: 47 100% 41%;
|
||||
--color-secondary-foreground: 0 0% 0%;
|
||||
|
||||
/* Page background */
|
||||
--color-background: 0 0% 87%;
|
||||
|
||||
/* Main text color */
|
||||
--color-foreground: 0 0% 17%;
|
||||
|
||||
/* Surfaces (cards, modals, etc.) */
|
||||
--color-surface: 0 0% 100%;
|
||||
--color-surface-hover: 0 0% 96%;
|
||||
--color-surface-elevated: 0 0% 100%;
|
||||
|
||||
/* Muted/subtle elements */
|
||||
--color-muted: 0 0% 90%;
|
||||
--color-muted-foreground: 0 0% 40%;
|
||||
|
||||
/* Borders */
|
||||
--color-border: 0 0% 90%;
|
||||
--color-border-strong: 0 0% 80%;
|
||||
|
||||
/* Semantic colors */
|
||||
--color-error: 6 78% 57%;
|
||||
--color-success: 145 63% 42%;
|
||||
--color-warning: 36 100% 50%;
|
||||
|
||||
/* Form elements */
|
||||
--color-input: 0 0% 100%;
|
||||
--color-ring: 47 95% 58%;
|
||||
|
||||
/* Border radius */
|
||||
--radius-sm: 0.25rem;
|
||||
--radius: 0.375rem;
|
||||
--radius-md: 0.5rem;
|
||||
--radius-lg: 0.75rem;
|
||||
--radius-xl: 1rem;
|
||||
--radius-2xl: 1.5rem;
|
||||
--radius-3xl: 2rem;
|
||||
}
|
||||
|
||||
/* Lume Dark */
|
||||
/* ===== Dark Mode ===== */
|
||||
.dark,
|
||||
[data-theme="lume"].dark,
|
||||
[data-theme="lume"] .dark {
|
||||
--color-primary: #f8d62b;
|
||||
--color-primary-button: #7C6B16;
|
||||
--color-primary-button-text: #ffffff;
|
||||
--color-secondary: #D4B200;
|
||||
--color-secondary-button: #1E1E1E;
|
||||
|
||||
--color-background: #101010;
|
||||
--color-foreground: #ffffff;
|
||||
--color-content: #1E1E1E;
|
||||
--color-content-hover: #333333;
|
||||
--color-content-page: #121212;
|
||||
--color-menu: #101010;
|
||||
--color-menu-hover: #333333;
|
||||
|
||||
--color-text: #ffffff;
|
||||
--color-text-secondary: #a0a0a0;
|
||||
|
||||
--color-border-light: #333333;
|
||||
--color-border: #424242;
|
||||
--color-border-strong: #616161;
|
||||
|
||||
--color-error: #e74c3c;
|
||||
--color-success: #2ecc71;
|
||||
--color-warning: #f1c40f;
|
||||
:root.dark {
|
||||
--color-primary: 47 95% 58%;
|
||||
--color-primary-foreground: 0 0% 0%;
|
||||
|
||||
--color-secondary: 47 70% 29%;
|
||||
--color-secondary-foreground: 0 0% 100%;
|
||||
|
||||
--color-background: 0 0% 6%;
|
||||
--color-foreground: 0 0% 100%;
|
||||
|
||||
--color-surface: 0 0% 12%;
|
||||
--color-surface-hover: 0 0% 16%;
|
||||
--color-surface-elevated: 0 0% 14%;
|
||||
|
||||
--color-muted: 0 0% 20%;
|
||||
--color-muted-foreground: 0 0% 60%;
|
||||
|
||||
--color-border: 0 0% 26%;
|
||||
--color-border-strong: 0 0% 35%;
|
||||
|
||||
--color-error: 6 78% 57%;
|
||||
--color-success: 145 63% 49%;
|
||||
--color-warning: 48 100% 50%;
|
||||
|
||||
--color-input: 0 0% 14%;
|
||||
--color-ring: 47 95% 58%;
|
||||
}
|
||||
|
||||
/* Nature Theme */
|
||||
/* ===== Lume Theme (Gold) ===== */
|
||||
[data-theme="lume"] {
|
||||
--color-primary: 47 95% 58%;
|
||||
--color-primary-foreground: 0 0% 0%;
|
||||
--color-secondary: 47 100% 41%;
|
||||
--color-secondary-foreground: 0 0% 0%;
|
||||
--color-background: 0 0% 87%;
|
||||
--color-foreground: 0 0% 17%;
|
||||
--color-surface: 0 0% 100%;
|
||||
--color-surface-hover: 0 0% 96%;
|
||||
--color-surface-elevated: 0 0% 100%;
|
||||
--color-muted: 0 0% 90%;
|
||||
--color-muted-foreground: 0 0% 40%;
|
||||
--color-border: 0 0% 90%;
|
||||
--color-border-strong: 0 0% 80%;
|
||||
--color-error: 6 78% 57%;
|
||||
--color-success: 145 63% 42%;
|
||||
--color-warning: 36 100% 50%;
|
||||
--color-input: 0 0% 100%;
|
||||
--color-ring: 47 95% 58%;
|
||||
}
|
||||
|
||||
[data-theme="lume"].dark,
|
||||
.dark[data-theme="lume"] {
|
||||
--color-primary: 47 95% 58%;
|
||||
--color-primary-foreground: 0 0% 0%;
|
||||
--color-secondary: 47 70% 29%;
|
||||
--color-secondary-foreground: 0 0% 100%;
|
||||
--color-background: 0 0% 6%;
|
||||
--color-foreground: 0 0% 100%;
|
||||
--color-surface: 0 0% 12%;
|
||||
--color-surface-hover: 0 0% 16%;
|
||||
--color-surface-elevated: 0 0% 14%;
|
||||
--color-muted: 0 0% 20%;
|
||||
--color-muted-foreground: 0 0% 60%;
|
||||
--color-border: 0 0% 26%;
|
||||
--color-border-strong: 0 0% 35%;
|
||||
--color-error: 6 78% 57%;
|
||||
--color-success: 145 63% 49%;
|
||||
--color-warning: 48 100% 50%;
|
||||
--color-input: 0 0% 14%;
|
||||
--color-ring: 47 95% 58%;
|
||||
}
|
||||
|
||||
/* ===== Nature Theme (Green) ===== */
|
||||
[data-theme="nature"] {
|
||||
--color-primary: #4CAF50;
|
||||
--color-primary-button: #A08500;
|
||||
--color-primary-button-text: #ffffff;
|
||||
--color-secondary: #81C784;
|
||||
--color-secondary-button: #F1F8E9;
|
||||
|
||||
--color-background: #FBFDF8;
|
||||
--color-foreground: #1B5E20;
|
||||
--color-content: #F1F8E9;
|
||||
--color-content-hover: #E8F5E9;
|
||||
--color-content-page: #ffffff;
|
||||
--color-menu: #E8F5E9;
|
||||
--color-menu-hover: #C8E6C9;
|
||||
|
||||
--color-text: #1B5E20;
|
||||
--color-text-secondary: #388E3C;
|
||||
|
||||
--color-border-light: #E8F5E9;
|
||||
--color-border: #C8E6C9;
|
||||
--color-border-strong: #A5D6A7;
|
||||
|
||||
--color-error: #E57373;
|
||||
--color-success: #66BB6A;
|
||||
--color-warning: #FFB74D;
|
||||
--color-primary: 122 39% 49%;
|
||||
--color-primary-foreground: 0 0% 100%;
|
||||
--color-secondary: 122 38% 63%;
|
||||
--color-secondary-foreground: 0 0% 0%;
|
||||
--color-background: 80 33% 97%;
|
||||
--color-foreground: 122 56% 24%;
|
||||
--color-surface: 0 0% 100%;
|
||||
--color-surface-hover: 120 25% 95%;
|
||||
--color-surface-elevated: 0 0% 100%;
|
||||
--color-muted: 120 25% 95%;
|
||||
--color-muted-foreground: 122 20% 40%;
|
||||
--color-border: 120 25% 91%;
|
||||
--color-border-strong: 120 26% 79%;
|
||||
--color-error: 0 65% 67%;
|
||||
--color-success: 122 39% 49%;
|
||||
--color-warning: 36 100% 50%;
|
||||
--color-input: 0 0% 100%;
|
||||
--color-ring: 122 39% 49%;
|
||||
}
|
||||
|
||||
[data-theme="nature"].dark,
|
||||
[data-theme="nature"] .dark {
|
||||
--color-primary: #4CAF50;
|
||||
--color-primary-button: #FF9500;
|
||||
--color-primary-button-text: #000000;
|
||||
--color-secondary: #81C784;
|
||||
--color-secondary-button: #1E1E1E;
|
||||
|
||||
--color-background: #121212;
|
||||
--color-foreground: #FFFFFF;
|
||||
--color-content: #1E1E1E;
|
||||
--color-content-hover: #2E7D32;
|
||||
--color-content-page: #121212;
|
||||
--color-menu: #252525;
|
||||
--color-menu-hover: #2E7D32;
|
||||
|
||||
--color-text: #FFFFFF;
|
||||
--color-text-secondary: #A5D6A7;
|
||||
|
||||
--color-border-light: #1B5E20;
|
||||
--color-border: #2E7D32;
|
||||
--color-border-strong: #388E3C;
|
||||
|
||||
--color-error: #CF6679;
|
||||
--color-success: #81C784;
|
||||
--color-warning: #FFD54F;
|
||||
.dark[data-theme="nature"] {
|
||||
--color-primary: 122 39% 49%;
|
||||
--color-primary-foreground: 0 0% 100%;
|
||||
--color-secondary: 122 30% 35%;
|
||||
--color-secondary-foreground: 0 0% 100%;
|
||||
--color-background: 0 0% 7%;
|
||||
--color-foreground: 0 0% 100%;
|
||||
--color-surface: 120 10% 12%;
|
||||
--color-surface-hover: 120 10% 16%;
|
||||
--color-surface-elevated: 120 10% 14%;
|
||||
--color-muted: 120 10% 20%;
|
||||
--color-muted-foreground: 120 10% 60%;
|
||||
--color-border: 120 10% 25%;
|
||||
--color-border-strong: 120 10% 35%;
|
||||
--color-error: 0 65% 57%;
|
||||
--color-success: 122 50% 55%;
|
||||
--color-warning: 48 100% 50%;
|
||||
--color-input: 120 10% 14%;
|
||||
--color-ring: 122 39% 49%;
|
||||
}
|
||||
|
||||
/* Stone Theme */
|
||||
/* ===== Stone Theme (Blue Gray) ===== */
|
||||
[data-theme="stone"] {
|
||||
--color-primary: #607D8B;
|
||||
--color-primary-button: #FF9500;
|
||||
--color-primary-button-text: #000000;
|
||||
--color-secondary: #90A4AE;
|
||||
--color-secondary-button: #ECEFF1;
|
||||
|
||||
--color-background: #F5F7F9;
|
||||
--color-foreground: #263238;
|
||||
--color-content: #ECEFF1;
|
||||
--color-content-hover: #E0E6EA;
|
||||
--color-content-page: #ffffff;
|
||||
--color-menu: #E0E6EA;
|
||||
--color-menu-hover: #CFD8DC;
|
||||
|
||||
--color-text: #263238;
|
||||
--color-text-secondary: #546E7A;
|
||||
|
||||
--color-border-light: #ECEFF1;
|
||||
--color-border: #CFD8DC;
|
||||
--color-border-strong: #B0BEC5;
|
||||
|
||||
--color-error: #EF5350;
|
||||
--color-success: #66BB6A;
|
||||
--color-warning: #FFA726;
|
||||
--color-primary: 200 18% 46%;
|
||||
--color-primary-foreground: 0 0% 100%;
|
||||
--color-secondary: 200 15% 62%;
|
||||
--color-secondary-foreground: 0 0% 0%;
|
||||
--color-background: 210 17% 97%;
|
||||
--color-foreground: 200 19% 18%;
|
||||
--color-surface: 0 0% 100%;
|
||||
--color-surface-hover: 200 10% 94%;
|
||||
--color-surface-elevated: 0 0% 100%;
|
||||
--color-muted: 200 10% 94%;
|
||||
--color-muted-foreground: 200 10% 45%;
|
||||
--color-border: 200 10% 88%;
|
||||
--color-border-strong: 200 12% 75%;
|
||||
--color-error: 4 90% 63%;
|
||||
--color-success: 145 63% 42%;
|
||||
--color-warning: 36 100% 50%;
|
||||
--color-input: 0 0% 100%;
|
||||
--color-ring: 200 18% 46%;
|
||||
}
|
||||
|
||||
[data-theme="stone"].dark,
|
||||
[data-theme="stone"] .dark {
|
||||
--color-primary: #78909C;
|
||||
--color-primary-button: #FF9500;
|
||||
--color-primary-button-text: #000000;
|
||||
--color-secondary: #90A4AE;
|
||||
--color-secondary-button: #1E1E1E;
|
||||
|
||||
--color-background: #121212;
|
||||
--color-foreground: #FFFFFF;
|
||||
--color-content: #1E1E1E;
|
||||
--color-content-hover: #37474F;
|
||||
--color-content-page: #121212;
|
||||
--color-menu: #252525;
|
||||
--color-menu-hover: #37474F;
|
||||
|
||||
--color-text: #FFFFFF;
|
||||
--color-text-secondary: #B0BEC5;
|
||||
|
||||
--color-border-light: #37474F;
|
||||
--color-border: #455A64;
|
||||
--color-border-strong: #546E7A;
|
||||
|
||||
--color-error: #CF6679;
|
||||
--color-success: #81C784;
|
||||
--color-warning: #FFD54F;
|
||||
.dark[data-theme="stone"] {
|
||||
--color-primary: 200 15% 52%;
|
||||
--color-primary-foreground: 0 0% 0%;
|
||||
--color-secondary: 200 12% 35%;
|
||||
--color-secondary-foreground: 0 0% 100%;
|
||||
--color-background: 0 0% 7%;
|
||||
--color-foreground: 0 0% 100%;
|
||||
--color-surface: 200 10% 12%;
|
||||
--color-surface-hover: 200 10% 16%;
|
||||
--color-surface-elevated: 200 10% 14%;
|
||||
--color-muted: 200 10% 20%;
|
||||
--color-muted-foreground: 200 10% 60%;
|
||||
--color-border: 200 10% 25%;
|
||||
--color-border-strong: 200 10% 35%;
|
||||
--color-error: 4 90% 58%;
|
||||
--color-success: 145 63% 49%;
|
||||
--color-warning: 48 100% 50%;
|
||||
--color-input: 200 10% 14%;
|
||||
--color-ring: 200 15% 52%;
|
||||
}
|
||||
|
||||
/* Ocean Theme */
|
||||
/* ===== Ocean Theme (Blue) ===== */
|
||||
[data-theme="ocean"] {
|
||||
--color-primary: #039BE5;
|
||||
--color-primary-button: #FF9500;
|
||||
--color-primary-button-text: #000000;
|
||||
--color-secondary: #4FC3F7;
|
||||
--color-secondary-button: #E1F5FE;
|
||||
|
||||
--color-background: #F5FCFF;
|
||||
--color-foreground: #01579B;
|
||||
--color-content: #E1F5FE;
|
||||
--color-content-hover: #B3E5FC;
|
||||
--color-content-page: #ffffff;
|
||||
--color-menu: #E1F5FE;
|
||||
--color-menu-hover: #B3E5FC;
|
||||
|
||||
--color-text: #01579B;
|
||||
--color-text-secondary: #0277BD;
|
||||
|
||||
--color-border-light: #E1F5FE;
|
||||
--color-border: #B3E5FC;
|
||||
--color-border-strong: #81D4FA;
|
||||
|
||||
--color-error: #EF5350;
|
||||
--color-success: #66BB6A;
|
||||
--color-warning: #FFA726;
|
||||
--color-primary: 199 98% 45%;
|
||||
--color-primary-foreground: 0 0% 100%;
|
||||
--color-secondary: 199 92% 64%;
|
||||
--color-secondary-foreground: 0 0% 0%;
|
||||
--color-background: 199 100% 97%;
|
||||
--color-foreground: 199 100% 18%;
|
||||
--color-surface: 0 0% 100%;
|
||||
--color-surface-hover: 199 100% 94%;
|
||||
--color-surface-elevated: 0 0% 100%;
|
||||
--color-muted: 199 100% 94%;
|
||||
--color-muted-foreground: 199 50% 40%;
|
||||
--color-border: 199 71% 87%;
|
||||
--color-border-strong: 199 79% 76%;
|
||||
--color-error: 4 90% 63%;
|
||||
--color-success: 145 63% 42%;
|
||||
--color-warning: 36 100% 50%;
|
||||
--color-input: 0 0% 100%;
|
||||
--color-ring: 199 98% 45%;
|
||||
}
|
||||
|
||||
[data-theme="ocean"].dark,
|
||||
[data-theme="ocean"] .dark {
|
||||
--color-primary: #039BE5;
|
||||
--color-primary-button: #FF9500;
|
||||
--color-primary-button-text: #000000;
|
||||
--color-secondary: #4FC3F7;
|
||||
--color-secondary-button: #1E1E1E;
|
||||
|
||||
--color-background: #121212;
|
||||
--color-foreground: #FFFFFF;
|
||||
--color-content: #1E1E1E;
|
||||
--color-content-hover: #0277BD;
|
||||
--color-content-page: #121212;
|
||||
--color-menu: #252525;
|
||||
--color-menu-hover: #0277BD;
|
||||
|
||||
--color-text: #FFFFFF;
|
||||
--color-text-secondary: #81D4FA;
|
||||
|
||||
--color-border-light: #01579B;
|
||||
--color-border: #0277BD;
|
||||
--color-border-strong: #0288D1;
|
||||
|
||||
--color-error: #CF6679;
|
||||
--color-success: #81C784;
|
||||
--color-warning: #FFD54F;
|
||||
.dark[data-theme="ocean"] {
|
||||
--color-primary: 199 98% 48%;
|
||||
--color-primary-foreground: 0 0% 0%;
|
||||
--color-secondary: 199 60% 35%;
|
||||
--color-secondary-foreground: 0 0% 100%;
|
||||
--color-background: 0 0% 7%;
|
||||
--color-foreground: 0 0% 100%;
|
||||
--color-surface: 199 30% 12%;
|
||||
--color-surface-hover: 199 30% 16%;
|
||||
--color-surface-elevated: 199 30% 14%;
|
||||
--color-muted: 199 20% 20%;
|
||||
--color-muted-foreground: 199 20% 60%;
|
||||
--color-border: 199 20% 25%;
|
||||
--color-border-strong: 199 20% 35%;
|
||||
--color-error: 4 90% 58%;
|
||||
--color-success: 145 63% 49%;
|
||||
--color-warning: 48 100% 50%;
|
||||
--color-input: 199 30% 14%;
|
||||
--color-ring: 199 98% 48%;
|
||||
}
|
||||
|
||||
/* Dark mode via media query */
|
||||
/* ===== Dark mode via media query (fallback) ===== */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root:not([data-theme]) {
|
||||
--color-primary: #f8d62b;
|
||||
--color-primary-button: #7C6B16;
|
||||
--color-primary-button-text: #ffffff;
|
||||
--color-secondary: #D4B200;
|
||||
--color-secondary-button: #1E1E1E;
|
||||
|
||||
--color-background: #101010;
|
||||
--color-foreground: #ffffff;
|
||||
--color-content: #1E1E1E;
|
||||
--color-content-hover: #333333;
|
||||
--color-content-page: #121212;
|
||||
--color-menu: #101010;
|
||||
--color-menu-hover: #333333;
|
||||
|
||||
--color-text: #ffffff;
|
||||
--color-text-secondary: #a0a0a0;
|
||||
|
||||
--color-border-light: #333333;
|
||||
--color-border: #424242;
|
||||
--color-border-strong: #616161;
|
||||
|
||||
--color-error: #e74c3c;
|
||||
--color-success: #2ecc71;
|
||||
--color-warning: #f1c40f;
|
||||
:root:not(.dark):not([data-theme]) {
|
||||
--color-primary: 47 95% 58%;
|
||||
--color-primary-foreground: 0 0% 0%;
|
||||
--color-secondary: 47 70% 29%;
|
||||
--color-secondary-foreground: 0 0% 100%;
|
||||
--color-background: 0 0% 6%;
|
||||
--color-foreground: 0 0% 100%;
|
||||
--color-surface: 0 0% 12%;
|
||||
--color-surface-hover: 0 0% 16%;
|
||||
--color-surface-elevated: 0 0% 14%;
|
||||
--color-muted: 0 0% 20%;
|
||||
--color-muted-foreground: 0 0% 60%;
|
||||
--color-border: 0 0% 26%;
|
||||
--color-border-strong: 0 0% 35%;
|
||||
--color-error: 6 78% 57%;
|
||||
--color-success: 145 63% 49%;
|
||||
--color-warning: 48 100% 50%;
|
||||
--color-input: 0 0% 14%;
|
||||
--color-ring: 47 95% 58%;
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== Base Styles ===== */
|
||||
@layer base {
|
||||
* {
|
||||
border-color: hsl(var(--color-border));
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: hsl(var(--color-background));
|
||||
color: hsl(var(--color-foreground));
|
||||
font-family: 'Inter', system-ui, -apple-system, sans-serif;
|
||||
}
|
||||
|
||||
/* Smooth color transitions for theme switching */
|
||||
html {
|
||||
color-scheme: light;
|
||||
}
|
||||
|
||||
html.dark {
|
||||
color-scheme: dark;
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== Component Utilities ===== */
|
||||
@layer components {
|
||||
/* Primary Button */
|
||||
.btn-primary {
|
||||
background-color: hsl(var(--color-primary));
|
||||
color: hsl(var(--color-primary-foreground));
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: var(--radius-md);
|
||||
font-weight: 500;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
filter: brightness(0.9);
|
||||
}
|
||||
|
||||
.btn-primary:focus-visible {
|
||||
outline: 2px solid hsl(var(--color-ring));
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* Secondary Button */
|
||||
.btn-secondary {
|
||||
background-color: hsl(var(--color-secondary));
|
||||
color: hsl(var(--color-secondary-foreground));
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: var(--radius-md);
|
||||
font-weight: 500;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
filter: brightness(0.95);
|
||||
}
|
||||
|
||||
/* Ghost Button */
|
||||
.btn-ghost {
|
||||
background-color: transparent;
|
||||
color: hsl(var(--color-foreground));
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: var(--radius-md);
|
||||
font-weight: 500;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.btn-ghost:hover {
|
||||
background-color: hsl(var(--color-surface-hover));
|
||||
}
|
||||
|
||||
/* Card */
|
||||
.card {
|
||||
background-color: hsl(var(--color-surface));
|
||||
border: 1px solid hsl(var(--color-border));
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.card-elevated {
|
||||
background-color: hsl(var(--color-surface-elevated));
|
||||
box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
||||
}
|
||||
|
||||
/* Input */
|
||||
.input {
|
||||
background-color: hsl(var(--color-input));
|
||||
border: 1px solid hsl(var(--color-border));
|
||||
border-radius: var(--radius-md);
|
||||
padding: 0.5rem 0.75rem;
|
||||
color: hsl(var(--color-foreground));
|
||||
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.input:focus {
|
||||
border-color: hsl(var(--color-ring));
|
||||
box-shadow: 0 0 0 2px hsl(var(--color-ring) / 0.2);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.input::placeholder {
|
||||
color: hsl(var(--color-muted-foreground));
|
||||
}
|
||||
|
||||
/* Badge */
|
||||
.badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: var(--radius);
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
background-color: hsl(var(--color-muted));
|
||||
color: hsl(var(--color-muted-foreground));
|
||||
}
|
||||
|
||||
.badge-primary {
|
||||
background-color: hsl(var(--color-primary) / 0.1);
|
||||
color: hsl(var(--color-primary));
|
||||
}
|
||||
|
||||
.badge-success {
|
||||
background-color: hsl(var(--color-success) / 0.1);
|
||||
color: hsl(var(--color-success));
|
||||
}
|
||||
|
||||
.badge-error {
|
||||
background-color: hsl(var(--color-error) / 0.1);
|
||||
color: hsl(var(--color-error));
|
||||
}
|
||||
|
||||
.badge-warning {
|
||||
background-color: hsl(var(--color-warning) / 0.1);
|
||||
color: hsl(var(--color-warning));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue