Refactor — Consolidamento token tema mobile

Commit: refactor(theme): consolidate tokens, wire violet as tertiary · Branch: edit/mobile-audit-ux-polish · Audit di riferimento: Audit UX polish 2026-04-20

Problema

L’audit ha rilevato tre classi di debito nel file lib/core/theme/app_theme.dart:

  1. Token bugatoradius32 = 24.0 con commento “WAS 32. Reduced for density”. Il nome del token mente sul valore. Ogni sviluppatore che scriveva AppTheme.radius32 si aspettava 32 e ne otteneva 24 → valore sbagliato che si propaga nei consumer senza avvertire nessuno.
  2. Alias ambiguitextSecondary, textSecondaryLight e textPrimaryLight con valori identici declared come literal separati (doppio source-of-truth). Più textSecondaryWarm con un valore diverso ma nome non-semantico.
  3. Violet non esposto nel temaColorScheme.secondary era impostato a primary (coral), quindi Material FAB, chip selezionati, focus ring usavano tutti coral. Zero gerarchia cromatica; il violet — secondo colore brand — viveva solo in gradient decorativi.

Decisione

Token radius

  • Rimosso radius32 (valore mentitorio). I 5 call-site esterni sono stati aggiornati caso per caso:
FileContestoNuovo token
features/profile/screens/profile_screen.dart:209Container/Cardradius24
features/settings/screens/privacy_settings_screen.dart:530Top di bottom sheetradius28
shared/widgets/feedback_survey_sheet.dart:105Top di bottom sheetradius28
shared/widgets/guest_guard.dart:30Top di bottom sheetradius28
shared/widgets/minimal_event_card.dart:369Cardradius24

Regola: radius28 per i top dei bottom sheet (coerente con bottomSheetTheme), radius24 per card e contenitori “hero”.

Token colore testo

Riorganizzata la sezione Text Colors in 3 token canonici + 3 alias legacy:

// Canonici — preferire in nuovo codice
static const Color textPrimary   = Color(0xFF18181B); // Zinc 900
static const Color textSecondary = Color(0xFF52525B); // Zinc 600
static const Color textMuted     = Color(0xFF9E9E9E); // Softer grey
static const Color textPrimaryDark   = Color(0xFFFFFFFF);
static const Color textSecondaryDark = Color(0xFFA1A1AA);
 
// Legacy — non rimossi per evitare churn di 28 file, ora puntano agli canonici
static const Color textPrimaryLight   = textPrimary;
static const Color textSecondaryLight = textSecondary;
static const Color textSecondaryWarm  = textMuted;

Il rename completo dei 147 call-site è deferito (troppo rischio di regressione in un singolo commit). I nuovi schermi vanno scritti usando solo i nomi canonici.

Token primaryBrand

Rimosso. Aveva un unico consumer (manage_event_types_screen.dart:119) sostituito con il token esplicito electricCoral. Non serviva un alias nominativo.

ColorScheme — gerarchia cromatica

// Prima
colorScheme: ColorScheme(
  primary: electricCoral,
  secondary: electricCoral,   // ← duplicato
  ...
);
 
// Ora
colorScheme: ColorScheme(
  primary: electricCoral,     // main CTAs, brand signature
  secondary: electricViolet,  // brand complement, selected chips, highlights
  onSecondary: pureWhite,
  tertiary: mintGreen,        // success accents, gamification states
  onTertiary: inkBlack,
  ...
);

Impatto visivo atteso

Con il wiring del nuovo ColorScheme, i seguenti widget cambiano colore senza modifiche al loro codice:

  • chat_screen.dart typing indicator (colorScheme.secondary) — da coral a violet (status muted, più raffinato).
  • profile_avatar.dart color-from-hash — ora pesca fra coral / violet / mint, aumentando la varietà degli avatar generati.
  • Tutti i futuri consumer di Theme.of(context).colorScheme.tertiary ora hanno un verde brand disponibile senza hardcodare.

Verifica

flutter analyze lib/core/theme/app_theme.dart \
  lib/features/events/screens/manage_event_types_screen.dart \
  lib/features/profile/screens/profile_screen.dart \
  lib/features/settings/screens/privacy_settings_screen.dart \
  lib/shared/widgets/feedback_survey_sheet.dart \
  lib/shared/widgets/guest_guard.dart \
  lib/shared/widgets/minimal_event_card.dart
# → No issues found

Non toccato (deferito)

  • Shadow dark-mode (shadowSM/MD/LG usano Colors.black invisibile su inkBlack) — L4 nell’audit. Richiede shadow brightness-aware o switch a surfaceTint Material 3; non blocca.
  • Rename dei 147 call-site da alias legacy a nomi canonici — task meccanico, va in un commit a sé.
  • Shadow violet-tinted come alternativa brand — da valutare dopo che l’app è stilisticamente coerente.

Lessons

  • Un commento non risolve un bug di naming. Il tipo static const double radius32 = 24.0; // WAS 32 è un antipattern che preserva il valore sbagliato dietro un nome che pretende il giusto. O si rinomina o si cambia valore — mai si lascia il mismatch con giustificazione archeologica.
  • ColorScheme vuoto ⇒ Material 3 inferisce male. Non impostare tertiary/secondary fa sì che Flutter deriva da primary con color-mixing automatico; il risultato è sempre color-blind-friendly ma mai brand-accurate. Esporre esplicitamente tutti e tre i livelli è l’unica strada per controllare la palette.