You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This would allow us to have a specific and a base theme and fallback to the base theme's file if we don't override it in the other one. The import file path with the || could be generated by our theming plugin (https://github.com/andywer/postcss-theme).
We could implement it ourselves and open a PR. But do you actually like the concept?
Quick feedback would be highly appreciated :)
Thanks in advance!
The reason is that in the field you usually don't have two completely different themes, but instead a base theme and another theme that overrides some of the base theme's variables.
Currently you have to provide every file that exists in the base theme also in the other themes. It is not a deal-breaker, I think, but for real-world use cases it is more desirable to be able to only override files of the base theme whose variables you really want to change.
In SASS you would probably solve that by using !default and import the base theme's files first, other theme's files later. But in CSS modules you cannot go this way due to the missing !default and the local scope.
I am quite curious, though, if only theming would benefit from the proposed feature or if someone comes up with another use case for it as well.
Thanks for submitting the idea! I like what you're doing with postcss-theme but I don't think we should extend :import to have to deal with files not being there. It seems like a big addition of complexity, and the behaviour of "if X is present, load it, otherwise load Y" isn't something that css modules needs to be concerned with.
I wonder if you could write a webpack require resolver that understands this theming behaviour, so that if a file is not found it looks for the same file in ../../base/[name].css or something. CSS Modules doesn't care if the file it requests (../fancy-theme/colors.css) doesn't actually exist, as long as it gets its exports so it can pass through @value and composes statements.
But I think there could be a better way, that makes more sense with the postcss-theme idea. You could introduce the idea of a theme override file, like so:
Because ../base-theme/colors.css is a CSS Module you can get its list of :exports, and for every one you don't override, simply inject a pass-through @value statement, resulting in:
@value black:#303030; /* redefined */@value white from"../base-theme/colors.css"; /* passed through */
Or, if you prefer, skip the @value and go straight to ICSS:
Yeah, it would be great to have postcss as loader-agnostic as possible (actually I am in JSPM/System.js land right now). I understand that changing :import behavior is a major thing and shouldn't be done unless really necessary.
Regarding the @theme-override/postcss-extend-module idea: Is it possible at all to enumerate all exports of a css module at the time when the plugins are run?
The underlying css loader can surely do this, but I have some serious doubts a plugin could do it, since when the plugin is run the base file (whose values we are going to override) is probably not yet loaded.
Yeah I didn't mean webpack-specific, just loader-specific & project-specific. Either/or path rules seem like something you'd attach at the loader level, not a core language feature.
As for the plugins actually enumerating their dependencies, you're right, that's a bit tricky. At the moment almost all the plugins for CSS Modules can run on their own file, before any imports are resolved. We could find a place for the plugin the way you describe, but I have an issue:
What color does .foo get? You've implicitly imported white from base but because this isn't Sass, you don't use variables like $white so you don't even know it's been redefined. This is one of the core concepts of CSS Modules, you shouldn't have magic at a distance, you need to import everything you depend on and export everything explicitly.
So, I think the way you're proposing, where you load theme.css if it's present and load base.css if it's not, is better than an extend-module directive. But I am reluctant to include it in core CSS Modules. In JSPM land, you have the locate function for rewriting paths to resources. if you could inject the logic there, would that be sufficient?
One idea I've been playing with as an answer to theming is: what if we treated a css module's @values as inputs? So you can import a parameterized instance of a css module.
Eg.
/* shared/rounded-box.css */
@value radius 10px; /* this is the default */
.box {
border-radius: radius;
}
/* my-component.css */
@module box from "./shared/rounded-box.css" with {
radius: 22px;
}
.root {
composes: box;
}
Any thoughts on this? Would mean a few extra bits of syntax, as well as treating composed modules as instances rather than globals. But I think it would work nicely as a theming solution, even for publishing themeable modules to npm.
@geelen Yeah, I completely agree. I will just extend the loader. Maybe it makes sense to not change the loader itself, but create a new loader that inherits from the jspm-loader-css and provides the additional logic.
@joshwnj Interesting concept. I personally like it, but it is in fact hard to see that the @values can be overridden if the comment is missing ;) Maybe add a @param directive which is an alias for @value, but only those @param can be overridden from the outside...
Activity
ai commentedon Apr 13, 2016
I like the concept of postcss-theme — really great solution. But why postcss-theme also need this changes?
andywer commentedon Apr 13, 2016
The reason is that in the field you usually don't have two completely different themes, but instead a base theme and another theme that overrides some of the base theme's variables.
Currently you have to provide every file that exists in the base theme also in the other themes. It is not a deal-breaker, I think, but for real-world use cases it is more desirable to be able to only override files of the base theme whose variables you really want to change.
In SASS you would probably solve that by using
!defaultand import the base theme's files first, other theme's files later. But in CSS modules you cannot go this way due to the missing!defaultand the local scope.I am quite curious, though, if only theming would benefit from the proposed feature or if someone comes up with another use case for it as well.
geelen commentedon Apr 14, 2016
Thanks for submitting the idea! I like what you're doing with
postcss-themebut I don't think we should extend:importto have to deal with files not being there. It seems like a big addition of complexity, and the behaviour of "if X is present, load it, otherwise load Y" isn't something that css modules needs to be concerned with.I wonder if you could write a webpack require resolver that understands this theming behaviour, so that if a file is not found it looks for the same file in
../../base/[name].cssor something. CSS Modules doesn't care if the file it requests (../fancy-theme/colors.css) doesn't actually exist, as long as it gets its exports so it can pass through@valueandcomposesstatements.But I think there could be a better way, that makes more sense with the
postcss-themeidea. You could introduce the idea of a theme override file, like so:Because
../base-theme/colors.cssis a CSS Module you can get its list of:exports, and for every one you don't override, simply inject a pass-through@valuestatement, resulting in:Or, if you prefer, skip the
@valueand go straight to ICSS:Would that be sufficient for your use case?
ai commentedon Apr 14, 2016
I think CSS Modules should not be focused on webpack. We have Rollup and many other good things.
And because theming is a one if of the biggest question for components, we definitely should have good solution for it.
But this
:importreally looks too tricky and complicated. I think we should try to make it more elegant.ai commentedon Apr 14, 2016
Why we can't load main component theme to our custom theme and export again all unchanged variables?
We can make some
postcss-extend-moduleplugin for it.andywer commentedon Apr 14, 2016
Yeah, it would be great to have postcss as loader-agnostic as possible (actually I am in JSPM/System.js land right now). I understand that changing
:importbehavior is a major thing and shouldn't be done unless really necessary.Regarding the
@theme-override/postcss-extend-moduleidea: Is it possible at all to enumerate all exports of a css module at the time when the plugins are run?The underlying css loader can surely do this, but I have some serious doubts a plugin could do it, since when the plugin is run the base file (whose values we are going to override) is probably not yet loaded.
ai commentedon Apr 14, 2016
@andywer it is a question for @geelen but at least you can always take file AST and fine all necessary at-rules by hands.
geelen commentedon Apr 14, 2016
Yeah I didn't mean webpack-specific, just loader-specific & project-specific. Either/or path rules seem like something you'd attach at the loader level, not a core language feature.
As for the plugins actually enumerating their dependencies, you're right, that's a bit tricky. At the moment almost all the plugins for CSS Modules can run on their own file, before any imports are resolved. We could find a place for the plugin the way you describe, but I have an issue:
What
colordoes.fooget? You've implicitly importedwhitefrombasebut because this isn't Sass, you don't use variables like$whiteso you don't even know it's been redefined. This is one of the core concepts of CSS Modules, you shouldn't have magic at a distance, you need to import everything you depend on and export everything explicitly.So, I think the way you're proposing, where you load
theme.cssif it's present and loadbase.cssif it's not, is better than anextend-moduledirective. But I am reluctant to include it in core CSS Modules. In JSPM land, you have the locate function for rewriting paths to resources. if you could inject the logic there, would that be sufficient?joshwnj commentedon Apr 14, 2016
One idea I've been playing with as an answer to theming is: what if we treated a css module's
@values as inputs? So you can import a parameterized instance of a css module.Eg.
Any thoughts on this? Would mean a few extra bits of syntax, as well as treating composed modules as instances rather than globals. But I think it would work nicely as a theming solution, even for publishing themeable modules to npm.
andywer commentedon Apr 15, 2016
@geelen Yeah, I completely agree. I will just extend the loader. Maybe it makes sense to not change the loader itself, but create a new loader that inherits from the
jspm-loader-cssand provides the additional logic.@joshwnj Interesting concept. I personally like it, but it is in fact hard to see that the
@values can be overridden if the comment is missing ;) Maybe add a@paramdirective which is an alias for @value, but only those@paramcan be overridden from the outside...Is there yet any way to pass data from one module into another?
PS: Would be cool if it would also work with https://github.com/postcss/postcss-mixins