feat(web): refonte visuelle « Emerald » (thème clair/sombre, polices, tokens)
Adopte le langage visuel du design system Emerald dans l'IDE web : neutres zinc + accent emerald, polices auto-hébergées Inter + JetBrains Mono, radii/pills mono, points de statut, motif terminal, halo/grain. Refonte purement visuelle et additive (layout, routing, stores, protocole inchangés). - style.css : défauts sombres dans @theme + override clair sous html[data-theme=light] ; accent scindé (bright #34d399 / solid #059669 / hover) ; tokens border-soft/info/warn/danger/coffee ; idiomes globaux (focus/selection accent, scrollbars fines, jl-pulse/jl-blink, classes .chip/.status/.eyebrow/.label-mono/.caret/.halo/.grain, .btn*/.badge pill). - lib/theme.ts : singleton themeMode/resolvedTheme (dark/light/system, persistedRef arb.theme) + application dataset.theme/metas ; script anti-FOUC inline dans index.html ; toggle ActivityBar + Réglages. - Thèmes Monaco + xterm dark/light réactifs (palette ANSI + coloration syntaxique) ; fonts.ready + remeasureFonts pour l'alignement des glyphes. - status-tokens : tons sémantiques tokenisés ; balayage des ~340 couleurs Tailwind brutes vers les tokens sur tout components/* et views/* (exceptions assumées commentées). - GroupSummary.color exploité : icône de groupe teintée + ColorSwatchPicker (lib/group-colors.ts) dans les modals de groupe. - Polices via @fontsource-variable/{inter,jetbrains-mono} (offline).
This commit is contained in:
@@ -1,35 +1,86 @@
|
||||
@import 'tailwindcss';
|
||||
|
||||
/* Tokens de design centralisés.
|
||||
Couche SÉMANTIQUE aliasant la palette Tailwind (zinc/emerald/amber/sky/red) : rendu
|
||||
identique aujourd'hui, mais un point d'entrée unique pour la refonte IDE et un futur
|
||||
thème clair (il suffirait de redéfinir ces variables sous [data-theme=light]). */
|
||||
/* ============================================================
|
||||
Tokens de design « Emerald ».
|
||||
Couche SÉMANTIQUE : les défauts (bloc @theme) portent les valeurs du thème SOMBRE ;
|
||||
le thème CLAIR redéfinit les mêmes --color-* sous html[data-theme="light"].
|
||||
Mécanique Tailwind v4 : @theme émet les vars sur :root et les utilitaires
|
||||
(bg-surface-0, text-fg, ...) RÉFÉRENCENT ces vars ; comme [data-theme=light] cible le
|
||||
même <html> que :root (et gagne la cascade), il suffit de réécrire les vars pour
|
||||
basculer tout le thème, sans dupliquer d'utilitaires. Les modificateurs d'opacité
|
||||
(bg-danger/15) génèrent un color-mix(... var(--color-danger) ...) → suivent le thème.
|
||||
============================================================ */
|
||||
@theme {
|
||||
/* Ombres (sombre par défaut ; override clair plus bas). */
|
||||
--shadow-card: 0 1px 2px 0 rgb(0 0 0 / 0.4);
|
||||
--shadow-pop: 0 12px 32px -12px rgb(0 0 0 / 0.7);
|
||||
--shadow-pop: 0 24px 48px -20px rgb(0 0 0 / 0.65);
|
||||
|
||||
/* Surfaces : du fond le plus profond de l'app aux surfaces survolées. */
|
||||
--color-surface-0: var(--color-zinc-950); /* fond application (ex-#09090b) */
|
||||
--color-surface-1: var(--color-zinc-900); /* cartes, panneaux */
|
||||
--color-surface-2: var(--color-zinc-800); /* boutons, chips */
|
||||
--color-surface-3: var(--color-zinc-700); /* survol des surfaces */
|
||||
--color-surface-0: #09090b; /* fond application (Emerald --bg) */
|
||||
--color-surface-1: #18181b; /* cartes, panneaux (Emerald --surface) */
|
||||
--color-surface-2: #27272a; /* surface interactive opaque (boutons, chips) */
|
||||
--color-surface-3: #3f3f46; /* survol des surfaces */
|
||||
|
||||
/* Bordures. */
|
||||
--color-border: var(--color-zinc-800);
|
||||
--color-border-strong: var(--color-zinc-700);
|
||||
--color-border: #27272a;
|
||||
--color-border-strong: #3f3f46;
|
||||
--color-border-soft: #1c1c1f; /* séparateurs discrets (Emerald --border-soft) */
|
||||
|
||||
/* Texte. */
|
||||
--color-fg: var(--color-zinc-200);
|
||||
--color-fg-muted: var(--color-zinc-400);
|
||||
--color-fg-subtle: var(--color-zinc-500);
|
||||
--color-fg: #f4f4f5;
|
||||
--color-fg-muted: #a1a1aa;
|
||||
--color-fg-subtle: #52525b;
|
||||
|
||||
/* Accent (interactions, focus). */
|
||||
--color-accent: var(--color-emerald-600);
|
||||
--color-accent-hover: var(--color-emerald-500);
|
||||
/* Accent. Emerald scinde en 3 :
|
||||
- accent (BRIGHT #34d399) : texte, points, anneaux, liens, carets, indicateurs ;
|
||||
- accent-solid (#059669) : fond plein avec texte blanc (bouton primaire) ;
|
||||
- accent-hover (#10b981) : survol du plein. */
|
||||
--color-accent: #34d399;
|
||||
--color-accent-solid: #059669;
|
||||
--color-accent-hover: #10b981;
|
||||
|
||||
/* Tons sémantiques (statuts / signalétique). */
|
||||
--color-coffee: #f59e0b;
|
||||
--color-info: #7dd3fc;
|
||||
--color-warn: #fcd34d;
|
||||
--color-danger: #f87171;
|
||||
|
||||
/* Polices auto-hébergées (fontes variables). Ces tokens alimentent aussi font-sans /
|
||||
font-mono de Tailwind. Noms exacts des paquets @fontsource-variable : "... Variable". */
|
||||
--font-sans: 'Inter Variable', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
|
||||
--font-mono: 'JetBrains Mono Variable', ui-monospace, 'Fira Code', Menlo, Consolas, monospace;
|
||||
}
|
||||
|
||||
/* Métriques du chrome IDE (barres, onglets, lignes). Consommées via des valeurs
|
||||
arbitraires Tailwind, ex. h-[var(--ide-tab-h)]. */
|
||||
/* ---------- Thème clair : réécriture des mêmes tokens (même élément que :root) ---------- */
|
||||
html[data-theme='light'] {
|
||||
--shadow-card: 0 1px 2px 0 rgb(24 24 27 / 0.08);
|
||||
--shadow-pop: 0 18px 38px -22px rgb(24 24 27 / 0.22);
|
||||
|
||||
--color-surface-0: #fafafa;
|
||||
--color-surface-1: #ffffff;
|
||||
--color-surface-2: #f4f4f5;
|
||||
--color-surface-3: #e4e4e7;
|
||||
|
||||
--color-border: #e4e4e7;
|
||||
--color-border-strong: #d4d4d8;
|
||||
--color-border-soft: #ececee;
|
||||
|
||||
--color-fg: #18181b;
|
||||
--color-fg-muted: #52525b;
|
||||
--color-fg-subtle: #a1a1aa;
|
||||
|
||||
--color-accent: #059669;
|
||||
--color-accent-solid: #059669;
|
||||
--color-accent-hover: #047857;
|
||||
|
||||
--color-coffee: #d97706;
|
||||
--color-info: #0284c7;
|
||||
--color-warn: #d97706;
|
||||
--color-danger: #dc2626;
|
||||
}
|
||||
|
||||
/* Métriques du chrome IDE (barres, onglets, lignes), indépendantes du thème.
|
||||
Consommées via des valeurs arbitraires Tailwind, ex. h-[var(--ide-tab-h)]. */
|
||||
:root {
|
||||
--ide-activitybar-w: 3rem;
|
||||
--ide-titlebar-h: 2.25rem;
|
||||
@@ -47,9 +98,31 @@ body,
|
||||
body {
|
||||
background-color: var(--color-surface-0);
|
||||
color: var(--color-fg);
|
||||
font-family: var(--font-sans);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
/* ---------- Animations signature (hors couches : les @keyframes ne sont pas layerisés) ---------- */
|
||||
@keyframes jl-pulse {
|
||||
0%,
|
||||
100% {
|
||||
box-shadow: 0 0 0 0 rgb(52 211 153 / 0.55);
|
||||
}
|
||||
50% {
|
||||
box-shadow: 0 0 0 5px rgb(52 211 153 / 0);
|
||||
}
|
||||
}
|
||||
@keyframes jl-blink {
|
||||
0%,
|
||||
49% {
|
||||
opacity: 1;
|
||||
}
|
||||
50%,
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
/* Tailwind v4 ne met plus cursor:pointer sur les boutons : on le rétablit
|
||||
(sauf désactivés, qui gardent le curseur par défaut / not-allowed). */
|
||||
@@ -57,38 +130,168 @@ body {
|
||||
[role='button']:not([aria-disabled='true']) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Focus visible global (fallback accent). Les composants qui posent leur propre
|
||||
focus-visible:outline-none + ring gardent leur anneau (leur règle gagne en spécificité). */
|
||||
:focus-visible {
|
||||
outline: 2px solid var(--color-accent);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* Sélection de texte du chrome (xterm/Monaco gardent la leur). */
|
||||
::selection {
|
||||
background: var(--color-accent);
|
||||
color: var(--color-surface-0);
|
||||
}
|
||||
|
||||
/* Scrollbars fines et discrètes (absentes auparavant). */
|
||||
* {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: var(--color-surface-3) transparent;
|
||||
}
|
||||
::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: var(--color-surface-3);
|
||||
border-radius: 9999px;
|
||||
border: 2px solid transparent;
|
||||
background-clip: content-box;
|
||||
}
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--color-border-strong);
|
||||
}
|
||||
::-webkit-scrollbar-corner {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* Respect de prefers-reduced-motion (généralise le bloc scopé de ToastContainer). */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
animation-duration: 0.001ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.001ms !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
/* Champs : rayon unifié + anneau de focus visible (absent auparavant). */
|
||||
/* Champs : rayon unifié + anneau de focus visible. */
|
||||
.input {
|
||||
@apply w-full rounded-lg border border-border-strong bg-surface-0 px-2.5 py-1.5 text-sm text-zinc-100 outline-none transition-colors placeholder:text-zinc-600 focus-visible:border-zinc-500 focus-visible:ring-2 focus-visible:ring-accent-hover/40;
|
||||
@apply w-full rounded-lg border border-border-strong bg-surface-0 px-2.5 py-1.5 text-sm text-fg outline-none transition-colors placeholder:text-fg-subtle focus-visible:border-accent focus-visible:ring-2 focus-visible:ring-accent/40;
|
||||
}
|
||||
|
||||
/* Boutons : socle CSS pour les usages hors composant (ex. RouterLink class="btn").
|
||||
Le composant BaseButton réplique ces variantes et ajoute tailles / loading / focus. */
|
||||
/* Boutons : socle CSS pour les usages hors composant (RouterLink class="btn").
|
||||
BaseButton réplique ces variantes et ajoute tailles / loading. Idiomes Emerald :
|
||||
rayon 11px, font 600, hover accent. */
|
||||
.btn {
|
||||
@apply inline-flex items-center justify-center gap-1.5 rounded-lg border border-border-strong bg-surface-2 px-3 py-1.5 text-sm font-medium text-fg transition-colors hover:bg-surface-3 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent-hover/70 focus-visible:ring-offset-2 focus-visible:ring-offset-surface-0 disabled:cursor-not-allowed disabled:opacity-50;
|
||||
@apply inline-flex items-center justify-center gap-[9px] rounded-[11px] border border-border bg-surface-2 px-3 py-1.5 text-sm font-semibold text-fg transition-[background-color,border-color,color,transform] hover:border-accent hover:text-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/60 focus-visible:ring-offset-2 focus-visible:ring-offset-surface-0 disabled:cursor-not-allowed disabled:opacity-50;
|
||||
}
|
||||
.btn-primary {
|
||||
@apply inline-flex items-center justify-center gap-1.5 rounded-lg bg-accent px-3 py-1.5 text-sm font-medium text-white transition-colors hover:bg-accent-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent-hover/70 focus-visible:ring-offset-2 focus-visible:ring-offset-surface-0 disabled:cursor-not-allowed disabled:opacity-50;
|
||||
@apply inline-flex items-center justify-center gap-[9px] rounded-[11px] border border-accent-solid bg-accent-solid px-3 py-1.5 text-sm font-semibold text-white transition-[background-color,border-color,color,transform] hover:-translate-y-px hover:bg-accent-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/70 focus-visible:ring-offset-2 focus-visible:ring-offset-surface-0 disabled:cursor-not-allowed disabled:opacity-50;
|
||||
}
|
||||
.btn-danger {
|
||||
@apply inline-flex items-center justify-center gap-1.5 rounded-lg border border-red-900 bg-red-950 px-3 py-1.5 text-sm font-medium text-red-300 transition-colors hover:bg-red-900 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-red-500/70 focus-visible:ring-offset-2 focus-visible:ring-offset-surface-0 disabled:cursor-not-allowed disabled:opacity-50;
|
||||
@apply inline-flex items-center justify-center gap-[9px] rounded-[11px] border border-danger/40 bg-danger/10 px-3 py-1.5 text-sm font-semibold text-danger transition-[background-color,border-color,color,transform] hover:bg-danger/20 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-danger/70 focus-visible:ring-offset-2 focus-visible:ring-offset-surface-0 disabled:cursor-not-allowed disabled:opacity-50;
|
||||
}
|
||||
.btn-ghost {
|
||||
@apply inline-flex items-center justify-center gap-1.5 rounded-lg px-3 py-1.5 text-sm font-medium text-zinc-300 transition-colors hover:bg-surface-2/60 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent-hover/70 focus-visible:ring-offset-2 focus-visible:ring-offset-surface-0 disabled:cursor-not-allowed disabled:opacity-50;
|
||||
@apply inline-flex items-center justify-center gap-[9px] rounded-[11px] px-3 py-1.5 text-sm font-semibold text-fg-muted transition-[background-color,border-color,color] hover:bg-surface-2/60 hover:text-fg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/60 focus-visible:ring-offset-2 focus-visible:ring-offset-surface-0 disabled:cursor-not-allowed disabled:opacity-50;
|
||||
}
|
||||
.btn-coffee {
|
||||
@apply inline-flex items-center justify-center gap-[7px] rounded-[9px] border border-coffee bg-transparent px-[13px] py-2 text-[13px] font-semibold text-coffee transition-[background-color,transform] hover:-translate-y-px hover:bg-coffee/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-coffee/60 focus-visible:ring-offset-2 focus-visible:ring-offset-surface-0;
|
||||
}
|
||||
|
||||
/* Pastille (pill mono Emerald). Les variantes de ton vivent dans lib/status-tokens. */
|
||||
.badge {
|
||||
@apply inline-flex items-center gap-1 rounded px-1.5 py-0.5 text-[11px] font-medium;
|
||||
@apply inline-flex items-center gap-1.5 rounded-full px-2.5 py-0.5 font-mono text-[11px];
|
||||
}
|
||||
/* Puce d'accent pulsée (halo box-shadow, pas un fondu d'opacité). */
|
||||
.badge-dot {
|
||||
@apply inline-block h-2 w-2 rounded-full bg-accent;
|
||||
animation: jl-pulse 2.2s infinite;
|
||||
}
|
||||
|
||||
/* Cartes : factorise le motif répété dans les listes/sections/formulaires. */
|
||||
/* Chip (tag mono discret). */
|
||||
.chip {
|
||||
@apply inline-flex items-center rounded-full border border-border bg-surface-0 px-[9px] py-1 font-mono text-[11px] text-fg-muted;
|
||||
}
|
||||
|
||||
/* Statut mono uppercase + point coloré. */
|
||||
.status {
|
||||
@apply inline-flex items-center gap-1.5 font-mono text-[11px] tracking-[0.05em] text-fg-muted uppercase;
|
||||
}
|
||||
.status-dot {
|
||||
@apply inline-block h-[7px] w-[7px] rounded-full;
|
||||
}
|
||||
|
||||
/* Micro-titres. */
|
||||
.eyebrow {
|
||||
@apply font-mono text-[13px] text-accent;
|
||||
}
|
||||
.label-mono {
|
||||
@apply font-mono text-[11px] tracking-[0.08em] text-fg-subtle uppercase;
|
||||
}
|
||||
|
||||
/* Caret clignotant pour les mockups terminaux (JAMAIS xterm). */
|
||||
.caret {
|
||||
@apply ml-0.5 inline-block h-[15px] w-2 bg-accent align-[-2px];
|
||||
animation: jl-blink 1.05s steps(1) infinite;
|
||||
}
|
||||
|
||||
/* Cartes. */
|
||||
.card {
|
||||
@apply rounded-xl border border-border bg-surface-1/50 p-3;
|
||||
@apply rounded-[14px] border border-border bg-surface-1/50 p-3;
|
||||
}
|
||||
.card-hover {
|
||||
@apply transition-[transform,border-color,box-shadow] hover:-translate-y-1 hover:border-accent hover:shadow-pop;
|
||||
}
|
||||
.card-inset {
|
||||
@apply rounded-lg border border-border bg-surface-0/40 p-2;
|
||||
@apply rounded-[10px] border border-border bg-surface-0/40 p-2;
|
||||
}
|
||||
|
||||
/* Calques décoratifs (halo accent + grain pointillé). Statiques (safe reduced-motion). */
|
||||
.halo {
|
||||
position: absolute;
|
||||
top: -40px;
|
||||
left: 50%;
|
||||
width: 1100px;
|
||||
max-width: 140%;
|
||||
height: 560px;
|
||||
transform: translateX(-50%);
|
||||
background: radial-gradient(50% 60% at 50% 0%, var(--color-accent) 0%, transparent 70%);
|
||||
opacity: 0.12;
|
||||
filter: blur(18px);
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
.grain {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-image: radial-gradient(var(--color-fg) 1px, transparent 1px);
|
||||
background-size: 34px 34px;
|
||||
opacity: 0.025;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
-webkit-mask-image: linear-gradient(to bottom, #000, transparent 70%);
|
||||
mask-image: linear-gradient(to bottom, #000, transparent 70%);
|
||||
}
|
||||
|
||||
/* Lignes de diff : le FOND teinté porte la sémantique (ajout/suppression), le texte
|
||||
reste neutre (lisible en clair ET sombre, façon GitHub). */
|
||||
.diff-add {
|
||||
@apply bg-accent/12 text-fg;
|
||||
}
|
||||
.diff-del {
|
||||
@apply bg-danger/12 text-fg;
|
||||
}
|
||||
.diff-ctx {
|
||||
@apply text-fg-muted;
|
||||
}
|
||||
}
|
||||
|
||||
/* Halo plus discret en thème clair. */
|
||||
html[data-theme='light'] .halo {
|
||||
opacity: 0.06;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user