Getting Sass to parse browser hacks - css

What's the best approach to getting Sass (3.4.15) to parse browser CSS property hacks - not using compass or any other library. E.g. '_property' or '*property'.
.hack-test{
display: inline-block;
display: *inline;
}
Invalid CSS after " display: ": expected expression (e.g. 1px, bold), was "*inline;"
I searched around Stack Overflow but could not find anything that could definitively answer this.

You can use sass strings, in such a way that the invalid css is injected as a string.
I put it all inside a mixin for re-use:
$star: "*inline";
#mixin hack-test($selector) {
#{$selector} {
display: inline-block;
display: #{$star};
}
}
So if you try and use the mixin:
#include hack-test('.foo');
The css output will be as desired:
.foo {
display: inline-block;
display: *inline;
}
You can view some SCSS browser hacks I've put on Github here

Related

Compile non-root CSS custom property

Are there any tools to compile CSS custom properties declared at not :root rule? I want following code with custom properties
.dark {
--bg-color: black;
--fg-color: white;
}
.light {
--bg-color: white;
--fg-color: black;
}
.foo {
background: var(--bg-color);
display: block;
}
.bar {
color: var(--fg-color);
display: inline;
}
be compiled to their non-custom-prop equivalents like that
.light .foo, .light.foo {
background: white;
}
.dark .foo, .dark.foo {
background: black;
}
.light .bar, .light.bar {
color: black;
}
.dark .bar, .dark.bar {
color: white;
}
.foo {
display: block;
}
.bar {
display: inline;
}
The goal is to
switch color schemes by switching dark/light class on root DOM element
use valid css syntax (no sass less)
keep rules code compact
It's actually not safe to do that. I can tell you because I tried so hard to make a safe transformation.
But I failed.
https://github.com/postcss/postcss-custom-properties/issues/1
Ideal solution. Your example is valid CSS and can be used in many browsers (not in IE, Edge (but is in development) and Opera Mini as of writing this answer, 2017-03-27, other major browsers are fine).
Suboptimal solution. Some CSS can be transpiled to achieve better browser support. The solution I found does not support variables on non-:root elements, however. There are also other objections against transpiling of 'future' CSS into 'current' CSS. To the best of my knowledge, you will have to implement your own transpiler (or postcss plugin) if you want to transpile custom properties not on the :root element, but be warned that that is hard in general. Now you don't need the general part, so it is possible. Just does, to the best of my knowledge, not exist yet.
Preprocessing solution. Of course, you don't need a general implementation of custom properties. You have different themes that have their own values for the same set of properties and that's it. Thus, a separate stylesheet can be created as a preprocessing step using any CSS preprocessor.
Now you say the following,
use valid css syntax (no sass less)
but I am going to show this anyway, because I believe that it is a valid solution to your problem. It is definitely the only one I know that actually works if you want to/need to support IE, Edge and/or older versions of other major browsers (Firefox < 31, Chrome < 49, Safari < 9.1, Opera < 36)
You could do this using SASS for example, to do the transpiling on the server side.
// define styles, use variables throughout them
// your entire style definition goes into this mixin
#mixin myStyles($fg-color, $bg-color) {
.foo {
display: block;
background: $bg-color;
}
.bar {
display: inline;
color: $fg-color;
}
}
// define themes, that set variables for the above styles
// use named arguments for clarity
.dark {
#include myStyles(
$fg-color: white,
$bg-color: black
);
}
.light {
#include myStyles(
$fg-color: black,
$bg-color: white
);
}
This compiles to the following.
.dark .foo {
display: block;
background: black;
}
.dark .bar {
display: inline;
color: white;
}
.light .foo {
display: block;
background: white;
}
.light .bar {
display: inline;
color: black;
}
This is not exactly what you want to obtain, but very close. Realistically, I think this is the closest you will get to obtaining your desired output. I know you want to
keep rules code compact
but what you are saying there (I think) is that you want to split out custom properties from their rules to save on number of rules, which is not something any preprocessor I know supports.
You can organize your source SASS in separate files to keep an overview easily. You can even set up a build system that generates a separate stylesheet for every theme you have. It is then possible to have your users select an alternative stylesheet. Browsers have some support for this, but switching using JavaScript is also definitely possible in the latter case. Simply set all stylesheets to be disabled except for the selected one. Here is an example.

