refactor: expand hero_section with page and error variants
Add variant support to hero_section component: - :default - Standard hero with section wrapper (home, about) - :page - Page header style with larger title, more spacing (contact) - :error - Error page with pre-title (404), dual buttons (error) Now used in: home, about, contact, error pages Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
f5b7693b96
commit
1589ebaeca
@ -861,77 +861,186 @@ defmodule SimpleshopThemeWeb.ShopComponents do
|
|||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Renders a centered hero section with title, description, and optional CTA.
|
Renders a centered hero section with title, description, and optional CTAs.
|
||||||
|
|
||||||
## Attributes
|
## Attributes
|
||||||
|
|
||||||
* `title` - Required. The main heading text.
|
* `title` - Required. The main heading text.
|
||||||
* `description` - Required. The description paragraph text.
|
* `description` - Required. The description paragraph text.
|
||||||
|
* `variant` - The visual variant:
|
||||||
|
- `:default` - Standard hero with section wrapper and padding
|
||||||
|
- `:page` - Page header style, larger title, more spacing, no section wrapper
|
||||||
|
- `:error` - Error page with pre-title (404), two buttons
|
||||||
* `background` - Background style. Either `:base` (default) or `:sunken`.
|
* `background` - Background style. Either `:base` (default) or `:sunken`.
|
||||||
* `cta_text` - Optional. Text for the CTA button.
|
* `pre_title` - Optional. Text shown above title (e.g., "404" for error pages).
|
||||||
|
* `cta_text` - Optional. Text for the primary CTA button.
|
||||||
* `cta_page` - Optional. Page to navigate to on click (for preview mode).
|
* `cta_page` - Optional. Page to navigate to on click (for preview mode).
|
||||||
|
* `cta_href` - Optional. URL for live mode navigation.
|
||||||
|
* `secondary_cta_text` - Optional. Text for secondary button (error variant).
|
||||||
|
* `secondary_cta_page` - Optional. Page for secondary button (preview mode).
|
||||||
|
* `secondary_cta_href` - Optional. URL for secondary button (live mode).
|
||||||
* `mode` - Either `:live` (default) or `:preview`.
|
* `mode` - Either `:live` (default) or `:preview`.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
<.hero_section
|
<.hero_section
|
||||||
title="Original designs, printed on demand"
|
title="Original designs, printed on demand"
|
||||||
description="From art prints to apparel – unique products created by independent artists."
|
description="From art prints to apparel..."
|
||||||
cta_text="Shop the collection"
|
cta_text="Shop the collection"
|
||||||
cta_page="collection"
|
cta_page="collection"
|
||||||
mode={:preview}
|
mode={:preview}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<.hero_section
|
<.hero_section
|
||||||
title="About the studio"
|
variant={:page}
|
||||||
description="Nature photography, printed with care"
|
title="Contact Us"
|
||||||
background={:sunken}
|
description="Questions about your order?"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<.hero_section
|
||||||
|
variant={:error}
|
||||||
|
pre_title="404"
|
||||||
|
title="Page Not Found"
|
||||||
|
description="Sorry, we couldn't find the page..."
|
||||||
|
cta_text="Go to Homepage"
|
||||||
|
secondary_cta_text="Browse Products"
|
||||||
|
mode={:preview}
|
||||||
/>
|
/>
|
||||||
"""
|
"""
|
||||||
attr :title, :string, required: true
|
attr :title, :string, required: true
|
||||||
attr :description, :string, required: true
|
attr :description, :string, required: true
|
||||||
|
attr :variant, :atom, default: :default
|
||||||
attr :background, :atom, default: :base
|
attr :background, :atom, default: :base
|
||||||
|
attr :pre_title, :string, default: nil
|
||||||
attr :cta_text, :string, default: nil
|
attr :cta_text, :string, default: nil
|
||||||
attr :cta_page, :string, default: nil
|
attr :cta_page, :string, default: nil
|
||||||
attr :cta_href, :string, default: nil
|
attr :cta_href, :string, default: nil
|
||||||
|
attr :secondary_cta_text, :string, default: nil
|
||||||
|
attr :secondary_cta_page, :string, default: nil
|
||||||
|
attr :secondary_cta_href, :string, default: nil
|
||||||
attr :mode, :atom, default: :live
|
attr :mode, :atom, default: :live
|
||||||
|
|
||||||
def hero_section(assigns) do
|
def hero_section(assigns) do
|
||||||
~H"""
|
~H"""
|
||||||
<section
|
<%= case @variant do %>
|
||||||
class="text-center"
|
<% :default -> %>
|
||||||
style={"padding: var(--space-2xl) var(--space-lg); background-color: var(--t-surface-#{@background});"}
|
<section
|
||||||
>
|
class="text-center"
|
||||||
<h1
|
style={"padding: var(--space-2xl) var(--space-lg); background-color: var(--t-surface-#{@background});"}
|
||||||
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);"
|
<h1
|
||||||
>
|
class="text-3xl md:text-4xl mb-4"
|
||||||
<%= @title %>
|
style="font-family: var(--t-font-heading); font-weight: var(--t-heading-weight); letter-spacing: var(--t-heading-tracking); color: var(--t-text-primary);"
|
||||||
</h1>
|
|
||||||
<p class="text-lg max-w-lg mx-auto mb-8" style="color: var(--t-text-secondary); line-height: 1.6;">
|
|
||||||
<%= @description %>
|
|
||||||
</p>
|
|
||||||
<%= if @cta_text do %>
|
|
||||||
<%= if @mode == :preview do %>
|
|
||||||
<button
|
|
||||||
phx-click="change_preview_page"
|
|
||||||
phx-value-page={@cta_page}
|
|
||||||
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); cursor: pointer; border: none;"
|
|
||||||
>
|
>
|
||||||
<%= @cta_text %>
|
<%= @title %>
|
||||||
</button>
|
</h1>
|
||||||
<% else %>
|
<p class="text-lg max-w-lg mx-auto mb-8" style="color: var(--t-text-secondary); line-height: 1.6;">
|
||||||
<a
|
<%= @description %>
|
||||||
href={@cta_href || "/products"}
|
</p>
|
||||||
class="inline-block px-6 py-3 font-medium transition-all"
|
<.hero_cta
|
||||||
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); text-decoration: none;"
|
:if={@cta_text}
|
||||||
|
text={@cta_text}
|
||||||
|
page={@cta_page}
|
||||||
|
href={@cta_href}
|
||||||
|
mode={@mode}
|
||||||
|
variant={:primary}
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<% :page -> %>
|
||||||
|
<div class="text-center">
|
||||||
|
<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);"
|
||||||
>
|
>
|
||||||
<%= @cta_text %>
|
<%= @title %>
|
||||||
</a>
|
</h1>
|
||||||
<% end %>
|
<p class="text-lg mb-12 max-w-2xl mx-auto" style="color: var(--t-text-secondary);">
|
||||||
<% end %>
|
<%= @description %>
|
||||||
</section>
|
</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);"
|
||||||
|
>
|
||||||
|
<%= @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);"
|
||||||
|
>
|
||||||
|
<%= @title %>
|
||||||
|
</h2>
|
||||||
|
<p class="text-lg mb-8 max-w-md mx-auto" style="color: var(--t-text-secondary);">
|
||||||
|
<%= @description %>
|
||||||
|
</p>
|
||||||
|
<%= if @cta_text || @secondary_cta_text do %>
|
||||||
|
<div class="flex flex-col sm:flex-row gap-4 justify-center">
|
||||||
|
<.hero_cta
|
||||||
|
:if={@cta_text}
|
||||||
|
text={@cta_text}
|
||||||
|
page={@cta_page}
|
||||||
|
href={@cta_href}
|
||||||
|
mode={@mode}
|
||||||
|
variant={:primary}
|
||||||
|
/>
|
||||||
|
<.hero_cta
|
||||||
|
:if={@secondary_cta_text}
|
||||||
|
text={@secondary_cta_text}
|
||||||
|
page={@secondary_cta_page}
|
||||||
|
href={@secondary_cta_href}
|
||||||
|
mode={@mode}
|
||||||
|
variant={:secondary}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
|
|
||||||
|
attr :text, :string, required: true
|
||||||
|
attr :page, :string, default: nil
|
||||||
|
attr :href, :string, default: nil
|
||||||
|
attr :mode, :atom, required: true
|
||||||
|
attr :variant, :atom, required: true
|
||||||
|
|
||||||
|
defp hero_cta(assigns) do
|
||||||
|
~H"""
|
||||||
|
<%= if @mode == :preview do %>
|
||||||
|
<button
|
||||||
|
phx-click="change_preview_page"
|
||||||
|
phx-value-page={@page}
|
||||||
|
class={hero_cta_classes(@variant)}
|
||||||
|
style={hero_cta_style(@variant)}
|
||||||
|
>
|
||||||
|
<%= @text %>
|
||||||
|
</button>
|
||||||
|
<% else %>
|
||||||
|
<a
|
||||||
|
href={@href || "/"}
|
||||||
|
class={["inline-block", hero_cta_classes(@variant)]}
|
||||||
|
style={hero_cta_style(@variant) <> " text-decoration: none;"}
|
||||||
|
>
|
||||||
|
<%= @text %>
|
||||||
|
</a>
|
||||||
|
<% end %>
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
|
||||||
|
defp hero_cta_classes(:primary), do: "px-8 py-3 font-semibold transition-all"
|
||||||
|
defp hero_cta_classes(:secondary), do: "px-8 py-3 font-semibold transition-all"
|
||||||
|
|
||||||
|
defp hero_cta_style(:primary) do
|
||||||
|
"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;"
|
||||||
|
end
|
||||||
|
|
||||||
|
defp hero_cta_style(:secondary) do
|
||||||
|
"border: 2px solid var(--t-border-default); color: var(--t-text-primary); border-radius: var(--t-radius-button); background: transparent; cursor: pointer;"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -11,13 +11,11 @@
|
|||||||
<.shop_header theme_settings={@theme_settings} logo_image={@logo_image} header_image={@header_image} active_page="contact" mode={:preview} cart_count={2} />
|
<.shop_header theme_settings={@theme_settings} logo_image={@logo_image} header_image={@header_image} active_page="contact" mode={:preview} cart_count={2} />
|
||||||
|
|
||||||
<main id="main-content" 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);">
|
<.hero_section
|
||||||
Contact Us
|
variant={:page}
|
||||||
</h1>
|
title="Contact Us"
|
||||||
|
description="Questions about your order or just want to say hello? Drop us a message and we'll get back to you as soon as we can."
|
||||||
<p class="text-lg mb-12 text-center max-w-2xl mx-auto" style="color: var(--t-text-secondary);">
|
/>
|
||||||
Questions about your order or just want to say hello? Drop us a message and we'll get back to you as soon as we can.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div class="grid gap-8 md:grid-cols-2 mb-12">
|
<div class="grid gap-8 md:grid-cols-2 mb-12">
|
||||||
<!-- Contact Form -->
|
<!-- Contact Form -->
|
||||||
|
|||||||
@ -11,34 +11,18 @@
|
|||||||
<.shop_header theme_settings={@theme_settings} logo_image={@logo_image} header_image={@header_image} active_page="error" mode={:preview} cart_count={2} />
|
<.shop_header theme_settings={@theme_settings} logo_image={@logo_image} header_image={@header_image} active_page="error" mode={:preview} cart_count={2} />
|
||||||
|
|
||||||
<main id="main-content" 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">
|
<div class="max-w-2xl mx-auto px-4 sm:px-6 lg:px-8 py-16">
|
||||||
<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);">
|
<.hero_section
|
||||||
404
|
variant={:error}
|
||||||
</h1>
|
pre_title="404"
|
||||||
|
title="Page Not Found"
|
||||||
<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);">
|
description="Sorry, we couldn't find the page you're looking for. Perhaps you've mistyped the URL or the page has been moved."
|
||||||
Page Not Found
|
cta_text="Go to Homepage"
|
||||||
</h2>
|
cta_page="home"
|
||||||
|
secondary_cta_text="Browse Products"
|
||||||
<p class="text-lg mb-8 max-w-md mx-auto" style="color: var(--t-text-secondary);">
|
secondary_cta_page="collection"
|
||||||
Sorry, we couldn't find the page you're looking for. Perhaps you've mistyped the URL or the page has been moved.
|
mode={:preview}
|
||||||
</p>
|
/>
|
||||||
|
|
||||||
<div class="flex flex-col sm:flex-row gap-4 justify-center">
|
|
||||||
<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);"
|
|
||||||
>
|
|
||||||
Go to Homepage
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button
|
|
||||||
class="px-8 py-3 font-semibold transition-all"
|
|
||||||
style="border: 2px solid var(--t-border-default); color: var(--t-text-primary); border-radius: var(--t-radius-button);"
|
|
||||||
>
|
|
||||||
Browse Products
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<.product_grid columns={:fixed_4} gap="gap-4" class="mt-12 max-w-xl mx-auto">
|
<.product_grid columns={:fixed_4} gap="gap-4" class="mt-12 max-w-xl mx-auto">
|
||||||
<%= for product <- Enum.take(@preview_data.products, 4) do %>
|
<%= for product <- Enum.take(@preview_data.products, 4) do %>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user