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

tacel-universal-components

v1.0.0

Published

Tacel Design System — Universal UI components, utility styles, animation keyframes, theme tokens, and per-app theme presets shared by all Tacel Electron apps.

Readme

tacel-universal-components

The Tacel Design System — a comprehensive UI foundation library providing 39 components, 22 JS globals, 34 themes, utility styles, animation keyframes, and a full runtime customization API. All Tacel Electron apps share this as their base layer.

npm package: tacel-universal-components


Table of Contents

  1. Quick Start
  2. Components
  3. JS Component APIs
  4. CSS-Only Component Usage (HTML Examples)
  5. Theme System
  6. Customization API
  7. Utility Styles
  8. Animation Keyframes
  9. CSS Token Reference
  10. File Structure
  11. Test App

1. Quick Start

Install

npm install tacel-universal-components

Import CSS + JS

<!-- Full CSS bundle (tokens + reset + scrollbar + animations + utilities + all 39 component CSS) -->
<link rel="stylesheet" href="./node_modules/tacel-universal-components/tacel-ui.css">

<!-- JS entry point (TacelUI API + all 22 JS component globals) -->
<script src="./node_modules/tacel-universal-components/index.js"></script>

Or import individual pieces:

<link rel="stylesheet" href="./node_modules/tacel-universal-components/tokens.css">
<link rel="stylesheet" href="./node_modules/tacel-universal-components/components/button.css">
<script src="./node_modules/tacel-universal-components/components/toast.js"></script>

Apply a Theme

TacelUI.applyTheme('shipworks');  // Any of the 34 preset themes

Use Components

TacelToast.show('File saved!', { type: 'success' });
TacelModal.show({ title: 'Edit RMA', body: '<p>Content</p>', size: 'md' });
TacelConfirm.show({ title: 'Delete?', message: 'Cannot be undone.', variant: 'danger' });
var ls = TacelLoadingScreen.create('#app-container', { title: 'Tacel App', status: 'Loading...' });

2. Components

39 components total. Each is a self-contained CSS file (some with a JS file). All are theme-aware via --tc-* CSS variables. All CSS classes are prefixed with tc-.

CSS + JS Components (22 JS globals)

| # | Component | JS Global | CSS File | JS File | Description | |---|-----------|-----------|----------|---------|-------------| | 1 | Toast | TacelToast | toast.css | toast.js | Toast notifications with stacking, auto-dismiss, action buttons, progress bar | | 2 | Notification | TacelNotification | notification.css | notification.js | Rich notification cards with icon, title, message, actions, auto-dismiss | | 3 | Modal | TacelModal | modal.css | modal.js | Modal overlays with sizes (sm/md/lg/xl), focus trapping, Escape to close | | 4 | Search | TacelSearch | search.css | search.js | Debounced search bar with clear button and icon | | 5 | Avatar | TacelAvatar | avatar.css | avatar.js | User avatars with initials fallback, status dots, size variants, groups | | 6 | Tooltip | TacelTooltip | tooltip.css | tooltip.js | Hover tooltips with placement (top/bottom/left/right), delay, rich content | | 7 | Dropdown | TacelDropdown | dropdown.css | dropdown.js | Dropdown/context menus with icons, dividers, keyboard nav, click/contextmenu trigger | | 8 | Tabs | TacelTabs | tabs.css | tabs.js | Tab bar with underline/pills styles, badges, keyboard navigation | | 9 | Accordion | TacelAccordion | accordion.css | accordion.js | Collapsible sections with smooth animation, single/multi-open modes | | 10 | Chips | TacelChips | chips.css | chips.js | Tag/chip input with suggestions, removable chips, duplicate prevention | | 11 | Empty State | TacelEmptyState | empty-state.css | empty-state.js | "No data" placeholder with icon, title, message, action button | | 12 | Error State | TacelErrorState | error-state.css | error-state.js | Error display with retry button | | 13 | Toggle | TacelToggle | toggle.css | toggle.js | Toggle switches with labels, sizes, disabled state | | 14 | Confirm | TacelConfirm | confirm.css | confirm.js | Confirmation dialogs (danger/warning/info) — replaces window.confirm() | | 15 | Dropzone | TacelDropzone | dropzone.css | dropzone.js | File upload dropzone with drag-and-drop, click-to-browse, file list | | 16 | Range Slider | TacelRangeSlider | range-slider.css | range-slider.js | Slider with gradient track, value bubble, tick marks, step labels | | 17 | Priority Selector | TacelPrioritySelector | priority-selector.css | priority-selector.js | Priority level picker (1–5) with color-coded levels | | 18 | Filter Bar | TacelFilterBar | filter-bar.css | filter-bar.js | Search + filter chips with counts, active state, reset | | 19 | Context Menu | TacelContextMenu | context-menu.css | context-menu.js | Right-click menus with icons, shortcuts, dividers, subheaders, checkboxes, danger items | | 20 | Page Loader | TacelPageLoader | page-loader.css | page-loader.js | Page transition overlay with dual-ring spinner | | 21 | Segmented Control | TacelSegmented | segmented-control.css | segmented-control.js | Segmented option picker with active state, accent variant | | 22 | Loading Screen | TacelLoadingScreen | loading-screen.css | loading-screen.js | Full-page startup animation with particles, glass card, progress, glow |

