npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@coyalabs/bts-style

v1.3.10

Published

BTS Theme Svelte component templates

Downloads

4,424

Readme

@coyalabs/bts-style

Coya Behind the Scenes - A Svelte 4 component library with a serif 1920s aesthetic.

Installation

npm install @coyalabs/bts-style

Quick Start

<script>
  import { BasePage, Button, BaseText } from '@coyalabs/bts-style';
</script>

<BasePage>
  <BaseText variant="title">Hello BTS Theme!</BaseText>
  <Button theme="primary">Click Me</Button>
</BasePage>

Design System

Colors

  • Background: #0B070D - Deep dark page background
  • Default Text: #E3D8D8 - Light text and icons
  • Toned: #A18F8F - Muted icons and accents
  • Accent: #FFEFF6 - Interactive elements

Fonts

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

Theme Variants

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

Base Components

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:

<BasePage favicon="/custom-icon.svg">
  <!-- Your app content -->
</BasePage>

<!-- Without background decorations -->
<BasePage showBGDetails={false}>
  <!-- Clean page without gears/chains -->
</BasePage>

<!-- Custom chain color -->
<BasePage chainColor="#1a1520">
  <!-- Content with custom chain color -->
</BasePage>

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:

<BaseContainer theme="primary" borderRadiusTopLeft="35px">
  <slot />
</BaseContainer>

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:

<BaseText variant="title">Page Title</BaseText>
<BaseText variant="content">This is content text.</BaseText>

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:

<script>
  import { BaseIcon, icons } from '@coyalabs/bts-style';
</script>

<BaseIcon svg={icons.folder} variant="toned" size="24px" />

Structure Components

TextHeader

Header component with title and optional subtitle.

Props:

  • title: string - Main heading text
  • subtitle?: string - Optional subheading

Example:

<TextHeader 
  title="Welcome" 
  subtitle="Get started with BTS Theme" 
/>

Interactive Components

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:

<script>
  import { Button, icons } from '@coyalabs/bts-style';
</script>

<Button 
  theme="primary" 
  icon={icons.folder}
  actionIconRotation={-45}
  on:click={() => console.log('Clicked!')}
>
  Open Folder
</Button>

<!-- Button without action icon -->
<Button actionIcon={null} theme="secondary">
  Simple Button
</Button>

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:

<script>
  import { IconButton, icons } from '@coyalabs/bts-style';
</script>

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

<script>
  import { InputBox, icons } from '@coyalabs/bts-style';
  let username = '';
</script>

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

<script>
  import { Toggle } from '@coyalabs/bts-style';
  let enabled = false;
</script>

<Toggle bind:checked={enabled} />
<p>State: {enabled ? 'On' : 'Off'}</p>

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:

<script>
  import { Tooltip, icons } from '@coyalabs/bts-style';
</script>

<Tooltip 
  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<TabItem> - Array of tabs and separators
  • activeTab: string - Currently active tab ID (bindable)
  • onTabChange?: (tabId: string) => void - Callback when tab changes

TabItem Type:

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:

<script>
  import { TabBar, icons } from '@coyalabs/bts-style';
  
  let currentTab = 'home';
  
  const tabs = [
    { type: 'separator', label: 'Navigation' },
    { id: 'home', label: 'Home', icon: icons.folder },
    { id: 'about', label: 'About' },
    { type: 'separator', label: 'Account' },
    { id: 'settings', label: 'Settings' },
    { id: 'profile', label: 'Profile' }
  ];
</script>

