ember-css-modules 2.1.1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +311 -0
- package/addon/-runtime.js +1 -0
- package/docs/ORDERING.MD +51 -0
- package/docs/POSTCSS.md +89 -0
- package/docs/PREPROCESSORS.md +61 -0
- package/docs/TAILWINDCSS.md +77 -0
- package/docs/VIRTUAL_MODULES.md +41 -0
- package/index.js +19 -16
- package/lib/modules-preprocessor.js +5 -5
- package/package.json +19 -14
- package/addon/helpers/local-class.js +0 -34
- package/addon/index.js +0 -0
- package/addon/templates/static-helpers-hack.hbs +0 -1
- package/app/helpers/local-class.js +0 -1
- package/app/initializers/ensure-local-class-included.js +0 -12
- package/lib/htmlbars-plugin/index.js +0 -262
- package/lib/htmlbars-plugin/utils.js +0 -92
- package/lib/plugin/index.js +0 -29
- package/lib/plugin/registry.js +0 -100
package/README.md
ADDED
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
# ember-css-modules
|
|
2
|
+
|
|
3
|
+
> [!IMPORTANT]
|
|
4
|
+
> This package has been the core means of using CSS Modules in Ember projects for a long time, and it will continue to be maintained, but v3 is likely the final major version and this probably isn't what you want to use in new projects. See [the main README in this repository](https://github.com/salsify/ember-css-modules) for more details on the current state of CSS Modules in Ember.
|
|
5
|
+
>
|
|
6
|
+
> Ready to migrate? See the migration guides for [apps](../../docs/MIGRATING-V1-APP.md) and [addons](../../docs/MIGRATING-V1-ADDON.md).
|
|
7
|
+
|
|
8
|
+
<details>
|
|
9
|
+
<summary>Full documentation</summary>
|
|
10
|
+
|
|
11
|
+
Ember-flavored support for [CSS Modules](https://github.com/css-modules/css-modules). For an overview of some of the motivations for the CSS Modules concept, see [this blog post](http://blog.salsify.com/engineering/good-fences-with-css-modules).
|
|
12
|
+
|
|
13
|
+
- [What and Why?](#what-and-why)
|
|
14
|
+
- [Usage](#usage)
|
|
15
|
+
- [Simple Example](#simple-example)
|
|
16
|
+
- [Component Colocation Example](#component-colocation-example)
|
|
17
|
+
- [Styling Reuse](#styling-reuse)
|
|
18
|
+
- [Programmatic Styles Access](#programmatic-styles-access)
|
|
19
|
+
- [Global Classes](#global-classes)
|
|
20
|
+
- [Values](#values)
|
|
21
|
+
- [Glint usage](#glint-usage)
|
|
22
|
+
- [Usage in Addons](#usage-in-addons)
|
|
23
|
+
- [Advanced Configuration](#advanced-configuration)
|
|
24
|
+
- [Where to Specify Options](#where-to-specify-options)
|
|
25
|
+
- [Extensions in Module Paths](#extensions-in-module-paths)
|
|
26
|
+
- [Scoped Name Generation](#scoped-name-generation)
|
|
27
|
+
- [Source Maps](#source-maps)
|
|
28
|
+
- [Notes](#notes)
|
|
29
|
+
- [Ember Support](#ember-support)
|
|
30
|
+
|
|
31
|
+
## What and Why?
|
|
32
|
+
|
|
33
|
+
When you build a component, you drop a `.js` file and a `.hbs` file in your app directory, and your tooling takes care of the rest. Babel takes your fancy ES6 module and restructures it into nice browser-friendly code, while still giving you isolation and modularity guarantees. Meanwhile, the rest of the Ember CLI build pipeline picks up these new files and automatically incorporates them into your final JS artifact. And when it's time to come back and tweak your component, you (just like the Ember resolver) know exactly where those files live based just on the name of the component.
|
|
34
|
+
|
|
35
|
+
**With ember-css-modules, your styling is a first-class citizen alongside your templates and JavaScript**. You have one `.css` file per component (or route controller), in the same structure you're already using in the rest of your app. Every class you write is local to that file by default, with explicit mechanisms for opting into sharing, just like your JavaScript modules. And just like all your JS modules are automatically included in `<app-name>.js`, your CSS modules will automatically be included in `<app-name>.css`.
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
### Simple Example
|
|
40
|
+
|
|
41
|
+
With ember-css-modules, you define styles on a per-component (or -controller) basis. You define these styles using the same file layout you use for templates; for example, in pod structure you'd put `styles.css` alongside `template.hbs` in the component's pod. The classes in that stylesheet are then automatically namespaced to the corresponding component or controller. In order to reference them, you use the `local-class` attribute rather than the standard `class`.
|
|
42
|
+
|
|
43
|
+
```hbs
|
|
44
|
+
{{! app/components/my-component/template.hbs }}
|
|
45
|
+
<div local-class='hello-class'>Hello, world!</div>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
```css
|
|
49
|
+
/* app/components/my-component/styles.css */
|
|
50
|
+
.hello-class {
|
|
51
|
+
font-weight: bold;
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Similarly, if you were styling e.g. your application controller, you would place your styles alongside `controller.js` in `<podModulePrefix>/application/styles.css`.
|
|
56
|
+
|
|
57
|
+
### Component Colocation Example
|
|
58
|
+
|
|
59
|
+
In Octane apps, where component templates can be colocated with their backing class, your styles module for a component takes the same name as the backing class and template files:
|
|
60
|
+
|
|
61
|
+
```hbs
|
|
62
|
+
{{! app/components/my-component.hbs }}
|
|
63
|
+
<div local-class='hello-class'>Hello, world!</div>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
```css
|
|
67
|
+
/* app/components/my-component.css */
|
|
68
|
+
.hello-class {
|
|
69
|
+
font-weight: bold;
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Styling Reuse
|
|
74
|
+
|
|
75
|
+
In the example above, `hello-class` is rewritten internally to something like `_hello-class_1dr4n4` to ensure it doesn't conflict with a `hello-class` defined in some other module.
|
|
76
|
+
|
|
77
|
+
For cases where class reuse is desired, there's [the `composes` property](https://github.com/css-modules/css-modules#composition). Suppose you have a title in your component that you'd like to inherit your app-wide "secondary header" styling, which itself uses generic styling shared by all headers:
|
|
78
|
+
|
|
79
|
+
```css
|
|
80
|
+
/* app/styles/headers.css */
|
|
81
|
+
.header {
|
|
82
|
+
font-weight: bold;
|
|
83
|
+
text-decoration: underline;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.secondary-header {
|
|
87
|
+
composes: header;
|
|
88
|
+
color: #339;
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
```css
|
|
93
|
+
/* app/components/my-component/styles.css */
|
|
94
|
+
.component-title {
|
|
95
|
+
composes: secondary-header from 'my-app-name/styles/headers';
|
|
96
|
+
background-color: #eee;
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
In the template for `my-component`, an element with `local-class="component-title"` will end up with an actual class string like `_component-title_1dr4n4 _secondary-header_1658xu _header_1658xu`, incorporating styles from all of the composing classes.
|
|
101
|
+
|
|
102
|
+
Note that you may also use relative paths to specify the source modules for composition.
|
|
103
|
+
|
|
104
|
+
Finally, you can compose local classes from global un-namespaced ones that are provided e.g. by a CSS framework by specifying `global` as the source of the class:
|
|
105
|
+
|
|
106
|
+
```css
|
|
107
|
+
/* vendor/some-lib.css */
|
|
108
|
+
.super-important {
|
|
109
|
+
color: orange;
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
```css
|
|
114
|
+
/* app/components/my-component/styles.css */
|
|
115
|
+
.special-button {
|
|
116
|
+
composes: super-important from global;
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Programmatic Styles Access
|
|
121
|
+
|
|
122
|
+
Currently the `local-class` attribute is honored on HTML elements and component invocations, e.g. `<div local-class="foo {{bar}}">` and `{{input local-class="baz"}}`. It is not (currently) supported in subexpressions like the `(component)` helper.
|
|
123
|
+
|
|
124
|
+
If you need to access a local class in a template in other scenarios (such as passing in a class name as a property or reusing a class from another module), there is also a `local-class` helper you can use. For example, the "secondary-header" example above can be written as:
|
|
125
|
+
|
|
126
|
+
```hbs
|
|
127
|
+
{{! app/components/my-component/template.hbs }}
|
|
128
|
+
<div
|
|
129
|
+
class='{{local-class "secondary-header" from='my-app-name/styles/headers'}}'
|
|
130
|
+
>Hello, world!</div>
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Note that the `from` parameter is optional; by default classes will come from the current module, as with the `local-class` attribute.
|
|
134
|
+
|
|
135
|
+
In a JavaScript context, the class mappings can also be imported directly from whatever path the corresponding CSS module occupies, e.g.
|
|
136
|
+
|
|
137
|
+
```js
|
|
138
|
+
import styles from 'my-app-name/components/my-component/styles';
|
|
139
|
+
console.log(styles['hello-class']);
|
|
140
|
+
// => "_hello-class_1dr4n4"
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
**Note**: by default, the import path for a styles module does _not_ include the `.css` (or equivalent) extension. However, if you set `includeExtensionInModulePath: true`, then you'd instead write:
|
|
144
|
+
|
|
145
|
+
```js
|
|
146
|
+
import styles from 'my-app-name/components/my-component/styles.css';
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Note that the extension is **always** included for styles modules that are part of an Octane "colocated" component, to avoid a conflict with the import path for the component itself.
|
|
150
|
+
|
|
151
|
+
### Global Classes
|
|
152
|
+
|
|
153
|
+
Some libraries provide explicit class names as part of their public interface in order to allow customization of their look and feel. If, for example, you're wrapping such a library in a component, you need to be able to reference those unscoped class names in the context of your component styles. The `:global` pseudoselector allows for this:
|
|
154
|
+
|
|
155
|
+
```css
|
|
156
|
+
.my-component :global(.some-library-class) {
|
|
157
|
+
color: orange;
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
For more details on `:local` and `:global` exceptions, see [the CSS Modules documentation](https://github.com/css-modules/css-modules#exceptions).
|
|
162
|
+
|
|
163
|
+
### Values
|
|
164
|
+
|
|
165
|
+
For exposing data other than class names across module boundaries, you can use `@value`.
|
|
166
|
+
|
|
167
|
+
```css
|
|
168
|
+
/* app/styles/colors.css */
|
|
169
|
+
@value primary-color: #8af;
|
|
170
|
+
@value secondary-color: #fc0;
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
```css
|
|
174
|
+
/* app/some-route-pod/styles.css */
|
|
175
|
+
@value primary-color, secondary-color from 'my-app-name/styles/colors';
|
|
176
|
+
|
|
177
|
+
.blurb {
|
|
178
|
+
color: primary-color;
|
|
179
|
+
background-color: secondary-color;
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Note that values are also exposed on the `styles` object for a given module, so they are also accessible from JavaScript if you need to coordinate between the two. As a contrived example:
|
|
184
|
+
|
|
185
|
+
```js
|
|
186
|
+
// app/some-route-pod/controller.js
|
|
187
|
+
import styles from 'app/some-route-pod/styles';
|
|
188
|
+
|
|
189
|
+
export default Ember.Controller.extend({
|
|
190
|
+
logColor() {
|
|
191
|
+
console.log('primary color is', styles['primary-color']);
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Glint usage
|
|
197
|
+
|
|
198
|
+
Helper `{{local-class}}` has proper [Glint](https://github.com/typed-ember/glint) types,
|
|
199
|
+
which allow you when using TypeScript to get strict type checking in your templates.
|
|
200
|
+
|
|
201
|
+
Unless you are using [strict mode](http://emberjs.github.io/rfcs/0496-handlebars-strict-mode.html) templates
|
|
202
|
+
(via [first class component templates](http://emberjs.github.io/rfcs/0779-first-class-component-templates.html)),
|
|
203
|
+
you need to import the addon's Glint template registry and extend your app's registry declaration
|
|
204
|
+
as described in the [Using Addons](https://typed-ember.gitbook.io/glint/using-glint/ember/using-addons#using-glint-enabled-addons) documentation:
|
|
205
|
+
|
|
206
|
+
```ts
|
|
207
|
+
import '@glint/environment-ember-loose';
|
|
208
|
+
import type EmberCssModulesRegistry from 'ember-css-modules/template-registry';
|
|
209
|
+
|
|
210
|
+
declare module '@glint/environment-ember-loose/registry' {
|
|
211
|
+
export default interface Registry
|
|
212
|
+
extends EmberCssModulesRegistry /* other addon registries */ {
|
|
213
|
+
// local entries
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Usage in Addons
|
|
219
|
+
|
|
220
|
+
You can also use ember-css-modules in addons that expose components to their consuming application. To do this you'll need to move `ember-css-modules` out of `devDependencies` and into `dependencies` in your addon's `package.json` ([see issue #8](https://github.com/salsify/ember-css-modules/issues/8)).
|
|
221
|
+
|
|
222
|
+
Note also that **your addon must have an `addon/styles` directory** in order to trigger CSS processing in Ember CLI. In order for the directory to be preserved when you publish your addon, you can create an empty `.placeholder` file (`.gitkeep` won't work; by default, the `.npmignore` for your addon will prevent files with that name from being published).
|
|
223
|
+
|
|
224
|
+
## Advanced Configuration
|
|
225
|
+
|
|
226
|
+
Details about specific advanced configuration options are broken out into smaller mini-guides that each focus on a single topic:
|
|
227
|
+
|
|
228
|
+
- [Using CSS Modules with Tailwind CSS](./docs/TAILWINDCSS.md)
|
|
229
|
+
- [Using CSS Modules with other preprocessors like Sass or Less](./docs/PREPROCESSORS.md)
|
|
230
|
+
- [Working with PostCSS plugins](./docs/POSTCSS.md)
|
|
231
|
+
- [Module ordering](./docs/ORDERING.md)
|
|
232
|
+
- [Defining values at build time with virtual modules](./docs/VIRTUAL_MODULES.md)
|
|
233
|
+
|
|
234
|
+
### Where to Specify Options
|
|
235
|
+
|
|
236
|
+
For applications, custom configuration for ember-css-modules may be specified in `ember-cli-build.js`:
|
|
237
|
+
|
|
238
|
+
```js
|
|
239
|
+
new EmberApp(defaults, {
|
|
240
|
+
// ...
|
|
241
|
+
cssModules: {
|
|
242
|
+
// config
|
|
243
|
+
},
|
|
244
|
+
});
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
For addons, configuration may be specified in your addon's `index.js` instead:
|
|
248
|
+
|
|
249
|
+
```js
|
|
250
|
+
module.exports = {
|
|
251
|
+
// ...
|
|
252
|
+
options: {
|
|
253
|
+
cssModules: {
|
|
254
|
+
// config
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
};
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Extensions in Module Paths
|
|
261
|
+
|
|
262
|
+
When importing a CSS module's values from JS, or referencing it via `@value` or `composes:`, by default you do not include the `.css` extension in the import path. The exception to this rule is for modules that are part of an Octane-style colocated component, as the extension is the only thing to differentiate the styles module from the component module itself.
|
|
263
|
+
|
|
264
|
+
If you wish to enable this behavior for _all_ modules, you can set the `includeExtensionInModulePath` flag in your configuration:
|
|
265
|
+
|
|
266
|
+
```js
|
|
267
|
+
new EmberApp(defaults, {
|
|
268
|
+
cssModules: {
|
|
269
|
+
includeExtensionInModulePath: true,
|
|
270
|
+
},
|
|
271
|
+
});
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Scoped Name Generation
|
|
275
|
+
|
|
276
|
+
By default, ember-css-modules produces a unique scoped name for each class in a module by combining the original class name with a hash of the path of the containing module. You can override this behavior by passing a `generateScopedName` function in the configuration.
|
|
277
|
+
|
|
278
|
+
```js
|
|
279
|
+
new EmberApp(defaults, {
|
|
280
|
+
cssModules: {
|
|
281
|
+
generateScopedName(className, modulePath) {
|
|
282
|
+
// Your logic here
|
|
283
|
+
},
|
|
284
|
+
},
|
|
285
|
+
});
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
Note that addons may specify their own `generateScopedName` function, but otherwise they will fall back to using the one (if any) specified by the host application.
|
|
289
|
+
|
|
290
|
+
### Source Maps
|
|
291
|
+
|
|
292
|
+
Ember CLI allows you to [specify source map settings](https://ember-cli.com/user-guide/#source-maps) for your entire build process, and ember-css-modules will honor that configuration. For instance, to enable source maps in all environments for both JS and CSS files, you could put the following in your `ember-cli-build.js`:
|
|
293
|
+
|
|
294
|
+
```
|
|
295
|
+
sourcemaps: {
|
|
296
|
+
enabled: true,
|
|
297
|
+
extensions: ['js', 'css']
|
|
298
|
+
}
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
#### Notes
|
|
302
|
+
|
|
303
|
+
- You should specify the `css` extension in your source map configuration even if you're using a different extension for your modules themselves, since the final output file will be a `.css` file.
|
|
304
|
+
- Currently CSS source maps (for _any_ Ember CLI preprocessor) only work for applications, not for addons. Watch [ember-cli/broccoli-concat#58](https://github.com/ember-cli/broccoli-concat/issues/58) for progress on that front.
|
|
305
|
+
- Enabling source maps for CSS can cause Ember CLI to output an invalid comment at the end of your `vendor.css` file. This is harmless in many situations, but can cause issues with tools that postprocess your css, like ember-cli-autoprefixer. [ember-cli/broccoli-concat#58](https://github.com/ember-cli/broccoli-concat/issues/58) is the root cause of this issue as well.
|
|
306
|
+
|
|
307
|
+
## Ember Support
|
|
308
|
+
|
|
309
|
+
This addon supports Ember 3.28 and later, and is primarily for use in classic-build applications and v1 addons. See [the main README in this repository](https://github.com/salsify/ember-css-modules) for more details on more effective ways of using CSS Modules with Embroider and in v2 addons.
|
|
310
|
+
|
|
311
|
+
</details>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from 'glimmer-local-class-transform/-runtime';
|
package/docs/ORDERING.MD
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Module Ordering
|
|
2
|
+
|
|
3
|
+
All `.css` files in your `app`/`addon` directories are automatically concatenated into a single output file. Since [the ordering of rules in CSS is what breaks specificity ties](https://developer.mozilla.org/en/docs/Web/CSS/Specificity), the details of this concatenation can be important.
|
|
4
|
+
|
|
5
|
+
## Implicit Ordering
|
|
6
|
+
|
|
7
|
+
Where possible, ember-css-modules takes advantage of information it has about the dependencies between your CSS modules when making decisions about ordering. Any time, for instance, a class in one module `a` composes a class in module `b`, the contents of module `b` will be included earlier in the file output than the contents of `a`. This means you can override properties from composed classes without worrying about specificity hacks:
|
|
8
|
+
|
|
9
|
+
```css
|
|
10
|
+
/* app/styles/b.css */
|
|
11
|
+
.b {
|
|
12
|
+
color: green;
|
|
13
|
+
font-weight: bold;
|
|
14
|
+
}
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
```css
|
|
18
|
+
/* app/styles/a.css */
|
|
19
|
+
.a {
|
|
20
|
+
composes: b from './b';
|
|
21
|
+
color: orange;
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Explicit Ordering
|
|
26
|
+
|
|
27
|
+
You may also have cases where you explicitly want certain modules to be included early or late in the concatenated CSS. This may be common if, for example, you have a set of global base classes in your application.
|
|
28
|
+
|
|
29
|
+
To meet this goal, you can declare `headerModules` and `footerModules` in your ember-css-modules configuration.
|
|
30
|
+
|
|
31
|
+
```js
|
|
32
|
+
cssModules: {
|
|
33
|
+
headerModules: [
|
|
34
|
+
'my-app/styles/simple-elements',
|
|
35
|
+
'my-app/styles/typography'
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
You use the same paths when configuring `headerModules` and `footerModules` as you would with `@value` and `composes`.
|
|
41
|
+
|
|
42
|
+
#### Final Output
|
|
43
|
+
|
|
44
|
+
Given the rules above, the final ordering for the modules included in an app or addon build will look something like this:
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
<modules configured in headerModules>
|
|
48
|
+
<modules connected via composes: or @value imports>
|
|
49
|
+
<modules with no connections and no explicit ordering>
|
|
50
|
+
<modules configured in footerModules>
|
|
51
|
+
```
|
package/docs/POSTCSS.md
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# PostCSS Plugins
|
|
2
|
+
|
|
3
|
+
Since the CSS module loader is built on [PostCSS](https://github.com/postcss/postcss), your modules have access to the [full range of plugins](http://postcss.parts/) that exist.
|
|
4
|
+
|
|
5
|
+
## Simple Usage
|
|
6
|
+
|
|
7
|
+
For example, to automatically manage vendor prefixes with [Autoprefixer](https://github.com/postcss/autoprefixer):
|
|
8
|
+
|
|
9
|
+
```js
|
|
10
|
+
var autoprefixer = require('autoprefixer');
|
|
11
|
+
// ...
|
|
12
|
+
new EmberApp(defaults, {
|
|
13
|
+
cssModules: {
|
|
14
|
+
plugins: [
|
|
15
|
+
autoprefixer('last 2 versions')
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Plugin Configuration
|
|
22
|
+
|
|
23
|
+
If you're unfamiliar with PostCSS, it may not be obvious how you pass configuration to a plugin. PostCSS plugins are themselves actually functions that you call with the configuration you want to pass to them. In the Autoprefixer example above, note that we call the `autoprefixer` function with the config we want to use with it (in this case a simple string, but with many plugins this would be an object literal with any number of option keys provided).
|
|
24
|
+
|
|
25
|
+
Note: if you're coming from `ember-cli-postcss`, its nonstandard `[plugin, config]` tuple format is _not_ supported.
|
|
26
|
+
|
|
27
|
+
## Before/After PostCSS Plugins
|
|
28
|
+
|
|
29
|
+
By default, any PostCSS plugins you specify will be applied after the module transformation. To apply a set of plugins beforehand instead, you can pass a hash with `before` and `after` keys. For instance, if you wanted to use [postcss-nested](https://github.com/postcss/postcss-nested) so that you could define a set of global classes as a single block:
|
|
30
|
+
|
|
31
|
+
```js
|
|
32
|
+
new EmberApp(defaults, {
|
|
33
|
+
cssModules: {
|
|
34
|
+
plugins: {
|
|
35
|
+
before: [
|
|
36
|
+
nested
|
|
37
|
+
],
|
|
38
|
+
after: [
|
|
39
|
+
autoprefixer('last 2 versions')
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Post-Process Plugins
|
|
47
|
+
|
|
48
|
+
You can also provide a set of `postprocess` plugins that will run on the file after it has been concatenated. This is useful for plugins like `postcss-sprites` that behave better when run against a single file. The `postprocess` array will be passed through to the `plugins` option in [`broccoli-postcss`](https://github.com/jeffjewiss/broccoli-postcss#broccolipostcsstree-options); see that package for details.
|
|
49
|
+
|
|
50
|
+
```javascript
|
|
51
|
+
new EmberApp(defaults, {
|
|
52
|
+
cssModules: {
|
|
53
|
+
plugins: {
|
|
54
|
+
postprocess: [
|
|
55
|
+
require('postcss-sprites')({ /* options */ })
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Importing Third Party Files
|
|
63
|
+
|
|
64
|
+
Out of the box, ember-css-modules doesn't provide a way to to include CSS from outside the app or addon in development. Where possible, including these styles by `app.import`ing them from Bower or using a tool like [ember-cli-node-assets](https://github.com/dfreeman/ember-cli-node-assets) is a good practice, since the build pipeline will have to do less work during development, and your users will benefit from better caching in `vendor.css`.
|
|
65
|
+
|
|
66
|
+
Some styling tools, however, allow for customization as part of a build process. As a specific example, [Basscss](http://www.basscss.com/) allows you to define specific [CSS variables](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_variables) to customize its default styling. To accomplish this with ember-css-modules, you can use [postcss-import](https://github.com/postcss/postcss-import) and [postcss-css-variables](https://github.com/MadLittleMods/postcss-css-variables).
|
|
67
|
+
|
|
68
|
+
```js
|
|
69
|
+
new EmberApp(defaults, {
|
|
70
|
+
cssModules: {
|
|
71
|
+
plugins: [
|
|
72
|
+
require('postcss-import'),
|
|
73
|
+
require('postcss-css-variables')
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
```css
|
|
80
|
+
/* app/styles/third-party.css */
|
|
81
|
+
@import 'some-other-library';
|
|
82
|
+
@import 'basscss';
|
|
83
|
+
|
|
84
|
+
:root {
|
|
85
|
+
--h1: 4rem;
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Note that any plugins that run _after_ postcss-import will be applied to the imported files, which is why setting the `--h1` variable above affects the Basscss output.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Using ember-css-modules with other preprocessors
|
|
2
|
+
|
|
3
|
+
The one-step solution for using ember-css-modules in concert with another preprocessor like Sass or Less is to use a plugin like [ember-css-modules-sass](https://npmjs.com/package/ember-css-modules-sass). However, if you want to tweak the default behavior or are looking to implement your own integration with another preprocessor, the settings here will help you in that direction.
|
|
4
|
+
|
|
5
|
+
## Two Approaches
|
|
6
|
+
|
|
7
|
+
High level, there are two approaches to using ECM alongside another preprocessor.
|
|
8
|
+
|
|
9
|
+
### Modules and preprocessor syntax in isolation
|
|
10
|
+
|
|
11
|
+
The first is to have your modules be completely separate from e.g. your Sass, only combining the two at the very end of the build into a single stylesheet. In this scenario, you use vanilla CSS (possibly with some PostCSS plugins) in all your `.css` module files, and ember-css-modules doesn't touch any of your `.scss`/`.less`/etc files, leaving them to behave globally exactly as they did before.
|
|
12
|
+
|
|
13
|
+
The advantage to this approach is that it provides a clear migration path: as you convert global classes into local modularized styling, the file extension and syntax you use also changes with it, making the divide clear.
|
|
14
|
+
|
|
15
|
+
For example, with Sass you could install ember-cli-sass and then configure ember-css-modules to emit a `_modules` partial:
|
|
16
|
+
|
|
17
|
+
```js
|
|
18
|
+
cssModules: {
|
|
19
|
+
intermediateOutputPath: 'app/styles/_modules.scss'
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
And then in your `app.scss`, simply import it:
|
|
24
|
+
|
|
25
|
+
```scss
|
|
26
|
+
// other Sass code and imports
|
|
27
|
+
@import 'modules';
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Note: when specifying `intermediateOutputPath`, for an app you must include `app/styles/_modules.scss` at the front, but addon styles output is already implicitly scoped, so you would just set `intermediateOutputPath` to `_modules.scss`.
|
|
31
|
+
|
|
32
|
+
#### Custom syntax directly in modules
|
|
33
|
+
|
|
34
|
+
The second approach is viable for preprocessors for which there is a PostCSS syntax extension, such as [Sass](https://github.com/postcss/postcss-scss) and (at least partially) [Less](https://github.com/gilt/postcss-less). It allows for using custom preprocessor syntax directly in CSS modules, handing off the concatenated final output directly to the preprocessor. This is the approach taken by plugins like `ember-css-modules-sass`.
|
|
35
|
+
|
|
36
|
+
Again using Sass as an example, you would specify `app.scss` as your intermediate output file so that ember-cli-sass would pick it up directly, and then tell ember-css-modules to look for `.scss` files and pass through custom PostCSS syntax configuration.
|
|
37
|
+
|
|
38
|
+
```js
|
|
39
|
+
cssModules: {
|
|
40
|
+
// Emit a combined SCSS file for ember-cli-sass to compile
|
|
41
|
+
intermediateOutputPath: 'app/styles/app.scss',
|
|
42
|
+
|
|
43
|
+
// Use .scss as the extension for CSS modules instead of the default .css
|
|
44
|
+
extension: 'scss',
|
|
45
|
+
|
|
46
|
+
// Pass a custom parser/stringifyer through to PostCSS for processing modules
|
|
47
|
+
postcssOptions: {
|
|
48
|
+
syntax: require('postcss-scss')
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Passing Through Other Files
|
|
54
|
+
|
|
55
|
+
When you configure an `intermediateOutputPath`, ember-css-modules will automatically pass through all files with stylesheet-like extensions for subsequent processors to be able to include. If you're relying on being able to reference data from other files in a downstream processing step, you may configure the extensions of files that should be passed on.
|
|
56
|
+
|
|
57
|
+
```js
|
|
58
|
+
cssModules: {
|
|
59
|
+
passthroughFileExtensions: ['json']
|
|
60
|
+
}
|
|
61
|
+
```
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Using ember-css-modules with tailwindcss
|
|
2
|
+
|
|
3
|
+
Adapted from https://github.com/chrism/emberjs-tailwind-purgecss
|
|
4
|
+
NOTE: if you're coming from this installation be aware that you'll need to uninstall ember-cli-postcss
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
ember install ember-css-modules
|
|
10
|
+
yarn add tailwindcss postcss-import @fullhuman/postcss-purgecss -D
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Setup tailwindcss
|
|
14
|
+
```bash
|
|
15
|
+
mkdir app/tailwind
|
|
16
|
+
npx tailwind init app/tailwind/config.js --full
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Create new CSS files and import Tailwind
|
|
20
|
+
|
|
21
|
+
Create app/styles/components.css and app/styles/utilities.css then update app.css
|
|
22
|
+
|
|
23
|
+
```css
|
|
24
|
+
@import "tailwindcss/base";
|
|
25
|
+
|
|
26
|
+
@import "tailwindcss/components";
|
|
27
|
+
@import "components.css";
|
|
28
|
+
|
|
29
|
+
@import "tailwindcss/utilities";
|
|
30
|
+
@import "utilities.css";
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Update build pipeline to include plugins
|
|
34
|
+
|
|
35
|
+
```js
|
|
36
|
+
// ember-cli-build.js
|
|
37
|
+
'use strict';
|
|
38
|
+
|
|
39
|
+
const EmberApp = require('ember-cli/lib/broccoli/ember-app');
|
|
40
|
+
const isProduction = EmberApp.env() === 'production';
|
|
41
|
+
|
|
42
|
+
const purgecssOptions = {
|
|
43
|
+
content: [
|
|
44
|
+
// add extra paths here for components/controllers which include tailwind classes
|
|
45
|
+
'./app/index.html',
|
|
46
|
+
'./app/templates/**/*.hbs',
|
|
47
|
+
'./app/components/**/*.hbs'
|
|
48
|
+
],
|
|
49
|
+
defaultExtractor: content => {
|
|
50
|
+
return content.match(/[A-Za-z0-9-_:/]+/g) || []
|
|
51
|
+
},
|
|
52
|
+
// Leave css definitions starting with an underscore untouched.
|
|
53
|
+
// This prevents css definitions generated by ember-css-modules
|
|
54
|
+
// from being purged.
|
|
55
|
+
safelist: [/^_/]
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
let cssModulesPlugins = [
|
|
59
|
+
require('postcss-import')({ path: ['node_modules'] }),
|
|
60
|
+
require('tailwindcss')('./app/tailwind/config.js'),
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
if (isProduction) {
|
|
64
|
+
const purgecss = require('@fullhuman/postcss-purgecss')(purgecssOptions);
|
|
65
|
+
cssModulesPlugins.push(purgecss);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
module.exports = function(defaults) {
|
|
69
|
+
let app = new EmberApp(defaults, {
|
|
70
|
+
cssModules: {
|
|
71
|
+
plugins: cssModulesPlugins
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
return app.toTree();
|
|
76
|
+
};
|
|
77
|
+
```
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Virtual Modules
|
|
2
|
+
|
|
3
|
+
There are many scenarios where it can be useful to programmatically define constants at build time rather than hard coding them in your source. You can accomplish this in ember-css-modules by passing a `virtualModules` hash in your config.
|
|
4
|
+
|
|
5
|
+
For example, given this configuration:
|
|
6
|
+
|
|
7
|
+
```js
|
|
8
|
+
cssModules: {
|
|
9
|
+
virtualModules: {
|
|
10
|
+
'color-palette': {
|
|
11
|
+
'grass-green': '#4dbd33'
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The following import would retrieve the value `#4dbd33`:
|
|
18
|
+
|
|
19
|
+
```css
|
|
20
|
+
@value grass-green from 'color-palette';
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Virtual modules may be particularly useful for addon authors, as they provide a way to make your addon styling configurable by consumers of your addon at build time. For instance, in your `index.js` you might have something like:
|
|
24
|
+
|
|
25
|
+
```js
|
|
26
|
+
included() {
|
|
27
|
+
// ...
|
|
28
|
+
this.options = Object.assign({}, this.options, {
|
|
29
|
+
cssModules: {
|
|
30
|
+
virtualModules: {
|
|
31
|
+
'my-addon-config': {
|
|
32
|
+
'header-color': config.headerColor || 'green',
|
|
33
|
+
'header-background': config.headerBackground || 'gray'
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
this._super.included.apply(this, arguments);
|
|
39
|
+
// ...
|
|
40
|
+
}
|
|
41
|
+
```
|