Condense classes and change properties - sass

I have a donut chart that has 5 states (0 - 5). I'm using it in a Vue application, and the donut chart state changes depending on information.
I've created 5 classes to represent the 5 steps of the chart. Step 0 = 0%, step 1 = 20%, etc. The svg element takes a stroke-dasharray property, this shows how far the chart is filled.
So I created 5 css classes for the steps:
.donut-segment-0 {
animation: donut0 1s;
}
#keyframes donut0{
0% {
stroke-dasharray: 0, 100;
}
100% {
stroke-dasharray: 0, 100;
}
}
.donut-segment-1 {
animation: donut1 1s;
}
#keyframes donut1{
0% {
stroke-dasharray: 0, 100;
}
100% {
stroke-dasharray: 20, 80;
}
}
.donut-segment-2 {
animation: donut2 1s;
}
#keyframes donut2{
0% {
stroke-dasharray: 20, 80;
}
100% {
stroke-dasharray: 40, 60;
}
}
.donut-segment-3 {
animation: donut3 1s;
}
#keyframes donut3{
0% {
stroke-dasharray: 40, 60;
}
100% {
stroke-dasharray: 60, 40;
}
}
.donut-segment-4 {
animation: donut4 1s;
}
#keyframes donut4{
0% {
stroke-dasharray: 60, 40;
}
100% {
stroke-dasharray: 80, 20;
}
}
.donut-segment-5 {
animation: donut5 1s;
}
#keyframes donut5{
0% {
stroke-dasharray: 80, 20;
}
100% {
stroke-dasharray: 100, 00;
}
}
I was wondering if it's possible to condese this a bit. There's a simple logic to it. In each step I change the stroke-daskarray with 20 and call a specific animation.

Took me a bit of time but figured it out:
#for $i from 0 through 5 {
.donut-step-#{$i} {
animation: donut-#{$i} 1s;
}
#keyframes donut-#{$i} {
0% {
stroke-dasharray: ($i - 1) * 20, 100 - (($i - 1) * 20);
}
100% {
stroke-dasharray: $i * 20, 100 - ($i * 20);
}
}
}

Related

How to pass multiple content blocks as arguments to a mixin?

I wanna be able to do something like so:
.myTransitionableElement {
transition: all .5s;
.subChild { transition: all 1s }
#include transitionKeyframes(
start: {
opacity: 0;
transform: tranlsate(50px);
.subChild {
transform: rotate(45deg);
}
},
end: {
opacity: 1;
transform: tranlsate(0);
.subChild {
transform: rotate(0);
}
}
)
}
#mixin transitionKeyframes($args) {
&.transitionStartKeyframe {
#include map_get($args, "start");
}
&.transitionEndKeyframe {
#include map_get($args, "end");
}
}
Which should at the end be equivalent to:
.myTransitionableElement {
transition: all .5s;
.subChild { transition: all 1s }
&.transitionStartKeyframe {
opacity: 0;
transform: tranlsate(50px);
.subChild {
transform: rotate(45deg);
}
}
&.transitionEndKeyframe {
opacity: 1;
transform: tranlsate(0);
.subChild {
transform: rotate(0);
}
}
}
The reason behind this is that I wanna find a way to not have to remember those classes names every time I use a JS abstraction that uses those classes.
You can't pass different content as arguments in SASS mixins.
Below is example of what you can achieve using SASS.
Can you specify what you want to achieve here?
#mixin rtl() {
&.ltr {
#content;
direction: ltr;
}
&.rtl {
#content;
direction: rtl;
}
}
.parent {
#include rtl {
display: flex;
}
}
You can try different approaches here. In this particular one I'm using map to hold the data for me. But if attributes and class names are fixed; you can even simplify it.
$map: (Start: (self: (opacity: '0', transform: 'tranlsate(50px)'), child: (transform: rotate(45deg))), End: (self: (opacity: '1', transform: 'tranlsate(0)'), child: (transform: rotate(0))));
$prefix: transition; $postfix: Keyframe;
#mixin transitionKeyframes($map) {
#each $key, $val in $map {
&.#{$prefix}#{$key}#{$postfix} {
#each $attr, $prop in map-get($val, self) {
#{$attr}: #{$prop};
}
.subChild {
#each $attr, $prop in map-get($val, child) {
#{$attr}: #{$prop};
}
}
}
}
}
.myTransitionableElement {
#include transitionKeyframes($map);
transition: all .5s;
.subChild { transition: all 1s }
}
Use mixins for a repetitive task. Modify the mixin or $frames if you have many variations. Breakdown opacity and transistion further to smaller mixins and invoke them conditionally.
$prefix: transition; $postfix: Keyframe;
$frames: 'Start', 'End';
$opcity-main: ('Start': 0, 'End': 1);
$translate-main: ('Start': 50, 'End': 0);
#mixin transitionKeyframes($frame) {
#each $key in $frame {
&.#{$prefix}#{$key}#{$postfix} {
$opacity: map-get($opcity-main, $key);
$translate: map-get($translate-main, $key);
opacity: $opacity;
transform: tranlsate($translate + px);
}
}
}
.myTransitionableElement {
#include transitionKeyframes($frames);
transition: all .5s;
.subChild { transition: all 1s }
}

