feat: implement UX pattern improvements for accessibility & performance
Accessibility: - Add skip link component for keyboard navigation - Add visible focus rings on all interactive elements - Add aria-current="page" to navigation active states - Ensure 44px minimum touch targets on header icons and filter pills Product Page (PDP): - Add accordion layout for Description, Size Guide, Shipping & Returns - Convert Reviews section to accordion format (open by default) - Make Add to Basket button sticky on mobile, normal on desktop Product Cards (home & collection): - Add "Free delivery over £40" shipping badge - Add loading="lazy" and decoding="async" to images Cart Drawer: - Add "Delivery: Calculated at checkout" label - Add Remove button to each cart item All Preview Pages: - Add skip link to all 7 preview pages - Wrap main content in <main id="main-content"> - Pass active_page to shop_header for nav highlighting Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
0c43d65a04
commit
aa469ffb50
@ -105,3 +105,77 @@
|
||||
.preview-frame[data-mood="dark"], .shop-root[data-mood="dark"] {
|
||||
--t-accent-subtle: hsl(var(--t-accent-h) 30% 15%);
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
ACCESSIBILITY - Focus Rings
|
||||
Visible focus indicators for all interactive elements
|
||||
======================================== */
|
||||
|
||||
/* Base focus ring style */
|
||||
.shop-container a:focus-visible,
|
||||
.shop-container button:focus-visible,
|
||||
.shop-container input:focus-visible,
|
||||
.shop-container select:focus-visible,
|
||||
.shop-container textarea:focus-visible,
|
||||
.shop-container [tabindex]:focus-visible,
|
||||
.shop-container details summary:focus-visible {
|
||||
outline: 2px solid hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l));
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* Remove default browser outlines when using focus-visible */
|
||||
.shop-container a:focus:not(:focus-visible),
|
||||
.shop-container button:focus:not(:focus-visible),
|
||||
.shop-container input:focus:not(:focus-visible),
|
||||
.shop-container select:focus:not(:focus-visible),
|
||||
.shop-container textarea:focus:not(:focus-visible) {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* Skip link for keyboard navigation */
|
||||
.skip-link {
|
||||
position: absolute;
|
||||
top: -100px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l));
|
||||
color: var(--t-text-inverse);
|
||||
padding: 0.75rem 1.5rem;
|
||||
z-index: 9999;
|
||||
border-radius: var(--t-radius-button);
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
transition: top 0.2s ease;
|
||||
}
|
||||
|
||||
.skip-link:focus {
|
||||
top: 1rem;
|
||||
}
|
||||
|
||||
/* Ensure minimum touch target size (44x44px) */
|
||||
.shop-container .header-icon-btn {
|
||||
min-width: 44px;
|
||||
min-height: 44px;
|
||||
}
|
||||
|
||||
.shop-container .filter-pill {
|
||||
min-height: 44px;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
/* Nav link styling with active state indicator */
|
||||
.shop-nav a {
|
||||
padding: 0.5rem 0;
|
||||
border-bottom: 2px solid transparent;
|
||||
transition: border-color 0.2s ease, color 0.2s ease;
|
||||
}
|
||||
|
||||
.shop-nav a:hover {
|
||||
color: var(--t-text-primary);
|
||||
}
|
||||
|
||||
.shop-nav a[aria-current="page"] {
|
||||
color: var(--t-text-primary);
|
||||
border-bottom-color: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l));
|
||||
}
|
||||
|
||||
@ -3,6 +3,17 @@ defmodule SimpleshopThemeWeb.ThemeLive.PreviewPages do
|
||||
|
||||
embed_templates "preview_pages/*"
|
||||
|
||||
@doc """
|
||||
Renders the skip link for keyboard navigation accessibility.
|
||||
"""
|
||||
def skip_link(assigns) do
|
||||
~H"""
|
||||
<a href="#main-content" class="skip-link">
|
||||
Skip to main content
|
||||
</a>
|
||||
"""
|
||||
end
|
||||
|
||||
@doc """
|
||||
Renders the announcement bar.
|
||||
"""
|
||||
@ -25,6 +36,7 @@ defmodule SimpleshopThemeWeb.ThemeLive.PreviewPages do
|
||||
attr :theme_settings, :map, required: true
|
||||
attr :logo_image, :map, default: nil
|
||||
attr :header_image, :map, default: nil
|
||||
attr :active_page, :string, default: nil
|
||||
|
||||
def shop_header(assigns) do
|
||||
~H"""
|
||||
@ -76,10 +88,10 @@ defmodule SimpleshopThemeWeb.ThemeLive.PreviewPages do
|
||||
</div>
|
||||
|
||||
<nav class="shop-nav hidden md:flex" style="gap: 1.5rem; position: relative; z-index: 1;">
|
||||
<a href="#" phx-click="change_preview_page" phx-value-page="home" style="color: var(--t-text-secondary); text-decoration: none; cursor: pointer;">Home</a>
|
||||
<a href="#" phx-click="change_preview_page" phx-value-page="collection" style="color: var(--t-text-secondary); text-decoration: none; cursor: pointer;">Shop</a>
|
||||
<a href="#" phx-click="change_preview_page" phx-value-page="about" style="color: var(--t-text-secondary); text-decoration: none; cursor: pointer;">About</a>
|
||||
<a href="#" phx-click="change_preview_page" phx-value-page="contact" style="color: var(--t-text-secondary); text-decoration: none; cursor: pointer;">Contact</a>
|
||||
<a href="#" phx-click="change_preview_page" phx-value-page="home" aria-current={if @active_page == "home", do: "page"} style="color: var(--t-text-secondary); text-decoration: none; cursor: pointer;">Home</a>
|
||||
<a href="#" phx-click="change_preview_page" phx-value-page="collection" aria-current={if @active_page in ["collection", "pdp"], do: "page"} style="color: var(--t-text-secondary); text-decoration: none; cursor: pointer;">Shop</a>
|
||||
<a href="#" phx-click="change_preview_page" phx-value-page="about" aria-current={if @active_page == "about", do: "page"} style="color: var(--t-text-secondary); text-decoration: none; cursor: pointer;">About</a>
|
||||
<a href="#" phx-click="change_preview_page" phx-value-page="contact" aria-current={if @active_page == "contact", do: "page"} style="color: var(--t-text-secondary); text-decoration: none; cursor: pointer;">Contact</a>
|
||||
</nav>
|
||||
|
||||
<div class="shop-actions flex items-center gap-1" style="position: relative; z-index: 1;">
|
||||
@ -266,15 +278,24 @@ defmodule SimpleshopThemeWeb.ThemeLive.PreviewPages do
|
||||
<p style="font-family: var(--t-font-body); font-size: var(--t-text-caption); color: var(--t-text-tertiary); margin: 0;">
|
||||
<%= item.variant %>
|
||||
</p>
|
||||
<p class="cart-drawer-item-price" style="color: var(--t-text-primary); font-weight: 500; margin-top: 4px; font-size: var(--t-text-small);">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-top: 4px;">
|
||||
<p class="cart-drawer-item-price" style="color: var(--t-text-primary); font-weight: 500; font-size: var(--t-text-small); margin: 0;">
|
||||
<%= item.price %>
|
||||
</p>
|
||||
<button type="button" style="background: none; border: none; padding: 0; cursor: pointer; font-family: var(--t-font-body); font-size: var(--t-text-caption); color: var(--t-text-tertiary); text-decoration: underline;">
|
||||
Remove
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div class="cart-drawer-footer" style="padding: 1rem 1.5rem; border-top: 1px solid var(--t-border-default); background: var(--t-surface-sunken);">
|
||||
<div style="display: flex; justify-content: space-between; font-family: var(--t-font-body); font-size: var(--t-text-small); color: var(--t-text-secondary); margin-bottom: 0.5rem;">
|
||||
<span>Delivery</span>
|
||||
<span>Calculated at checkout</span>
|
||||
</div>
|
||||
<div class="cart-drawer-total" style="display: flex; justify-content: space-between; font-family: var(--t-font-body); font-size: var(--t-text-base); font-weight: 600; color: var(--t-text-primary); margin-bottom: 1rem;">
|
||||
<span>Subtotal</span>
|
||||
<span>£72.00</span>
|
||||
|
||||
@ -1,14 +1,17 @@
|
||||
<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);">
|
||||
<!-- Skip Link for Accessibility -->
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.skip_link />
|
||||
|
||||
<!-- 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} />
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.shop_header theme_settings={@theme_settings} logo_image={@logo_image} header_image={@header_image} active_page="about" />
|
||||
|
||||
<!-- Content Page -->
|
||||
<div class="content-page" style="background-color: var(--t-surface-base);">
|
||||
<main id="main-content" 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);">
|
||||
@ -60,7 +63,7 @@
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- Footer -->
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.shop_footer theme_settings={@theme_settings} />
|
||||
|
||||
@ -1,13 +1,16 @@
|
||||
<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);">
|
||||
<!-- Skip Link for Accessibility -->
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.skip_link />
|
||||
|
||||
<!-- 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} />
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.shop_header theme_settings={@theme_settings} logo_image={@logo_image} header_image={@header_image} active_page="cart" />
|
||||
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<main id="main-content" class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<h1 class="text-3xl md:text-4xl font-bold mb-8" style="font-family: var(--t-font-heading); color: var(--t-text-primary); font-weight: var(--t-heading-weight); letter-spacing: var(--t-heading-tracking);">
|
||||
Your basket
|
||||
</h1>
|
||||
@ -113,7 +116,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- Footer -->
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.shop_footer theme_settings={@theme_settings} />
|
||||
|
||||
@ -1,13 +1,17 @@
|
||||
<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);">
|
||||
<!-- Skip Link for Accessibility -->
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.skip_link />
|
||||
|
||||
<!-- 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} />
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.shop_header theme_settings={@theme_settings} logo_image={@logo_image} header_image={@header_image} active_page="collection" />
|
||||
|
||||
<!-- Page Header -->
|
||||
<main id="main-content">
|
||||
<div class="border-b" style="background-color: var(--t-surface-raised); border-color: var(--t-border-default);">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<h1 class="text-3xl md:text-4xl font-bold mb-2" style="font-family: var(--t-font-heading); color: var(--t-text-primary); font-weight: var(--t-heading-weight); letter-spacing: var(--t-heading-tracking);">
|
||||
@ -77,6 +81,8 @@
|
||||
<img
|
||||
src={product.image_url}
|
||||
alt={product.name}
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
class="product-image-primary w-full h-full object-cover transition-opacity duration-300"
|
||||
/>
|
||||
<!-- Hover Image -->
|
||||
@ -84,6 +90,8 @@
|
||||
<img
|
||||
src={product.hover_image_url}
|
||||
alt={product.name}
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
class="product-image-hover w-full h-full object-cover"
|
||||
/>
|
||||
<% end %>
|
||||
@ -111,11 +119,15 @@
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
<p class="text-xs mt-1" style="color: var(--t-text-tertiary);">
|
||||
Free delivery over £40
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- Footer -->
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.shop_footer theme_settings={@theme_settings} />
|
||||
|
||||
@ -1,13 +1,16 @@
|
||||
<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);">
|
||||
<!-- Skip Link for Accessibility -->
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.skip_link />
|
||||
|
||||
<!-- 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} />
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.shop_header theme_settings={@theme_settings} logo_image={@logo_image} header_image={@header_image} active_page="contact" />
|
||||
|
||||
<div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-16">
|
||||
<main id="main-content" 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);">
|
||||
Contact Us
|
||||
</h1>
|
||||
@ -180,7 +183,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- Footer -->
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.shop_footer theme_settings={@theme_settings} />
|
||||
|
||||
@ -1,13 +1,16 @@
|
||||
<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);">
|
||||
<!-- Skip Link for Accessibility -->
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.skip_link />
|
||||
|
||||
<!-- 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} />
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.shop_header theme_settings={@theme_settings} logo_image={@logo_image} header_image={@header_image} active_page="error" />
|
||||
|
||||
<div class="flex items-center justify-center" style="min-height: calc(100vh - 4rem);">
|
||||
<main id="main-content" class="flex items-center justify-center" style="min-height: calc(100vh - 4rem);">
|
||||
<div class="max-w-2xl mx-auto px-4 sm:px-6 lg:px-8 py-16 text-center">
|
||||
<h1 class="text-8xl md:text-9xl font-bold mb-4" style="font-family: var(--t-font-heading); color: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l)); font-weight: var(--t-heading-weight);">
|
||||
404
|
||||
@ -71,7 +74,7 @@
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- Footer -->
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.shop_footer theme_settings={@theme_settings} />
|
||||
|
||||
@ -1,13 +1,17 @@
|
||||
<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);">
|
||||
<!-- Skip Link for Accessibility -->
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.skip_link />
|
||||
|
||||
<!-- 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} />
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.shop_header theme_settings={@theme_settings} logo_image={@logo_image} header_image={@header_image} active_page="home" />
|
||||
|
||||
<!-- Hero Section -->
|
||||
<main id="main-content">
|
||||
<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);">
|
||||
Original designs, printed on demand
|
||||
@ -74,12 +78,16 @@
|
||||
<img
|
||||
src={product.image_url}
|
||||
alt={product.name}
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
class="product-image-primary w-full h-full object-cover transition-opacity duration-300"
|
||||
/>
|
||||
<%= if @theme_settings.hover_image && product[:hover_image_url] do %>
|
||||
<img
|
||||
src={product.hover_image_url}
|
||||
alt={product.name}
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
class="product-image-hover w-full h-full object-cover"
|
||||
/>
|
||||
<% end %>
|
||||
@ -96,6 +104,9 @@
|
||||
£<%= product.price / 100 %>
|
||||
</p>
|
||||
<% end %>
|
||||
<p class="text-xs mt-1" style="color: var(--t-text-tertiary);">
|
||||
Free delivery over £40
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
@ -137,6 +148,7 @@
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<!-- Footer -->
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.shop_footer theme_settings={@theme_settings} />
|
||||
|
||||
@ -2,15 +2,18 @@
|
||||
product = List.first(@preview_data.products)
|
||||
%>
|
||||
<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);">
|
||||
<!-- Skip Link for Accessibility -->
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.skip_link />
|
||||
|
||||
<!-- 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} />
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.shop_header theme_settings={@theme_settings} logo_image={@logo_image} header_image={@header_image} active_page="pdp" />
|
||||
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<main id="main-content" class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<!-- Breadcrumb -->
|
||||
<nav class="mb-8 flex items-center gap-2 text-sm" style="color: var(--t-text-secondary);">
|
||||
<a href="#" phx-click="change_preview_page" phx-value-page="home" class="hover:underline">Home</a>
|
||||
@ -157,10 +160,6 @@
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<p class="mb-8 leading-relaxed" style="color: var(--t-text-secondary);">
|
||||
<%= product.description %>. Crafted with attention to detail and quality materials, this product is designed to last. Perfect for everyday use or special occasions.
|
||||
</p>
|
||||
|
||||
<!-- Variant Options -->
|
||||
<div class="mb-6">
|
||||
<label class="block font-semibold mb-2" style="color: var(--t-text-primary);">
|
||||
@ -199,14 +198,16 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add to Cart -->
|
||||
<!-- Add to Cart - Sticky on mobile -->
|
||||
<div class="sticky bottom-0 z-10 py-3 md:relative md:py-0 -mx-4 px-4 md:mx-0 md:px-0 border-t md:border-0 mb-4" style="background-color: var(--t-surface-base); border-color: var(--t-border-subtle);">
|
||||
<button
|
||||
phx-click={Phoenix.LiveView.JS.add_class("open", to: "#cart-drawer") |> Phoenix.LiveView.JS.add_class("open", to: "#cart-drawer-overlay")}
|
||||
class="w-full px-6 py-4 text-lg font-semibold transition-all mb-4"
|
||||
class="w-full px-6 py-4 text-lg 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); cursor: pointer; border: none;"
|
||||
>
|
||||
Add to basket
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Features / Trust Badges -->
|
||||
<%= if @theme_settings.pdp_trust_badges do %>
|
||||
@ -231,28 +232,113 @@
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<!-- Product Info Accordion -->
|
||||
<div class="mt-8 divide-y" style="border-top: 1px solid var(--t-border-subtle); border-bottom: 1px solid var(--t-border-subtle); border-color: var(--t-border-subtle);">
|
||||
<!-- Description - open by default -->
|
||||
<details open class="group">
|
||||
<summary class="flex justify-between items-center py-4 cursor-pointer list-none [&::-webkit-details-marker]:hidden" style="color: var(--t-text-primary);">
|
||||
<span class="font-semibold">Description</span>
|
||||
<svg class="w-5 h-5 transition-transform duration-200 group-open:rotate-180" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</summary>
|
||||
<div class="pb-4 leading-relaxed" style="color: var(--t-text-secondary);">
|
||||
<p><%= product.description %>. Crafted with attention to detail and quality materials, this product is designed to last. Perfect for everyday use or special occasions.</p>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<!-- Size Guide - collapsed by default -->
|
||||
<details class="group">
|
||||
<summary class="flex justify-between items-center py-4 cursor-pointer list-none [&::-webkit-details-marker]:hidden" style="color: var(--t-text-primary);">
|
||||
<span class="font-semibold">Size Guide</span>
|
||||
<svg class="w-5 h-5 transition-transform duration-200 group-open:rotate-180" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</summary>
|
||||
<div class="pb-4" style="color: var(--t-text-secondary);">
|
||||
<table class="w-full text-sm">
|
||||
<thead>
|
||||
<tr style="border-bottom: 1px solid var(--t-border-subtle);">
|
||||
<th class="text-left py-2 font-semibold" style="color: var(--t-text-primary);">Size</th>
|
||||
<th class="text-left py-2 font-semibold" style="color: var(--t-text-primary);">Chest (cm)</th>
|
||||
<th class="text-left py-2 font-semibold" style="color: var(--t-text-primary);">Length (cm)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr style="border-bottom: 1px solid var(--t-border-subtle);">
|
||||
<td class="py-2">S</td>
|
||||
<td class="py-2">86-91</td>
|
||||
<td class="py-2">71</td>
|
||||
</tr>
|
||||
<tr style="border-bottom: 1px solid var(--t-border-subtle);">
|
||||
<td class="py-2">M</td>
|
||||
<td class="py-2">91-96</td>
|
||||
<td class="py-2">73</td>
|
||||
</tr>
|
||||
<tr style="border-bottom: 1px solid var(--t-border-subtle);">
|
||||
<td class="py-2">L</td>
|
||||
<td class="py-2">96-101</td>
|
||||
<td class="py-2">75</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="py-2">XL</td>
|
||||
<td class="py-2">101-106</td>
|
||||
<td class="py-2">77</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<!-- Shipping & Returns - collapsed by default -->
|
||||
<details class="group">
|
||||
<summary class="flex justify-between items-center py-4 cursor-pointer list-none [&::-webkit-details-marker]:hidden" style="color: var(--t-text-primary);">
|
||||
<span class="font-semibold">Shipping & Returns</span>
|
||||
<svg class="w-5 h-5 transition-transform duration-200 group-open:rotate-180" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</summary>
|
||||
<div class="pb-4 space-y-3" style="color: var(--t-text-secondary);">
|
||||
<div>
|
||||
<p class="font-semibold mb-1" style="color: var(--t-text-primary);">Delivery</p>
|
||||
<p class="text-sm">Free UK delivery on orders over £40. Standard delivery 3-5 working days. Express delivery available at checkout.</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="font-semibold mb-1" style="color: var(--t-text-primary);">Returns</p>
|
||||
<p class="text-sm">We offer a 30-day return policy. Items must be unused and in original packaging. Please contact us to arrange a return.</p>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Reviews Section -->
|
||||
<!-- Reviews Section - Accordion format, open by default for social proof -->
|
||||
<%= if @theme_settings.pdp_reviews do %>
|
||||
<div class="pdp-reviews py-12" style="border-top: 1px solid var(--t-border-default);">
|
||||
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-8">
|
||||
<details open class="pdp-reviews group" style="border-top: 1px solid var(--t-border-default);">
|
||||
<summary class="flex justify-between items-center py-6 cursor-pointer list-none [&::-webkit-details-marker]:hidden" style="color: var(--t-text-primary);">
|
||||
<div class="flex flex-col sm:flex-row sm:items-center gap-2 sm:gap-4">
|
||||
<h2 class="text-2xl font-bold" style="font-family: var(--t-font-heading); color: var(--t-text-primary); font-weight: var(--t-heading-weight);">
|
||||
Customer reviews
|
||||
</h2>
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="flex gap-0.5">
|
||||
<%= for _i <- 1..5 do %>
|
||||
<svg class="w-5 h-5" viewBox="0 0 20 20" style="color: #f59e0b;">
|
||||
<svg class="w-4 h-4" viewBox="0 0 20 20" style="color: #f59e0b;">
|
||||
<path d="M10 15l-5.878 3.09 1.123-6.545L.489 6.91l6.572-.955L10 0l2.939 5.955 6.572.955-4.756 4.635 1.123 6.545z" fill="currentColor"/>
|
||||
</svg>
|
||||
<% end %>
|
||||
</div>
|
||||
<span class="text-sm" style="color: var(--t-text-secondary);">Based on 24 reviews</span>
|
||||
<span class="text-sm" style="color: var(--t-text-secondary);">(24)</span>
|
||||
</div>
|
||||
</div>
|
||||
<svg class="w-5 h-5 transition-transform duration-200 group-open:rotate-180 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</summary>
|
||||
|
||||
<div class="pb-8">
|
||||
<div class="space-y-6">
|
||||
<!-- Review 1 -->
|
||||
<article class="pb-6" style="border-bottom: 1px solid var(--t-border-subtle);">
|
||||
@ -306,6 +392,7 @@
|
||||
Load more reviews
|
||||
</button>
|
||||
</div>
|
||||
</details>
|
||||
<% end %>
|
||||
|
||||
<!-- Related Products -->
|
||||
@ -328,12 +415,16 @@
|
||||
<img
|
||||
src={related_product.image_url}
|
||||
alt={related_product.name}
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
class="product-image-primary w-full h-full object-cover transition-opacity duration-300"
|
||||
/>
|
||||
<%= if @theme_settings.hover_image && related_product[:hover_image_url] do %>
|
||||
<img
|
||||
src={related_product.hover_image_url}
|
||||
alt={related_product.name}
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
class="product-image-hover w-full h-full object-cover"
|
||||
/>
|
||||
<% end %>
|
||||
@ -353,7 +444,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- Footer -->
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.shop_footer theme_settings={@theme_settings} />
|
||||
|
||||
Loading…
Reference in New Issue
Block a user