How to prevent CSS code duplication inside media queries? - css

I am trying to develop my own grid system. This is my first attempt so maybe I am missing something. Here is my CSS:
.column-1 {
width: 6.86666666667%;
}
.column-2 {
width: 15.3333333333%;
}
// More such columns
#media only screen and (max-width: 768px) {
.column-s-1 {
width: 6.86666666667%;
}
.column-s-2 {
width: 15.3333333333%;
}
}
As you can see the values are duplicated but class names are different. Is there any way I can avoid this duplication because it will become more and more complex with each additional class.

You can avoid some of duplication by grouping selectors:
.column-1,
.column-s-1 {
width: 6.86666666667%;
}
.column-2,
.column-s-2 {
width: 15.3333333333%;
}
// More such columns
#media only screen and (max-width: 768px) {
.column-s-1 {
/* only properties characteristic for this width*/
}
}
Another option is to use LESS or SASS

Related

How to make a sass based grid system with small through large columns

I can't seem to find what I'm looking for on Google. I am trying to set up a responsive grid system that uses columns like foundation or bootstrap. Basically I want to pull off column column-med column-lg but I don't understand how column-med is supposed to override column-lg when it changes breakpoints. I know it has to be a breakpoint somehow, but I want use a sass map to do this and not have a ton of media queries. Could someone point me in the right direction here?
First you need to create a mixin for the media queries. Something like:
#mixin small {
#media screen and (min-width: 768px) {
#content;
}
}
#mixin medium {
#media screen and (min-width: 1024px) {
#content;
}
}
Do the same for any sizes you want. In order to call that mixin in your .scss file you would do the following.
#include small {
//Your css rules here
}
#include small {
//Your css rules here
}
To make a grid you want to use flexbox to make your columns. First start with you default columns.
.col {
flex-grow: 0;
flex-shrink: 0;
max-width: 100%;
padding-left: 10px;
padding-left: 10px;
box-sizing: border-box; //so that the padding is included in the size
}
.col-6 {
flex-basis: 50%;
max-width: 50%;
}
So that will make two columns for any size window with the class 'col' and 'col-6'. Also, you don't have to use the 'col' class if you don't want gutters.
Then make classes with media queries.
#include small {
.col-sm-6 {
flex-basis: 50%;
max-width: 50%;
}
}
#include medium {
.col-md-4 {
flex-basis: 33.333333%;
max-width: 33.333333%;
}
}
That's it. Just add the classes 'col' and 'col-sm-6' and 'col-md-4' you'll have one column from 0-767 pixels wide, two from 768 to 1023 and three from 1024+.

Using Sass mixin to set global variable [duplicate]

