Last active
January 19, 2020 21:22
-
-
Save csilverman/9868a3fac0586dfa21782023cf43f50a to your computer and use it in GitHub Desktop.
DRY-reveal
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ---- | |
// Sass (v3.4.25) | |
// Compass (v1.0.3) | |
// ---- | |
/* Here's the problem this is trying to solve. | |
Say an element is supposed to fade in after a Javascript event. It needs to be hidden and then | |
revealed. If JS is off or broken, though, the element will never be revealed. That | |
could be a problem. | |
To solve this, you'd set up a CSS animation that automatically reveals the element after a | |
certain period of time. (You won't know when exactly JS fails, or how long it | |
might take to load - that's the downside. You'd have to hardcode a delay, and | |
then hope that JS loads within that time. I think Typekit's wf-loading did something | |
similar.) | |
Things are hidden in different ways. An element might be transparent, or it might | |
have a certain background color, or it might be positioned offscreen. No one | |
animation satisfies all situations. So you'll have a series of animations whose only | |
role is to reveal elements that haven't already been revealed by a JS event. These | |
animations should be tied to a .no-js class. | |
What you might do is have a mixin to generate the reveal animation: specify the | |
start state, end state, and property, and you're there. If you have a lot of elements, | |
though, that's not going to be very DRY - you might generate multiple instances | |
of the same animation. | |
The following code creates each animation only once. If it notices that a | |
particular kind of animation has already been created, it doesn't write new | |
code - it just @extends the existing animation. | |
*/ | |
@function str-replace($string, $search, $replace: "") { | |
$index: str-index($string, $search); | |
@if $index { | |
@return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace); | |
} | |
@return $string; | |
} | |
/* The idea is that we don't want to generate the same reveal | |
*animations over and over again. We can't tell if an | |
*animation already exists, but we can generate variables | |
*based on the animation parameters and then check to | |
*see if those variables exist. If it doesn't, create | |
*the animation inside a class. If it does, extend the | |
*existing class. | |
* | |
*Idea: add this to a running list containing | |
*all the reveal animations, similar to how bootstrap lets you | |
*add or subtract theme colors. */ | |
// ask for feedback on css-tricks forum: is this useful? Am I | |
// missing something? | |
// Set up the list | |
$all-anims: (); | |
@function sanitize($string) { | |
$string: inspect($string); | |
$string: str-replace($string, "#", ""); | |
@return $string; | |
} | |
@mixin safe-reveal($property, $start, $finish, $root:'') { | |
// $root is an optional parameter for specifying a root-level class, | |
// like "page" or something. This needs to be specified in the mixin | |
// parameter list, since it'll need to be appended to .no-js. | |
// Note that this mixin *cannot* be called in an element contained in | |
// a root-level container. The resulting selector order will be incorrect; | |
// the root-level container will be a child of the .no-js container. | |
// So: | |
// html.page a { @include safe-reveal("background", #000, #fff); } is not good | |
// it'll result in ".no-js html.page a" | |
// a { @include safe-reveal("background", #000, #fff, html.page); } is good | |
// it'll result in " html.page.no-js a" | |
$start-clean: sanitize($start); | |
$finish-clean: sanitize($finish); | |
$anim-name: #{$property}-#{$start-clean}-#{$finish-clean}; | |
@if index($all-anims, $anim-name) { | |
// Is this animation name already in the list of animations? | |
// If the animation already exists, don't generated the code | |
// for a new one; simply extend the existing animation. | |
@extend %#{$anim-name}; | |
} | |
@else { | |
// If the animation isn't in the list, that means it needs | |
// to be created. | |
// First, add it to the list. Make sure to specify that | |
// you're adding it to the global list. | |
$all-anims: $all-anims $anim-name !global; | |
// @at-root ensures that the resulting selector | |
// (in this case, a placeholder one) is created at the root. | |
// Needs to be tied to .no-js, since we only want this to run if | |
// whatever JS that was supposed to trigger the normal behavior | |
// doesn't run. | |
@at-root #{$root}.no-js { | |
%#{$anim-name} { | |
#{$property}: $start; | |
animation-name: $anim-name; | |
animation-duration: 1s; | |
animation-delay: 2s; | |
animation-fill-mode: forwards; | |
} | |
} | |
// Having created the selector, we now need to apply it | |
// to the element in which we invoked the mixin. | |
@extend %#{$anim-name}; | |
// Here's our reveal animation. It's meant to be super simple, since | |
// all it does is undo the CSS we're using to hide/show an element | |
// that will then be shown/hidden by Javascript. | |
@keyframes #{$anim-name} { | |
0% { | |
#{$property}: $start; | |
} | |
100% { | |
#{$property}: $finish; | |
} | |
} | |
} | |
} | |
.content { | |
@include safe-reveal("background", #000, #fff); | |
} | |
.zx { | |
@include safe-reveal("opacity", 0, 1); | |
} | |
.xbob { | |
@include safe-reveal("opacity", 0, 1); | |
} | |
.sdfxbob { | |
@include safe-reveal("opacity", 0, 1); | |
} | |
.dave { | |
@include safe-reveal("opacity", 0, 1); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Here's the problem this is trying to solve. | |
Say an element is supposed to fade in after a Javascript event. It needs to be hidden and then | |
revealed. If JS is off or broken, though, the element will never be revealed. That | |
could be a problem. | |
To solve this, you'd set up a CSS animation that automatically reveals the element after a | |
certain period of time. (You won't know when exactly JS fails, or how long it | |
might take to load - that's the downside. You'd have to hardcode a delay, and | |
then hope that JS loads within that time. I think Typekit's wf-loading did something | |
similar.) | |
Things are hidden in different ways. An element might be transparent, or it might | |
have a certain background color, or it might be positioned offscreen. No one | |
animation satisfies all situations. So you'll have a series of animations whose only | |
role is to reveal elements that haven't already been revealed by a JS event. These | |
animations should be tied to a .no-js class. | |
What you might do is have a mixin to generate the reveal animation: specify the | |
start state, end state, and property, and you're there. If you have a lot of elements, | |
though, that's not going to be very DRY - you might generate multiple instances | |
of the same animation. | |
The following code creates each animation only once. If it notices that a | |
particular kind of animation has already been created, it doesn't write new | |
code - it just @extends the existing animation. | |
*/ | |
/* The idea is that we don't want to generate the same reveal | |
*animations over and over again. We can't tell if an | |
*animation already exists, but we can generate variables | |
*based on the animation parameters and then check to | |
*see if those variables exist. If it doesn't, create | |
*the animation inside a class. If it does, extend the | |
*existing class. | |
* | |
*Idea: add this to a running list containing | |
*all the reveal animations, similar to how bootstrap lets you | |
*add or subtract theme colors. */ | |
.no-js .content { | |
background: #000; | |
animation-name: background-000-fff; | |
animation-duration: 1s; | |
animation-delay: 2s; | |
animation-fill-mode: forwards; | |
} | |
@keyframes background-000-fff { | |
0% { | |
background: #000; | |
} | |
100% { | |
background: #fff; | |
} | |
} | |
.no-js .zx, .no-js .xbob, .no-js .sdfxbob, .no-js .dave { | |
opacity: 0; | |
animation-name: opacity-0-1; | |
animation-duration: 1s; | |
animation-delay: 2s; | |
animation-fill-mode: forwards; | |
} | |
@keyframes opacity-0-1 { | |
0% { | |
opacity: 0; | |
} | |
100% { | |
opacity: 1; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment