UNPKG

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
# 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