Files
orion/docs/architecture/theme-system/presets.md
Samir Boulahtit 35d1559162
Some checks failed
CI / ruff (push) Successful in 10s
CI / pytest (push) Failing after 47m30s
CI / validate (push) Successful in 24s
CI / dependency-scanning (push) Successful in 29s
CI / docs (push) Has been skipped
CI / deploy (push) Has been skipped
feat(monitoring): add Redis exporter + Sentry docs to deployment guide
- Add redis-exporter container to docker-compose (oliver006/redis_exporter, 32MB)
- Add Redis scrape target to Prometheus config
- Add 4 Redis alert rules: RedisDown, HighMemory, HighConnections, RejectedConnections
- Document Step 19b (Sentry Error Tracking) in Hetzner deployment guide
- Document Step 19c (Redis Monitoring) in Hetzner deployment guide
- Update resource budget and port reference tables

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 23:30:18 +01:00

8.9 KiB

THEME PRESETS USAGE GUIDE

What Changed in Your Presets

What You Had Right

  • Good preset structure with colors, fonts, layout
  • Clean apply_preset() function
  • Good preset names (modern, classic, minimal, vibrant)

🔧 What We Added

  1. Missing color fields: background, text, border
  2. Missing layout field: product_card style
  3. "default" preset: Your platform's default theme
  4. Extra presets: "elegant" and "nature" themes
  5. Helper functions: get_preset(), get_available_presets(), get_preset_preview()
  6. Custom preset builder: create_custom_preset()

Usage Examples

1. Apply Preset to New Store

from models.database.store_theme import StoreTheme
from app.core.theme_presets import apply_preset
from app.core.database import SessionLocal

# Create theme for store
db = SessionLocal()
store_id = 1

# Create and apply preset
theme = StoreTheme(store_id=store_id)
apply_preset(theme, "modern")

db.add(theme)
db.commit()

2. Change Store's Theme

from models.database.store_theme import StoreTheme
from app.core.theme_presets import apply_preset

# Get existing theme
theme = db.query(StoreTheme).filter(
    StoreTheme.store_id == store_id
).first()

if theme:
    # Update to new preset
    apply_preset(theme, "classic")
else:
    # Create new theme
    theme = StoreTheme(store_id=store_id)
    apply_preset(theme, "classic")
    db.add(theme)

db.commit()

3. Get Available Presets (For UI Dropdown)

from app.core.theme_presets import get_available_presets, get_preset_preview

# Get list of preset names
presets = get_available_presets()
# Returns: ['default', 'modern', 'classic', 'minimal', 'vibrant', 'elegant', 'nature']

# Get preview info for UI
previews = []
for preset_name in presets:
    preview = get_preset_preview(preset_name)
    previews.append(preview)

# Returns list of dicts with:
# {
#     "name": "modern",
#     "description": "Contemporary tech-inspired design...",
#     "primary_color": "#6366f1",
#     "secondary_color": "#8b5cf6",
#     ...
# }

4. API Endpoint to Apply Preset

# In your API route
from fastapi import APIRouter, Depends
from app.core.theme_presets import apply_preset, get_available_presets

@router.put("/theme/preset")
def apply_theme_preset(
    preset_name: str,
    store: Store = Depends(require_store_context()),
    db: Session = Depends(get_db),
):
    """Apply a theme preset to store."""

    # Validate preset name
    if preset_name not in get_available_presets():
        raise HTTPException(
            status_code=400,
            detail=f"Invalid preset. Available: {get_available_presets()}"
        )

    # Get or create store theme
    theme = db.query(StoreTheme).filter(
        StoreTheme.store_id == store.id
    ).first()

    if not theme:
        theme = StoreTheme(store_id=store.id)
        db.add(theme)

    # Apply preset
    apply_preset(theme, preset_name)
    db.commit()
    db.refresh(theme)

    return {
        "message": f"Theme preset '{preset_name}' applied successfully",
        "theme": theme.to_dict()
    }

5. Get All Presets for Theme Selector

@router.get("/theme/presets")
def get_theme_presets():
    """Get all available theme presets with previews."""
    from app.core.theme_presets import get_available_presets, get_preset_preview

    presets = []
    for preset_name in get_available_presets():
        preview = get_preset_preview(preset_name)
        presets.append(preview)

    return {"presets": presets}

# Returns:
# {
#   "presets": [
#     {
#       "name": "default",
#       "description": "Clean and professional...",
#       "primary_color": "#6366f1",
#       "secondary_color": "#8b5cf6",
#       "accent_color": "#ec4899",
#       "heading_font": "Inter, sans-serif",
#       "body_font": "Inter, sans-serif",
#       "layout_style": "grid"
#     },
#     ...
#   ]
# }

