diff --git a/PROGRESS.md b/PROGRESS.md index 33d01ec..405e1fc 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -65,7 +65,7 @@ Plans: [admin-redesign.md](docs/plans/admin-redesign.md) | [setup-wizard.md](doc | ~~33~~ | ~~Phase 1: Layout primitives + reset~~ | 32 | 1.5h | done | | ~~34~~ | ~~Phase 2: Extract product inline styles~~ | 33 | 3h | done | | ~~35~~ | ~~Phase 3: Extract layout + cart inline styles~~ | 33 | 3h | done | -| 36 | Phase 4: Extract content + template inline styles | 33 | 2.5h | | +| ~~36~~ | ~~Phase 4: Extract content + template inline styles~~ | 33 | 2.5h | done | | 37 | Phase 5: Remove Tailwind from shop | 34-36 | 3h | | | 38 | Phase 6: Replace DaisyUI (admin) | 37 | 3h | | | 39 | Phase 7: Remove Tailwind entirely | 38 | 1.5h | | diff --git a/assets/css/shop/components.css b/assets/css/shop/components.css index 5990313..ae95fa4 100644 --- a/assets/css/shop/components.css +++ b/assets/css/shop/components.css @@ -335,12 +335,25 @@ font-size: var(--t-text-small); } + /* ── Page container (shared responsive centering) ── */ + + .page-container { + max-width: 80rem; + margin-inline: auto; + padding: 2rem 1rem; + } + /* ── Shop container (body-level defaults) ── */ .shop-container { background-color: var(--t-surface-base); font-family: var(--t-font-body); color: var(--t-text-primary); + min-height: 100vh; + } + + .shop-container[data-bottom-nav] { + padding-bottom: 5rem; } /* ── Shop header ── */ @@ -350,11 +363,13 @@ border-bottom: 1px solid var(--t-border-default); display: flex; align-items: center; + padding: 0.5rem; } .shop-logo { display: flex; align-items: center; + gap: 0.5rem; position: relative; z-index: 1; } @@ -362,6 +377,7 @@ .shop-logo-link { display: flex; align-items: center; + gap: 0.5rem; text-decoration: none; } @@ -377,24 +393,39 @@ } .shop-nav { + display: none; position: relative; z-index: 1; } .shop-actions { + display: flex; + align-items: center; position: relative; z-index: 1; } .header-icon-btn { + width: 2.25rem; + height: 2.25rem; + display: flex; + align-items: center; + justify-content: center; + position: relative; color: var(--t-text-secondary); border-radius: var(--t-radius-button); + transition: all 0.2s ease; &:where(button) { background: none; border: none; cursor: pointer; } + + & svg { + width: 1.25rem; + height: 1.25rem; + } } .cart-badge { @@ -441,22 +472,50 @@ padding-bottom: env(safe-area-inset-bottom, 0px); & ul { + display: flex; + justify-content: space-around; + align-items: center; + height: 4rem; margin: 0; padding: 0; list-style: none; } + + & li { + flex: 1; + } } .mobile-nav-link { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 0.25rem; + padding-block: 0.5rem; + margin-inline: 0.25rem; + min-height: 56px; + border-radius: var(--t-radius-card, 0.5rem); + font-size: var(--t-text-caption); color: var(--t-text-secondary); text-decoration: none; font-weight: 500; background-color: transparent; + & svg { + width: 1.25rem; + height: 1.25rem; + } + &[aria-current="page"] { color: hsl(var(--t-accent-h) var(--t-accent-s) calc(var(--t-accent-l) - 15%)); font-weight: 600; background-color: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l) / 0.1); + + & svg { + width: 1.5rem; + height: 1.5rem; + } } } @@ -473,6 +532,9 @@ } .search-panel { + width: 100%; + max-width: 36rem; + margin-inline: 1rem; background: var(--t-surface-raised); border-radius: var(--t-radius-card); overflow: hidden; @@ -480,39 +542,79 @@ } .search-bar { + display: flex; + align-items: center; + gap: 0.75rem; + padding: 1rem; border-bottom: 1px solid var(--t-border-default); } .search-icon { + width: 1.25rem; + height: 1.25rem; + flex-shrink: 0; color: var(--t-text-tertiary); } .search-input { + flex: 1; + font-size: var(--t-text-large, 1.125rem); font-family: var(--t-font-body); color: var(--t-text-primary); + background: transparent; + border: none; + outline: none; } .search-kbd { + display: none; + align-items: center; + gap: 0.25rem; + font-size: var(--t-text-caption); + padding: 0.125rem 0.375rem; + border-radius: var(--t-radius-button, 0.25rem); color: var(--t-text-tertiary); border: 1px solid var(--t-border-default); } .search-close { + width: 2rem; + height: 2rem; + display: flex; + align-items: center; + justify-content: center; color: var(--t-text-tertiary); background: none; border: none; cursor: pointer; border-radius: var(--t-radius-button); + transition: all 0.2s ease; + + & svg { + width: 1.25rem; + height: 1.25rem; + } } .search-results { max-height: 60vh; overflow-y: auto; + + & ul { + padding-block: 0.5rem; + margin: 0; + list-style: none; + } } .search-result { + display: flex; + align-items: center; + gap: 0.75rem; + padding: 0.75rem 1rem; text-decoration: none; color: inherit; + transition: background-color 0.15s ease; &:hover { background: var(--t-surface-sunken); @@ -520,19 +622,47 @@ } .search-result-thumb { + width: 3rem; + height: 3rem; + flex-shrink: 0; + border-radius: var(--t-radius-card, 0.25rem); + overflow: hidden; background: var(--t-surface-sunken); + + & img { + width: 100%; + height: 100%; + object-fit: cover; + } + } + + .search-result-details { + flex: 1; + min-width: 0; } .search-result-title { + font-size: var(--t-text-small); + font-weight: 500; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; color: var(--t-text-primary); } .search-result-meta { + font-size: var(--t-text-caption); color: var(--t-text-tertiary); + + & span { + margin-left: 0.5rem; + } } .search-hint { + padding: 1.5rem; color: var(--t-text-tertiary); + font-size: var(--t-text-small); } /* ── Shop footer ── */ @@ -542,21 +672,65 @@ border-top: 1px solid var(--t-border-default); } + .shop-footer-inner { + max-width: 80rem; + margin-inline: auto; + padding: 3rem 1rem; + } + + .footer-grid { + display: grid; + grid-template-columns: 1fr; + gap: 3rem; + } + + .footer-links { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 2rem; + } + .footer-heading { font-family: var(--t-font-heading); + font-weight: 600; + font-size: var(--t-text-small); + margin-bottom: 1rem; color: var(--t-text-primary); } + .footer-nav { + display: flex; + flex-direction: column; + gap: 0.5rem; + font-size: var(--t-text-small); + list-style: none; + margin: 0; + padding: 0; + } + .footer-link { color: var(--t-text-secondary); cursor: pointer; + transition: opacity 0.15s ease; + + &:hover { + opacity: 0.8; + } } .footer-bottom { + margin-top: 3rem; + padding-top: 2rem; + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: center; + gap: 1rem; border-top: 1px solid var(--t-border-subtle); } .footer-copyright { + font-size: var(--t-text-caption); color: var(--t-text-tertiary); } @@ -1081,7 +1255,24 @@ /* ── Checkout success ── */ + .checkout-main { + max-width: 48rem; + padding-block: 4rem; + } + + .checkout-header { + text-align: center; + margin-bottom: 3rem; + } + .checkout-icon { + display: inline-flex; + align-items: center; + justify-content: center; + width: 4rem; + height: 4rem; + border-radius: 9999px; + margin-bottom: 1.5rem; background-color: var(--t-accent); color: var(--t-accent-contrast); } @@ -1089,52 +1280,120 @@ .checkout-heading { font-family: var(--t-font-heading); color: var(--t-text-primary); + + &:where(h1) { + font-size: var(--t-text-3xl, 1.875rem); + font-weight: 700; + margin-bottom: 0.75rem; + } + + &:where(h2) { + font-size: var(--t-text-large, 1.125rem); + font-weight: 600; + margin-bottom: 1rem; + } } .checkout-meta { + font-size: var(--t-text-large, 1.125rem); + margin-bottom: 0.5rem; color: var(--t-text-secondary); & strong { color: var(--t-text-primary); } + + &:last-child { + font-size: var(--t-text-base, 1rem); + margin-bottom: 0; + } + } + + .checkout-card { + padding: 1.5rem; + margin-bottom: 2rem; } .checkout-items { + display: flex; + flex-direction: column; + gap: 1rem; + margin-bottom: 1.5rem; list-style: none; - margin: 0; + margin-top: 0; padding: 0; } .checkout-item { - border-color: var(--t-border-default); + display: flex; + justify-content: space-between; + align-items: flex-start; + padding-bottom: 1rem; + border-bottom: 1px solid var(--t-border-default); + + &:last-child { + border-bottom: none; + padding-bottom: 0; + } } .checkout-item-name { + font-weight: 500; color: var(--t-text-primary); } .checkout-item-detail { + font-size: var(--t-text-small, 0.875rem); color: var(--t-text-secondary); } .checkout-item-price { + font-weight: 500; color: var(--t-text-primary); } .checkout-total-border { - border-color: var(--t-border-default); + border-top: 1px solid var(--t-border-default); + padding-top: 1rem; } .checkout-total { + display: flex; + justify-content: space-between; + font-size: var(--t-text-large, 1.125rem); color: var(--t-text-primary); } + .checkout-total-label { + font-weight: 600; + } + + .checkout-total-amount { + font-weight: 700; + } + .checkout-shipping-address { color: var(--t-text-secondary); } + .checkout-actions { + text-align: center; + } + + .checkout-cta { + padding: 0.75rem 2rem; + } + .checkout-pending-icon { + display: inline-flex; + align-items: center; + justify-content: center; + width: 4rem; + height: 4rem; + border-radius: 9999px; + margin-bottom: 1.5rem; background-color: var(--t-surface-sunken); + animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; } .checkout-pending-spinner { @@ -1142,34 +1401,140 @@ } .checkout-pending-text { + font-size: var(--t-text-large, 1.125rem); + margin-bottom: 2rem; color: var(--t-text-secondary); } .checkout-pending-hint { + font-size: var(--t-text-small, 0.875rem); color: var(--t-text-tertiary); } .checkout-contact-link { + text-decoration: underline; color: var(--t-accent); } + @keyframes pulse { + 50% { opacity: 0.5; } + } + /* ── Error page ── */ .error-main { + display: flex; + align-items: center; + justify-content: center; min-height: calc(100vh - 4rem); } - /* ── PDP variant fallback ── */ + .error-container { + max-width: 42rem; + padding-block: 4rem; + } + + /* ── PDP page ── */ + + .pdp-grid { + display: grid; + grid-template-columns: 1fr; + gap: 3rem; + margin-bottom: 4rem; + } .pdp-variant-fallback { + margin-bottom: 1.5rem; + font-size: var(--t-text-small, 0.875rem); color: var(--t-text-secondary); } - /* ── Cart page list ── */ + /* ── Cart page ── */ + + .cart-grid { + display: grid; + grid-template-columns: 1fr; + gap: 2rem; + } + + .cart-page-card { + padding: 1rem; + } .cart-page-list { + display: flex; + flex-direction: column; + gap: 1rem; list-style: none; margin: 0; padding: 0; } + + /* ── Contact page ── */ + + .contact-main { + max-width: 56rem; + padding-top: 0; + padding-bottom: 4rem; + } + + .contact-grid { + display: grid; + gap: 2rem; + margin-bottom: 3rem; + } + + .contact-sidebar { + display: flex; + flex-direction: column; + gap: 1.5rem; + } + + /* ── Content page image ── */ + + .content-hero-image { + width: 100%; + height: 300px; + object-fit: cover; + } + + /* ── Screen reader only ── */ + + .sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; + } + + /* ── Responsive breakpoints ── */ + + @media (min-width: 640px) { + .page-container { padding-inline: 1.5rem; } + .shop-header { padding: 0.75rem 1rem; } + .shop-footer-inner { padding-inline: 1.5rem; } + .search-kbd { display: flex; } + } + + @media (min-width: 768px) { + .shop-container[data-bottom-nav] { padding-bottom: 0; } + .shop-header { padding: 1rem 2rem; } + .shop-nav { display: flex; gap: 1.5rem; } + .mobile-bottom-nav { display: none; } + .footer-grid { grid-template-columns: repeat(2, 1fr); } + .footer-bottom { flex-direction: row; } + .pdp-grid { grid-template-columns: repeat(2, 1fr); } + .contact-grid { grid-template-columns: repeat(2, 1fr); } + } + + @media (min-width: 1024px) { + .page-container { padding-inline: 2rem; } + .shop-footer-inner { padding-inline: 2rem; } + .cart-grid { grid-template-columns: 2fr 1fr; } + } } diff --git a/lib/simpleshop_theme_web/components/page_templates/cart.html.heex b/lib/simpleshop_theme_web/components/page_templates/cart.html.heex index 811fdc4..8f01965 100644 --- a/lib/simpleshop_theme_web/components/page_templates/cart.html.heex +++ b/lib/simpleshop_theme_web/components/page_templates/cart.html.heex @@ -1,20 +1,20 @@ <.shop_layout {layout_assigns(assigns)} active_page="cart"> -
+
<.page_title text="Your basket" /> <%= if @cart_items == [] do %> <.cart_empty_state mode={@mode} /> <% else %> -
-
+
+
    <%= for item <- @cart_items do %>
  • - <.shop_card class="p-4"> + <.shop_card class="cart-page-card"> <.cart_item_row item={item} size={:default} show_quantity_controls mode={@mode} />
  • diff --git a/lib/simpleshop_theme_web/components/page_templates/checkout_success.html.heex b/lib/simpleshop_theme_web/components/page_templates/checkout_success.html.heex index 55f8b9b..ecee4f7 100644 --- a/lib/simpleshop_theme_web/components/page_templates/checkout_success.html.heex +++ b/lib/simpleshop_theme_web/components/page_templates/checkout_success.html.heex @@ -1,10 +1,11 @@ <.shop_layout {layout_assigns(assigns)} active_page="checkout"> -
    +
    <%= if @order && @order.payment_status == "paid" do %> -
    -
    +
    +
    -

    +

    Thank you for your order

    -

    +

    Order {@order.order_number}

    @@ -29,38 +30,38 @@ <% end %>
    - <.shop_card class="p-6 mb-8"> -

    + <.shop_card class="checkout-card"> +

    Order details

    -
      +
        <%= for item <- @order.items do %> -
      • +
      • -

        +

        {item.product_name}

        <%= if item.variant_title do %> -

        +

        {item.variant_title}

        <% end %> -

        +

        Qty: {item.quantity}

        - + {SimpleshopTheme.Cart.format_price(item.unit_price * item.quantity)}
      • <% end %>
      -
      -
      - Total - +
      +
      + Total + {SimpleshopTheme.Cart.format_price(@order.total)}
      @@ -68,8 +69,8 @@ <%= if @order.shipping_address != %{} do %> - <.shop_card class="p-6 mb-8"> -

      + <.shop_card class="checkout-card"> +

      Shipping to

      @@ -86,18 +87,19 @@ <% end %> -
      - <.shop_link_button href="/collections/all" class="px-8 py-3"> +
      + <.shop_link_button href="/collections/all" class="checkout-cta"> Continue shopping
      <% else %> <%!-- Payment pending or order not found --%> -
      -
      +
      +
      -

      +

      Processing your payment

      -

      +

      Please wait while we confirm your payment. This usually takes a few seconds.

      -

      +

      If this page doesn't update, please <.link navigate="/contact" - class="checkout-contact-link underline" + class="checkout-contact-link" >contact us.

      diff --git a/lib/simpleshop_theme_web/components/page_templates/collection.html.heex b/lib/simpleshop_theme_web/components/page_templates/collection.html.heex index b2ccddf..3d3ebc7 100644 --- a/lib/simpleshop_theme_web/components/page_templates/collection.html.heex +++ b/lib/simpleshop_theme_web/components/page_templates/collection.html.heex @@ -2,7 +2,7 @@
      <.collection_header title="All Products" product_count={length(assigns[:products] || [])} /> -
      +
      <.filter_bar categories={assigns[:categories] || []} /> <.product_grid theme_settings={@theme_settings}> diff --git a/lib/simpleshop_theme_web/components/page_templates/contact.html.heex b/lib/simpleshop_theme_web/components/page_templates/contact.html.heex index dfa579d..d70e05f 100644 --- a/lib/simpleshop_theme_web/components/page_templates/contact.html.heex +++ b/lib/simpleshop_theme_web/components/page_templates/contact.html.heex @@ -1,15 +1,15 @@ <.shop_layout {layout_assigns(assigns)} active_page="contact"> -
      +
      <.hero_section variant={:page} title="Get in touch" description="Sample contact page for the demo store. Add your own message here – something friendly about how customers can reach you." /> -
      +
      <.contact_form email="hello@example.com" /> -
      +
      <.order_tracking_card /> <.info_card diff --git a/lib/simpleshop_theme_web/components/page_templates/content.html.heex b/lib/simpleshop_theme_web/components/page_templates/content.html.heex index 2fda258..6f62480 100644 --- a/lib/simpleshop_theme_web/components/page_templates/content.html.heex +++ b/lib/simpleshop_theme_web/components/page_templates/content.html.heex @@ -22,7 +22,7 @@ source_width={1200} alt={@image_alt} sizes="(max-width: 800px) 100vw, 800px" - class="w-full h-[300px] object-cover" + class="content-hero-image" />
      <% end %> diff --git a/lib/simpleshop_theme_web/components/page_templates/error.html.heex b/lib/simpleshop_theme_web/components/page_templates/error.html.heex index e143aa2..ef6da17 100644 --- a/lib/simpleshop_theme_web/components/page_templates/error.html.heex +++ b/lib/simpleshop_theme_web/components/page_templates/error.html.heex @@ -1,9 +1,9 @@ <.shop_layout {layout_assigns(assigns)} active_page="error" error_page>
      -
      +
      <.hero_section variant={:error} pre_title={@error_code} diff --git a/lib/simpleshop_theme_web/components/page_templates/pdp.html.heex b/lib/simpleshop_theme_web/components/page_templates/pdp.html.heex index 06281fd..94bcb5e 100644 --- a/lib/simpleshop_theme_web/components/page_templates/pdp.html.heex +++ b/lib/simpleshop_theme_web/components/page_templates/pdp.html.heex @@ -1,5 +1,5 @@ <.shop_layout {layout_assigns(assigns)} active_page="pdp"> -
      +
      <.breadcrumb items={ if @product.category do @@ -19,7 +19,7 @@ mode={@mode} /> -
      +
      <.product_gallery images={@gallery_images} product_name={@product.title} />
      @@ -38,7 +38,7 @@ <%!-- Fallback for products with no variant options --%>
      One size
      diff --git a/lib/simpleshop_theme_web/components/shop_components/layout.ex b/lib/simpleshop_theme_web/components/shop_components/layout.ex index 294a14f..5566ebc 100644 --- a/lib/simpleshop_theme_web/components/shop_components/layout.ex +++ b/lib/simpleshop_theme_web/components/shop_components/layout.ex @@ -100,7 +100,8 @@ defmodule SimpleshopThemeWeb.ShopComponents.Layout do
      <.skip_link /> @@ -177,10 +178,10 @@ defmodule SimpleshopThemeWeb.ShopComponents.Layout do def mobile_bottom_nav(assigns) do ~H"""