I've been learning about using CSS in React and have come across the idea of CSS modules, as part of that I came across this article https://blog.fearcat.in/a?ID=00550-af5ece9b-eb49-4e13-8711-26e00c48c84e which discusses using :global and :local in CSS files but it mentions this at one point
:local(.className)
, Which is equivalent to .className
So I understand from this article that using :global would put that CSS rule at the global level, but why use :local if it is equivalent to just having .className? there doesn't seem to be any functional difference.
By default, the CSS in CSS modules is local. But when you use :global qualifier/block, and inside that, if you want to switch back to local CSS, then you should it.
Imagine you are using global mode for CSS modules using css-loader options:
module: {
rules: [
{
test: /\.css$/i,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
// THIS IS IMPORTANT
modules: 'global'
}
}
],
}
]
}
For above configuration, consider following example:
.abc { color: red; }
:global(.abc) { color: green; }
:local .xyz { color: blue; }
:local(.xyz) .pqr { color: yellow; }
.abc :local .xyz .pqr { color: cyan; }
It would be compiled into following CSS.
/* Since it is global model for CSS modules, classname is not hashed */
.abc { color: red; }
/* .global(.abc) has no effect here */
.abc { color: green; }
/* :local .xyz means anything after :local will be hashed */
.dkzEiLgPjIar3vTOyiHv { color: blue; }
/* .local(.xyz) .pqr means only .xyz is hashed */
.dkzEiLgPjIar3vTOyiHv .pqr { color: yellow; }
/* Everything after the :local is hashed i.e. both .xyz and .pqr */
.abc .dkzEiLgPjIar3vTOyiHv .k3SKPTpkzRcircxhU4Dd { color: cyan; }
The difference between :local and :local(selector) is subtle. The former is aggressive (hashed selector after it) and latter is lenient. The same but inverse logic applies if I am using CSS Scope as local i.e. modules: true or modules: 'local'.
Now to your question, why would I use :local inside :global is when I have some component whose styles should be changed when it is inside some or top level component. Imagine, a .text should be 18px in size when it it is inside the .blogArticle. Otherwise this component should render by default as 14px:
:local .text { font-size: 14px; }
.blogArticle :local(.text) { font-size: 18px ;}
But in general, you are right. You would rarely need to put local inside global. The only reason you would be doing that is some legacy complex CSS that you are migrating to newer codebase and want to keep related blocks of CSS closer together for better readability. Last but not least, consider deep nested legacy CSS like this:
.abc :local .xyz .pqr :global(.def) {
color: cyan;
}
I start global, then switch to :local and then again to :global(.def). But needing to do this is a good case of anti-pattern/code-smell.
There is a difference when you will have the same className in multiple components.
For example you have the compoment A with <div className={classes.wrapper}> and then you have Component B with <div className={classes.wrapper}. With normal css the if the attributes in css file of those "wrapper" class will be different will overide the first one by the last one.
Is like blocked scoped in your component and dont "lives" away from the component.
And of course you can add every other attribute in global.css file that will occur in the whole project
.wrapper{ //component A
display:flex;
flex-direction:column;
}
.wrapper { //component B
display:flex;
flex-direction:column;
width:60%;
}
With normal css the last attributes will overide the first .wrapper so with modules is 100% not the same style in your div.
Related
Let’s say I have a few utility classes:
.primary-text {
color: blue;
}
.danger-text {
color: red;
}
.display-400 {
width: 400px;
}
.max-width-100 {
max-width: 100%;
}
Do classes like this require the !important keyword?
If you have some other CSS files that are loaded before this file, you have three ways to force your CSS content to load:
add !important
add your CSS file link tag at the end of another link tag
find a more accurate selector for your tag like this:
span.primary-text {
color: blue;
}
This code has higher priority.
But if you don't use any other CSS file that contains these selectors with the same properties, you don’t need to use !important.
We are using a class on the html-element to determine whether the user is in dark or light mode of the app.
<html class="dark-mode"></html>
This class is added using Renderer2 in a service that detects the selected setting of the user. This works fine so far.
Now, we have to adjust all the components to look good in the dark mode as well. But the problem is Angular's ViewEncapsulation.
What we thought would come in handy, is to simply update the SCSS of the component:
.headline {
color: black;
.dark-mode & {
color: white;
}
}
Or in plain CSS:
.headline {
color: black;
}
.dark-mode .headline {
color: white;
}
Now this doesn't work, because the class .dark-mode seems to be encapsulated, as expected.
How can we solve this problem?
:host-context provides access to css classes above the :host. By using :host-context you are able to check if any of the parents of the host have a specific css class and apply styling:
:host-context(.dark-mode) h2 {
color: white;
}
Documentation: https://angular.io/guide/component-styles#host-context
Are there any tools to compile CSS custom properties declared at not :root rule? I want following code with custom properties
.dark {
--bg-color: black;
--fg-color: white;
}
.light {
--bg-color: white;
--fg-color: black;
}
.foo {
background: var(--bg-color);
display: block;
}
.bar {
color: var(--fg-color);
display: inline;
}
be compiled to their non-custom-prop equivalents like that
.light .foo, .light.foo {
background: white;
}
.dark .foo, .dark.foo {
background: black;
}
.light .bar, .light.bar {
color: black;
}
.dark .bar, .dark.bar {
color: white;
}
.foo {
display: block;
}
.bar {
display: inline;
}
The goal is to
switch color schemes by switching dark/light class on root DOM element
use valid css syntax (no sass less)
keep rules code compact
It's actually not safe to do that. I can tell you because I tried so hard to make a safe transformation.
But I failed.
https://github.com/postcss/postcss-custom-properties/issues/1
Ideal solution. Your example is valid CSS and can be used in many browsers (not in IE, Edge (but is in development) and Opera Mini as of writing this answer, 2017-03-27, other major browsers are fine).
Suboptimal solution. Some CSS can be transpiled to achieve better browser support. The solution I found does not support variables on non-:root elements, however. There are also other objections against transpiling of 'future' CSS into 'current' CSS. To the best of my knowledge, you will have to implement your own transpiler (or postcss plugin) if you want to transpile custom properties not on the :root element, but be warned that that is hard in general. Now you don't need the general part, so it is possible. Just does, to the best of my knowledge, not exist yet.
Preprocessing solution. Of course, you don't need a general implementation of custom properties. You have different themes that have their own values for the same set of properties and that's it. Thus, a separate stylesheet can be created as a preprocessing step using any CSS preprocessor.
Now you say the following,
use valid css syntax (no sass less)
but I am going to show this anyway, because I believe that it is a valid solution to your problem. It is definitely the only one I know that actually works if you want to/need to support IE, Edge and/or older versions of other major browsers (Firefox < 31, Chrome < 49, Safari < 9.1, Opera < 36)
You could do this using SASS for example, to do the transpiling on the server side.
// define styles, use variables throughout them
// your entire style definition goes into this mixin
#mixin myStyles($fg-color, $bg-color) {
.foo {
display: block;
background: $bg-color;
}
.bar {
display: inline;
color: $fg-color;
}
}
// define themes, that set variables for the above styles
// use named arguments for clarity
.dark {
#include myStyles(
$fg-color: white,
$bg-color: black
);
}
.light {
#include myStyles(
$fg-color: black,
$bg-color: white
);
}
This compiles to the following.
.dark .foo {
display: block;
background: black;
}
.dark .bar {
display: inline;
color: white;
}
.light .foo {
display: block;
background: white;
}
.light .bar {
display: inline;
color: black;
}
This is not exactly what you want to obtain, but very close. Realistically, I think this is the closest you will get to obtaining your desired output. I know you want to
keep rules code compact
but what you are saying there (I think) is that you want to split out custom properties from their rules to save on number of rules, which is not something any preprocessor I know supports.
You can organize your source SASS in separate files to keep an overview easily. You can even set up a build system that generates a separate stylesheet for every theme you have. It is then possible to have your users select an alternative stylesheet. Browsers have some support for this, but switching using JavaScript is also definitely possible in the latter case. Simply set all stylesheets to be disabled except for the selected one. Here is an example.
So,
I have appended a home class to body like so:
document.body.classList.add("home")
I want to select appContainer a child element of body class by doing
html body.home #appContainer { ..... }
This works without CSS Modules but was wondering how I can do it with CSS modules. Thanks
You need to use wrap the class that you want to be global into :global(). If your selector uses an element you must write it directly after the element with no space in between, like element:global(.class) which translates into element.class.
Therefore, in your case html body:global(.home) #appContainer is the answer.
For anyone else that comes across this issue, I am using postcss-preset-env and I had to do this:
Worked ✅
.toolTipTest :global .rc-tooltip-arrow {
color: blue;
}
This did not work ❌
.toolTipTest:global(.rc-tooltip-arrow) {
color: blue;
}
And neither did this ❌
.toolTipTest:global(.rc-tooltip-arrow) {
color: blue;
}
// Neither Did this
.toolTipTest {
&:global(.rc-tooltip-arrow) {
color: blue;
}
}
I'm using the SASS port of Bootstrap, and I'm wondering if there's any difference between using the pre-defined mixins and using SASS's #extend.
For instance, if I have:
<div class="wrapper">
Some content here....
</div>
Is there any difference between doing
.wrapper {
#include make-row();
}
and
.wrapper {
#extend .row;
}
?
If there's no difference, are there other mixins that aren't equivalent to a single #extend statement? If there aren't such mixins, why do the mixins even exist?
The big difference between #extend and a mixin is the way the css is compiled. It doesn't look like much in simple examples, but the differences and implications are significant and can be a real headache in the wild if used carelessly. #extend is a little bit like fools gold, looks great at first, but ...
Let's look at a simple example:
#extend
.row {
width: 50px;
}
.new-row {
#extend .row;
}
.another-row {
#extend .row;
}
compiles into:
.row,
.new-row,
.another-row {
width: 50px;
}
mixin
#mixin row() {
width: 50px;
}
.new-row {
#include row();
}
.another-row {
#include row();
}
compiles into:
.new-row {
width: 50px;
}
.another-row {
width: 50px;
}
A mixin includes the properties everywhere it is hit - copying them each time - whereas an #extend groups the selectors and defines the properties once. This isn't immediately obvious, because the difference is in the compiled css but it has some important implications:
Load order
With #extend the selectors will be grouped at the first point in the sass where they are encountered which can lead to some weird over-riding. If you define a selector and use #extend to bring in a property to and try to override a property defined earlier in your sass, but after the point at which the extended properties are grouped in the css then the override will not work. This can be quite perplexing.
Consider this logically ordered set of css definitions and the likely HTML: <div class='row highlight-row'></div>:
.red-text {
color: red;
}
.row {
color: green;
}
.highlight-row {
#extend .red-text;
}
compiles into:
.red-text,
.highlight-row {
color: red;
}
.row {
color: green;
}
So even though the sass ordering makes it look like the row colour would be red, the compiled css will make it green
Poor groupings
#extend can result in poorly grouped selectors in the resulting css. You can end up with thirty or forty unrelated things all sharing the same property for example. Using #extend for fonts is a good example of this.
Nesting
If you are using deeply nested sass (which is not good, btw) and you use #extend you will duplicate the fully nested selector for every #extend you use, resulting in bloated css. I've seen this a lot:
.selector-1 .selector-2 .selector-3 .selector-4,
.selector-1 .selector-2 .selector-3 .selector-4 a,
.selector-1 .selector-2 .selector-3 .selector-4 li,
.selector-1 .selector-2 .selector-3 .selector-4 td {
font-family: arial;
}
If you're new to SASS it pays to look at the compiled css.
Media queries
#extend do not work inside media queries, because media queries are not selectors.
Conclusion
My rule of thumb is to use an #extend over a mixin if you have no parameters and if you can reasonably define the #extend and share it amongst a few tightly related selectors that exist nearby in the sass, for example, in the same file that defines a sass module. Buttons are a good example of well used #extend:
%button {
padding: 10px;
}
.call-to-action {
#extend %button;
background-color: $green;
}
.submit {
#extend %button;
background-color: $grey;
}
The best article to help make the choice is here
PS, the % sign is a use of placeholder extends