Spec for complete visual overhaul of MyIce KMP app: - Teal arctique color scheme (full Material 3 slots, light/dark) - Typography scale, shape tokens, spacing tokens (Dimens) - 4 screens redesigned (Login, Schedule, EventDetail + navigation) - New components: SkeletonCard, EmptyState, ErrorState, FilterChips - EventCard rework: color border, type badge, leading icons - Sticky date headers in schedule list - French accents corrected, hardcoded colors removed - Branded as 'MyIce' (dropped tech codename 'K') Refs: brainstorming session 2026-08-06
12 KiB
MyIce UI Redesign — Design Spec
Date: 2026-08-06 Status: Approved (pending review) Scope: Refonte visuelle complète — thème, 4 écrans, composants
Contexte
L'app MyIce (KMP, Compose Multiplatform) est fonctionnellement complète mais visuellement squelettique: thème à 4 slots Material 3, zéro icônerie, listes plates, couleurs hardcodées, accents français manquants, pas de regroupement par date, pas de typographie dédiée.
Ce spec décrit une refonte visuelle complète dans une direction moderne & épurée avec un accent teal arctique.
Direction visuelle
- Style: Moderne & épuré — beaucoup d'air, surfaces propres, typo hiérarchisée, accents subtils
- Accent: Teal arctique (
#0D9488light /#2DD4BFdark) — évoque la glace et le hockey - Philosophie: Le chrome disparaît, les données sont les vedettes
1. Thème & Design Tokens
1.1 Palette (Material 3 complet)
Light scheme:
| Slot | Valeur | Note |
|---|---|---|
| primary | #0D9488 (teal-600) |
|
| onPrimary | #FFFFFF |
|
| primaryContainer | #99F6E4 (teal-100) |
|
| onPrimaryContainer | #134E4A (teal-900) |
|
| secondary | #475569 (slate-600) |
|
| secondaryContainer | #E2E8F0 (slate-200) |
|
| onSecondaryContainer | #1E293B |
|
| tertiary | #0EA5E9 (sky-500) |
états interactifs secondaires |
| tertiaryContainer | #E0F2FE |
|
| error | #DC2626 |
|
| onError | #FFFFFF |
|
| errorContainer | #FEE2E2 |
|
| onErrorContainer | #7F1D1D |
|
| background | #FAFAFA (warm white) |
|
| onBackground | #1C1917 |
|
| surface | #FFFFFF |
|
| onSurface | #1C1917 (warm dark) |
|
| surfaceVariant | #F1F5F9 (slate-100) |
|
| onSurfaceVariant | #475569 |
|
| outline | #CBD5E1 (slate-300) |
Dark scheme:
| Slot | Valeur | Note |
|---|---|---|
| primary | #2DD4BF (teal-400) |
|
| onPrimary | #003833 |
|
| primaryContainer | #0D9488 |
|
| onPrimaryContainer | #99F6E4 |
|
| secondary | #94A3B8 (slate-400) |
|
| secondaryContainer | #334155 (slate-700) |
|
| tertiary | #0EA5E9 |
|
| background | #0F172A (slate-900) |
|
| onBackground | #F1F5F9 |
|
| surface | #1E293B (slate-800) |
|
| onSurface | #F1F5F9 |
|
| surfaceVariant | #334155 |
|
| onSurfaceVariant | #94A3B8 |
|
| outline | #475569 |
|
| error | #F87171 |
|
| onError | #7F1D1D |
|
| errorContainer | #7F1D1D |
|
| onErrorContainer | #FECACA |
1.2 Typographie
Échelle système (pas de custom font — trop complexe en KMP), mais poids et tailles définis explicitement via Typography():
| Style | Taille | Poids | Usage |
|---|---|---|---|
| headlineLarge | 28sp | Bold | App name (login) |
| titleLarge | 22sp | SemiBold | Screen titles (TopAppBar) |
| titleMedium | 16sp | SemiBold | Card titles |
| bodyLarge | 15sp | Regular | Main content |
| bodyMedium | 14sp | Regular | Secondary info |
| bodySmall | 13sp | Regular | Metadata, labels |
| labelMedium | 12sp | Medium | Chips, badges, section headers |
1.3 Shapes
| Nom | Rayon | Usage |
|---|---|---|
| small | 8dp | Chips, badges |
| medium | 12dp | Cards, inputs |
| large | 16dp | Sheets, big cards |
1.4 Spacing tokens (Dimens object)
| Token | Valeur |
|---|---|
| xs | 4dp |
| sm | 8dp |
| md | 12dp |
| lg | 16dp |
| xl | 24dp |
| xxl | 32dp |
1.5 Elevation
| Niveau | Valeur | Usage |
|---|---|---|
| level0 | 0dp | Flat surfaces |
| level1 | 1dp | Cards |
| level2 | 3dp | App bar, dialogs |
2. Écrans
2.1 LoginScreen
Layout centré vertical:
- Icône
SportsHockey(Material) 48dp dans un cercleprimaryContainer80dp, en haut "MyIce"enheadlineLarge(le "K" de "MyIceK" était un codename tech — supprimé)- Sous-titre
"Votre calendrier hockey"entitleMediumonSurfaceVariant - Spacer
xxl(32dp) Buttonplein, 48dp de haut, icôneLoginleading:"Se connecter avec Infomaniak"- Snackbar erreur en
errorContainer/onErrorContainer - Fond
background, contenu centré verticalement (pas collé au haut)
2.2 ScheduleScreen
TopAppBar:
- Titre
"Calendrier"(au lieu de "MyIceK") - Actions: icône
Refresh+ icôneLogoutuniquement (Material icons, pas de texte) - L'email utilisateur est supprimé de la barre (trop encombrant sur mobile). Il n'est pas relocalisé — un menu compte futur est hors scope.
Filtres:
SegmentedButton(singleChoice, 3 segments) sur une ligne:"Tous"/"Matchs"/"Entraînements"(remplace leFilterDropdownpour le type)AssistChipcliquables pour Compte / Age / Sous-groupe (affichent la valeur sélectionnée,"Tous"par défaut). Utilisentaccount.labelau lieu deaccount.namepour le dropdown Compte.AssistChip"Effacer" avec icôneCloseapparaît si au moins un filtre est actif- Section filtres dans une
CardsurfaceVariant,mediumshape, paddinglg
Liste d'événements:
LazyColumnavec sticky headers par jour:"Samedi 15 mars"enlabelMediumuppercaseonSurfaceVariant, fondsurface, divider subtil en dessousEventCardrepensé (voir Section 3)- Indicateur
"Dernière mise à jour: ..."en bas de liste,bodySmallonSurfaceVariant(utiliselastUpdateddu state — actuellement non affiché) PullToRefreshBoxconservé (Material 3 par défaut)
États partagés:
- Loading: 3-4
SkeletonCardanimés (voir Section 3) - Error:
ErrorStateavec icôneCloudOff, message, bouton"Réessayer" - Empty:
EmptyStateavec icôneCalendarClear,"Aucun événement disponible"
2.3 EventDetailScreen
TopAppBar:
- Navigation icon:
ArrowBack(icône, pasTextButton("Retour")) - Titre: titre de l'événement tronqué
PlatformBackHandlerconservé
Section infos (carte unique, medium shape, elevation 1dp):
- Chaque ligne: icône leading 18dp
onSurfaceVariant+ labelbodySmalluppercaseonSurfaceVariant+ valeurbodyLargeonSurface - Lignes:
Event→ type,Place→ lieu,CalendarMonth→ date,Schedule→ heure - Plus de
"Type: Match"interpolé — label et valeur visuellement distincts
Convocation:
- En-tête
"Joueurs (N)"entitleMediumSemiBold +BadgedBoxavec count - Joueurs:
ListItemMaterial 3 — numéro dans un cercleprimaryContainer40dp (avatar), nombodyLarge, position +"(née 2008)"enbodySmalldessous. Triés par numéro. - Si aucun joueur: carte
errorContaineravec icôneWarning+"Aucun joueur ni personnel convoqué" - Personnel:
ListItemavec icônePersonleading, nombodyLarge, role enAssistChipsecondaryContainerà droite - En-tête
"Personnel"entitleMediumSemiBold
États partagés:
- Loading:
LoadingIndicatorconservé - Error:
ErrorStateavec icôneErrorOutline, message, bouton"Réessayer"
2.4 Navigation & transitions
- Navigation manuelle conservée (sealed class +
mutableStateOf— pas de lib externe) AnimatedContentajouté dansApp.ktpour transitions fondues entre écrans (tween(300ms))PlatformBackHandlerconservé (known limitation: no-op sur desktop/iOS)
3. Composants
3.1 EventCard (repensé)
Carte medium shape (12dp), elevation 1dp, cliquable:
- Barre couleur 4dp à gauche via
Modifier.bordersur leCardentier (pas unRow) — couleurevent.color(hex parsé), fallbackprimary - Badge type en haut-droite:
AssistChip"Match"(couleur teal/primary) ou"Entraînement"(couleur secondary/slate) - Titre: pour un match (
eventType == "Jeu"):"Match vs {opponent}". Pour un entraînement:"Entraînement". Suppression de la ligne"Adversaire:"pour les entraînements. - Lignes info avec icônes leading 18dp
onSurfaceVariant:Place18dp + lieu enbodyMediumSchedule18dp + heure condensée (formatEventTimeRange:"18h00 – 19h30")
- Âge + nom (
"{agegroup} - {name}") entitleMediumSemiBold - La date est dans le header de section (pas dans la card)
3.2 SkeletonCard
Carte placeholder grise aux mêmes proportions que EventCard:
- Fond
surfaceVariant,alpha(0.3f) - Animation shimmer: translation horizontale d'un dégradé clair (
InfiniteTransition+rememberInfiniteTransition) - 4 lignes fictives (titre + 2 lignes info + badge)
- Réutilisé pour le loading state du Schedule
3.3 EmptyState / ErrorState (composables partagés)
EmptyState(icon: ImageVector, title: String, message: String)
ErrorState(icon: ImageVector, title: String, message: String, onRetry: () -> Unit)
- Centrés verticalement dans un
Column(Arrangement.Center,HorizontalAlignment.CenterHorizontally) - Icône 48dp
onSurfaceVariant - Titre
titleMediumonSurface - Message
bodyMediumonSurfaceVariant ErrorStateajoute unButton"Réessayer"aveconRetry- Réutilisés sur ScheduleScreen et EventDetailScreen
3.4 Wrappers filtres
SegmentedButton(singleChoice) pour le type: 3 options"Tous"/"Matchs"/"Entraînements", mappe versTypeFilter.ALL/GAMES/PRACTICESAssistChipwrapper pour Compte/Age/Sous-groupe: affiche label + valeur sélectionnée, clic ouvre unDropdownMenuAssistChip"Effacer" avec icôneClose: visible si au moins un filtre non-défaut est actif, reset tous les filtres
4. Corrections transversales
4.1 Accents français
Toutes les strings UI sont corrigées:
- "Rafraichir" → "Rafraîchir"
- "Deconnexion" → "Déconnexion"
- "Reessayer" → "Réessayer"
- "Aucun evenement" → "Aucun événement"
- "Entrainements" → "Entraînements"
- "convoque" → "convoqué"
- "donnees" → "données"
4.2 Couleurs hardcodées supprimées
Color.Gray(ScheduleScreen) →onSurfaceVariantColor(0xFFFFC107)(EventDetailScreen warning) →errorContainer/onErrorContainerColor(0xFF000000 or value)(EventCard color mask) →event.colordirect ouprimaryfallback via une util helperparseHexColor
4.3 Branding
- "MyIceK" → "MyIce" partout (TopAppBar, LoginScreen, titre fenêtre desktop)
account.labelutilisé au lieu deaccount.namedans le dropdown Compte
4.4 Strings
Toutes les strings restent hardcodées inline (pas de localization infrastructure — hors scope). Pas de object Strings non plus: ajouter une couche d'indirection sans localization réelle est du YAGNI. Les accents français (4.1) sont corrigés directement dans les strings inline.
5. Hors scope
- Migration vers Navigation Compose library
viewModel()au lieu deremember{}(known limitation — perte de state rotation Android)- Localization infrastructure (string resources)
- Custom fonts (trop complexe en KMP)
- Tests UI (Composition Testing) — le projet n'a pas de framework de test UI configuré
- iOS
.xcodeprojcreation - Desktop window icon / menu bar
6. Fichiers impactés
| Fichier | Changement |
|---|---|
ui/theme/Theme.kt |
Réécriture complète: palette, typo, shapes, elevation, Dimens |
ui/theme/Color.kt |
Nouveau — définitions de couleur teal/slate |
ui/theme/Dimens.kt |
Nouveau — spacing tokens |
ui/screens/LoginScreen.kt |
Refonte: icône, branding, layout |
ui/screens/ScheduleScreen.kt |
Refonte: TopAppBar, chips filtres, sticky headers, états partagés, lastUpdated |
ui/screens/EventDetailScreen.kt |
Refonte: icône back, section infos stylisée, ListItems pour joueurs/staff |
ui/components/EventCard.kt |
Refonte: border couleur, badge type, icônes leading, logique match/entraînement |
ui/components/SkeletonCard.kt |
Nouveau — skeleton loading |
ui/components/EmptyState.kt |
Nouveau — état vide partagé |
ui/components/ErrorState.kt |
Nouveau — état erreur partagé |
ui/components/FilterChips.kt |
Nouveau — SegmentedButton + AssistChips (remplace FilterDropdown) |
ui/components/FilterDropdown.kt |
Supprimé — remplacé par FilterChips |
ui/components/LoadingIndicator.kt |
Conservé (utilisé par EventDetail) |
App.kt |
AnimatedContent pour transitions, branding "MyIce" |
util/EventDateTimeFormatter.kt |
Ajout formatEventTimeRange condensé (existant conservé) |
desktopMain/Main.kt |
Titre fenêtre "MyIce" |