Good idea to use #mixin for "DRY'ing" up #keyframes?

Is it a good idea to use mixins to DRY up keyframes as the following example:
Note that I just have left and right here but if I where to have more I think the use of #mixins is a better choice. I am not familiar with the customs of writing neat scss/sass, what is the preferred way?
#keyframes fade-in-left {
0% {
opacity: 0;
transform: translate3d(-100%, 0, 0);
}
100% {
opacity: 1;
transform: translate3d(0, 0, 0);
}
}
#keyframes fade-in-right {
0% {
opacity: 0;
transform: translate3d(100%, 0, 0);
}
100% {
opacity: 1;
transform: translate3d(0, 0, 0);
}
}
vs
#mixin fade-in-horizontally($direction_value) {
#keyframes fade-in-left {
0% {
opacity: 0;
transform: translate3d($direction_value, 0, 0);
}
100% {
opacity: 1;
transform: translate3d(0, 0, 0);
}
}
}
Mixins are great but should not be used just to shorten some code. In that case, you only want to define your #keyframes once.
So, here is a third option: using a list and an each directive.
$keyframes-fade: (
('left', -100%),
('right', 100%)
);
#each $dir, $translateTX in $keyframes-fade {
#keyframes fade-in-#{$dir} {
0% {
opacity: 0;
transform: translate3d($translateTX, 0, 0);
}
100% {
opacity: 1;
transform: translate3d(0, 0, 0);
}
}
}
This will make your code shorter while keeping it easy to understand.
You can see the output here

CSS3 - Display an element after a few seconds

I'm using a pure CSS3 AJAX loader animation, however, I'm hoping to modify it a little.
Currently it just spins several balls in a circle, however, I'd like to display a message after about 30 seconds to please continue waiting and after about 60 seconds display another message that things might have failed.
Is it possible to accomplish this with pure CSS3? Or would JavaScript be required to get this done?
this is a pure CSS3 animation effect JSFiddle
.animate {
-webkit-animation: NAME-YOUR-ANIMATION 60s;
-moz-animation: NAME-YOUR-ANIMATION 60s;
-o-animation: NAME-YOUR-ANIMATION 60s;
animation: NAME-YOUR-ANIMATION 60s;
opacity: 0;
}
#keyframes NAME-YOUR-ANIMATION {
0% {
opacity: 0;
}
49% {
opacity: 0;
}
50% {
opacity: 1;
}
99% {
opacity: 1;
}
100% {
opacity: 0;
}
}
#-webkit-keyframes NAME-YOUR-ANIMATION {
0% {
opacity: 0;
}
49% {
opacity: 0;
}
50% {
opacity: 1;
}
99% {
opacity: 1;
}
100% {
opacity: 0;
}
}
#-moz-keyframes NAME-YOUR-ANIMATION {
0% {
opacity: 0;
}
49% {
opacity: 0;
}
50% {
opacity: 1;
}
99% {
opacity: 1;
}
100% {
opacity: 0;
}
}
#-o-keyframes NAME-YOUR-ANIMATION {
0% {
opacity: 0;
}
49% {
opacity: 0;
}
50% {
opacity: 1;
}
99% {
opacity: 1;
}
100% {
opacity: 0;
}
}
#keyframes NAME-YOUR-ANIMATION {
0% {
opacity: 0;
}
49% {
opacity: 0;
}
50% {
opacity: 1;
}
99% {
opacity: 1;
}
100% {
opacity: 0;
}
}
you'll need to do the same for the other message, the vendor css are important if you want it to work for all browsers.
Sources

Grouping classes with similar rules in SASS