<TabBar 
  {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<TreeItem> - 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:

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:

<script>
  import { TreeDirectory, icons } from '@coyalabs/bts-style';
  
  const fileTree = [
    {
      name: 'src',
      type: 'folder',
      variant: 'primary',
      children: [
        { 
          name: 'App.svelte', 
          type: 'file',
          variant: 'secondary',
          suffixIcon: icons.pen 
        },
        { name: 'main.js', type: 'file' }
      ]
    },
    { 
      name: 'README.md', 
      type: 'file',
      suffixIcon: '<svg>...</svg>'
    }
  ];
  
  function handleItemClick(event) {
    const { item, type } = event.detail;
    console.log(`Clicked ${type}:`, item.name);
  }
  
  function handleRightClick(event) {
    const { item, type, event: mouseEvent } = event.detail;
    console.log(`Right-clicked ${type}:`, item.name);
    // Use mouseEvent.clientX and mouseEvent.clientY for positioning context menus
  }
  
  function handleDrop(event) {
    const { item, sourcePath, targetPath } = event.detail;
    console.log(`Moved ${item.name} from ${sourcePath} to ${targetPath}`);
  }
</script>

<TreeDirectory 
  items={fileTree} 
  showCount={true}
  topFoldersExpanded={true}
  draggable={true}
  on:itemClick={handleItemClick}
  on:rightClick={handleRightClick}
  on:itemDrop={handleDrop}
/>

Methods:

<script>
  let tree;
  
  // Get current state
  const state = tree.getState();
  
  // Restore state
  tree.setState(state);
  
  // Expand all folders
  tree.expandAll();
  
  // Collapse all folders
  tree.collapseAll();
</script>

<TreeDirectory bind:this={tree} items={fileTree} />

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:

{
  name: 'config.json',
  type: 'file',
  suffixIcon: '<svg>...</svg>'  // Any SVG string
}

Popup System

Popup

Global modal popup overlay (singleton).

Usage:

<script>
  import { Popup, popupStore } from '@coyalabs/bts-style';
</script>

<!-- Place once at app root -->
<Popup />

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()
popupStore.open(
  title: string,
  component: SvelteComponent,
  props?: object,
  subtitle?: string
)

Opens popup with custom component.

Example:

<script>
  import { popupStore } from '@coyalabs/bts-style';
  import MyCustomPopup from './MyCustomPopup.svelte';
</script>

<button on:click={() => 
  popupStore.open(
    'Settings', 
    MyCustomPopup, 
    { userId: 123 },
    'Configure your preferences'
  )
}>
  Open Settings
</button>
confirm()
popupStore.confirm(
  title: string,
  message: string,
  options?: {
    onConfirm?: () => void,
    onCancel?: () => void,
    confirmText?: string,
    cancelText?: string
  }
)

Shows confirmation dialog.

Example:

<button on:click={() => 
  popupStore.confirm(
    'Delete Item',
    'Are you sure? This cannot be undone.',
    {
      onConfirm: () => deleteItem(),
      onCancel: () => console.log('Cancelled'),
      confirmText: 'Delete',
      cancelText: 'Keep'
    }
  )
}>
  Delete
</button>
alert()
popupStore.alert(
  title: string,
  message: string,
  options?: {
    onOk?: () => void,
    okText?: string
  }
)

Shows alert dialog.

Example:

popupStore.alert(
  'Success',
  'Your changes have been saved!',
  {
    onOk: () => navigateToHome(),
    okText: 'Got it'
  }
);
prompt()
popupStore.prompt(
  title: string,
  message: string,
  options?: {
    onSubmit?: (value: string) => void,
    onCancel?: () => void,
    placeholder?: string,
    submitText?: string,
    cancelText?: string
  }
)

Shows input prompt dialog.

Example:

popupStore.prompt(
  'Enter Name',
  'Please provide your display name:',
  {
    onSubmit: (name) => updateProfile(name),
    placeholder: 'Your name...',
    submitText: 'Save',
    cancelText: 'Skip'
  }
);
close()
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:

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:

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:

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:

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')
  }
);

Toast System

Toast

Global toast notification container (singleton).

Usage:

<script>
  import { Toast, toastStore } from '@coyalabs/bts-style';
</script>

<!-- Place once at app root -->
<Toast />

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()
toastStore.show(
  message: string,
  duration?: number  // Default: 4000ms, 0 = no auto-dismiss
): string  // Returns toast ID

Shows a toast notification.

Example:

<script>
  import { toastStore } from '@coyalabs/bts-style';
</script>

<button on:click={() => toastStore.show('File saved successfully!')}>
  Save
</button>

<!-- Custom duration -->
<button on:click={() => toastStore.show('Processing...', 2000)}>
  Process
</button>

<!-- No auto-dismiss -->
<button on:click={() => toastStore.show('Important message', 0)}>
  Important
</button>
dismiss()
toastStore.dismiss(id: string)

Dismisses a specific toast by ID.

Example:

<script>
  const toastId = toastStore.show('Click to dismiss', 0);
</script>

<button on:click={() => toastStore.dismiss(toastId)}>
  Dismiss Toast
</button>
dismissAll()
toastStore.dismissAll()

Dismisses all active toasts.

Example:

<button on:click={() => toastStore.dismissAll()}>
  Clear All Toasts
</button>

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 <Toast /> once at your app root alongside <Popup />:

<!-- App.svelte -->
<script>
  import { BasePage, Popup, Toast } from '@coyalabs/bts-style';
</script>

<BasePage>
  <!-- App content -->
</BasePage>

<Popup />
<Toast />

Special Components

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:

<script>
  import { SpecialAction } from '@coyalabs/bts-style';
</script>

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

<script>
  import { SpecialParagraph } from '@coyalabs/bts-style';
  let paragraph;
</script>

<!-- Auto-playing paragraph -->
<SpecialParagraph 
  text="This text will animate in word by word with smooth effects"
  wordDelay={80}
  variant="content"
/>

<!-- Manual control -->
<SpecialParagraph 
  bind:this={paragraph}
  text="Click the button to animate!"
  autoPlay={false}
/>
<button on:click={() => paragraph.play()}>Play Animation</button>

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<ContextMenuItem> - Array of menu items and separators
  • selectedValue?: any - Currently selected item value
  • onSelect?: (value: any) => void - Callback when item is selected

