extract product.ex inline styles to CSS component classes (Phase 2)
Move ~80 inline style= attributes from product.ex into ~40 CSS classes in @layer components. Only genuinely dynamic values (hex colours, background-image URLs) remain as inline styles. Pre-declare CSS layer order in shop_root.html.heex so reset < components in the cascade. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
fcd1b1ce80
commit
2af2d782d5
@ -1,11 +1,327 @@
|
||||
/* Component styles — extracted from inline styles in later phases.
|
||||
/* Component styles — extracted from inline styles in product.ex, layout.ex, etc.
|
||||
Each component gets its own section. */
|
||||
|
||||
@layer components {
|
||||
/* Phase 2: product cards, grid, badges, hero, categories */
|
||||
/* Phase 2: PDP, variant selector, gallery, accordion */
|
||||
/* Phase 3: layout components (header, footer, nav, search) */
|
||||
/* Phase 3: cart components (drawer, items, summary) */
|
||||
/* Phase 4: content components (contact, reviews, newsletter) */
|
||||
/* Phase 4: page templates (checkout success, etc.) */
|
||||
/* ── Shared heading treatment ──
|
||||
font-family + weight + tracking + colour used across
|
||||
hero titles, section headings, collection headers, PDP, etc. */
|
||||
|
||||
.t-heading {
|
||||
font-family: var(--t-font-heading);
|
||||
font-weight: var(--t-heading-weight);
|
||||
letter-spacing: var(--t-heading-tracking);
|
||||
color: var(--t-text-primary);
|
||||
}
|
||||
|
||||
/* ── Product card ── */
|
||||
|
||||
.product-card {
|
||||
background-color: var(--t-surface-raised);
|
||||
border-radius: var(--t-radius-card);
|
||||
|
||||
&[data-variant="default"],
|
||||
&[data-variant="compact"] {
|
||||
border: 1px solid var(--t-border-default);
|
||||
}
|
||||
|
||||
&[data-variant="minimal"] {
|
||||
border: 1px solid var(--t-border-subtle);
|
||||
}
|
||||
|
||||
&[data-variant="default"],
|
||||
&[data-variant="featured"] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&[data-clickable] {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
& .stretched-link {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.product-card-image-wrap {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.product-card-placeholder {
|
||||
color: var(--t-text-tertiary);
|
||||
}
|
||||
|
||||
.product-card-category {
|
||||
color: var(--t-text-tertiary);
|
||||
text-decoration: none;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.product-card-title {
|
||||
color: var(--t-text-primary);
|
||||
}
|
||||
|
||||
.product-card[data-variant="default"] .product-card-title,
|
||||
.product-card[data-variant="compact"] .product-card-title {
|
||||
font-family: var(--t-font-heading);
|
||||
}
|
||||
|
||||
.product-card-delivery {
|
||||
color: var(--t-text-tertiary);
|
||||
}
|
||||
|
||||
/* ── Product prices (shared between cards and PDP) ── */
|
||||
|
||||
.product-price--sale {
|
||||
color: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l));
|
||||
}
|
||||
|
||||
.product-price--compare {
|
||||
color: var(--t-text-tertiary);
|
||||
}
|
||||
|
||||
.product-price--regular {
|
||||
color: var(--t-text-primary);
|
||||
}
|
||||
|
||||
.product-price--secondary {
|
||||
color: var(--t-text-secondary);
|
||||
}
|
||||
|
||||
.sale-badge {
|
||||
background-color: var(--t-sale-color);
|
||||
}
|
||||
|
||||
/* ── Hero section ── */
|
||||
|
||||
.hero-section {
|
||||
padding: var(--space-2xl) var(--space-lg);
|
||||
|
||||
&[data-background="base"] {
|
||||
background-color: var(--t-surface-base);
|
||||
}
|
||||
|
||||
&[data-background="sunken"] {
|
||||
background-color: var(--t-surface-sunken);
|
||||
}
|
||||
}
|
||||
|
||||
.hero-section--page {
|
||||
padding-top: var(--space-2xl);
|
||||
}
|
||||
|
||||
.hero-pre-title {
|
||||
font-family: var(--t-font-heading);
|
||||
font-weight: var(--t-heading-weight);
|
||||
color: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l));
|
||||
}
|
||||
|
||||
.hero-description {
|
||||
color: var(--t-text-secondary);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* ── Category nav ── */
|
||||
|
||||
.category-nav-section {
|
||||
padding: var(--space-xl) var(--space-lg);
|
||||
background-color: var(--t-surface-base);
|
||||
}
|
||||
|
||||
.category-card {
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.category-name {
|
||||
font-family: var(--t-font-body);
|
||||
color: var(--t-text-primary);
|
||||
}
|
||||
|
||||
/* ── Featured products section ── */
|
||||
|
||||
.featured-section {
|
||||
padding: var(--space-xl) var(--space-lg);
|
||||
background-color: var(--t-surface-sunken);
|
||||
}
|
||||
|
||||
.outline-button {
|
||||
background-color: transparent;
|
||||
color: var(--t-text-primary);
|
||||
border: 1px solid var(--t-text-primary);
|
||||
border-radius: var(--t-radius-button);
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* ── Image + text section ── */
|
||||
|
||||
.image-text-section {
|
||||
padding: var(--space-2xl) var(--space-lg);
|
||||
background-color: var(--t-surface-base);
|
||||
}
|
||||
|
||||
.image-text-image {
|
||||
border-radius: var(--t-radius-image);
|
||||
}
|
||||
|
||||
.image-text-body {
|
||||
color: var(--t-text-secondary);
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.accent-link {
|
||||
color: var(--t-accent-text, hsl(var(--t-accent-h) var(--t-accent-s) 38%));
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* ── Collection header ── */
|
||||
|
||||
.collection-header-wrap {
|
||||
background-color: var(--t-surface-raised);
|
||||
border-color: var(--t-border-default);
|
||||
}
|
||||
|
||||
.collection-header-meta {
|
||||
color: var(--t-text-secondary);
|
||||
}
|
||||
|
||||
/* ── Breadcrumb ── */
|
||||
|
||||
.breadcrumb {
|
||||
color: var(--t-text-secondary);
|
||||
|
||||
& [aria-current="page"] {
|
||||
color: var(--t-text-primary);
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Related products ── */
|
||||
|
||||
.related-section {
|
||||
border-top: 1px solid var(--t-border-default);
|
||||
}
|
||||
|
||||
/* ── PDP gallery ── */
|
||||
|
||||
.pdp-gallery-frame {
|
||||
border-radius: var(--t-radius-image);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.pdp-thumbnail {
|
||||
border-radius: var(--t-radius-image);
|
||||
}
|
||||
|
||||
/* ── Variant selector ── */
|
||||
|
||||
.variant-label {
|
||||
color: var(--t-text-primary);
|
||||
}
|
||||
|
||||
.variant-label-value {
|
||||
color: var(--t-text-secondary);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.color-swatch {
|
||||
border-color: var(--t-border-default);
|
||||
|
||||
&[aria-pressed="true"] {
|
||||
border-color: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l));
|
||||
--tw-ring-color: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l));
|
||||
}
|
||||
}
|
||||
|
||||
.size-btn {
|
||||
border: 2px solid var(--t-border-default);
|
||||
border-radius: var(--t-radius-button);
|
||||
color: var(--t-text-primary);
|
||||
background: transparent;
|
||||
|
||||
&[aria-pressed="true"] {
|
||||
border-color: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l));
|
||||
background: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l) / 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Quantity selector ── */
|
||||
|
||||
.qty-label {
|
||||
color: var(--t-text-primary);
|
||||
}
|
||||
|
||||
.qty-group {
|
||||
border: 2px solid var(--t-border-default);
|
||||
border-radius: var(--t-radius-input);
|
||||
}
|
||||
|
||||
.qty-btn {
|
||||
color: var(--t-text-primary);
|
||||
}
|
||||
|
||||
.qty-display {
|
||||
border-color: var(--t-border-default);
|
||||
color: var(--t-text-primary);
|
||||
}
|
||||
|
||||
.stock-in {
|
||||
color: var(--t-text-tertiary);
|
||||
}
|
||||
|
||||
.stock-out {
|
||||
color: var(--t-sale-color);
|
||||
}
|
||||
|
||||
/* ── Add to cart ── */
|
||||
|
||||
.atc-wrap {
|
||||
background-color: var(--t-surface-base);
|
||||
border-color: var(--t-border-subtle);
|
||||
}
|
||||
|
||||
.atc-btn {
|
||||
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);
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
|
||||
&:disabled {
|
||||
background-color: var(--t-border-default);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Accordion ── */
|
||||
|
||||
.accordion-summary {
|
||||
color: var(--t-text-primary);
|
||||
}
|
||||
|
||||
.accordion-body {
|
||||
color: var(--t-text-secondary);
|
||||
}
|
||||
|
||||
/* ── Product details ── */
|
||||
|
||||
.details-wrap {
|
||||
border-top: 1px solid var(--t-border-subtle);
|
||||
border-bottom: 1px solid var(--t-border-subtle);
|
||||
border-color: var(--t-border-subtle);
|
||||
}
|
||||
|
||||
.details-table-row {
|
||||
border-bottom: 1px solid var(--t-border-subtle);
|
||||
}
|
||||
|
||||
.details-th {
|
||||
color: var(--t-text-primary);
|
||||
}
|
||||
|
||||
.details-subheading {
|
||||
color: var(--t-text-primary);
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,6 +19,10 @@
|
||||
) do %>
|
||||
<link rel="preload" href={preload.href} as="font" type="font/woff2" crossorigin />
|
||||
<% end %>
|
||||
<!-- Pre-declare layer order so reset < components regardless of load order -->
|
||||
<style>
|
||||
@layer properties, reset, primitives, tokens, theme, base, components, layout, utilities, overrides;
|
||||
</style>
|
||||
<link phx-track-static rel="stylesheet" href={~p"/assets/css/app-shop.css"} />
|
||||
<link phx-track-static rel="stylesheet" href={~p"/assets/css/shop.css"} />
|
||||
<script defer phx-track-static src={~p"/assets/js/app.js"}>
|
||||
|
||||
@ -63,7 +63,8 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
|
||||
~H"""
|
||||
<article
|
||||
class={card_classes(@variant)}
|
||||
style={card_style(@variant) <> if(@clickable_resolved, do: " position: relative;", else: "")}
|
||||
data-variant={@variant}
|
||||
data-clickable={@clickable_resolved || nil}
|
||||
>
|
||||
<.product_card_inner
|
||||
product={@product}
|
||||
@ -102,7 +103,7 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
|
||||
|> assign(:has_hover_image, assigns.theme_settings.hover_image && hover_image != nil)
|
||||
|
||||
~H"""
|
||||
<div class={image_container_classes(@variant)} style="z-index: 1;">
|
||||
<div class={["product-card-image-wrap", image_container_classes(@variant)]}>
|
||||
<%= if @show_badges do %>
|
||||
<.product_badge product={@product} />
|
||||
<% end %>
|
||||
@ -145,23 +146,19 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
|
||||
<div class={content_padding_class(@variant)}>
|
||||
<%= if @show_category && Map.get(@product, :category) do %>
|
||||
<%= if @mode == :preview do %>
|
||||
<p
|
||||
class="text-xs mb-1"
|
||||
style="color: var(--t-text-tertiary); position: relative; z-index: 1;"
|
||||
>
|
||||
<p class="product-card-category text-xs mb-1">
|
||||
{@product.category}
|
||||
</p>
|
||||
<% else %>
|
||||
<.link
|
||||
navigate={"/collections/#{Slug.slugify(@product.category)}"}
|
||||
class="text-xs mb-1 block hover:underline"
|
||||
style="color: var(--t-text-tertiary); text-decoration: none; position: relative; z-index: 1;"
|
||||
class="product-card-category text-xs mb-1 block hover:underline"
|
||||
>
|
||||
{@product.category}
|
||||
</.link>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<h3 class={title_classes(@variant)} style={title_style(@variant)}>
|
||||
<h3 class={["product-card-title", title_classes(@variant)]}>
|
||||
<%= if @clickable do %>
|
||||
<%= if @mode == :preview do %>
|
||||
<a
|
||||
@ -169,7 +166,6 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
|
||||
phx-click="change_preview_page"
|
||||
phx-value-page="pdp"
|
||||
class="stretched-link"
|
||||
style="color: inherit; text-decoration: none;"
|
||||
>
|
||||
{@product.title}
|
||||
</a>
|
||||
@ -177,7 +173,6 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
|
||||
<.link
|
||||
navigate={"/products/#{Map.get(@product, :slug) || Map.get(@product, :id)}"}
|
||||
class="stretched-link"
|
||||
style="color: inherit; text-decoration: none;"
|
||||
>
|
||||
{@product.title}
|
||||
</.link>
|
||||
@ -190,7 +185,7 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
|
||||
<.product_price product={@product} variant={@variant} />
|
||||
<% end %>
|
||||
<%= if @show_delivery_text do %>
|
||||
<p class="text-xs mt-1" style="color: var(--t-text-tertiary);">
|
||||
<p class="product-card-delivery text-xs mt-1">
|
||||
Made to order
|
||||
</p>
|
||||
<% end %>
|
||||
@ -231,8 +226,7 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
|
||||
<%= cond do %>
|
||||
<% is_nil(@src) -> %>
|
||||
<div
|
||||
class={[@class, "flex items-center justify-center"]}
|
||||
style="color: var(--t-text-tertiary);"
|
||||
class={[@class, "product-card-placeholder flex items-center justify-center"]}
|
||||
role="img"
|
||||
aria-label={@alt}
|
||||
>
|
||||
@ -303,36 +297,33 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
|
||||
<% :default -> %>
|
||||
<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));"
|
||||
>
|
||||
<span class="product-price--sale text-lg font-bold">
|
||||
{SimpleshopTheme.Cart.format_price(@product.cheapest_price)}
|
||||
</span>
|
||||
<span class="text-sm line-through ml-2" style="color: var(--t-text-tertiary);">
|
||||
<span class="product-price--compare text-sm line-through ml-2">
|
||||
{SimpleshopTheme.Cart.format_price(@product.compare_at_price)}
|
||||
</span>
|
||||
<% else %>
|
||||
<span class="text-lg font-bold" style="color: var(--t-text-primary);">
|
||||
<span class="product-price--regular text-lg font-bold">
|
||||
{SimpleshopTheme.Cart.format_price(@product.cheapest_price)}
|
||||
</span>
|
||||
<% end %>
|
||||
</div>
|
||||
<% :featured -> %>
|
||||
<p class="text-sm" style="color: var(--t-text-secondary);">
|
||||
<p class="product-price--secondary text-sm">
|
||||
<%= if @product.on_sale do %>
|
||||
<span class="line-through mr-1" style="color: var(--t-text-tertiary);">
|
||||
<span class="product-price--compare line-through mr-1">
|
||||
{SimpleshopTheme.Cart.format_price(@product.compare_at_price)}
|
||||
</span>
|
||||
<% end %>
|
||||
{SimpleshopTheme.Cart.format_price(@product.cheapest_price)}
|
||||
</p>
|
||||
<% :compact -> %>
|
||||
<p class="font-bold" style="color: var(--t-text-primary);">
|
||||
<p class="product-price--regular font-bold">
|
||||
{SimpleshopTheme.Cart.format_price(@product.cheapest_price)}
|
||||
</p>
|
||||
<% :minimal -> %>
|
||||
<p class="text-xs" style="color: var(--t-text-secondary);">
|
||||
<p class="product-price--secondary text-xs">
|
||||
{SimpleshopTheme.Cart.format_price(@product.cheapest_price)}
|
||||
</p>
|
||||
<% end %>
|
||||
@ -359,22 +350,6 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
|
||||
defp card_classes(:compact), do: "product-card group overflow-hidden cursor-pointer"
|
||||
defp card_classes(:minimal), do: "product-card group overflow-hidden"
|
||||
|
||||
defp card_style(:default),
|
||||
do:
|
||||
"background-color: var(--t-surface-raised); border: 1px solid var(--t-border-default); border-radius: var(--t-radius-card); cursor: pointer;"
|
||||
|
||||
defp card_style(:featured),
|
||||
do:
|
||||
"background-color: var(--t-surface-raised); border-radius: var(--t-radius-card); cursor: pointer;"
|
||||
|
||||
defp card_style(:compact),
|
||||
do:
|
||||
"background-color: var(--t-surface-raised); border: 1px solid var(--t-border-default); border-radius: var(--t-radius-card);"
|
||||
|
||||
defp card_style(:minimal),
|
||||
do:
|
||||
"background-color: var(--t-surface-raised); border: 1px solid var(--t-border-subtle); border-radius: var(--t-radius-card);"
|
||||
|
||||
defp image_container_classes(:compact),
|
||||
do: "product-image-container aspect-square bg-gray-200 overflow-hidden relative"
|
||||
|
||||
@ -393,16 +368,6 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
|
||||
defp title_classes(:compact), do: "font-semibold text-sm mb-1"
|
||||
defp title_classes(:minimal), do: "text-xs font-semibold truncate"
|
||||
|
||||
defp title_style(:default),
|
||||
do: "font-family: var(--t-font-heading); color: var(--t-text-primary);"
|
||||
|
||||
defp title_style(:featured), do: "color: var(--t-text-primary);"
|
||||
|
||||
defp title_style(:compact),
|
||||
do: "font-family: var(--t-font-heading); color: var(--t-text-primary);"
|
||||
|
||||
defp title_style(:minimal), do: "color: var(--t-text-primary);"
|
||||
|
||||
@doc """
|
||||
Renders a responsive product grid container.
|
||||
|
||||
@ -544,20 +509,11 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
|
||||
~H"""
|
||||
<%= case @variant do %>
|
||||
<% :default -> %>
|
||||
<section
|
||||
class="text-center"
|
||||
style={"padding: var(--space-2xl) var(--space-lg); background-color: var(--t-surface-#{@background});"}
|
||||
>
|
||||
<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);"
|
||||
>
|
||||
<section class="hero-section text-center" data-background={@background}>
|
||||
<h1 class="t-heading text-3xl md:text-4xl mb-4">
|
||||
{@title}
|
||||
</h1>
|
||||
<p
|
||||
class="text-lg max-w-lg mx-auto mb-8"
|
||||
style="color: var(--t-text-secondary); line-height: 1.6;"
|
||||
>
|
||||
<p class="hero-description text-lg max-w-lg mx-auto mb-8">
|
||||
{@description}
|
||||
</p>
|
||||
<.hero_cta
|
||||
@ -570,34 +526,25 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
|
||||
/>
|
||||
</section>
|
||||
<% :page -> %>
|
||||
<div class="text-center" style="padding-top: var(--space-2xl);">
|
||||
<h1
|
||||
class="text-4xl md:text-5xl 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);"
|
||||
>
|
||||
<div class="hero-section--page text-center">
|
||||
<h1 class="t-heading text-4xl md:text-5xl mb-6">
|
||||
{@title}
|
||||
</h1>
|
||||
<p class="text-lg mb-12 max-w-2xl mx-auto" style="color: var(--t-text-secondary);">
|
||||
<p class="hero-description text-lg mb-12 max-w-2xl mx-auto">
|
||||
{@description}
|
||||
</p>
|
||||
</div>
|
||||
<% :error -> %>
|
||||
<div class="text-center">
|
||||
<%= if @pre_title do %>
|
||||
<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);"
|
||||
>
|
||||
<h1 class="hero-pre-title text-8xl md:text-9xl mb-4">
|
||||
{@pre_title}
|
||||
</h1>
|
||||
<% end %>
|
||||
<h2
|
||||
class="text-3xl md:text-4xl 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);"
|
||||
>
|
||||
<h2 class="t-heading text-3xl md:text-4xl mb-6">
|
||||
{@title}
|
||||
</h2>
|
||||
<p class="text-lg mb-8 max-w-md mx-auto" style="color: var(--t-text-secondary);">
|
||||
<p class="hero-description text-lg mb-8 max-w-md mx-auto">
|
||||
{@description}
|
||||
</p>
|
||||
<%= if @cta_text || @secondary_cta_text do %>
|
||||
@ -638,7 +585,6 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
|
||||
phx-click="change_preview_page"
|
||||
phx-value-page={@page}
|
||||
class={hero_cta_classes(@variant)}
|
||||
style={hero_cta_style(@variant)}
|
||||
>
|
||||
{@text}
|
||||
</button>
|
||||
@ -646,7 +592,6 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
|
||||
<.link
|
||||
navigate={@href || "/"}
|
||||
class={["inline-block", hero_cta_classes(@variant)]}
|
||||
style={hero_cta_style(@variant) <> " text-decoration: none;"}
|
||||
>
|
||||
{@text}
|
||||
</.link>
|
||||
@ -659,9 +604,6 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
|
||||
defp hero_cta_classes(:secondary),
|
||||
do: "themed-button-outline px-8 py-3 font-semibold transition-all"
|
||||
|
||||
defp hero_cta_style(:primary), do: ""
|
||||
defp hero_cta_style(:secondary), do: ""
|
||||
|
||||
@doc """
|
||||
Renders a row of category circles for navigation.
|
||||
|
||||
@ -682,7 +624,7 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
|
||||
|
||||
def category_nav(assigns) do
|
||||
~H"""
|
||||
<section style="padding: var(--space-xl) var(--space-lg); background-color: var(--t-surface-base);">
|
||||
<section class="category-nav-section">
|
||||
<h2 class="sr-only">Shop by Category</h2>
|
||||
<nav class="grid grid-cols-3 gap-4 max-w-3xl mx-auto" aria-label="Product categories">
|
||||
<%= for category <- Enum.take(@categories, @limit) do %>
|
||||
@ -691,8 +633,7 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
|
||||
href="#"
|
||||
phx-click="change_preview_page"
|
||||
phx-value-page="collection"
|
||||
class="flex flex-col items-center gap-3 p-4 rounded-lg transition-colors hover:bg-black/5"
|
||||
style="text-decoration: none; cursor: pointer;"
|
||||
class="category-card flex flex-col items-center gap-3 p-4 rounded-lg transition-colors hover:bg-black/5"
|
||||
>
|
||||
<div
|
||||
class="w-24 h-24 rounded-full bg-gray-200 bg-cover bg-center transition-transform hover:scale-105"
|
||||
@ -704,18 +645,14 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
|
||||
}
|
||||
>
|
||||
</div>
|
||||
<span
|
||||
class="text-sm font-medium"
|
||||
style="font-family: var(--t-font-body); color: var(--t-text-primary);"
|
||||
>
|
||||
<span class="category-name text-sm font-medium">
|
||||
{category.name}
|
||||
</span>
|
||||
</a>
|
||||
<% else %>
|
||||
<.link
|
||||
navigate={"/collections/#{category.slug}"}
|
||||
class="flex flex-col items-center gap-3 p-4 rounded-lg transition-colors hover:bg-black/5"
|
||||
style="text-decoration: none;"
|
||||
class="category-card flex flex-col items-center gap-3 p-4 rounded-lg transition-colors hover:bg-black/5"
|
||||
>
|
||||
<div
|
||||
class="w-24 h-24 rounded-full bg-gray-200 bg-cover bg-center transition-transform hover:scale-105"
|
||||
@ -727,10 +664,7 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
|
||||
}
|
||||
>
|
||||
</div>
|
||||
<span
|
||||
class="text-sm font-medium"
|
||||
style="font-family: var(--t-font-body); color: var(--t-text-primary);"
|
||||
>
|
||||
<span class="category-name text-sm font-medium">
|
||||
{category.name}
|
||||
</span>
|
||||
</.link>
|
||||
@ -775,11 +709,8 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
|
||||
|
||||
def featured_products_section(assigns) do
|
||||
~H"""
|
||||
<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);"
|
||||
>
|
||||
<section class="featured-section">
|
||||
<h2 class="t-heading text-2xl mb-6">
|
||||
{@title}
|
||||
</h2>
|
||||
|
||||
@ -800,16 +731,14 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
|
||||
<button
|
||||
phx-click="change_preview_page"
|
||||
phx-value-page={@cta_page}
|
||||
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); cursor: pointer;"
|
||||
class="outline-button px-6 py-3 font-medium transition-all"
|
||||
>
|
||||
{@cta_text}
|
||||
</button>
|
||||
<% else %>
|
||||
<.link
|
||||
navigate={@cta_href}
|
||||
class="inline-block 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); text-decoration: none;"
|
||||
class="outline-button inline-block px-6 py-3 font-medium transition-all"
|
||||
>
|
||||
{@cta_text}
|
||||
</.link>
|
||||
@ -855,10 +784,7 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
|
||||
|
||||
def image_text_section(assigns) do
|
||||
~H"""
|
||||
<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);"
|
||||
>
|
||||
<section class="image-text-section grid grid-cols-1 md:grid-cols-2 gap-12 items-center">
|
||||
<%= if @image_position == :left do %>
|
||||
<.image_text_image image_url={@image_url} />
|
||||
<.image_text_content
|
||||
@ -889,8 +815,8 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
|
||||
defp image_text_image(assigns) do
|
||||
~H"""
|
||||
<div
|
||||
class="h-72 rounded-lg bg-cover bg-center"
|
||||
style={"background-image: url('#{@image_url}'); border-radius: var(--t-radius-image);"}
|
||||
class="image-text-image h-72 bg-cover bg-center"
|
||||
style={"background-image: url('#{@image_url}');"}
|
||||
>
|
||||
</div>
|
||||
"""
|
||||
@ -906,13 +832,10 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
|
||||
defp image_text_content(assigns) do
|
||||
~H"""
|
||||
<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);"
|
||||
>
|
||||
<h2 class="t-heading text-2xl mb-4">
|
||||
{@title}
|
||||
</h2>
|
||||
<p class="text-base mb-4" style="color: var(--t-text-secondary); line-height: 1.7;">
|
||||
<p class="image-text-body text-base mb-4">
|
||||
{@description}
|
||||
</p>
|
||||
<%= if @link_text do %>
|
||||
@ -921,16 +844,14 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
|
||||
href="#"
|
||||
phx-click="change_preview_page"
|
||||
phx-value-page={@link_page}
|
||||
class="text-sm font-medium transition-colors"
|
||||
style="color: var(--t-accent-text, hsl(var(--t-accent-h) var(--t-accent-s) 38%)); text-decoration: none; cursor: pointer;"
|
||||
class="accent-link text-sm font-medium transition-colors"
|
||||
>
|
||||
{@link_text}
|
||||
</a>
|
||||
<% else %>
|
||||
<.link
|
||||
navigate={@link_href || "/"}
|
||||
class="text-sm font-medium transition-colors"
|
||||
style="color: var(--t-accent-text, hsl(var(--t-accent-h) var(--t-accent-s) 38%)); text-decoration: none;"
|
||||
class="accent-link text-sm font-medium transition-colors"
|
||||
>
|
||||
{@link_text}
|
||||
</.link>
|
||||
@ -959,22 +880,16 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
|
||||
|
||||
def collection_header(assigns) do
|
||||
~H"""
|
||||
<div
|
||||
class="border-b"
|
||||
style="background-color: var(--t-surface-raised); border-color: var(--t-border-default);"
|
||||
>
|
||||
<div class="collection-header-wrap border-b">
|
||||
<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);"
|
||||
>
|
||||
<h1 class="t-heading text-3xl md:text-4xl mb-2">
|
||||
{@title}
|
||||
</h1>
|
||||
<%= if @subtitle do %>
|
||||
<p style="color: var(--t-text-secondary);">{@subtitle}</p>
|
||||
<p class="collection-header-meta">{@subtitle}</p>
|
||||
<% end %>
|
||||
<%= if @product_count do %>
|
||||
<p style="color: var(--t-text-secondary);">{@product_count} products</p>
|
||||
<p class="collection-header-meta">{@product_count} products</p>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
@ -1053,11 +968,11 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
|
||||
|
||||
def breadcrumb(assigns) do
|
||||
~H"""
|
||||
<nav aria-label="Breadcrumb" class="breadcrumb" style="color: var(--t-text-secondary);">
|
||||
<nav aria-label="Breadcrumb" class="breadcrumb">
|
||||
<ol>
|
||||
<%= for {item, _index} <- Enum.with_index(@items) do %>
|
||||
<%= if item[:current] do %>
|
||||
<li aria-current="page" style="color: var(--t-text-primary);">{item.label}</li>
|
||||
<li aria-current="page">{item.label}</li>
|
||||
<% else %>
|
||||
<li>
|
||||
<%= if @mode == :preview do %>
|
||||
@ -1107,11 +1022,8 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
|
||||
|
||||
def related_products_section(assigns) do
|
||||
~H"""
|
||||
<div class="py-12" style="border-top: 1px solid var(--t-border-default);">
|
||||
<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);"
|
||||
>
|
||||
<div class="related-section py-12">
|
||||
<h2 class="t-heading text-2xl mb-6">
|
||||
{@title}
|
||||
</h2>
|
||||
|
||||
@ -1150,7 +1062,7 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
|
||||
~H"""
|
||||
<div class="pdp-gallery">
|
||||
<%!-- Image area (relative container for dots + desktop nav) --%>
|
||||
<div class="relative" style="border-radius: var(--t-radius-image); overflow: hidden;">
|
||||
<div class="pdp-gallery-frame relative">
|
||||
<%!-- Scroll-snap carousel (2+ images) or single image --%>
|
||||
<%= if length(@images) > 1 do %>
|
||||
<div
|
||||
@ -1217,8 +1129,7 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
|
||||
<div class="pdp-gallery-single">
|
||||
<%= if @images == [] do %>
|
||||
<div
|
||||
class="w-full h-full flex items-center justify-center"
|
||||
style="color: var(--t-text-tertiary);"
|
||||
class="product-card-placeholder w-full h-full flex items-center justify-center"
|
||||
role="img"
|
||||
aria-label={@product_name}
|
||||
>
|
||||
@ -1274,7 +1185,6 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
|
||||
<button
|
||||
type="button"
|
||||
class={"aspect-square bg-gray-200 overflow-hidden pdp-thumbnail#{if idx == 0, do: " pdp-thumbnail-active", else: ""}"}
|
||||
style="border-radius: var(--t-radius-image);"
|
||||
aria-label={"View image #{idx + 1} of #{length(@images)}"}
|
||||
phx-click={
|
||||
Phoenix.LiveView.JS.dispatch("pdp:scroll-to",
|
||||
@ -1427,32 +1337,23 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
|
||||
|
||||
~H"""
|
||||
<div>
|
||||
<h1
|
||||
class="text-3xl md:text-4xl font-bold mb-4"
|
||||
style="font-family: var(--t-font-heading); color: var(--t-text-primary); font-weight: var(--t-heading-weight); letter-spacing: var(--t-heading-tracking);"
|
||||
>
|
||||
<h1 class="t-heading text-3xl md:text-4xl mb-4">
|
||||
{@product.title}
|
||||
</h1>
|
||||
|
||||
<div class="flex items-center gap-4 mb-6">
|
||||
<%= if @product.on_sale do %>
|
||||
<span
|
||||
class="text-3xl font-bold"
|
||||
style="color: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l));"
|
||||
>
|
||||
<span class="product-price--sale text-3xl font-bold">
|
||||
{SimpleshopTheme.Cart.format_price(@price)}
|
||||
</span>
|
||||
<span class="text-xl line-through" style="color: var(--t-text-tertiary);">
|
||||
<span class="product-price--compare text-xl line-through">
|
||||
{SimpleshopTheme.Cart.format_price(@product.compare_at_price)}
|
||||
</span>
|
||||
<span
|
||||
class="px-2 py-1 text-sm font-bold text-white rounded"
|
||||
style="background-color: var(--t-sale-color);"
|
||||
>
|
||||
<span class="sale-badge px-2 py-1 text-sm font-bold text-white rounded">
|
||||
SAVE {round((@product.compare_at_price - @price) / @product.compare_at_price * 100)}%
|
||||
</span>
|
||||
<% else %>
|
||||
<span class="text-3xl font-bold" style="color: var(--t-text-primary);">
|
||||
<span class="product-price--regular text-3xl font-bold">
|
||||
{SimpleshopTheme.Cart.format_price(@price)}
|
||||
</span>
|
||||
<% end %>
|
||||
@ -1490,10 +1391,10 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
|
||||
def variant_selector(assigns) do
|
||||
~H"""
|
||||
<div class="mb-6">
|
||||
<div class="block font-semibold mb-2" style="color: var(--t-text-primary);">
|
||||
<div class="variant-label block font-semibold mb-2">
|
||||
{@option_type.name}<span
|
||||
:if={@selected}
|
||||
style="color: var(--t-text-secondary); font-weight: normal;"
|
||||
class="variant-label-value"
|
||||
>: {@selected}</span>
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
@ -1537,10 +1438,10 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
|
||||
phx-value-option={@option_name}
|
||||
phx-value-selected={@title}
|
||||
class={[
|
||||
"w-10 h-10 rounded-full border-2 transition-all relative",
|
||||
"color-swatch w-10 h-10 rounded-full border-2 transition-all relative",
|
||||
@selected && "ring-2 ring-offset-2"
|
||||
]}
|
||||
style={"background-color: #{@hex}; border-color: #{if @selected, do: "hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l))", else: "var(--t-border-default)"}; --tw-ring-color: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l));"}
|
||||
style={"background-color: #{@hex};"}
|
||||
title={@title}
|
||||
aria-label={"Select #{@title}"}
|
||||
aria-pressed={to_string(@selected)}
|
||||
@ -1562,10 +1463,7 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
|
||||
phx-click={if @mode == :shop, do: "select_option"}
|
||||
phx-value-option={@option_name}
|
||||
phx-value-selected={@title}
|
||||
class={[
|
||||
"px-4 py-2 font-medium transition-all"
|
||||
]}
|
||||
style={"border: 2px solid #{if @selected, do: "hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l))", else: "var(--t-border-default)"}; border-radius: var(--t-radius-button); color: var(--t-text-primary); background: #{if @selected, do: "hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l) / 0.1)", else: "transparent"};"}
|
||||
class="size-btn px-4 py-2 font-medium transition-all"
|
||||
aria-pressed={to_string(@selected)}
|
||||
>
|
||||
{@title}
|
||||
@ -1596,28 +1494,21 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
|
||||
def quantity_selector(assigns) do
|
||||
~H"""
|
||||
<div class="mb-8">
|
||||
<label class="block font-semibold mb-2" style="color: var(--t-text-primary);">
|
||||
<label class="qty-label block font-semibold mb-2">
|
||||
Quantity
|
||||
</label>
|
||||
<div class="flex items-center gap-4">
|
||||
<div
|
||||
class="flex items-center"
|
||||
style="border: 2px solid var(--t-border-default); border-radius: var(--t-radius-input);"
|
||||
>
|
||||
<div class="qty-group flex items-center">
|
||||
<button
|
||||
type="button"
|
||||
phx-click="decrement_quantity"
|
||||
disabled={@quantity <= @min}
|
||||
aria-label="Decrease quantity"
|
||||
class="px-4 py-2 disabled:opacity-30"
|
||||
style="color: var(--t-text-primary);"
|
||||
class="qty-btn px-4 py-2 disabled:opacity-30"
|
||||
>
|
||||
−
|
||||
</button>
|
||||
<span
|
||||
class="px-4 py-2 border-x-2 min-w-12 text-center tabular-nums"
|
||||
style="border-color: var(--t-border-default); color: var(--t-text-primary);"
|
||||
>
|
||||
<span class="qty-display px-4 py-2 border-x-2 min-w-12 text-center tabular-nums">
|
||||
{@quantity}
|
||||
</span>
|
||||
<button
|
||||
@ -1625,16 +1516,15 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
|
||||
phx-click="increment_quantity"
|
||||
disabled={@quantity >= @max}
|
||||
aria-label="Increase quantity"
|
||||
class="px-4 py-2 disabled:opacity-30"
|
||||
style="color: var(--t-text-primary);"
|
||||
class="qty-btn px-4 py-2 disabled:opacity-30"
|
||||
>
|
||||
+
|
||||
</button>
|
||||
</div>
|
||||
<%= if @in_stock do %>
|
||||
<span class="text-sm" style="color: var(--t-text-tertiary);">In stock</span>
|
||||
<span class="stock-in text-sm">In stock</span>
|
||||
<% else %>
|
||||
<span class="text-sm font-semibold" style="color: var(--t-sale-color);">Out of stock</span>
|
||||
<span class="stock-out text-sm font-semibold">Out of stock</span>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
@ -1663,20 +1553,16 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
|
||||
|
||||
def add_to_cart_button(assigns) do
|
||||
~H"""
|
||||
<div
|
||||
class={[
|
||||
"mb-4",
|
||||
@sticky &&
|
||||
"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"
|
||||
]}
|
||||
style="background-color: var(--t-surface-base); border-color: var(--t-border-subtle);"
|
||||
>
|
||||
<div class={[
|
||||
"atc-wrap mb-4",
|
||||
@sticky &&
|
||||
"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"
|
||||
]}>
|
||||
<button
|
||||
type="button"
|
||||
phx-click={if @mode == :preview, do: open_cart_drawer_js(), else: "add_to_cart"}
|
||||
disabled={@disabled}
|
||||
class="w-full px-6 py-4 text-lg font-semibold transition-all"
|
||||
style={"background-color: #{if @disabled, do: "var(--t-border-default)", else: "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: #{if @disabled, do: "not-allowed", else: "pointer"}; border: none;"}
|
||||
class="atc-btn w-full px-6 py-4 text-lg font-semibold transition-all"
|
||||
>
|
||||
{@text}
|
||||
</button>
|
||||
@ -1714,10 +1600,7 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
|
||||
def accordion_item(assigns) do
|
||||
~H"""
|
||||
<details open={@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);"
|
||||
>
|
||||
<summary class="accordion-summary flex justify-between items-center py-4 cursor-pointer list-none [&::-webkit-details-marker]:hidden">
|
||||
<span class="font-semibold">{@title}</span>
|
||||
<svg
|
||||
class="w-5 h-5 transition-transform duration-200 group-open:rotate-180"
|
||||
@ -1730,7 +1613,7 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
|
||||
<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);">
|
||||
<div class="accordion-body pb-4">
|
||||
{render_slot(@inner_block)}
|
||||
</div>
|
||||
</details>
|
||||
@ -1768,10 +1651,7 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
|
||||
end)
|
||||
|
||||
~H"""
|
||||
<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);"
|
||||
>
|
||||
<div class="details-wrap mt-8 divide-y">
|
||||
<.accordion_item title="Description" open={true}>
|
||||
<p class="leading-relaxed">
|
||||
{@product.description}. Crafted with attention to detail and quality materials, this product is designed to last. Perfect for everyday use or special occasions.
|
||||
@ -1782,25 +1662,21 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
|
||||
<.accordion_item title="Size Guide">
|
||||
<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);">
|
||||
<tr class="details-table-row">
|
||||
<th class="details-th text-left py-2 font-semibold">
|
||||
Size
|
||||
</th>
|
||||
<th class="text-left py-2 font-semibold" style="color: var(--t-text-primary);">
|
||||
<th class="details-th text-left py-2 font-semibold">
|
||||
Chest (cm)
|
||||
</th>
|
||||
<th class="text-left py-2 font-semibold" style="color: var(--t-text-primary);">
|
||||
<th class="details-th text-left py-2 font-semibold">
|
||||
Length (cm)
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<%= for {size_row, idx} <- Enum.with_index(@sizes) do %>
|
||||
<tr style={
|
||||
if idx < length(@sizes) - 1,
|
||||
do: "border-bottom: 1px solid var(--t-border-subtle);",
|
||||
else: ""
|
||||
}>
|
||||
<tr class={idx < length(@sizes) - 1 && "details-table-row"}>
|
||||
<td class="py-2">{size_row.size}</td>
|
||||
<td class="py-2">{size_row.chest}</td>
|
||||
<td class="py-2">{size_row.length}</td>
|
||||
@ -1814,13 +1690,13 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
|
||||
<.accordion_item title="Shipping & Returns">
|
||||
<div class="flex flex-col gap-3">
|
||||
<div>
|
||||
<p class="font-semibold mb-1" style="color: var(--t-text-primary);">Delivery</p>
|
||||
<p class="details-subheading font-semibold mb-1">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="details-subheading font-semibold mb-1">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>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user