Files
myice-kmp/docs/superpowers/specs/2026-08-06-ui-redesign-design.md
T
herel d4f43de933 docs: add UI redesign design spec (teal arctique, moderne & épuré)
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
2026-08-06 15:35:22 +02:00

12 KiB
Raw Blame History

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 (#0D9488 light / #2DD4BF dark) — é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 cercle primaryContainer 80dp, en haut
  • "MyIce" en headlineLarge (le "K" de "MyIceK" était un codename tech — supprimé)
  • Sous-titre "Votre calendrier hockey" en titleMedium onSurfaceVariant
  • Spacer xxl (32dp)
  • Button plein, 48dp de haut, icône Login leading: "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ône Logout uniquement (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 le FilterDropdown pour le type)
  • AssistChip cliquables pour Compte / Age / Sous-groupe (affichent la valeur sélectionnée, "Tous" par défaut). Utilisent account.label au lieu de account.name pour le dropdown Compte.
  • AssistChip "Effacer" avec icône Close apparaît si au moins un filtre est actif
  • Section filtres dans une Card surfaceVariant, medium shape, padding lg

Liste d'événements:

  • LazyColumn avec sticky headers par jour: "Samedi 15 mars" en labelMedium uppercase onSurfaceVariant, fond surface, divider subtil en dessous
  • EventCard repensé (voir Section 3)
  • Indicateur "Dernière mise à jour: ..." en bas de liste, bodySmall onSurfaceVariant (utilise lastUpdated du state — actuellement non affiché)
  • PullToRefreshBox conservé (Material 3 par défaut)

États partagés:

  • Loading: 3-4 SkeletonCard animés (voir Section 3)
  • Error: ErrorState avec icône CloudOff, message, bouton "Réessayer"
  • Empty: EmptyState avec icône CalendarClear, "Aucun événement disponible"

2.3 EventDetailScreen

TopAppBar:

  • Navigation icon: ArrowBack (icône, pas TextButton("Retour"))
  • Titre: titre de l'événement tronqué
  • PlatformBackHandler conservé

Section infos (carte unique, medium shape, elevation 1dp):

  • Chaque ligne: icône leading 18dp onSurfaceVariant + label bodySmall uppercase onSurfaceVariant + valeur bodyLarge onSurface
  • 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)" en titleMedium SemiBold + BadgedBox avec count
  • Joueurs: ListItem Material 3 — numéro dans un cercle primaryContainer 40dp (avatar), nom bodyLarge, position + "(née 2008)" en bodySmall dessous. Triés par numéro.
  • Si aucun joueur: carte errorContainer avec icône Warning + "Aucun joueur ni personnel convoqué"
  • Personnel: ListItem avec icône Person leading, nom bodyLarge, role en AssistChip secondaryContainer à droite
  • En-tête "Personnel" en titleMedium SemiBold

États partagés:

  • Loading: LoadingIndicator conservé
  • Error: ErrorState avec icône ErrorOutline, message, bouton "Réessayer"

2.4 Navigation & transitions

  • Navigation manuelle conservée (sealed class + mutableStateOf — pas de lib externe)
  • AnimatedContent ajouté dans App.kt pour transitions fondues entre écrans (tween(300ms))
  • PlatformBackHandler conservé (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.border sur le Card entier (pas un Row) — couleur event.color (hex parsé), fallback primary
  • 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:
    • Place 18dp + lieu en bodyMedium
    • Schedule 18dp + heure condensée (formatEventTimeRange: "18h00 19h30")
  • Âge + nom ("{agegroup} - {name}") en titleMedium SemiBold
  • 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 titleMedium onSurface
  • Message bodyMedium onSurfaceVariant
  • ErrorState ajoute un Button "Réessayer" avec onRetry
  • Réutilisés sur ScheduleScreen et EventDetailScreen

3.4 Wrappers filtres

  • SegmentedButton (singleChoice) pour le type: 3 options "Tous"/"Matchs"/"Entraînements", mappe vers TypeFilter.ALL/GAMES/PRACTICES
  • AssistChip wrapper pour Compte/Age/Sous-groupe: affiche label + valeur sélectionnée, clic ouvre un DropdownMenu
  • AssistChip "Effacer" avec icône Close: 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) → onSurfaceVariant
  • Color(0xFFFFC107) (EventDetailScreen warning) → errorContainer/onErrorContainer
  • Color(0xFF000000 or value) (EventCard color mask) → event.color direct ou primary fallback via une util helper parseHexColor

4.3 Branding

  • "MyIceK" → "MyIce" partout (TopAppBar, LoginScreen, titre fenêtre desktop)
  • account.label utilisé au lieu de account.name dans 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 de remember{} (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 .xcodeproj creation
  • 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"