Simplifying comma separated CSS selectors with common prefix/suffix [duplicate]

This question already has answers here:
Succinct way of specifying two or more values for an attribute in css selector
(2 answers)
Closed 6 years ago.
Is it possible to simplify comma separated CSS selectors with common prefix/suffix?
My current style looks like this (much longer though):
html:lang(qw) div[data-domain*='abc.com'], html:lang(qw) div[data-domain*='def.com'], html:lang(qw) div[data-domain*='ghi.com'], html:lang(qw) div[data-domain*='jkl.com'] {
display: none!important;
}
I'm wondering if something like the following would be possible:
html:lang(qw) div[data-domain*=('abc.com', 'def.com', 'ghi.com', 'jkl.com')] {
display: none!important;
}
As per the comments, this is simply not possible with plain CSS right now. Your only option to shorten the selector is to use a pre-processor, like SASS (Syntactically Awesome StyleSheets). SASS allows you to write more readable, shorter code. You can compile a SASS (*.scss) file to plain CSS on your own computer, so by the time it's on the server, it's the plain old CSS you are used to, understood by all browsers. No extra requirement from your users.
For this particular case, you could use a for-each loop.
#each $domain in 'abc.com', 'def.com', 'ghi.com', 'jkl.com' {
html:lang(qw) div[data-domain*='#{$domain}'] {
display: none !important;
}
}
This would result in the following CSS:
html:lang(qw) div[data-domain*='abc.com'] {
display: none !important;
}
html:lang(qw) div[data-domain*='def.com'] {
display: none !important;
}
html:lang(qw) div[data-domain*='ghi.com'] {
display: none !important;
}
html:lang(qw) div[data-domain*='jkl.com'] {
display: none !important;
}

How to use & selector in Sass version 3.4.22?

I have the following piece of code in my .scss file:
.navbar {
min-height: 30px;
&-nav{
li{
a {
padding-top: 5px;
padding-bottom: 5px;
}
}
}
}
It is generating the following error:
Syntax error: Invalid CSS after " &:" expected "{", was "-nav{"
Basically, I want my resulting selector to be .navbar-nav, can anyone let me know how to implement this in Sass 3.4.22
Remove the brackets and semicolons and use indentation:
.navbar
min-height: 30px
&-nav
li
a
padding-top: 5px
padding-bottom: 5px
From RubyDoc:
Sass has two syntaxes. The new main syntax (as of Sass 3) is known as
"SCSS" (for "Sassy CSS"), and is a superset of CSS's syntax. This
means that every valid CSS stylesheet is valid SCSS as well. SCSS
files use the extension .scss.
The second, older syntax is known as the indented syntax (or just
"Sass"). Inspired by Haml's terseness, it's intended for people who
prefer conciseness over similarity to CSS. Instead of brackets and
semicolons, it uses the indentation of lines to specify blocks.
Although no longer the primary syntax, the indented syntax will
continue to be supported. Files in the indented syntax use the
extension .sass.
If you put your example to http://www.sassmeister.com/ it works as expected. They're using v3.4.21.
Maybe try wrapping the & symbol with interpolation helpers #{&}:
.navbar {
min-height: 30px;
#{&}-nav {
li{
a {
padding-top: 5px;
padding-bottom: 5px;
}
}
}
}
It should produce the same result.

SASS and Bootstrap - mixins vs. #extend

