glimmer-scoped-css
is an Ember addon that lets you embed <style>
tags in component templates that will be scoped to only apply within those components:
If you have app/components/something.hbs
:
<style>
p {
color: blue;
}
</style>
<h1>An h1</h1>
<p>A p.</p>
you get this generated HTML:
<h1 data-scopedcss-58ccb4dfe0-e9125e9996>An h1</h1>
<p data-scopedcss-58ccb4dfe0-e9125e9996>A p.</p>
and this generated CSS:
p[data-scopedcss-58ccb4dfe0-e9125e9996] {
color: blue;
}
Nested components only have the parent component’s styles on elements with ...attributes
. You can see this in action in test-app
.
The implementation is adapted from a Vue PostCSS plugin. It also supports these pseudo-elements:
:global
If you want to use CSS in your component but want a selector to not be scoped, you can use :global
:
:global(.red) {
color: red;
}
The generated CSS will look like this:
.red {
color: red;
}
:deep
Using :deep
on a selector will attach the scoping attribute to the element selector before it.
.a :deep(.b) {
color: pink;
}
The generated CSS will look like this:
.a[data-scopedcss-3afb00313e] .b {
color: pink;
}
This is a pre-1.0 release with several limitations:
@embroider/webpack
style
element in index.html
, not linkedunexpectedly found "<style>\n p { color: blue" when slicing source, but expected "data-scopedcss-53259f1da9-58ccb4dfe0"
ember install glimmer-scoped-css
Include in ember-cli-build.js
:
const { Webpack } = require('@embroider/webpack');
+const { GlimmerScopedCSSWebpackPlugin } = require('glimmer-scoped-css/webpack');
return require('@embroider/compat').compatBuild(app, Webpack, {
+ packagerOptions: {
+ webpackConfig: {
+ plugins: [new GlimmerScopedCSSWebpackPlugin()],
+ },
+ },
});
Add an in-repo addon to install the Handlebars preprocessor:
In package.json
:
"ember": {
"edition": "octane"
+},
+"ember-addon": {
+ "paths": [
+ "lib/setup-ast-transforms"
+ ]
}
Add lib/setup-ast-transforms/package.json
:
{
"name": "setup-ast-transforms",
"keywords": [
"ember-addon"
],
"dependencies": {
"glimmer-scoped-css": "*"
}
}
Add lib/setup-ast-transforms/index.js
:
'use strict';
const { installScopedCSS } = require('glimmer-scoped-css');
module.exports = {
name: require('./package').name,
setupPreprocessorRegistry(type, registry) {
if (type === 'parent') {
installScopedCSS(registry);
}
},
};
Install the preprocessor directly in the addon’s index.js
:
'use strict'
+const { installScopedCSS } = require('glimmer-scoped-css');
module.exports = {
name: require('./package').name,
isDevelopingAddon() {
return true;
},
+ setupPreprocessorRegistry(type, registry) {
+ if (type === 'self') {
+ installScopedCSS(registry);
+ }
+ },
};
Add a top-level <style>
element in your component .hbs
file and it will be scoped to elements in that component only. It also works in <template>
in .gjs
/.gts
files.
Nested <style>
elements cannot be processed for scoping. Use <style unscoped>
if you need a nested element, it will not receive scoping attributes and will be passed through to output without the unscoped
attribute.
glimmer-scoped-css consists of two parts. The first part is an AST transform that takes as input your <style>
tags inside handlebars and emits as output specially crafted import statements that account for that scoped CSS.
The second part is a plugin for your current environment (by default, webpack) that satisfies the specially-crafted import statements by turning them into CSS. To implement a new plugin, you should use import { isScopedCSSRequest, decodeScopedCSSRequest } from 'glimmer-scoped-css'
to identify these imports and turn them back into CSS, respectively.
node/no-missing-require
lint errorsError: 6:51 error "glimmer-scoped-css/webpack" is not found node/no-missing-require
The eslint-plugin-node
package that produces this error doesn’t understand the exports
structure supported by newer Node versions and is unmaintained. Ember CLI has moved to using eslint-plugin-n
as a drop-in replacement as of 4.10.
Changing to eslint-plugin-n
and updating the lint configuration fixes these errors.
See the Contributing guide for details.
This project is licensed under the MIT License.