1. Structura proiectului si HTML semantic
# Structura proiect rulata cu Python structura = { "pagina-mea/": { "index.html": "Pagina principala", "despre.html": "Pagina Despre mine", "contact.html": "Pagina Contact", "assets/css/style.css": "Stiluri", "assets/js/main.js": "Script JS", "assets/img/profil.jpg": "Foto" } } for fisier, rol in structura["pagina-mea/"].items(): print(f" {fisier} → {rol}")
index.html → Pagina principala despre.html → Pagina Despre mine contact.html → Pagina Contact assets/css/style.css → Stiluri assets/js/main.js → Script JS assets/img/profil.jpg → Foto
<!-- Structura semantica HTML --> <!DOCTYPE html> <html lang="ro"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Pagina Mea</title> <link rel="stylesheet" href="assets/css/style.css"> </head> <body> <header> <!-- antetul paginii --> <nav>...</nav> <!-- meniu navigatie --> </header> <main> <!-- continut principal --> <section id="hero">...</section> <section id="despre">...</section> <section id="contact">...</section> </main> <footer>...</footer> <!-- subsolul paginii --> <script src="assets/js/main.js"></script> </body> </html>
Tag-urile semantice (<header>, <main>, <footer>, <section>, <nav>) ajuta motoarele de cautare sa inteleaga pagina si imbunatatesc accesibilitatea pentru utilizatorii cu dizabilitati (cititoare de ecran).
2. CSS: Flexbox pentru layout si navigatie
# Proprietatile Flexbox verificate cu Python flexbox_props = [ "display: flex", "justify-content", "align-items", "flex-direction" ] for prop in flexbox_props: print(f" PREZENT: {prop}")
PREZENT: display: flex PREZENT: justify-content PREZENT: align-items PREZENT: flex-direction
/* Navigatie cu Flexbox */ nav { display: flex; justify-content: space-between; /* spatiere uniforma */ align-items: center; /* aliniere verticala */ background-color: #1a1a2e; padding: 1rem 2rem; } nav a { color: white; text-decoration: none; font-size: 1rem; padding: 0.5rem 1rem; border-radius: 6px; transition: background 0.3s; } nav a:hover { background-color: rgba(255,255,255,0.15); } /* Sectiunea hero - centrata cu Flexbox */ .hero { display: flex; flex-direction: column; /* pe coloana */ justify-content: center; align-items: center; min-height: 60vh; background: linear-gradient(135deg, #1a1a2e, #16213e); color: white; text-align: center; } /* Cards - aranjate in rand */ .cards-grid { display: flex; gap: 1.5rem; justify-content: center; flex-wrap: wrap; /* trec pe rand nou daca nu incap */ }
flex-start— aliniere la stangaflex-end— aliniere la dreaptacenter— centratspace-between— spatiere uniforma, cu margini la capetespace-around— spatiere uniforma inclusiv la margini
3. Design responsive cu media queries
# Breakpoints standard (verificare Python) breakpoints = [(768, "Tableta"), (480, "Mobil")] for bp, nivel in breakpoints: print(f" @media (max-width: {bp}px) → {nivel}")
@media (max-width: 768px) → Tableta @media (max-width: 480px) → Mobil
/* Desktop - stil default (fara media query) */ .cards-grid { display: flex; gap: 1.5rem; justify-content: center; } .card { width: 280px; background: white; border-radius: 12px; padding: 1.5rem; box-shadow: 0 4px 12px rgba(0,0,0,0.1); } /* Tableta: max-width 768px */ @media (max-width: 768px) { .cards-grid { flex-direction: column; /* carduri pe coloana */ align-items: center; } .card { width: 90%; max-width: 400px; } nav { flex-direction: column; gap: 0.5rem; } } /* Mobil: max-width 480px */ @media (max-width: 480px) { .hero h1 { font-size: 2rem; /* titlu mai mic */ } .hero p { font-size: 1rem; padding: 0 1rem; } }
La proiecte profesionale se scrie mai intai CSS-ul pentru mobil (ecran mic) si se adauga min-width pentru ecrane mai mari. La scoala, abordarea max-width (desktop-first) este mai usor de inteles si acceptata.
4. JavaScript: interactivitate (mod intunecat + formular)
# Simulam toggle dark mode in Python def toggle_dark_mode(classes): if 'dark' in classes: classes.remove('dark') return classes, 'Modul Intunecat: OFF' else: classes.append('dark') return classes, 'Modul Intunecat: ON' classes = [] classes, msg = toggle_dark_mode(classes) print(msg) # Modul Intunecat: ON classes, msg = toggle_dark_mode(classes) print(msg) # Modul Intunecat: OFF
Modul Intunecat: ON Modul Intunecat: OFF
// Mod intunecat cu JavaScript const btnDark = document.getElementById('btn-dark'); const body = document.body; btnDark.addEventListener('click', function() { body.classList.toggle('dark'); // adauga/elimina clasa if (body.classList.contains('dark')) { btnDark.textContent = 'Mod Luminos ☀️'; } else { btnDark.textContent = 'Mod Intunecat 🌙'; } }); // Contor caractere pentru textarea const msgArea = document.getElementById('mesaj'); const counter = document.getElementById('contor'); const MAX_CHARS = 200; msgArea.addEventListener('input', function() { const left = MAX_CHARS - msgArea.value.length; counter.textContent = left + ' caractere ramase'; counter.style.color = left < 20 ? 'red' : 'gray'; });
# Contor caractere Python def count_chars(text, max_chars=200): left = max_chars - len(text) return len(text), left used, left = count_chars("Salut, vreau o oferta!", 200) print(f"{used} folositi, {left} ramasi din 200")
22 folositi, 178 ramasi din 200
5. Validare formular cu JavaScript
# Validare formular - logica Python def validate_form(name, email, message): errors = [] if not name.strip(): errors.append("Numele este obligatoriu") if not email.strip() or '@' not in email: errors.append("Email invalid") if len(message.strip()) < 10: errors.append("Mesajul trebuie sa aiba cel putin 10 caractere") return errors errs = validate_form("", "nu-e-email", "scurt") print("Date invalide:", errs) errs = validate_form("Ion Pop", "ion@email.ro", "Vreau o oferta pentru site.") print("Date valide:", "OK" if not errs else errs)
Date invalide: ['Numele este obligatoriu', 'Email invalid', 'Mesajul trebuie sa aiba cel putin 10 caractere'] Date valide: OK
// Validare formular cu JavaScript function validateForm() { const name = document.getElementById('name').value; const email = document.getElementById('email').value; const message = document.getElementById('mesaj').value; const errors = []; if (!name.trim()) { errors.push('Numele este obligatoriu'); } if (!email.includes('@')) { errors.push('Email invalid'); } if (message.trim().length < 10) { errors.push('Mesajul trebuie sa aiba minim 10 caractere'); } if (errors.length > 0) { document.getElementById('form-errors').innerHTML = errors.map(e => `<li>${e}</li>`).join(''); return false; // opreste trimiterea } return true; // trimitere permisa } // Atasam la formularul din HTML document.getElementById('form-contact') .addEventListener('submit', function(e) { if (!validateForm()) { e.preventDefault(); // blocheaza submit } });
6. SEO si meta tags pentru vizibilitate
# Verificam prezenta meta tags SEO import re meta_tags_html = """ <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="description" content="Pagina personala Ion Pop."> <title>Ion Pop | Pagina Personala</title> <meta property="og:title" content="Ion Pop | Pagina Personala"> """ required = { 'charset': r'meta\s+charset=', 'viewport': r'name="viewport"', 'description': r'name="description"', 'title': r'<title>', 'og:title': r'property="og:title"' } for name, pattern in required.items(): ok = bool(re.search(pattern, meta_tags_html)) print(f" {'OK' if ok else 'LIPSA'}: {name}")
OK: charset OK: viewport OK: description OK: title OK: og:title
<!-- Meta tags complete pentru SEO --> <head> <meta charset="UTF-8"> <!-- Esential: afiseaza corect pe mobil --> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- Textul din rezultatele Google (max 160 caractere) --> <meta name="description" content="Pagina personala a lui Ion Pop, pasionat de programare si design web. Proiecte, blog si contact."> <meta name="author" content="Ion Pop"> <title>Ion Pop | Pagina Personala</title> <!-- Open Graph: afisare in social media --> <meta property="og:title" content="Ion Pop | Pagina Personala"> <meta property="og:description" content="Pasionat de programare si design web."> <meta property="og:type" content="website"> <link rel="stylesheet" href="assets/css/style.css"> </head>
<title>: 50–60 caractere, contine cuvantul cheie principaldescription: 120–160 caractere, rezuma continutul- O singura eticheta
<h1>per pagina - Toate imaginile au atribut
altdescriptiv - URL-uri curate:
pagina-mea.htmlnup123.html