Sass calculcate columnwidth percentage - css

I'm trying to make my own little columngrid with sass but I can't wrap my head around this problem.
This is what I have come up with so far but it is no the right solution. I'm getting a clean grid like this but the problem is that I'm removing an extra percentage in every row.
I'm removing the gutter_width of 1%(in this case) on the width of each column and replacing the room by using my gutter_width as the margin-left. So for each column I'm removing a percentage and adding it as margin, creating the gutters. The problem arises when I remove margin of the first-child in the row wich leaves me with a 99% row.
Could somebody help me out with this? Or maybe suggest a better way.
$container_width: 970px; // Main container width
$gutter_width: 1%;
$columns: 12; // Twelve columns
/* #Calculate the columnwidths */
/* Calculate the width of a single column and multiple it by columncount
================================================== */
#for $i from 1 through $columns {
.column-#{$i} {
width: ((100% / $columns) * $i) - $gutter_width;
}
}
.container {
max-width: $container_width;
margin: 0 auto;
}
.row {
width: 100%;
margin: 1em 0;
#include pie-clearfix;
}
// Select all element that contains class 'column'
[class*="column"] {
float: left;
margin-left: $gutter_width;
&:first-child {
margin-left: 0;
}
}

There should be one less gutter than there are columns - both in the context and in the span-width. The correct math is actually:
// the width of a single column has to account for "$columns - 1" gutters.
$column_width: (100% - $gutter_width * ($columns - 1)) / $columns;
#for $i from 1 through $columns {
.column-#{$i} {
the width of a span should cover "$i" columns, plus "$i - 1" gutters
width: $column-width * $i + $gutter_width * ($i -1);
}
}
Classes like this create a fairly fragile system if you want to nest any grid-spans inside other grid-spans, but it should cover the basics. Once you are using a pre-processor like Sass, I recommend leaving behind .column-x classes entirely, and just using mixins.

Because you have removed a $gutter_width from the first child you need to add it again to the last. One way to go about it could be to place this inside your loops:
&:last-of-type{
width: (100% / $columns) * $i;
}
That way if the column is the last element the width is recalculated with the extra 1%. If you want older browser support just replace it with a utility class called .last-column or something.

Related

CSS calc() behaviour in CSS variables

I'm curious to the behaviour of using calc() in setting a CSS variable.
Example:
#test {
--halfWidth: calc(100% / 2);
}
Now, if the #test element, say a div, was 500px wide, I would like the --halfWidth variable to be set to 250px.
But, as far as I can tell the var(--halfWidth) code used elsewhere simply drops in the calc(100% / 2) string instead of 250px. Which means that I can't use the calculation of say element A and use it in element B later on, since it would simply set for example width: var(--halfWidth); as half the width of element B instead of half the width of element A, where the variable was defined.
I've scoured the web trying to find any documentation on the behaviour of this, but I have so far drawn a blank.
Ideally, setting a CSS variable using calc should be available in two variants:
One variant working just like this example, simply dropping in the string as-is, bar any in-string variable replacements.
A second variant where calc() would yield the result of the calculation instead of simply replacing the string.
How to achieve this? I'd rather leave the actual implementation to people suited to it, but one possibility would be an eval() kind of thing; like eval(calc(100% / 2)) would give the result 250px.
Anyway, if anyone have any real documentation on this behaviour or a solution to how to get the example above to yield the result instead, I'm all ears!
Edit: Just FYI, I have read the specs at https://drafts.csswg.org/css-variables/
This is kind of a tough question to answer cause the answer will not be:
Do it like this...then it will work
The problem you are facing is the normal behavior of CSS. It cascades the styles. If what you are trying to achieve would work it would get real messy after a short amount of time.
I mean how cool is it that you can define a variable like this
#test {
--halfWidth: calc(100% / 2);
}
where var(--halfWidth) should always be calc(100% / 2). Did you note that it will always be half the width of the parent element?
Imagine how strange it would be if a programmer in a few months reads your code and has box with a width of 1000px set with --halfWidth and now it is 250px wide ... I would think the internet is broken :) It should just be 500px wide.
To achieve what you want, you could/should define different vars defining the widths of the parent elements. And split it down to the children.
One approach to this is to dynamically add a line to the CSS Object Model (CSSOM) which explicitly declares the width of the .halfwidth class.
This width will then apply to all divs with the .halfwidth class.
In the example below, I have, additionally, made .element-a horizontally resizable, so that you can see more clearly that as you change the width of .element-a, the width of both .halfwidth divs changes proportionately, including the .halfwidth div which is a child of .element-b.
Working Example:
let myStylesheet = document.styleSheets[0];
const elementA = document.getElementsByClassName('element-a')[0];
let elementAWidth = window.getComputedStyle(elementA).getPropertyValue('width');
const calculateHalfWidth = (elementAWidth) => {
myStylesheet.insertRule('.halfWidth { width: ' + (parseInt(elementAWidth) / 2) + 'px; }', myStylesheet.cssRules.length);
}
calculateHalfWidth(elementAWidth);
// ================================================================================
// THE SCRIPT BELOW USES A ResizeObserver TO WATCH THE RESIZABLE .element-a DIV
// ================================================================================
const elementAObserver = new ResizeObserver(entries => {
for (let entry of entries) {
if (entry.contentRect.width !== elementAWidth) {
calculateHalfWidth(entry.contentRect.width);
}
}
});
elementAObserver.observe(elementA);
body {
font-family: sans-serif;
}
div.element {
float: left;
width: 200px;
height: 100px;
margin: 12px 3px;
text-align: center;
border: 1px solid rgb(0, 0, 0);
}
div.element h2 {
font-size: 18px;
}
div.element-a {
resize: horizontal;
overflow: auto;
}
div.element-b {
width: 300px;
}
div.halfWidth {
height: 40px;
margin: 0 auto;
border: 1px dashed rgb(255, 0, 0);
}
div.halfWidth h2 {
font-size: 14px;
}
<div class="element element-a">
<h2>Element A</h2>
<div class="halfWidth">
<h2>halfWidth</h2>
</div>
</div>
<div class="element element-b">
<h2>Element B</h2>
<div class="halfWidth">
<h2>halfWidth</h2>
</div>
</div>

