I have been working with the calc() CSS property and I have one doubt about it:
.main {
display: block;
margin-left: 4rem;
width: (100% - nav);
background: red;
}
nav {
position: fixed;
top: 0;
left: 0;
width: 4rem;
height: 100vh;
background: #292929;
border-right: 1px solid rgba(255, 255, 255, 0.1);
}
Like you see in the code I'm trying to subtract 100% - The nav WIDTH that it is in rem for the responsive mode, but obviously it doesn't work, is there any way to do this?
you can't use selectors in calc() as argument, if you want to subtract the nav's width then subtract nav's width already set:
.main {
width: calc(100% - 4rem);
/* mores styles */
}
The calc() CSS function can be used anywhere a <length>, <frequency>,
<angle>, <time>, <number>, or <integer> is required.
.....
Expressions
The expression can be any simple expression combining the following
operators, using standard operator precedence rules:
+
Addition.
-
Subtraction.
*
Multiplication. At least one of the arguments must be a <number>.
/
Division. The right-hand side must be a <number>.
The operands in the expression may be any length syntax value. You can
use different units for each value in your expression, if you wish.
You may also use parentheses to establish computation order when
needed.
I figured I would see if CSS variables could work and it seems to work for me in Chrome (though, not in Edge). I'm pretty sure compatibility issues might make this answer unacceptable until browser support increases, but I wanted to add it for future visitors or personal projects:
:root {
--nav-width: 5em;
}
nav {
background: red;
width: var(--nav-width);
height: 1em;
}
div {
background: blue;
width: calc(100% - var(--nav-width));
margin-left: var(--nav-width);
height: 1em;
}
<nav></nav>
<div></div>
Unfortunately it doesn't work that way. It needs to be a predetermined value such as "10px" or "3rem".
However you can do this:
.main {
/* ... */
width: calc(100% - 4rem);
}
You can't substract whole element(nav), it must be exact value:
<length>, <frequency>, <angle>, <time>, <number>, or <integer>
If you want to do it this way consider using preprocessor like Sass, you can add new variable and make it equal to some width value. You can substract then your width and this variable.
Sass way:
$nav-width: 10px;
.main {
display: block;
margin-left: 4rem;
width: (100% - $nav-width);
background: red;
}
Related
I created a mixin to manipulate easily images and replace, now my app it is growing and I don't know how to improve this code.
basically I have a include: #include img-replace("logo.png", 104px, 47px, inline-block); where I simple change the name of the image and define the pixels width and height.
I would like change it because now, some developers want just change that image name and not worry about the size anymore understand?
in that case the image has: width: 104px and height:47px, so they would like not to worry about it anymore since the next image can be bigger or smaller.
so guys any solution for this? thank you.
$path--rel : "../images";
#mixin img-replace($img, $w, $h, $disp: block) {
background-image: url('#{$path--rel}/#{$img}');
background-repeat: no-repeat;
width: $w;
height: $h;
display: $disp;
}
.site-logo {
#include img-replace("logo.png", 104px, 47px, inline-block);
margin-top: 8px;
margin-left: 6px;
}
Using SASS, you are able to set default values against parameters in a mixin; in your example for instance, I have specified the width to be 104px by default and the height to be 47px by default:
$path--rel: "../images";
#mixin img-replace($img, $w:104px, $h:47px, $disp:null) {
background-image: url('#{$path--rel}/#{$img}');
background-repeat: no-repeat;
width: $w;
height: $h;
#if ($disp) {display: $disp;}
}
.site-logo {
#include img-replace(
$img: "logo.png",
$disp: "inline-block"
);
margin-top: 8px;
margin-left: 6px;
}
If $w,$h or $disp are left off the default values get rendered. This essentially makes them optional.
The problem is that if you make the sizes optional, the element will have no width or height. This means the dev will still have to determine the elements' size or else it will just be 0x0 and the picture won't show!
If the problem is that the dev is too lazy to find and write the size of the images, you could always use a map to store all images and their sizes, so the function would insert the correct sizing depending on the image value. Read more here
So if i understand correctly, you want to use this mixin, by just passing the image path. But each image has a different size.
This cannot be done with SASS.
Instead, you should add your image inline, eg:
<img src="images/logo.png" alt="">
or
<img src="images/logo.png" alt="" width="104" height="74">
Otherwise the answer by #chris-spittles above is correct, meaning that you should pass the default width and height to your mixin.
And if you want to continue using the mixin you will need to pass the width and height for the images that have different dimensions.
As suggested before if you change your mixin to this -
#mixin img-replace($img, $w: null, $h: null, $disp: block) {
background-image: url('#{$path--rel}/#{$img}');
background-repeat: no-repeat;
display: $disp;
width: $w;
height: $h;
}
You can use your code flexibly without having any need to assign width and height arguments. So, now, if you write this -
.site-logo {
#include img-replace("logo.png");
margin-top: 8px;
margin-left: 6px;
}
it will get compiled to -
.site-logo {
background-image: url("../images/logo.png");
background-repeat: no-repeat;
display: block;
margin-top: 8px;
margin-left: 6px;
}
It will also preserve your previously written codes, without any changes.
Now, if you have any specific requirements, like providing default values to different types of images which a developer can assign, you can add maps in your code -
$small-img: ( w: 100px, h: 100px );
$medium-img: ( w: 200px, h: 200px );
Now you can call img-replace like this -
.site-logo {
#include img-replace("logo.png", $small-img...);
}
.site-medium-image {
#include img-replace("logo.png", $medium-img...);
}
This will get compiled to -
.site-logo {
background-image: url("../images/logo.png");
background-repeat: no-repeat;
display: block;
width: 100px;
height: 100px;
}
.site-medium-image {
background-image: url("../images/logo.png");
background-repeat: no-repeat;
display: block;
width: 200px;
height: 200px;
}
This ... makes arguments variable arguments
Sass supports "variable arguments," which are arguments at the end of
a mixin or function declaration that take all leftover arguments and
package them up as a list. These arguments look just like normal
arguments, but are followed by ...
I want to be able to do the following:
height: 25% - 5px;
Obviously when I do that I get the error:
Incompatible units: 'px' and '%'.
Sass cannot perform arithmetic on values that cannot be converted from one unit to the next. Sass has no way of knowing exactly how wide "100%" is in terms of pixels or any other unit. That's something only the browser knows.
You need to use calc() instead. Check browser compatibility on Can I use...
.foo {
height: calc(25% - 5px);
}
If your values are in variables, you may need to use interpolation turn them into strings (otherwise Sass just tries to perform arithmetic):
$a: 25%;
$b: 5px;
.foo {
width: calc(#{$a} - #{$b});
}
There is a calc function in both SCSS [compile-time] and CSS [run-time]. You're likely invoking the former instead of the latter.
For obvious reasons mixing units won't work compile-time, but will at run-time.
You can force the latter by using unquote, a SCSS function.
.selector { height: unquote("-webkit-calc(100% - 40px)"); }
$var:25%;
$foo:5px;
.selector {
height:unquote("calc( #{$var} - #{$foo} )");
}
IF you know the width of the container, you could do like this:
#container
width: #{200}px
#element
width: #{(0.25 * 200) - 5}px
I'm aware that in many cases #container could have a relative width. Then this wouldn't work.
Sorry for reviving old thread - Compass' stretch with an :after pseudo-selector might suit your purpose - eg. if you want a div to fill width from left to (50% + 10px) of screen you could use (in SASS indented syntax):
.example
background: red
+stretch(0, -10px, 0, 0)
&:after
+stretch(0, 0, 0, 50%)
content: ' '
background: blue
The :after element fills 50% to the right of .example (leaving 50% available for .example's width), then .example is stretched to that width plus 10px.
Just add the percentage value into a variable and use #{$variable}
for example
$twentyFivePercent:25%;
.selector {
height: calc(#{$twentyFivePercent} - 5px);
}
I will calculate width in some element from percent to pixel so I will minus -10px via using LESS and calc(). It´s possible?
div {
span {
width:calc(100% - 10px);
}
}
I using CSS3 calc() so it doesn't work: calc(100% - 10px)
Example: if 100% = 500px so width = 490px (500-10);
I made a demo for testing : http://jsfiddle.net/4DujZ/55/
so padding will say: 5 (10px / 2) all the time when I resizing.
Can I do it in LESS? I know how to do in jQuery and simple CSS like margin padding or else... but i will try to do functional in LESS with calc()
You can escape the calc arguments in order to prevent them from being evaluated on compilation.
Using your example, you would simply surround the arguments, like this:
calc(~'100% - 10px')
Demo : http://jsfiddle.net/c5aq20b6/
I find that I use this in one of the following three ways:
Basic Escaping
Everything inside the calc arguments is defined as a string, and is totally static until it's evaluated by the client:
LESS Input
div {
> span {
width: calc(~'100% - 10px');
}
}
CSS Output
div > span {
width: calc(100% - 10px);
}
Interpolation of Variables
You can insert a LESS variable into the string:
LESS Input
div {
> span {
#pad: 10px;
width: calc(~'100% - #{pad}');
}
}
CSS Output
div > span {
width: calc(100% - 10px);
}
Mixing Escaped and Compiled Values
You may want to escape a percentage value, but go ahead and evaluate something on compilation:
LESS Input
#btnWidth: 40px;
div {
> span {
#pad: 10px;
width: calc(~'(100% - #{pad})' - (#btnWidth * 2));
}
}
CSS Output
div > span {
width: calc((100% - 10px) - 80px);
}
Source: http://lesscss.org/functions/#string-functions-escape.
I think width: -moz-calc(25% - 1em); is what you are looking for.
And you may want to give this Link a look for any further assistance
Or, you could use the margin attribute like this:
{
background:#222;
width:100%;
height:100px;
margin-left: 10px;
margin-right: 10px;
display:block;
}
Try this :
width:auto;
margin-right:50px;
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%;
}
I would like to use conditions in my CSS.
The idea is that I have a variable that I replace when the site is run to generate the right style-sheet.
I want it so that according to this variable the style-sheet changes!
It looks like:
[if {var} eq 2 ]
background-position : 150px 8px;
[else]
background-position : 4px 8px;
Can this be done? How do you do this?
Not in the traditional sense, but you can use classes for this, if you have access to the HTML. Consider this:
<p class="normal">Text</p>
<p class="active">Text</p>
and in your CSS file:
p.normal {
background-position : 150px 8px;
}
p.active {
background-position : 4px 8px;
}
That's the CSS way to do it.
Then there are CSS preprocessors like Sass. You can use conditionals there, which'd look like this:
$type: monster;
p {
#if $type == ocean {
color: blue;
} #else if $type == matador {
color: red;
} #else if $type == monster {
color: green;
} #else {
color: black;
}
}
Disadvantages are, that you're bound to pre-process your stylesheets, and that the condition is evaluated at compile time, not run time.
A newer feature of CSS proper are custom properties (a.k.a. CSS variables). They are evaluated at run time (in browsers supporting them).
With them you could do something along the line:
:root {
--main-bg-color: brown;
}
.one {
background-color: var(--main-bg-color);
}
.two {
background-color: black;
}
Finally, you can preprocess your stylesheet with your favourite server-side language. If you're using PHP, serve a style.css.php file, that looks something like this:
p {
background-position: <?php echo (#$_GET['foo'] == 'bar')? "150" : "4"; ?>px 8px;
}
In this case, you will however have a performance impact, since caching such a stylesheet will be difficult.
I am surprised that nobody has mentioned CSS pseudo-classes, which are also a sort-of conditionals in CSS. You can do some pretty advanced things with this, without a single line of JavaScript.
Some pseudo-classes:
:active - Is the element being clicked?
:checked - Is the radio/checkbox/option checked? (This allows for conditional styling through the use of a checkbox!)
:empty - Is the element empty?
:fullscreen - Is the document in full-screen mode?
:focus - Does the element have keyboard focus?
:focus-within - Does the element, or any of its children, have keyboard focus?
:has([selector]) - Does the element contain a child that matches [selector]? (Sadly, not supported by any of the major browsers.)
:hover - Does the mouse hover over this element?
:in-range/:out-of-range - Is the input value between/outside min and max limits?
:invalid/:valid - Does the form element have invalid/valid contents?
:link - Is this an unvisited link?
:not() - Invert the selector.
:target - Is this element the target of the URL fragment?
:visited - Has the user visited this link before?
Example:
div { color: white; background: red }
input:checked + div { background: green }
<input type=checkbox>Click me!
<div>Red or green?</div>
Update:
I've written a article regarding the below unique method in CSS-Tricks which goes into futher detail
I've devised the below demo using a mix of tricks which allows simulating if/else scenarios for some properties. Any property which is numerical in its essence is easy target for this method, but properties with text values are.
This code has 3 if/else scenarios, for opacity, background color & width. All 3 are governed by two Boolean variables bool and its opposite notBool.
Those two Booleans are the key to this method, and to achieve a Boolean out of a none-boolean dynamic value, requires some math which luckily CSS allows using min & max functions.
Obviously those functions (min/max) are supported in recent browsers' versions which also supports CSS custom properties (variables).
var elm = document.querySelector('div')
setInterval(()=>{
elm.style.setProperty('--width', Math.round(Math.random()*80 + 20))
}, 1000)
:root{
--color1: lightgreen;
--color2: salmon;
--width: 70; /* starting value, randomly changed by javascript every 1 second */
}
div{
--widthThreshold: 50;
--is-width-above-limit: Min(1, Max(var(--width) - var(--widthThreshold), 0));
--is-width-below-limit: calc(1 - var(--is-width-above-limit));
--opacity-wide: .4; /* if width is ABOVE 50 */
--radius-narrow: 10px; /* if width is BELOW 50 */
--radius-wide: 60px; /* if width is ABOVE 50 */
--height-narrow: 80px; /* if width is ABOVE 50 */
--height-wide: 160px; /* if width is ABOVE 50 */
--radiusToggle: Max(var(--radius-narrow), var(--radius-wide) * var(--is-width-above-limit));
--opacityToggle: calc(calc(1 + var(--opacity-wide)) - var(--is-width-above-limit));
--colorsToggle: var(--color1) calc(100% * var(--is-width-above-limit)),
var(--color2) calc(100% * var(--is-width-above-limit)),
var(--color2) calc(100% * (1 - var(--is-width-above-limit)));
--height: Max(var(--height-wide) * var(--is-width-above-limit), var(--height-narrow));
height: var(--height);
text-align: center;
line-height: var(--height);
width: calc(var(--width) * 1%);
opacity: var(--opacityToggle);
border-radius: var(--radiusToggle);
background: linear-gradient(var(--colorsToggle));
transition: .3s;
}
/* prints some variables */
div::before{
counter-reset: aa var(--width);
content: counter(aa)"%";
}
div::after{
counter-reset: bb var(--is-width-above-limit);
content: " is over 50% ? "counter(bb);
}
<div></div>
Another simple way using clamp:
label{ --width: 150 }
input:checked + div{ --width: 400 }
div{
--isWide: Clamp(0, (var(--width) - 150) * 99999, 1);
width: calc(var(--width) * 1px);
height: 150px;
border-radius: calc(var(--isWide) * 20px); /* if wide - add radius */
background: lightgreen;
}
<label>
<input type='checkbox' hidden>
<div>Click to toggle width</div>
</label>
Best so far:
I have come up with a totally unique method, which is even simpler!
This method is so cool because it is so easy to implement and also to understand. it is based on animation step() function.
Since bool can be easily calculated as either 0 or 1, this value can be used in the step! if only a single step is defined, then the if/else problem is solved.
Using the keyword forwards persist the changes.
var elm = document.querySelector('div')
setInterval(()=>{
elm.style.setProperty('--width', Math.round(Math.random()*80 + 20))
}, 1000)
:root{
--color1: salmon;
--color2: lightgreen;
}
#keyframes if-over-threshold--container{
to{
--height: 160px;
--radius: 30px;
--color: var(--color2);
opacity: .4; /* consider this as additional, never-before, style */
}
}
#keyframes if-over-threshold--after{
to{
content: "true";
color: green;
}
}
div{
--width: 70; /* must be unitless */
--height: 80px;
--radius: 10px;
--color: var(--color1);
--widthThreshold: 50;
--is-width-over-threshold: Min(1, Max(var(--width) - var(--widthThreshold), 0));
text-align: center;
white-space: nowrap;
transition: .3s;
/* if element is narrower than --widthThreshold */
width: calc(var(--width) * 1%);
height: var(--height);
line-height: var(--height);
border-radius: var(--radius);
background: var(--color);
/* else */
animation: if-over-threshold--container forwards steps(var(--is-width-over-threshold));
}
/* prints some variables */
div::before{
counter-reset: aa var(--width);
content: counter(aa)"% is over 50% width ? ";
}
div::after{
content: 'false';
font-weight: bold;
color: darkred;
/* if element is wider than --widthThreshold */
animation: if-over-threshold--after forwards steps(var(--is-width-over-threshold)) ;
}
<div></div>
I've found a Chrome bug which I have reported that can affect this method in some situations where specific type of calculations is necessary, but there's a way around it.
You can use calc() in combination with var() to sort of mimic conditionals:
:root {
--var-eq-two: 0;
}
.var-eq-two {
--var-eq-two: 1;
}
.block {
background-position: calc(
150px * var(--var-eq-two) +
4px * (1 - var(--var-eq-two))
) 8px;
}
concept
Below is my old answer which is still valid but I have a more opinionated approach today:
One of the reasons why CSS sucks so much is exactly that it doesn't have conditional syntax. CSS is per se completely unusable in the modern web stack. Use SASS for just a little while and you'll know why I say that. SASS has conditional syntax... and a LOT of other advantages over primitive CSS too.
Old answer (still valid):
It cannot be done in CSS in general!
You have the browser conditionals like:
/*[if IE]*/
body {height:100%;}
/*[endif]*/
But nobody keeps you from using Javascript to alter the DOM or assigning classes dynamically or even concatenating styles in your respective programming language.
I sometimes send css classes as strings to the view and echo them into the code like that (php):
<div id="myid" class="<?php echo $this->cssClass; ?>">content</div>
You could create two separate stylesheets and include one of them based on the comparison result
In one of the you can put
background-position : 150px 8px;
In the other one
background-position : 4px 8px;
I think that the only check you can perform in CSS is browser recognition:
Conditional-CSS
CSS is a nicely designed paradigm, and many of it's features are not much used.
If by a condition and variable you mean a mechanism to distribute a change of some value to the whole document, or under a scope of some element, then this is how to do it:
var myVar = 4;
document.body.className = (myVar == 5 ? "active" : "normal");
body.active .menuItem {
background-position : 150px 8px;
background-color: black;
}
body.normal .menuItem {
background-position : 4px 8px;
background-color: green;
}
<body>
<div class="menuItem"></div>
</body>
This way, you distribute the impact of the variable throughout the CSS styles.
This is similar to what #amichai and #SeReGa propose, but more versatile.
Another such trick is to distribute the ID of some active item throughout the document, e.g. again when highlighting a menu: (Freemarker syntax used)
var chosenCategory = 15;
document.body.className = "category" + chosenCategory;
<#list categories as cat >
body.category${cat.id} .menuItem { font-weight: bold; }
</#list>
<body>
<div class="menuItem"></div>
</body>
Sure,this is only practical with a limited set of items, like categories or states, and not unlimited sets like e-shop goods, otherwise the generated CSS would be too big. But it is especially convenient when generating static offline documents.
One more trick to do "conditions" with CSS in combination with the generating platform is this:
.myList {
/* Default list formatting */
}
.myList.count0 {
/* Hide the list when there is no item. */
display: none;
}
.myList.count1 {
/* Special treatment if there is just 1 item */
color: gray;
}
<ul class="myList count${items.size()}">
<!-- Iterate list's items here -->
<li>Something...</div>
</ul>
You can use not instead of if like
.Container *:not(a)
{
color: #fff;
}
Set the server up to parse css files as PHP and then define the variable variable with a simple PHP statement.
Of course this assumes you are using PHP...
This is a little extra info to the Boldewyn answer above.
Add some php code to do the if/else
if($x==1){
print "<p class=\"normal\">Text</p>\n";
} else {
print "<p class=\"active\">Text</p>\n";
}
CSS has a feature: Conditional Rules. This feature of CSS is applied based on a specific condition. Conditional Rules are:
#supports
#media
#document
Syntax:
#supports ("condition") {
/* your css style */
}
Example code snippet:
<!DOCTYPE html>
<html>
<head>
<title>Supports Rule</title>
<style>
#supports (display: block) {
section h1 {
background-color: pink;
color: white;
}
section h2 {
background-color: pink;
color: black;
}
}
</style>
</head>
<body>
<section>
<h1>Stackoverflow</h1>
<h2>Stackoverflow</h2>
</section>
</body>
</html>
As far as i know, there is no if/then/else in css. Alternatively, you can use javascript function to alter the background-position property of an element.
Yet another option (based on whether you want that if statement to be dynamically evaluated or not) is to use the C preprocessor, as described here.
You can use javascript for this purpose, this way:
first you set the CSS for the 'normal' class and for the 'active' class
then you give to your element the id 'MyElement'
and now you make your condition in JavaScript, something like the example below... (you can run it, change the value of myVar to 5 and you will see how it works)
var myVar = 4;
if(myVar == 5){
document.getElementById("MyElement").className = "active";
}
else{
document.getElementById("MyElement").className = "normal";
}
.active{
background-position : 150px 8px;
background-color: black;
}
.normal{
background-position : 4px 8px;
background-color: green;
}
div{
width: 100px;
height: 100px;
}
<div id="MyElement">
</div>
You can add container div for all your condition scope.
Add the condition value as a class to the container div. (you can set it by server side programming - php/asp...)
<!--container div-->
<div class="true-value">
<!-- your content -->
<p>my content</p>
<p>my content</p>
<p>my content</p>
</div>
Now you can use the container class as a global variable for all elements in the div using a nested selector, without adding the class to each element.
.true-value p{
background-color:green;
}
.false-value p{
background-color:red;
}
Besides the answers above, soon another way to directly use if/else -like conditions, and even more closely aligned with other scripting languages, would be via #when / #else conditionals. These conditionals would be implemented to exercise easily recognizable logic chain, for example:
#when supports(display: flex) {
.container {
display: flex
}
} #else media and (min-width: 768px) {
.container {
min-width: 768px
}
} #else {
.container {
width: 100%
}
}
As of February 2022 there is no browser support. Please see this W3C module for more info.
(Yes, old thread. But it turned up on top of a Google-search so others might be interested as well)
I guess the if/else-logic could be done with javascript, which in turn can dynamically load/unload stylesheets. I haven't tested this across browsers etc. but it should work. This will get you started:
http://www.javascriptkit.com/javatutors/loadjavascriptcss.shtml
If you're open to using jquery, you can set conditional statements using javascript within the html:
$('.class').css("color",((Variable > 0) ? "#009933":"#000"));
This will change the text color of .class to green if the value of Variable is greater than 0.