Copy styles to other class in SCSS without #extend? - css

I'm trying to create a helper mixin in my SCSS file for easily styling form input placeholder texts. For a while, I only needed to change the text's color, so I had this mixin:
#mixin input-placeholder($color, $opacity, $focusColor: null, $focusOpacity: null) {
&:-moz-placeholder {
color: $color;
opacity: $opacity;
}
&::-moz-placeholder {
color: $color;
opacity: $opacity;
}
&:-ms-input-placeholder {
color: $color;
opacity: $opacity;
}
&::-webkit-input-placeholder {
color: $color;
opacity: $opacity;
}
&:placeholder {
color: $color;
opacity: $opacity;
}
&:invalid {
color: $color;
}
&:focus {
#if($focusColor==null) {
$focusColor: transparent;
}
#if($focusOpacity==null) {
$focusOpacity: 0;
}
&::-webkit-input-placeholder {
color: $focusColor !important;
opacity: $focusOpacity !important;
}
&:-moz-placeholder {
color: $focusColor !important;
opacity: $focusOpacity !important;
}
&::-moz-placeholder {
color: $focusColor !important;
opacity: $focusOpacity !important;
}
&:-ms-input-placeholder {
color: $focusColor !important;
opacity: $focusOpacity !important;
}
&::-webkit-input-placeholder {
color: $focusColor !important;
opacity: $focusOpacity !important;
}
&:placeholder {
color: $focusColor !important;
opacity: $focusOpacity !important;
}
}
}
This purposely adds each selector separately and not in a comma separated list due to the fact that some browsers will ignore the entire entry if one is invalid.
I wanted to use #extend so that I could create a mixin like this:
#mixin style-input-placeholder($module) {
&:-moz-placeholder {
#extend #{$module};
}
&::-moz-placeholder {
#extend #{$module};
}
&:-ms-input-placeholder {
#extend #{$module};
}
&::-webkit-input-placeholder {
#extend #{$module};
}
&:placeholder {
#extend #{$module};
}
&:invalid {
#extend #{$module};
}
}
Where $module is the selector I pass to the mixin to extend the styles of, and I'd be able to use it like this:
.some-special-placeholder-styles {
color: purple;
opacity: 0;
text-transform: uppercase;
font-family: 'Open Sans', Arial, sans-serif;
}
input {
#include style-input-placeholder('.some-special-placeholder-styles');
}
It would allow me to modify more than just the color and opacity without having to annoyingly specify each attribute as a mixin parameter. But because of the nature of #extend, it combines all of those selectors into one comma separated list. So is there something else I can do or any workarounds that you've come across?

You can use #content directive to pass arbitrary content inside mixins (was added into sass 3.2). So your code may look like:
#mixin style-input-placeholder() {
&:-moz-placeholder {
#content;
}
&::-moz-placeholder {
#content;
}
&:-ms-input-placeholder {
#content;
}
&::-webkit-input-placeholder {
#content;
}
&:placeholder {
#content;
}
&:invalid {
#content;
}
}
input {
#include style-input-placeholder() {
color: purple;
opacity: 0;
text-transform: uppercase;
font-family: 'Open Sans', Arial, sans-serif;
}
}
Also you can try to wire your sass compilation with PostCSS and use excellent Autoprefixer plugin that will free you from defining all these vendor-specific prefixes.

Related

#mixin scss with nested elements is possible?

