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>
885 lines
19 KiB
Markdown
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.*
|