CSS-Only Components (17)

| # | Component | CSS File | Description | |---|-----------|----------|-------------| | 23 | Button | button.css | Variants (primary/secondary/outline/ghost/danger/success), sizes, icon, block, loading | | 24 | Card | card.css | Card with header/body/footer, hover lift, flat, compact variants | | 25 | Form | form.css | Inputs, selects, textareas, labels, hints, errors, icon inputs, rows | | 26 | Badge | badge.css | Status badges, outline, dots, counts, pills | | 27 | Table | table.css | Simple table with header, rows, hover, alternating backgrounds | | 28 | Progress | progress.css | Progress bars — determinate, indeterminate, striped, animated, color variants | | 29 | Spinner | spinner.css | CSS spinners in sm/md/lg sizes | | 30 | Coming Soon | coming-soon.css | Placeholder for unbuilt pages | | 31 | FAB | fab.css | Floating action button with gradient, shadow, hover lift | | 32 | Status Badge | status-badge.css | Online/away/busy/offline status indicators with pulse animation | | 33 | Timeline | timeline.css | Vertical timeline and horizontal stepper with connector lines | | 34 | Stat Card | stat-card.css | Dashboard stat cards with icon, value, label, trend indicator | | 35 | Date Range | date-range.css | Date range picker with preset buttons | | 36 | Alert Banner | alert-banner.css | Alert banners (info/success/warning/error) with dismiss, filled variant | | 37 | Login Screen | login-screen.css | Full-page login with particles, glass form card, error messages, button spinner | | 38 | Update Modal | update-modal.css | Auto-update UI with version badges, progress bar, log viewer, action buttons | | 39 | Sidebar Animation | sidebar-animation.css | Lottie container in sidebar header with glow effects and particles |

CSS-Only Layout Components (4 — no JS, used via HTML classes)

| Component | CSS File | Description | |-----------|----------|-------------| | Breadcrumb | breadcrumb.css | Navigation breadcrumb trail with separator variants (slash/arrow/dot) | | Sidebar | sidebar.css | Sidebar navigation with items, sections, badges, user area, collapsed state | | Detail List | detail-list.css | Key-value list with inline/grid/striped/card variants | | Skeleton | skeleton.css | Skeleton loading placeholders (text, circle, rect, card, table, grid) | | Button Loading | button-loading.css | Inline spinner inside buttons during async operations |


3. JS Component APIs

TacelToast

TacelToast.show('Message', {
  type: 'success',        // 'success' | 'error' | 'warning' | 'info'
  duration: 3000,         // ms, 0 = persistent
  position: 'top-right',  // 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left'
  progress: true,         // show countdown bar
  action: { label: 'Undo', onClick: () => {} }
});
TacelToast.clearAll();

TacelNotification

TacelNotification.show({
  title: 'RMA Updated',
  message: 'Status changed to Closed',
  type: 'success',
  duration: 5000,
  position: 'bottom-right',
  icon: 'fas fa-check-circle',
  progress: true,
  actions: [
    { label: 'View', variant: 'primary', onClick: () => {} },
    { label: 'Dismiss', variant: 'secondary', onClick: () => {} }
  ]
});

TacelModal

TacelModal.show({
  title: 'Edit Record',
  body: '<div>HTML content</div>',
  size: 'md',             // 'sm' | 'md' | 'lg'
  footer: [
    { label: 'Cancel', variant: 'secondary', onClick: (close) => close() },
    { label: 'Save', variant: 'primary', onClick: (close) => { save(); close(); } }
  ]
});

TacelSearch

const search = TacelSearch.create(container, {
  placeholder: 'Search RMAs...',
  debounceMs: 300,
  onChange: (value) => {},
  onClear: () => {}
});
search.getValue(); search.setValue('RMA-001'); search.clear(); search.focus();

TacelAvatar

const avatar = TacelAvatar.create({ name: 'Pierre', size: 'md', status: 'online' });
container.appendChild(avatar);
// Group: TacelAvatar.createGroup(container, [{ name: 'A' }, { name: 'B' }], { max: 3 });

TacelTooltip

TacelTooltip.attach(element, { content: 'Tooltip text', placement: 'top', delay: 300 });
TacelTooltip.detach(element);

TacelDropdown

TacelDropdown.create(triggerElement, {
  items: [
    { label: 'Edit', icon: 'fas fa-edit', onClick: () => {} },
    { divider: true },
    { label: 'Delete', icon: 'fas fa-trash', onClick: () => {}, variant: 'danger' }
  ],
  trigger: 'click'  // 'click' | 'contextmenu'
});

TacelTabs

const tabs = TacelTabs.create(container, {
  tabs: [
    { id: 'general', label: 'General', icon: 'fas fa-cog' },
    { id: 'advanced', label: 'Advanced', badge: 3 }
  ],
  defaultTab: 'general',
  style: 'underline',  // 'underline' | 'pills'
  onChange: (tabId) => {}
});
tabs.setActive('advanced'); tabs.setBadge('advanced', 5);

TacelAccordion

const accordion = TacelAccordion.create(container, {
  items: [
    { id: 's1', title: 'General', content: '<p>Content</p>' },
    { id: 's2', title: 'Advanced', content: '<p>Content</p>', defaultOpen: true }
  ],
  singleOpen: true
});
accordion.open('s1'); accordion.close('s1'); accordion.toggle('s1');

