From 4b22bb4a4b77d19a6974c4f085b29676de3d4a95 Mon Sep 17 00:00:00 2001 From: Jamey Greenwood Date: Tue, 20 Jan 2026 22:03:42 +0000 Subject: [PATCH] feat: add mobile bottom navigation bar Replace cramped horizontal nav on mobile with a fixed bottom tab bar for thumb-friendly navigation. The header nav is now hidden on mobile (<768px) and the bottom nav provides Home, Shop, About, and Contact links with icons. - Add mobile_bottom_nav component with icon + label nav items - Active page has accent-colored background highlight and larger icon - Add shadow to lift nav visually off the page - Update all page templates with bottom padding and bottom nav - Remove CSS rule that was overriding Tailwind's hidden class - Responsive header padding (tighter on mobile) Co-Authored-By: Claude Opus 4.5 --- assets/css/theme-layer2-attributes.css | 6 +- .../components/page_templates/about.html.heex | 4 +- .../components/page_templates/cart.html.heex | 4 +- .../page_templates/collection.html.heex | 4 +- .../page_templates/contact.html.heex | 4 +- .../components/page_templates/home.html.heex | 4 +- .../components/page_templates/pdp.html.heex | 4 +- .../components/shop_components.ex | 174 +++++++++++++++++- .../live/shop_live/collection.ex | 4 +- 9 files changed, 189 insertions(+), 19 deletions(-) diff --git a/assets/css/theme-layer2-attributes.css b/assets/css/theme-layer2-attributes.css index 190d949..fd9e732 100644 --- a/assets/css/theme-layer2-attributes.css +++ b/assets/css/theme-layer2-attributes.css @@ -413,10 +413,8 @@ } } -/* Shop nav display */ -.shop-nav { - display: flex; -} +/* Shop nav display - hidden on mobile, flex on md+ (via Tailwind classes in component) */ +/* Note: Removed explicit display:flex here as it overrides Tailwind's hidden class */ /* ============================================= STANDALONE COMPONENTS (Context-independent) diff --git a/lib/simpleshop_theme_web/components/page_templates/about.html.heex b/lib/simpleshop_theme_web/components/page_templates/about.html.heex index fbbacf8..2abe244 100644 --- a/lib/simpleshop_theme_web/components/page_templates/about.html.heex +++ b/lib/simpleshop_theme_web/components/page_templates/about.html.heex @@ -1,4 +1,4 @@ -
+
<.skip_link /> <%= if @theme_settings.announcement_bar do %> @@ -24,4 +24,6 @@ <.cart_drawer cart_items={@cart_items} subtotal={@cart_subtotal} mode={@mode} /> <.search_modal hint_text={~s(Try searching for "mountain", "forest", or "ocean")} /> + + <.mobile_bottom_nav active_page="about" mode={@mode} />
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 7371879..308d556 100644 --- a/lib/simpleshop_theme_web/components/page_templates/cart.html.heex +++ b/lib/simpleshop_theme_web/components/page_templates/cart.html.heex @@ -1,4 +1,4 @@ -
+
<.skip_link /> <%= if @theme_settings.announcement_bar do %> @@ -16,4 +16,6 @@ <.cart_drawer cart_items={@cart_items} subtotal={@cart_subtotal} mode={@mode} /> <.search_modal hint_text={~s(Try searching for "mountain", "forest", or "ocean")} /> + + <.mobile_bottom_nav active_page="cart" mode={@mode} />
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 2dd9029..c3e6619 100644 --- a/lib/simpleshop_theme_web/components/page_templates/collection.html.heex +++ b/lib/simpleshop_theme_web/components/page_templates/collection.html.heex @@ -1,4 +1,4 @@ -
+
<.skip_link /> <%= if @theme_settings.announcement_bar do %> @@ -32,4 +32,6 @@ <.cart_drawer cart_items={@cart_items} subtotal={@cart_subtotal} mode={@mode} /> <.search_modal hint_text={~s(Try searching for "mountain", "forest", or "ocean")} /> + + <.mobile_bottom_nav active_page="collection" mode={@mode} />
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 1f2ebab..b465d0e 100644 --- a/lib/simpleshop_theme_web/components/page_templates/contact.html.heex +++ b/lib/simpleshop_theme_web/components/page_templates/contact.html.heex @@ -1,4 +1,4 @@ -
+
<.skip_link /> <%= if @theme_settings.announcement_bar do %> @@ -44,4 +44,6 @@ <.cart_drawer cart_items={@cart_items} subtotal={@cart_subtotal} mode={@mode} /> <.search_modal hint_text={~s(Try searching for "mountain", "forest", or "ocean")} /> + + <.mobile_bottom_nav active_page="contact" mode={@mode} />
diff --git a/lib/simpleshop_theme_web/components/page_templates/home.html.heex b/lib/simpleshop_theme_web/components/page_templates/home.html.heex index 31dadc2..d570e30 100644 --- a/lib/simpleshop_theme_web/components/page_templates/home.html.heex +++ b/lib/simpleshop_theme_web/components/page_templates/home.html.heex @@ -1,4 +1,4 @@ -
+
<.skip_link /> <%= if @theme_settings.announcement_bar do %> @@ -40,4 +40,6 @@ <.cart_drawer cart_items={@cart_items} subtotal={@cart_subtotal} mode={@mode} /> <.search_modal hint_text={~s(Try searching for "mountain", "forest", or "ocean")} /> + + <.mobile_bottom_nav active_page="home" mode={@mode} />
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 7d0d0ce..6034ed2 100644 --- a/lib/simpleshop_theme_web/components/page_templates/pdp.html.heex +++ b/lib/simpleshop_theme_web/components/page_templates/pdp.html.heex @@ -1,4 +1,4 @@ -
+
<.skip_link /> <%= if @theme_settings.announcement_bar do %> @@ -41,4 +41,6 @@ <.cart_drawer cart_items={@cart_items} subtotal={@cart_subtotal} mode={@mode} /> <.search_modal hint_text={~s(Try searching for "mountain", "forest", or "ocean")} /> + + <.mobile_bottom_nav active_page="pdp" mode={@mode} />
diff --git a/lib/simpleshop_theme_web/components/shop_components.ex b/lib/simpleshop_theme_web/components/shop_components.ex index 62dc797..bf4ad66 100644 --- a/lib/simpleshop_theme_web/components/shop_components.ex +++ b/lib/simpleshop_theme_web/components/shop_components.ex @@ -53,6 +53,164 @@ defmodule SimpleshopThemeWeb.ShopComponents do """ end + @doc """ + Renders a mobile bottom navigation bar. + + This component provides thumb-friendly navigation for mobile devices, + following modern UX best practices. It's hidden on larger screens where + the standard header navigation is used. + + ## Attributes + + * `active_page` - Required. The current page identifier (e.g., "home", "collection", "about", "contact"). + * `mode` - Optional. Either `:live` (default) for real navigation or + `:preview` for theme preview mode with phx-click handlers. + * `cart_count` - Optional. Number of items in cart for badge display. Default: 0. + + ## Examples + + <.mobile_bottom_nav active_page="home" /> + <.mobile_bottom_nav active_page="collection" mode={:preview} /> + """ + attr :active_page, :string, required: true + attr :mode, :atom, default: :live + attr :cart_count, :integer, default: 0 + + def mobile_bottom_nav(assigns) do + ~H""" + + """ + end + + attr :icon, :atom, required: true + attr :label, :string, required: true + attr :page, :string, required: true + attr :href, :string, required: true + attr :active_page, :string, required: true + attr :active_pages, :list, default: nil + attr :mode, :atom, default: :live + + defp mobile_nav_item(assigns) do + active_pages = assigns.active_pages || [assigns.page] + is_current = assigns.active_page in active_pages + assigns = assign(assigns, :is_current, is_current) + + ~H""" +
  • + <%= if @mode == :preview do %> + + <.nav_icon icon={@icon} size={if @is_current, do: "w-6 h-6", else: "w-5 h-5"} /> + {@label} + + <% else %> + + <.nav_icon icon={@icon} size={if @is_current, do: "w-6 h-6", else: "w-5 h-5"} /> + {@label} + + <% end %> +
  • + """ + end + + defp nav_icon(%{icon: :home} = assigns) do + assigns = assign_new(assigns, :size, fn -> "w-5 h-5" end) + + ~H""" + + + + + """ + end + + defp nav_icon(%{icon: :shop} = assigns) do + assigns = assign_new(assigns, :size, fn -> "w-5 h-5" end) + + ~H""" + + + + + + + """ + end + + defp nav_icon(%{icon: :about} = assigns) do + assigns = assign_new(assigns, :size, fn -> "w-5 h-5" end) + + ~H""" + + + + + + """ + end + + defp nav_icon(%{icon: :contact} = assigns) do + assigns = assign_new(assigns, :size, fn -> "w-5 h-5" end) + + ~H""" + + + + + """ + end + @doc """ Renders the search modal overlay. @@ -250,8 +408,8 @@ defmodule SimpleshopThemeWeb.ShopComponents do def shop_header(assigns) do ~H"""
    <%= if @theme_settings.header_background_enabled && @header_image do %>
    @@ -266,7 +424,7 @@ defmodule SimpleshopThemeWeb.ShopComponents do />
    -