Translate: Exploring @ the property and its Animating Powers – | CSS Tricks

Translated by Baymax


Before looking at @Property, some background might be helpful.

CSS Custom Properties are widely supported and have been used in many front-end component libraries, such as Material-UI.

Attribute names prefixed with –, such as –example — name, represent custom attributes with values that can be reused document-wide through the var() function

– MDN

/* Modifed from MDN example*/
:root {
    /* Is defined in the pseudo-root node so it is globally available */
  --main-bg-color: #488cff;
}

#firstParagraph {
    /* var(custom attribute value, rollback value)*/
  background-color: var(--main-bg-color, blue);
}
Copy the code

It helps us better manage values in complex styles and improves code readability (after all –main-bg-color is always a better description of values than #488cff). However, in current use, there is no way to achieve stable animation with custom properties.

Glamorize, they can even be transitioned or animated, since the UA has no way to interpret their contents, They always use the “flips at 50%” behavior that is used for any other pair of values that can’t be intelligently interpolated.

In particular, they (custom attributes) can even be transitioned or animated, but because the user agent can’t interpret them, they always adopt the “coin flip” behavior for all the values they can’t intelligently fill. CSS Custom Properties for Cascading Variables Module Level 1

@ Property helps us achieve this goal by allowing us to explicitly define these CSS custom properties, such as providing a type, default value, and whether the value should inherit from @ Property. This article introduces the concrete implementation method in detail.

@property --property-name {
  syntax: '<color>';
  initial-value: #c0ffee;
  inherits: false;
}
Copy the code

It should be noted that @Property is still in the Working Draft stage and production use may need to wait for more browsers to support CSS Properties and Values API Level 1

The body of the

Well, what is @property? It’s a new CSS feature! It gave you super powers. I’m not kidding, there are some things @Property can do that unlock things we’ve never been able to do in CSS before.

While all the @property stuff is exciting, the most interesting thing is that it provides a way to specify types for custom CSS properties. The type gives the browser more context information, and this does something cool: we can give the browser the information it needs to transition and animate these properties.

But before we get too carried away with it, it’s worth mentioning that browser support for it is patchy. At the time of this writing, @Property is only supported in Chrome and Edge (via an extension). We need to keep looking at browser support to see when we can use it in other places like Firefox and Safari.

First, we have type checking

@property --spinAngle {
  /* The initial value of the custom attribute */
  initial-value: 0deg;
  /* Whether */ should be inherited from the parent element
  inherits: false;
  / * type. Right, type. Think TypeScript is cool */
  syntax: '<angle>';
}

@keyframes spin {
  to {
    --spinAngle: 360deg; }}Copy the code

There is nothing wrong! Type checking in the CSS. It’s kind of like creating our own mini-CSS specification. This is just a very simple example. Look at all the types we can use

  • length

  • number

  • percentage

  • length-percentage

  • color

  • image

  • url

  • integer

  • angle

  • time

  • resolution

  • transform-list

  • transform-function

  • Custom-ident (a custom identification string)

    Before we have these types, we need to rely on a few “tricks” to support animations using custom attributes.

    .jump
    --size 50
    --height 1
    --squished calc(1 + (2 * (var(--height) / 10)))
    --extended calc(1 - (2 * (var(--height) / 10)))
    height calc(var(--size) * 1px)
    width calc(var(--size) * 1px)
    background radial-gradient(circle at 10% 10%, hsl(25.100%.70%), hsl(25.100%.40%))
    transform-origin bottom center
    animation jump 1s infinite ease
    
    
    Copy the code
@keyframes jump
  0%.100%
    transform translate(0.0) scale(var(--squished), var(--extended))
  25%.75%
    transform translate(0.0) scale(1.1)
  50%/* / transform translate(0, calc(var(--height) * -100%)) scale(var(--extended), var(--squished))

#jump:checked ~ .ground .jump
  --height 1

#higher:checked ~ .ground .jump
  --height 2

#highest:checked ~ .ground .jump
  --height 3

#jump:checked ~ [for='jump'],
#higher:checked ~ [for='higher'],
#highest:checked ~ [for='highest']
  background hsl(24.100%.75)

label[for]:hover
  background hsl(24.100%.85)
Copy the code

Codepen. IO/jh3y/pen/zY…

So what interesting things can we do with it? Let’s look at (examples) to spark our imagination.

Let’s animate the colors

How would you animate an element to show a series of colors moving between them? I am a fan of HSL Color Space, which separates colors into easy-to-understand numbers: Hue, Saturation and Lightness.

Animating hue felt like something fun we could do. What is colorful? A rainbow! There are many ways to draw a rainbow, and the following is one example.

![Step1.gif](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cc259852b6ff4122bef9ad31d0f89660~tplv-k3u1fbpfcp-watermark.image)
.rainbow
  - let b = 0
  while b < 7
    .rainbow__band(style=`--index: ${b};`)
    -- b+ + *box-sizing border-box

body
  min-height 100vh
  display grid
  place-items center
  background hsl(210.80%.90%)

.rainbow
  height 25vmin
  width 50vmin
  position relative
  overflow hidden

  &__band
    --size calc((50 - (var(--index, 0) * 4)) * 1vmin)
    height 50vmin
    width 50vmin
    border-radius 50%
    position absolute
    top 100%
    left 50%
    border-width 2vmin
    border-styleSolid // Notice hereborder-color 'hsl(%s, 80%.50%%) 'var(--hue, 10)
    transform translate(-50%, -50%)
    height var(--size)
    width var(--size)

    &:nth-of-type(2)
      --hue 35
    &:nth-of-type(3)
      --hue 55
    &:nth-of-type(4)
      --hue 110
    &:nth-of-type(5)
      --hue 200
    &:nth-of-type(6)
      --hue 230
    &:nth-of-type(7)
    
Copy the code

In this example, the CSS custom properties are set to each band of the rainbow, and their scope is set to each individual band by using: nth-Child (). Each band has a –index to help them adjust their size.

To animate the bands, we can use –index to set some negative animation delay for them, and then iterate over them using the same KeyFrame animation.

.rainbow__band {
  border-color: hsl(var(--hue, 10), 80%.50%);
  animation: rainbow 2s calc(var(--index, 0) * -0.2 s) infinite linear;
}

@keyframes rainbow {
  0%.100% {
    --hue: 10;
  }
  14% {
    --hue: 35;
  }
  28% {
    --hue: 55;
  }
  42% {
    --hue: 110;
  }
  56% {
    --hue: 200;
  }
  70% {
    --hue: 230;
  }
  84% {
    --hue: 280; }}Copy the code

If you want a “step by step” effect, this might look fine. But each step of these keyframes is not particularly accurate. I used 14% for each step as a rough range.

We can animate the border-color and this will achieve our effect. But we still have the problem of calculating the keyFrame step. And we need to write a lot of CSS to achieve this effect.

@keyframes rainbow {
  0%.100% {
    border-color: hsl(10.80%.50%);
  }
  14% {
    border-color: hsl(35.80%.50%);
  }
  28% {
    border-color: hsl(55.80%.50%);
  }
  42% {
    border-color: hsl(110.80%.50%);
  }
  56% {
    border-color: hsl(200.80%.50%);
  }
  70% {
    border-color: hsl(230.80%.50%);
  }
  84% {
    border-color: hsl(280.80%.50%); }}Copy the code

@property is in. First, define a custom property for hue. This tells the browser that –hue will be a number (rather than a string that looks like a number) :

@property --hue {
  initial-value: 0;
  inherits: false;
  /* We specify the type */
  syntax: '<number>'; } 'HSL hue values can be obtained from0Increased to360. We learn from0Start as an initial value. This value will not be inherited. And in this case, our value is a number. Then the animation will be reduced to: '@keyframes rainbow {
  to {
    --hue: 360; }}Copy the code

