how do you share styles in react? - javascript

I have a component in which I got:
const Title = styled(Typography)({
fontFamily: 'Manrope',
fontStyle: 'normal',
fontWeight: 600,
fontSize: 28,
lineHeight: '38px',
color: '#20232C',
marginTop: 17,
height: 50,
display: 'block',
});
and in this component i use it like this..
<Title>Title here</Title>
As you can imagine, Title is something that could be used in many places in the app. So I took the above Title and put it in components/Titles/styles.tsx. and then export it from there.
Now, wherever I need this, I import Title and just use it.
The problem: In some other places, this Title needs to have different fontWeight and marginTop, and so on. Who knows, in the future, maybe in one place, it could need 4-5 fields different then in the current Title styles.
How can we workaround this ?
Way 1:
export const Title = styled(Typography)(
({ fontWeight }: { fontWeight?: number }) => ({
width: 'fit-content',
fontFamily: 'Manrope',
fontStyle: 'normal',
fontWeight: fontWeight || 'normal',
fontSize: 28,
lineHeight: '25px',
color: '#20232C',
marginTop: '17px',
}),
);
As I made fontWeight optional, We could do this for all fields.
What do you think of this way and how do you handle situations like this ? it's really almost the first time I am dealing with styles in js. I come from a vue.js world.

Making the fontWeight as an attribute is of course fine, but might come too complex when extending it further.
The other option would be inherit the Title into another styled component (always best when you find meaningful use-cases for naming), e.g.:
export const Subtitle = styled(Title)`
font-size: 24px;
font-weight: bold;
`

This is specific to styled-components.
There are two common ways, both covered in the documentation.
Props
const Button = styled.button`
/* Adapt the colors based on primary prop */
background: ${props => props.primary ? "palevioletred" : "white"};
color: ${props => props.primary ? "white" : "palevioletred"};
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid palevioletred;
border-radius: 3px;
`;
render(
<div>
<Button>Normal</Button>
<Button primary>Primary</Button>
</div>
);
Extending
// The Button from the last section without the interpolations
const Button = styled.button`
color: palevioletred;
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid palevioletred;
border-radius: 3px;
`;
// A new component based on Button, but with some override styles
const TomatoButton = styled(Button)`
color: tomato;
border-color: tomato;
`;
render(
<div>
<Button>Normal Button</Button>
<TomatoButton>Tomato Button</TomatoButton>
</div>
);

You should be able to use props in the Title tag and pass them to the individual component like this:
<Title thisTitlesHight="80px" thisTitlesWidth="50px">Title Here<Title>
Then using these props inside your child component:
export const Title = styled(Typography)(
({ fontWeight }: { fontWeight?: number }) => ({
height: this.props.thisTitlesHeight,
width: this.props.thistitlesWidth
}),
);
you also have to define props in the function.
I created a codeSandbox so you can see the code for it: https://codesandbox.io/s/stupefied-wildflower-wmw26?file=/src/App.js

Related

React CSS class gets override automatically on production server

