Skip to content

socketopp/svelte-split-flap

Repository files navigation

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

npm install svelte-split-flap

Quick Start

Basic Usage

<script>
	import { SplitFlap } from 'svelte-split-flap';
	import 'svelte-split-flap/styles.css';

	let currentText = 'HELLO WORLD';
</script>

<SplitFlap text={currentText} />

Dynamic Text Updates

<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' 'din' Font family
fontWeight 'extralight' | 'light' | 'medium' | 'semibold' | 'bold' 'medium' 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.)
text Text content spans Text styling within flaps
hinge Hinge line between flaps Center divider line styles

Examples

Airport-Style Display

<SplitFlap
	text="FLIGHT AB1234"
	color="#FFD700"
	fontFamily="din"
	fontWeight="semibold"
	size="large"
	length={16}
	padMode="end"
/>

Retro Counter

<SplitFlap
	text="000042"
	color="green-400"
	fontFamily="mono"
	fontWeight="bold"
	length={6}
	padMode="start"
	padChar="0"
/>

Train Station Display

<SplitFlap
	text="PLATFORM 9¾"
	color="white"
	fontFamily="din"
	fontWeight="medium"
	timing={80}
	duration={250}
	soundEnabled={true}
/>

Sound Configuration

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

Override component styles for complete customization:

<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

<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

<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

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

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

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

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

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

<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

# 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

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

# 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

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

<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.5.tgz - Packaged library

Publishing

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

import 'svelte-split-flap/styles.css';

Component not rendering: Ensure you're using Svelte 5 syntax:

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

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

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published