TacelChips

const chips = TacelChips.create(container, {
  value: ['urgent'],
  suggestions: ['urgent', 'warranty', 'repair'],
  placeholder: 'Add tag...',
  maxChips: 10,
  allowCustom: true,
  onChange: (tags) => {}
});
chips.getValue(); chips.setValue(['new']); chips.clear();

TacelToggle

const toggle = TacelToggle.create(container, {
  label: 'Dark Mode',
  checked: false,
  onChange: (checked) => {}
});
toggle.getValue(); toggle.setValue(true);

TacelConfirm

TacelConfirm.show({
  title: 'Delete Item?',
  message: 'This action cannot be undone.',
  variant: 'danger',       // 'danger' | 'warning' | 'info'
  confirmLabel: 'Delete',
  cancelLabel: 'Keep',
  onConfirm: () => {},
  onCancel: () => {}
});

TacelDropzone

const dz = TacelDropzone.create(container, {
  accept: '.jpg,.png,.pdf',
  maxSize: 10 * 1024 * 1024,
  multiple: true,
  onFiles: (files) => {}
});

TacelRangeSlider

TacelRangeSlider.create(container, {
  min: 0, max: 100, value: 50, step: 1,
  showValue: true, showTicks: true,
  onChange: (value) => {}
});

TacelPrioritySelector

TacelPrioritySelector.create(container, {
  value: 3, levels: 5,
  onChange: (level) => {}
});

TacelFilterBar

TacelFilterBar.create(container, {
  placeholder: 'Search...',
  filters: [
    { id: 'open', label: 'Open', icon: 'fas fa-folder-open', count: 24 },
    { id: 'closed', label: 'Closed', icon: 'fas fa-check-circle', count: 156 }
  ],
  onSearch: (query) => {},
  onFilter: (activeIds) => {}
});

TacelContextMenu

// Attach to any element for right-click
TacelContextMenu.attach(element, {
  items: [
    { id: 'copy', label: 'Copy', icon: 'fas fa-copy' },
    { divider: true },
    { id: 'delete', label: 'Delete', icon: 'fas fa-trash', variant: 'danger' }
  ],
  onSelect: (id) => {}
});

// Or show programmatically at coordinates
TacelContextMenu.show(x, y, { items: [...], onSelect: (id) => {} });

TacelPageLoader

TacelPageLoader.show({ text: 'Loading page...' });
TacelPageLoader.hide();
TacelPageLoader.setText('Almost ready...');

TacelSegmented

const seg = TacelSegmented.create(container, {
  options: [
    { value: 'list', label: 'List', icon: 'fas fa-list' },
    { value: 'grid', label: 'Grid', icon: 'fas fa-th' }
  ],
  value: 'list',
  onChange: (value) => {}
});
seg.getValue(); seg.setValue('grid');

TacelLoadingScreen

Matches the actual startup loading screens in ShipWorks, Office-HQ, and Tech-Portal exactly. Uses create() to build the loading screen inside a container (like the real apps do in index.html).

// Create loading screen inside a container (matches how apps have it in #app-container)
const ls = TacelLoadingScreen.create('#app-container', {
  title: 'Tacel App',                    // h1 text
  status: 'Initializing...',             // status pill text
  lottie: './animations/loading.json',   // Lottie JSON path (requires lottie-web)
  logo: './icons/app-icon.ico',          // fallback image if no Lottie
  particles: 10,                         // background particles (default: 10)
  glows: 6                               // glow orbs around center (default: 6)
});

// Returns: { el, lottieContainer, particlesContainer, hide, show, setStatus, setProgress, destroy }
ls.setStatus('Connecting to database...');  // update status text
ls.setProgress(75);                         // 0–100 (stops CSS auto-animation, sets manual width)
ls.hide();                                  // display: none (matches real app behavior)
ls.show();                                  // display: flex (re-show)
ls.destroy();                               // remove from DOM entirely

3b. CSS-Only Component Usage (HTML Examples)

Every CSS-only component works by adding the right classes to your HTML. No JS required.

Button

<button class="tc-btn tc-btn-primary">Save</button>
<button class="tc-btn tc-btn-secondary">Cancel</button>
<button class="tc-btn tc-btn-outline">Details</button>
<button class="tc-btn tc-btn-ghost">Skip</button>
<button class="tc-btn tc-btn-danger">Delete</button>
<button class="tc-btn tc-btn-success">Approve</button>

<!-- Sizes -->
<button class="tc-btn tc-btn-primary tc-btn-sm">Small</button>
<button class="tc-btn tc-btn-primary tc-btn-lg">Large</button>

<!-- Icon button -->
<button class="tc-btn tc-btn-primary tc-btn-icon"><i class="fas fa-plus"></i></button>

<!-- Full width -->
<button class="tc-btn tc-btn-primary tc-btn-block">Full Width</button>

<!-- Loading state (add class to show spinner) -->
<button class="tc-btn tc-btn-primary tc-btn-loading">Saving...</button>

<!-- Disabled -->
<button class="tc-btn tc-btn-primary" disabled>Disabled</button>

Card

<div class="tc-card">
  <div class="tc-card-header">Card Title</div>
  <div class="tc-card-body">Card content goes here.</div>
  <div class="tc-card-footer">
    <button class="tc-btn tc-btn-primary tc-btn-sm">Action</button>
  </div>
