CSS variables defaults: set if not already set

Declare default values in :root, then override in selectors.

:root {
  --primary-color: red;
}

* {
  color: var(--primary-color);
  border: 1px solid var(--primary-color);
  padding: 0.25rem;
  margin: 0;
}

div {
  --primary-color: green;
}

p {
  --primary-color: blue;
}
<div>HI!</div>
&hellip;
<p>Bye!</p>

To complement the previous answers, there might be a case where you don't want to declare your variables in the global :root scope. For example, when you're creating a re-usable component, you want to declare its styles locally, without depending on the global project styles. Especially if you're building a library for other developers.

In that case, the solution is to expose one variable name to the "outer world", and use a different variable name inside of the component. The component container should just map the optional external variable to the inner variable, and set its default value:

.my-component-container {
  /* map optional "external" variables to required "internal" variables */
  --my-variable-inner: var(--my-variable, blue);
}

.my-component-container .my-nested-element {
  color: var(--my-variable-inner);
}

.my-component-container .my-other-nested-element {
  border-color: var(--my-variable-inner);
}

This way you can ensure that --my-variable-inner is always defined in the component, and make it optional for the external consumers to define --my-variable.

The downside is that you need to remember two variable names instead of one. But here you can think of some project-wide convention, e.g. add --inner or some other suffix to each variable like that.