Update CSS Module variables from Javascript
Asked Answered
L

3

17

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!

Leaseholder answered 21/4, 2017 at 0:58 Comment(5)
@JaredChu I'm just looking to update the colors in this one location that multiple other CSS files read from. So I'm asking how to update the variables, not override them. Thanks though!Leaseholder
Are you using PostCSS to deal with CSS variables or just using them natively? If PostCSS, what's your current PostCSS plugin stack?Misogyny
@toomuchdesign CSS variables are supported in CSS3 See the browser support for CSS variables here but there are a little gap of 12,3% of the browsers there are in use not support it yetBobodioulasso
Did my solution not work for you? Just noticed you unselected it as the accepted solution.Horme
Hey Michael, thanks for following up! I'm using React and a don't want to do 'if / else' statements for css for all of my components for picking the CSS. So though your answer does work, it's not ideal and I'm hoping for a cleaner solution where I just update the values in the CSS and all the components reading from it will automatically update. Did unselecting your answer make you lose points? If so, I'll accept it again. Just hoping to get an answer that's super clean for my project.Leaseholder
H
12

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>
Horme answered 28/4, 2017 at 0:39 Comment(0)
M
5

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}
Multiplicate answered 3/5, 2017 at 18:3 Comment(0)
S
3

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>
Servo answered 27/4, 2017 at 9:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.