Dynamically add styles to styled-jsx in Next.js - javascript

Please see the code. I want to add some extra style for the about link conditionally, so I am adding it inside ${} of the template string for the tag. This doesn't apply the new styles.
I have tried by concatenating the template strings. That didn't work either.
I cannot set style={style} in the tag because then I won't be able to use the :hover style.
import Link from 'next/link'
import { withRouter } from 'next/router'
const Header = ({ children, router, href }) => {
const isIndex = router.pathname === "/";
const isAbout = router.pathname === "/about";
return (
<div>
<Link href="/">
<a id="home">Timers</a>
</Link>
<Link href="/about">
<a id="about">About</a>
</Link>
<style jsx>{
`
a {
font-family: 'Montserrat Subrayada', sans-serif;
color: #ffffff;
text-decoration: none;
font-size: 24px;
padding: 2px;
margin-right: 30px;
}
a:hover {
color: #000000;
background-color: #ffffff;
border-radius: 2px;
}
${isAbout ? `
#about {
background-color: red;
color: #000000;
}
#about:hover {
background-color: blue;
color: #ffffff;
}
` : ``}
`
}</style>
</div>
)}
export default withRouter(Header)
How can I apply conditional styles?

The solution is to only change the property values dynamically, rather than add entire new selectors in a single conditional you must do it for each property, like this:
<style jsx>{
`
a {
font-family: 'Montserrat Subrayada', sans-serif;
color: #ffffff;
text-decoration: none;
font-size: 24px;
padding: 2px;
margin-right: 30px;
border-radius: 2px;
}
a:hover {
color: #000000;
background-color: #ffffff;
}
#home {
background-color: ${isHome ? "#ffffff" : ""};
color: ${isHome ? "#000000" : ""};
}
#about {
background-color: ${isAbout ? "#ffffff" : ""};
color: ${isAbout ? "#000000" : ""};
}
`
}</style>
(I figured out the answer to my question while I was asking it, so I thought i'd just post it with my answer anyway in case someone else needs it)

Since styled-jsx compiles styles at build time, it cannot preprocess dynamic styles, this is a limitation that is mentioned in the docshere
Plugins make it possible to use popular preprocessors like SASS, Less, Stylus, PostCSS or apply custom transformations to the styles at compile time.

Related

React js app sidebar selected item not highlighted

I created this react app with a side bar, and it looks pretty good, but one thing I can't figure out is how to mark the item (page link) that was clicked.
Here is the code, I added activeClassName="selected" to the SidebarLink const, and added it to the styled-component, but it's not working.
import React, { useState } from "react";
import { Link } from "react-router-dom";
import styled from "styled-components";
const SidebarLink = styled(Link)`
/*border: 3px solid green;*/
display: flex;
color: #e1e9fc;
justify-content: space-between;
align-items: center;
padding: 20px;
list-style: none;
height: 60px;
text-decoration: none;
font-size: 18px;
&:hover {
background: #252831;
border-left: 4px solid #ffbc2e;
cursor: pointer;
}
.selected {
color: red;
}
`;
const SidebarLabel = styled.span`
margin-left: 16px;
`;
/* Used for Training dropdown */
const DropdownLink = styled(Link)`
/*border: 3px solid red;*/
background: #252831;
height: 60px;
padding-left: 3rem;
display: flex;
align-items: center;
text-decoration: none;
color: #f5f5f5;
font-size: 16px;
font-weight: bold;
&:hover {
background: #48526F;
border-left: 4px solid #ffbc2e;
cursor: pointer;
}
`;
const SubMenu = ({ item }) => {
const [subnav, setSubnav] = useState(false);
const showSubnav = () => setSubnav(!subnav);
return (
<>
<SidebarLink to={item.path} onClick={item.subNav && showSubnav} activeClassName="selected" exact>
<div className="sidebarTextContainer">
<div className="sidebarIcon">{item.icon}</div>
<div className="sidebarText"><SidebarLabel>{item.title}</SidebarLabel></div>
</div>
<div>
{item.subNav && subnav
? item.iconOpened
: item.subNav
? item.iconClosed
: null}
</div>
</SidebarLink>
{subnav &&
item.subNav.map((item, index) => {
return (
<DropdownLink to={item.path} key={index}>
{item.icon}
<SidebarLabel>{item.title}</SidebarLabel>
</DropdownLink>
);
})}
</>
);
};
export default SubMenu;
Anyone got any idea please on how to fix this?
Even though you're asking about remembering which item was clicked, I think what you actually want is that the link corresponding to the current page be highlighted as active.
If you're using react-router v6, you should use NavLink instead of Link (docs).
A <NavLink> is a special kind of <Link> that knows whether or not it is "active".
By default, an active class is added to a <NavLink> component when it is active.
You should update the imported component and remove the activeClassName.
const SidebarLink = styled(NavLink)`
// ...
&.active {
color: red;
}
`
// ...
<SidebarLink to={item.path} onClick={item.subNav && showSubnav} exact>
...
</SidebarLink>
If you're using react-router v5, then you should also use NavLink but you have to pass the activeClassName, like you do in your example. In this case you simply need to substitute Link for NavLink.
Also note that you nested CSS syntax is incorrect. It is applied to the children of SidebarLink, not on SidebarLink itself. You'd want to replace .selected { color: red; } with &.selected { color: red }.

Change CSS style sheet directly

I have a unique situation here. I have several buttons in HTML:
Normally they would look like the one on the left, but when I hover my mouse on them, they will animate and look like the right one.
I also added two color pickers which change the background and foreground respectively. (Both pickers effect the whole document as well as the buttons)
// Background color changer
var allb = document.getElementsByClassName('button');
for (var i = 0; i < allb.length; i++) {
allb[i].style.backgroundColor = bgcolor;
}
// Foreground color changer
var allf = document.getElementsByClassName('button');
for (var i = 0; i < allf.length; i++) {
allf[i].style.color = focolor;
allf[i].style.borderColor = focolor;
}
If I use both color pickers, then the animation will break and nothing will change when I hover hover the buttons. I understand why - the color pickers have a persisting effect on the style.
Is there a way to directly change the CSS file, or otherwise bypass this problem?
My code
.button {
font-family: SF, Arial, Helvetica, sans-serif;
background-color: #4c56af;
border: none;
padding: 16px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 30px;
margin: 4px 2px;
transition-duration: 0.4s;
cursor: pointer;
}
.button1 {
border-radius: 10px;
font-family: SF, Arial, Helvetica, sans-serif;
background-color: rgba(255, 255, 255, 0);
color: black;
border: 4px solid #4c56af;
}
.button1:hover {
border-radius: 10px;
font-family: SF, Arial, Helvetica, sans-serif;
background-color: #4c56af;
color: white;
}
Each button would look roughly like this:
<button type="button" onclick=func() class="button button1">Play</button>
You can modify the list of rules contained in a stylesheet. You can do that by means of StyleSheetList and CSSStyleSheet interfaces.
Select the stylesheet in question:
const styleSheet = document.styleSheets[0] // Use the index for the stylesheet whose rules you want to modify
Insert the rules using the insertRule() method:
const bgcolor = "green";
const focolor = "yellow";
styleSheet.insertRule(`
button {
background-color: ${bgcolor};
color: ${focolor};
border-color: ${focolor};
}
`, styleSheet.cssRules.length);
styleSheet.insertRule(`
button:hover {
background-color: ${focolor};
color: ${bgcolor};
}
`, styleSheet.cssRules.length);
If you need, you can remove those rules afterwards through the deleteRule() method.
This is a workaround.
Write your CSS animation code as a separate class, like this:
.button--animated {
border-radius: 10px;
font-family: SF, Arial, Helvetica, sans-serif;
background-color: #4c56af;
color: white !important;
}
Then use themouseenter event on all button elements for adding the transition class to it, and use the 'mouseleave' for removing it:
buttonElement.addEventListener("mouseenter", function() {
element.classList.add('button--animated')
});
buttonElement.addEventListener("mouseleave", function() {
element.classList.remove('button--animated')
});
If it doesn't work, add !important to the button--animated class's CSS. it's not ideal but may get the work done.

How to dynamically change button size and color in React

I have created a custom Button component in my application using react. As of right now I have it where I can dynamically change the color to what ever I specify within it's color attribute, but now I want to have the option to change the size of the button as well (ie. 'small', 'large'). I'm still a beginner with my CSS so I'm not quite sure how I should do this.
My Button.css file
.ButtonUI {
border-radius: 4px;
border: none;
padding: 10px 24px;
margin-right: 20px;
margin-left: 20px;
}
.G-green {
color: white;
background-color: #0F9D58;
}
.small {
border-radius: 4px;
border: none;
padding: 6px 12px;
}
.ButtonUI.G-green:hover{
cursor: pointer;
background-color: #0d8c4f;
}
.G-blue {
color: white;
background-color: #4285F4;
}
.ButtonUI.G-blue:hover{
cursor: pointer;
background-color: #336ac4;
}
.G-red {
color: white;
background-color: #DB4437;
}
.ButtonUI.G-red:hover{
cursor: pointer;
background-color: #b0362c;
}
.G-yellow {
color: white;
background-color: #F4B400;
}
.ButtonUI.G-yellow:hover{
cursor: pointer;
background-color: #d19b02;
}
.ata-teal{
color: white;
background-color: #04837B;
}
.ButtonUI.ata-teal:hover{
cursor: pointer;
background-color: #005e58;
}
.ata-orange{
color: white;
background-color: #ffa600;
}
.ButtonUI.ata-orange:hover{
cursor: pointer;
background-color: #db8f00;
}
My Button.js file
import React from 'react'
import '../../StyleSheets/Button.css'
export default class Button extends React.Component{
constructor(props){
super(props);
this.state = {
}
}
render(){
return(
<div id="button">
<button className={"ButtonUI " + this.props.color + this.props.size} onClick={this.props.click} id={this.props.name}>{this.props.name}</button>
</div>
)
}
}
I've tried calling the component like this: <Button color="G-green" size="small" name="Click Me"></Button> but this breaks my css and my button shows up blank.
If anyone knows a good way of going about this, I would greatly appreciate it. Thanks!
<button className={`ButtonUI ${this.props.color} ${this.props.size}`} onClick={this.props.click} id={this.props.name}>{this.props.name}</button>
Or
<button className={['ButtonUI', this.props.color, this.props.size].join(' ')} onClick={this.props.click} id={this.props.name}>{this.props.name}</button>
Or if you want to install classnames package, which is really good for conditional classes:
import classNames from 'classnames'
<button className={classNames('ButtonUI', this.props.color, this.props.size)} onClick={this.props.click} id={this.props.name}>{this.props.name}</button>
Hey i just came up with this, Hope it helps:
constructor(){
super();
this.state = {
black: true
}
}
changeColor(){
this.setState({black: !this.state.black})
}
render(){
let btn_class = this.state.black ? "blackButton" : "whiteButton";
return (
<div>
<button className={btn_class}
onClick={this.changeColor.bind(this)}>
Button
</button>
</div>
)
}
Here is the css:
button{
width: 80px;
height: 40px;
margin: 15px;
}
.blackButton{
background-color: black;
color: white;
}
.whiteButton{
background-color: white;
color: black;
width: 120px;
height: 140px;
margin: 15px;
}

Styling a Router Link with styled-components

What is the best way to style the Link using the styled-components library in the code that follows.
I can find lots of examples to work with anchor tag but none to work with react-router link.
Am I going about the correct way.
import React from "react";
import { Link } from "react-router-dom";
import styled from "styled-components";
const Nav = ({ className }) => {
return (
<div className={className}>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
</ul>
</div>
);
};
export default styled(Nav)`
color: white;
text-align: left;
background: teal;
width: 100%;
ul {
color: red;
display: flex;
flex-direction: row;
border: 1px solid green;
list-style: none;
li {
padding: 15px 15px;
border: 2px solid purple;
}
}
`;
Thanks
Joseph Shanahan
Yes thanks for your help. A slimmed down version of what I will implement is as follows.
It also has the advantage in that I did not have to implement an unordered list.
import React from "react";
import { Link } from "react-router-dom";
import styled from "styled-components";
const Nav = ({ className }) => {
return (
<div className={className}>
<NavLink to="/">Home</NavLink>
<NavLink to="/about">About</NavLink>
</div>
);
};
const NavLink = styled(Link)`
padding: 20px;
color: white;
text-decoration: none;
&:hover {
color: red;
background: blue;
}
`;
export default styled(Nav)`
color: white;
width: 100%;
display: flex;
justify-content: flex-end;
`;
Thanks Joseph Shanahan
You are in the right way, but little wrong, instead of using ul you just pass the component reference.
export default styled(Nav)`
color: white;
text-align: left;
background: teal;
width: 100%;
${Link} {
/* style for Link Component */
}
`;
Check this answer, it's very familiar.
react-router link translates to after all in <a> tags so style them in the same way you would style an <a> tag
so let's say, you need to change their color to red then:
ul {
color: red;
}
won't work, you will need to do:
ul a {
color: red;
}
If you just want to style the Link component, then the usual way in my experience is to create a styled component like so:
const NavLink = styled(Link)`
/* Link styles */
`
And then just render it like <NavLink>Home</NavLink>.
This also makes it easy to reuse this styled component.

Programmatically change the primary color used in several places in the CSS

The primary color of my Single Page Application is programmatically client-side defined. The problem is that is it used in many places in my code.
Eg. on load, I have (in SCSS):
body {
background-color: #XXXXXX;
}
.btn-primary {
background-color: #XXXXXX;
}
.content ul li a {
color: #XXXXXX;
border: thin solid #XXXXXX;
&:hover {
background-color: #XXXXX;
color: white;
}
}
And I need to replace#XXXXXX to #YYYYYY everywhere, on the client side.
The different options I see are:
1. isolate the color-related CSS, and wrap it with a class
Something like:
body {
background-color: #XXXXXX;
.btn-primary {
background-color: #XXXXXX;
}
.content ul li a {
color: #XXXXXX;
border-color: #XXXXXX;
&:hover {
background-color: #XXXXX;
}
}
&.other-color {
background-color: #YYYYYY;
.btn-primary {
background-color: #YYYYYY;
}
.content ul li a {
color: #YYYYYY;
border-color: #YYYYYY;
&:hover {
background-color: #YYYYYY;
}
}
}
}
+ Pro It will eventually work
- Con Not DRY at all!
2. Using a dirty JS that would inspect every occurences of #XXXXXX and replace with #YYYYYY in the DOM.
+ Pro Seems clean to me
- Con Not sure it is doable
3. Using a dirty JS that would inspect every occurences of #XXXXXX and replace with #YYYYYY in the CSS file.
+ Pro Should do the job
- Con Not sure it is doable. And it doesn't feel like the right way to do it.
4. Compile several times the application.css to application-other-color.css and use it accordingly.
+ Pro Pretty easy to implement (compared to the other solutions)
- Con The user needs to load different assets, which is not optimum for a SPA
Has anyone faced this issue before? Is there any better solution than those two? If not, which one would you suggest? Did I miss some pros/cons?
CSS variables seem the perfect solution:
body {
--my-color: red; /* Set some value */
background-color: var(--my-color); /* Use the value in the variable */
}
.btn-primary {
background-color: var(--my-color); /* Use the inherited value */
}
Then, changing the colors is simple:
document.body.style.setProperty('--my-color', newColor);
document.querySelector('button').onclick = function() {
var rndcolor = '#' + ('00000' + Math.floor(Math.random() * 256 * 256 * 256).toString(16)).slice(-6);
document.body.style.setProperty('--my-color', rndcolor);
};
body {
--my-color: red;
background-color: var(--my-color);
}
.content {
background-color: #fff;
padding: 20px;
}
.btn-primary {
background-color: var(--my-color);
}
.content a {
color: var(--mycolor);
border: thin solid var(--my-color);
}
.content a:hover {
background-color: var(--my-color);
color: white;
}
<div class="content">
<button class="btn-primary">Click me to change color</button>
<a>I have a border</a>
</div>
The problem is that CSS variables are not widely supported yet.
Basically, #1, but I would structure it differently. It's best to separate these if they are indeed different states/classes, and name them semantically.
That way, if you decide, for example, that .body-content--error also needs to have green text, or whatever, you can easily edit it in. Now you just need to set the javascript to switch classes, which is simple.
This is actually very DRY code, in that .body-content and .body-content--error are different states of the same element. In fact, with the code below, you can set the class of body to class="body-content body-content--error" and inherit all of the .body-content CSS, and just change the background-color with .body-content--error.
$primary-color: #XXXXXX;
$error-color: #YYYYYY;
// Body content
.body-content {
background-color: $primary-color;
margin: 10px;
padding: 10px;
}
.body-content--error {
background-color: $error-color;
}
//Button (primary)
.btn-primary {
background-color: $primary-color;
}
.btn-primary--error {
background-color: $error-color;
}
// Links in the .content list
.content-list-link {
color: $primary-color;
border-color: $primary-color;
&:hover {
background-color: $primary-color;
}
}
.content-list-link--error {
color: $error-color;
border-color: $error-color;
&:hover {
background-color: $error-color;
}
}
The whole approach is described above thanks to #MattDiamant but for the records, here's the final solution I built, which I reckon is highly useful.
TL;DR
I made a dynamic mixin which I simply need to call #include variable-color($property-name); and to change the <body> class to change variable-color everywhere.
Explanations
I used scss and erb: the solution needs a preprocessor before SCSS, could be grunt, gulp, etc.
If there is a 100% SCSS solution, I'd be curious to read about it.
I have a Ruby hash of colors:
colors = {
green: '#00FF00',
blue: '#0000FF',
red: '#FF0000'
}
In _colors.css.scss.erb, I have:
$default-background-color: #000000;
<% colors.each do |key, value| %>
<%= "$#{key}: #{value};" %>
<% end %>
which will output in _colors.css.scss:
$default-background-color: black;
$green = #00FF00;
$blue = #0000FF;
$red = #FF0000;
And further in _colors.css.scss.erb:
#mixin body-prefixed-rule($property, $class-name, $color) {
body.#{$class-name} & {
#{$property}: $color;
}
}
#mixin variable-color($property) {
#{$property}: $default-background-color; // Static default color, defined above
<% colors.each do |key, value| %>
<%= "#include body-prefixed-rule($property, '#{key}', $#{key});" %>
<% end %>
}
The second mixin will output in _colors.css.scss:
#mixin variable-color($property) {
#{property}: $default-background-color; // Static default color, defined above
#include body-prefixed-rule($property, 'green', $green);
#include body-prefixed-rule($property, 'blue', $blue);
#include body-prefixed-rule($property, 'red', $red);
}
So in application.css.scss, I have:
#import '_colors';
button {
color: white;
#include variable-color('background-color');
&:hover {
background-color: white;
#include variable-color('color');
}
}
which finally outputs in SCSS without mixins:
button {
color: white;
background-color: black;
body.green & {
color: white;
background-color: green;
}
body.blue & {
color: white;
background-color: blue;
}
body.red & {
color: white;
background-color: red;
}
&:hover {
color: black;
background-color: white;
body.green & {
color: white;
background-color: green;
}
body.blue & {
color: white;
background-color: blue;
}
body.red & {
color: white;
background-color: red;
}
}
}
So I only need to change the class of my <body> to toggle background color everywhere, simply by using #include variable-color($property-name);

Categories