2

I'd like to use CSS variables to define some compound properties. In this example, I'm defining a --border-width variable as well as a --border variable that uses the --border-width variable. I then override the border width value on certain elements:

:root {
  --border-width: 1px;
  --border: var(--border-width) solid #000;
}

.demo {
  border: var(--border);
  margin: 1em;
}

.demo.active {
  --border-width: 10px; 
}
<div class="demo">
  Should have thin border
</div>

<div class="demo active">
  Should have thick border
</div>

I know that the new variable is being applied to the element; I can see it in the inspector and if I add an explicit border-width: var(--border-width) property to .demo, it applies the expected width value. However, it appears that --border hasn't been recalculated to reflect the change to --border-width.

I don't want each of my elements to have to specify all of the longhand properties each time I use something with a shorthand, but it seems like I'm missing something fundamental about CSS variables.

For example, I'd prefer to not have to do this:

:root {
  --border-width: 1px;
  --border-style: solid;
  --border-color: #000;
}

.demo {
  border: var(--border-width) var(--border-style) var(--border-color);
  
  /*
  Or this
  
  border-width: var(--border-width);
  border-style: var(--border-style);
  border-color: var(--border-color);
  */
  
  margin: 1em;
}

.demo.active {
  --border-width: 10px; 
}
<div class="demo">
  Should have thin border
</div>

<div class="demo active">
  Should have thick border
</div>

1
  • 1
    @TemaniAfif thanks! I knew had to have been asked before, but all of my searching wasn't pulling any duplicates. Commented Aug 27, 2019 at 12:18

1 Answer 1

0

You are correct in that your .demo element has the border applied to it that you set in :root. However, in your .demo.active you merely redefine the CSS variable --border-width (as opposed to making use of the existing one). You need to explicitly use the property border-width.

In order to reuse the --border-width variable in .demo.active, you'd again be looking to make use of var() with border-width: var(--border-width):

:root {
  --border-width: 1px;
  --border: var(--border-width) solid #000;
}

.demo {
  border: var(--border);
  margin: 1em;
}

.demo.active {
  border-width: var(--border-width);
}
<div class="demo">
  Should have thin border
</div>

<div class="demo active">
  Should have thick border
</div>

Note that this will copy what was set in the :root --border-width. However, given that you also mention that you're expecting a thicker border, what I suspect you may actually be looking for is to either override this setting (with border-width: 10px), or extend it to also include an addition, with
border-width: var(--border-width) + 10px:

:root {
  --border-width: 1px;
  --border: var(--border-width) solid #000;
}

.demo {
  border: var(--border);
  margin: 1em;
}

.demo.active {
  border-width: var(--border-width) + 10px;
}
<div class="demo">
  Should have thin border
</div>

<div class="demo active">
  Should have thick border
</div>

Sign up to request clarification or add additional context in comments.

5 Comments

you merely redefine the CSS variable --border-width — yes, my intention is that by doing so, the --border variable reflects the change, then border: var(--border) would as well. Effectively, I'm looking for a cascade of the variables.
In the OP, I stated if I add an explicit border-width: var(--border-width) property, but followed that up with I don't want each of my elements to have to specify all of the longhand properties; are you stating that what I want is impossible?
Essentially yes, I believe so -- something would still need to make use of that -border-width variable, with var(). If you're looking to apply a variable hierarchy in that regard, you're better off looking into a pre-processor like LESS, and nesting selectors (possibly making use of the parent selector &). I don't think that can be done in pure CSS, as the pre-processor simply compiles out each of the variables like above.
I don't follow your first example, as it doesn't have a think border in either case. Please also see my update where I show a way to make it work by only defining an updated variable.
imho the first example is missing --border-width: 10px;, but essentially, yes, you would have to redefine --border for it to take the new --border-width into account. That makes sense tho. If .active was capable of changing the :root --border, then both the active and non active elements would have a thick border. It's more a question of scope than "cascade".

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.