I am facing a weird CSS issue in my React project. A particular part of the JSX <div> has a class applied to it and added some style properties in the main .css file of the project. In local development, everything works fine but as soon as the build is created and uploaded to the production server, that particular part of the JSX <div> CSS class changes and the styling gets distorted.
Example:
Original JSX
import React, { useEffect, useState, useContext } from "react";
import Tooltip from "#material-ui/core/Tooltip";
import { withStyles, makeStyles } from "#material-ui/core/styles";
import Slider from "#material-ui/core/Slider";
const useStyles = makeStyles((theme) => ({
root: {
width: 450,
},
margin: {
height: 100,
},
}));
const PrettoSlider = withStyles({
root: {
color: "red",
height: 8,
},
thumb: {
height: 24,
width: 24,
backgroundColor: "#fff",
border: "2px solid currentColor",
marginTop: -8,
marginLeft: -12,
"&:focus,&:hover,&$active": {
boxShadow: "inherit",
border: "2px solid #fff407 !important",
},
},
active: {
backgroundColor: "#fff407",
},
})(Slider);
const CustomizedSlider = ({
id,
abbr,
type,
minElig,
maxElig,
}) => {
useEffect(() => {
setValue(sliderPreviousValue);
}, [sliderPreviousValue]);
const classes = useStyles();
return (
<>
<div className={classes.root}>
{type === "intervention" ? (
<ProgressBar max={maxElig} value={sliderValue} />
) : null}
{renderSlider}
</div>
</>
);
};
Original DOM:
<div class="diabMetr clearfix">
<span class="diabLabl">Diabetes</span>
<div class="makeStyles-root-1">
<span class="MuiSlider-root WithStyles(ForwardRef(Slider))-root-3 MuiSlider-colorPrimary"><span class="MuiSlider-rail WithStyles(ForwardRef(Slider))-rail-8"></span><span class="MuiSlider-track WithStyles(ForwardRef(Slider))-track-7" style="left: 0%; width: 83.3333%;"></span><input type="hidden" value="200"><span class="MuiSlider-thumb WithStyles(ForwardRef(Slider))-thumb-4 MuiSlider-thumbColorPrimary PrivateValueLabel-open-12 PrivateValueLabel-thumb-11" tabindex="0" role="slider" data-index="0" aria-label="pretto slider" aria-orientation="horizontal" aria-valuemax="240" aria-valuemin="0" aria-valuenow="200" style="left: 83.3333%;"><span class="PrivateValueLabel-offset-13 MuiSlider-valueLabel WithStyles(ForwardRef(Slider))-valueLabel-6"><span class="PrivateValueLabel-circle-14"><span class="PrivateValueLabel-label-15">200</span></span></span></span></span>
<div class="valueOuter clearfix"><label class="valueLeft">0</label><label class="valueRight">240</label></div>
</div>
</div>
The CSS for this JSX is:
.diabMetr {
padding-top: 10px;
span.diabLabl {
display: inline-block;
width: 200px;
text-align: left;
font-size: 12px;
line-height: 30px;
text-align: right;
#include respond-to(media-xl) {
width: 120px;
}
}
span.MuiSlider-root {
width: 100%;
padding: 0;
height: 0px;
.MuiSlider-rail {
height: 30px;
border-radius: 15px;
background: #e8e8e8;
opacity: 1;
}
.MuiSlider-track {
height: 30px;
background: #88d479;
border-radius: 15px;
}
.MuiSlider-thumb {
z-index: 12;
width: 35px;
height: 35px;
border-radius: 50%;
margin-left: -17px;
border: #88d479 solid 2px;
margin-top: -3px;
}
.MuiSlider-markLabel.MuiSlider-markLabelActive:last-child() {
right: 0 !important;
}
}
}
.makeStyles-root-1 {
width: calc(100% - 220px) !important;
float: right;
margin-top: -22px;
}
The DOM changes after build and uploaded to the server:
<div class="diabMetr clearfix">
<span class="diabLabl">Diabetes</span>
<div class="jss16">
<span class="MuiSlider-root jss18 MuiSlider-colorPrimary"><span class="MuiSlider-rail jss23"></span><span class="MuiSlider-track jss22" style="left: 0%; width: 83.3333%;"></span><input type="hidden" value="200"><span class="MuiSlider-thumb jss19 MuiSlider-thumbColorPrimary jss27 jss26" tabindex="0" role="slider" data-index="0" aria-label="pretto slider" aria-orientation="horizontal" aria-valuemax="240" aria-valuemin="0" aria-valuenow="200" style="left: 83.3333%;"><span class="jss28 MuiSlider-valueLabel jss21"><span class="jss29"><span class="jss30">200</span></span></span></span></span>
<div class="valueOuter clearfix"><label class="valueLeft">0</label><label class="valueRight">240</label></div>
</div>
</div>
The CSS for the class .jss16 is:
.jss16 {
width: 450px;
}
Issue to notice
Only the class .makeStyles-root-1 gets replaced with some random class .jss16 when the build gets uploaded to the server and the CSS changes accordingly, the rest of the JSX remains unchanged. I tried searching for the class .jss16 everywhere in the code, but it's not found. Also, everything works fine on localhost.
I tried adding the CSS properties to the .jss16 like this:
.jss16 {
width: 450px;
width: calc(100% - 220px) !important;
margin-top: -22px;
float: right;
}
and then re-initiate the uploading process but then instead of .jss16, another class is replaced something like .jss42. This keeps on repeating and does not work on any new build created.
I also tried the following CSS:
.diabMetr + span + div {
width: 450px;
width: calc(100% - 220px) !important;
margin-top: -22px;
float: right;
},
but this also didn't help. The styling of the app still remains distorted (incorrect, not as on localhost).
I spent several hours searching for this but in vain. If anyone can assist me in understanding this error and resolve the same, will be highly appreciated. Thanks in advance!
there are quite a few issues with this code. First in jsx CSS class is given as className as #Max has mentioned in his/her answer.
Another issue is that #material-ui's makeStyle doesn't work in this way. The classNames inside the makeStyle change to random names in the build stage. This happens to keep the classNames uniques, this is #material-ui's feature. I'd suggest you to read this #matrial-ui's documentation about makeStyles. And here a code example is provided.
To use makeStyles classes you've to hook it into your component.
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
const useStyles = makeStyles({
root: {
backgroundColor: 'red',
color: props => props.color,
},
});
export default function MyComponent(props) {
const classes = useStyles(props);
return (
<div className={classes.root}>
Lorem iosum poder
</div>
);
}
Update
According to your jsx code, add the styles which you've added in css class .makeStyles-root-1 in the useStyles object. It'll add the styles to the element.
After adding those CSS styles in useStyles this object will look like this:-
const useStyles = makeStyles((theme) => ({
root: {
width: 'calc(100% - 220px) !important',
float: 'right',
marginTop: '-22px'
},
margin: {
height: 100,
},
}));
The root class will contain those styles and it'll be applied without providing the styles separately from the CSS file.
Solution
I am not sure what component creates the div with "jss16" class, assume it is ExternalComponent.
You should add a custom className (assuming ExternalComponent handles this correctly):
<ExternalComponent className="myclass">
...
</ExternalComponent>
this will create a DOM like this:
<div class="jss16 myclass">
...
</div>
Sou you can create css for myclass:
.myclass {
width: calc(100% - 220px) !important;
float: right;
margin-top: -22px;
}
Explanation
ExternalComponent uses jss to dynamically generate css classes, so you cant rely on the name of the dynamically generated class. In most of the cases, components with custom classes should append props.className to the generated jss like this:
return (
<div className={jssClassname + props.className ? ' ' + props.className : ''}>
{children}
</div>
);
I couldn't reproduce the error because I had some syntax issues, so I wonder if fixing these, the build will behave correctly:
Add closing / to input
Write style's using objects, example style={{left: '0%', width: '83.3333%'}}
Update class to className
Update tabindex to tabIndex
Lastly, if that doesn't help, to make your CSS work, ie .diabMetr + span + div, rewrite it to:
.diabMetr > span + div {}
or
.diabMetr > div {}
Right now, it's not selecting the child element.

