@mochi-css/vanilla 0.0.3 → 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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Mochi-CSS/vanilla
2
2
 
3
- This package is part of [Mochi-CSS project](https://github.com/Niikelion/mochi-css) that provides styling functions and type definitions.
3
+ This package is part of the [Mochi-CSS project](https://github.com/Niikelion/mochi-css). It provides type-safe CSS-in-JS styling functions with static extraction support, allowing you to write styles in TypeScript that get extracted to plain CSS at build time.
4
4
 
5
5
  ## Functions
6
6
 
@@ -9,15 +9,16 @@ This package is part of [Mochi-CSS project](https://github.com/Niikelion/mochi-c
9
9
  `css` is the fundamental styling function of Mochi-CSS.
10
10
  It takes style definitions as parameters, marks them for static extraction and returns class names to apply on elements.
11
11
 
12
- ```ts
12
+ ```tsx
13
13
  import {css} from "@mochi-css/vanilla"
14
14
 
15
15
  const buttonStyles = css({
16
16
  borderRadius: 10,
17
- border: "10px solid red"
17
+ border: "2px solid red"
18
18
  })
19
19
 
20
- console.log(`${buttonStyles}`)
20
+ // Use in JSX
21
+ <button className={buttonStyles}>Click me</button>
21
22
  ```
22
23
 
23
24
  Output of the `css` function is also a valid style definition, so you can split your styles:
@@ -29,38 +30,250 @@ const textStyles = css({
29
30
 
30
31
  const buttonStyles = css(textStyles, {
31
32
  borderRadius: 10,
32
- border: "10px solid red"
33
+ border: "2px solid red"
33
34
  })
34
35
  ```
35
36
 
36
37
  ### `styled(component, ...styles)`
37
38
 
38
- `styled` is just a simple wrapper around `css` that takes a component as the first argument and applies all the classnames for you.
39
+ `styled` creates a styled component by combining a base element or component with style definitions. It automatically applies the generated class names and forwards variant props.
39
40
 
40
41
  ```tsx
41
42
  import {styled} from "@mochi-css/vanilla"
42
43
 
43
44
  const Button = styled("button", {
44
45
  borderRadius: 10,
45
- border: "10px solid red"
46
+ border: "2px solid red"
46
47
  })
47
48
  ```
48
49
 
49
50
  ## Style definitions
50
51
 
51
- Now, that we know how to use our style definitions, we probably should learn how to write them, right?
52
- Style definition is either bundle of styles returned by `css` or object containing:
52
+ A style definition is either a bundle of styles returned by `css`, or an object containing:
53
53
 
54
- * any number of valid css properties converted to camelCase, like in reacts styles property
55
- * any number of css variable assignments, more on that later
54
+ * any number of valid CSS properties converted to camelCase, like in React's style property
55
+ * any number of CSS variable assignments (see [Tokens](#tokens))
56
56
  * optional variants definition
57
57
  * optional default variants definition
58
58
 
59
- In the future, nested css selectors and media queries will be available as parts of style definitions
59
+ ## Nested Selectors
60
+
61
+ Mochi-CSS supports nested selectors, allowing you to define styles for child elements, pseudo-classes, and pseudo-elements directly within your style definitions.
62
+
63
+ The `&` character represents the parent selector and must be included in every nested selector:
64
+
65
+ ```ts
66
+ import { css } from "@mochi-css/vanilla"
67
+
68
+ const buttonStyle = css({
69
+ backgroundColor: "blue",
70
+ color: "white",
71
+
72
+ // Pseudo-classes
73
+ "&:hover": {
74
+ backgroundColor: "darkblue"
75
+ },
76
+ "&:active": {
77
+ backgroundColor: "navy"
78
+ },
79
+ "&:disabled": {
80
+ backgroundColor: "gray",
81
+ cursor: "not-allowed"
82
+ },
83
+
84
+ // Pseudo-elements
85
+ "&::before": {
86
+ content: '""',
87
+ display: "block"
88
+ },
89
+
90
+ // Child selectors
91
+ "& > span": {
92
+ fontWeight: "bold"
93
+ },
94
+
95
+ // Descendant selectors
96
+ "& p": {
97
+ margin: 0
98
+ },
99
+
100
+ // Compound selectors (when element also has another class)
101
+ "&.active": {
102
+ borderColor: "green"
103
+ },
104
+
105
+ // Adjacent sibling
106
+ "& + &": {
107
+ marginTop: 8
108
+ }
109
+ })
110
+ ```
111
+
112
+ ### Selector Position
113
+
114
+ The `&` can appear anywhere in the selector, allowing for flexible parent-child relationships:
115
+
116
+ ```ts
117
+ const linkStyle = css({
118
+ color: "blue",
119
+
120
+ // Parent context: style this element when inside .dark-mode
121
+ ".dark-mode &": {
122
+ color: "lightblue"
123
+ },
124
+
125
+ // Multiple parent contexts
126
+ "nav &, footer &": {
127
+ textDecoration: "underline"
128
+ }
129
+ })
130
+ ```
131
+
132
+ ## Media Selectors
133
+
134
+ Media selectors allow you to apply styles conditionally based on viewport size, color scheme preferences, and other media features. Media selectors start with the `@` symbol and are compiled into CSS media queries:
135
+
136
+ ```ts
137
+ import { css } from "@mochi-css/vanilla"
138
+
139
+ const responsiveContainer = css({
140
+ display: "flex",
141
+ flexDirection: "row",
142
+ padding: 32,
143
+
144
+ // Responsive breakpoints
145
+ "@max-width: 768px": {
146
+ flexDirection: "column",
147
+ padding: 16
148
+ },
149
+
150
+ "@max-width: 480px": {
151
+ padding: 8
152
+ },
153
+
154
+ // Modern range syntax
155
+ "@width <= 1024px": {
156
+ gap: 16
157
+ },
158
+
159
+ // Color scheme preferences
160
+ "@prefers-color-scheme: dark": {
161
+ backgroundColor: "#1a1a1a",
162
+ color: "white"
163
+ },
164
+
165
+ // Reduced motion
166
+ "@prefers-reduced-motion: reduce": {
167
+ transition: "none"
168
+ }
169
+ })
170
+ ```
171
+
172
+ ### Combined Media Selectors
173
+
174
+ You can combine multiple media conditions using `and` and `not` operators:
175
+
176
+ ```ts
177
+ const responsiveLayout = css({
178
+ display: "grid",
179
+ gridTemplateColumns: "1fr",
180
+
181
+ // Screen media type with width condition
182
+ "@screen and (width > 1000px)": {
183
+ gridTemplateColumns: "1fr 1fr"
184
+ },
185
+
186
+ // Multiple conditions with 'and'
187
+ "@screen and (min-width: 768px) and (max-width: 1024px)": {
188
+ gridTemplateColumns: "1fr 1fr",
189
+ gap: 16
190
+ },
191
+
192
+ // Print styles
193
+ "@print": {
194
+ display: "block"
195
+ },
196
+
197
+ // Combining media type with preference
198
+ "@screen and (not (prefers-color-scheme: dark))": {
199
+ backgroundColor: "#121212"
200
+ }
201
+ })
202
+ ```
203
+
204
+ ### Combining Nested Selectors with Media Selectors
205
+
206
+ Nested selectors and media selectors can be combined for fine-grained control:
207
+
208
+ ```ts
209
+ const buttonStyle = css({
210
+ backgroundColor: "blue",
211
+
212
+ "&:hover": {
213
+ backgroundColor: "darkblue",
214
+
215
+ // Media selector inside nested selector
216
+ "@width <= 480px": {
217
+ // Disable hover effects on mobile (touch devices)
218
+ backgroundColor: "blue"
219
+ }
220
+ },
221
+
222
+ // Media selector with nested selectors inside
223
+ "@max-width: 768px": {
224
+ padding: 8,
225
+
226
+ "& > span": {
227
+ display: "none"
228
+ }
229
+ }
230
+ })
231
+ ```
232
+
233
+ ### Using with Variants
234
+
235
+ Nested selectors and media selectors work seamlessly inside variant definitions:
236
+
237
+ ```ts
238
+ const cardStyle = css({
239
+ padding: 16,
240
+ borderRadius: 8,
241
+
242
+ variants: {
243
+ size: {
244
+ small: {
245
+ padding: 8,
246
+ "@max-width: 480px": {
247
+ padding: 4
248
+ }
249
+ },
250
+ large: {
251
+ padding: 32,
252
+ "@max-width: 480px": {
253
+ padding: 16
254
+ }
255
+ }
256
+ },
257
+ interactive: {
258
+ true: {
259
+ cursor: "pointer",
260
+ "&:hover": {
261
+ transform: "translateY(-2px)"
262
+ }
263
+ },
264
+ false: {}
265
+ }
266
+ },
267
+ defaultVariants: {
268
+ size: "small",
269
+ interactive: false
270
+ }
271
+ })
272
+ ```
60
273
 
61
274
  ## Variants
62
275
 
63
- You may want to create many variants of a button, with a shared set of css:
276
+ You may want to create multiple variants of a button that share a common base style:
64
277
 
65
278
  ```ts
66
279
  import {css} from "@mochi-css/vanilla"
@@ -76,8 +289,8 @@ const redButtonStyle = css(baseButtonStyle, {
76
289
  })
77
290
  ```
78
291
 
79
- This works, but now, either you have to wrap your component manually and decide which style to apply or use different components.
80
- To make the code simpler and cleaner, Mochi-CSS allows you to specify variants inside the style definition, and even handles variant params for you!
292
+ This works, but requires you to either manually select which style to apply in your component logic, or create separate components for each variant.
293
+ Mochi-CSS allows you to define variants directly in your style definition and automatically generates the corresponding props for your component.
81
294
 
82
295
  ```tsx
83
296
  import {styled, css} from "@mochi-css/vanilla"
@@ -109,12 +322,13 @@ const SomeComponent = () => <div>
109
322
  </div>
110
323
  ```
111
324
 
112
- `defaultVariants` is optional, but we recommend that you specify defaults for all the variants.
325
+ `defaultVariants` is optional, but specifying defaults for all variants ensures predictable styling when variant props are omitted.
113
326
 
114
327
  ## Tokens
115
328
 
116
- To help with type safety, Mochi-CSS provides typed wrappers around concept of css variables.
117
- Variables can be created using `createToken<T>(name)` function:
329
+ Mochi-CSS provides typed wrappers around CSS variables to help with type safety. Tokens ensure that only valid values are assigned to your CSS variables.
330
+
331
+ Create tokens using the `createToken<T>(name)` function, where `T` is a CSS value type like `CssColorLike`, `CssLengthLike`, or `string`:
118
332
 
119
333
  ```ts
120
334
  import {createToken, css, CssColorLike} from "@mochi-css/vanilla"
@@ -128,10 +342,10 @@ const buttonStyle = css({
128
342
  variants: {
129
343
  variant: {
130
344
  primary: {
131
- [buttonColor]: primaryColor
345
+ [buttonColor.variable]: primaryColor
132
346
  },
133
347
  secondary: {
134
- [buttonColor]: secondaryColor
348
+ [buttonColor.variable]: secondaryColor
135
349
  }
136
350
  }
137
351
  },
@@ -141,4 +355,6 @@ const buttonStyle = css({
141
355
  })
142
356
  ```
143
357
 
144
- As you can see, tokens can be used as css values and as keys in style definition objects.
358
+ Tokens can be used in two ways:
359
+ - **As values**: Use the token directly (e.g., `backgroundColor: buttonColor`) to reference the CSS variable
360
+ - **As keys**: Use `token.variable` (e.g., `[buttonColor.variable]: primaryColor`) to assign a value to the CSS variable