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

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

Related

Render comma separated selectors separately in SCSS

I'm trying to style progress bars using SCSS. To get this working in both Webkit and Gecko browsers I need to use both -webkit and -moz prefixes:
progress {
height: 50px;
-webkit-appearance: none;
appearance: none;
background: cyan;
&::-moz-progress-bar,
&::-webkit-progress-value {
background-color: orange;
}
&::-webkit-progress-bar {
background-color: cyan;
}
}
which renders to
progress {
height: 50px;
-webkit-appearance: none;
appearance: none;
background: cyan;
}
progress::-moz-progress-bar, progress::-webkit-progress-value {
background-color: orange;
}
progress::-webkit-progress-bar {
background-color: cyan;
}
This works great in Firefox, but Chrome doesn't seem to like it. Compare the following two implementations:
Comma separated selectors
progress {
height: 50px;
-webkit-appearance: none;
appearance: none;
background: cyan;
}
progress::-moz-progress-bar, progress::-webkit-progress-value {
background-color: orange;
}
progress::-webkit-progress-bar {
background-color: cyan;
}
<progress max="1" value="0.5"></progress>
Entirely separate declarations
progress {
height: 50px;
-webkit-appearance: none;
appearance: none;
background: cyan;
}
progress::-webkit-progress-value {
background-color: orange;
}
progress::-moz-progress-bar {
background-color: orange;
}
progress::-webkit-progress-bar {
background-color: cyan;
}
<progress max="1" value="0.5"></progress>
The above code snippets render in Firefox and Chrome as shown below
Firefox
Chrome
comma separated
separate declarations
It seems like the problem comes from rendering the CSS with vendor-specific pseudos in comma-separated lists. Is there any way to force the SASS processor to render each selector in a comma separated list as its own declaration?
It would be nice to not use mix-ins, but if it's the only way it's the only way.
Yes, you are able to do so! If you want to render SASS to seperate CSS rules simply divide the comma seperated list into two seperate rules. SASS keeps different rules seperate and will not wrap them together. Example:
// ### > SASS
xprogress {
height: 50px;
appearance: none;
background: cyan;
//## divide comma seperated selectors
//## into different rules
&::-moz-progress-bar {
background-color: orange;
}
&::-webkit-progress-value {
background-color: orange;
}
&::-webkit-progress-bar {
background-color: cyan;
}
}
// ### > compiles to css
progress {
height: 50px;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background: cyan;
}
//## when compiling CSS
//## different rules will survive
progress::-moz-progress-bar {
background-color: orange;
}
progress::-webkit-progress-value {
background-color: orange;
}
progress::-webkit-progress-bar {
background-color: cyan;
}

Reuse just specific property of another class in scss

it is possible to reuse all properties of another class in scss using the #extend method.
.class1 {
height: 10px;
width: 10px;
}
.class2 {
#extend .class1;
border-color: #000000;
}
Which will result in
.class1, .class2 {
height: 10px;
width: 10px;
}
.class2 {
border-color: #000000;
}
Is it possible, to reuse only custom properties of another class? Something like
.class2 {
#extend .class1.height;
border-color: #000000;
}
Which results in something like this
.class1 {
height: 10px;
width: 10px;
}
.class2 {
height: 10px;
border-color: #000000;
}
Thank you very much.
you can, but for that you need to extend a placeholder rather than a class. you abstract the desired properties to a placeholder, and apply at desired classes:
%custom-height {
height: 10px;
}
.class1 {
#extend %custom-height;
width: 10px;
}
.class2 {
#extend %custom-height;
border-color: #000000;
}
which will result in:
.class1, .class2 {
height: 10px;
}
.class1 {
width: 10px;
}
.class2 {
border-color: #000000;
}

In SASS, How to refer tag select?

