How to use variables in defining breakpoints of media query in JSS - jss

I want to use my variable from a constant file in media query breakpoint definition. I want to write something like this:
.footer1 {
'#media (max-width: ' + Breakpoint.mobile + 'px)': {
position: "fixed",
bottom: 0,
left: 0,
width: "100vw",
},
}
It throws me this error on on the plus sign in terminal:
Module build failed: SyntaxError: Unexpected token (7:28)
'#media (min-width: ' + STYLE_CONST.breakPoints.tablets + 'px)': {
^
It would be great if I can use variables for defining breakpoints. Is there a solution?

Well you can't concatenating the string like that,
The proper way is by using template literals
[`#media (max-width:${Breakpoint.mobile}px)`]
I prefer this way, since it would be much cleaner
const mobileBreak = '#media (max-width: 720px)';
.footer1 {`
[mobileBreak]: `{
position: "fixed",
bottom: 0,
left: 0,
width: "100vw",
},
}

I proffer use backquote operator and putting .footer1 inside of your media query, see below code:
[`#media (max-width:${Breakpoint.mobile}px)`]: {
.footer1 : {
position: 'fixed',
bottom: 0,
left: 0,
width: '100vw',
},
}

Related

Clarifying the status of custom attributes in HTML Custom Elements in WebComponents

Until recently, whenever I've needed a custom attribute in my HTML, I've always used an HTML5 data-* custom attribute.
Having recently started experimenting with WebComponents and, specifically, Custom Elements, I have started thinking in terms of custom attributes which are not HTML5 data-* custom attributes.
Before inadvertently adopting any non-recommended practices, I would like to clarify the following...
In the list below we have 4 elements:
Element i is a standard element with a data-* attribute
Element ii is a standard element with a custom attribute
Element iii is a custom element with a data-* attribute
Element iv is a custom element with a custom attribute
const toggleDataAttribute = (e) => {
e.target.dataset.inverted = (e.target.dataset.inverted === 'true') ? 'false' : 'true';
}
const toggleCustomAttribute = (e) => {
if (e.target.getAttribute('inverted') === 'true') {
e.target.setAttribute('inverted', 'false');
}
else {
e.target.setAttribute('inverted', 'true');
}
}
const toggleInvert = (e) => {
if (e.target.dataset.inverted) {
toggleDataAttribute(e);
}
else {
toggleCustomAttribute(e);
}
}
// Attach click event TO <div> elements
let divs = [...document.getElementsByTagName('div')];
divs.forEach((div) => div.addEventListener('click', toggleInvert, false));
// Attach click event TO <my-circle> elements
let myCircles = [...document.getElementsByTagName('my-circle')];
myCircles.forEach((myCircle) => myCircle.addEventListener('click', toggleInvert, false));
// Define <my-circle> element
class myCircle extends HTMLElement {
constructor() {
super();
this.root = this.attachShadow({mode: "open"});
}
connectedCallback() {
this.root.appendChild(document.createElement('slot'));
}
}
customElements.define('my-circle', myCircle);
aside {
position: absolute;
top: 0;
right: 0;
width: 280px;
line-height: 24px;
}
div {
float: left;
margin: 0 12px 12px 0;
width: 80px;
height: 80px;
line-height: 80px;
text-align: center;
font-size: 36px;
border-radius: 50%;
cursor: pointer;
}
my-circle {
display: block;
float: left;
margin: 0 12px 12px 0;
width: 80px;
height: 80px;
line-height: 80px;
text-align: center;
font-size: 36px;
background: radial-gradient(#fff, #000);
border-radius: 50%;
cursor: pointer;
}
my-circle:first-of-type {
clear: left;
}
div:nth-of-type(1) {
background: radial-gradient(rgb(255, 255, 0), rgb(255, 0, 0));
}
div:nth-of-type(2) {
background: radial-gradient(rgb(255, 255, 0), rgb(0, 163, 0));
}
my-circle:nth-of-type(1) {
background: radial-gradient(rgb(255, 255, 0), rgb(223, 163, 0));
}
my-circle:nth-of-type(2) {
background: radial-gradient(rgb(255, 127, 127), rgb(255, 0, 0));
}
div[data-inverted="true"],
div[inverted="true"],
my-circle[data-inverted="true"],
my-circle[inverted="true"] {
filter: hue-rotate(180deg);
}
<div data-inverted="false">i</div>
<div inverted="false">ii</div>
<my-circle data-inverted="false">iii</my-circle>
<my-circle inverted="false">iv</my-circle>
<aside>
<p><strong>Click</strong> on each of the circles on the left to invert their backgrounds.</p>
</aside>
Although the set up above works technically, which of the following is true:
A) Custom attributes may be used universally, in standard elements and custom elements.
Conclusion: Elements i, ii, iii & iv are all valid
B) Custom attributes may only be used in custom elements. They are invalid elsewhere.
Conclusion: Elements i, iii & iv are valid, while ii is invalid
C) Data-* attributes are for standard elements, custom attributes are for custom elements.
Conclusion: Elements i & iv are valid, while ii & iii are invalid
D) Custom attributes are not even a thing. Where did you get this idea from?
Conclusion: Elements i & iii are valid, while ii & iv are invalid
Added:
To illustrate my question above, I'd like to give an example of where custom attributes appear not to be valid:
Go to: https://validator.w3.org/nu/#textarea
Select text input
Enter:
<!DOCTYPE html>
<html lang="">
<head>
<title>Test</title>
</head>
<body>
<div data-inverted="false">i</div>
<div inverted="false">ii</div>
</body>
</html>
Check the markup
The validator returns the error:
Error: Attribute inverted not allowed on element div at this point.
From line 10, column 1; to line 10, column 22
i</div>↩↩<div inverted="false">ii</di
Though... I'm not sure if the tool at https://validator.w3.org/nu/ is outdated and / or abandoned and the Error returned should no longer be regarded as an error in 2020 (?)
All 4 usages work, so why should they be invalid?
data- prefix gives the added bonus they are available in element.dataset.
-- Attributes are Attributes -- , nothing special in the Custom Elements API,
apart from observedAttributes(). Yes, you can use data-* attributes there to.
note
class myCircle extends HTMLElement {
constructor() {
super();
this.root = this.attachShadow({mode: "open"});
}
connectedCallback() {
this.root.appendChild(document.createElement('slot'));
}
}
can be written as:
class myCircle extends HTMLElement {
constructor() {
super()
.attachShadow({mode: "open"})
.append(document.createElement('slot'));
}
}
because super() returns 'this'
and attachShadow both sets and returns this.shadowRoot for free
you are not doing anything with appendChild() return value, so append() (which can take multiple parameters) is enough.
Also note there is a toggleAttribute method.
https://developer.mozilla.org/en-US/docs/Web/API/ParentNode/append
https://developer.mozilla.org/en-US/docs/Web/API/Element/toggleAttribute