I'm using the following algorithm to create column classes in SASS:
$columns: 8;
$exclude-columns: 5 7;
#for $i from 1 through $columns {
#for $j from 1 through $i {
$width: (100% / $i) * $j;
#if $i != $j and index($exclude-columns, $i) {
.layout-unit-#{$j}of#{$i} {
width: $width;
}
}
}
}
It's working fine, however, the output has quite a bit of duplication:
.layout-unit-1of2 { width: 50%; }
.layout-unit-1of3 { width: 33.33333%; }
.layout-unit-2of3 { width: 66.66667%; }
.layout-unit-1of4 { width: 25%; }
.layout-unit-2of4 { width: 50%; }
.layout-unit-3of4 { width: 75%; }
.layout-unit-1of6 { width: 16.66667%; }
.layout-unit-2of6 { width: 33.33333%; }
.layout-unit-3of6 { width: 50%; }
.layout-unit-4of6 { width: 66.66667%; }
.layout-unit-5of6 { width: 83.33333%; }
.layout-unit-1of8 { width: 12.5%; }
.layout-unit-2of8 { width: 25%; }
.layout-unit-3of8 { width: 37.5%; }
.layout-unit-4of8 { width: 50%; }
.layout-unit-5of8 { width: 62.5%; }
.layout-unit-6of8 { width: 75%; }
.layout-unit-7of8 { width: 87.5%; }
Is there a way to get the output to look more like this:
.layout-unit-1of2,
.layout-unit-2of4,
.layout-unit-3of6,
.layout-unit-4of8 { width: 50%; }
.layout-unit-1of3,
.layout-unit-2of6 { width: 33.33333%; }
.layout-unit-2of3,
.layout-unit-4of6 { width: 66.66667%; }
.layout-unit-1of4,
.layout-unit-2of8 { width: 25%; }
.layout-unit-3of4,
.layout-unit-6of8 { width: 75%; }
.layout-unit-1of6 { width: 16.66667%; }
.layout-unit-5of6 { width: 83.33333%; }
.layout-unit-1of8 { width: 12.5%; }
.layout-unit-3of8 { width: 37.5%; }
.layout-unit-5of8 { width: 62.5%; }
.layout-unit-7of8 { width: 87.5%; }
Or is this a limitation of SASS?
It's not a limitation of Sass, but the algorithm.
Here is a solution that requires at least Sass 3.3 (see live demo on SassMeister):
Note: I fixed your code to support the exclusion of columns.
$columns: 8;
$exclude-columns: 5 7;
// A stack to store the different widths.
$width_stack: ();
#for $i from 1 through $columns {
#for $j from 1 through $i {
#if $i != $j and not index($exclude-columns, $i) {
$width: (100% / $i) * $j;
// Compute the number 66.66667% to a valid CSS selector: "66-66667".
$width_unitless: $width / 1% + unquote("");
$width_dot: str-index($width_unitless, '.');
#if $width_dot {
$width_unitless: str-slice($width_unitless, 0, $width_dot - 1) +
"-" +
str-slice($width_unitless, $width_dot + 1);
}
// Manage the stack of known widths to avoid repeats.
#if not index($width_stack, $width_unitless) {
$width_stack: append($width_stack, $width_unitless);
// Dynamic placeholder!
%mycols-#{$width_unitless} {
width: $width;
}
}
.layout-unit-#{$j}of#{$i} {
#extend %mycols-#{$width_unitless};
}
}
}
}

SASS keyframes not compiling as wanted

I am using the following keyframes mixin for SCSS
#mixin keyframes($name) {
#-webkit-keyframes #{$name} {
#content;
}
#-moz-keyframes #{$name} {
#content;
}
#-ms-keyframes #{$name} {
#content;
}
#keyframes #{$name} {
#content;
}
}
it works perfectly, when i have to animate, which has the animation property, but when i try to use it for child elements i get not the output, that i need (but syntax-valid)
Example:
#include keyframes('text') {
0% {
span { color: red; }
}
100% {
span { color: green; }
}
}
Will output (short ouput):
#keyframes text {
0% span { color: red; }
100% span { color: green; }
}
But what i need is:
#keyframes text {
0% {
span { color: red; }
}
100% {
span { color: green; }
}
}
How can i prevent sass from snapping the first brackets?
As far as i am concerned you apply keyframes to an element. so your desired output:
#keyframes text {
0% {
span { color: red; }
}
100% {
span { color: green; }
}
}
does not make much sense in CSS.
I think what you need is something like :
#keyframes span#text {
0% {color: red; }
100% { color: green; }
}
also it is not your mixin problem as this SASS:
0% {
span { color: red; }
}
100% {
span { color: green; }
}
compiles to:
0% span {
color: red; }
100% span {
color: green; }
as desired.
I think the intended usege of your keyframes mixin would be something like:
span#text {
#include keyframes(text) {
0% {
color: red;
}
100% {
color: green;
}
}
}

Resources