From 5c49294ad15c4473f065917f93e21252785ebcf0 Mon Sep 17 00:00:00 2001 From: Davide Grilli Date: Sat, 5 Sep 2026 12:42:24 +0200 Subject: [PATCH] =?UTF-8?q?Introduce=20la=20primitiva=20Card=20e=20sistema?= =?UTF-8?q?=20tocco=20e=20accessibilit=C3=A0=20dei=20componenti?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `rounded-3xl bg-card p-4 shadow-card` era ricopiato a mano 22 volte: cambiare raggio od ombra voleva dire toccare venti file. `Card` in `ui-bits.tsx` è ora l'unica definizione delle superfici, con la gerarchia dei raggi scritta invece che implicita (contenitore 3xl, elemento interno 2xl, controllo full). Tocco e tastiera: - i chip presenza di `EventoCard` erano alti ~28 px ed è il controllo più toccato dell'app: ora `min-h-11`, con 8 px di spazio fra l'uno e l'altro; - stessa cura per il link «Apri partita», la chiusura di `CelebrazioneBadge` e il titolo di `SezioneTendina`; - `aria-pressed` sui controlli a stato, `aria-controls` sulle tendine (che avevano `aria-expanded` senza il pannello a cui si riferisce), `aria-busy` sui caricamenti; - `BottomNav`: lo stato attivo era comunicato solo dal colore. Ora cambia anche il peso del testo, compare una barretta sopra l'icona e c'è `aria-current="page"`. La voce «Home» diventa «Oggi», che dice cosa contiene. La barra è un materiale traslucido con bordo sfumato al posto della riga netta. - `Barra` espone `role="progressbar"` con i valori. Il testo sotto i 12 px è sparito (108 occorrenze fra `text-[9px]`, `text-[10px]` e `text-[11px]`): sotto quella soglia la leggibilità cala e su iOS non scala con Dynamic Type. La gerarchia la fanno peso e maiuscolo. `Avatar` aveva `alt="Foto di 12"` quando il fallback è il numero di maglia — non descrive niente e il nome è già scritto accanto, quindi alt vuoto. Aggiunti `width`/`height` per non far saltare il layout al caricamento. Co-Authored-By: Claude Opus 5 --- src/components/crapp/Avatar.tsx | 13 +++++- src/components/crapp/BadgeDrawer.tsx | 14 +++---- src/components/crapp/BottomNav.tsx | 26 +++++++++--- src/components/crapp/CelebrazioneBadge.tsx | 4 +- src/components/crapp/CollezioneBadge.tsx | 32 +++++++-------- src/components/crapp/EventoCard.tsx | 24 ++++++----- src/components/crapp/Pagelle.tsx | 23 ++++++----- .../crapp/ProfiloAmministrativo.tsx | 2 +- src/components/crapp/RosaPresenze.tsx | 17 ++++---- src/components/crapp/ScoutEntry.tsx | 2 +- src/components/crapp/SerieCard.tsx | 13 +++--- src/components/crapp/SondaggioCacche.tsx | 13 +++--- src/components/crapp/TurnoPalloni.tsx | 4 +- src/components/crapp/VotazioneMvp.tsx | 8 ++-- src/components/crapp/VotoSocial.tsx | 10 ++--- src/components/crapp/ui-bits.tsx | 40 ++++++++++++++----- 16 files changed, 149 insertions(+), 96 deletions(-) diff --git a/src/components/crapp/Avatar.tsx b/src/components/crapp/Avatar.tsx index 6e9f81d..1ab7039 100644 --- a/src/components/crapp/Avatar.tsx +++ b/src/components/crapp/Avatar.tsx @@ -24,9 +24,18 @@ export function Avatar({ return ( {alt setErrore(true)} - className={cn("shrink-0 rounded-2xl object-cover", className)} + className={cn("aspect-square shrink-0 rounded-2xl object-cover", className)} /> ); } diff --git a/src/components/crapp/BadgeDrawer.tsx b/src/components/crapp/BadgeDrawer.tsx index a6417b1..f923156 100644 --- a/src/components/crapp/BadgeDrawer.tsx +++ b/src/components/crapp/BadgeDrawer.tsx @@ -42,15 +42,13 @@ function DettaglioBadge({ def, stato }: { def: BadgeDef; stato?: BadgeStato }) { {stato ? (
-

+

Stato attuale

{grado ? ( - - {meta?.label} - + {meta?.label} ) : ( - + In progresso )} @@ -73,7 +71,7 @@ function DettaglioBadge({ def, stato }: { def: BadgeDef; stato?: BadgeStato }) { ) : null}
-

+

Soglie

@@ -90,7 +88,7 @@ function DettaglioBadge({ def, stato }: { def: BadgeDef; stato?: BadgeStato }) { >

@@ -104,7 +102,7 @@ function DettaglioBadge({ def, stato }: { def: BadgeDef; stato?: BadgeStato }) { > {def.soglie[g]}

-

{def.unita}

+

{def.unita}

); })} diff --git a/src/components/crapp/BottomNav.tsx b/src/components/crapp/BottomNav.tsx index 9db2121..8e7913a 100644 --- a/src/components/crapp/BottomNav.tsx +++ b/src/components/crapp/BottomNav.tsx @@ -2,7 +2,7 @@ import { Link } from "@tanstack/react-router"; import { CalendarDays, Home, Trophy, User, Users } from "lucide-react"; const items = [ - { to: "/", label: "Home", icon: Home }, + { to: "/", label: "Oggi", icon: Home }, { to: "/calendario", label: "Calendario", icon: CalendarDays }, { to: "/squadra", label: "Squadra", icon: Users }, { to: "/classifica", label: "Classifica", icon: Trophy }, @@ -11,17 +11,33 @@ const items = [ export function BottomNav() { return ( -
-

- Vinto -

+

Vinto

{conteggio}{" "} @@ -141,7 +139,7 @@ export function CollezioneBadge({

-

+

Collezione badge

@@ -158,7 +156,7 @@ export function CollezioneBadge({ {vicino ? (

-

+

Prossimo traguardo

{vicino.def.nome}

@@ -171,7 +169,7 @@ export function CollezioneBadge({ {c.sbloccati.length > 0 ? (
-

+

Sbloccati ({c.sbloccati.length})

@@ -186,7 +184,7 @@ export function CollezioneBadge({ {c.inProgresso.length > 0 ? (
-

+

In progresso ({c.inProgresso.length})

@@ -201,7 +199,7 @@ export function CollezioneBadge({ {socialVinti.length > 0 ? (
-

+

Votati dai compagni

@@ -210,7 +208,7 @@ export function CollezioneBadge({

{cat.emoji}

{cat.nome}

-

+

Vinto {social[cat.id]} {social[cat.id] === 1 ? "volta" : "volte"}

@@ -221,7 +219,7 @@ export function CollezioneBadge({ ) : null}
-

+

Badge segreti

@@ -232,7 +230,7 @@ export function CollezioneBadge({

{b.def.nome}

-

+

{b.def.celebrazione ?? "Badge segreto sbloccato."}

@@ -246,7 +244,7 @@ export function CollezioneBadge({ >

???

-

Badge segreto da scoprire

+

Badge segreto da scoprire

))}
diff --git a/src/components/crapp/EventoCard.tsx b/src/components/crapp/EventoCard.tsx index 51fabc4..f485a57 100644 --- a/src/components/crapp/EventoCard.tsx +++ b/src/components/crapp/EventoCard.tsx @@ -1,6 +1,7 @@ import { MapPin, Clock, Users, Cake, ArrowRight } from "lucide-react"; import { Link } from "@tanstack/react-router"; import { cn } from "@/lib/utils"; +import { Card } from "@/components/crapp/ui-bits"; import { formatData, statoMeta, type Stato } from "@/lib/crapp-data"; import type { Evento } from "@/lib/eventi"; import { Barra } from "@/components/motion/Barra"; @@ -53,7 +54,7 @@ export function EventoCard({ if (isCompleanno) { return ( -
+
@@ -63,21 +64,21 @@ export function EventoCard({

{evento.data.slice(8, 10)}

-

+

{formatData(evento.data).split(" ")[2]?.slice(0, 3)}

- + ); } return ( -
+
@@ -87,7 +88,7 @@ export function EventoCard({

{evento.data.slice(8, 10)}

-

+

{formatData(evento.data).split(" ")[2]?.slice(0, 3)}

@@ -98,7 +99,7 @@ export function EventoCard({ {linkTo.label} @@ -121,7 +122,7 @@ export function EventoCard({ {io ? ( -
+
{stati.map((s) => { const meta = statoMeta[s]; const attivo = stato === s; @@ -137,8 +138,11 @@ export function EventoCard({ stato: attivo ? null : s, }) } + aria-pressed={attivo} className={cn( - "rounded-full border border-border px-2.5 py-1.5 text-[11px] font-semibold transition-all active:scale-95", + // min-h-11: sono i controlli più toccati dell'app, sotto i + // 44px si sbaglia bersaglio. + "min-h-11 rounded-full border border-border px-3 text-xs font-semibold transition-all active:scale-95", attivo ? cn(meta.className, "border-transparent shadow-card") : "bg-background text-muted-foreground", @@ -150,6 +154,6 @@ export function EventoCard({ })}
) : null} -
+ ); } diff --git a/src/components/crapp/Pagelle.tsx b/src/components/crapp/Pagelle.tsx index cb23062..f36c266 100644 --- a/src/components/crapp/Pagelle.tsx +++ b/src/components/crapp/Pagelle.tsx @@ -2,6 +2,7 @@ import { useState } from "react"; import { ClipboardCheck, Lock } from "lucide-react"; import { toast } from "sonner"; import { cn } from "@/lib/utils"; +import { Card } from "@/components/crapp/ui-bits"; import { Avatar } from "@/components/crapp/Avatar"; import type { Giocatore } from "@/lib/crapp-data"; import { useGiocatoreCorrente } from "@/lib/user-store"; @@ -41,21 +42,23 @@ export function Pagelle({ } return ( -
+
-

+

Pagelle anonime

- + {chiuse ? "Votazioni chiuse" : `${fatti}/${daVotare.length} votati`}
-

+

Dai un voto da 1 a 10 ai compagni: nessuno vedrà chi ha votato cosa, solo la media.

{isPending ? ( -

Carico le pagelle…

+

+ Carico le pagelle… +

) : (
{convocati.map((g) => { @@ -69,12 +72,12 @@ export function Pagelle({ {g.nome} - + {media ? `Media ${media.media} · ${media.voti} voti` : "Nessun voto"} {sonoIo ? ( - + Sei tu ) : chiuse ? ( @@ -85,7 +88,7 @@ export function Pagelle({ disabled={!io || vota.isPending} onClick={() => setApertoPer(aperto ? null : g.id)} className={cn( - "shrink-0 rounded-full px-3 py-1 text-[10px] font-bold uppercase transition-colors disabled:opacity-50", + "shrink-0 rounded-full px-3 py-1 text-xs font-bold uppercase transition-colors disabled:opacity-50", mio !== undefined ? "bg-accent text-accent-foreground" : "bg-card text-foreground", @@ -103,7 +106,7 @@ export function Pagelle({ type="button" onClick={() => invia(g.id, v)} className={cn( - "rounded-lg py-1.5 text-[11px] font-bold tabular-nums transition-transform active:scale-90", + "rounded-lg py-1.5 text-xs font-bold tabular-nums transition-transform active:scale-90", mio === v ? "bg-accent text-accent-foreground" : "bg-card text-foreground", @@ -119,6 +122,6 @@ export function Pagelle({ })}
)} -
+ ); } diff --git a/src/components/crapp/ProfiloAmministrativo.tsx b/src/components/crapp/ProfiloAmministrativo.tsx index f4f97c0..3ef62a7 100644 --- a/src/components/crapp/ProfiloAmministrativo.tsx +++ b/src/components/crapp/ProfiloAmministrativo.tsx @@ -27,7 +27,7 @@ const classiInput = "w-full rounded-xl border border-border bg-background px-3 p function Campo({ label, children }: { label: string; children: React.ReactNode }) { return (