shimmer-ui
A practical guide to magic.mov's UI component system and how to use it in your spells.
Interactive Component Tour
Explore our components interactively using the Component Tour:
<script>
import { ComponentTour } from '$lib/components/ui';
</script>
<ComponentTour
component="Button"
variants={['primary', 'secondary', 'ghost']}
showCode={true}
initialProps={{
size: 'lg',
disabled: false
}}
/>
The tour provides:
- Live component demonstrations
- Real-time prop customization
- Code snippets with TypeScript hints
- Visual state management
- Variant switching
Component Structure
Our UI components are organized in $lib/components/ui
following the shadcn-svelte pattern.
Core Components
$lib/components/ui/
├── button/
├── card/
├── dialog/
├── sheet/
├── input/
├── form/
├── ... (and more standard shadcn components)
Custom Film-Inspired Components
These are unique to magic.mov and add cinematic flair to our UI:
- FilmGrainBackground.svelte: Adds subtle film grain texture to backgrounds
- Spotlight.svelte: Creates a spotlight effect that follows cursor movement
- ShinyText.svelte: Text with a subtle shimmer effect
- DotBackground.svelte: Creates a dot matrix background pattern
Using Components
Import components directly from the UI folder:
<script>
import { Button } from '$lib/components/ui/button';
import { Card } from '$lib/components/ui/card';
import { FilmGrainBackground } from '$lib/components/ui';
</script>
Basic Example
<script>
import { Button } from '$lib/components/ui/button';
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '$lib/components/ui/card';
</script>
<Card>
<CardHeader>
<CardTitle>Card Title</CardTitle>
</CardHeader>
<CardContent>
<p>This is a basic card component with a button.</p>
</CardContent>
<CardFooter>
<Button>Action</Button>
</CardFooter>
</Card>
Film-Inspired Effects
FilmGrainBackground
Adds a subtle film grain texture to create a cinematic atmosphere.
<script>
import { FilmGrainBackground } from '$lib/components/ui';
</script>
<div class="relative h-screen">
<FilmGrainBackground grainOpacity={0.05} />
<div class="relative z-10">
<!-- Your content here -->
</div>
</div>
Spotlight
Creates a spotlight effect that follows the cursor.
<script>
import { Spotlight } from '$lib/components/ui';
</script>
<div class="relative h-screen">
<Spotlight size={300} opacity={0.15} />
<div class="relative z-10">
<!-- Your content here -->
</div>
</div>
ShinyText
Text with a subtle shimmer effect.
<script>
import { ShinyText } from '$lib/components/ui';
</script>
<ShinyText>magic.mov</ShinyText>
Component Architecture
All components follow a consistent pattern:
- Index files - Each component folder has an index.ts that exports the component
- Variants - Components use the cn utility for variants through Tailwind classes
- Accessibility - ARIA attributes are properly implemented
- Dark mode - Components automatically adapt to light/dark mode
Component Categories
Our components are organized into three main categories:
Core UI Components
- Basic elements (buttons, inputs, etc.)
- Layout components
- Navigation elements
Cinematic Components
- Visual effects (FilmGrain, Spotlight)
- Animation components
- Text effects (ShinyText, CinematicText)
Spell-Specific Components
- Custom components for each spell
- Specialized UI elements
- Interactive tools
Best Practices
- Use Svelte 5 Runes (
$state
,$derived
,$effect
,$props
) for state management - Keep components small and focused on a single responsibility
- Leverage Tailwind's utility classes for styling
- Ensure components are fully keyboard accessible
- Test components across browsers and screen sizes
- Use the Component Tour to explore and understand components before implementation
Creating New Components
When creating new components for a spell:
- Start by looking for existing components in
$lib/components/ui
- Create spell-specific components in
src/spells/[spell-name]/components
- Use the cn utility for conditional class names
- Add your component to the Component Tour for documentation
<script lang="ts">
import { cn } from '$lib/utils';
let { class: className } = $props<{ class?: string }>();
</script>
<div class={cn("base-styles", className)}>
<slot />
</div>
For detailed styling information, see the Styles page.