6. Create Custom Theme (Not from Preset)

from app.core.theme_presets import create_custom_preset

# User provides custom colors
custom_preset = create_custom_preset(
    colors={
        "primary": "#ff0000",
        "secondary": "#00ff00",
        "accent": "#0000ff",
        "background": "#ffffff",
        "text": "#000000",
        "border": "#cccccc"
    },
    fonts={
        "heading": "Arial, sans-serif",
        "body": "Verdana, sans-serif"
    },
    layout={
        "style": "grid",
        "header": "fixed",
        "product_card": "modern"
    },
    name="my_custom"
)

# Apply to store theme
theme = StoreTheme(store_id=store_id)
theme.theme_name = "custom"
theme.colors = custom_preset["colors"]
theme.font_family_heading = custom_preset["fonts"]["heading"]
theme.font_family_body = custom_preset["fonts"]["body"]
theme.layout_style = custom_preset["layout"]["style"]
theme.header_style = custom_preset["layout"]["header"]
theme.product_card_style = custom_preset["layout"]["product_card"]
theme.is_active = True

db.add(theme)
db.commit()

Available Presets

Preset Description Primary Color Use Case
default Clean & professional Indigo (#6366f1) General purpose
modern Tech-inspired Indigo (#6366f1) Tech products
classic Traditional Dark Blue (#1e40af) Established brands
minimal Ultra-clean B&W Black (#000000) Minimalist brands
vibrant Bold & energetic Orange (#f59e0b) Creative brands
elegant Sophisticated Gray (#6b7280) Luxury products
nature Eco-friendly Green (#059669) Organic/eco brands

Complete Preset Structure

Each preset includes:

{
    "colors": {
        "primary": "#6366f1",      # Main brand color
        "secondary": "#8b5cf6",    # Supporting color
        "accent": "#ec4899",       # Call-to-action color
        "background": "#ffffff",   # Page background
        "text": "#1f2937",        # Text color
        "border": "#e5e7eb"       # Border/divider color
    },
    "fonts": {
        "heading": "Inter, sans-serif",  # Headings (h1-h6)
        "body": "Inter, sans-serif"      # Body text
    },
    "layout": {
        "style": "grid",           # grid | list | masonry
        "header": "fixed",         # fixed | static | transparent
        "product_card": "modern"   # modern | classic | minimal
    }
}

Integration with Admin Panel

Theme Editor UI Flow

  1. Preset Selector

    // Fetch available presets
    fetch('/api/v1/store/theme/presets')
      .then(r => r.json())
      .then(data => {
        // Display preset cards with previews
        data.presets.forEach(preset => {
          showPresetCard(preset.name, preset.primary_color, preset.description)
        })
      })
    
  2. Apply Preset Button

    function applyPreset(presetName) {
      fetch('/api/v1/store/theme/preset', {
        method: 'PUT',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({preset_name: presetName})
      })
      .then(() => {
        alert('Theme updated!')
        location.reload()  // Refresh to show new theme
      })
    }
    
  3. Custom Color Picker (After applying preset)

    // User can then customize colors
    function updateColors(colors) {
      fetch('/api/v1/store/theme/colors', {
        method: 'PUT',
        body: JSON.stringify({colors})
      })
    }
    

Testing Presets

# Test script
from app.core.theme_presets import apply_preset, get_available_presets
from models.database.store_theme import StoreTheme

def test_all_presets():
    """Test applying all presets"""
    presets = get_available_presets()

    for preset_name in presets:
        theme = StoreTheme(store_id=999)  # Test store
        apply_preset(theme, preset_name)

        assert theme.theme_name == preset_name
        assert theme.colors is not None
        assert theme.font_family_heading is not None
        assert theme.is_active == True

        print(f"✅ {preset_name} preset OK")

test_all_presets()

CSS Variables Generation

Your middleware already handles this via StoreTheme.to_dict(), which includes:

"css_variables": {
    "--color-primary": "#6366f1",
    "--color-secondary": "#8b5cf6",
    "--color-accent": "#ec4899",
    "--color-background": "#ffffff",
    "--color-text": "#1f2937",
    "--color-border": "#e5e7eb",
    "--font-heading": "Inter, sans-serif",
    "--font-body": "Inter, sans-serif",
}

Use in templates:

<style>
:root {
  {% for key, value in theme.css_variables.items() %}
  {{ key }}: {{ value }};
  {% endfor %}
}
</style>

Next Steps

  1. Copy theme_presets.py to app/core/theme_presets.py
  2. Create API endpoints for applying presets
  3. Build theme selector UI in admin panel
  4. Test all presets work correctly
  5. Add custom color picker for fine-tuning

Perfect! Your presets are now complete and production-ready! 🎨