@stylix/core 6.1.1 → 6.3.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.
@@ -0,0 +1,291 @@
1
+ # Performance Guide
2
+
3
+ Stylix is a runtime CSS-in-JS library. This means styles are generated and injected into the DOM at runtime, rather than at build time. For most applications, this has negligible performance impact. However, understanding how Stylix works can help you avoid patterns that cause unnecessary work.
4
+
5
+ ---
6
+
7
+ ## How Stylix Works
8
+
9
+ When a Stylix component renders:
10
+
11
+ 1. **Style props are collected** and combined with any `$css` prop
12
+ 2. **Styles are normalized** ("preprocessing" - minimal; falsy values are removed)
13
+ 3. **A cache key is created** by serializing the style object
14
+ 4. **If the key is new**, a className is generated, plugins process the styles into CSS (resolving media keywords, adding units, etc.), and the rules are stored
15
+ 5. **If the key exists**, the existing className is reused (no new CSS generated)
16
+ 6. **Reference counting** tracks how many components use each style
17
+ 7. **The stylesheet is updated** via `useInsertionEffect`, batching changes efficiently
18
+
19
+ This means:
20
+ - **Identical styles share a class name** — rendering 100 `<$.div color="red" />` elements creates only one CSS rule
21
+ - **Style generation is cached** — the same styles produce the same class name
22
+ - **Unused styles are cleaned up** — when no components reference a style, it's removed
23
+
24
+ ---
25
+
26
+ ## Do's
27
+
28
+ ### Do: Keep Style Objects Stable
29
+
30
+ For extra high performance (100+ reuses of a component), define style objects outside the component:
31
+
32
+ ```tsx
33
+ // Good: Object reference is stable
34
+ const cardStyles = {
35
+ padding: 16,
36
+ borderRadius: 8,
37
+ '&:hover': { boxShadow: '0 4px 12px rgba(0,0,0,0.1)' }
38
+ };
39
+
40
+ function Card({ children }) {
41
+ return <$.div $css={cardStyles}>{children}</$.div>;
42
+ }
43
+ ```
44
+
45
+ Even if styles are only simple CSS properties, using `$css` with a stable object reference avoids unnecessary hashing work.
46
+
47
+ ### Do: Define Media Config at App Root
48
+
49
+ Define breakpoints once, not in every component:
50
+
51
+ ```tsx
52
+ // Good: Single definition at root
53
+ <StylixProvider
54
+ media={{
55
+ mobile: styles => ({ '@media (max-width: 767px)': styles }),
56
+ tablet: styles => ({ '@media (max-width: 1023px)': styles }),
57
+ }}
58
+ >
59
+ <App />
60
+ </StylixProvider>
61
+ ```
62
+
63
+ ### Do: Use CSS Transitions for Animations
64
+
65
+ For property changes, CSS transitions are more efficient than re-rendering:
66
+
67
+ ```tsx
68
+ // Good: Browser handles the animation
69
+ <$.div
70
+ opacity={isVisible ? 1 : 0}
71
+ transform={isVisible ? 'translateY(0)' : 'translateY(-10px)'}
72
+ transition="opacity 0.3s, transform 0.3s"
73
+ />
74
+ ```
75
+
76
+ ### Do: Use `useKeyframes` for Complex Animations
77
+
78
+ Keyframes are hoisted and deduplicated automatically:
79
+
80
+ ```tsx
81
+ const fadeIn = useKeyframes({
82
+ from: { opacity: 0 },
83
+ to: { opacity: 1 }
84
+ });
85
+
86
+ // The keyframes are only injected once, even if many components use them
87
+ <$.div animation={`${fadeIn} 0.3s ease-out`} />
88
+ ```
89
+
90
+ ---
91
+
92
+ ## Don'ts
93
+
94
+ ### Don't: Create New Style Objects Every Render
95
+
96
+ For extra high performance (100+ reuses of a component), don't use inline style objects, which are recreated on every render:
97
+
98
+ ```tsx
99
+ // Avoid: New object every render
100
+ function Card({ children }) {
101
+ return (
102
+ <$.div
103
+ $css={{ // New object reference every render
104
+ padding: 16,
105
+ '&:hover': { boxShadow: '...' }
106
+ }}
107
+ >
108
+ {children}
109
+ </$.div>
110
+ );
111
+ }
112
+ ```
113
+
114
+ **Why it matters:** While Stylix caches styles by content (so the CSS won't be regenerated), it still has to hash and compare the object each render. For frequently-rendered components, this adds up.
115
+
116
+ **Fix:** Extract the styles or memoize them:
117
+
118
+ ```tsx
119
+ // Better: Stable reference
120
+ const cardStyles = {
121
+ padding: 16,
122
+ '&:hover': { boxShadow: '...' }
123
+ };
124
+
125
+ function Card({ children }) {
126
+ return <$.div $css={cardStyles}>{children}</$.div>;
127
+ }
128
+ ```
129
+
130
+ ### Don't: Generate Many Unique Style Combinations Dynamically
131
+
132
+ ```tsx
133
+ // Avoid: Potentially thousands of unique styles
134
+ function DataCell({ value }) {
135
+ return (
136
+ <$.td
137
+ background={`hsl(${value * 3.6}, 70%, 50%)`} // Unique for each value 0-100
138
+ />
139
+ );
140
+ }
141
+
142
+ // Used in a large table:
143
+ {data.map((row, i) => (
144
+ <tr key={i}>
145
+ {row.map((value, j) => (
146
+ <DataCell key={j} value={value} /> // Could generate 100+ unique styles
147
+ ))}
148
+ </tr>
149
+ ))}
150
+ ```
151
+
152
+ **Why it matters:** Each unique style combination creates a new CSS rule. Hundreds of rules slow down both generation and browser style calculation.
153
+
154
+ **Fix:** Use the native `style` prop for truly dynamic values, or bucket values into ranges:
155
+
156
+ ```tsx
157
+ // Better: Use style prop for per-cell values
158
+ function DataCell({ value }) {
159
+ return (
160
+ <$.td style={{ background: `hsl(${value * 3.6}, 70%, 50%)` }} />
161
+ );
162
+ }
163
+
164
+ // Or: Bucket into classes
165
+ function DataCell({ value }) {
166
+ const bucket = Math.floor(value / 10) * 10; // 0, 10, 20, ... 100
167
+ return <$.td background={`var(--heat-${bucket})`} />;
168
+ }
169
+ ```
170
+
171
+ ### Don't: Use Stylix for High-Frequency Updates
172
+
173
+ ```tsx
174
+ // Avoid: Style changes 60 times per second
175
+ function AnimatedElement({ progress }) {
176
+ return (
177
+ <$.div
178
+ transform={`translateX(${progress * 100}px)`} // Changes every frame
179
+ />
180
+ );
181
+ }
182
+ ```
183
+
184
+ **Why it matters:** Generating and comparing styles on every animation frame adds overhead. Browsers are optimized for inline style updates, not class changes.
185
+
186
+ **Fix:** Use the native `style` prop or CSS animations:
187
+
188
+ ```tsx
189
+ // Better: Use style prop for frame-by-frame updates
190
+ function AnimatedElement({ progress }) {
191
+ return (
192
+ <$.div
193
+ style={{ transform: `translateX(${progress * 100}px)` }}
194
+ />
195
+ );
196
+ }
197
+
198
+ // Best: Use CSS for the animation entirely
199
+ function AnimatedElement() {
200
+ const slide = useKeyframes({
201
+ from: { transform: 'translateX(0)' },
202
+ to: { transform: 'translateX(100px)' }
203
+ });
204
+
205
+ return <$.div animation={`${slide} 1s ease-out forwards`} />;
206
+ }
207
+ ```
208
+
209
+ ### Don't: Redefine Media Config in Multiple Places
210
+
211
+ ```tsx
212
+ // Avoid: Inconsistent breakpoints
213
+ function ComponentA() {
214
+ return (
215
+ <StylixProvider media={{ mobile: s => ({ '@media (max-width: 768px)': s }) }}>
216
+ ...
217
+ </StylixProvider>
218
+ );
219
+ }
220
+
221
+ function ComponentB() {
222
+ return (
223
+ <StylixProvider media={{ mobile: s => ({ '@media (max-width: 480px)': s }) }}>
224
+ ... // Different breakpoint!
225
+ </StylixProvider>
226
+ );
227
+ }
228
+ ```
229
+
230
+ **Fix:** Define once at app root, use everywhere. This is less about performance and more about consistency.
231
+
232
+ ---
233
+
234
+ ## When to Use Native `style` Prop
235
+
236
+ The native `style` prop bypasses Stylix entirely and applies styles inline. Use it for:
237
+
238
+ 1. **Values that change very frequently** (animations, drag position)
239
+ 2. **Truly unique values** that won't benefit from class sharing
240
+ 3. **Values from user input** (sliders, color pickers)
241
+
242
+ ```tsx
243
+ <$.div
244
+ // Static styles via Stylix (cached, deduplicated)
245
+ padding={16}
246
+ border-radius={8}
247
+ background="white"
248
+ transition="transform 0.2s"
249
+
250
+ // Dynamic styles via style prop (no caching overhead)
251
+ style={{
252
+ transform: `translate(${x}px, ${y}px)`,
253
+ opacity: progress
254
+ }}
255
+ />
256
+ ```
257
+
258
+ ---
259
+
260
+ ## Performance Checklist
261
+
262
+ Before shipping, verify:
263
+
264
+ - [ ] Style objects are stable (not recreated every render) for frequently-rendered components
265
+ - [ ] High-frequency animations use `style` prop or CSS animations
266
+ - [ ] No components generate hundreds of unique style variations
267
+ - [ ] Media configuration is defined once at app root
268
+ - [ ] Large lists don't have unique styles per item (use `style` prop if needed)
269
+
270
+ ---
271
+
272
+ ## Measuring Performance
273
+
274
+ If you suspect Stylix is causing performance issues:
275
+
276
+ 1. **Profile with React DevTools** — Look for components spending time in Stylix-related code during renders
277
+ 2. **Check `<style>` tag size** — In DevTools, inspect `<head>` and look at the Stylix `<style>` elements. Thousands of rules might indicate over-generation
278
+ 3. **Use React.memo** — Prevent unnecessary re-renders of styled components
279
+ 4. **Profile with Chrome DevTools** — The Performance panel shows style recalculation time
280
+
281
+ ---
282
+
283
+ ## The Reality
284
+
285
+ For most applications, Stylix performance is not a concern. Problems typically only appear when:
286
+
287
+ - Rendering large lists (1000+ items) with unique styles per item
288
+ - Animating style props at 60fps
289
+ - Generating styles in tight loops
290
+
291
+ If you're building a typical UI with dozens or hundreds of elements, following basic React best practices (avoiding unnecessary re-renders) is usually sufficient.
@@ -0,0 +1,168 @@
1
+ # Philosophy: Why Stylix?
2
+
3
+ ## The Short Version
4
+
5
+ Stylix is a runtime CSS-in-JS library for React. If you know CSS and React, you already know how to use it—there's no new mental model to learn. You write CSS properties as JSX props, and Stylix handles generating class names and injecting styles.
6
+
7
+ If you're working in a codebase that uses Stylix, this document explains why it exists, what problems it solves, and how to think about it.
8
+
9
+ ---
10
+
11
+ ## A Bit of History
12
+
13
+ Stylix was created before the industry's shift away from runtime CSS-in-JS. At the time, libraries like styled-components and Emotion were dominant, and the idea of colocating styles with components was exciting and new.
14
+
15
+ Stylix took a different approach than most CSS-in-JS libraries: instead of template literals or `css()` functions, it used props. The idea was simple—if you're already passing props to React elements, why not pass CSS the same way?
16
+
17
+ ```tsx
18
+ // Instead of this (styled-components):
19
+ const Box = styled.div`
20
+ color: red;
21
+ padding: 16px;
22
+ `;
23
+
24
+ // Or this (Emotion):
25
+ <div css={{ color: 'red', padding: 16 }} />
26
+
27
+ // Stylix does this:
28
+ <$.div color="red" padding={16} />
29
+ ```
30
+
31
+ The props-based approach felt more natural in React. No new syntax to learn, no tagged template literals, no special `css` prop to remember. Just props.
32
+
33
+ ---
34
+
35
+ ## The Industry Moved On (And That's Okay)
36
+
37
+ Today, the prevailing wisdom favors zero-runtime solutions: Tailwind CSS, CSS Modules, vanilla-extract, and others that do their work at build time rather than runtime.
38
+
39
+ The arguments are valid:
40
+ - No runtime overhead for style generation
41
+ - Better performance for initial page load
42
+ - No style injection during hydration
43
+ - Smaller bundle sizes
44
+
45
+ If you're starting a new project today with performance as a top priority, a build-time solution is likely the better choice.
46
+
47
+ **But here's the thing:** for many applications, runtime CSS-in-JS works just fine. The performance overhead is negligible for apps that aren't rendering thousands of unique styled elements per second. The developer experience benefits—colocation, dynamic styles, type safety—are real and meaningful.
48
+
49
+ Stylix exists in production applications that serve real users without performance issues. If you're maintaining one of these codebases, you don't need to feel like you're working with "legacy" technology. You're working with a tool that made reasonable tradeoffs for its use case.
50
+
51
+ ---
52
+
53
+ ## What Stylix Is Good At
54
+
55
+ ### 1. Low Learning Curve
56
+
57
+ If you know CSS, you know Stylix. There's no utility class vocabulary to memorize (like Tailwind), no new API to learn (like styled-components' template syntax), and no special build configuration required.
58
+
59
+ ```tsx
60
+ // This is valid CSS knowledge directly applied:
61
+ <$.div
62
+ display="flex"
63
+ justify-content="space-between"
64
+ padding={16}
65
+ border-radius={8}
66
+ />
67
+ ```
68
+
69
+ ### 2. Colocation
70
+
71
+ Styles live with the component that uses them. When you read a component, you see exactly how it's styled without jumping to another file or searching for class names.
72
+
73
+ ### 3. Dynamic Styles Without Ceremony
74
+
75
+ React's rendering model makes dynamic styles natural:
76
+
77
+ ```tsx
78
+ <$.div
79
+ background={isActive ? 'blue' : 'gray'}
80
+ opacity={isDisabled ? 0.5 : 1}
81
+ />
82
+ ```
83
+
84
+ No conditional class name logic, no ternary-heavy `className` strings, no separate "variant" definitions.
85
+
86
+ ### 4. Responsive Design Made Simple
87
+
88
+ The media object pattern centralizes breakpoint definitions and makes responsive props readable:
89
+
90
+ ```tsx
91
+ <$.div
92
+ font-size={{ default: 16, tablet: 14, mobile: 12 }}
93
+ flex-direction={{ default: 'row', mobile: 'column' }}
94
+ />
95
+ ```
96
+
97
+ ### 5. Type Safety
98
+
99
+ TypeScript knows about CSS properties. You get autocomplete for valid properties and type errors for invalid values. This catches bugs that would otherwise only appear at runtime (or worse, silently fail).
100
+
101
+ ### 6. Gradual Adoption
102
+
103
+ Stylix doesn't require you to style everything with it. You can use it alongside CSS Modules, Tailwind, or plain CSS. It's a tool, not a framework demanding total commitment.
104
+
105
+ ---
106
+
107
+ ## What Stylix Is Not
108
+
109
+ ### Not a Design System
110
+
111
+ Stylix has no opinions about your design. No predefined colors, spacing scales, or component styles. You bring your own design decisions.
112
+
113
+ ### Not Zero-Runtime
114
+
115
+ Stylix generates styles at runtime. For most applications, this is fine. For performance-critical applications with many unique style combinations, consider whether a build-time solution might be more appropriate.
116
+
117
+ ### Not for Every Use Case
118
+
119
+ Some scenarios are genuinely better served by other approaches:
120
+
121
+ - **Component libraries** intended for wide reuse benefit from static CSS that consumers can easily customize and override
122
+ - **Highly dynamic UIs** with thousands of elements updating rapidly (think spreadsheets or data visualizations) may want to avoid runtime style generation
123
+ - **Frame-rate-sensitive animations** should use CSS transitions, keyframe animations, or the native `style` attribute
124
+
125
+ ---
126
+
127
+ ## How to Think About Stylix
128
+
129
+ Think of Stylix as the `style` prop, but better:
130
+
131
+ | Feature | Native `style` prop | Stylix |
132
+ |---------|---------------------|--------|
133
+ | Pseudo-classes (`:hover`, `:focus`) | No | Yes |
134
+ | Media queries | No | Yes |
135
+ | Keyframe animations | No | Yes |
136
+ | Selectors | No | Yes |
137
+ | Server-side rendering | Inline only | Full support |
138
+ | Deduplication | No | Yes |
139
+
140
+ Stylix bridges the gap between the simplicity of inline styles and the power of CSS, without requiring you to context-switch to a separate styling language or file.
141
+
142
+ ---
143
+
144
+ ## For Those Inheriting a Stylix Codebase
145
+
146
+ If you've joined a team that uses Stylix, here's what you need to know:
147
+
148
+ 1. **It's just CSS.** The props are CSS properties. The values are CSS values. If you're unsure what a prop does, MDN has the answer.
149
+
150
+ 2. **Look for the `$css` prop** when you see complex styles. That's where pseudo-classes, media queries, and nested selectors live.
151
+
152
+ 3. **Check the `<StylixProvider>`** at the app root to see if media keywords (like `mobile` or `tablet`) are defined. These are shortcuts you can use in style props.
153
+
154
+ 4. **Spreading `{...styles}` is intentional.** Components often accept style props and spread them onto an inner element. This is how parent components can customize children.
155
+
156
+ 5. **Performance is rarely an issue** unless you're generating many unique styles in a tight loop. Normal component rendering is fine.
157
+
158
+ ---
159
+
160
+ ## The Bottom Line
161
+
162
+ Stylix is a straightforward tool: write CSS as props, get scoped styles. It made sense when it was built, and it continues to work well for the applications using it.
163
+
164
+ The web development landscape constantly evolves. Today's best practice becomes tomorrow's legacy. What matters is whether a tool solves real problems for real users—and Stylix does that.
165
+
166
+ If you're using Stylix, use it well. If you're evaluating it for a new project, understand the tradeoffs and choose what's right for your situation.
167
+
168
+ Either way, you're writing CSS. That skill isn't going anywhere.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stylix/core",
3
- "version": "6.1.1",
3
+ "version": "6.3.0",
4
4
  "description": "React, with style. Add styles to your React apps with props: the easy, natural, and low learning curve approach.",
