svelte-split-flap
Version:
A Svelte component library for creating realistic split-flap displays with animations and sound effects
593 lines (465 loc) • 19.8 kB
Markdown
# Svelte Split-Flap
A modern Svelte component library for creating realistic split-flap displays with smooth animations and sound effects. Perfect for creating retro mechanical displays with authentic flip animations.
## Features
- ✅ **Smooth Animations**: Realistic split-flap flip effects
- ✅ **Customizable Styling**: Support for colors, fonts, and sizes
- ✅ **Sound Effects**: Realistic split-flap audio with Web Audio API
- ✅ **Svelte 5 Ready**: Built with modern Svelte 5 runes
- ✅ **Self-contained**: No external dependencies required
## Installation
```bash
npm install svelte-split-flap
```
## Quick Start
### Basic Usage
```svelte
<script>
import { SplitFlap } from 'svelte-split-flap';
import 'svelte-split-flap/styles.css';
let currentText = 'HELLO WORLD';
</script>
<SplitFlap text={currentText} />
```
### Dynamic Text Updates
```svelte
<script>
import { SplitFlap } from 'svelte-split-flap';
import 'svelte-split-flap/styles.css';
let messages = ['TOKYO SK07', 'BERLIN 12', 'ATHENS 97'];
let currentIndex = 0;
function changeText() {
currentIndex = (currentIndex + 1) % messages.length;
}
</script>
<SplitFlap text={messages[currentIndex]} size="large" fontFamily="din" />
<button onclick={changeText}>Change Text</button>
```
## Component API
### SplitFlap Props
| Prop | Type | Default | Description |
| -------------- | ------------------------------------------------------------------------------------------------------- | --------------------------------------------------- | ---------------------------------- |
| `text` | `string` | `''` | Text to display |
| `chars` | `string[]` | `'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 '.split('')` | Available character set |
| `length` | `number \| undefined` | `undefined` | Fixed display length with padding |
| `padChar` | `string` | `' '` | Character used for padding |
| `padMode` | `'auto' \| 'start' \| 'end'` | `'auto'` | Padding alignment |
| `timing` | `number` | `60` | Animation timing in milliseconds |
| `duration` | `number` | `300` | Flip duration in milliseconds |
| `easing` | `string` | `'ease-in-out'` | CSS easing function |
| `size` | `number \| string` | `'medium'` | Size variant or custom pixel size |
| `color` | `string` | `'white'` | Text color (CSS or Tailwind class) |
| `fontFamily` | `'din' \| 'brains' \| 'mono' \| 'ibm' \| 'roboto' \| 'mont' \| 'barlow' \| 'cabin' \| 'cabinCondensed'` | `'roboto'` | Font family |
| `fontWeight` | `'extralight' \| 'light' \| 'medium' \| 'semibold' \| 'bold'` | `'bold'` | Font weight |
| `soundEnabled` | `boolean` | `true` | Enable sound effects |
| `soundUrl` | `string` | `'default'` | Custom sound file URL |
| `styles` | `StyleOverrides` | `{}` | Custom CSS styles for components |
### Size Options
- `'xsmall'` - 16px
- `'small'` - 24px
- `'medium'` - 48px (default)
- `'large'` - 72px
- `'xlarge'` - 96px
- `'2xlarge'` - 120px
- `'3xlarge'` - 144px
- `number` - Custom pixel size (e.g., `64`)
### Style Overrides
The `styles` prop accepts an object with CSS strings for different component parts:
| Style Key | Target | Description |
| ------------ | ------------------------------- | ---------------------------------------------------------------------- |
| `container` | Main container div | Overall display container styles |
| `digit` | Individual character containers | Digit/character container styles |
| `flap` | Individual flap elements | Flap panel styles (background, borders, etc.) - applies to both halves |
| `flapTop` | Top flap half only | Styles for upper flap half only |
| `flapBottom` | Bottom flap half only | Styles for lower flap half only |
| `text` | Text content spans | Text styling within flaps |
| `hinge` | Hinge line between flaps | Center divider line styles |
## Examples
### Airport-Style Display
```svelte
<SplitFlap
text="FLIGHT AB1234"
color="#FFD700"
fontFamily="din"
fontWeight="semibold"
size="large"
length={16}
padMode="end"
/>
```
### Retro Counter
```svelte
<SplitFlap
text="000042"
color="green-400"
fontFamily="mono"
fontWeight="bold"
length={6}
padMode="start"
padChar="0"
/>
```
### Train Station Display
```svelte
<SplitFlap
text="PLATFORM 9¾"
color="white"
fontFamily="din"
fontWeight="medium"
timing={80}
duration={250}
soundEnabled={true}
/>
```
### Sound Configuration
```svelte
<!-- Default bundled sound -->
<SplitFlap text="HELLO" />
<!-- External URL - uses HTML Audio Element (CORS-friendly) -->
<SplitFlap text="HELLO" soundUrl="https://www.soundjay.com/communication/typewriter-2.wav" />
<!-- Custom local file in user's public folder -->
<SplitFlap text="HELLO" soundUrl="/my-custom-sound.mp3" />
<!-- Silent mode -->
<SplitFlap text="HELLO" soundEnabled={false} />
```
**Sound Loading Strategy:**
- **Default**: Uses bundled split-flap audio with full features (HTML Audio Element + Web Audio API for advanced features)
- **External URLs**: Uses HTML Audio Element only (basic playback, no panning/advanced features due to CORS)
- **Fallback**: Gracefully falls back to silent mode if audio fails to load
**Note**: External URLs have limited audio features due to browser CORS restrictions. For full feature support, use local files or the bundled sound.
### Custom Styling
#### Two-Tone Flaps (Different Colors for Top/Bottom)
```svelte
<SplitFlap
text="DEPARTURE"
styles={{
flapTop:
'background: linear-gradient(145deg, #4f46e5, #3730a3); border-bottom: 1px solid #1e1b4b;',
flapBottom:
'background: linear-gradient(145deg, #dc2626, #991b1b); border-top: 1px solid #7f1d1d;',
text: 'color: white; font-weight: bold; text-shadow: 0 1px 2px rgba(0,0,0,0.5);',
hinge: 'background: #1f2937; height: 2px; box-shadow: 0 1px 3px rgba(0,0,0,0.8);'
}}
fontFamily="din"
size="large"
/>
```
#### Classic Airport Style with Split Colors
```svelte
<SplitFlap
text="FLIGHT 1234"
styles={{
container: 'background: #1a1a1a; padding: 20px; border-radius: 8px;',
flapTop:
'background: linear-gradient(145deg, #2d2d2d, #1e1e1e); border-bottom: 1px solid #000;',
flapBottom:
'background: linear-gradient(145deg, #404040, #2d2d2d); border-top: 1px solid #555;',
text: 'color: #ffd700; font-weight: bold; text-shadow: 0 0 8px rgba(255,215,0,0.5);',
hinge: 'background: linear-gradient(90deg, #666, #888, #666); height: 2px;'
}}
fontFamily="din"
fontWeight="bold"
size="large"
/>
```
Override component styles for complete customization:
```svelte
<SplitFlap
text="CUSTOM"
styles={{
container:
'background: linear-gradient(45deg, #ff6b35, #f7931e); padding: 20px; border-radius: 12px;',
digit: 'border: 2px solid #fff; border-radius: 8px; margin: 4px;',
flap: 'background: linear-gradient(145deg, #2c3e50, #34495e); border: 1px solid #3498db;',
text: 'color: #ecf0f1; text-shadow: 0 1px 2px rgba(0,0,0,0.5);',
hinge: 'background: #e74c3c; height: 3px; box-shadow: 0 0 5px rgba(0,0,0,0.3);'
}}
/>
```
### Neon/Cyberpunk Style
```svelte
<SplitFlap
text="CYBER 2077"
styles={{
container: 'filter: drop-shadow(0 0 10px #00ff41);',
flap: 'background: linear-gradient(145deg, #000, #111); border: 1px solid #00ff41; box-shadow: inset 0 1px 1px rgba(0,255,65,0.1);',
text: 'color: #00ff41; text-shadow: 0 0 10px #00ff41, 0 0 20px #00ff41;',
hinge: 'background: #00ff41; height: 2px; box-shadow: 0 0 5px #00ff41;'
}}
fontFamily="mono"
size="large"
/>
```
### Vintage/Retro Style
```svelte
<SplitFlap
text="DEPARTURE 15:42"
styles={{
container:
'background: #8b4513; padding: 16px; border: 4px solid #654321; border-radius: 4px;',
flap: 'background: linear-gradient(145deg, #f4a460, #daa520); border: 1px solid #8b4513; box-shadow: inset 0 1px 2px rgba(255,255,255,0.2);',
text: 'color: #2f1b14; font-weight: bold; text-shadow: 0 1px 1px rgba(255,255,255,0.3);',
hinge: 'background: #654321; height: 3px;'
}}
fontFamily="din"
size="medium"
/>
```
### Minimalist Style
```svelte
<SplitFlap
text="MINIMAL"
styles={{
container: 'background: #f8f9fa; padding: 12px; border-radius: 8px;',
flap: 'background: #ffffff; border: 1px solid #e9ecef; box-shadow: 0 1px 3px rgba(0,0,0,0.1);',
text: 'color: #212529; font-weight: 500;',
hinge: 'background: #dee2e6; height: 1px;'
}}
fontFamily="ibm"
size="medium"
/>
```
## Font Configuration
### Using Built-in Web Fonts (Default)
The library uses web fonts by default for maximum compatibility:
```svelte
<SplitFlap text="HELLO" fontFamily="brains" />
<!-- JetBrains Mono -->
<SplitFlap text="HELLO" fontFamily="ibm" />
<!-- IBM Plex Mono -->
<SplitFlap text="HELLO" fontFamily="roboto" />
<!-- Roboto Mono -->
<SplitFlap text="HELLO" fontFamily="mont" />
<!-- Montserrat -->
<SplitFlap text="HELLO" fontFamily="barlow" />
<!-- Barlow Condensed -->
<SplitFlap text="HELLO" fontFamily="cabin" />
<!-- Cabin -->
<SplitFlap text="HELLO" fontFamily="cabinCondensed" /><!-- Cabin Condensed -->
```
### Font Family Options
| Font Family | Description | Best For |
| ---------------- | -------------------------------------------------- | ----------------------------------- |
| `brains` | JetBrains Mono - Clean monospace with coding focus | Technical displays, code-like text |
| `ibm` | IBM Plex Mono - Professional IBM typeface | Corporate, professional displays |
| `roboto` | Roboto Mono - Google's versatile monospace | Modern, clean displays |
| `mont` | Montserrat - Geometric sans-serif | Headlines, modern look |
| `barlow` | Barlow Condensed - Tall, condensed sans-serif | Space-efficient displays, headlines |
| `cabin` | Cabin - Humanist sans-serif with warmth | Friendly, approachable displays |
| `cabinCondensed` | Cabin Condensed - Space-saving version | Compact displays, multiple lines |
| `din` | DIN 1451 - German industrial standard | Authentic European transport look |
| `mono` | System monospace fallback | Universal compatibility |
### Font Showcase Examples
#### Modern Corporate Style (Barlow Condensed)
```svelte
<SplitFlap
text="QUARTERLY RESULTS"
fontFamily="barlow"
fontWeight="semibold"
size="large"
color="#0066cc"
styles={{
container:
'background: linear-gradient(135deg, #f8f9fa, #e9ecef); padding: 20px; border-radius: 8px;',
flap: 'background: linear-gradient(145deg, #ffffff, #f8f9fa); border: 1px solid #dee2e6;',
text: 'color: #0066cc; font-weight: 600;'
}}
/>
```
#### Friendly Information Display (Cabin)
```svelte
<SplitFlap
text="WELCOME GUESTS"
fontFamily="cabin"
fontWeight="medium"
size="large"
styles={{
container: 'background: #2c3e50; padding: 18px; border-radius: 12px;',
flap: 'background: linear-gradient(145deg, #ecf0f1, #bdc3c7); border: 1px solid #95a5a6;',
text: 'color: #2c3e50; font-weight: 500;'
}}
/>
```
#### Compact Multi-line Display (Cabin Condensed)
```svelte
<SplitFlap
text="GATE 12A BOARDING"
fontFamily="cabinCondensed"
fontWeight="bold"
size="medium"
styles={{
container: 'background: #1a1a1a; padding: 16px;',
flap: 'background: linear-gradient(145deg, #333, #222); border: 1px solid #444;',
text: 'color: #ffd700; font-weight: bold; text-shadow: 0 0 8px rgba(255,215,0,0.5);'
}}
/>
```
#### Old School Style with White Text (using new fonts)
```svelte
<SplitFlap
text="PLATFORM 9"
fontFamily="barlow"
fontWeight="medium"
timing={80}
duration={350}
easing="ease-in-out"
size={64}
styles={{
container:
'background: #2c1810; padding: 24px; border: 4px solid #8b4513; border-radius: 8px; box-shadow: inset 0 0 20px rgba(0,0,0,0.3), 0 8px 32px rgba(0,0,0,0.4);',
flap: 'background: linear-gradient(145deg, #f5f5dc, #e6e6fa); border: 2px solid #8b7355; box-shadow: inset 0 2px 4px rgba(0,0,0,0.1), 0 2px 6px rgba(0,0,0,0.2); border-radius: 2px;',
text: 'color: #2f2f2f; font-weight: 600; text-shadow: 0 1px 2px rgba(255,255,255,0.8);',
hinge: 'background: linear-gradient(90deg, #654321, #8b4513, #654321); height: 3px; box-shadow: 0 2px 4px rgba(0,0,0,0.5);'
}}
/>
```
## Development
### Prerequisites
- Node.js 20+
- npm or pnpm
### Setup
```bash
# Clone the repository
git clone https://github.com/socketopp/svelte-split-flap.git
cd svelte-split-flap
# Install dependencies
npm install
# Start development server
npm run dev
```
### Building the Library
```bash
# Build CSS and package the library
npm run build
# This runs:
# 1. npm run build:css - Generates Tailwind CSS from components
# 2. npm run prepack - Runs svelte-package and publint
```
### Testing Changes
The project includes a test environment to verify library functionality:
#### Method 1: Automated Testing (Recommended)
```bash
# Build library, pack it, and install in test project
npm run test-local
# This will:
# 1. Build the library (CSS + packaging)
# 2. Create svelte-split-flap-1.0.5.tgz
# 3. Install it in test-library/
# 4. Update dependencies
# Run the test project
cd test-library
npm run dev
```
#### Method 2: Manual Testing
```bash
# Build and pack the library
npm run build
npm pack
# Navigate to test project
cd test-library
# Remove old version and install new tarball
npm uninstall svelte-split-flap
npm install ../svelte-split-flap-[X.Y.Z].tgz
# Start test server
npm run dev
```
### Test Project Structure
The `test-library/` folder contains a minimal Svelte 5 application for testing:
```
test-library/
├── package.json # Svelte 5 + Vite setup
├── vite.config.js # Vite configuration
├── index.html # Entry HTML
└── src/
├── main.js # Svelte 5 mount syntax
└── App.svelte # Test component usage
```
### Testing Different Scenarios
Modify `test-library/src/App.svelte` to test various configurations:
```svelte
<script>
import { SplitFlap } from 'svelte-split-flap';
import 'svelte-split-flap/styles.css';
// Test different scenarios
let scenarios = [
{ text: 'HELLO WORLD', size: 'medium' },
{ text: 'FLIGHT AB1234', size: 'large', fontFamily: 'din' },
{ text: '000042', size: 64, fontFamily: 'mono', padMode: 'start' },
{ text: 'CUSTOM SOUND', soundUrl: 'https://www.soundjay.com/misc/beep-07a.wav' }
];
let currentScenario = $state(0);
</script>
{#each scenarios as scenario, i}
<div class="mb-4">
<h3>Scenario {i + 1}</h3>
<SplitFlap {...scenario} />
</div>
{/each}
```
### Verifying the Build
After building, check that these files exist:
- `dist/styles.css` - Generated Tailwind CSS
- `dist/components/` - Compiled Svelte components
- `dist/sounds/` - Audio files
- `svelte-split-flap-1.0.X.tgz` - Packaged library
### Publishing
```bash
# Ensure everything builds correctly
npm run build
# Publish to npm (requires npm login)
npm publish
```
## Troubleshooting
### Common Issues
**CSS not loading**: Make sure to import the CSS file:
```javascript
import 'svelte-split-flap/styles.css';
```
**Component not rendering**: Ensure you're using Svelte 5 syntax:
```javascript
import { mount } from 'svelte';
import App from './App.svelte';
mount(App, { target: document.getElementById('app') });
```
**Audio not playing**: Check browser console for audio loading errors. The component will fallback gracefully if audio files are unavailable.
**Build errors**: Ensure Node.js 20+ and compatible npm version.
**Custom sounds not working**:
- **External URLs**: Should work with any publicly accessible audio file. The library uses HTML Audio Element which doesn't have CORS restrictions.
- **Local Files**: Place audio files in your `public` folder (e.g., `/my-sound.mp3`)
- **File Formats**: Ensure the audio format is supported by browsers (MP3, WAV, OGG recommended)
- **HTTPS**: Some browsers require HTTPS for audio playback in production
## Browser Support
- Chrome 60+
- Firefox 55+
- Safari 11+
- Edge 79+
## License
MIT
## Contributing
1. Fork the repository
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Make your changes
4. Test using the test-library project
5. Commit your changes (`git commit -m 'Add amazing feature'`)
6. Push to the branch (`git push origin feature/amazing-feature`)
7. Open a Pull Request
### Development Workflow
1. Make changes to components in `src/lib/`
2. Test changes: `npm run test-local`
3. Verify in browser at `http://localhost:5173`
4. Update documentation if needed
5. Submit PR with clear description of changes
## Credits
Credits to original creators
- akira02 - [react-split-flap](https://github.com/akira02/react-split-flap)
- jayKayEss - [react-split-flap-effect](https://github.com/jayKayEss/react-split-flap-effect)
## Roadmap
- ✅ Custom styles parameter for all components
- [ ] Grid/multi-line displays
- [ ] Animation patterns
- [ ] More font options
- [ ] Custom sound effects
- [ ] Accessibility improvements
- [ ] Implement LongFlap
- [ ] Implement Emojis/Numbers
- [ ] Improve fonts, add capability for using fonts from fontsource
- [ ] Setup UI testing