I'm using the SASS port of Bootstrap, and I'm wondering if there's any difference between using the pre-defined mixins and using SASS's #extend.
For instance, if I have:
<div class="wrapper">
Some content here....
</div>
Is there any difference between doing
.wrapper {
#include make-row();
}
and
.wrapper {
#extend .row;
}
?
If there's no difference, are there other mixins that aren't equivalent to a single #extend statement? If there aren't such mixins, why do the mixins even exist?
The big difference between #extend and a mixin is the way the css is compiled. It doesn't look like much in simple examples, but the differences and implications are significant and can be a real headache in the wild if used carelessly. #extend is a little bit like fools gold, looks great at first, but ...
Let's look at a simple example:
#extend
.row {
width: 50px;
}
.new-row {
#extend .row;
}
.another-row {
#extend .row;
}
compiles into:
.row,
.new-row,
.another-row {
width: 50px;
}
mixin
#mixin row() {
width: 50px;
}
.new-row {
#include row();
}
.another-row {
#include row();
}
compiles into:
.new-row {
width: 50px;
}
.another-row {
width: 50px;
}
A mixin includes the properties everywhere it is hit - copying them each time - whereas an #extend groups the selectors and defines the properties once. This isn't immediately obvious, because the difference is in the compiled css but it has some important implications:
Load order
With #extend the selectors will be grouped at the first point in the sass where they are encountered which can lead to some weird over-riding. If you define a selector and use #extend to bring in a property to and try to override a property defined earlier in your sass, but after the point at which the extended properties are grouped in the css then the override will not work. This can be quite perplexing.
Consider this logically ordered set of css definitions and the likely HTML: <div class='row highlight-row'></div>:
.red-text {
color: red;
}
.row {
color: green;
}
.highlight-row {
#extend .red-text;
}
compiles into:
.red-text,
.highlight-row {
color: red;
}
.row {
color: green;
}
So even though the sass ordering makes it look like the row colour would be red, the compiled css will make it green
Poor groupings
#extend can result in poorly grouped selectors in the resulting css. You can end up with thirty or forty unrelated things all sharing the same property for example. Using #extend for fonts is a good example of this.
Nesting
If you are using deeply nested sass (which is not good, btw) and you use #extend you will duplicate the fully nested selector for every #extend you use, resulting in bloated css. I've seen this a lot:
.selector-1 .selector-2 .selector-3 .selector-4,
.selector-1 .selector-2 .selector-3 .selector-4 a,
.selector-1 .selector-2 .selector-3 .selector-4 li,
.selector-1 .selector-2 .selector-3 .selector-4 td {
font-family: arial;
}
If you're new to SASS it pays to look at the compiled css.
Media queries
#extend do not work inside media queries, because media queries are not selectors.
Conclusion
My rule of thumb is to use an #extend over a mixin if you have no parameters and if you can reasonably define the #extend and share it amongst a few tightly related selectors that exist nearby in the sass, for example, in the same file that defines a sass module. Buttons are a good example of well used #extend:
%button {
padding: 10px;
}
.call-to-action {
#extend %button;
background-color: $green;
}
.submit {
#extend %button;
background-color: $grey;
}
The best article to help make the choice is here
PS, the % sign is a use of placeholder extends

How to use !default in a SASS mixin?

I have this SASS mixin:
#mixin micro-clearfix
&:after,
&:before
content: ""
display: table
&:after
clear: both
* html &
height: 1% !default
*+html &
min-height: 1% !default
Unfortunately, it does not compile, unless I remove !default which would be the point of having this mixin.
The error message I'm getting is:
Invalid CSS after "1% ": expected expression (e.g. 1px, bold), was "!default")
What I'd like to achieve is that if height (or min-height) has already been defined for the selector then the mixin should use that value, otherwise it should define this property as 1%.
I don't wish to use zoom since that's not a valid property and I like to keep my CSS clean.
Am I using !default the wrong way?
I have Compass 0.12.1 and SASS 3.1.10.
!default is intended to use only when declaring variables: http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#variable_defaults_
What you are trying to do should be done with CSS !important declaration, which should be used in the rule outside the mixin (the one you want to prevail). Anyway, using !important usually it's not a good practice. Maybe you could rely on cascade or specificity.
Here's how I've done it finally:
#mixin micro-clearfix
$minHeight: 1% !default
&:after,
&:before
content: ""
display: table
&:after
clear: both
* html &
height: $minHeight
*+html &
min-height: $minHeight

Resources