5
5
  "keywords": [
6
6
  "react",
@@ -29,34 +29,37 @@
29
29
  },
30
30
  "author": "Alex Brombal",
31
31
  "license": "MIT",
32
- "homepage": "https://stylix.dev/",
32
+ "homepage": "https://github.com/brombal/stylix",
33
33
  "bugs": "https://github.com/brombal/stylix/issues",
34
- "repository": "https://github.com/brombal/stylix",
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "git+https://github.com/brombal/stylix.git"
37
+ },
35
38
  "peerDependencies": {
36
39
  "react": ">=18.0.0",
37
40
  "react-dom": ">=18.0.0"
38
41
  },
39
42
  "devDependencies": {
40
- "@biomejs/biome": "^2.0.5",
43
+ "@biomejs/biome": "^2.4.4",
41
44
  "@rollup/plugin-json": "^6.1.0",
42
- "@rollup/plugin-typescript": "^12.1.3",
43
- "@testing-library/dom": "^10.4.0",
44
- "@testing-library/jest-dom": "^6.6.3",
45
- "@testing-library/react": "^16.3.0",
45
+ "@rollup/plugin-typescript": "^12.3.0",
46
+ "@testing-library/dom": "^10.4.1",
47
+ "@testing-library/jest-dom": "^6.9.1",
48
+ "@testing-library/react": "^16.3.2",
46
49
  "@types/jest": "^30.0.0",
47
- "@types/node": "^24.0.4",
48
- "@types/react": "^19.1.8",
49
- "@types/react-dom": "^19.1.6",
50
- "csstype": "^3.1.3",
50
+ "@types/node": "^25.3.2",
51
+ "@types/react": "^19.2.14",
52
+ "@types/react-dom": "^19.2.3",
53
+ "csstype": "^3.2.3",
51
54
  "husky": "^9.1.7",
52
- "jest": "^30.0.2",
53
- "jest-environment-jsdom": "^30.0.2",
54
- "mdn-data": "^2.21.0",
55
- "rollup": "^4.44.0",
56
- "rollup-plugin-dts": "^6.2.1",
57
- "ts-jest": "^29.4.0",
55
+ "jest": "^30.2.0",
56
+ "jest-environment-jsdom": "^30.2.0",
57
+ "mdn-data": "^2.27.1",
58
+ "rollup": "^4.59.0",
59
+ "rollup-plugin-dts": "^6.3.0",
60
+ "ts-jest": "^29.4.6",
58
61
  "tslib": "^2.8.1",
59
- "tsx": "^4.20.3",
60
- "typescript": "^5.8.3"
62
+ "tsx": "^4.21.0",
63
+ "typescript": "^5.9.3"
61
64
  }
62
65
  }
package/tsconfig.json CHANGED
File without changes