From 7df583a3b2f4beeead8b76c9de06ef3cd9c9d63c Mon Sep 17 00:00:00 2001 From: claude Date: Fri, 12 Jun 2026 12:54:22 +0000 Subject: [PATCH] feat: creative footer, card tags, selection bar theme, i18n fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Footer: dark navy (#0d1b24) with noise texture, teal→blue gradient top stripe; serif author credit on the left, monospace/uppercase meta links on the right; same design on login page - Thumbnail: larger inset (14px top, 12px sides) so more background gradient shows around the preview; subtle drop shadow added - Card tags: projects now show their label chips on the card (colored dot + name, read-only, max 3, no close button inside the link) - Selection bar: btn-secondary recoloured to Lumière palette; dropdown menus rounded with teal accent on hover - i18n fr.json: add n_projects_selected, n_projects_selected_plural, toolbar_selected_projects (×4 variants) Co-Authored-By: Claude Sonnet 4.6 --- .../components/project-list-lumiere.tsx | 29 ++++ .../stylesheets/pages/login-register.scss | 49 +++++- .../pages/project-list-lumiere.scss | 152 +++++++++++++++--- services/web/locales/fr.json | 6 + 4 files changed, 211 insertions(+), 25 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 0329f91bfe..346ecaebce 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 @@ -5,6 +5,7 @@ import { Project } from '../../../../../types/project/dashboard/api' import { getOwnerName } from '../util/project' import { fromNowDate } from '../../../utils/dates' import { ProjectCompiler } from '../../../../../types/project-settings' +import { getTagColor } from '../util/tag' import getMeta from '@/utils/meta' import DefaultNavbar from '@/shared/components/navbar/default-navbar' import Footer from '@/shared/components/footer/footer' @@ -57,10 +58,14 @@ const ProjectCard = memo(function ProjectCard({ }: { project: Project }) { + const { tags } = useProjectListContext() const variant = getFormatVariant(project.compiler, project.quartoFlavor) const ownerName = getOwnerName(project) const date = fromNowDate(project.lastUpdated) const initial = project.name.charAt(0).toUpperCase() || '?' + const projectTags = tags + .filter(tag => tag.project_ids?.includes(project.id)) + .slice(0, 3) return (
@@ -99,6 +104,30 @@ const ProjectCard = memo(function ProjectCard({ )}
+ {projectTags.length > 0 && ( +
+ {projectTags.map(tag => { + const color = getTagColor(tag) + return ( + + + {tag.name} + + ) + })} +
+ )} {date} diff --git a/services/web/frontend/stylesheets/pages/login-register.scss b/services/web/frontend/stylesheets/pages/login-register.scss index 91e3704bb9..6d273fed85 100644 --- a/services/web/frontend/stylesheets/pages/login-register.scss +++ b/services/web/frontend/stylesheets/pages/login-register.scss @@ -52,23 +52,58 @@ } // ── Login page footer — Lumière override ────────────────────────────────────── -// Verso always renders ThinFooter (footer.site-footer), not .fat-footer. +// Same dark-navy treatment as the project page footer. body:has(.login-page) footer.site-footer { - background-color: #c8e4de !important; - color: #1a2e3b !important; - border-top-color: rgba(42, 157, 143, 0.2) !important; + position: relative; + background-color: #0d1b24 !important; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.75' numOctaves='4' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.28'/%3E%3C/svg%3E") !important; + background-size: 200px 200px !important; + background-repeat: repeat !important; + border-top: none !important; + color: rgba(255, 255, 255, 0.38) !important; + font-size: 0.78rem; + letter-spacing: 0.03em; + + &::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 2px; + background: linear-gradient(90deg, #2a9d8f 0%, #3d7ebf 100%); + pointer-events: none; + } a { - color: #64748b !important; + color: rgba(255, 255, 255, 0.38) !important; + text-decoration: none; + transition: color 0.15s ease; &:hover { color: #2a9d8f !important; } } - --link-color: #64748b; + .col-lg-9 .site-footer-items > li:first-child a { + font-family: Georgia, 'Times New Roman', 'DejaVu Serif', serif; + color: rgba(255, 255, 255, 0.55) !important; + } + + .col-lg-3 { + font-family: ui-monospace, 'SFMono-Regular', 'Fira Code', Consolas, monospace; + font-size: 0.70rem; + text-transform: uppercase; + letter-spacing: 0.07em; + } + + .text-muted { + color: rgba(255, 255, 255, 0.16) !important; + } + + --link-color: rgba(255, 255, 255, 0.38); --link-hover-color: #2a9d8f; - --link-visited-color: #64748b; + --link-visited-color: rgba(255, 255, 255, 0.38); } .login-lumiere-card { diff --git a/services/web/frontend/stylesheets/pages/project-list-lumiere.scss b/services/web/frontend/stylesheets/pages/project-list-lumiere.scss index 2a11caa600..93da0b4de3 100644 --- a/services/web/frontend/stylesheets/pages/project-list-lumiere.scss +++ b/services/web/frontend/stylesheets/pages/project-list-lumiere.scss @@ -415,8 +415,8 @@ $lum-noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' wi // selected (:has(input:checked) on the grid). .lumiere-card-checkbox { position: absolute; - top: 8px; - left: 8px; + top: 10px; + left: 10px; z-index: 2; opacity: 0.35; transition: opacity 0.15s ease; @@ -544,17 +544,18 @@ $lum-noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' wi // the card rises beneath it (matching the card's translateY(-3px) lift). .lumiere-card-thumb-img { position: absolute; - top: 6px; - left: 6px; - right: 6px; + top: 14px; + left: 12px; + right: 12px; bottom: 0; - width: calc(100% - 12px); - height: calc(100% - 6px); + width: calc(100% - 24px); + height: calc(100% - 14px); object-fit: cover; object-position: top center; - border-radius: 4px 4px 0 0; + border-radius: 5px 5px 0 0; z-index: 1; transition: transform 0.18s ease; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.18); } .lumiere-card:hover .lumiere-card-thumb-img, @@ -660,28 +661,143 @@ $lum-noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' wi background: rgba(#ede9fe, 0.9); color: #7c4dff; } + + // ── Card tags ───────────────────────────────────────────────────────────── + + .lumiere-card-tags { + display: flex; + flex-wrap: wrap; + gap: 4px; + margin-top: 4px; + } + + .lumiere-card-tag { + display: inline-flex; + align-items: center; + gap: 4px; + font-size: 0.65rem; + font-weight: 500; + padding: 2px 6px; + border-radius: 999px; + border: 1px solid; + max-width: 80px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .lumiere-card-tag-dot { + flex-shrink: 0; + width: 6px; + height: 6px; + border-radius: 50%; + } + + // ── Selection bar — tool buttons ────────────────────────────────────────── + + .lumiere-selection-bar { + .btn-toolbar { gap: 4px; } + .btn-group { gap: 2px; } + + .btn-secondary, + .btn.btn-secondary { + background-color: rgba(255, 255, 255, 0.7) !important; + border-color: rgba($lum-teal, 0.28) !important; + color: $lum-text !important; + border-radius: 8px !important; + font-size: 0.82rem; + transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease; + + &:hover, + &:focus, + &.show { + background-color: rgba($lum-teal, 0.12) !important; + border-color: rgba($lum-teal, 0.45) !important; + color: $lum-teal !important; + } + } + + .dropdown-menu { + border: 1px solid $lum-border; + border-radius: 10px; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); + + .dropdown-header { + font-size: 0.7rem; + font-weight: 700; + letter-spacing: 0.07em; + text-transform: uppercase; + color: $lum-teal; + padding-top: 0.6rem; + } + + .dropdown-item:hover, + .dropdown-item:focus { + background-color: rgba($lum-teal, 0.07); + color: $lum-teal; + } + } + } } // ── Footer — Lumière override ───────────────────────────────────────────────── -// Verso always renders a ThinFooter (site-footer) because showThinFooter is -// forced true for non-SaaS instances. Target footer.site-footer, not .fat-footer. -// !important beats the dark theme rule: -// :root [data-theme='default'] .project-ds-nav-page footer.site-footer (0,3,1). +// Dark-navy footer with noise texture, gradient accent stripe, and two type +// treatments: serif copyright on the left, monospace meta links on the right. +// !important beats :root [data-theme='default'] .project-ds-nav-page footer.site-footer. .project-list-lumiere footer.site-footer { - background-color: #c8e4de !important; - color: $lum-text !important; - border-top-color: $lum-border !important; + position: relative; + background-color: #0d1b24 !important; + background-image: #{$lum-noise} !important; + background-size: 200px 200px !important; + background-repeat: repeat !important; + border-top: none !important; + color: rgba(255, 255, 255, 0.38) !important; + font-size: 0.78rem; + letter-spacing: 0.03em; + + // Teal-to-blue gradient stripe matching the navbar accent + &::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 2px; + background: linear-gradient(90deg, $lum-teal 0%, $lum-blue 100%); + pointer-events: none; + } a { - color: $lum-text-sub !important; + color: rgba(255, 255, 255, 0.38) !important; + text-decoration: none; + transition: color 0.15s ease; &:hover { color: $lum-teal !important; } } - --link-color: #{$lum-text-sub}; + // Author credit — warm serif for a human touch + .col-lg-9 .site-footer-items > li:first-child a { + font-family: Georgia, 'Times New Roman', 'DejaVu Serif', serif; + color: rgba(255, 255, 255, 0.55) !important; + } + + // Meta links (AGPL, source code) — subtle monospace/uppercase treatment + .col-lg-3 { + font-family: ui-monospace, 'SFMono-Regular', 'Fira Code', Consolas, monospace; + font-size: 0.70rem; + text-transform: uppercase; + letter-spacing: 0.07em; + } + + // Pipe separators — nearly invisible + .text-muted { + color: rgba(255, 255, 255, 0.16) !important; + } + + --link-color: rgba(255, 255, 255, 0.38); --link-hover-color: #{$lum-teal}; - --link-visited-color: #{$lum-text-sub}; + --link-visited-color: rgba(255, 255, 255, 0.38); } diff --git a/services/web/locales/fr.json b/services/web/locales/fr.json index f8111f3d4d..c86ce1dbc3 100644 --- a/services/web/locales/fr.json +++ b/services/web/locales/fr.json @@ -769,6 +769,8 @@ "no_pdf_error_title": "Pas de PDF", "no_planned_maintenance": "Il n’y a pas de maintenance prévue pour le moment", "no_preview_available": "Désolé, aucune prévisualisation possible.", + "n_projects_selected": "__count__ projet sélectionné", + "n_projects_selected_plural": "__count__ projets sélectionnés", "no_projects": "Aucun projet", "no_search_results": "Aucun résultat pour la recherche", "no_selection_select_file": "Aucun fichier sélectionné. Veuillez sélectionner un fichier depuis l’arborescence.", @@ -1125,6 +1127,10 @@ "too_many_requests": "Trop de requêtes ont été reçues sur une courte période. Veuillez patienter quelques instants puis réessayer.", "too_recently_compiled": "Ce projet a été compilé très récemment, cette compilation a donc été passée.", "tooltip_hide_pdf": "Cliquez pour cacher le PDF", + "toolbar_selected_projects": "Projets sélectionnés", + "toolbar_selected_projects_management_actions": "Actions de gestion des projets sélectionnés", + "toolbar_selected_projects_remove": "Supprimer les projets sélectionnés", + "toolbar_selected_projects_restore": "Restaurer les projets sélectionnés", "tooltip_show_pdf": "Cliquez pour afficher le PDF", "total_words": "Total des mots", "tr": "Turque",