simpleshop_theme/SIMPLESHOP_THEME_STUDIO_SPEC.md
Jamey Greenwood a401365943 feat: add Settings and Media contexts with theme settings schema
- Create settings table for site-wide key-value configuration
- Create images table for BLOB storage of logo/header images
- Add Setting schema with JSON/string/integer/boolean support
- Add ThemeSettings embedded schema with all theme options
- Add Settings context with get/put/update operations
- Add Media context for image uploads and retrieval
- Add Image schema with SVG detection and storage
- Add 9 curated theme presets (gallery, studio, boutique, etc.)
- Add comprehensive tests for Settings and Media contexts
- Add seeds with default Studio preset
- All tests passing (29 tests, 0 failures)
2025-12-30 21:35:52 +00:00

13 KiB
Raw Blame History

SimpleShop Theme Studio - Complete Implementation Summary

Overview

This document summarises the complete Theme Studio feature developed for SimpleShop - an open-source POD (print-on-demand) e-commerce platform built with Phoenix/Elixir/LiveView. The Theme Studio allows sellers to customise their shop's appearance through a constrained but flexible system of presets and options.

Design Philosophy

Core Principles

  1. "One theme, infinite variations" - Rather than offering multiple themes, we provide one solid foundation with curated customisation options
  2. Constrained creativity - Limit choices to prevent poor design outcomes while maintaining perceived variety
  3. No professional photography required - Defaults work well with product mockups, not just lifestyle imagery
  4. Mobile-first - All features work on touch devices (no hover-only interactions)
  5. Ethical design - No dark patterns like countdown timers or fake urgency

Target Audience

  • Solo POD creators (artists, illustrators, designers)
  • Small catalogs (typically <50 products)
  • Non-technical users who found WooCommerce too complex
  • Budget-conscious sellers (£5-10/month target price)

Feature Architecture

Preset System

Nine curated presets that combine multiple settings into cohesive looks:

@presets %{
  gallery: %{
    mood: "warm",
    typography: "editorial", 
    shape: "soft",
    density: "spacious",
    grid: "3",
    header: "centered",
    accent: "#e85d04"
  },
  studio: %{
    mood: "neutral",
    typography: "clean",
    shape: "soft", 
    density: "balanced",
    grid: "4",
    header: "standard",
    accent: "#3b82f6"
  },
  boutique: %{
    mood: "warm",
    typography: "classic",
    shape: "soft",
    density: "balanced",
    grid: "3",
    header: "centered",
    accent: "#b45309"
  },
  bold: %{
    mood: "neutral",
    typography: "modern",
    shape: "sharp",
    density: "compact",
    grid: "4",
    header: "standard",
    accent: "#dc2626"
  },
  playful: %{
    mood: "neutral",
    typography: "friendly",
    shape: "pill",
    density: "balanced",
    grid: "4",
    header: "standard",
    accent: "#8b5cf6"
  },
  minimal: %{
    mood: "cool",
    typography: "minimal",
    shape: "sharp",
    density: "spacious",
    grid: "2",
    header: "minimal",
    accent: "#171717"
  },
  night: %{
    mood: "dark",
    typography: "modern",
    shape: "soft",
    density: "balanced",
    grid: "4",
    header: "standard",
    accent: "#f97316"
  },
  classic: %{
    mood: "warm",
    typography: "classic",
    shape: "soft",
    density: "spacious",
    grid: "3",
    header: "standard",
    accent: "#166534"
  },
  impulse: %{
    mood: "neutral",
    typography: "impulse",
    shape: "sharp",
    density: "spacious",
    grid: "3",
    header: "centered",
    accent: "#000000",
    font_size: "medium",
    heading_weight: "regular",
    layout_width: "full",
    button_style: "filled",
    card_shadow: "none",
    product_text: "center"
  }
}

Customisation Options

Typography Styles

Style Heading Font Body Font Weight Use Case
clean Inter Inter 600 Default, versatile
editorial Fraunces (serif) Source Sans 600 Art galleries, editorial
modern Space Grotesk Space Grotesk 500 Tech, contemporary
classic Libre Baskerville Source Sans 400 Traditional, luxury
friendly Nunito Nunito 700 Playful, approachable
minimal Outfit Outfit 300 Ultra-clean, light
impulse Nunito Sans Nunito Sans 300 Fashion editorial

Colour Moods

