I'm using Semantic UI React and trying to figure the best way to override default styles, so that I can change the appearance of cards and the overall theme.
Option 1 seems to be to define my CSS and put !important after every rule, which is not great.
Option 2 is the theming support, which sounds like what I want, except I cannot determine how to get started with that. My app is using CRA, and I'm a bit lost in documentation between changing my webpack configuration file (I don't have one), out of date blog posts from 2017 advising me to install a bunch of modules whose purpose is unclear, and the theming site itself which is advising me to define flexible variable files (an approach I like).
I can't determine why my theme files aren't getting picked up though, and it appears some sort of build fix is necessary that isn't covered by the theming guide.
What's the best way to get theming working while using a CRA build process? (./node_modules/.bin/react-scripts build
)
Actually you should never use !important
unless you really REALY have too. Apart from making you styles look very ugly, its a very bad practice to make all your styles !important
Since you mentioned CRA, which supports SCSS Modules, I can tell you that there is a third option which in my opinion is a bit nicer.
Here it goes in 3 easy steps:
1. Set an id somewhere high in the hierarchy. I do it in the public/index.html
on the body tag:
// public/index.html
...
<body id='app'>
...
2. Create a SCSS module file and wrap all your classes in a :global(#app)
// src/page.module.scss
:global(#app) {
.container {
padding: 2rem;
}
.button {
font-size: 3em;
}
}
3. Import the styles and pass them to the semantic components
// src/page.jsx
import React from 'react';
import { Button, Container } from 'semantic-ui-react';
import styles from './page.module.scss'
const Page = () => (
<Container className={styles.container}>
<Button className={styles.button} />
</Container>
)
The reason this works is because the output from the page.module.scss
SCSS Module will be compiled to this:
#app .page_container_2oYQ {
padding: 2rem;
}
#app .page_button_2oYQ {
font-size: 3em;
}
As you can see it will add the #app
id selector before the modularised class name, which will increase the specificity of your selectors, which in turn will override the semantic-ui ones.
Specificity is king. Only time would need to use !important
would be when inline style is present and the library does not expose a way to toggle the property off in some way (poor architecture choice).
The following list of selector types increases by specificity:
Type selectors (e.g., h1) and pseudo-elements (e.g., ::before).
Class selectors (e.g., .example), attributes selectors (e.g., [type="radio"]) and pseudo-classes (e.g., :hover).
ID selectors (e.g., #example).
https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity
Take a look at the first UI Button for Semantic UI here, is comprised of the following HTML:
<button class="ui button">Click Here</button>
CSS is attached via semantic.min.css:
.ui.button {
cursor: pointer;
display: inline-block;
min-height: 1em;
outline: 0;
border: none;
vertical-align: baseline;
background: #e0e1e2 none;
color: rgba(0,0,0,.6);
font-family: Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;
margin: 0 .25em 0 0;
padding: .78571429em 1.5em .78571429em;
text-transform: none;
text-shadow: none;
font-weight: 700;
line-height: 1em;
font-style: normal;
text-align: center;
text-decoration: none;
border-radius: .28571429rem;
-webkit-box-shadow: 0 0 0 1px transparent inset, 0 0 0 0 rgba(34,36,38,.15) inset;
box-shadow: 0 0 0 1px transparent inset, 0 0 0 0 rgba(34,36,38,.15) inset;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-transition: opacity .1s ease,background-color .1s ease,color .1s ease,background .1s ease,-webkit-box-shadow .1s ease;
transition: opacity .1s ease,background-color .1s ease,color .1s ease,background .1s ease,-webkit-box-shadow .1s ease;
transition: opacity .1s ease,background-color .1s ease,color .1s ease,box-shadow .1s ease,background .1s ease;
transition: opacity .1s ease,background-color .1s ease,color .1s ease,box-shadow .1s ease,background .1s ease,-webkit-box-shadow .1s ease;
will-change: '';
-webkit-tap-highlight-color: transparent;
}
To override say, the font color, all we have to do is write a selector that is more specific than this selector. We can achieve this by combining their two class selectors (equally specific) with a type selector (additional specificity).
This would look like:
button.ui.button {
color: red;
}
Now since button.ui.button
is more specific in describing the location of the element in the page (DOM), than say just .ui.button
, this signals to the browser that this style should override the previous declaration. This is a common way to customize a theme.
Great docs here: https://developer.mozilla.org/en-US/docs/Learn/CSS/Introduction_to_CSS
.ui.button {
cursor: pointer;
display: inline-block;
min-height: 1em;
outline: 0;
border: none;
vertical-align: baseline;
background: #e0e1e2 none;
color: rgba(0,0,0,.6);
font-family: Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;
margin: 0 .25em 0 0;
padding: .78571429em 1.5em .78571429em;
text-transform: none;
text-shadow: none;
font-weight: 700;
line-height: 1em;
font-style: normal;
text-align: center;
text-decoration: none;
border-radius: .28571429rem;
-webkit-box-shadow: 0 0 0 1px transparent inset, 0 0 0 0 rgba(34,36,38,.15) inset;
box-shadow: 0 0 0 1px transparent inset, 0 0 0 0 rgba(34,36,38,.15) inset;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-transition: opacity .1s ease,background-color .1s ease,color .1s ease,background .1s ease,-webkit-box-shadow .1s ease;
transition: opacity .1s ease,background-color .1s ease,color .1s ease,background .1s ease,-webkit-box-shadow .1s ease;
transition: opacity .1s ease,background-color .1s ease,color .1s ease,box-shadow .1s ease,background .1s ease;
transition: opacity .1s ease,background-color .1s ease,color .1s ease,box-shadow .1s ease,background .1s ease,-webkit-box-shadow .1s ease;
will-change: '';
-webkit-tap-highlight-color: transparent;
}
button.ui.button {
color: red;
}
<button class="ui button">Click Here</button>
When you have CSS file from semantic ui, bootstrap, angular material ui, etc, just to name a few. When you want to override the css for any element, the order in which you render or place your css file in your html determines the precedence.
For your css file to override some other css file, list yours at the bottom. Of course ensure your css selector targets the element your want to override.
An picture is worth a 1000 words
Assuming you want to override this below from semantic ui
<!-- from semantic-ui.min.css file or cdn -->
@media only screen and (min-width: 1200px) {
.ui.container {
max-width: 768px !important;
margin-left: auto !important;
margin-right: auto !important;
}
}
<!--override in my-custom-ui.css file --->
@media only screen and (min-width: 1200px) {
.ui.container {
max-width: 360px !important;
margin-left: 10px !important;
margin-right: 10px !important;
}
}
<!--this is where precedence is set, the last css file has the highest precedence-->
<!DOCTYPE html>
<html lang="en">
<head>
<title>my title</title>
<meta charset="utf-8" />
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/semantic-ui/dist/semantic.min.css"
/>
<!-- place your css file containing css overridden for semantic ui elements or other css at the bottom -->
<link rel="stylesheet" href="my-custom-ui.css" />
</head>
<body>
<header>
My header
</header>
<content>
My content
</content>
<footer>
My footer
</footer>
</body
<script/>
</html>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With