Il mio primo frontend era brutto e va bene così
Perché ho spedito prima il funzionante
Ho consegnato un MVP funzionante anche se il frontend non seguiva tutte le best practice. Il motivo è semplice: dovevo validare il flusso — corsi, lezioni, esercizi, wallet — prima di rifinire la pelle dell’interfaccia. Un’interfaccia bella su un prodotto che non gira è un bel mockup; un’interfaccia grezza su un prodotto che gira è apprendimento reale.
Spedire presto mi ha dato segnali utili: cosa cercano studenti e docenti, dove inciampano, quali azioni sono davvero frequenti. Da lì ho iniziato a disegnare il piano di rientro su UX/UI, senza buttare via il lavoro.
Approfondimento tecnico: raccontato passo-passo in “Da Figma a React + Vite + TypeScript: il frontend dietro SchoolPlatform”.
Indice dei contenuti
Refactor o “purga”? Il mio criterio
Mi sono trovato davanti al bivio classico:
- Refactor graduale: tenere l’esistente e migliorare per passi.
- Purga (riscrittura mirata): tagliare e rifare dove il debito tecnico blocca.
Ho scelto un approccio ibrido:
- Purga chirurgica su moduli con accoppiamento forte e stili hardcoded.
- Refactor incrementale su parti già modulari, iniziando da routing, layout e tipografia.
Il criterio pratico:
- Impatto utente: tocco prima ciò che sblocca la frizione maggiore (chiarezza dei percorsi, leggibilità, stati dei bottoni).
- Rischio regressioni: evito riscritture ampie nelle settimane con rilasci funzionali.
- Riutilizzo futuro: investo dove posso creare primitives riusabili.
Piano UX/UI post-MVP
Design tokens → primitives
Traduco colori, spaziature e tipografia in design tokens (CSS/TS) e li promuovo a primitives (Button, Input, Card, Badge). In questo modo ogni pagina eredita coerenza automaticamente.
Esempio (semplificato):
// src/design/tokens.ts
export const colors = {
primary: "#2563eb",
surface: "#0b1220",
text: "#e6e6e6",
success: "#16a34a",
warning: "#f59e0b"
};
export const space = { xs: 4, sm: 8, md: 12, lg: 16, xl: 24 };
export const font = {
family: "'Inter', system-ui, -apple-system, Segoe UI, Roboto, sans-serif",
size: { sm: "0.875rem", base: "1rem", lg: "1.125rem" },
weight: { regular: 400, medium: 500, bold: 700 },
lineHeight: 1.5
};
src/primitives/Button.tsx
import { colors, space, font } from "@/design/tokens";
type Variant = "primary" | "ghost" | "danger" | "success";
export function Button({
variant = "primary",
disabled,
children,
...props
}: React.ButtonHTMLAttributes<HTMLButtonElement> & { variant?: Variant }) {
const styles: Record<Variant, React.CSSProperties> = {
primary: { background: colors.primary, color: colors.text },
ghost: { background: "transparent", border: `1px solid ${colors.text}`, color: colors.text },
danger: { background: "#dc2626", color: colors.text },
success: { background: colors.success, color: colors.text }
};
return (
<button
style={Object.assign({}, styles[variant], {
padding: `${space.sm}px ${space.lg}px`,
fontFamily: font.family,
fontSize: font.size.base,
borderRadius: 12,
opacity: disabled ? 0.6 : 1,
cursor: disabled ? "not-allowed" : "pointer"
})}
{...props}
>
{children}
</button>
);
}
Accessibilità e naming
- Focus state visibile di default sulle primitives.
- Contrasto testo/sfondo ≥ 4.5:1 dove possibile.
- ARIA per componenti interattivi.
- Naming consistente:
Page
,Section
,Panel
,Card
,Badge
.
Component library e regole di stile
- Storybook per documentare le primitives.
- ESLint + Prettier per standardizzare.
- Regole di commit (Conventional Commits) per tracciare refactor vs feat.
Micro-performance e DX: piccoli interventi ad alto impatto
- Lazy routes e code-split: carico le pagine solo quando servono.
- Bundle diet: rimuovo dipendenze duplicate, import nominativi.
- Cruciale per UX: ridurre il Time to Interactive nelle pagine corso/esercizi.
Esempio sintetico:
src/router.tsx
import { createBrowserRouter } from "react-router-dom";
import React from "react";
const Course = React.lazy(() => import("./pages/Course"));
const Lesson = React.lazy(() => import("./pages/Lesson"));
const Dashboard = React.lazy(() => import("./pages/Dashboard"));
export const router = createBrowserRouter([
{ path: "/", element: <React.Suspense fallback={null}><Dashboard/></React.Suspense> },
{ path: "/course/:id", element: <React.Suspense fallback={null}><Course/></React.Suspense> },
{ path: "/lesson/:id", element: <React.Suspense fallback={null}><Lesson/></React.Suspense> }
]);
import { createBrowserRouter } from "react-router-dom";
import React from "react";
const Course = React.lazy(() => import("./pages/Course"));
const Lesson = React.lazy(() => import("./pages/Lesson"));
const Dashboard = React.lazy(() => import("./pages/Dashboard"));
export const router = createBrowserRouter([
{ path: "/", element: <React.Suspense fallback={null}><Dashboard/></React.Suspense> },
{ path: "/course/:id", element: <React.Suspense fallback={null}><Course/></React.Suspense> },
{ path: "/lesson/:id", element: <React.Suspense fallback={null}><Lesson/></React.Suspense> }
]);
Cosa mi porto a casa
- Done is better than perfect, ma con una rotta chiara di rientro su UX/UI.
- I design tokens sono il ponte tra bozza e prodotto.
- La coppia primitives + regole moltiplica la velocità senza perdere coerenza.
- Refactor e “purga” non sono religioni: sono strumenti con criteri.
FAQ
Perché non hai usato subito un design system completo? Perché dovevo validare flussi critici in tempi rapidi. Ora che li ho, introdurre un system è più mirato e meno teorico.
Quando scegliere la “purga” rispetto al refactor? Quando l’accoppiamento è alto, lo stile è hardcoded ovunque e i test non coprono i casi base. In quel caso, rifare un modulo è più economico che limarne i bordi.
Come garantisci che il refactor non rallenti lo sviluppo? Lavoro per feature slice e UI slice, introduco primitives backward-compatible e migro le pagine una alla volta.
Quanto incide davvero il code-split sulla UX? Nelle pagine con librerie pesanti (editor, grafici, Web3) l’effetto è tangibile: caricamento percepito più rapido e meno freeze iniziali.
- Vuoi vedere come trasformo token in primitives su un caso reale? Leggi “Da Figma a React + Vite + TypeScript: il frontend dietro SchoolPlatform”.
- Curioso del codice? Dai un’occhiata al mio portfolio e ai repo collegati su GitHub dalla pagina.
- Se ti interessa questo percorso di redesign, scrivimi dalla pagina contact “newsletter” e la tua email.
Come sempre, Grazie per essere arrivato fin qui!
Matteo Ricci | Full Stack Developer |