</div>

<!-- Variants -->
<div class="tc-card tc-card-hover">Lifts on hover</div>
<div class="tc-card tc-card-flat">No shadow</div>
<div class="tc-card tc-card-compact">Less padding</div>

Form

<div class="tc-form-group">
  <label class="tc-form-label">Username</label>
  <input class="tc-form-input" type="text" placeholder="Enter username">
  <span class="tc-form-hint">Must be at least 3 characters</span>
</div>

<div class="tc-form-group">
  <label class="tc-form-label">Description</label>
  <textarea class="tc-form-textarea" rows="3" placeholder="Enter description"></textarea>
</div>

<div class="tc-form-group">
  <label class="tc-form-label">Category</label>
  <select class="tc-form-select">
    <option>Option A</option>
    <option>Option B</option>
  </select>
</div>

<!-- Error state -->
<div class="tc-form-group tc-form-error">
  <label class="tc-form-label">Email</label>
  <input class="tc-form-input" type="email" value="bad">
  <span class="tc-form-error-text">Invalid email address</span>
</div>

<!-- Input with icon -->
<div class="tc-form-icon-input">
  <i class="fas fa-search"></i>
  <input class="tc-form-input" type="text" placeholder="Search...">
</div>

<!-- Inline row -->
<div class="tc-form-row">
  <div class="tc-form-group"><input class="tc-form-input" placeholder="First"></div>
  <div class="tc-form-group"><input class="tc-form-input" placeholder="Last"></div>
</div>

Badge

<span class="tc-badge">Default</span>
<span class="tc-badge tc-badge-success">Active</span>
<span class="tc-badge tc-badge-error">Failed</span>
<span class="tc-badge tc-badge-warning">Pending</span>
<span class="tc-badge tc-badge-info">New</span>
<span class="tc-badge tc-badge-primary">v1.3</span>

<!-- Outline variant -->
<span class="tc-badge tc-badge-outline tc-badge-success">Open</span>

<!-- Pill (rounded) -->
<span class="tc-pill tc-badge-info">12 items</span>

<!-- Dot indicator -->
<span class="tc-badge tc-badge-dot tc-badge-success">Online</span>

Table

<div class="tc-table">
  <div class="tc-table-header">
    <div class="tc-table-cell">Name</div>
    <div class="tc-table-cell">Status</div>
    <div class="tc-table-cell">Date</div>
  </div>
  <div class="tc-table-row">
    <div class="tc-table-cell">RMA-001</div>
    <div class="tc-table-cell"><span class="tc-badge tc-badge-success">Open</span></div>
    <div class="tc-table-cell">2025-01-15</div>
  </div>
  <div class="tc-table-row">
    <div class="tc-table-cell">RMA-002</div>
    <div class="tc-table-cell"><span class="tc-badge tc-badge-error">Closed</span></div>
    <div class="tc-table-cell">2025-01-16</div>
  </div>
</div>

Progress Bar

<!-- Basic -->
<div class="tc-progress">
  <div class="tc-progress-bar" style="width: 65%"></div>
</div>

<!-- With label -->
<div class="tc-progress-label"><span>Upload</span><span>65%</span></div>
<div class="tc-progress">
  <div class="tc-progress-bar" style="width: 65%"></div>
</div>

<!-- Color variants -->
<div class="tc-progress"><div class="tc-progress-bar tc-progress-success" style="width:80%"></div></div>
<div class="tc-progress"><div class="tc-progress-bar tc-progress-warning" style="width:50%"></div></div>
<div class="tc-progress"><div class="tc-progress-bar tc-progress-error" style="width:30%"></div></div>

<!-- Striped + animated -->
<div class="tc-progress">
  <div class="tc-progress-bar tc-progress-striped tc-progress-animated" style="width:70%"></div>
</div>

<!-- Indeterminate -->
<div class="tc-progress">
  <div class="tc-progress-bar tc-progress-indeterminate"></div>
</div>

Spinner

<div class="tc-spinner"></div>
<div class="tc-spinner tc-spinner-sm"></div>
<div class="tc-spinner tc-spinner-lg"></div>

FAB (Floating Action Button)

<button class="tc-fab"><i class="fas fa-plus"></i></button>

Status Badge

<span class="tc-status-badge tc-status-online"><span class="tc-status-dot"></span> Online</span>
<span class="tc-status-badge tc-status-away"><span class="tc-status-dot"></span> Away</span>
<span class="tc-status-badge tc-status-busy"><span class="tc-status-dot"></span> Busy</span>
<span class="tc-status-badge tc-status-offline"><span class="tc-status-dot"></span> Offline</span>

Timeline / Stepper

<!-- Vertical timeline -->
<div class="tc-timeline">
  <div class="tc-timeline-item tc-timeline-completed">
    <div class="tc-timeline-marker"></div>
    <div class="tc-timeline-content">
      <div class="tc-timeline-title">RMA Created</div>
      <div class="tc-timeline-desc">Jan 15, 2025</div>
    </div>
  </div>
  <div class="tc-timeline-item tc-timeline-active">
    <div class="tc-timeline-marker"></div>
    <div class="tc-timeline-content">
      <div class="tc-timeline-title">Under Review</div>
      <div class="tc-timeline-desc">In progress</div>
    </div>
  </div>
  <div class="tc-timeline-item">
    <div class="tc-timeline-marker"></div>
    <div class="tc-timeline-content">
      <div class="tc-timeline-title">Resolved</div>
    </div>
  </div>
