@animus-ui/core 0.1.1-fffe37d2.0 → 0.2.0-beta.2
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/ANIMUS_ARCHITECTURE.md +173 -0
- package/CHANGELOG.md +80 -8
- package/CLAUDE.md +1240 -0
- package/dist/Animus.d.ts +77 -63
- package/dist/AnimusConfig.d.ts +8 -8
- package/dist/AnimusExtended.d.ts +76 -0
- package/dist/__fixtures__/testConfig.d.ts +153 -153
- package/dist/compatTheme.d.ts +28 -20
- package/dist/config.d.ts +2321 -2193
- package/dist/createAnimus.d.ts +2 -2
- package/dist/index.d.ts +1084 -1019
- package/dist/index.js +2006 -0
- package/dist/legacy/compose.d.ts +2 -2
- package/dist/legacy/config.d.ts +86 -85
- package/dist/legacy/core.d.ts +12 -12
- package/dist/legacy/create.d.ts +2 -2
- package/dist/legacy/createCss.d.ts +2 -2
- package/dist/legacy/createParser.d.ts +2 -9
- package/dist/legacy/createStates.d.ts +2 -2
- package/dist/legacy/createTransform.d.ts +2 -2
- package/dist/legacy/createVariant.d.ts +2 -2
- package/dist/legacy/responsive.d.ts +14 -14
- package/dist/properties/getStylePropNames.d.ts +1 -1
- package/dist/properties/orderPropNames.d.ts +6 -6
- package/dist/properties/styledOptions.d.ts +20 -20
- package/dist/scales/createScale.d.ts +6 -3
- package/dist/scales/lookupScaleValue.d.ts +3 -3
- package/dist/styles/createParser.d.ts +2 -9
- package/dist/styles/createPropertyStyle.d.ts +4 -3
- package/dist/styles/createStylist.d.ts +2 -2
- package/dist/styles/responsive.d.ts +14 -14
- package/dist/transforms/border.d.ts +1 -1
- package/dist/transforms/grid.d.ts +4 -4
- package/dist/transforms/index.d.ts +4 -4
- package/dist/transforms/size.d.ts +2 -2
- package/dist/transforms/utils.d.ts +2 -1
- package/dist/types/config.d.ts +50 -44
- package/dist/types/properties.d.ts +23 -26
- package/dist/types/props.d.ts +34 -43
- package/dist/types/scales.d.ts +2 -2
- package/dist/types/shared.d.ts +4 -4
- package/dist/types/theme.d.ts +17 -17
- package/dist/types/utils.d.ts +4 -4
- package/jest.config.js +1 -0
- package/package.json +11 -9
- package/rollup.config.js +2 -2
- package/tsconfig.json +4 -2
- package/dist/index.cjs.js +0 -1
- package/dist/index.esm.js +0 -1
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,1240 @@
|
|
|
1
|
+
# CLAUDE.md - Animus Core Package
|
|
2
|
+
|
|
3
|
+
This file provides detailed guidance for working with the Animus core builder implementation.
|
|
4
|
+
|
|
5
|
+
## Quick Philosophy Reminder for AI Agents
|
|
6
|
+
|
|
7
|
+
**This is optimal for us because:**
|
|
8
|
+
- Method order = semantic intent (styles=base, variants=choices, states=interactions, props=dynamics)
|
|
9
|
+
- Constraints guide us to correct patterns, not limit capabilities
|
|
10
|
+
- `extend()` provides full flexibility when needed
|
|
11
|
+
- One canonical path eliminates ambiguity
|
|
12
|
+
|
|
13
|
+
**Key Insight**: We're not limited in WHAT we can style, only guided in HOW we express styling intent. This transforms CSS from an open-ended problem to a solvable pattern-matching exercise.
|
|
14
|
+
|
|
15
|
+
## Architecture Overview
|
|
16
|
+
|
|
17
|
+
The Animus builder implements a sophisticated type-safe fluent API pattern using TypeScript's class inheritance and type narrowing. The architecture enforces a specific method call order to mirror CSS cascade principles.
|
|
18
|
+
|
|
19
|
+
## Builder Chain Architecture
|
|
20
|
+
|
|
21
|
+
### Class Hierarchy
|
|
22
|
+
```
|
|
23
|
+
AnimusConfig → Animus → AnimusWithBase → AnimusWithVariants → AnimusWithStates → AnimusWithSystem → AnimusWithAll
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Each class progressively narrows available methods:
|
|
27
|
+
- **AnimusConfig**: Entry point with `addGroup()` and `build()`
|
|
28
|
+
- **Animus**: Adds `styles()` method
|
|
29
|
+
- **AnimusWithBase**: Adds `variant()` method
|
|
30
|
+
- **AnimusWithVariants**: Adds `states()` and `variant()` methods
|
|
31
|
+
- **AnimusWithStates**: Adds `groups()` method
|
|
32
|
+
- **AnimusWithSystem**: Adds `props()` method
|
|
33
|
+
- **AnimusWithAll**: Terminal class with `asElement()`, `asComponent()`, `build()`, and `extend()`
|
|
34
|
+
|
|
35
|
+
### Method Chain Enforcement
|
|
36
|
+
|
|
37
|
+
The builder enforces this cascade order:
|
|
38
|
+
1. `.styles()` - Base styles (foundation layer)
|
|
39
|
+
2. `.variant()` - Variant-based styles (can be called multiple times)
|
|
40
|
+
3. `.states()` - Boolean state styles
|
|
41
|
+
4. `.groups()` - Enable prop groups
|
|
42
|
+
5. `.props()` - Custom props with scales
|
|
43
|
+
6. `.asElement()` or `.asComponent()` - Terminal methods
|
|
44
|
+
|
|
45
|
+
This order mirrors CSS cascade specificity where later rules override earlier ones.
|
|
46
|
+
|
|
47
|
+
### Extension Pattern
|
|
48
|
+
|
|
49
|
+
The `extend()` method is special - it returns an `AnimusExtended` instance that:
|
|
50
|
+
- Preserves all configuration from the parent
|
|
51
|
+
- Allows any builder method to be called in any order
|
|
52
|
+
- Merges configurations using lodash `merge()` to combine nested objects
|
|
53
|
+
- Maintains proper style execution order despite flexible API
|
|
54
|
+
|
|
55
|
+
## Key Components
|
|
56
|
+
|
|
57
|
+
### AnimusConfig (Entry Point)
|
|
58
|
+
Located in `AnimusConfig.ts`:
|
|
59
|
+
- Manages prop registry and group definitions
|
|
60
|
+
- `addGroup()`: Registers named groups of props
|
|
61
|
+
- `build()`: Creates the initial Animus instance
|
|
62
|
+
|
|
63
|
+
### Animus Classes (Builder Chain)
|
|
64
|
+
Located in `Animus.ts`:
|
|
65
|
+
- Each class extends the previous, adding specific methods
|
|
66
|
+
- Uses TypeScript generics to track configuration state
|
|
67
|
+
- Methods return new instances of the next class in chain
|
|
68
|
+
|
|
69
|
+
### AnimusExtended (Extension Handler)
|
|
70
|
+
Located in `AnimusExtended.ts`:
|
|
71
|
+
- Handles component extension with full method access
|
|
72
|
+
- Merges parent and child configurations
|
|
73
|
+
- Preserves cascade order during style execution
|
|
74
|
+
|
|
75
|
+
### Style Processing
|
|
76
|
+
|
|
77
|
+
#### createStylist (Style Compiler)
|
|
78
|
+
Located in `styles/createStylist.ts`:
|
|
79
|
+
- Compiles all style layers into a single function
|
|
80
|
+
- Handles selector generation for variants and states
|
|
81
|
+
- Manages responsive breakpoint extraction
|
|
82
|
+
- Applies styles in correct cascade order:
|
|
83
|
+
1. Base styles
|
|
84
|
+
2. Variant styles (when active)
|
|
85
|
+
3. State styles (when active)
|
|
86
|
+
4. System props (from parser)
|
|
87
|
+
5. Custom props
|
|
88
|
+
|
|
89
|
+
#### createParser (Prop Parser)
|
|
90
|
+
Located in `styles/createParser.ts`:
|
|
91
|
+
- Converts prop values to CSS using scale lookups
|
|
92
|
+
- Handles responsive prop arrays
|
|
93
|
+
- Manages prop-to-CSS property mapping
|
|
94
|
+
|
|
95
|
+
## Type System
|
|
96
|
+
|
|
97
|
+
The builder uses extensive TypeScript generics to track:
|
|
98
|
+
- **PropRegistry**: All registered props and their types
|
|
99
|
+
- **GroupRegistry**: Named groups and their constituent props
|
|
100
|
+
- **BaseParser**: Parser configuration for system props
|
|
101
|
+
- **BaseStyles**: Base CSS styles
|
|
102
|
+
- **Variants**: Variant configurations with their options
|
|
103
|
+
- **States**: Boolean state configurations
|
|
104
|
+
- **ActiveGroups**: Currently enabled prop groups
|
|
105
|
+
- **CustomProps**: User-defined custom props
|
|
106
|
+
|
|
107
|
+
## Style Execution Order
|
|
108
|
+
|
|
109
|
+
When a component renders, styles are applied in this order:
|
|
110
|
+
1. CSS variables (from `vars` prop)
|
|
111
|
+
2. Base styles from `.styles()`
|
|
112
|
+
3. Active variant styles
|
|
113
|
+
4. Active state styles
|
|
114
|
+
5. System props (parsed from component props)
|
|
115
|
+
6. Inline style overrides
|
|
116
|
+
|
|
117
|
+
## Prop Forwarding
|
|
118
|
+
|
|
119
|
+
The builder automatically filters props:
|
|
120
|
+
- System props (in PropRegistry) are consumed and not forwarded
|
|
121
|
+
- Valid HTML attributes are forwarded (using @emotion/is-prop-valid)
|
|
122
|
+
- Custom props are consumed based on configuration
|
|
123
|
+
|
|
124
|
+
## Best Practices
|
|
125
|
+
|
|
126
|
+
1. **Define base styles first** - Use `.styles()` for foundational styles
|
|
127
|
+
2. **Use variants for design options** - Color schemes, sizes, etc.
|
|
128
|
+
3. **Use states for interactive states** - Hover, focus, disabled, etc.
|
|
129
|
+
4. **Group related props** - Use `.groups()` to enable sets of props
|
|
130
|
+
5. **Extend for variations** - Use `.extend()` to create component variants
|
|
131
|
+
|
|
132
|
+
## Common Patterns
|
|
133
|
+
|
|
134
|
+
### Basic Component
|
|
135
|
+
```typescript
|
|
136
|
+
const Button = animus
|
|
137
|
+
.styles({
|
|
138
|
+
padding: '8px 16px',
|
|
139
|
+
borderRadius: '4px'
|
|
140
|
+
})
|
|
141
|
+
.asElement('button');
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Component with Variants
|
|
145
|
+
```typescript
|
|
146
|
+
const Button = animus
|
|
147
|
+
.styles({ base: '...' })
|
|
148
|
+
.variant({
|
|
149
|
+
prop: 'size',
|
|
150
|
+
variants: {
|
|
151
|
+
small: { padding: '4px 8px' },
|
|
152
|
+
large: { padding: '12px 24px' }
|
|
153
|
+
}
|
|
154
|
+
})
|
|
155
|
+
.asElement('button');
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Extended Component
|
|
159
|
+
```typescript
|
|
160
|
+
const PrimaryButton = Button.extend()
|
|
161
|
+
.styles({
|
|
162
|
+
backgroundColor: 'blue',
|
|
163
|
+
color: 'white'
|
|
164
|
+
})
|
|
165
|
+
.asElement('button');
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Debugging Tips
|
|
169
|
+
|
|
170
|
+
1. **Check method order** - Ensure builder methods are called in correct sequence
|
|
171
|
+
2. **Inspect built styles** - Use `.build()` to see the compiled style function
|
|
172
|
+
3. **Verify prop filtering** - Check `shouldForwardProp` logic
|
|
173
|
+
4. **Trace style cascade** - Follow execution through createStylist
|
|
174
|
+
5. **Type errors** - Usually indicate incorrect method order or invalid configurations
|
|
175
|
+
|
|
176
|
+
## New Insights from Real-World Usage
|
|
177
|
+
|
|
178
|
+
Based on analyzing actual implementations across the codebase:
|
|
179
|
+
|
|
180
|
+
### 1. Gradient Token System
|
|
181
|
+
The codebase uses semantic gradient tokens like `gradient: 'flowX'` that map to complex gradient definitions. This suggests a sophisticated token system beyond simple color values.
|
|
182
|
+
|
|
183
|
+
### 2. Grid Area Shorthand
|
|
184
|
+
Components use `area: 'sidebar'` as shorthand for `gridArea: 'sidebar'`, indicating custom CSS property mappings in the prop system.
|
|
185
|
+
|
|
186
|
+
### 3. Layered Component Architecture
|
|
187
|
+
Complex components like Button separate visual layers (container vs foreground) rather than combining all styles in one component. This pattern enables:
|
|
188
|
+
- Independent animation of layers
|
|
189
|
+
- Better performance (transforms on specific layers)
|
|
190
|
+
- Cleaner separation of concerns
|
|
191
|
+
|
|
192
|
+
### 4. Responsive Grid Templates
|
|
193
|
+
The Layout component shows responsive grid template areas:
|
|
194
|
+
```tsx
|
|
195
|
+
gridTemplateAreas: {
|
|
196
|
+
_: '"header header" "content content"',
|
|
197
|
+
sm: '"header header" "sidebar content"',
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### 5. CSS Custom Scrollbar Hiding
|
|
202
|
+
Components use webkit-specific scrollbar hiding:
|
|
203
|
+
```tsx
|
|
204
|
+
'::-webkit-scrollbar': {
|
|
205
|
+
display: 'none',
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### 6. Position-Based Responsive Patterns
|
|
210
|
+
Components change positioning strategy based on viewport:
|
|
211
|
+
```tsx
|
|
212
|
+
position: {
|
|
213
|
+
_: 'absolute',
|
|
214
|
+
sm: 'static',
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### 7. Size Shorthands
|
|
219
|
+
The codebase uses numeric shorthands like `width: 1` (likely 100%), `size: 1`, `inset: 0`, suggesting a normalized prop value system.
|
|
220
|
+
|
|
221
|
+
## Open Questions from Analysis
|
|
222
|
+
|
|
223
|
+
1. **Gradient Token Architecture**: How are gradient tokens like 'flowX' defined and resolved? Are they CSS variables or JS objects?
|
|
224
|
+
|
|
225
|
+
2. **Numeric Value System**: What's the complete mapping for numeric values (0, 1, 0.5)? How do they translate to CSS?
|
|
226
|
+
|
|
227
|
+
3. **Custom Prop Mappings**: Beyond 'area' for 'gridArea', what other CSS property shorthands exist?
|
|
228
|
+
|
|
229
|
+
4. **Animation Integration**: How does the `import { flow } from '../animations/flow'` pattern work with Emotion keyframes?
|
|
230
|
+
|
|
231
|
+
5. **Theme Token Categories**: What are all the semantic token categories (text-shadow: 'flush', 'link-raised')?
|
|
232
|
+
|
|
233
|
+
6. **Performance of Pseudo-Elements**: Given heavy use of :before/:after for effects, are there performance benchmarks?
|
|
234
|
+
|
|
235
|
+
7. **TypeScript Compound Pattern**: Is the Layout compound component TypeScript pattern the recommended approach?
|
|
236
|
+
|
|
237
|
+
8. **Group Selection Strategy**: What determines which groups to enable for each component type?
|
|
238
|
+
|
|
239
|
+
9. **Variant vs State Decision**: When should something be a variant vs a state? The pattern isn't always clear.
|
|
240
|
+
|
|
241
|
+
10. **Build Tool Integration**: How do production builds optimize these complex styled components?
|
|
242
|
+
|
|
243
|
+
## Practical Patterns & Examples
|
|
244
|
+
|
|
245
|
+
This section provides answers to common implementation questions for AI agents.
|
|
246
|
+
|
|
247
|
+
### Responsive Design
|
|
248
|
+
*Documentation coming soon - how arrays work, breakpoint system*
|
|
249
|
+
Array responsive syntax works for specificing values mobile first, from no breakpoint to the highest breakpoint configured in the theme. Omitted sequential values in the array are ignored (only if undefined).
|
|
250
|
+
|
|
251
|
+
The object syntax is equivalent but uses named keys for the sequence. Please see the example:
|
|
252
|
+
|
|
253
|
+
```tsx
|
|
254
|
+
// Object syntax
|
|
255
|
+
animus.styles({ width: { _: '100px', xs: '200px', md: '300px' }})
|
|
256
|
+
// Equivalent Array Syntax
|
|
257
|
+
animus.styles({ width: ['100px', '200px', , '300px' ]});
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Responsive syntax is available definitionally in `.styles()`, `.variant()` and `.states()` for example:
|
|
261
|
+
```tsx
|
|
262
|
+
animus
|
|
263
|
+
.variant({
|
|
264
|
+
prop: 'size',
|
|
265
|
+
variants: {
|
|
266
|
+
large: { padding: ['1rem', '2rem'] } // Is this valid?
|
|
267
|
+
}
|
|
268
|
+
})
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
And for `groups()` and custom `props()` they are available in the final prop API as valid syntax E.G:
|
|
272
|
+
|
|
273
|
+
```tsx
|
|
274
|
+
const Box = animus.groups({ space: true }).asElement('div');
|
|
275
|
+
|
|
276
|
+
<Box p={['1rem', '2rem', '3rem']} />
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### Theming Integration
|
|
280
|
+
|
|
281
|
+
The Animus builder integrates seamlessly with themes provided via Emotion's ThemeProvider. When using ThemeBuilder from `@animus-ui/theming`, the integration works as follows:
|
|
282
|
+
|
|
283
|
+
#### Theme Value Resolution
|
|
284
|
+
|
|
285
|
+
Props can reference theme scales using the `scale` property in prop definitions:
|
|
286
|
+
|
|
287
|
+
```typescript
|
|
288
|
+
animus
|
|
289
|
+
.props({
|
|
290
|
+
bg: {
|
|
291
|
+
property: 'backgroundColor',
|
|
292
|
+
scale: 'colors' // References theme.colors scale
|
|
293
|
+
}
|
|
294
|
+
})
|
|
295
|
+
.asElement('div');
|
|
296
|
+
|
|
297
|
+
// Usage: <Box bg="primary" /> resolves to theme.colors.primary
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
#### Design Token Flow
|
|
301
|
+
|
|
302
|
+
1. **ThemeBuilder creates CSS variables**:
|
|
303
|
+
```typescript
|
|
304
|
+
const theme = createTheme(baseTheme)
|
|
305
|
+
.addColors({ primary: '#007bff' })
|
|
306
|
+
.build();
|
|
307
|
+
// Creates: theme.colors.primary = 'var(--colors-primary)'
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
2. **Animus resolves theme values**:
|
|
311
|
+
```typescript
|
|
312
|
+
// When bg="primary" is used:
|
|
313
|
+
// lookupScaleValue('primary', 'colors', theme) → 'var(--colors-primary)'
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
3. **CSS variables must be injected**:
|
|
317
|
+
```typescript
|
|
318
|
+
import { Global } from '@emotion/react';
|
|
319
|
+
|
|
320
|
+
const GlobalStyles = () => {
|
|
321
|
+
const theme = useTheme();
|
|
322
|
+
return (
|
|
323
|
+
<Global styles={{
|
|
324
|
+
':root': theme._variables.root
|
|
325
|
+
}} />
|
|
326
|
+
);
|
|
327
|
+
};
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
#### Type Safety with Themes
|
|
331
|
+
|
|
332
|
+
For proper TypeScript support, extend Emotion's theme interface:
|
|
333
|
+
|
|
334
|
+
```typescript
|
|
335
|
+
declare module '@emotion/react' {
|
|
336
|
+
export interface Theme extends MyThemeType {}
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
#### Theme Scales Available
|
|
341
|
+
|
|
342
|
+
Common scales that work with Animus props:
|
|
343
|
+
- `colors` - Color values
|
|
344
|
+
- `space` - Spacing/margin/padding values
|
|
345
|
+
- `fontSizes` - Typography sizes
|
|
346
|
+
- `fonts` - Font families
|
|
347
|
+
- `lineHeights` - Line height values
|
|
348
|
+
- `letterSpacings` - Letter spacing values
|
|
349
|
+
- `sizes` - Width/height values
|
|
350
|
+
- `borders` - Border styles
|
|
351
|
+
- `borderWidths` - Border width values
|
|
352
|
+
- `borderStyles` - Border style values
|
|
353
|
+
- `radii` - Border radius values
|
|
354
|
+
- `shadows` - Box shadow values
|
|
355
|
+
- `zIndices` - Z-index values
|
|
356
|
+
- `transitions` - Transition values
|
|
357
|
+
|
|
358
|
+
### Complex Selectors
|
|
359
|
+
*Documentation coming soon - pseudo-selectors, nested selectors placement*
|
|
360
|
+
Pseudo selectors and nested selectors are available for `.styles()`, `.variant()` and `.states()` as they all represent a set of CSS rules rather than indivdiual utilities.
|
|
361
|
+
|
|
362
|
+
```tsx
|
|
363
|
+
const HoverableItem = animus
|
|
364
|
+
.styles({
|
|
365
|
+
backgroundColor: 'white',
|
|
366
|
+
'&:hover': {
|
|
367
|
+
backgroundColor: 'grey',
|
|
368
|
+
}
|
|
369
|
+
})
|
|
370
|
+
.variant({
|
|
371
|
+
prop: 'intent',
|
|
372
|
+
variants: {
|
|
373
|
+
primary: {
|
|
374
|
+
backgroundColor: 'blue',
|
|
375
|
+
color: 'white',
|
|
376
|
+
'&:hover': {
|
|
377
|
+
backgroundColor: 'darkblue',
|
|
378
|
+
}
|
|
379
|
+
},
|
|
380
|
+
danger: {
|
|
381
|
+
backgroundColor: 'red',
|
|
382
|
+
color: 'white',
|
|
383
|
+
'&:hover': {
|
|
384
|
+
backgroundColor: 'darkred',
|
|
385
|
+
}
|
|
386
|
+
},
|
|
387
|
+
}
|
|
388
|
+
})
|
|
389
|
+
.states({
|
|
390
|
+
disabled: {
|
|
391
|
+
backgroundColor: 'grey',
|
|
392
|
+
opacity: 0.8,
|
|
393
|
+
'&:hover': {
|
|
394
|
+
cursor: 'not-allowed',
|
|
395
|
+
backgroundColor: 'grey',
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
})
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
This also applies to complex or multiple selectors and or pseudo elements like - however may not include escaping the current context with `.parent + &`:
|
|
402
|
+
```tsx
|
|
403
|
+
const ComplexThing = animus
|
|
404
|
+
.styles({
|
|
405
|
+
'&:before': {
|
|
406
|
+
content: '""',
|
|
407
|
+
position: 'absolute',
|
|
408
|
+
borderRadius: 4,
|
|
409
|
+
gradient: 'flowX',
|
|
410
|
+
backgroundSize: '300px 100px',
|
|
411
|
+
backgroundPosition: '0px 0%',
|
|
412
|
+
transition: 'bg',
|
|
413
|
+
inset: 0,
|
|
414
|
+
bg: 'background-current',
|
|
415
|
+
zIndex: 0,
|
|
416
|
+
},
|
|
417
|
+
'&:after': {
|
|
418
|
+
content: '""',
|
|
419
|
+
inset: 2,
|
|
420
|
+
borderRadius: 2,
|
|
421
|
+
bg: 'background-current',
|
|
422
|
+
zIndex: 0,
|
|
423
|
+
position: 'absolute',
|
|
424
|
+
},
|
|
425
|
+
'&:hover:before': {
|
|
426
|
+
backgroundPosition: '-100px 0%',
|
|
427
|
+
},
|
|
428
|
+
'&:active:hover:before': {
|
|
429
|
+
backgroundPosition: '-400px 0%',
|
|
430
|
+
},
|
|
431
|
+
});
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
Chain definition order reflects the order of evaluation:
|
|
435
|
+
1. `styles`
|
|
436
|
+
2. `variant` preserving the order in which variants are registered if this is called multiple times.
|
|
437
|
+
3. `states`
|
|
438
|
+
4. `groups`
|
|
439
|
+
5. `props`
|
|
440
|
+
|
|
441
|
+
When extending a component using `.extend()` you can inject new styles to specific layers without effecting the prop cascade!
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
### Animation Patterns
|
|
446
|
+
|
|
447
|
+
Animations in Animus follow the same cascade principles as other styles. Here's where each type of animation belongs:
|
|
448
|
+
|
|
449
|
+
#### Base Animations (in `.styles()`)
|
|
450
|
+
|
|
451
|
+
For animations that are fundamental to the component:
|
|
452
|
+
|
|
453
|
+
```typescript
|
|
454
|
+
const FadeIn = animus
|
|
455
|
+
.styles({
|
|
456
|
+
animation: 'fadeIn 0.3s ease-in-out',
|
|
457
|
+
'@keyframes fadeIn': {
|
|
458
|
+
from: { opacity: 0 },
|
|
459
|
+
to: { opacity: 1 }
|
|
460
|
+
}
|
|
461
|
+
})
|
|
462
|
+
.asElement('div');
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
#### Variant-Based Animations (in `.variant()`)
|
|
466
|
+
|
|
467
|
+
For animations that change based on component variations:
|
|
468
|
+
|
|
469
|
+
```typescript
|
|
470
|
+
const Alert = animus
|
|
471
|
+
.styles({
|
|
472
|
+
padding: '1rem',
|
|
473
|
+
borderRadius: '4px'
|
|
474
|
+
})
|
|
475
|
+
.variant({
|
|
476
|
+
prop: 'type',
|
|
477
|
+
variants: {
|
|
478
|
+
success: {
|
|
479
|
+
backgroundColor: 'green.100',
|
|
480
|
+
animation: 'slideInLeft 0.3s ease-out'
|
|
481
|
+
},
|
|
482
|
+
error: {
|
|
483
|
+
backgroundColor: 'red.100',
|
|
484
|
+
animation: 'shake 0.5s ease-in-out'
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
})
|
|
488
|
+
.asElement('div');
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
#### State-Based Animations (in `.states()`)
|
|
492
|
+
|
|
493
|
+
For animations triggered by interactive states:
|
|
494
|
+
|
|
495
|
+
```typescript
|
|
496
|
+
const Button = animus
|
|
497
|
+
.styles({
|
|
498
|
+
transition: 'all 0.2s ease'
|
|
499
|
+
})
|
|
500
|
+
.states({
|
|
501
|
+
loading: {
|
|
502
|
+
position: 'relative',
|
|
503
|
+
color: 'transparent',
|
|
504
|
+
'&::after': {
|
|
505
|
+
content: '""',
|
|
506
|
+
position: 'absolute',
|
|
507
|
+
width: '16px',
|
|
508
|
+
height: '16px',
|
|
509
|
+
top: '50%',
|
|
510
|
+
left: '50%',
|
|
511
|
+
margin: '-8px 0 0 -8px',
|
|
512
|
+
border: '2px solid',
|
|
513
|
+
borderColor: 'currentColor',
|
|
514
|
+
borderRadius: '50%',
|
|
515
|
+
borderTopColor: 'transparent',
|
|
516
|
+
animation: 'spin 0.6s linear infinite'
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
})
|
|
520
|
+
.asElement('button');
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
#### CSS Transition Integration
|
|
524
|
+
|
|
525
|
+
For smooth property transitions, define them in base styles:
|
|
526
|
+
|
|
527
|
+
```typescript
|
|
528
|
+
const Card = animus
|
|
529
|
+
.styles({
|
|
530
|
+
backgroundColor: 'white',
|
|
531
|
+
transform: 'translateY(0)',
|
|
532
|
+
transition: 'transform 0.2s ease, box-shadow 0.2s ease',
|
|
533
|
+
'&:hover': {
|
|
534
|
+
transform: 'translateY(-4px)',
|
|
535
|
+
boxShadow: 'lg'
|
|
536
|
+
}
|
|
537
|
+
})
|
|
538
|
+
.asElement('div');
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
#### Complex Animation Sequences
|
|
542
|
+
|
|
543
|
+
For multi-step animations, use keyframes in the appropriate layer:
|
|
544
|
+
|
|
545
|
+
```typescript
|
|
546
|
+
const LoadingDots = animus
|
|
547
|
+
.styles({
|
|
548
|
+
display: 'inline-flex',
|
|
549
|
+
gap: '4px',
|
|
550
|
+
'& span': {
|
|
551
|
+
width: '8px',
|
|
552
|
+
height: '8px',
|
|
553
|
+
borderRadius: '50%',
|
|
554
|
+
backgroundColor: 'currentColor',
|
|
555
|
+
animation: 'bounce 1.4s ease-in-out infinite both'
|
|
556
|
+
},
|
|
557
|
+
'& span:nth-of-type(1)': {
|
|
558
|
+
animationDelay: '-0.32s'
|
|
559
|
+
},
|
|
560
|
+
'& span:nth-of-type(2)': {
|
|
561
|
+
animationDelay: '-0.16s'
|
|
562
|
+
},
|
|
563
|
+
'@keyframes bounce': {
|
|
564
|
+
'0%, 80%, 100%': {
|
|
565
|
+
transform: 'scale(0)'
|
|
566
|
+
},
|
|
567
|
+
'40%': {
|
|
568
|
+
transform: 'scale(1)'
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
})
|
|
572
|
+
.asElement('div');
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
#### Animation Best Practices
|
|
576
|
+
|
|
577
|
+
1. **Performance**: Use `transform` and `opacity` for best performance
|
|
578
|
+
2. **Accessibility**: Respect `prefers-reduced-motion`:
|
|
579
|
+
```typescript
|
|
580
|
+
.styles({
|
|
581
|
+
'@media (prefers-reduced-motion: reduce)': {
|
|
582
|
+
animation: 'none',
|
|
583
|
+
transition: 'none'
|
|
584
|
+
}
|
|
585
|
+
})
|
|
586
|
+
```
|
|
587
|
+
3. **Duration**: Keep animations short (200-400ms for micro-interactions)
|
|
588
|
+
4. **Easing**: Use appropriate easing functions:
|
|
589
|
+
- `ease-out` for enter animations
|
|
590
|
+
- `ease-in` for exit animations
|
|
591
|
+
- `ease-in-out` for state changes
|
|
592
|
+
|
|
593
|
+
### Compound Components
|
|
594
|
+
|
|
595
|
+
Compound components are a powerful pattern for creating related components that work together. Here's the recommended approach based on real-world usage:
|
|
596
|
+
|
|
597
|
+
#### Basic Pattern
|
|
598
|
+
|
|
599
|
+
```tsx
|
|
600
|
+
const Card = animus
|
|
601
|
+
.styles({
|
|
602
|
+
padding: '1rem',
|
|
603
|
+
backgroundColor: 'grey',
|
|
604
|
+
border: 1,
|
|
605
|
+
})
|
|
606
|
+
.asElement('div');
|
|
607
|
+
|
|
608
|
+
const CardHeader = animus
|
|
609
|
+
.styles({
|
|
610
|
+
fontSize: 18,
|
|
611
|
+
fontWeight: 500
|
|
612
|
+
})
|
|
613
|
+
.asElement('h2');
|
|
614
|
+
|
|
615
|
+
const CardBody = animus
|
|
616
|
+
.styles({
|
|
617
|
+
padding: '1rem',
|
|
618
|
+
fontSize: 14,
|
|
619
|
+
})
|
|
620
|
+
.asElement('div');
|
|
621
|
+
|
|
622
|
+
Card.Header = CardHeader;
|
|
623
|
+
Card.Body = CardBody;
|
|
624
|
+
|
|
625
|
+
export default Card;
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
#### Advanced Pattern with TypeScript (from Layout.tsx)
|
|
629
|
+
|
|
630
|
+
```tsx
|
|
631
|
+
// Define individual components
|
|
632
|
+
const LayoutContainer = animus
|
|
633
|
+
.styles({
|
|
634
|
+
display: 'grid',
|
|
635
|
+
gridTemplateAreas: '"header header" "sidebar content"',
|
|
636
|
+
})
|
|
637
|
+
.states({
|
|
638
|
+
sidebar: {
|
|
639
|
+
gridTemplateAreas: {
|
|
640
|
+
_: '"header header" "content content"',
|
|
641
|
+
sm: '"header header" "sidebar content"',
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
})
|
|
645
|
+
.asElement('div');
|
|
646
|
+
|
|
647
|
+
const HeaderContainer = animus
|
|
648
|
+
.styles({ area: 'header' })
|
|
649
|
+
.asElement('div');
|
|
650
|
+
|
|
651
|
+
const SidebarContainer = animus
|
|
652
|
+
.styles({ area: 'sidebar' })
|
|
653
|
+
.asElement('div');
|
|
654
|
+
|
|
655
|
+
// Create compound type
|
|
656
|
+
type LayoutContainer = typeof LayoutContainer;
|
|
657
|
+
|
|
658
|
+
export interface Layout extends LayoutContainer {
|
|
659
|
+
Header: typeof HeaderContainer;
|
|
660
|
+
Sidebar: typeof SidebarContainer;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// Export with sub-components attached
|
|
664
|
+
export const Layout = LayoutContainer as Layout;
|
|
665
|
+
Layout.Header = HeaderContainer;
|
|
666
|
+
Layout.Sidebar = SidebarContainer;
|
|
667
|
+
|
|
668
|
+
// Usage
|
|
669
|
+
<Layout sidebar>
|
|
670
|
+
<Layout.Header>...</Layout.Header>
|
|
671
|
+
<Layout.Sidebar>...</Layout.Sidebar>
|
|
672
|
+
</Layout>
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
This pattern provides:
|
|
676
|
+
- Type-safe compound components
|
|
677
|
+
- Clear parent-child relationships
|
|
678
|
+
- Cohesive API for related components
|
|
679
|
+
|
|
680
|
+
### Global Styles & Resets
|
|
681
|
+
*Documentation coming soon - app-wide styles approach*
|
|
682
|
+
|
|
683
|
+
### Performance Considerations
|
|
684
|
+
|
|
685
|
+
Based on real-world usage patterns, here are key performance optimizations:
|
|
686
|
+
|
|
687
|
+
#### 1. Avoid Excessive Pseudo-Elements
|
|
688
|
+
|
|
689
|
+
While powerful, overuse of `:before` and `:after` can impact performance:
|
|
690
|
+
|
|
691
|
+
```tsx
|
|
692
|
+
// ❌ Avoid: Multiple layers of pseudo-elements
|
|
693
|
+
.styles({
|
|
694
|
+
'&:before': { /* gradient layer */ },
|
|
695
|
+
'&:after': { /* shadow layer */ },
|
|
696
|
+
'& > div:before': { /* more effects */ },
|
|
697
|
+
'& > div:after': { /* even more */ }
|
|
698
|
+
})
|
|
699
|
+
|
|
700
|
+
// ✅ Better: Use actual elements when needed
|
|
701
|
+
const Container = animus.styles({...}).asElement('div');
|
|
702
|
+
const GradientLayer = animus.styles({...}).asElement('div');
|
|
703
|
+
const ContentLayer = animus.styles({...}).asElement('div');
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
#### 2. Optimize Gradient Animations
|
|
707
|
+
|
|
708
|
+
Gradient animations can be expensive. Use transform instead:
|
|
709
|
+
|
|
710
|
+
```tsx
|
|
711
|
+
// ❌ Avoid: Animating background-position continuously
|
|
712
|
+
.styles({
|
|
713
|
+
animation: 'flow 5s linear infinite',
|
|
714
|
+
'@keyframes flow': {
|
|
715
|
+
'0%': { backgroundPosition: '0% 0%' },
|
|
716
|
+
'100%': { backgroundPosition: '100% 0%' }
|
|
717
|
+
}
|
|
718
|
+
})
|
|
719
|
+
|
|
720
|
+
// ✅ Better: Use transform for smoother performance
|
|
721
|
+
.styles({
|
|
722
|
+
'&:before': {
|
|
723
|
+
content: '""',
|
|
724
|
+
position: 'absolute',
|
|
725
|
+
inset: 0,
|
|
726
|
+
background: 'gradient',
|
|
727
|
+
transform: 'translateX(0)',
|
|
728
|
+
transition: 'transform 0.3s ease',
|
|
729
|
+
},
|
|
730
|
+
'&:hover:before': {
|
|
731
|
+
transform: 'translateX(-100px)',
|
|
732
|
+
}
|
|
733
|
+
})
|
|
734
|
+
```
|
|
735
|
+
|
|
736
|
+
#### 3. Group-Based Code Splitting
|
|
737
|
+
|
|
738
|
+
Enable only the groups you need to reduce bundle size:
|
|
739
|
+
|
|
740
|
+
```tsx
|
|
741
|
+
// ❌ Avoid: Enabling all groups
|
|
742
|
+
.groups({
|
|
743
|
+
layout: true,
|
|
744
|
+
space: true,
|
|
745
|
+
color: true,
|
|
746
|
+
typography: true,
|
|
747
|
+
borders: true,
|
|
748
|
+
shadows: true,
|
|
749
|
+
positioning: true,
|
|
750
|
+
background: true,
|
|
751
|
+
flex: true,
|
|
752
|
+
grid: true
|
|
753
|
+
})
|
|
754
|
+
|
|
755
|
+
// ✅ Better: Enable only what's needed
|
|
756
|
+
.groups({
|
|
757
|
+
layout: true,
|
|
758
|
+
space: true,
|
|
759
|
+
color: true
|
|
760
|
+
})
|
|
761
|
+
```
|
|
762
|
+
|
|
763
|
+
#### 4. Responsive Value Optimization
|
|
764
|
+
|
|
765
|
+
Use responsive values judiciously:
|
|
766
|
+
|
|
767
|
+
```tsx
|
|
768
|
+
// ❌ Avoid: Responsive values for every property
|
|
769
|
+
.styles({
|
|
770
|
+
padding: { _: 8, xs: 12, sm: 16, md: 20, lg: 24, xl: 32 },
|
|
771
|
+
margin: { _: 4, xs: 6, sm: 8, md: 10, lg: 12, xl: 16 },
|
|
772
|
+
fontSize: { _: 14, xs: 15, sm: 16, md: 17, lg: 18, xl: 20 },
|
|
773
|
+
})
|
|
774
|
+
|
|
775
|
+
// ✅ Better: Use responsive values for key breakpoints
|
|
776
|
+
.styles({
|
|
777
|
+
padding: { _: 8, sm: 16, lg: 24 },
|
|
778
|
+
margin: 8,
|
|
779
|
+
fontSize: { _: 14, sm: 16 }
|
|
780
|
+
})
|
|
781
|
+
```
|
|
782
|
+
|
|
783
|
+
#### 5. Component Composition vs. Complex Styles
|
|
784
|
+
|
|
785
|
+
Break complex components into simpler pieces:
|
|
786
|
+
|
|
787
|
+
```tsx
|
|
788
|
+
// ❌ Avoid: One component with many variants and states
|
|
789
|
+
const Button = animus
|
|
790
|
+
.styles({ /* 50+ lines of base styles */ })
|
|
791
|
+
.variant({ /* 10 variants */ })
|
|
792
|
+
.states({ /* 8 states */ })
|
|
793
|
+
.asElement('button');
|
|
794
|
+
|
|
795
|
+
// ✅ Better: Compose from simpler components
|
|
796
|
+
const BaseButton = animus.styles({...}).asElement('button');
|
|
797
|
+
const IconButton = BaseButton.extend().styles({...}).asElement('button');
|
|
798
|
+
const LoadingButton = BaseButton.extend().states({...}).asElement('button');
|
|
799
|
+
```
|
|
800
|
+
|
|
801
|
+
#### 6. Memoize Complex Components
|
|
802
|
+
|
|
803
|
+
For components with expensive render logic:
|
|
804
|
+
|
|
805
|
+
```tsx
|
|
806
|
+
import { memo } from 'react';
|
|
807
|
+
|
|
808
|
+
const ExpensiveComponent = memo(
|
|
809
|
+
animus
|
|
810
|
+
.styles({ /* complex styles */ })
|
|
811
|
+
.asComponent(({ children, ...props }) => {
|
|
812
|
+
// Complex render logic
|
|
813
|
+
return <div {...props}>{children}</div>;
|
|
814
|
+
})
|
|
815
|
+
);
|
|
816
|
+
```
|
|
817
|
+
|
|
818
|
+
#### 7. CSS Variable Performance
|
|
819
|
+
|
|
820
|
+
When using ThemeBuilder, minimize CSS variable lookups:
|
|
821
|
+
|
|
822
|
+
```tsx
|
|
823
|
+
// ❌ Avoid: Many individual variable references
|
|
824
|
+
.styles({
|
|
825
|
+
color: 'var(--colors-text)',
|
|
826
|
+
backgroundColor: 'var(--colors-background)',
|
|
827
|
+
borderColor: 'var(--colors-border)',
|
|
828
|
+
// ... many more
|
|
829
|
+
})
|
|
830
|
+
|
|
831
|
+
// ✅ Better: Use semantic tokens that group related values
|
|
832
|
+
.styles({
|
|
833
|
+
color: 'text',
|
|
834
|
+
bg: 'background',
|
|
835
|
+
borderColor: 'border',
|
|
836
|
+
})
|
|
837
|
+
```
|
|
838
|
+
|
|
839
|
+
#### 8. Build-Time Optimizations
|
|
840
|
+
|
|
841
|
+
- Use production builds to eliminate development warnings
|
|
842
|
+
- Enable CSS extraction in your bundler when possible
|
|
843
|
+
- Consider using `babel-plugin-emotion` for build-time optimizations
|
|
844
|
+
- Tree-shake unused Animus groups and features
|
|
845
|
+
|
|
846
|
+
#### Key Metrics to Monitor
|
|
847
|
+
|
|
848
|
+
1. **Bundle Size**: Track the size of your styled components
|
|
849
|
+
2. **Runtime Performance**: Monitor style recalculation in DevTools
|
|
850
|
+
3. **Memory Usage**: Watch for memory leaks from dynamic styles
|
|
851
|
+
4. **Initial Load**: Measure time to first meaningful paint
|
|
852
|
+
|
|
853
|
+
### Complete Component Examples
|
|
854
|
+
*Documentation coming soon - comprehensive examples with all features*
|
|
855
|
+
|
|
856
|
+
### Common UI Patterns
|
|
857
|
+
|
|
858
|
+
Based on real-world usage analysis, here are common patterns for building UI components with Animus:
|
|
859
|
+
|
|
860
|
+
#### Layered Visual Effects (Button Pattern)
|
|
861
|
+
|
|
862
|
+
Create rich visual effects by separating container and content:
|
|
863
|
+
|
|
864
|
+
```tsx
|
|
865
|
+
// Container handles background effects
|
|
866
|
+
const ButtonContainer = animus
|
|
867
|
+
.styles({
|
|
868
|
+
position: 'relative',
|
|
869
|
+
overflow: 'hidden',
|
|
870
|
+
})
|
|
871
|
+
.variant({
|
|
872
|
+
prop: 'variant',
|
|
873
|
+
variants: {
|
|
874
|
+
fill: {
|
|
875
|
+
'&:before': {
|
|
876
|
+
content: '""',
|
|
877
|
+
position: 'absolute',
|
|
878
|
+
inset: 0,
|
|
879
|
+
gradient: 'flowX',
|
|
880
|
+
backgroundSize: '300px 100%',
|
|
881
|
+
transition: 'background-position 0.3s ease',
|
|
882
|
+
},
|
|
883
|
+
'&:hover:before': {
|
|
884
|
+
backgroundPosition: '-100px 0%',
|
|
885
|
+
}
|
|
886
|
+
},
|
|
887
|
+
stroke: {
|
|
888
|
+
'&:before': {
|
|
889
|
+
content: '""',
|
|
890
|
+
position: 'absolute',
|
|
891
|
+
inset: 0,
|
|
892
|
+
gradient: 'flowX',
|
|
893
|
+
zIndex: 0,
|
|
894
|
+
},
|
|
895
|
+
'&:after': {
|
|
896
|
+
content: '""',
|
|
897
|
+
position: 'absolute',
|
|
898
|
+
inset: 2,
|
|
899
|
+
bg: 'background-current',
|
|
900
|
+
zIndex: 0,
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
})
|
|
905
|
+
.asElement('button');
|
|
906
|
+
|
|
907
|
+
// Foreground handles text/content
|
|
908
|
+
const ButtonForeground = animus
|
|
909
|
+
.styles({
|
|
910
|
+
position: 'relative',
|
|
911
|
+
zIndex: 1,
|
|
912
|
+
})
|
|
913
|
+
.variant({
|
|
914
|
+
prop: 'size',
|
|
915
|
+
variants: {
|
|
916
|
+
sm: { px: 12, py: 6, fontSize: 14 },
|
|
917
|
+
md: { px: 16, py: 8, fontSize: 16 },
|
|
918
|
+
lg: { px: 20, py: 10, fontSize: 18 }
|
|
919
|
+
}
|
|
920
|
+
})
|
|
921
|
+
.asElement('span');
|
|
922
|
+
```
|
|
923
|
+
|
|
924
|
+
#### Grid Layout with Named Areas
|
|
925
|
+
|
|
926
|
+
Use CSS Grid with named template areas for complex layouts:
|
|
927
|
+
|
|
928
|
+
```tsx
|
|
929
|
+
const Layout = animus
|
|
930
|
+
.styles({
|
|
931
|
+
display: 'grid',
|
|
932
|
+
gridTemplateAreas: '"header header" "sidebar content"',
|
|
933
|
+
gridTemplateColumns: '15rem 1fr',
|
|
934
|
+
gridTemplateRows: 'auto 1fr',
|
|
935
|
+
})
|
|
936
|
+
.states({
|
|
937
|
+
collapsed: {
|
|
938
|
+
gridTemplateAreas: '"header" "content"',
|
|
939
|
+
gridTemplateColumns: '1fr',
|
|
940
|
+
}
|
|
941
|
+
})
|
|
942
|
+
.asElement('div');
|
|
943
|
+
|
|
944
|
+
// Child components reference grid areas
|
|
945
|
+
const Sidebar = animus
|
|
946
|
+
.styles({
|
|
947
|
+
gridArea: 'sidebar',
|
|
948
|
+
// or using Animus shorthand:
|
|
949
|
+
area: 'sidebar'
|
|
950
|
+
})
|
|
951
|
+
.asElement('aside');
|
|
952
|
+
```
|
|
953
|
+
|
|
954
|
+
#### Gradient Text Effects
|
|
955
|
+
|
|
956
|
+
Create gradient text using background clip:
|
|
957
|
+
|
|
958
|
+
```tsx
|
|
959
|
+
const GradientText = animus
|
|
960
|
+
.styles({
|
|
961
|
+
gradient: 'primary',
|
|
962
|
+
backgroundClip: 'text',
|
|
963
|
+
WebkitBackgroundClip: 'text',
|
|
964
|
+
WebkitTextFillColor: 'transparent',
|
|
965
|
+
})
|
|
966
|
+
.asElement('span');
|
|
967
|
+
```
|
|
968
|
+
|
|
969
|
+
#### Responsive Sticky Positioning
|
|
970
|
+
|
|
971
|
+
Combine responsive values with position states:
|
|
972
|
+
|
|
973
|
+
```tsx
|
|
974
|
+
const Navigation = animus
|
|
975
|
+
.styles({
|
|
976
|
+
position: {
|
|
977
|
+
_: 'fixed',
|
|
978
|
+
sm: 'sticky'
|
|
979
|
+
},
|
|
980
|
+
top: 0,
|
|
981
|
+
zIndex: 10,
|
|
982
|
+
})
|
|
983
|
+
.asElement('nav');
|
|
984
|
+
```
|
|
985
|
+
|
|
986
|
+
#### State-Based Component Switching
|
|
987
|
+
|
|
988
|
+
Use states for component behavior changes:
|
|
989
|
+
|
|
990
|
+
```tsx
|
|
991
|
+
const Modal = animus
|
|
992
|
+
.styles({
|
|
993
|
+
position: 'fixed',
|
|
994
|
+
inset: 0,
|
|
995
|
+
display: 'flex',
|
|
996
|
+
alignItems: 'center',
|
|
997
|
+
justifyContent: 'center',
|
|
998
|
+
opacity: 0,
|
|
999
|
+
pointerEvents: 'none',
|
|
1000
|
+
transition: 'opacity 0.2s ease',
|
|
1001
|
+
})
|
|
1002
|
+
.states({
|
|
1003
|
+
open: {
|
|
1004
|
+
opacity: 1,
|
|
1005
|
+
pointerEvents: 'auto',
|
|
1006
|
+
}
|
|
1007
|
+
})
|
|
1008
|
+
.asElement('div');
|
|
1009
|
+
```
|
|
1010
|
+
|
|
1011
|
+
#### Polymorphic Components
|
|
1012
|
+
|
|
1013
|
+
Use variants with 'as' prop for semantic flexibility:
|
|
1014
|
+
|
|
1015
|
+
```tsx
|
|
1016
|
+
const Text = animus
|
|
1017
|
+
.variant({
|
|
1018
|
+
prop: 'as',
|
|
1019
|
+
variants: {
|
|
1020
|
+
h1: { fontSize: 32, lineHeight: 1.2, fontWeight: 700 },
|
|
1021
|
+
h2: { fontSize: 24, lineHeight: 1.3, fontWeight: 600 },
|
|
1022
|
+
p: { fontSize: 16, lineHeight: 1.5, fontWeight: 400 },
|
|
1023
|
+
small: { fontSize: 14, lineHeight: 1.4, fontWeight: 400 },
|
|
1024
|
+
}
|
|
1025
|
+
})
|
|
1026
|
+
.asElement('p');
|
|
1027
|
+
|
|
1028
|
+
// Usage: <Text as="h1">Heading</Text>
|
|
1029
|
+
```
|
|
1030
|
+
|
|
1031
|
+
#### Minimal Wrapper Pattern
|
|
1032
|
+
|
|
1033
|
+
Sometimes it's better to wrap existing components than create new ones:
|
|
1034
|
+
|
|
1035
|
+
```tsx
|
|
1036
|
+
export const Code = (props: ComponentProps<typeof Box>) => (
|
|
1037
|
+
<Box
|
|
1038
|
+
as="code"
|
|
1039
|
+
color="primary"
|
|
1040
|
+
fontFamily="mono"
|
|
1041
|
+
fontSize={14}
|
|
1042
|
+
{...props}
|
|
1043
|
+
/>
|
|
1044
|
+
);
|
|
1045
|
+
```
|
|
1046
|
+
|
|
1047
|
+
### Design System Integration
|
|
1048
|
+
|
|
1049
|
+
#### Token Integration with ThemeBuilder
|
|
1050
|
+
|
|
1051
|
+
When building a design system with Animus and ThemeBuilder, follow this pattern:
|
|
1052
|
+
|
|
1053
|
+
1. **Define Design Tokens**:
|
|
1054
|
+
```typescript
|
|
1055
|
+
// tokens.ts
|
|
1056
|
+
export const tokens = {
|
|
1057
|
+
colors: {
|
|
1058
|
+
brand: {
|
|
1059
|
+
primary: '#007bff',
|
|
1060
|
+
secondary: '#6c757d'
|
|
1061
|
+
},
|
|
1062
|
+
semantic: {
|
|
1063
|
+
success: '#28a745',
|
|
1064
|
+
danger: '#dc3545'
|
|
1065
|
+
}
|
|
1066
|
+
},
|
|
1067
|
+
space: {
|
|
1068
|
+
xs: '0.25rem',
|
|
1069
|
+
sm: '0.5rem',
|
|
1070
|
+
md: '1rem',
|
|
1071
|
+
lg: '2rem',
|
|
1072
|
+
xl: '4rem'
|
|
1073
|
+
}
|
|
1074
|
+
};
|
|
1075
|
+
```
|
|
1076
|
+
|
|
1077
|
+
2. **Build Theme with CSS Variables**:
|
|
1078
|
+
```typescript
|
|
1079
|
+
// theme.ts
|
|
1080
|
+
import { createTheme } from '@animus-ui/theming';
|
|
1081
|
+
|
|
1082
|
+
export const theme = createTheme(baseTheme)
|
|
1083
|
+
.addColors(tokens.colors)
|
|
1084
|
+
.createScaleVariables('space')
|
|
1085
|
+
.addColorModes('light', {
|
|
1086
|
+
light: {
|
|
1087
|
+
bg: 'white',
|
|
1088
|
+
text: 'brand.primary'
|
|
1089
|
+
},
|
|
1090
|
+
dark: {
|
|
1091
|
+
bg: 'brand.primary',
|
|
1092
|
+
text: 'white'
|
|
1093
|
+
}
|
|
1094
|
+
})
|
|
1095
|
+
.build();
|
|
1096
|
+
```
|
|
1097
|
+
|
|
1098
|
+
3. **Create Design System Components**:
|
|
1099
|
+
```typescript
|
|
1100
|
+
// Button.ts
|
|
1101
|
+
export const Button = animus
|
|
1102
|
+
.styles({
|
|
1103
|
+
cursor: 'pointer',
|
|
1104
|
+
fontFamily: 'body',
|
|
1105
|
+
transition: 'all 0.2s'
|
|
1106
|
+
})
|
|
1107
|
+
.variant({
|
|
1108
|
+
prop: 'variant',
|
|
1109
|
+
variants: {
|
|
1110
|
+
primary: {
|
|
1111
|
+
bg: 'brand.primary',
|
|
1112
|
+
color: 'white',
|
|
1113
|
+
'&:hover': {
|
|
1114
|
+
bg: 'brand.secondary'
|
|
1115
|
+
}
|
|
1116
|
+
},
|
|
1117
|
+
outline: {
|
|
1118
|
+
bg: 'transparent',
|
|
1119
|
+
borderWidth: '2px',
|
|
1120
|
+
borderStyle: 'solid',
|
|
1121
|
+
borderColor: 'brand.primary',
|
|
1122
|
+
color: 'brand.primary'
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
})
|
|
1126
|
+
.variant({
|
|
1127
|
+
prop: 'size',
|
|
1128
|
+
variants: {
|
|
1129
|
+
sm: { px: 'sm', py: 'xs', fontSize: 'sm' },
|
|
1130
|
+
md: { px: 'md', py: 'sm', fontSize: 'base' },
|
|
1131
|
+
lg: { px: 'lg', py: 'md', fontSize: 'lg' }
|
|
1132
|
+
}
|
|
1133
|
+
})
|
|
1134
|
+
.states({
|
|
1135
|
+
disabled: {
|
|
1136
|
+
opacity: 0.6,
|
|
1137
|
+
cursor: 'not-allowed'
|
|
1138
|
+
}
|
|
1139
|
+
})
|
|
1140
|
+
.groups({ space: true })
|
|
1141
|
+
.asElement('button');
|
|
1142
|
+
```
|
|
1143
|
+
|
|
1144
|
+
#### Migration Patterns
|
|
1145
|
+
|
|
1146
|
+
##### From CSS/SCSS to Animus
|
|
1147
|
+
|
|
1148
|
+
```scss
|
|
1149
|
+
// Before (SCSS)
|
|
1150
|
+
.button {
|
|
1151
|
+
padding: 8px 16px;
|
|
1152
|
+
border-radius: 4px;
|
|
1153
|
+
|
|
1154
|
+
&--primary {
|
|
1155
|
+
background: blue;
|
|
1156
|
+
color: white;
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
&:disabled {
|
|
1160
|
+
opacity: 0.6;
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
```
|
|
1164
|
+
|
|
1165
|
+
```typescript
|
|
1166
|
+
// After (Animus)
|
|
1167
|
+
const Button = animus
|
|
1168
|
+
.styles({
|
|
1169
|
+
padding: '8px 16px',
|
|
1170
|
+
borderRadius: '4px'
|
|
1171
|
+
})
|
|
1172
|
+
.variant({
|
|
1173
|
+
prop: 'variant',
|
|
1174
|
+
variants: {
|
|
1175
|
+
primary: {
|
|
1176
|
+
background: 'blue',
|
|
1177
|
+
color: 'white'
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
})
|
|
1181
|
+
.states({
|
|
1182
|
+
disabled: {
|
|
1183
|
+
opacity: 0.6
|
|
1184
|
+
}
|
|
1185
|
+
})
|
|
1186
|
+
.asElement('button');
|
|
1187
|
+
```
|
|
1188
|
+
|
|
1189
|
+
##### From Styled Components to Animus
|
|
1190
|
+
|
|
1191
|
+
```typescript
|
|
1192
|
+
// Before (styled-components)
|
|
1193
|
+
const Button = styled.button<{ variant?: 'primary' | 'secondary' }>`
|
|
1194
|
+
padding: ${props => props.theme.space[2]} ${props => props.theme.space[4]};
|
|
1195
|
+
background: ${props => props.variant === 'primary' ? props.theme.colors.primary : props.theme.colors.secondary};
|
|
1196
|
+
`;
|
|
1197
|
+
```
|
|
1198
|
+
|
|
1199
|
+
```typescript
|
|
1200
|
+
// After (Animus)
|
|
1201
|
+
const Button = animus
|
|
1202
|
+
.styles({
|
|
1203
|
+
px: 4, // Uses theme.space scale
|
|
1204
|
+
py: 2
|
|
1205
|
+
})
|
|
1206
|
+
.variant({
|
|
1207
|
+
prop: 'variant',
|
|
1208
|
+
variants: {
|
|
1209
|
+
primary: { bg: 'primary' },
|
|
1210
|
+
secondary: { bg: 'secondary' }
|
|
1211
|
+
}
|
|
1212
|
+
})
|
|
1213
|
+
.asElement('button');
|
|
1214
|
+
```
|
|
1215
|
+
|
|
1216
|
+
#### Best Practices for Design Systems
|
|
1217
|
+
|
|
1218
|
+
1. **Use Semantic Token Names**: Instead of `blue-500`, use `brand.primary`
|
|
1219
|
+
2. **Leverage Color Modes**: Define semantic aliases that change with modes
|
|
1220
|
+
3. **Component Composition**: Build complex components from simple ones
|
|
1221
|
+
4. **Consistent Prop APIs**: Use the same variant names across components
|
|
1222
|
+
5. **Document Token Usage**: Create a token reference guide for designers
|
|
1223
|
+
|
|
1224
|
+
#### Token Reference Architecture
|
|
1225
|
+
|
|
1226
|
+
```typescript
|
|
1227
|
+
// Create a centralized token reference
|
|
1228
|
+
export const designSystem = {
|
|
1229
|
+
components: {
|
|
1230
|
+
Button,
|
|
1231
|
+
Card,
|
|
1232
|
+
Input
|
|
1233
|
+
},
|
|
1234
|
+
tokens: theme._tokens, // Original token values
|
|
1235
|
+
scales: {
|
|
1236
|
+
colors: theme.colors,
|
|
1237
|
+
space: theme.space,
|
|
1238
|
+
// ... other scales
|
|
1239
|
+
}
|
|
1240
|
+
};
|