modul 00 / start: ce este css
css & cele trei moduri de a-l aplica
CSS (Cascading Style Sheets) descrie cum arată documentul: culori, tipografie, spațiere, layout, animații. HTML-ul spune CE este conținutul; CSS-ul — CUM se prezintă. „Cascading” din nume e chiar mecanismul central: mai multe surse de stiluri se combină după reguli precise (modulul 1).
trei moduri de a aplica css
| Mod | Cum | Verdict |
| Extern | <link rel="stylesheet" href="style.css"> în head | Standardul. Un fișier, cache-uit, refolosit de toate paginile |
| Intern | <style>…</style> în head | Pagini single-file, emailuri, critical CSS — cazuri speciale |
| Inline | <p style="color:red"> | De evitat: nespecific, nerefolosibil, greu de suprascris. Legitim doar pentru valori dinamice din JS |
anatomia unei reguli
selector { proprietate: valoare; proprietate: valoare; }
h1 {
color: #DCFF2F;
font-size: 2rem; /* comentariu */
}
- Selectorul — pe cine se aplică; declarațiile — perechi proprietate:valoare, separate cu ;.
- CSS-ul, ca HTML-ul, iartă în tăcere: o proprietate scrisă greșit e pur și simplu ignorată — fără eroare. Simptomul: „stilul nu se aplică și nu-mi spune de ce”.
devtools: laboratorul
- Inspect pe orice element → panoul Styles: toate regulile care îl ating, în ordinea câștigului; cele învinse apar tăiate.
- Editezi valorile LIVE — bifezi/debifezi proprietăți, încerci culori și spații direct în pagină, apoi copiezi în fișier. Așa se lucrează CSS în practică: mai mult în DevTools decât în editor.
- Panoul Computed — valorile finale calculate + box model-ul vizual al elementului.
- Proprietățile invalide au un ⚠ — primul loc unde te uiți când „nu merge”.
mindset
CSS-ul nu se „memorează” — se înțeleg sistemele lui: cascada, box model-ul, contextele de formatare (flow, flex, grid). Cine le stăpânește pe acestea deduce restul; cine le sare copiază snippete la nimereală și se luptă cu !important. Cursul e construit pe sisteme.
modul 00 / start: ce este css
valori, unități & culori
unitățile care contează
| Unitate | Relativă la | Folosește pentru |
| px | — (absolută pe ecran) | Borduri, umbre, detalii fine |
| rem | font-size pe <html> (implicit 16px) | Fonturi, spațieri, dimensiuni — respectă setarea de mărime a utilizatorului |
| em | font-size al elementului curent | Spațieri care scalează cu textul local (padding la butoane) |
| % | dimensiunea părintelui | Lățimi fluide, imagini |
| vw / vh | viewport (1vw = 1% din lățime) | Secțiuni full-screen, tipografie fluidă |
- Regula practică modernă: rem pentru aproape tot, px pentru detalii de 1–3px, % și vw/vh pentru fluid. Fonturile fixate în px ignoră preferința de accesibilitate a utilizatorului — rem o respectă.
- calc() amestecă unități: width: calc(100% - 2rem). min(), max(), clamp() — le vezi la responsive (modulul 7).
culorile
color: #DCFF2F; /* hex — cel mai răspândit */
color: rgb(220 255 47 / 80%); /* cu transparență */
color: hsl(74 100% 59%); /* nuanță, saturație, luminozitate */
color: oklch(93% 0.24 118); /* modern: percepție uniformă */
color: transparent · currentColor
- HSL e cel mai intuitiv pentru palete: aceeași nuanță (H), variezi luminozitatea → tonuri consistente.
- currentColor = culoarea de text curentă — bordurile și iconițele SVG o moștenesc elegant.
- Contrast text/fundal: minim 4,5:1 (WCAG AA) — DevTools îl afișează la color picker.
proprietăți scurte (shorthand)
margin: 10px 20px; /* sus-jos 10, stânga-dreapta 20 */
margin: 10px 20px 30px 40px; /* sus dreapta jos stânga — sensul acelor de ceas */
border: 1px solid #333; · font: italic 700 1rem/1.5 Poppins, sans-serif;
background: url(bg.jpg) center / cover no-repeat;
capcană
Shorthand-urile resetează ce nu specifici: background: red șterge și background-image setat anterior. Când suprascrii o singură componentă, folosește proprietatea lungă (background-color: red).
modul 01 / selectori, cascadă & specificitate
selectorii de bază & combinatorii
selectorii fundamentali
p { } /* tip: toate paragrafele */
.card { } /* clasă: class="card" — calul de bătaie */
#logo { } /* id: unic — evită-l pentru stil */
[type="email"] { } /* atribut */
* { } /* universal — resets */
h1, h2, .titlu { } /* grupare: aceeași regulă pentru toți */
Practica modernă: stilizezi cu clase. Tipurile — pentru baze globale (body, a, img); id-urile — pentru ancore și JS, nu pentru stil (specificitate prea mare, nereciclabile).
combinatorii — relațiile dintre elemente
article p { } /* descendent: p oriunde în article */
article > p { } /* copil direct */
h2 + p { } /* fratele imediat următor */
h2 ~ p { } /* toți frații următori */
- h2 + p e pattern-ul clasic pentru „primul paragraf după titlu” (lead mai mare).
- Selectoarele adânci (.nav ul li a span) sunt fragile — se rup la orice schimbare de structură. Țintește direct cu o clasă.
selectorii de atribut, complet
[href^="https"] /* începe cu */ · [href$=".pdf"] /* se termină cu */
[href*="romeo"] /* conține */ · [aria-current="page"] /* stare accesibilă */
Ultimul exemplu e un pattern de aur: stilizezi pe semantica de accesibilitate ([aria-current], [aria-expanded="true"], :disabled) — starea și stilul rămân sincronizate prin definiție.
modul 01 / selectori, cascadă & specificitate
pseudo-clase & pseudo-elemente
pseudo-clase: stări și poziții
a:hover · a:focus-visible · button:active · input:disabled · input:checked
li:first-child · li:last-child · li:nth-child(2n) /* pare */ · li:nth-child(3n+1)
p:first-of-type · :not(.exceptie) · :is(h1, h2, h3) · :has(img) /* părinte cu imagine! */
- :hover nu există pe touch — nu ascunde funcționalitate doar în hover.
- :focus-visible — inelul de focus doar la navigarea cu tastatura (nu la click) — soluția modernă a conflictului „designerul vrea fără outline”.
- :nth-child(2n) — zebră pe tabele; atenție: numără TOȚI copiii, nu doar cei de tipul selectat (pentru asta e :nth-of-type).
- :is() scurtează grupările (:is(h1,h2,h3) a); :not() exclude; :has() — mult-așteptatul „selector de părinte”: .card:has(img) = cardurile care conțin imagine.
pseudo-elemente: părți generate sau interne
p::first-line · p::first-letter /* letrină */
.btn::before { content: "→ "; } · .new::after { content: "nou"; }
::selection { background: #DCFF2F; } · input::placeholder { color: #888; }
- ::before / ::after creează un copil decorativ la începutul/finalul conținutului — obligatoriu cu proprietatea content (măcar goală). Uz: iconițe, ornamente, overlays, clearfix istoric.
- Conținutul din ::before/::after nu e selectabil și nu e conținut real — decorativ da, text esențial nu (accesibilitate).
- Convenție: pseudo-clasele cu :, pseudo-elementele cu ::.
pattern-uri de reținut
Zebra: tr:nth-child(2n){background:#f6f6f6} · separatoare între itemi fără primul: li + li{border-top:…} · link extern marcat: a[target="_blank"]::after{content:" ↗"}.
modul 01 / selectori, cascadă & specificitate
cascada, specificitatea & moștenirea
Trei mecanisme decid valoarea finală a oricărei proprietăți. Cine le știe nu mai are „CSS care nu se aplică misterios”.
1. cascada — ordinea deciziei
- Origine & importanță: stilurile browserului < stilurile tale < ale tale cu !important (inversează ordinea).
- Specificitate — vezi mai jos.
- Ordinea în cod — la egalitate, câștigă ultima regulă declarată.
2. specificitatea — scorul selectorului
(id-uri, clase/atribute/pseudo-clase, tipuri/pseudo-elemente)
p → (0,0,1) · .card p → (0,1,1) · #hero .card p → (1,1,1)
style="…" inline → bate orice selector · !important → bate tot (inclusiv inline)
- Comparația e pe niveluri: un id bate oricâte clase; o clasă bate oricâte tipuri.
- De aici regula: stil pe clase (nivel jos, uniform) → cascadă previzibilă. Id-urile și inline-ul creează „turnuri” greu de suprascris.
- !important = ultima soluție, nu unealtă de zi cu zi: fiecare !important cere următorul !important. Legitim doar pentru utilitare finale sau suprascrierea stilurilor unor librării terțe.
3. moștenirea
- Proprietățile de text se moștenesc (color, font-*, line-height, text-align) — de aceea setezi fontul pe body o singură dată.
- Cele de cutie NU se moștenesc (margin, padding, border, width, background).
- Valori de control: inherit (forțează moștenirea), initial (default-ul specificației), unset, revert (înapoi la stilul browserului).
debugging-ul „nu se aplică”
DevTools → Styles pe element: regulile sunt listate în ordinea câștigului, proprietățile învinse — tăiate, cu regula câștigătoare deasupra. Nu ghici specificitatea din cap: citește-o din panou, apoi decide dacă întărești selectorul sau (mai bine) cobori turnul care câștigă nemeritat.
modul 02 / box model
cutia: content, padding, border, margin
În CSS, totul e o cutie. Fiecare element generează o cutie cu patru straturi, dinspre interior spre exterior:
┌ margin (spațiul FAȚĂ de vecini — transparent) ┐
│ ┌ border (conturul) ┐ │
│ │ ┌ padding (spațiul INTERIOR) ┐ │ │
│ │ │ content (textul, imaginea) │ │ │
└─┴─┴──────────────────────┴───────┴─┘
- Padding — între conținut și bordură; primește culoarea de fundal a elementului. „Aerul dinăuntru”.
- Border — conturul: border: 1px solid #333; colțuri cu border-radius.
- Margin — distanța față de alte cutii; mereu transparent. „Aerul dinafară”. margin-inline: auto (sau margin: 0 auto) — centrarea orizontală clasică a blocurilor cu lățime fixă.
box-sizing: reparația obligatorie
/* implicit (content-box): width = DOAR conținutul */
width: 300px; padding: 20px; border: 5px → lățime reală 350px 😖
/* standardul practic: */
*, *::before, *::after { box-sizing: border-box; }
→ width 300px INCLUDE padding și border. Ce declari e ce măsoară.
Regula border-box pe tot e prima linie din orice proiect modern — elimină aritmetica mentală și „de ce nu încap două coloane de 50%?”.
display: tipul cutiei
| Valoare | Comportament |
| block | Rând propriu, umple lățimea; acceptă width/height (div, p, h1) |
| inline | Curge în text; IGNORĂ width/height și margin vertical (a, span, em) |
| inline-block | Curge în text, DAR acceptă dimensiuni — hibridul util |
| none | Scos complet din pagină (și din accessibility tree) — vs visibility:hidden care păstrează spațiul |
| flex / grid | Contexte de layout pentru copii — modulele 5 și 6 |
modul 02 / box model
margin collapse, overflow & dimensiuni
colapsarea marginilor — surpriza clasică
Marginile verticale ale blocurilor adiacente nu se adună — fuzionează: câștigă cea mai mare.
p1 { margin-bottom: 30px; } + p2 { margin-top: 20px; }
→ distanța dintre ele = 30px (nu 50px)
- Colapsează și părinte cu primul/ultimul copil: marginea copilului „iese” prin părinte — faimosul „de ce h1-ul îmi împinge tot containerul în jos?”. Remedii: padding pe părinte, border, sau orice context de formatare nou (overflow, flex/grid — în flex și grid colapsarea NU există).
- Marginile orizontale nu colapsează niciodată.
- Convenție sănătoasă de spațiere: mergi într-o singură direcție (doar margin-bottom, sau modern: gap în flex/grid) — fără dueluri de margini.
overflow: când conținutul nu încape
overflow: visible /* implicit — dă pe dinafară */
overflow: hidden /* taie */ · overflow: auto /* scroll doar la nevoie */
overflow-x: auto /* scroll orizontal — tabelele pe mobil! */
- Textul nu se taie niciodată „de la sine” — dacă dispare, cineva a pus hidden. Pentru trunchiere elegantă: text-overflow: ellipsis (cu white-space: nowrap + overflow: hidden) sau -webkit-line-clamp pe mai multe rânduri.
min/max — dimensiuni cu limite
.container { width: 90%; max-width: 1200px; margin-inline: auto; }
img { max-width: 100%; height: auto; } /* imaginile nu sparg layoutul */
textarea { min-height: 8rem; }
- Primul pattern = containerul universal: fluid pe mic, plafonat pe mare, centrat.
- Al doilea = regula de aur a imaginilor responsive: niciodată mai late decât părintele.
- Preferă min-height lui height: conținutul poate crește fără să dea pe dinafară. height fix pe cutii cu text = overflow garantat la primul text mai lung.
reflex de depanare
Element „prea mare/prea mic misterios”? DevTools → Computed → diagrama box model: vezi exact content/padding/border/margin în pixeli reali. Jumătate din bug-urile de spațiere se văd instant acolo.
modul 03 / tipografie & fonturi web
proprietățile tipografice
setul de bază
body {
font-family: 'Poppins', system-ui, sans-serif; /* stivă cu fallback! */
font-size: 1rem; /* 16px implicit */
line-height: 1.6; /* FĂRĂ unitate — se scalează cu fontul */
color: #1a1a1a;
}
- Stiva de fonturi: primul disponibil câștigă; termină mereu cu o familie generică (sans-serif, serif, monospace). system-ui = fontul sistemului — gratis, instant, excelent fallback.
- line-height fără unitate (1.5–1.7 pentru body) — valoarea cu unitate (px/em) se moștenește calculată și strică titlurile.
- Lizibilitate: body minim 16px; lungimea rândului 45–75 caractere — max-width: 65ch pe blocurile de text e trucul de aur (ch = lățimea cifrei 0).
restul arsenalului
font-weight: 400 | 700 | 100–900 · font-style: italic
text-align: left | center | justify (justify pe web: mai degrabă nu)
text-decoration: underline | none · text-transform: uppercase | capitalize
letter-spacing: .02em · word-spacing · text-indent
white-space: nowrap | pre-wrap · hyphens: auto (cu lang="ro" setat!)
- UPPERCASE cere letter-spacing pozitiv (.05–.1em) — majusculele lipite se citesc greu.
- Linkurile din text păstrează un semnal vizual dincolo de culoare (underline) — accesibilitate pentru daltoniști.
modul 03 / tipografie & fonturi web
fonturi web, font-display & diacriticele românești
de unde vin fonturile
/* 1. serviciu (Google Fonts): */
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;700&display=swap" rel="stylesheet">
/* 2. self-hosted: */
@font-face {
font-family: 'Poppins';
src: url('/fonts/poppins.woff2') format('woff2');
font-weight: 400; font-display: swap;
}
- Formatul modern unic: WOFF2 — comprimat, suportat peste tot.
- Cere DOAR greutățile folosite (400+700 de regulă) — fiecare greutate = un fișier descărcat.
- Self-hosting = control + fără request extern (viteză, GDPR); Google Fonts = comoditate.
font-display: ce vede utilizatorul cât se încarcă fontul
| Valoare | Comportament |
| swap | Text imediat cu fallback, se schimbă la sosirea fontului — alegerea standard |
| block | Text invizibil până la ~3s (FOIT) — de evitat |
| optional | Fontul se folosește doar dacă e deja în cache — performanță maximă |
diacriticele: lecția plătită scump
Ș, ț, ă, î, â trăiesc în subsetul latin-extended. Multe fonturi de display (inclusiv unele populare pe Google Fonts) au DOAR latin — rezultatul: diacriticele se randează din fontul de fallback, iar textul arată „peticit”: învață cu ță-ul vizibil dintr-alt font.
- Pe Google Fonts, verifică tab-ul Glyphs/Language support: fontul are latin-extended? Cere subsetul: &subset=latin-ext.
- Testează cu propoziția-tortură: „Șarpele țâșnește prin înghețată” — orice caracter peticit sare în ochi.
- Capcana s-comma vs s-cedilla: româna corectă folosește ș/ț cu virgulă (U+0219/U+021B), nu ş/ţ cu sedilă (turcești). Fonturile vechi le desenează doar pe cele cu sedilă.
- Dacă fontul perfect n-are diacritice: alegi altul, sau (avansat) patch cu fontTools — dar pentru 99% din proiecte, alegerea unui font cu latin-ext e răspunsul corect.
exact problema platformei
Chiar acest site a trecut prin lecția asta: fontul inițial de display nu avea latin-extended și „învață” apărea cârpit. Soluția aplicată: font de display cu suport latin-ext complet (Space Grotesk). Când alegi fonturi pentru proiecte românești, subsetul se verifică înaintea esteticii.
modul 04 / flow & poziționare
fluxul normal & position
normal flow — baza pe care stă tot
Fără nicio intervenție, browserul așază cutiile în flux normal: blocurile unul sub altul, inline-urile curgând în rânduri. Layout-ul moderne = fluxul normal + flex/grid pe containere; position e pentru excepții: elemente scoase din flux sau lipite de ceva.
valorile position
| Valoare | Comportament | Uz tipic |
| static | Implicit — în flux, top/left ignorate | — |
| relative | Rămâne în flux (spațiul se păstrează), se poate deplasa vizual; devine ANCORĂ pentru copiii absoluți | Micro-ajustări + rolul de ancoră |
| absolute | SCOS din flux; poziționat față de cel mai apropiat strămoș poziționat (altfel — pagina) | Badge pe card, iconițe în input, overlays |
| fixed | Scos din flux; lipit de viewport — nu se mișcă la scroll | Bare de navigare, butoane flotante |
| sticky | Hibrid: în flux până atinge pragul (top: 0), apoi se lipește în containerul părinte | Headere de secțiuni, sidebar-uri |
pattern-ul fundamental: ancora relativă
.card { position: relative; }
.card .badge {
position: absolute;
top: 12px; right: 12px;
}
„Absolute față de părinte, nu față de pagină” = părintele primește position: relative. Cel mai folosit duo din CSS. inset: 0 = top/right/bottom/left 0 — overlay-ul care umple complet părintele.
capcanele fiecăruia
absolute — elementul nu mai ocupă loc: părintele se poate „goli” (înălțime 0). fixed — acoperă conținut: compensezi cu padding pe body/scroll-margin. sticky — „nu se lipește” de obicei din 2 motive: lipsește top, sau un părinte are overflow: hidden care îi ucide efectul.
modul 04 / flow & poziționare
z-index & contextele de stivuire
a treia dimensiune
- z-index decide cine stă deasupra la suprapuneri — dar funcționează doar pe elemente poziționate (sau itemi flex/grid).
- Numerele nu sunt globale: fiecare stacking context e o „lume” proprie. Un copil cu z-index 9999 dintr-un context inferior nu poate ieși deasupra unui element din contextul vecin cu z-index 10.
ce creează un stacking context
position + z-index diferit de auto · opacity < 1 · transform · filter
position: fixed / sticky · isolation: isolate
De aici clasicul „modalul meu cu z-index 99999 stă SUB header” — headerul sau un strămoș al modalului a deschis propriul context (adesea printr-un transform sau opacity uitat).
igiena z-index-ului
- Scară de valori mici și documentate: 1 (ridicat local), 10 (dropdown), 100 (header), 1000 (modal), nu licitații de 999999.
- Ideal, ca variabile: --z-header: 100; --z-modal: 1000; — un singur loc de adevăr.
- isolation: isolate pe componente — le închizi lumea interioară deliberat, ca z-indexurile lor să nu scape în pagină.
- Pentru modale, browserul modern oferă scurtătura supremă: elementul <dialog> cu showModal() se randează în top layer — deasupra a tot, fără niciun z-index.
debugging
Suprapunere greșită? Caută în DevTools primul strămoș cu transform / opacity / filter / z-index — acolo e granița contextului. Repari mutând elementul afară din context sau ridicând contextul întreg, nu umflând numărul.
modul 05 / flexbox
containerul flex: axe & aliniere
Flexbox = layout pe O dimensiune (un rând SAU o coloană): navbar-uri, grupuri de butoane, carduri în linie, centrări. Activezi cu display: flex pe părinte — copiii devin flex items automat.
cele două axe
flex-direction: row (implicit) → axa principală = orizontală
flex-direction: column → axa principală = verticală
justify-content → aliniere pe axa PRINCIPALĂ
align-items → aliniere pe axa TRANSVERSALĂ
- justify-content: flex-start · center · flex-end · space-between (primul și ultimul la margini — pattern-ul de navbar) · space-around · space-evenly.
- align-items: stretch (implicit — itemii se întind pe înălțime!) · center · flex-start · flex-end · baseline (aliniere pe linia textului — butoane lângă text).
- Când schimbi direction pe column, axele se rotesc: justify devine vertical. Ține minte: justify = axa principală, oricare ar fi ea.
gap — spațierea civilizată
.nav { display: flex; align-items: center; gap: 1rem; }
gap pune spațiu DOAR între itemi — fără margini care se scurg la capete, fără :last-child { margin: 0 }. Standardul modern de spațiere în flex și grid.
centrarea perfectă — în sfârșit banală
.parinte { display: flex; justify-content: center; align-items: center; }
/* orice copil: centrat orizontal și vertical. atât. */
flex-wrap
- Implicit itemii se înghesuie pe un rând (nowrap) — se strivesc oricât. Cu flex-wrap: wrap, trec pe rândul următor când nu mai încap — grile fluide simple.
modul 05 / flexbox
itemii flex: grow, shrink, basis
cum se împarte spațiul
flex: [grow] [shrink] [basis]
flex-basis → mărimea de pornire a itemului (înlocuiește width pe axa principală)
flex-grow → cât din spațiul LIBER primește (proporție; 0 = nu crește)
flex-shrink → cât cedează când NU e loc (1 implicit; 0 = refuză să se strângă)
rețetele care acoperă 95% din cazuri
| Rețetă | Efect |
| flex: 1 (= 1 1 0) | Itemii împart spațiul EGAL, ignorând conținutul — coloane egale |
| flex: auto (= 1 1 auto) | Cresc din mărimea conținutului — proporțional cu ce conțin |
| flex: none (= 0 0 auto) | Rigid: nici nu crește, nici nu se strânge (logo, iconițe) |
| Un item cu margin-left: auto | Împinge itemul (și ce urmează) la capătul opus — butonul din dreapta navbar-ului |
| flex: 2 pe unul, flex: 1 pe restul | Primul primește dublu din spațiul liber |
pattern-ul sfânt: media object
.media { display: flex; gap: 1rem; align-items: flex-start; }
.media img { flex: none; width: 64px; }
.media .text { flex: 1; min-width: 0; } /* textul ia restul */
min-width: 0 pe itemul de text e vaccinul contra celui mai enervant bug flex: implicit itemii refuză să se strângă sub conținutul minim (un cuvânt lung, un URL) și sparg layoutul. min-width: 0 le permite; textul lung îl îmblânzești apoi cu overflow-wrap.
order & align-self
- order rearanjează vizual fără a atinge HTML-ul — cu grijă: ordinea de Tab și citirea AT rămân pe DOM (accesibilitate).
- align-self — alinierea individuală a unui item, peste align-items.
regula de decizie flex vs grid
Conținutul își dictează mărimile de-a lungul UNEI direcții → flex. Ai o STRUCTURĂ pe două dimensiuni (rânduri ȘI coloane care trebuie să se alinieze) → grid (modulul următor). În practică: componente = des flex; pagini și grile = grid.
modul 06 / css grid
grid: definirea grilei
Grid = layout pe DOUĂ dimensiuni: definești rânduri și coloane pe container, itemii se așază în celule. Pagini întregi, galerii, dashboard-uri, formulare complexe.
definirea coloanelor & rândurilor
.grila {
display: grid;
grid-template-columns: 240px 1fr 1fr;
grid-template-rows: auto 1fr auto;
gap: 1.5rem;
}
- fr — fracțiune din spațiul liber: 1fr 2fr = o treime / două treimi. Moneda grid-ului.
- auto — cât cere conținutul; valorile fixe (px/rem) — rigide.
- repeat(): repeat(4, 1fr) = patru coloane egale; repeat(12, 1fr) = clasicul sistem de 12.
minmax & grila responsivă fără media queries
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
Formula-vedetă a CSS-ului modern: „câte coloane încap, minim 300px fiecare, întinse egal”. Grila de carduri care se re-aranjează singură la orice lățime — zero media queries. (auto-fill păstrează coloane goale; auto-fit le colapsează și întinde itemii existenți.)
alinierea în grid
justify-items / align-items → conținutul ÎN celule (stretch implicit)
justify-content / align-content → grila întreagă în container
place-items: center → centrare totală în celule (shorthand)
grile implicite
- Itemii peste rândurile definite cad în rânduri implicite; le controlezi mărimea cu grid-auto-rows: minmax(120px, auto).
modul 06 / css grid
plasarea itemilor & grid-area
plasarea pe linii
.hero {
grid-column: 1 / -1; /* de la prima la ultima linie = tot rândul */
grid-row: 1 / 3; /* pe două rânduri */
}
.featured { grid-column: span 2; } /* ocupă 2 coloane de unde se află */
- Numeri liniile, nu celulele: 3 coloane = 4 linii (1…4). -1 = ultima linie — de aceea 1 / -1 = „pe toată lățimea”, indiferent câte coloane are grila.
- span n = întinde-te pe n piste. Restul itemilor curg automat în celulele libere.
grid-template-areas — layoutul care se citește
.pagina {
display: grid;
grid-template-columns: 260px 1fr;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
}
header { grid-area: header; } aside { grid-area: sidebar; }
main { grid-area: main; } footer { grid-area: footer; }
Layoutul e desenat ASCII în cod — auto-documentat. La breakpoint, redesenezi doar template-areas („header” / „main” / „sidebar” / „footer” pe o coloană) și totul se re-aranjează, fără să atingi itemii.
subgrid — alinierea în profunzime
grid-template-rows: subgrid pe un item-container îi lasă copiii să se alinieze la grila BUNICULUI — cardurile dintr-o grilă își aliniază între ele titlurile/butoanele indiferent de lungimea textului. Suportat în toate browserele moderne.
rețeta de pagină standard
Body pe grid cu grid-template-rows: auto 1fr auto (header / main / footer) + min-height: 100dvh = footerul stă jos și pe paginile scurte — rezolvarea modernă a eternului „sticky footer”.
modul 07 / responsive design
mobile-first & media queries
filozofia mobile-first
Scrii stilurile de bază pentru ecranul mic (layout simplu, o coloană), apoi ADAUGI complexitate pe ecrane mari cu min-width. De ce în ordinea asta:
- Majoritatea traficului e mobil; varianta mobilă e cea implicită, nu adaptarea.
- E mai ușor să ADAUGI coloane decât să demontezi un layout complex — CSS-ul desktop-first se umple de „anulări”.
- Telefoanele (dispozitivele mai slabe) procesează mai puțin CSS condițional.
/* bază = mobil */
.grila { display: grid; gap: 1rem; }
@media (min-width: 640px) { .grila { grid-template-columns: 1fr 1fr; } }
@media (min-width: 1024px) { .grila { grid-template-columns: repeat(3, 1fr); } }
breakpoints — pe conținut, nu pe dispozitive
- Nu există „breakpointul iPhone”: pui pragul unde design-ul se strică — lărgești fereastra și te uiți unde devine incomod.
- Repere uzuale de pornire: ~640px, ~960–1024px, ~1280px — dar conținutul decide.
- Sintaxa modernă de interval: @media (width >= 640px) și @media (640px <= width < 1024px) — suportată în toate browserele actuale.
alte interogări care contează
@media (prefers-color-scheme: dark) { … } /* dark mode al sistemului */
@media (prefers-reduced-motion: reduce) { … } /* oprești animațiile */
@media (hover: none) { … } /* touch — fără interacțiuni doar-hover */
@media print { … } /* stiluri de tipărire */
fundația din html
Nimic din toate acestea nu funcționează fără <meta name="viewport" content="width=device-width, initial-scale=1"> — fără el, mobilul randează pagina la ~980px și o micșorează. Prima verificare când „media queries nu merg pe telefon”.
modul 07 / responsive design
fluid: clamp() & container queries
tipografie & spațieri fluide cu clamp()
clamp(minim, valoare-fluidă, maxim)
h1 { font-size: clamp(2rem, 5vw + 1rem, 4rem); }
.section { padding-block: clamp(3rem, 8vh, 7rem); }
- Titlul crește CONTINUU cu fereastra, între plafoane — fără trepte de media queries. Trei valori înlocuiesc cinci breakpoints.
- Amestecul vw + rem în termenul fluid păstrează scalarea la zoom (accesibilitate) — vw pur o rupe.
- Rude: min() — width: min(90%, 1200px) = containerul universal într-o singură declarație; max() — praguri de siguranță.
container queries: componenta responsivă la SPAȚIUL EI
Media queries văd fereastra; dar un card poate sta într-un sidebar îngust sau într-o zonă lată — pe aceeași fereastră. Container queries interoghează lățimea containerului:
.zona-carduri { container-type: inline-size; }
@container (min-width: 400px) {
.card { display: flex; } /* orizontal doar când ARE loc */
}
- Componenta devine cu adevărat portabilă: se adaptează la orice context, fără să știe de pagina din jur.
- Suport complet în browserele moderne; pattern-ul standard pentru design systems.
unitățile de viewport moderne
100vh — problema clasică pe mobil: bara browserului „taie” sau „sare”
100dvh — dynamic viewport height: urmărește bara → înălțimea REALĂ vizibilă
svh / lvh — varianta minimă / maximă stabilă
Pentru secțiuni full-screen pe mobil: dvh înlocuiește vh — dispare celebrul „butonul de sub fold tăiat de bara Safari”.
rețeta responsive completă
Viewport meta + mobile-first + grile auto-fill/minmax + clamp() pe tipografie/spațieri + container queries pe componente + dvh la full-screen. Cu acest set, media queries rămân pentru schimbările MARI de layout — restul curge singur.
modul 08 / efecte, tranziții & animații
fundaluri, gradiente & umbre
background — straturile
background-image: url(hero.jpg);
background-size: cover; /* umple, taie ce nu încape */ | contain
background-position: center; · background-repeat: no-repeat;
/* straturi multiple — primul e deasupra: */
background: linear-gradient(rgb(0 0 0 / .55), rgb(0 0 0 / .55)), url(hero.jpg) center/cover;
Ultimul pattern = overlay-ul de lizibilitate: gradientul întunecat peste fotografie ca textul alb să treacă de contrast. Imaginile de CONȚINUT rămân <img> (alt, indexare); background = doar decor.
gradiente
linear-gradient(135deg, #35E0E0, #5D8DFF)
radial-gradient(circle at top right, #333, #111)
conic-gradient(from 0deg, …) /* pie charts pure CSS */
repeating-linear-gradient(45deg, #222 0 10px, #333 10px 20px) /* hașuri */
umbre & rotunjiri
border-radius: 12px; · border-radius: 50% /* cerc din pătrat */
box-shadow: 0 4px 24px rgb(0 0 0 / .12); /* x y blur culoare */
box-shadow: inset 0 1px 0 rgb(255 255 255 / .1); /* interior */
text-shadow: 0 1px 2px rgb(0 0 0 / .4);
filter: blur(4px) | brightness(1.1) | grayscale(1) · backdrop-filter: blur(12px) /* sticla mată */
- Umbrele credibile: difuze și subtile (blur mare, opacitate mică), eventual stratificate — nu „chenarul gri dur” din 2010.
- backdrop-filter: blur() + fundal semitransparent = efectul glassmorphism al barelor de navigare moderne (exact ca bara acestui site).
modul 08 / efecte, tranziții & animații
transform, tranziții & @keyframes
transform — mișcare fără layout
transform: translateY(-4px) · translate(-50%, -50%)
transform: scale(1.05) · rotate(3deg) · skewX(-6deg)
transform-origin: center | top left /* pivotul */
Transformările nu declanșează re-layout — vecinii nu se mișcă, iar browserul le compune pe GPU. De aceea regula de performanță: animezi transform și opacity; animarea lui width/height/top/margin recalculează layoutul la fiecare cadru = sacadat.
tranziții — stările îmblânzite
.card {
transition: transform .2s ease, box-shadow .2s ease;
}
.card:hover { transform: translateY(-4px); box-shadow: 0 12px 32px rgb(0 0 0 / .16); }
- Tranziția se declară pe starea de bază (ca să anime și „întoarcerea”), nu pe :hover.
- Durate de interfață: 150–300ms; easing: ease / ease-out pentru intrări; cubic-bezier() pentru personalitate.
- Enumeră proprietățile explicit — transition: all e leneș și animă accidental orice.
@keyframes — animații autonome
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: .4; }
}
.badge { animation: pulse 2s ease-in-out infinite; }
/* intrări la încărcare: */
@keyframes fade-up { from { opacity: 0; transform: translateY(16px); } }
.hero > * { animation: fade-up .6s ease-out backwards; }
- animation-fill-mode: backwards/forwards — starea dinainte/de după rulare; animation-delay în trepte = efectul de „cascadă” pe copii.
- Scroll-driven: pattern-ul IntersectionObserver + clasă (.in) — exact reveal-ul folosit pe landing-ul acestei platforme; CSS-ul modern aduce și animation-timeline: view() nativ.
reduced motion — obligatoriu, nu opțional
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: .01ms !important;
transition-duration: .01ms !important;
}
}
Utilizatorii cu tulburări vestibulare setează preferința în sistem; animațiile ample le provoacă amețeli reale. Blocul de mai sus e în boilerplate-ul oricărui proiect serios.
modul 09 / arhitectură, variabile & proiect
variabile css & organizarea codului
custom properties — sistemul de design în cod
:root {
--bg: #0B0E11; --text: #E8ECEF; --accent: #5D8DFF;
--space: 1rem; --radius: 12px;
--font-b: 'Poppins', system-ui, sans-serif;
}
.btn { background: var(--accent); border-radius: var(--radius); }
.btn-mare { padding: calc(var(--space) * 1.5); }
- Declarate pe :root = globale; se moștenesc și se pot suprascrie local — aici bat orice preprocesor: .card-promo { --accent: #DCFF2F; } re-tematizează tot ce e înăuntru.
- Dark mode în trei rânduri: suprascrii variabilele în @media (prefers-color-scheme: dark) sau pe [data-theme="dark"] — componentele nu se ating.
- Fallback: var(--poate-lipsi, valoare-implicită). JS le citește/scrie cu setProperty — puntea spre interactivitate.
- Exact sistemul pe care rulează acest site: paletele fiecărui curs sunt un set de variabile suprascrise.
organizare: convenții care scalează
/* BEM — bloc__element--modificator: */
.card { } .card__title { } .card--featured { }
→ specificitate plată (o clasă), fără coliziuni, citești apartenența din nume
- Ordinea fișierului: variabile → reset → elemente de bază (body, headings, links) → layout → componente → utilitare. Un singur fișier e ok până la ~1000 de linii; apoi spargi pe componente.
- Alternativa utility-first (Tailwind) o întâlnești în industrie — principiile de aici (cascadă, box model, flex/grid) rămân identice sub orice sintaxă.
- Numește semantic (--accent, .btn-primary), nu vizual (--albastru, .text-mare) — la rebranding schimbi valoarea, nu clasele din tot HTML-ul.
reset-ul modern minimal
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
body { min-height: 100dvh; line-height: 1.6; -webkit-font-smoothing: antialiased; }
img, picture, video, svg { display: block; max-width: 100%; }
input, button, textarea, select { font: inherit; }
h1, h2, h3, h4 { text-wrap: balance; }
Șase reguli care elimină 90% din surprizele cross-browser: border-box peste tot, marginile implicite la zero (spațierea o decizi TU), imaginile cuminți, formularele cu fontul paginii.
modul 09 / arhitectură, variabile & proiect
debugging css & proiectul final
protocolul de debugging
- Stilul nu se aplică? DevTools → Styles: regula apare? (Nu → selectorul nu prinde elementul.) Apare tăiată? (→ pierde la specificitate/ordine — vezi cine câștigă deasupra.) Are ⚠? (→ valoare invalidă.)
- Layout ciudat? Computed → box model-ul vizual; apoi întreabă: ce context de formatare e părintele (flow? flex? grid?) — jumătate din confuzii sunt reguli de flow aplicate în flex.
- Element misterios de lat / scroll orizontal? Vinovații clasici: lățimi fixe, imagini fără max-width, min-width:auto în flex, 100vw (include scrollbar-ul). Trucul depistării: * { outline: 1px solid red } — vezi toate cutiile.
- Diferă între browsere? Validează CSS-ul (jigsaw.w3.org), verifică suportul pe caniuse.com, caută reset-ul lipsă.
- Izolare: reproduce bugul minimal în CodePen — de cele mai multe ori îl înțelegi în timp ce-l izolezi.
proiectul final: îmbraci pagina din cursul html
Iei „Atelier Foto Chișinău” — pagina construită la finalul cursului de HTML — și o stilizezi cap-coadă. Fără să atingi HTML-ul (testul structurii corecte!):
- Fundația: reset-ul modern + :root cu paleta (fundal închis, un accent, 2 fonturi Google cu latin-ext — verifică „Șarpele țâșnește…”).
- Tipografia: scară cu clamp() pe h1–h3; body 1rem/1.6; max-width: 65ch pe paragrafe.
- Layoutul: header sticky cu backdrop-filter; nav pe flex cu gap; serviciile pe grid auto-fill/minmax; portofoliul pe grid cu un element span 2; footer pe flex space-between.
- Componentele: carduri cu border-radius, umbră difuză și hover translateY + tranziție; tabelul de prețuri cu zebră (:nth-child) și wrapper overflow-x; formularul cu :focus-visible clar și :user-invalid roșu.
- Responsive: mobile-first, un singur breakpoint mare; secțiunea hero pe min-height: 100dvh cu overlay de gradient.
- Finisaje: reveal la scroll (tranziție + clasă), blocul prefers-reduced-motion, dark/light prin variabile (bonus).
definiția lui „gata”
- Zero !important; zero id-uri în selectori; specificitate maximă = o clasă + pseudo-clasă.
- Lighthouse: Accessibility ≥ 95 (contrastul!); fără scroll orizontal la 320px; zoom 200% fără pierderi.
- Testul rebrandingului: schimbi 3 variabile în :root → tot site-ul își schimbă identitatea coerent.
mai departe
Cu HTML + CSS stăpânite, urmează JavaScript — al treilea strat, comportamentul. Iar cursul de WordPress din platformă îți arată cum aceleași principii se aplică în teme reale. Testul final te așteaptă mai jos — 20 de întrebări din tot cursul. Prag: 70%.