When someone opens your web app, their browser downloads a bundle of files and assembles the page on their device, not yours. That half of the system is the client side: the part you ship out, give away, and can never fully control. Understanding the four pieces that make it work, HTML, CSS, the DOM and cookies, is less about coding and more about knowing where your product's logic, your users' data, and your security boundary actually sit.

The quick version

  • HTML is the structure, the headings, paragraphs, buttons and form fields that say what is on the page. CSS is the presentation, the colours, spacing and layout that say how it looks. Keeping the two separate is a deliberate design principle, not an accident.
  • The DOM (Document Object Model) is the live, in-memory tree the browser builds from your HTML. JavaScript reads and changes the DOM to make a page interactive, so "the page" you see is the DOM, not the original file.
  • Cookies are small bits of data the browser stores and sends back to your server on each request, so a stateless web can remember who is logged in. They are also the engine of tracking, consent banners and a stack of privacy law.
  • The load-bearing rule: anything on the client side can be read, copied or tampered with by the user. Never trust it for security, and never put a secret there.

The idea in depth: structure, then style, kept apart

A web page begins as HTML, HyperText Markup Language, a text document that marks up content with meaning: this is a heading, this is a paragraph, this is a link, this is a text input. HTML answers "what is this?". It says nothing about colour or position. That separation is intentional. As the W3C established when it standardised CSS (Cascading Style Sheets) as a Recommendation in December 1996, the goal was to pull presentation out of structure: HTML carries the content, a separate CSS file carries the look. Before that, formatting was tangled into the markup itself, and changing a colour meant editing every page.

Treat that split as a governance principle, not a developer preference. When structure and style are kept separate, one stylesheet can re-skin a thousand pages, and a rebrand becomes a CSS change rather than a content rewrite. Blur the line, styling jammed inline, layout hacked into the markup, and every change gets slower. So if you ever hear that a small visual tweak "touches hundreds of files," that separation has probably broken down somewhere.

HTML says what is on the page; CSS says how it looks. Keep them apart and a rebrand is one file, not a thousand.

An honest limitation. The separation is a principle, not a law of physics, and modern front-end frameworks (React, Vue and the like) deliberately bend it, co-locating structure, style and behaviour in one component because it makes large apps easier to reason about. That is a legitimate trade-off, not a violation. The point for a leader is to know which trade your team has made and why, rather than to enforce a 1996 ideal that the tooling has moved past.

The DOM: the page is a living tree, not a file

Here is the idea that trips up most non-engineers. The HTML file you wrote is not the page the user interacts with. When the browser loads your HTML, it parses it into the Document Object Model, a tree of objects, one per element, held in memory. As Mozilla's MDN documentation puts it, the DOM is "a programming interface for web documents" that "represents the document as nodes and objects" so that programming languages, almost always JavaScript, can change the structure, style and content of the page. Click a button, and JavaScript reaches into the DOM and rewrites part of the tree; the screen updates without fetching a new page.

