Issue mixing variable with mixin with keyframe animation? - sass

First time using SCSS, and testing my knowledge from the Sass-Lang.com guide. According to the guide, it is possible to both set variables and use mixins to simplify your CSS.
I was coding an animation where the div is clipped from bottom to top. I used variables to set the initial and final clip-path settings, and used them while calling a mixin. Yet I get the error, 'Invalid CSS after "...slider-initial)": expected "{", was "; }"'. What am I doing wrong?
Here is my code:
<body>
<section id='main'>
<div id='left'></div>
<div id='right'></div>
<section>
</body>
$slider-initial: inset(0 0 0 0);
$slider-final: inset(0 0 100% 0);
#mixin slider-clip($slider-state) {
-webkit-clip-path: $slider-state;
clip-path: $slider-state;
}
body {
height: 100%; width: 100%;
margin: 0 auto;
}
#main {
height: 64vh; width: 38vw;
margin: 0 auto;
margin-top: 10%;
position: relative;
display: flex;
flex-direction: row;
justify-content: center;
border: 1vh solid black;
}
#left {
order: 1;
width: 4%;
height: 100%;
margin-left: 46%;
background: green;
}
#right {
opacity: 1;
order: 2;
width: 4%;
height: 100%;
margin: auto;
margin-left: 0;
animation-name: dropdown;
animation-duration: 4s;
background: red;
}
#keyframes dropdown {
from { #mixin slider-clip($slider-initial); }
to { #mixin slider-clip($slider-final); }
}

You called your mixin in a wrong way:
#keyframes dropdown {
from { #mixin slider-clip($slider-initial); }
to { #mixin slider-clip($slider-final); }
}
In the guide on sass-lang.com, you can see the following example of how to include a mixin:
.box { #include border-radius(10px); }
Applied to your case, your code should look like this:
#keyframes dropdown {
from { #include slider-clip($slider-inital); }
to { #include slider-clip($slider-final); }
}

Related

Using media queries, following standards with SASS and BEM?

I am following the BEM practice and want to add specific break points, it appears 2 formats work for me. Does anyone know the advantage of either ?
Here is the first, I embed the media directly into the element (BEM)
.my-component {
&__section-field {
display: flex;
flex-wrap: wrap;
}
&__section-sep {
width: 100%;
#media(min-width: 900px) {
width: 50%;
}
}
}
Here is the second, where I embed the media query outside of the section and redefine the section again.
.my-component {
&__section-field {
display: flex;
flex-wrap: wrap;
}
&__section-sep {
width: 100%;
}
#media(min-width: 900px) {
&__section-sep {
width: 50%;
}
}
}
As you can see the, I am basically changing the width of an item between either 50% or 100% depending if it's mobile only resolution. I am using flexbox with flex-wrap and it wraps depending on there is space left on the line.
They both seem to work the same as far as I can say. Would anyone confirm if there is a difference and which one would be more scalable and maintainable?
Maybe there is something that I haven't thought of, I did think about storing my media queries in a separate file but I was trying to keep everything together and follow the bem methodology.
I prefer the 2nd variant for the following reason: when you have many media queries, indeed, the 1st variant requires less copy/paste.
But though, the code becomes much less readable if you have many elements or modifiers in the block, i.e. selectors. It's common to face a situation when it's needed to change style for several elements/modifiers at exact screen. And when that case appears - as for me - it's easier to navigate between media queries, find the needed media and change code there - so you work at one place in the file, whereas in the 1st variant you would need to jump between selectors. In my opinion when it comes to work with media queries - it's faster to navigate between them, then to navigate between selectors.
imagine the following code with media query inside every selector :
.section_name_educational {
display: flex;
flex-direction: row;
padding: 0px;
.fp-tableCell {
display: flex;
flex-direction: row;
}
.section {
&__inner {
display: flex;
flex-direction: row-reverse;
height: 100vh;
height: calc(100vh - 80px);
box-sizing: border-box;
width: 50%;
padding-left: 40px;
margin-top: auto;
padding-bottom: 40px;
}
&__header {
position: relative;
width: 100%;
top: 62px;
left: 40px;
}
&__text-holder {
width: 100%;
}
&__title {
font-size: 48px;
}
&__subtitle {
width: 150%;
margin: 20px 0px;
}
&__description {
color: #669900;
font-size: 18px;
}
&__primary {
height: 100%;
display: flex;
box-sizing: border-box;
flex-direction: column;
justify-content: space-between;
width: 100%;
}
&__additional {
display: none;
}
&__kettles {
display: flex;
}
&__kettle {
height: auto;
margin-right: 20px;
&_order {
&_1 {
width: 183px;
min-width: 183px;
max-width: 183px;
}
&_2 {
width: 108px;
min-width: 108px;
max-width: 108px;
}
&_3 {
width: 127px;
min-width: 127px;
max-width: 127px;
}
}
}
&__background-holder {
overflow: hidden;
max-height: 100vh;
}
&__background {
position: relative;
width: auto;
height: 100vh;
}
}
}
#media all and (max-height: 600px) {
.section_name_educational {
.section {
&__kettle {
&_order {
&_2 {
width: 68px;
max-width: 68px;
min-width: 68px;
height: 120px;
margin-left: 30px;
}
&_3 {
width: 78px;
max-width: 78px;
min-width: 78px;
height: 120px;
}
}
}
}
}
}
#media all and (max-height: 760px) {
.section_name_educational {
.section {
&__header {
top: 40px;
}
&__subtitle {
width: 100% !important;
}
&__additional {
display: none !important;
}
}
}
}
#media (--large) {
.section_name_educational {
.section {
&__subtitle {
width: 120%;
}
}
}
}
#media (--xlarge) {
.section_name_educational {
padding-top: 120px;
.section {
&__inner {
height: calc(100vh - 60px);
margin-top: 0;
}
&__header {
transition-delay: 1s;
opacity: 0;
right: -100px;
bottom: -40px;
transform: translateY(20px);
}
&__subtitle {
width: 120%;
}
&__primary {
width: calc(100% - 160px);
}
&__additional {
display: flex;
flex-direction: column;
justify-content: space-between;
}
&__bubbles {
display: block;
position: relative;
top: 40px;
width: 160px;
min-width: 160px;
max-width: 160px;
height: auto;
transform: translateY(20px);
opacity: 0;
transition-delay: 1s;
}
&__kettle {
opacity: 0;
transform: translateY(20px);
transition-delay: 1s;
}
}
&.active {
.section {
&__header {
transition: opacity 1s ease-out 0.8s,
transform 0.8s ease-out 0.8s;
opacity: 1;
transform: translateY(0px);
}
&__kettle {
opacity: 1;
transform: translateY(0px);
transition: opacity 0.6s ease-out,
transform 0.6s ease-out;
&_order {
&_1 {
transition-delay: 1.6s;
display: block;
}
&_2 {
transition-delay: 1.9s;
}
&_3 {
transition-delay: 2.1s;
}
}
}
&__bubbles {
transition: opacity 0.8s ease-out 2.5s,
transform 0.8s ease-out 2.3s;
transform: translateY(0px);
opacity: 1;
}
}
}
}
}
#media all and (min-width: 1400px) {
.section_name_educational {
.section {
&__header {
left: 60px;
}
&__subtitle {
width: 110%;
}
}
}
}
#media (--xxlarge) {
.section_name_educational {
.section {
&__primary {
width: calc(100% - 148px);
}
&__subtitle {
width: 80%;
margin: 40px 0px;
}
&__description-inner {
width: 60%;
}
&__bubbles {
width: 148px;
min-width: 148px;
max-width: 148px;
top: 40px;
}
}
}
}
#media (--monster) {
.section_name_educational {
.section {
&__primary {
width: calc(100% - 227px);
}
&__header {
left: 200px;
top: 150px;
}
&__title {
font-size: 58px;
}
&__subtitle {
font-size: 24px;
width: 80%;
}
&__description {
font-size: 24px;
}
&__bubbles {
width: 227px;
min-width: 227px;
max-width: 227px;
left: 0px;
}
}
}
}
As for me - it would be difficult to read it if I had done it with the 1st variant.