Yes, exactly:

To make the starting point accurate, we can adjust the delay for each strip. This gives us some cool flexibility. For example, we can increase animation-duration to get a slow loop. Use the following example to try different speeds

&__band
  --size calc((50 - (var(--index, 0) * 4)) * 1vmin)
  height 50vmin
  width 50vmin
  border-radius 50%
  position absolute
  top 100%
  left 50%
  border-width 2vmin
  border-color 'hsl(%s, 80%.50%%) 'var(--hue)
  border-style solid
  transform translate(-50%, -50%)
  height var(--size)
  width var(size) / / hereanimation rainbow calc(var(--duration, 8) * 1s) calc(var(--index, 0) * -0.2 s) infinite linear
Copy the code

This may not be the “most fanciful” example, but I think there are some interesting scenarios for animating colors when we use numeric logical color Spaces. Animating the color wheel used to be tricky. For example, a keyframe is generated from a preprocessor, such as Stylus.

@keyframes party
for $frame in (0.100)
    {$frame _ 1%}
background 'hsl(%s, 65%.40%)' % (\$frame _ 3.6)
Copy the code

We do this simply because browsers don’t understand what we’re trying to do. The browser interprets the transition from 0 to 360 on the color wheel as an instantaneous transition, because the starting and ending HSL values represent the same color.

