Rubrică observabilă pe o singură captură (sau pe un set de capturi pe viewport)
pentru a judeca layout-urile generate de AI în Constructor și pentru a corecta agentul.
Nu e un BlockKind. Completează:
layout.md (grilă),
global-styles.md (accent / font),
pages.md (pagini),
AGENTS.md.
Surse de adevăr (calibrare vizuală):
design/GDS Portal.dc.html, design/src/GStack/*.dc.html, design/src/GPay/*.dc.html,
projects/gds-core/src/lib/layout-system.ts, tokens.ts, css.ts.
Machine-readable: design-quality.json — ids, severități, praguri
(pentru prompturi / review automat).
| Unitate | Valoare canonică | Observabil pe imagine |
|---|
| Celulă grilă | 8px (GDS_GRID_CELL) | Estimare din rail / gutters |
| Coloane conținut | 12 | Span-uri: 3 / 4 / 6 / 8 / 12 |
| Rail sidenav (desktop) | 30 celule ≈ 240px (ref. 236–250px) | Lățime bandă stângă |
| Padding conținut | 4 celule = 32px (ref. 24–34px) | Spațiu între rail și primul bloc; dreapta artboard |
| Gutter | 2 celule = 16px (ref. 14–16px) | Spațiu între carduri pe același rând |
| Gap vertical între blocuri | 2 celule = 16px (±8px) | Distanță între marginile exterioare ale blocurilor |
| Artboard desktop | 1920×1080 | Dacă e vizibil frame-ul Constructor |
Aria de conținut (cu rail): începe la ~col 34 (rail 30 + padding 4), se termină la
~col 236 (padding dreapta 4) → lățime ≈ 202 celule ≈ 1616px pe board 1920.
Toleranță screenshot: ±1 celulă (±8px) pe aliniere; ±2 celule pe ritm vertical.
Orice criteriu sub „Pass” = eșec la acel id.
Verdict pagină (desktop):
- FAIL dacă există ≥1 blocker, sau ≥2 major;
- WARN dacă 1 major și 0 blockers;
- PASS dacă doar minor / zero eșecuri.
- Checklist review — parcurge tabelul de mai jos pe o captură; notează
id + pass/fail.
- Corecție agent — pentru fiecare eșec, trimite fraza din coloana Corecție agent
(sau din
design-quality.json → agentFix).
| Id | Criteriu | Cum se măsoară | Pass | Severitate | Corecție agent |
|---|
ALIGN.LEFT | Margine stângă comună a conținutului | Trasează o linie verticală pe muchia stângă a page-header / KPI / chart / table. Distanța față de muchia dreaptă a rail-ului = padding. | Toate blocurile de conținut (non-floating) pornesc pe aceeași linie ±8px; padding față de rail ∈ 24–40px (ideal 32). | blocker | Aliniază toate blocurile de conținut la aceeași coloană stângă a content area (gdsContentArea); padding exterior 4 celule (32px) după rail. |
ALIGN.RIGHT | Margine dreaptă comună | Muchia dreaptă a rândurilor full / a perechilor care umplu 12 coloane. | Diff între muchiile drepte ale rândurilor „pline” ≤ 16px. Rândurile cu span < 12 trebuie să aibă sibling pe același rând (vezi DEAD.ROW). | blocker | Păstrează o muchie dreaptă comună: rândurile full (span 12) și perechile 8+4 / 6+6 / 4×3 trebuie să ajungă la același content.right. Nu lăsa panouri care „se strâng” spre stânga pe pagină. |
ALIGN.RAIL | Rail vs conținut — fără overlap | Intersecție vizuală sidenav ∩ conținut; text tăiat de rail. | Zero overlap; conținutul nu intră sub / peste sidenav. | blocker | Mută conținutul în dreapta rail-ului (30 celule). Nu plasa blocuri la col < railRight + padding. |
ALIGN.CHROME | Chrome pe lățimea disponibilă | Topbar / brand-bar / site-nav: stânga = după rail (sau 0 fără rail); dreapta = muchia artboard. | Chrome span pe toată lățimea lângă rail; fără „insulă” îngustă. | major | Chrome (topbar / brand-bar / site-nav) pe span 12 în aria chrome (fără padding-ul de conținut). |
| Id | Criteriu | Cum se măsoară | Pass | Severitate | Corecție agent |
|---|
GRID.SPAN | Span-uri din setul canonic | Estimare lățime bloc / lățime content ≈ k/12. | Span ∈ {3,4,6,8,12}. KPI = span 12 cu 3–4 carduri egale. | major | Folosește doar span-uri 3/4/6/8/12 via layout:{span}. KPI pe 12; chart principal 8 + panou 4; nu inventa lățimi arbitare. |
GRID.GUTTER | Guttere orizontale egale | Spațiu între carduri KPI / panouri pe același rând. | Gap ∈ 14–18px (ideal 16); variație între gap-uri pe același rând ≤ 4px. | major | Gutter 2 celule (16px) între toate panourile din același rând. |
GRID.RHYTHM8 | Snap pe ritmul de 8px | Poziții / dimensiuni estimate. | Margini și gap-uri multipli de ~8px (±4px). | minor | Snap pe grila de 8px; nu folosi spațieri „libere” (ex. 11px, 23px). |
GRID.PAIR | Pairing 8+4 / 6+6 | Rând cu ≥2 panouri combinabile. | Suma span-urilor pe rând = 12 (ex. 8+4, 6+6, 4+4+4, 3×4). Un singur panou combinabil (donut/activity) pe rând fără sibling = eșec (legate de DEAD.ROW). | blocker | Nu lăsa donut-chart / activity / panouri span-4 singure pe rând. Perechează: line-chart|bar-chart (8) + donut-chart|activity (4) pe același rând. |
| Id | Criteriu | Cum se măsoară | Pass | Severitate | Corecție agent |
|---|
DEAD.ROW | Gol dreapta pe un rând de conținut | unused% = (contentRight − rowContentRight) / contentWidth × 100. Exclude: constrained centrat (form/auth), floating (toast/chat/drawer). | unused% ≤ 8% (~≤1 coloană / ≤16px din validate). | blocker dacă unused% ≥ 25%; major dacă 8% < unused% < 25%. | Umple rândul până la marginea dreaptă: span 12 sau pereche a cărei sumă e 12. Gol ≥25% din lățimea de conținut pe dreapta e interzis. |
DEAD.NARROW | Blocuri care se îngustează pe verticală | Compară lățimea blocului N cu blocul full/pair de deasupra. | Un bloc de conținut mai îngust cu ≥25% față de rândul plin de deasupra, fără sibling care umple restul → fail. | blocker | Nu stivui un chart full-width apoi un donut îngust sub el. Fie ambele pe același rând (8+4), fie donut-ul e înlocuit/completat cu un panou care umple restul. |
DEAD.BOARD | Treime goală din board | Bandă verticală goală pe dreapta conținutului, pe ≥2 rânduri. | Nicio bandă goală ≥ 33% din lățimea de conținut pe ≥2 rânduri consecutive. | blocker | Redistribuie layout-ul: elimină coloana goală din dreapta; folosește toată content area pe fiecare rând de conținut. |
| Id | Criteriu | Cum se măsoară | Pass | Severitate | Corecție agent |
|---|
VERT.GAP | Gap constant între blocuri | Distanțe între page-header → KPI → primary → secondary. | Gap ∈ 12–24px (ideal 16); max−min pe pagină ≤ 12px. | major | Gap vertical 2 celule (16px) între toate blocurile de conținut consecutive. |
VERT.CRAMP | Zone înghesuite | Text lipit de bordură / carduri fără aer. | Padding interior card ≥ 12px; gap < 8px între blocuri = fail. | major | Crește spațierea: nu lipi blocurile; păstrează ≥16px între stack-uri. |
VERT.ORPHAN | Spațiu orfan mare | Gol vertical anormal între două secțiuni (fără footer sticky). | Gol vertical izolat ≤ 48px (6 celule) între secțiuni consecutive de conținut. | minor | Elimină spațiile goale mari între secțiuni; împachetează stack-ul cu gap 16px. |
| Id | Criteriu | Cum se măsoară | Pass | Severitate | Corecție agent |
|---|
HIER.HEADER | Prezență page-header | Titlu de pagină vizibil sub chrome. | Dashboard / listă / overview: există page-header (titlu ≥ ~22px, descriere opțională). | major | Adaugă page-header cu titlu + descriere scurtă imediat sub chrome, înainte de KPI. |
HIER.LANDING | Titlu landing sub navigare | Pe o pagină publică, compară muchia de jos a `site-nav | brand-barcu muchia de sus ahero`. | Există navigare de sus; hero-ul începe complet sub ea, cu titlul integral vizibil. hero poate înlocui page-header, nu și chrome-ul. | blocker |
HIER.SCALE | Scară tipografică | Titlu pagină vs titluri card vs body vs meta. | Titlu pagină > titlu card > body; fără două „H1” concurente pe același viewport. | minor | Un singur titlu de pagină dominant; titlurile de card rămân secundare (--gds-fs-*). |
HIER.KPI_FIRST | KPI înaintea detaliului | Ordine verticală. | Dacă există KPI și chart/table: KPI deasupra primary visual. | major | Ordine: page-header → kpi-row → chart/table principal → panouri secundare. |
HIER.PRIMARY | Un vizual principal | Aria ocupată / poziție. | Exact un bloc „primary” pe primul ecran util (chart sau table), nu 3 chart-uri egale fără ierarhie. | minor | Un vizual principal (span 8 sau 12); restul secundar (span 4) sau sub fold. |
| Id | Criteriu | Cum se măsoară | Pass | Severitate | Corecție agent |
|---|
DENS.KPI | Număr carduri KPI | Numără cardurile pe primul rând KPI. | Desktop: 3 sau 4 carduri egale pe un rând (2 pe tablet; 1–2 pe phone). 1 card pe tot rândul desktop = fail major. | major | KPI row cu 3–4 carduri egale pe desktop (span 12). |
DENS.ROW | Blocuri pe rând | Numără panourile pe un rând de conținut. | 1 (full), 2 (8+4 / 6+6) sau 3–4 (egale). ≥5 panouri pe un rând desktop = overcrowded. | major | Max 4 panouri pe rând; preferă 8+4 sau 4×3. |
DENS.SPARSE | Rând prea aerisit | Un singur card mic pe un rând lat. | Același prag ca DEAD.ROW / DEAD.NARROW. | blocker | Vezi DEAD.ROW: umple rândul sau mută panoul lângă un sibling. |
| Id | Criteriu | Cum se măsoară | Pass | Severitate | Corecție agent |
|---|
COMP.ORDER | Secvența canonică | Citește de sus în jos. | App: sidenav? → topbar → page-header → KPI → primary → secondary → footer?. Public: site-nav|brand-bar → hero|page-header → content → footer?. Inversiunile sau conținutul la row 0 înaintea chrome-ului = fail. | major | Respectă sloturile din layout.md: chrome de deschidere primul, titlul al doilea, footer ultimul. |
COMP.FOOTER | Footer dacă e prezent | Poziție. | Footer jos, span full chrome; nu intercalat între KPI și chart. | minor | site-footer doar la finalul stack-ului, pe lățimea chrome. |
| Id | Criteriu | Cum se măsoară | Pass | Severitate | Corecție agent |
|---|
XPAGE.RAIL | Același rail | Lățime / stil sidenav pe paginile aceluiași produs. | Diff lățime rail ≤ 16px; același tip de shell (light/dark). | major | Păstrează același sidenav width (~240px) și același pattern de shell pe toate paginile proiectului. |
XPAGE.CONTENT | Aceeași content area | Stânga/dreapta conținut. | Aliniere stângă/dreaptă identică ±8px între pagini. | major | Aceeași content area pe toate paginile; nu schimba padding-ul per pagină. |
XPAGE.HEADER | Același pattern de antet | Prezență topbar + page-header. | Același chrome + același tip de page-header pe paginile de app (nu marketing). | minor | Reutilizează același topbar + page-header pattern pe paginile de produs. |
| Id | Criteriu | Cum se măsoară | Pass | Severitate | Corecție agent |
|---|
RESP.COL | Stivuire pe viewport îngust | Pe phone: coloane. | Phone 430: o singură coloană de conținut. Tablet 820: max 2 coloane; KPI 2×2. | blocker | Pe layouts.phone: toate blocurile span full / o coloană. Pe tablet: densitate redusă, secundare stivuite. |
RESP.NAV | Navigare adaptată | Rail vizibil? | Tablet/foldable: icon-rail sau colaps. Phone: top app bar / bottom-menu — nu rail 240px permanent pe 430. | blocker | Pe phone colapsează sidenav-ul (top bar + sheet); pe tablet folosește icon-rail. |
RESP.OVERFLOW | Fără scroll orizontal | Conținut tăiat / scrollbar orizontal. | Niciun overflow orizontal; nimic tăiat de frame. | blocker | Elimină overflow-ul orizontal; reflow pe lățimea frame-ului. |
RESP.TOUCH | Ținte tactile | Butoane / itemi nav. | Înălțime tap ≥ 40px (ideal 44) pe phone. | major | Mărește țintele tactile la ≥40px pe phone. |
| Id | Criteriu | Cum se măsoară | Pass | Severitate | Corecție agent |
|---|
COMP.GLYPH | Iconițe = simboluri | Text vizibil în sloturile de icon. | Niciun literal de tip home, bell, settings, user, search, menu afișat ca text în loc de glyph/SVG. | blocker | Iconurile trebuie să fie glyph-uri monochrome / SVG (ex. ▣ ☰ ⚙), nu numele iconului ca string vizibil. |
COMP.OVERLAP | Text / blocuri care se suprapun | Intersecții vizibile. | Zero overlap text∩text sau text∩UI. | blocker | Repară overlap-ul: repoziționează blocurile (packGdsBlock / fără dreptunghiuri care se intersectează). |
COMP.CLIP | Conținut tăiat | Text/chart tăiat de bordură sau de frame. | Niciun clip pe label-uri, KPI values, legende. | major | Mărește rowSpan / lățimea până ce textul și legenda sunt complete. |
COMP.DATA | Date populate | Placeholder-uri goale. | KPI cu valori numerice; chart cu serie; table cu ≥3 rânduri; fără „— — —” / „Lorem” dominant. | major | Populează date reale (faker / dataset); nu lăsa carduri sau chart-uri goale. |
| Id | Criteriu | Cum se măsoară | Pass | Severitate | Corecție agent |
|---|
COLOR.ACCENT | Un accent de brand | Active nav, butoane primary, highlight. | O singură familie de accent (indigo/emerald/amber/rose/blue/teal); status colors doar pe delta/badge. | major | Accent doar prin meta.accent → --gds-accent / --gds-primary. Fără hex hardcodat pe blocuri. |
COLOR.CONTRAST | Contrast text | Text pe fundal. | Text principal lizibil pe surface/shell; fără gri deschis pe alb pentru body. | major | Folosește --gds-text-normal / ink tokens; asigură contrast adecvat. |
COLOR.CLASH | Culori discordante | Pete neon / gradient random pe carduri. | Fără fundaluri hardcodate în afara surface / accent-soft / ink / status-soft. | minor | Elimină culorile hardcodate; folosește tokens din css.ts / tokens.ts. |
TYPE.FONT | Familie coerentă | Uniformitate. | O familie din FontName pe tot ecranul (mono doar pe cod/ID). | minor | meta.font unic pe proiect; mono doar pentru ID-uri / log lines. |
Bifează pe screenshot. Oricare bifă = eșec la id-ul mapat (severitate din checklist).
| Anti-pattern | Semnal pe imagine | Id-uri |
|---|
| Coloană goală ~⅓ din board pe dreapta | Bandă albă verticală ≥33% content width | DEAD.BOARD, DEAD.ROW |
| Blocuri care se îngustează în jos | Full → mid → narrow, fără siblings | DEAD.NARROW, ALIGN.RIGHT |
| Chart full + donut singur sub el | Donut <50% din lățimea chart-ului de deasupra | GRID.PAIR, DEAD.NARROW, DENS.SPARSE |
| Lățimi inconsistente pe stack | Muchii drepte „în scară” | ALIGN.RIGHT, GRID.SPAN |
Icon name ca text (home, bell) | Cuvinte în loc de simboluri în nav/KPI | COMP.GLYPH |
| Conținut peste sidenav | Text/carduri sub sau prin rail | ALIGN.RAIL |
| Gap-uri haotice | 4px apoi 40px între aceleași tipuri de blocuri | VERT.GAP |
| Fără page-header | KPI / chart încep direct sub topbar | HIER.HEADER, COMP.ORDER |
| Rail 240px pe phone 430 | Conținut zdrobit; overflow | RESP.NAV, RESP.OVERFLOW |
Din layout.md / blockLayoutIntent — pe desktop, un rând trebuie să arate ca unul din:
| Preset | Span-uri | Folosit pentru |
|---|
| Full | 12 | page-header, kpi-row, table, alert, search, CTA |
| Primary + side | 8 + 4 | line/bar/hbar + donut/activity/deadline |
| Split egal | 6 + 6 | două panouri echilibrate |
| Triadă | 4 + 4 + 4 | trei carduri |
| Quad | 3 + 3 + 3 + 3 | patru carduri compacte |
| Constrained | 6 centrat | form, auth-gate, profile, receipt |
| Floating | 4 end | toast, chat, drawer — excluse din DEAD.* |
Referințele hand-made (GLog, GPay) folosesc KPI 4× egale + rând ~1.55fr 1fr (≈8+4), gutter 14–16px, padding 26–28px, rail 236px — mapate pe Constructor la 16 / 32 / 240.
| Probă | Așteptare |
|---|
| AI output cunoscut-defect (donut îngust, gol dreapta, stack îngustat) | FAIL pe DEAD.ROW, DEAD.NARROW, ALIGN.RIGHT, GRID.PAIR (blocker) |
| Referință hand-made GLog / GPay / GDS Portal | PASS (cel mult minor pe toleranță screenshot) |
Dacă un review trece AI-defectul sau pică referința, pragurile DEAD.* / ALIGN.RIGHT / GRID.PAIR trebuie strânse — nu relaxate.
Măsură pe imagine: KPI + line-chart pe lățime comună; donut pe rând separat, ~⅔ din
lățimea de deasupra → unused% ≈ 30–40% pe dreapta (≥25% → blocker).
| Id | Rezultat | Notă |
|---|
ALIGN.LEFT | pass | Stânga comună header/KPI/chart |
ALIGN.RIGHT | FAIL blocker | Muchia dreaptă a donut ≠ muchia KPI/line |
ALIGN.RAIL | pass | Fără overlap pe sidenav |
GRID.PAIR | FAIL blocker | Donut combinabil singur pe rând |
DEAD.ROW | FAIL blocker | unused% ≥ 25% |
DEAD.NARROW | FAIL blocker | Îngustare ≥25% față de rândul plin de deasupra |
DEAD.BOARD | FAIL sau borderline | Bandă goală pe ≥1 rând; dacă e pe un singur rând, DEAD.ROW e suficient |
VERT.* | pass | Gap-uri între secțiuni aparent regulate |
HIER.* / COMP.ORDER | pass | Header → KPI → chart → secondary |
DENS.KPI | pass | 4 carduri |
DENS.SPARSE | FAIL blocker | Rând donut aerisit |
COMP.GLYPH | pass | Glyph-uri, nu home/bell ca text |
COMP.DATA | pass | Valori + serii populate |
COLOR.* | pass | Accent blue / status pe delta |
Verdict: FAIL (≥4 blockers pe aliniere / spațiu mort / pairing).
Din markup: rail 236px, main padding 26×28, KPI repeat(4,1fr) gap 16,
rând principal 1.55fr 1fr gap 16 (umple lățimea), iconițe SVG, ordine canonică.
| Id | Rezultat |
|---|
ALIGN.LEFT / ALIGN.RIGHT / ALIGN.RAIL | pass |
GRID.SPAN / GRID.GUTTER / GRID.PAIR | pass (≈8+4) |
DEAD.ROW / DEAD.NARROW / DEAD.BOARD | pass (unused% ≈ 0) |
VERT.GAP / HIER.* / COMP.ORDER / DENS.KPI | pass |
COMP.GLYPH / COMP.DATA | pass |
RESP.* | pass în CSS (max-width:880 / 480: stack, drawer) — pe captură desktop N/A |
Verdict: PASS.