</div>

<!-- Horizontal stepper -->
<div class="tc-stepper">
  <div class="tc-step tc-step-completed"><span class="tc-step-circle">1</span><span class="tc-step-label">Details</span></div>
  <div class="tc-step tc-step-active"><span class="tc-step-circle">2</span><span class="tc-step-label">Review</span></div>
  <div class="tc-step"><span class="tc-step-circle">3</span><span class="tc-step-label">Submit</span></div>
</div>

Stat Card

<div class="tc-stat-card">
  <div class="tc-stat-icon"><i class="fas fa-box"></i></div>
  <div class="tc-stat-content">
    <div class="tc-stat-value">1,247</div>
    <div class="tc-stat-label">Total RMAs</div>
    <div class="tc-stat-trend tc-stat-trend-up"><i class="fas fa-arrow-up"></i> 12%</div>
  </div>
</div>

Date Range

<div class="tc-date-range">
  <div class="tc-date-inputs">
    <input class="tc-form-input tc-date-input" type="date">
    <span class="tc-date-separator">to</span>
    <input class="tc-form-input tc-date-input" type="date">
  </div>
  <div class="tc-date-presets">
    <button class="tc-date-preset tc-date-preset-active">Today</button>
    <button class="tc-date-preset">This Week</button>
    <button class="tc-date-preset">This Month</button>
    <button class="tc-date-preset">This Year</button>
  </div>
</div>

Alert Banner

<div class="tc-alert tc-alert-info">
  <i class="fas fa-info-circle tc-alert-icon"></i>
  <div class="tc-alert-content">This is an informational message.</div>
  <button class="tc-alert-dismiss"><i class="fas fa-times"></i></button>
</div>

<div class="tc-alert tc-alert-success">
  <i class="fas fa-check-circle tc-alert-icon"></i>
  <div class="tc-alert-content">Operation completed successfully.</div>
</div>

<div class="tc-alert tc-alert-warning">
  <i class="fas fa-exclamation-triangle tc-alert-icon"></i>
  <div class="tc-alert-content">Please review before continuing.</div>
</div>

<div class="tc-alert tc-alert-error">
  <i class="fas fa-times-circle tc-alert-icon"></i>
  <div class="tc-alert-content">Something went wrong.</div>
</div>

<!-- Filled variant (solid background) -->
<div class="tc-alert tc-alert-info tc-alert-filled">Filled info alert.</div>

Coming Soon

<div class="tc-coming-soon">
  <div class="tc-coming-soon-icon"><i class="fas fa-hard-hat"></i></div>
  <div class="tc-coming-soon-badge">Coming Soon</div>
  <h3>Feature Name</h3>
  <p>This feature is under development.</p>
</div>

Breadcrumb

<nav class="tc-breadcrumb">
  <span class="tc-breadcrumb-item">Home</span>
  <span class="tc-breadcrumb-item">RMA</span>
  <span class="tc-breadcrumb-item tc-breadcrumb-active">RMA-001</span>
</nav>

<!-- Separator variants -->
<nav class="tc-breadcrumb tc-breadcrumb-arrow">...</nav>
<nav class="tc-breadcrumb tc-breadcrumb-dot">...</nav>

<!-- Background variant -->
<nav class="tc-breadcrumb tc-breadcrumb-bg">...</nav>

Sidebar / Nav

<aside class="tc-sidebar">
  <div class="tc-sidebar-header">
    <span class="tc-sidebar-logo">App</span>
    <span class="tc-sidebar-version">v1.3</span>
  </div>
  <div class="tc-sidebar-section">
    <div class="tc-sidebar-section-title">Main</div>
    <div class="tc-sidebar-item tc-sidebar-active">
      <i class="fas fa-home tc-sidebar-item-icon"></i>
      <span class="tc-sidebar-item-label">Dashboard</span>
    </div>
    <div class="tc-sidebar-item">
      <i class="fas fa-exchange-alt tc-sidebar-item-icon"></i>
      <span class="tc-sidebar-item-label">RMA</span>
      <span class="tc-sidebar-badge">24</span>
    </div>
  </div>
  <div class="tc-sidebar-user">
    <div class="tc-avatar tc-avatar-sm">PD</div>
    <span>Pierre</span>
  </div>
</aside>

Detail List (Key-Value)

<!-- Inline layout -->
<div class="tc-detail-list">
  <div class="tc-detail-item">
    <span class="tc-detail-label">RMA Number</span>
    <span class="tc-detail-value">RMA-2025-001</span>
  </div>
  <div class="tc-detail-item">
    <span class="tc-detail-label">Status</span>
    <span class="tc-detail-value"><span class="tc-badge tc-badge-success">Open</span></span>
  </div>
</div>

<!-- Grid layout -->
<div class="tc-detail-list tc-detail-grid">...</div>

<!-- Striped -->
<div class="tc-detail-list tc-detail-striped">...</div>

<!-- Card variant -->
<div class="tc-detail-list tc-detail-card">...</div>

Skeleton Loader

<!-- Text lines -->
<div class="tc-skeleton tc-skeleton-text"></div>
<div class="tc-skeleton tc-skeleton-text" style="width:75%"></div>