@keyframes party {
  from {
    background: hsl(0.80%.50%); 
  }
  to {
    background: hsl(360.80%.50%); }}Copy the code

These keyframes are all the same, so the browser assumes that the animation will stay at the same backgound value, even though we want the browser to render the entire spectrum of colors, starting at one value and doing some action and ending at the same value.

Think about all our other usage scenarios. We can:

  • Animated saturation
  • Use different easing functions
  • Animated brightness
  • Try to RGB ()
  • The best thing about trying angles in HSL () and declaring our custom property type is that we can share this animated value scoped across different elements. Think about this button. Its border and shadow will rotate an entire color wheel over the hover.

button Start Party!
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@500&display=swap')

@property --hue
  syntax '<integer>'
  inherits true
  initial-value 0

:root
  --bg hsl(0.0%.10%)
  --button-bg hsl(0.0%.0%)

*
  box-sizing border-box

body
  background hsl(0.0%.10%)
  display flex
  align-items center
  justify-content center
  min-height 100vh
  transform-style preserve-3d
  perspective 800px

button
  --border 'hsl(%s, 0%, 50%)' % var(--hue, 0)
  --shadow 'hsl(%s, 0%, 80%)' % var(--hue, 0)
  user-select none
  font-family 'Inter', sans-serif
  font-size 2rem
  padding 1.25 rem 2.5 rem
  border-radius 0.5 rem
  border 0.25 rem solid
  background var(--button-bg)
  color hsl(0.0%.100%)
  border-color var(--border)
  box-shadow 0 1rem 2rem -1.5 rem var(--shadow)
  transition transform 0.2 s, box-shadow 0.2 s
  cursor pointer
  outline transparent

  &:hover
    --border 'hsl(%s, 80%, 50%)' % var(--hue, 0)
    --shadow 'hsl(%s, 80%, 50%)' % var(--hue, 0)
    animation hueJump 0.75 s infinite linear
    transform rotateY(10deg) rotateX(10deg)

  &:active
    transform rotateY(10deg) rotateX(10deg) translate3d(0.0, -15px)
    box-shadow 0 0rem 0rem 0rem var(--shadow)
    animation-play-state paused

@keyframes hueJump
  to
    --hue 360
Copy the code

Animating colors makes me think… Wow!!!

- const COUNT = 20
- let t = 0
while t < COUNT
  h1(style=`--index: ${COUNT - t}; `) Wow! - t++@import url('https://fonts.googleapis.com/css2?family=Montserrat:ital, WGHT @ 1900 & display = swap')

@property --hue
  inherits false
  initial-value 0
  syntax '<number>'

:root
  --bg hsl(45.80%.50%)
  --stroke hsl(0.0%.10%)

*
  box-sizing border-box

body
  min-height 100vh
  background var(--bg)
  font-family 'Montserrat', sans-serif
  min-height 100vh
  overflow hidden

h1
  --color 'hsl(%s, 80%, 60%)' % var(--hue)
  text-transform uppercase
  font-size 150px
  letter-spacing 0.25 vmin
  position absolute
  margin 0
  top 50%
  left 50%
  line-height 0.8
  color var(--color)
  transform translate(-30%, -70%) translate(calc(var(--index) * (var(--x, -4) * 1%)), calc(var(--index) * (var(--y, 20) * 1%)))
  -webkit-text-stroke 1.25 vmin var(--stroke)
  animation party 1s calc(var(--index) * -0.1 s) infinite linear

