Understanding the @property Variable in CSS

Gagan BNGagan BN
4 min read

We all know about variables in CSS, which are similar to JavaScript variables in that they store values. However, do you know the downside of CSS variables? It’s the inheritance nature of CSS variables. While inheritance has its advantages, it also comes with some disadvantages.

Advantages:

One of the key advantages is the ability to inherit the value of the nearest defined variable.

Consider the following HTML structure:

<div class="parent">
  <div class="child">
    <div class="grandChild"></div>
  </div>
</div>

And the CSS:

.parent {
    --store: 50deg;
}

.grandChild {
    width: 50px;
    height: 50px;
    margin-top: 5rem;
    background-color: #522add;
    transform: rotate(var(--store));
}

In this case, the --store variable will inherit its value from the parent element, and the grandChild element will be rotated by 50 degrees as expected.

Disadvantages:

Now, consider you are collaborating with others on a large project with extensive CSS. Someone might add a variable with the same name but store a different type of value, such as a hex code for a color. This would break the rotate CSS of the grandChild since --store would no longer be a degree value but a color. As a result, the grandChild element would not rotate.

.parent {
    --store: 50deg;
}

.child {
    --store: #552ADD;
}

.grandChild {
    width: 50px;
    height: 50px;
    margin-top: 5rem;
    background-color: #522add;
    transform: rotate(var(--store));
}

This issue can be avoided by using the @property rule. Let’s see how:

@property --store {
    syntax: "<angle>";
    inherits: true;
    initial-value: 0deg;
}

.parent {
    --store: 50deg;
}

.child {
    --store: #552ADD;
}

.grandChild {
    width: 50px;
    height: 50px;
    margin-top: 5rem;
    background-color: #552ADD;
    transform: rotate(var(--store));
}

When declaring the --store property with @property, we set the syntax to "<angle>", meaning it can only accept angle values. The inherits property is set to true, allowing it to inherit the nearest declared --store value. We also provide an initial-value of 0deg.

Now, even though we have set --store inside .parent to 50deg and --store inside .child to #552ADD, the rotate CSS of grandChild will not break. This is because we have defined that --store should only accept angle values, so the grandChild will still rotate by 50 degrees. Even though inherits is set to true, the invalid value in .child will be ignored.

Note: If you don’t want the variable to inherit the nearest declared value, you can set the inherits property to false. This ensures that even if you update the value in a nearby redeclared variable, the @property variable will still retain the initial value declared during initialization.

Another Scenario:

Let’s consider another scenario. Imagine you have the following HTML structure:

<div class="parent">
    <div class="child">
      <div class="grandChild">
        <div style="border: 1px solid white"></div>
      </div>
    </div>
  </div>

And you want to animate the rotation of the grandChild element with the following CSS:

.parent {
    --store: 0deg;
}

.child {}

.grandChild {
    width: 50px;
    height: 50px;
    background-color: #552ADD;
    animation: spin linear 3s infinite;
    transform: rotate(var(--store));
}

@keyframes spin {
    0% {
        --store: 0deg;
    }

    20% {
        --store: 72deg;
    }

    40% {
        --store: 144deg;
    }

    60% {
        --store: 216deg;
    }

    80% {
        --store: 288deg;
    }

    100% {
        --store: 360deg;
    }
}

The problem here is that the element will not rotate smoothly; it will jump from 0 to 72, 72 to 144, and so on.

However, when you use the @property variable, the element will rotate smoothly, like this:

@property --store {
    syntax: "<angle>";
    inherits: true;
    initial-value: 0deg;
}

.parent {
    --store: 0deg;
}

.child {}

.grandChild {
    width: 50px;
    height: 50px;
    background-color: #552ADD;
    animation: spin linear 3s infinite;
    transform: rotate(var(--store));
}

@keyframes spin {
    0% {
        --store: 0deg;
    }

    20% {
        --store: 72deg;
    }

    40% {
        --store: 144deg;
    }

    60% {
        --store: 216deg;
    }

    80% {
        --store: 288deg;
    }

    100% {
        --store: 360deg;
    }
}

What happens when you use the @property variable is that, because we defined the syntax as "<angle>", the value of the --store variable will not jump directly from 0 to 72, 72 to 144, and so on. Instead, it will gradually increment (e.g., 0, 1, 2, 3, up to 360 degrees), creating a smooth, transition-like effect during the rotation. The grandChild element will rotate smoothly as a result.

Summary

  • CSS Variables and Inheritance:

    • CSS variables, like JavaScript variables, store values.

    • Inheritance allows CSS variables to inherit values from parent elements.

    • While useful, inheritance can cause issues in large projects if variables with the same name are reused for different purposes.

  • Limitations:

    • If a variable like --store is redefined with a different value type (e.g., a color instead of a degree), it can break the intended functionality.
  • Using @property:

    • The @property rule allows you to define the expected syntax, inheritance behavior, and initial value of a CSS variable.

    • This ensures that variables maintain their intended purpose, preventing issues like incorrect value types.

  • Practical Example:

    • The blog demonstrates how using @property can prevent issues with variable inheritance and improve the smoothness of CSS animations, ensuring transitions are gradual rather than abrupt.
  • Additional Note:

    • If you don’t want the variable to inherit the nearest declared value, you can set the inherits property to false. This ensures that the variable retains its initial value even if it is redeclared nearby.
0
Subscribe to my newsletter

Read articles from Gagan BN directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Gagan BN
Gagan BN