Some checks failed
- 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>
8.9 KiB
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
- Missing color fields:
background,text,border - Missing layout field:
product_cardstyle - "default" preset: Your platform's default theme
- Extra presets: "elegant" and "nature" themes
- Helper functions:
get_preset(),get_available_presets(),get_preset_preview() - 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
-
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) }) }) -
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 }) } -
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
- ✅ Copy
theme_presets.pytoapp/core/theme_presets.py - ✅ Create API endpoints for applying presets
- ✅ Build theme selector UI in admin panel
- ✅ Test all presets work correctly
- ✅ Add custom color picker for fine-tuning
Perfect! Your presets are now complete and production-ready! 🎨