@keyframes party
  0%
    --hue 0
  100%
    --hue 360
import gsap from 'https://cdn.skypack.dev/gsap'

document.addEventListener('pointermove', ({ x, y }) => {
  gsap.set('h1', {
    '--x': gsap.utils.mapRange(0, window.innerWidth, -10.10, x),
    '--y': gsap.utils.mapRange(0, window.innerHeight, -10.10, y)
  })
})
Copy the code

Pure count

Because we can define types for numbers — such as INTEGER and number — that means we can animate those numbers as well, rather than treating them as part of something else. Carter Li wrote an article on CSS-Tricks. The secret is to use integer and CSS Counter. This is similar to how we use counters in a “pure CSS” game.

Using counter and pseudo-elements provides a way to turn numbers into strings. We can then use these characters as content for pseudo-elements. Here are some highlights:

@property --milliseconds {
  inherits: false;
  initial-value: 0;
  syntax: '<integer>';
}

.counter {
  counter-reset: ms var(--milliseconds);
  animation: count 1s steps(100) infinite;
}

.counter:after {
  content: counter(ms);
}

@keyframes count {
  to {
    --milliseconds: 100; }}Copy the code

This gives us this look, which is really cool

.counter
@property --milliseconds {
  inherits: false;
  initial-value: 0;
  syntax: '<integer>';
}

body {
  min-height: 100vh;
  display: grid;
  place-items: center;
  background: hsl(10.10%.10%);
}

.counter {
  position: relative;
  counter-reset: ms var(--milliseconds);
  animation: count 1s steps(100) infinite;
}

.counter:after {
  content: counter(ms);
  position: absolute;
  top: 0;
  left: 0;
  font-size: 5rem;
  transform: translate(-50%.0);
  color: hsl(0.0%.100%);
  font-weight: bold;
  font-family: sans-serif;
}

@keyframes count {
  to {
    --milliseconds: 100; }}Copy the code

Enhance the zillion dots, and you have a working stopwatch, using only CSS and HTML. Try these buttons! And the best thing about this is that this actually works like a timer. It will not be affected by Time Drift. In some ways, it’s even more accurate than the typical JavaScript scheme implemented through setInterval. Check out this video on JavaScript counters from Google Chrome Developer.

Pure CSS Working Stopwatch 😎 (@Property) What other places can you animate numbers? Maybe a countdown timer?

Animate gradients

You know that. Linear, Radial, and Conic. Have you ever thought about transitioning or animating Color stops? Okay, so at sign property can do that.

Consider a gradient that mimics waves on the beach. When we stack some images together we can make something like the following:

body {
  background-image:
    linear-gradient(transparent 0 calc(35% + (var(--wave) * 0.5)), var(--wave-four) calc(75% + var(--wave)) 100%),
    linear-gradient(transparent 0 calc(35% + (var(--wave) * 0.5)), var(--wave-three) calc(50% + var(--wave)) calc(75% + var(--wave))),
    linear-gradient(transparent 0 calc(20% + (var(--wave) * 0.5)), var(--wave-two) calc(35% + var(--wave)) calc(50% + var(--wave))),
    linear-gradient(transparent 0 calc(15% + (var(--wave) * 0.5)), var(--wave-one) calc(25% + var(--wave)) calc(35% + var(--wave))), var(--sand);
}
Copy the code

There’s a lot of code here. But breaking it down, we use calc() to create each color breakpoint. And in the calculation, we add the value of –wave. The neat trick here is that when we animate a wave, all the waves move.

*
  box-sizing border-box

:root
  --sand hsl(45.40%.50%)
  --wave-one hsl(200.50%.100%)
  --wave-two hsl(200.50%.90%)
  --wave-three hsl(210.50%.60%)
  --wave-four hsl(210.80%.25%)

@property --wave
  inherits false
  initial-value 0%
  syntax '<percentage>'

