@animus-ui/core 0.2.0-beta.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 +14 -0
- package/CLAUDE.md +895 -7
- package/dist/AnimusExtended.d.ts +43 -16
- package/dist/index.d.ts +8 -8
- package/dist/index.js +129 -73
- package/dist/properties/styledOptions.d.ts +1 -1
- package/dist/transforms/index.d.ts +2 -2
- package/package.json +2 -2
- package/dist/static-poc/atomic-css.d.ts +0 -14
- package/dist/static-poc/run-poc.d.ts +0 -5
- package/dist/static-poc/static-mode.d.ts +0 -15
- package/dist/static-poc/validate-poc.d.ts +0 -5
- package/docs/AGENT-QUESTIONS.md +0 -79
- package/docs/agentic-alliance.md +0 -415
- package/docs/ai-collaboration-guide.md +0 -141
- package/docs/critical-findings.md +0 -87
- package/docs/gemini-tamagui-compiler-learnings.md +0 -393
- package/docs/tamagui-compiler-learnings.md +0 -575
package/CLAUDE.md
CHANGED
|
@@ -173,6 +173,73 @@ const PrimaryButton = Button.extend()
|
|
|
173
173
|
4. **Trace style cascade** - Follow execution through createStylist
|
|
174
174
|
5. **Type errors** - Usually indicate incorrect method order or invalid configurations
|
|
175
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
|
+
|
|
176
243
|
## Practical Patterns & Examples
|
|
177
244
|
|
|
178
245
|
This section provides answers to common implementation questions for AI agents.
|
|
@@ -210,7 +277,83 @@ const Box = animus.groups({ space: true }).asElement('div');
|
|
|
210
277
|
```
|
|
211
278
|
|
|
212
279
|
### Theming Integration
|
|
213
|
-
|
|
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
|
|
214
357
|
|
|
215
358
|
### Complex Selectors
|
|
216
359
|
*Documentation coming soon - pseudo-selectors, nested selectors placement*
|
|
@@ -300,12 +443,158 @@ When extending a component using `.extend()` you can inject new styles to specif
|
|
|
300
443
|
|
|
301
444
|
|
|
302
445
|
### Animation Patterns
|
|
303
|
-
|
|
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
|
|
304
592
|
|
|
305
593
|
### Compound Components
|
|
306
|
-
*Documentation coming soon - Card.Header, Card.Body patterns*
|
|
307
594
|
|
|
308
|
-
Compound
|
|
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
|
|
309
598
|
|
|
310
599
|
```tsx
|
|
311
600
|
const Card = animus
|
|
@@ -336,17 +625,616 @@ Card.Body = CardBody;
|
|
|
336
625
|
export default Card;
|
|
337
626
|
```
|
|
338
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
|
+
|
|
339
680
|
### Global Styles & Resets
|
|
340
681
|
*Documentation coming soon - app-wide styles approach*
|
|
341
682
|
|
|
342
683
|
### Performance Considerations
|
|
343
|
-
|
|
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
|
|
344
852
|
|
|
345
853
|
### Complete Component Examples
|
|
346
854
|
*Documentation coming soon - comprehensive examples with all features*
|
|
347
855
|
|
|
348
856
|
### Common UI Patterns
|
|
349
|
-
|
|
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
|
+
```
|
|
350
1046
|
|
|
351
1047
|
### Design System Integration
|
|
352
|
-
|
|
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
|
+
};
|