Search code examples
css-gridsveltecss-variables

How to reactively change css variables in svelte?


I want to display objects like this one inside a css grid:

let data = {
    rows: [
      {
        id: "id",
        col1: "value1",
        col2: "value2",
      },
      {
        id: "id",
        col1: "value1",
        col2: "value2",
      },
    ],
  };

For this object the result should be this:

enter image description here

My problem is that the object can change and can contain a different number of columns each time.

Currently I am using:

<script>
  export let data = {
    rows: [
      {
        id: "id",
        col1: "value1",
        col2: "value2",
      },
      {
        id: "id",
        col1: "value1",
        col2: "value2",
      },
    ],
  };

  function changeCols() {
    let r = document.querySelector(":root");
    r.style.setProperty("--columns", "4");
  }
</script>

<!-- A css grid to display sql data -->
<div class="grid">
  {#each data.rows as row}
    {#each Object.keys(row) as key}
      <div class="cell">{row[key]}</div>
    {/each}
  {/each}
</div>

<button on:click={changeCols}>make 4</button>

<style>
  :root {
    --columns: 3;
  }

  .grid {
    display: grid;
    grid-template-columns: repeat(var(--columns), 1fr);
    grid-gap: 1rem;
    padding: 1rem;
  }

  .cell {
    padding: 1rem;
    border: 1px solid #ccc;
    border-radius: 4px;
  }
</style>

changeCols() throws an error saying that the style property doesn't exist. Why is this?

What would be a solution to this problem where I can easily change several css properties inside my javascript?


Solution

  • The variable doesn't have to be set on the :root element, here it can be set on the grid element (style:property) and the number of columns automatically determined based on the data with a reactive variable

    REPL

    <script>
        export let data = {
            rows: [
                {
                    id: "id",
                    col1: "value1",
                    col2: "value2",
                },
                {
                    id: "id",
                    col1: "value1",
                    col2: "value2",
                },
            ],
        };
    
        $: cols = Object.entries(data.rows[0] ?? {}).length
    
        function changeCols() {
            data.rows.forEach(row => row['col3'] = 'value3') 
            data = data
        }
    
    </script>
    
    <div class="grid" style:--columns={cols}>
        {#each data.rows as row}
        {#each Object.keys(row) as key}
        <div class="cell">{row[key]}</div>
        {/each}
        {/each}
    </div>
    
    <button on:click={changeCols}>make 4</button>
    
    <style>
        .grid {
            display: grid;
            grid-template-columns: repeat(var(--columns), 1fr);
            grid-gap: 1rem;
            padding: 1rem;
        }
    
        .cell {
            padding: 1rem;
            border: 1px solid #ccc;
            border-radius: 4px;
        }
    </style>