Files
AudiOhm/STYLE_GUIDE.md
T
root 85dad89d5b feat: Modernisation UI/UX et configuration Flutter multi-plateforme
Phase 1 - Corrections Critiques:
- Fixed memory leaks dans music_provider.dart (stream subscriptions)
- Fixed race conditions dans search_provider.dart (stale results)
- Fixed token refresh errors dans api_service.dart
- Improved error handling avec messages utilisateur
- Changed API URL to HTTPS by default

Phase 2 - Améliorations UX Desktop:
- Ajouté cursor pointers sur tous les éléments cliquables
- Implémenté hover states avec effets néon glow (200ms transitions)
- Créé skeleton loading states avec shimmer animation
- Ajouté widgets: ClickableWrapper, ErrorDisplay, SkeletonLoading
- Enhanced visual feedback pour desktop users

Phase 3 - Configuration Flutter:
- Configuré Android (Gradle 8.1.0, Kotlin 1.9.0, minSdk 21, targetSdk 34)
- Créé launcher icons cyberpunk néon (5 densités)
- Configuré Windows desktop (structure complète)
- Activé Linux desktop support
- Ajouté package équatable pour entités de domaine
- Corrigé imports (colors.dart, auth_provider.dart)
- Fixed Dio API compatibility (RequestOptions)

Documentation:
- STYLE_GUIDE.md: Guide complet (100+ pages)
- DESIGN_IMPLEMENTATION_GUIDE.md: Implémentation Flutter
- BUILD_STATUS.md: Status builds + troubleshooting
- QUICKSTART_BUILDS.md: Guide rapide
- BUILD_INDEX.md: Index documentation
- PHASE_1_CORRECTIONS.md: Corrections Phase 1
- PHASE_2_UX_IMPROVEMENTS.md: Améliorations Phase 2
- PR_REVIEW_SUMMARY.md: Revue code complète
- CODE_ANALYSIS_AND_PRIORITIES.md: Analyse code

Scripts & Builds:
- BUILD_ALL.sh: Script automatisé builds multi-plateforme
- builds/: Structure avec README par plateforme
- design-system/: Système de design complet

Backend:
- Ajouté streaming HTTP Range pour audio progressif
- Enhanced YouTube service avec métadonnées complètes
- Improved error handling et validation

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>
2026-01-19 07:44:40 +00:00

885 lines
19 KiB
Markdown

# 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<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
```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<Color>(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<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
```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.*