@greenfinity/rescript-typed-css-modules 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Balázs Reé <ree@greenfinity.hu>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,204 @@
1
+ # rescript-typed-css-modules
2
+
3
+ Generate type-safe ReScript bindings from CSS Modules (`.module.css` / `.module.scss`) and global CSS (`.global.css` / `.global.scss`).
4
+
5
+ Only tested with Next.js and might not work with other frameworks out of the box.
6
+
7
+ ## Features
8
+
9
+ - Extracts class names from CSS/SCSS module and global files
10
+ - Generates a typed record for all class names
11
+ - Supports `@import` rules (imported classes are included in the generated bindings)
12
+ - Also enable access to non-scoped classes (e.g. `:global()` classes)
13
+ - Supports type-safe access to global css classes (`.global.css` / `.global.scss`)
14
+ - Supports recursive directory scanning
15
+ - Watch mode for automatic regeneration on file changes
16
+ - Process multiple files or directories in a single command
17
+
18
+ ## File naming conventions
19
+
20
+ | Input file | Generated binding |
21
+ |----------------------------------|-------------------|
22
+ | `*.module.css` / `*.module.scss` | `*_CssModule.res` |
23
+ | `*.global.css` / `*.global.scss` | `*_GlobalCss.res` |
24
+
25
+ ### Why global CSS support?
26
+
27
+ The use case is to access classes in a type safe way from third party css libraries, that emit HTML markup with predefined class names without the use of css modules (e.g. React Aria Components).
28
+
29
+ Global CSS files (`.global.css` / `.global.scss`) also get type-safe bindings, but they are not imported, and their class names are **not hashed**. This is useful when working with third-party libraries (such as React Aria Components) that emit HTML markup with predefined class names that you need to style.
30
+
31
+ Even though global CSS classes aren't scoped, generating typed bindings provides the benefit of compile-time checking that class names exist.
32
+
33
+ The imports are not done because these css typically has to be imported from the top of the component hierarchy. So the import has to be done manually. To get type safe access, simply rename the file to `.global.css` (or create one and import the original css from it).
34
+
35
+ (Remark: we could also provide a way to import the css automatically, but this would open issues with removing duplicates during bundling, and handling the css on route changes. NextJs does not support these use cases out of the box, so we revert to the manual import for global css.)
36
+
37
+ ## Installation
38
+
39
+ ```bash
40
+ yarn add @greenfinity/rescript-typed-css-modules
41
+ ```
42
+
43
+ ## Usage
44
+
45
+ ```text
46
+ Usage
47
+ $ css-to-rescript <file.module.css|scss|global.css|scss...>
48
+ $ css-to-rescript <directory...>
49
+
50
+ Options
51
+ --watch, -w Watch for changes and regenerate bindings (directories only)
52
+ --skip-initial, -s Skip initial compilation in watch mode
53
+ --output-dir, -o Directory to write generated .res files
54
+ (multiple files or single directory only)
55
+
56
+ Examples
57
+ $ css-to-rescript src/Card.module.scss
58
+ $ css-to-rescript src/Theme.global.css
59
+ $ css-to-rescript src/Button.module.css src/Card.module.scss -o src/bindings
60
+ $ css-to-rescript src/components
61
+ $ css-to-rescript src/components src/pages --watch
62
+ $ css-to-rescript src/components --watch --skip-initial
63
+ ```
64
+
65
+ ## Example
66
+
67
+ Given a CSS module:
68
+
69
+ ```css
70
+ /* Button.module.css */
71
+ .btn {
72
+ padding: 0.5rem 1rem;
73
+ }
74
+
75
+ .btn-lg {
76
+ padding: 1rem 2rem;
77
+ }
78
+
79
+ .disabled {
80
+ opacity: 0.5;
81
+ }
82
+ ```
83
+
84
+ The tool generates:
85
+
86
+ ```rescript
87
+ // Button_CssModule.res
88
+ // Generated from Button.module.css
89
+ // Do not edit manually
90
+
91
+ type t = {
92
+ "btn": string,
93
+ "btn-lg": string,
94
+ "disabled": string
95
+ }
96
+ @module("./Button.module.css") external css: t = "default"
97
+ ```
98
+
99
+ ### Using the bindings
100
+
101
+ ```rescript
102
+ <button className={Button_CssModule.css["btn-lg"]}>
103
+ <button className={Button_CssModule.css["disabled"]}>
104
+ ```
105
+
106
+ You can combine multiple classes with template strings:
107
+
108
+ ```rescript
109
+ <button className={`${Button_CssModule.css["btn"]} ${Button_CssModule.css["btn-lg"]}`}>
110
+ ```
111
+
112
+ ### CSS imports
113
+
114
+ The tool supports `@import` rules. Imported classes are included in the generated bindings:
115
+
116
+ ```css
117
+ /* shared.css */
118
+ .shared-class {
119
+ color: red;
120
+ }
121
+ ```
122
+
123
+ ```css
124
+ /* WithImport.module.css */
125
+ @import "./shared.css";
126
+
127
+ .local-class {
128
+ background: blue;
129
+ }
130
+ ```
131
+
132
+ Generates:
133
+
134
+ ```rescript
135
+ // WithImport_CssModule.res
136
+ type t = {
137
+ "local-class": string,
138
+ "shared-class": string
139
+ }
140
+ @module("./WithImport.module.css") external css: t = "default"
141
+ ```
142
+
143
+ ### Global CSS
144
+
145
+ For third-party libraries that emit HTML with predefined class names (e.g. React Aria Components), you can create a `.global.css` file to get type-safe bindings without CSS Modules scoping:
146
+
147
+ ```css
148
+ /* Theme.global.css */
149
+ .dark-mode {
150
+ background: #1a1a1a;
151
+ }
152
+
153
+ .light-mode {
154
+ background: #ffffff;
155
+ }
156
+
157
+ .primary-color {
158
+ color: blue;
159
+ }
160
+ ```
161
+
162
+ Generates:
163
+
164
+ ```rescript
165
+ // Theme_CssGlobal.res
166
+ type t = {
167
+ "dark-mode": string,
168
+ "light-mode": string,
169
+ "primary-color": string
170
+ }
171
+
172
+ // Class names are returned as-is (no hashing)
173
+ let css = ...
174
+ ```
175
+
176
+ Usage:
177
+
178
+ ```rescript
179
+ // Import the CSS manually at your app root
180
+ %%raw(`import "./Theme.global.css"`)
181
+
182
+ // Then use type-safe class names anywhere
183
+ <div className={Theme_CssGlobal.css["dark-mode"]}>
184
+ ```
185
+
186
+ ## How it works
187
+
188
+ 1. Parses CSS/SCSS files using PostCSS to extract class names
189
+ 2. Generates a typed record `css` containing all class names, accessible via string keys.
190
+
191
+ ## Caveats
192
+
193
+ This tool generates bindings that assume CSS Modules are processed by a bundler with CSS Modules support. Currently tested with Next.js, which applies PostCSS module transformation automatically.
194
+
195
+ If using outside of Next.js, you may need to configure your bundler (Vite, Webpack, etc.) to handle CSS Modules scoping. This has not been tested outside of Next.js.
196
+
197
+ ## Requirements
198
+
199
+ - Node.js >= 22.12.0
200
+ - ReScript >= 12.0.0
201
+
202
+ ## License
203
+
204
+ MIT
File without changes