Can't Style Grommet icon inside Anchor

I'm new to Grommet with styled components.
I Have already checked all the docs and can't find the solution.
PROBLEM
I have an Anchor with an icon and a label.
Problem is I cannot target the icon for styling when i hover or it is active.
Text / Label changes the styling though. How can i achieve/fix this?
I've also tried using styled components and putting an Icon and a Text inside a Grommet Box, but didn't work.
Please help!
import React from "react";
import { Anchor, Box, Text } from "grommet";
import styled from "styled-components";
import { Currency as PayoutIcon, Menu as MenuIcon } from "grommet-icons";
const StyledAnchor = styled(Anchor)`
display: flex;
height: 56px;
color: #808191;
padding: px 20px;
border-radius: 12px;
background: transparent;
width: max-content;
text-decoration: none;
font-family: Inter;
color: #808191;
padding: 0px 20px;
background: transparent;
transition: all 0.25s ease 0s;
text-decoration: none;
border: none;
&:visited {
text-decoration: none;
border: none;
}
&:hover {
color: #6c5dd3;
text-decoration: none;
}
&:active {
color: #fff;
background: #6c5dd3;
text-decoration: none;
border: none;
}
&:focus {
color: #fff;
background: #6c5dd3;
textdecoration: none;
border: none;
}
`;
const SidebarItem = () => {
return (
// <Box color="#808191" hoverIndicator="true">
<StyledAnchor
color="#808191"
label="Payouts"
onClick={() => {}}
href="#"
icon={<PayoutIcon />}
/>
// </Box>
);
};
export default SidebarItem;
For the granularity of styles you are looking for, I think you can directly use the Button component instead of Anchor, nevertheless, the usage of Button is more compliant with accessibility standards (WCAG) for the Sidebar interactive elements that you are describing above.
Grommet works best with styled-components, yet grommet theme-ing is also very powerful, and knowing how to leverage its capabilities will help you use styled-components much less.
Recently, grommet extended the Button theme (kind/default button), and that should do the trick for you with no sweat and no need for styled-components, here is an example:
import React, { useState } from "react";
import { render } from "react-dom";
import { Box, Grommet, Button } from "grommet";
import { Currency as PayoutIcon } from "grommet-icons";
const theme = {
global: {
colors: {
myColor: "#808191",
"background-contrast": {
dark: "#FFFFFF14",
light: "#0000000A"
},
"active-background": "background-contrast",
"active-text": "red",
icon: "text",
// focus color is an important indication for keyboard navigation accessibility,
// it will be an ill advice to set it to undefined and remove focus
focus: "teal",
text: {
dark: "#C0CADC",
light: "#444444"
}
}
},
button: {
default: {
color: "#808191",
border: undefined,
font: {
weight: 700
},
padding: {
horizontal: "12px",
vertical: "6px"
}
},
hover: {
default: {
background: {
color: "background-contrast"
},
color: "brand"
},
secondary: {
border: {
width: "3px"
},
padding: {
horizontal: "9px",
vertical: "3px"
}
}
},
active: {
background: {
color: "aliceblue"
},
color: "teal",
secondary: {
border: {
color: "transparent"
}
}
}
}
};
const SidebarItem = () => {
const [active, setActive] = useState();
return (
<Button
active={active}
label="Payouts"
icon={<PayoutIcon />}
onClick={() => {
setActive(!active);
}}
href="#"
/>
);
};
export const App = () => {
return (
<Grommet theme={theme}>
<Box pad="small" align="start">
<SidebarItem />
</Box>
</Grommet>
);
};
render(<App />, document.getElementById("root"));
Here is a codesandbox for running it live.
The Button has the granularity for active/hover/disabled and more, you can basically gain the same functionality in Anchor using the theme anchor.extend but this way is a much cleaner approach.