ContextMenuItem Type:

type ContextMenuItem = {
  label: string;
  value?: any;           // Required for items, omit for separators
  disabled?: boolean;
  type?: 'item' | 'separator';  // Default: 'item'
}

Example:

<script>
  import { ContextMenu } from '@coyalabs/bts-style';
  
  let selectedValue = 'cut';
  
  const menuItems = [
    { label: 'Edit', type: 'separator' },
    { label: 'Cut', value: 'cut' },
    { label: 'Copy', value: 'copy' },
    { label: 'Paste', value: 'paste', disabled: true },
    { label: 'View', type: 'separator' },
    { label: 'Zoom In', value: 'zoomIn' },
    { label: 'Zoom Out', value: 'zoomOut' }
  ];
</script>

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

type DropdownOption = {
  label: string;
  value: any;
  disabled?: boolean;
  type?: 'item' | 'separator';  // Optional: use for category separation
}

Example:

<script>
  import { Dropdown, icons } from '@coyalabs/bts-style';
  
  let selectedValue = 'option1';
  
  const options = [
    { label: 'Basic', type: 'separator' },
    { label: 'Option 1', value: 'option1' },
    { label: 'Option 2', value: 'option2' },
    { label: 'Advanced', type: 'separator' },
    { label: 'Option 3', value: 'option3' },
    { label: 'Disabled', value: 'option4', disabled: true }
  ];
</script>

<Dropdown
  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<ListItem> - Array of list items

ListItem Type:

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:

<script>
  import { LinearList, icons } from '@coyalabs/bts-style';
  
  const items = [
    {
      data: { id: 1, name: 'First Item' },
      customActions: [
        { label: 'Edit', actionIcon: icons.pen },
        { label: 'View' }
      ],
      removeButton: true
    },
    {
      data: { id: 2, name: 'Second Item' },
      customActions: [
        { label: 'Download' }
      ],
      removeButton: true
    }
  ];
  
  function handleAction(event) {
    const { index, actionLabel, item } = event.detail;
    console.log(`${actionLabel} clicked on item ${index}`, item);
  }
  
  function handleRemove(event) {
    const { index, item } = event.detail;
    console.log(`Remove item ${index}`, item);
  }
</script>

<LinearList 
  {items} 
  on:action={handleAction}
  on:remove={handleRemove}
>
  {#snippet children({ item, index })}
    <div>{item.data.name}</div>
  {/snippet}
</LinearList>

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:

<script>
  import { Separator } from '@coyalabs/bts-style';
</script>

<Separator />
<Separator height="20px" margin="3rem 0" />

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

Icons

The package exports a collection of built-in SVG icons.

Usage:

<script>
  import { icons, BaseIcon } from '@coyalabs/bts-style';
</script>

<BaseIcon svg={icons.arrow} />
<BaseIcon svg={icons.folder} />
<BaseIcon svg={icons.icon_expand} />
<BaseIcon svg={icons.cross} />
<BaseIcon svg={icons.pen} />

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:

<script>
  const myIcon = '<svg>...</svg>';
</script>

<Button icon={myIcon}>Custom Icon</Button>
<BaseIcon svg={myIcon} />

Styling

Corner Radius Customization

All components extending BaseContainer support individual corner radius props:

<Button
  borderRadiusTopLeft="35px"
  borderRadiusTopRight="35px"
  borderRadiusBottomLeft="15px"
  borderRadiusBottomRight="15px"
  theme="primary"
>
  Asymmetric Button
</Button>

Theme Customization

You can customize filled theme backgrounds by targeting CSS variables or extending components.


Best Practices

Layout

<BasePage>
  <main style="max-width: 900px; margin: 0 auto; padding: 3rem 2rem;">
    <TextHeader title="My App" subtitle="Welcome" />
    
    <!-- Your content -->
  </main>
</BasePage>

Popup Management

Place <Popup /> once at your app root:

<!-- App.svelte -->
<script>
  import { BasePage, Popup } from '@coyalabs/bts-style';
</script>

<BasePage>
  <!-- App content -->
</BasePage>

<Popup />

Icons

For consistent styling, prefer using BaseIcon over raw SVG:

<!-- Good -->
<BaseIcon svg={myIcon} variant="toned" />

<!-- Less ideal -->
{@html myIcon}

Development

Local Development

Link the package locally for testing:

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

npm run package

Note: You may need to clear Vite cache after rebuilding:

rm -rf node_modules/.vite

Publishing

npm run release  # Bumps version, packages, and publishes

TypeScript Support

All components include TypeScript definitions. Import types as needed:

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

License

MIT


Repository

github.com/sparklescoya/svelte-bts-theme


Credits

Created with ❤️ using Svelte 5

Fonts: Google Fonts (Noto Serif KR), Fontshare (Satoshi)