Files
CollectFlow/src/app/globals.css
T
Claude 5ad222431d Carte tendance : refonte du graphique selon les regles data-viz
Petits multiples empiles sur un axe des mois commun, palette validee par
script, etiquetage selectif, survol et vue tableau.

FORME. Trois mesures d'ordres de grandeur differents (qte/magasin,
volumes, magasins) ne peuvent pas partager un cadre : il faudrait deux ou
trois echelles verticales dont l'alignement serait arbitraire, ce qui
inventerait des correlations absentes des donnees. Trois facettes, une
echelle chacune, le meme axe horizontal. La facette PRINCIPALE est la
qte/magasin.

COULEUR. Slots 1-3 de la palette categorielle de reference, en variables
CSS, avec un jeu de pas CHOISI pour le fond sombre (pas un eclaircissement
automatique). Palette passee au validateur dans les deux modes, toutes
paires : bande de clarte, plancher de chroma, separation daltonienne
(pire paire ΔE 9,2 clair / 9,4 sombre) et contraste. En mode clair l'aqua
tombe a 2,82:1 — la regle de relief s'applique, d'ou etiquettes visibles
ET vue tableau, toutes deux presentes.

La teinte de tendance (vert/rouge/gris) ne vit plus que dans la tuile de
verdict, avec fleche et libelle : un etat ne se signale jamais par la
couleur seule, et la meme couleur ne veut pas dire deux choses.

LISIBILITE. Etiquetage selectif — l'extreme et le dernier mois, pas les 36
valeurs. Le texte ne porte jamais la couleur d'une serie (illisible en
teinte claire) : l'identite vient du repere pose a cote. Filets de base
pleins et discrets, jamais pointilles. Reticule au survol reliant les
trois facettes au meme mois, et vue tableau depliable : aucune valeur
n'est accessible au seul survol.

En-tete refait en tuiles : verdict, qte/magasin du dernier mois, magasins
vendeurs. Chiffres proportionnels sur les valeurs mises en avant,
tabulaires seulement dans le tableau ou les colonnes s'alignent.

Rendu verifie en image dans les deux themes avant livraison : la version
precedente faisait passer l'etiquette du pic par-dessus le titre de la
facette — elle bascule desormais sous le point quand elle toucherait le
bandeau.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DfqUihgixw4K1AmJhizWiu
2026-08-01 09:58:31 +00:00

499 lines
12 KiB
CSS

