Using env(safe-area-inset-top) in SCSS with max() function - css

I am developing a site which I wish to display correctly on devices with a notch (particularly the iPhone X as I own one).
In this page the following code sample is given:
#supports(padding: max(0px)) {
.post {
padding-left: max(12px, env(safe-area-inset-left));
padding-right: max(12px, env(safe-area-inset-right));
}
}
However when I have this set, in Chrome I can see that it's not valid (see photo linked below)
Is there a way to correct this or can an SCSS #if statement be used to detect if a parent element has padding > 0 and if not add 1rem of padding to it?
My problem is not the one mentioned here, this is how I am using the code, I have also tried putting this in a standard CSS file without the unquote however its not working either.

If I read your question right you are referring to the css max function not the max function provided by Sass - also the example is CSS and hence needs the mentioned Sass 'hack' to work in SCSS.
The first thing you need to deal with is the iOS 11.0 - 11.2 implementation using constant. The easiest way to do this is to assign the safe-area-inset to CSS variables.
In the example below I've created a default value of 0px for all variables – but you could also use fallback values when using the variables var(--some-var, 12px) (uses 12px if --some-var is not defined).
The second part is your code using the --safe-area-inset variables.
I hope it makes sense :-)
:root {
/* -------------------------------------------------------------------
Assign the default/constant/env values to CSS variables
*/
--safe-area-inset-top : 0px;
--safe-area-inset-right : 0px;
--safe-area-inset-bottom: 0px;
--safe-area-inset-left : 0px;
/* it is probably safe to skip the `constant` test in 2023 :) */
#supports (top: constant(safe-area-inset-top)){
--safe-area-inset-top : constant(safe-area-inset-top);
--safe-area-inset-right : constant(safe-area-inset-right);
--safe-area-inset-bottom: constant(safe-area-inset-bottom);
--safe-area-inset-left : constant(safe-area-inset-left);
}
#supports (top: env(safe-area-inset-top)){
--safe-area-inset-top : env(safe-area-inset-top);
--safe-area-inset-right : env(safe-area-inset-right);
--safe-area-inset-bottom: env(safe-area-inset-bottom);
--safe-area-inset-left : env(safe-area-inset-left);
}
}
#supports(padding: Max(0px)) {
.post {
/* -------------------------------------------------------------------
Use the CSS variables in the max function
*/
padding-left: Max(12px, var(--safe-area-inset-left));
padding-right: Max(12px, var(--safe-area-inset-right));
}
}

You need to unquote max in #supports too, the referenced example should be:
#supports(padding: unquote('max(0px)')) {
padding-left: unquote('max(#{$susy-gutter-width}, env(safe-area-inset-left))');
padding-right: unquote('max(#{$susy-gutter-width}, env(safe-area-inset-right))');
}

Related

Creating custom unit for sass

I want to create custom css unit, that I'll be able to use in sass with node.js. Is there any guide about creating sass plugin for this? Just for example, I want to create unit "dpx", that will work as double pixel, so "width: 20dpx" will be processed to "width: 40px".
Other solution (not sass plugin), that can work with node is also acceptable.
Use a SASS function that accepts a font-size and returns the value doubled.
#function dpx($size) {
#return $size * 2;
}
div {
font-size: dpx(20px); // output: font-size: 40px;
}
As a simplified version of the current answer, you could also write the following:
$d: 2px;
div { font-size: 20*$d; }
I know this is an old question, but since I found it, other people will find it too.
In such case as yours a good solution would be to make a 1rem equal to 2px.
You can do it this way:
html {
font-size: 2px;
}
now each 1rem will be equal to 2px. If you want to make sure this doesn't break your current page, you can always add
body {
font-size: 8rem;
}
to set the global font-size to 16px (just a guess since this is a default value).

SASS Customize Class Names with Variables

