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.
Related
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.
I'm trying to figure out a good workflow of how to use Tailwind together with CSS Modules in Next.js, but I'm running into some tricky problems regarding specificity and the order in which styles are inserted by Webpack.
Consider the following use case: I want to create a reusable Button component whose styles can be overridden with utility classes. Option 1 is to extract the component, as mentioned in the Tailwind Docs:
/* button.jsx */
export const Button = props => <button {...props} className={`btn {props.className}`} />
and in my tailwind.css file I'd add the classname in the #layer directive:
#tailwind base;
#tailwind components;
#tailwind utilities;
#layer components {
.btn { #apply ... }
}
This is nice because I can override any of the buttons styles with utility classes, since they have higher specificity. However, I see a few minor problems here:
the class is now global, so it's included in every page even if I don't use the component
the style and component are no longer colocated - this might get messy if I create many such classes
Option 2 would be to use a CSS Module and apply the classes like this:
/* button.module.css */
.btn { #apply ...}
/* button.jsx */
import styles from "./button.module.css";
export const Button = props => <button {...props} className={`${styles.btn} {props.className}`} />
This way the CSS only gets loaded when the component is used, and the styles are colocated. Problem here is that in Next.js CSS Modules are inserted after global CSS, so they have higher specificity. Which totally makes sense, usually I'd want my CSS Modules to be able to override global styles. However, in this case I'd like them to be inserted before my global styles so that I can override them with utility classes.
I assume this should be possible by adjusting the Webpack config? Does anyone proficient with Webpack know how to achieve this?
I actually found a nice solution in tailwind v3.0, which is using the new important option. In your tailwind.config.js you can now add this line:
module.exports = {
// ...
important: '#__next'
}
This way, all your utilities are getting prefixed with #__next (the root element in Next.js), which increases their specificity above that of CSS Modules.
Go for the second option and simply add ! to the property to make that important.
/* button.module.css */
.btn { #apply !mb-0 }
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. :)
There's a global css class I want to override (.ant-content), but I want to do it per route, I've tried importing css files which override .ant-content per react-component that gets loaded up in various routes but they only end up loading their css even when not rendering (probably because the imports happen regardless if a component is loaded).
Assuming you have a class like this somewhere which you want to override:
.ant-content{
color: red
}
you could try using the specifity ordering rules of CSS to override this. So, for example if in one of your routes you have a component you want to overwrite which looks like this
<div className='override'>
<Component className='ant-content/>
</div>
Then in the CSS you import into that component you could use:
.override .ant-content{
color: blue
}
This should override the original .ant-content class as it is more 'specific' than the original class declaration.
You can read more about specifity here: https://www.w3schools.com/css/css_specificity.asp
I'd like to ask for a little nudge to get my brain out of the box I got it into.
Context
Angular 4 using Angular CLI and AoT.
All methods mentioned in https://angular.io/docs/ts/latest/guide/component-styles.html describe ways to set complex CSS of a component while it is being written by a developer.
After a component is created, Angular allows to adjust individual styles and even assign various CSS class names to tags in the component as you please, all that using [ngClass], <div [class.something]="condition">, various #HostBinding decorators and some other methods.
However, none of the above can change the CSS declaration the component is using. The methods above can either (a) use what is already available in the stylesheet defined by the developer or (b) set individual CSS properties to individual HTML tags in the component's template.
Question
How would I update the CSS for the whole component on runtime so that all elements in that component respond to the new CSS?
Say I introduce a new style for a div.someClass and I want all matching elements div.someClass to reflect the new style.
Plunker
A showcase of my attempts is here: https://plnkr.co/edit/N2C40cSb7hd1AyOxWWdT
The button should be red, based on the value of MyChildComponent.styles
I think I understand why it doesn't work the way I would expect: shortly said, styles are built in the component during compilation, not runtime, including those found inside <style /> tags in the template.
But knowing why it doesn't work doesn't help me to make it work.
Any help highly appreciated.
Solution 1
Inserting a new css class is not possible ( as far as i know ) but you can insert css properties to your component dynamically.
I modified your dynamicStyles() to this
get dynamicStyles(): any{
return {'background': 'red' };
}
that returns an object instead of string because you will pass this object to ngStyle of your button.
In your template, I change the button like this
<button type="button"
[ngStyle]="styles">
Button
</button>
Here's a plunkr
Solution 2
This is something that I would not recommend but in your case it might be useful. You can add this
encapsulation: ViewEncapsulation.None
and the import
import {ViewEncapsulation} from '#angular/core'
to your #Component.You can leak your component's css so that you can use it on your child component. Then in your child component, add a [ngClass] in your button so that you can just pass a variable via #Input() if it should be red.
<button type="button"
[ngClass]="{'redButton': isButtonRed}"
>Button</button>
And in your style.css
.redButton{
background:red;
}
And in your main component.
<div>
<h2>Hello name</h2>
<my-child [isButtonRed]="true"></my-child>
</div>
Here's another plunkr
Hope this helps.