Change variable dynamically SCSS [duplicate] - html

Suppose I have two virtually identical HTML structures, but with different class names. They only differ by a few variables, like width and height. By using SASS/SCSS variables I thought I could do something like this:
.widget-a {
$width: 50px;
}
.widget-b {
$width: 100px;
}
.widget-a,
.widget-b {
button {
background: red;
width: $width;
}
}
This would let me write a single piece of SASS nested code for both widgets a and b. However, variables are only visible inside a nested scope, so SASS returns 'variable undefined' errors. Of course I could rewrite it by simply doing something like:
.widget-a,
.widget-b {
button {
background: red;
}
}
.widget-a {
button {
width: 50px;
}
}
.widget-b {
button {
width: 100px;
}
}
But that seems pretty cumbersome. Is there any other method of making this work?

Variables in SASS are only scoped to the block they appear in. Your first .widget-a declaration and the one declaring both A and B are two separate scopes. You'll have to do something like this (assuming you need to use the widths more than once):
$wbackground: red;
.widget-a {
$wawidth: 50px; /* widget A width */
button {
background: $wbackground;
width: $wawidth;
}
}
.widget-b {
$wbwidth: 100px; /* widget B width */
button {
background: $wbackground;
width: $wbwidth;
}
}

Ran into the same problem, this is how I'm going to try to solve it... (this works)
#mixin foo($type:"default")
.mClass
$bg: inherit
#if $type == "banana"
$bg: yellow
background: $bg
.firstClass
#include foo
.secondClass
#include foo("banana")

Your problem can be solved by using a mixin.
#mixin button($width){
button{
background:red;
width:$width;
}
}
.widget-a{ #include button(50px); }
.widget-b{ #include button(100px); }

I don't see the advantage of creating a mixin only for this specific situation, it is hardly useful on a couple of occasions, but it is just my opinion.
Anyway, I've created a mixin, just for fun. I think that it can help you to deal with this specific situation. Here is the mixin and I'm going to try to explain how it works:
#include button($selectors, $property, $values, $child: false) {
// Common properties that are included in all $selectors
}
This mixin takes four parameters:
$selectors: List of selectors, in your case, .widget-a and .widget-b, they should be enclosed in quotes.
$property: Here you should enter the name of the property, in your case width
$values: Values ​​are, as the name implies , the values of the property for each selector
$child: Here you can enter the name of a child, this is optional.
Into the brackets {} you should write all the properties that you want to include in all $parameters
The order of each selector must match the order of their corresponding value
So, here's an example using this mixin to solve your problem. This is the #include:
#include (".widget-a" ".widget-b", width, 50px 100px, button) {
background: red;
}
And this, the code that returns:
.widget-a button, .widget-b button {
background: red; }
.widget-a button {
width: 50px; }
.widget-b button {
width: 100px; }
This is another way to achieve the same result:
#include button(".widget-a .button" ".widget-b .button", width, 50px 100px) {
background: red;
}
Download the mixin here

Related

Modifying elements within a block--modifier correctly

I am trying to closely stick to the BEM methodology and I am frequently running into this issue which I feel it is time to ask for a solution (or opinion) to.
Consider I have a human body; this human body is light by default and therefore, it's arms are also light. My pseudo-code in CSS may look like the below:
.human {
background-color: white;
}
.human__arm {
background-color: white;
}
Now we want to add a class for humans who are dark. We can create a modifier for this:
.human--dark {
background-color: black;
}
The problem I am facing is that by now, the human's arm is still white. We can make them black in two ways that I know of:
Solution A
.human--dark {
background-color: black;
}
.human--dark .human__arm {
background-color: black;
}
This solution breaks the BEM methodology by adding specificity to the CSS. However, I feel that this solution is more portable where you are sure to only modify the human and assume that all of its elements will also adapt to the changes of the parent.
Solution B
.human--dark {
background-color: black;
}
.human__arm--dark {
background-color: black;
}
I like to think that a 'Block' is a reusable component in BEM. If the human has various other body parts that also need turning white, it seems less maintainable to modify all of the blocks elements in order to achieve this.
While background-color: inherit; may look like a solution to this case, in a real-world application we may have a dark background that contains elements that require light text.
How would we modify the light text to become dark when its parent block becomes light?
The first solution is the better one assuming that a dark human may not necessarily have a white arm.
The idea behind BEM syntax is to allow for composability. Writing your selector that way defeats this purpose.
A better way to define these selectors is:
.human {} /* block */
.human--dark {} /* modified block */
.human--arm {} /* block element */
These can be altogether composed this way in markup:
<div class="human human--dark">
<div class="human__arm">
Human
</div>
</div>
For your second question, you may want to take advantage of a CSS preprocessor to compute the inverse of the background color for the text color.
In Sass, it's done this way using a custom mixin:
#mixin duotone ($color) {
background-color: $color;
color: invert($color);
}
.human {
#include duotone(white);
}
.human--dark {
#include duotone(black);
}
.human__arm {
}
In order to modify child elements via parent modifier
we should nest the element selector like below:
Block with an element:
.human {
background-color: white;
}
.human__arm {
background-color: white;
}
Block Modifier modifying the child element:
.human--dark {
background-color: black;
}
.human--dark .human__arm {
background-color: black;
}
If we want to achieve the same in SCSS, we can use the following approach:
.human {
background-color: white;
&__arm {
background-color: white;
}
&--dark { // block modifier
background-color: black;
.human { // block
&__arm {
background-color: black;
}
}
}
}
If you're using scss you can use interpolation to avoid duplication
.human {
$c: &;
background-color: white;
&__arm {
background-color: white;
}
&--dark {
background-color: black;
#{$c}__arm {
background-color: black;
}
}
}
Also, this may not be relevant to the question, but using background-color: inherit on the arms would mean you wouldn't have to change any css for the arms as it would inherit from the parent.