Mood Background Text Border Use Case
neutral #ffffff #171717 #e5e5e5 Default, works with any accent
warm #fdf8f3 #1c1917 #e7e0d8 Cosy, artisan, handmade
cool #f4f7fb #0f172a #d4dce8 Tech, modern, professional
dark #0a0a0a #fafafa #262626 Premium, dramatic, night mode

Shape Options

Shape Border Radius Use Case
sharp 0 Modern, editorial, bold
soft 0.5rem Default, approachable
round 0.75-1rem Friendly, playful
pill 9999px (buttons) Fun, casual, rounded

Density Options

Density Multiplier Use Case
spacious 1.25x Editorial, luxury, few products
balanced 1x Default, most stores
compact 0.75x Large catalogs, utilitarian

Layout Options

  • Grid columns: 2, 3, or 4 products per row
  • Header layout: Standard (left logo), Centered, Minimal
  • Layout width: Contained (1200px), Wide (1400px), Full width
  • Button style: Filled, Outline, Soft
  • Card shadow: None, Subtle, Pronounced
  • Product text alignment: Left, Center
  • Image aspect ratio: Square, Portrait, Landscape

Branding Options

Logo Modes

  1. Text only - Shop name as styled text
  2. Logo + text - Logo image alongside shop name
  3. Logo only - Just the logo image
  4. Header image - Full-width header background
  5. Logo + header image - Both logo and header background

Logo Features

  • Upload support (PNG, JPG, WebP, SVG)
  • Size slider (24-120px)
  • SVG recolouring option (change logo colour to match theme)

Header Image Features

  • Background image upload
  • Zoom control (100-200%)
  • Horizontal position (0-100%)
  • Vertical position (0-100%)

Toggle Features

  • Announcement bar (on/off)
  • Sticky header (on/off)
  • Second image on hover (on/off)
  • Quick add button (on/off)
  • Show prices (on/off)
  • Trust badges on PDP (on/off)
  • Reviews section on PDP (on/off)
  • Related products on PDP (on/off)

Data Model

Theme Settings Schema

defmodule SimpleShop.Shops.ThemeSettings do
  use Ecto.Schema
  import Ecto.Changeset

  embedded_schema do
    # Core preset (optional - if set, provides defaults)
    field :preset, :string
    
    # Branding
    field :shop_name, :string
    field :logo_mode, :string, default: "text-only"
    field :logo_url, :string
    field :logo_size, :integer, default: 36
    field :logo_recolor, :boolean, default: false
    field :logo_color, :string, default: "#171717"
    field :header_image_url, :string
    field :header_zoom, :integer, default: 100
    field :header_position_x, :integer, default: 50
    field :header_position_y, :integer, default: 50
    
    # Theme tokens
    field :mood, :string, default: "neutral"
    field :typography, :string, default: "clean"
    field :shape, :string, default: "soft"
    field :density, :string, default: "balanced"
    field :accent_color, :string, default: "#f97316"
    field :hover_color, :string
    field :sale_color, :string, default: "#dc2626"
    
    # Layout
    field :grid_columns, :integer, default: 4
    field :header_layout, :string, default: "standard"
    field :layout_width, :string, default: "wide"
    field :font_size, :string, default: "medium"
    field :heading_weight, :string, default: "bold"
    field :button_style, :string, default: "filled"
    field :card_shadow, :string, default: "none"
    field :product_text_align, :string, default: "left"
    field :image_aspect_ratio, :string, default: "square"
    field :gallery_position, :string, default: "left"
    
    # Feature toggles
    field :announcement_bar, :boolean, default: true
    field :sticky_header, :boolean, default: false
    field :hover_image, :boolean, default: true
    field :quick_add, :boolean, default: true
    field :show_prices, :boolean, default: true
    field :pdp_trust_badges, :boolean, default: true
    field :pdp_reviews, :boolean, default: true
    field :pdp_related_products, :boolean, default: true
  end
end

CSS Custom Properties

The theme system uses CSS custom properties for real-time updates. Key variables:

/* Primitives (fixed) */
--p-space-1 through --p-space-24
--p-radius-none through --p-radius-full
--p-font-inter, --p-font-fraunces, etc.
--p-text-xs through --p-text-4xl

/* Theme tokens (dynamic) */
--t-surface-base, --t-surface-raised, --t-surface-sunken
--t-text-primary, --t-text-secondary, --t-text-tertiary
--t-border-default, --t-border-subtle
--t-accent (HSL-based for easy manipulation)
--t-font-heading, --t-font-body
--t-heading-weight, --t-heading-tracking
--t-radius-button, --t-radius-card, --t-radius-input
--t-density (multiplier)

