De la 85 la 100 pe mobil: cum am găsit adevărata frână de viteză (și nu erau pozele)
Toată lumea repară un scor slab de viteză comprimând imagini. De cele mai multe ori e răspunsul greșit. Cum citești breakdown-ul din Lighthouse ca să găsești frâna reală — render delay-ul, nu byte-urii — pas cu pas, pe homepage-ul NovaFit.

Când un site pică la testul de viteză Google, primul reflex al oricui — inclusiv al multor developeri — e același: „comprimă pozele". E răspunsul evident. E și, de cele mai multe ori, răspunsul greșit.
Homepage-ul NovaFit — sala de fitness din Câmpina pentru care am reconstruit site-ul pe Next.js — avea un scor PageSpeed mobil de 85 și un LCP de ~3,9 secunde (galben, adică „ai o problemă"). Am făcut prima dată exact ce ai face și tu: am scăzut calitatea imaginii de fundal. Scorul nu s-a mișcat cu un punct. Ăsta e semnalul clasic că tratezi simptomul greșit — și momentul în care majoritatea se apucă să comprime și mai tare, obținând tot nimic.
O oră mai târziu, homepage-ul avea 100 la toate cele patru categorii — Performanță, Accesibilitate, Best Practices, SEO — cu LCP-ul coborât de la 3,9s la 1,4s. Articolul ăsta arată exact cum — și, mai important, de ce mutarea care a contat n-a fost cea la care te-ai fi gândit.
Simptomul care păcălește
Hero-ul homepage-ului NovaFit e „greu" din construcție: un fundal întunecat pe tot ecranul, peste el un inel roz mare, un decupaj cu un atlet și un titlu uriaș — patru straturi care se compun într-o singură imagine. Exact genul de secțiune la care instinctul spune „e prea multă imagine, de-aia e lent".
Doar că instinctul nu se măsoară. Iar când am tăiat din calitatea imaginii și scorul a rămas fix, a devenit clar: frâna e în altă parte, iar orice minut petrecut pe compresie e minut pierdut.
Regula care schimbă totul: măsori, nu ghicești
În loc să presupun ce anume întârzie, am scos din raportul Lighthouse exact ce element e „cel mai mare conținut vizibil" (LCP — Largest Contentful Paint, metrica pe care Google o pedepsește cel mai tare) și din ce se compune întârzierea lui. Două constatări au tranșat totul.
Întâi: elementul LCP era, într-adevăr, fundalul hero — imaginea care acoperă tot ecranul, deci cea mai mare suprafață pictată. Până aici, instinctul avea dreptate.
Al doilea lucru a dat totul peste cap. Defalcarea timpului de LCP arăta așa:
| Sub-parte a LCP-ului | Durată |
|---|---|
| Timp până la primul byte | 0 ms |
| Întârziere de pornire | 100 ms |
| Descărcarea imaginii | 270 ms |
| Întârziere de pictare | 900 ms |
Imaginea se descărca în 270 de milisecunde — rapid. Dar era desenată pe ecran abia 900 ms mai târziu. Tradus: browserul avea imaginea în mână și stătea aproape o secundă fără s-o poată picta. Compresia atacă acei 270 ms — care erau deja mici. Adevărata frână erau cei 900 ms de întârziere de pictare.
Iar raportul spunea și de ce: în capul listei de probleme, „resurse care blochează randarea" — fișierul CSS obliga browserul să aștepte ~700 ms înainte să deseneze orice.
Asta nu se vede cu ochiul liber și nu se ghicește. Se citește.
Ce am schimbat, mai exact
1. Am dimensionat imaginea LCP după cât costă decodarea, nu după kilobytes
Fundalul e o imagine de 1920×1080 pe orizontală, dar pe telefon e afișată „acoperind" un ecran vertical — deci deja mărită ca să umple înălțimea. Pe un telefon modern, browserul cerea un bitmap de peste 1200 px lățime pe care procesorul, în condițiile de test ale Google (simulare de telefon lent), îl decoda scump.
Fiind un fundal întunecat, la 60% opacitate, am putut cere o versiune mult mai mică și o calitate mai joasă fără nicio diferență vizibilă — dar cu decodare și transfer de câteva ori mai ușoare. Pe un fundal decorativ nimeni nu vede diferența; procesorul, da.
2. Am eliminat un strat grafic cât tot ecranul
Fundalul avea un efect subtil de parallax, care rula pe accelerare grafică (will-change). Problema: parallax-ul se mișcă doar la mouse — pe telefon e complet inutil — dar tot ocupa un strat grafic cât tot ecranul, pe care browserul trebuia să-l pregătească înainte de pictare. L-am scos de pe fundal (l-am păstrat pe elementele mici din față, unde chiar se vede). Direct în contul celor 900 ms.
3. Pârghia mare: CSS-ul livrat odată cu pagina
Cauza principală a întârzierii era CSS-ul care bloca randarea. Normal, browserul descarcă pagina, descoperă în ea o legătură către fișierul de stiluri, face un drum în plus ca să-l aducă, și abia apoi desenează. Am activat livrarea stilurilor direct în pagină, în loc de fișier separat: stilurile ajung odată cu conținutul, iar browserul poate picta imediat.
Am verificat pe build că funcționează cum trebuie: pagina iese cu stilurile incluse și zero fișiere de CSS care blochează randarea. Tehnica are un domeniu îngust în care e potrivită — stiluri mici (folosim un sistem „atomic", care generează doar clasele efectiv folosite) și majoritatea vizitatorilor la prima vizită. E fix cazul de aici; pe un site cu CSS mare și trafic care revine des, ar fi o alegere greșită.
Capcana care înghite optimizări în tăcere
Merită spusă separat, pentru că e genul de detaliu care te ține pe loc ore întregi. Framework-ul folosit (Next.js, versiunea nouă) are o particularitate: ignoră în tăcere orice calitate de imagine pe care n-ai declarat-o explicit într-o listă permisă. Pui „calitate 45", nu primești nicio eroare, și imaginea rămâne pe valoarea default. Adică jumătate din optimizări par că „nu fac nimic" — fix simptomul de la care pornise toată povestea.
Odată declarată lista corect, schimbările au început să conteze. Fără pasul ăsta, ai fi jurat că optimizarea imaginii nu ajută — și ai fi avut dreptate, din motivul greșit.
Bonus: și accesibilitatea, tot pe cifre
Aceeași disciplină a rezolvat și ultimul punct galben — accesibilitatea, blocată la 96. Lighthouse semnala un singur lucru: pe secțiunea de galerie, textul secundar gri pe un fundal gri deschis avea un raport de contrast de 4,13:1 — sub pragul minim de 4,5:1 cerut de standardul WCAG. Nu „textul e cam șters", ci o cifră exactă, sub un prag exact.
Fixul: o singură valoare de culoare, întunecată cu câteva procente, cât să urce la 4,74:1 — rămâne text secundar discret, dar devine lizibil pentru toată lumea. Accesibilitatea a trecut la 100. Aceeași abordare: nu „ajustăm ceva la ochi", ci citim exact ce raportează unealta și reparăm exact atât.
Ce confirmă cifrele, pas cu pas
Cel mai frumos lucru la povestea asta e că progresul, măsurat pas cu pas, confirmă exact ce spuneau datele. Fiecare intervenție a fost re-măsurată pe site-ul live:
| Moment | Scor mobil | LCP | Ce s-a livrat |
|---|---|---|---|
| Start | 85 | 3,9s | punct de plecare |
| Pasul 1 | 90 | 3,6s | imagine dimensionată la decode + stratul GPU inutil scos |
| Pasul 2 | 100 | 1,4s | CSS-ul livrat inline (scos din calea critică) |
Comparația dintre pași e partea utilă. Optimizările de imagine — exact lucrul la care ai reacționa instinctiv — au adus 5 puncte și au mișcat LCP-ul abia din 3,9 în 3,6s. Mutarea pe întârzierea de pictare (CSS-ul inline) a adus 10 puncte și a tăiat LCP-ul de la 3,6s la 1,4s.
Raportul de mărime dintre cele două salturi e chiar concluzia: problema nu erau byte-urii, era momentul pictării. Dacă intervenția s-ar fi oprit la „am comprimat pozele", scorul rămânea la 90, cu un LCP încă pe galben.
Scorul final — 100 pe toate categoriile, pe mobil și pe desktop:
- Mobil: 100 · 100 · 100 · 100 — Performanță · Accesibilitate · Best Practices · SEO FCP 1,0s · LCP ~1,4s · Speed Index 2,2s · TBT 70 ms · CLS 0 (telefon emulat Moto G, 4G lent)
- Desktop: 100 · 100 · 100 · 100 FCP 0,3s · LCP 0,5s · Speed Index 0,5s · TBT 10 ms · CLS 0
- Fiecare schimbare comisă atomic, cu build curat verificat înainte de a ajunge live
Cum s-a lucrat, tehnic (pentru curioși)
Partea care contează aici nu e câte linii de cod s-au schimbat — sunt puține — ci ordinea corectă a mișcărilor: întâi diagnostic din date reale, abia apoi intervenție. Concret:
- Nu s-a pornit nicio modificare până când raportul n-a arătat unde e frâna. Prima ipoteză („e imaginea") a fost testată și infirmată de date, nu de intuiție.
- Fiecare intervenție a fost verificată la sursă: că imaginea chiar se servește mai mică, că fișierul de CSS chiar dispare din calea critică — verificat pe build, nu presupus.
- Git disciplinat: commit-uri atomice, un fix pe commit, mesaje care explică de ce, nimic live fără build curat — ca fiecare pas să fie reversibil independent.
Ce a rămas uman: decizia „până unde comprimăm fără să se vadă", judecata că un fundal la 60% opacitate suportă o calitate joasă, și fiecare „dă drumul în producție".
Aceeași metodă, pas cu pas, am aplicat-o și pe alte proiecte: de la 74 la 97 pe mobil (unde frâna era analytics-ul, nu imaginile), un LCP de 56 de secunde readus sub 2 și un ghid de la 75 la 99 pe Lighthouse cu capcanele clasice de măsurare.
Întrebări frecvente
De ce nu a fost de-ajuns să comprim imaginile? Pentru că descărcarea imaginii dura deja puțin (270 ms). Frâna reală erau ~900 ms în care browserul avea imaginea, dar nu o putea picta — din cauza CSS-ului care bloca randarea. Compresia nu atinge partea aia. De-asta „mai comprimă o dată" nu mișca scorul.
De unde știți care e adevărata problemă și nu ghiciți? Din breakdown-ul de LCP al Lighthouse, care spune exact cât s-a pierdut la descărcare vs. la pictare, plus lista de resurse care blochează randarea. Măsurătoarea decide direcția; instinctul doar generează ipoteze de testat.
Se vede diferența la calitatea mai mică de imagine? Pe un fundal decorativ, întunecat, la 60% opacitate — nu. Tocmai de-aia am ales acolo compresia agresivă și am lăsat calitatea sus pe subiectele clare din prim-plan. Optimizarea de performanță bună e invizibilă pentru vizitator.
Ce faceți când un scor e blocat sub prag? Același proces din articol: diagnostic din date (breakdown-ul metricii, nu impresia), intervenție pe cauza reală, re-măsurare după fiecare pas. Nu „mai comprimăm ceva și sperăm". Direcția o dă raportul, nu intuiția — de-asta se poate repeta pe orice site, nu doar pe ăsta.
Ai un site care „merge", dar pică la testul de viteză Google? Scrie-ne — îți spunem din ce e frânat, cu raportul în față. Vezi și serviciul de audit SEO / AI-SEO + fix.