Is there anyway to style html elements through html classes with variables?

I'm just curious to know if it is possible to have specific stylings based on the name of of a class.
For example, Bootstrap 4 has a helper class for margins and padding like:
<div class="m-t-1 p-a-0"></div>
This gives the div 1em of margin to the top, and removes padding from all sides.
I am sure they have pre-styled this class in their CSS to achieve this.
But I am curious if there is a way to use the class as a variable.
for example:
<div class="fs-x"></div>
where x can be any number, this class would then give the styling the font-size: x to the div.
Is this possible to do?
Thanks.
You can use a CSS pre-processor such as SASS or LESS to achieve this however it generates static classes within a specified range below is an example from the SASS documentation:
$class-slug: for !default
#for $i from 1 through 4
.#{$class-slug}-#{$i}
width: 60px + $i
Which emits this CSS:
.for-1 {
width: 61px;
}
.for-2 {
width: 62px;
}
.for-3 {
width: 63px;
}
.for-4 {
width: 64px;
}
All CSS classes must be explicitly defined. So every variation if X would need to exist in a .css file
you can use constant in css for example
$x = 10px;
img{
margin-bottom : $x;
}
but however you can declare variables with this way
:root {
--color-principal: #06c;
}
#foo h1 {
color: var(--color-principal);
}

can I write my own pseudo function in CSS

Consider this:
.voffset1 { margin-top: 1px; }
.voffset2 { margin-top: 2px; }
.voffset3 { margin-top: 3px; }
......
.voffset100 { margin-top: 100px; }
Is it possible to turn this into a function in CSS or CSS3
so I don't have to manually have 100 css classes, but
just have 1 css 'function' for it?
like below:
.voffset:(n) {margin-top : (n)px;}
if not, can we do better than writing 100 lines?
I prefer not using scss or any variants.
Sure.
<div style="margin-top:1px">
When your CSS is so obviously redundant, just use inline styles.
Or redesign your code to actually use classes properly.

Set CSS class for several ID

I have done the following:
#my-el .some-class {
width: 10%;
}
That works but now I'd like to apply the same class to the element #my-new-el. I've tried:
#my-el #my-new-el .some-class {
width: 10%;
}
But this does not work. What would be the correct syntax to achieve this ?
If .some-class only applies to descendents of #my-el and #my-new-el then you need to specify the two selectors in full, separated by a comma:
#my-el .some-class, #my-new-el .some-class {
width: 10%;
}

Avoiding repeated constants in CSS