Pass props to styled-components

I am querying data for my react site using graphql from my CMS (prismic.io) in order to produce color themed pages. I want to pass a variable or props into my styled component to change the background color based on what is sent back from the CMS.
In the below example, my graphql query will return a HEX that has been inputted by the user, this would then be applied to buttons etc to theme that page.
The colour can and will change from page to page as the user will be selecting it within the CMS.
Any help would be appreciated. Code example below:
Props
props.data.case_study_color
Component
const ContactButton = styled.button `
background: #004655;
color: #fff;
font-size: 2rem;
padding: 10px;
`;
You could do the following.
const ContactButton = styled.button`
background: #004655;
color: ${props => props.color || '#fff'};
font-size: 2rem;
padding: 10px;
`;
See codesandbox example here.
This would be the component code:
.....component
const [color, setColor] = React.useState("#fff");
React.useEffect(() => {
fetch(URL).then(data => {
setColor(data.response);
});
}, []);
return (
<div className="App">
<ContactButton color={color}>White</ContactButton>
</div>
);
const ContactButton = styled.button `
background: ${props => props.caseStudyColor};
color: #fff;
font-size: 2rem;
padding: 10px;
`;
<ContactButton caseStudyColor={'#004655'} />
As my solution was slightly different but based on Paul's answer it might be useful for someone else.
Button Component
const ContactButton = styled.button`
background: ${props => props.themeColor || '#004655'};`
Color Component
const themeColor = props.data.case_study_color;
Button
<ContactButton themeColor={themeColor}>Get in touch</ContactButton>

Stripe.js - cannot change background color or height of credit card input