body
  background linear-gradient(transparent 0 calc(35% + (var(--wave) * 0.5)), var(--wave-four) calc(75% + var(--wave)) 100%),
             linear-gradient(transparent 0 calc(35% + (var(--wave) * 0.5)), var(--wave-three) calc(50% + var(--wave)) calc(75% + var(--wave))),
             linear-gradient(transparent 0 calc(20% + (var(--wave) * 0.5)), var(--wave-two) calc(35% + var(--wave)) calc(50% + var(--wave))),
             linear-gradient(transparent 0 calc(15% + (var(--wave) * 0.5)), var(--wave-one) calc(25% + var(--wave)) calc(35% + var(--wave))),
             var(--sand)
  min-height 100vh
Copy the code

Here is all the code we need to implement the wave animation:

body {
  animation: waves 5s infinite ease-in-out;
}
@keyframes waves {
  50% {
    --wave: 25%; }}Copy the code

If we didn’t use @property, our waves would cycle step by step between high and low. But with @property, we can get a really nice effect.

It’s exciting to think of other scenarios that we can achieve by manipulating images. Like rotation. Or animating conic-gradient… . However, within a border-image. Bramus Van Damme explained this concept very well.

Let’s explain this step by step by implementing a charging indicator. We’ll animate the Angle and hue at the same time. Let’s start by defining these two custom properties:

@property --angle {
  initial-value: 0deg;
  inherits: false;
  syntax: '<number>';
}

@property --hue {
  initial-value: 0;
  inherits: false;
  syntax: '<angle>';
}
Copy the code

The animation pauses for each iteration to update the Angle and hue.

@keyframes load {
  0%.10% {
    --angle: 0deg;
    --hue: 0;
  }
  100% {
    --angle: 360deg;
    --hue: 100; }}Copy the code

Now let’s apply it to the element as a border-image

.loader {
  --charge: hsl(var(--hue), 80%.50%);
  border-image: conic-gradient(var(--charge) var(--angle), transparent calc(var(--angle) * 0.5 deg)) 30;
  animation: load 2s infinite ease-in-out;
}
Copy the code

Very cool

.loader Charging...
*
  box-sizing border-box

:root
  --bg hsl(0.10%.10%)

body
  min-height 100vh
  background var(--bg)
  display grid
  place-items center

@property --a
  initial-value 0deg
  inherits false
  syntax '<angle>'
@property --h
  initial-value 0
  inherits false
  syntax '<number>'

.loader
  padding 2rem 4rem
  font-family monospace
  font-weight bold
  color hsl(0.0%.100%)
  border-style solid
  border-width 1vmin
  font-size 2rem
  --charge 'hsl(%s, 80%, 50%)' % var(--h, 0)
  border-image conic-gradient(var(--charge) var(--a), transparent calc(var(--a) + 0.5 deg)) 30
  animation load 2s infinite ease-in-out

@keyframes load
  0%.10%
    --a 0deg
    --h 0
  100%
    --a 360deg
    --h 100
Copy the code
Unfortunately, **border-image** and **border-radius** do not work well together. However, we can add a pseudo-element after the element. Combined with the animating digital tricks mentioned earlier, we have a finished charging/loading animation. (Yes, it will charge at 100% charge)Copy the code

Transform is cool, too

One of the problems with animating transformations is transitioning between different parts. It often ends up simply not working or not looking like it should. A classic example is a ball being thrown. We want it to move from point A to point B and mimic the effect of gravity at the same time.

An early attempt might look something like this:


@keyframes throw {
  0% {
    transform: translate(-500%.0);
  }
  50% {
    transform: translate(0, -250%);
  }
  100% {
    transform: translate(500%.0); }}Copy the code

But we’ll soon see that it doesn’t look like what we want.