Are there any useful techniques for reducing the repetition of constants in a CSS file?
(For example, a bunch of different selectors which should all apply the same colour, or the same font size)?
Recently, variables have been added to the official CSS specs.
Variables allow you to so something like this :
body, html {
margin: 0;
height: 100%;
}
.theme-default {
--page-background-color: #cec;
--page-color: #333;
--button-border-width: 1px;
--button-border-color: #333;
--button-background-color: #f55;
--button-color: #fff;
--gutter-width: 1em;
float: left;
height: 100%;
width: 100%;
background-color: var(--page-background-color);
color: var(--page-color);
}
button {
background-color: var(--button-background-color);
color: var(--button-color);
border-color: var(--button-border-color);
border-width: var(--button-border-width);
}
.pad-box {
padding: var(--gutter-width);
}
<div class="theme-default">
<div class="pad-box">
<p>
This is a test
</p>
<button>
Themed button
</button>
</div>
</div>
Unfortunately, browser support is still very poor. According to CanIUse, the only browsers that support this feature today (march 9th, 2016), are Firefox 43+, Chrome 49+, Safari 9.1+ and iOS Safari 9.3+ :
Alternatives :
Until CSS variables are widely supported, you could consider using a CSS pre-processor language like Less or Sass.
CSS pre-processors wouldn't just allow you to use variables, but pretty much allow you to do anything you can do with a programming language.
For example, in Sass, you could create a function like this :
#function exponent($base, $exponent) {
$value: $base;
#if $exponent > 1 {
#for $i from 2 through $exponent {
$value: $value * $base;
}
}
#if $exponent < 1 {
#for $i from 0 through -$exponent {
$value: $value / $base;
}
}
#return $value;
}
Elements can belong to more than one class, so you can do something like this:
.DefaultBackColor
{
background-color: #123456;
}
.SomeOtherStyle
{
//other stuff here
}
.DefaultForeColor
{
color:#654321;
}
And then in the content portion somewhere:
<div class="DefaultBackColor SomeOtherStyle DefaultForeColor">Your content</div>
The weaknesses here are that it gets pretty wordy in the body and you're unlikely to be able to get it down to listing a color only once. But you might be able to do it only two or three times and you can group those colors together, perhaps in their own sheet. Now when you want to change the color scheme they're all together and the change is pretty simple.
But, yeah, my biggest complain with CSS is the inability to define your own constants.
You should comma seperate each id or class for example:
h1,h2 {
color: #fff;
}
You can use global variables to avoid duplicacy.
p{
background-color: #ccc;
}
h1{
background-color: #ccc;
}
Here, you can initialize a global variable in :root pseudo class selector. :root is top level of the DOM.
:root{
--main--color: #ccc;
}
p{
background-color: var(--main-color);
}
h1{
background-color: var(--main-color);
}
NOTE: This is an experimental technology
Because this technology's specification has not stabilized, check the compatibility table for the proper prefixes to use in various browsers. Also note that the syntax and behavior of an experimental technology is subject to change in future versions of browsers as the spec changes. More Info here
However, you can always use the Syntactically Awesome Style Sheets i.e.
In case Sass, you have to use $variable_name at the top to initialize the global variable.
$base : #ccc;
p{
background-color: $base;
}
h1{
background-color: $base;
}
You can use dynamic css frameworks like less.
Personally, I just use comma-separed selector, but there some solution for writing css programmatically. Maybe this is a little overkill for you simpler needs, but take a look at CleverCSS (Python)
Try Global variables to avoid duplicate coding
h1 {
color: red;
}
p {
font-weight: bold;
}
Or you can create different classes
.deflt-color {
color: green;
}
.dflt-nrml-font {
font-size: 12px;
}
.dflt-header-font {
font-size: 18px;
}
As far as I know, without programmatically generating the CSS file, there's no way to, say, define your favorite shade of blue (#E0EAF1) in one and only one spot.
You could pretty easily write a computer program to generate the file. Execute a simple find-and-replace operation and then save as a .css file.
Go from this source.css…
h1,h2 {
color: %%YOURFAVORITECOLOR%%;
}
div.something {
border-color: %%YOURFAVORITECOLOR%%;
}
to this target.css…
h1,h2 {
color: #E0EAF1;
}
div.something {
border-color: #E0EAF1;
}
with code like this… (VB.NET)
Dim CssText As String = System.IO.File.ReadAllText("C:\source.css")
CssText = CssText.Replace("%%YOURFAVORITECOLOR%%", "#E0EAF1")
System.IO.File.WriteAllText("C:\target.css", CssText)
You can use multiple inheritance in your html elements (e.g. <div class="one two">) but I'm not aware of a way of having constants in the CSS files themselves.
This link (the first found when googling your question) seems to have a fairly indepth look at the issue:
http://icant.co.uk/articles/cssconstants/
CSS Variables, if it ever becomes implemented in all major browsers, may one day resolve this issue.
Until then, you'll either have to copy and paste, or use a preprocessor of whatever sort, like others have suggested (typically using server-sider scripting).
:root {
--primary-color: red;
}
p {
color: var(--primary-color);
}
<p> some red text </p>
You can change color by JS
var styles = getComputedStyle(document.documentElement);
var value = String(styles.getPropertyValue('--primary-color')).trim();
document.documentElement.style.setProperty('--primary-color', 'blue');