goodday all,
I am trying to figure out if there is a way to link .scss in a way that they share their classnames.
So here is what I am trying to achieve.
|- theme
| |- theme_default.scss
|- snippets
| |-componentA
| | |-componentA.js
| | |-componentA.scss
In the ComponentA.js file I am attaching a class to the component from ComponentA.scss using the css-loader/CommonJS (I am using a webpack dev-environment if anyone needs to know).
In the theme_default.scss I am trying to apply a theme-style to 'ComponentA'. The default theme-file should be exchangable by other theme-files.
The problem is that the class-names (eventhough identical in the source-files) are hashed when compiled into regular '.css' files, meaning the class-name from the theme cssfile will never match the class-name from the ComponentA cssfile.
So how can I make clear the theme-file should use the same class-names as the Component-file without declaring the styles as global? I tried using @import and @use in the theme-file, but it only copies the rules while the class-names are still not identical.
Any input is welcome, thanks in advance for reading.
EDIT
On request some examples
theme_default.scss
.theme_default {
.comp_a /* This class should be the same (hashed) as in the componentA.scss */ {
background-color: red; /* awefull, but just for example sake */
}
}
componentA.scss
.comp_a {
display: flex;
flex-wrap: no-wrap;
/*.. etc.*/
}
componentA.js
import style from './componentA.scss'
// this is how I set the style, but this class does NOT know about any theme stylesheet. So nothing can be done from here
element.classList.add(style.comp_a);
To apply the theme I set the theme_default class (the hashed version) to a root element and let the browser figure out how to efficiently (re)render it.
I read that the css-loader has a mechanism with compose(s), which might solve this problem. But it seemingly doesn't work with either webpack or vsCode. So in any case someone needs it, here is the snippet from the webpack config file regarding css handling.
webpack.configuration.js
{
test: /\.s[ac]ss$/i,
include: [
path.resolve(__dirname, 'src')
],
exclude: [
path.resolve(__dirname, 'node_modules'),
path.resolve(__dirname, 'build'),
path.resolve(__dirname, 'test'),
],
use: [
// Creates `style` nodes from JS strings
isDevelopment ? 'style-loader' : MiniCssExtractPlugin.loader,
// Translates CSS into CommonJS
{
loader: 'css-loader',
options:
{
modules:
{
exportGlobals: true,
},
importLoaders: 2,
sourceMap: isDevelopment,
}
},
// Transform urls
{
loader: 'resolve-url-loader',
options:
{
sourceMap: isDevelopment
}
},
// Compiles Sass to CSS
{
loader: 'sass-loader',
options:
{
sassOptions:
{
indentWidth: 4,
sourceMap: isDevelopment,
sourceMapContents: false,
outputStyle: 'expanded'
},
},
}
],
},
Theming is a little bit trickier with the use of CSS Modules, since the intent of it is to create locally-scoped CSS that isn't affected by the CSS cascade.
That being said, there are some options available to you depending on the route you'd like to take.
Because CSS Modules don't want to be affected by the cascade, it's quite difficult to change component-level CSS by making use of class names higher in the DOM tree. The CSS-Modules project does have a page on theming, but I think it's a bit of a departure from how you're attempting to implement your styles. I would encourage you to consider it, though.
Their approach is to create a themed stylesheet per component and then import them into said component, switching out which one you're going to use based on a variable. In React, this would be a prop.
dark.css
.button {
border: 1px solid #333;
}
neon.css
.button {
border: 1px solid #0f0;
}
Button.js
export default class Button {
constructor(theme) {
this.theme = theme;
}
render() {
const theme = this.theme;
return `<div class="${theme.button}">Hello World</div>`;
}
}
app.js
import darkTheme from "./dark.css";
import neonTheme from "./neon.css";
import Button from "./button";
// use component
new Button(dark);
new Button(neon);
This approach leans more into the traditional CSS Modules approach in which you'll duplicate some styles in your source, but you make up for it in reducing what you deliver to the client.
Since you're using Sass, you can cut down on the duplication and tediousness of changing things in the future by making use of Sass Mixins.
This is particularly helpful if you have a set of CSS properties that are common throughout many components that will consume them (background-color, border-color, etc), and will allow you to centralise these rules into a single file where they can be changed and propagate throughout other files.
themes.scss
@mixin neon {
background-color: #0f0;
border: 2px solid #ff0;
color: #000;
}
button/neon.scss
.button {
@include neon;
}
// Or, if you're using the latest Dart Sass and its @use rule
@use "themes";
.button {
@include themes.neon;
}
This still requires you to set up and the theme as an application variable and pass it in as an arg or prop, but should reduce the need for you to duplicate the CSS rules in every file.
An alternative path available to you, if your browser support allows it, is the use of CSS Custom Properties. This would allow you to control values inside your CSS Module generated classnames from outside.
style.css
.button {
background-color: var(--button-bg, #900); /* #900 default fallback if --button-bg doesn't exist*/
}
app.css
body {
--button-bg: #f48024;
}
body.dark {
--button-bg: #a34d08;
}
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