Featured image for TWIL blog post illustrating dynamic CSS property updates with HTML integration for smarter, JavaScript-free styling.

Dive into the latest edition of TWIL, our weekly series that sparks curiosity and fosters continuous learning in the realm of software development. In this installment, Katie demystifies Dynamic Values with Inline Custom Properties in CSS. Witness how CSS var() function advancements create a new realm of possibilities, enabling effortless thematic changes and innovative styling without the heavy lifting of JavaScript. From tooltips with pseudo-elements to vibrant, customized progress bars, learn how HTML attributes and CSS custom properties team up to elegantly code an array of web components.

Dynamic Values with Inline Custom Properties in CSS

I’ve been trying for years to do clever things with the CSS attr() function and being able to dynamically set styling values via HTML attributes (without having to manipulate styles with JavaScript), but it has not been able to accomplish everything I’ve wanted it to do. I’ve recently learned that what I could not do with attr(), I can instead achieve with the clever use of CSS’s var() function instead.

attr()

This function can be really handy for pulling content from an element to use in it’s styling; something like a tooltip or popover that uses ::before or ::after pseudo-elements, for example, can pull the content value directly from the element if it’s defined there. For example:

<span class="list-title" separator=":">Here follows a list of things</span>

<!-- Or, a more useful example using Rails/ERB and variable content: -->
<span class="list-title" separator="<%= preferences.separator %>">
	<%= list.title %>
</span>
.list-title::after {
  content: attr(separator); 
  /* You can concat this too, like so:
  content: 'Hot Tip: ' attr(tooltip) ' ...JSYK';
  */
}

This, however, only works with strings. If you wanted to pull a value for use with a numeric property, for example (e.g., to dynamically set element widths for a horizontal proportion/progress bar), attr() won’t work for that.

var()

This function allows you to use custom properties, like variables, in your style definitions.

:root {
  --thing-color: #BADA55;
  --thing-size: 10em;
}

.thing {
  color: var(--thing-color);
  font-size: var(--thing-size);
}

The Special Sauce

While this has obvious benefits in use cases such as defining themes or simply reducing duplication, I had not considered the ability to define custom properties outside my CSS to then use within my CSS.

A quick example with simple progress bars:

<div class="progress-bar" style="--progress: 50%; --bar-color: #BADA55;"></div>
<div class="progress-bar" style="--progress: 25%; --bar-color: red;"></div>
<div class="progress-bar" style="--progress: 75%; --bar-color: green;"></div>
.progress-bar {
  height: 20px;
  background-color: var(--bar-color);
  width: var(--progress);
}

Approach one, where progress bar is set with both background color and width %

.progress-bar {
  background-color: var(--bar-color);
  height: 20px;
  position: relative;
  width: 100%;
}
.progress-bar::after {
  background-color: white;
  content: "";
  display: block;
  height: 100%;
  position: absolute;
  right: 0;
  width: calc(100% - var(--progress));
}

Approach two, a more complex (if admittedly contrived) setup to demonstrate that we can still use var() within calc() (etc.) to set the progress bar to the full color, then essentially cover the remaining % with an ::after pseudo-element.

With just the HTML and CSS (either approach produces the same visual result) above, we get this. I should have set a border or background on the whole body to prove that the bottom bar is 75% wide, but here we are.

Resources

  • CSS
  • HTML
Katie Linero's profile picture
Katie Linero

Senior Software Engineer

Related Posts

AWS logo centered over dark blue stylized map of Europe with concentric radar-style rings emanating from Germany, representing the AWS European Sovereign Cloud infrastructure launch for EU data sovereignty and GDPR compliance
January 26, 2026 • Frank Valcarcel

AWS Launches European Sovereign Cloud

AWS launched a physically separate cloud infrastructure in Europe with EU-only governance, zero US dependencies, and over 90 services. Here is what organizations in healthcare, finance, and government need to know about the sovereign cloud and how to evaluate it for their compliance strategy.

Code snippet showing a Python Pydantic MovieReview model with typed fields (title, rating, summary, pros, cons) and OpenAI's response_format parameter for structured outputs, syntax highlighted on a dark editor background
November 12, 2025 • Frank Valcarcel

How to Get Guaranteed JSON from LLMs with Structured Outputs

Tired of parsing flaky JSON from LLM responses? OpenAI’s Structured Outputs feature guarantees your responses match your schema exactly. Here’s how to use it with Pydantic, when to choose it over function calling, and the gotchas you’ll encounter in production.

Let's work together

Tell us about your project and how Cuttlesoft can help. Schedule a consultation with one of our experts today.

Contact Us