#media rule not turning off when screen gets larger

I have the following in my CSS file, and it works fine apart from the rule around --md and --sm arent turning off when the screen width goes over 899px.
.ProductThumbnail {
position: relative;
overflow: visible;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
&--md,
&--sm {
display: none;
background-position: 30% top;
#include media('sm', true, true) {
display: block;
}
}
&--lg {
display: none;
background-position: 25% top;
width: 1600px;
#include media('md', true) {
display: block;
}
}
}
The return function in the react component is as follows:
return (
<>
<div
className="ProductThumbnail__bg ProductThumbnail__bg--xs"
style={{
backgroundImage: getURL({
w: 600,
h: 455,
}),
}}
/>
<div
className="ProductThumbnail__bg ProductThumbnail__bg--md"
style={{
backgroundImage: getURL({
h: 455,
}),
}}
/>
<div
className="ProductThumbnail__bg ProductThumbnail__bg--lg"
style={{
backgroundImage: getURL({
w: 2560,
h: 1040,
}),
}}
/>
</>
);
I can see in the dev tools that the rules are being applied as expected for --md and --sm but they dont disappear when the screen gets bigger.
Update, media mixin code:
#mixin media(
$breakpoint,
$is-minimum-only: false,
$is-maximum-only: false) {
#if map_has_key($breakpoint-ranges, $breakpoint) {
$breakpoint-range: get-break-point-range($breakpoint);
$breakpoint: "";
#if length($breakpoint-range) < 2 or $is-minimum-only {
$breakpoint: "(min-width:#{nth($breakpoint-range, 1)})";
} #else if $is-maximum-only {
$breakpoint: "(max-width:#{nth($breakpoint-range, 2)})";
} #else {
$breakpoint: "(min-width:#{nth($breakpoint-range, 1)}) and (max-width:#{nth($breakpoint-range, 2)})";
}
#media screen and #{$breakpoint} {
#content;
}
} #else {
#warn "No registered breakpoint for `#{$breakpoint}`";
}
}
if I understand your mixin correctly, it should generate a media-query with "min" and "max" value if there's no "$is-minimum-only" or "$is-maximum-only" set. So in your case I would remove both "true" settings in this line:
#include media('sm', true, true) {
so it looks like this
#include media('sm') {
Now the third case inside the "#if length($breakpoint-range)" statement should take effect.
Not sure if it even makes sense to set both variables to "true". Because they have a "only" in their names, I suppose only one of them should apply at the same time ;)
I hope that helps.

