type-css-modules
v2.0.6
Published
Generate declaration files for CSS modules
Readme
type-css-modules
Generate declaration files for CSS modules
What is it?
The package provides accurate types for CSS module files. It's designed with Ember projects in mind, but can be used with any JavaScript framework and build tool.
Installation
pnpm add -D type-css-modulesEnsure that CSS declaration files exist before types are checked. For example, you can write a pre-script.
/* package.json */
{
"scripts": {
"prelint:types": "type-css-modules <arguments>",
"lint:types": "ember-tsc --noEmit" // or "glint"
},
"devDependencies": {
"type-css-modules": "...",
"typescript": "..."
}
}Arguments
You must pass --src to indicate the location of your CSS module files.
type-css-modules --src appYou can pass multiple values or use glob patterns to specify multiple locations.
type-css-modules --src app/components app/templates
type-css-modules --src "app/{components,controllers,templates}"Pass --root to run the codemod somewhere else (i.e. not in the current directory).
type-css-modules --root <path/to/your/project>Use Prettier?
type-css-modules uses quotation marks in declaration files so that we can always use class selector names as object keys.
On the other hand, prettier removes quotation marks when it deems unnecessary. To separate formatting concerns, you can pass the option quoteProps: 'preserve' for *.css.d.ts files:
/* prettier.config.mjs */
export default {
overrides: [
{
files: '*.css.d.ts',
options: {
quoteProps: 'preserve',
},
},
],
};Alternatively, you can use .prettierignore to ignore *.css.d.ts files.
Can I use the file extension *.module.css?
Yes! You may use *.module.css to indicate the stylesheets that are for CSS modules. type-css-modules will create declaration files with the extension *.module.css.d.ts.
The Prettier configuration (shown above) can remain as is.
Limitations
To reduce complexity, type-css-modules expects you to follow the conventions of embroider-css-modules:
- Give the local scope to the styles that you own1
- Avoid nesting styles2
- Use the default import to import styles
Here are some examples that meet the syntax requirements.
/* app/components/ui/page.css */
.container {
display: grid;
grid-template-areas:
"header"
"body";
grid-template-columns: 1fr;
grid-template-rows: auto 1fr;
height: calc(100% - 3em);
overflow-y: auto;
padding: 1.5rem 1rem;
scrollbar-gutter: stable;
}
.header {
grid-area: header;
}
.body {
grid-area: body;
}{{! app/components/ui/page.hbs }}
<div class={{local this.styles "container"}}>
<h1 class={{this.styles.header}}>
{{@title}}
</h1>
<div class="{{this.styles.body}}">
{{yield}}
</div>
</div>/* app/components/ui/page.ts */
import Component from '@glimmer/component';
import styles from './page.css';
export default class UiPageComponent extends Component {
styles = styles;
}/* app/components/ui/page.gts */
import { local } from 'embroider-css-modules';
import styles from './page.css';
<template>
<div class={{local styles "container"}}>
<h1 class={{styles.header}}>
{{@title}}
</h1>
<div class="{{styles.body}}">
{{yield}}
</div>
</div>
</template>And some counterexamples (what not to do):
/* app/components/ui/page.css */
:local(.container) {
display: grid;
grid-template-areas:
"header"
"body";
grid-template-columns: 1fr;
grid-template-rows: auto 1fr;
height: calc(100% - 3em);
overflow-y: auto;
padding: 1.5rem 1rem;
scrollbar-gutter: stable;
}
:local(.header) {
grid-area: header;
}
:local(.body) {
grid-area: body;
}/* app/components/ui/page.css */
.container {
display: grid;
grid-template-areas:
"header"
"body";
grid-template-columns: 1fr;
grid-template-rows: auto 1fr;
height: calc(100% - 3em);
overflow-y: auto;
padding: 1.5rem 1rem;
scrollbar-gutter: stable;
.header {
grid-area: header;
}
.body {
grid-area: body;
}
}/* app/components/ui/page.gts */
import { container, header, body } from './page.css';
<template>
<div class={{container}}>
<h1 class={{header}}>
{{@title}}
</h1>
<div class="{{body}}">
{{yield}}
</div>
</div>
</template>1. With webpack, for example, you can configure mode to be a function that returns 'local' or 'global'. In stylesheets, you can use the :global() pseudo-class selector to refer to "things from outside."
2. CSS nesting is in spec. To reduce maintenance cost, type-css-modules will leave it up to css-tree to parse nested styles (see issue #210).
Compatibility
- Node.js v20 or above
Contributing
See the Contributing guide for details.
License
This project is licensed under the MIT License.
