I have been unable to determine from any explanation I could find, about when the value of a css variable has been set., so I conducted a test
The html
<div class="flex">
<div class="item1">
<div class="included"></div>
</div>
<div class="item2">
<div class="included"></div>
</div>
</div>
the css
:host {
display: grid;
place-content: center;
}
.flex {
display: flex;
flex-direction: column;
width:800px;
height: 300px;
background-color: blue;
--div-width: calc(100% - 10px);
}
.item1, .item2 {
background-color: green;
height: 100px;
}
.item1 {
width: 80%;
}
.item2 {
width: 40%;
}
.included {
width: var(--div-width);
background-color:yellow;
height:50px;
}
Note I did the test inside a custom element, hence the :host
descriptor, however that is not important.
What is important is that I set --div-width
as calc(100% - 10px)
inside a div that is 800px wide - so would expect it to have a value of 790px.
However , from this screenshot
It is obvious that the calculation is being delayed until the place where the variable is used. as the yellow boxes are just 10px short of the surrounding element.
Is there a way of telling css to set the value of the property as the element where it is declared, rather than where it is used.
I did try and proxy the variable - like this ...
--proxy-width: calc(100% - 10px);
--div-width: var(--proxy-width);
but it made no difference.
PS This is a trivial example, how I actually want to use it is to control the width of an item (a textarea) inside a custom element, dependent on the context, so I can make the element responsive to changes of the width of some outer container.
I found a "perfect" work around!.
The solution to this is to use the 'resize' event to read the size of the element you wanted use 100% on using getBoundingClientRect();
and using the width returned to set the "calc" as width px - whatever
So for the example in the question I would use this code
_resize() {
if (this.resizeInProgress) return;
this.resizeInProgress = true;
requestAnimationFrame(() => {
const container = this.shadowRoot.querySelector('.flex');
const bound = container.getBoundingClientRect();
container.style.setProperty('--div-width', `calc(${bound.width}px - 10px)`);
this.resizeInProgress = false;
});
}
I bind that function to this
in my custom element constructor
this.resizeInProgress = true;
this._resize = this._resize.bind(this);
and this in the connectedCallback
do
window.addEventListener('resize', this._resize);
this.resizeInProgress = false;
and in my disconnectedCallback
remove it again with
this.resizeInProgress = true;
window.removeEventListener('resize', this._resize);
I retain the calc()
function because in my real life cases as the amount subtracted is in "em" units