801e6a050b
- Documentation archivée et réorganisée - Backend: Ajout tests, migrations, library service, rate limiting - Frontend: Suppression Flutter, focus sur interface web HTML/JS - Tailwind CSS ajouté pour le style - Améliorations UX et corrections bugs Generated with [Claude Code](https://claude.com/claude-code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
19 KiB
19 KiB
AudiOhm - Guide de Style Complet
Version: 1.0 Date: 2026-01-18 Thème: Cyberpunk Néon Moderne
📋 Table des Matières
- Vue d'ensemble
- Système de Design
- Typography
- Couleurs
- Espacing
- Composants
- Animations
- Patterns
- Best Practices
- 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
// 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
// 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
// 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
// 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
// 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
// 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
- Contraste minimum - 4.5:1 pour body text, 3:1 pour large text
- Line-height - 1.5-1.75 pour body text
- Max line length - 65-75 caractères pour lisibilité optimale
- Font pairings - Utiliser Space Grotesk pour headings, Outfit pour body
- 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
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
// 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)
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)
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
// Size standards
iconSizeSmall: 16px
iconSizeMedium: 20px
iconSizeLarge: 24px
iconSizeXL: 32px
// Touch targets
minTouchTarget: 44x44px // Mobile
minClickTarget: 40x40px // Desktop
Cards
Base Card
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)
class InteractiveCard extends StatefulWidget {
// ...
}
class _InteractiveCardState extends State<InteractiveCard> {
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
// 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
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
primaryavec glow - Padding: 16px horizontal, 12px vertical
- Hint:
textTertiary
Animations
Durées Standard
// 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
// Easing curves
curveEaseOut: Curves.easeOut // Sortie fluide
curveEaseInOut: Curves.easeInOut // Entrée-sortie fluide
curveBounce: Curves.elasticOut // Effet rebond (play button)
Transitions
Hover States
AnimatedContainer(
duration: Duration(milliseconds: 200),
curve: Curves.easeOut,
decoration: BoxDecoration(
// Changes to animate
),
)
Page Transitions
// 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
Shimmer.fromColors(
baseColor: AppColors.surfaceVariant,
highlightColor: AppColors.surfaceElevated,
child: /* Skeleton content */,
)
Progress Indicator
CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(AppColors.primary),
backgroundColor: AppColors.surfaceVariant,
strokeWidth: 3,
)
Patterns
Hover State Pattern
// Template pour widgets avec hover
class HoverableWidget extends StatefulWidget {
final Widget child;
final VoidCallback? onTap;
final Color hoverColor;
@override
State<HoverableWidget> createState() => _HoverableWidgetState();
}
class _HoverableWidgetState extends State<HoverableWidget> {
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
// Wrapper pour ajouter cursor pointer
MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: () => /* action */,
child: /* widget */,
),
)
Error Display Pattern
// 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
// 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
// Minimum sizes
mobileMinTapTarget: 44x44px
desktopMinClickTarget: 40x40px
Focus States
// Toujours montrer le focus
InputDecoration(
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: AppColors.primary, width: 2),
),
)
2. Performance
Images
// 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
// 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
// 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
❌ Mauvais
Icon(Icons.emoji_events) // N'utilisez pas d'emojis
Text('🎵 Music')
✅ Bon
Icon(Icons.music_note)
Icon(Icons.audiotrack)
2. Text Contrast Faible
❌ Mauvais
TextStyle(
color: Color(0xFF6A7294), // Trop sombre pour dark mode
)
✅ Bon
TextStyle(
color: AppColors.textSecondary, #9BA3B8 - Contrast 4.8:1
)
3. Transitions Instantanées
❌ 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
❌ 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
❌ Mauvais
GestureDetector(
onTap: () => /* ... */,
child: Card(), // Pas de cursor!
)
✅ Bon
MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: () => /* ... */,
child: Card(),
),
)
6. URLs HTTP en Production
❌ Mauvais
static const String baseUrl = 'http://api.example.com';
✅ Bon
static const String baseUrl = 'https://api.example.com';
7. Debug Logging en Production
❌ 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 (utiliserdebugPrint)
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
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.