From 6f419477df051fc0aad8b02f4a318c51583a0fc5 Mon Sep 17 00:00:00 2001 From: claude Date: Thu, 18 Jun 2026 08:54:30 +0000 Subject: [PATCH] lumiere: fix mobile overflow + restore tile view with XS/M/L zoom MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Overflow fix: ActionsCell (up to 9 icon buttons) was assigned to the `auto` column in the compact row, blowing out the row to ~300px. Replace with ActionsDropdown (single ⋮ button) on mobile via d-md-none / d-none d-md-flex, same pattern as the classic table. Tile view: remove the isMobile force to compact (effectiveScale=0). Add a mobile-only toolbar row (d-flex d-md-none) combining the filter pills and a new XS/M/L zoom control: - XS (scale=0) → compact rows - M (scale=1) → 2 tiles per row (CSS: repeat(2, 1fr)) - L (scale≥1.35) → 1 tile per row (CSS: 1fr, class --mobile-1col) The --lum-card-scale inline var is suppressed on mobile so CSS media query controls thumbnail height (0.85 for 2-col, 1.3 for 1-col). Co-Authored-By: Claude Sonnet 4.6 --- .../components/project-list-lumiere.tsx | 84 ++++++++++++++----- .../pages/project-list-lumiere.scss | 51 ++++++++--- 2 files changed, 103 insertions(+), 32 deletions(-) diff --git a/services/web/frontend/js/features/project-list/components/project-list-lumiere.tsx b/services/web/frontend/js/features/project-list/components/project-list-lumiere.tsx index c11f654302..e132d6964f 100644 --- a/services/web/frontend/js/features/project-list/components/project-list-lumiere.tsx +++ b/services/web/frontend/js/features/project-list/components/project-list-lumiere.tsx @@ -41,6 +41,7 @@ import FormatCell from './table/cells/format-cell' import OwnerCell from './table/cells/owner-cell' import LastUpdatedCell from './table/cells/last-updated-cell' import ActionsCell from './table/cells/actions-cell' +import ActionsDropdown from './dropdown/actions-dropdown' import InlineTags from './table/cells/inline-tags' import WelcomePageContent from './welcome-page-content' @@ -68,6 +69,12 @@ const ZOOM_OPTIONS: { value: ZoomLevel; label: string }[] = [ { value: 1.35, label: 'M' }, { value: 1.75, label: 'L' }, ] +// Mobile offers 3 sizes: XS (rows), M (2 tiles), L (1 tile) +const MOBILE_ZOOM_OPTIONS: { value: ZoomLevel; label: string }[] = [ + { value: 0, label: 'XS' }, + { value: 1, label: 'M' }, + { value: 1.35, label: 'L' }, +] const ZOOM_STORAGE_KEY = 'lumiere-card-scale' function useLumiereCardScale(): [ZoomLevel, (z: ZoomLevel) => void] { @@ -245,7 +252,13 @@ const ProjectCardCompact = memo(function ProjectCardCompact({
- + {/* ⋮ dropdown on mobile (single button), icon strip on desktop */} +
+ +
+
+ +
) @@ -271,8 +284,14 @@ export function ProjectListLumiere() { selectFilter, } = useProjectListContext() - // On mobile, always use the compact (XS) row view — cards overflow on small screens - const effectiveScale = isMobile ? 0 : cardScale + // On mobile: M option (scale=1) → 2 tiles/row; L (scale≥1.35) → 1 tile/row. + // CSS overrides the card grid columns on mobile; we only add a class for L. + const mobileIsL = isMobile && cardScale !== 0 && cardScale >= 1.35 + const activeMobileZoom = cardScale === 0 + ? 0 + : cardScale >= 1.35 + ? 1.35 + : 1 const selectedTag = tags.find(tag => tag._id === selectedTagId) const allSelected = @@ -351,19 +370,42 @@ export function ProjectListLumiere() { ))} - {/* Mobile-only filter pills replacing the hidden sidebar */} -
- {MOBILE_FILTERS.map(({ f, label }) => ( - - ))} + {/* Mobile-only: filter pills + zoom control */} +
+
+ {MOBILE_FILTERS.map(({ f, label }) => ( + + ))} +
+
+ {MOBILE_ZOOM_OPTIONS.map(({ value, label }) => ( + + ))} +
{t('no_projects')}

) : (
{visibleProjects.map(project => - effectiveScale === 0 ? ( + cardScale === 0 ? ( ) : ( diff --git a/services/web/frontend/stylesheets/pages/project-list-lumiere.scss b/services/web/frontend/stylesheets/pages/project-list-lumiere.scss index 356c03c0fe..7fc0640d99 100644 --- a/services/web/frontend/stylesheets/pages/project-list-lumiere.scss +++ b/services/web/frontend/stylesheets/pages/project-list-lumiere.scss @@ -870,12 +870,15 @@ $lum-noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' wi opacity: 1; } - // ── Mobile compact row — 2-line layout ──────────────────────────────────── - // On small screens the 6-column grid overflows. Switch to: - // Row 1: [checkbox] [name+tags] [actions] - // Row 2: [checkbox] [format · owner · date] + // ── Mobile layout overrides ──────────────────────────────────────────────── @media (max-width: 767px) { + + // Compact row: 2-line layout + // Row 1: [checkbox] [name+tags] [⋮ action] + // Row 2: [checkbox] [format · owner · date] + // ActionsDropdown (single ⋮ button) is shown via d-md-none; ActionsCell + // (many icon buttons) is hidden on mobile, so the auto column stays tiny. .lumiere-compact-row { grid-template-columns: 28px 1fr auto; grid-template-rows: auto auto; @@ -896,6 +899,9 @@ $lum-noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' wi grid-column: 3; grid-row: 1; align-self: center; + opacity: 1; + + .action-btn { opacity: 1; } } // Meta wrapper becomes a flex row spanning the second line @@ -907,30 +913,49 @@ $lum-noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' wi grid-column: 2 / 4; grid-row: 2; } + } - // Actions always visible — no hover on touch devices - @media (hover: none) { - .lumiere-compact-actions .action-btn { - opacity: 1; - } + // Card tile grid: M = 2 tiles/row, L = 1 tile/row. + // --lum-card-scale inline var is suppressed on mobile (isMobile=true in JS), + // so the CSS custom property here wins for thumbnail sizing. + .lumiere-card-grid:not(.lumiere-card-grid--compact) { + grid-template-columns: repeat(2, 1fr); + gap: 0.75rem; + --lum-card-scale: 0.85; + + &.lumiere-card-grid--mobile-1col { + grid-template-columns: 1fr; + --lum-card-scale: 1.3; } } } - // ── Mobile filter pills (replaces hidden sidebar on xs/sm) ──────────────── + // ── Mobile toolbar: filter pills + zoom control ─────────────────────────── + + .lumiere-mobile-toolbar { + align-items: center; + gap: 0.5rem; + width: 100%; + padding: 0.25rem 0 0.5rem; + flex-shrink: 0; + } .lumiere-mobile-filters { overflow-x: auto; + display: flex; gap: 0.45rem; - padding: 0.25rem 0 0.5rem; + flex: 1; + min-width: 0; scrollbar-width: none; -ms-overflow-style: none; - flex-shrink: 0; - width: 100%; &::-webkit-scrollbar { display: none; } } + .lumiere-mobile-zoom { + flex-shrink: 0; + } + .lumiere-mobile-filter-pill { flex-shrink: 0; padding: 0.3rem 0.8rem;