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",