@import "tailwindcss";
/* Police de l'app : Inter (chargée via <link> dans le layout, fallback système).
Définie ici pour que l'utilitaire `font-sans` et var(--font-sans) la prennent
sans recourir à next/font/google (pas de fetch réseau au build). */
@theme {
--font-sans: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
}
/* ═══════════════════════════════════════════
CollectFlow — Apple-inspired Design System
═══════════════════════════════════════════ */
/* ── Dark Mode (default) ── */
:root {
/* Apple macOS dark aligned to Slate palette as per UX Spec */
--bg-base: #020617;
/* Slate 950 */
--bg-surface: #0f172a;
/* Slate 900 */
--bg-elevated: #1e293b;
/* Slate 800 */
--bg-overlay: rgba(15, 23, 42, 0.85);
/* Slate 900 with opacity */
--border: #334155;
/* Slate 700 */
/* ── Palette data-viz (3 séries) ──
Slots 1-3 de la palette catégorielle de référence, pas-de-teinte propre au
fond sombre. Validée sur ce fond : bande de clarté, plancher de chroma,
séparation daltonienne (pire paire ΔE 9,4) et contraste ≥ 3:1 — toutes
paires, pas seulement les adjacentes. Ne pas retoucher une valeur isolément :
c'est l'ENSEMBLE qui passe les contrôles. */
--viz-1: #3987e5;
--viz-2: #d95926;
--viz-3: #199e70;
--border-strong: #475569;
/* Slate 600 */
--text-primary: #e2e8f0;
/* Slate 200 */
--text-secondary: #94a3b8;
/* Slate 400 */
--text-muted: #64748b;
/* Slate 500 */
--accent: #2dd4bf;
/* emerald-ish, Apple-tuned */
--accent-bg: rgba(45, 212, 191, 0.10);
--accent-border: rgba(45, 212, 191, 0.20);
/* Semantic UI accents */
--accent-success: #10b981;
--accent-success-bg: rgba(16, 185, 129, 0.10);
--accent-warning: #f59e0b;
--accent-warning-bg: rgba(245, 158, 11, 0.10);
--accent-error: #f43f5e;
--accent-error-bg: rgba(244, 63, 94, 0.10);
/* Primary Action / Brand */
--brand-solid: #10b981;
--brand-hover: #059669;
--action-secondary-bg: #2c2c2e;
--action-secondary-text: #f5f5f7;
--action-secondary-border: rgba(255, 255, 255, 0.1);
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.6), 0 1px 2px rgba(0, 0, 0, 0.4);
--shadow-md: 0 4px 16px rgba(0, 0, 0, 0.7), 0 1px 4px rgba(0, 0, 0, 0.5);
--shadow-lg: 0 8px 32px rgba(0, 0, 0, 0.8), 0 2px 8px rgba(0, 0, 0, 0.6);
}
/* ── Light Mode ── */
.light {
--bg-base: #f5f5f7;
/* Apple light gray */
--bg-surface: #ffffff;
--bg-elevated: #f0f0f5;
--bg-overlay: rgba(255, 255, 255, 0.82);
--border: rgba(0, 0, 0, 0.07);
--border-strong: rgba(0, 0, 0, 0.12);
/* Mêmes teintes, pas choisis pour le fond clair (et non un simple éclaircissement
des valeurs sombres). L'aqua passe sous 3:1 sur blanc : la « règle de relief »
s'applique — étiquettes directes visibles + vue tableau, toutes deux présentes. */
--viz-1: #2a78d6;
--viz-2: #eb6834;
--viz-3: #1baf7a;
--text-primary: #1d1d1f;
/* Apple dark */
--text-secondary: #515154;
--text-muted: #86868b;
--accent: #0d9488;
/* deeper teal in light */
--accent-bg: rgba(13, 148, 136, 0.08);
--accent-border: rgba(13, 148, 136, 0.18);
/* Semantic UI accents (Light Mode Adjustments) */
--accent-success: #059669;
--accent-success-bg: rgba(5, 150, 105, 0.08);
--accent-warning: #d97706;
--accent-warning-bg: rgba(217, 119, 6, 0.08);
--accent-error: #e11d48;
--accent-error-bg: rgba(225, 29, 72, 0.08);
/* Primary Action / Brand (Light) */
--brand-solid: #0d9488;
--brand-hover: #0f766e;
--action-secondary-bg: #ffffff;
--action-secondary-text: #1d1d1f;
--action-secondary-border: rgba(0, 0, 0, 0.1);
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.08), 0 1px 2px rgba(0, 0, 0, 0.05);
--shadow-md: 0 4px 16px rgba(0, 0, 0, 0.10), 0 1px 4px rgba(0, 0, 0, 0.06);
--shadow-lg: 0 8px 32px rgba(0, 0, 0, 0.12), 0 2px 8px rgba(0, 0, 0, 0.08);
}
@layer base {
html {
color-scheme: dark;
/* Force no system scroll jump */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
html.light {
color-scheme: light;
}
body {
background-color: var(--bg-base);
color: var(--text-primary);
font-family: var(--font-sans, -apple-system, "Inter", system-ui, sans-serif);
font-size: 14px;
line-height: 1.5;
transition: background-color 0.25s cubic-bezier(0.4, 0, 0.2, 1),
color 0.25s cubic-bezier(0.4, 0, 0.2, 1);
}
/* Apple-style focus ring */
*:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
border-radius: 4px;
}
}
/* ── Typography ── */
.text-primary {
color: var(--text-primary);
}
.text-secondary {
color: var(--text-secondary);
}
.text-muted {
color: var(--text-muted);
}
/* ── Surfaces & containers ── */
.surface {
background: var(--bg-surface);
}
.elevated {
background: var(--bg-elevated);
}
.apple-card {
background: var(--bg-surface);
border: 1px solid var(--border);
border-radius: 14px;
overflow: hidden;
box-shadow: var(--shadow-sm);
}
.apple-card-header {
padding: 16px 20px;
background: var(--bg-elevated);
border-bottom: 1px solid var(--border);
}
.apple-card-content {
padding: 20px;
}
/* ── Glass panels (sidebar, header) ── */
.glass {
background: var(--bg-overlay);
backdrop-filter: saturate(180%) blur(20px);
-webkit-backdrop-filter: saturate(180%) blur(20px);
}
/* ── Apple border ── */
.border-apple {
border: 1px solid var(--border);
}
.border-apple-strong {
border: 1px solid var(--border-strong);
}
/* ── Tabular numbers & Mono ── */
.tabular-nums {
font-variant-numeric: tabular-nums;
letter-spacing: -0.015em;
}
.font-mono-nums {
font-family: var(--font-sans, system-ui, sans-serif);
font-variant-numeric: tabular-nums;
letter-spacing: -0.015em;
}
/* ── Refined scrollbar ── */
.overflow-auto::-webkit-scrollbar {
width: 8px;
height: 8px;
}
.overflow-auto::-webkit-scrollbar-track {
background: transparent;
}
.overflow-auto::-webkit-scrollbar-thumb {
background: var(--border-strong);
border-radius: 99px;
}
.overflow-auto::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.2);
}
.light .overflow-auto::-webkit-scrollbar-thumb:hover {
background: rgba(0, 0, 0, 0.18);
}
/* ── Heatmap cells ── */
.heatmap-zero {
background: var(--bg-elevated);
color: var(--text-muted);
}
.heatmap-low {
background: rgba(14, 165, 233, 0.08);
/* sky-500 */
color: rgba(14, 165, 233, 0.70);
}
.heatmap-mid {
background: rgba(14, 165, 233, 0.15);
color: rgba(14, 165, 233, 0.90);
}
.heatmap-high {
background: rgba(14, 165, 233, 0.25);
color: #0ea5e9;
}
.light .heatmap-low {
background: rgba(2, 132, 199, 0.08);
/* sky-600 */
color: #0369a1;
}
.light .heatmap-mid {
background: rgba(2, 132, 199, 0.14);
color: #0284c7;
}
.light .heatmap-high {
background: rgba(2, 132, 199, 0.22);
color: #0369a1;
}
/* ── Apple-style input ── */
.apple-input {
width: 100%;
background: var(--bg-elevated);
border: 1px solid var(--border);
border-radius: 8px;
padding: 8px 12px;
font-size: 13px;
color: var(--text-primary);
transition: border-color 0.15s, box-shadow 0.15s;
}
.apple-input::placeholder {
color: var(--text-muted);
}
.apple-input:focus {
outline: none;
border-color: var(--accent);
box-shadow: 0 0 0 3px var(--accent-bg);
}
/* ── Apple-style Action Buttons (Glossy & Premium v2) ── */
.apple-btn-primary,
.apple-btn-secondary,
.apple-btn-danger,
.apple-btn-ai,
.apple-btn-rules {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 10px;
height: 40px;
padding: 0 20px;
border-radius: 12px;
font-size: 13px;
font-weight: 600;
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
cursor: pointer;
position: relative;
overflow: hidden;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.apple-btn-primary {
background: linear-gradient(180deg, var(--brand-solid) 0%, var(--brand-hover) 100%);
color: #ffffff;
box-shadow: 0 4px 12px rgba(16, 185, 129, 0.25), inset 0 1px 1px rgba(255, 255, 255, 0.2);
}
.apple-btn-secondary,
.apple-btn-rules {
background: var(--bg-surface);
color: var(--text-primary);
border-color: var(--border-strong);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.05);
}
.apple-btn-danger {
background: linear-gradient(180deg, var(--accent-error) 0%, #be123c 100%);
color: #ffffff;
box-shadow: 0 4px 12px rgba(244, 63, 94, 0.25), inset 0 1px 1px rgba(255, 255, 255, 0.2);
}
.apple-btn-ai {
background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 50%, #d946ef 100%);
color: #ffffff;
box-shadow: 0 4px 15px rgba(139, 92, 246, 0.3), inset 0 1px 1px rgba(255, 255, 255, 0.3);
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
}
.apple-btn-primary:hover,
.apple-btn-danger:hover,
.apple-btn-ai:hover {
filter: brightness(1.1);
transform: translateY(-1px);
}
.apple-btn-secondary:hover,
.apple-btn-rules:hover {
background: var(--bg-elevated);
transform: translateY(-1px);
}
.apple-btn-primary:active,
.apple-btn-secondary:active,
.apple-btn-danger:active,
.apple-btn-ai:active,
.apple-btn-rules:active {
transform: scale(0.97) translateY(0);
}
/* Glass effect overlay for loading/success */
.btn-glass-overlay {
position: absolute;
inset: 0;
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(4px);
display: flex;
align-items: center;
justify-content: center;
}
@keyframes apple-pulse {
0% {
transform: scale(1);
box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.4);
}
70% {
transform: scale(1.02);
box-shadow: 0 0 0 10px rgba(16, 185, 129, 0);
}
100% {
transform: scale(1);
box-shadow: 0 0 0 0 rgba(16, 185, 129, 0);
}
}
.animate-apple-save {
animation: apple-pulse 2s infinite ease-in-out;
}
.btn-action {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
height: 36px;
padding: 0 16px;
border-radius: 10px;
font-size: 13px;
font-weight: 600;
transition: all 0.15s ease-out;
cursor: pointer;
}
.btn-action:active {
transform: scale(0.96);
}
.btn-action:disabled {
opacity: 0.5;
pointer-events: none;
}
.btn-action-primary {
background: var(--brand-solid);
color: #fff;
border: 1px solid rgba(0, 0, 0, 0.1);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}
.btn-action-primary:hover {
filter: brightness(1.1);
}
.btn-action-secondary {
background: var(--bg-surface);
color: var(--text-primary);
border: 1px solid var(--border-strong);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}
.btn-action-secondary:hover {
background: var(--bg-elevated);
}
.btn-action-ai {
background: var(--accent);
color: #fff;
border: 1px solid rgba(255, 255, 255, 0.1);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}
.btn-action-ai:hover {
filter: brightness(1.1);
}
.btn-action-success {
background: var(--accent-success-bg);
color: var(--accent-success);
border: 1px solid var(--accent-success);
cursor: default;
}
.btn-action-success:active {
transform: none;
}
/* ── Segment control (theme toggle) ── */
.segment-control {
display: inline-flex;
gap: 2px;
padding: 3px;
background: var(--bg-elevated);
border: 1px solid var(--border);
border-radius: 9px;
}
.segment-btn {
display: inline-flex;
align-items: center;
gap: 5px;
padding: 5px 14px;
border-radius: 7px;
font-size: 12px;
font-weight: 500;
cursor: pointer;
border: none;
background: transparent;
color: var(--text-secondary);
transition: background 0.15s, color 0.15s, box-shadow 0.15s;
}
.segment-btn.active {
background: var(--bg-surface);
color: var(--text-primary);
box-shadow: var(--shadow-sm);
}