Combing global class names with component specific style - css

I'm building a design system where I put emphasis on the architecture - I want it to be a good developer experience.
Bootstrap is used in this system but also together with component-specific styles. I'm thinking I want CSS-in-JS because I like to write style in the actual component, not in a different CSS file.
I have an idea, something like this:
const componentSpecificStyle = `margin-bottom: 0rem;`
const globalBootstrapClass = ['card', 'card-body']
return <div someArgument={componentSpecificStyle + globalBootstrapClass} />
This way it would be standard, two types of classes, one argument on the element, less confusion for coming developers.
What I've tried
Looked into Styled-Components and Emotion, from what I can tell they don't help me achieve this. It's possible to do
const Component = styled.div`margin-bottom: 0rem;`
return <Component className={'card card-body'}
But it seems a bit clumsy to me.
React-Bootstrap would be a solution for this but I think I usually end up adding extra classnames anyway, so I won't abstract that away...
ex.
import { Card } from 'react-bootstrap'
const componentSpecificStyle = `margin-bottom: 0rem;`
return <Card.body style={{componentSpecificStyle}} />
or something similar... but as I said, what if I want to add another classname on top of this? then we are styling in three different ways, react-bootstrap, inline-ish style and className.
Question
Am I making it unnecessarily complex?
Any suggestions or thoughts on the matter are highly appreciated!

If you want to define CSS inside your components, you're going to need CSS-in-JS indeed.
Theoretically, you're allowed define inline styles like so:
const style = {
marginBottom: '0rem',
};
return <div style={style} />
but this doesn't scale well, all styles are inlined, and you don't get all the benefits of CSS-in-JS, like ability to use props, themes, style inheritance, and having your styles autoprefixed.
Example of what you've tried in pt 1 actually makes a lot of sense.
Maybe it isn't the absolute best idea to mix global class names with styled-components, so if you want to go all-in with styled-components, you could define some set of styles in styled-components only, and inherit them one in another, like so:
const Card = styled.div`
/* Your card styles go here */
`;
const CardBody = styled(Card)`
/* Your card body styles go here */
/* (in your example you apply card and card-body to the same element, */
/* so I assume card-body is a "variant" of card) */
`;
const Component = styled(CardBody)`
margin-bottom: 0rem;
`;
return <Component />
This way, you have all your styles managed by styled-components, and you don't need to worry about global styles overwriting something you wrote in styled-components - because Component styles will be more important than CardBody styles, and CardBody styles will be more important than Card styles.

The best solution I've come up with so far is using Emotion and their css-prop on elements. https://emotion.sh/docs/css-prop
This way I can write code like it's inline-css but the end result is a class on the element, which I think is neat.
Here's an example:
const styleEmployeeBox = css`border-right: black solid 1px;`
const bstyleCard = 'card';
...
return <div css={styleEmployeeBox} className={bstyleCard}>
Inspecting the element shows me:
<div class="card css-nrnevs-ComponentName">
Writing this kind of css-in-js is something I'm choosing to go with because I see a lot of developers in my team writing inlined style on the style prop -- probably because they are lazy and don't want to add a css-classes for small specific fixes.
If they are reasons to why I shouldn't go ahead with this solution, I'd be HAPPY to hear about it. :)

Related

How to use pseudo class inside style attribute in React

import React from 'react';
export function App(props) {
return (
<div>
<h1
style={{
'&::hover': {
color: 'white'
},
color: 'red'
}}
>Hello React.</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
);
}
This hover does not work, but I wanted to be able to do it within the component's style, without a css file, class or objects.
Even if this method is not valid, there is some validity without using the components mentioned above.
The React JSX style attribute lets you provide an object of inline styles, but inline styles cannot include selectors like the pseudo-class :hover. Inline styles can only include properties. If you want to make inline styles more powerful, there are several methods of CSS-In-JS libraries that make this possible. Solutions I've tried include Emotion (which I like a lot) and styled-components (would not recommend).
Another possible option is TailwindCSS, which is a different kind of library that has a similar feel to what you want. I would personally recommend Tailwind as a go-to for this kind of thing.
Here's how you could do what you're looking for with Tailwind:
<h1 className="color-red hover:color-white">Hello React.</h1>
Again, you can't do this with just inline style attributes.

Using css variable with styled-compoents

I know you can wrap your react app with <ThemeProvider /> and you can set a variable for the theme and access it like so:
const Text styled`
color: ${((props) => props.theme.red)};
`
But it's it CSS itself have the variable feature?
You can just do
:root {
--red: tomato;
}
.text {
color: var(--red);
}
is it ok to mix them? or stick to either styled-components or CSS?
Yes it is OK. Those 2 technologies have nothing to do with each other and are not conflicting.
As long as your components can inherit the CSS variables (AKA custom properties) they will work.
Just make sure they are really inherit.
Clearly defining variables on the root element selector (html) will make them available everywhere, but sometimes you don't want global variables, but more local, per page/area/component, so you need to structure you code taking that into account.
The real power of CSS variables is in their inheritance (unlike SCSS variables which are injected/replaces during the build-process).
The power of styled-components is isolation & the ability to share code with javascript. This does hinder their ability to inherit CSS variables defined at parents-level.
You need not bother yourself with this question at all, and simply ignore the fact you are using styled-components. All that matters is the HTML structure, which how inheritance works.
I Googled things for you:
https://medium.com/fbdevclagos/how-to-leverage-styled-components-and-css-variables-to-build-truly-reusable-components-in-react-4bbf50467666
https://betterprogramming.pub/7-ways-to-inherit-styles-using-styled-components-69debaad97e3
https://dev.to/arnonate/using-css-variables-to-tame-styled-component-props-2f9o

