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);
}
I would like to contain all relevant styles for a selector within a single code block, so that it can be easily referenced.
In my application, a selectors effective styles will be altered dramatically depending on the context in which it sits. For instance, let's assume this CSS:
.container.theme-dark .message
{
font-size: 16px;
background-color: black;
color: white;
}
.container.theme-light .message
{
font-size: 16px;
background-color: white;
color: black;
}
Then, imagine I have the following HTML:
<div>
<div class="container theme-dark">
<div class="message">Hello World</div>
</div>
<div class="container theme-light">
<div class="message">Hello World</div>
</div>
</div>
Right now with SCSS, I would create the relevant CSS like this:
.container
{
&.theme-dark
{
.message
{
background-color: black;
color: white;
}
}
&.theme-light
{
.message
{
background-color: white;
color: black;
}
}
.message
{
font-size: 16px;
}
}
I want to be able to generate that CSS using SCSS, with all of the relevant information for the .message element in one place. For instance (using a made-up $ operator that would do what I'm trying to accomplish):
.container
{
.message
{
font-size: 16px;
$.theme-light
{
background-color: white;
color: black;
}
$.theme-dark
{
background-color: black;
color: white;
}
}
}
Any ideas?
I'm thinking this might work, and is like what you're saying? (It would help me currently if you labeled each example as "Ideal CSS output", "Current SCSS, too many .message blocks", and "Ideal SCSS format")
.container
{
#at-root .message
{
font-size: 16px;
.theme-light &
{
background-color: white;
color: black;
}
.theme-dark &
{
background-color: black;
color: white;
}
}
}
With the #at-root there, it will generate .theme-light .message, which might be too permissive for some usages, so not the ideal solution...
https://codepen.io/anon/pen/ZMxjEq
Basically & gets replaced with the full tree-path, so .container .message, which without #at-root, will generate .theme-light .container .message, which does not work with the structure. Perhaps also consider this, which makes a reasonable compromise I would say:
.container
{
.message
{
font-size: 16px;
}
#at-root .message
{
.theme-dark
{
...
}
.theme-light
{
...
}
}
}
It's apparently a kind of hacky solution, but apparently works
This page might have some better guidance as well
This organization can be achieved if you use sass programatically:
$themes: light dark;
.container {
#for $i from 1 through length($themes) {
&.theme-#{nth($themes,$i)} {
.message {
font-size: 16px;
#if nth($themes,$i) == light {
background-color: white;
color: black;
} #else if nth($themes,$i) == dark {
background-color: black;
color: white;
}
}
}
}
}
This generates:
.container.theme-light .message {
font-size: 16px;
background-color: white;
color: black;
}
.container.theme-dark .message {
font-size: 16px;
background-color: black;
color: white;
}
The nested looping automatically groups the details at each level in the same block of code. This also scales to multiple levels of nesting. The critical point is that at inner loops you can reference the selectors of outer loops.
I eventually found this GitHub:
https://github.com/imkremen/sass-parent-append/blob/master/parrent-append.scss
Which I have adapted into this solution:
#function str-to-list($string, $separator: ' ', $startAt: 1)
{
$workStr: str-slice($string, $startAt);
$list: ();
$indexOfFirstSpace: str-index($workStr, $separator);
#if ($indexOfFirstSpace == null)
{
$list: ($workStr);
}
#else
{
$list: (str-slice($workStr, 1, $indexOfFirstSpace - 1));
$list: join($list, str-to-list($workStr, $startAt: $indexOfFirstSpace + 1));
}
#return $list;
}
#function getBase($appendix)
{
$parentSelector: str-to-list(#{&});
$pos: (length($parentSelector) - 1);
$selector: set-nth($parentSelector, $pos, nth($parentSelector, $pos) + $appendix);
#return $selector;
}
#mixin base($appendix)
{
#at-root #{getBase($appendix)}
{
#content;
}
}
Which I can then use like this:
.container
{
.message
{
font-size: 16px;
}
#include base(".theme-light")
{
background-color: white;
color: black;
}
#include base(".theme-dark")
{
background-color: black;
color: white;
}
}
which compiles into this:
.container .message
{
font-size: 16px;
}
.container.theme-light .message
{
background-color: white;
color: black;
}
.container.theme-dark .message
{
background-color: black;
color: white;
}
Lets say that I have the following:
.class-1 {
font-weight: bold;
p{
color: red;
}
}
And I want to define in LESS a class-2 that has the same styling with the p element in the class-1. Is there any way to do so other than writing the code?
if I understand you correctly, you want to style your p if it's the child of class-1 or class-2. but class-1 and class-2 should have different styling.
I'd recommend writing it out twice or a different approach, but you can use mixins if you don't want to write it out twice.
LESS
.p() {
color:red;
}
.class-1 {
font-weight: bold;
p{
.p;
}
}
.class-2{
p{
.p;
}
}
CSS output
.class-1 {
font-weight: bold;
}
.class-1 p {
color: red;
}
.class-2 p {
color: red;
}
.class-1, .class-2 {
font-weight: bold;
p{
color: red;
}
}
I'm using Sass 3.4.1 and BEM so my scss is:
.photo-of-the-day{
&--title{
font-size: 16px;
}
}
and I want every time hover over .photo-of-the-day something happen with title, that's pretty common so usually in css:
.photo-of-the-day:hover .photo-of-the-day--title{
font-size:12px
}
the thing is using BEM this is the only way I found and looks kinda ugly
.photo-of-the-day{
&--title{
font-size: 16px;
}
&:hover{
background: red;
/* this is ugly */
.photo-of-the-day--title{
text-decoration: underline;
}
}
}
so I was wondering if I can inherit .photo-of-the-day selector and use it inside the hover to avoid copy again the full selector.
Ideally would be something like:
.photo-of-the-day{
&--title{
font-size: 16px;
}
&:hover{
background: red;
&&--title{
text-decoration: underline;
}
}
}
Or something close to comeback to the parent selector for BEM. Is it possible?
If you insist on nesting everything, the best you can do is this:
.photo-of-the-day {
$root: &;
&--title{
font-size: 16px;
}
&:hover{
#{$root}--title {
text-decoration: underline;
}
}
}
You can use this syntax:
.photo-of-the-day {
&--title {
font-size: 16px;
}
&:hover &--title {
text-decoration: underline;
}
}
Say I've got:
.apple {
color: red;
}
Now, let's say I've also got:
.big {
.apple {
font-size: 1.25em;
}
}
Is there a way I can put the .big selector inside the rule for .apple? In psuedocode, something like:
.apple {
color: red;
&:[WHEN INSIDE `.big`] {
font-size: 1.25em;
}
}
You place the & at the end:
.apple {
color: red;
.big & {
font-size: 1.25em;
}
}