SCSS List grid mixin inline-block causing problems

I've just been practicing making some mixins, which at the moment are very basic but just do some simple tasks.
I've made a mixin that I can call in which turns child elements into inline-block elements instead of floats, so that the parent can use text-align to center the elements as things shrink responsively.
The problem I have is this was working fine, but since tweaking it, it doesn't seem to work as it did. I'll provide a working example below. In my example when 3 per row are set, I'm only getting two per row.
My initial thoughts are that it might be extra space created by inline-block? Although my vertical-align: top; was to negate that.
HTML
<div id="site-wrap">
<div class="list__wrap">
<div class="list__item">1</div>
<div class="list__item">2</div>
<div class="list__item">3</div>
<div class="list__item">4</div>
<div class="list__item">5</div>
<div class="list__item">6</div>
<div class="list__item">7</div>
<div class="list__item">7</div>
</div>
</div>
SCSS
// The mighty Mixin...
#mixin list-grid($per-row, $spacing, $child, $prefix){
margin: 0 em(-$spacing/2);
#include clearfix;
//if a class
#if $prefix == 'class' {
> .#{$child}{
width: 100%/$per-row;
//position: relative;
padding: 0 em($spacing/2) em($spacing) em($spacing/2);
display: inline-block;
vertical-align: top;
background-clip: content-box;
}
}
#if $prefix == 'id' {
> ##{$child}{
width: 100%/$per-row;
position: relative;
padding: 0 em($spacing/2) em($spacing) em($spacing/2);
display: inline-block;
vertical-align: top;
background-clip: content-box;
}
}
#if $prefix == 'none' {
> #{$child}{
width: 100%/$per-row;
position: relative;
padding: 0 em($spacing/2) em($spacing) em($spacing/2);
display: inline-block;
vertical-align: top;
background-clip: content-box;
word-wrap: break-word;
}
}
}
//start of our styles
#site-wrap{
max-width: 1020px;
margin: 0 auto;
background: {
color: Black;
}
}
.list__wrap{
#include clearfix;
// Call in our mixin on the inner divs
#include list-grid(3, 10, list__item, class);
// We can use text-align to center the list when it's shrinking down
text-align: center;
.list__item{
background: {
color: Tomato;
}
}
}
Working example: http://codepen.io/vdecree/pen/wuExl
I think I found a solution that is not as icky as having to format the HTML in a certain way. Rather that use font-size: 0; on the parent (because this broke my negative margins) — you can use letter-spacing: -0.31em;
// The mighty Mixin...
#mixin list-grid($per-row, $spacing, $child, $prefix){
margin: 0 em(-$spacing/2);
#include clearfix;
letter-spacing: -0.31em;
//if a class
#if $prefix == 'class' {
> .#{$child}{
width: 100%/$per-row;
font-size: 16px;
position: relative;
padding: 0 em($spacing/2) em($spacing) em($spacing/2);
display: inline-block;
vertical-align: top;
letter-spacing: 0;
background-clip: content-box;
}
}
#if $prefix == 'id' {
> ##{$child}{
width: 100%/$per-row;
position: relative;
padding: 0 em($spacing/2) em($spacing) em($spacing/2);
display: inline-block;
vertical-align: top;
letter-spacing: 0;
background-clip: content-box;
}
}
#if $prefix == 'none' {
> #{$child}{
width: 100%/$per-row;
position: relative;
padding: 0 em($spacing/2) em($spacing) em($spacing/2);
display: inline-block;
vertical-align: top;
background-clip: content-box;
letter-spacing: 0;
word-wrap: break-word;
}
}
}
Source

