2026-01-01 16:20:31 +00:00
# SimpleShop Theme - Implementation Plan
## Overview
Integrate the complete theme feature from `theme-demo-v28.html` into the Phoenix LiveView application. The system will provide 9 curated presets and extensive customization options with real-time preview. All data (including uploaded images) will be stored in a single SQLite database file.
**Key Concept:** This codebase IS the shop (like WordPress is one site). Multiple users can manage it, but there's only one shop/site per deployment.
## Core Architectural Decisions
### 1. Database Storage Strategy
- **Images as BLOBs in SQLite** - Complete self-containment in single database file
- **Application-level settings** - NO Shop model, just Settings tables for the site
- **Multiple admin users** - Any authenticated user can manage theme (no ownership model)
- **SVG stored as both BLOB and TEXT** - Enable recoloring feature
### 2. CSS Architecture
- **Three-layer CSS system** using Custom Properties:
- Layer 1: Primitives (static) - spacing, fonts, radii
- Layer 2: Theme tokens (dynamic) - generated from settings
- Layer 3: Semantic aliases (static) - friendly variable names
- **Inline `<style>` generation** in LiveView for instant preview
- **ETS cache** for storefront CSS (invalidate on save)
### 3. LiveView Structure
- **Single LiveView** (`ThemeLive.Index`) with nested function components
- **No iframe** - Direct preview in same LiveView using data attributes
- **Smart preview data** - Mock data initially, switch to real products when available
- **Optimistic UI** - Update preview immediately, persist on "Save" button
- **Idiomatic Phoenix/LiveView** - Use Tailwind utilities where possible, CSS variables for dynamic theming
### 4. Development Workflow
- **Semantic git commits** - Commit each logical step for easy rollback
- **Test-driven** - Write Phoenix tests alongside implementation (ExUnit, ConnCase, LiveViewTest)
- **Manual verification** - Test in browser after each phase
- **Progressive enhancement** - Each commit should leave app in working state
## Database Schema
**Philosophy:** No Shop model - this app IS the shop. Settings are site-wide, managed by any admin user.
### Migration 1: Create Settings Table
**File:** `priv/repo/migrations/YYYYMMDDHHMMSS_create_settings.exs`
```elixir
defmodule SimpleshopTheme.Repo.Migrations.CreateSettings do
use Ecto.Migration
def change do
create table(:settings, primary_key: false) do
add :id, :binary_id, primary_key: true
add :key, :string, null: false # e.g., "site_name", "theme_settings"
add :value, :text, null: false # JSON or plain text
add :value_type, :string, null: false, default: "string" # string, json, integer, boolean
timestamps(type: :utc_datetime)
end
create unique_index(:settings, [:key])
end
end
```
### Migration 2: Create Images Table
**File:** `priv/repo/migrations/YYYYMMDDHHMMSS_create_images.exs`
```elixir
defmodule SimpleshopTheme.Repo.Migrations.CreateImages do
use Ecto.Migration
def change do
create table(:images, primary_key: false) do
add :id, :binary_id, primary_key: true
add :image_type, :string, null: false # "logo" | "header" | "product"
add :filename, :string, null: false
add :content_type, :string, null: false
add :file_size, :integer, null: false
# BLOB storage
add :data, :binary, null: false
# SVG support
add :is_svg, :boolean, default: false
add :svg_content, :text # For recoloring
# Optional: thumbnail for admin UI performance
add :thumbnail_data, :binary
timestamps(type: :utc_datetime)
end
create index(:images, [:image_type])
end
end
```
## File Structure
### New Contexts and Schemas
```
lib/simpleshop_theme/
├── settings/
│ ├── setting.ex # Setting schema (key-value store)
│ └── theme_settings.ex # Embedded schema for theme settings
├── settings.ex # Settings context (site-wide config API)
├── media/
│ ├── image.ex # Image schema (replaces ShopImage)
│ └── svg_recolorer.ex # Recolor SVG logos
├── media.ex # Media context (image uploads/serving)
└── theme/
├── presets.ex # Preset definitions (9 presets)
├── css_generator.ex # Generate CSS from settings
├── css_cache.ex # ETS cache GenServer
└── preview_data.ex # Smart preview (mock → real products)
```
### LiveView Components
```
lib/simpleshop_theme_web/live/theme_live/
├── index.ex # Main LiveView
├── index.html.heex # Template
├── preset_selector.ex # 9 preset buttons (function component)
├── branding_section.ex # Logo/header uploads
├── customization_panel.ex # Accordion with all options
├── preview_frame.ex # Preview area with page switcher
└── preview_pages/
├── home.html.heex # Preview homepage
├── collection.html.heex # Preview product grid
├── pdp.html.heex # Preview product detail page
├── cart.html.heex # Preview cart
├── about.html.heex # Preview about page
├── contact.html.heex # Preview contact page
└── error.html.heex # Preview 404 page
```
### Controllers
```
lib/simpleshop_theme_web/controllers/
└── image_controller.ex # Serve images from BLOBs
```
### Static Assets & Tests
```
priv/static/css/
├── theme-primitives.css # Layer 1: Fixed CSS variables
└── theme-semantic.css # Layer 3: Semantic aliases
assets/css/
└── app.css # Import theme CSS here
test/simpleshop_theme/
├── settings_test.exs # Settings context tests
├── media_test.exs # Media context tests
└── theme/
├── css_generator_test.exs # CSS generation tests
├── presets_test.exs # Preset validation tests
└── preview_data_test.exs # Preview data tests
test/simpleshop_theme_web/
├── controllers/
│ └── image_controller_test.exs
└── live/
└── theme_live_test.exs # LiveView integration tests
```
2026-01-01 16:37:55 +00:00
## Implementation Status
2026-01-01 16:20:31 +00:00
2026-01-01 16:37:55 +00:00
### Completed Phases (1-8): ✅
- ✅ Phase 1: Database Foundation
- ✅ Phase 2: CSS Architecture
- ✅ Phase 3: Smart Preview Data
- ✅ Phase 4: Theme LiveView (Basic)
- ✅ Phase 5: Preview Pages
- ✅ Phase 6: Customization Options
- ✅ Phase 6.5: Fix Theme Visual Application
- ✅ Phase 7: File Uploads (Logo & Header)
- ✅ Phase 8: Persistence & Polish
### Current Phase: Phase 9 - Storefront Integration 🚧
**See detailed Phase 9 plan at:** `.claude/plans/snuggly-forging-cat.md`
**Phase 9 Goal:** Apply the theme system to the actual public-facing storefront
**Key deliverables:**
- Public storefront routes (`/`, `/products/:slug` , `/collections` , `/cart` , etc.)
- Storefront LiveViews using real/mock data
- LoadTheme plug to inject theme settings and CSS
- CSS cache integration for performance
- Navigation between admin theme editor and public shop
- All 7 pages working on storefront (home, collection, product, cart, about, contact, error)
---
## Implementation Phases (Details)
### Phase 1: Database Foundation ✅
2026-01-01 16:20:31 +00:00
**Goal:** Set up Settings and Media models for site-wide configuration
**Steps:**
1. Create migrations for `settings` and `images` tables
2. Run migrations
3. Create `lib/simpleshop_theme/settings/setting.ex` schema
4. Create `lib/simpleshop_theme/settings/theme_settings.ex` embedded schema
5. Create `lib/simpleshop_theme/settings.ex` context with:
- `get_setting/2` - Get setting by key with default
- `put_setting/3` - Set a setting value
- `get_theme_settings/0` - Get theme settings as struct
- `update_theme_settings/1` - Update theme settings
6. Create `lib/simpleshop_theme/media/image.ex` schema
7. Create `lib/simpleshop_theme/media.ex` context with:
- `upload_image/2` - Upload image with type
- `get_image/1` - Get image by ID
- `delete_image/1` - Delete image
8. Create `lib/simpleshop_theme/theme/presets.ex` with all 9 preset definitions
9. Create seed data in `priv/repo/seeds.exs` for default theme settings
10. Write tests:
- `test/simpleshop_theme/settings_test.exs`
- `test/simpleshop_theme/media_test.exs`
**Files to create:**
- `priv/repo/migrations/*_create_settings.exs`
- `priv/repo/migrations/*_create_images.exs`
- `lib/simpleshop_theme/settings/setting.ex`
- `lib/simpleshop_theme/settings/theme_settings.ex`
- `lib/simpleshop_theme/settings.ex`
- `lib/simpleshop_theme/media/image.ex`
- `lib/simpleshop_theme/media.ex`
- `lib/simpleshop_theme/theme/presets.ex`
- `test/simpleshop_theme/settings_test.exs`
- `test/simpleshop_theme/media_test.exs`
**Files to modify:**
- `priv/repo/seeds.exs` - Add default theme settings
**Git commit:** `feat: add Settings and Media contexts with theme settings schema`
**Validation:**
- Run tests: `mix test`
- Run `iex -S mix` and test Settings API
- Run seeds: `mix run priv/repo/seeds.exs`
---
### Phase 2: CSS Architecture ✓
**Goal:** Set up CSS generation system using Phoenix/Tailwind best practices
**Steps:**
1. Create `priv/static/css/theme-primitives.css` with primitive CSS variables from demo
2. Create `priv/static/css/theme-semantic.css` with semantic aliases
3. Update `assets/css/app.css` to import theme CSS files
4. Create `lib/simpleshop_theme/theme/css_generator.ex` module:
- `generate/1` - Takes ThemeSettings, returns CSS custom properties string
- Helper functions for each theme token (mood, typography, shape, density)
- `hex_to_hsl/1` for accent color conversion
- Use Tailwind-friendly approach (CSS variables that Tailwind can reference)
5. Create `lib/simpleshop_theme/theme/css_cache.ex` GenServer with ETS table
6. Add `CSSCache` to supervision tree in `lib/simpleshop_theme/application.ex`
7. Write tests:
- `test/simpleshop_theme/theme/css_generator_test.exs`
- `test/simpleshop_theme/theme/presets_test.exs`
**Files to create:**
- `priv/static/css/theme-primitives.css`
- `priv/static/css/theme-semantic.css`
- `lib/simpleshop_theme/theme/css_generator.ex`
- `lib/simpleshop_theme/theme/css_cache.ex`
- `test/simpleshop_theme/theme/css_generator_test.exs`
- `test/simpleshop_theme/theme/presets_test.exs`
**Files to modify:**
- `assets/css/app.css`
- `lib/simpleshop_theme/application.ex`
**Git commit:** `feat: add CSS generation system with custom properties and ETS cache`
**Validation:**
- Run tests: `mix test test/simpleshop_theme/theme/`
- Generate CSS for each preset in IEx, verify output
- Check CSS variables work with Tailwind
---
### Phase 3: Smart Preview Data ✓
**Goal:** Create preview data system that uses real data when available, falls back to mock
**Steps:**
1. Create `lib/simpleshop_theme/theme/preview_data.ex` with:
- `products/0` - Real products if exist, else mock products
- `cart_items/0` - Mock cart contents
- `testimonials/0` - Mock testimonials for homepage
- `categories/0` - Mock product categories
- `has_real_products?/0` - Check if shop has products
- Mock data uses placeholder images from `https://placehold.co/`
2. Write tests:
- `test/simpleshop_theme/theme/preview_data_test.exs`
**Files to create:**
- `lib/simpleshop_theme/theme/preview_data.ex`
- `test/simpleshop_theme/theme/preview_data_test.exs`
**Git commit:** `feat: add smart preview data system with mock fallback`
**Validation:**
- Run tests: `mix test test/simpleshop_theme/theme/preview_data_test.exs`
- Call functions in IEx, verify data structure
- Verify falls back to mock when no products exist
---
### Phase 4: Theme LiveView (Basic) ✓
**Goal:** Get basic theme editor working with preset switching
**Steps:**
1. Create `lib/simpleshop_theme_web/live/theme_live/index.ex` :
- Mount: Load theme settings from Settings context
- Assign: `:theme_settings` , `:preview_page` , `:generated_css`
- Event handlers: `apply_preset` , `change_preview_page` , `save_theme`
2. Create `lib/simpleshop_theme_web/live/theme_live/index.html.heex` :
- Two-column layout (controls sidebar + preview area)
- Render preset buttons using Tailwind/daisyUI components
- Render preview frame with generated CSS in `<style>` tag
- Use Phoenix.Component for reusable pieces
3. Add route to `lib/simpleshop_theme_web/router.ex` :
- `live "/admin/theme", ThemeLive.Index` (authenticated)
4. Add "Theme" link to user menu/navigation
5. Write tests:
- `test/simpleshop_theme_web/live/theme_live_test.exs`
**Files to create:**
- `lib/simpleshop_theme_web/live/theme_live/index.ex`
- `lib/simpleshop_theme_web/live/theme_live/index.html.heex`
- `test/simpleshop_theme_web/live/theme_live_test.exs`
**Files to modify:**
- `lib/simpleshop_theme_web/router.ex`
- `lib/simpleshop_theme_web/components/layouts.ex` (add nav link)
**Git commit:** `feat: add Theme LiveView with preset switching`
**Validation:**
- Run tests: `mix test test/simpleshop_theme_web/live/theme_live_test.exs`
- Visit `/admin/theme` , click presets, verify CSS changes
- Test authentication requirement
---
### Phase 5: Preview Pages ✓
**Goal:** Create all 7 preview page templates
**Steps:**
1. Create preview page templates by porting HTML from `theme-demo-v28.html` :
- `lib/simpleshop_theme_web/live/theme_studio_live/preview_pages/home.html.heex`
- `lib/simpleshop_theme_web/live/theme_studio_live/preview_pages/collection.html.heex`
- `lib/simpleshop_theme_web/live/theme_studio_live/preview_pages/pdp.html.heex`
- `lib/simpleshop_theme_web/live/theme_studio_live/preview_pages/cart.html.heex`
- `lib/simpleshop_theme_web/live/theme_studio_live/preview_pages/about.html.heex`
- `lib/simpleshop_theme_web/live/theme_studio_live/preview_pages/contact.html.heex`
- `lib/simpleshop_theme_web/live/theme_studio_live/preview_pages/error.html.heex`
2. Port CSS from demo to theme CSS files (primitives, semantic)
3. Wire up mock data in templates
4. Implement page switcher buttons in preview frame
5. Add data attributes system (`data-mood`, `data-typography` , etc.) to preview container
**Files to create:**
- 7 preview page `.html.heex` files in `preview_pages/` directory
**Files to modify:**
- `lib/simpleshop_theme_web/live/theme_studio_live/index.html.heex` - Add page switcher
- CSS files - Port all styles from demo
**Git commit:** `feat: add preview page templates with theme styling`
**Validation:**
- Run tests: `mix test test/simpleshop_theme_web/live/theme_live_test.exs`
- Switch between all 7 pages, verify they render correctly
- Verify preview data shows (mock or real)
---
### Phase 6: Customization Options ✓
**Goal:** Add all customization controls beyond presets
**Steps:**
1. Create option group components in `index.html.heex` :
- Mood selector (4 options: neutral, warm, cool, dark)
- Typography selector (7 options: clean, editorial, modern, classic, friendly, minimal, impulse)
- Shape selector (4 options: sharp, soft, round, pill)
- Density selector (3 options: spacious, balanced, compact)
- Grid columns selector (3 options: 2, 3, 4)
- Header layout selector (3 options: standard, centered, minimal)
2. Add color pickers:
- Accent color (with live preview)
- Secondary accent color
- Sale color
3. Add advanced options accordion:
- Font size (small, medium, large)
- Heading weight (light, regular, bold)
- Layout width (contained, wide, full)
- Button style (filled, outline, soft)
- Card shadow (none, subtle, pronounced)
- Product text align (left, center)
- Image aspect ratio (square, portrait, landscape)
4. Add feature toggles:
- Announcement bar
- Sticky header
- Hover image
- Quick add button
- Show prices
- PDP trust badges
- PDP reviews
- PDP related products
5. Implement `update_setting` event handler in LiveView
6. Wire all controls to update theme settings and regenerate CSS
**Files to modify:**
- `lib/simpleshop_theme_web/live/theme_live/index.ex` - Add `update_setting` handler
- `lib/simpleshop_theme_web/live/theme_live/index.html.heex` - Add all controls
- `test/simpleshop_theme_web/live/theme_live_test.exs` - Add tests for customization
**Git commit:** `feat: add full customization controls with real-time preview`
**Validation:**
- Run tests: `mix test test/simpleshop_theme_web/live/theme_live_test.exs`
- Change each option, verify preview updates instantly
- Test all toggles work correctly
---
### Phase 6.5: Fix Theme Visual Application 🔧
**Goal:** Fix the theme controls so they actually visually affect the preview pages
**Problem:** Theme controls update the database but don't visually change the preview. The Phoenix implementation is missing the data-attribute-based CSS system that the demo uses.
**Root Cause Analysis:**
The demo HTML uses data attributes (`data-mood="warm"`, `data-typography="editorial"` , etc.) on the `.preview-frame` element, and the CSS uses attribute selectors like `.preview-frame[data-mood="warm"] { ... }` to apply styles. The Phoenix LiveView implementation is missing these data attributes entirely.
**Discovered Issues:**
| Issue | Status | Root Cause |
|-------|--------|------------|
| Fonts don't change | ❌ Broken | 1. Google Fonts not loaded< br > 2. No data attributes on preview-frame |
| Mood/colors don't work (except dark) | ❌ Broken | No data attributes on preview-frame |
| Shape doesn't work | ❌ Broken | No data attributes on preview-frame |
| Density doesn't work | ❌ Broken | No data attributes on preview-frame |
| Grid columns 4 shows as 2 | ❌ Broken | Tailwind can't process dynamic class interpolation |
| Accent color doesn't update | ⚠️ Partial | CSS generated but may be overridden |
| Header layout has no component | ❌ Missing | No header component in preview pages |
**Steps:**
1. **Add Google Fonts to root layout** (`lib/simpleshop_theme_web/components/layouts/root.html.heex`):
```heex
< link rel = "preconnect" href = "https://fonts.googleapis.com" >
< link rel = "preconnect" href = "https://fonts.gstatic.com" crossorigin >
< link href = "https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,400;9..144,500;9..144,600;9..144,700&family=Inter:wght@400;500;600;700&family=Libre+Baskerville:wght@400;700&family=Nunito:wght@400;600;700&family=Nunito+Sans:opsz,wght@6..12,300;6..12,400;6..12,500;6..12,600&family=Outfit:wght@300;400;500;600&family=Source+Sans+3:wght@400;500;600&family=Space+Grotesk:wght@400;500;600&display=swap" rel = "stylesheet" >
```
2. **Add data attributes to preview frame** (`lib/simpleshop_theme_web/live/theme_live/index.html.heex:205`):
```heex
< div class = "preview-frame bg-white overflow-auto"
data-mood={@theme_settings.mood}
data-typography={@theme_settings.typography}
data-shape={@theme_settings.shape}
data-density={@theme_settings.density}
data-grid={@theme_settings.grid_columns}
data-header={@theme_settings.header_layout}
style="min-height: 600px; max-height: calc(100vh - 200px);">
```
3. **Copy CSS from demo file** (`theme-demo-v28.html` lines 81-217) to create `priv/static/css/theme-layer2-attributes.css` :
- All the `.preview-frame[data-mood="..."]` rules
- All the `.preview-frame[data-typography="..."]` rules
- All the `.preview-frame[data-shape="..."]` rules
- All the `.preview-frame[data-density="..."]` rules
- All the `.preview-frame[data-grid="..."]` rules
- All the `.preview-frame[data-header="..."]` rules
4. **Import the new CSS file** in `assets/css/app.css` :
```css
@import "../priv/static/css/theme-primitives.css";
@import "../priv/static/css/theme-layer2-attributes.css";
@import "../priv/static/css/theme-semantic.css";
```
5. **Fix grid columns dynamic classes** - Replace string interpolation with conditional logic in all preview pages:
```heex
<!-- BEFORE (broken): -->
< div class = {"grid gap-6 grid-cols-1 sm:grid-cols-2 lg:grid-cols- # { @ theme_settings . grid_columns } " } >
<!-- AFTER (working): -->
< div class = {["grid gap-6 grid-cols-1 sm:grid-cols-2 " ,
case @theme_settings .grid_columns do
"2" -> "lg:grid-cols-2"
"3" -> "lg:grid-cols-3"
"4" -> "lg:grid-cols-4"
_ -> "lg:grid-cols-3"
end]}>
```
6. **Add header component** to preview pages - Create a header section that responds to `data-header` attribute:
```heex
< header class = "theme-header" style = "background-color: var(--t-surface-raised); border-bottom: 1px solid var(--t-border-subtle);" >
<!-- Header content that adapts based on data - header="standard|centered|minimal" -->
< / header >
```
7. **Update CSSGenerator** to work alongside attribute-based CSS:
- Keep generating accent color HSL variables (these are used by all themes)
- Remove mood/typography/shape/density generation (now handled by attribute CSS)
- Focus on dynamic values like accent_color, secondary_accent_color, sale_color
8. **Test all controls** in browser to verify visual changes work
**Files to create:**
- `priv/static/css/theme-layer2-attributes.css` - Attribute-based theme CSS from demo
**Files to modify:**
- `lib/simpleshop_theme_web/components/layouts/root.html.heex` - Add Google Fonts
- `lib/simpleshop_theme_web/live/theme_live/index.html.heex` - Add data attributes to preview-frame
- `assets/css/app.css` - Import new CSS file
- `lib/simpleshop_theme/theme/css_generator.ex` - Simplify to focus on color variables
- All preview page files - Fix grid columns dynamic classes
- All preview page files - Add header component
**Git commit:** `fix: add data attributes and attribute-based CSS to make theme controls work visually`
**Validation:**
- Change mood - verify background colors change
- Change typography - verify fonts change
- Change shape - verify button/card border radius changes
- Change density - verify spacing changes
- Change grid columns - verify all 3 options (2, 3, 4) work correctly
- Change accent color - verify primary buttons change color
- Change header layout - verify header structure changes
---
### Phase 7: File Uploads (Logo & Header) ✓
**Goal:** Enable logo and header image uploads with database storage
**Steps:**
1. Add dependency to `mix.exs` : `{:image, "~> 0.54"}` for thumbnail generation
2. Implement SVG detection and text storage in `image.ex` changeset
3. Add Media context functions for logo/header:
- Update `upload_image/2` to handle thumbnails
- Add `get_logo/0` , `get_header/0` convenience functions
4. Create `lib/simpleshop_theme_web/controllers/image_controller.ex` :
- `show/2` - Serve image BLOB with proper content-type and caching
- `recolored_svg/2` - Serve recolored SVG
5. Create `lib/simpleshop_theme/media/svg_recolorer.ex` for SVG color manipulation
6. Add routes for image serving in router
7. Configure LiveView uploads in `index.ex` :
- `:logo_upload` - Accept PNG, JPG, WebP, SVG (max 2MB)
- `:header_upload` - Accept PNG, JPG, WebP (max 5MB)
8. Add upload controls to branding section in template using daisyUI components:
- Logo mode selector (text-only, logo+text, logo-only, header-image, logo+header)
- Logo upload with preview
- Logo size slider (24-120px)
- SVG recolor toggle + color picker
- Header image upload with preview
- Header zoom slider (100-200%)
- Header position sliders (X and Y: 0-100%)
9. Implement upload event handlers
10. Display uploaded images in preview
11. Write tests:
- `test/simpleshop_theme_web/controllers/image_controller_test.exs`
- `test/simpleshop_theme/media/svg_recolorer_test.exs`
**Files to create:**
- `lib/simpleshop_theme_web/controllers/image_controller.ex`
- `lib/simpleshop_theme/media/svg_recolorer.ex`
- `test/simpleshop_theme_web/controllers/image_controller_test.exs`
- `test/simpleshop_theme/media/svg_recolorer_test.exs`
**Files to modify:**
- `lib/simpleshop_theme/media.ex` - Add logo/header helpers
- `lib/simpleshop_theme/media/image.ex` - SVG handling
- `lib/simpleshop_theme_web/live/theme_live/index.ex` - Upload config and handlers
- `lib/simpleshop_theme_web/live/theme_live/index.html.heex` - Branding section UI
- `lib/simpleshop_theme_web/router.ex` - Image serving routes
- `mix.exs` - Add `:image` dependency
- `test/simpleshop_theme_web/live/theme_live_test.exs` - Add upload tests
**Git commit:** `feat: add image uploads with BLOB storage and SVG recoloring`
**Validation:**
- Run tests: `mix test`
- Upload logo and header images
- Verify BLOB storage in database
- Test SVG recoloring works
- Verify images display in preview
- Test image serving with proper cache headers
---
### Phase 8: Persistence & Polish ✓
**Goal:** Complete save functionality and UX polish
**Steps:**
1. Implement "Save Theme" button and handler:
- Show loading state during save
- Flash success/error message
- Invalidate CSS cache
2. Add "Reset to Preset" functionality
3. Implement unsaved changes warning
4. Add keyboard shortcuts (Cmd+S to save)
5. Debounce color picker inputs (300ms) to avoid excessive renders
6. Add loading skeletons for preview
7. Handle edge cases:
- File too large error
- Invalid file type error
- Network errors
8. Add client-side validation
9. Improve mobile responsiveness of editor
10. Add tooltips/help text for complex options
**Files to modify:**
- `lib/simpleshop_theme_web/live/theme_live/index.ex` - Save logic, debouncing
- `lib/simpleshop_theme_web/live/theme_live/index.html.heex` - UI polish
- JavaScript hooks for keyboard shortcuts (if needed)
- `test/simpleshop_theme_web/live/theme_live_test.exs` - Test persistence
**Git commit:** `feat: add theme persistence with UX polish and validation`
**Validation:**
- Run tests: `mix test`
- Save theme, reload page, verify settings persist
- Test unsaved changes warning
- Test keyboard shortcuts
- Verify error handling for edge cases
- Test mobile responsiveness
---
2026-01-01 16:37:55 +00:00
### Phase 9: Storefront Integration 🚧
2026-01-01 16:20:31 +00:00
**Goal:** Apply theme to actual public-facing shop
2026-01-01 16:37:55 +00:00
**Status:** Ready to implement - See detailed plan in `.claude/plans/snuggly-forging-cat.md`
**High-level overview:**
1. Extract shared components from preview pages (header, footer, product card, etc.)
2. Create LoadTheme plug to inject theme settings and CSS into all public requests
3. Create dedicated shop layout with `.shop-root` class and data attributes
4. Build storefront LiveViews for all pages:
- Home (`/`)
- Collection (`/collections/:slug`)
- Product Detail (`/products/:slug`)
- Cart (`/cart`)
- About (`/about`)
- Contact (`/contact`)
- Error (404 page)
5. Wire up CSS cache invalidation when theme is saved
6. Add cache warming on application startup
7. Add navigation links between admin and storefront
8. Comprehensive testing and polish
**Key files to create (23 files):**
- 5 shared component modules in `components/shop/`
- 1 LoadTheme plug
- 1 shop layout template
- 6 storefront LiveView modules + templates
- 1 error page template
- 6 test files
**Key files to modify (9 files):**
- Router (add public routes and plug)
- Settings context (add cache invalidation)
- Application (add cache warming)
- Theme editor (add "View Shop" link)
- Preview pages (use shared components)
**Detailed implementation plan:** See `.claude/plans/snuggly-forging-cat.md` for 12-step implementation guide with code examples, validation steps, and architectural decisions.
**Git commit strategy:** Commit after each major step (components extraction, plug creation, each LiveView, etc.)
**Validation approach:**
- Test theme editor still works after component extraction
- Test each storefront page individually as built
- Verify CSS cache invalidation flow
- Test navigation between admin and storefront
- Comprehensive testing in Step 12
2026-01-01 16:20:31 +00:00
---
## Technical Implementation Details
### CSS Generation Example
```elixir
# lib/simpleshop_theme/theme/css_generator.ex
defmodule SimpleshopTheme.Theme.CSSGenerator do
alias SimpleshopTheme.Settings.ThemeSettings
def generate(%ThemeSettings{} = settings) do
"""
.preview-frame, .shop-root {
#{generate_accent(settings.accent_color)}
#{generate_mood(settings.mood)}
#{generate_typography(settings.typography)}
#{generate_shape(settings.shape)}
#{generate_density(settings.density)}
}
"""
end
defp generate_accent(hex) do
{h, s, l} = hex_to_hsl(hex)
"""
--t-accent-h: #{h};
--t-accent-s: #{s}%;
--t-accent-l: #{l}%;
--t-accent: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l));
"""
end
defp hex_to_hsl("#" < > hex), do: hex_to_hsl(hex)
defp hex_to_hsl(hex) when byte_size(hex) == 6 do
{r, ""} = Integer.parse(String.slice(hex, 0..1), 16)
{g, ""} = Integer.parse(String.slice(hex, 2..3), 16)
{b, ""} = Integer.parse(String.slice(hex, 4..5), 16)
r = r / 255
g = g / 255
b = b / 255
max = Enum.max([r, g, b])
min = Enum.min([r, g, b])
l = (max + min) / 2
if max == min do
{0, 0, round(l * 100)}
else
d = max - min
s = if l > 0.5, do: d / (2 - max - min), else: d / (max + min)
h = cond do
max == r -> (g - b) / d + (if g < b , do: 6 , else: 0 )
max == g -> (b - r) / d + 2
max == b -> (r - g) / d + 4
end
{round(h * 60), round(s * 100), round(l * 100)}
end
end
defp generate_mood("neutral"), do: """
--t-surface-base: #ffffff ;
--t-surface-raised: #ffffff ;
--t-text-primary: #171717 ;
--t-border-default: #e5e5e5 ;
"""
defp generate_mood("warm"), do: """
--t-surface-base: #fdf8f3 ;
--t-surface-raised: #fffcf8 ;
--t-text-primary: #1c1917 ;
--t-border-default: #e7e0d8 ;
"""
defp generate_mood("cool"), do: """
--t-surface-base: #f4f7fb ;
--t-surface-raised: #f8fafc ;
--t-text-primary: #0f172a ;
--t-border-default: #d4dce8 ;
"""
defp generate_mood("dark"), do: """
--t-surface-base: #0a0a0a ;
--t-surface-raised: #171717 ;
--t-text-primary: #fafafa ;
--t-border-default: #262626 ;
--p-shadow-strength: 0.25;
"""
# Similar for typography, shape, density...
end
```
### LiveView Upload Configuration
```elixir
# In mount/3
socket =
socket
|> allow_upload(:logo_upload,
accept: ~w(.png .jpg .jpeg .webp .svg),
max_entries: 1,
max_file_size: 2_000_000,
auto_upload: true
)
|> allow_upload(:header_upload,
accept: ~w(.png .jpg .jpeg .webp),
max_entries: 1,
max_file_size: 5_000_000,
auto_upload: true
)
# Handle upload completion
def handle_event("logo_uploaded", _params, socket) do
uploaded_files =
consume_uploaded_entries(socket, :logo_upload, fn %{path: path}, entry ->
file_binary = File.read!(path)
{:ok, image} = Media.upload_image(%{
image_type: "logo",
filename: entry.client_name,
content_type: entry.client_type,
file_size: entry.client_size,
data: file_binary
})
{:ok, image}
end)
case uploaded_files do
[image | _] ->
# Update theme settings to reference this image
settings = socket.assigns.theme_settings
{:ok, _} = Settings.update_theme_settings(%{settings | logo_image_id: image.id})
{:noreply, assign(socket, :logo_image, image)}
[] ->
{:noreply, socket}
end
end
```
### Image Controller Serving
```elixir
defmodule SimpleshopThemeWeb.ImageController do
use SimpleshopThemeWeb, :controller
alias SimpleshopTheme.Media
def show(conn, %{"id" => id}) do
case Media.get_image(id) do
nil ->
send_resp(conn, 404, "Image not found")
image ->
conn
|> put_resp_content_type(image.content_type)
|> put_resp_header("cache-control", "public, max-age=31536000, immutable")
|> put_resp_header("etag", "\"#{image.id}\"")
|> send_resp(200, image.data)
end
end
def recolored_svg(conn, %{"id" => id, "color" => color}) do
with %{is_svg: true, svg_content: svg} < - Media . get_image ( id ) ,
clean_color < - String . trim_leading ( color , " # " ) ,
recolored < - SimpleshopTheme . Media . SVGRecolorer . recolor ( svg , " # # { clean_color } " ) do
conn
|> put_resp_content_type("image/svg+xml")
|> put_resp_header("cache-control", "public, max-age=3600")
|> send_resp(200, recolored)
else
_ -> send_resp(conn, 400, "Invalid request")
end
end
end
```
## Key Dependencies to Add
```elixir
# mix.exs
{:image, "~> 0.54"} # Thumbnail generation (uses libvips)
{:fast_sanitize, "~> 0.2"} # SVG sanitization (security)
```
## Router Updates
```elixir
# lib/simpleshop_theme_web/router.ex
# Theme editor (authenticated)
scope "/admin", SimpleshopThemeWeb do
pipe_through [:browser, :require_authenticated_user]
live_session :theme_editor,
on_mount: [{SimpleshopThemeWeb.UserAuth, :require_authenticated}] do
live "/theme", ThemeLive.Index, :index
end
end
# Image serving (public)
scope "/", SimpleshopThemeWeb do
pipe_through :browser
get "/images/:id", ImageController, :show
get "/images/:id/recolored/:color", ImageController, :recolored_svg
end
# Public storefront (future - Phase 9)
# scope "/", SimpleshopThemeWeb do
# pipe_through :browser
# live "/", ShopLive.Home, :index
# live "/products/:slug", ShopLive.Product, :show
# end
```
## Critical Files Reference
### To Create
- `lib/simpleshop_theme/settings.ex` - Settings context
- `lib/simpleshop_theme/settings/setting.ex` - Setting schema (key-value)
- `lib/simpleshop_theme/settings/theme_settings.ex` - Embedded theme settings
- `lib/simpleshop_theme/media.ex` - Media context
- `lib/simpleshop_theme/media/image.ex` - Image schema
- `lib/simpleshop_theme/media/svg_recolorer.ex` - SVG manipulation
- `lib/simpleshop_theme/theme/presets.ex` - 9 preset definitions
- `lib/simpleshop_theme/theme/css_generator.ex` - CSS generation
- `lib/simpleshop_theme/theme/css_cache.ex` - ETS caching
- `lib/simpleshop_theme/theme/preview_data.ex` - Smart preview data
- `lib/simpleshop_theme_web/live/theme_live/index.ex` - Main LiveView
- `lib/simpleshop_theme_web/controllers/image_controller.ex` - Image serving
- `priv/static/css/theme-primitives.css` - CSS Layer 1
- `priv/static/css/theme-semantic.css` - CSS Layer 3
- Comprehensive test files for all contexts and LiveViews
### To Modify
- `lib/simpleshop_theme/application.ex` - Add CSS cache to supervision tree
- `lib/simpleshop_theme_web/router.ex` - Add theme and image routes
- `lib/simpleshop_theme_web/components/layouts.ex` - Add theme nav link
- `assets/css/app.css` - Import theme CSS
- `priv/repo/seeds.exs` - Add default theme settings
- `mix.exs` - Add dependencies
### Reference Files
- `SIMPLESHOP_THEME_STUDIO_SPEC.md` - Complete specification
- `theme-demo-v28.html` - Working prototype (CSS, HTML structure, JavaScript logic)
## Success Criteria
✅ User can access theme studio at `/admin/theme`
✅ All 9 presets work with real-time preview
✅ All customization options update preview instantly
✅ Logo and header images upload and store as BLOBs
✅ SVG recoloring works correctly
✅ Preview shows all 7 mock pages
✅ Settings persist to database
✅ "Save" button works with success feedback
✅ Images served efficiently from database
✅ CSS cached in ETS for performance
✅ Everything self-contained in SQLite file
## Next Steps After Implementation
1. **Add Products Context** - So users can add real products
2. **Build Storefront** - Public-facing shop pages using saved theme
3. **Add Orders/Cart** - E-commerce functionality
4. **Multi-admin Support** - Invite additional users to manage shop
5. **Custom Domains** - Allow users to point their own domain
6. **Theme Export/Import** - Share themes between shops
7. **Advanced Features** - Custom CSS, code injection, etc.