# AudiOhm - Guide de Style Complet **Version:** 1.0 **Date:** 2026-01-18 **Thème:** Cyberpunk Néon Moderne --- ## 📋 Table des Matières 1. [Vue d'ensemble](#vue-densemble) 2. [Système de Design](#système-de-design) 3. [Typography](#typography) 4. [Couleurs](#couleurs) 5. [Espacing](#espacement) 6. [Composants](#composants) 7. [Animations](#animations) 8. [Patterns](#patterns) 9. [Best Practices](#best-practices) 10. [Anti-Patterns](#anti-patterns) --- ## Vue d'ensemble ### Identité de Marque **AudiOhm** est une plateforme de streaming musicale alternative avec une esthétique **cyberpunk moderne**. **Mots-clés:** - Néon - Futuriste - Énergique - Immersif - Premium **Principes de Design:** - **Contraste élevé** - Lisibilité maximale - **Feedback immédiat** - Réponse à chaque interaction - **Transitions fluides** - Animations 200ms - **Glow néon** - Effets lumineux subtils - **Accessibilité** - WCAG AA compliant --- ## Système de Design ### Palette de Couleurs #### Backgrounds ```dart // Couleurs de fond background #0A0E27 // Fond principal (bleu nuit très foncé) surface #151932 // Surfaces (cards, panels) surfaceElevated #1F2342 // Surfaces élevées (hover) border #2A2F4A // Bordures, diviseurs ``` #### Néon Accents ```dart // Couleurs néon pour accents primary #00F0FF // Cyan électrique (CTA principal) secondary #BF00FF // Violet/magenta (actions secondaires) accent #FF006E // Rose néon (highlights, likes) success #00FF94 // Vert néon matrix (success states) warning #FFB800 // Jaune néon (warnings) error #FF3B3B // Rouge néon (errors) ``` #### Text ```dart // Couleurs de texte textPrimary #F0F4F8 // Titres, texte principal (14:1 contrast) textSecondary #9BA3B8 // Sous-titres, descriptions (4.8:1 contrast) textTertiary #6B7280 // Tertiaire, disabled (4.5:1 contrast) textInverted #0A0E27 // Texte sur fond néon ``` #### Gradients ```dart // Dégradés prédéfinis gradientPrimary: LinearGradient(135deg, #00F0FF, #BF00FF) gradientAccent: LinearGradient(135deg, #BF00FF, #FF006E) gradientFull: LinearGradient(-1,-1 → 1,1, #00F0FF, #BF00FF, #FF006E) gradientSurface: LinearGradient(180deg, rgba(21,25,50,0.9), rgba(10,14,39,0.95)) ``` #### Effets de Glow ```dart // Ombres néon glowPrimary(color: AppColors.primary) // BoxShadow avec cyan glowSecondary(color: AppColors.secondary) // BoxShadow avec violet glowAccent(color: AppColors.accent) // BoxShadow avec rose ``` --- ## Typography ### Font Families ```dart // Fonts importés de Google Fonts fontHeading: 'Space Grotesk' // Titres, headings fontBody: 'Outfit' // Corps de texte fontMono: 'JetBrains Mono' // Code, détails techniques // Imports import 'package:google_fonts/google_fonts.dart'; GoogleFonts.spaceGrotesk(fontWeight: FontWeight.w700) GoogleFonts.outfit(fontWeight: FontWeight.w400) ``` ### Type Scale | Role | Size | Weight | Line-Height | Usage | |------|------|--------|-------------|-------| | **Display** | 48px | 700 | 1.1 | Hero section, grands titres | | **H1** | 36px | 700 | 1.2 | Titres de pages | | **H2** | 28px | 600 | 1.3 | Sections | | **H3** | 22px | 600 | 1.4 | Sous-sections, card titles | | **Body Large** | 18px | 400 | 1.5 | Texte important | | **Body** | 16px | 400 | 1.6 | Texte standard | | **Body Small** | 14px | 400 | 1.6 | Texte secondaire | | **Caption** | 12px | 500 | 1.5 | Métadonnées | | **Overline** | 11px | 600 | 1.4 | Labels, tags, UPPERCASE | ### Règles Typography 1. **Contraste minimum** - 4.5:1 pour body text, 3:1 pour large text 2. **Line-height** - 1.5-1.75 pour body text 3. **Max line length** - 65-75 caractères pour lisibilité optimale 4. **Font pairings** - Utiliser Space Grotesk pour headings, Outfit pour body 5. **No font mix** - Ne pas mélanger plus de 2 fonts par page --- ## Espacement ### Système de Spacing Base: **4px** - Tous les espacements sont des multiples de 4 ```dart spacing1 4px // Gaps serrés, icon padding spacing2 8px // Small gaps, button padding compact spacing3 12px // Card padding compact, gaps spacing4 16px // Standard spacing, card padding spacing5 20px // Medium gaps spacing6 24px // Section padding, form fields spacing8 32px // Large gaps, content sections spacing10 40px // XL gaps, page padding spacing12 48px // XXL gaps, major sections spacing16 64px // Hero sections, page margins ``` ### Padding Standards ```dart // Cards paddingSmall: 12px // Compact cards paddingMedium: 16px // Standard cards paddingLarge: 20px // Large cards, featured cards // Pages paddingPage: 24px // Pages standard paddingPageLg: 32px // Pages avec plus d'espace // Sections gapSection: 48px // Espace entre sections majeures gapSubsection: 24px // Espace entre sous-sections ``` --- ## Composants ### Buttons #### Primary Button (Cyan Néon) ```dart Container( decoration: BoxDecoration( gradient: AppColors.gradientPrimary, borderRadius: BorderRadius.circular(8), boxShadow: AppColors.glowPrimary, ), child: Material( color: Colors.transparent, child: InkWell( onTap: onTap, borderRadius: BorderRadius.circular(8), child: Padding( padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12), child: Text( 'Button Text', style: TextStyle( color: AppColors.textInverted, fontWeight: FontWeight.w600, fontSize: 16, ), ), ), ), ), ) ``` **Règles:** - Background: Gradient cyan → violet - Text: Blanc sur fond néon - Glow: BoxShadow avec cyan - Hover: Glow intensifié - Padding: 24px horizontal, 12px vertical - Border radius: 8px #### Secondary Button (Ghost) ```dart Container( decoration: BoxDecoration( border: Border.all(color: AppColors.primary, width: 1), borderRadius: BorderRadius.circular(8), ), child: Material( color: Colors.transparent, child: InkWell( onTap: onTap, borderRadius: BorderRadius.circular(8), child: Padding( padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12), child: Text( 'Button Text', style: TextStyle( color: AppColors.primary, fontWeight: FontWeight.w600, fontSize: 16, ), ), ), ), ), ) ``` **Règles:** - Background: Transparent - Border: 1px cyan - Text: Cyan - Hover: Background cyan avec 10% opacity - Glow: Optional sur hover #### Icon Buttons ```dart // Size standards iconSizeSmall: 16px iconSizeMedium: 20px iconSizeLarge: 24px iconSizeXL: 32px // Touch targets minTouchTarget: 44x44px // Mobile minClickTarget: 40x40px // Desktop ``` ### Cards #### Base Card ```dart Container( decoration: BoxDecoration( color: AppColors.surface, borderRadius: BorderRadius.circular(16), border: Border.all( color: AppColors.border, width: 1, ), ), child: Padding( padding: EdgeInsets.all(20), child: /* Card content */, ), ) ``` #### Interactive Card (avec Hover) ```dart class InteractiveCard extends StatefulWidget { // ... } class _InteractiveCardState extends State { bool _isHovered = false; @override Widget build(BuildContext context) { return MouseRegion( onEnter: (_) => setState(() => _isHovered = true), onExit: (_) => setState(() => _isHovered = false), cursor: SystemMouseCursors.click, child: GestureDetector( onTap: widget.onTap, child: AnimatedContainer( duration: Duration(milliseconds: 200), decoration: BoxDecoration( color: AppColors.surface, borderRadius: BorderRadius.circular(16), border: Border.all( color: _isHovered ? AppColors.primary : AppColors.border, width: _isHovered ? 2 : 1, ), boxShadow: _isHovered ? [ BoxShadow( color: AppColors.primary.withOpacity(0.15), blurRadius: 20, offset: Offset(0, 4), ), ] : null, ), child: /* Card content */, ), ), ); } } ``` **Règles:** - Background: `surface` - Border: `border` (1px) - Border radius: 16px - Padding: 20px - Hover: Border accent + glow #### Album Card ```dart // Dimensions albumCardSmall: 120x120px // Mobile, compact grids albumCardMedium: 160x160px // Tablet, desktop albumCardLarge: 200x200px // Featured, hero // Aspect ratios albumArtSquare: 1:1 playlistCover: 1:1 artistThumbnail: 1:1 ``` ### Inputs #### Text Field ```dart TextField( decoration: InputDecoration( filled: true, fillColor: AppColors.background, border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: AppColors.border, width: 2), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: AppColors.border, width: 2), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: AppColors.primary, width: 2), ), contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 12), hintStyle: TextStyle( color: AppColors.textTertiary, fontSize: 16, ), ), ) ``` **Règles:** - Background: `background` (plus foncé que surface) - Border: `border` (2px) - Border radius: 8px - Focus: Border devient `primary` avec glow - Padding: 16px horizontal, 12px vertical - Hint: `textTertiary` --- ## Animations ### Durées Standard ```dart // Timing fast: Duration(milliseconds: 150) // Micro-interactions base: Duration(milliseconds: 200) // Hover, color changes slow: Duration(milliseconds: 300) // Layout changes, modals slower: Duration(milliseconds: 500) // Page transitions ``` ### Curves ```dart // Easing curves curveEaseOut: Curves.easeOut // Sortie fluide curveEaseInOut: Curves.easeInOut // Entrée-sortie fluide curveBounce: Curves.elasticOut // Effet rebond (play button) ``` ### Transitions #### Hover States ```dart AnimatedContainer( duration: Duration(milliseconds: 200), curve: Curves.easeOut, decoration: BoxDecoration( // Changes to animate ), ) ``` #### Page Transitions ```dart // Slide up (modals, sheets) PageRouteBuilder( pageBuilder: (context, animation, _) => child, transitionDuration: Duration(milliseconds: 300), transitionsBuilder: (context, animation, _, child) { const begin = Offset(0.0, 1.0); const end = Offset.zero; final tween = Tween(begin: begin, end: end); return SlideTransition( position: animation.drive(tween), child: child, ); }, ) // Fade (page changes) PageRouteBuilder( pageBuilder: (context, animation, _) => child, transitionDuration: Duration(milliseconds: 200), transitionsBuilder: (context, animation, _, child) { return FadeTransition( opacity: animation, child: child, ); }, ) ``` ### Loading States #### Skeleton Loading ```dart Shimmer.fromColors( baseColor: AppColors.surfaceVariant, highlightColor: AppColors.surfaceElevated, child: /* Skeleton content */, ) ``` #### Progress Indicator ```dart CircularProgressIndicator( valueColor: AlwaysStoppedAnimation(AppColors.primary), backgroundColor: AppColors.surfaceVariant, strokeWidth: 3, ) ``` --- ## Patterns ### Hover State Pattern ```dart // Template pour widgets avec hover class HoverableWidget extends StatefulWidget { final Widget child; final VoidCallback? onTap; final Color hoverColor; @override State createState() => _HoverableWidgetState(); } class _HoverableWidgetState extends State { bool _isHovered = false; @override Widget build(BuildContext context) { return MouseRegion( onEnter: (_) => setState(() => _isHovered = true), onExit: (_) => setState(() => _isHovered = false), cursor: SystemMouseCursors.click, child: GestureDetector( onTap: widget.onTap, child: AnimatedContainer( duration: Duration(milliseconds: 200), decoration: BoxDecoration( border: Border.all( color: _isHovered ? widget.hoverColor : AppColors.border, width: _isHovered ? 2 : 1, ), boxShadow: _isHovered ? [ BoxShadow( color: widget.hoverColor.withOpacity(0.3), blurRadius: 20, ), ] : null, ), child: widget.child, ), ), ); } } ``` ### Clickable Wrapper Pattern ```dart // Wrapper pour ajouter cursor pointer MouseRegion( cursor: SystemMouseCursors.click, child: GestureDetector( onTap: () => /* action */, child: /* widget */, ), ) ``` ### Error Display Pattern ```dart // Affichage user-friendly des erreurs if (errorMessage != null) { Container( padding: EdgeInsets.all(16), decoration: BoxDecoration( color: AppColors.error.withOpacity(0.1), border: Border.all( color: AppColors.error.withOpacity(0.3), width: 1, ), borderRadius: BorderRadius.circular(12), ), child: Row( children: [ Icon(Icons.error_outline, color: AppColors.error, size: 20), SizedBox(width: 8), Expanded(child: Text(errorMessage)), if (onRetry != null) TextButton( onPressed: onRetry, child: Text('Retry'), ), ], ), ) } ``` --- ## Best Practices ### 1. Accessibilité #### Contrast ```dart // TOUJOURS vérifier le contraste ✅ Bon: textPrimary (#F0F4F8) sur background (#0A0E27) = 14:1 ✅ Bon: textSecondary (#9BA3B8) sur background (#0A0E27) = 4.8:1 ❌ Mauvais: muted (#6A7294) sur background (#0A0E27) = 2.1:1 ``` #### Touch Targets ```dart // Minimum sizes mobileMinTapTarget: 44x44px desktopMinClickTarget: 40x40px ``` #### Focus States ```dart // Toujours montrer le focus InputDecoration( focusedBorder: OutlineInputBorder( borderSide: BorderSide(color: AppColors.primary, width: 2), ), ) ``` ### 2. Performance #### Images ```dart // Utiliser cached_network_image CachedNetworkImage( imageUrl: url, placeholder: (context, url) => CircularProgressIndicator(), errorWidget: (context, url, error) => Icon(Icons.error), ) // OU utiliser le widget personnalisé AlbumArtImage(imageUrl: url) ``` #### Animations ```dart // Utiliser AnimatedContainer au lieu de AnimationController AnimatedContainer( duration: Duration(milliseconds: 200), // Props to animate ) // Pour reduced-motion if (MediaQuery.of(context).disableAnimations) { // Skip animations } ``` ### 3. Responsive ```dart // Breakpoints mobile: < 768px tablet: 768px - 1024px desktop: >= 1024px // Exemple LayoutBuilder( builder: (context, constraints) { if (constraints.maxWidth >= 1024) { return DesktopLayout(); } else if (constraints.maxWidth >= 768) { return TabletLayout(); } else { return MobileLayout(); } }, ) ``` --- ## Anti-Patterns ### ❌ À ÉVITER #### 1. Emojis comme Icônes ```dart ❌ Mauvais Icon(Icons.emoji_events) // N'utilisez pas d'emojis Text('🎵 Music') ✅ Bon Icon(Icons.music_note) Icon(Icons.audiotrack) ``` #### 2. Text Contrast Faible ```dart ❌ Mauvais TextStyle( color: Color(0xFF6A7294), // Trop sombre pour dark mode ) ✅ Bon TextStyle( color: AppColors.textSecondary, #9BA3B8 - Contrast 4.8:1 ) ``` #### 3. Transitions Instantanées ```dart ❌ Mauvais duration: Duration(milliseconds: 0) duration: Duration(milliseconds: 100) // Trop rapide ✅ Bon duration: Duration(milliseconds: 200) // Standard duration: Duration(milliseconds: 300) // Pour layouts ``` #### 4. Scale sur Hover ```dart ❌ Mauvais - Provoque layout shift Transform.scale( scale: _isHovered ? 1.02 : 1.0, child: Card(), ) ✅ Bon - Utilise color/shadow AnimatedContainer( decoration: BoxDecoration( border: Border.all( color: _isHovered ? AppColors.primary : AppColors.border, width: _isHovered ? 2 : 1, ), boxShadow: _isHovered ? [/* glow */] : null, ), ) ``` #### 5. Cursor Pointer Manquant ```dart ❌ Mauvais GestureDetector( onTap: () => /* ... */, child: Card(), // Pas de cursor! ) ✅ Bon MouseRegion( cursor: SystemMouseCursors.click, child: GestureDetector( onTap: () => /* ... */, child: Card(), ), ) ``` #### 6. URLs HTTP en Production ```dart ❌ Mauvais static const String baseUrl = 'http://api.example.com'; ✅ Bon static const String baseUrl = 'https://api.example.com'; ``` #### 7. Debug Logging en Production ```dart ❌ Mauvais print('Debug info'); print(userToken); // Expose des données sensibles! ✅ Bon if (kDebugMode) { debugPrint('Debug info'); } ``` --- ## Glossaire ### Terms - **Néon**: Couleurs vives, lumineuses (cyan, violet, rose) - **Glow**: Ombre portée lumineuse autour des éléments - **Skeleton**: Placeholder animé pendant le chargement - **Hover**: État quand la souris passe sur un élément - **CTA**: Call-to-Action (bouton principal) - **WCAG AA**: Standard d'accessibilité web (contrast minimum 4.5:1) ### Color Names - **Cyan**: Primary accent color (#00F0FF) - **Violet**: Secondary accent (#BF00FF) - **Rose**: Accent color (#FF006E) - **Surface**: Elevated backgrounds (#151932) - **Muted**: Disabled text (#6B7280) --- ## Checklist Rapide ### Avant de Committer - [ ] Pas d'emojis comme icônes - [ ] Tous les éléments cliquables ont `cursor: pointer` - [ ] Hover states avec transitions 200ms - [ ] Contrast minimum 4.5:1 pour text - [ ] Focus states visibles sur inputs - [ ] Animations respectent `prefers-reduced-motion` - [ ] Images utilisent `CachedNetworkImage` - [ ] Pas de scale transforms sur hover - [ ] URLs en HTTPS (pas HTTP) - [ ] Pas de `print()` en production (utiliser `debugPrint`) ### Tests - [ ] Test à 375px (mobile) - [ ] Test à 768px (tablet) - [ ] Test à 1024px (desktop) - [ ] Test à 1440px (wide) - [ ] Test hover states sur desktop - [ ] Test animations avec reduced-motion - [ ] Test contrast avec color blindness simulator - [ ] Test touch targets sur mobile --- ## Ressources ### Documentation - **Design System Master**: `design-system/MASTER.md` - **Page Overrides**: `design-system/pages/*.md` - **Implementation Guide**: `DESIGN_IMPLEMENTATION_GUIDE.md` ### Outils - **Contrast Checker**: https://webaim.org/resources/contrastchecker/ - **Color Blindness Simulator**: https://www.toptal.com/designers/colorfilterweb/ - **Google Fonts**: https://fonts.google.com/ - **Lucide Icons**: https://lucide.dev/ - **Heroicons**: https://heroicons.com/ ### Packages Flutter ```yaml dependencies: google_fonts: ^6.1.0 shimmer: ^3.0.0 cached_network_image: ^3.3.1 dev_dependencies: flutter_lints: ^3.0.1 ``` --- ## Changelog ### Version 1.0 (2026-01-18) - Initial style guide - Système de couleurs cyberpunk néon - Typography (Space Grotesk + Outfit) - Composants standards (buttons, cards, inputs) - Animations et transitions - Patterns et best practices - Anti-patterns documentés --- **Mainteneurs:** AudiOhm Design Team **Contact:** design@audiohm.com **License:** MIT --- *Ce guide est la source de vérité pour tous les aspects visuels et d'UX d'AudiOhm. Toute nouvelle implémentation doit suivre ces standards.*