I'm trying to combine the use of a Sass variable with #media queries as follows:
$base_width:1160px;
#media screen and (max-width: 1170px) {$base_width: 960px;}
#media screen and (min-width: 1171px) {$base_width: 1160px;}
$base_width is then defined at various points in the stylesheet width percentage-based measurements to produce fluid layouts.
When I do this, the variable seems to be recognized properly but the conditions for the media query are not. For example, the above code produces an 1160px layout regardless of screen width. If I flip-flop the #media statements like so:
#media screen and (min-width: 1171px) {$base_width: 1160px;}
#media screen and (max-width: 1170px) {$base_width: 960px;}
It produces a 960px layout, again regardless of screen width. Also note that if I remove the first line of $base_width: 1160px; it returns an error for an undefined variable. Any ideas what I'm missing?
This is simply not possible. Since the trigger #media screen and (max-width: 1170px) happens on the client-side.
Achieving your expected result would only be possible if SASS grabbed all rules and properties in your stylesheet containing your $base_width variable and copied/changed them accordingly.
Since it won't work automatically you could do it by hand like this:
#media screen and (max-width: 1170px)
$base_width: 960px // you need to indent it to (re)set it just within this media-query
// now you copy all the css rules/properties that contain or are relative to $base_width e.g.
#wrapper
width: $base_width
...
#media screen and (min-width: 1171px)
$base_width: 1160px
#wrapper
width: $base_width
...
This is not really DRY but the best you can do.
If the changes are the same every time you could also prepare a mixin containing all the changing values, so you wouldn't need to repeat it. Additionally you can try to combine the mixin with specific changes. Like:
#media screen and (min-width: 1171px)
+base_width_changes(1160px)
#width-1171-specific-element // additional specific changes, that aren't in the mixin
display: block
And the Mixin would look like this
=base_width_changes($base_width)
#wrapper
width: $base_width
Similar to Philipp Zedler's answer, you can do it with a mixin. That lets you have everything in a single file if you want.
#mixin styling($base-width) {
// your SCSS here, e.g.
#Contents {
width: $base-width;
}
}
#media screen and (max-width: 1170px) {
#include styling($base-width: 960px);
}
#media screen and (min-width: 1171px) {
#include styling($base-width: 1160px);
}
This isn't possible with SASS, but it is possible with CSS variables (or CSS custom properties). The only drawback is browser support – but there's actually a PostCSS plugin - postcss-css-variables - that "flattens" the use of CSS variables (which gives you support for older browsers, too).
The following example works great with SASS (and with postcss-css-variables you get support for older browsers too).
SCSS
$mq-laptop: 1440px;
$mq-desktop: 1680px;
:root {
--font-size-regular: 14px;
--gutter: 1rem;
}
// The fact that we have to use a `max-width` media query here, so as to not
// overlap with the next media query, is a quirk of postcss-css-variables
#media (min-width: $mq-laptop) and (max-width: $mq-desktop - 1px) {
:root {
--font-size-regular: 16px;
--gutter: 1.5rem;
}
}
#media (min-width: $mq-desktop) {
:root {
--font-size-regular: 18px;
--gutter: 1.75rem;
}
}
.my-element {
font-size: var(--font-size-regular);
padding: 0 calc(var(--gutter) / 2);
}
This would result in the following CSS. The repetitive media queries will increase the file size, but I have found that the increase is usually negligible once the web server applies gzip (which it will usually do automatically).
CSS
.my-element {
font-size: 14px;
padding: 0 calc(1rem / 2);
}
#media (min-width: 1680px) {
.my-element {
padding: 0 calc(1.75rem / 2);
}
}
#media (min-width: 1440px) and (max-width: 1679px) {
.my-element {
padding: 0 calc(1.5rem / 2);
}
}
#media (min-width: 1680px) {
.my-element {
font-size: 18px;
}
}
#media (min-width: 1440px) and (max-width: 1679px) {
.my-element {
font-size: 16px;
}
}
Edit: Please do not use this solution. The answer by ronen is much better.
As a DRY solution, you can use the #import statement inside a media query, e.g. like this.
#media screen and (max-width: 1170px) {
$base_width: 960px;
#import "responsive_elements";
}
#media screen and (min-width: 1171px) {
$base_width: 1160px;
#import "responsive_elements";
}
You define all responsive elements in the file included using the variables defined in the media query. So, all you need to repeat is the import statement.
With #ronen's great answer and a map, there's some real power available:
#mixin styling($map) {
.myDiv {
background: map-get($map, 'foo');
font-size: map-get($map, 'bar');
}
}
#media (min-height: 500px) {
#include styling((
foo: green,
bar: 50px
));
}
#media (min-height: 1000px) {
#include styling((
foo: red,
bar: 100px
));
}
It's now possible to have lots more DRY media queries targeting .myDiv with a bunch of different values.
Map docs: https://sass-lang.com/documentation/functions/map
Example map usage: https://www.sitepoint.com/using-sass-maps/
I had the same problem.
The $menu-width variable should be 240px on the mobile view #media only screen and (max-width : 768px) and 340px on the desktop view.
So i have simply created two variables:
$menu-width: 340px;
$menu-mobile-width: 240px;
And here is how i have used it:
.menu {
width: $menu-width;
#media only screen and (max-width : 768px) {
width: $menu-mobile-width;
}
}
Two recommendations
1
Write your "default" CSS statements to be for small screens and only use media queries for larger screens. There's usually no need for a max-width media query.
Example (assuming the element has class "container")
#mixin min-width($width) {
#media screen and (max-width: $width) {
#content;
}
}
.container {
width: 960px;
#include min-width(1170px) {
width: 1160px;
}
}
2 Use CSS variables to solve the problem, if you can.
#mixin min-width($width) {
#media screen and (max-width: $width) {
#content;
}
}
:root {
--container-width: 960px;
#include min-width(1170px) {
--container-width: 1160px;
}
}
.container {
width: var(--container-width);
}
Note:
Since it will have the width of 1160px when the window has a width of 1170px, it may be better to use a width of 100% and max-width of 1160px, and the parent element might have a horizontal padding of 5px, as long as the box-sizing property is set to border-box. There are a lot of ways to solve the problem. If the parent is not a flex or grid container you might use .container { margin: auto }.
This is also possible with %placeholders.
%placeholders can be wrapped in media queries. So you could set up multiple variables to use at different screen sizes, and then the placeholders would automagically pre-process accordingly. I'm using some mixins to shorten my media query declarations here also.
In your _vars.scss file:
$width-1: 960px;
$width-2: 1160px;
In your _placeholders.scss file:
%variable-site-width {
#media screen and (max-width: 1170px) { width: $width-1; }
#media screen and (min-width: 1171px) { width: $width-2; }
}
In your page.scss file:
.wrapper. { #extend %variable-site-width; background: red; etc... }
And this will compile to something similar to:
#media screen and (max-width: 1170px) {
.wrapper { width: 960px; }
}
#media screen and (min-width: 1171px) {
.wrapper { width: 1160px; }
}
Voila!
I use this technique extensively for things like variable font sizes and a raft of other things.

