I'm using a (now older) version of react-boilerplate which came with CSS Modules. What's nice about them is that you can create variables and import them in other CSS files.
Here's my colors.css file
:root {
/* Status colors */
--error: #842A2B;
--success: #657C59;
--pending: #666;
--warning: #7E6939;
}
When I'm importing that file I just have to use at the top of my .css file:
#import 'components/App/colors.css';
I'm looking to have the option for two themes for my website and I would like to be able to dynamically update those variables with Javascript. What's the best way to do this?
Edit: I was hoping there's a way to update the colors.css file and not have to do conditional imports in all the components that draw from two possible css files... let me know if there's a way to do that and if there is I'll change the accepted answer. Thank you to all who answered!
I would just use the default color vars on the element/body/whatever, then put the alternate theme colors in another class, and toggle the theme class via JS. Here's a demo.
$("button").on("click", function() {
$("body").toggleClass("foo");
});
body {
--red: red;
--blue: blue;
--yellow: yellow;
background: #ccc;
text-align: center;
font-size: 5em;
}
.foo {
--red: #ce1126;
--blue: #68bfe5;
--yellow: #ffd100;
}
.red {
color: var(--red);
}
.blue {
color: var(--blue);
}
.yellow {
color: var(--yellow);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span class="red">RED</span> <span class="blue">BLUE</span> <span class="yellow">YELLOW</span>
<br>
<button>click me</button>
I would have 2 sheets and conditionally switch between the two:
colours.scss
:root {
/* Status colors */
--error: #842A2B;
--success: #657C59;
--pending: #666;
--warning: #7E6939;
}
otherColours.scss
:root {
/* Status colors */
--error: #FF0000;
--success: #00FF00;
--pending: #6666FF;
--warning: #FF00FF;
}
then in your react code import them and use them as you wish:
import styles from 'colours.scss';
import alternativeStyles from 'otherColours.scss';
...
{this.props.useNormalStyle ? styles.myClass : alternativeStyles.myClass}
Is this what you are looking for?
// get the inputs
const inputs = [].slice.call(document.querySelectorAll('.controls input'));
// listen for changes
inputs.forEach(input => input.addEventListener('change', handleUpdate));
inputs.forEach(input => input.addEventListener('mousemove', handleUpdate));
function handleUpdate(e) {
// append 'px' to the end of spacing and blur variables
const suffix = (this.id === 'base' ? '' : 'px');
document.documentElement.style.setProperty(`--${this.id}`, this.value + suffix);
}
:root {
--base: #ffc600;
--spacing: 10px;
--blur: 10px;
}
body {
text-align: center;
}
img {
padding: var(--spacing);
background: var(--base);
-webkit-filter: blur(var(--blur));
/* 👴 */
filter: blur(var(--blur));
}
.hl {
color: var(--base);
}
/*
misc styles, nothing to do with CSS variables
*/
body {
background: #193549;
color: white;
font-family: 'helvetica neue', sans-serif;
font-weight: 100;
font-size: 50px;
}
.controls {
margin-bottom: 50px;
}
a {
color: var(--base);
text-decoration: none;
}
input {
width:100px;
}
<h2>Update CSS Variables with <span class='hl'>JS</span></h2>
<div class="controls">
<label>Spacing:</label>
<input type="range" id="spacing" min="10" max="200" value="10">
<label>Blur:</label>
<input type="range" id="blur" min="0" max="25" value="10">
<label>Base Color</label>
<input type="color" id="base" value="#ffc600">
</div>
<img src="http://unsplash.it/800/500?image=899">
<p class="love">😘</p>
<p class="love">Chrome 49+, Firefox 31+</p>
Related
I am trying to create a basic CSS template for a project. It needs to support both a light and dark mode.
In the html, the body tag has data-layout-color attribute. I have some toggles that allow switching between light and dark, and it is updating this attribute. In my CSS sheet, I use the attribute selector for background color, and it works! Now I need to be able to set other elements color based on the light/dark mode, but that's not working as the individual element doesn't have the attribute. I don't want to add data-layout-color to everything, and then have to update it all with my js. Any suggestions?
HTML:
<body ng-controller="myApp" data-layout-color="dark" data-layout="topnav">
<button type="button" class="btn btn-primary">PRESS ME!</button>
</body>
CSS:
body[data-layout-color="dark"]{
background-color: var(--my-body-dark-bg);
}
body[data-layout-color="light"]{
background-color: var(--my-body-light-bg);
}
.btn-primary[data-layout-color="light" {
color: var(--my-white-light);
background-color: var(--my-primary-light);
border-color: var(--my-primary-light);
}
.btn-primary[data-layout-color="dark" {
color: var(--my-white-dark);
background-color: var(--my-primary-dark);
border-color: var(--my-primary-dark);
}
You could write your selectors such that the attribute selector remains on body:
/* primary button under a "light" layout parent */
[data-layout-color="light"] .btn-primary {
color: var(--my-white-light);
background-color: var(--my-primary-light);
border-color: var(--my-primary-light);
}
But I think a better idea would be to change the custom property values so you don't need the theme-specific selectors on child elements in the first place:
[data-layout-color="dark"] {
--button-color-bg: white;
--button-color-fg: black;
}
[data-layout-color="light"] {
--button-color-bg: black;
--button-color-fg: white;
}
.btn-primary {
background-color: var(--button-color-bg);
color: var(--button-color-fg);
display: inline-block;
padding: 0.25em 1em;
border: 1px solid grey;
margin: 0.5em;
}
<div data-layout-color="dark">
<div class="btn-primary">Dark Body</div>
</div>
<div data-layout-color="light">
<div class="btn-primary">Light Body</div>
</div>
With plain css you can write it like this
body[data-layout-color="dark"]{
background-color: var(--my-body-dark-bg);
}
body[data-layout-color="dark"] .btn-primary{
color: var(--my-white-dark);
background-color: var(--my-primary-dark);
border-color: var(--my-primary-dark);
}
body[data-layout-color="dark"] .btn-primary a{
text-decoration: underline overline #FF3028;
}
I suggest you use scss though. It will make your life easier. If you'r using visualstudio code just download Live sass complier and click watch sass in the bottom right corner.
Using scss you would write it like this:
body[data-layout-color="dark"]{
background-color: var(--my-body-dark-bg);
.btn-primary{
color: var(--my-white-dark);
background-color: var(--my-primary-dark);
border-color: var(--my-primary-dark);
a{
text-decoration: underline overline #FF3028;
}
}
.btn-secondary{
color: var(--my-white-dark-secondary);
background-color: var(--my-primary-dark-secondary);
border-color: var(--my-primary-dark-secondary);
}
p{
color: var(--my-white-dark);
}
}
body[data-layout-color="light"]{
background-color: var(--my-body-light-bg);
/*etc etc*/
}
I want to give a different color to the element if either of the two conditions exists.
Here is the code:
.btn-link{
color: $color-black;
margin: 0;
&:hover{
color: $color-light-blue;
}
&:not(.collapsed){
color: $color-light-blue;
}
}
Everything is good, but it will be better if you can combine the two selectors
I already tried:
&:hover&:not(.collapsed){
color: $color-light-blue;
}
But only the hover is identified
Same way as you do in CSS:
&:hover, &:not(.collapsed) {
color: $color-light-blue;
}
This sets the color only if the element is in a hover state or doesn't have the class collapsed.
You can put a comma between the two combining selectors, like this: &:hover, &:not(.collapsed) {
Full example:
HTML:
<span class="btn-link">one class</span>
<span class="btn-link collapsed">two class</span>
CSS:
$color-black: black;
$color-light-blue: lightblue;
.btn-link {
color: $color-black;
margin: 0;
&:hover, &:not(.collapsed) {
color: $color-light-blue;
}
}
JSfiddle.
Sorry, no StackSnippet. We still can't handle SCSS here!
You can try this simple change your code
/*SCSS*/
$color-light-blue : red;
.btn-link {
&:hover:not(.collapsed) {
color: $color-light-blue;
}
}
/*compiled CSS*/
.btn-link:hover:not(.collapsed) {
color: red;
}
<span class="btn-link">one class</span>
<span class="btn-link collapsed">two class</span>
I do not understand why pseudo classes like :focus-within need to be within the :host() function brackets when acting on the host itself. Why can it not be :host:focus-within div?
It's even more weird that it works on :host inside of another :host().
class MyElementFail extends HTMLElement {
constructor(...args) {
super(...args)
this.attachShadow({mode: 'open'}).innerHTML = `
<style>
:host{
display: block;
padding: 20px;
background-color: salmon;
}
:host div{
background-color: white;
}
/*This part is different:*/
:host:focus-within div{
background-color: green;
}
</style>
<input type="text" value="click in here"/>
<div>
Change to green
</div>`
}
}
window.customElements.define('my-element-fail', MyElementFail);
class MyElement extends HTMLElement {
constructor(...args) {
super(...args)
this.attachShadow({mode: 'open'}).innerHTML = `
<style>
:host{
display: block;
padding: 20px;
background-color: salmon;
}
:host div{
background-color: white;
}
/*This part is different:*/
:host(my-element:focus-within) div{
background-color: green;
}
</style>
<input type="text" value="click in here"/>
<div>
Change to green
</div>`
}
}
window.customElements.define('my-element', MyElement);
class MyElementTwo extends HTMLElement {
constructor(...args) {
super(...args)
this.attachShadow({mode: 'open'}).innerHTML = `
<style>
:host{
display: block;
padding: 20px;
background-color: salmon;
}
:host div{
background-color: white;
}
/*This part is different:*/
:host(:host:focus-within) div{
background-color: green;
}
</style>
<input type="text" value="click in here"/>
<div>
Change to green
</div>`
}
}
window.customElements.define('my-element-two', MyElementTwo);
No Good:
<my-element-fail></my-element-fail>
Good:
<my-element></my-element>
Good also:
<my-element-two></my-element-two>
Essentially, why does,
:host(:host:focus-within) div{ work, and
:host(my-element:focus-within) div{ work, but
:host:focus-within div{ not work?
:host is only to indicate the host element of the shadowDOM.
:host(.something) indicated the host with a class of .something.
You can not use :host.something you must use the parenthesis.
:host() is not a function. It is just how to select a :host with additional specificity.
class MyElement extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: 'open'}).innerHTML = `
<style>
:host{
display: block;
padding: 20px;
background-color: salmon;
}
div{
background-color: white;
}
:host(:focus-within) div{
background-color: green;
}
</style>
<input type="text" value="click in here"/>
<div>Change to green</div>`;
}
}
window.customElements.define('my-element', MyElement);
<my-element></my-element>
Actually the reason is given in Selector Level 4 specification:
The shadow host in a shadow tree is featureless and therefore cannot be matched by any pseudo-class except for :host [...].
It is illustrated in the hyperlink in the example (and actually also the link you pointed in your comment to #Intervalia's answer).
Transposed to your use case:
:focus-within doesn't match the shadow host. So, :host:focus-within which is more specific, should/could not match anything (that would be contradictory to the CSS selection fundamental).
Hence the :host() function pseudo-class that will mimic the other selectors but won't break their logic.
I have a very simple problem with CSS variables. I would like to swap two CSS variables, basically the CSS equivalent of [a, b] = [b, a] in ES6. Here's a simple example:
<p>White background</p>
<button>Black background</button>
<div>
<p>Black background</p>
<button>White background</button>
</div>
:root {
--primary-color: #fff;
--secondary-color: #000;
}
body {
background-color: var(--primary-color);
}
button {
background-color: var(--secondary-color);
}
div {
/* i'd like to do the following: */
--primary-color: var(--secondary-color);
--secondary-color: var(--primary-color);
/* so here, `--primary-color` would be `--secondary-color` from `:root`
* and any children have these colors swapped as well
*/
background-color: var(--primary-color);
}
However, this fails because CSS var()s are live bindings. Am I missing something here? Or is this the way the spec currently works?
You are creating a cyclic dependence because you are defining each property using the other one and this won't work. Instead you may try something like this by introducing more variables:
:root {
--p:#fff;
--s:#000;
--primary-color: var(--p);
--secondary-color: var(--s);
}
body {
background-color: var(--primary-color);
}
button {
background-color: var(--secondary-color);
}
div {
/* i'd like to do the following: */
--primary-color: var(--s);
--secondary-color: var(--p);
background-color: var(--primary-color);
}
<p>White background</p>
<button>Black background</button>
<div>
<p>Black background</p>
<button>White background</button>
</div>
I am new to SCSS .I am trying to style my place holder text from light to dark grey.
Attached is the html code:
<div class="form-group">
<textarea class="thread-textarea" ng-maxlength="255" maxlength="255" ng-model="newMessage.Content" ng-keyup="keycount($event)"placeholder="If this is an emergency, call 911. Otherwise, type your message here. We try to respond within 24 hours."title="Type your message" spellcheck="true" aria-labelledby="typeYourMessage"></textarea>
<span class="aria-hidden" id="typeYourMessage">Type your message</span>
</div>
Attached is my related scss code.
$darkgrey: #333333;
textarea{
#include placeholder
{
color: $darkgrey;
}
}
I want to change the color of placeholder from grey to dark grey. Any help would be highly appreciated.
/* cross browser way */
textarea::-webkit-input-placeholder { /* Chrome/Opera/Safari */
color: pink;
}
textarea::-moz-placeholder { /* Firefox 19+ */
color: pink;
}
textarea:-ms-input-placeholder { /* IE 10+ */
color: pink;
}
textarea:-moz-placeholder { /* Firefox 18- */
color: pink;
}
/* sass whould be like this */
/*
$darkgrey: #333333;
textarea{
&::-webkit-input-placeholder {
color: $darkgrey;
}
&::-moz-placeholder {
color: $darkgrey;
}
&:-ms-input-placeholder {
color: $darkgrey;
}
&:-moz-placeholder {
color: $darkgrey;
}
}
*/
<div class="form-group">
<textarea class="thread-textarea" ng-maxlength="255" maxlength="255" ng-model="newMessage.Content" ng-keyup="keycount($event)" placeholder="If this is an emergency, call 911. Otherwise, type your message here. We try to respond within 24 hours." title="Type your message"
spellcheck="true" aria-labelledby="typeYourMessage"></textarea>
<span class="aria-hidden" id="typeYourMessage">Type your message</span>
</div>
Just take this and apply your sass styles to it.