2

I designed a site so that changing two user inputted colors should change the color scheme of the entire site.

What is the best way to accomplish this. I know that I would have to save the items in the database and pull every time the user logged in in order to implement the color scheme with every login.

But at the moment I am more worried about a live change as soon as the user changes the html color value.

I know of an option to where I add a CSS class to every component that would change such as ... .primaryColor and .secondaryColor. And then alter all of the elements with that class. Is there a better way with React or another CSS/Javascript solution?

Also another complication is that it would have to be in a way that when the user loads other components that have not rendered yet, the change is still in affect.

Sequential
  • 1,455
  • 2
  • 17
  • 27
  • Is the user creating an arbitrary theme, or choosing between a couple predefined ones? The current solutions seem to be assuming the user is choosing between a couple predefined themes. – speckledcarp May 08 '17 at 18:39
  • @speckledcarp You are correct. So I started down the path of saving the primaryColor and secondaryColor in a Redux state. How does that solution sound? My issue now is how to implement it after stored in state. I have been using style tags on each element. Seems Repetitive. – Sequential May 08 '17 at 19:48
  • @speckledcarp and of course I just came across an issue where applying the style tag cancels any :hover or :focus elements... Which I can use Radium for, but I would rather not have to include Radium in the project just for this ... – Sequential May 08 '17 at 20:02

3 Answers3

1

I would use an event listener on the input, read the value, and if it matches whatever you want to trigger the color scheme change, apply the theme value to a data attribute on a root element and use CSS to control the color schemes.

var input = document.getElementById('input'),
    body = document.getElementsByTagName('body')[0];
input.addEventListener('keyup',function() {
  var val = this.value;
  if (this.value == 'foo') {
    body.setAttribute('data-theme','secondary');
  } else if (this.value == 'bar') {
    body.setAttribute('data-theme','primary');
  } else {
    body.setAttribute('data-theme','');
  }
  // ajax request to save theme pref in db
})
/* defaults */

body {
  color: #333;
}

/* primary theme */

[data-theme="primary"] {
  color: red;
}

[data-theme="primary"] p {
  background: yellow;
}

/* secondary theme */

[data-theme="secondary"] {
  color: blue;
}

[data-theme="secondary"] ul {
  background: grey;
}
<input id="input" placeholder="enter 'foo' or 'bar'">

<p>
  paragraph
</p>

<ul>
  <li>list</li>
</ul>
Michael Coker
  • 52,626
  • 5
  • 64
  • 64
  • 1
    Interesting. Really simple. Thank You! – Sequential May 08 '17 at 17:51
  • Michael Coker - doesn't this solution manipulate the DOM directly? If so, @Sequential is likely to have the same problem they would have if they implemented a jQuery solution. – speckledcarp May 08 '17 at 18:36
  • @speckledcarp where did they say they don't want to manipulate the DOM directly? I don't use react very much, but seems like this could easily be done with react, too. – Michael Coker May 08 '17 at 18:42
  • React wants to manage the DOM itself. When you use React everything is rendered to a Virtual DOM, and React manages updating the DOM itself. You have to be really careful if you're doing direct DOM updates in a React app, or React will overwrite your changes on re-renders. It's possible to work around, but difficult sometimes. – speckledcarp May 08 '17 at 18:44
  • Apologies now, I understand the answer more. @speckledcarp is correct. – Sequential May 08 '17 at 19:47
  • I've only used react a few times, but wouldn't this be easy to build the input as a component with the `onKeyUp` event on the element, then update the state of the app/parent and store the state in a data attribute? – Michael Coker May 08 '17 at 19:53
  • @MichaelCoker Yes, which is exactly what I have done, but now I have an issue with the correct way to apply it after stored in state. If I use style attributes then it cancels all :hover and :focus elements. Bummer. Also I would like to find a more efficient way than applying a style tag to each component that requires it. – Sequential May 08 '17 at 20:04
  • I can't help you with the way to load it after in the stored state, that seems to go beyond the intent of your OP or I wouldn't have answered. I don't know what to say about :hover and :focus since you didn't include any other code or mention anything like that in your OP either. Maybe it's an easy fix, maybe not. And I don't know what you mean by "apply a style tag to each component that requires it". – Michael Coker May 08 '17 at 20:12
1

One possible solution is to use the <style> element coupled with dangerouslySetInnerHTML, like this. (Notice the backticks ` around the CSS - it's an interpolated string literal.)

const Theme = props => {
  <style dangerouslySetInnerHTML={{__html: `
    .styled { color: ${props.userColor} }
    `}} 
  />
}

Then a component that used the theme would simply be <div className="styled" />

I got the idea for this solution here.

If you use this method, be very careful you're using sanitized variables to create your CSS theme. Otherwise, there's potential problems with injection attacks.

speckledcarp
  • 6,196
  • 4
  • 22
  • 22
0

you can easily do this using js. just add your class .primaryColor, .secondaryColor with jQuery addClass() Method. select the element

example : $(selector).addClass(classname,function(index,currentclass))

more example :https://www.w3schools.com/jquery/tryit.asp?filename=tryjquery_html_addclass

  • Thank you. I know that route in Jquery. But I try and stay away from Jquery as I have React in my stack. – Sequential May 08 '17 at 17:55
  • i am not good with react my maybe this can help you .http://stackoverflow.com/questions/36209432/reactjs-add-dynamic-class-to-manual-class-names – Akankha Ahmed May 08 '17 at 18:14