CSS dynamic class

Lets say I have a flex wrapping container with an attribute data-columns like this:
<div class="grid" data-columns="2"></div>
and
.grid {
display:flex;
flex-wrap:wrap;
}
.grid[data-columns=2] > * {
width:50%;
}
.grid[data-columns=3] > * {
width:33.3%;
}
.grid[data-columns=4] > * {
width:25%;
}
the idea is that the children of .grid class elements have the width of 1 / grid.data-columns. Now i could of course just write a bunch of them but I'm wondering if there is a more dynamic way to do this? Could css actually fetch the number of columns from their parent element and use it in a calculation to determine width?
Don't understanding at it all with "Could css actually fetch the number of columns from their parent element and use it in a calculation to determine width?", but if you got, how to say 2 childs with the class "grids_child" in "grid" you can do
.grid_child{width:calc(100% / 2);}
and that will make all the elements with the ".grid_child" class, to have 50%
I didn't understand your question, but I told you this cause I see the "1 / .grid".
A good choice it to usea the pre-proccessor like someone told you
As Marc said in the comment,
CSS3 had the attr() function that might work but it can only get the attribute values of the selected element, not a parent element.
and also there's no browser supporting CSS3's attr() currently. So it have to be done with external tools such as LESS and SASS.
Here's an example written in LESS:
#max: 10;
.col(#index) when (#index =< #max) { // iterate
.grid[data-columns=#{index}] > * {
width: percentage(1 / #index);
}
.col((#index + 1));
}
.col(#index) when (#index > #max) {} // terminal
.col(1); // kickstart it
and this will be translated to
.grid[data-columns=1] > * {
width: 100%;
}
.grid[data-columns=2] > * {
width: 50%;
}
.grid[data-columns=3] > * {
width: 33.33333333%;
}
/* ...... */
.grid[data-columns=10] > * {
width: 10%;
}

LESS mixin with parameters syntax error

I've got the following mixin that adjusts the width and padding of an item to cope with IE7's lack of support for box-sizing:border-box. It gives me a syntax on & .width(#width: 100, #paddinglr: 0)
I appreciate this is missing a % but any ideas why it's breaking?
.width(#width: 100, #paddinglr: 0) {
width: #width;
padding: #paddinglr;
}
body {
&.lt-ie8 {
& .width(#width: 100, #paddinglr: 0) {
width: #width-#paddinglr;
padding: #paddinglr;
}
}
}
You cannot define a mixin as a selector string, so & .width() for your nested portion cannot be a mixin definition (which is what you have tried to make it).
I think what you are trying to do is make a generic .width() mixin to use on any particular element. It appears that you intend to just set a single number for padding, which is fine.
However, it also appears that (based off your % comment), that you expect this code to produce a width value that is 100% of the parent minus the value of the padding. This is okay, too, assuming you are using percentages for padding also. If you are not, but intend instead that the padding be a pixel value, that mixed units cannot be done by LESS as you might expect, as LESS is a preprocessor, so it is not dynamic in the sense of being able to detect the width of the parent based off the percent at run time and then subtract the padding pixel value.
Now, if your intentions are percentages, or any equal measurement values for both width and padding (whether both px, both em units, etc.), then you can get what you desire by various means. One of the many solutions would be by overriding the .width() mixin within the .lt-ie8 nest, so for example:
.width(#width: 100%, #paddinglr: 0) {
width: #width;
padding: #paddinglr;
}
body {
.someDiv {
.width(100%, 10%);
}
&.lt-ie8 {
/* here is the override of the mixin */
.width(#width: 100%, #paddinglr: 0) {
/* note, I believe you will want to multiply the padding by 2 for the width change due to left and right padding */
width: #width - (2 * #paddinglr);
padding: #paddinglr;
}
/* and here is the override of the actual css */
.someDiv {
.width(100%, 10%);
}
}
}
Which produces this CSS (minus the comments above which were just to communicate to you):
body .someDiv {
width: 100%;
padding: 10%;
}
body.lt-ie8 .someDiv {
width: 80%;
padding: 10%;
}

Calculate a percent with SCSS/SASS

I want to set a width in percentage in scss via calculation, but it gives me errors..
Invalid CSS after "...-width: (4/12)%": expected expression (e.g. 1px,
bold), was ";"
I want to do something like
$my_width: (4/12)%;
div{
width: $my_width;
}
how do I add the % sign in there?
Same question with px and em
Have you tried the percentage function ?
$my_width: percentage(4/12);
div{
width: $my_width;
}
UPDATE
This function was updated since version 1.33.0 and now this is a correct method to do it:
#use "sass:math";
div {
width: math.percentage(math.div(4,12));
}
Source: https://sass-lang.com/documentation/modules/math#percentage
Another way:
$my_width: 4/12 * 100%;
div {
width: $my_width; // 33.33333%
}
Sass will output the value in %.
I was able to make it work this way:
div{
width: (4/12)* 1%;
}
This way you don't need to use any special function.
If you wanna use a loop, maybe this solution will be working
#for $i from 1 through 12 {
.col-#{$i} {
width: #{calc(100 * $i / 12) + '%'};
}
}

Susy: How to extend content box to cover grid-padding as well?

I just started to play with Susy. I have a 12 column grid that has grid-padding on it. Now i want the header of my page to span the whole grid including the grid-padding. What I'm doing right now is calculating the overall width and then setting a negative margin on the header. That's feels rather hacky to me... Is there a cleaner way to do it?
$total-columns : 12;
$column-width : 3.5em;
$gutter-width : 1.25em;
$grid-padding : 2em;
$total-width: ( $total-columns * ($column-width + $gutter-width) ) + ( 2 * $grid-padding ) - $gutter-width;
header {
height: 150px;
width: $total-width;
margin-left: -$grid-padding;
}
You have two good options. One is a simplified version of what you have. Since block elements are 100% width by default, you can simply eliminate your width setting (and all that hacky math).
header {
height: 150px;
margin: 0 0 - $grid-padding;
}
Your other option is to use multiple containers on the page. That requires a change to the markup, but sometimes it's a simplification that works well. If you move the header outside your current container, and declare it as a container of it's own, that will do the trick.
(as a side note: if you do need the full width ever, you can simply use the columns-width() function (for inner width, without padding) or container-outer-width() for the full width including the padding.)
UPDATE:
I've been using this mixin, to apply bleed anywhere I need it:
#mixin bleed($padding: $grid-padding, $sides: left right) {
#if $sides == 'all' {
margin: - $padding;
padding: $padding;
} #else {
#each $side in $sides {
margin-#{$side}: - $padding;
padding-#{$side}: $padding;
}
}
}
Some examples:
#header { #include bleed; }
#nav { #include bleed($sides: left); }
#main { #include bleed($sides: right); }
#footer { #include bleed(space(3)); }

Resources