Search code examples
csscss-variablescss-calc

How can I assign the value of a css calc() to a css-variable, and not have it delay the calculation until the css-variable is used


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 enter image description here

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.


Solution

  • 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