learn.colinkim.dev

CSS for HTML and JavaScript integration

Learn how CSS fits with semantic HTML, class hooks, data attributes, ARIA state, DOM changes, and when JavaScript should own behavior.

CSS works best when HTML and JavaScript keep their own responsibilities clear.

HTML provides meaningful structure:

<button class="menu-button" type="button" aria-expanded="false">
  Menu
</button>

CSS styles the state:

.menu-button[aria-expanded="true"] {
  background: royalblue;
  color: white;
}

JavaScript changes the state when behavior happens.

Use semantic HTML examples

Good CSS starts with good markup:

<article class="course-card">
  <h2><a href="/courses/css/">CSS</a></h2>
  <p>Learn selectors, cascade, layout, and responsive design.</p>
</article>

CSS can style that without changing the meaning:

.course-card {
  padding: 1rem;
  border: 1px solid var(--border);
  border-radius: 0.5rem;
}

.course-card:has(:focus-visible) {
  outline: 3px solid var(--focus);
  outline-offset: 4px;
}

Classes for styling, data attributes for state

Classes are good for component identity:

<button class="tab">Overview</button>

Data attributes are useful for JavaScript-managed state:

<button class="tab" data-state="active">Overview</button>
.tab[data-state="active"] {
  border-block-end-color: currentColor;
  font-weight: 700;
}

If an ARIA attribute already represents real accessibility state, style that instead:

.accordion-button[aria-expanded="true"] {
  color: var(--accent);
}

Do not add ARIA only for styling. ARIA must remain truthful.

CSS can handle many states alone

CSS can style:

  • hover
  • focus
  • active
  • disabled
  • checked
  • invalid
  • current page
  • open popovers and dialogs
  • reduced motion
  • dark mode
  • container size

JavaScript is needed when state changes because of behavior, data, or application logic.

A menu boundary

CSS can style menu states:

.menu[hidden] {
  display: none;
}

.menu-button[aria-expanded="true"] + .menu {
  display: block;
}

JavaScript should toggle hidden and aria-expanded when the button is activated. CSS should not fake button behavior with non-semantic elements.

Inline styles from JavaScript

Sometimes JavaScript sets custom properties:

card.style.setProperty("--progress", `${progress}%`);

CSS consumes the value:

.progress-bar {
  inline-size: var(--progress, 0%);
}

This is cleaner than JavaScript writing many CSS properties directly.

What to carry forward

  • semantic HTML gives CSS stable, meaningful targets
  • classes identify components and styling roles
  • ARIA and data attributes can expose state to CSS
  • ARIA must remain truthful, not decorative
  • JavaScript should change state; CSS should present state
  • custom properties are a clean bridge from JavaScript values to CSS styling

The next lesson builds a responsive page from the pieces you have learned.

Progress

Quick checks

No quick checks in this lesson.

Mark lesson manually or answer quick checks to track progress.