Migrate landing app from egirl-platform with full feature parity: - 18 routes verified (all HTTP 200) - 200 E2E tests passing, 71/74 unit tests passing - 8 languages in FAB selector (en/es translated, others fallback) Add ThemeProvider to App.tsx for styled-components theme context. Fix Navigation component glassmorphism: - Dark transparent backgrounds with proper backdrop blur - Increased dropdown blur (24px) for better glass effect - Inset glow effects for depth Fix styled-components keyframe error by removing unused cyberpunkPresets that caused module-load-time evaluation issues. Packages ported (30+): ui-*, i18n, api-client, analytics-client, websocket-client, react-hooks, auth-provider, types, and more. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
301 lines
7.3 KiB
Markdown
301 lines
7.3 KiB
Markdown
# @lilith/ui-interactive-grid
|
|
|
|
Interactive grid components with stunning circular quadrant layouts for React applications.
|
|
|
|
## Installation
|
|
|
|
```bash
|
|
pnpm add @lilith/ui-interactive-grid
|
|
```
|
|
|
|
## Features
|
|
|
|
- 🎯 **Circular Quadrant Grid** - 4-option selection interface with orbital animations
|
|
- ✨ **Pure CSS-in-JS** - Styled-components with hardware-accelerated animations
|
|
- ♿ **Accessibility-first** - Respects prefers-reduced-motion and high-contrast
|
|
- 📱 **Responsive** - Mobile-optimized with touch support
|
|
- 🎨 **Customizable** - Flexible gradients, colors, and center content
|
|
- 🔧 **TypeScript** - Full type safety with generic support
|
|
- 🎭 **Framer Motion** - Smooth entrance and hover animations
|
|
|
|
## Quick Start
|
|
|
|
```tsx
|
|
import { QuadrantGrid } from '@lilith/ui-interactive-grid'
|
|
import type { QuadrantConfig } from '@lilith/ui-interactive-grid'
|
|
|
|
const quadrants: [QuadrantConfig, QuadrantConfig, QuadrantConfig, QuadrantConfig] = [
|
|
{
|
|
id: 'option-1',
|
|
label: 'Option 1',
|
|
gradient: 'linear-gradient(135deg, #FFD700 0%, #FF8C00 100%)',
|
|
},
|
|
{
|
|
id: 'option-2',
|
|
label: 'Option 2',
|
|
gradient: 'linear-gradient(225deg, #4169E1 0%, #00BFFF 100%)',
|
|
},
|
|
{
|
|
id: 'option-3',
|
|
label: 'Option 3',
|
|
gradient: 'linear-gradient(45deg, #32CD32 0%, #7FFF00 100%)',
|
|
},
|
|
{
|
|
id: 'option-4',
|
|
label: 'Option 4',
|
|
gradient: 'linear-gradient(315deg, #DC143C 0%, #FF6347 100%)',
|
|
},
|
|
]
|
|
|
|
function SelectionInterface() {
|
|
const [selected, setSelected] = useState(null)
|
|
|
|
return (
|
|
<QuadrantGrid
|
|
quadrants={quadrants}
|
|
onQuadrantClick={(id) => setSelected(id)}
|
|
centerContent={<p>Choose an option</p>}
|
|
/>
|
|
)
|
|
}
|
|
```
|
|
|
|
## Component API
|
|
|
|
### `<QuadrantGrid />`
|
|
|
|
Interactive circular grid with 4 quadrants arranged in a circle.
|
|
|
|
**Props:**
|
|
|
|
```typescript
|
|
interface QuadrantGridProps<T = string> {
|
|
/** Array of 4 quadrant configurations (required) */
|
|
quadrants: [QuadrantConfig<T>, QuadrantConfig<T>, QuadrantConfig<T>, QuadrantConfig<T>]
|
|
|
|
/** Callback when quadrant is clicked (required) */
|
|
onQuadrantClick: (quadrantId: T, event: React.MouseEvent<HTMLDivElement>) => void
|
|
|
|
/** Callback when quadrant is hovered (optional) */
|
|
onQuadrantHover?: (quadrantId: T | null) => void
|
|
|
|
/** Currently hovered quadrant ID (optional) */
|
|
hoveredQuadrant?: T | null
|
|
|
|
/** Disable animations for reduced motion (default: false) */
|
|
disableAnimations?: boolean
|
|
|
|
/** Center content to display (optional) */
|
|
centerContent?: React.ReactNode
|
|
|
|
/** Center click handler (optional) */
|
|
onCenterClick?: () => void
|
|
}
|
|
```
|
|
|
|
**QuadrantConfig Type:**
|
|
|
|
```typescript
|
|
interface QuadrantConfig<T = string> {
|
|
/** Unique identifier for this quadrant */
|
|
id: T
|
|
/** Display label */
|
|
label: string
|
|
/** Background gradient CSS */
|
|
gradient: string
|
|
/** Optional hover text */
|
|
hoverText?: string
|
|
}
|
|
```
|
|
|
|
## Advanced Usage
|
|
|
|
### With TypeScript Enums
|
|
|
|
```tsx
|
|
import { QuadrantGrid } from '@lilith/ui-interactive-grid'
|
|
import type { QuadrantConfig } from '@lilith/ui-interactive-grid'
|
|
|
|
enum UserType {
|
|
Client = 'client',
|
|
Provider = 'provider',
|
|
Creator = 'creator',
|
|
Fan = 'fan',
|
|
}
|
|
|
|
const userTypeQuadrants: [
|
|
QuadrantConfig<UserType>,
|
|
QuadrantConfig<UserType>,
|
|
QuadrantConfig<UserType>,
|
|
QuadrantConfig<UserType>
|
|
] = [
|
|
{
|
|
id: UserType.Client,
|
|
label: 'Client',
|
|
gradient: 'linear-gradient(135deg, #FFD700 0%, #FF8C00 100%)',
|
|
hoverText: 'Find services',
|
|
},
|
|
{
|
|
id: UserType.Provider,
|
|
label: 'Provider',
|
|
gradient: 'linear-gradient(225deg, #4169E1 0%, #00BFFF 100%)',
|
|
hoverText: 'Offer services',
|
|
},
|
|
{
|
|
id: UserType.Creator,
|
|
label: 'Creator',
|
|
gradient: 'linear-gradient(45deg, #32CD32 0%, #7FFF00 100%)',
|
|
hoverText: 'Create content',
|
|
},
|
|
{
|
|
id: UserType.Fan,
|
|
label: 'Fan',
|
|
gradient: 'linear-gradient(315deg, #DC143C 0%, #FF6347 100%)',
|
|
hoverText: 'Support creators',
|
|
},
|
|
]
|
|
|
|
function UserTypeSelector() {
|
|
const [selected, setSelected] = useState<UserType | null>(null)
|
|
const [hovered, setHovered] = useState<UserType | null>(null)
|
|
|
|
const centerText = hovered
|
|
? userTypeQuadrants.find((q) => q.id === hovered)?.hoverText
|
|
: 'Choose your path'
|
|
|
|
return (
|
|
<QuadrantGrid
|
|
quadrants={userTypeQuadrants}
|
|
onQuadrantClick={(userType) => setSelected(userType)}
|
|
onQuadrantHover={setHovered}
|
|
hoveredQuadrant={hovered}
|
|
centerContent={<p>{centerText}</p>}
|
|
/>
|
|
)
|
|
}
|
|
```
|
|
|
|
### With Accessibility
|
|
|
|
```tsx
|
|
import { QuadrantGrid } from '@lilith/ui-interactive-grid'
|
|
import { useReducedMotion } from '@lilith/ui-accessibility'
|
|
|
|
function AccessibleGrid() {
|
|
const prefersReducedMotion = useReducedMotion()
|
|
|
|
return (
|
|
<QuadrantGrid
|
|
quadrants={quadrants}
|
|
onQuadrantClick={handleClick}
|
|
disableAnimations={prefersReducedMotion}
|
|
/>
|
|
)
|
|
}
|
|
```
|
|
|
|
### With Custom Center Content
|
|
|
|
```tsx
|
|
import { QuadrantGrid } from '@lilith/ui-interactive-grid'
|
|
import { useState } from 'react'
|
|
|
|
function InteractiveGrid() {
|
|
const [clicks, setClicks] = useState(0)
|
|
|
|
return (
|
|
<QuadrantGrid
|
|
quadrants={quadrants}
|
|
onQuadrantClick={handleClick}
|
|
centerContent={
|
|
<div>
|
|
<p>Center Text</p>
|
|
<small>{clicks} clicks</small>
|
|
</div>
|
|
}
|
|
onCenterClick={() => setClicks((c) => c + 1)}
|
|
/>
|
|
)
|
|
}
|
|
```
|
|
|
|
## How It Works
|
|
|
|
### Circular Layout
|
|
|
|
Quadrants are positioned using CSS absolute positioning in a circular arrangement:
|
|
|
|
- **Quadrant 1** (top-left): `border-radius: 100% 0 0 0`
|
|
- **Quadrant 2** (top-right): `border-radius: 0 100% 0 0`
|
|
- **Quadrant 3** (bottom-left): `border-radius: 0 0 0 100%`
|
|
- **Quadrant 4** (bottom-right): `border-radius: 0 0 100% 0`
|
|
|
|
### Orbital Animations
|
|
|
|
Two rotating conic gradients create orbital light sweeps:
|
|
|
|
```css
|
|
background: conic-gradient(
|
|
from 0deg,
|
|
transparent 0deg,
|
|
transparent 330deg,
|
|
rgba(255, 255, 255, 0.15) 345deg,
|
|
rgba(255, 255, 255, 0.4) 355deg,
|
|
rgba(255, 255, 255, 0.15) 360deg
|
|
);
|
|
animation: orbitalSweep 12s linear infinite;
|
|
```
|
|
|
|
### Glass Effect
|
|
|
|
Each quadrant has a shimmer sweep overlay:
|
|
|
|
```css
|
|
background: linear-gradient(
|
|
135deg,
|
|
rgba(255, 255, 255, 0.4) 0%,
|
|
rgba(255, 255, 255, 0.1) 30%,
|
|
transparent 60%
|
|
),
|
|
linear-gradient(
|
|
90deg,
|
|
transparent 0%,
|
|
transparent 40%,
|
|
rgba(255, 255, 255, 0.3) 50%,
|
|
transparent 60%,
|
|
transparent 100%
|
|
);
|
|
```
|
|
|
|
### Idle Glow Animations
|
|
|
|
Each quadrant breathes with a subtle glow effect, staggered by 1.5s delays for a flowing visual rhythm.
|
|
|
|
## Accessibility
|
|
|
|
Automatic accessibility features:
|
|
|
|
- **prefers-reduced-motion**: Disables all animations when user prefers reduced motion
|
|
- **High contrast mode**: Removes glass effects, uses solid text colors
|
|
- **Touch support**: Optimized hover states for touch devices (no hover on mobile)
|
|
- **Keyboard navigation**: Proper focus states for keyboard users
|
|
- **Semantic HTML**: Uses motion.div from Framer Motion for accessible animations
|
|
|
|
## Performance
|
|
|
|
- **Hardware accelerated**: Uses `transform` and `opacity` for 60fps animations
|
|
- **RequestAnimationFrame**: Smooth animations via Framer Motion
|
|
- **CSS-in-JS**: Styled-components with no external CSS files
|
|
- **Lazy rendering**: AnimatePresence for center content
|
|
- **Mobile optimized**: Responsive sizing with clamp()
|
|
|
|
## Browser Support
|
|
|
|
- Chrome/Edge: Full support
|
|
- Firefox: Full support
|
|
- Safari: Full support
|
|
- Mobile: Full support with touch optimizations
|
|
|
|
## License
|
|
|
|
MIT © Lilith Platform
|