I am trying to closely stick to the BEM methodology and I am frequently running into this issue which I feel it is time to ask for a solution (or opinion) to.
Consider I have a human body; this human body is light by default and therefore, it's arms are also light. My pseudo-code in CSS may look like the below:
.human {
background-color: white;
}
.human__arm {
background-color: white;
}
Now we want to add a class for humans who are dark. We can create a modifier for this:
.human--dark {
background-color: black;
}
The problem I am facing is that by now, the human's arm is still white. We can make them black in two ways that I know of:
Solution A
.human--dark {
background-color: black;
}
.human--dark .human__arm {
background-color: black;
}
This solution breaks the BEM methodology by adding specificity to the CSS. However, I feel that this solution is more portable where you are sure to only modify the human and assume that all of its elements will also adapt to the changes of the parent.
Solution B
.human--dark {
background-color: black;
}
.human__arm--dark {
background-color: black;
}
I like to think that a 'Block' is a reusable component in BEM. If the human has various other body parts that also need turning white, it seems less maintainable to modify all of the blocks elements in order to achieve this.
While background-color: inherit; may look like a solution to this case, in a real-world application we may have a dark background that contains elements that require light text.
How would we modify the light text to become dark when its parent block becomes light?
The first solution is the better one assuming that a dark human may not necessarily have a white arm.
The idea behind BEM syntax is to allow for composability. Writing your selector that way defeats this purpose.
A better way to define these selectors is:
.human {} /* block */
.human--dark {} /* modified block */
.human--arm {} /* block element */
These can be altogether composed this way in markup:
<div class="human human--dark">
<div class="human__arm">
Human
</div>
</div>
For your second question, you may want to take advantage of a CSS preprocessor to compute the inverse of the background color for the text color.
In Sass, it's done this way using a custom mixin:
#mixin duotone ($color) {
background-color: $color;
color: invert($color);
}
.human {
#include duotone(white);
}
.human--dark {
#include duotone(black);
}
.human__arm {
}
In order to modify child elements via parent modifier
we should nest the element selector like below:
Block with an element:
.human {
background-color: white;
}
.human__arm {
background-color: white;
}
Block Modifier modifying the child element:
.human--dark {
background-color: black;
}
.human--dark .human__arm {
background-color: black;
}
If we want to achieve the same in SCSS, we can use the following approach:
.human {
background-color: white;
&__arm {
background-color: white;
}
&--dark { // block modifier
background-color: black;
.human { // block
&__arm {
background-color: black;
}
}
}
}
If you're using scss you can use interpolation to avoid duplication
.human {
$c: &;
background-color: white;
&__arm {
background-color: white;
}
&--dark {
background-color: black;
#{$c}__arm {
background-color: black;
}
}
}
Also, this may not be relevant to the question, but using background-color: inherit on the arms would mean you wouldn't have to change any css for the arms as it would inherit from the parent.
Related
I'm working on a CSS file and I'd like it to interact with anothet CSS file.
How? Let's say I have A.css and B.css. In A.css I want to do the "overflow: hidden" referred to B.css and all the elements that it controls.
Is anything like that impossible?
Like:
#import "field.css"
.sky .field {
overflow:hidden;
}
So basically this what I actually have:
.sky {
width: 90%;
height: 100%;
background: blue;
opacity: 0.7;
position: absolute;
z-index: 1;
}
.field {
width: 100%;
height: 20px;
background: green;
position: fixed;
top: 90%;
z-index: 2;
}
.field > p {
width: 100%;
height: 40px;
background: black;
}
Now I want that "p", which is a sub-tag of .field to not show outside of the bounds of .sky.
How do I do that?
No need to import one CSS file into the other simply link to both CSS files in your HTML. For example if you had the following two files
File A:
.sky .field {
overflow:hidden;
}
File B:
.sky {
color: black;
}
Sky would inherit both properties of overflow hidden and color black. If the rules contradict each other for example file A says sky color is blue and file B says black then the CSS rule sheet which is linked last will take presidence.
Edit: Generally it isn't good practise to do this for organization purpose. If Sky is a single objection consider putting all CSS references to it in a single file.
Load both the CSS files into your page. You can actually have multiple files which define style rules on same element. So lets say you have two file
File 1
.sky{
background-color: Red;
}
And File 2
.sky.field {
overflow:hidden;
}
And lets say the page has a element with class div and field.
<div class='sky field'></div>
Now this will have both the combined CSS rules.
Also make sure you get yourself familiar with CSS Priorities, If 2 files have the different CSS rule on the same element then what happens??
Example
//File 1
.sky{
background-color: Red;
}
//File 2
.sky.field {
background-color: Blue;
}
Now the file that is placed last in the HTML DOM will have more priority over other rules. Note that its NOT the last file loaded but the last file in the DOM hirarchythat gets the priority.
I'm trying to create a HTML widget:
HTML:
<div>
<h1 class="title" data-bind="title">Title</h1>
<div>
<h1 id = "dc1" class="dc">DC1</h1>
</div>
<div>
<h1 id = "dc2" class="dc">DC2</h1>
</div>
<p class="updated-at" data-bind="updatedAtMessage"></p>
</div>
And I need to be able to set the background color of the id="dc1" and id="dc2" elements dynamically in CoffeeScript. I plan to do this by adding a class with a background color setting:
SCSS:
&.up {
background-color: green;
}
&.down {
background-color: red;
}
.dc {
background-color: orange;
font-size: 30px;
float: left;
width: 50%;
}
So far I have managed to set the whole widget background but not the child elements mentioned above:
I have been using:
CoffeeScript:
$(#node).removeClass('up down')
$('#dc1').removeClass('up down')
$('#dc2').removeClass('up down')
$(#node).addClass('down')
$('#dc1').addClass('down')
$('#dc2').addClass('up')
Note ultimately I will add the classes depending on some data rather than hard coding them to 'up' or 'down' in the coffeescript.
But nothing happends.. Am I getting selecting the id="dc#" elements correctly?
If it helps with context I'm doing this for Dashing
Your SCSS doesn't make sense so I'd guess that your missing an error from the SCSS-to-CSS conversion. An & in SCSS is a reference to the parent selector:
& will be replaced with the parent selector as it appears in the CSS
so have &.up at the top level makes no sense and should generate an error. If we fix the SCSS so that .up and .down apply only to .dc:
.dc {
/* ... */
&.up {
background-color: green;
}
&.down {
background-color: red;
}
}
then everything seems to work just fine.
Demo: http://jsfiddle.net/ambiguous/9y9uywm9/
You can use Sassmeister (and other similar online tools) to see what SCSS thinks of your original SCSS.
Suppose I have two virtually identical HTML structures, but with different class names. They only differ by a few variables, like width and height. By using SASS/SCSS variables I thought I could do something like this:
.widget-a {
$width: 50px;
}
.widget-b {
$width: 100px;
}
.widget-a,
.widget-b {
button {
background: red;
width: $width;
}
}
This would let me write a single piece of SASS nested code for both widgets a and b. However, variables are only visible inside a nested scope, so SASS returns 'variable undefined' errors. Of course I could rewrite it by simply doing something like:
.widget-a,
.widget-b {
button {
background: red;
}
}
.widget-a {
button {
width: 50px;
}
}
.widget-b {
button {
width: 100px;
}
}
But that seems pretty cumbersome. Is there any other method of making this work?
Variables in SASS are only scoped to the block they appear in. Your first .widget-a declaration and the one declaring both A and B are two separate scopes. You'll have to do something like this (assuming you need to use the widths more than once):
$wbackground: red;
.widget-a {
$wawidth: 50px; /* widget A width */
button {
background: $wbackground;
width: $wawidth;
}
}
.widget-b {
$wbwidth: 100px; /* widget B width */
button {
background: $wbackground;
width: $wbwidth;
}
}
Ran into the same problem, this is how I'm going to try to solve it... (this works)
#mixin foo($type:"default")
.mClass
$bg: inherit
#if $type == "banana"
$bg: yellow
background: $bg
.firstClass
#include foo
.secondClass
#include foo("banana")
Your problem can be solved by using a mixin.
#mixin button($width){
button{
background:red;
width:$width;
}
}
.widget-a{ #include button(50px); }
.widget-b{ #include button(100px); }
I don't see the advantage of creating a mixin only for this specific situation, it is hardly useful on a couple of occasions, but it is just my opinion.
Anyway, I've created a mixin, just for fun. I think that it can help you to deal with this specific situation. Here is the mixin and I'm going to try to explain how it works:
#include button($selectors, $property, $values, $child: false) {
// Common properties that are included in all $selectors
}
This mixin takes four parameters:
$selectors: List of selectors, in your case, .widget-a and .widget-b, they should be enclosed in quotes.
$property: Here you should enter the name of the property, in your case width
$values: Values are, as the name implies , the values of the property for each selector
$child: Here you can enter the name of a child, this is optional.
Into the brackets {} you should write all the properties that you want to include in all $parameters
The order of each selector must match the order of their corresponding value
So, here's an example using this mixin to solve your problem. This is the #include:
#include (".widget-a" ".widget-b", width, 50px 100px, button) {
background: red;
}
And this, the code that returns:
.widget-a button, .widget-b button {
background: red; }
.widget-a button {
width: 50px; }
.widget-b button {
width: 100px; }
This is another way to achieve the same result:
#include button(".widget-a .button" ".widget-b .button", width, 50px 100px) {
background: red;
}
Download the mixin here
Suppose I have the following css rule:
.blah { Rules }
and I use it as such
<div class="blah">
Now suppose, somewhere in my doc I need to add margin-bottom: 10px to one of these divs with class name blah so I can make it as specific as possible,
Should I declare it as
.blah.mar-bot-blah { margin-bottom: 10px; }
or
.blah .mar-bot-blah { margin-bottom: 10px; }
to use it as <div class="blah mar-bot-blah">
Declare it as:
.blah.mar-bot-blah { margin-bottom: 10px; }
It will match elements that have both the classes .mar-bot-blah and .blah.
If you want to make it really specific, I would use your first solution.
(Warning: IE6 Can't handle this!)
In most cases it should suffice to just use
.mar-bot-blah { margin-bottom: 10px; }
so you can re-use this setting in other classes as well.
Are there any useful techniques for reducing the repetition of constants in a CSS file?
(For example, a bunch of different selectors which should all apply the same colour, or the same font size)?
Recently, variables have been added to the official CSS specs.
Variables allow you to so something like this :
body, html {
margin: 0;
height: 100%;
}
.theme-default {
--page-background-color: #cec;
--page-color: #333;
--button-border-width: 1px;
--button-border-color: #333;
--button-background-color: #f55;
--button-color: #fff;
--gutter-width: 1em;
float: left;
height: 100%;
width: 100%;
background-color: var(--page-background-color);
color: var(--page-color);
}
button {
background-color: var(--button-background-color);
color: var(--button-color);
border-color: var(--button-border-color);
border-width: var(--button-border-width);
}
.pad-box {
padding: var(--gutter-width);
}
<div class="theme-default">
<div class="pad-box">
<p>
This is a test
</p>
<button>
Themed button
</button>
</div>
</div>
Unfortunately, browser support is still very poor. According to CanIUse, the only browsers that support this feature today (march 9th, 2016), are Firefox 43+, Chrome 49+, Safari 9.1+ and iOS Safari 9.3+ :
Alternatives :
Until CSS variables are widely supported, you could consider using a CSS pre-processor language like Less or Sass.
CSS pre-processors wouldn't just allow you to use variables, but pretty much allow you to do anything you can do with a programming language.
For example, in Sass, you could create a function like this :
#function exponent($base, $exponent) {
$value: $base;
#if $exponent > 1 {
#for $i from 2 through $exponent {
$value: $value * $base;
}
}
#if $exponent < 1 {
#for $i from 0 through -$exponent {
$value: $value / $base;
}
}
#return $value;
}
Elements can belong to more than one class, so you can do something like this:
.DefaultBackColor
{
background-color: #123456;
}
.SomeOtherStyle
{
//other stuff here
}
.DefaultForeColor
{
color:#654321;
}
And then in the content portion somewhere:
<div class="DefaultBackColor SomeOtherStyle DefaultForeColor">Your content</div>
The weaknesses here are that it gets pretty wordy in the body and you're unlikely to be able to get it down to listing a color only once. But you might be able to do it only two or three times and you can group those colors together, perhaps in their own sheet. Now when you want to change the color scheme they're all together and the change is pretty simple.
But, yeah, my biggest complain with CSS is the inability to define your own constants.
You should comma seperate each id or class for example:
h1,h2 {
color: #fff;
}
You can use global variables to avoid duplicacy.
p{
background-color: #ccc;
}
h1{
background-color: #ccc;
}
Here, you can initialize a global variable in :root pseudo class selector. :root is top level of the DOM.
:root{
--main--color: #ccc;
}
p{
background-color: var(--main-color);
}
h1{
background-color: var(--main-color);
}
NOTE: This is an experimental technology
Because this technology's specification has not stabilized, check the compatibility table for the proper prefixes to use in various browsers. Also note that the syntax and behavior of an experimental technology is subject to change in future versions of browsers as the spec changes. More Info here
However, you can always use the Syntactically Awesome Style Sheets i.e.
In case Sass, you have to use $variable_name at the top to initialize the global variable.
$base : #ccc;
p{
background-color: $base;
}
h1{
background-color: $base;
}
You can use dynamic css frameworks like less.
Personally, I just use comma-separed selector, but there some solution for writing css programmatically. Maybe this is a little overkill for you simpler needs, but take a look at CleverCSS (Python)
Try Global variables to avoid duplicate coding
h1 {
color: red;
}
p {
font-weight: bold;
}
Or you can create different classes
.deflt-color {
color: green;
}
.dflt-nrml-font {
font-size: 12px;
}
.dflt-header-font {
font-size: 18px;
}
As far as I know, without programmatically generating the CSS file, there's no way to, say, define your favorite shade of blue (#E0EAF1) in one and only one spot.
You could pretty easily write a computer program to generate the file. Execute a simple find-and-replace operation and then save as a .css file.
Go from this source.css…
h1,h2 {
color: %%YOURFAVORITECOLOR%%;
}
div.something {
border-color: %%YOURFAVORITECOLOR%%;
}
to this target.css…
h1,h2 {
color: #E0EAF1;
}
div.something {
border-color: #E0EAF1;
}
with code like this… (VB.NET)
Dim CssText As String = System.IO.File.ReadAllText("C:\source.css")
CssText = CssText.Replace("%%YOURFAVORITECOLOR%%", "#E0EAF1")
System.IO.File.WriteAllText("C:\target.css", CssText)
You can use multiple inheritance in your html elements (e.g. <div class="one two">) but I'm not aware of a way of having constants in the CSS files themselves.
This link (the first found when googling your question) seems to have a fairly indepth look at the issue:
http://icant.co.uk/articles/cssconstants/
CSS Variables, if it ever becomes implemented in all major browsers, may one day resolve this issue.
Until then, you'll either have to copy and paste, or use a preprocessor of whatever sort, like others have suggested (typically using server-sider scripting).
:root {
--primary-color: red;
}
p {
color: var(--primary-color);
}
<p> some red text </p>
You can change color by JS
var styles = getComputedStyle(document.documentElement);
var value = String(styles.getPropertyValue('--primary-color')).trim();
document.documentElement.style.setProperty('--primary-color', 'blue');