fix breadcrumb styling: semantic markup, chevron separators, mobile sizing

Rewrite breadcrumb as semantic ol/li with aria-current="page", CSS
chevron separators, 0.875em font size, and ellipsis truncation on
mobile for long product names.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
jamey 2026-02-10 15:58:26 +00:00
parent 8445e9e8b1
commit dc8bf28892
3 changed files with 61 additions and 21 deletions

View File

@ -41,7 +41,8 @@ Issues found during hands-on testing of the deployed prod site on mobile and des
- [x] Product card second image: swipe to reveal on mobile (currently hover-only) - [x] Product card second image: swipe to reveal on mobile (currently hover-only)
### Product detail page ### Product detail page
- [ ] Product category breadcrumbs look bad — review styling/layout - [x] PDP image gallery: scroll-snap carousel with dots (mobile), thumbnails + arrows + lightbox (desktop)
- [x] Product category breadcrumbs look bad — review styling/layout
- [ ] Quantity selector on product page doesn't work - [ ] Quantity selector on product page doesn't work
- [ ] Trust badges: two different tick icons, should use sentence case not title case - [ ] Trust badges: two different tick icons, should use sentence case not title case
- [ ] Real product variants need testing and refinement with live data - [ ] Real product variants need testing and refinement with live data
@ -303,7 +304,8 @@ See: [docs/plans/page-builder.md](docs/plans/page-builder.md) for design
| PageSpeed CI | 516d0d0 | `mix lighthouse` task, prod asset build, gzip, 99-100 mobile scores | | PageSpeed CI | 516d0d0 | `mix lighthouse` task, prod asset build, gzip, 99-100 mobile scores |
| Observability | eaa4bbb | LiveDashboard in prod, ErrorTracker, JSON logging, Oban/LV metrics, os_mon | | Observability | eaa4bbb | LiveDashboard in prod, ErrorTracker, JSON logging, Oban/LV metrics, os_mon |
| Hosting & deployment | — | Alpine Docker, Fly.io, health check, release path fixes | | Hosting & deployment | — | Alpine Docker, Fly.io, health check, release path fixes |
| CI pipeline | — | mix ci/precommit aliases, credo, dialyzer, 612 tests | | PDP image gallery | 8445e9e | Scroll-snap carousel, dots, thumbnails, arrows, lightbox, 9 tests |
| CI pipeline | — | mix ci/precommit aliases, credo, dialyzer, 621 tests |
| Default content pages | 5a43cfc | Generic Content LiveView, delivery/privacy/terms pages, 10 tests | | Default content pages | 5a43cfc | Generic Content LiveView, delivery/privacy/terms pages, 10 tests |
| Transactional emails | — | Plain text order confirmation + shipping notification, 10 tests | | Transactional emails | — | Plain text order confirmation + shipping notification, 10 tests |
| Printify order submission & fulfilment | — | Submit, track, webhooks, polling, admin UI, 33 tests | | Printify order submission & fulfilment | — | Submit, track, webhooks, polling, admin UI, 33 tests |

View File

@ -435,6 +435,43 @@
transition: transform 0.2s ease; transition: transform 0.2s ease;
} }
/* Breadcrumb */
.breadcrumb {
margin-bottom: 1rem;
font-size: 0.875em;
& ol {
display: flex;
flex-wrap: wrap;
align-items: center;
list-style: none;
margin: 0;
padding: 0;
}
& li + li::before {
content: "\203A";
padding-inline: 0.375em;
}
& li[aria-current="page"] {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 20ch;
}
}
@media (min-width: 640px) {
.breadcrumb {
margin-bottom: 2rem;
& li[aria-current="page"] {
max-width: none;
}
}
}
/* PDP Gallery — mobile: swipe + dots, desktop: carousel + thumbs */ /* PDP Gallery — mobile: swipe + dots, desktop: carousel + thumbs */
.pdp-gallery-carousel, .pdp-gallery-carousel,
.pdp-gallery-single { .pdp-gallery-single {

View File

@ -1040,28 +1040,29 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do
def breadcrumb(assigns) do def breadcrumb(assigns) do
~H""" ~H"""
<nav class="mb-8 flex items-center gap-2 text-sm" style="color: var(--t-text-secondary);"> <nav aria-label="Breadcrumb" class="breadcrumb" style="color: var(--t-text-secondary);">
<%= for {item, index} <- Enum.with_index(@items) do %> <ol>
<%= if index > 0 do %> <%= for {item, _index} <- Enum.with_index(@items) do %>
<span>/</span> <%= if item[:current] do %>
<% end %> <li aria-current="page" style="color: var(--t-text-primary);">{item.label}</li>
<%= if item[:current] do %>
<span style="color: var(--t-text-primary);">{item.label}</span>
<% else %>
<%= if @mode == :preview do %>
<a
href="#"
phx-click="change_preview_page"
phx-value-page={item.page}
class="hover:underline"
>
{item.label}
</a>
<% else %> <% else %>
<a href={item.href || "/"} class="hover:underline">{item.label}</a> <li>
<%= if @mode == :preview do %>
<a
href="#"
phx-click="change_preview_page"
phx-value-page={item.page}
class="hover:underline"
>
{item.label}
</a>
<% else %>
<a href={item.href || "/"} class="hover:underline">{item.label}</a>
<% end %>
</li>
<% end %> <% end %>
<% end %> <% end %>
<% end %> </ol>
</nav> </nav>
""" """
end end