Shell & layout

Un singur breakpoint în tot DS-ul: 1024px. Sub el — layout mobil: o coloană, sidebar absent, două shell-uri — .app-mobile (produs: header voalat + .tabbar) și .flow-chrome (fluxuri). De la el în sus — layout desktop: .app-shell cu sidebar opțional (rail fix, în afara grilei) și .layout-12, grila de 12 coloane capată la 1440px — lățimea canvasului pe care se desenează, deci layout-ul nu se întinde peste ce a fost văzut la design.

Markup canonic · components/foundation-layout.json

sursa = JSON · generat de scripts/build-catalog.py

Conținut principal

Breakpoint unic — 1024px

Nu există prag intermediar. Tabletele intră în banda mobile: același shell, aceleași reguli, doar coloana se centrează. Valoarea trăiește în tokenul --bp-desktop; în @media se scrie literal (custom properties nu funcționează în media queries).

… se citește lățimea ferestrei…

Redimensionează fereastra peste / sub 1024px — indicatorul de mai sus reflectă banda reală, nu o simulare.

< 1024px · mobile

O coloană, sidebar ascuns. Două shell-uri: ecranele de produs stau în .app-mobile — .app-mobile-top sus, .tabbar jos (App navigation, Header); fluxurile în .flow-chrome — bară sus (back · titlu · meniu), CTA jos. Coloana e fluidă până la --flow-max (560px), apoi centrată.

≥ 1024px · desktop

Sidebar opțional (rail 240px / 64px icon-only) + .layout-12. Cele 12 coloane trăiesc în zona de conținut, capate la --layout-max și centrate. Fluxurile folosesc .app-footer.

Tokens

--bp-desktop     1024px   /* SINGURUL breakpoint din DS */
--layout-max     1440px   /* lățimea canvasului de design · peste atât grila se lipește de rail (centrată doar fără sidebar) */
--layout-pad     32px     /* gutter exterior desktop */
--layout-pad-mob 20px     /* gutter exterior mobile */
--grid-cols      12       /* coloane pe desktop — cu sau fără sidebar */
--grid-gutter    24px     /* gutter între coloane */
--sidebar-w      240px    /* sidebar full — rail fix, în afara grilei */
--sidebar-w-icon 64px     /* sidebar icon-only */
--flow-max       560px    /* lățimea coloanei .flow-chrome în banda mobile */

--layout-max e lățimea canvasului de design: 1440px. Pe full HD zona de conținut de lângă rail e mai lată (1920 − 240 = 1680), deci grila se oprește la 1440, lipită de rail — cei 240px rămași cad în dreapta și devin aer, nu coloane. Cu railul icon-only: 1920 − 64 = 1856, 416px aer în dreapta.

1920  viewport
 240  rail sidebar
1680  zona de conținut          (mai lată decât capul)
   0  aer stânga (grila e lipită de rail)
1440  grila = --layout-max
  32  --layout-pad stânga
1376  cele 12 coloane + 11 gutters
 264  --grid-gutter × 11
  92.67  o coloană = (1376 − 264) / 12
  32  --layout-pad dreapta
 240  aer dreapta = 1680 − 1440

Grila de 12 coloane · .layout-12 + .col-*

Spanurile .col-1….col-12 se aplică doar peste breakpoint. Sub 1024px grila are o singură coloană și fiecare copil ocupă toată lățimea — fără clase suplimentare, fără variante mobile de span.

12 coloane · gutter 24px
.col-12
col-12
.col-6 + .col-6
col-6
col-6
.col-8 + .col-4 — conținut principal + coloană secundară
col-8
col-4
.col-4 × 3 — grid de carduri / KPI
col-4
col-4
col-4
.col-3 × 4 — densitate maximă utilă
col-3
col-3
col-3
col-3
cele 12 coloane, ca riglă
1
2
3
4
5
6
7
8
9
10
11
12

Shell cu sidebar — .app-shell

Sidebar-ul e un rail fix în afara grilei — nu consumă coloane. Grid-ul părinte îi măsoară lățimea cu :has(): 240px default, 64px pentru .icon-only. Sub breakpoint dispare (display:none) și shell-ul rămâne o coloană. Conținutul are padding egal stânga/dreapta (--layout-pad) — aceeași respirație spre rail și spre marginea opusă. Peste cap-ul de 1440px grila rămâne lipită de rail și aerul cade în dreapta; se centrează doar în shell-ul fără sidebar.

