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.
Maintainers
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
- Quick Start
- Components
- JS Component APIs
- CSS-Only Component Usage (HTML Examples)
- Theme System
- Customization API
- Utility Styles
- Animation Keyframes
- CSS Token Reference
- File Structure
- Test App
1. Quick Start
Install
npm install tacel-universal-componentsImport 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 themesUse 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 entirely3b. 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.css10. 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
- Single source of truth — All shared styles, components, and tokens live here. Apps override tokens, never selectors.
- CSS variable theming — Every visual property is a
--tc-*variable. Apps override variables via theme presets or runtime API. - Zero app-specific code — No RMA, tickets, shipping, or business logic. Pure UI primitives.
- Namespaced classes — All classes prefixed with
tc-to avoid collisions with existing app styles. - Progressive adoption — Import the full bundle or individual pieces. Existing styles continue to work.
- Frontend only — Renderer-process CSS and JS. No backend, no IPC, no Node.js dependencies.