In SASS's class selecter, I want to select parent's sibling.
.bw-textarea {
height: 150px;
padding: 10px;
overflow-y: auto;
background: white;
text-align: left;
width: 100%;
border: 1px solid #eeeeee;
font-size: 12px !important;
color: #666666;
// textarea:disabled & {
// background: #efefef;
// }
}
Compiled above sass code,
.bw-textarea textarea:disabled {
background: #efefef;
}
But I want to show result like this css code...
How to select like this in sass?
textarea.bw-textarea:disabled {
background: #efefef;
}
You gotta use #root in this case. It's pretty simple
This link will give a clear idea about this selector
.bw-textarea {
#at-root textarea#{&}:disabled{
background: cyan;
}
}
This will compile to
textarea.bw-textarea:disabled {
background: cyan;
}
See this PEN
This is what your looking for:
.bw-textarea {
height: 150px;
padding: 10px;
overflow-y: auto;
background: white;
text-align: left;
width: 100%;
border: 1px solid #eeeeee;
font-size: 12px !important;
color: #666666;
&:disabled {
background: #efefef;
}
}
Check out this SO answer for a lil more idea on the nested pseudo selectors
Sass parent selector and hover?
And of course check the sass docs
https://sass-lang.com/documentation/file.SASS_REFERENCE.html

Not able to find pre-defined sass variables in Jekyll project