<!-- Circle (avatar placeholder) -->
<div class="tc-skeleton tc-skeleton-circle"></div>

<!-- Rectangle (image placeholder) -->
<div class="tc-skeleton tc-skeleton-rect"></div>

<!-- Card skeleton -->
<div class="tc-skeleton-card">
  <div class="tc-skeleton tc-skeleton-rect"></div>
  <div class="tc-skeleton tc-skeleton-text"></div>
  <div class="tc-skeleton tc-skeleton-text" style="width:60%"></div>
</div>

<!-- Table skeleton -->
<div class="tc-skeleton-table">
  <div class="tc-skeleton tc-skeleton-text"></div>
  <div class="tc-skeleton tc-skeleton-text"></div>
  <div class="tc-skeleton tc-skeleton-text"></div>
</div>

Button Loading

<!-- Just add tc-btn-loading class to any button -->
<button class="tc-btn tc-btn-primary tc-btn-loading">Saving...</button>
<button class="tc-btn tc-btn-secondary tc-btn-loading">Loading</button>
<button class="tc-btn tc-btn-danger tc-btn-loading">Deleting...</button>

Login Screen

<div class="tc-login-container">
  <div class="tc-login-particles" id="login-particles"></div>
  <div class="tc-login-form-container">
    <div class="tc-login-logo-container">
      <div class="tc-login-logo-animation" id="login-logo"></div>
    </div>
    <h1>App Name</h1>
    <h2>Sign in to continue</h2>
    <div class="tc-login-form">
      <div class="tc-form-group">
        <input class="tc-form-input" type="text" placeholder="Username">
      </div>
      <div class="tc-form-group">
        <input class="tc-form-input" type="password" placeholder="Password">
      </div>
      <button class="tc-login-button">Sign In</button>
      <!-- Loading state: -->
      <button class="tc-login-button" disabled>
        <span class="tc-login-spinner"></span> Signing in...
      </button>
    </div>
    <div class="tc-login-error">
      <i class="fas fa-exclamation-circle"></i> Invalid credentials
    </div>
    <div class="tc-login-footer">v1.3.0</div>
  </div>
</div>

Update Modal

<div class="tc-update-modal-overlay tc-visible">
  <div class="tc-update-modal">
    <div class="tc-update-modal-header">
      <div class="tc-update-modal-icon"><i class="fas fa-arrow-circle-up"></i></div>
      <div class="tc-update-modal-title">
        <h2>Update Available</h2>
        <p>A new version is ready to install</p>
      </div>
    </div>
    <div class="tc-update-modal-body">
      <div class="tc-update-version-info">
        <span class="tc-version-badge tc-current">v1.2.0</span>
        <span class="tc-version-arrow">→</span>
        <span class="tc-version-badge tc-new">v1.3.0</span>
      </div>
      <!-- Progress (add tc-visible class to show) -->
      <div class="tc-update-progress-section tc-visible">
        <div class="tc-update-progress-bar-container">
          <div class="tc-update-progress-bar" style="width:65%"></div>
        </div>
        <div class="tc-update-progress-text">
          <span class="tc-update-progress-message">Copying files...</span>
          <span class="tc-update-progress-percent">65%</span>
        </div>
      </div>
      <!-- Status (add tc-visible + tc-success or tc-error) -->
      <div class="tc-update-status tc-visible tc-success">Update complete!</div>
      <!-- Log viewer (add tc-visible to show) -->
      <div class="tc-update-log tc-visible">
        <div class="tc-update-log-entry">Checking version...</div>
        <div class="tc-update-log-entry">Downloading files...</div>
      </div>
    </div>
    <div class="tc-update-modal-footer">
      <button class="tc-update-btn tc-update-btn-secondary">Cancel</button>
      <button class="tc-update-btn tc-update-btn-primary">
        <i class="fas fa-download"></i> Update Now
      </button>
    </div>
  </div>
</div>

Sidebar Animation (Lottie Container)

<div class="tc-sidebar-header" style="position:relative;">
  <!-- Particles in header -->
  <div class="tc-sidebar-particles">
    <div class="tc-sidebar-particle"></div>
    <div class="tc-sidebar-particle"></div>
    <div class="tc-sidebar-particle"></div>
  </div>
  <!-- Lottie container -->
  <div class="tc-sidebar-lottie-container" id="sidebar-lottie">
    <!-- Lottie animation renders here via lottie.loadAnimation() -->
  </div>
  <span>App Name</span>
</div>

<!-- Add tc-animation-hover class on mouseenter/mouseleave for glow effect -->

4. Theme System

34 preset themes — all override the full set of visual tokens. Every component responds to theme changes automatically.

App-Specific Themes (6)