<svg class="ball" viewBox="0 0 496 512" title="basketball-ball">
  <path d="M212.3 10.3c-43.8 6.3-86.2 24.1-122.2 53.8l77.4 77.4c27.8-35.8 43.3-81.2 44.8-131.2zM248 222L405.9 64.1c-42.4-35-93.6-53.5-145.5-56.1-1.2 63.9-21.5 122.3-58.7 167.7L248 222zM56.1 98.1c-29.7 36-47.5 78.4-53.8 122.2 50-1.5 95.5-17 131.2-44.8L56.1 98.1zm272.2 204.2c45.3-37.1 103.7-57.4 167.7-58.7-2.6-51.9-21.1-103.1-56.1-145.5L282 256l46.3 46.3zM248 290L90.1 447.9c42.4 34.9 93.6 53.5 145.5 56.1 1.3-64 21.6-122.4 58.7-167.7L248 290zm191.9 123.9c29.7-36 47.5-78.4 53.8-122.2-50.1 1.6-95.5 17.1-131.2 44.8l77.4 77.4zM167.7 209.7C122.3 246.9 63.9 267.3 0 268.4c2.6 51.9 21.1 103.1 56.1 145.5L214 256l-46.3-46.3zm116 292c43.8-6.3 86.2-24.1 122.2-53.8l-77.4-77.4c-27.7 35.7-43.2 81.2-44.8 131.2z" />
</svg>
*
  box-sizing border-box

body
  min-height 100vh
  display grid
  place-items center
  background hsl(190.80%.90%)


.ball
  height 10vmin
  width 10vmin
  border-radius 50%
  fill hsl(35.80%.50%)
  background hsl(35.80%.35%)
  animation throw 1s infinite alternate

@keyframes throw
  0%
    transform translate(-500%.0)
  50%
    transform translate(0, -250%)
  100%
    transform translate(500%.0)
Copy the code

Previously, we might have done this by wrapping elements and animating them separately. However, with @property, we can animate each individual value in the transformation at the same time. Let’s take a quick look at how this is done by defining custom properties and then setting transformations on the ball.


@property --x {
  inherits: false;
  initial-value: 0%;
  syntax: '<percentage>';
}

@property --y {
  inherits: false;
  initial-value: 0%;
  syntax: '<percentage>';
}

@property --rotate {
  inherits: false;
  initial-value: 0deg;
  syntax: '<angle>';
}

.ball {
  animation: throw 1s infinite alternate ease-in-out;
  transform: translateX(var(--x)) translateY(var(--y)) rotate(var(--rotate));
}
Copy the code

Now for our animation, we can combine the transformations we want in the KeyFrame.

@keyframes throw {
  0% {
    --x: -500%;
    --rotate: 0deg;
  }
  50% {
    --y: -250%;
  }
  100% {
    --x: 500%;
    --rotate: 360deg; }}Copy the code

So what’s the result? The result is that we have the curve path that we want. And we can make it look different depending on the timing function we use. We can split the animation into three directions and use different timing functions. And that makes a difference in the way the ball moves.

Let’s consider another example where we want to drive a car around a rounded quadrilateral.

.road
img.car(src="https://assets.codepen.io/605876/little-red-car.png" alt="Little red car") * box-sizing border-box :root --road hsl(220, 8%, 50%) --grass hsl(90, 40%, 50%) --island hsl(45, 40%, 50%) --lines hsl(45, 80%, 90) @inherits --x inherits false initial-value-22.5 syntax '
      
       ' @inherits false initial-value 0 syntax '
       
        ' @property --r inherits false initial-value 0deg syntax '
        
         ' body min-height 100vh display grid place-items center background var(--grass) .car animation journey 5s infinite linear transform translate(calc(var(--x) *  1vmin), calc(var(--y) * 1vmin)) rotate(var(--r)) width 3vmin object-fit cover .road height 50vmin width 50vmin border-radius 12.5% border 5vmin solid var(--road) background var(--road) position Absolute Top 50% left 50% transform Translate (-50%, -50%) &: Before Content '' position Absolute height 44vmin width 44vmin border-radius 11% border 0.5vmin bounding var(--lines) top 50% left 50% transform translate(-50%, -50%) &:after content '' position absolute height 40vmin width 40vmin background var(--island) top 50% left 50% transform translate(-50%, -50%) border-radius 10%
        
       
      Copy the code

We can do something similar to what we did with the ball

@property --x {
  inherits: false;
  initial-value: -22.5;
  syntax: '<number>';
}

@property --y {
  inherits: false;
  initial-value: 0;
  syntax: '<number>';
}

@property --r {
  inherits: false;
  initial-value: 0deg;
  syntax: '<angle>';
}
Copy the code

