cf.completefrontendCode editorOpen lab
THE JAVASCRIPT FIELD GUIDE

Styles & classes

Change how elements look from JavaScript with className, classList, inline styles, getComputedStyle, and CSS custom properties.

By the end you can
  • 01
    Choose the right toolUse classes for designed states, inline styles for truly dynamic values, and custom properties to feed values into CSS.
  • 02
    Edit classes safelyCompare className with the live classList token API, including toggle(token, force) and replace.
  • 03
    Read the final resultUse getComputedStyle for resolved values while avoiding style recalculation in tight loops.

JavaScript chooses the look; CSS designs it

A DOM element can look different because of stylesheet rules, classes, inline styles, inherited values, browser defaults, and custom properties. JavaScript can touch all of those, but the professional skill is choosing the smallest tool that says what you mean.

The rule of thumb is simple: classes for designed states, inline styles for live numbers, and custom properties when CSS should still do the drawing. Then use getComputedStyle when you need to inspect the final result the browser actually resolved.

The short version

CSS describes the outfits. JavaScript chooses which outfit an element wears, paints a few truly dynamic details when needed, and can take a computed-style photo of the final look.

Real-life analogyClasses are outfits in a wardrobe

Think of a button as a person and your stylesheet as a wardrobe. JavaScript should usually not sew a new outfit in the moment. It should say “wear the selected outfit now” by changing a class.

In real life: A wardrobe full of named outfits
In JavaScript: CSS classes such as is-open, warning, and theme-dark
In real life: Choosing the raincoat today
In JavaScript: JavaScript adds a class with classList.add()
In real life: Taking off the hoodie
In JavaScript: JavaScript removes a class with classList.remove()
In real life: The tailor who designed the clothes
In JavaScript: Your CSS stylesheet

Where the analogy stops: A person can mix clothes however they like. CSS still follows the cascade: specificity, order, inheritance, and inline styles can change which rule wins.

In this lesson you will operate a real classList, explore the traps of el.style, read resolved values with getComputedStyle, and use a custom property as a tiny theme thermostat.

className and classList

INTERACTIVE

className is the class attribute as one string. It has this name because class was already a reserved word in JavaScript. Setting it is sometimes useful when you want to replace the whole class attribute at once:

Replace every classPop out in the code editor (opens in a new tab)JavaScript
button.className = "button button--primary is-loading";
console.log(button.className);

Most day-to-day UI changes are better with classList. It is a live DOMTokenList: a token API connected to the element’s real attribute. It prevents duplicate class tokens and gives you purpose-built methods.

Replay class tokens
Step 0 of 9Ready
Your turn: follow the blue line

Step through a tiny model of classList. The real playground below uses actual DOM methods; this replay makes the token changes visible.

Running in
  1. script
Next: line 1
Click the blue line to take the next stepPop out in the code editor (opens in a new tab)JavaScript
const classes = new Set(card.className.split(" "));classes.add("is-raised");card.classList.toggle("is-selected");classes.delete("card");const replaced = classes.delete("is-raised");if (replaced) classes.add("is-flat");card.className = [...classes].join(" ");console.log(card.className);
CallStoreChangeResultRun = next line. Ran = already executed.
Recent returnsNothing yet. Start with the blue line.
Switch line 4 and watch the returned state

The replay is a plain model so tests can prove it without a DOM. The next playground uses a real element.

A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

Now try the same operations on a real element. Notice the return values: toggle(token, force) returns the resulting state, and replace(oldToken, newToken) returns a boolean telling you whether the old token existed.

classList console
Try these classList operationsPop out in the code editor (opens in a new tab)JavaScript
const card = document.querySelector(".demo-card");card.classList.add("is-raised");card.classList.remove("is-muted");card.classList.toggle("is-selected");card.classList.toggle("is-selected", true);card.classList.replace("is-raised", "is-flat");card.classList.contains("is-selected");console.log(card.className);
Live elementDOMTokenList
classNamedemo-card is-muted
classListdemo-card | is-muted
Try it yourself

Ready. Pick an operation.

The card lives inside an empty ref container. React renders the shell; DOM methods own the demo node.
Class tokens are not a CSS parser

classList.add("one two") is invalid because a token cannot contain spaces. Add two tokens as add("one", "two") or call add twice.

The style property

LAB

el.style reflects only the inline style attribute. If a class makes text purple, el.style.color is still an empty string until JavaScript or HTML sets an inline color.

Real-life analogyInline style is paint on the person

Inline style is like painting one detail directly onto the person. It is perfect for a one-off dynamic number, such as a progress bar width or pointer coordinate, but clumsy for a complete state like “error”, “selected”, or “dark theme”.

In real life: Paint brushed directly on a jacket
In JavaScript: An inline declaration such as style.backgroundColor = 'gold'
In real life: Harder to reuse than a wardrobe outfit
In JavaScript: Harder to share than a CSS class
In real life: Very specific paint wins over normal clothes
In JavaScript: Inline style beats normal stylesheet declarations