Is there any way to customize the variables in SASS?
For example:
.m-b-{$number} {
margin-bottom: $number;
}
If I give class="m-b-50" to an element, it should take margin-bottom 50. I just want to know if it is possible with SASS.
Yes it is possible with the help of variable interpolation or variable substitution which uses #{} for variable substitution in SASS and mixins which is a block of code just like function.
Interpolation is the process of evaluating an expression or a string containing one or more variables, yielding a result in which the variables are replaced with their corresponding values.
Simple example of interpolation and set values to the css property in SASS:
$number:60;
$n: 20px;
.m-b-#{$number}{
margin-bottom: #{$number}px;
margin-top: $n;
}
To create customize class names, will use mixins:
#mixin margin-class($side, $number) {
$firstLetter: str-slice($side, 0, 1);
.m-#{$firstLetter}-#{$number}{
margin-#{$side}: #{$number}px;
}
}
$margins: (10, 20);
$sides: ("top", "right", "bottom", "left");
#mixin generate-margin(){
#each $margin in $margins{
#each $side in $sides{
#include margin-class($side, $margin);
}
}
}
#include generate-margin();
Here, generate-margin() will get executed which will call margin-class() for each $margins and $sides, and will generate the below CSS classes:
.m-t-10 {
margin-top: 10px;
}
.m-r-10 {
margin-right: 10px;
}
.m-b-10 {
margin-bottom: 10px;
}
.m-l-10 {
margin-left: 10px;
}
.m-t-20 {
margin-top: 20px;
}
.m-r-20 {
margin-right: 20px;
}
.m-b-20 {
margin-bottom: 20px;
}
.m-l-20 {
margin-left: 20px;
}
That's the one way when you want only for specific values, but if you want to create margin class for 0-20, you can loop thru 0 to 20 as shown below:
#mixin generate-margin(){
#for $margin from 1 through 20{
#each $side in $sides{
#include margin-class($side, $margin);
}
}
}
For anyone else facing this issue, here is how one can achieve this:-
#for $i from 1 through 10 {
.mb-#{$i} {
margin-bottom: #{$i}rem;
}
}
The answer is: no it is not possible. SASS is just a language to pre-generate CSS for you. There is no on-demand, dynamic creation of classes triggered by the contents of your HTML markup. When it comes time for the browser to render your HTML and apply your specified classes, it is still just using CSS. I.e. if you assign class="m-b-50" to an element, the class .m-b-50 must already be explicitly defined somewhere. As noted in the other answers, SASS can make it easier to generate a bunch of pre-defined classes but you must know which values you want to support up front.
Now, you could generate classes for some very large, all-inclusive range like -1000 to 1000 to effectively support all values you might ever try to use and it would seem to do what you wanted, but you would be forcing your users to download a larger CSS file with, most likely, a large percentage of it being unused CSS which is wasteful and can be inconsiderate in a world of paid & limited data plans.

What is the use of parent selector (&) alone as a selector? Is it bad practice to use such selectors?

After reading tutorial after tutorial regarding Less (LessCSS), I was just wondering how this & operator is supposed to be used. I know it's referring the parent element like:
div {
&.fullheight {
height: 100%;
}
}
// turns into
div.fullheight {
height: 100%;
}
But I often saw this:
div {
span {
& {
padding: 1em;
margin: 1em;
}
}
}
// turns into
div span {
padding: 1em;
margin: 1em;
}
Like when using ONLY the & operator inside of a class, it represents pretty much the parent element, but is doing this bad practise since you can have the same result when you would type like this:
div {
span {
padding: 1em;
margin: 1em;
}
}
Both work, so is it bad/good practise or are each of them maybe used in different situations?
For extra clarity, below is the link to an answer where I first saw that you can write & only in a class without anything else.
LESSCSS - use calculation and return value - First post by ScottS, fourth solution in his post.
Generally writing something like below would be considered as bad practice because the & there is just redundant and does no value add at all. It just outputs the entire parent selector div span.
div {
span {
& {
padding: 1em;
margin: 1em;
}
}
}
So, you should avoid writing such selectors which use only the & (parent selector).
The other example to which you have linked is an interesting case which I would term as an educated hack to get around the variable scoping and lazy loading concepts in Less.
Assume that the same code was written without the parent selectors (like below).
#unit:em;
#basevalue:1;
#val: 1;
#setUnit: unit(#basevalue*#val, #unit);
.someAwesomeClass {
#val: .2;
padding: #setUnit;
#val: .1;
margin: #setUnit;
}
Here the #val variable is declared twice within the same block. Since Less does lazy loading of the variables, they need not be declared before being used (and) if the same variable is declared twice or more within the same scope, the last declaration would win.
When defining a variable twice, the last definition of the variable is used, searching from the current scope upwards. This is similar to CSS itself where the last property inside a definition is used to determine the value.
So, the compiled CSS output would have the value as 0.1em for both padding and margin whereas the expectation is for padding to be 0.2em and for margin to be 0.1em.
To overcome this, the author of that answer has introduced two namespaces (with no name) and has thus restricted the scoping issue. The variable defined within each nested block becomes local to that block only and so will be considered as two separate variables.
#unit:em;
#basevalue:1;
#val: 1;
#setUnit: unit(#basevalue*#val, #unit);
.someAwesomeClass {
&{
#val: .2; /* this declaration applies only within this nest */
padding: #setUnit;
}
&{
#val: .1; /* this declaration applies only within this nest */
margin: #setUnit;
}
}
As indicated by the author of that answer (in the first line), it was a workaround because there was no way to create a true function with Less.
But starting with Less v2, we can define our own custom functions in Less and use them as described in this answer by Bass Jobsen. The ability to write such custom functions should eliminate the need to write such hacks.
You can also refer to the comment by seven-phases-max in the same thread for a solution without the need for such hacks.
Bottomline is that usage of & alone as a selector is a bad practice. The solution in the linked answer was a hack which was useful in earlier versions of Less. It is still useful but there are alternate options and so & alone as a selector should be used only in extremely rare circumstances where none of the other option work.