The transformation of the trolley is calculated using Vmin to maintain responsiveness.

.car {
  transform: translate(calc(var(--x) * 1vmin), calc(var(--y) * 1vmin)) rotate(var(--r)); } 'Now we can write the precise frame-to-frame travel of the car. We can start with --x@keyframes journey {
  0%.100% {
    --x: -22.5;
  }
  25% {
    --x: 0;
  }
  50% {
    --x: 22.5;
  }
  75% {
    --x: 0; }}Copy the code

We can see that the car is traveling correctly on the X axis.

And then on top of that we add the Y-axis stroke

@keyframes journey {
  0%.100% {    --x: -22.5;
    --y: 0;
  }
  25% {
    --x: 0;
    --y: -22.5;
  }
  50% {
    --x: 22.5;
    --y: 0;
  }
  75% {
    --x: 0;
    --y: 22.5; }}Copy the code

But it didn’t turn out quite right.

Let’s add some extra steps to our @keyframes to fix this.

@keyframes journey {
  0%.100% {
    --x: -22.5;
    --y: 0;
  }
  12.5% {
    --x: -22.5;
    --y: -22.5;
  }
  25% {
    --x: 0;
    --y: -22.5;
  }
  37.5% {
    --y: -22.5;
    --x: 22.5;
  }
  50% {
    --x: 22.5;
    --y: 0;
  }
  62.5% {
    --x: 22.5;
    --y: 22.5;
  }
  75% {
    --x: 0;
    --y: 22.5;
  }
  87.5% {
    --x: -22.5;
    --y: 22.5; }}Copy the code

Well, it looks better now

All that’s left is for the car to spin. We chose 5% of the time in the corner to spin. It’s not exact, but it shows the potential of what this approach can do.

@keyframes journey {
  0% {
    --x: -22.5;
    --y: 0;
    --r: 0deg;
  }
  10% {
    --r: 0deg;
  }
  12.5% {
    --x: -22.5;
    --y: -22.5;
  }
  15% {
    --r: 90deg;
  }
  25% {
    --x: 0;
    --y: -22.5;
  }
  35% {
    --r: 90deg;
  }
  37.5% {
    --y: -22.5;
    --x: 22.5;
  }
  40% {
    --r: 180deg;
  }
  50% {
    --x: 22.5;
    --y: 0;
  }
  60% {
    --r: 180deg;
  }
  62.5% {
    --x: 22.5;
    --y: 22.5;
  }
  65% {
    --r: 270deg;
  }
  75% {
    --x: 0;
    --y: 22.5;
  }
  85% {
    --r: 270deg;
  }
  87.5% {
    --x: -22.5;
    --y: 22.5;
  }
  90% {
    --r: 360deg;
  }
  100% {
    --x: -22.5;
    --y: 0;
    --r: 360deg; }}Copy the code

And then we have what we want, a car that goes around a rounded quadrilateral. No extra wrapping elements, no complicated math. And we do it all through custom properties.

Variables drive the entire scenario

We’ve seen some pretty good @property usage scenarios so far, but combining all the techniques we’ve seen takes it to another level. For example, we can drive an entire scene by using only a few custom properties.

Consider the following concept of a 404 page. Two registered custom properties drive different moving parts. We have a moving gradient clipped by WebKit-backgroun-clip (mimicking text highlights). The shadow is moved by reading the value of the property (mimicking the shadow formed by text). Then we oscillate another element (mimicking a oscillating light source) for the effect of light.

That’s it!

It’s exciting to think of what we can do with our ability to define types with @Property. By providing the browser with some extra context about custom attributes, we can achieve some fantastic things that were previously impossible with basic strings.

Do you have any thoughts on other genres? The timing and resolution felt like an interesting transition, although I admit I didn’t get them to work the way I wanted them to. Urls also feel good, like a carousel between a series of sources. Just brainstorming here.

I hope this quick look at @property inspires you to learn and make your own awesome examples! I can’t wait to see your ideas. In fact, share them with me in the comments.

Original link: css-tricks.com/exploring-p…

Welcome to "Byte front-end ByteFE" resume delivery email "[email protected]"Copy the code