Where the analogy stops: Important stylesheet rules and the cascade still exist. Also, inline style only stores declarations you put on the element; it is not the whole final look.

Dot form uses camelCase property names such as backgroundColor. The generic methods use CSS spelling such as background-color. Invalid values are silently ignored, and many lengths need a unit.

Inline style playground
Inline style operationsPop out in the code editor (opens in a new tab)JavaScript
const box = document.querySelector(".box");box.classList.add("warning");console.log(box.style.color);box.style.backgroundColor = "gold";box.style.setProperty("border-radius", "16px");box.style.width = 100;console.log(box.style.width);box.style.width = "100px";box.style.removeProperty("background-color");
Live style attributeinline only
style attribute
Try it yourself

The box starts with its color from a class, not inline style.

Invalid CSS values are silently ignored by the browser. This is why the unitless width step appears to do nothing.
Choosing between classList and style
ToolBest forAvoid when
classListNamed states designed in CSS: open, active, error, darkThe value changes continuously, like mouse position
style.propertyKnown CSS properties with JavaScript camelCase namesYou need a custom property or a kebab-case name
style.setProperty()Kebab-case properties, custom properties, and optional important priorityA class would express the state more clearly
style.cssTextReplacing the whole inline style string intentionallyYou only meant to update one property

getComputedStyle

BROWSER PHOTO

If el.style is the inline paint can, getComputedStyle is a photo of the final look after the cascade, inheritance, default styles, and layout-dependent values have been considered. The values are resolved: colors often appear as rgb(...), and many lengths appear in pixels.

Real-life analogyComputed style is a photo of the final outfit

When you need to know what the browser ended up using, take a computed-style photo. It is especially handy when a value came from a class or inherited rule rather than inline style.

In real life: A photo after everyone got dressed
In JavaScript: getComputedStyle(element)
In real life: The photo shows the visible result
In JavaScript: Resolved values after rules, inheritance, and defaults
In real life: You cannot edit the clothes by drawing on the photo
In JavaScript: The returned style declaration is read-only for changing the page

Where the analogy stops: The photo comparison sounds frozen, but browsers may expose a live object. Treat it as something to read, not mutate, and do not call it repeatedly in tight loops.

getComputedStyle photo booth
Read resolved stylesPop out in the code editor (opens in a new tab)JavaScript
const computed = getComputedStyle(box);console.log(computed.color);console.log(computed.width);console.log(box.style.color);console.log(getComputedStyle(box, "::before").content);
Resolved readingsbrowser

No computed photo yet.

Try it yourself

Read computed values after the demo element exists in the browser.

getComputedStyle may force the browser to recalculate styles. Read once, store what you need, and avoid tight loops.

The second argument reads pseudo-elements, so getComputedStyle(el, "::before") can inspect generated content. Because a computed-style read can force style recalculation, read once outside hot animation loops and reuse the values you need.

CSS custom properties

THEME LAB

CSS custom properties are names like --hue or --brand-color. They are case-sensitive, inherit like normal CSS properties, and must be changed with setProperty and read with getPropertyValue. Dot notation cannot spell a property that starts with --.

Real-life analogyA custom property is a thermostat setting

Instead of JavaScript painting every border, background, and badge, set one custom property and let CSS do the design work. This keeps dynamic data in JavaScript and visual relationships in CSS.

In real life: One thermostat setting
In JavaScript: One value such as --hue: 210
In real life: Every room reads that setting
In JavaScript: Many CSS rules use the same custom property
In real life: Turn the dial once
In JavaScript: JavaScript calls style.setProperty('--hue', value)

Where the analogy stops: A thermostat controls temperature, while a custom property is just text until a CSS property uses it. Invalid final CSS can still be ignored.

CSS custom property thermostat
Custom property patternCSS + JavaScript
:root { --hue: 210; }.card { background: hsl(var(--hue) 80% 45%); } panel.style.setProperty("--hue", "320");const hue = getComputedStyle(panel).getPropertyValue("--hue").trim();
Sandboxed themeday
parent preview hue210
Try it yourself

The frame reads back --hue as waiting. The parent changed one value; several CSS rules reacted.

The iframe is sandboxed and contains its own demo CSS, including a matching custom-property declaration for every use. Newer browsers also support typed custom properties with @property and CSS.registerProperty.

You can also pass a third argument to setProperty, such as setProperty("color", "red", "important"), but use it sparingly. Newer browsers support typed, animatable custom properties with @property in CSS and CSS.registerProperty in JavaScript; feature-detect before depending on them.

Where you will use this

SORT IT

Real interfaces mix all three tools. A menu opens with a class, a dragged panel follows the pointer with a transform, and a theme editor passes one chosen hue into CSS as a custom property.