Sass / Scss: Changing the order of nested psuedo selectors for :before and :hover in generated CSS?

Given the following Sass:
div.test {
display: inline-block;
background-color: #ffffff;
color: #000000;
&:before {
& {
&:hover {
border: 1px solid salmon;
}
}
width: 25px;
height: 25px;
content: "";
}
}
The resulting CSS compiles to:
div.test {
display: inline-block;
background-color: #ffffff;
color: #000000;
}
div.test:before {
width: 25px;
height: 25px;
content: "";
}
div.test:before:hover {
border: 1px solid salmon;
}
What I am attempting to do is generate div.test:hover:before (the current output is before:hover).
NOTE: I am able to generate the expected CSS by using the following Sass:
div.test {
display: inline-block;
background-color: #ffffff;
color: #000000;
&:hover {
&:before {
border: 1px solid salmon;
}
}
&:before {
width: 25px;
height: 25px;
content: "";
}
}
However I would like to know if it is possible using the first nested approach or some modification of it.
The goal was to avoid having to repeat &:before if there was such a way to do so using Sass syntax. I am also OK with knowing it isn't possible.
While initially the plan was to have '&' available in SassScript as a string that could be manipulated so that you could insert values wherever you wanted, those plans have been abandoned for 3.3 due to complication. Unfortunately you'll have to wait a while to be able to do this. At the moment '&' is immutable and just means "whatever the selector chain up to this point is".
EDIT (2020.02.15):
it is now technically possible to achieve this with recent versions of dart-sass:
#use "sass:selector";
#mixin unify-parent($child) {
#at-root #{selector.unify(&, $child)} {
#content;
}
}
div.test {
display: inline-block;
background-color: #ffffff;
color: #000000;
&:before {
width: 25px;
height: 25px;
content: "";
#include unify-parent(":hover") {
border: 1px solid salmon;
}
}
}
Sources:
https://sass-lang.com/blog/the-module-system-is-launched
https://sass-lang.com/documentation/style-rules/parent-selector#advanced-nesting

