I am currently using variables keeping track of colors for both light and dark themes (e.g. --light-background-color, --dark-background-color). This isn't too hard with two themes but seems a bit manual and if faced with more themes it becomes impractical.
I have seen things like night shift that apply CSS filters which invert the colors on a webpage. How do these filters work? and how would I go about implementing them?
One way to go about this is to have a set of general theme color variables, rather than specific color variables for specific themes like you're trying to do here.
You can define these variables in the body element and override them with the class or a custom attribute of the body.
Use these variables as you would normally for your other HTML elements, and just change the attribute of the body element to apply a different theme.
The important part here is to make sure your theme color variables have corresponding contrasting color variables as well, so that things like white text on a dark background can swap to dark text on a white background.
Here's an example, where primary and secondary theme and contrast colors are defined in the body element, and are overridden when the body has the "dark" class applied to it:
document.querySelector("button").addEventListener("click", () => document.body.classList.toggle("dark"));
body {
--color-primary: #b4e9ce;
--color-primary-contrast: #000000;
--color-secondary: #308d43;
--color-secondary-contrast: #ffffff;
/* Other theme colors... */
}
body.dark {
--color-primary: #202d26;
--color-primary-contrast: #ffffff;
--color-secondary: #8f8f8f;
--color-secondary-contrast: #000000;
/* Other theme colors... */
}
button {
padding: 10px;
margin-bottom: 20px;
}
.wrapper {
padding: 20px;
background-color: var(--color-primary);
border: solid var(--color-secondary) 10px;
}
.wrapper h1 {
font-family: sans-serif;
text-align: center;
color: var(--color-primary-contrast);
}
<body>
<button>Toggle Theme</button>
<div class="wrapper">
<h1>Hello World</h1>
</div>
</body>
Single Line CSS will change Light Theme to Dark:
<style>
body
{
filter: invert(1);
}
</style>
I want to create a local inverted theme (modern browsers). Color shades are set using CSS Vars (CSS custom properties). Some elements have more contrast, others are low contrast. Now the inverted container has a black background. Everything within there, should be reversed. Dark grey should be light grey. Light grey should be dark grey.
My goal is to achieve this without reassigning the vars in CSS selectors. For this example it would be easy, but the actual code base is big and there are many selectors. So instead of that I just want change the CSS Vars. Also, I want keep the original CSS Vars to be editable.
Final goal mockup
Simple reassignment of the Vars (light = dark, dark = light) does not work, obviously. I tried to transpose the values to a new placeholder var, but that also didn't worked. Maybe I was doing it wrong? Is there a clean way? I don't think so.
I am aware of workarounds using SASS, or hacks using mix-blend-mode.
Playground:
https://codepen.io/esher/pen/WzRJBy
Example code:
<p class="high-contrast">high contrast</p>
<p class="low-contrast">low contrast</p>
<div class="inverted">
<p class="high-contrast">high contrast</p>
<p class="low-contrast">low contrast</p>
</div>
<style>
:root {
--high-contrast: #222;
--low-contrast: #aaa;
}
.high-contrast { color: var(--high-contrast) }
.low-contrast { color: var(--low-contrast) }
.inverted {
background-color: black;
/* Switching vars does not work
--high-contrast: var(--low-contrast);
--low-contrast: var(--high-contrast);
*/
/* Transposing Vars also doesn't work:
--transposed-low-contrast: var(--low-contrast);
--transposed-high-contrast: var(--high-contrast);
--high-contrast: var(--transposed-low-contrast);
--low-contrast: var(--transposed-high-contrast);
*/
}
/*
I am aware of this solution (see description above):
.inverted p.high-contrast { color: var(--low-contrast); }
.inverted p.low-contrast { color: var(--high-contrast); }
*/
<style>
What about something like this:
:root {
--high-contrast: var(--high);
--low-contrast: var(--low);
--high: #222;
--low: #aaa;
/* Yes I can put them at the end and it will work, why?
Because it's not C, C++ or a programming language, it's CSS
And the order doesn't matter BUT we need to avoid
cyclic dependence between variables.
*/
}
.high-contrast {
color: var(--high-contrast)
}
.low-contrast {
color: var(--low-contrast)
}
.inverted {
--high-contrast: var(--low);
--low-contrast: var(--high);
}
<p class="high-contrast">high contrast</p>
<p class="low-contrast">low contrast</p>
<div class="inverted">
<p class="high-contrast">high contrast</p>
<p class="low-contrast">low contrast</p>
</div>
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.
I just had the idea of organizing my work as follows:
Create very basic CSS classes, for example this :
.backgroundRed {
background-color:red;
}
.backgroundGreen {
background-color:green;
}
.fixed300 {
width:300px;
}
.percent100 {
width: 100%;
}
.centered {
margin: auto;
}
.centeredTextHorizontally {
text-align:center;
}
.colorWhite {
color:white;
}
and then use 2-3-4 of them simultaneously, to create what I want, for example:
<div class = "backgroundGreen fixed300 centered">
<div class="centeredTextHorizontally">300px wide green stripe with centered text</div>
<div class="centeredTextHorizontally colorWhite">Centered white text</div>
</div>
</div>
Im sure you get the idea.
Now this has the problem that if in the future we want to change the web site, we need to edit the HTML of all those DIVs, which breaks the very pupropse of using CSS in the first place.
So I would like to be able to define CSS classes as follows
.navbar {
.colorRed;
.backgroundColorGreen;
}
etc etc. So that if the website colors need to be changed, for example, I only change the .navbar and not the DIVs in the HTML.
Is it possible to perform something like the above and how ?
Its not possible with pure css. you will need to look into a CSS pre-processor. Two popular ones are called Sass and Less. These links should give you more information on them:
Sass
Less
This will help you get started with your specific problem:
including another class in Sass
.navbar {
.colorRed;
.backgroundColorGreen;
}
This is not css rule. You have to use class like below -
.navbar.colorRed.backgroundColorGreen {
/* Your css styles */
}
Notice, there is no space between class name and dot . next to it.
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');