the netflix logo animation in pure CSS
A Pen by Gregor Adams on CodePen.
| input.retrigger(type="radio" name="rerun" id="retrigger--1") | |
| input.retrigger(type="radio" name="rerun" id="retrigger--2" checked) | |
| .buttons | |
| label.button(for="retrigger--1" class="button--1") PLAY AGAIN | |
| label.button(for="retrigger--2" class="button--2") PLAY AGAIN | |
| .logo | |
| span N | |
| span E | |
| span T | |
| span F | |
| span L | |
| span I | |
| span X |
the netflix logo animation in pure CSS
A Pen by Gregor Adams on CodePen.
| /// Create the same keyframes twice (for retrigger in pure CSS) | |
| /// @author Gregor Adams | |
| /// @param {Keyword} $name - name of the animation (will be suffixed with --1 and --2) | |
| @mixin double-keyframes($name) { | |
| // write the keyframe rules to the document root | |
| @at-root { | |
| // write the same keyframes twice | |
| @for $i from 1 through 2 { | |
| $keyframe-name: unquote($name + "--" + $i); | |
| @keyframes #{$keyframe-name} { | |
| @content; | |
| } | |
| } | |
| } | |
| } | |
| /// Create a 3d-shadow in a certain direction | |
| /// @author Gregor Adams | |
| /// @param {Number} $depth - length of the shadow | |
| /// @param {Unit} $color - color of the shadow | |
| /// @param {Unit} $x - step to next shadow on the x axis | |
| /// @param {Unit} $y - step to next shadow on the y axis | |
| /// @param {Unit} $blur - blur of the shadow | |
| /// @param {Color|false} $mix - optinally add a color to mix in | |
| /// @return {List} - returns a text-shadow | |
| @function d3($depth, $color, $x: 1px, $y: 1px, $blur: 0, $mix: false) { | |
| $shadow: (); | |
| @for $i from 1 through $depth { | |
| // append to the existing shadow | |
| @if type-of($mix) != 'color' { | |
| $shadow: append($shadow, round($i * $x) round($i * $y) $blur $color, comma); | |
| } @else { | |
| $shadow: append($shadow, round($i * $x) round($i * $y) $blur mix($mix,$color,0.3%*$i), comma); | |
| } | |
| } | |
| @return $shadow; | |
| } | |
| // options | |
| $c_fg: #f00; | |
| $c_bg: #fff; | |
| $c_3d: #f2f2f2; | |
| $c_shadow: #dadada; | |
| $c_shadow-mix: #6998da; | |
| $google-font: Oswald; // should be a google font | |
| $font-weight: 700; | |
| $letters: 7; | |
| // fonts | |
| @import url(http://fonts.googleapis.com/css?family=#{$google-font}:#{$font-weight}); | |
| $font-family: $google-font, Impact, Haettenschweiler, 'Arial Narrow Bold', sans-serif; | |
| // basic styles | |
| html, | |
| body { | |
| // force full viewport size | |
| height: 100%; | |
| width: 100%; | |
| // disable scrolling | |
| overflow: hidden; | |
| } | |
| body { | |
| // remove the body margin | |
| margin: 0; | |
| // create a flex-box model | |
| display: flex; | |
| flex-flow: row wrap; | |
| flex-wrap: wrap; | |
| flex-direction: row; | |
| align-items: flex-start; | |
| justify-content: center; | |
| } | |
| // hide the radios that allow us to retrigger the animation | |
| .retrigger { | |
| position: absolute; | |
| left: -5em; | |
| opacity: 0; | |
| } | |
| // the section containing the retrigger button | |
| .buttons { | |
| flex: 0 0 auto; | |
| height: 2em; | |
| line-height: 2em; | |
| background: $c_bg; | |
| text-align: center; | |
| cursor: pointer; | |
| user-select: none; | |
| // the labels are disguised as buttons | |
| .button { | |
| padding: 0.3em 1em; | |
| font-family: $font-family; | |
| color: $c_fg; | |
| font-size: 1.3em; | |
| display: none; | |
| cursor: pointer; | |
| border: 2px solid $c_fg; | |
| &:hover { | |
| color: $c_bg; | |
| background: $c_fg; | |
| } | |
| // display the label for the unchecked input | |
| @for $i from 1 through 2 { | |
| &--#{$i} { | |
| #retrigger--#{$i % 2 + 1}:checked ~ & { | |
| display: block; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| // the logo area | |
| .logo { | |
| // create a flex-box model | |
| flex: 0 0 100%; | |
| display: flex; | |
| justify-content: center; | |
| // set the perspective | |
| perspective: 1000px; | |
| perspective-origin: 50% 0%; | |
| // define fonts; | |
| font-size: 8em; | |
| font-family: $font-family; | |
| color: $c_bg; | |
| // scale the logo | |
| transform: scaleY(0.8); | |
| } | |
| // animation for the color | |
| @include double-keyframes("color") { | |
| 0% { | |
| color: $c_bg; | |
| } | |
| 100% { | |
| color: $c_fg; | |
| } | |
| } | |
| // the letters | |
| span { | |
| // enable webkit GPU rendering | |
| -webkit-backface-visibility: hidden; | |
| // animation settings | |
| animation-fill-mode: forwards; | |
| animation-timing-function: ease-in, ease-out, linear; | |
| // handle each letter separately | |
| @for $i from 1 through $letters { | |
| $offset: $i - ceil($letters / 2); | |
| $trans: if($offset > 0, -89.5deg, 89.5deg); | |
| &:nth-child(#{$i}) { | |
| $del: 1s + 0.1s * $i; | |
| $maxdel: 1s + 0.1s * $letters; | |
| $dur: 1s + 0.05s * $i; | |
| $maxdur: 1s + 0.05s * $letters; | |
| animation-delay: $del, $maxdel + $maxdur, $maxdel + $maxdur + 0.4s; | |
| animation-duration: $dur, 3s, 0.1s; | |
| // allow to run the animation again | |
| @for $j from 1 through 2 { | |
| #retrigger--#{$j}:checked ~ .logo & { | |
| animation-name: in_#{$i}--#{$j}, out_#{$i}--#{$j}, color--#{$j}; | |
| } | |
| } | |
| // trans/de-form the letters | |
| transform-origin: 50% + 50%/$offset 200%; | |
| font-size: if($offset == 0, | |
| 0.85em, | |
| 0.9em + 0.015*pow(abs($offset),2)); | |
| } | |
| // the animation magic | |
| @include double-keyframes("in_" + $i) { | |
| 0% { | |
| transform: | |
| if($offset == 0, scale(1, 1), scale(95.9 - abs($offset) * 10, 1)) | |
| if($offset == 0, translatey(0%), rotatey($trans)); | |
| text-shadow: | |
| d3(15, rgba($c_3d, 0), 0, 0), | |
| d3(50, rgba($c_shadow, 0), 0, 0); | |
| } | |
| 50% { | |
| transform: | |
| if($offset == 0, scale(1.2, 1.2), scale(126.2 - abs($offset) * 10, 1.2)) | |
| if($offset == 0, translatey(-16%), rotatey($trans)); | |
| text-shadow: | |
| d3(15, $c_3d, if($offset == 0, 0, -0.25px * $offset), 0), | |
| d3(50, $c_shadow, 1px, 3px, 3px, $c_shadow-mix); | |
| } | |
| 100% { | |
| transform: | |
| if($offset == 0, scale(1.1, 1.1), scale(116.2 - abs($offset) * 10, 1.1)) | |
| if($offset == 0, translatey(-12%), rotatey($trans)); | |
| text-shadow: | |
| d3(15, $c_3d, if($offset == 0, 0, -0.25px * $offset), 0), | |
| d3(50, $c_shadow, 1px, 3px, 3px, $c_shadow-mix); | |
| } | |
| } | |
| @include double-keyframes("out_" + $i) { | |
| 0% { | |
| transform: | |
| if($offset == 0, scale(1.1, 1.1), scale(116.2 - abs($offset) * 10, 1.1)) | |
| if($offset == 0, translatey(-12%), rotatey($trans)); | |
| text-shadow: | |
| d3(15, $c_3d, if($offset == 0, 0, -0.25px * $offset), 0), | |
| d3(50, $c_shadow, 1px, 3px, 3px, $c_shadow-mix); | |
| } | |
| 20% { | |
| transform: | |
| if($offset == 0, scale(1.05, 1.05), scale(105.9 - abs($offset) * 10, 1.05)) | |
| if($offset == 0, translatey(-7%), rotatey($trans)); | |
| text-shadow: | |
| d3(15, rgba($c_3d, 0), 0, 0), | |
| d3(50, rgba($c_shadow, 0), 0, 0); | |
| } | |
| 100% { | |
| transform: | |
| if($offset == 0, scale(1, 1), scale(95.9 - abs($offset) * 10, 1)) | |
| if($offset == 0, translatey(0%), rotatey($trans)); | |
| text-shadow: | |
| d3(15, rgba($c_3d, 0), 0, 0), | |
| d3(50, rgba($c_shadow, 0), 0, 0); | |
| } | |
| } | |
| } | |
| } |