feat: enhance theme customization with layout controls and real product images
Add comprehensive layout and styling controls including header layout options (standard, centered, left), content width settings (contained, wide, full), and card shadow levels. Update all theme presets with these new settings. Replace placeholder images with real Unsplash product and category images for more realistic previews. Add announcement bar and sticky header toggle options for enhanced header customization. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
1ca703e548
commit
a8c0e150c8
@ -63,7 +63,7 @@
|
||||
.preview-frame[data-typography="editorial"] {
|
||||
--t-font-heading: var(--p-font-fraunces);
|
||||
--t-font-body: var(--p-font-source);
|
||||
--t-heading-weight: 600;
|
||||
--t-heading-weight: 700;
|
||||
--t-heading-tracking: -0.02em;
|
||||
}
|
||||
|
||||
@ -171,9 +171,23 @@
|
||||
display: flex;
|
||||
}
|
||||
|
||||
/* Standard header - logo left, nav center, cart right */
|
||||
.preview-frame[data-header="standard"] .shop-header {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
/* Centered header - logo on top, nav and cart on same row below */
|
||||
.preview-frame[data-header="centered"] .shop-header {
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 0.5rem 1.5rem;
|
||||
padding-top: 1.5rem;
|
||||
padding-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.preview-frame[data-header="centered"] .shop-logo {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@ -181,6 +195,126 @@
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.preview-frame[data-header="minimal"] .shop-nav {
|
||||
display: none !important;
|
||||
.preview-frame[data-header="centered"] .shop-cart {
|
||||
/* Cart flows inline with nav, no absolute positioning */
|
||||
}
|
||||
|
||||
/* Left header - logo and nav grouped left, cart right */
|
||||
.preview-frame[data-header="left"] .shop-header {
|
||||
justify-content: flex-start;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.preview-frame[data-header="left"] .shop-cart {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
/* Sticky header */
|
||||
.preview-frame[data-sticky="true"] .shop-header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 50;
|
||||
}
|
||||
|
||||
/* Layout Width */
|
||||
.preview-frame[data-layout="contained"] .max-w-7xl {
|
||||
max-width: 1024px;
|
||||
}
|
||||
|
||||
.preview-frame[data-layout="wide"] .max-w-7xl {
|
||||
max-width: 1280px;
|
||||
}
|
||||
|
||||
.preview-frame[data-layout="full"] .max-w-7xl {
|
||||
max-width: 100%;
|
||||
padding-left: 2rem;
|
||||
padding-right: 2rem;
|
||||
}
|
||||
|
||||
/* Card Shadow */
|
||||
.preview-frame[data-shadow="none"] .product-card,
|
||||
.preview-frame[data-shadow="none"] .category-card {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.preview-frame[data-shadow="sm"] .product-card,
|
||||
.preview-frame[data-shadow="sm"] .category-card {
|
||||
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.preview-frame[data-shadow="md"] .product-card,
|
||||
.preview-frame[data-shadow="md"] .category-card {
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.preview-frame[data-shadow="lg"] .product-card,
|
||||
.preview-frame[data-shadow="lg"] .category-card {
|
||||
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* Product Badges */
|
||||
.product-badge {
|
||||
position: absolute;
|
||||
top: 0.5rem;
|
||||
left: 0.5rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.025em;
|
||||
border-radius: var(--t-radius-sm, 4px);
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.badge-sale {
|
||||
background-color: var(--t-sale-color, #dc2626);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.badge-new {
|
||||
background-color: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l));
|
||||
color: var(--t-text-inverse);
|
||||
}
|
||||
|
||||
/* Product Hover Image */
|
||||
.product-image-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.product-image-hover {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.product-card:hover .product-image-hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.product-card:hover .product-image-primary {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* Social Links */
|
||||
.social-link:hover {
|
||||
background-color: var(--t-surface-sunken);
|
||||
color: var(--t-text-primary);
|
||||
}
|
||||
|
||||
/* Header Icon Buttons */
|
||||
.header-icon-btn:hover {
|
||||
background-color: var(--t-surface-sunken);
|
||||
color: var(--t-text-primary);
|
||||
}
|
||||
|
||||
/* Search Modal Animation */
|
||||
.search-modal {
|
||||
opacity: 1;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.search-modal-content {
|
||||
transform: translateY(0);
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
@ -3,9 +3,12 @@
|
||||
Semantic aliases for easy usage
|
||||
======================================== */
|
||||
|
||||
:root {
|
||||
.preview-frame, .shop-root {
|
||||
/* Accent color - HSL components set dynamically by CSS generator */
|
||||
/* Derived accent colors use the dynamic HSL values */
|
||||
--t-accent-h: 24;
|
||||
--t-accent-s: 95%;
|
||||
--t-accent-l: 53%;
|
||||
|
||||
--t-accent: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l));
|
||||
--t-accent-hover: hsl(var(--t-accent-h) var(--t-accent-s) calc(var(--t-accent-l) - 8%));
|
||||
--t-accent-subtle: hsl(var(--t-accent-h) 40% 95%);
|
||||
@ -15,8 +18,9 @@
|
||||
--t-secondary-accent: #ea580c;
|
||||
--t-sale-color: #dc2626;
|
||||
|
||||
/* Density multiplier */
|
||||
--t-density: 1;
|
||||
/* Font size scale */
|
||||
--t-font-size-scale: 1;
|
||||
--t-heading-weight: 600;
|
||||
|
||||
/* Layout */
|
||||
--t-layout-max-width: 1400px;
|
||||
@ -48,6 +52,14 @@
|
||||
--weight-heading: var(--t-heading-weight);
|
||||
--tracking-heading: var(--t-heading-tracking);
|
||||
|
||||
/* Density-aware spacing */
|
||||
--space-xs: calc(var(--p-space-2) * var(--t-density));
|
||||
--space-sm: calc(var(--p-space-3) * var(--t-density));
|
||||
--space-md: calc(var(--p-space-4) * var(--t-density));
|
||||
--space-lg: calc(var(--p-space-6) * var(--t-density));
|
||||
--space-xl: calc(var(--p-space-8) * var(--t-density));
|
||||
--space-2xl: calc(var(--p-space-12) * var(--t-density));
|
||||
|
||||
/* Border radius */
|
||||
--radius-button: var(--t-radius-button);
|
||||
--radius-card: var(--t-radius-card);
|
||||
@ -59,7 +71,7 @@
|
||||
0 1px 2px hsl(var(--p-shadow-color) / calc(var(--p-shadow-strength) * 0.5)),
|
||||
0 1px 3px hsl(var(--p-shadow-color) / var(--p-shadow-strength));
|
||||
--shadow-md:
|
||||
0 2px 4px hsl(var(--p-shadow-color) / calc(--p-shadow-strength) * 0.5)),
|
||||
0 2px 4px hsl(var(--p-shadow-color) / calc(var(--p-shadow-strength) * 0.5)),
|
||||
0 4px 8px hsl(var(--p-shadow-color) / var(--p-shadow-strength)),
|
||||
0 8px 16px hsl(var(--p-shadow-color) / calc(var(--p-shadow-strength) * 0.5));
|
||||
|
||||
@ -68,3 +80,8 @@
|
||||
--transition-normal: var(--p-duration-normal) var(--p-ease-out);
|
||||
--transition-bounce: var(--p-duration-normal) var(--p-ease-out-back);
|
||||
}
|
||||
|
||||
/* Dark mode accent-subtle override */
|
||||
.preview-frame[data-mood="dark"], .shop-root[data-mood="dark"] {
|
||||
--t-accent-subtle: hsl(var(--t-accent-h) 30% 15%);
|
||||
}
|
||||
|
||||
@ -97,11 +97,13 @@ defmodule SimpleshopTheme.Settings.ThemeSettings do
|
||||
|> validate_inclusion(:shape, ~w(sharp soft round pill))
|
||||
|> validate_inclusion(:density, ~w(spacious balanced compact))
|
||||
|> validate_inclusion(:grid_columns, ~w(2 3 4))
|
||||
|> validate_inclusion(:header_layout, ~w(standard centered minimal))
|
||||
|> validate_inclusion(:header_layout, ~w(standard centered left))
|
||||
|> validate_inclusion(:logo_mode, ~w(text-only logo-text logo-only))
|
||||
|> validate_number(:logo_size, greater_than_or_equal_to: 24, less_than_or_equal_to: 120)
|
||||
|> validate_number(:header_zoom, greater_than_or_equal_to: 100, less_than_or_equal_to: 200)
|
||||
|> validate_number(:header_position_x, greater_than_or_equal_to: 0, less_than_or_equal_to: 100)
|
||||
|> validate_number(:header_position_y, greater_than_or_equal_to: 0, less_than_or_equal_to: 100)
|
||||
|> validate_inclusion(:layout_width, ~w(contained wide full))
|
||||
|> validate_inclusion(:card_shadow, ~w(none sm md lg))
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
defmodule SimpleshopTheme.Theme.Presets do
|
||||
@moduledoc """
|
||||
Defines the 9 curated theme presets for SimpleShop.
|
||||
Defines the 8 curated theme presets for SimpleShop.
|
||||
"""
|
||||
|
||||
@presets %{
|
||||
@ -11,7 +11,10 @@ defmodule SimpleshopTheme.Theme.Presets do
|
||||
density: "spacious",
|
||||
grid_columns: "3",
|
||||
header_layout: "centered",
|
||||
accent_color: "#e85d04"
|
||||
accent_color: "#e85d04",
|
||||
layout_width: "wide",
|
||||
card_shadow: "sm",
|
||||
announcement_bar: true
|
||||
},
|
||||
studio: %{
|
||||
mood: "neutral",
|
||||
@ -20,7 +23,10 @@ defmodule SimpleshopTheme.Theme.Presets do
|
||||
density: "balanced",
|
||||
grid_columns: "4",
|
||||
header_layout: "standard",
|
||||
accent_color: "#3b82f6"
|
||||
accent_color: "#3b82f6",
|
||||
layout_width: "wide",
|
||||
card_shadow: "sm",
|
||||
announcement_bar: true
|
||||
},
|
||||
boutique: %{
|
||||
mood: "warm",
|
||||
@ -28,8 +34,11 @@ defmodule SimpleshopTheme.Theme.Presets do
|
||||
shape: "soft",
|
||||
density: "balanced",
|
||||
grid_columns: "3",
|
||||
header_layout: "centered",
|
||||
accent_color: "#b45309"
|
||||
header_layout: "left",
|
||||
accent_color: "#b45309",
|
||||
layout_width: "contained",
|
||||
card_shadow: "md",
|
||||
announcement_bar: true
|
||||
},
|
||||
bold: %{
|
||||
mood: "neutral",
|
||||
@ -38,7 +47,10 @@ defmodule SimpleshopTheme.Theme.Presets do
|
||||
density: "compact",
|
||||
grid_columns: "4",
|
||||
header_layout: "standard",
|
||||
accent_color: "#dc2626"
|
||||
accent_color: "#dc2626",
|
||||
layout_width: "full",
|
||||
card_shadow: "none",
|
||||
announcement_bar: true
|
||||
},
|
||||
playful: %{
|
||||
mood: "neutral",
|
||||
@ -47,16 +59,22 @@ defmodule SimpleshopTheme.Theme.Presets do
|
||||
density: "balanced",
|
||||
grid_columns: "4",
|
||||
header_layout: "standard",
|
||||
accent_color: "#8b5cf6"
|
||||
accent_color: "#8b5cf6",
|
||||
layout_width: "wide",
|
||||
card_shadow: "md",
|
||||
announcement_bar: true
|
||||
},
|
||||
minimal: %{
|
||||
mood: "cool",
|
||||
typography: "minimal",
|
||||
mood: "neutral",
|
||||
typography: "impulse",
|
||||
shape: "sharp",
|
||||
density: "spacious",
|
||||
grid_columns: "2",
|
||||
header_layout: "minimal",
|
||||
accent_color: "#171717"
|
||||
header_layout: "standard",
|
||||
accent_color: "#171717",
|
||||
layout_width: "full",
|
||||
card_shadow: "none",
|
||||
announcement_bar: false
|
||||
},
|
||||
night: %{
|
||||
mood: "dark",
|
||||
@ -65,7 +83,10 @@ defmodule SimpleshopTheme.Theme.Presets do
|
||||
density: "balanced",
|
||||
grid_columns: "4",
|
||||
header_layout: "standard",
|
||||
accent_color: "#f97316"
|
||||
accent_color: "#f97316",
|
||||
layout_width: "wide",
|
||||
card_shadow: "lg",
|
||||
announcement_bar: true
|
||||
},
|
||||
classic: %{
|
||||
mood: "warm",
|
||||
@ -74,22 +95,10 @@ defmodule SimpleshopTheme.Theme.Presets do
|
||||
density: "spacious",
|
||||
grid_columns: "3",
|
||||
header_layout: "standard",
|
||||
accent_color: "#166534"
|
||||
},
|
||||
impulse: %{
|
||||
mood: "neutral",
|
||||
typography: "impulse",
|
||||
shape: "sharp",
|
||||
density: "spacious",
|
||||
grid_columns: "3",
|
||||
header_layout: "centered",
|
||||
accent_color: "#000000",
|
||||
font_size: "medium",
|
||||
heading_weight: "regular",
|
||||
layout_width: "full",
|
||||
button_style: "filled",
|
||||
card_shadow: "none",
|
||||
product_text_align: "center"
|
||||
accent_color: "#166534",
|
||||
layout_width: "contained",
|
||||
card_shadow: "sm",
|
||||
announcement_bar: true
|
||||
}
|
||||
}
|
||||
|
||||
@ -99,14 +108,13 @@ defmodule SimpleshopTheme.Theme.Presets do
|
||||
boutique: "Warm & sophisticated",
|
||||
bold: "High contrast, strong",
|
||||
playful: "Fun & approachable",
|
||||
minimal: "Understated & modern",
|
||||
minimal: "Light & airy",
|
||||
night: "Dark & dramatic",
|
||||
classic: "Traditional & refined",
|
||||
impulse: "Light & airy"
|
||||
classic: "Traditional & refined"
|
||||
}
|
||||
|
||||
# Core keys used to match presets (excludes branding-specific settings)
|
||||
@core_keys ~w(mood typography shape density grid_columns header_layout accent_color)a
|
||||
@core_keys ~w(mood typography shape density grid_columns header_layout accent_color layout_width card_shadow announcement_bar)a
|
||||
|
||||
@doc """
|
||||
Returns all available presets.
|
||||
@ -141,7 +149,7 @@ defmodule SimpleshopTheme.Theme.Presets do
|
||||
## Examples
|
||||
|
||||
iex> list_names()
|
||||
[:gallery, :studio, :boutique, :bold, :playful, :minimal, :night, :classic, :impulse]
|
||||
[:gallery, :studio, :boutique, :bold, :playful, :minimal, :night, :classic]
|
||||
|
||||
"""
|
||||
def list_names do
|
||||
|
||||
@ -79,8 +79,8 @@ defmodule SimpleshopTheme.Theme.PreviewData do
|
||||
description: "Soft, breathable cotton tee perfect for everyday wear",
|
||||
price: 2999,
|
||||
compare_at_price: nil,
|
||||
image_url: "https://placehold.co/600x800/e5e5e5/525252?text=Cotton+Tee",
|
||||
hover_image_url: "https://placehold.co/600x800/d4d4d4/525252?text=Cotton+Tee",
|
||||
image_url: "https://images.unsplash.com/photo-1521572163474-6864f9cf17ab?w=600&h=800&fit=crop&q=80",
|
||||
hover_image_url: "https://images.unsplash.com/photo-1622445275576-721325763afe?w=600&h=800&fit=crop&q=80",
|
||||
category: "Clothing",
|
||||
in_stock: true,
|
||||
on_sale: false
|
||||
@ -91,8 +91,8 @@ defmodule SimpleshopTheme.Theme.PreviewData do
|
||||
description: "Handcrafted genuine leather bag with adjustable strap",
|
||||
price: 8999,
|
||||
compare_at_price: 11999,
|
||||
image_url: "https://placehold.co/600x800/d4a574/1c1917?text=Leather+Bag",
|
||||
hover_image_url: "https://placehold.co/600x800/c49563/1c1917?text=Leather+Bag",
|
||||
image_url: "https://images.unsplash.com/photo-1548036328-c9fa89d128fa?w=600&h=800&fit=crop&q=80",
|
||||
hover_image_url: "https://images.unsplash.com/photo-1590874103328-eac38a683ce7?w=600&h=800&fit=crop&q=80",
|
||||
category: "Accessories",
|
||||
in_stock: true,
|
||||
on_sale: true
|
||||
@ -103,8 +103,8 @@ defmodule SimpleshopTheme.Theme.PreviewData do
|
||||
description: "Handmade ceramic mug with unique glaze finish",
|
||||
price: 2499,
|
||||
compare_at_price: nil,
|
||||
image_url: "https://placehold.co/600x800/94a3b8/0f172a?text=Coffee+Mug",
|
||||
hover_image_url: "https://placehold.co/600x800/64748b/0f172a?text=Coffee+Mug",
|
||||
image_url: "https://images.unsplash.com/photo-1514228742587-6b1558fcca3d?w=600&h=800&fit=crop&q=80",
|
||||
hover_image_url: "https://images.unsplash.com/photo-1481833761820-0509d3217039?w=600&h=800&fit=crop&q=80",
|
||||
category: "Home",
|
||||
in_stock: true,
|
||||
on_sale: false
|
||||
@ -115,8 +115,8 @@ defmodule SimpleshopTheme.Theme.PreviewData do
|
||||
description: "Sleek design with Japanese quartz movement",
|
||||
price: 12999,
|
||||
compare_at_price: nil,
|
||||
image_url: "https://placehold.co/600x800/171717/fafafa?text=Watch",
|
||||
hover_image_url: "https://placehold.co/600x800/262626/fafafa?text=Watch",
|
||||
image_url: "https://images.unsplash.com/photo-1523275335684-37898b6baf30?w=600&h=800&fit=crop&q=80",
|
||||
hover_image_url: "https://images.unsplash.com/photo-1524592094714-0f0654e20314?w=600&h=800&fit=crop&q=80",
|
||||
category: "Accessories",
|
||||
in_stock: true,
|
||||
on_sale: false
|
||||
@ -127,8 +127,8 @@ defmodule SimpleshopTheme.Theme.PreviewData do
|
||||
description: "Cozy merino wool blanket in herringbone pattern",
|
||||
price: 7999,
|
||||
compare_at_price: 9999,
|
||||
image_url: "https://placehold.co/600x800/a3a3a3/171717?text=Blanket",
|
||||
hover_image_url: "https://placehold.co/600x800/8a8a8a/171717?text=Blanket",
|
||||
image_url: "https://images.unsplash.com/photo-1580301762395-21ce84d00bc6?w=600&h=800&fit=crop&q=80",
|
||||
hover_image_url: "https://images.unsplash.com/photo-1600369671854-2d9f5db4a5e0?w=600&h=800&fit=crop&q=80",
|
||||
category: "Home",
|
||||
in_stock: false,
|
||||
on_sale: true
|
||||
@ -139,8 +139,8 @@ defmodule SimpleshopTheme.Theme.PreviewData do
|
||||
description: "Natural handmade soaps with essential oils",
|
||||
price: 3499,
|
||||
compare_at_price: nil,
|
||||
image_url: "https://placehold.co/600x800/fdf8f3/1c1917?text=Soap+Set",
|
||||
hover_image_url: "https://placehold.co/600x800/f5ebe0/1c1917?text=Soap+Set",
|
||||
image_url: "https://images.unsplash.com/photo-1607006344380-b6775a0824a7?w=600&h=800&fit=crop&q=80",
|
||||
hover_image_url: "https://images.unsplash.com/photo-1600857544200-b2f666a9a2ec?w=600&h=800&fit=crop&q=80",
|
||||
category: "Beauty",
|
||||
in_stock: true,
|
||||
on_sale: false
|
||||
@ -151,8 +151,8 @@ defmodule SimpleshopTheme.Theme.PreviewData do
|
||||
description: "Classic cut denim jacket with vintage wash",
|
||||
price: 8499,
|
||||
compare_at_price: nil,
|
||||
image_url: "https://placehold.co/600x800/3b82f6/ffffff?text=Denim+Jacket",
|
||||
hover_image_url: "https://placehold.co/600x800/2563eb/ffffff?text=Denim+Jacket",
|
||||
image_url: "https://images.unsplash.com/photo-1576995853123-5a10305d93c0?w=600&h=800&fit=crop&q=80",
|
||||
hover_image_url: "https://images.unsplash.com/photo-1601333144130-8cbb312386b6?w=600&h=800&fit=crop&q=80",
|
||||
category: "Clothing",
|
||||
in_stock: true,
|
||||
on_sale: false
|
||||
@ -163,8 +163,8 @@ defmodule SimpleshopTheme.Theme.PreviewData do
|
||||
description: "Durable organic cotton canvas with reinforced handles",
|
||||
price: 2999,
|
||||
compare_at_price: nil,
|
||||
image_url: "https://placehold.co/600x800/f5f5f5/171717?text=Tote+Bag",
|
||||
hover_image_url: "https://placehold.co/600x800/e5e5e5/171717?text=Tote+Bag",
|
||||
image_url: "https://images.unsplash.com/photo-1622560480605-d83c853bc5c3?w=600&h=800&fit=crop&q=80",
|
||||
hover_image_url: "https://images.unsplash.com/photo-1597633125097-5a9ae3cb8a8f?w=600&h=800&fit=crop&q=80",
|
||||
category: "Accessories",
|
||||
in_stock: true,
|
||||
on_sale: false
|
||||
@ -175,8 +175,8 @@ defmodule SimpleshopTheme.Theme.PreviewData do
|
||||
description: "Soy wax candle with cedar and vanilla notes",
|
||||
price: 3299,
|
||||
compare_at_price: 3999,
|
||||
image_url: "https://placehold.co/600x800/fdf8f3/57534e?text=Candle",
|
||||
hover_image_url: "https://placehold.co/600x800/f5ebe0/57534e?text=Candle",
|
||||
image_url: "https://images.unsplash.com/photo-1602028915047-37269d1a73f7?w=600&h=800&fit=crop&q=80",
|
||||
hover_image_url: "https://images.unsplash.com/photo-1603006905003-be475563bc59?w=600&h=800&fit=crop&q=80",
|
||||
category: "Home",
|
||||
in_stock: true,
|
||||
on_sale: true
|
||||
@ -187,8 +187,8 @@ defmodule SimpleshopTheme.Theme.PreviewData do
|
||||
description: "Insulated bottle keeps drinks cold for 24 hours",
|
||||
price: 3999,
|
||||
compare_at_price: nil,
|
||||
image_url: "https://placehold.co/600x800/0a0a0a/fafafa?text=Water+Bottle",
|
||||
hover_image_url: "https://placehold.co/600x800/171717/fafafa?text=Water+Bottle",
|
||||
image_url: "https://images.unsplash.com/photo-1602143407151-7111542de6e8?w=600&h=800&fit=crop&q=80",
|
||||
hover_image_url: "https://images.unsplash.com/photo-1570831739435-6601aa3fa4fb?w=600&h=800&fit=crop&q=80",
|
||||
category: "Accessories",
|
||||
in_stock: true,
|
||||
on_sale: false
|
||||
@ -199,8 +199,8 @@ defmodule SimpleshopTheme.Theme.PreviewData do
|
||||
description: "Comfortable crew socks in solid colors",
|
||||
price: 1499,
|
||||
compare_at_price: nil,
|
||||
image_url: "https://placehold.co/600x800/e5e5e5/525252?text=Socks",
|
||||
hover_image_url: "https://placehold.co/600x800/d4d4d4/525252?text=Socks",
|
||||
image_url: "https://images.unsplash.com/photo-1586350977771-b3b0abd50c82?w=600&h=800&fit=crop&q=80",
|
||||
hover_image_url: "https://images.unsplash.com/photo-1582966772680-860e372bb558?w=600&h=800&fit=crop&q=80",
|
||||
category: "Clothing",
|
||||
in_stock: true,
|
||||
on_sale: false
|
||||
@ -211,8 +211,8 @@ defmodule SimpleshopTheme.Theme.PreviewData do
|
||||
description: "Sustainable bamboo with juice groove",
|
||||
price: 4499,
|
||||
compare_at_price: nil,
|
||||
image_url: "https://placehold.co/600x800/d4a574/1c1917?text=Cutting+Board",
|
||||
hover_image_url: "https://placehold.co/600x800/c49563/1c1917?text=Cutting+Board",
|
||||
image_url: "https://images.unsplash.com/photo-1594226801341-41427b4e5c22?w=600&h=800&fit=crop&q=80",
|
||||
hover_image_url: "https://images.unsplash.com/photo-1606760227091-3dd870d97f1d?w=600&h=800&fit=crop&q=80",
|
||||
category: "Kitchen",
|
||||
in_stock: true,
|
||||
on_sale: false
|
||||
@ -296,35 +296,35 @@ defmodule SimpleshopTheme.Theme.PreviewData do
|
||||
name: "Clothing",
|
||||
slug: "clothing",
|
||||
product_count: 3,
|
||||
image_url: "https://placehold.co/400x300/e5e5e5/525252?text=Clothing"
|
||||
image_url: "https://images.unsplash.com/photo-1489987707025-afc232f7ea0f?w=400&h=300&fit=crop&q=80"
|
||||
},
|
||||
%{
|
||||
id: "2",
|
||||
name: "Accessories",
|
||||
slug: "accessories",
|
||||
product_count: 4,
|
||||
image_url: "https://placehold.co/400x300/d4a574/1c1917?text=Accessories"
|
||||
image_url: "https://images.unsplash.com/photo-1606760227091-3dd870d97f1d?w=400&h=300&fit=crop&q=80"
|
||||
},
|
||||
%{
|
||||
id: "3",
|
||||
name: "Home",
|
||||
slug: "home",
|
||||
product_count: 3,
|
||||
image_url: "https://placehold.co/400x300/94a3b8/0f172a?text=Home"
|
||||
image_url: "https://images.unsplash.com/photo-1616046229478-9901c5536a45?w=400&h=300&fit=crop&q=80"
|
||||
},
|
||||
%{
|
||||
id: "4",
|
||||
name: "Kitchen",
|
||||
slug: "kitchen",
|
||||
product_count: 1,
|
||||
image_url: "https://placehold.co/400x300/fdf8f3/1c1917?text=Kitchen"
|
||||
image_url: "https://images.unsplash.com/photo-1556909114-f6e7ad7d3136?w=400&h=300&fit=crop&q=80"
|
||||
},
|
||||
%{
|
||||
id: "5",
|
||||
name: "Beauty",
|
||||
slug: "beauty",
|
||||
product_count: 1,
|
||||
image_url: "https://placehold.co/400x300/f5f5f5/171717?text=Beauty"
|
||||
image_url: "https://images.unsplash.com/photo-1596462502278-27bfdc403348?w=400&h=300&fit=crop&q=80"
|
||||
}
|
||||
]
|
||||
end
|
||||
|
||||
@ -17,7 +17,7 @@ defmodule SimpleshopThemeWeb do
|
||||
those modules here.
|
||||
"""
|
||||
|
||||
def static_paths, do: ~w(assets css fonts images favicon.ico robots.txt)
|
||||
def static_paths, do: ~w(assets css fonts images favicon.ico robots.txt demo.html)
|
||||
|
||||
def router do
|
||||
quote do
|
||||
|
||||
@ -201,6 +201,30 @@ defmodule SimpleshopThemeWeb.ThemeLive.Index do
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("toggle_setting", %{"field" => field}, socket) do
|
||||
field_atom = String.to_existing_atom(field)
|
||||
current_value = Map.get(socket.assigns.theme_settings, field_atom)
|
||||
attrs = %{field_atom => !current_value}
|
||||
|
||||
case Settings.update_theme_settings(attrs) do
|
||||
{:ok, theme_settings} ->
|
||||
generated_css = CSSGenerator.generate(theme_settings)
|
||||
active_preset = Presets.detect_preset(theme_settings)
|
||||
|
||||
socket =
|
||||
socket
|
||||
|> assign(:theme_settings, theme_settings)
|
||||
|> assign(:generated_css, generated_css)
|
||||
|> assign(:active_preset, active_preset)
|
||||
|
||||
{:noreply, socket}
|
||||
|
||||
{:error, _} ->
|
||||
{:noreply, socket}
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("save_theme", _params, socket) do
|
||||
socket = put_flash(socket, :info, "Theme saved successfully")
|
||||
|
||||
@ -466,7 +466,7 @@
|
||||
<div class="mb-4">
|
||||
<label class="block text-xs font-semibold uppercase tracking-wider text-base-content/60 mb-3">Header layout</label>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<%= for layout <- ["standard", "centered", "minimal"] do %>
|
||||
<%= for layout <- ["standard", "centered", "left"] do %>
|
||||
<button
|
||||
type="button"
|
||||
phx-click="update_setting"
|
||||
@ -485,6 +485,32 @@
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="flex items-center gap-3 cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={@theme_settings.announcement_bar}
|
||||
phx-click="toggle_setting"
|
||||
phx-value-field="announcement_bar"
|
||||
class="checkbox checkbox-sm"
|
||||
/>
|
||||
<span class="text-sm text-base-content">Announcement bar</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="flex items-center gap-3 cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={@theme_settings.sticky_header}
|
||||
phx-click="toggle_setting"
|
||||
phx-value-field="sticky_header"
|
||||
class="checkbox checkbox-sm"
|
||||
/>
|
||||
<span class="text-sm text-base-content">Sticky header</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Shape Group -->
|
||||
@ -518,6 +544,65 @@
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="block text-xs font-semibold uppercase tracking-wider text-base-content/60 mb-3">Card shadow</label>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<%= for {value, label} <- [{"none", "None"}, {"sm", "Subtle"}, {"md", "Medium"}, {"lg", "Strong"}] do %>
|
||||
<button
|
||||
type="button"
|
||||
phx-click="update_setting"
|
||||
phx-value-field="card_shadow"
|
||||
phx-value-setting_value={value}
|
||||
class={[
|
||||
"px-3 py-2 text-sm rounded-lg border-2 transition-all",
|
||||
if(@theme_settings.card_shadow == value,
|
||||
do: "border-base-content bg-base-100 text-base-content",
|
||||
else: "border-transparent bg-base-200 hover:bg-base-300 text-base-content"
|
||||
)
|
||||
]}
|
||||
>
|
||||
<%= label %>
|
||||
</button>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Layout Group -->
|
||||
<div class="mb-4">
|
||||
<div class="flex items-center gap-2 mb-4">
|
||||
<svg class="w-4 h-4 text-base-content/50" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<rect x="3" y="3" width="7" height="7"></rect>
|
||||
<rect x="14" y="3" width="7" height="7"></rect>
|
||||
<rect x="14" y="14" width="7" height="7"></rect>
|
||||
<rect x="3" y="14" width="7" height="7"></rect>
|
||||
</svg>
|
||||
<span class="text-sm font-semibold text-base-content">Layout</span>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="block text-xs font-semibold uppercase tracking-wider text-base-content/60 mb-3">Content width</label>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<%= for width <- ["contained", "wide", "full"] do %>
|
||||
<button
|
||||
type="button"
|
||||
phx-click="update_setting"
|
||||
phx-value-field="layout_width"
|
||||
phx-value-setting_value={width}
|
||||
class={[
|
||||
"px-3 py-2 text-sm rounded-lg border-2 transition-all capitalize",
|
||||
if(@theme_settings.layout_width == width,
|
||||
do: "border-base-content bg-base-100 text-base-content",
|
||||
else: "border-transparent bg-base-200 hover:bg-base-300 text-base-content"
|
||||
)
|
||||
]}
|
||||
>
|
||||
<%= width %>
|
||||
</button>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
@ -586,7 +671,10 @@
|
||||
data-shape={@theme_settings.shape}
|
||||
data-density={@theme_settings.density}
|
||||
data-grid={@theme_settings.grid_columns}
|
||||
data-header={@theme_settings.header_layout}>
|
||||
data-header={@theme_settings.header_layout}
|
||||
data-sticky={to_string(@theme_settings.sticky_header)}
|
||||
data-layout={@theme_settings.layout_width}
|
||||
data-shadow={@theme_settings.card_shadow}>
|
||||
<style>
|
||||
<%= Phoenix.HTML.raw(@generated_css) %>
|
||||
</style>
|
||||
|
||||
@ -3,6 +3,22 @@ defmodule SimpleshopThemeWeb.ThemeLive.PreviewPages do
|
||||
|
||||
embed_templates "preview_pages/*"
|
||||
|
||||
@doc """
|
||||
Renders the announcement bar.
|
||||
"""
|
||||
attr :theme_settings, :map, required: true
|
||||
|
||||
def announcement_bar(assigns) do
|
||||
~H"""
|
||||
<div
|
||||
class="announcement-bar"
|
||||
style="background-color: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l)); color: var(--t-text-inverse); text-align: center; padding: 0.5rem 1rem; font-size: 0.875rem;"
|
||||
>
|
||||
<p style="margin: 0;">Free shipping on orders over $50</p>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
@doc """
|
||||
Renders the shop header with logo based on logo_mode setting.
|
||||
"""
|
||||
@ -14,19 +30,12 @@ defmodule SimpleshopThemeWeb.ThemeLive.PreviewPages do
|
||||
~H"""
|
||||
<header
|
||||
class="shop-header"
|
||||
style={"position: relative; background-color: var(--t-surface-raised); border-bottom: 1px solid var(--t-border-default); padding: 1rem 2rem; display: flex; align-items: center; #{header_justify(@theme_settings.header_layout)};"}
|
||||
style="background-color: var(--t-surface-raised); border-bottom: 1px solid var(--t-border-default); padding: 1rem 2rem; display: flex; align-items: center;"
|
||||
>
|
||||
<%= if @theme_settings.header_background_enabled && @header_image do %>
|
||||
<div style={header_background_style(@theme_settings, @header_image)} />
|
||||
<% end %>
|
||||
|
||||
<%= if @theme_settings.header_layout == "centered" do %>
|
||||
<nav class="shop-nav hidden md:flex" style="gap: 1.5rem; position: relative; z-index: 1;">
|
||||
<a href="#" style="color: var(--t-text-secondary); text-decoration: none;">Home</a>
|
||||
<a href="#" style="color: var(--t-text-secondary); text-decoration: none;">Shop</a>
|
||||
</nav>
|
||||
<% end %>
|
||||
|
||||
<div class="shop-logo" style="display: flex; align-items: center; position: relative; z-index: 1;">
|
||||
<%= case @theme_settings.logo_mode do %>
|
||||
<% "text-only" -> %>
|
||||
@ -66,28 +75,44 @@ defmodule SimpleshopThemeWeb.ThemeLive.PreviewPages do
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<%= if @theme_settings.header_layout != "minimal" do %>
|
||||
<nav class={"shop-nav #{if @theme_settings.header_layout == "centered", do: "hidden md:flex", else: "hidden md:flex"}"} style="gap: 1.5rem; position: relative; z-index: 1;">
|
||||
<%= unless @theme_settings.header_layout == "centered" do %>
|
||||
<nav class="shop-nav hidden md:flex" style="gap: 1.5rem; position: relative; z-index: 1;">
|
||||
<a href="#" style="color: var(--t-text-secondary); text-decoration: none;">Home</a>
|
||||
<a href="#" style="color: var(--t-text-secondary); text-decoration: none;">Shop</a>
|
||||
<% end %>
|
||||
<a href="#" style="color: var(--t-text-secondary); text-decoration: none;">About</a>
|
||||
<a href="#" style="color: var(--t-text-secondary); text-decoration: none;">Contact</a>
|
||||
</nav>
|
||||
<% end %>
|
||||
|
||||
<div class="shop-cart" style="position: relative; z-index: 1;">
|
||||
<button style="color: var(--t-text-primary); background: none; border: none; cursor: pointer;">Cart (0)</button>
|
||||
<div class="shop-actions flex items-center gap-1" style="position: relative; z-index: 1;">
|
||||
<button
|
||||
type="button"
|
||||
class="header-icon-btn w-9 h-9 flex items-center justify-center transition-all"
|
||||
style="color: var(--t-text-secondary); background: none; border: none; cursor: pointer; border-radius: var(--t-radius-button);"
|
||||
phx-click={Phoenix.LiveView.JS.show(to: "#search-modal", display: "flex") |> Phoenix.LiveView.JS.focus(to: "#search-input")}
|
||||
aria-label="Search"
|
||||
>
|
||||
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<circle cx="11" cy="11" r="8"></circle>
|
||||
<path d="M21 21l-4.35-4.35"></path>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="header-icon-btn w-9 h-9 flex items-center justify-center transition-all"
|
||||
style="color: var(--t-text-secondary); background: none; border: none; cursor: pointer; border-radius: var(--t-radius-button);"
|
||||
aria-label="Cart"
|
||||
>
|
||||
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M6 2L3 6v14a2 2 0 002 2h14a2 2 0 002-2V6l-3-4z"></path>
|
||||
<line x1="3" y1="6" x2="21" y2="6"></line>
|
||||
<path d="M16 10a4 4 0 01-8 0"></path>
|
||||
</svg>
|
||||
<span class="sr-only">Cart (0)</span>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
"""
|
||||
end
|
||||
|
||||
defp header_justify("centered"), do: "justify-content: center; gap: 2rem;"
|
||||
defp header_justify("minimal"), do: "justify-content: space-between;"
|
||||
defp header_justify(_), do: "justify-content: space-between;"
|
||||
|
||||
defp logo_url(logo_image, %{logo_recolor: true, logo_color: color}) when logo_image.is_svg do
|
||||
clean_color = String.trim_leading(color, "#")
|
||||
"/images/#{logo_image.id}/recolored/#{clean_color}"
|
||||
@ -101,4 +126,154 @@ defmodule SimpleshopThemeWeb.ThemeLive.PreviewPages do
|
||||
"background-position: #{settings.header_position_x}% #{settings.header_position_y}%; " <>
|
||||
"background-repeat: no-repeat; z-index: 0;"
|
||||
end
|
||||
|
||||
@doc """
|
||||
Renders the shop footer with newsletter and links.
|
||||
"""
|
||||
attr :theme_settings, :map, required: true
|
||||
|
||||
def shop_footer(assigns) do
|
||||
~H"""
|
||||
<footer style="background-color: var(--t-surface-raised); border-top: 1px solid var(--t-border-default);">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-12">
|
||||
<!-- Newsletter -->
|
||||
<div>
|
||||
<h3 class="text-xl font-bold mb-2" style="font-family: var(--t-font-heading); color: var(--t-text-primary); font-weight: var(--t-heading-weight);">
|
||||
Join the studio
|
||||
</h3>
|
||||
<p class="mb-4 text-sm" style="color: var(--t-text-secondary);">
|
||||
Get 10% off your first order and be the first to know about new prints.
|
||||
</p>
|
||||
<form class="flex gap-2">
|
||||
<input
|
||||
type="email"
|
||||
placeholder="your@email.com"
|
||||
class="flex-1 px-4 py-2 text-sm"
|
||||
style="background-color: var(--t-surface-base); color: var(--t-text-primary); border: 1px solid var(--t-border-default); border-radius: var(--t-radius-input);"
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
class="px-6 py-2 font-medium transition-all text-sm"
|
||||
style="background-color: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l)); color: var(--t-text-inverse); border-radius: var(--t-radius-button);"
|
||||
>
|
||||
Subscribe
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Links -->
|
||||
<div class="grid grid-cols-2 gap-8">
|
||||
<div>
|
||||
<h4 class="font-semibold mb-4 text-sm" style="font-family: var(--t-font-heading); color: var(--t-text-primary);">
|
||||
Shop
|
||||
</h4>
|
||||
<ul class="space-y-2 text-sm">
|
||||
<li><a href="#" class="transition-colors hover:opacity-80" style="color: var(--t-text-secondary);">All products</a></li>
|
||||
<li><a href="#" class="transition-colors hover:opacity-80" style="color: var(--t-text-secondary);">New arrivals</a></li>
|
||||
<li><a href="#" class="transition-colors hover:opacity-80" style="color: var(--t-text-secondary);">Best sellers</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="font-semibold mb-4 text-sm" style="font-family: var(--t-font-heading); color: var(--t-text-primary);">
|
||||
Help
|
||||
</h4>
|
||||
<ul class="space-y-2 text-sm">
|
||||
<li><a href="#" class="transition-colors hover:opacity-80" style="color: var(--t-text-secondary);">Shipping</a></li>
|
||||
<li><a href="#" class="transition-colors hover:opacity-80" style="color: var(--t-text-secondary);">Returns</a></li>
|
||||
<li><a href="#" class="transition-colors hover:opacity-80" style="color: var(--t-text-secondary);">Contact</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bottom Bar -->
|
||||
<div class="mt-12 pt-8 flex flex-col md:flex-row justify-between items-center gap-4" style="border-top: 1px solid var(--t-border-subtle);">
|
||||
<p class="text-xs" style="color: var(--t-text-tertiary);">
|
||||
© 2025 <%= @theme_settings.site_name %>
|
||||
</p>
|
||||
<div class="flex gap-2">
|
||||
<a
|
||||
href="#"
|
||||
class="social-link w-9 h-9 flex items-center justify-center transition-all"
|
||||
style="color: var(--t-text-secondary); border-radius: var(--t-radius-button);"
|
||||
aria-label="Instagram"
|
||||
>
|
||||
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<rect x="2" y="2" width="20" height="20" rx="5" ry="5"></rect>
|
||||
<path d="M16 11.37A4 4 0 1 1 12.63 8 4 4 0 0 1 16 11.37z"></path>
|
||||
<line x1="17.5" y1="6.5" x2="17.51" y2="6.5"></line>
|
||||
</svg>
|
||||
</a>
|
||||
<a
|
||||
href="#"
|
||||
class="social-link w-9 h-9 flex items-center justify-center transition-all"
|
||||
style="color: var(--t-text-secondary); border-radius: var(--t-radius-button);"
|
||||
aria-label="Pinterest"
|
||||
>
|
||||
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<circle cx="12" cy="12" r="10"></circle>
|
||||
<path d="M8 12c0-2.2 1.8-4 4-4s4 1.8 4 4-1.8 4-4 4"></path>
|
||||
<line x1="12" y1="16" x2="9" y2="21"></line>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
"""
|
||||
end
|
||||
|
||||
@doc """
|
||||
Renders the search modal overlay.
|
||||
"""
|
||||
def search_modal(assigns) do
|
||||
~H"""
|
||||
<div
|
||||
id="search-modal"
|
||||
class="search-modal"
|
||||
style="position: fixed; inset: 0; background: rgba(0,0,0,0.5); z-index: 1001; display: none; align-items: flex-start; justify-content: center; padding-top: 10vh;"
|
||||
phx-click={Phoenix.LiveView.JS.hide(to: "#search-modal")}
|
||||
>
|
||||
<div
|
||||
class="search-modal-content w-full max-w-xl mx-4"
|
||||
style="background: var(--t-surface-raised); border-radius: var(--t-radius-card); overflow: hidden; box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);"
|
||||
phx-click-away={Phoenix.LiveView.JS.hide(to: "#search-modal")}
|
||||
>
|
||||
<div
|
||||
class="flex items-center gap-3 p-4"
|
||||
style="border-bottom: 1px solid var(--t-border-default);"
|
||||
>
|
||||
<svg class="w-5 h-5 flex-shrink-0" style="color: var(--t-text-tertiary);" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<circle cx="11" cy="11" r="8"></circle>
|
||||
<path d="M21 21l-4.35-4.35"></path>
|
||||
</svg>
|
||||
<input
|
||||
type="text"
|
||||
id="search-input"
|
||||
class="flex-1 text-lg bg-transparent border-none outline-none"
|
||||
style="font-family: var(--t-font-body); color: var(--t-text-primary);"
|
||||
placeholder="Search products..."
|
||||
phx-click={Phoenix.LiveView.JS.dispatch("stop-propagation")}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
class="w-8 h-8 flex items-center justify-center transition-all"
|
||||
style="color: var(--t-text-tertiary); background: none; border: none; cursor: pointer; border-radius: var(--t-radius-button);"
|
||||
phx-click={Phoenix.LiveView.JS.hide(to: "#search-modal")}
|
||||
aria-label="Close search"
|
||||
>
|
||||
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<line x1="18" y1="6" x2="6" y2="18"></line>
|
||||
<line x1="6" y1="6" x2="18" y2="18"></line>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="p-6" style="color: var(--t-text-tertiary);">
|
||||
<p class="text-sm">Try searching for "fern", "roses", or "botanical"</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,70 +1,66 @@
|
||||
<div class="min-h-screen" style="background-color: var(--t-surface-base); font-family: var(--t-font-body); color: var(--t-text-primary);">
|
||||
<div class="shop-container min-h-screen" style="position: relative; background-color: var(--t-surface-base); font-family: var(--t-font-body); color: var(--t-text-primary);">
|
||||
<!-- Announcement Bar -->
|
||||
<%= if @theme_settings.announcement_bar do %>
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.announcement_bar theme_settings={@theme_settings} />
|
||||
<% end %>
|
||||
|
||||
<!-- Header -->
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.shop_header theme_settings={@theme_settings} logo_image={@logo_image} header_image={@header_image} />
|
||||
|
||||
<div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-16">
|
||||
<h1 class="text-4xl md:text-5xl font-bold mb-6 text-center" style="font-family: var(--t-font-heading); color: var(--t-text-primary); font-weight: var(--t-heading-weight); letter-spacing: var(--t-heading-tracking);">
|
||||
About Us
|
||||
<!-- Content Page -->
|
||||
<div class="content-page" style="background-color: var(--t-surface-base);">
|
||||
<!-- Hero Section -->
|
||||
<div class="content-hero text-center" style="padding: var(--space-2xl) var(--space-lg); background-color: var(--t-surface-sunken);">
|
||||
<h1 class="text-3xl md:text-4xl mb-3" style="font-family: var(--t-font-heading); font-weight: var(--t-heading-weight); letter-spacing: var(--t-heading-tracking); color: var(--t-text-primary);">
|
||||
About the studio
|
||||
</h1>
|
||||
|
||||
<p class="text-lg mb-12 text-center max-w-2xl mx-auto" style="color: var(--t-text-secondary);">
|
||||
We're passionate about bringing you the finest products, handpicked with care and attention to detail.
|
||||
<p class="text-lg" style="color: var(--t-text-secondary);">
|
||||
Nature-inspired art, made with care
|
||||
</p>
|
||||
|
||||
<div class="aspect-video bg-gray-200 mb-12 overflow-hidden" style="border-radius: var(--t-radius-card);">
|
||||
<img
|
||||
src="https://placehold.co/1200x675/e5e5e5/525252?text=Our+Story"
|
||||
alt="Our Story"
|
||||
class="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="prose max-w-none mb-16">
|
||||
<h2 class="text-2xl font-bold mb-4" style="font-family: var(--t-font-heading); color: var(--t-text-primary); font-weight: var(--t-heading-weight);">
|
||||
Our Story
|
||||
<!-- Content Body -->
|
||||
<div class="content-body" style="padding: var(--space-xl) var(--space-lg); max-width: 800px; margin: 0 auto;">
|
||||
<!-- About Image -->
|
||||
<div
|
||||
class="content-image about-image"
|
||||
style="width: 100%; height: 300px; border-radius: var(--t-radius-image); margin-bottom: var(--space-lg); background-size: cover; background-position: center; background-image: url('https://picsum.photos/seed/studio/800/400');"
|
||||
></div>
|
||||
|
||||
<!-- Content Text -->
|
||||
<div class="content-text" style="line-height: 1.7;">
|
||||
<p class="lead-text text-lg mb-4" style="color: var(--t-text-primary);">
|
||||
Botanical Studio was born from a love of the natural world and a desire to bring its beauty indoors.
|
||||
</p>
|
||||
|
||||
<p class="mb-4" style="color: var(--t-text-secondary);">
|
||||
Every illustration starts as a pencil sketch, inspired by the plants and flowers found in British woodlands, meadows, and gardens. These sketches are then refined and printed on museum-quality archival paper using pigment-based inks that will last a lifetime.
|
||||
</p>
|
||||
|
||||
<p class="mb-4" style="color: var(--t-text-secondary);">
|
||||
Based in the Yorkshire countryside, I work from a small garden studio surrounded by the very nature that inspires each piece. Every print is checked by hand before being carefully packaged and sent on its way.
|
||||
</p>
|
||||
|
||||
<h2 class="mt-8 mb-3" style="font-family: var(--t-font-heading); font-weight: var(--t-heading-weight); font-size: var(--p-text-xl); color: var(--t-text-primary);">
|
||||
Sustainability
|
||||
</h2>
|
||||
<p class="mb-4 leading-relaxed" style="color: var(--t-text-secondary);">
|
||||
Founded in 2020, our journey began with a simple mission: to curate and deliver exceptional products that enhance everyday life. We believe that quality shouldn't be compromised, and that's why every item in our collection is carefully selected for its craftsmanship, sustainability, and timeless appeal.
|
||||
</p>
|
||||
<p class="mb-4 leading-relaxed" style="color: var(--t-text-secondary);">
|
||||
What started as a small passion project has grown into a community of like-minded individuals who appreciate the finer things in life. We work directly with artisans and makers who share our values of quality, authenticity, and ethical production.
|
||||
<p class="mb-4" style="color: var(--t-text-secondary);">
|
||||
I believe beautiful art shouldn't cost the earth. All prints are produced on FSC-certified paper, shipped in plastic-free packaging, and printed locally to reduce transport emissions.
|
||||
</p>
|
||||
|
||||
<h2 class="text-2xl font-bold mb-4 mt-8" style="font-family: var(--t-font-heading); color: var(--t-text-primary); font-weight: var(--t-heading-weight);">
|
||||
Our Values
|
||||
<h2 class="mt-8 mb-3" style="font-family: var(--t-font-heading); font-weight: var(--t-heading-weight); font-size: var(--p-text-xl); color: var(--t-text-primary);">
|
||||
The process
|
||||
</h2>
|
||||
<div class="grid gap-6 md:grid-cols-3 mb-8">
|
||||
<div class="p-6" style="background-color: var(--t-surface-raised); border: 1px solid var(--t-border-default); border-radius: var(--t-radius-card);">
|
||||
<h3 class="font-bold mb-2" style="color: var(--t-text-primary);">Quality First</h3>
|
||||
<p class="text-sm" style="color: var(--t-text-secondary);">
|
||||
Every product is vetted for exceptional quality and durability.
|
||||
</p>
|
||||
</div>
|
||||
<div class="p-6" style="background-color: var(--t-surface-raised); border: 1px solid var(--t-border-default); border-radius: var(--t-radius-card);">
|
||||
<h3 class="font-bold mb-2" style="color: var(--t-text-primary);">Sustainability</h3>
|
||||
<p class="text-sm" style="color: var(--t-text-secondary);">
|
||||
We prioritize eco-friendly materials and ethical production methods.
|
||||
</p>
|
||||
</div>
|
||||
<div class="p-6" style="background-color: var(--t-surface-raised); border: 1px solid var(--t-border-default); border-radius: var(--t-radius-card);">
|
||||
<h3 class="font-bold mb-2" style="color: var(--t-text-primary);">Community</h3>
|
||||
<p class="text-sm" style="color: var(--t-text-secondary);">
|
||||
Supporting local artisans and building lasting relationships.
|
||||
<p class="mb-4" style="color: var(--t-text-secondary);">
|
||||
Each botanical illustration takes between 20-40 hours to complete, from initial field sketches through to the final digital refinement. I work primarily in graphite and watercolour, which are then scanned and carefully colour-corrected to ensure the prints match the original artwork.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-center">
|
||||
<h2 class="text-2xl font-bold mb-6" style="font-family: var(--t-font-heading); color: var(--t-text-primary); font-weight: var(--t-heading-weight);">
|
||||
Ready to Explore?
|
||||
</h2>
|
||||
<button
|
||||
class="px-8 py-3 font-semibold transition-all"
|
||||
style="background-color: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l)); color: var(--t-text-inverse); border-radius: var(--t-radius-button);"
|
||||
>
|
||||
Shop Our Collection
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Footer -->
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.shop_footer theme_settings={@theme_settings} />
|
||||
|
||||
<!-- Search Modal -->
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.search_modal />
|
||||
</div>
|
||||
|
||||
@ -1,4 +1,9 @@
|
||||
<div class="min-h-screen" style="background-color: var(--t-surface-base); font-family: var(--t-font-body); color: var(--t-text-primary);">
|
||||
<div class="shop-container min-h-screen" style="position: relative; background-color: var(--t-surface-base); font-family: var(--t-font-body); color: var(--t-text-primary);">
|
||||
<!-- Announcement Bar -->
|
||||
<%= if @theme_settings.announcement_bar do %>
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.announcement_bar theme_settings={@theme_settings} />
|
||||
<% end %>
|
||||
|
||||
<!-- Header -->
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.shop_header theme_settings={@theme_settings} logo_image={@logo_image} header_image={@header_image} />
|
||||
|
||||
@ -109,4 +114,10 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.shop_footer theme_settings={@theme_settings} />
|
||||
|
||||
<!-- Search Modal -->
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.search_modal />
|
||||
</div>
|
||||
|
||||
@ -1,4 +1,9 @@
|
||||
<div class="min-h-screen" style="background-color: var(--t-surface-base); font-family: var(--t-font-body); color: var(--t-text-primary);">
|
||||
<div class="shop-container min-h-screen" style="position: relative; background-color: var(--t-surface-base); font-family: var(--t-font-body); color: var(--t-text-primary);">
|
||||
<!-- Announcement Bar -->
|
||||
<%= if @theme_settings.announcement_bar do %>
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.announcement_bar theme_settings={@theme_settings} />
|
||||
<% end %>
|
||||
|
||||
<!-- Header -->
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.shop_header theme_settings={@theme_settings} logo_image={@logo_image} header_image={@header_image} />
|
||||
|
||||
@ -109,25 +114,33 @@
|
||||
]}>
|
||||
<%= for product <- @preview_data.products do %>
|
||||
<div
|
||||
class="group overflow-hidden transition-all"
|
||||
class="product-card group overflow-hidden transition-all"
|
||||
style="background-color: var(--t-surface-raised); border: 1px solid var(--t-border-default); border-radius: var(--t-radius-card);"
|
||||
>
|
||||
<div class="aspect-square bg-gray-200 overflow-hidden relative">
|
||||
<div class="product-image-container aspect-square bg-gray-200 overflow-hidden relative">
|
||||
<!-- Product Badge -->
|
||||
<%= if product.on_sale do %>
|
||||
<div class="absolute top-2 right-2 px-2 py-1 text-xs font-bold text-white rounded" style="background-color: var(--t-sale-color);">
|
||||
SALE
|
||||
</div>
|
||||
<span class="product-badge badge-sale">Sale</span>
|
||||
<% end %>
|
||||
<%= if not product.in_stock do %>
|
||||
<div class="absolute inset-0 bg-black bg-opacity-50 flex items-center justify-center">
|
||||
<div class="absolute inset-0 bg-black bg-opacity-50 flex items-center justify-center z-20">
|
||||
<span class="text-white font-semibold">Out of Stock</span>
|
||||
</div>
|
||||
<% end %>
|
||||
<!-- Primary Image -->
|
||||
<img
|
||||
src={product.image_url}
|
||||
alt={product.name}
|
||||
class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
|
||||
class="product-image-primary w-full h-full object-cover transition-opacity duration-300"
|
||||
/>
|
||||
<!-- Hover Image -->
|
||||
<%= if product[:hover_image_url] do %>
|
||||
<img
|
||||
src={product.hover_image_url}
|
||||
alt={product.name}
|
||||
class="product-image-hover w-full h-full object-cover"
|
||||
/>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<p class="text-xs mb-1" style="color: var(--t-text-tertiary);">
|
||||
@ -153,10 +166,10 @@
|
||||
</div>
|
||||
<%= if product.in_stock do %>
|
||||
<button
|
||||
class="px-3 py-1.5 text-sm font-medium transition-all"
|
||||
class="quick-add-btn px-3 py-1.5 text-sm font-medium transition-all"
|
||||
style="background-color: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l)); color: var(--t-text-inverse); border-radius: var(--t-radius-button);"
|
||||
>
|
||||
Add
|
||||
Quick add
|
||||
</button>
|
||||
<% end %>
|
||||
</div>
|
||||
@ -167,4 +180,10 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.shop_footer theme_settings={@theme_settings} />
|
||||
|
||||
<!-- Search Modal -->
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.search_modal />
|
||||
</div>
|
||||
|
||||
@ -1,4 +1,9 @@
|
||||
<div class="min-h-screen" style="background-color: var(--t-surface-base); font-family: var(--t-font-body); color: var(--t-text-primary);">
|
||||
<div class="shop-container min-h-screen" style="position: relative; background-color: var(--t-surface-base); font-family: var(--t-font-body); color: var(--t-text-primary);">
|
||||
<!-- Announcement Bar -->
|
||||
<%= if @theme_settings.announcement_bar do %>
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.announcement_bar theme_settings={@theme_settings} />
|
||||
<% end %>
|
||||
|
||||
<!-- Header -->
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.shop_header theme_settings={@theme_settings} logo_image={@logo_image} header_image={@header_image} />
|
||||
|
||||
@ -124,4 +129,10 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.shop_footer theme_settings={@theme_settings} />
|
||||
|
||||
<!-- Search Modal -->
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.search_modal />
|
||||
</div>
|
||||
|
||||
@ -1,4 +1,9 @@
|
||||
<div class="min-h-screen" style="background-color: var(--t-surface-base); font-family: var(--t-font-body); color: var(--t-text-primary);">
|
||||
<div class="shop-container min-h-screen" style="position: relative; background-color: var(--t-surface-base); font-family: var(--t-font-body); color: var(--t-text-primary);">
|
||||
<!-- Announcement Bar -->
|
||||
<%= if @theme_settings.announcement_bar do %>
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.announcement_bar theme_settings={@theme_settings} />
|
||||
<% end %>
|
||||
|
||||
<!-- Header -->
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.shop_header theme_settings={@theme_settings} logo_image={@logo_image} header_image={@header_image} />
|
||||
|
||||
@ -35,7 +40,7 @@
|
||||
<div class="mt-12 grid gap-4 grid-cols-2 md:grid-cols-4 max-w-xl mx-auto">
|
||||
<%= for product <- Enum.take(@preview_data.products, 4) do %>
|
||||
<div
|
||||
class="group overflow-hidden"
|
||||
class="product-card group overflow-hidden"
|
||||
style="background-color: var(--t-surface-raised); border: 1px solid var(--t-border-subtle); border-radius: var(--t-radius-card);"
|
||||
>
|
||||
<div class="aspect-square bg-gray-200 overflow-hidden">
|
||||
@ -58,4 +63,10 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.shop_footer theme_settings={@theme_settings} />
|
||||
|
||||
<!-- Search Modal -->
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.search_modal />
|
||||
</div>
|
||||
|
||||
@ -1,145 +1,142 @@
|
||||
<div class="min-h-screen" style="background-color: var(--t-surface-base); font-family: var(--t-font-body); color: var(--t-text-primary);">
|
||||
<div class="shop-container min-h-screen" style="background-color: var(--t-surface-base); font-family: var(--t-font-body); color: var(--t-text-primary);">
|
||||
<!-- Announcement Bar -->
|
||||
<%= if @theme_settings.announcement_bar do %>
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.announcement_bar theme_settings={@theme_settings} />
|
||||
<% end %>
|
||||
|
||||
<!-- Header -->
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.shop_header theme_settings={@theme_settings} logo_image={@logo_image} header_image={@header_image} />
|
||||
|
||||
<!-- Hero Section -->
|
||||
<div class="relative" style="background-color: var(--t-surface-raised);">
|
||||
<div class="max-w-7xl mx-auto" style="padding: var(--space-2xl) var(--space-md);">
|
||||
<div class="text-center">
|
||||
<h1 class="text-4xl md:text-6xl font-bold mb-6" style="font-family: var(--t-font-heading); color: var(--t-text-primary); font-weight: var(--t-heading-weight); letter-spacing: var(--t-heading-tracking);">
|
||||
Welcome to Our Store
|
||||
<section class="text-center" style="padding: var(--space-2xl) var(--space-lg); background-color: var(--t-surface-base);">
|
||||
<h1 class="text-3xl md:text-4xl mb-4" style="font-family: var(--t-font-heading); font-weight: var(--t-heading-weight); letter-spacing: var(--t-heading-tracking); color: var(--t-text-primary);">
|
||||
Nature-inspired art prints
|
||||
</h1>
|
||||
<p class="text-lg md:text-xl mb-8 max-w-2xl mx-auto" style="color: var(--t-text-secondary);">
|
||||
Discover our curated collection of handpicked products crafted with care
|
||||
<p class="text-lg max-w-lg mx-auto mb-8" style="color: var(--t-text-secondary); line-height: 1.6;">
|
||||
Original botanical illustrations, printed on premium archival paper. Each piece brings a little bit of the outside, inside.
|
||||
</p>
|
||||
<button
|
||||
class="px-8 py-3 font-semibold transition-all"
|
||||
class="px-6 py-3 font-medium transition-all"
|
||||
style="background-color: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l)); color: var(--t-text-inverse); border-radius: var(--t-radius-button);"
|
||||
>
|
||||
Shop Now
|
||||
Shop the collection
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Categories -->
|
||||
<section style="padding: var(--space-xl) var(--space-lg); background-color: var(--t-surface-base);">
|
||||
<nav class="grid grid-cols-3 gap-4 max-w-3xl mx-auto" aria-label="Product categories">
|
||||
<%= for category <- Enum.take(@preview_data.categories, 3) do %>
|
||||
<a href="#" class="flex flex-col items-center gap-3 p-4 rounded-lg transition-colors hover:bg-black/5" style="text-decoration: none;">
|
||||
<div
|
||||
class="w-24 h-24 rounded-full bg-gray-200 bg-cover bg-center transition-transform hover:scale-105"
|
||||
style={"background-image: url('#{category.image_url}');"}
|
||||
></div>
|
||||
<span class="text-sm font-medium" style="font-family: var(--t-font-body); color: var(--t-text-primary);">
|
||||
<%= category.name %>
|
||||
</span>
|
||||
</a>
|
||||
<% end %>
|
||||
</nav>
|
||||
</section>
|
||||
|
||||
<!-- Featured Products -->
|
||||
<div class="max-w-7xl mx-auto" style="padding: var(--space-2xl) var(--space-md);">
|
||||
<h2 class="text-3xl font-bold mb-8" style="font-family: var(--t-font-heading); color: var(--t-text-primary); font-weight: var(--t-heading-weight);">
|
||||
Featured Products
|
||||
<section style="padding: var(--space-xl) var(--space-lg); background-color: var(--t-surface-sunken);">
|
||||
<h2 class="text-2xl mb-6" style="font-family: var(--t-font-heading); font-weight: var(--t-heading-weight); letter-spacing: var(--t-heading-tracking); color: var(--t-text-primary);">
|
||||
Featured prints
|
||||
</h2>
|
||||
|
||||
<div class={[
|
||||
"grid grid-cols-1 sm:grid-cols-2",
|
||||
"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
|
||||
]} style="gap: var(--space-lg);">
|
||||
<%= for product <- Enum.take(@preview_data.products, 6) do %>
|
||||
]}>
|
||||
<%= for product <- Enum.take(@preview_data.products, 4) do %>
|
||||
<div
|
||||
class="group overflow-hidden transition-all"
|
||||
style="background-color: var(--t-surface-raised); border: 1px solid var(--t-border-default); border-radius: var(--t-radius-card);"
|
||||
class="product-card group overflow-hidden transition-all hover:-translate-y-1"
|
||||
style="background-color: var(--t-surface-raised); border-radius: var(--t-radius-card);"
|
||||
>
|
||||
<div class="aspect-square bg-gray-200 overflow-hidden">
|
||||
<div class="product-image-container aspect-square bg-gray-200 overflow-hidden relative">
|
||||
<%= if product[:is_new] do %>
|
||||
<span class="product-badge badge-new">New</span>
|
||||
<% end %>
|
||||
<%= if product.on_sale do %>
|
||||
<span class="product-badge badge-sale">Sale</span>
|
||||
<% end %>
|
||||
<img
|
||||
src={product.image_url}
|
||||
alt={product.name}
|
||||
class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
|
||||
class="product-image-primary w-full h-full object-cover transition-opacity duration-300"
|
||||
/>
|
||||
<%= if product[:hover_image_url] do %>
|
||||
<img
|
||||
src={product.hover_image_url}
|
||||
alt={product.name}
|
||||
class="product-image-hover w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
<div style="padding: var(--space-md);">
|
||||
<h3 class="font-semibold" style="font-family: var(--t-font-heading); color: var(--t-text-primary); margin-bottom: var(--space-xs);">
|
||||
<%= product.name %>
|
||||
</h3>
|
||||
<p class="text-sm mb-3" style="color: var(--t-text-secondary);">
|
||||
<%= product.description %>
|
||||
</p>
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<%= if product.on_sale do %>
|
||||
<span class="text-lg font-bold" style="color: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l));">
|
||||
$<%= product.price / 100 %>
|
||||
</span>
|
||||
<span class="text-sm line-through ml-2" style="color: var(--t-text-tertiary);">
|
||||
$<%= product.compare_at_price / 100 %>
|
||||
</span>
|
||||
<% else %>
|
||||
<span class="text-lg font-bold" style="color: var(--t-text-primary);">
|
||||
$<%= product.price / 100 %>
|
||||
</span>
|
||||
<% end %>
|
||||
</div>
|
||||
<button
|
||||
class="px-4 py-2 text-sm font-medium transition-all"
|
||||
style="background-color: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l)); color: var(--t-text-inverse); border-radius: var(--t-radius-button);"
|
||||
class="absolute bottom-2 left-2 right-2 px-3 py-2 text-sm font-medium opacity-0 translate-y-2 group-hover:opacity-100 group-hover:translate-y-0 transition-all"
|
||||
style="background-color: var(--t-surface-raised); color: var(--t-text-primary); border-radius: var(--t-radius-button);"
|
||||
>
|
||||
Add to Cart
|
||||
Quick add
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Testimonials -->
|
||||
<div class="py-16" style="background-color: var(--t-surface-sunken);">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<h2 class="text-3xl font-bold mb-12 text-center" style="font-family: var(--t-font-heading); color: var(--t-text-primary); font-weight: var(--t-heading-weight);">
|
||||
What Our Customers Say
|
||||
</h2>
|
||||
|
||||
<div class="grid gap-6 grid-cols-1 md:grid-cols-3">
|
||||
<%= for testimonial <- Enum.take(@preview_data.testimonials, 3) do %>
|
||||
<div class="p-6" style="background-color: var(--t-surface-base); border: 1px solid var(--t-border-default); border-radius: var(--t-radius-card);">
|
||||
<div class="flex mb-3">
|
||||
<%= for _ <- 1..testimonial.rating do %>
|
||||
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" style="color: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l));">
|
||||
<path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" />
|
||||
</svg>
|
||||
<% end %>
|
||||
</div>
|
||||
<p class="mb-4" style="color: var(--t-text-primary);">
|
||||
"<%= testimonial.content %>"
|
||||
</p>
|
||||
<p class="font-semibold" style="color: var(--t-text-secondary);">
|
||||
— <%= testimonial.author %>
|
||||
</p>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Categories -->
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-16">
|
||||
<h2 class="text-3xl font-bold mb-8" style="font-family: var(--t-font-heading); color: var(--t-text-primary); font-weight: var(--t-heading-weight);">
|
||||
Shop by Category
|
||||
</h2>
|
||||
|
||||
<div class="grid gap-6 grid-cols-2 md:grid-cols-3 lg:grid-cols-5">
|
||||
<%= for category <- @preview_data.categories do %>
|
||||
<div
|
||||
class="group cursor-pointer overflow-hidden"
|
||||
style="border-radius: var(--t-radius-card);"
|
||||
>
|
||||
<div class="aspect-square bg-gray-200 overflow-hidden mb-3">
|
||||
<img
|
||||
src={category.image_url}
|
||||
alt={category.name}
|
||||
class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
|
||||
/>
|
||||
</div>
|
||||
<h3 class="font-semibold text-center" style="font-family: var(--t-font-heading); color: var(--t-text-primary);">
|
||||
<%= category.name %>
|
||||
<div style="padding: var(--space-md);">
|
||||
<h3 class="text-sm font-medium mb-1" style="color: var(--t-text-primary);">
|
||||
<%= product.name %>
|
||||
</h3>
|
||||
<p class="text-sm text-center" style="color: var(--t-text-tertiary);">
|
||||
<%= category.product_count %> products
|
||||
<p class="text-sm" style="color: var(--t-text-secondary);">
|
||||
<%= if product.on_sale do %>
|
||||
<span class="line-through mr-1" style="color: var(--t-text-tertiary);">$<%= product.compare_at_price / 100 %></span>
|
||||
<% end %>
|
||||
$<%= product.price / 100 %>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div class="text-center mt-8">
|
||||
<button
|
||||
class="px-6 py-3 font-medium transition-all"
|
||||
style="background-color: transparent; color: var(--t-text-primary); border: 1px solid var(--t-text-primary); border-radius: var(--t-radius-button);"
|
||||
>
|
||||
View all products
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- About Section -->
|
||||
<section class="grid grid-cols-1 md:grid-cols-2 gap-12 items-center" style="padding: var(--space-2xl) var(--space-lg); background-color: var(--t-surface-base);">
|
||||
<div
|
||||
class="h-72 rounded-lg bg-cover bg-center"
|
||||
style="background-image: url('https://picsum.photos/seed/studio/600/400'); border-radius: var(--t-radius-image);"
|
||||
></div>
|
||||
<div>
|
||||
<h2 class="text-2xl mb-4" style="font-family: var(--t-font-heading); font-weight: var(--t-heading-weight); letter-spacing: var(--t-heading-tracking); color: var(--t-text-primary);">
|
||||
Made with care in Yorkshire
|
||||
</h2>
|
||||
<p class="text-base mb-4" style="color: var(--t-text-secondary); line-height: 1.7;">
|
||||
Every illustration starts as a pencil sketch, inspired by the plants and flowers found in British woodlands, meadows, and gardens. Printed on museum-quality archival paper using pigment-based inks that will last a lifetime.
|
||||
</p>
|
||||
<a
|
||||
href="#"
|
||||
class="text-sm font-medium transition-colors"
|
||||
style="color: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l)); text-decoration: none;"
|
||||
>
|
||||
Learn more about the studio →
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Footer -->
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.shop_footer theme_settings={@theme_settings} />
|
||||
|
||||
<!-- Search Modal -->
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.search_modal />
|
||||
</div>
|
||||
|
||||
@ -1,7 +1,12 @@
|
||||
<%
|
||||
product = List.first(@preview_data.products)
|
||||
%>
|
||||
<div class="min-h-screen" style="background-color: var(--t-surface-base); font-family: var(--t-font-body); color: var(--t-text-primary);">
|
||||
<div class="shop-container min-h-screen" style="position: relative; background-color: var(--t-surface-base); font-family: var(--t-font-body); color: var(--t-text-primary);">
|
||||
<!-- Announcement Bar -->
|
||||
<%= if @theme_settings.announcement_bar do %>
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.announcement_bar theme_settings={@theme_settings} />
|
||||
<% end %>
|
||||
|
||||
<!-- Header -->
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.shop_header theme_settings={@theme_settings} logo_image={@logo_image} header_image={@header_image} />
|
||||
|
||||
@ -145,7 +150,7 @@
|
||||
<div class="grid gap-6 grid-cols-2 md:grid-cols-4">
|
||||
<%= for related_product <- Enum.slice(@preview_data.products, 1, 4) do %>
|
||||
<div
|
||||
class="group overflow-hidden"
|
||||
class="product-card group overflow-hidden"
|
||||
style="background-color: var(--t-surface-raised); border: 1px solid var(--t-border-default); border-radius: var(--t-radius-card);"
|
||||
>
|
||||
<div class="aspect-square bg-gray-200 overflow-hidden">
|
||||
@ -168,4 +173,10 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.shop_footer theme_settings={@theme_settings} />
|
||||
|
||||
<!-- Search Modal -->
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.search_modal />
|
||||
</div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user