| Theme | Type | Description | |-------|------|-------------| | default | Light | Neutral gray baseline | | shipworks | Light | Pastel pink/blue (#D49BAE / #678CEC) | | office-hq | Light | Gold/brown (#c9a227 / #5c4033) | | tech-portal | Dark | Purple/green (#ab7ce2 / #50faab) | | wire-scheduler | Light | Purple/indigo (#4a3f8a / #6366f1) | | admin-pro | Dark | Deep purple (#9370DB / #7B68EE) |

Light Themes (13)

slate, arctic, lavender, rose-gold, sand, emerald, mint, coral, nordic, ivory, sapphire, peach, clay

Dark Themes (15)

midnight-blue, forest, sunset, charcoal, ocean, cherry, storm, copper, plum, obsidian, mocha, steel, aurora, amethyst, dusk

See THEMES.md for the full theme catalog with descriptions.


5. Customization API

The TacelUI global provides full runtime control over themes and tokens.

Apply Preset Theme

TacelUI.applyTheme('ocean');
TacelUI.getCurrentTheme();   // 'ocean'
TacelUI.themes;              // ['default', 'shipworks', ..., 'dusk']

Override Tokens at Runtime

TacelUI.setToken('--tc-accent', '#ff6600');
TacelUI.setTokens({ '--tc-accent': '#ff6600', '--tc-bg-primary': '#1a1a2e' });
TacelUI.getToken('--tc-accent');
TacelUI.getAllTokens();
TacelUI.resetTokens();       // revert all overrides
TacelUI.resetToken('--tc-accent');

Create Custom Themes

TacelUI.createTheme('my-brand', {
  '--tc-primary': '#1a1a2e',
  '--tc-accent': '#ff6600',
  '--tc-bg-primary': '#ffffff'
});
TacelUI.applyTheme('my-brand');

Extend Existing Themes

TacelUI.extendTheme('ocean', 'ocean-red', {
  '--tc-accent': '#ef4444',
  '--tc-gradient-accent': 'linear-gradient(135deg, #ef4444, #dc2626)'
});
TacelUI.applyTheme('ocean-red');

Scoped Overrides

TacelUI.scopeTokens('#my-sidebar', { '--tc-accent': '#8b5cf6' });
TacelUI.removeScope('#my-sidebar');

Component Defaults

TacelUI.configure('toast', { duration: 3000, position: 'top-right' });
TacelUI.configure('modal', { size: 'md' });

6. Utility Styles

CSS-only utility classes for common layout and styling. All prefixed with tc-.

| Category | Classes | |----------|---------| | Typography | .tc-h1.tc-h6, .tc-body, .tc-small, .tc-caption, .tc-mono, .tc-truncate, .tc-uppercase, .tc-font-bold | | Spacing | .tc-m{t,r,b,l,x,y}-{0–16}, .tc-p{t,r,b,l,x,y}-{0–16}, .tc-mx-auto | | Flexbox | .tc-flex, .tc-flex-col, .tc-flex-wrap, .tc-items-center, .tc-justify-between, .tc-gap-{1–8}, .tc-flex-1 | | Grid | .tc-grid, .tc-grid-cols-{1–4}, .tc-col-span-{2,3,full} | | Borders | .tc-border, .tc-border-{t,b,l,r}, .tc-border-none, .tc-rounded-{sm,md,lg,xl,full} | | Shadows | .tc-shadow-{none,sm,md,lg,xl} | | Transitions | .tc-transition, .tc-transition-{fast,normal,slow,none} | | Colors | .tc-text-{primary,secondary,tertiary,muted,success,error,warning,info,accent} | | Backgrounds | .tc-bg-{primary,secondary,tertiary,success,error,warning,info,transparent} | | Visibility | .tc-hidden, .tc-visible, .tc-invisible, .tc-sr-only, .tc-overflow-{hidden,auto} | | Interaction | .tc-cursor-{pointer,default,not-allowed}, .tc-pointer-events-none, .tc-select-{none,all} |


7. Animation Keyframes

All shared keyframes are in animations.css. Apps no longer need their own animation files.

| Keyframe | Description | |----------|-------------| | tc-spin | 360° rotation | | tc-pulse | Scale 1 → 1.05 → 1 | | tc-pulse-subtle | Scale 1 → 1.03 → 1 | | tc-fadeIn | Opacity 0 → 1 | | tc-fadeInUp | Opacity + translateY up | | tc-fadeInDown | Opacity + translateY down | | tc-fadeOut | Opacity 1 → 0 | | tc-slideInRight | Slide from right | | tc-slideOutRight | Slide to right | | tc-slideInLeft | Slide from left | | tc-slideInUp | Slide from below | | tc-float | Gentle vertical float | | tc-shake | Horizontal shake | | tc-ripple | Click ripple effect | | tc-text-glow | Text shadow pulse (uses --tc-accent-rgb) | | tc-expand-line | Width 0 → 80% | | tc-loading-progress | Progress bar fill | | tc-progress-shrink | Progress bar shrink (countdown) | | tc-skeleton-shimmer | Skeleton loading shimmer | | tc-float-particle | Particle float upward | | tc-pulse-glow | Glow orb pulse |


8. CSS Token Reference

Every --tc-* variable can be overridden. All 34 themes override the full set.

| Category | Variables | |----------|-----------| | Brand Colors | --tc-primary (+light/dark/rgb), --tc-secondary (+light/dark), --tc-accent (+light/dark/rgb) | | Backgrounds | --tc-bg-primary, --tc-bg-secondary, --tc-bg-tertiary, --tc-bg-input, --tc-bg-overlay, --tc-bg-app | | Text | --tc-text-primary, --tc-text-secondary, --tc-text-tertiary, --tc-text-muted, --tc-text-inverse | | Status | --tc-success (+light/rgb), --tc-error (+light/rgb), --tc-warning (+light/rgb), --tc-info (+light/rgb) | | Borders | --tc-border-color, --tc-border-hover, --tc-border-focus | | Shadows | --tc-shadow-sm, --tc-shadow, --tc-shadow-md, --tc-shadow-lg, --tc-shadow-xl | | Typography | --tc-font-family, --tc-font-family-mono, --tc-text-xs--tc-text-3xl, --tc-font-normal--tc-font-extrabold | | Spacing | --tc-space-0 through --tc-space-16 (4px base) | | Radius | --tc-radius-sm, --tc-radius, --tc-radius-md, --tc-radius-lg, --tc-radius-xl, --tc-radius-2xl, --tc-radius-full | | Z-Index | --tc-z-dropdown (100), --tc-z-sticky (200), --tc-z-sidebar (300), --tc-z-modal (9999), --tc-z-popover (10000), --tc-z-toast (10001), --tc-z-tooltip (10002) | | Transitions | --tc-transition-fast, --tc-transition, --tc-transition-normal, --tc-transition-slow, --tc-transition-spring | | Scrollbar | --tc-scrollbar-thumb, --tc-scrollbar-thumb-hover, --tc-scrollbar-thumb-active, --tc-scrollbar-track | | Gradients | --tc-gradient-primary, --tc-gradient-accent, --tc-gradient-sidebar |


9. File Structure

universal-components-module/
├── index.js                          # TacelUI API + loads all 22 JS components
├── package.json                      # npm: tacel-universal-components
├── tacel-ui.css                      # Single-file CSS import (everything)
├── README.md                         # This file
├── THEMES.md                         # Full theme catalog + customization guide
│
├── tokens.css                        # All --tc-* CSS variable defaults
├── reset.css                         # Box-sizing, margin/padding reset, body base
├── scrollbar.css                     # Global scrollbar styling
├── animations.css                    # All shared @keyframes
│
├── themes/                           # 34 theme preset CSS files
│   ├── default.css
│   ├── shipworks.css
│   ├── office-hq.css
│   ├── tech-portal.css
│   ├── wire-scheduler.css
│   ├── admin-pro.css
│   ├── slate.css ... clay.css        # 13 light themes
│   └── midnight-blue.css ... dusk.css # 15 dark themes
│
├── utilities/                        # 10 utility CSS files
│   ├── typography.css
│   ├── spacing.css
│   ├── flexbox.css
│   ├── grid.css
│   ├── borders.css
│   ├── shadows.css
│   ├── transitions.css
│   ├── visibility.css
│   ├── colors.css
│   └── interaction.css
│
└── components/                       # 39 components (66 files)
    ├── toast.js + toast.css
    ├── notification.js + notification.css
    ├── modal.js + modal.css
    ├── search.js + search.css
    ├── avatar.js + avatar.css
    ├── tooltip.js + tooltip.css
    ├── dropdown.js + dropdown.css
    ├── tabs.js + tabs.css
    ├── accordion.js + accordion.css
    ├── chips.js + chips.css
    ├── empty-state.js + empty-state.css
    ├── error-state.js + error-state.css
    ├── toggle.js + toggle.css
    ├── confirm.js + confirm.css
    ├── dropzone.js + dropzone.css
    ├── range-slider.js + range-slider.css
    ├── priority-selector.js + priority-selector.css
    ├── filter-bar.js + filter-bar.css
    ├── context-menu.js + context-menu.css
    ├── page-loader.js + page-loader.css
    ├── segmented-control.js + segmented-control.css
    ├── loading-screen.js + loading-screen.css
    ├── button.css
    ├── card.css
    ├── form.css
    ├── badge.css
    ├── table.css
    ├── progress.css
    ├── spinner.css
    ├── coming-soon.css
    ├── fab.css
    ├── status-badge.css
    ├── timeline.css
    ├── stat-card.css
    ├── date-range.css
    ├── alert-banner.css
    ├── breadcrumb.css
    ├── sidebar.css
    ├── detail-list.css
    ├── skeleton.css
    ├── button-loading.css
    ├── login-screen.css
    ├── update-modal.css
    └── sidebar-animation.css

10. Test App

A comprehensive demo app lives at Random/universal-components-test-app/:

  • All 39 components rendered with interactive demos
  • All 34 themes selectable via theme picker
  • Customization Playground — live token overrides, scoped overrides, theme extension
  • Right-click context menus on 20+ component types
  • Loading Screen — inline preview + full-screen demo + simulated app startup sequence
  • Login Screen — inline preview with particles, glass form card, error message
  • Update Modal — inline preview + interactive full-screen demos with progress simulation
  • Sidebar Animation — inline preview with Lottie container, particles, hover glow toggle

Run with:

cd Random/universal-components-test-app
npx electron .

Design Principles

  1. Single source of truth — All shared styles, components, and tokens live here. Apps override tokens, never selectors.
  2. CSS variable theming — Every visual property is a --tc-* variable. Apps override variables via theme presets or runtime API.
  3. Zero app-specific code — No RMA, tickets, shipping, or business logic. Pure UI primitives.
  4. Namespaced classes — All classes prefixed with tc- to avoid collisions with existing app styles.
  5. Progressive adoption — Import the full bundle or individual pieces. Existing styles continue to work.
  6. Frontend only — Renderer-process CSS and JS. No backend, no IPC, no Node.js dependencies.