/* Container Query pentru transformare Tabel -> Card Stack */
.table-wrapper {
container-type: inline-size;
container-name: dashboard-table;
}
@container dashboard-table (max-width: 600px) {
.responsive-table thead {
display: none;
}
.responsive-table tr {
display: flex;
flex-direction: column;
margin-bottom: 1rem;
border: 1px solid #e2e8f0;
border-radius: 8px;
padding: 0.75rem;
}
.responsive-table td {
display: flex;
justify-content: space-between;
padding: 0.5rem 0;
border-bottom: 1px solid #f1f5f9;
}
.responsive-table td::before {
content: attr(data-label);
font-weight: 600;
color: #64748b;
}
}Am refăcut recent UI-ul la un SaaS de gestiune unde peste 30% din utilizatori intrau de pe tablete sau telefoane să verifice rapid comenzi. Bordul arăta groaznic pe ecran mic: tabele tăiate violent, scroll orizontal infinit pe tot body-ul și un sidebar care acoperea 80% din ecran. Am reușit să scădem tichetele de suport pe zona de UI cu 40% în prima lună doar prin refactorizarea structurii responsive.
Sidebar-ul: Renunță la JS inutil
Multe dashboard-uri moderne au problema asta: pun 100 de linii de JS și state-uri de React doar ca să ascundă un meniu. Am făcut și eu greșeala asta la un proiect cu 12k utilizatori activi și aveam un lag deranjant pe telefoane ieftine când deschideai meniul.
Regula mea actuală e simplă: pe desktop, sidebar-ul e position: sticky și reduce lățimea de la 260px la 64px (rămân doar iconițele vizibile) prin adăugarea unei clase .collapsed. Pe mobil, sub 768px, sidebar-ul e position: fixed, complet scos din flux și mutat în afara ecranului cu transform: translateX(-100%).
Când utilizatorul apasă pe hamburger, adaugi o clasă .is-open care aplică transform: translateX(0). De ce transform și nu left sau width? Pentru că transform folosește GPU-ul și rulează la 60fps pe orice telefon ieftin, fără să declanșeze reflow în browser.
Dilema tabelului: Scroll orizontal vs. Card Stack
Aici se dau cele mai mari bătălii în echipa de produs. Product Manager-ul vrea 12 coloane de date vizibile pe ecranul unui iPhone SE, iar tu știi că fizic nu ai cum.
Am testat două abordări pe utilizatori reali și trade-off-urile sunt clare:
- Scroll orizontal cu container dedicat (
overflow-x: auto): E cel mai ușor de implementat. Păstrezi structura HTML de<table>. Funcționează bine dacă fixezi primele 1-2 coloane (ex: numele clientului sau ID-ul) cuposition: sticky; left: 0. Altfel, utilizatorul face scroll în dreapta și uită despre ce rând era vorba. - Card Stack (rândurile devin carduri): Ascunzi
<thead>-ul pe mobil, iar pe<tbody>schimbitr-ul în flex sau grid. Fiecare celulă primește un atributdata-labelafișat cu pseudoelementul::before. Arată excelent pe mobil și e super citibil.
Sincer, card-urile câștigă detașat dacă ai sub 6-7 câmpuri per rând. Dacă ai un tabel financiar greu, cu selecție multiplă și acțiuni în masă, card stack-ul devine prea lung și greu de urmărit. Acolo păstrezi tabelul clasic, dar pui scroll orizontal DOAR pe wrapper-ul tabelului, niciodată pe tot ecranul.
Container Queries schimbă jocul
Până acum ceva timp foloseam @media (max-width: 768px) pentru toate regulile de responsive. Problema e că aceeași componentă de tabel arăta diferit dacă era pusă pe ecran complet sau într-un widget pe jumătate de ecran.
Cu CSS Container Queries, layout-ul depinde de lățimea părintelui, nu a ferestrei browser-ului. Setezi container-type: inline-size pe wrapper-ul tabelului și poți comuta de la tabel la card stack fix când spațiul din widget devine prea strâmt.
Tu ce strategie folosești la tabelele mari pe mobil? Mergi pe scroll orizontal cu coloană fixă sau le transformi în carduri?