diff --git a/docs/DESIGN.md b/docs/DESIGN.md new file mode 100644 index 0000000..8a8eee7 --- /dev/null +++ b/docs/DESIGN.md @@ -0,0 +1,232 @@ +# ZEN — Design Language + +Document de référence visuelle. Décrit l'apparence, le ressenti et les règles visuelles du produit. Valable pour toute reproduction, quelle que soit la technologie. + +--- + +## Philosophie + +ZEN est un outil de travail. Pas un produit marketing. L'interface disparaît pour laisser place au contenu. + +**Un seul principe directeur : ne jamais ajouter un élément sans raison.** + +Chaque couleur, bordure, espace ou texte supplémentaire doit justifier sa présence. Le doute se résout toujours en faveur de la suppression. + +--- + +## Palette + +### Base + +Le produit est **noir et blanc**. Pas de couleurs de marque, pas de teintes bleues ou violettes dans les fonds. Le fond est blanc pur. Le texte est presque noir. + +| Rôle | Clair | Sombre | +|---|---|---| +| Fond de page | `#ffffff` | `#0c0c0b` | +| Fond hover | `#f5f5f4` | `#161614` | +| Fond actif / sélectionné | `#ebebea` | `#1f1f1d` | +| Bordure standard | `#e4e4e2` | `#272724` | +| Bordure forte | `#c8c8c4` | `#3a3a36` | +| Texte principal | `#0c0c0b` | `#f0f0ed` | +| Texte secondaire | `#6b6b68` | `#6b6b68` | +| Texte discret | `#a3a39e` | `#3d3d3a` | + +### Accents sémantiques + +Quatre couleurs uniquement. Utilisées **avec grande parcimonie** — pour les statuts, les alertes, les deltas. Jamais comme couleur décorative. Jamais sur un fond. + +| Couleur | Usage | +|---|---| +| **Bleu** | Information, liens, action principale | +| **Vert** | Succès, actif, delta positif | +| **Ambre** | Avertissement, en attente, brouillon | +| **Rouge** | Erreur, danger, suppression, delta négatif | + +Chaque couleur existe en trois déclinaisons : la couleur elle-même (pour le texte), un fond très pâle, et une bordure légère. Ces trois valeurs forment toujours un ensemble — on ne mélange pas les déclinaisons de couleurs différentes. + +### Ce qu'on n'utilise pas + +- Dégradés +- Couleurs de fond dans la sidebar ou les sections (sauf hover/actif) +- Couleurs de marque personnalisées +- Transparence et flou (sauf l'overlay des modales) + +--- + +## Typographie + +**Police unique : IBM Plex Sans.** Pour les codes, IDs, montants et dates : IBM Plex Mono. + +### Hiérarchie + +| Rôle | Taille | Graisse | Notes | +|---|---|---|---| +| Titre de page | 20px | 600 | Lettre-espacement -0.01em | +| Titre de section | 16–20px | 600 | | +| Corps de texte | 14px | 400 | | +| Texte secondaire | 13px | 400 | Dans les tableaux, les listes | +| Labels / captions | 12px | 400–500 | | +| Labels colonnes | 11px | 500 | Uppercase, lettre-espacement +0.04em | +| Codes / IDs / montants | 11–13px | 400 | IBM Plex Mono | + +**Taille minimale : 11px.** En dessous, on ne descend pas. + +### Règles d'écriture + +- **Sentence case partout.** "Tableau de bord", pas "TABLEAU DE BORD". +- Seule exception : les en-têtes de colonnes de tableau, en majuscules petits. +- Texte court et direct. Pas de phrase là où un mot suffit. +- Les montants financiers, les dates ISO et les identifiants sont toujours en monospace. + +--- + +## Espace + +Base : **4px**. Tout espacement est un multiple de 4. + +| Valeur | Usage typique | +|---|---| +| 4px | Espacement interne minimal (gap entre icône et texte) | +| 8px | Padding compact, gap entre éléments proches | +| 12px | Padding interne des petits composants | +| 16px | Padding standard des cartes et panneaux | +| 24px | Espacement entre sections | +| 32px | Padding latéral du contenu principal | +| 48–64px | Espacement entre blocs majeurs | + +La densité est **moyenne**. Pas étouffant, pas aéré. Un admin est un outil qu'on utilise plusieurs heures par jour — la densité confortable prime sur l'esthétique aérée. + +--- + +## Formes et bordures + +### Rayon de bordure + +| Contexte | Rayon | +|---|---| +| Badges, étiquettes, petits éléments | 8px | +| Boutons, champs de saisie, cartes | 12px | +| Modales, panneaux flottants importants | 16px | +| Avatars, indicateurs ronds | Cercle complet | + +**12px est le rayon standard.** On ne mélange pas les rayons dans un même composant. + +### Bordures + +Toutes les séparations sont des **bordures 1px solid**. Pas d'ombres sur les cartes et surfaces statiques. Pas de lignes de 2px ou plus. + +Les ombres existent uniquement pour les éléments qui **flottent au-dessus** de l'interface : menus déroulants, modales, toasts. Elles sont discrètes — juste assez pour indiquer la profondeur. + +--- + +## Structure de la page + +``` +┌─────────────────────────────────────────────┐ +│ Sidebar 220px │ Barre supérieure 48px │ +│ ├─────────────────────────── │ +│ Logo │ │ +│ │ Zone de contenu │ +│ Nav principale │ padding 28px / 32px │ +│ │ │ +│ ────────────── │ │ +│ Paramètres │ │ +└─────────────────────────────────────────────┘ +``` + +- **Sidebar** : fixe, 220px. Fond blanc (`#ffffff`). Séparée par une bordure droite. +- **Barre supérieure** : fixe, 48px. Fond blanc. Fil d'Ariane à gauche, utilisateur à droite. +- **Contenu** : gris très pâle (`#fafafa` / `neutral-50`). Les cartes et tableaux ont un fond blanc, ce qui crée naturellement la séparation visuelle sans avoir besoin de bordures de section. + +--- + +## Navigation sidebar + +- Les items inactifs sont en texte secondaire (`#6b6b68`). +- Au survol : fond légèrement grisé, texte principal. +- L'item actif : fond légèrement grisé plus prononcé, texte principal gras. +- Les icônes sont toujours à gauche du texte. Taille 15×15px. +- Les sous-menus se révèlent par accordéon (chevron qui tourne). +- Rayon des items : 8px. +- Padding interne : 7px vertical, 10px horizontal. + +--- + +## Boutons + +Quatre variantes seulement : + +| Variante | Apparence | Usage | +|---|---|---| +| **Primaire** | Fond noir, texte blanc | Action principale de la page | +| **Secondaire** | Fond transparent, bordure, texte sombre | Actions secondaires, annulation | +| **Fantôme** | Fond transparent, pas de bordure, texte sombre | Actions tertiaires | +| **Danger** | Fond rouge très pâle, texte rouge, bordure rouge pâle | Suppression uniquement | + +**Taille standard : petite.** Padding 6px vertical / 12px horizontal, texte 12px. +Il ne doit jamais y avoir plus d'un bouton primaire par section ou en-tête de page. + +--- + +## Cartes et panneaux + +- Fond blanc. +- Bordure 1px. +- Rayon 12px. +- Padding interne 16–20px. +- **Pas d'ombre.** +- En-tête interne séparé du contenu par une bordure horizontale. +- Pied de panneau (actions de formulaire) séparé par une bordure horizontale. + +--- + +## Tableaux + +- En-têtes de colonnes : texte 11px uppercase, couleur secondaire. +- Lignes : fond blanc. Hover : fond légèrement grisé. +- Bordure horizontale entre chaque ligne. Pas de bordure sur la dernière ligne. +- Données numériques, IDs, dates : IBM Plex Mono. +- Actions (modifier, supprimer) : à l'extrême droite, boutons icône discrets. +- Pas de fond alterné sur les lignes. + +--- + +## Badges de statut + +Petits éléments inline qui indiquent un état. Toujours composés de trois éléments : fond pâle de la couleur sémantique, bordure légère, texte de la couleur sémantique. + +Les statuts "vivants" (actif, en attente, en retard) ont un point coloré à gauche du texte. +Les statuts terminaux (archivé, publié) n'ont pas de point. + +Rayon : 8px. Taille du texte : 11px. + +--- + +## États interactifs + +| État | Apparence | +|---|---| +| Hover | Fond légèrement plus sombre (4–5% de gris) | +| Actif / focus | Bordure de 1px devient la couleur du texte principal | +| Désactivé | Opacité 50%, curseur interdit | +| Chargement | Pas défini — à traiter au cas par cas | + +Toutes les transitions durent **120ms**, courbe `ease-out`. Aucune animation décorative. + +--- + +## Ce qu'on n'utilise jamais + +- Dégradés de couleur sur les fonds ou les boutons +- Cartes avec une bordure colorée uniquement sur le côté gauche +- Ombres internes +- Texte en gras uniquement pour l'emphase décorative +- Icônes sans fonction claire +- Statistiques ou chiffres inventés pour remplir l'espace +- Couleurs de fond dans les sections de contenu + +--- + +## Thème sombre + +Le thème sombre est une inversion calibrée : les fonds passent au quasi-noir, les textes s'éclaircissent, les accents gagnent légèrement en luminosité pour rester lisibles. La structure, les espaces et les proportions restent identiques. Détection automatique via la préférence système — pas de toggle manuel. diff --git a/src/features/admin/components/AdminHeader.js b/src/features/admin/components/AdminHeader.js index d614f99..8fdb2a5 100644 --- a/src/features/admin/components/AdminHeader.js +++ b/src/features/admin/components/AdminHeader.js @@ -52,7 +52,7 @@ const AdminHeader = ({ isMobileMenuOpen, setIsMobileMenuOpen, user, onLogout, ap const userInitials = getUserInitials(user?.name); return ( -
+
{/* Left section — Mobile menu button + Logo */}
@@ -164,7 +164,7 @@ const AdminHeader = ({ isMobileMenuOpen, setIsMobileMenuOpen, user, onLogout, ap diff --git a/src/features/admin/components/AdminPagesLayout.js b/src/features/admin/components/AdminPagesLayout.js index 301ea06..d13e09c 100644 --- a/src/features/admin/components/AdminPagesLayout.js +++ b/src/features/admin/components/AdminPagesLayout.js @@ -19,7 +19,7 @@ export default function AdminPagesLayout({ children, user, onLogout, appName, en
-
+
{children}
diff --git a/src/features/admin/components/AdminSidebar.js b/src/features/admin/components/AdminSidebar.js index 743f353..dd8af05 100644 --- a/src/features/admin/components/AdminSidebar.js +++ b/src/features/admin/components/AdminSidebar.js @@ -11,35 +11,28 @@ import { ChevronDownIcon } from '@zen/core/shared/icons'; * Icons are passed as strings from server to avoid serialization issues */ function resolveIcon(iconNameOrComponent) { - // If it's already a component (function), return it if (typeof iconNameOrComponent === 'function') { return iconNameOrComponent; } - // If it's a string, look up in Icons if (typeof iconNameOrComponent === 'string') { return Icons[iconNameOrComponent] || Icons.DashboardSquare03Icon; } - // Default fallback return Icons.DashboardSquare03Icon; } const AdminSidebar = ({ isMobileMenuOpen, setIsMobileMenuOpen, appName, enabledModules, navigationSections: serverNavigationSections }) => { const pathname = usePathname(); const router = useRouter(); - - // State to manage collapsed sections (all open by default) + const [collapsedSections, setCollapsedSections] = useState(new Set()); - // Function to toggle a section's state const toggleSection = (sectionId) => { - // Find the section to check if it has active items const section = navigationSections.find(s => s.id === sectionId); - - // Don't allow collapsing sections with active items + if (section && isSectionActive(section)) { return; } - + setCollapsedSections(prev => { const newCollapsed = new Set(prev); if (newCollapsed.has(sectionId)) { @@ -51,15 +44,13 @@ const AdminSidebar = ({ isMobileMenuOpen, setIsMobileMenuOpen, appName, enabledM }); }; - // Handle mobile menu closure when clicking on a link const handleMobileLinkClick = () => { setIsMobileMenuOpen(false); }; - // Close mobile menu on screen size change useEffect(() => { const handleResize = () => { - if (window.innerWidth >= 1024) { // lg breakpoint + if (window.innerWidth >= 1024) { setIsMobileMenuOpen(false); } }; @@ -68,23 +59,18 @@ const AdminSidebar = ({ isMobileMenuOpen, setIsMobileMenuOpen, appName, enabledM return () => window.removeEventListener('resize', handleResize); }, [setIsMobileMenuOpen]); - // Function to check if any item in a section is currently active const isSectionActive = (section) => { return section.items.some(item => item.current); }; - // Function to check if a section should be rendered as a direct link const shouldRenderAsDirectLink = (section) => { - // Check if there's only one item and it has the same name as the section - return section.items.length === 1 && + return section.items.length === 1 && section.items[0].name.toLowerCase() === section.title.toLowerCase(); }; - // Update collapsed sections when pathname changes to ensure active sections are open useEffect(() => { setCollapsedSections(prev => { const newSet = new Set(prev); - // Add any sections that have active items to ensure they stay open navigationSections.forEach(section => { if (isSectionActive(section)) { newSet.add(section.id); @@ -95,9 +81,6 @@ const AdminSidebar = ({ isMobileMenuOpen, setIsMobileMenuOpen, appName, enabledM // eslint-disable-next-line react-hooks/exhaustive-deps }, [pathname]); - // Use server-provided navigation sections if available, otherwise use core-only fallback - // Server navigation includes module navigation, fallback only has core pages - // Update the 'current' property based on the actual pathname (client-side) const navigationSections = serverNavigationSections.map(section => ({ ...section, items: section.items.map(item => ({ @@ -106,11 +89,15 @@ const AdminSidebar = ({ isMobileMenuOpen, setIsMobileMenuOpen, appName, enabledM })) })); - // Function to render a complete navigation section + const itemBase = 'w-full flex items-center justify-between px-[10px] py-[7px] rounded-lg text-[13px] transition-colors duration-[120ms] ease-out'; + const itemActive = 'bg-neutral-200 dark:bg-neutral-800 text-neutral-900 dark:text-white font-semibold'; + const itemInactive = 'text-neutral-500 dark:text-neutral-400 hover:bg-neutral-100 dark:hover:bg-neutral-900 hover:text-neutral-900 dark:hover:text-white'; + + const subItemBase = 'w-full flex items-center justify-between px-[10px] py-[7px] rounded-lg text-[12px] transition-colors duration-[120ms] ease-out'; + const renderNavSection = (section) => { const Icon = resolveIcon(section.icon); - - // If section should be rendered as a direct link + if (shouldRenderAsDirectLink(section)) { const item = section.items[0]; return ( @@ -118,18 +105,14 @@ const AdminSidebar = ({ isMobileMenuOpen, setIsMobileMenuOpen, appName, enabledM -
- +
+ {section.title}
{item.badge && ( - + {item.badge} )} @@ -138,9 +121,9 @@ const AdminSidebar = ({ isMobileMenuOpen, setIsMobileMenuOpen, appName, enabledM ); } - // Regular section with expandable sub-items const isCollapsed = !collapsedSections.has(section.id); - + const isActive = isSectionActive(section); + return (
-
-
    +
      {section.items.map(renderNavItem)}
@@ -177,7 +160,6 @@ const AdminSidebar = ({ isMobileMenuOpen, setIsMobileMenuOpen, appName, enabledM ); }; - // Function to render a navigation item const renderNavItem = (item) => { const Icon = resolveIcon(item.icon); return ( @@ -185,18 +167,14 @@ const AdminSidebar = ({ isMobileMenuOpen, setIsMobileMenuOpen, appName, enabledM -
- +
+ {item.name}
{item.badge && ( - + {item.badge} )} @@ -209,8 +187,8 @@ const AdminSidebar = ({ isMobileMenuOpen, setIsMobileMenuOpen, appName, enabledM <> {/* Mobile overlay */} {isMobileMenuOpen && ( -
setIsMobileMenuOpen(false)} /> )} @@ -218,18 +196,18 @@ const AdminSidebar = ({ isMobileMenuOpen, setIsMobileMenuOpen, appName, enabledM {/* Sidebar */}
- {/* Logo Section */} - + {/* Logo */} +

{appName}

- + Admin {/* Navigation */} -
@@ -237,4 +215,4 @@ const AdminSidebar = ({ isMobileMenuOpen, setIsMobileMenuOpen, appName, enabledM ); }; -export default AdminSidebar; +export default AdminSidebar; diff --git a/src/features/auth/components/pages/LoginPage.js b/src/features/auth/components/pages/LoginPage.js index 3a46431..9211b6f 100644 --- a/src/features/auth/components/pages/LoginPage.js +++ b/src/features/auth/components/pages/LoginPage.js @@ -85,10 +85,10 @@ export default function LoginPage({ onSubmit, onNavigate, onSetSessionCookie, re } }; - const inputClasses = 'w-full px-3 py-2.5 rounded-lg text-sm focus:outline-none transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed bg-white border border-neutral-300 text-neutral-900 placeholder-neutral-400 focus:border-neutral-500 focus:ring-1 focus:ring-neutral-500/20 dark:bg-neutral-900 dark:border-neutral-700/50 dark:text-white dark:placeholder-neutral-500 dark:focus:border-neutral-600 dark:focus:ring-neutral-600/20'; + const inputClasses = 'w-full px-3 py-2.5 rounded-xl text-sm focus:outline-none transition-all duration-[120ms] ease-out disabled:opacity-50 disabled:cursor-not-allowed bg-white border border-neutral-300 text-neutral-900 placeholder-neutral-400 focus:border-neutral-500 focus:ring-1 focus:ring-neutral-500/20 dark:bg-neutral-900 dark:border-neutral-700/50 dark:text-white dark:placeholder-neutral-500 dark:focus:border-neutral-600 dark:focus:ring-neutral-600/20'; return ( -
+
{/* Header */}

@@ -190,7 +190,7 @@ export default function LoginPage({ onSubmit, onNavigate, onSetSessionCookie, re type="button" onClick={handleSubmit} disabled={isLoading || success || currentUser} - className="cursor-pointer w-full bg-neutral-900 text-white mt-2 py-2.5 px-4 rounded-lg text-sm font-medium hover:bg-neutral-800 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200 focus:outline-none focus:ring-1 focus:ring-neutral-900/20 dark:bg-white dark:text-black dark:hover:bg-neutral-100 dark:focus:ring-white/20" + className="cursor-pointer w-full bg-neutral-900 text-white mt-2 py-2.5 px-4 rounded-xl text-sm font-medium hover:bg-neutral-800 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-[120ms] ease-out focus:outline-none focus:ring-1 focus:ring-neutral-900/20 dark:bg-white dark:text-black dark:hover:bg-neutral-100 dark:focus:ring-white/20" > {isLoading ? (
diff --git a/src/shared/components/Badge.js b/src/shared/components/Badge.js index 55487dc..4c95572 100644 --- a/src/shared/components/Badge.js +++ b/src/shared/components/Badge.js @@ -24,9 +24,9 @@ const Badge = ({ }; const sizes = { - sm: 'px-2 py-0.5 rounded-full text-xs', - md: 'px-2.5 py-0.5 rounded-full text-xs', - lg: 'px-3 py-1 rounded-full text-sm' + sm: 'px-2 py-0.5 rounded-lg text-[11px]', + md: 'px-2.5 py-0.5 rounded-lg text-[11px]', + lg: 'px-3 py-1 rounded-lg text-xs' }; return ( diff --git a/src/shared/components/Button.js b/src/shared/components/Button.js index e2ad103..3a7d9f3 100644 --- a/src/shared/components/Button.js +++ b/src/shared/components/Button.js @@ -15,7 +15,7 @@ const Button = ({ className = '', ...props }) => { - const baseClassName = 'cursor-pointer inline-flex items-center justify-center font-medium rounded-xl transition-all duration-200 focus:outline-none focus:ring-1 disabled:opacity-50 disabled:cursor-not-allowed'; + const baseClassName = 'cursor-pointer inline-flex items-center justify-center font-medium rounded-xl transition-all duration-[120ms] ease-out focus:outline-none focus:ring-1 disabled:opacity-50 disabled:cursor-not-allowed'; const variants = { primary: 'bg-neutral-900 text-white hover:bg-neutral-800 focus:ring-neutral-900/20 dark:bg-white dark:text-black dark:hover:bg-neutral-100 dark:focus:ring-white/20', @@ -27,7 +27,7 @@ const Button = ({ }; const sizes = { - sm: 'px-3 py-2 text-xs gap-1.5', + sm: 'px-3 py-1.5 text-xs gap-1.5', md: 'px-4 py-2.5 text-sm gap-2', lg: 'px-6 py-3 text-base gap-2.5' }; diff --git a/src/shared/components/Card.js b/src/shared/components/Card.js index da865df..faa57d3 100644 --- a/src/shared/components/Card.js +++ b/src/shared/components/Card.js @@ -10,12 +10,12 @@ const Card = ({ footer, variant = 'default', padding = 'md', - hover = true, + hover = false, spacing = 'md', className = '', ...props }) => { - const baseClassName = 'border transition-all duration-300'; + const baseClassName = 'border transition-all duration-[120ms] ease-out'; const isLightDark = variant === 'lightDark'; const variants = { @@ -23,7 +23,7 @@ const Card = ({ elevated: 'rounded-xl bg-neutral-50/80 dark:bg-neutral-900/40 border-neutral-200 dark:border-neutral-800/50', outline: 'rounded-xl bg-transparent border-neutral-300 dark:border-neutral-700/50', solid: 'rounded-xl bg-neutral-100 dark:bg-neutral-800 border-neutral-200 dark:border-neutral-700', - lightDark: 'rounded-2xl bg-white/80 dark:bg-neutral-900/40 border-neutral-200/80 dark:border-neutral-800/50', + lightDark: 'rounded-xl bg-white dark:bg-neutral-900/40 border-neutral-200 dark:border-neutral-800/50', success: 'rounded-xl bg-green-50 dark:bg-green-900/30 border-green-200 dark:border-green-900/50', info: 'rounded-xl bg-blue-50 dark:bg-blue-900/30 border-blue-200 dark:border-blue-900/50', warning: 'rounded-xl bg-yellow-50 dark:bg-yellow-900/30 border-yellow-200 dark:border-yellow-900/50', diff --git a/src/shared/components/Input.js b/src/shared/components/Input.js index b106717..6500165 100644 --- a/src/shared/components/Input.js +++ b/src/shared/components/Input.js @@ -18,7 +18,7 @@ const Input = ({ step, ...props }) => { - const baseInputClassName = `w-full px-3 py-2.5 rounded-xl text-sm focus:outline-none transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed bg-white border border-neutral-300 text-neutral-900 placeholder-neutral-400 focus:border-neutral-500 focus:ring-1 focus:ring-neutral-500/20 dark:bg-neutral-900/60 dark:border-neutral-700/50 dark:text-white dark:placeholder-neutral-500 dark:focus:border-neutral-600 dark:focus:ring-neutral-600/20 dark:hover:bg-neutral-900/80 ${ + const baseInputClassName = `w-full px-3 py-2.5 rounded-xl text-sm focus:outline-none transition-all duration-[120ms] ease-out disabled:opacity-50 disabled:cursor-not-allowed bg-white border border-neutral-300 text-neutral-900 placeholder-neutral-400 focus:border-neutral-500 focus:ring-1 focus:ring-neutral-500/20 dark:bg-neutral-900/60 dark:border-neutral-700/50 dark:text-white dark:placeholder-neutral-500 dark:focus:border-neutral-600 dark:focus:ring-neutral-600/20 dark:hover:bg-neutral-900/80 ${ error ? 'border-red-500/50 dark:border-red-500/50' : '' } ${className}`; diff --git a/src/shared/components/StatCard.js b/src/shared/components/StatCard.js index ef20352..e41219a 100644 --- a/src/shared/components/StatCard.js +++ b/src/shared/components/StatCard.js @@ -1,6 +1,5 @@ 'use client'; -import React from 'react'; import { Skeleton } from './LoadingState'; const StatCard = ({ @@ -31,7 +30,7 @@ const StatCard = ({ return (
diff --git a/src/shared/fonts/IBM_Plex_Mono/IBMPlexMono-Bold.ttf b/src/shared/fonts/IBM_Plex_Mono/IBMPlexMono-Bold.ttf new file mode 100644 index 0000000..247979c Binary files /dev/null and b/src/shared/fonts/IBM_Plex_Mono/IBMPlexMono-Bold.ttf differ diff --git a/src/shared/fonts/IBM_Plex_Mono/IBMPlexMono-BoldItalic.ttf b/src/shared/fonts/IBM_Plex_Mono/IBMPlexMono-BoldItalic.ttf new file mode 100644 index 0000000..2321473 Binary files /dev/null and b/src/shared/fonts/IBM_Plex_Mono/IBMPlexMono-BoldItalic.ttf differ diff --git a/src/shared/fonts/IBM_Plex_Mono/IBMPlexMono-ExtraLight.ttf b/src/shared/fonts/IBM_Plex_Mono/IBMPlexMono-ExtraLight.ttf new file mode 100644 index 0000000..d6ab75d Binary files /dev/null and b/src/shared/fonts/IBM_Plex_Mono/IBMPlexMono-ExtraLight.ttf differ diff --git a/src/shared/fonts/IBM_Plex_Mono/IBMPlexMono-ExtraLightItalic.ttf b/src/shared/fonts/IBM_Plex_Mono/IBMPlexMono-ExtraLightItalic.ttf new file mode 100644 index 0000000..88308ef Binary files /dev/null and b/src/shared/fonts/IBM_Plex_Mono/IBMPlexMono-ExtraLightItalic.ttf differ diff --git a/src/shared/fonts/IBM_Plex_Mono/IBMPlexMono-Italic.ttf b/src/shared/fonts/IBM_Plex_Mono/IBMPlexMono-Italic.ttf new file mode 100644 index 0000000..e259e84 Binary files /dev/null and b/src/shared/fonts/IBM_Plex_Mono/IBMPlexMono-Italic.ttf differ diff --git a/src/shared/fonts/IBM_Plex_Mono/IBMPlexMono-Light.ttf b/src/shared/fonts/IBM_Plex_Mono/IBMPlexMono-Light.ttf new file mode 100644 index 0000000..0dcb2fb Binary files /dev/null and b/src/shared/fonts/IBM_Plex_Mono/IBMPlexMono-Light.ttf differ diff --git a/src/shared/fonts/IBM_Plex_Mono/IBMPlexMono-LightItalic.ttf b/src/shared/fonts/IBM_Plex_Mono/IBMPlexMono-LightItalic.ttf new file mode 100644 index 0000000..f4a5fea Binary files /dev/null and b/src/shared/fonts/IBM_Plex_Mono/IBMPlexMono-LightItalic.ttf differ diff --git a/src/shared/fonts/IBM_Plex_Mono/IBMPlexMono-Medium.ttf b/src/shared/fonts/IBM_Plex_Mono/IBMPlexMono-Medium.ttf new file mode 100644 index 0000000..8253c5f Binary files /dev/null and b/src/shared/fonts/IBM_Plex_Mono/IBMPlexMono-Medium.ttf differ diff --git a/src/shared/fonts/IBM_Plex_Mono/IBMPlexMono-MediumItalic.ttf b/src/shared/fonts/IBM_Plex_Mono/IBMPlexMono-MediumItalic.ttf new file mode 100644 index 0000000..528b13b Binary files /dev/null and b/src/shared/fonts/IBM_Plex_Mono/IBMPlexMono-MediumItalic.ttf differ diff --git a/src/shared/fonts/IBM_Plex_Mono/IBMPlexMono-Regular.ttf b/src/shared/fonts/IBM_Plex_Mono/IBMPlexMono-Regular.ttf new file mode 100644 index 0000000..601ae94 Binary files /dev/null and b/src/shared/fonts/IBM_Plex_Mono/IBMPlexMono-Regular.ttf differ diff --git a/src/shared/fonts/IBM_Plex_Mono/IBMPlexMono-SemiBold.ttf b/src/shared/fonts/IBM_Plex_Mono/IBMPlexMono-SemiBold.ttf new file mode 100644 index 0000000..5e0b41d Binary files /dev/null and b/src/shared/fonts/IBM_Plex_Mono/IBMPlexMono-SemiBold.ttf differ diff --git a/src/shared/fonts/IBM_Plex_Mono/IBMPlexMono-SemiBoldItalic.ttf b/src/shared/fonts/IBM_Plex_Mono/IBMPlexMono-SemiBoldItalic.ttf new file mode 100644 index 0000000..58243dd Binary files /dev/null and b/src/shared/fonts/IBM_Plex_Mono/IBMPlexMono-SemiBoldItalic.ttf differ diff --git a/src/shared/fonts/IBM_Plex_Mono/IBMPlexMono-Thin.ttf b/src/shared/fonts/IBM_Plex_Mono/IBMPlexMono-Thin.ttf new file mode 100644 index 0000000..e069a64 Binary files /dev/null and b/src/shared/fonts/IBM_Plex_Mono/IBMPlexMono-Thin.ttf differ diff --git a/src/shared/fonts/IBM_Plex_Mono/IBMPlexMono-ThinItalic.ttf b/src/shared/fonts/IBM_Plex_Mono/IBMPlexMono-ThinItalic.ttf new file mode 100644 index 0000000..f3ed26b Binary files /dev/null and b/src/shared/fonts/IBM_Plex_Mono/IBMPlexMono-ThinItalic.ttf differ diff --git a/src/shared/fonts/IBM_Plex_Sans/IBMPlexSans-Italic-VariableFont_wdth,wght.ttf b/src/shared/fonts/IBM_Plex_Sans/IBMPlexSans-Italic-VariableFont_wdth,wght.ttf new file mode 100644 index 0000000..6232aaa Binary files /dev/null and b/src/shared/fonts/IBM_Plex_Sans/IBMPlexSans-Italic-VariableFont_wdth,wght.ttf differ diff --git a/src/shared/fonts/IBM_Plex_Sans/IBMPlexSans-VariableFont_wdth,wght.ttf b/src/shared/fonts/IBM_Plex_Sans/IBMPlexSans-VariableFont_wdth,wght.ttf new file mode 100644 index 0000000..9add875 Binary files /dev/null and b/src/shared/fonts/IBM_Plex_Sans/IBMPlexSans-VariableFont_wdth,wght.ttf differ diff --git a/src/shared/styles/zen.css b/src/shared/styles/zen.css index 3070597..4ca8c04 100644 --- a/src/shared/styles/zen.css +++ b/src/shared/styles/zen.css @@ -1,2 +1,125 @@ /* Tailwind v4: tells the consumer's Tailwind to scan this package's components */ @source "../../**/*.js"; + +/* ── IBM Plex Sans (variable font) ──────────────────────────────────────── */ +@font-face { + font-family: "IBM Plex Sans"; + src: url("../fonts/IBM_Plex_Sans/IBMPlexSans-VariableFont_wdth,wght.ttf") format("truetype"); + font-weight: 100 700; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: "IBM Plex Sans"; + src: url("../fonts/IBM_Plex_Sans/IBMPlexSans-Italic-VariableFont_wdth,wght.ttf") format("truetype"); + font-weight: 100 700; + font-style: italic; + font-display: swap; +} + +/* ── IBM Plex Mono (static fonts) ───────────────────────────────────────── */ +@font-face { + font-family: "IBM Plex Mono"; + src: url("../fonts/IBM_Plex_Mono/IBMPlexMono-Thin.ttf") format("truetype"); + font-weight: 100; + font-style: normal; + font-display: swap; +} +@font-face { + font-family: "IBM Plex Mono"; + src: url("../fonts/IBM_Plex_Mono/IBMPlexMono-ThinItalic.ttf") format("truetype"); + font-weight: 100; + font-style: italic; + font-display: swap; +} +@font-face { + font-family: "IBM Plex Mono"; + src: url("../fonts/IBM_Plex_Mono/IBMPlexMono-ExtraLight.ttf") format("truetype"); + font-weight: 200; + font-style: normal; + font-display: swap; +} +@font-face { + font-family: "IBM Plex Mono"; + src: url("../fonts/IBM_Plex_Mono/IBMPlexMono-ExtraLightItalic.ttf") format("truetype"); + font-weight: 200; + font-style: italic; + font-display: swap; +} +@font-face { + font-family: "IBM Plex Mono"; + src: url("../fonts/IBM_Plex_Mono/IBMPlexMono-Light.ttf") format("truetype"); + font-weight: 300; + font-style: normal; + font-display: swap; +} +@font-face { + font-family: "IBM Plex Mono"; + src: url("../fonts/IBM_Plex_Mono/IBMPlexMono-LightItalic.ttf") format("truetype"); + font-weight: 300; + font-style: italic; + font-display: swap; +} +@font-face { + font-family: "IBM Plex Mono"; + src: url("../fonts/IBM_Plex_Mono/IBMPlexMono-Regular.ttf") format("truetype"); + font-weight: 400; + font-style: normal; + font-display: swap; +} +@font-face { + font-family: "IBM Plex Mono"; + src: url("../fonts/IBM_Plex_Mono/IBMPlexMono-Italic.ttf") format("truetype"); + font-weight: 400; + font-style: italic; + font-display: swap; +} +@font-face { + font-family: "IBM Plex Mono"; + src: url("../fonts/IBM_Plex_Mono/IBMPlexMono-Medium.ttf") format("truetype"); + font-weight: 500; + font-style: normal; + font-display: swap; +} +@font-face { + font-family: "IBM Plex Mono"; + src: url("../fonts/IBM_Plex_Mono/IBMPlexMono-MediumItalic.ttf") format("truetype"); + font-weight: 500; + font-style: italic; + font-display: swap; +} +@font-face { + font-family: "IBM Plex Mono"; + src: url("../fonts/IBM_Plex_Mono/IBMPlexMono-SemiBold.ttf") format("truetype"); + font-weight: 600; + font-style: normal; + font-display: swap; +} +@font-face { + font-family: "IBM Plex Mono"; + src: url("../fonts/IBM_Plex_Mono/IBMPlexMono-SemiBoldItalic.ttf") format("truetype"); + font-weight: 600; + font-style: italic; + font-display: swap; +} +@font-face { + font-family: "IBM Plex Mono"; + src: url("../fonts/IBM_Plex_Mono/IBMPlexMono-Bold.ttf") format("truetype"); + font-weight: 700; + font-style: normal; + font-display: swap; +} +@font-face { + font-family: "IBM Plex Mono"; + src: url("../fonts/IBM_Plex_Mono/IBMPlexMono-BoldItalic.ttf") format("truetype"); + font-weight: 700; + font-style: italic; + font-display: swap; +} + +/* ── Enregistrement dans le thème Tailwind v4 ───────────────────────────── */ +@theme { + --font-ibm-plex-sans: "IBM Plex Sans", sans-serif; + --font-ibm-plex-mono: "IBM Plex Mono", monospace; +}