TailwindCSS 3 classes doesn't override previous classes

I am facing an issue which is mind-numbing in the world of CSS.
TailwindCSS 3 classes just doesn't override previous classes.
For example, there is this component, I created:
import * as React from "react";
const TextBox = ({ addClassName, children }) => {
const className = `text-xl leading-7.5 font-lato font-normal ${addClassName}`;
return <div className={className}>{children}</div>;
};
export default TextBox;
Then I go ahead and use this component above at another place, like this:
<TextBox addClassName="text-4xl">My New Text</TextBox>
Now when I inspect it in the browser, it shows both of the font-size:
Screenshot from the browser inspect
class="text-xl leading-7.5 font-lato font-normal text-4xl"
As you can see both of the classes are there, both referring to font-size, and the larger one is after the smaller one.
And still ONLY the small ( the original ) font-size will be the dominant.
( as a side note, I did try to put the addClassName variable in the front as well, no help )
Why is this?
I appreciate any help getting with this.
Thank you
I found the answer from dev.to.
The reasons your code didn't work are:
It turns out that the space-separated CSS class list that the class HTML attribute accepts is not treated as a list when calculating CSS rules precedence by the browser. The class attribute actually contains the set of classes the element has, so the order doesn't matter.
As the order that the classes appear in the class attribute doesn't matter, the rule that comes later in the CSS stylesheets wins.
Moreover, it wasn't warranted that the 'text-4xl' was defined after the 'text-xl' in the CSS stylesheet.
So to solve this problem, I recommended using tailwind-merge to override previous classes.
Tailwind-merge is a utility function to efficiently merge Tailwind CSS classes in JS without style conflicts.
One of its features was: Last conflicting class wins
twMerge('p-5 p-2 p-4') // → 'p-4'
Quoting from redit
In CSS if you have two selectors with equal specificity the one that comes last in the CSS structure takes precedence. The order in the class attribute has no effect.
Suggested solution would be to write your own class after tailwindcss import statement, or edit it using inline style.
My personal tip: Don't use two classes that target the same css property, text-lg and text-4xg both target font-size, you need a way (suggest clsx lib) to put only one class name and not the other
import clsx from 'clsx';
let condition = false;
function Component(){
return (
<div>
<p className={clsx({
"text-lg": condition,
"text-4xl": !condition,
})}>
...
</p>
</div>
);
}
I have had the same problem, and didn't want to add yet another dependency and increase my app's bundle size to solve something that doesn't have to be this hard. So I looked into the tailwind docs and found this:
Use the components layer for any more complicated classes you want to
add to your project that you’d still like to be able to override with
utility classes.
#tailwind base;
#tailwind components;
#tailwind utilities;
#layer components {
.card {
background-color: theme('colors.white');
border-radius: theme('borderRadius.lg');
padding: theme('spacing.6');
box-shadow: theme('boxShadow.xl');
}
/* ... */
}
By defining component classes in the components layer, you can still
use utility classes to override them when necessary:
<!-- Will look like a card, but with square corners -->
<div class="card rounded-none">
<!-- ... -->
</div>
Though this means you will have to write a component class every time you want to override an element with predefined classes.
https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes
I would suggest you to try clsx or clsssnames for better usage with tailwind classes instead of string interpolation
I found a Solution guys.
Setup props with default CSS values at the original component, and then at the time of the usage of this aforementioned component, if we need different style, we just gave that in the props.

React - Override all previous clashing styles when adding a new class

I have seen a lot of threads on the topic, but nothing seems to work for me the way I want it. I have a React component which takes in className as a prop. I also have a HOC which takes in a component and default style, and returns a component which takes in a new style, as such:
function withCombinedStyles(WrappedComponent, defaultClassName) {
return ({className: newClassName, ...rest}) => {
const className = combineClassNames(newClassName, defaultClassName);
return <WrappedComponent className={className} {...rest} />;
};
};
Currently, combineClassNames just concatenates the strings. I want to make it so that the newClassName always overrides the clashing styles with the defaultClassName but keeps the old ones. Currently, one of the styles seems to override the other but without apparent order - it doesn't matter how I put defaultClassName and newClassName. How can I do that? Currently, I use CSS modules but the project is still small enough for me to be able to rewrite the styles with another technology if that would achieve the desired result and CSS modules cannot do that (though I would prefer to use CSS modules and a React-specific solution with them would work since).
I would use !important but that would mean that I can never add a third className and it could be challenging to extend the styling in the future.
If you were doing CSS in JS then the order would matter, but because you're just combining CSS class names what really matters is specificity. Say newClassName has declarations that are less specific than the ones in defaultClassName - then the more specific declarations in defaultClassName will win over. You can probably fix your CSS by just ensuring the statements you want to 'win' are more specific.
Check out calculators like this one
If you went with inline CSS-in-JS styles I think something like this might work:
function withCombinedStyles(WrappedComponent, defaultStyles) {
return ({styles: newStyles, ...rest}) => {
return <WrappedComponent style={{...defaultStyles, ...newStyles}} {...rest} />;
};
};
Check this out < my library, which effectively does .logo.logo under the hood, but in a neater way.

