Website Markup with HTML5/CSS3
HTML5 and CSS3 are not outdated stack, but the current minimum for any website. Semantic markup affects SEO and accessibility. Modern CSS3 (Grid, Custom Properties, clamp(), container queries) solves tasks that previously required JavaScript and complex frameworks.
Semantic Document Structure
Browsers and search engines build accessibility tree from HTML. Proper semantics is not religion, it's concrete Lighthouse points and correct indexing:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Page Title — Site Name</title>
<meta name="description" content="Page description up to 160 characters">
<link rel="canonical" href="https://example.com/page/">
<!-- Preload critical resources -->
<link rel="preload" href="/fonts/inter.woff2" as="font" type="font/woff2" crossorigin>
</head>
<body>
<header role="banner">
<nav aria-label="Main navigation">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about/" aria-current="page">About</a></li>
</ul>
</nav>
</header>
<main id="main-content">
<article>
<header>
<h1>Article Title</h1>
<time datetime="2025-03-28">March 28, 2025</time>
</header>
<section aria-labelledby="section-intro">
<h2 id="section-intro">Introduction</h2>
<p>Text...</p>
</section>
</article>
<aside aria-label="Related materials">
<!-- widgets -->
</aside>
</main>
<footer role="contentinfo">
<!-- ... -->
</footer>
</body>
</html>
<article> — self-contained content (article, product card, comment). <section> — thematic block within document. <aside> — supplementary content not affecting main understanding. Don't use <div> where semantic tag exists.
Modern CSS: What to Use in 2025
CSS Custom Properties as design system:
:root {
--font-sans: 'Inter', system-ui, sans-serif;
--font-mono: 'JetBrains Mono', monospace;
--space-xs: 0.25rem;
--space-sm: 0.5rem;
--space-md: 1rem;
--space-lg: 2rem;
--space-xl: 4rem;
--color-text: hsl(220 13% 13%);
--color-muted: hsl(220 9% 46%);
--color-accent: hsl(221 83% 53%);
--color-surface: hsl(0 0% 98%);
}
clamp() for fluid typography — font size scales with screen width without media queries:
h1 { font-size: clamp(1.75rem, 4vw + 1rem, 3rem); }
h2 { font-size: clamp(1.375rem, 2.5vw + 0.75rem, 2.25rem); }
p { font-size: clamp(1rem, 1.5vw + 0.5rem, 1.125rem); }
Container Queries — component adapts to container size, not screen:
.card-wrapper {
container-type: inline-size;
container-name: card;
}
.card { display: block; }
@container card (min-width: 400px) {
.card {
display: grid;
grid-template-columns: 200px 1fr;
}
}
CSS Performance
Critical CSS — styles above fold inlined in <head>, rest loads asynchronously:
<style>
/* Inline critical CSS — ~1-3kb */
body { margin: 0; font-family: var(--font-sans); }
header { ... }
.hero { ... }
</style>
<link rel="stylesheet" href="/css/main.css" media="print" onload="this.media='all'">
<noscript><link rel="stylesheet" href="/css/main.css"></noscript>
CSS Containment for repaint isolation:
.card {
contain: layout paint; /* Browser won't repaint outside card */
}
will-change only where needed — don't apply globally:
/* Only on elements that are truly animated */
.modal { will-change: transform, opacity; }
Forms
<form novalidate>
<div class="field">
<label for="email">Email <span aria-hidden="true">*</span></label>
<input
type="email"
id="email"
name="email"
autocomplete="email"
aria-required="true"
aria-describedby="email-error"
>
<span id="email-error" role="alert" class="field__error" hidden>
Enter valid email
</span>
</div>
</form>
autocomplete is mandatory attribute for login and registration forms. Browsers and password managers use it for autofill.
Images
<!-- Responsive images with srcset -->
<img
src="image-800.webp"
srcset="image-400.webp 400w, image-800.webp 800w, image-1200.webp 1200w"
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 400px"
alt="Image description"
width="800"
height="600"
loading="lazy"
decoding="async"
>
<!-- For hero image — loading="eager" + fetchpriority="high" -->
<img
src="hero.webp"
alt="..."
width="1440"
height="600"
loading="eager"
fetchpriority="high"
>
width and height are mandatory — browser reserves space before load and avoids CLS (Cumulative Layout Shift).
Accessibility (a11y)
- All interactive elements reachable via keyboard (Tab, Enter, Space)
- Focus style is visible: don't hide
outlinewithout replacement - Color contrast ≥ 4.5:1 for text (WCAG 2.1 AA)
- Icons without text —
aria-labelor hidden<span>
/* Custom focus instead of default outline */
:focus-visible {
outline: 2px solid var(--color-accent);
outline-offset: 3px;
border-radius: 3px;
}
/* Hide for mouse interaction */
:focus:not(:focus-visible) {
outline: none;
}
Timeframe
For HTML5/CSS3 markup, timeframes depend entirely on scope:
| Scope | Time |
|---|---|
| Landing page (1 screen) | 0.5–1 day |
| Full landing page (6–8 sections) | 1–2 days |
| Corporate site (5–10 pages) | 3–5 days |
| E-commerce (category, product, cart templates) | 5–8 days |
This is markup without logic (without JavaScript frameworks and backend).