Can I have different breakpoints based on individual element in a responsive design?

I am creating a responsive design for my site and I have read quite a bit on media queries and breakpoints. I understand that there is no set breakpoints since there are so many screens now-a-days and we need to choose the best that suits our layout. But what I am not sure is if its okay to have different breakpoints for each element styled separately or is it better to group all the css to a common breakpoint that I use in my entire site. Here is an example to explain what I mean by this:
Right now I do something like this:
Method 1:
.search_bar {
width: 30%;
float: right;
}
/* Media Queries for Search Bar */
#media only screen and (max-width: 900px) {
.search_bar {
width: 40%;
}
}
#media only screen and (max-width: 780px) {
.search_bar {
width: 50%;
}
}
#media only screen and (max-width: 500px) {
.search_bar {
width: 100%;
}
}
.side_bar_wrap {
width: 300px;
}
/* Media Queries for Side Bar */
#media only screen and (max-width: 850px) {
.side_bar_wrap {
width: 250px;
}
}
#media only screen and (max-width: 600px) {
.side_bar_wrap {
width: 150px;
}
}
#media only screen and (max-width: 400px) {
.side_bar_wrap {
display:none;
}
}
etc..etc..
If you noticed above, I do not have a set number of breakpoints for my entire template. I defined different breakpoints for each element based on how that individual elements behave in different screen sizes. This gives me an accurate way to align individual elements rather than defining all the elements in a common breakpoints. What I dont know is if the above method is recommended (or okay to use) or should I not do it like that? Do I need to change the above into something like this where I create common breakpoints for my entire website and add the styles grouped together into one of these common breakpoints? Something like:
Method 2:
.search_bar {
width: 30%;
float: right;
}
.side_bar_wrap {
width: 300px;
}
/* Common Breakpoints to my entire site */
/* Media Queries for all elements are grouped into one of these */
#media only screen and (max-width: 900px) {
.side_bar_wrap {
width: 250px;
}
.search_bar {
width: 40%;
}
}
#media only screen and (max-width: 750px) {
.side_bar_wrap {
width: 150px;
}
.search_bar {
width: 50%;
}
}
#media only screen and (max-width: 500px) {
.side_bar_wrap {
display:none;
}
.search_bar {
width: 100%;
}
}
Is my first method a valid practice or I shouldn't be doing it like that? Can someone advise me if I am doing it right or wrong plz?
You are looking for best practice and solutions for questions like that are often based primarily on opinions. I'll try to give an objective answer, though.
In my opinion the most maintainable variant would be to go with method 2, i.e. keep media selectors to minimum:
It easily allows to split CSS for different media devices to separate files.
You have better outlook at what needs to be customized across different layouts: instead of scattering it, you keep it in one place.
It makes CSS output code smaller, implying negligibly faster loading times.
Cons are that you scatter semantically coupled definitions, i.e. in this example rules for #menu get scattered all over the place:
#menu { base rules ... }
other base selectors and rules ...
#media only screen and (max-width: 500px) { #menu { custom rules 1 } }
#media only screen and (max-width: 700px) { #menu { custom rules 2 } }
One solution to that would be giving up on trying to define base rules and define everything that needs to be customized in media selectors. That's not very good approach since you easily break Don't Repeat Yourself principle. Another, better approach would be to introduce base containers, like #menu-container which has constant base rules, and then #menu "implementation" which has rules defined exclusively in #media selectors.
Anyway, the point is not to mix base rules with device-specific rules, since it creates confusion when reading and modifying the code. (An exception to that are rules that define default layout on all devices; I'd keep these near device-specific rules.)

Including #media queries as mixins with LESS

Mixins with LESS are really simple:
.some-rules() {
/* some rules */
}
.some-class {
.some-rules;
}
However, suppose I've got a ruleset within a media query, like:
#media (min-width: 800px) {
.my_ruleset {
/* more rules */
}
}
How can I include such a ruleset as a mixin?
My motivation here is that I am using Bootstrap. I am trying to avoid semantically polluting my markup with its selectors, and would rather incorporate its rulesets as mixins. That's simple enough given the first example above, but I'm not sure how to incorporate the #media selectors.
EDIT
Specifically, here is a summary of the code I am trying to use as a mixin:
.container {
.container-fixed();
}
// ...
#media (min-width: #screen-sm) {
.container {
max-width: #container-sm;
}
// ...
}
#media (min-width: #screen-md) {
.container {
max-width: #container-md;
}
// ...
}
#media (min-width: #screen-lg-min) {
.container {
max-width: #container-lg;
}
// ...
}
Just simply add:-
.my_ruleset {
/* more rules */
}
#media (min-width: 800px) {
.my_ruleset;
}
If you want to add parameters to this for instance then:-
.my_ruleset(#myMargin:5px;) {
margin:#myMargin;
}
#media (min-width: 800px) {
.my_ruleset(10px);
/* New Rules*/
}
.
.
/* And so On */
Update:-
if you want to adapt this in a dynamic form take whole media query into mixin and play with it...
.container(
#minSize:768px;
#maxSize:979px;
#myColor:green;
/* So on */
) {
#media (min-width: #minSize) and (max-width: #maxSize) {
.my_ruleset {
background-color:#myColor;
}
/* New Rules*/
}
}
.container(0px,480px,black);
.container(481px,767px,blue);
.container(768px,979px,pink);
.container(980px,1200px,white);
.container(1700px,2200px,red);
EDIT
Checkout these two answers (class set in media query, media query grouping), I feel like they are very closely related to your question. Looking at the second answer I tried to put something together. The snippet is below, but you can also see a demo.tar.gz I put together as well.
main.less
#import "less/bootstrap"; /*Obviously, make sure to have all the less src :)*/
/*source: http://tinyurl.com/less-query */
.make-container(#min-width) {
#media (min-width: #min-width) {
.page-maker(#max-width) {
.page {
max-width: #max-width;
.container-fixed();
}
}
.make-page-style() when (#min-width=#screen-lg-min) {
.page-maker(#container-lg);
}
.make-page-style() when (#min-width=#screen-md) {
.page-maker(#container-md);
}
.make-page-style() when (#min-width=#screen-sm) {
.page-maker(#container-sm);
}
.make-page-style();
}
}
.make-container(#screen-sm);
.make-container(#screen-md);
.make-container(#screen-lg-min);
HTML
...
<body>
<div class="page">
...
</div>
...
OLD
have you tried using the following mixins (n is the number of columns):
.make-row()
.make-lg-comun(n)
.make-md-column(n)
.make-xs-column(n)
These mixins are used to semantically structure the page. For example, if you have the following markup:
<div class="main">
<div class="left">
</div>
<div class="right">
</div>
</div>
then in your less you can do:
.main{
.make-row();
}
.left{
.make-lg-column(5); /* makes five large desktop column */
}
.right{
.make-lg-column(7);
}
if this is not what you are looking for, perhaps elaborate on your intent more, maybe you won't need to do a lot of media queries.
You can use parent selector:
#screen-sm: 400;
#screen-md: 800;
#screen-lg-min: 1000;
.responsive(#width-sm, #width-md, #width-lg) {
#media (min-width: #screen-sm) {
& {
max-width: #width-sm;
}
}
#media (min-width: #screen-md) {
& {
max-width: #width-md;
}
}
#media (min-width: #screen-lg-min) {
& {
max-width: #width-lg;
}
}
}
.container {
.responsive(100px, 200px, 300px);
}
Output:
#media (min-width: 400) {
.container {
max-width: 100px;
}
}
#media (min-width: 800) {
.container {
max-width: 200px;
}
}
#media (min-width: 1000) {
.container {
max-width: 300px;
}
}

Media Queries with SCSS not applying

I'm trying to test the following code out in Chrome and Firefox and neither is picking it up. I have added it to the end of my stylesheet which works fine anyway.
#media all and (max-width : 850px) {
h1#site-name {
width: 100%;
a {
margin: auto;
}
}
nav#main-menu {
float: left;
}
}
I am re-sizing my browser window and the changes are obviously not taking effect at any width. The code is within a .scss file for reference.
Any ideas?
You can use mixins for media queries in scss too.
#mixin mq($mq) {
#if $mq == medium {
#media (max-width: 850px) { #content; }
}
}
#main-menu {
// default styles
// media query 850px max-width
#include mq(medium) {
float: left;
}
}
Check out this article for more info.. http://css-tricks.com/media-queries-sass-3-2-and-codekit/

Resources