How do I implement OOCSS' spacing module in SCSS?

I've got a SCSS-based layout in which I want to use the spacing module from OOCSS.
The OOCSS module is pure CSS - ptl, for example, stands for padding-top: large, where large is a defined value (by default 20px).
I'd like to enhance it with SCSS. So far I've been able to replace the fixed values with SCSS variables, so I can change the values in one place if I want to (I don't want to):
$spacing-small: 5px;
$spacing-medium: 10px;
$spacing-large: 20px;
...
.pts,.pvs,.pas{padding-top:$spacing-small !important}
Now I'd like to be able to use ptn,pvs, etc. as mixins, so I can do this:
.client-name {
#include spacing-pvs; // this has the same padding properties as pvs
}
I'm flexible in the syntax, but that's the functionality I'd be interested in having.
The only way I can think of for doing this is manually defining every single mixin:
#mixin spacing-pvs {
padding-top: $spacing-small !important;
padding-bottom: $spacing-small !important;
}
.pvs { #include spacing-pvs; }
But there are around 56 styles/mixins. Doing each one individually like this would be pain to write and to maintain.
Is there a better way to do this in SASS/SCSS?
The most efficient mixin would be like this (you'll need a similar mixin for padding, or add an extra argument to switch between margin/padding):
#mixin marginify($t: null, $r: null, $b: null, $l: null) {
margin-top: $t;
margin-right: $r;
margin-bottom: $b;
margin-left: $l;
}
.test {
#include marginify($t: 10px, $b: 10px);
color: green;
}
Which generates this:
.test {
margin-top: 10px;
margin-bottom: 10px;
color: green;
}
The null (available in Sass 3.2+) is doing its magic here: if a variable is null, then it doesn't generate a property for it. However, you have to give up the use of !important (most people would argue that you should only use it as a last resort anyway). Reliance on this mixin is going to introduce a fair bit of bloat because the longhand form is always used over the shorthand (margin: 10px 0), so you'll need to use it responsibly or write a more powerful mixin that will generate the shorthand if appropriate.
That said, using a mixin for this purpose (adding margins) does reduce readability in your code. Before I looked at the entire source, the names made no sense. There's a lot to be said about the readability of vanilla CSS. The marginify mixin isn't really a reusable pattern like a clearfix or inline-menu mixin might be: writing a mixin isn't just about saving keystrokes.
I ended up not using mixins at all. Instead, I left the CSS rules as they were, and I used this less documented feature called #extend. Behold!:
.client-name {
#extend .pvs; // this has the same padding properties as .pvs
}

LESS issues (scope and explicit node name)

Is there any way to bypass LESS scoping? It's becoming annoying. Basically, I have a .text-box which defines background, border, etc. Then, in a sub-section there's a one-off change to add a margin-top: .text-box { margin-top: 10px }. Now I can't use .text-box within that section and get my original box styles; instead, all I get is the margin-top. How can I get the definition higher in the heirarchy? I suppose I could make it a function, and call that function in both places, but being that I'm using LESS, I want to do less and KISS. In PHP, you'd get to the global namespace by using / prefix, or in C++ using :: prefix.
Additionally, it doesn't seem like any definitions with the node name work for prototyping. Meaning, I can't declare it ul.products, and then use ul.categories { ul.products }. I have to omit the node name in order to re-use it. Meaning: .categories { .products }. Is this an oversight/impossibility?
Thanks
ok so let's say you've got your mixin defined, for example:
.text-box {
background: #eee;
border: 1px solid #ccc;
color: #333;
margin-top: 5px;
}
now you want to add property or modify it in some subsection, then simply do this:
div.content {
div.sub_section {
.text-box;
margin-top: 10px; // this will override 5px defined in the mixin.
}
}
...which is putting your mixin in place, and adding some property you need to add (which will override any property from the mixin itself BUT make sure the overriding property is defined AFTER the mixin is called.
it's not ideal solution, as it creates two declarations in the output css file (there will be one from mixin followed by the one you defined in .sub_section), but otherwise I don't know a solution to this problem other than defining a parametric mixin..
--
your second issue - I think that less doesn't support scope-limited definitions on purpose... if you really need to know that certain mixin is to be used by a specific tag, I would deal with it like so:
.ul_products { ... }
.ul_categories { .ul_products; ... }
ul.categories { .ul_categories; }
you can also define a bundle and call stuff from there:
#ul {
.products { ... }
.categories { ... }
}
ul.categories { #ul > categories; }
i hope i got it right.. ?

Resources