Mixins and placeholder selector scope - Styles not being applied to current selector

I'm writing a mixin for adding a graphical effect to the corner of a box:
The mixin will accept a corner position (tl, tr, bl, br), size, and colors:
#mixin notch($notch-location, $size, $foreground-color, $background-color) {
%top--left {
#extend %notch;
&:before {
top: 0; left: 0;
border-width: $size $size 0 0;
}
}
// etc ...
%notch {
position: relative;
&:before {
#extend .pel;
position: absolute;
border-style: solid;
border-color: $foreground-color $background-color;
}
}
#if $notch-location == top-left {
#extend %top--left;
}
// etc ...
}
I then use the mixin on a selector, for example:
a {
#include notch(top-left, 24px, $color-brand, #fff);
}
Unfortunately the resulting CSS isn't what I'm expecting:
.menu.collapsed .nav .nav--current a a:before {
top: 0;
left: 0;
border-width: 24px 24px 0 0;
}
.menu.collapsed .nav .nav--current a a {
position: relative;
}
.menu.collapsed .nav .nav--current a a:before {
position: absolute;
border-style: solid;
border-color: #ec5b25 white;
}
Example:
SCSS (jsFiddle)
Compiled CSS (jsFiddle)
As you can see, the styles added via the mixin are being qualified with an extra a. Why is this happening?
The output is exactly as I would expect because of the nature of extends. The %notch class belongs to the parent selector (a in your case). If you change it to .notch instead, it becomes obvious.
Extend classes are not ephemeral. It's a good idea to avoid defining them within a mixin you plan on reusing. Doing so will cause the class to be generated each time you invoke the mixin, causing duplication of code all over the place (which you probably don't want).
%notch {
position: relative;
&:before {
#extend .pel;
position: absolute;
border-style: solid;
}
}
#mixin notch($notch-location, $size, $foreground-color, $background-color) {
#extend %notch;
border-color: $foreground-color $background-color;
&:before {
#if $notch-location == top-left {
top: 0; left: 0;
border-width: $size $size 0 0;
} #else if $notch-location == top-right {
top: 0; right: 0;
border-width: $size 0 0 $size;
} #else if $notch-location == bottom-left {
bottom: 0; left: 0;
border-width: 0 $size $size 0;
} #else {
bottom: 0; right: 0;
border-width: 0 0 $size $size;
}
}
}
a {
display: block;
width: 100px; height: 100px;
background: #0f0;
#include notch(top-left, 24px, #0f0, #0f0);
}
It's also worth noting that extends aren't always the best choice, they can cause the code to be larger than it would be if you'd simply duplicated the code due to repeating the selector.
You seem to have messed up your code structure.
I'm not sure why this extra a appears, but when i refactor your code to have reasonable structure, the problem disappears:
$color-brand: pink;
%notch {
position: relative;
&:before {
#extend .pel !optional;
position: absolute;
border-style: solid;
}
}
%top--left {
#extend %notch;
&:before {
top: 0; left: 0;
}
}
#mixin notch($notch-location, $size, $foreground-color, $background-color) {
border-color: $foreground-color $background-color;
#if $notch-location == top-left {
#extend %top--left;
border-width: $size $size 0 0;
}
// etc ...
}
a {
#include notch(top-left, 24px, $color-brand, #fff);
}
Demo: http://sassbin.com/gist/6019481/

