Is it possible, in Sass, to manipulate a value a given element already inherits?
I am aiming for something like this:
body
color: blue
.warning
color: red
strong
color: darken(inherit,20)
Inheritance
No. Sass doesn't 'know' what selector to inherit the color from. It would have to know that strong is a descendant of body. That seems like a reasonable enough assumption for you and I since strong is not allowed outside of the body, but that sort of assumption cannot be made about most selectors. Sass would also have to know that there are no cascades happening from other ancestor elements.
ul {
color: red;
}
ol {
color: blue;
}
li {
// which color do I inherit from ????
}
Well can I specify which selector I want to copy from?
Sass does not grant access to the values of any previously declared variables in any fashion, either. There is no way to specify "be darker than the body's color". CSS rules are not objects or mappings and are not accessible in any way. Your case may be simple, but consider a more complex case like this:
.foo {
background: mix(white, blue); // fallback for non-rgba browsers
background: rgba(blue, .5);
.baz & {
background: yellow;
}
#media (min-width 30em) {
background: orange;
}
#supports (flex-wrap: wrap) {
background: red;
}
}
.bar {
// access which background color from .foo ????
}
Well what can I do?
You'll either need to use variables or it has to be a feature of vanilla CSS to do what you want.
Old-Fashioned CSS
Some properties can give the illusion of being generated/inherited dynamically using stuff that's been supported by browsers for years:
ul.one {
background: white;
}
ul.two {
background: yellow;
}
ul {
background: rgba(0, 120, 255, .2);
padding: 1em;
}
<ul class="one">
<li><ul>
<li><ul>
<li>Foo</li>
</ul></li>
</ul></li>
</ul>
<ul class="two">
<li><ul>
<li><ul>
<li>Foo</li>
</ul></li>
</ul></li>
</ul>
CSS Variables
Generating CSS variables is about as close as you're going to get to being able to manipulate an inherited property. Browser support isn't quite there yet (check caniuse), but here's what that would look like:
Sass:
ul {
--list-color: orange;
--darker-color: darken(orange, 15%);
color: var(--list-color);
}
ol {
--list-color: green;
--darker-color: darken(green, 10%);
color: var(--list-color);
}
li {
background: var(--darker-color);
}
Output:
ul {
--list-color: orange;
--darker-color: #b37400;
color: var(--list-color);
}
ol {
--list-color: green;
--darker-color: #004d00;
color: var(--list-color);
}
li {
background: var(--darker-color);
}
<ul>
<li>Foo</li>
</ul>
<ol>
<li>Bar</li>
</ol>
If you're using a browser that supports CSS variables, the result should look like this:
I was looking for the same thing, and came across this. Your question was answered, but it didn't solve the problem.
Here's the solution: http://codepen.io/monsto/pen/tiokl
If your HTML was this:
<div class="main">
<header class="header">
<div class="warning">
<p><strong>Danger,</strong> Will Robinson!</p>
</div>
</header>
</div>
Then using SASS you could do this:
$bg: #f88;
#mixin colorize {
$bg: darken($bg,15%) !global; // !global flag required for 3.4 or later, remove if using 3.3 or older
background: $bg;
}
.warning {
background: $bg;
p {
#include colorize;
strong {
#include colorize;
}
}
}
SASS seems to have no idea of the results of it's output. Therefore, inherit means nothing to it. You're basically asking it to know what the output is before it's output.
It does however know it's variables as, by default, they're tightly scoped.
From the docs:
Variables are only available within the level of nested selectors where they’re defined. If they’re defined outside of any nested selectors, they’re available everywhere.
AND THEN variables in mixins:
The block of content passed to a mixin are evaluated in the scope where the block is defined, not in the scope of the mixin.
This allows the above mixin to take a known variable, defined in the parent level, and redefines it for the current level and available to it's children. It's like doing $x = $x + 1 inside a multi-nested loop
TBPH, this rather changes the way I think about SASS. It's clearly a lot more programmatic than I thought.
Given that an element cannot have multiple of the same properties that combine and the fact that inherit can't know what the current rendered state is, your options are to
1) Keep track of the past transforms yourself using SASS variables: Demo
.parent {
$firstTrans: translateX(50%);
transform: $firstTrans;
.child {
/* Old followed by new */
transform: $firstTrans rotate(10deg);
}
}
2) Apply the transform to a parent (perhaps adding another container if needed): Demo
3) Use Javascript to combine the current transform with the one you want to add (this is the only way you can make sure to remove the transform applied to the parent if that's desired): Demo
Note: This answer is from a merged post because of this meta post.
This answers addresses the darken function specifically: A possible alternative is using the CSS brightness() filter instead of SASS's (or LESS's) darken() function. You will basically need to wrap the color inside a span tag so the filter would not affect other elements.
Simple demo:
.red {color: red}
.blue {color: blue}
.green {color: green}
span {
display: inline-block;
padding: 1em;
}
.darken span {
-webkit-filter: brightness(0.4);
filter: brightness(0.4);
}
<span class="red">Red</span>
<span class="blue">Blue</span>
<span class="green">Green</span>
<div class="darken">
<span class="red">Red</span>
<span class="blue">Blue</span>
<span class="green">Green</span>
</div>
jsFiddle: https://jsfiddle.net/azizn/hhorhz9s/
You need to keep in mind browser compatibility, it should work for IE Edge, latest Firefox and Chrome. See caniuse or MDN for more information.
In the case of a background darken, you could use a pseudo selector with opacity or add a semi-transparent black PNG background-image.
Related
I am writing a stylesheet to extend a base stylesheet whose CSS has many pseudo classes applied to certain elements. I would like my stylesheet to override some of these styles with a single style that is applied to an element no matter what state it is in, whether hovered on, focussed etc.
For example, the base stylesheet might have the styles
.classname {
color:#f00;
}
.classname:hover {
color:#0f0;
}
.classname:active {
color:#00f;
}
but adding the following after these styles does not override the pseudo states...
.classname {
color:#fff;
}
The following works, but it feels a lot of code for something that seems simple.
.classname,
.classname:active,
.classname:hover,
.classname:focus,
.classname:visited,
.classname:valid{
color:#fff;
}
Likewise, I know an !important would work, but that's normally a warning sign of a poorly structured stylesheet.
Is there anything along the lines of a .classname:* that would cover every possible state, or some way to simply remove all pseudo classes?
If you are able to put the classes inside some wrapper id you can prevent the pseudo-classes to take effect due to specificity:
body {
background: black;
}
.classname {
color:#f00;
}
.classname:hover {
color:#0f0;
}
.classname:active {
color:#00f;
}
#a .classname {
color:#fff;
}
<div class="classname">all pseudo works</div>
<div id="a">
<div class="classname">none of the pseudo works</div>
</div>
I think, it could be solved with :any pseudo-class.
Google
<style>
a:link { color: blue; }
a:hover { color: red; }
a:-webkit-any(a) { color: green; }
</style>
https://jsfiddle.net/ycfokuju
Browser support is not perfect: https://developer.mozilla.org/en/docs/Web/CSS/:any
Edit:
Actually, as I discovered, this answer isn't very accurate. (Despite it was upvoted 4 times, lol).
First of all, you don't need :any fot this task. You need :any-link.
The second point is that :any itself is a former name of :matches. So, in our terminology we should use terms :any-link and :matches and don't use term :any.
Example of using :any-link: https://developer.mozilla.org/en-US/docs/Web/CSS/:any-link
Examples of using :mathes: https://css-tricks.com/almanac/selectors/m/matches/
I haven't edited the code itself, so fix it yourself according to this new information.
here is a fiddle with the problem:
https://jsfiddle.net/c2exs2f7/3/
How does the second "blue" stay like the first instance (it should have color: white) without changing the HTML structure?
HTML
<div class="blue">
<div class="content">
<div class="label">blue</div>
<div class="yellow">
<div class="content">
<div class="label">yellow</div>
<div class="blue">
<div class="content">
<div class="label">blue</div>
</div>
</div>
</div>
</div>
</div>
</div>
SCSS
// Skip until...
div {
border-radius: .25em;
padding: .5em;
font-family: helvetica, sans-serif;
}
// ...here:
.blue {
background-color: hsl(220,100%,50%);
.content {
color: white;
}
}
.yellow {
background-color: hsl(60,100%,50%);
.content {
color: hsl(0,0%,10%);
}
}
EDIT #1
Thank you guys for these fast responses!
I am working on a grid system where I am able to nest different grid systems (with different CSS values).
The selectors .yellow .content and .blue .content have the same specificity (20 in this case), therefore the selector that appears later in the stylesheet will override the first one due to the cascading nature of a stylesheet. In this case, the selector .yellow .content is overriding .blue .content, which is why the nested .blue element is black.
One quick solution would be to select nested .blue element with the selector .blue .blue:
Updated Example
.blue,
.blue .blue {
background-color: hsl(220,100%,50%);
.content {
color: white;
}
}
An arguably better approach would be to only select direct .content children elements using the child selector, >:
Updated Example
.blue {
background-color: hsl(220,100%,50%);
> .content {
color: white;
}
}
.yellow {
background-color: hsl(60,100%,50%);
> .content {
color: hsl(0,0%,10%);
}
}
Based on your comments, the ordering/layering of the elements may vary. An alternative solution would be to set the color property on the .blue/.yellow element and then set the color property of the children elements to inherit:
Updated Example - this seems to work for all variants.
.blue {
background-color: hsl(220,100%,50%);
color: white;
.content {
color: inherit;
}
}
.yellow {
background-color: hsl(60,100%,50%);
color: hsl(0,0%,10%);
.content {
color: inherit;
}
}
See https://jsfiddle.net/c2exs2f7/4/
What I did was to enforce inheritance only for the child content classed DIV, not the entire descendance.
Applying the immediate children operator > in the SCSS makes the .content div to consider only its immediate parent color.
Go on and try nesting more DIVs, you will see that it works.
You can't. Not with inherent anyway. Because the second blue will inherent from the yellow. So if u want all blue always have white letters and yellow always black letters. Why not just put:
.blue { color: #fff; }
.yellow { color: hsl(0,0%,10%); }
And you won't need the ".content" wrapper.
I had this same issue where the HTML nesting varies and so it's not possible to make more specific selectors due to overwhelming complexity and non-DRY code.
Here's the solution I came to:
https://jsfiddle.net/cg0u8v1s/
Basically, a systematic approach to the class names is key so you can use a CSS attribute selector reliably (although I'd recommend a more unique naming convention than "color-" as it's too generic.).
Example:
.color-blue {
&,
[class*="color-"] &,
[class*="color-"] [class*="color-"] & {// Only needed if you want a 3rd level of nesting to work.
background-color: blue;
.content {
color: skyblue;
}
}
}
.color-yellow {
&,
[class*="color-"] &,
[class*="color-"] [class*="color-"] & {// Only needed if you want a 3rd level of nesting to work.
background-color: yellow;
.content {
color: brown;
}
}
}
This will output selectors that become more specific with nesting without the need for non-DRY code or having to use !important.
The CSS output will look like this:
.color-blue,
[class*="color-"] .color-blue,
[class*="color-"] [class*="color-"] .color-blue {
// code...
}
Is it possible, in Sass, to manipulate a value a given element already inherits?
I am aiming for something like this:
body
color: blue
.warning
color: red
strong
color: darken(inherit,20)
Inheritance
No. Sass doesn't 'know' what selector to inherit the color from. It would have to know that strong is a descendant of body. That seems like a reasonable enough assumption for you and I since strong is not allowed outside of the body, but that sort of assumption cannot be made about most selectors. Sass would also have to know that there are no cascades happening from other ancestor elements.
ul {
color: red;
}
ol {
color: blue;
}
li {
// which color do I inherit from ????
}
Well can I specify which selector I want to copy from?
Sass does not grant access to the values of any previously declared variables in any fashion, either. There is no way to specify "be darker than the body's color". CSS rules are not objects or mappings and are not accessible in any way. Your case may be simple, but consider a more complex case like this:
.foo {
background: mix(white, blue); // fallback for non-rgba browsers
background: rgba(blue, .5);
.baz & {
background: yellow;
}
#media (min-width 30em) {
background: orange;
}
#supports (flex-wrap: wrap) {
background: red;
}
}
.bar {
// access which background color from .foo ????
}
Well what can I do?
You'll either need to use variables or it has to be a feature of vanilla CSS to do what you want.
Old-Fashioned CSS
Some properties can give the illusion of being generated/inherited dynamically using stuff that's been supported by browsers for years:
ul.one {
background: white;
}
ul.two {
background: yellow;
}
ul {
background: rgba(0, 120, 255, .2);
padding: 1em;
}
<ul class="one">
<li><ul>
<li><ul>
<li>Foo</li>
</ul></li>
</ul></li>
</ul>
<ul class="two">
<li><ul>
<li><ul>
<li>Foo</li>
</ul></li>
</ul></li>
</ul>
CSS Variables
Generating CSS variables is about as close as you're going to get to being able to manipulate an inherited property. Browser support isn't quite there yet (check caniuse), but here's what that would look like:
Sass:
ul {
--list-color: orange;
--darker-color: darken(orange, 15%);
color: var(--list-color);
}
ol {
--list-color: green;
--darker-color: darken(green, 10%);
color: var(--list-color);
}
li {
background: var(--darker-color);
}
Output:
ul {
--list-color: orange;
--darker-color: #b37400;
color: var(--list-color);
}
ol {
--list-color: green;
--darker-color: #004d00;
color: var(--list-color);
}
li {
background: var(--darker-color);
}
<ul>
<li>Foo</li>
</ul>
<ol>
<li>Bar</li>
</ol>
If you're using a browser that supports CSS variables, the result should look like this:
I was looking for the same thing, and came across this. Your question was answered, but it didn't solve the problem.
Here's the solution: http://codepen.io/monsto/pen/tiokl
If your HTML was this:
<div class="main">
<header class="header">
<div class="warning">
<p><strong>Danger,</strong> Will Robinson!</p>
</div>
</header>
</div>
Then using SASS you could do this:
$bg: #f88;
#mixin colorize {
$bg: darken($bg,15%) !global; // !global flag required for 3.4 or later, remove if using 3.3 or older
background: $bg;
}
.warning {
background: $bg;
p {
#include colorize;
strong {
#include colorize;
}
}
}
SASS seems to have no idea of the results of it's output. Therefore, inherit means nothing to it. You're basically asking it to know what the output is before it's output.
It does however know it's variables as, by default, they're tightly scoped.
From the docs:
Variables are only available within the level of nested selectors where they’re defined. If they’re defined outside of any nested selectors, they’re available everywhere.
AND THEN variables in mixins:
The block of content passed to a mixin are evaluated in the scope where the block is defined, not in the scope of the mixin.
This allows the above mixin to take a known variable, defined in the parent level, and redefines it for the current level and available to it's children. It's like doing $x = $x + 1 inside a multi-nested loop
TBPH, this rather changes the way I think about SASS. It's clearly a lot more programmatic than I thought.
Given that an element cannot have multiple of the same properties that combine and the fact that inherit can't know what the current rendered state is, your options are to
1) Keep track of the past transforms yourself using SASS variables: Demo
.parent {
$firstTrans: translateX(50%);
transform: $firstTrans;
.child {
/* Old followed by new */
transform: $firstTrans rotate(10deg);
}
}
2) Apply the transform to a parent (perhaps adding another container if needed): Demo
3) Use Javascript to combine the current transform with the one you want to add (this is the only way you can make sure to remove the transform applied to the parent if that's desired): Demo
Note: This answer is from a merged post because of this meta post.
This answers addresses the darken function specifically: A possible alternative is using the CSS brightness() filter instead of SASS's (or LESS's) darken() function. You will basically need to wrap the color inside a span tag so the filter would not affect other elements.
Simple demo:
.red {color: red}
.blue {color: blue}
.green {color: green}
span {
display: inline-block;
padding: 1em;
}
.darken span {
-webkit-filter: brightness(0.4);
filter: brightness(0.4);
}
<span class="red">Red</span>
<span class="blue">Blue</span>
<span class="green">Green</span>
<div class="darken">
<span class="red">Red</span>
<span class="blue">Blue</span>
<span class="green">Green</span>
</div>
jsFiddle: https://jsfiddle.net/azizn/hhorhz9s/
You need to keep in mind browser compatibility, it should work for IE Edge, latest Firefox and Chrome. See caniuse or MDN for more information.
In the case of a background darken, you could use a pseudo selector with opacity or add a semi-transparent black PNG background-image.
I'm having some issues with the CSS "hierarchy" (not sure if it's proper to call it a hierarchy). I'm trying to style the below bit of HTML.
<body>
<section id="content">
<article>
<ul class="posts-list">
<li class="post-item">
<h2>[post title]</h2>
<p class="item-description">...</p>
<p class="item-meta">...</p>
</li>
...
</ul>
</article>
</section>
</body>
Since section#content changes on every page I have, I wanted to maintain consistent styles across all of them, so I wrote some "global" CSS rules.
#content {
color: #000;
margin-left: 300px;
max-width: 620px;
padding: 0px 10px;
position: relative;
}
#content p,
#content li {
color: #111;
font: 16px / 24px serif;
}
I wanted to style HTML within a ul.posts-list differently, so I wrote these rules.
li.post-item > * {
margin: 0px;
}
.item-description {
color: #FFF;
}
.item-meta {
color: #666;
}
However, I ran into some issues. Here is how Chrome is rendering the CSS:
For some reason, the rules #content p, #content li are overriding my rules for .item-description and .item-meta. My impression was that class/id names are considered specific and thus higher priority. However, it seems that I have a misunderstanding of how CSS works. What am I doing wrong here?
Edit: Also, where can I read up more about how this hierarchy works?
Elements id have the priority in CSS since they are the most specific.
You just have to use the id:
#content li.post-item > * {
margin: 0px;
}
#content .item-description {
color: #FFF;
}
#content .item-meta {
color: #666;
}
Basically id have the priority on class which the priority on tags(p,li,ul, h1...). To override the rule, just make sure you have the priority ;)
The "hierarchy" in which CSS rules are measured is called specificity. Each part of a CSS rule has an actual numerical base-10 value. IDs are worth 100 while classes are only 10.
For more information see http://coding.smashingmagazine.com/2007/07/27/css-specificity-things-you-should-know/
Targeting ID's is more specific than targeting classes. More specific styling will overwrite less specific styling. It should be noted that in-line styling in HTML is more specific and will therefore overwrite ID-targeted styling. In other words:
<p style="color:white" id="itemDescId" class="item-description">...</p>
With the CSS:
p{color:blue;}
#itemDescId{color:red;}
.item-description{color:green}
The text will appear white - not because it's closest to the html code, but because it's higher in the specificity hierarchy. If you remove the inline styling (and you normally should for cleaner more manageable code), then the text would become red. Remove the ID and it will be green. And finally it will be blue once the class is removed.
This is one of the more complex topics to understand in CSS, and I'm only scratching the surface, but the easiest description I've found on how CSS specificity works is over at CSS tricks:
http://css-tricks.com/specifics-on-css-specificity/
My response should have been a "comment" on the answer, but I have the correct fix although #tibo answered correctly:
li.post-item > * {
margin: 0px !important;
}
.item-description {
color: #FFF !important;
}
.item-meta {
color: #666 !important;
}
The !important rule will override the order of evaluation between id and class.
Here is a link to an article, When Using !important is The Right Choice, that will help you to understand... it made my life easier :)
Better to follow the CSS standards.
choose css selector and makeit under its parent then u may not to get conflicts when loading css fles (like .css files)
Just wondering when you use multiple classes on the one element such as class="foo bar" and those classes are setup as below:
.foo {
margin-right: 10px;
}
.bar {
margin-right: 0px;
}
Which class will have specificity? Will the margin be 10px or 0px?
It works based on precedence within the CSS. Therefore the item to occur most recently will override any previous styles.
CASE 1
.foo { background : red; }
.bar { background : blue; }
class = 'foo bar' would be blue in this instance.
CASE 2
.bar { background : blue; }
.foo { background : red; }
class = 'foo bar' would be red in this instance.
Working Example
Also, if you wish to target the element who has only both classes, you can use this syntax:
<ul>
<li class="foo first">Something</li>
<li class="foo">Somthing else</li>
<li class="foo">Something more</li>
</ul>
.foo {
color: red;
}
.foo.first {
color: blue
}
A single class name carries the same weight. In such a scenario, the rule that is listed first will be overwritten by the second, and hence, the element will have margin-right: 0px;
Here is a simple example using color instead of margin, because it's easier to visualize. The value specified in bar will be chosen by the browser.
In addition, more "specific" class will override a more generic one:
HTML:
<div class="foo">
<div class="bar">Hello World!</div>
</div>
With the following CSS:
.foo .bar { margin-left:25px }
.bar { margin-left:0px }
Notice how the inner div still has 25px margin to the left?
Also, read up on "!important" argument after providing the value:
.bar { margin-left:0px!important }
Check out