I'm trying to use a mixin to make the code less repetitive, but I can't reach the nested elements.
Could anyone tell me if this approach is possible?
#mixin schema-calendar($backgroundColor: var(--ion-color-primary), $titleColor: var(--ion-color-secondary)) {
background-color: $backgroundColor !important;
.switch-btn,
ion-icon {
color: $titleColor !important;
text-transform: uppercase;
}
.week-toolbar li {
color: $titleColor !important;
font-weight: 900;
}
button.today p { color: $titleColor !important; }
button.on-selected {
p { color: var(--ion-color-secondary-contrast) !important;}
}
}
ion-calendar.schemaDefault {
#include schema-calendar();
}
ion-calendar.schemaBlackRed {
#include schema-calendar(var(--ion-card-black-red));
}
ion-calendar.schemaIndigo {
#include schema-calendar(var(--ion-card-indigo), var(--ion-color-light));
}
When used directly in css it works perfectly, I know that when scss is compiled to css the code is modified but the result is the same, but when I use the mixin according to the code above, only the background-color property is applied.
ion-calendar.schemaBlackRed {
// primaryColor: 'var(--ion-card-blackred)', secondaryColor: 'var(--ion-color-secondary)', titleColor: 'var(--ion-color-secondary)',
background-color: var(--ion-card-black-red) !important;
.switch-btn,
ion-icon {
color: var(--ion-color-secondary) !important;
text-transform: uppercase;
}
.week-toolbar li {
color: var(--ion-color-secondary) !important;
font-weight: 900;
}
button.today p { color: var(--ion-color-secondary) !important; }
button.on-selected {
p { color: var(--ion-color-secondary-contrast) !important;}
}
}
I found that the problem was occurring because I had included the style at the page level, moved the code from the initial Ionic load and everything worked as expected.

How to import a scss file inside a scss class

I want to add a different theme when i add "dark-theme" class to body. My implementation looks like this:
#import '../../../../node_modules/angular-grids/styles/material.scss';
.app-dark {
#import '../../../../node_modules/angular-grids/styles/material-dark.scss';
}
Without any luck. Any clue on how to do this?
There are two methods in order to do that. Both of them include mixins.
meta.load-css
The sass:meta feature gives the ability to do what you want.
Say you have this scss file with a theme:
//theme/_code.scss
$border-contrast: false !default;
code {
background-color: #6b717f;
color: #d2e1dd;
#if $border-contrast {
border-color: #dadbdf;
}
}
you can include that code inside another scss file like so:
// other-theme.scss
#use "sass:meta";
body.dark {
#include meta.load-css("theme/code",
$with: ("border-contrast": true));
}
This will result in the following css:
body.dark code {
background-color: #6b717f;
color: #d2e1dd;
border-color: #dadbdf;
}
You can read more about this feature here
old fashioned mixin
But you can do basically the same thing if you use mixin and include.
So, let's say you have this class you want to import into another class:
.title {
font-size: 2em;
font-weight: bold;
}
And another sass file with another theme:
.dark-theme {
.title {
font-size: 2em;
font-weight: bold;
color: white;
}
}
You can use a scss mixin and import it into both files:
mixin.scss
#mixin shared-items() {
.title {
font-size: 2em;
font-weight: bold;
}
}
then, in the theme files:
white-theme.scss
#import './mixin.scss';
/* will be included as is without a parent class */
#include shared-items;
dark-theme.scss
#import './mixin.scss';
/* will be included inside the dark-theme class */
.dark-theme {
.title {
color: white;
}
#include shared-items;
}
this will generate this css:
.title {
font-size: 2em;
font-weight: bold;
}
.dark-theme {
.title { color: white; }
.title {
font-size: 2em;
font-weight: bold;
}
}
Notice that you can also pass parameters to mixin and use them as functions.
So you can easily pass colors and use them with your theme variables.
for example:
# an example of giving a color to a placeholder mixin:
#mixin nk-placeholder($color: #C4C4CC) {
&::-webkit-input-placeholder {
color: $color;
font: inherit;
}
&::-moz-placeholder {
color: $color;
font: inherit;
}
&:-ms-input-placeholder {
color: $color;
font: inherit;
}
&:-moz-placeholder {
color: $color;
font: inherit;
}
&::placeholder {
color: $color;
font: inherit;
}
}
# same thing as above
#mixin shared-items($text-color: black) {
.title {
font-size: 2em;
font-weight: bold;
color: $text-color;
}
}
.white-theme {
#include shared-items;
}
.dark-theme {
#include shared-items(white);
}

How to add a "modified" class for an element in SCSS