I'm new the Jeykll and Sass and have a quick question. Within the pre-built theme when starting a new project, where are these variables defined?
I can't seem to find them aware through out the build.
This is in the _sass/layout.scss directory.
*/
.site-header {
border-top: 5px solid $grey-color-dark;
border-bottom: 1px solid $grey-color-light;
min-height: 56px;
// Positioning context for the mobile navigation icon
position: relative;
}
.site-title {
font-size: 26px;
line-height: 56px;
letter-spacing: -1px;
margin-bottom: 0;
float: left;
&,
&:visited {
color: $grey-color-dark;
}
}
.site-nav {
float: right;
line-height: 56px;
.menu-icon {
display: none;
}
.page-link {
color: $text-color;
line-height: $base-line-height;
// Gaps between nav items, but not on the first one
&:not(:first-child) {
margin-left: 20px;
}
}
#include media-query($on-palm) {
position: absolute;
top: 9px;
right: 30px;
background-color: $background-color;
border: 1px solid $grey-color-light;
border-radius: 5px;
text-align: right;
.menu-icon {
display: block;
float: right;
width: 36px;
height: 26px;
line-height: 0;
padding-top: 10px;
text-align: center;
> svg {
width: 18px;
height: 15px;
path {
fill: $grey-color-dark;
}
}
}
.trigger {
clear: both;
display: none;
}
&:hover .trigger {
display: block;
padding-bottom: 5px;
}
.page-link {
display: block;
padding: 5px 10px;
}
}
}
/**
* Site footer
*/
.site-footer {
border-top: 1px solid $grey-color-light;
padding: $spacing-unit 0;
}
.footer-heading {
font-size: 18px;
margin-bottom: $spacing-unit / 2;
}
.contact-list,
.social-media-list {
list-style: none;
margin-left: 0;
}
.footer-col-wrapper {
font-size: 15px;
color: $grey-color;
margin-left: -$spacing-unit / 2;
#extend %clearfix;
}
.footer-col {
float: left;
margin-bottom: $spacing-unit / 2;
padding-left: $spacing-unit / 2;
}
.footer-col-1 {
width: -webkit-calc(35% - (#{$spacing-unit} / 2));
width: calc(35% - (#{$spacing-unit} / 2));
}
.footer-col-2 {
width: -webkit-calc(20% - (#{$spacing-unit} / 2));
width: calc(20% - (#{$spacing-unit} / 2));
}
.footer-col-3 {
width: -webkit-calc(45% - (#{$spacing-unit} / 2));
width: calc(45% - (#{$spacing-unit} / 2));
}
#include media-query($on-laptop) {
.footer-col-1,
.footer-col-2 {
width: -webkit-calc(50% - (#{$spacing-unit} / 2));
width: calc(50% - (#{$spacing-unit} / 2));
}
.footer-col-3 {
width: -webkit-calc(100% - (#{$spacing-unit} / 2));
width: calc(100% - (#{$spacing-unit} / 2));
}
}
#include media-query($on-palm) {
.footer-col {
float: none;
width: -webkit-calc(100% - (#{$spacing-unit} / 2));
width: calc(100% - (#{$spacing-unit} / 2));
}
}
/**
* Page content
*/
.page-content {
padding: $spacing-unit 0;
}
.page-heading {
font-size: 20px;
}
.post-list {
margin-left: 0;
list-style: none;
> li {
margin-bottom: $spacing-unit;
}
}
.post-meta {
font-size: $small-font-size;
color: $grey-color;
}
.post-link {
display: block;
font-size: 24px;
}
/**
* Posts
*/
.post-header {
margin-bottom: $spacing-unit;
}
.post-title {
font-size: 42px;
letter-spacing: -1px;
line-height: 1;
#include media-query($on-laptop) {
font-size: 36px;
}
}
.post-content {
margin-bottom: $spacing-unit;
h2 {
font-size: 32px;
#include media-query($on-laptop) {
font-size: 28px;
}
}
h3 {
font-size: 26px;
#include media-query($on-laptop) {
font-size: 22px;
}
}
h4 {
font-size: 20px;
#include media-query($on-laptop) {
font-size: 18px;
}
}
}
Notice: Jekyll has since been updated, its default theme, minima, is now in a separate gem, and the definitions are there, in _sass/minima.scss.
Look at this, with relative path to site root in default Jekyl site css/main.scss. There they are. After all the definitions, imports and processing, there will be css/main.css in the resulting site.
Everything in _sass is just for importing into real assets that will be converted to CSS. The difference between Sass/SCSS files in _sass and everywhere else is that files from _sass are not compiled directly, but rather just lay where they are and wait to be imported. Every possibly reusable piece of Sass should be in _sass, that's what Sass partials and this system overall is designed for. This is especially useful if you use a lot of Sass libraries (like I do, and it's still a mess) and don't want them to be modified in order to be put into your assets.
All the other files (not in _sass), however, will be processed into corresponding *.css files, and in order to distinguish files for processing, Jekyll requires such files to have a YAML front-matter, usually empty.
---
---
// Sass code here
(I used to utilize Liquid for embedding variables in Sass, but that feature vanished after some updates, so now non-empty YFM in assets is of no use now).

Custom directives within scss files? Perhaps for a new pseudo class?

Is it be possible to use some sort of #directive creation syntax, similar to creating #mixins? Secondly, is it possible to create a SASS-only pseudo class?
I'd like to declare my own SASS directive,although I'd prefer not to have to force my teammates to install an extra ruby gem to use it so I'd want to store it in a scss partial. I do understand that they are orders of levels in complexity, so perhaps it just isn't possible.
In addition to perhaps creating a new scss-only pseudo class (such as :parent, :clicked, :unchecked, etc) I'd be interested in a custom-made directive that assists with using checkboxes to direct css animations ("css checkbox hack"). Here is my scss pseudocode to generalize what I'm trying to do:
// this code monitors when a checkbox (#someinput) is checked,
// then applies style to div #target div. Basically an 'onClick' event in scss.
body {
#wrapper {
#targetdiv {
#spotcheck(#someinput) { #
color: red; border: 2px solid blue; # <-- move this ...
} #
color: blue; border: 0;
#someinput {
width: 20px; height: 20px;
}
}
}
}
// ... the above scss should be converted to this pre-compiled state, also scss
body {
#someinput:checked ~ #targetdiv { #
color: red; border: 2px solid blue; # <-- ..to here. it needs to be
} # above the #targetdiv
#wrapper {
#targetdiv {
color: blue; border: 0;
#someinput {
width: 20px; height: 20px;
}
}
}
}
Make your selectors only as specific as they absolutely need to be and no more. A mixin would only be more verbose with no real benefit.
#targetdiv {
color: blue; border: 0;
#someinput:checked ~ & {
color: red; border: 2px solid blue;
}
}
#someinput {
width: 20px; height: 20px;
}
Output:
#targetdiv {
color: blue;
border: 0;
}
#someinput:checked ~ #targetdiv {
color: red;
border: 2px solid blue;
}
#someinput {
width: 20px;
height: 20px;
}
Alternately, this would give the same result with the ability to overqualify as much as you want:
#targetdiv {
color: blue; border: 0;
}
#someinput {
width: 20px; height: 20px;
~ #targetdiv {
color: red; border: 2px solid blue;
}
}

Resources