classList, style, or custom property?
  • Toggle dark mode for a whole page
  • Move a tooltip to follow the mouse
  • Let a user pick a brand hue
  • Hide or show a completed item
  • Set a progress bar to exactly 63%
  • Make every card read --hue
Try it yourself
0 of 6 correct

Choose the tool that communicates the job most clearly. Some real apps could use more than one; pick the best default.

Choose a category for every card. You can change an answer at any time; Reset clears them all.
A realistic patternPop out in the code editor (opens in a new tab)JavaScript
function updateMenu(menu, open) {
  menu.classList.toggle("is-open", open);
}

function moveTooltip(tooltip, x, y) {
  tooltip.style.transform = `translate(${x}px, ${y}px)`;
}

function setBrandHue(panel, hue) {
  panel.style.setProperty("--brand-hue", String(hue));
}

Common misconceptions

  • “className and classList are separate storage.” They are two views of the same class attribute. Change one and the other reflects it.
  • “el.style.color tells me the visible color.” It only tells you the inline color. Use getComputedStyle(el).color for the resolved color.
  • “JavaScript adds units for CSS lengths.” It does not. width = 100 is ignored by a real DOM style declaration; use "100px" or another valid unit.
  • “Inline style is always bad.” It is bad for reusable states, but excellent for real-time values such as coordinates, measured sizes, and progress numbers.
  • “Computed styles are free to read anywhere.” They can force style recalculation, so avoid reading them in tight loops.
  • “Custom properties are JavaScript variables.” They are CSS properties. JavaScript can set them, but CSS decides how they affect the final property values.
What each style API reads or writes
APIReadsWritesReturn values to remember
classNameThe whole class attribute stringThe whole class attribute stringA string
classListLive class tokensIndividual tokenstoggle returns state; replace returns success
el.styleInline declarations onlyInline declarations onlyInvalid values are ignored
getComputedStyleResolved final valuesNothing usefulRead-only resolved strings
Custom propertiesThrough computed style or inline styleUse setPropertyNames are case-sensitive

Practice exercises

5 EXERCISES
Exercise 1 · Warm-upPredict the class string

Type exactly what the final console.log prints.

Starter codePop out in the code editor (opens in a new tab)JavaScript
const el = { className: "card active" };
const list = new Set(el.className.split(" "));
list.delete("active");
list.add("hidden");
el.className = [...list].join(" ");
console.log(el.className);

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · PracticeFind the inline-style misconception

    Why can the visible text have a color while this code still prints true?

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    const el = { style: { color: "" } };
    console.log(el.style.color === "");

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeWrite a safe active-state helper

      Write the body for a helper that makes a tab active or inactive without branching.

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      function setActive(tab, active) {
        // Add or remove the "is-active" class based on active.
      }
        Exercise 4 · PracticeWhat does a failed replace report?

        Predict the boolean.

        Starter codePop out in the code editor (opens in a new tab)JavaScript
        const classes = new Set(["card", "raised"]);
        const replaced = classes.delete("missing");
        if (replaced) classes.add("flat");
        console.log(replaced);

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengeTheme a card with one custom property

          Write the line that passes the number into CSS.

          Starter codePop out in the code editor (opens in a new tab)JavaScript
          const card = document.querySelector(".card");
          const hue = 280;
          // Set --brand-hue on card so CSS can use it.

            Check your understanding

            7 QUESTIONS
            Styles & classes quiz · 7 questionsScore: first tries count
            1. Question 1 of 7When should you prefer classList.add('is-open') over several inline style assignments?

              Choose an answer to see the explanation.

            2. Question 2 of 7What does el.style.color read?

              Choose an answer to see the explanation.

            3. Question 3 of 7What does this class-token model print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const classes = new Set(["card"]);
              const result = classes.has("active") ? false : (classes.add("active"), true);
              console.log(result);
              console.log([...classes].join(" "));

              Choose an answer to see the explanation.

            4. Question 4 of 7Which method is required for CSS custom properties such as --brand-hue?

              Choose an answer to see the explanation.

            5. Question 5 of 7What does this plain object width example print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const style = { width: "" };
              style.width = String(100);
              console.log(style.width);

              Choose an answer to see the explanation.

            6. Question 6 of 7What is the best description of getComputedStyle(el)?

              Choose an answer to see the explanation.

            7. Question 7 of 7What does classList.replace('old', 'new') return when old is missing?

              Choose an answer to see the explanation.

            Key takeaways

            • className is the whole class attribute string; classList is the live token API.
            • toggle(token, force) returns the resulting state, and replace returns whether it succeeded.
            • el.style reads and writes inline declarations only; invalid CSS values are silently ignored.
            • getComputedStyle reads resolved final values and can trigger style recalculation.
            • Custom properties are the bridge for dynamic values that CSS should reuse.

            Final definition: styles and classes are the DOM tools for choosing designed states, applying dynamic inline values, and reading the browser’s final visual result.

            Up next: Sizes, scrolling & coordinates.

            CompleteFrontend Clear concepts. Working examples.