Playing with LESS - optimize css generated by passing ruleset to mixin

I'm just playing with LESS lately. I wanted to generate rules for elements with numeric ID. At some stage I got code like this:
#myRule: {padding: 0;};
.myLoop(#c, #rules) when (#c >= 0) {
.myLoop((#c - 1), #rules);
&[id*=#{c}] { #rules(); }
&[name*=#{c}] { #rules(); }
}
.myClass {
.myLoop(2, #myRule);
}
which compiles to
.myClass[id*=0] {
padding: 0;
}
.myClass[name*=0] {
padding: 0;
}
.myClass[id*=1] {
padding: 0;
}
.myClass[name*=1] {
padding: 0;
}
.myClass[id*=2] {
padding: 0;
}
.myClass[name*=2] {
padding: 0;
}
My question is: can I in any way make it compile to sth like this:
.myClass[id*=0],
.myClass[name*=0],
.myClass[id*=1],
.myClass[name*=1],
.myClass[id*=2],
.myClass[name*=2] {
padding: 0;
}
I was looking for something like 'extending mixins`, 'parametric extend' or 'extending ruleset' but all lead to issues that are either 'wontfix' or 'nice-to-have' :-) So I guess it's not yet possible, but I would just like to reach out to people more familiar with less then I am, to be sure.
Yes, neither extending parametric mixins nor scoped extend are possible currently, so the easiest method to achieve the result is to extend a dummy ruleset. E.g.:
.my-repeat(#i, #f) when (#i >= 0) {
.my-repeat((#i - 1), #f);
&[id*=#{i}], &[name*=#{i}] {#f();}
}
.my-class-style {
padding: 0;
}
.my-class {
.my-repeat(2, {
&:extend(.my-class-style);
});
}
where .my-class-style is the dummy selector to appear in the resulting CSS too.

How to Customize Bootstrap Column Widths?

I have this, but I feel 4 is too big for my sidebar width and 3 is too small (it has to add up to 12).
<div class="col-md-8">
<div class="col-md-4">
I tried this but it doesn't work:
<div class="col-md-8.5">
<div class="col-md-3.5">
Is there another way to get a similar outcome?
Thanks for your help!
To expand on #isherwood's answer, here is the complete code for creating custom -sm- widths in Bootstrap 3.3
In general you want to search for an existing column width (say col-sm-3) and copy over all the styles that apply to it, including generic ones, over to your custom stylesheet where you define new column widths.
.col-sm-3half, .col-sm-8half {
position: relative;
min-height: 1px;
padding-right: 15px;
padding-left: 15px;
}
#media (min-width: 768px) {
.col-sm-3half, .col-sm-8half {
float: left;
}
.col-sm-3half {
width: 29.16666667%;
}
.col-sm-8half {
width: 70.83333333%;
}
}
For a 12 columns grid, if you want to add half of a column (4,16667%) to each column width. This is what you do.
For example, for col-md-X, define .col-md-X-5 with the following values.
.col-md-1-5 { width: 12,5%; } // = 8,3333 + 4,16667
.col-md-2-5 { width: 20,83333%; } // = 16,6666 + 4,16667
.col-md-3-5 { width: 29,16667%; } // = 25 + 4,16667
.col-md-4-5 { width: 37,5%; } // = 33,3333 + 4,16667
.col-md-5-5 { width: 45,83333%; } // = 41,6667 + 4,16667
.col-md-6-5 { width: 54,16667%; } // = 50 + 4,16667
.col-md-7-5 { width: 62,5%; } // = 58,3333 + 4,16667
.col-md-8-5 { width: 70,83333%; } // = 66,6666 + 4,16667
.col-md-9-5 { width: 79,16667%; } // = 75 + 4,16667
.col-md-10-5 { width: 87,5%; } // = 83,3333 + 4,16667
.col-md-11-5 { width: 95,8333%; } // = 91,6666 + 4,16667
I rounded certain values.
Secondly, to avoid copying css code from the original col-md-X, use them in the class declaration. Be careful that they should be added before your modified ones. That way, only the width gets override.
<div class="col-md-2 col-md-2-5">...</div>
<div class="col-md-5">...</div>
<div class="col-md-4 col-md-4-5">...</div>
Finally, don't forget that the total should not exceed 12 columns, which total 100%.
I hope it helps!
You could certainly create your own classes:
.col-md-3point5 {width: 28.75%}
.col-md-8point5 {width: 81.25%;}
I'd do this before I'd mess with the default columns. You may want to use those inside these.
You'd probably also want to put those inside a media query statement so that they only apply for larger-than-mobile screen sizes.
Bootstrap 4.1+ version of Antoni's answer:
The Bootstrap mixin is now #include make-col($size, $columns: $grid-columns)
.col-md-8half {
#include make-col-ready();
#include media-breakpoint-up(md) {
#include make-col(8.5);
}
}
.col-md-3half {
#include make-col-ready();
#include media-breakpoint-up(md) {
#include make-col(3.5);
}
}
Source:
Official documentation
Bootstrap 4 Sass Mixins [Cheat sheet with examples]
You can use Bootstrap's own column mixins make-xx-column():
.col-md-8half {
.make-md-column(8.5);
}
.col-md-3half {
.make-md-column(3.5);
}
you can customize bootstrap stylesheet, as in:
.col-md-8{
width: /*as you wish*/;
}
Then, set the media query for that too, as in:
#media screen and (max-width:768px){
.col-md-8{
width:99%;
}
}

Less CSS variable encapsulation and reusable mixins

Can anyone offer any solutions for combining encapsulation and mixin reuse for Less/CSS? I'm trying to keep my variables encapsulated by namespace, but I haven't figured out how to reuse mixins for it.
Example:
#signup-module {
#button-width: 100px;
#button-height: 30px;
#textfield-width: 300px;
#textfield-height: 20px;
.width( #value, #mod:0 ) {
width: ##value + #mod;
}
.height( #value, #mod:0 ) {
height: ##value + #mod;
}
}
.home-page-signup-module {
#signup-module > .width( button-width, -20px );
#signup-module > .height( button-height, -20px );
#signup-module > .width( textfield-width );
#signup-module > .height( textfield-height );
}
The problem is when I create a new module, the width() and height() mixins are repeated.
#contact-us-module {
#button-width: 50px;
#button-height: 20px;
#textfield-width: 300px;
#textfield-height: 20px;
.width( #value, #mod:0 ) {
width: ##value + #mod;
}
.height( #value, #mod:0 ) {
height: ##value + #mod;
}
}
Is there a way to maintain variable encapsulation and eliminate the mixin repetition? I'd like to write .width() and .height() once, but :extend() doesn't seem to work in this context.
Update: May 15, 2014
seven-phases-max offered a great solution below for reusing mixins, but I think I ran into a variable scope issue and the statement below returned an error. It said, "variable #textfield-width is undefined."
.home-page-signup-module {
.module-a.width(textfield-width, -20px);
}
So I tried adding .module-a which seems to work. I'm not 100% sure if this is correct usage but it does fix the error and return the correct value.
.home-page-signup-module {
.module-a;
.module-a.width(textfield-width, -20px);
}
You can collect shared mixins into another namespace/mixin and expand it in each "module" you need, something like this for example:
.shared-stuff() {
.width(#value, #mod: 0) {
width: ##value + #mod;
}
.height(#value, #mod: 0) {
height: ##value + #mod;
}
}
.module-a {
.shared-stuff();
#button-width: 100px;
#button-height: 30px;
#textfield-width: 300px;
#textfield-height: 20px;
}
.module-b {
.shared-stuff();
#button-width: 200px;
// etc.
}
// usage:
.home-page-signup-module {
.module-a.width(button-width, -20px);
.module-b.width(button-width, +33px);
}

Resources