/* Semantic aliases */
--color-page, --color-card, --color-heading, --color-body
--font-heading, --font-body, --weight-heading
--space-xs through --space-2xl
--radius-button, --radius-card

LiveView Implementation Notes

Real-time Preview

The theme editor should provide instant visual feedback. In Phoenix LiveView:

defmodule SimpleShopWeb.ThemeEditorLive do
  use SimpleShopWeb, :live_view
  
  def mount(_params, _session, socket) do
    {:ok, assign(socket, 
      settings: default_settings(),
      preview_page: "home"
    )}
  end
  
  def handle_event("update_setting", %{"key" => key, "value" => value}, socket) do
    settings = Map.put(socket.assigns.settings, String.to_atom(key), value)
    {:noreply, assign(socket, settings: settings)}
  end
  
  def handle_event("apply_preset", %{"preset" => preset_name}, socket) do
    preset = Map.get(@presets, String.to_atom(preset_name))
    settings = Map.merge(socket.assigns.settings, preset)
    {:noreply, assign(socket, settings: settings)}
  end
  
  def handle_event("change_preview_page", %{"page" => page}, socket) do
    {:noreply, assign(socket, preview_page: page)}
  end
end

CSS Generation

Generate CSS custom properties from settings:

defmodule SimpleShop.Theme.CSSGenerator do
  def generate_css(settings) do
    """
    :root {
      --t-accent-h: #{accent_hue(settings.accent_color)};
      --t-accent-s: #{accent_saturation(settings.accent_color)}%;
      --t-accent-l: #{accent_lightness(settings.accent_color)}%;
      /* ... other properties */
    }
    
    #{mood_css(settings.mood)}
    #{typography_css(settings.typography)}
    #{shape_css(settings.shape)}
    #{density_css(settings.density)}
    """
  end
end

File Uploads

Logo Upload

  • Accepted formats: PNG, JPG, WebP, SVG
  • Max size: 2MB recommended
  • SVG special handling: Store raw content for recolouring feature
  • Storage: Local or S3-compatible (user's choice for data sovereignty)

Header Image Upload

  • Accepted formats: PNG, JPG, WebP
  • Max size: 5MB recommended
  • Responsive serving: Generate multiple sizes

Preview Pages

The demo includes 7 preview pages to show theme in context:

  1. Home - Hero, categories, featured products, testimonials
  2. Collection - Product grid with filters
  3. Product (PDP) - Gallery, details, add to cart, reviews
  4. Cart - Line items, totals, checkout button
  5. About - Brand story, values
  6. Contact - Contact form, details
  7. Error (404) - Error state styling

Research Findings: POD Theme Best Practices

What POD Sellers Actually Use

  • Free themes dominate: Dawn, Spotlight, Studio most popular
  • Premium choice: Streamline ($350) best for growing POD brands
  • Impulse is fashion-focused, not ideal POD default

Key Requirements for POD

  1. Large product imagery (mockups need to shine)
  2. Clean/minimal design (products are the focus)
  3. Quick setup (non-technical users)
  4. Mobile-responsive (60%+ traffic)
  5. Works with small catalogs
  6. No reliance on lifestyle photography

What to Avoid

  • Countdown timers (dark pattern)
  • Complex promotional systems
  • Hover-only interactions
  • Features requiring professional photography
  • Enterprise complexity

Phase 1: Core Theme System

  1. Create ThemeSettings schema and migrations
  2. Build CSS generator module
  3. Implement preset system
  4. Create basic LiveView editor with sidebar controls

Phase 2: Branding

  1. Logo upload with LiveView uploads
  2. SVG parsing and recolouring
  3. Header image with positioning controls

Phase 3: Preview System

  1. Live preview component
  2. Page switching (home/collection/product/etc.)
  3. Mock data for preview products

Phase 4: Persistence

  1. Save to shop record
  2. Apply to storefront templates
  3. CSS caching strategy

Prototype File

The complete working HTML prototype is available at:

  • /home/claude/theme-demo-v28.html
  • /mnt/user-data/outputs/theme-demo-v28.html

This 6,277-line file contains all CSS, HTML structure, and JavaScript logic that can be referenced when building the Phoenix/LiveView implementation.

Combination Count

With current options, the system offers:

  • 4 moods × 7 typographies × 4 shapes × 3 densities × 3 grids × 3 headers = 3,024 base combinations
  • Plus accent colours, layout width, button styles, shadows, etc.
  • Marketing claim: "100,000+ possible combinations"