flowchart LR
  A(["HTML file
(text you ship)"]) --> B(["Browser parses it"]) B --> C(["DOM
(live tree in memory)"]) D(["CSS file"]) --> E(["CSSOM
(style rules)"]) C --> F(["Render tree → pixels
on screen"]) E --> F G(["JavaScript"]) -.->|"reads & changes"| C
The browser turns your HTML and CSS into separate in-memory models, combines them to paint the screen, and lets JavaScript keep editing the DOM live. Leaders Loop

The same machinery explains why pages feel slow. The browser builds the DOM from HTML and a parallel model (the CSSOM) from CSS, then combines the two into a render tree before it can paint anything. And here is the catch: Google's web.dev guidance is blunt that CSS is render-blocking, the browser will not show content until it has processed the stylesheet, because a rule further down the file could change everything above it. When a page feels sluggish, ask where the time goes. A heavy DOM (tens of thousands of nodes) and bloated CSS both slow the assembly before a single pixel appears, and that is a budget your engineers can measure and cut, not a law of nature.

The deeper leadership point is about where work happens. Client-side rendering, building the page in the browser with JavaScript, gives slick, app-like interactivity but pushes effort onto the user's device and can hurt first-load and search visibility. Server-side rendering does the assembly on your machines instead, and most modern stacks blend the two. You do not need to adjudicate the architecture, but you should know it is a real trade with real cost, and ask which side of the line your team chose.

Cookies: how a forgetful web remembers you

HTTP, the protocol the web runs on, is essentially stateless: each request arrives as if the server has never met you. Cookies are the patch. As defined in RFC 6265, the HTTP State Management Mechanism (IETF, April 2011), a server sends a small piece of data via a Set-Cookie header; the browser stores it and returns it on every subsequent request, letting the server "maintain a stateful session over the otherwise stateless HTTP protocol." That is how a site remembers you are logged in between clicks.

The attributes a cookie carries are squarely a security and privacy matter, so it is worth checking your team sets them on purpose rather than by default. Per the spec and MDN's guide to using cookies: HttpOnly hides a cookie from client-side JavaScript, so a cross-site scripting bug cannot steal a session token; Secure sends it only over encrypted HTTPS; and SameSite controls whether the cookie rides along on requests coming from other sites, the main browser-side defence against cross-site request forgery. Auth tokens should be HttpOnly, Secure, and SameSite by default. Where they are not, that is a vulnerability, not a preference.

An honest limitation, and the law. RFC 6265 is candid that cookies carry historical infelicities that degrade their security and privacy, the Secure attribute, for instance, protects confidentiality but not integrity, so an active network attacker can still overwrite a Secure cookie from an insecure channel. Cookies are also the backbone of third-party tracking, which is why they sit under privacy regimes such as the EU's ePrivacy rules and GDPR, and California's CCPA. The consent banner on every site is the visible edge of that law. As a rough rule, non-essential cookies generally need consent before they are set, and the specifics vary by jurisdiction, so treat cookie consent as a compliance question for qualified counsel in your markets, not a checkbox engineering bolts on at the end.

A worked example: the login that leaked

Picture a small SaaS team, call it Tasklet, adding a "remember me" feature. (Illustrative scenario; not a real company.) To save a round-trip, a developer decides to store the signed-in user's role in the browser: a cookie reading role=standard, plus a bit of JavaScript that hides the admin menu when the role is not admin. It ships. It demos beautifully.

Two weeks later, a curious user opens their browser's developer tools, finds the cookie, and edits role=standard to role=admin. The admin menu reappears, and because the server trusted the cookie too, the admin actions actually work. Nothing was "hacked." The user simply changed a value on a device they own, which is their absolute right, because the client side is theirs.

flowchart TD
  A(["User edits cookie
role=standard → role=admin"]) --> B{"Where is the
permission check?"} B -->|"Client trusts the cookie"| C(["Admin menu unlocks
privilege escalation"]) B -->|"Server re-checks on
every request"| D(["Edit is ignored
access denied"])
The same tampered cookie is harmless or catastrophic depending entirely on whether the server re-checks the permission. Leaders Loop

The fix is not a cleverer cookie. It is the rule this whole topic turns on: the client side is for convenience, the server side is for truth. By all means hide the admin menu in the browser for a tidy experience, but the server must independently verify, on every privileged request, that this user really is an admin, ignoring whatever the cookie claims. Hiding a button is user experience; enforcing a permission is security, and security can only live on a machine the user does not control. Tasklet's bug was treating a convenience as a control.

Frequently asked questions

Is client-side the same as front-end?

Nearly, and people use them interchangeably, but there is a shade of difference. "Client-side" describes where code runs, in the user's browser, as opposed to server-side. "Front-end" describes the discipline of building the user-facing interface, which is mostly client-side work but also involves talking to back-end services. In day-to-day conversation, treating them as synonyms will rarely get you into trouble.

If users can see and change client-side code, why ship logic there at all?

Because responsiveness lives there. Validating a form field, animating a menu, or updating a counter instantly, without a round-trip to the server, is what makes a web app feel like an app rather than a sequence of page reloads. The discipline is to put experience on the client and trust on the server. Client-side logic is real and valuable; it just cannot be the thing that protects you.

Are cookies being phased out?

Third-party cookies, the cross-site tracking kind, have been under sustained pressure from browsers and regulators, and the advertising industry is adapting. But first-party cookies, the ones that keep your own users logged in, are not going anywhere; they are fundamental to how sessions work. If someone says "cookies are dead," they almost always mean the third-party tracking variety, not the mechanism itself.

What is the difference between cookies and local storage?

Both store data in the browser, but cookies are automatically sent to the server on every request (which is what makes them useful for sessions, and a performance cost if overused), whereas local storage stays in the browser and is read only by your JavaScript. A rough rule: use cookies for things the server needs to see each request, such as a session token; use local storage for client-only state, such as a draft or a UI preference.

Do I, as a non-engineer, actually need to know this?

You need the boundary, not the syntax. Knowing that the client side is untrusted, that secrets cannot live there, and that "we'll check it in the browser" is not a security control will let you ask the one question that catches a surprising number of real incidents before they ship: "And where does the server verify that?"

Related in the Toolkit

The client side is one half of a pair: it makes far more sense once you can see the whole request travel from a browser to a server and back (how the web works), and once you know what waits on the other side to be the source of truth (server-side).

Where to go next