Safari: Fixed background + transition

Example site
I have a site divided into your usual vertical sections. Header and footer both contain backgrounds with background-attachment: fixed. I have a slide-out nav, which you can see is activated on the first link. Everything works dandy except...
Issue:
Safari 6 (I'm not sure about 5.1, but it seems to be on Mac as my Windows Safari doesn't have the issue) has a nasty flicker upon animation. This can be resolved with the usual -webkit-backface hack HOWEVER upon using this, a new problem arises. The fixed background images start behaving very badly, and if you scroll/resize the browser enough, the images get distorted or content overlays improperly. Is there an alternative method I can use for this technique, or an actual fix?
HTML
<section>Hi CLICKME</section>
<section>hi</section>
<section>hi</section>
<section>hi</section>
<footer><p>I am some text</p></footer>
<aside class="menu">
I'm a menu.
</aside>
CSS
body {
background: #222;
transition: all 0.3s;
-webkit-backface-visibility: hidden;
}
body.bump {
transform: translate(-258px, 0);
}
section {
background: #CBA;
color: white;
line-height: 450px;
font-size: 32px;
height: 500px;
position: relative;
text-align: center;
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.3);
z-index: 1;
}
section:nth-child(2) {
background: #FAFAFA;
}
section:nth-child(3) {
background: #CCC;
}
section:nth-child(4) {
background: #ABC;
}
section:first-child {
background: url(http://placekitten.com/1600/500) center top;
background-attachment: fixed;
-webkit-backface-visibility: hidden;
}
#media all and (min-width: 73.75em) {
section:first-child {
background-size: cover;
}
}
footer {
background: url(http://placekitten.com/1400/500) center top;
background-attachment: fixed;
color: white;
font-size: 32px;
height: 500px;
}
#media all and (min-width: 73.75em) {
footer {
background-size: cover;
}
}
footer p {
position: fixed;
bottom: 200px;
left: 0;
text-align: center;
width: 100%;
}
aside.menu {
background: #222;
color: #FFF;
height: 100%;
padding-top: 30px;
position: fixed;
top: 0;
right: 0;
text-align: left;
transform: translate(516px, 0);
transition: all 0.3s;
width: 258px;
-webkit-backface-visibility: hidden;
}
.bump aside.menu {
transform: translate(258px, 0);
}
JS (using Jquery)
$('section a').click( function(e) {
$('body').toggleClass('bump');
});
I did a workaround, by applying the fixed background to the body, wrapping everything in body in another div (animating that instead, so it wasn't affecting the body background) and the footer stayed the same, since having scrolled that far there is no way to pop the sidebar out anyway (so no animation flicker to worry about).

Resources