BTS Theme Svelte component templates
npm install @coyalabs/bts-stylebash
npm install @coyalabs/bts-style
`
Quick Start
`svelte
Hello BTS Theme!
`
Design System
$3
- Background: #0B070D - Deep dark page background
- Default Text: #E3D8D8 - Light text and icons
- Toned: #A18F8F - Muted icons and accents
- Accent: #FFEFF6 - Interactive elements
$3
The package uses fonts from Google Fonts and Fontshare (loaded via CDN):
- Noto Serif KR (900) - Titles and button text
- Satoshi (400, 500, 700, 900) - Content and UI text
$3
All container-based components support these themes:
- full - Standard glass container with subtle background
- primary - Slightly more prominent glass effect
- secondary - Alternative glass styling
- vague - Subtle thing
- filled - Use for solid container sections (customizable)
---
Components
$3
#### BasePage
Root page component with automatic favicon setup, dark background, and decorative background elements.
Props:
- favicon?: string | null - Custom favicon URL (defaults to BTS theme icon)
- showBGDetails?: boolean - Show/hide decorative background elements (default: true)
- chainColor?: string - Color of the animated chain (default: "#130F15")
Example:
`svelte
`
Features:
- Sets min-height: 100vh
- Applies #0B070D background
- Automatically injects BTS favicon unless overridden
- Resets body/html margins
- Decorative BGDetails layer with animated gears and swinging chain
Background Details (BGDetails):
The BGDetails component renders decorative 1920s-style animated elements:
- Chain - A physics-simulated swinging chain in the top-left corner
- Gears - Rotating interlocked gears in the top-right corner
- Single Gear - A slowly rotating gear in the bottom-left corner
These elements are purely decorative (pointer-events: none) and sit behind your content.
Disable with showBGDetails={false}.
---
#### BaseContainer
Container with customizable corners and themes.
Props:
- theme?: 'full' | 'primary' | 'secondary' | 'filled' - Visual theme (default: 'full')
- padding?: string - CSS padding value (default: '1.5rem')
- borderRadiusTopLeft?: string - Top-left corner radius (default: '25px')
- borderRadiusTopRight?: string - Top-right corner radius (default: '25px')
- borderRadiusBottomLeft?: string - Bottom-left corner radius (default: '25px')
- borderRadiusBottomRight?: string - Bottom-right corner radius (default: '25px')
Example:
`svelte
`
---
#### BaseText
Typography component with three text variants.
Props:
- variant: 'title' | 'content' | 'button' - Text style variant
Variants:
- title - Noto Serif KR 900, 30px - For headings
- content - Satoshi 700, 18px - For body text
- button - Noto Serif KR 900, 17px - For button labels
Example:
`svelte
Page Title
This is content text.
`
---
#### BaseIcon
SVG icon wrapper with color variants.
Props:
- svg: string - SVG markup string
- variant?: 'default' | 'toned' - Color variant (default: 'default')
- size?: string - Icon size (default: '18px')
Example:
`svelte
`
---
$3
#### TextHeader
Header component with title and optional subtitle.
Props:
- title: string - Main heading text
- subtitle?: string - Optional subheading
Example:
`svelte
title="Welcome"
subtitle="Get started with BTS Theme"
/>
`
---
$3
#### Button
Clickable button extending BaseContainer with icon support.
Props:
- theme?: 'full' | 'primary' | 'secondary' | 'filled' - Button theme
- icon?: string - Left icon SVG
- actionIcon?: string | null - Right icon SVG (default: arrow, null to hide)
- iconRotation?: number - Left icon rotation in degrees
- actionIconRotation?: number - Right icon rotation in degrees
- iconSize?: string - Left icon size (default: '18px')
- actionIconSize?: string - Right icon size (default: '18px')
- All BaseContainer corner radius props
Events:
- Forwards all events including on:click
Example:
`svelte
theme="primary"
icon={icons.folder}
actionIconRotation={-45}
on:click={() => console.log('Clicked!')}
>
Open Folder
`
States:
- Hover: Subtle background lightening
- Active/Pressed: Visual feedback
---
#### IconButton
Circular icon-only button with hover effects.
Props:
- svg: string - Icon SVG markup
- variant?: 'default' | 'toned' - Icon color variant
- size?: string - Button size (default: '20px')
Events:
- Forwards all events including on:click
Example:
`svelte
svg={icons.cross}
variant="toned"
on:click={() => closeModal()}
/>
`
Effects:
- Hover: Scale 1.1, subtle background
- Active: Scale 0.95
---
#### InputBox
Text input field with icon support and theme matching.
Props:
- value?: string - Input value (bindable)
- placeholder?: string - Placeholder text
- type?: string - Input type (default: 'text')
- theme?: 'full' | 'primary' | 'secondary' | 'filled' - Visual theme
- icon?: string - Left icon SVG
- All BaseContainer corner radius props
Example:
`svelte
bind:value={username}
placeholder="Enter username..."
icon={icons.pen}
theme="primary"
/>
`
---
#### Toggle
Animated toggle switch with on/off states.
Props:
- checked?: boolean - Toggle state (bindable)
Example:
`svelte
State: {enabled ? 'On' : 'Off'}
`
---
#### Tooltip
Hover tooltip that displays above or below an icon.
Props:
- icon: string - Icon SVG markup
- text: string - Tooltip text content
- iconSize?: string - Icon size (default: '18px')
Example:
`svelte
icon={icons.folder}
text="This is a helpful explanation"
iconSize="20px"
/>
`
Features:
- Automatic positioning to fit on screen
- Min width 150px, max width 300px
---
#### TabBar
Vertical tab navigation with support for categorized groups.
Props:
- tabs: Array - Array of tabs and separators
- activeTab: string - Currently active tab ID (bindable)
- onTabChange?: (tabId: string) => void - Callback when tab changes
TabItem Type:
`typescript
type TabItem = {
id?: string; // Required for tabs, omit for separators
label: string; // Tab/separator text
icon?: string; // Optional icon SVG
type?: 'tab' | 'separator'; // Default: 'tab'
}
`
Example:
`svelte
{tabs}
bind:activeTab={currentTab}
onTabChange={(id) => console.log('Tab:', id)}
/>
`
Features:
- Vertical layout, 175px fixed width
- Active tab uses primary theme, inactive uses secondary
- Adaptive corner radius: 25px at group boundaries, 18px for middle tabs
- Text separators for grouping tabs into categories
- Categories have 1rem spacing between them
- Buttons within categories have 4px spacing
Corner Radius Behavior:
- First tab in a group: top corners 25px
- Last tab in a group: bottom corners 25px
- Middle tabs: all corners 18px
- Separators create group boundaries
---
#### TreeDirectory
Expandable file/folder tree with recursive structure.
Props:
- items: Array - Tree data structure
- showCount?: boolean - Show item counts in folders (default: false)
- itemIcon?: string - Custom icon for items
- topFoldersExpanded?: boolean - Expand top-level folders initially (default: false)
- draggable?: boolean - Enable drag and drop functionality (default: false)
- currentPath?: string - Current folder path for nested items (default: '')
- level?: number - Internal: Current nesting level (default: 0)
TreeItem Type:
`typescript
type TreeItem = {
name: string;
type: 'file' | 'folder';
children?: TreeItem[];
data?: any; // Custom data attached to item
variant?: 'full' | 'primary' | 'secondary' | 'filled' | 'special-filled'; // Theme variant
suffixIcon?: string; // SVG icon displayed on the right
}
`
Events:
- on:itemClick - Fired when any item is clicked
- event.detail.item - The clicked item object
- event.detail.type - Either 'file' or 'folder'
- on:rightClick - Fired when any item is right-clicked
- event.detail.item - The right-clicked item object
- event.detail.type - Either 'file' or 'folder'
- event.detail.event - The original mouse event (for positioning context menus)
- on:dragStart - Fired when dragging a file starts (requires draggable={true})
- event.detail.item - The dragged item object
- event.detail.sourcePath - Path of the source folder
- on:itemDrop - Fired when a file is dropped (requires draggable={true})
- event.detail.item - The dropped item data
- event.detail.sourcePath - Original folder path
- event.detail.targetPath - Destination folder path
Example:
`svelte
items={fileTree}
showCount={true}
topFoldersExpanded={true}
draggable={true}
on:itemClick={handleItemClick}
on:rightClick={handleRightClick}
on:itemDrop={handleDrop}
/>
`
Methods:
`svelte
`
Features:
- Slide animations (150ms, skipped on initial render)
- Recursive item counting
- State persistence
- Event forwarding for click handling
- Initial expansion option for top-level folders
- Adaptive corner radius based on nesting
- Padding increases with depth
- Per-item theme variants (override default folder/file themes)
- Suffix icon support for displaying icons on the right side
- Optional drag and drop functionality for files
- Visual feedback during drag operations (folder highlight)
- Path tracking for nested folder structures
Drag and Drop:
When draggable={true}:
- Only files can be dragged (folders are drop targets)
- Dragged files show reduced opacity
- Folders highlight when dragged over
- Drop on folders to move files into them
- Drop on empty space to move to current level
- on:itemDrop event provides source and target paths for handling moves
Customization:
Item Variants:
Each item can have a custom variant to override the default theme:
- Folders default to 'primary'
- Files default to 'secondary'
- Set variant on any item to use a different theme
Suffix Icons:
Display additional icons on the right side of items:
`svelte
{
name: 'config.json',
type: 'file',
suffixIcon: '' // Any SVG string
}
`
---
$3
#### Popup
Global modal popup overlay (singleton).
Usage:
`svelte
`
Features:
- Fade overlay (250ms)
- Fly dialog animation (300ms, backOut easing)
- Close button with toned icon
- ESC key support (built-in)
---
#### popupStore
Writable store for controlling the popup.
Methods:
##### open()
`typescript
popupStore.open(
title: string,
component: SvelteComponent,
props?: object,
subtitle?: string
)
`
Opens popup with custom component.
Example:
`svelte
`
##### confirm()
`typescript
popupStore.confirm(
title: string,
message: string,
options?: {
onConfirm?: () => void,
onCancel?: () => void,
confirmText?: string,
cancelText?: string
}
)
`
Shows confirmation dialog.
Example:
`svelte
`
##### alert()
`typescript
popupStore.alert(
title: string,
message: string,
options?: {
onOk?: () => void,
okText?: string
}
)
`
Shows alert dialog.
Example:
`svelte
popupStore.alert(
'Success',
'Your changes have been saved!',
{
onOk: () => navigateToHome(),
okText: 'Got it'
}
);
`
##### prompt()
`typescript
popupStore.prompt(
title: string,
message: string,
options?: {
onSubmit?: (value: string) => void,
onCancel?: () => void,
placeholder?: string,
submitText?: string,
cancelText?: string
}
)
`
Shows input prompt dialog.
Example:
`svelte
popupStore.prompt(
'Enter Name',
'Please provide your display name:',
{
onSubmit: (name) => updateProfile(name),
placeholder: 'Your name...',
submitText: 'Save',
cancelText: 'Skip'
}
);
`
##### close()
`typescript
popupStore.close()
`
Closes the current popup.
---
#### Popup Presets
The popup system includes three pre-built popup components that can be triggered via popupStore methods.
##### ConfirmPopup
A two-button confirmation dialog with confirm and cancel actions.
Features:
- Primary theme confirm button (left)
- Secondary theme cancel button (right)
- Callbacks: onConfirm, onCancel
- Customizable button text
Triggered via:
`svelte
popupStore.confirm(title, message, options)
`
Visual Layout:
- Message text displayed as subtitle
- Two buttons side-by-side
- Confirm button uses primary theme
- Cancel button uses secondary theme
---
##### AlertPopup
A single-button alert dialog for notifications.
Features:
- Single OK button with primary theme
- Callback: onOk
- Customizable button text
- Auto-closes on OK click
Triggered via:
`svelte
popupStore.alert(title, message, options)
`
Visual Layout:
- Message text displayed as subtitle
- Single centered OK button
- Primary theme button
---
##### PromptPopup
An input dialog that prompts the user for text input.
Features:
- InputBox component for text entry
- Submit button (primary theme)
- Cancel button (secondary theme)
- Callbacks: onSubmit(value), onCancel
- Customizable placeholder and button text
Triggered via:
`svelte
popupStore.prompt(title, message, options)
`
Visual Layout:
- Message text displayed as subtitle
- InputBox with customizable placeholder
- Two buttons: Submit (primary) and Cancel (secondary)
- Submit returns the input value to callback
Example with all options:
`svelte
popupStore.prompt(
'Rename File',
'Enter a new name for this file:',
{
placeholder: 'filename.txt',
submitText: 'Rename',
cancelText: 'Cancel',
onSubmit: (newName) => {
console.log('New name:', newName);
renameFile(newName);
},
onCancel: () => console.log('Rename cancelled')
}
);
`
---
$3
#### Toast
Global toast notification container (singleton).
Usage:
`svelte
`
Features:
- Bottom-right stacking
- Smooth slide animations (300ms in, 200ms out)
- Auto-dismiss with configurable duration
- Manual dismiss via close button
- Non-blocking, less intrusive than popups
---
#### toastStore
Writable store for controlling toast notifications.
Methods:
##### show()
`typescript
toastStore.show(
message: string,
duration?: number // Default: 4000ms, 0 = no auto-dismiss
): string // Returns toast ID
`
Shows a toast notification.
Example:
`svelte
`
##### dismiss()
`typescript
toastStore.dismiss(id: string)
`
Dismisses a specific toast by ID.
Example:
`svelte
`
##### dismissAll()
`typescript
toastStore.dismissAll()
`
Dismisses all active toasts.
Example:
`svelte
`
Visual Design:
- Uses Button component with secondary theme
- Help icon on the left (info indicator)
- Close icon (crossb) on the right
- Stacks vertically with 0.75rem gap
- Slides in from right, slides out to right
- Fixed position at bottom-right (2rem from edges)
- Z-index 10000 (above popups)
Best Practices:
Place once at your app root alongside :
`svelte
`
---
$3
Glowy components with unique styling and animations.
#### SpecialAction
A special-themed button with gradient background and optional tooltip.
Props:
- label: string - Button text
- tooltipText?: string - Optional tooltip text displayed on the right
Example:
`svelte
label="AI Generate"
tooltipText="Uses AI to generate content"
/>
`
Features:
- Purple gradient background (special-filled theme)
- AI icon on the left
- Tooltip positioned at far right (when provided)
- Enhanced hover effect with brighter gradient
- Pressed state with darker gradient
For?
- AI actions.
Styling:
- Background: Linear gradient purple tones
- Enhanced glow effects on hover
- User-select disabled for better UX
---
#### SpecialParagraph
Animated text component that reveals words sequentially with fade and blur effects.
Props:
- text: string - The text content to animate
- wordDelay?: number - Delay between each word appearing in ms (default: 50)
- startDelay?: number - Initial delay before animation starts in ms (default: 0)
- animationDuration?: number - Animation duration for each word in ms (default: 300)
- variant?: 'title' | 'content' | 'button' - Text styling variant (default: 'content')
- textModifier?: string - Font size adjustment (default: '0px')
- autoPlay?: boolean - Start animation automatically on mount (default: true)
Methods:
- play() - Start/restart the animation
- reset() - Reset to hidden state
- showAll() - Show all words immediately
Example:
`svelte
text="This text will animate in word by word with smooth effects"
wordDelay={80}
variant="content"
/>
bind:this={paragraph}
text="Click the button to animate!"
autoPlay={false}
/>
`
Animation Effects:
- Each word fades in from opacity 0 to 1
- Slides up from 8px translateY offset
- Blur transitions from 4px to 0
- Smooth easing transitions
For?
- Fancier AI.
Use Cases:
- Intro text animations
- Loading state messages
- Drawing attention to important content
- Storytelling and narrative interfaces
---
#### ContextMenu
Categorized menu component with separator support, similar to TabBar layout.
Props:
- items: Array - Array of menu items and separators
- selectedValue?: any - Currently selected item value
- onSelect?: (value: any) => void - Callback when item is selected
ContextMenuItem Type:
`typescript
type ContextMenuItem = {
label: string;
value?: any; // Required for items, omit for separators
disabled?: boolean;
type?: 'item' | 'separator'; // Default: 'item'
}
`
Example:
`svelte
items={menuItems}
{selectedValue}
onSelect={(val) => handleAction(val)}
/>
`
Features:
- Category grouping with separator labels
- Selected item highlighting
- Disabled item support with reduced opacity
- Hover effects on enabled items
- Filled theme container
- Automatic category spacing and borders
- Text ellipsis for long labels
Visual Layout:
- Categories separated by labeled dividers
- First category has no top border
- Subsequent categories have subtle top border
- 0.5rem padding around separators
- 4px spacing between items
---
#### Dropdown
Select dropdown with collapsible options menu.
Props:
- label: string - Default button text before selection
- icon?: string - Optional left icon SVG
- theme?: 'full' | 'primary' | 'secondary' - Button theme (default: 'full')
- width?: string - Fixed width for dropdown (default: '200px')
- options: Array - Array of selectable options
- value?: any - Currently selected value (bindable)
- onChange?: (value: any) => void - Callback when selection changes
- All BaseContainer corner radius props
DropdownOption Type:
`typescript
type DropdownOption = {
label: string;
value: any;
disabled?: boolean;
type?: 'item' | 'separator'; // Optional: use for category separation
}
`
Example:
`svelte
label="Select an option"
icon={icons.folder}
theme="primary"
width="250px"
{options}
bind:value={selectedValue}
onChange={(val) => console.log('Selected:', val)}
/>
`
Features:
- Fixed width with text truncation (ellipsis)
- Expand icon rotates 180° when open
- Slide animation for menu (150ms)
- Click outside to close
- Uses ContextMenu component internally
- Support for category separators
- Selected item highlighted
- Disabled items shown with reduced opacity
---
#### LinearList
Vertical list component with customizable actions for each item.
Props:
- items: Array - Array of list items
ListItem Type:
`typescript
type CustomAction = {
label: string;
actionIcon?: string;
}
type ListItem = {
data?: any; // Custom data attached to item
customActions?: CustomAction[];
removeButton?: boolean;
}
`
Events:
- on:action - Fired when any custom action is clicked
- event.detail.index - Item index
- event.detail.actionLabel - Action label that was clicked
- event.detail.item - The item object
- on:remove - Fired when remove button is clicked
- event.detail.index - Item index
- event.detail.item - The item object
Example:
`svelte
{items}
on:action={handleAction}
on:remove={handleRemove}
>
{#snippet children({ item, index })}
{item.data.name}
{/snippet}
`
Features:
- Left-aligned content slot with item and index props
- Right-aligned action buttons (horizontal)
- Optional remove button (icon-only, toned variant)
- 1px bottom border separator (rgba(161, 143, 143, 0.24))
- No border on last item
- 10px vertical padding per item
- No horizontal padding
- No spacing between items
- Event-based action handling
Visual Layout:
- Each item is a flex row with space-between
- Content on the left, actions on the right
- Actions have 0.5rem gap between them
- Remove button appears at the end of actions
- Borders are internal strokes (bottom edge only)
---
#### Separator
Decorative SVG separator with tiled middle section.
Props:
- height?: string - Separator height (default: '12px')
- width?: string - Separator width (default: '100%')
- margin?: string - CSS margin (default: '2rem 0')
Example:
`svelte
`
Features:
- Three-part design: left piece, tiled middle, right piece
- Inline SVG data URLs for performance
- Scales to container width
- Elegant visual break between sections
---
$3
The package exports a collection of built-in SVG icons.
Usage:
`svelte
`
Available Icons:
- arrow - Right arrow navigation
- folder - Folder icon
- icon_expand - Expand/collapse chevron
- cross - Close/dismiss X
- pen - Edit/write pen
Custom Icons:
You can use any SVG string with icon-supporting components:
`svelte
`
---
Styling
$3
All components extending BaseContainer support individual corner radius props:
`svelte
borderRadiusTopLeft="35px"
borderRadiusTopRight="35px"
borderRadiusBottomLeft="15px"
borderRadiusBottomRight="15px"
theme="primary"
>
Asymmetric Button
`
$3
You can customize filled theme backgrounds by targeting CSS variables or extending components.
---
Best Practices
$3
`svelte
`
$3
Place once at your app root:
`svelte
`
$3
For consistent styling, prefer using BaseIcon over raw SVG:
`svelte
{@html myIcon}
`
---
Development
$3
Link the package locally for testing:
`bash
In the package directory
cd @bts-theme/bts-theme
npm run package
npm link
In your project
npm link @coyalabs/bts-style
`
After making changes:
`bash
npm run package
`
Note: You may need to clear Vite cache after rebuilding:
`bash
rm -rf node_modules/.vite
`
$3
`bash
npm run release # Bumps version, packages, and publishes
`
---
TypeScript Support
All components include TypeScript definitions. Import types as needed:
`typescript
import type { TreeItem } from '@coyalabs/bts-style';
`
---
Package Structure
`
@coyalabs/bts-style/
├── dist/ # Compiled package
├── public/ # Static assets
│ ├── favicon.svg # Default BTS favicon
│ └── PLACE_YOUR_IMAGES_HERE.txt
├── src/
│ ├── Base/ # Base components
│ ├── Components/ # Interactive components
│ ├── Structure/ # Layout components
│ ├── icons.js # Icon definitions
│ └── index.ts # Main export
└── package.json
``