← Back to series

Frontend Development Fundamentals, Part 1: The Building Blocks

By Devsantara Team ·

HTML, CSS, and JavaScript still do the same three jobs they always have — this post looks at what's changed about how we write them, and what hasn't.

  • frontend
  • html
  • css
  • javascript

Every frontend, no matter how much tooling sits in front of it, still compiles down to three things: structure, presentation, and behavior. HTML, CSS, and JavaScript haven't been replaced — they've just picked up a lot more capability than they had a decade ago.

HTML: structure with more built-in meaning

Semantic elements (<article>, <nav>, <dialog>, <details>) aren't just for search engines anymore — screen readers, browser devtools, and even styling hooks all lean on them. A <dialog> element today gets you a focus-trapped, top-layer modal with zero JavaScript. A <details> element gets you a disclosure widget for free.

The lesson: reach for the native element before reaching for a <div> and a library. Less markup, less JavaScript, better accessibility by default.

CSS: layout stopped being a workaround

For years, centering something vertically was the industry's running joke. Flexbox and Grid ended that. What's newer is how much CSS now handles that used to require JavaScript:

  • Container queries — components that respond to the size of their container, not the viewport
  • :has() — a parent selector, letting CSS react to what's inside an element
  • Scroll-driven animations — animations tied to scroll position without a scroll event listener
  • Nesting — native CSS nesting, no preprocessor required
.card:has(img) {
  grid-template-rows: auto 1fr;
}

@container (min-width: 400px) {
  .card {
    grid-template-columns: 120px 1fr;
  }
}

None of this replaces a design system, but it does mean less CSS is spent working around the language and more is spent expressing the design.

JavaScript: the platform absorbed the utility layer

A lot of what used to require a library is now a language or platform feature: fetch, Array.prototype.flatMap, optional chaining, top-level await, structured cloning. The practical effect is a smaller dependency tree for the same functionality — worth auditing for in any codebase that's a few years old.

const response = await fetch('/api/posts');
const posts = await response.json();
const tags = posts.flatMap((post) => post.tags);

Where this leaves the stack

None of this argues against frameworks — component models, reactivity, and routing still solve real problems that raw HTML/CSS/JS don't. But the gap between "vanilla" and "framework" is narrower than it used to be, and knowing where the platform now carries its own weight changes what you should build versus install.

Part 2 picks up from here: how these building blocks get organized into components, and where that organization starts to look like an application.