{"id":384,"date":"2024-05-13T08:00:00","date_gmt":"2024-05-13T08:00:00","guid":{"rendered":"https:\/\/kerrprogroup.com\/?p=384"},"modified":"2025-03-19T12:20:50","modified_gmt":"2025-03-19T12:20:50","slug":"the-times-you-need-a-custom-property-instead-of-a-css-variable","status":"publish","type":"post","link":"https:\/\/kerrprogroup.com\/index.php\/2024\/05\/13\/the-times-you-need-a-custom-property-instead-of-a-css-variable\/","title":{"rendered":"The Times You Need A Custom @property Instead Of A CSS Variable"},"content":{"rendered":"

The Times You Need A Custom @property Instead Of A CSS Variable<\/title><\/p>\n<article>\n<header>\n<h1>The Times You Need A Custom @property Instead Of A CSS Variable<\/h1>\n<address>Preethi Sam<\/address>\n<p> 2024-05-13T08:00:00+00:00<br \/>\n 2025-03-19T12:04:52+00:00<br \/>\n <\/header>\n<p>We generally use a CSS variable as a placeholder for some value we plan to reuse \u2014 to avoid repeating the same value and to easily update that value across the board if it needs to be updated.<\/p>\n<pre><code class=\"language-css\">:root { \n --mix: color-mix(in srgb, #8A9B0F, #fff 25%);\n}\n\ndiv {\n box-shadow: 0 0 15px 25px var(--mix);\n}\n<\/code><\/pre>\n<p>We can register <em>custom<\/em> properties in CSS using <code>@property<\/code>. The most common example you\u2019ll likely find demonstrates how <code>@property<\/code> can <a href=\"https:\/\/css-tricks.com\/interpolating-numeric-css-variables\/\">animate the colors of a gradient<\/a>, something we\u2019re unable to do otherwise since a CSS variable is recognized as a string and what we need is a number format that can interpolate between two numeric values. That\u2019s where <code>@property<\/code> allows us to define not only the variable\u2019s <em>value<\/em> but its <em>syntax,<\/em> <em>initial value<\/em>, and <em>inheritance<\/em>, just like you\u2019ll find documented in CSS specifications.<\/p>\n<p>For example, here\u2019s how we register a custom property called <code>--circleSize<\/code>, which is formatted as a percentage value that is set to <code>10%<\/code> by default and is not inherited by child elements.<\/p>\n<pre><code class=\"language-css\">@property --circleSize {\n syntax: \"\";\n inherits: false;\n initial-value: 10%;\n}\n\ndiv { \/* red div *\/\n clip-path: circle(var(--circleSize) at center bottom);\n transition: --circleSize 300ms linear;\n}\n\nsection:hover div { \n --circleSize: 125%; \n}\n<\/code><\/pre>\n<p>In this example, a <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/basic-shape\/circle\"><code>circle()<\/code><\/a> function is used to clip the <code><\/p>\n<div><\/div>\n<p><\/code> element into \u2014 you guessed it \u2014 a circle. The size value of the <code>circle()<\/code>\u2019s radius is set to the registered custom property, <code>--circleSize<\/code>, which is then independently changed on hover using a <code>transition<\/code>. The result is something close to <a href=\"https:\/\/m3.material.io\/foundations\/interaction\/states\/applying-states#d8d475ac-672e-4692-ae60-eb557c4990bc\">Material Design\u2019s ripple effect<\/a>, and we can do it because we\u2019ve told CSS to treat the custom property as a percentage value rather than a string:<\/p>\n<figure class=\"break-out\">\n<p data-height=\"480\" data-theme-id=\"light\" data-slug-hash=\"PovwepK\" data-user=\"smashingmag\" data-default-tab=\"result\" class=\"codepen\">See the Pen [CSS @property [forked]](https:\/\/codepen.io\/smashingmag\/pen\/PovwepK) by <a href=\"https:\/\/codepen.io\/rpsthecoder\">Preethi Sam<\/a>.<\/p><figcaption>See the Pen <a href=\"https:\/\/codepen.io\/smashingmag\/pen\/PovwepK\">CSS @property [forked]<\/a> by <a href=\"https:\/\/codepen.io\/rpsthecoder\">Preethi Sam<\/a>.<\/figcaption><\/figure>\n<blockquote class=\"pull-quote\">\n<p>\n <a class=\"pull-quote__link\" aria-label=\"Share on Twitter\" href=\"https:\/\/twitter.com\/share?text=%0aThe%20freedom%20to%20define%20and%20spec%20our%20own%20CSS%20properties%20gives%20us%20new%20animating%20superpowers%20that%20were%20once%20only%20possible%20with%20JavaScript,%20like%20transitioning%20the%20colors%20of%20a%20gradient.%0a&url=https:\/\/smashingmagazine.com%2f2024%2f05%2ftimes-need-custom-property-instead-css-variable%2f\"><\/p>\n<p>The freedom to define and spec our own CSS properties gives us new animating superpowers that were once only possible with JavaScript, like transitioning the colors of a gradient.<\/p>\n<p> <\/a>\n <\/p>\n<div class=\"pull-quote__quotation\">\n<div class=\"pull-quote__bg\">\n <span class=\"pull-quote__symbol\">\u201c<\/span><\/div>\n<\/p><\/div>\n<\/blockquote>\n<p>Here\u2019s an idea I have that uses the same basic idea as the ripple, only it chains multiple custom properties together that are formatted as colors, lengths, and angle degrees for a more complex animation where text slides up the container as the text changes colors.<\/p>\n<figure class=\"break-out\">\n<p data-height=\"480\" data-theme-id=\"light\" data-slug-hash=\"rNgavyb\" data-user=\"smashingmag\" data-default-tab=\"result\" class=\"codepen\">See the Pen [Text animation with @property [forked]](https:\/\/codepen.io\/smashingmag\/pen\/rNgavyb) by <a href=\"https:\/\/codepen.io\/rpsthecoder\">Preethi Sam<\/a>.<\/p><figcaption>See the Pen <a href=\"https:\/\/codepen.io\/smashingmag\/pen\/rNgavyb\">Text animation with @property [forked]<\/a> by <a href=\"https:\/\/codepen.io\/rpsthecoder\">Preethi Sam<\/a>.<\/figcaption><\/figure>\n<p>Let\u2019s use this demo as an exercise to learn more about defining custom properties with the <code>@property<\/code> at-rule, combining what we just saw in the ripple with the concept of interpolating gradient values.<\/p>\n<div data-audience=\"non-subscriber\" data-remove=\"true\" class=\"feature-panel-container\">\n<aside class=\"feature-panel\">\n<div class=\"feature-panel-left-col\">\n<div class=\"feature-panel-description\">\n<p>Meet <strong><a data-instant href=\"https:\/\/www.smashingconf.com\/online-workshops\/\">Smashing Workshops<\/a><\/strong> on <strong>front-end, design & UX<\/strong>, with practical takeaways, live sessions, <strong>video recordings<\/strong> and a friendly Q&A. With Brad Frost, St\u00e9ph Walter and <a href=\"https:\/\/smashingconf.com\/online-workshops\/workshops\">so many others<\/a>.<\/p>\n<p><a data-instant href=\"smashing-workshops\" class=\"btn btn--green btn--large\">Jump to the workshops\u00a0\u21ac<\/a><\/div>\n<\/div>\n<div class=\"feature-panel-right-col\"><a data-instant href=\"smashing-workshops\" class=\"feature-panel-image-link\"><\/p>\n<div class=\"feature-panel-image\">\n<img decoding=\"async\" loading=\"lazy\" class=\"feature-panel-image-img lazyload\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"Feature Panel\" width=\"257\" height=\"355\" data-src=\"\/images\/smashing-cat\/cat-scubadiving-panel.svg\"><\/p>\n<\/div>\n<p><\/a>\n<\/div>\n<\/aside>\n<\/div>\n<h2 id=\"the-html\">The HTML<\/h2>\n<pre><code class=\"language-html\"><div class=\"scrolling-text\">\n <div class=\"text-container\">\n <div class=\"text\">\n <ruby>\u58f9<rt>one<\/rt><\/ruby>\n <ruby>\u8700<rt>two<\/rt><\/ruby>\n <ruby>\u5169<rt>three<\/rt><\/ruby>\n <\/div>\n <\/div>\n<\/div>\n<\/code><\/pre>\n<p>The HTML contains Chinese characters we\u2019re going to animate. These Chinese characters are marked up with <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTML\/Element\/ruby\"><code><ruby><\/ruby><\/code><\/a> tags so that their English translations can be supplied in <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTML\/Element\/rt\"><code><rt><\/rt><\/code><\/a> tags. The idea is that <code>.scrolling-text<\/code> is the component\u2019s parent container and, in it, is a child element holding the sliding text characters that allow the characters to slide in and out of view.<\/p>\n<h2 id=\"vertical-sliding\">Vertical Sliding<\/h2>\n<p>In CSS, let\u2019s make the characters slide vertically on hover. What we\u2019re making is a container with a fixed height we can use to clip the characters out of view when they overflow the available space.<\/p>\n<pre><code class=\"language-css\">.scrolling-text {\n height: 1lh;\n overflow: hidden;\n width: min-content;\n}\n.text-container:has(:hover, :focus) .text {\n transform: translateY(-2lh) ;\n}\n.text {\n transition: transform 2.4s ease-in-out;\n}\n<\/code><\/pre>\n<figure class=\"break-out\">\n<p data-height=\"480\" data-theme-id=\"light\" data-slug-hash=\"pomvVPx\" data-user=\"smashingmag\" data-default-tab=\"result\" class=\"codepen\">See the Pen [Vertical text transition [forked]](https:\/\/codepen.io\/smashingmag\/pen\/pomvVPx) by <a href=\"https:\/\/codepen.io\/rpsthecoder\">Preethi Sam<\/a>.<\/p><figcaption>See the Pen <a href=\"https:\/\/codepen.io\/smashingmag\/pen\/pomvVPx\">Vertical text transition [forked]<\/a> by <a href=\"https:\/\/codepen.io\/rpsthecoder\">Preethi Sam<\/a>.<\/figcaption><\/figure>\n<p>Setting the <code>.scrolling-text<\/code> container\u2019s width to <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/min-content\"><code>min-content<\/code><\/a> gives the characters a tight fit, stacking them vertically in a single column. The container\u2019s height is set <code>1lh<\/code>. And since we\u2019ve set <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/overflow\"><code>overflow: hidden<\/code><\/a> on the container, only one character is shown in the container at any given point in time.<\/p>\n<blockquote><p><strong>Tip<\/strong>: You can also use the HTML <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTML\/Element\/pre\"><code><\/p>\n<pre><\/pre>\n<p><\/code><\/a> element or either the <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/white-space\"><code>white-space<\/code><\/a> or <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/text-wrap\"><code>text-wrap<\/code><\/a> properties to control how text wraps.<\/p><\/blockquote>\n<p>On hover, the text moves <code>-2lh<\/code>, or double the height of a single text character in the opposite, or up, direction. So, basically, we\u2019re sliding things up by two characters in order to animate from the first character to the third character when the container holding the text is in a hovered state.<\/p>\n<div class=\"partners__lead-place\"><\/div>\n<h2 id=\"applying-gradients-to-text\">Applying Gradients To Text<\/h2>\n<p>Here\u2019s a fun bit of styling:<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-css\">.text {\n background: repeating-linear-gradient(\n 180deg, \n rgb(224, 236, 236), \n rgb(224, 236, 236) 5px, \n rgb(92, 198, 162) 5px, \n rgb(92, 198, 162) 6px);\n background-clip: text;\n color: transparent; \/* to show the background underneath *\/\n background-size: 20% 20%;\n}\n<\/code><\/pre>\n<\/div>\n<p>How often do you find yourself using repeating gradients in your work? The fun part, though, is what comes after it. See, we\u2019re setting a <code>transparent<\/code> color on the text and that allows the <code>repeating-linear-gradient()<\/code> to show through it. <a href=\"https:\/\/css-tricks.com\/the-css-box-model\/\">But since text is a box like everything else in CSS<\/a>, we clip the background at the text itself to make it look like the text is cut out of the gradient.<\/p>\n<figure class=\"break-out\">\n<p data-height=\"480\" data-theme-id=\"light\" data-slug-hash=\"BaeyxZJ\" data-user=\"smashingmag\" data-default-tab=\"result\" class=\"codepen\">See the Pen [A gradient text (Note: View in Safari or Chrome) [forked]](https:\/\/codepen.io\/smashingmag\/pen\/BaeyxZJ) by <a href=\"https:\/\/codepen.io\/rpsthecoder\">Preethi Sam<\/a>.<\/p><figcaption>See the Pen <a href=\"https:\/\/codepen.io\/smashingmag\/pen\/BaeyxZJ\">A gradient text (Note: View in Safari or Chrome) [forked]<\/a> by <a href=\"https:\/\/codepen.io\/rpsthecoder\">Preethi Sam<\/a>.<\/figcaption><\/figure>\n<p>Pretty neat, right? Now, it looks like our text characters have a striped pattern painted on them.<\/p>\n<h2 id=\"animating-the-gradient\">Animating The Gradient<\/h2>\n<p>This is where we take the same animated gradient concept covered in other tutorials and work it into what we\u2019re doing here. For that, we\u2019ll first register some of the <code>repeating-linear-gradient()<\/code> values as custom properties. But unlike the other implementations, ours is a bit more complex because we will animate several values rather than, say, updating the hue.<\/p>\n<p>Instead, we\u2019re animating two colors, a length, and an angle.<\/p>\n<pre><code class=\"language-css\">@property --c1 {\n syntax: \"\";\n inherits: false;\n initial-value: rgb(224, 236, 236);\n}\n@property --c2 {\n syntax: \"\";\n inherits: false;\n initial-value: rgb(92, 198, 162);\n}\n@property --l {\n syntax: \" | \";\n inherits: false;\n initial-value: 5px;\n}\n@property --angle {\n syntax: \"\";\n inherits: false;\n initial-value: 180deg;\n}\n\n.text {\n background: repeating-linear-gradient(\n var(--angle), \n var(--c1), \n var(--c1) 5px, \n var(--c2) var(--l), \n var(--c2) 6px);\n}\n<\/code><\/pre>\n<p>We want to update the values of our registered custom properties when the container that holds the text is hovered or in focus. All that takes is re-declaring the properties with the updated values.<\/p>\n<pre><code class=\"language-css\">.text-container:has(:hover, :focus) .text {\n --c1: pink;\n --c2: transparent; \n --l: 100%;\n --angle: 90deg;\n\n background-size: 50% 100%;\n transform: translateY(-2lh);\n}\n<\/code><\/pre>\n<p>To be super clear about what\u2019s happening, these are the custom properties and values that update on hover:<\/p>\n<ul>\n<li><code>--c1<\/code>: Starts with a color value of <code>rgb(224, 236, 236)<\/code> and updates to <code>pink<\/code>.<\/li>\n<li><code>--c2<\/code>: Starts with a color value of <code>rgb(92, 198, 162)<\/code> and updates to <code>transparent<\/code>.<\/li>\n<li><code>--l<\/code>: Starts with length value <code>5px<\/code> and updates to <code>100%<\/code>.<\/li>\n<li><code>--a<\/code>: Starts with an angle value of <code>180deg<\/code> and updates to <code>90deg<\/code>.<\/li>\n<\/ul>\n<p>So, the two colors used in the gradient transition into other colors while the overall size of the gradient increases and rotates. It\u2019s as though we\u2019re choreographing a short dance routine for the gradient.<\/p>\n<div class=\"partners__lead-place\"><\/div>\n<h2 id=\"refining-the-transition\">Refining The Transition<\/h2>\n<p>All the while, the <code>.text<\/code> element containing the characters slides up to reveal one character at a time. The only thing is that we have to tell CSS what will <code>transition<\/code> on hover, which we do directly on the <code>.text<\/code> element:<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-css\">.text {\n transition: --l, --angle, --c1, --c2, background-size, transform 2.4s ease-in-out;\n transition-duration: 2s; \n}\n<\/code><\/pre>\n<\/div>\n<p>Yes, I could just as easily have used the <code>all<\/code> keyword to select all of the transitioning properties. But I prefer taking the extra step of declaring each one individually. It\u2019s a little habit to keep the browser from having to watch for too many things, which could slow things down even a smidge.<\/p>\n<h2 id=\"final-demo\">Final Demo<\/h2>\n<p>Here\u2019s the final outcome once again:<\/p>\n<figure class=\"break-out\">\n<p data-height=\"480\" data-theme-id=\"light\" data-slug-hash=\"qBGEYXO\" data-user=\"smashingmag\" data-default-tab=\"result\" class=\"codepen\">See the Pen [Text animation with @property [forked]](https:\/\/codepen.io\/smashingmag\/pen\/qBGEYXO) by <a href=\"https:\/\/codepen.io\/rpsthecoder\">Preethi Sam<\/a>.<\/p><figcaption>See the Pen <a href=\"https:\/\/codepen.io\/smashingmag\/pen\/qBGEYXO\">Text animation with @property [forked]<\/a> by <a href=\"https:\/\/codepen.io\/rpsthecoder\">Preethi Sam<\/a>.<\/figcaption><\/figure>\n<p>I hope this little exercise not only demonstrates the sorts of fancy things we can make with CSS custom properties but also helps clarify the differences between custom properties and standard variables. Standard variables are excellent placeholders for more maintainable code (and a few <a href=\"https:\/\/css-tricks.com\/the-css-custom-property-toggle-trick\/\">fancy tricks of their own<\/a>) but when you find yourself needing to update one value in a property that supports multiple values \u2014 such as colors in a gradient \u2014 the <code>@property<\/code> at-rule is where it\u2019s at because it lets us define variables with a custom specification that sets the variable\u2019s syntax, initial value, and inheritance behavior.<\/p>\n<blockquote class=\"pull-quote\">\n<p>\n <a class=\"pull-quote__link\" aria-label=\"Share on Twitter\" href=\"https:\/\/twitter.com\/share?text=%0aWhen%20we%20get%20to%20amend%20values%20individually%20and%20independently%20with%20a%20promise%20of%20animation,%20it%20both%20helps%20streamline%20the%20code%20and%20opens%20up%20new%20possibilities%20for%20designing%20elaborate%20animations%20with%20relatively%20nimble%20code.%0a&url=https:\/\/smashingmagazine.com%2f2024%2f05%2ftimes-need-custom-property-instead-css-variable%2f\"><\/p>\n<p>When we get to amend values individually and independently with a promise of animation, it both helps streamline the code and opens up new possibilities for designing elaborate animations with relatively nimble code.<\/p>\n<p> <\/a>\n <\/p>\n<div class=\"pull-quote__quotation\">\n<div class=\"pull-quote__bg\">\n <span class=\"pull-quote__symbol\">\u201c<\/span><\/div>\n<\/p><\/div>\n<\/blockquote>\n<p>That\u2019s why <code>@property<\/code> is a useful CSS standard to keep in mind and keep ready to use when you are thinking about animations that involve isolated value changes.<\/p>\n<h3 id=\"further-reading-on-smashingmag\">Further Reading On SmashingMag<\/h3>\n<ul>\n<li>\u201c<a href=\"https:\/\/www.smashingmagazine.com\/2022\/10\/advanced-animations-css\/\">How To Create Advanced Animations With CSS<\/a>,\u201d Yosra Emad<\/li>\n<li>\u201c<a href=\"https:\/\/www.smashingmagazine.com\/2021\/04\/easing-functions-css-animations-transitions\/\">Understanding Easing Functions For CSS Animations And Transitions<\/a>,\u201d Adrian Bece<\/li>\n<li>\u201c<a href=\"https:\/\/www.smashingmagazine.com\/2023\/09\/path-css-easing-linear-function\/\">The Path To Awesome CSS Easing With The linear() Function<\/a>,\u201d Jhey Tompkins<\/li>\n<li>\u201c<a href=\"https:\/\/www.smashingmagazine.com\/2022\/01\/css-radial-conic-gradient\/\">A Deep CSS Dive Into Radial And Conic Gradients<\/a>,\u201d Ahmad Shadeed<\/li>\n<\/ul>\n<div class=\"signature\">\n <img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"Smashing Editorial\" width=\"35\" height=\"46\" loading=\"lazy\" class=\"lazyload\" data-src=\"https:\/\/www.smashingmagazine.com\/images\/logo\/logo--red.png\"><br \/>\n <span>(gg, yk)<\/span>\n<\/div>\n<\/article>\n","protected":false},"excerpt":{"rendered":"<p>The Times You Need A Custom @property Instead Of A CSS Variable The Times You Need A Custom @property Instead Of A CSS Variable Preethi Sam 2024-05-13T08:00:00+00:00 2025-03-19T12:04:52+00:00 We generally use a CSS variable as a placeholder for some value we plan to reuse \u2014 to avoid repeating the same value and to easily update that value across the board if it needs to be updated. :root { –mix: color-mix(in…<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[11],"tags":[],"_links":{"self":[{"href":"https:\/\/kerrprogroup.com\/index.php\/wp-json\/wp\/v2\/posts\/384"}],"collection":[{"href":"https:\/\/kerrprogroup.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/kerrprogroup.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/kerrprogroup.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/kerrprogroup.com\/index.php\/wp-json\/wp\/v2\/comments?post=384"}],"version-history":[{"count":1,"href":"https:\/\/kerrprogroup.com\/index.php\/wp-json\/wp\/v2\/posts\/384\/revisions"}],"predecessor-version":[{"id":385,"href":"https:\/\/kerrprogroup.com\/index.php\/wp-json\/wp\/v2\/posts\/384\/revisions\/385"}],"wp:attachment":[{"href":"https:\/\/kerrprogroup.com\/index.php\/wp-json\/wp\/v2\/media?parent=384"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/kerrprogroup.com\/index.php\/wp-json\/wp\/v2\/categories?post=384"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/kerrprogroup.com\/index.php\/wp-json\/wp\/v2\/tags?post=384"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}