I'm having an issue styling a Stripe credit card input in a Vue.js application. I'd like to change the background color to #f1f1f1 and the height to 60px, but I'm unable to do this either through the base styles or with css. What am I doing wrong? Ideally, I'd like to target it with css. Here's my code:
loadStripe() {
card = elements.create("card", {
style: {
base: {
iconColor: '#2A2A2A',
color: '#2A2A2A',
fontWeight: 300,
fontFamily: 'Lato, Open Sans, Segoe UI, sans-serif',
fontSize: '19px',
// height: '60px',
// background: '#f1f1f1',
'::placeholder': {
color: '#2A2A2A',
fontSize: '19px',
},
},
}
});
Thank in advance!
You can change the background color like below,
style: {
base: {
backgroundColor: 'red',
}
}
According to the documentation: https://stripe.com/docs/js/appendix/style the method is "backgroundColor". But the same documentation recommends so far to make some adjustments directly to the CSS of the element container. And especially for the 'background' and 'height' for me worked better to modify directly the CSS. Working example:
<template>
...
<div id="card-element"></div>
...
</template>
<script>
...
let adjustments = {
hidePostalCode: true,
style: {
base: {
// backgroundColor: "#cbf5f8",
fontWeight: "400",
fontFamily: "Avenir, Helvetica, Arial, sans-serif",
fontSize: "16px",
fontSmoothing: "antialiased",
":-webkit-autofill": {
color: "#fce883",
},
},
invalid: {
iconColor: "#FFC7EE",
color: "#FFC7EE",
},
},
};
card = elements.create("card", adjustments);
...
</script>
<style>
#card-element {
border: 1px solid currentColor;
border-radius: 4px;
height: 50px;
background: #cbf5f8;
margin-bottom: 20px;
padding-top: 10px;
}
</style>
Like described in the official documentation : https://stripe.com/docs/stripe-js/reference#elements-create , there is no support to add a background color for the input element.
But you can use some hack to the parent element that you has on your HTML code to make it looks like an input and style it like you want.

React.js child component not updating styling derived from props, why? (styled-components)

I am new to react.js and I am trying to get my head around conditional styling depending on props passed down from parent components.
I'm trying to make a button that has styling differences depending on whether the 'disabled' prop is true or false. If the button is disabled (true) it should appear grey, otherwise, it's blue.
This is the code I have so far, although it is not working and I'm not sure why.
Parent component
import React from 'react';
import { storiesOf } from '#storybook/react';
import { action } from '#storybook/addon-actions';
import { linkTo } from '#storybook/addon-links';
import { Welcome } from '#storybook/react/demo';
// Buttons
import Primary from '../components/ButtonPrimary'
storiesOf('Welcome', module).add('to Storybook', () => <Welcome showApp={linkTo('Button')} />);
storiesOf('Buttons')
.add('Primary', () => <Primary label="Default" disabled="false"></Primary>)
Child component
import React from "react";
import styled from 'styled-components';
const Button = styled.button`
background-color: ${props => props.disabled ? '#EDEDED' : '#0076C0'};
border: ${props => props.disabled ? '1px solid #DADADA' : 'none'};
color: ${props => props.disabled ? '#818181' : '#FFFFFF'};
cursor: ${props => props.disabled ? 'unset' : 'pointer'};
border-radius: 2px;
font-family: Roboto-Regular;
font-size: 16px;
padding: 6px 32px;
font-family: roboto, helvetica, sans-serif;
font-size: 18px;
&:focus {
outline: none;
}
&:hover {
box-shadow: ${props => props.disabled ? 'unset' : '0px 1px 2px 1px #b3b3b3'};
}
&:active {
box-shadow: ${props => props.disabled ? 'unset' : 'inset 0 0 10px #2f2f2f80'};
}
`;
export default class ButtonPrimary extends React.Component {
render() {
return (
<div>
<Button disabled={this.props.disabled}>{this.props.label}</Button>
</div>
)
}
}
Does anyone have any idea why?
In your parent component, you need to change disabled to be a boolean instead of a string.
storiesOf('Buttons')
.add('Primary', () => <Primary label="Default" disabled={false} ></Primary>)
Or in case if you need to use it as a string you need to specify your conditional
${props => props.disabled === 'true' ? '#EDEDED' : '#0076C0'};

Categories