modul 00 / start: cum funcționează web-ul
cum ajunge o pagină în browserul tău
Înainte de primul tag, trebuie să înțelegi scena pe care joacă HTML-ul. Curriculumul MDN începe exact aici — și pe bună dreptate: totul din front-end are sens abia când știi drumul unei pagini.
actorii
- Clientul — browserul tău (Chrome, Safari, Firefox): cere resurse și le afișează.
- Serverul — un calculator care ascultă cereri și răspunde cu fișiere (HTML, CSS, JS, imagini) sau cu răspunsuri generate dinamic.
- Protocolul — limba comună: HTTP/HTTPS (cereri și răspunsuri).
drumul unei cereri, pas cu pas
- Tastezi romeo.studio → browserul întreabă DNS-ul: „ce adresă IP are acest domeniu?” DNS = agenda de telefon a internetului.
- Browserul deschide o conexiune spre acel IP (TLS pentru HTTPS) și trimite o cerere: GET / HTTP/1.1.
- Serverul răspunde cu un cod de status (200 OK, 404 Not Found, 301 Redirect, 500 Server Error) + headere + corpul răspunsului — documentul HTML.
- Browserul citește HTML-ul de sus în jos și descoperă resurse suplimentare (CSS, JS, imagini) → cereri noi pentru fiecare.
cum construiește browserul pagina
- Parsează HTML-ul → DOM (Document Object Model) — arborele de elemente.
- Parsează CSS-ul → CSSOM; le combină în render tree.
- Layout — calculează unde stă fiecare cutie; Paint — desenează pixelii.
- JavaScript-ul poate modifica DOM-ul → browserul recalculează.
de ce contează pentru html
HTML-ul tău devine DOM — o structură de date pe care browserul, CSS-ul, JavaScript-ul, motoarele de căutare și cititoarele de ecran o interpretează. Scrii HTML bun = construiești un arbore corect și descriptiv, nu doar „ceva care arată ok”.
triunghiul tehnologiilor
| Tehnologie | Rol | Metaforă |
| HTML | Structură și sens (conținutul) | Scheletul |
| CSS | Prezentare (aspectul) | Hainele |
| JavaScript | Comportament (interactivitatea) | Mușchii |
Separarea responsabilităților e principiu de bază: structura în HTML, stilul în CSS, logica în JS. Cursul acesta acoperă primul strat — cel de care depind celelalte două.
modul 00 / start: cum funcționează web-ul
unelte, primul fișier & standardele web
ce îți trebuie (spoiler: aproape nimic)
- Un editor de cod: VS Code (standardul de facto, gratuit) — cu extensia Live Server pentru reîncărcare automată.
- Un browser modern + DevTools (F12) — inspectorul de elemente e a doua ta casă.
- Atât. HTML nu se compilează, nu are dependențe: fișier text → browser.
primul document
Creează index.html și scrie scheletul minimal valid:
<!DOCTYPE html>
<html lang="ro">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Prima mea pagină</title>
</head>
<body>
<h1>Salut, web!</h1>
</body>
</html>
- <!DOCTYPE html> — declară „HTML modern”; fără el browserul intră în quirks mode (comportamente vechi, imprevizibile).
- <html lang="ro"> — limba documentului: pronunție corectă pentru screen readers, ortografie, traducere.
- <head> — metadate (nu se afișează); <body> — tot ce se vede.
- index.html e numele special pe care serverele îl servesc implicit pentru un director.
cine decide ce e „html corect”
HTML e un standard deschis, întreținut de WHATWG (HTML Living Standard) cu participarea browserelor; alte tehnologii web sunt standardizate de W3C. Consecințe practice:
- HTML e „living standard” — evoluează continuu, nu mai există „HTML6”.
- Compatibilitatea înapoi e sacră: pagini din 1996 se deschid și azi.
- Parsarea e permisivă: browserul nu aruncă erori la HTML greșit — repară în tăcere, uneori altfel decât ai vrut. De aceea validarea (modulul 8) e obiceiul profesionistului.
- Suportul pe browsere îl verifici pe caniuse.com și în tabelele MDN.
resursa cursului
MDN Web Docs (developer.mozilla.org) e referința oficioasă a industriei — acest curs îi urmează curriculumul. Obișnuință de format: orice element nou întâlnești, îl cauți „mdn element-name” și citești pagina lui.
modul 01 / sintaxă & fundamentele textului
anatomia elementelor html
elementul — unitatea de bază
<p class="intro">Conținutul elementului</p>
└ tag de deschidere + atribute └ tag de închidere
- Tagul de deschidere — numele elementului în unghiulare: <p>.
- Atributele — perechi nume="valoare" în tagul de deschidere: informații suplimentare (clasă, id, sursă, limbă). Valorile mereu în ghilimele.
- Conținutul — text sau alte elemente.
- Tagul de închidere — cu slash: </p>.
elemente void (fără conținut)
Unele elemente nu pot avea conținut și nu au tag de închidere: <img>, <br>, <hr>, <input>, <meta>, <link>, <source>.
imbricare (nesting)
Elementele se conțin unele pe altele — dar trebuie închise în ordinea inversă deschiderii:
corect: <p>Text <strong>important</strong> aici.</p>
greșit: <p>Text <strong>important</p></strong>
atribute globale de știut de la început
| Atribut | Rol |
| class | Etichete pentru stilizare/selectare; mai multe, separate prin spațiu |
| id | Identificator UNIC pe pagină — ținte de linkuri, formulare, JS |
| lang | Limba conținutului (poate diferi local de limba paginii) |
| title | Tooltip la hover — doar informație suplimentară, nu esențială |
| hidden / data-* | Ascundere semantică / date custom pentru JS |
comentarii & entități
<!-- comentariu: invizibil în pagină, vizibil în sursă -->
entități: < → < · > → > · & → & · → spațiu neseparabil
© → © · „ ” → ghilimele românești „ ”
capcană
Browserul colapsează spațiile: 10 spații și 3 Enter-uri devin UN spațiu la afișare. Spațierea vizuală e treaba CSS-ului, nu a -urilor înșirate — acela e un anti-pattern clasic de începător.
modul 01 / sintaxă & fundamentele textului
head-ul documentului: metadatele care contează
Ce pui în <head> nu se vede în pagină, dar decide cum e afișată, indexată și distribuită pagina.
obligatoriile
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Titlul paginii — Numele site-ului</title>
- charset UTF-8 — prima în head: fără ea, diacriticele (ș, ț, ă) devin mojibake (â€ș...).
- viewport — fără ea, telefoanele afișează pagina „de desktop” micșorată. Obligatorie pentru responsive.
- title — tabul browserului, bookmarkul și titlul din rezultatele Google. Unic per pagină, ~50–60 de caractere, cu subiectul paginii la început.
seo & social
<meta name="description" content="Rezumat de ~150 caractere — apare sub titlu în Google.">
<link rel="canonical" href="https://exemplu.md/pagina/">
<meta property="og:title" content="Titlu la share">
<meta property="og:description" content="Descriere la share">
<meta property="og:image" content="https://exemplu.md/preview.jpg">
- description — nu influențează direct rankingul, dar decide dacă omul dă click.
- Open Graph (og:) — cardul afișat la share pe Facebook/LinkedIn/Telegram; og:image de ~1200×630px.
- canonical — varianta „oficială” a URL-ului, contra duplicatelor.
resurse legate
<link rel="stylesheet" href="styles.css">
<link rel="icon" href="favicon.ico" sizes="any">
<link rel="icon" href="icon.svg" type="image/svg+xml">
<script src="app.js" defer></script>
- CSS-ul se leagă în head (pagina se randează stilizată de la început).
- JS-ul modern: în head cu defer (se descarcă în paralel, rulează după parsarea HTML) — echivalentul curat al vechiului „script înainte de </body>”.
checklist de head profesionist
charset · viewport · title unic · description · canonical · favicon · og:title/description/image · lang pe <html>. Opt linii care despart un site amator de unul serios.
modul 01 / sintaxă & fundamentele textului
textul: headings, paragrafe, liste, accent
headings — h1…h6
- Titlurile formează schița documentului — exact ca un cuprins. Utilizatorii de screen reader navighează sărind între headings; Google le folosește pentru a înțelege structura.
- Un singur <h1> per pagină (subiectul paginii), apoi ierarhie logică: h2 pentru secțiuni, h3 pentru subsecțiuni — fără să sari niveluri (h2 → h4 = greșit).
- Nu alege headingul după mărimea fontului — mărimea o dă CSS-ul; nivelul dă sensul.
paragrafe & rupturi
- <p> — unitatea de text. Distanța dintre paragrafe = CSS, nu <br>.
- <br> — ruptură de linie DOAR unde e parte din conținut (adrese, versuri). <br><br> pentru spațiere = anti-pattern.
- <hr> — schimbare tematică (separator semantic, nu doar linie decorativă).
liste
<ul> — listă neordonată (buline): meniuri, enumerări fără ordine
<ol> — listă ordonată (numere): pași, topuri; atribute: start, reversed, type
<dl> + <dt>/<dd> — listă de definiții: termen → descriere (glosare, specificații)
Listele se pot imbrica (un <ul> în interiorul unui <li>). Regulă: în <ul>/<ol>, copiii direcți pot fi doar <li>.
accent și semnificație în linie
| Element | Sens | Nu confunda cu |
| <em> | Accent de intonație (schimbă sensul frazei) | <i> — voce alternativă: termeni străini, titluri de opere, gânduri |
| <strong> | Importanță serioasă (avertismente, esențial) | <b> — atenție stilistică fără importanță sporită (cuvinte-cheie) |
| <mark> | Evidențiere de relevanță (rezultat de căutare) | — |
| <small> / <s> / <u> | Text secundar (legal) / inexact / adnotat | — |
| <code>, <pre>, <kbd> | Cod în linie, bloc preformatat (păstrează spațiile), taste | — |
| <sub> / <sup> | Indice / exponent: H<sub>2</sub>O, m<sup>2</sup> | — |
| <abbr title="…"> | Abrevieri cu explicație la hover | — |
citate
<blockquote cite="https://sursa.md"> — citat-bloc, cu sursa în atribut
<q> — citat scurt în linie (browserul adaugă ghilimele)
<cite> — titlul operei citate
principiul modulului
Alege elementul după ce ESTE conținutul, nu după cum vrei să arate. „Vreau text îngroșat” e o decizie CSS; „acesta e un avertisment important” e o decizie HTML (<strong>). Dacă interiorizezi doar atât din tot cursul — deja scrii HTML mai bun decât jumătate din industrie.
modul 02 / semantică & structura paginii
de ce semantică (și de ce nu doar div-uri)
Poți construi orice pagină doar din <div>-uri — va „arăta” identic. Atunci de ce există zeci de elemente? Pentru că HTML-ul nu e citit doar de ochi:
cine „citește” semantica ta
- Tehnologiile asistive — un screen reader anunță „navigare”, „articol”, „listă cu 5 elemente”, permite saltul între landmark-uri și headings. Într-o pagină de div-uri, nevăzătorul aude o supă de text fără hartă.
- Motoarele de căutare — înțeleg ce e conținut principal, ce e navigare, ce e boilerplate. Semantica bună = indexare mai precisă.
- Browserele — Reader Mode extrage <article>; funcții native (validare, focus) vin gratis cu elementele corecte.
- Colegii tăi — <nav> îți spune instant ce face blocul; <div class="wrapper-2-final"> nu.
regula de decizie
Există un element care descrie sensul conținutului? Folosește-l. Nu există? Abia atunci <div> (bloc) sau <span> (în linie) — elemente fără sens, doar cârlige pentru stil. Div-ul nu e interzis; e ultima opțiune, nu prima.
bloc vs în linie (comportament implicit)
- Block-level — ocupă toată lățimea, începe pe rând nou: p, h1–h6, ul, div, section…
- Inline — curge în text: a, em, strong, span, img… Un element inline nu poate conține elemente block (excepția modernă: <a> poate împacheta blocuri).
modul 02 / semantică & structura paginii
scheletul paginii: landmark-urile
structura canonică
<body>
<header> … logo, titlu site, <nav>meniu</nav> … </header>
<main>
<article> … conținutul principal … </article>
<aside> … conținut adiacent … </aside>
</main>
<footer> … contact, copyright, linkuri … </footer>
</body>
| Element | Sens | Reguli |
| <header> | Introducerea unei zone: logo+meniu la nivel de pagină; titlu+meta la nivel de articol | Poate apărea de mai multe ori (pagină, articol) |
| <nav> | Bloc MAJOR de navigare | Nu orice grup de linkuri — meniul principal, breadcrumbs; nu linkurile din footer neapărat |
| <main> | Conținutul unic al paginii | UNUL singur per pagină; nu conține elemente repetate pe tot site-ul (header/footer) |
| <article> | Conținut de sine stătător, distribuibil independent: articol, produs, comentariu, card | Testul: ar avea sens în RSS / pe alt site? |
| <section> | Secțiune tematică a unui întreg | De regulă cu propriul heading; nu container generic de stil |
| <aside> | Conținut tangențial: sidebar, „citește și”, glosar lateral | Pagina rămâne completă și fără el |
| <footer> | Încheierea unei zone: autor, copyright, linkuri secundare | Ca header — poate fi și per articol |
article vs section vs div — decizia în 3 întrebări
- E de sine stătător, are sens scos din context? → article
- E o secțiune tematică a documentului, cu titlu propriu? → section
- E doar un container pentru layout/stil? → div
capcană de examen (și de interviu)
<section> NU e „div-ul semantic universal”. Fără heading și fără temă proprie, secțiunea ta e de fapt un div. Iar <article> nu înseamnă doar „articol de blog” — un card de produs sau un comentariu sunt tot articles: unități autonome de conținut.
modul 02 / semantică & structura paginii
restul arsenalului semantic
figuri și legende
<figure>
<img src="grafic.png" alt="Evoluția vânzărilor 2020–2025: creștere de la 1,2M la 4,8M">
<figcaption>Fig. 1 — Vânzările au crescut de 4× în cinci ani.</figcaption>
</figure>
<figure> = conținut ilustrativ referențiat din text (imagini, diagrame, cod, video), cu legenda în <figcaption>. Legenda și alt-ul au roluri diferite: alt descrie imaginea, figcaption o comentează.
timp, adrese, date
<time datetime="2026-07-19">19 iulie 2026</time>
<time datetime="2026-07-19T18:30">azi la 18:30</time>
<address>Contact: <a href="mailto:hi@romeo.studio">hi@romeo.studio</a></address>
- <time datetime> — data în format ISO, lizibilă de mașini (Google, calendare), afișată oricum vrei pentru oameni.
- <address> — datele de contact ale autorului articolului/paginii (nu orice adresă poștală din conținut).
widgeturi native fără javascript
<details>
<summary>Ce include prețul?</summary>
<p>Design, implementare, hosting pentru primul an…</p>
</details>
details/summary = acordeon nativ (FAQ-uri!): accesibil, cu tastatură, zero JS. Alte gratuități: <dialog> (ferestre modale native), <progress> și <meter> (bare de progres/măsură).
span & div — locul lor legitim
După toată pledoaria semantică: div și span rămân indispensabile pentru layout și stilizare (grid-uri, wrappere, accente vizuale fără sens). Diferența dintre amator și profesionist nu e absența div-urilor, ci ordinea deciziei: întâi sensul, apoi containerul.
exercițiu de aur
Ia homepage-ul unui site cunoscut și „scheletonizează-l” pe hârtie: unde e header, nav, main, câte article-uri vezi, ce e aside. Apoi deschide DevTools și compară cu realitatea. Fă asta cu 3 site-uri și structura ți se mută în reflexe.
modul 03 / linkuri & navigare
elementul <a> în profunzime
Linkul e invenția care face web-ul „web”. Pare simplu — și tocmai de aceea e plin de detalii pe care mulți nu le stăpânesc.
href: absolut vs relativ
absolut: https://romeo.studio/contact/ — alt site sau adresă completă
relativ la document: servicii.html · ../index.html · img/logo.png
relativ la rădăcină: /contact/ — începe cu / = de la rădăcina domeniului
fragment: #preturi — sare la elementul cu id="preturi" din pagină
- Intern folosește căi relative la rădăcină (/contact/) — stabile indiferent de adâncimea paginii curente.
- Fragmentele fac cuprinsuri și „back to top”: <a href="#top">. Se pot combina: /ghid/#instalare.
linkuri speciale
<a href="mailto:hi@romeo.studio?subject=Ofertă">Scrie-ne</a>
<a href="tel:+37360000000">+373 60 000 000</a>
<a href="oferta.pdf" download>Descarcă oferta (PDF, 2 MB)</a>
Atributul download forțează descărcarea în loc de deschidere (pentru fișiere de pe același domeniu). La linkuri spre fișiere, anunță formatul și mărimea — politețe de UX.
target="_blank" — corect
<a href="https://extern.md" target="_blank" rel="noopener noreferrer">Sursă externă</a>
- rel="noopener" — pagina deschisă nu primește acces la fereastra ta (securitate; implicit în browserele moderne, dar se scrie explicit).
- Deschide în tab nou cu zgârcenie: doar externe/documente. Utilizatorul își poate deschide singur tab-uri; tu nu decide pentru el la linkuri interne.
textul linkului — regulă de accesibilitate și seo
- Textul trebuie să aibă sens scos din context: utilizatorii de screen reader ascultă lista linkurilor din pagină. „Click aici” × 7 nu spune nimic.
- Corect: „Descarcă ghidul de instalare” · greșit: „ghidul de instalare este <a>aici</a>”.
- Textul linkului e semnal SEO pentru pagina țintă (anchor text) — descrie destinația.
capcană
Nu pune handler de JS pe un <div> ca să „facă pe linkul”. <a href> îți dă gratis: focus din tastatură, Enter, click-mijlociu pentru tab nou, meniul contextual, statusul vizitat, indexare. Recreate manual = muncă multă pentru un rezultat mai prost.
modul 03 / linkuri & navigare
meniuri de navigare făcute corect
pattern-ul canonic
<nav aria-label="Meniu principal">
<ul>
<li><a href="/">Acasă</a></li>
<li><a href="/servicii/" aria-current="page">Servicii</a></li>
<li><a href="/contact/">Contact</a></li>
</ul>
</nav>
- nav + ul + li + a — meniul e o listă de linkuri: screen readerul anunță „listă, 3 elemente”, utilizatorul știe dimensiunea meniului.
- aria-label distinge meniurile când ai mai multe („Meniu principal”, „Meniu subsol”).
- aria-current="page" — marchează pagina curentă semantic (stilul vine din CSS pe [aria-current]).
submeniuri
Structural: un <ul> imbricat în <li>-ul părinte. Interactivitatea (deschidere la hover/click) e treaba CSS/JS, dar HTML-ul rămâne o listă imbricată — crawlerele și AT văd ierarhia chiar fără stiluri.
skip link — mica mare politețe
<body>
<a class="skip-link" href="#continut">Sari la conținut</a>
<header>…meniu lung…</header>
<main id="continut">…</main>
Primul element focusabil din pagină: permite utilizatorilor de tastatură să sară peste meniu, în loc să dea Tab de 15 ori pe fiecare pagină. Vizual se ascunde până primește focus (CSS).
breadcrumbs
<nav aria-label="Breadcrumb">
<ol>
<li><a href="/">Acasă</a></li>
<li><a href="/blog/">Blog</a></li>
<li aria-current="page">Titlul articolului</li>
</ol>
</nav>
Listă ordonată (ierarhia are ordine), ultimul element fără link. Google le afișează în rezultate (cu structured data devine și mai explicit).
modul 04 / media: imagini, video, audio, embed
imagini: img, alt și performanța
elementul img, complet
<img src="echipa.jpg"
alt="Echipa Romeo Studio la lansarea proiectului MEROM"
width="1200" height="800"
loading="lazy" decoding="async">
- alt — textul alternativ. Regulile de aur:
- Imagine cu informație → descrie informația („Grafic: vânzările au crescut de 4×”), nu „imagine de grafic”.
- Imagine pur decorativă → alt="" (gol, dar PREZENT) — screen readerul o sare. Alt lipsă = readerul citește numele fișierului: „IMG underscore 4821 dot jpg”.
- Imagine-link → alt descrie destinația („Pagina principală Romeo Studio”, nu „logo”).
- width + height — dimensiunile intrinseci: browserul rezervă spațiul înainte de încărcare → fără săritura layout-ului (CLS, metrică Core Web Vitals). Dimensiunea afișată o controlează CSS-ul.
- loading="lazy" — amână imaginile din afara ecranului. NU pe imaginea hero (aceea trebuie imediat).
formatele și când le alegi
| Format | Folosește pentru |
| WebP / AVIF | Standardul modern pentru fotografii și grafice — 25–50% mai mic decât JPEG |
| JPEG | Fotografii, fallback universal |
| PNG | Transparență + grafice cu muchii dure (când SVG nu se poate) |
| SVG | Logo-uri, iconițe, ilustrații vectoriale — scalare infinită, KB puțini |
| GIF | Aproape niciodată — video scurt mut > GIF (de 10× mai mic) |
modul 04 / media: imagini, video, audio, embed
imagini responsive: srcset, sizes, picture
Problema: același <img> se afișează pe un telefon de 360px și pe un desktop 4K. Servești tuturor fișierul de 2000px? Mobilul plătește degeaba. Aici intră imaginile responsive.
srcset + sizes: aceeași imagine, rezoluții diferite
<img src="hero-800.jpg"
srcset="hero-400.jpg 400w, hero-800.jpg 800w, hero-1600.jpg 1600w"
sizes="(max-width: 600px) 100vw, 50vw"
alt="…">
- srcset — lista variantelor cu lățimea reală a fiecăreia (descriptorul w).
- sizes — cât spațiu va ocupa imaginea în layout la diferite lățimi de ecran („sub 600px: tot ecranul; altfel: jumătate”).
- Browserul alege singur varianta optimă, ținând cont și de densitatea pixelilor (ecrane retina). Tu descrii, el decide.
picture: art direction & formate moderne
<picture>
<source srcset="hero.avif" type="image/avif">
<source srcset="hero.webp" type="image/webp">
<source media="(max-width: 600px)" srcset="hero-portrait.jpg">
<img src="hero.jpg" alt="…">
</picture>
<picture> e pentru două scenarii pe care srcset nu le acoperă:
- Formate cu fallback: browserul ia primul <source> pe care îl suportă (AVIF → WebP → JPEG).
- Art direction: pe mobil vrei ALTĂ imagine (crop vertical, subiect mai mare), nu doar aceeași mai mică — alegi cu media queries.
<img> din interior rămâne obligatoriu: e fallback-ul și purtătorul alt-ului.
regula practică
Aceeași imagine la mărimi diferite → srcset/sizes. Imagini diferite sau formate cu fallback → picture. În ambele cazuri: width/height pe img, lazy pe cele sub fold.
modul 04 / media: imagini, video, audio, embed
video, audio & subtitrări
video nativ
<video controls width="1280" height="720" poster="preview.jpg" preload="metadata">
<source src="demo.webm" type="video/webm">
<source src="demo.mp4" type="video/mp4">
<track src="subtitrari-ro.vtt" kind="subtitles" srclang="ro" label="Română" default>
<p>Browserul tău nu suportă video. <a href="demo.mp4">Descarcă-l</a>.</p>
</video>
- controls — interfața nativă de redare. Fără el, utilizatorul n-are cum porni videoul (decât prin JS).
- <source> multiple — browserul ia primul format suportat. MP4 (H.264) e fallback-ul universal; WebM e mai eficient.
- poster — imaginea de dinaintea redării; preload="metadata" — încarcă doar durata/dimensiunile, nu tot fișierul.
- autoplay funcționează doar împreună cu muted (politica browserelor) — pattern-ul „video de fundal”: autoplay muted loop playsinline.
track: subtitrări & accesibilitate
- <track> leagă fișiere WebVTT (.vtt): kind="subtitles" (traducere), "captions" (transcriere + sunete — pentru utilizatori surzi), "chapters".
- Subtitrările nu sunt „nice to have”: mare parte din vizionările pe mobil sunt fără sunet, iar pentru utilizatorii surzi sunt singura cale.
audio
<audio controls preload="metadata">
<source src="episod.mp3" type="audio/mpeg">
</audio>
Aceleași principii; pentru podcasturi, oferă lângă player și un transcript text — accesibilitate + conținut indexabil.
când nu folosești video nativ
Pentru streaming la scară (mii de vizionări, calități adaptive), platformele (YouTube/Vimeo/Bunny) sunt alegerea corectă — servesc adaptive bitrate și CDN. Video nativ = clipuri scurte, controlate, self-hosted.
modul 04 / media: imagini, video, audio, embed
iframe, embed & svg
iframe: o pagină în pagină
<iframe src="https://www.youtube-nocookie.com/embed/VIDEO_ID"
title="Demo produs — video YouTube"
width="560" height="315" loading="lazy"
allowfullscreen></iframe>
- Cazurile legitime: YouTube/Vimeo, hărți, formulare externe, payment widgets.
- title pe iframe = obligatoriu pentru accesibilitate (ce conține fereastra?).
- loading="lazy" pe iframe-uri = câștig mare de performanță (harta din footer nu blochează încărcarea).
- sandbox — restricționează ce poate face conținutul (scripturi, formulare, popup-uri); pentru conținut din surse neîncredere, sandbox cu permisiuni minime.
- Nu orice site se lasă înrămat: headerul X-Frame-Options / CSP frame-ancestors blochează embedding-ul (protecție anti-clickjacking).
svg: două moduri de folosire
| Mod | Cum | Când |
| Ca imagine | <img src="logo.svg" alt="Romeo Studio"> | Logo-uri, ilustrații statice — simplu, cacheable |
| Inline | <svg>…<path …></svg> direct în HTML | Iconițe stilizabile din CSS (culoare la hover), animații, acces din JS |
Inline SVG intră în DOM: îl colorezi cu currentColor, îl animezi, îl faci accesibil cu <title> intern + role="img". Prețul: nu se cache-uiește separat și umflă HTML-ul — pentru seturi mari de iconițe există sprite-uri SVG.
capcană de performanță
Fiecare iframe e o pagină întreagă cu propriile resurse. Trei embed-uri YouTube „normale” pot căra megaocteți de JS. Remedii: loading="lazy", varianta -nocookie, sau pattern-ul „fațadă”: o imagine + butonul play, iframe-ul real se încarcă abia la click.
modul 05 / tabele
tabele de date făcute corect
Tabelele au o singură meserie: date tabulare — informație care are sens pe rânduri și coloane. Pentru layout NU se folosesc din ~2005 (de aceea a fost inventat CSS-ul de layout).
anatomia completă
<table>
<caption>Prețurile pachetelor de hosting (2026)</caption>
<thead>
<tr><th scope="col">Pachet</th><th scope="col">Spațiu</th><th scope="col">Preț/lună</th></tr>
</thead>
<tbody>
<tr><th scope="row">Start</th><td>10 GB</td><td>3 €</td></tr>
<tr><th scope="row">Pro</th><td>50 GB</td><td>8 €</td></tr>
</tbody>
</table>
- <caption> — titlul tabelului, primul copil al <table>: screen readerul îl anunță înainte de a intra în date.
- <th> vs <td> — th = celulă-antet; td = celulă de date. Diferența nu e vizuală (bold), ci semantică.
- scope="col" / scope="row" — spune la ce se referă antetul. Când un utilizator AT ajunge într-o celulă, readerul anunță anteturile asociate: „Pro, Preț pe lună: 8 euro”. Fără scope, aude doar „8 euro” — orb în grilă.
- thead / tbody / tfoot — grupează logic; permit CSS-ului antet fix și browserului repetarea antetului la print.
celule întinse
<td colspan="2"> — se întinde pe 2 coloane
<td rowspan="3"> — se întinde pe 3 rânduri
Folosite cu măsură: tabelele puternic fuzionate devin greu de parcurs cu AT — uneori două tabele simple sunt mai bune decât unul stufos.
când nu folosești tabel
- Layout de pagină → CSS Grid/Flexbox.
- Liste de perechi termen-valoare → <dl>.
- Carduri de produse → grile de <article>.
- Testul: „are sens întrebarea «ce valoare e pe rândul X, coloana Y?»” Dacă nu — nu e tabel.
tabele pe mobil
Tabelele late nu încap pe 360px. Soluția standard: un wrapper cu overflow-x: auto (scroll orizontal doar pe tabel) — exact cum sunt afișate tabelele din acest curs.
modul 06 / formulare
fundamentele formularelor
Formularele sunt singurul loc unde HTML-ul devine conversație în ambele sensuri — și zona cu cele mai multe greșeli de accesibilitate din tot web-ul.
scheletul
<form action="/trimite" method="post">
<label for="email">Adresa de email</label>
<input type="email" id="email" name="email" required>
<button type="submit">Trimite</button>
</form>
- action — unde se trimit datele; method: get (datele în URL — căutări, filtre, bookmarkabile) vs post (datele în corpul cererii — tot ce modifică sau e sensibil).
- name — numele câmpului ÎN datele trimise. Fără name, câmpul nu se trimite deloc — bug clasic de „formularul vine gol”.
- button type="submit" — declanșează trimiterea. Atenție: în interiorul unui form, <button> fără type explicit e submit implicit — sursa butoanelor care „retrimit formularul misterios”; butoanele de UI primesc type="button".
label — regula sfântă
- Fiecare control primește un <label>, legat prin for ↔ id (sau împachetând controlul).
- Ce câștigi: screen readerul anunță eticheta la focus; click pe etichetă = focus/bifare pe control (ținte mai mari pe mobil — încearcă pe un checkbox).
- Placeholder NU e label: dispare când scrii, are contrast slab, nu e citit consecvent de AT. Placeholder = exemplu de format, cel mult.
tipurile de input care lucrează pentru tine
| Tip | Ce primești gratis |
| email / url / tel | Validare de format (email, url) + tastatura potrivită pe mobil |
| number / range | Săgeți/slider, min/max/step |
| date / time / datetime-local | Calendar nativ al platformei |
| password / search / hidden | Mascare / semantică de căutare / valori nevizibile |
| checkbox / radio | Multiple / exclusive (radio: același name = același grup) |
| file | Selector de fișiere (accept="image/*", multiple); formularul cere enctype="multipart/form-data" |
| color | Color picker nativ |
principiu
Tipul corect de input = validare + tastatură + UI native, fără un rând de JS. type="text" pentru orice e risipa clasică: utilizatorul de mobil primește tastatura de litere la câmpul de telefon.
modul 06 / formulare
controale complexe: select, textarea, grupare
select — lista derulantă
<label for="judet">Raion</label>
<select id="judet" name="judet">
<option value="">— alege —</option>
<optgroup label="Nord">
<option value="bl">Bălți</option><option value="sr">Soroca</option>
</optgroup>
</select>
- value e ce se trimite; textul e ce se vede. optgroup grupează vizual; selected preselectează; multiple permite selecții multiple (UX slab — preferă checkboxuri).
- Sub ~5 opțiuni, radio buttons sunt mai rapide (totul vizibil dintr-o privire); select-ul câștigă la liste lungi.
datalist — text liber cu sugestii
<input list="orase" id="oras" name="oras">
<datalist id="orase">
<option value="Chișinău"><option value="Cluj-Napoca"><option value="Iași">
</datalist>
Hibridul select+text: utilizatorul alege din sugestii SAU scrie altceva. Autocomplete nativ, zero JS.
textarea
<label for="msg">Mesajul tău</label>
<textarea id="msg" name="msg" rows="6" maxlength="1000"></textarea>
Text multiliniar; rows dă înălțimea inițială; conținutul preexistent stă ÎNTRE taguri (nu în value).
fieldset & legend — gruparea logică
<fieldset>
<legend>Metoda de livrare</legend>
<label><input type="radio" name="livrare" value="curier"> Curier</label>
<label><input type="radio" name="livrare" value="ridicare"> Ridicare din sediu</label>
</fieldset>
fieldset+legend e obligatoriu la grupurile de radio/checkbox: screen readerul citește legenda + eticheta („Metoda de livrare, Curier”) — fără ea, „Curier” plutește fără context. Bonus: disabled pe fieldset dezactivează tot grupul.
modul 06 / formulare
validare nativă & ux-ul formularelor
validarea încorporată în browser
<input type="email" required> — obligatoriu + format de email
<input type="text" minlength="3" maxlength="30"> — lungime
<input type="number" min="1" max="10" step="1"> — interval
<input type="text" pattern="[0-9]{6}" title="Cod din 6 cifre"> — regex custom
- Browserul blochează trimiterea și afișează mesajul lângă primul câmp invalid — gratis, localizat în limba browserului.
- pattern primește un regex; title devine parte din mesajul de eroare.
- CSS-ul stilizează stările: :required, :valid, :invalid, :user-invalid (ultima — abia după interacțiune, ca să nu țipe formularul roșu de la prima secundă).
- Validarea nativă e UX; validarea pe server rămâne obligatorie — clientul poate fi ocolit oricând.
autocomplete — formulare care se completează singure
<input name="fname" autocomplete="given-name">
<input name="email" autocomplete="email">
<input name="tel" autocomplete="tel">
<input name="adresa" autocomplete="street-address">
Valorile standard de autocomplete lasă browserul/managerul de parole să umple corect câmpurile. La checkout, diferența dintre 2 minute și 10 secunde. Pe mobil, adaugă și inputmode (numeric, decimal, tel) unde tipul nu o face deja.
ux-ul care ridică rata de completare
- Cere minimul. Fiecare câmp în plus taie din conversii; „compania” și „cum ne-ai găsit” pot fi opționale sau lipsă.
- Etichete vizibile permanent (nu doar placeholder), erori lângă câmp, formulate uman („Emailul pare incomplet — lipsește @”), nu „INVALID INPUT”.
- Nu bloca paste-ul, nu cere formate rigide („fără spații în telefon”) — normalizezi pe server.
- O singură coloană de câmpuri; ordinea logică; submit-ul spune ce face („Trimite cererea”, nu „Submit”).
- După trimitere: confirmare clară + ce urmează. Formularul care „doar se golește” lasă utilizatorul în ceață.
capcană
required pe TOATE + pattern-uri agresive = formular „sigur” pe hârtie, abandonat în practică. Validarea servește utilizatorul (ghidaj), nu invers. Strictețea reală trăiește pe server.
modul 07 / accesibilitate în html
accesibilitatea nativă: html-ul face 90% din treabă
Accesibilitatea (a11y) = pagina utilizabilă de toți: nevăzători cu screen reader, utilizatori doar de tastatură, daltoniști, oameni cu tremur, cu dizabilități cognitive — plus „dizabilitățile temporare” ale tuturor: soare pe ecran, o mână ocupată, conexiune slabă. Nu e feature; în tot mai multe jurisdicții e lege (European Accessibility Act, WCAG ca standard).
cum „vede” pagina un screen reader
- Browserul construiește din DOM un accessibility tree: fiecare element cu rolul (link, buton, heading), numele (textul/eticheta) și starea lui.
- Utilizatorul navighează pe headings (lista H-urilor = cuprinsul), pe landmark-uri (nav, main…), pe lista de linkuri și de controale de formular.
- De aceea tot ce ai învățat până acum ESTE accesibilitate: heading-uri ierarhice, landmark-uri, alt, label, caption/scope, textul linkurilor. HTML-ul semantic e 90% din a11y.
tastatura — testul de 2 minute
- Regula: tot ce se face cu mouse-ul se face și din tastatură: Tab/Shift+Tab între elemente, Enter pe linkuri, Enter/Space pe butoane, săgeți în radio/select.
- Elementele interactive native (a, button, input) sunt focusabile din fabrică. Div-ul cu onclick — NU: invizibil pentru tastatură și AT. Alt motiv pentru „folosește elementul corect”.
- Focus vizibil: inelul de focus nu se stinge (outline: none fără înlocuitor = păcat capital); ordinea de Tab urmează ordinea din DOM — încă un motiv pentru HTML ordonat logic.
alte reguli purtate de html
- Contrast: minim 4,5:1 pentru text normal (WCAG AA) — se decide în design, se verifică cu DevTools.
- lang corect pe <html> și pe pasajele în altă limbă (pronunție corectă).
- Nu comunica DOAR prin culoare („câmpurile roșii sunt obligatorii”) — dublează cu text/iconiță.
- Zoom la 200% fără pierdere de conținut — vine natural dacă nu fixezi totul în px și nu blochezi scalarea în viewport (fără maximum-scale=1!).
modul 07 / accesibilitate în html
aria: când html-ul nu ajunge
ce este aria
ARIA (Accessible Rich Internet Applications) = set de atribute care descriu rol, nume și stare pentru accessibility tree, acolo unde HTML-ul nativ nu are element potrivit (tab-uri, alerte dinamice, meniuri complexe).
prima regulă a aria: nu folosi aria
Regula oficială #1: dacă există element HTML nativ cu semantica necesară, folosește-l pe el. <button> bate <div role="button" tabindex="0" + JS pentru Enter/Space> din toate punctele de vedere. ARIA descrie, nu implementează: role="button" nu face div-ul apăsabil — doar minte screen readerul că ar fi.
atributele aria pe care chiar le folosești
| Atribut | Ce face | Exemplu |
| aria-label | Nume accesibil când nu există text vizibil | Buton-iconiță: <button aria-label="Închide meniul">✕</button> |
| aria-labelledby / describedby | Numele/descrierea = alt element existent | Input descris de hint-ul de sub el |
| aria-current | Elementul curent dintr-un set | Pagina activă din meniu |
| aria-expanded | Starea deschis/închis a unui declanșator | Butonul de meniu mobil (true/false din JS) |
| aria-hidden="true" | Ascunde de AT elemente pur decorative | Iconițe ornamentale lângă text |
| aria-live | Anunță schimbări dinamice de conținut | „Produs adăugat în coș” (polite/assertive) |
cum verifici accesibilitatea
- Testul tastaturii: parcurge pagina doar cu Tab/Enter/săgeți — ajungi peste tot? vezi mereu unde ești?
- Audit automat: Lighthouse (tab-ul Accessibility) sau extensia axe DevTools — prind ~30-40% din probleme (alt lipsă, contrast, label-uri), restul e judecată umană.
- Un screen reader măcar o dată: NVDA (Windows, gratuit) sau VoiceOver (macOS, Cmd+F5) pe propria pagină — 15 minute care schimbă definitiv cum scrii HTML.
capcană
„Am adăugat role-uri și aria peste tot, deci e accesibil.” ARIA aplicată greșit e mai rea decât lipsa ei — minte utilizatorul despre ce poate face pagina. Ordinea corectă: semantică nativă → tastatură → abia apoi ARIA, punctual, pentru ce rămâne.
modul 08 / debugging & bune practici
debugging: validator, devtools, greșeli clasice
de ce html-ul „merge” chiar stricat
Parsarea permisivă (modulul 0): browserul repară tăcut. Consecința: bug-urile de HTML nu apar ca erori, ci ca simptome ciudate — stiluri care nu se aplică, layout rupt doar într-un browser, JS care nu găsește elemente. Debugging-ul HTML = compararea a ce ai scris cu ce a construit browserul.
unealta 1: validatorul w3c
- validator.w3.org — lipești URL-ul sau codul; primești lista de erori cu linia exactă.
- Nu e birocrație: fiecare eroare e un loc unde browserul ghicește în locul tău. HTML valid = comportament predictibil în toate browserele.
- Rulează-l la finalul fiecărui proiect și după modificări mari de structură.
unealta 2: devtools → elements
- Panoul Elements arată DOM-ul real — inclusiv reparațiile browserului. Dacă vezi </p>-uri apărute din senin sau elemente mutate afară din părinte, HTML-ul tău a fost „corectat”.
- Click-dreapta → Inspect pe elementul problematic; compară structura cu intenția.
- Tab-ul Accessibility (în Elements) arată numele, rolul și arborele accesibil al elementului selectat.
top greșeli care produc simptome ciudate
| Greșeala | Simptomul tipic |
| Element block în <p> (div, ul în paragraf) | Browserul închide <p> forțat → stiluri „rupte” inexplicabil |
| id duplicat | getElementById/labels/fragmente țintesc doar primul — comportament „aleator” |
| Taguri neînchise (div, a) | Restul paginii „înghițit” în element; tot layoutul de sub el se strică |
| Ghilimele lipsă la atribute cu spații | class="btn primary" fără ghilimele → primary devine atribut |
| <li> în afara ul/ol, <tr> fără table | Elemente mutate/aruncate de parser |
| & neescapat în text/URL-uri | Entități accidentale, validare picată |
reflex de profesionist
Simptom ciudat de CSS/JS? Întâi validează HTML-ul. Jumătate din „bug-urile de CSS” ale începătorilor sunt HTML reparat de browser altfel decât se aștepta autorul.
modul 08 / debugging & bune practici
boilerplate profesionist & checklist seo on-page
boilerplate-ul de pornire (totul din curs, la un loc)
<!DOCTYPE html>
<html lang="ro">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Subiectul paginii — Brand</title>
<meta name="description" content="…">
<link rel="canonical" href="https://…">
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
<meta property="og:title" content="…"><meta property="og:image" content="…">
<link rel="stylesheet" href="/css/style.css">
<script src="/js/app.js" defer></script>
</head>
<body>
<a class="skip-link" href="#continut">Sari la conținut</a>
<header>…<nav aria-label="Meniu principal">…</nav></header>
<main id="continut">…</main>
<footer>…</footer>
</body>
</html>
checklist seo on-page (partea de html)
- title unic per pagină, subiectul la început, ~50–60 caractere.
- meta description ~150 caractere care vinde click-ul.
- Un h1 = subiectul; ierarhie h2/h3 reală (Google citește schița exact ca screen readerul).
- URL-uri & slug-uri descriptive; canonical pe varianta oficială.
- Alt pe imaginile de conținut; nume de fișiere descriptive.
- Linkuri interne cu anchor text descriptiv către paginile importante.
- Semantica structurii (main, article, nav) — ajută extragerea conținutului principal.
- Bonus: date structurate (JSON-LD în script type="application/ld+json") pentru breadcrumbs, produse, articole — rich results în Google.
igiena proiectului
- Nume de fișiere: litere mici, fără spații, fără diacritice, cu cratime: despre-noi.html. Serverele Linux sunt case-sensitive: Logo.PNG ≠ logo.png — sursa clasică de imagini lipsă „doar pe live”.
- Structură: /css /js /img + index.html în rădăcină; căi relative la rădăcină.
- Indentare consecventă (2 spații), un formatter (Prettier) — HTML-ul se citește de 10× mai des decât se scrie.
pasul următor
Scheletul de aici e exact ce vei stiliza în cursul de CSS al platformei: selectori pe semantica scrisă corect, layout pe landmark-uri, stări pe :focus-visible. HTML-ul bun face CSS-ul simplu.
modul 09 / proiect ghidat & test final
proiect ghidat: pagina completă, de la zero
Construiește pagina de prezentare a unui mic studio („Atelier Foto Chișinău”) aplicând TOT cursul. Lucrează real, în editor — nu doar citi. Mai jos e planul + criteriile; implementarea e a ta.
pasul 1 — schița de conținut (pe hârtie)
- Secțiuni: hero (cine suntem), servicii (3), portofoliu (4 imagini), prețuri (tabel), FAQ (3 întrebări), contact (formular).
- Scrie ierarhia headings înainte de cod: h1 = numele studioului; h2 = fiecare secțiune; h3 = serviciile individuale.
pasul 2 — scheletul semantic
header → logo (img cu alt) + nav aria-label cu 4 linkuri-fragment (#servicii, #portofoliu, #preturi, #contact)
main#continut → section#servicii (3 × article) · section#portofoliu (figure + figcaption) · section#preturi (table cu caption/scope) · section#faq (details/summary) · section#contact (form)
footer → address cu mailto și tel + <small> copyright
Nu uita: skip link înainte de header; un singur main; fiecare section cu h2-ul lui.
pasul 3 — punctele tehnice obligatorii
- Head complet: charset, viewport, title, description, favicon, og:title/image.
- Imaginile de portofoliu: width/height, loading="lazy", alt descriptiv; imaginea hero FĂRĂ lazy.
- Tabelul de prețuri: caption + th scope="col"/"row".
- Formularul: label pentru fiecare câmp, type-uri corecte (email, tel), textarea, un fieldset cu radio („Tip ședință: studio / exterior”), required unde are sens, autocomplete, button type="submit" cu text descriptiv.
- FAQ pe details/summary — zero JavaScript.
pasul 4 — verificarea (definiția lui „gata”)
- Validatorul W3C: 0 erori.
- Testul tastaturii: Tab parcurge logic tot; skip link-ul apare la primul Tab; focus vizibil peste tot.
- Lighthouse → Accessibility: 100 (pe o pagină fără CSS custom e complet realizabil).
- Testul schiței: în DevTools, citește doar headings-urile — se înțelege pagina fără restul textului?
criteriul suprem
Deschide pagina FĂRĂ niciun CSS. Dacă e urâtă dar perfect logică și utilizabilă — structură clară, ordine firească, totul etichetat — ai scris HTML de profesionist. Exact pe scheletul ăsta vei pune stilurile în cursul de CSS.
Mai jos: testul final — 20 de întrebări din tot cursul. Prag: 70%.