.app-shell > .sidebar + .layout-12
col-8 · conținut
col-4 · lateral
col-4
col-4
col-4

Shell cu rail icon-only — .sidebar.icon-only

Același shell, alt rail: 64px, măsurat tot cu :has(). Grila rămâne lipită de rail, iar etichetele meniului (.sidebar-label) apar ca tooltip la dreapta railului, la hover și la focus — treci cu mouse-ul peste iconuri. Railul icon-only nu scrollează. Markup-ul complet și regulile railului → templates/sidebar.html.

.app-shell > .sidebar.icon-only + .layout-12
col-8 · conținut
col-4 · lateral
col-4
col-4
col-4

Shell fără sidebar — aceleași 12 coloane

Sidebar-ul e opțional. Fără el nu se schimbă nici numărul de coloane, nici spanurile — doar lățimea disponibilă. O pagină nu are nevoie de un al doilea set de reguli ca să meargă în ambele ipostaze.

.app-shell fără sidebar
col-8 · conținut
col-4 · lateral

Lățimi

CeValoareNote
Layout funcțional desktopmax 1440pxlățimea canvasului de design — grila se oprește acolo, lipită de rail (centrată doar fără sidebar); fundalul curge la edge
Coloană de citit / documentmax 800pxlizibilitate — vezi docs/layout.md
Coloană mobile (flow chrome)max 560px--flow-max; fluidă sub, centrată peste
Sidebar240px / 64pxrail fix, în afara grilei

Reguli ferme

  • Un singur breakpoint: 1024px. Nu se adaugă praguri intermediare („tablet" la 768px). Dacă un layout pare să aibă nevoie de unul, problema e layout-ul.
  • Sub 1024px = mobile complet — o coloană, sidebar absent. Nu un desktop strâns.
  • Două shell-uri pe mobil, după ce e ecranul — produs (acasă, liste, setări, detalii) în .app-mobile: .app-mobile-scroll + .app-mobile-top + .tabbar (+ .mobile-menu); flux (wizard, semnare, onboarding) în .flow-chrome. Un ecran de detaliu e .app-mobile.no-bar cu header-ul .detail. Nu le combina.
  • Insetele de sistem stau pe shell — --safe-top / --safe-bottom sunt declarate pe .app-mobile și .flow-chrome (nu în :root); paginile de referință le simulează cu valori fixe pe ramă.
  • Sidebar-ul nu consumă coloane — rail fix în afara grilei. Cele 12 coloane sunt aceleași cu și fără el.
  • Respirație egală, grilă lipită de rail — padding --layout-pad identic spre sidebar și spre marginea opusă; peste 1440px grila stă lipită de rail și aerul cade în dreapta (centrare doar fără sidebar). Cu panou lateral dreapta (ex: ticker-ul Dublin), panoul nu are padding stânga propriu — golul vine din padding-ul coloanei, deci rail → conținut = conținut → panou.
  • Spanurile există doar peste breakpoint — sub el, fiecare copil e full width automat.
  • Nicio grilă ad-hoc — grid-template-columns: repeat(3, 1fr) pe o secțiune e drift; compune cu .col-*.
  • Conținutul funcțional nu trece de 1440px (lățimea canvasului de design) — peste atât grila se oprește, lipită de rail sau centrată fără sidebar; fundalul face bleed la edge.

DON'T

  • @media (min-width: 768px) — breakpoint inexistent în DS. Singurele forme corecte: min-width: 1024px și max-width: 1023.98px.
  • .flow-chrome / .app-mobile peste 1024px sau .app-footer / .sidebar sub — regula e bidirecțională, vezi components/flow-chrome.html și components/app-nav.html.
  • .tabbar într-un .flow-chrome, sau .flow-bar pe un ecran de produs — fiecare shell își are chrome-ul.
  • Sidebar pus în grilă (ex. .col-3 pe <nav class="sidebar">) — railul stă în afara celor 12 coloane.
  • Panou lateral dreapta cu padding stânga propriu — dublează golul (32 + n) și strică respirația egală; golul vine exclusiv din padding-ul coloanei de conținut.
  • Grilă centrată cu margin: auto propriu lângă un rail — la 1920px, lângă railul icon-only, asta lasă 208px de gol între rail și conținut; centrarea e a shell-ului fără sidebar.
  • Un al doilea set de spanuri „pentru mobile" — sub breakpoint nu există coloane de împărțit.