Given this scss
.root {
color: red;
&-child {
color: blue;
small & {
font-size: 80%;
}
}
}
This is the CSS I get:
.root {
color: red;
}
.root-child {
color: blue;
}
small .root-child {
font-size: 80%;
}
I want to style .root-child on small differently so the rule I need is:
small.root-child {
font-size: 80%;
}
(Notice no whitespace after small)
How can I do that?
You need to use #at-root and that will remove the white space in your selector, as well as it will be a valid syntax so no issues while you try to compile.
.root {
color: red;
&-child {
color: blue;
#at-root small#{&} {
font-size: 80%;
}
}
}
You can use #at-root like this:
SCSS
.root {
color: red;
&-child {
color: blue;
#at-root {
small#{&} {
font-size: 80%;
}
}
}
}
Compiled:
.root {
color: red;
}
.root-child {
color: blue;
}
small.root-child {
font-size: 80%;
}

Sass referencing parent selectors using the ampersand character within nested selectors

Just when I thought Sass was the coolest thing since sliced bread, it had to go and let me down. I'm trying to use the ampersand to select a parent of a nested item. It's a complex selection and its returning some unexpected results...
My sass:
.page--about-us {
a {
text-decoration:none;
}
.fa-stack {
.fa {
color:pink;
}
a & {
&:hover {
.fa-circle-thin {
color:red;
}
.fa-twitter {
color:blue;
}
}
}
}
}
Outputted CSS:
.page--about-us a {
text-decoration: none;
}
.page--about-us .fa-stack .fa {
color: pink;
}
a .page--about-us .fa-stack:hover .fa-circle-thin {
color: red;
}
a .page--about-us .fa-stack:hover .fa-twitter {
color: blue;
}
Expected Output (Note the placement of the a tag):
.page--about-us a {
text-decoration: none;
}
.page--about-us .fa-stack .fa {
color: pink;
}
.page--about-us a .fa-stack:hover .fa-circle-thin {
color: red;
}
.page--about-us a .fa-stack:hover .fa-twitter {
color: blue;
}
Demo:
http://sassmeister.com/gist/8ed68bbe811bc9526f15
You can store the parent selector in a variable!
Take the following BEM-like SASS:
.content-block {
&__heading {
font-size: 2em;
}
&__body {
font-size: 1em;
}
&--featured {
&__heading {
font-size: 4em;
font-weight: bold;
}
}
}
The selector inside of .content-block--featured is going to be .content-block--featured .content-block--featured__heading which might not be what you're after.
It's not as elegant as the single ampersand but you can stash the parent selector into a variable! So to get what you might be after from the above example without hard-coding the parent selector:
.content-block {
$p: &; // store parent selector for nested use
&__heading {
font-size: 2em;
}
&__body {
font-size: 1em;
}
&--featured {
#{$p}__heading {
font-size: 4em;
font-weight: bold;
}
}
}
So, OP, in your case you might try something like this:
.page--about-us {
$about: &; // store about us selector
a {
text-decoration:none;
}
.fa-stack {
.fa {
color:pink;
}
#{$about} a & {
&:hover {
.fa-circle-thin {
color:red;
}
.fa-twitter {
color:blue;
}
}
}
}
}
This is the normal behavior, as described in Sass documentation (link):
& will be replaced with the parent selector as it appears in the CSS. This means that if you have a deeply nested rule, the parent selector will be fully resolved before the & is replaced.
Meaning:
.foo {
.bar {
.baz & {
color: red;
}
}
}
Will render as:
.baz .foo .bar {
color: red;
}
And not:
.baz .bar {
color: red;
}
The right way to get your expected result is this one:
.page--about-us {
a {
text-decoration:none;
.fa-stack:hover {
.fa-circle-thin {
color:red;
}
.fa-twitter {
color:blue;
}
}
}
.fa-stack {
.fa {
color:pink;
}
}
}

Mixin for Group Pseudo Classes with SASS

Ideally I want to create a mixin to style all the placeholder pseudo classes. I'm not sure if this is even possible. Here's what I have:
#mixin placeholder($color) {
::-webkit-input-placeholder{
color: $color;
}
:-moz-placeholder {
color: $color;
}
::-moz-placeholder {
color: $color;
}
:-ms-input-placeholder {
color: $color;
}
}
Which ideally would be used like this:
input {
&#placeholder(red);
}
And would result in this CSS:
input::-webkit-input-placeholder{
color: red;
}
input:-moz-placeholder {
color: red;
}

Resources