CSS architecture with React that also can be themed

I'm currently building out a large React app. Css, has never been my strong point. But now CSS has sass / cssNext / inline styles with React. I've been using BEM with sass, but as my other applications have grown huge even that starts to break down. Especially when you had on the ability to "re-skin" or "theme" the pages outside of the primary color schemes etc..
so -- can someone point me to a proven way to create css with react that can scale very well, and allows for custome theme'ing when people want to borrow my components. For instance,
<MyComponent />
// this component has its styles but lets say Joe Schmoe wants be import
// it? But, Joe wants to overlay his custom styles?
// Is there a new paradigm that allows for an overlay or reskin within the component?
Or even the idea of the whole application being skinnable some time down the line. I know this is sorta a very base question, but whenever I build out projects my pain points also seem to be the CSS - so I want to know what really works.
This was, until recently, not a solved problem in the React world.
I'm one of the maintainers of ElementalUI, a React component library, and we've been trying out all the different ways of styling for the past 6-12 months. (!) You name it, we've tried it. (I talked about my experiences with some of the most popular libraries during my ReactNL keynote and where they break down)
The issue is that none of the current styling libraries have built-in support for theming at all. You can do it with most of them in a very hacky, non user-friendly way, but that's not what you want when you distribute a component, right?
That's why we built styled-components. styled-components has a bunch of interesting properties, and one of them is that theming is directly built into the library, making it the perfect choice for building distributable components!
Here is the short explanation, though I encourage you to go through our documentation which explains everything!
This is what the basic usage of styled-components looks like:
import React from 'react';
import styled from 'styled-components';
// Create a <Wrapper> react component that renders a <section> with
// some padding and a papayawhip background
const Wrapper = styled.section`
padding: 4em;
background: papayawhip;
`;
This variable, Wrapper, is now React components you can render:
// Use them like any other React component – except they're styled!
<Wrapper>
<Title>Hello World, this is my first styled component!</Title>
</Wrapper>
(if you click on the image you'll get a live playground)
When you pass an interpolated function into the tagged template literal, we pass you the properties passed to the component. This means you can adapt to the props:
import styled from 'styled-components';
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;
`;
Here, we've created a Button component that you can make primary like any other React component:
<Button>Normal</Button>
<Button primary>Primary</Button>
Now comes the theming aspect. We export a component called ThemeProvider that you can pass a theme to and wrap your app (or parts of the app) in:
import { ThemeProvider } from 'styled-components';
const theme = {
main: 'mediumseagreen',
};
ReactDOM.render(
<ThemeProvider theme={theme}>
<MyApp />
</ThemeProvider>,
myElem
);
Now any styled component within that ThemeProvider, no matter how deep thanks to context, will get this theme injected into the props.
This is what a themable Button would look like:
import styled from 'styled-components';
const Button = styled.button`
/* Color the background and border with theme.main */
background: ${props => props.theme.main};
border: 2px solid ${props => props.theme.main};
/* …more styles here… */
`;
Now your Button will take the theme it gets passed and change it's styling based on that! (you can also provide defaults via defaultProps)
That's the gist of styled-components and how it helps to build distributable components!
We have a currently WIP doc about writing third-party component libraries which I'd encourage you to check out, and of course the normal documentation is a good read too. We've tried to cover all the bases, so if you see anything you dislike or that you think is missing please immediately let us know and we'll discuss!
If you have any other questions about styled-components feel free to reply here or reach out on Twitter. (#mxstbr)
I don't know what is the best way. I think it's more like a personal preference. I will just share the tools that I'm using. What I'm using is css-modules with postcss.
css-modules gives you the power to create a local-scoped identifier with a global unique name for each of your CSS classes and also enable a modular and reusable CSS! You can also use postcss-modules-values to create a global settings file which contains all the settings variables. So that you can change the theme for your website.
Here's how I structure the components. Each of my component will have one css file which is very easy to maintain or make changes.
Here's the code for the Button component.
function Button(props) {
const className = props.className ? `${styles.button} ${props.className}` : styles.button;
return (
<button disabled={props.disabled} className={`${className}`} type="button" onClick={props.onClick}>
{Children.toArray(props.children)}
</button>
);
}
Notice that I have a className for the component which allows other component to pass in the class for the button component. So that when someone borrow your component they can extend or override your button styles.
If you need to create a customisable website, I will also recommend to use Less.js which provides the live preview of the customised website.

Resources