Building Modern UI Components
A comprehensive guide to creating reusable, theme-aware components with CSS variables.
A modern, minimalist UI Components with two distinct themes. Warm theme for content management systems and publishing, Cold theme for SaaS applications and API businesses.
Warm, content-focused design for CMS and publishing platforms. Features warm gray tones with earthy accents like Wine (#4a3636) and Olive (#7a7d6e).
Cold, professional design for SaaS and API-driven businesses. Features cool gray tones with emphasis on Cool Gray 600 (#4b5563).
Documentation-focused layout with sidebar navigation, search functionality, and content hierarchy.
View Details →Blog-focused layout with article listings, author information, and reading progress.
View Details →Our color system is built on Pantone colors with semantic variations for different use cases. Features both Warm theme (earth tones) and Cold theme (cool contrasts) with semantic colors for success, warning, error, and info states.
/* Cool Gray Color Scale */
--cool-100: #F7F8F9;
--cool-200: #E5E7EB;
--cool-300: #D1D5DB;
--cool-400: #9CA3AF;
--cool-500: #6B7280;
--cool-600: #4B5563;
--cool-700: #374151;
--cool-800: #1F2937;
--cool-900: #111827;
/* Theme Highlights */
--theme-highlight: #1E3A8A; /* Light: Deep navy blue */
--theme-highlight: #3B82F6; /* Dark: Navy blue */
Switch between Warm and Cold themes dynamically with a single CSS file.
Variable | Preview |
---|---|
Background | var(--theme-bg) |
Accent | var(--theme-accent) |
<!-- Load single CSS file with both themes -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@monochrome-edge/ui@latest/dist/monochrome.min.css">
<!-- Set initial theme -->
<html data-theme="light" data-theme-variant="warm">
<script>
// Switch theme dynamically
function switchTheme(variant) {
document.documentElement.setAttribute('data-theme-variant', variant);
}
</script>
<button onclick="switchTheme('warm')">Warm Theme</button>
<button onclick="switchTheme('cold')">Cold Theme</button>
Use Warm theme exclusively for content-focused sites (blogs, documentation).
Feature | Value |
---|---|
Font Size | 16px (body) |
Color Palette | Warm Gray |
Border Radius | 8px |
Best For | Blogs, Documentation |
<!-- Load main CSS and warm theme only -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@monochrome-edge/ui@latest/dist/monochrome.min.css">
<!-- Set warm theme variant -->
<html data-theme="light" data-theme-variant="warm">
<!-- No JavaScript needed - static theme -->
Use Cold theme exclusively for SaaS and dashboard applications.
Feature | Value |
---|---|
Font Size | 15px (body) |
Color Palette | Cool Gray |
Border Radius | 6px |
Best For | SaaS, Dashboards |
<!-- Load main CSS and cold theme only -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@monochrome-edge/ui@latest/dist/monochrome.min.css">
<!-- Set cold theme variant -->
<html data-theme="light" data-theme-variant="cold">
<!-- No JavaScript needed - static theme -->
Primary text with theme surface background
Secondary text color
<!-- Import Monochrome Edge CSS -->
<link rel="stylesheet" href="@monochrome-edge/ui/dist/monochrome.min.css">
<!-- Use CSS variables -->
<div style="background: var(--theme-surface); color: var(--theme-text-primary)">
Content with theme colors
</div>
<style>
.custom-element {
background: var(--theme-accent);
color: var(--theme-accent-contrast);
border: 1px solid var(--theme-border);
}
</style>
import '@monochrome-edge/ui/dist/monochrome.min.css';
function MyComponent() {
return (
<div style={{
background: 'var(--theme-surface)',
color: 'var(--theme-text-primary)',
padding: '1rem',
borderRadius: 'var(--border-radius)'
}}>
Content with theme colors
</div>
);
}
// Or use className with CSS
<div className="my-themed-component">Content</div>
<template>
<div :style="themeStyle">
Content with theme colors
</div>
</template>
<script setup>
import '@monochrome-edge/ui/dist/monochrome.min.css';
const themeStyle = {
background: 'var(--theme-surface)',
color: 'var(--theme-text-primary)',
padding: '1rem',
borderRadius: 'var(--border-radius)'
};
</script>
class MyComponent extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
connectedCallback() {
this.shadowRoot.innerHTML = \`
<style>
:host {
display: block;
background: var(--theme-surface);
color: var(--theme-text-primary);
padding: 1rem;
border-radius: var(--border-radius);
}
</style>
<slot></slot>
\`;
}
}
// Change theme dynamically
document.documentElement.setAttribute('data-theme', 'dark');
document.documentElement.setAttribute('data-theme-variant', 'cold');
// Access CSS variable values
const accentColor = getComputedStyle(document.documentElement)
.getPropertyValue('--theme-accent');
// Set custom CSS variable
document.documentElement.style.setProperty('--theme-accent', '#new-color');
// Apply to element
const element = document.createElement('div');
element.style.background = 'var(--theme-surface)';
element.style.color = 'var(--theme-text-primary)';
Clean, readable typography system based on Inter font family with full type scale (h1-h6), body text variants, display text, and semantic text styles (muted, emphasized, small).
Body text - 1rem
Small body text - 0.875rem
Caption text - 0.75rem
<!-- Typography is styled by default - no classes needed -->
<h1>Main Heading</h1>
<h2>Subheading</h2>
<p>Body text content</p>
<small>Secondary text</small>
<!-- Optional: Use utility classes for specific styling -->
<p class="text-muted">Muted text</p>
<p class="text-emphasis">Emphasized text</p>
import '@monochrome-edge/ui/styles.css';
function MyComponent() {
return (
<div>
<h1>Main Heading</h1>
<h2>Subheading</h2>
<p>Body text content</p>
<small>Secondary text</small>
{/* Optional utility classes */}
<p className="text-muted">Muted text</p>
</div>
);
}
<template>
<div>
<h1>Main Heading</h1>
<h2>Subheading</h2>
<p>Body text content</p>
<small>Secondary text</small>
<!-- Optional utility classes -->
<p class="text-muted">Muted text</p>
</div>
</template>
<script setup>
import '@monochrome-edge/ui/styles.css';
</script>
<link rel="stylesheet" href="@monochrome-edge/ui/styles.css">
<my-component>
<h1>Main Heading</h1>
<h2>Subheading</h2>
<p>Body text content</p>
<small>Secondary text</small>
</my-component>
// Add typography elements dynamically
$('<h1>').text('Main Heading').appendTo('#container');
$('<h2>').text('Subheading').appendTo('#container');
$('<p>').text('Body text content').appendTo('#container');
$('<small>').text('Secondary text').appendTo('#container');
// Add utility class if needed
$('<p>').addClass('text-muted').text('Muted text').appendTo('#container');
This is a regular paragraph with natural line height and comfortable reading width. Paragraphs support bold text, italic text, and hyperlinks for rich text formatting.
Multiple paragraphs are automatically spaced for optimal readability. The typography system ensures consistent vertical rhythm throughout your content.
<div class="blog-content">
<p>Regular paragraph with <strong>bold</strong>, <em>italic</em>, and <a href="#">link</a>.</p>
</div>
inline code
<div class="blog-content">
<ul>
<li>First item</li>
<li>Second item</li>
</ul>
<ol>
<li>First step</li>
<li>Second step</li>
</ol>
</div>
Use inline code for variable names like
const example = true
,
function names like
getUserData()
, or file
paths like
/src/components/Button.tsx
.
<div class="blog-content">
<p>Inline code: <code>const x = 1;</code></p>
</div>
// JavaScript example
function greet(name) {
return `Hello, ${name}!`;
}
console.log(greet('World'));
<div class="blog-content">
<pre><code>// Code block
function example() {
return true;
}</code></pre>
</div>
This is a blockquote with modern edge styling. It's used for quotations or important notes that need to stand out from the main content.
<div class="blog-content">
<blockquote>
<p>Blockquote text</p>
</blockquote>
</div>
Content above the divider.
Content below the divider.
<div class="blog-content">
<hr />
</div>
Feature | Description | Status |
---|---|---|
Typography | Complete type system | ✓ Done |
Components | UI component library | ✓ Done |
Themes | Dual-theme support | ✓ Done |
<div class="blog-content">
<table>
<thead>
<tr><th>Header</th></tr>
</thead>
<tbody>
<tr><td>Cell</td></tr>
</tbody>
</table>
</div>
The quadratic formula x = \frac{-b \pm \sqrt{b^2-4ac}}{2a} is used to solve quadratic equations.
import { MathRenderer, renderAllMath } from '@monochrome-edge/ui';
// Render a single equation
const equation = new MathRenderer({
container: '#my-equation',
displayMode: true
});
// Update equation dynamically
equation.update('E = mc^2');
// Or render all math elements on the page
renderAllMath({
inlineSelector: '.math-inline',
displaySelector: '.math-display'
});
// HTML:
// <span class="math-inline">E = mc^2</span>
// <div class="math-display">\int_0^1 x^2 dx</div>
Logo and brand identity elements for the Monochrome Edge design system.
Primary Logo
Full wordmark with symbol
Secondary Logo
Symbol mark only
<!-- Recommended: Use image files -->
<img src="assets/logo-primary.svg" alt="Monochrome Edge" width="220" height="40">
<img src="assets/logo-symbol.svg" alt="Monochrome Edge Symbol" width="40" height="40">
<!-- Or PNG for better compatibility -->
<img src="assets/logo-primary.png" alt="Monochrome Edge" width="220" height="40">
<img src="assets/logo-symbol.png" alt="Monochrome Edge Symbol" width="40" height="40">
Only use inline SVG if you need to dynamically control colors with CSS:
<!-- Inline SVG with theme-aware colors -->
<svg class="brand-logo-primary" width="220" height="40" viewBox="0 0 220 40">
<line x1="2" y1="35" x2="22" y2="5" stroke="#78716c" stroke-width="3"/>
<line x1="10" y1="35" x2="30" y2="5" stroke="#78716c" stroke-width="3"/>
<text x="40" y="25" fill="currentColor" font-family="system-ui"
font-size="14" font-weight="700">MONOCHROME EDGE</text>
</svg>
<style>
.brand-logo-primary {
color: var(--theme-text-primary);
}
</style>
bold
italic
strikethrough
code
code-block
list-ul
list-ol
checkbox
quote
link
image
table
sun
moon
flame
snowflake
palette
globe
search
menu
close
plus
check
completed
circle
file
folder
file-plus
folder-plus
github
x-social
youtube
slack
discord
// JavaScript - Using iconLoader
import { iconLoader } from '@src/iconLoader';
// Async loading
const svg = await iconLoader.load('search', { width: 20, height: 20 });
element.innerHTML = svg;
// Sync loading (cached or placeholder)
const svg = iconLoader.loadSync('search', { width: 20, height: 20 });
// Using helper utilities
import { createIcon, setIcon, createIconButton } from '@utils/icon.js';
// Create icon element
const iconEl = await createIcon('search', { width: 20, height: 20 });
// Set icon on existing element
await setIcon(buttonElement, 'search', { width: 16, height: 16 });
// Create icon button
const btn = await createIconButton('close', {
width: 16,
height: 16,
title: 'Close',
onClick: () => console.log('clicked')
});
Background patterns and textures for creating visual depth.
<!-- Wave Landscape -->
<div class="b-landscape b-landscape-wave"></div>
<!-- Mountain Landscape -->
<div class="b-landscape b-landscape-mountain"></div>
<!-- Forest Landscape -->
<div class="b-landscape b-landscape-forest"></div>
<!-- Desert Landscape -->
<div class="b-landscape b-landscape-desert"></div>
/* Base Landscape */
.b-landscape {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 320px;
pointer-events: none;
z-index: 0;
}
/* Wave - Animated flowing waves */
.b-landscape-wave .wave-layer {
animation: wave 8s ease-in-out infinite;
}
/* Mountain - Parallax mountain ranges */
.b-landscape-mountain .mountain-layer {
animation: parallax-mountain 20s ease-in-out infinite;
}
/* Forest - Swaying trees */
.b-landscape-forest .tree {
animation: tree-sway 4s ease-in-out infinite;
}
/* Desert - Shifting sand dunes */
.b-landscape-desert .dune {
animation: dune-shift 30s ease-in-out infinite;
}
Navigation components for article content with hover card and collapsible list styles.
<div class="toc-hover-card">
<div class="toc-card">
<h4 class="toc-card-title">Contents</h4>
<ul class="toc-card-list">
<li class="toc-card-item">
<a href="#intro" class="toc-card-link">Introduction</a>
</li>
<li class="toc-card-item">
<a href="#start" class="toc-card-link">Getting Started</a>
</li>
</ul>
</div>
</div>
<div class="toc-collapsible is-open">
<div class="toc-collapsible-header">
<h4 class="toc-collapsible-title">Table of Contents</h4>
<span class="toc-collapsible-icon">▼</span>
</div>
<div class="toc-collapsible-content">
<ul class="toc-list">
<li class="toc-list-item">
<a href="#intro" class="toc-list-link is-active">Introduction</a>
</li>
<li class="toc-list-item">
<a href="#start" class="toc-list-link">Getting Started</a>
</li>
</ul>
</div>
</div>
<script>
// Toggle collapsible TOC
$('.toc-collapsible-header').on('click', function() {
$(this).parent().toggleClass('is-open');
});
</script>
import { TOC } from '@monochrome-edge/ui/react';
const tocItems = [
{ id: '1', label: 'Introduction', href: '#intro' },
{ id: '2', label: 'Getting Started', href: '#start' },
{ id: '3', label: 'Core Concepts', href: '#concepts' },
{ id: '4', label: 'API Reference', href: '#api' }
];
<TOC
items={tocItems}
activeId="1"
collapsible={true}
title="Table of Contents"
onItemClick={(item) => console.log('Clicked:', item)}
/>
<script setup>
import { TOC } from '@monochrome-edge/ui/vue';
const tocItems = [
{ id: '1', label: 'Introduction', href: '#intro' },
{ id: '2', label: 'Getting Started', href: '#start' },
{ id: '3', label: 'Core Concepts', href: '#concepts' },
{ id: '4', label: 'API Reference', href: '#api' }
];
const handleItemClick = (item) => {
console.log('Clicked:', item);
};
</script>
<template>
<TOC
:items="tocItems"
active-id="1"
:collapsible="true"
title="Table of Contents"
@item-click="handleItemClick"
/>
</template>
<mce-toc
title="Table of Contents"
collapsible
active-id="1"
>
<mce-toc-item id="1" href="#intro">Introduction</mce-toc-item>
<mce-toc-item id="2" href="#start">Getting Started</mce-toc-item>
<mce-toc-item id="3" href="#concepts">Core Concepts</mce-toc-item>
<mce-toc-item id="4" href="#api">API Reference</mce-toc-item>
</mce-toc>
<script>
const toc = document.querySelector('mce-toc');
toc.addEventListener('item-click', (e) => {
console.log('Clicked:', e.detail);
});
</script>
$('#toc').toc({
items: [
{ id: '1', label: 'Introduction', href: '#intro' },
{ id: '2', label: 'Getting Started', href: '#start' },
{ id: '3', label: 'Core Concepts', href: '#concepts' },
{ id: '4', label: 'API Reference', href: '#api' }
],
activeId: '1',
collapsible: true,
title: 'Table of Contents',
onItemClick: function(item) {
console.log('Clicked:', item);
}
});
Visualize relationships between your documents and content. Click nodes to explore connections, hover for detailed metadata, and discover how your information is interconnected.
import { GraphView } from '@monochrome-edge/ui';
const documents = [
{
id: 'doc1',
title: 'Introduction',
tags: ['overview', 'getting-started'],
links: [{ target: 'doc2', type: 'reference' }]
},
{
id: 'doc2',
title: 'API Reference',
tags: ['api', 'reference'],
links: [{ target: 'doc3', type: 'reference' }]
}
];
const graphView = new MonochromeEdge.GraphView({
container: '#graph-container',
documents,
nodeRadius: 8,
showLabels: true,
onNodeClick: (node) => console.log('Clicked:', node.title),
onNodeHover: (node) => {
if (node) console.log('Hovering:', node.title);
}
});
import { GraphView } from '@monochrome-edge/react';
function DocumentGraph({ documents }) {
const handleNodeClick = (node) => {
console.log('Clicked:', node.title);
};
return (
<GraphView
documents={documents}
width={800}
height={600}
nodeRadius={8}
showLabels={true}
onNodeClick={handleNodeClick}
onNodeHover={(node) => {
if (node) console.log('Hovering:', node.title);
}}
/>
);
}
<template>
<GraphView
:documents="documents"
:width="800"
:height="600"
:node-radius="8"
:show-labels="true"
@node-click="handleNodeClick"
@node-hover="handleNodeHover"
/>
</template>
<script setup>
import { GraphView } from '@monochrome-edge/vue';
const documents = ref([
{ id: 'doc1', title: 'Introduction', tags: ['overview'] },
{ id: 'doc2', title: 'API Reference', tags: ['api'] }
]);
const handleNodeClick = (node) => {
console.log('Clicked:', node.title);
};
const handleNodeHover = (node) => {
if (node) console.log('Hovering:', node.title);
};
</script>
<mce-graph-view
id="graph"
width="800"
height="600"
></mce-graph-view>
<script type="module">
import '@monochrome-edge/web-components';
const graph = document.getElementById('graph');
const documents = [
{ id: 'doc1', title: 'Introduction', tags: ['overview'] },
{ id: 'doc2', title: 'API Reference', tags: ['api'] }
];
graph.setDocuments(documents);
graph.addEventListener('node-click', (e) => {
console.log('Clicked:', e.detail.title);
});
</script>
$('#graph-container').graphView({
documents: [
{ id: 'doc1', title: 'Introduction', tags: ['overview'] },
{ id: 'doc2', title: 'API Reference', tags: ['api'] }
],
width: 800,
height: 600,
nodeRadius: 8,
showLabels: true,
onNodeClick: (node) => {
console.log('Clicked:', node.title);
},
onNodeHover: (node) => {
if (node) console.log('Hovering:', node.title);
}
});
Navigate nested structures intuitively. Expand folders to reveal contents, collapse to simplify your view. Perfect for file systems, documentation, and any hierarchical data.
<div class="tree-view">
<div class="tree-node">
<div class="tree-node-content">
<span class="tree-toggle">▶</span>
<span class="tree-icon">📁</span>
<span class="tree-label">src</span>
</div>
<div class="tree-children">
<div class="tree-node">
<div class="tree-node-content">
<span class="tree-icon">📄</span>
<span class="tree-label">index.ts</span>
</div>
</div>
</div>
</div>
</div>
import { TreeView } from '@monochrome-edge/ui/react';
const treeData = [
{
id: 'src',
label: 'src',
icon: '📁',
children: [
{
id: 'components',
label: 'components',
icon: '📁',
children: [
{ id: 'button', label: 'Button.tsx', icon: '📄' },
{ id: 'card', label: 'Card.tsx', icon: '📄' }
]
},
{ id: 'utils', label: 'utils.ts', icon: '📄' }
]
}
];
<TreeView
data={treeData}
expandedByDefault={false}
onNodeClick={(node) => console.log('Clicked:', node.label)}
onNodeToggle={(node, isExpanded) => {
console.log('Toggled:', node.label, isExpanded);
}}
/>
<script setup>
import { TreeView } from '@monochrome-edge/ui/vue';
const treeData = [
{
id: 'src',
label: 'src',
icon: '📁',
children: [
{
id: 'components',
label: 'components',
icon: '📁',
children: [
{ id: 'button', label: 'Button.tsx', icon: '📄' },
{ id: 'card', label: 'Card.tsx', icon: '📄' }
]
},
{ id: 'utils', label: 'utils.ts', icon: '📄' }
]
}
];
const handleNodeClick = (node) => {
console.log('Clicked:', node.label);
};
const handleNodeToggle = (node, isExpanded) => {
console.log('Toggled:', node.label, isExpanded);
};
</script>
<template>
<TreeView
:data="treeData"
:expanded-by-default="false"
@node-click="handleNodeClick"
@node-toggle="handleNodeToggle"
/>
</template>
<mce-tree-view id="tree"></mce-tree-view>
<script type="module">
const tree = document.getElementById('tree');
const treeData = [
{
id: 'src',
label: 'src',
icon: '📁',
children: [
{
id: 'components',
label: 'components',
icon: '📁',
children: [
{ id: 'button', label: 'Button.tsx', icon: '📄' },
{ id: 'card', label: 'Card.tsx', icon: '📄' }
]
},
{ id: 'utils', label: 'utils.ts', icon: '📄' }
]
}
];
tree.data = treeData;
tree.addEventListener('node-click', (e) => {
console.log('Clicked:', e.detail.label);
});
tree.addEventListener('node-toggle', (e) => {
console.log('Toggled:', e.detail.label, e.detail.isExpanded);
});
</script>
$('#tree-container').treeView({
data: [
{
id: 'src',
label: 'src',
icon: '📁',
children: [
{
id: 'components',
label: 'components',
icon: '📁',
children: [
{ id: 'button', label: 'Button.tsx', icon: '📄' },
{ id: 'card', label: 'Card.tsx', icon: '📄' }
]
},
{ id: 'utils', label: 'utils.ts', icon: '📄' }
]
}
],
expandedByDefault: false,
onNodeClick: function(node) {
console.log('Clicked:', node.label);
},
onNodeToggle: function(node, isExpanded) {
console.log('Toggled:', node.label, isExpanded);
}
});
// API methods
$('#tree-container').treeView('expandAll');
$('#tree-container').treeView('collapseAll');
Tab navigation component with multiple styles and orientations.
Home tab content goes here.
<!-- Default Tabs -->
<div class="tabs">
<div class="tabs-list">
<button class="tab active">Home</button>
<button class="tab">Profile</button>
<button class="tab">Settings</button>
</div>
<div class="tabs-panels">
<div class="tab-panel active">Content here</div>
</div>
</div>
<!-- Pills Style -->
<div class="tabs">
<div class="tabs-list tabs-pills">
<button class="tab active">Dashboard</button>
<button class="tab">Analytics</button>
</div>
</div>
<!-- Boxed Style -->
<div class="tabs">
<div class="tabs-list tabs-boxed">
<button class="tab active">Code</button>
<button class="tab">Preview</button>
</div>
</div>
import { Tabs } from '@monochrome-edge/react';
function MyTabs() {
const tabs = [
{ id: 'home', label: 'Home', content: <div>Home content</div> },
{ id: 'profile', label: 'Profile', content: <div>Profile content</div> },
{ id: 'settings', label: 'Settings', content: <div>Settings content</div> }
];
const handleChange = (tabId) => {
console.log('Active tab:', tabId);
};
return (
<Tabs
tabs={tabs}
defaultActiveId="home"
onChange={handleChange}
/>
);
}
<template>
<Tabs
:tabs="tabs"
default-active-id="home"
@change="handleChange"
/>
</template>
<script setup>
import { Tabs } from '@monochrome-edge/vue';
const tabs = [
{ id: 'home', label: 'Home', content: 'Home content' },
{ id: 'profile', label: 'Profile', content: 'Profile content' },
{ id: 'settings', label: 'Settings', content: 'Settings content' }
];
const handleChange = (tabId) => {
console.log('Active tab:', tabId);
};
</script>
<mce-tabs id="tabs">
<div slot="tab-home" data-label="Home">Home content</div>
<div slot="tab-profile" data-label="Profile">Profile content</div>
<div slot="tab-settings" data-label="Settings">Settings content</div>
</mce-tabs>
<script type="module">
import '@monochrome-edge/web-components';
const tabs = document.getElementById('tabs');
tabs.addEventListener('tab-change', (e) => {
console.log('Active tab:', e.detail.tabId);
});
</script>
$('#tabs-container').tabs({
defaultActiveId: 'home',
onChange: (tabId) => {
console.log('Active tab:', tabId);
}
});
// HTML structure for jQuery
<div id="tabs-container">
<div data-tab-id="home" data-tab-label="Home">Home content</div>
<div data-tab-id="profile" data-tab-label="Profile">Profile content</div>
<div data-tab-id="settings" data-tab-label="Settings">Settings content</div>
</div>
Progress indicators for loading states, file uploads, and multi-step processes. Supports linear bars and step-based progress.
<!-- Basic Progress Bar with data attribute -->
<div class="progress" data-value="45">
<div class="progress-bar"></div>
</div>
<!-- Striped & Animated -->
<div class="progress" data-value="75">
<div class="progress-bar progress-striped progress-animated"></div>
</div>
<!-- Colored (Semantic) -->
<div class="progress" data-value="100">
<div class="progress-bar progress-success"></div>
</div>
<div class="progress" data-value="60">
<div class="progress-bar progress-warning"></div>
</div>
<div class="progress" data-value="30">
<div class="progress-bar progress-error"></div>
</div>
<!-- Indeterminate (no value needed) -->
<div class="progress progress-indeterminate">
<div class="progress-bar"></div>
</div>
<script>
// Initialize progress bars
document.querySelectorAll('.progress[data-value]').forEach(el => {
const value = el.getAttribute('data-value');
el.querySelector('.progress-bar').style.width = value + '%';
});
// Update progress dynamically
function setProgress(element, value) {
const bar = element.querySelector('.progress-bar');
bar.style.width = Math.min(100, Math.max(0, value)) + '%';
}
</script>
import { useState, useEffect } from 'react';
function ProgressDemo() {
const [progress, setProgress] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
setProgress((prev) => (prev >= 100 ? 0 : prev + 10));
}, 500);
return () => clearInterval(timer);
}, []);
return (
<div>
{/* Monochrome */}
<div className="progress">
<div className="progress-bar" style={{ width: `${progress}%` }} />
</div>
{/* Colored */}
<div className="progress">
<div className="progress-bar progress-success" style={{ width: '100%' }} />
</div>
{/* Indeterminate */}
<div className="progress progress-indeterminate">
<div className="progress-bar" />
</div>
</div>
);
}
<template>
<div>
<!-- Monochrome -->
<div class="progress">
<div class="progress-bar" :style="{ width: progress + '%' }" />
</div>
<!-- Colored -->
<div class="progress">
<div class="progress-bar progress-success" style="width: 100%" />
</div>
<!-- Indeterminate -->
<div class="progress progress-indeterminate">
<div class="progress-bar" />
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
const progress = ref(0);
let timer;
onMounted(() => {
timer = setInterval(() => {
progress.value = progress.value >= 100 ? 0 : progress.value + 10;
}, 500);
});
onUnmounted(() => clearInterval(timer));
</script>
// Animate progress
let progress = 0;
setInterval(() => {
progress = progress >= 100 ? 0 : progress + 10;
$('.progress-bar').css('width', progress + '%');
}, 500);
// Set specific progress
$('.progress-bar').css('width', '75%');
// Add classes dynamically
$('.progress-bar').addClass('progress-success');
$('.progress-bar').addClass('progress-striped progress-animated');
Flexible button system with multiple variants (primary, secondary, ghost, outline, text) and sizes (xs, sm, md, lg, xl). Includes disabled and loading states.
<button class="btn btn-primary">Primary</button>
<button class="btn btn-secondary">Secondary</button>
import { Button } from '@monochrome-edge/ui/react';
<Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
<template>
<Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
</template>
<script setup>
import { Button } from '@monochrome-edge/ui/vue';
</script>
<mce-button variant="primary">Primary</mce-button>
<mce-button variant="secondary">Secondary</mce-button>
<script type="module">
import '@monochrome-edge/ui/wc';
</script>
$('<button>').addClass('btn btn-primary').text('Primary').appendTo('#app');
$('<button>').addClass('btn btn-secondary').text('Secondary').appendTo('#app');
import { createButton } from '@monochrome-edge/ui';
const btn1 = createButton('Primary', { variant: 'primary' });
const btn2 = createButton('Secondary', { variant: 'secondary' });
document.body.appendChild(btn1);
document.body.appendChild(btn2);
<button class="btn btn-primary btn-small">Small</button>
<button class="btn btn-primary">Default</button>
<button class="btn btn-primary btn-large">Large</button>
import { Button } from '@monochrome-edge/ui/react';
<Button size="small">Small</Button>
<Button>Default</Button>
<Button size="large">Large</Button>
<template>
<Button size="small">Small</Button>
<Button>Default</Button>
<Button size="large">Large</Button>
</template>
<script setup>
import { Button } from '@monochrome-edge/ui/vue';
</script>
<mce-button size="small">Small</mce-button>
<mce-button>Default</mce-button>
<mce-button size="large">Large</mce-button>
<script type="module">
import '@monochrome-edge/ui/wc';
</script>
$('<button>').addClass('btn btn-primary btn-small').text('Small').appendTo('#app');
$('<button>').addClass('btn btn-primary').text('Default').appendTo('#app');
$('<button>').addClass('btn btn-primary btn-large').text('Large').appendTo('#app');
import { createButton } from '@monochrome-edge/ui';
const btnSmall = createButton('Small', { variant: 'primary', size: 'small' });
const btnDefault = createButton('Default', { variant: 'primary' });
const btnLarge = createButton('Large', { variant: 'primary', size: 'large' });
document.body.appendChild(btnSmall);
document.body.appendChild(btnDefault);
document.body.appendChild(btnLarge);
Icon-only buttons with multiple variants, sizes, and states for intuitive actions.
<!-- Using package icons -->
<button class="icon-btn">
<img src="https://cdn.jsdelivr.net/npm/@monochrome-edge/ui/assets/icons/sun.svg" class="icon" alt="Sun">
</button>
<button class="icon-btn icon-btn-primary">
<img src="https://cdn.jsdelivr.net/npm/@monochrome-edge/ui/assets/icons/sun.svg" class="icon" alt="Sun">
</button>
<button class="icon-btn icon-btn-ghost">
<img src="https://cdn.jsdelivr.net/npm/@monochrome-edge/ui/assets/icons/sun.svg" class="icon" alt="Sun">
</button>
<button class="icon-btn icon-btn-outline">
<img src="https://cdn.jsdelivr.net/npm/@monochrome-edge/ui/assets/icons/sun.svg" class="icon" alt="Sun">
</button>
<!-- Or copy icons to your project -->
<button class="icon-btn">
<img src="assets/icons/sun.svg" class="icon" alt="Sun">
</button>
import { IconButton } from '@monochrome-edge/react';
<IconButton icon="sun" variant="default" />
<IconButton icon="sun" variant="primary" />
<IconButton icon="sun" variant="ghost" />
<IconButton icon="sun" variant="outline" />
<template>
<IconButton icon="sun" variant="default" />
<IconButton icon="sun" variant="primary" />
<IconButton icon="sun" variant="ghost" />
<IconButton icon="sun" variant="outline" />
</template>
<script setup>
import { IconButton } from '@monochrome-edge/vue';
</script>
<mce-icon-button icon="sun" variant="default"></mce-icon-button>
<mce-icon-button icon="sun" variant="primary"></mce-icon-button>
<mce-icon-button icon="sun" variant="ghost"></mce-icon-button>
<mce-icon-button icon="sun" variant="outline"></mce-icon-button>
<script type="module">
import '@monochrome-edge/web-components';
</script>
const btn1 = $.createIconButton({ icon: 'sun', variant: 'default' });
const btn2 = $.createIconButton({ icon: 'sun', variant: 'primary' });
const btn3 = $.createIconButton({ icon: 'sun', variant: 'ghost' });
const btn4 = $.createIconButton({ icon: 'sun', variant: 'outline' });
import { createIconButton } from '@monochrome-edge/vanilla-ts';
const btn1 = createIconButton({ icon: 'sun', variant: 'default' });
const btn2 = createIconButton({ icon: 'sun', variant: 'primary' });
const btn3 = createIconButton({ icon: 'sun', variant: 'ghost' });
const btn4 = createIconButton({ icon: 'sun', variant: 'outline' });
Icon Resources:
<!-- Using package icons -->
<button class="icon-btn">
<img src="https://cdn.jsdelivr.net/npm/@monochrome-edge/ui/assets/icons/sun.svg" class="icon" alt="Sun">
</button>
<button class="icon-btn icon-btn-primary">
<img src="https://cdn.jsdelivr.net/npm/@monochrome-edge/ui/assets/icons/sun.svg" class="icon" alt="Sun">
</button>
<button class="icon-btn icon-btn-ghost">
<img src="https://cdn.jsdelivr.net/npm/@monochrome-edge/ui/assets/icons/sun.svg" class="icon" alt="Sun">
</button>
<button class="icon-btn icon-btn-outline">
<img src="https://cdn.jsdelivr.net/npm/@monochrome-edge/ui/assets/icons/sun.svg" class="icon" alt="Sun">
</button>
<!-- Or copy icons to your project -->
<button class="icon-btn">
<img src="assets/icons/sun.svg" class="icon" alt="Sun">
</button>
import { IconButton } from '@monochrome-edge/react';
<IconButton icon="sun" variant="default" />
<IconButton icon="sun" variant="primary" />
<IconButton icon="sun" variant="ghost" />
<IconButton icon="sun" variant="outline" />
<template>
<IconButton icon="sun" variant="default" />
<IconButton icon="sun" variant="primary" />
<IconButton icon="sun" variant="ghost" />
<IconButton icon="sun" variant="outline" />
</template>
<script setup>
import { IconButton } from '@monochrome-edge/vue';
</script>
<mce-icon-button icon="sun" variant="default"></mce-icon-button>
<mce-icon-button icon="sun" variant="primary"></mce-icon-button>
<mce-icon-button icon="sun" variant="ghost"></mce-icon-button>
<mce-icon-button icon="sun" variant="outline"></mce-icon-button>
<script type="module">
import '@monochrome-edge/web-components';
</script>
const btn1 = $.createIconButton({ icon: 'sun', variant: 'default' });
const btn2 = $.createIconButton({ icon: 'sun', variant: 'primary' });
const btn3 = $.createIconButton({ icon: 'sun', variant: 'ghost' });
const btn4 = $.createIconButton({ icon: 'sun', variant: 'outline' });
import { createIconButton } from '@monochrome-edge/vanilla-ts';
const btn1 = createIconButton({ icon: 'sun', variant: 'default' });
const btn2 = createIconButton({ icon: 'sun', variant: 'primary' });
const btn3 = createIconButton({ icon: 'sun', variant: 'ghost' });
const btn4 = createIconButton({ icon: 'sun', variant: 'outline' });
<!-- Using package icons -->
<button class="icon-btn icon-btn-sm">
<img src="https://cdn.jsdelivr.net/npm/@monochrome-edge/ui/assets/icons/sun.svg" class="icon" alt="Sun">
</button>
<button class="icon-btn icon-btn-md">
<img src="https://cdn.jsdelivr.net/npm/@monochrome-edge/ui/assets/icons/sun.svg" class="icon" alt="Sun">
</button>
<button class="icon-btn icon-btn-lg">
<img src="https://cdn.jsdelivr.net/npm/@monochrome-edge/ui/assets/icons/sun.svg" class="icon" alt="Sun">
</button>
<button class="icon-btn icon-btn-xl">
<img src="https://cdn.jsdelivr.net/npm/@monochrome-edge/ui/assets/icons/sun.svg" class="icon" alt="Sun">
</button>
<!-- Or copy icons to your project -->
<button class="icon-btn icon-btn-md">
<img src="assets/icons/sun.svg" class="icon" alt="Sun">
</button>
<IconButton icon="sun" size="sm" />
<IconButton icon="sun" size="md" />
<IconButton icon="sun" size="lg" />
<IconButton icon="sun" size="xl" />
<IconButton icon="sun" size="sm" />
<IconButton icon="sun" size="md" />
<IconButton icon="sun" size="lg" />
<IconButton icon="sun" size="xl" />
$.createIconButton({ icon: 'sun', size: 'sm' });
$.createIconButton({ icon: 'sun', size: 'md' });
$.createIconButton({ icon: 'sun', size: 'lg' });
$.createIconButton({ icon: 'sun', size: 'xl' });
<mce-icon-button icon="sun" size="sm"></mce-icon-button>
<mce-icon-button icon="sun" size="md"></mce-icon-button>
<mce-icon-button icon="sun" size="lg"></mce-icon-button>
<mce-icon-button icon="sun" size="xl"></mce-icon-button>
<script type="module">
import '@monochrome-edge/web-components';
</script>
import { createIconButton } from '@monochrome-edge/vanilla-ts';
const btnSm = createIconButton({ icon: 'sun', size: 'sm' });
const btnMd = createIconButton({ icon: 'sun', size: 'md' });
const btnLg = createIconButton({ icon: 'sun', size: 'lg' });
const btnXl = createIconButton({ icon: 'sun', size: 'xl' });
<!-- Using package icons -->
<button class="icon-btn">
<img src="https://cdn.jsdelivr.net/npm/@monochrome-edge/ui/assets/icons/sun.svg" class="icon" alt="Sun">
</button>
<button class="icon-btn active">
<img src="https://cdn.jsdelivr.net/npm/@monochrome-edge/ui/assets/icons/sun.svg" class="icon" alt="Sun">
</button>
<button class="icon-btn" disabled>
<img src="https://cdn.jsdelivr.net/npm/@monochrome-edge/ui/assets/icons/sun.svg" class="icon" alt="Sun">
</button>
<button class="icon-btn loading">
<img src="https://cdn.jsdelivr.net/npm/@monochrome-edge/ui/assets/icons/sun.svg" class="icon" alt="Sun">
</button>
<!-- Or copy icons to your project -->
<button class="icon-btn">
<img src="assets/icons/sun.svg" class="icon" alt="Sun">
</button>
<IconButton icon="sun" />
<IconButton icon="sun" active />
<IconButton icon="sun" disabled />
<IconButton icon="sun" loading />
<IconButton icon="sun" />
<IconButton icon="sun" :active="true" />
<IconButton icon="sun" :disabled="true" />
<IconButton icon="sun" :loading="true" />
Theme
Warm ↔ Cold
Mode
Light ↔ Dark
Color
Mono ↔ Color
Language
KO → EN → CN → JP
Click toggles to see animations:
Theme: Vertical slide with warm/cold gradient transition
Mode: Horizontal slide with light→dark gradient
Color: Instant toggle with gradient background
Language: Multi-language cycle (KO → EN → CN → JP) with diagonal animation
<!-- Theme Toggle (Warm ↔ Cold) -->
<button class="icon-btn-toggle icon-btn-toggle-theme" data-state="warm">
<div class="icon-btn-toggle-overlay"></div>
<div class="icon-btn-toggle-icon">
<svg>...flame icon...</svg>
<svg>...snowflake icon...</svg>
</div>
</button>
<!-- Mode Toggle (Light ↔ Dark) -->
<button class="icon-btn-toggle icon-btn-toggle-mode" data-state="light">
<div class="icon-btn-toggle-overlay"></div>
<div class="icon-btn-toggle-icon">
<svg>...sun icon...</svg>
<svg>...moon icon...</svg>
</div>
</button>
<!-- Color Toggle (Monochrome ↔ Colored) -->
<button class="icon-btn-toggle icon-btn-toggle-colored" data-state="monochrome">
<div class="icon-btn-toggle-overlay"></div>
<div class="icon-btn-toggle-icon">
<svg>...palette icon...</svg>
</div>
</button>
<!-- Language Toggle (KO ↔ EN) -->
<button class="language-toggle" data-toggled="false">
<div class="language-toggle-text">
<span class="lang-first">KO</span>
<span class="lang-slash"></span>
<span class="lang-second">EN</span>
</div>
</button>
// Theme toggle with vertical animation
const themeToggle = document.querySelector('.icon-btn-toggle-theme');
themeToggle.addEventListener('click', function() {
const current = this.dataset.state;
const next = current === 'warm' ? 'cold' : 'warm';
this.dataset.transitioning = `${current}-to-${next}`;
this.classList.add('is-animating');
setTimeout(() => {
this.dataset.state = next;
this.removeAttribute('data-transitioning');
this.classList.remove('is-animating');
document.documentElement.setAttribute('data-theme-variant', next);
}, 600);
});
// Mode toggle with horizontal animation
const modeToggle = document.querySelector('.icon-btn-toggle-mode');
modeToggle.addEventListener('click', function() {
const current = this.dataset.state;
const next = current === 'light' ? 'dark' : 'light';
this.dataset.transitioning = `${current}-to-${next}`;
this.classList.add('is-animating');
setTimeout(() => {
this.dataset.state = next;
this.removeAttribute('data-transitioning');
this.classList.remove('is-animating');
document.documentElement.setAttribute('data-theme', next);
}, 700);
});
// Color toggle (instant)
const colorToggle = document.querySelector('.icon-btn-toggle-colored');
colorToggle.addEventListener('click', function() {
const current = this.dataset.state;
const next = current === 'colored' ? 'monochrome' : 'colored';
this.dataset.state = next;
});
// Language toggle with diagonal flip animation
const languageToggle = document.querySelector('.language-toggle');
languageToggle.addEventListener('click', function() {
const isToggled = this.dataset.toggled === 'true';
this.classList.add('is-animating');
setTimeout(() => {
this.dataset.toggled = !isToggled;
this.classList.remove('is-animating');
}, 500);
});
<!-- Theme Toggle -->
<mce-icon-toggle type="theme"></mce-icon-toggle>
<!-- Mode Toggle -->
<mce-icon-toggle type="mode"></mce-icon-toggle>
<!-- Color Toggle -->
<mce-icon-toggle type="color"></mce-icon-toggle>
<!-- Language Toggle -->
<mce-icon-toggle type="language"></mce-icon-toggle>
<!-- Ghost variant -->
<mce-icon-toggle type="mode" variant="ghost"></mce-icon-toggle>
<script>
// Listen to toggle events
document.querySelector('mce-icon-toggle').addEventListener('toggle', (e) => {
console.log('Toggled to:', e.detail.state);
});
</script>
import { IconToggle } from '@monochrome-edge/react';
function MyComponent() {
const handleToggle = (state) => {
console.log('Toggled to:', state);
};
return (
<div style={{ display: 'flex', gap: '1rem' }}>
{/* Theme Toggle */}
<IconToggle
type="theme"
onToggle={handleToggle}
/>
{/* Mode Toggle */}
<IconToggle
type="mode"
onToggle={handleToggle}
/>
{/* Color Toggle */}
<IconToggle
type="color"
onToggle={handleToggle}
/>
{/* Language Toggle */}
<IconToggle
type="language"
onToggle={handleToggle}
/>
{/* Ghost variant */}
<IconToggle
type="mode"
variant="ghost"
onToggle={handleToggle}
/>
</div>
);
}
<template>
<div style="display: flex; gap: 1rem">
<!-- Theme Toggle -->
<IconToggle
type="theme"
@toggle="handleToggle"
/>
<!-- Mode Toggle -->
<IconToggle
type="mode"
@toggle="handleToggle"
/>
<!-- Color Toggle -->
<IconToggle
type="color"
@toggle="handleToggle"
/>
<!-- Language Toggle -->
<IconToggle
type="language"
@toggle="handleToggle"
/>
<!-- Ghost variant -->
<IconToggle
type="mode"
variant="ghost"
@toggle="handleToggle"
/>
</div>
</template>
<script setup>
import { IconToggle } from '@monochrome-edge/vue';
const handleToggle = (state) => {
console.log('Toggled to:', state);
};
</script>
// Theme toggle
$('#theme-toggle').mceIconToggle({
type: 'theme',
onToggle: function(state) {
console.log('Theme:', state);
}
});
// Mode toggle
$('#mode-toggle').mceIconToggle({
type: 'mode',
onToggle: function(state) {
console.log('Mode:', state);
}
});
// Color toggle
$('#color-toggle').mceIconToggle({
type: 'color',
onToggle: function(state) {
console.log('Color:', state);
}
});
// Language toggle
$('#language-toggle').mceIconToggle({
type: 'language',
onToggle: function(state) {
console.log('Language:', state);
}
});
// Ghost variant
$('#ghost-toggle').mceIconToggle({
type: 'mode',
variant: 'ghost'
});
Comprehensive form components including text inputs, textareas, selects, checkboxes, radio buttons, and switches. Features validation states (success, warning, error) and disabled states.
<div class="form-group">
<label class="label">Email Address</label>
<input type="email" class="input" placeholder="you@example.com">
<span class="helper-text">We'll never share your email.</span>
</div>
import { Input } from '@monochrome-edge/ui/react';
<Input
label="Email Address"
type="email"
placeholder="you@example.com"
helperText="We'll never share your email."
/>
<Input
label="Email Address"
type="email"
placeholder="you@example.com"
helper-text="We'll never share your email."
/>
<mce-input
label="Email Address"
type="email"
placeholder="you@example.com"
helper-text="We'll never share your email."
></mce-input>
<script type="module">
import '@monochrome-edge/ui/wc';
</script>
$('#email-input').wrap('<div class="form-group"></div>');
$('#email-input').before('<label class="label">Email Address</label>');
$('#email-input').after('<span class="helper-text">We\'ll never share your email.</span>');
Flexible dropdown menus and select components with theme support.
Animated toggle buttons for theme (warm/cold), mode (light/dark), and color options with smooth gradient transitions.
Theme
Mode
Color
<!-- Theme Toggle -->
<button class="icon-btn-toggle icon-btn-toggle-theme" data-state="warm">
<div class="icon-btn-toggle-overlay"></div>
<div class="icon-btn-toggle-icon">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24">
<path d="M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24">
<path d="M12 2v2m0 16v2M4.93 4.93l1.41 1.41m11.32 11.32l1.41 1.41M2 12h2m16 0h2M6.34 6.34L4.93 4.93m12.73 14.14l1.41 1.41"/>
<path d="M12 7c-2.8 0-5 2.2-5 5s2.2 5 5 5 5-2.2 5-5-2.2-5-5-5z"/>
</svg>
</div>
</button>
<!-- Mode Toggle -->
<button class="icon-btn-toggle icon-btn-toggle-mode" data-state="light">
<div class="icon-btn-toggle-overlay"></div>
<div class="icon-btn-toggle-icon">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24">
<circle cx="12" cy="12" r="4"/>
<!-- sun rays -->
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24">
<path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"/>
</svg>
</div>
</button>
<!-- Colored/Monochrome Toggle -->
<button class="icon-btn-toggle icon-btn-toggle-colored" data-state="monochrome">
<div class="icon-btn-toggle-overlay"></div>
<div class="icon-btn-toggle-icon">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24">
<!-- palette icon -->
</svg>
</div>
</button>
// Theme toggle with animation
const themeToggle = document.querySelector('.icon-btn-toggle-theme');
themeToggle.addEventListener('click', function() {
const current = this.dataset.state;
const next = current === 'warm' ? 'cold' : 'warm';
// Set transition direction
this.dataset.transitioning = `${current}-to-${next}`;
// Update state after animation
setTimeout(() => {
this.dataset.state = next;
this.removeAttribute('data-transitioning');
// Apply theme to document
document.documentElement.setAttribute('data-theme-variant', next);
}, 500);
});
// Mode toggle with animation
const modeToggle = document.querySelector('.icon-btn-toggle-mode');
modeToggle.addEventListener('click', function() {
const current = this.dataset.state;
const next = current === 'light' ? 'dark' : 'light';
this.dataset.transitioning = `${current}-to-${next}`;
setTimeout(() => {
this.dataset.state = next;
this.removeAttribute('data-transitioning');
// Apply mode to document
document.documentElement.setAttribute('data-theme', next);
}, 700);
});
// Color mode toggle
const colorToggle = document.querySelector('.icon-btn-toggle-colored');
colorToggle.addEventListener('click', function() {
const current = this.dataset.state;
const next = current === 'colored' ? 'monochrome' : 'colored';
this.dataset.state = next;
});
<!-- Theme Toggle -->
<mce-icon-toggle
type="theme"
variant="default"
></mce-icon-toggle>
<!-- Mode Toggle -->
<mce-icon-toggle
type="mode"
></mce-icon-toggle>
<!-- Color Toggle -->
<mce-icon-toggle
type="color"
></mce-icon-toggle>
<!-- Language Toggle -->
<mce-icon-toggle
type="language"
></mce-icon-toggle>
<!-- Ghost variant -->
<mce-icon-toggle
type="mode"
variant="ghost"
></mce-icon-toggle>
<script>
// Listen to toggle events
document.querySelector('mce-icon-toggle').addEventListener('toggle', (e) => {
console.log('Toggled to:', e.detail.state);
});
</script>
import { IconToggle } from '@monochrome-edge/react';
function MyComponent() {
const handleToggle = (state) => {
console.log('New state:', state);
};
return (
<div style={{ display: 'flex', gap: '1rem' }}>
{/* Theme Toggle */}
<IconToggle
type="theme"
onToggle={handleToggle}
/>
{/* Mode Toggle */}
<IconToggle
type="mode"
onToggle={handleToggle}
/>
{/* Color Toggle */}
<IconToggle
type="color"
onToggle={handleToggle}
/>
{/* Language Toggle */}
<IconToggle
type="language"
onToggle={handleToggle}
/>
{/* Ghost variant */}
<IconToggle
type="mode"
variant="ghost"
onToggle={handleToggle}
/>
{/* Disabled */}
<IconToggle
type="theme"
disabled
/>
</div>
);
}
<template>
<div style="display: flex; gap: 1rem">
<!-- Theme Toggle -->
<IconToggle
type="theme"
@toggle="handleToggle"
/>
<!-- Mode Toggle -->
<IconToggle
type="mode"
@toggle="handleToggle"
/>
<!-- Color Toggle -->
<IconToggle
type="color"
@toggle="handleToggle"
/>
<!-- Language Toggle -->
<IconToggle
type="language"
@toggle="handleToggle"
/>
<!-- Ghost variant -->
<IconToggle
type="mode"
variant="ghost"
@toggle="handleToggle"
/>
<!-- Disabled -->
<IconToggle
type="theme"
:disabled="true"
/>
</div>
</template>
<script setup>
import { IconToggle } from '@monochrome-edge/vue';
const handleToggle = (state) => {
console.log('New state:', state);
};
</script>
// Theme toggle
$('#theme-toggle').mceIconToggle({
type: 'theme',
variant: 'default',
onToggle: function(state) {
console.log('Theme toggled to:', state);
}
});
// Mode toggle
$('#mode-toggle').mceIconToggle({
type: 'mode',
onToggle: function(state) {
console.log('Mode toggled to:', state);
}
});
// Color toggle
$('#color-toggle').mceIconToggle({
type: 'color',
onToggle: function(state) {
console.log('Color mode:', state);
}
});
// Language toggle
$('#language-toggle').mceIconToggle({
type: 'language',
onToggle: function(state) {
console.log('Language:', state);
}
});
// Ghost variant
$('#ghost-toggle').mceIconToggle({
type: 'mode',
variant: 'ghost'
});
// Disabled
$('#disabled-toggle').mceIconToggle({
type: 'theme',
disabled: true
});
Powerful search with fuzzy autocomplete, filters, and tag selection. Type to search, select suggestions to add as tags, and use filters to refine your results. Perfect for documentation, e-commerce, and content management.
import { SearchToolbar } from '@monochrome-edge/ui';
// Basic usage
const toolbar = new MonochromeEdge.SearchToolbar('#search-toolbar', {
placeholder: 'Search...',
onSearch: (query) => {
console.log('Search:', query);
// Your search logic here
}
});
// With autocomplete
const toolbarWithAutocomplete = new MonochromeEdge.SearchToolbar('#search-toolbar', {
placeholder: 'Search components...',
autocomplete: async (query) => {
const items = ['React', 'Vue', 'Angular', 'Svelte'];
return items.filter(item =>
item.toLowerCase().includes(query.toLowerCase())
);
},
onSearch: (query) => {
// Handle search
}
});
// Advanced: With filters and sort (see full docs)
const advancedToolbar = new MonochromeEdge.SearchToolbar('#search-toolbar', {
placeholder: 'Advanced search...',
filters: [/* filter config */],
sortOptions: [/* sort config */],
onSearch: (query, filters, sort) => {
// Handle advanced search
}
});
import { SearchToolbar } from '@monochrome-edge/react';
function SearchExample() {
const handleSearch = (query, filters, sort) => {
console.log('Search:', { query, filters, sort });
// Perform your search logic here
};
return (
<SearchToolbar
placeholder="Search components..."
autocomplete={async (query) => {
const items = ['React', 'Vue', 'Angular', 'Svelte'];
return items.filter(item =>
item.toLowerCase().includes(query.toLowerCase())
);
}}
filters={[
{
id: 'category',
label: 'Category',
values: [
{ value: 'all', label: 'All' },
{ value: 'components', label: 'Components' }
],
default: 'all'
}
]}
sortOptions={[
{ value: 'relevance', label: 'Relevance' },
{ value: 'name', label: 'Name' }
]}
onSearch={handleSearch}
/>
);
}
<template>
<SearchToolbar
placeholder="Search components..."
:autocomplete="handleAutocomplete"
:filters="filters"
:sort-options="sortOptions"
@search="handleSearch"
/>
</template>
<script setup>
import { SearchToolbar } from '@monochrome-edge/vue';
const handleAutocomplete = async (query) => {
const items = ['React', 'Vue', 'Angular', 'Svelte'];
return items.filter(item =>
item.toLowerCase().includes(query.toLowerCase())
);
};
const filters = [
{
id: 'category',
label: 'Category',
values: [
{ value: 'all', label: 'All' },
{ value: 'components', label: 'Components' }
],
default: 'all'
}
];
const sortOptions = [
{ value: 'relevance', label: 'Relevance' },
{ value: 'name', label: 'Name' }
];
const handleSearch = (query, filters, sort) => {
console.log('Search:', { query, filters, sort });
// Perform your search logic here
};
</script>
<mce-search-toolbar
placeholder="Search components..."
id="search-toolbar"
></mce-search-toolbar>
<script type="module">
import '@monochrome-edge/web-components';
const searchToolbar = document.getElementById('search-toolbar');
searchToolbar.addEventListener('search', (e) => {
console.log('Search:', e.detail);
});
// Set autocomplete function
searchToolbar.setAutocomplete(async (query) => {
const items = ['React', 'Vue', 'Angular', 'Svelte'];
return items.filter(item =>
item.toLowerCase().includes(query.toLowerCase())
);
});
</script>
$('#search-container').searchToolbar({
placeholder: 'Search components...',
autocomplete: async (query) => {
const items = ['React', 'Vue', 'Angular', 'Svelte'];
return items.filter(item =>
item.toLowerCase().includes(query.toLowerCase())
);
},
filters: [
{
id: 'category',
label: 'Category',
values: [
{ value: 'all', label: 'All' },
{ value: 'components', label: 'Components' }
],
default: 'all'
}
],
sortOptions: [
{ value: 'relevance', label: 'Relevance' },
{ value: 'name', label: 'Name' }
],
onSearch: (query, filters, sort) => {
console.log('Search:', { query, filters, sort });
}
});
Expandable search input with underbar animation. Hover to preview, click to expand with autocomplete.
<!-- Expandable search input with underbar animation -->
<div class="search-input" id="search-input">
<svg class="search-input-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="11" cy="11" r="8"></circle>
<path d="m21 21-4.35-4.35"></path>
</svg>
<input
type="text"
class="search-input-field"
placeholder="Search documentation..."
/>
<button class="search-input-cancel" type="button">×</button>
<div class="search-input-suggestions"></div>
</div>
<script>
const searchInput = document.getElementById('search-input');
const searchField = searchInput.querySelector('.search-input-field');
const searchCancel = searchInput.querySelector('.search-input-cancel');
const suggestions = searchInput.querySelector('.search-input-suggestions');
// Expand on click
searchInput.addEventListener('click', () => {
searchInput.classList.add('is-expanded');
searchField.focus();
});
// Cancel button
searchCancel.addEventListener('click', (e) => {
e.stopPropagation();
searchField.value = '';
searchInput.classList.remove('is-expanded');
suggestions.classList.remove('is-visible');
});
// Collapse on outside click
document.addEventListener('click', (e) => {
if (!searchInput.contains(e.target)) {
searchInput.classList.remove('is-expanded');
suggestions.classList.remove('is-visible');
}
});
</script>
import { useState, useRef, useEffect } from 'react';
function SearchInput({ onSearch, suggestions = [] }) {
const [isExpanded, setIsExpanded] = useState(false);
const [value, setValue] = useState('');
const [showSuggestions, setShowSuggestions] = useState(false);
const containerRef = useRef(null);
useEffect(() => {
const handleClickOutside = (e) => {
if (containerRef.current && !containerRef.current.contains(e.target)) {
setIsExpanded(false);
setShowSuggestions(false);
}
};
document.addEventListener('click', handleClickOutside);
return () => document.removeEventListener('click', handleClickOutside);
}, []);
return (
<div
ref={containerRef}
className={`search-input ${isExpanded ? 'is-expanded' : ''}`}
onClick={() => setIsExpanded(true)}
>
<svg className="search-input-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<circle cx="11" cy="11" r="8" />
<path d="m21 21-4.35-4.35" />
</svg>
<input
type="text"
className="search-input-field"
placeholder="Search documentation..."
value={value}
onChange={(e) => {
setValue(e.target.value);
setShowSuggestions(e.target.value.length > 0);
}}
/>
<button
className="search-input-cancel"
onClick={(e) => {
e.stopPropagation();
setValue('');
setIsExpanded(false);
setShowSuggestions(false);
}}
>×</button>
<div className={`search-input-suggestions ${showSuggestions ? 'is-visible' : ''}`}>
{/* Suggestion items */}
</div>
</div>
);
}
Versatile card components for displaying grouped content with headers, body, footer, and action areas. Includes elevated, outlined, and interactive hover states.
This is a basic card with header and body.
<!-- Basic Card -->
<div class="card">
<div class="card-header">
<h4 class="card-header-title">Card Title</h4>
</div>
<div class="card-body">
<p>Card content</p>
</div>
</div>
<!-- Stat Card -->
<div class="stat-card">
<div class="stat-card-label">Total Revenue</div>
<div class="stat-card-value">$45,231</div>
<div class="stat-card-change stat-card-change-positive">+12.5%</div>
</div>
import { Card } from '@monochrome-edge/ui/react';
<Card title="Card Title">
<p>Card content</p>
</Card>
<Card title="Card Title">
<p>Card content</p>
</Card>
<mce-card title="Card Title">
<p>Card content</p>
</mce-card>
<script type="module">
import '@monochrome-edge/ui/wc';
</script>
// Create card dynamically
const $card = $('<div>').addClass('card');
const $header = $('<div>').addClass('card-header');
const $title = $('<h4>').addClass('card-header-title').text('Card Title');
const $body = $('<div>').addClass('card-body');
const $content = $('<p>').text('Card content');
$header.append($title);
$body.append($content);
$card.append($header, $body);
$('#container').append($card);
Breadcrumb navigation component for showing the current page location within a hierarchical structure.
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a href="#">Home</a>
</li>
<li class="breadcrumb-separator">/</li>
<li class="breadcrumb-item">
<a href="#">Components</a>
</li>
<li class="breadcrumb-separator">/</li>
<li class="breadcrumb-item">
<a href="#">Navigation</a>
</li>
<li class="breadcrumb-separator">/</li>
<li class="breadcrumb-item is-active">
<span>Breadcrumb</span>
</li>
</ol>
</nav>
<mce-breadcrumb>
<mce-breadcrumb-item href="#">Home</mce-breadcrumb-item>
<mce-breadcrumb-item href="#">Components</mce-breadcrumb-item>
<mce-breadcrumb-item href="#">Navigation</mce-breadcrumb-item>
<mce-breadcrumb-item active>Breadcrumb</mce-breadcrumb-item>
</mce-breadcrumb>
import { Breadcrumb } from '@monochrome-edge/react';
function MyComponent() {
const items = [
{ label: 'Home', href: '#' },
{ label: 'Components', href: '#' },
{ label: 'Navigation', href: '#' },
{ label: 'Breadcrumb', active: true }
];
return <Breadcrumb items={items} />;
}
<template>
<Breadcrumb :items="items" />
</template>
<script setup>
import { ref } from 'vue';
import { Breadcrumb } from '@monochrome-edge/vue';
const items = ref([
{ label: 'Home', href: '#' },
{ label: 'Components', href: '#' },
{ label: 'Navigation', href: '#' },
{ label: 'Breadcrumb', active: true }
]);
</script>
$('#breadcrumb').mceBreadcrumb({
items: [
{ label: 'Home', href: '#' },
{ label: 'Components', href: '#' },
{ label: 'Navigation', href: '#' },
{ label: 'Breadcrumb', active: true }
]
});
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a href="#">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path>
</svg>
Home
</a>
</li>
<li class="breadcrumb-separator">→</li>
<li class="breadcrumb-item">
<a href="#">Documentation</a>
</li>
<li class="breadcrumb-separator">→</li>
<li class="breadcrumb-item is-active">
<span>Getting Started</span>
</li>
</ol>
</nav>
Compact
Large
Contained
<!-- Compact -->
<nav aria-label="breadcrumb">
<ol class="breadcrumb breadcrumb-compact">
<li class="breadcrumb-item"><a href="#">Home</a></li>
<li class="breadcrumb-separator">/</li>
<li class="breadcrumb-item"><a href="#">Docs</a></li>
<li class="breadcrumb-separator">/</li>
<li class="breadcrumb-item is-active"><span>API</span></li>
</ol>
</nav>
<!-- Large -->
<nav aria-label="breadcrumb">
<ol class="breadcrumb breadcrumb-large">
<li class="breadcrumb-item"><a href="#">Home</a></li>
<li class="breadcrumb-separator">/</li>
<li class="breadcrumb-item"><a href="#">Components</a></li>
<li class="breadcrumb-separator">/</li>
<li class="breadcrumb-item is-active"><span>Breadcrumb</span></li>
</ol>
</nav>
<!-- Contained -->
<nav aria-label="breadcrumb">
<ol class="breadcrumb breadcrumb-contained">
<li class="breadcrumb-item"><a href="#">Home</a></li>
<li class="breadcrumb-separator">/</li>
<li class="breadcrumb-item"><a href="#">Library</a></li>
<li class="breadcrumb-separator">/</li>
<li class="breadcrumb-item is-active"><span>Settings</span></li>
</ol>
</nav>
<nav aria-label="breadcrumb">
<ol class="breadcrumb breadcrumb-collapsed">
<li class="breadcrumb-item"><a href="#">Home</a></li>
<li class="breadcrumb-separator">/</li>
<li class="breadcrumb-item"><a href="#">Products</a></li>
<li class="breadcrumb-separator">/</li>
<li class="breadcrumb-item"><a href="#">Electronics</a></li>
<li class="breadcrumb-separator">/</li>
<li class="breadcrumb-ellipsis">...</li>
<li class="breadcrumb-separator">/</li>
<li class="breadcrumb-item is-active"><span>Product Details</span></li>
</ol>
</nav>
Modal dialogs for user interactions and confirmations.
<div class="modal is-open">
<div class="modal-backdrop" onclick="closeModal()"></div>
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title">Modal Title</h3>
<button class="modal-close" onclick="closeModal()">×</button>
</div>
<div class="modal-body">
<p>Modal content goes here</p>
</div>
<div class="modal-footer">
<button class="btn btn-ghost" onclick="closeModal()">Cancel</button>
<button class="btn btn-primary">Confirm</button>
</div>
</div>
</div>
import { Modal } from '@monochrome-edge/ui/react';
{/* Simple modal with built-in footer actions */}
<Modal
isOpen={isOpen}
onClose={() => setIsOpen(false)}
title="Modal Title"
confirmText="Confirm"
cancelText="Cancel"
onConfirm={() => {
// Handle confirm action
setIsOpen(false);
}}
>
<p>Modal content goes here</p>
</Modal>
{/* Custom footer when needed */}
<Modal
isOpen={isOpen}
onClose={() => setIsOpen(false)}
title="Custom Actions"
>
<p>Content with custom footer</p>
<Modal.Footer>
<button className="btn btn-ghost" onClick={() => setIsOpen(false)}>Cancel</button>
<button className="btn btn-primary">Save</button>
<button className="btn btn-success">Publish</button>
</Modal.Footer>
</Modal>
<Modal
:is-open="isOpen"
@close="isOpen = false"
title="Modal Title"
>
<p>Modal content goes here</p>
<template #footer>
<Button variant="ghost" @click="isOpen = false">Cancel</Button>
<Button variant="primary">Confirm</Button>
</template>
</Modal>
<mce-modal is-open title="Modal Title">
<p>Modal content goes here</p>
<div slot="footer">
<button class="btn btn-ghost">Cancel</button>
<button class="btn btn-primary">Confirm</button>
</div>
</mce-modal>
<script>
const modal = document.querySelector('mce-modal');
modal.addEventListener('close', () => {
modal.removeAttribute('is-open');
});
</script>
// Open modal
$('#openModal').on('click', function() {
$('#myModal').addClass('is-open');
});
// Close modal
$('.modal-backdrop, .modal-close').on('click', function() {
$('.modal').removeClass('is-open');
});
Data tables for displaying structured information.
Name | Status | Role | Actions |
---|---|---|---|
John Doe | Active | Admin | |
Jane Smith | Pending | User |
<div class="table-container">
<table class="data-table">
<thead>
<tr>
<th>Name</th>
<th>Status</th>
<th>Role</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr>
<td>John Doe</td>
<td><span class="badge badge-success">Active</span></td>
<td>Admin</td>
<td><button class="btn btn-ghost btn-small">Edit</button></td>
</tr>
</tbody>
</table>
</div>
import { Table, Badge, Button } from '@monochrome-edge/ui/react';
const data = [
{ name: 'John Doe', status: 'active', role: 'Admin' },
{ name: 'Jane Smith', status: 'pending', role: 'User' }
];
<Table>
<Table.Header>
<Table.Row>
<Table.HeaderCell>Name</Table.HeaderCell>
<Table.HeaderCell>Status</Table.HeaderCell>
<Table.HeaderCell>Role</Table.HeaderCell>
<Table.HeaderCell>Actions</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
{data.map((user) => (
<Table.Row key={user.name}>
<Table.Cell>{user.name}</Table.Cell>
<Table.Cell>
<Badge variant={user.status === 'active' ? 'success' : 'warning'}>
{user.status}
</Badge>
</Table.Cell>
<Table.Cell>{user.role}</Table.Cell>
<Table.Cell>
<Button variant="ghost" size="small">Edit</Button>
</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table>
<Table>
<TableHeader>
<TableRow>
<TableHeaderCell>Name</TableHeaderCell>
<TableHeaderCell>Status</TableHeaderCell>
<TableHeaderCell>Role</TableHeaderCell>
<TableHeaderCell>Actions</TableHeaderCell>
</TableRow>
</TableHeader>
<TableBody>
<TableRow v-for="user in users" :key="user.name">
<TableCell>{{ user.name }}</TableCell>
<TableCell>
<Badge :variant="user.status === 'active' ? 'success' : 'warning'">
{{ user.status }}
</Badge>
</TableCell>
<TableCell>{{ user.role }}</TableCell>
<TableCell>
<Button variant="ghost" size="small">Edit</Button>
</TableCell>
</TableRow>
</TableBody>
</Table>
// Add row dynamically
$('#addRow').on('click', function() {
const newRow = `
<tr>
<td>New User</td>
<td><span class="badge badge-pending">Pending</span></td>
<td>User</td>
<td><button class="btn btn-ghost btn-small">Edit</button></td>
</tr>
`;
$('.data-table tbody').append(newRow);
});
// Sort table
$('th').on('click', function() {
// Sorting logic here
});
<mce-table>
<mce-table-header>
<mce-table-row>
<mce-table-header-cell>Name</mce-table-header-cell>
<mce-table-header-cell>Status</mce-table-header-cell>
<mce-table-header-cell>Role</mce-table-header-cell>
<mce-table-header-cell>Actions</mce-table-header-cell>
</mce-table-row>
</mce-table-header>
<mce-table-body>
<mce-table-row>
<mce-table-cell>John Doe</mce-table-cell>
<mce-table-cell>
<mce-badge variant="success">Active</mce-badge>
</mce-table-cell>
<mce-table-cell>Admin</mce-table-cell>
<mce-table-cell>
<mce-button variant="ghost" size="small">Edit</mce-button>
</mce-table-cell>
</mce-table-row>
</mce-table-body>
</mce-table>
<script type="module">
import '@monochrome-edge/web-components';
</script>
Pre-built layout templates for CMS and SaaS applications.
<div class="doc-layout">
<aside class="doc-sidebar">
<div class="doc-sidebar-header">
<h2>Documentation</h2>
</div>
<nav class="doc-nav">
<ul class="nav-list">
<li><a href="#getting-started">Getting Started</a></li>
<li><a href="#components">Components</a></li>
<li><a href="#api">API Reference</a></li>
</ul>
</nav>
</aside>
<main class="doc-main">
<article class="doc-article">
<h1>Page Title</h1>
<p>Content goes here...</p>
</article>
</main>
</div>
<div class="blog-layout">
<main class="blog-main">
<article class="blog-article">
<header class="article-header">
<h1>Article Title</h1>
<div class="article-meta">
<span class="article-date">Oct 13, 2025</span>
<span class="article-author">By John Doe</span>
</div>
</header>
<div class="article-content">
<p>Article content...</p>
</div>
</article>
</main>
<aside class="blog-sidebar">
<div class="widget">
<h3>Recent Posts</h3>
<ul>...</ul>
</div>
<div class="widget">
<h3>Tags</h3>
<div class="tag-cloud">...</div>
</div>
</aside>
</div>
Chronological display of events and milestones with various styles.
New project "Monochrome Edge" has been initialized with base configuration.
Production build completed successfully with no errors.
Application deployment to production environment initiated.
<div class="timeline">
<div class="timeline-item">
<div class="timeline-marker timeline-marker-dot">
<div class="timeline-dot"></div>
</div>
<div class="timeline-content">
<span class="timeline-time">2 hours ago</span>
<h4 class="timeline-title">Project Created</h4>
<p class="timeline-description">New project initialized.</p>
</div>
</div>
<div class="timeline-item timeline-success">
<div class="timeline-marker timeline-marker-dot">
<div class="timeline-dot"></div>
</div>
<div class="timeline-content">
<span class="timeline-time">1 hour ago</span>
<h4 class="timeline-title">Build Successful</h4>
<p class="timeline-description">Build completed.</p>
</div>
</div>
</div>
Interactive step indicators with SVG-based rendering for workflows, wizards, and progress tracking. Supports multiple layouts (horizontal, vertical, snake) and two types (default with circles, text with buttons). Features automatic responsive behavior and smart popup positioning.
<!-- HTML: Declarative API -->
<div class="stepper"
data-type="default"
data-layout="horizontal"
data-steps='[
{
"indicator": "1",
"labelTitle": "Account Setup hellow asdfdsafdsa",
"labelDesc": "Create your profile",
"state": "completed",
"title": "Account Setup adfasdfdsafas",
"desc": "Create your account and set up your profile."
},
{
"indicator": "2",
"labelTitle": "Verification",
"labelDesc": "Verify email",
"state": "failed",
"title": "Email Verification Failed",
"desc": "Email verification failed. Please check your email."
},
{
"indicator": "3",
"labelTitle": "Preferences",
"labelDesc": "Customize settings",
"state": "pending",
"title": "Set Preferences",
"desc": "Customize your application settings."
},
{
"indicator": "4",
"labelTitle": "Confirmation",
"labelDesc": "Review & confirm",
"state": "pending",
"title": "Final Confirmation",
"desc": "Review all your settings and confirm to complete setup."
}
]'>
</div>
<!-- States: pending, active, completed, failed -->
<!-- JavaScript: Programmatic API -->
<script>
const stepper = new MonochromeEdge.Stepper(container, {
type: 'default', // 'default' | 'text'
layout: 'horizontal', // 'horizontal' | 'vertical' | 'snake'
onStepClick: (index, step) => {
console.log('Step clicked:', index, step);
}
});
// Update step state dynamically
stepper.updateStep(1, { state: 'completed' });
</script>
import { useState } from 'react';
function StepperDemo() {
const [currentStep, setCurrentStep] = useState(0);
const [steps, setSteps] = useState([
{ indicator: '1', labelTitle: 'Account Setup', state: 'completed' },
{ indicator: '2', labelTitle: 'Verification', state: 'active' },
{ indicator: '3', labelTitle: 'Preferences', state: 'pending' },
{ indicator: '4', labelTitle: 'Confirmation', state: 'pending' }
]);
const nextStep = () => {
setSteps(prev => prev.map((step, idx) => ({
...step,
state: idx < currentStep + 1 ? 'completed' :
idx === currentStep + 1 ? 'active' : 'pending'
})));
setCurrentStep(prev => Math.min(prev + 1, steps.length - 1));
};
return (
<div>
<div
className="stepper"
data-type="default"
data-layout="horizontal"
data-steps={JSON.stringify(steps)}
/>
<button onClick={nextStep}>Next Step</button>
</div>
);
}
<template>
<div>
<div
class="stepper"
data-type="default"
data-layout="horizontal"
:data-steps="JSON.stringify(steps)"
/>
<button @click="nextStep">Next Step</button>
</div>
</template>
<script setup>
import { ref } from 'vue';
const currentStep = ref(0);
const steps = ref([
{ indicator: '1', labelTitle: 'Account Setup', state: 'completed' },
{ indicator: '2', labelTitle: 'Verification', state: 'active' },
{ indicator: '3', labelTitle: 'Preferences', state: 'pending' },
{ indicator: '4', labelTitle: 'Confirmation', state: 'pending' }
]);
const nextStep = () => {
steps.value = steps.value.map((step, idx) => ({
...step,
state: idx < currentStep.value + 1 ? 'completed' :
idx === currentStep.value + 1 ? 'active' : 'pending'
}));
currentStep.value = Math.min(currentStep.value + 1, steps.value.length - 1);
};
</script>
// Initialize stepper
const $container = $('.stepper');
const stepper = new MonochromeEdge.Stepper($container[0], {
type: 'default',
layout: 'horizontal',
onStepClick: (index, step) => {
console.log('Step clicked:', index, step);
}
});
// Update step state
$('#nextBtn').on('click', function() {
const currentActive = stepper.steps.findIndex(s => s.state === 'active');
if (currentActive >= 0) {
stepper.updateStep(currentActive, { state: 'completed' });
if (currentActive + 1 < stepper.steps.length) {
stepper.updateStep(currentActive + 1, { state: 'active' });
}
}
});
// Add new step
stepper.addStep({
indicator: '5',
labelTitle: 'New Step',
state: 'pending'
});
// Remove step
stepper.removeStep(stepper.steps.length - 1);
<div class="stepper"
data-type="default"
data-layout="vertical"
data-steps='[
{"indicator":"1","labelTitle":"Account","labelDesc":"Setup","state":"completed","title":"Account Setup","desc":"Create your account and set up your profile information."},
{"indicator":"2","labelTitle":"Verification","labelDesc":"Email","state":"completed","title":"Email Verification","desc":"Verify your email address to continue."},
{"indicator":"3","labelTitle":"Preferences","labelDesc":"Settings","state":"active","title":"Set Preferences","desc":"Customize your application settings and preferences."},
{"indicator":"4","labelTitle":"Confirmation","labelDesc":"Review","state":"pending","title":"Final Confirmation","desc":"Review all your settings and confirm to complete setup."}
]'>
</div>
import { useState, useEffect, useRef } from 'react';
function VerticalStepperDemo() {
const containerRef = useRef(null);
const [steps] = useState([
{ indicator: '1', labelTitle: 'Account', labelDesc: 'Setup', state: 'completed' },
{ indicator: '2', labelTitle: 'Verification', labelDesc: 'Email', state: 'completed' },
{ indicator: '3', labelTitle: 'Preferences', labelDesc: 'Settings', state: 'active' },
{ indicator: '4', labelTitle: 'Confirmation', labelDesc: 'Review', state: 'pending' }
]);
useEffect(() => {
if (containerRef.current) {
new MonochromeEdge.Stepper(containerRef.current, {
type: 'default',
layout: 'vertical'
});
}
}, []);
return (
<div
ref={containerRef}
className="stepper"
data-type="default"
data-layout="vertical"
data-steps={JSON.stringify(steps)}
/>
);
}
<template>
<div
ref="stepperRef"
class="stepper"
data-type="default"
data-layout="vertical"
:data-steps="JSON.stringify(steps)"
/>
</template>
<script setup>
import { ref, onMounted } from 'vue';
const stepperRef = ref(null);
const steps = ref([
{ indicator: '1', labelTitle: 'Account', labelDesc: 'Setup', state: 'completed' },
{ indicator: '2', labelTitle: 'Verification', labelDesc: 'Email', state: 'completed' },
{ indicator: '3', labelTitle: 'Preferences', labelDesc: 'Settings', state: 'active' },
{ indicator: '4', labelTitle: 'Confirmation', labelDesc: 'Review', state: 'pending' }
]);
onMounted(() => {
if (stepperRef.value) {
new MonochromeEdge.Stepper(stepperRef.value, {
type: 'default',
layout: 'vertical'
});
}
});
</script>
// Initialize vertical stepper
const stepper = new MonochromeEdge.Stepper($('.stepper')[0], {
type: 'default',
layout: 'vertical'
});
// Navigate between steps
$('#nextBtn').on('click', function() {
const currentIdx = stepper.steps.findIndex(s => s.state === 'active');
if (currentIdx >= 0 && currentIdx < stepper.steps.length - 1) {
stepper.updateStep(currentIdx, { state: 'completed' });
stepper.updateStep(currentIdx + 1, { state: 'active' });
}
});
<div class="stepper"
data-type="default"
data-layout="snake"
data-steps='[
{"indicator":"1","labelTitle":"Initialize","state":"completed","title":"Initialize Project","desc":"Set up the project structure and dependencies"},
{"indicator":"2","labelTitle":"Configure","labelDesc":"Setup","state":"completed","title":"Configuration","desc":"Configure build tools and environment"},
{"indicator":"3","labelTitle":"Build","state":"completed"},
{"indicator":"4","labelTitle":"Test","state":"completed"},
{"indicator":"5","labelTitle":"Deploy","state":"active","title":"Deployment","desc":"Deploy to production environment"},
{"indicator":"6","labelTitle":"Monitor","state":"pending"},
{"indicator":"7","labelTitle":"Optimize","state":"pending"},
{"indicator":"8","labelTitle":"Finalize","state":"pending"}
]'>
</div>
import { useEffect, useRef } from 'react';
function SnakeStepperDemo() {
const containerRef = useRef(null);
useEffect(() => {
if (containerRef.current) {
const stepper = new MonochromeEdge.Stepper(containerRef.current, {
type: 'default',
layout: 'snake',
onStepClick: (index) => console.log('Step:', index)
});
}
}, []);
const steps = [
{ indicator: '1', labelTitle: 'Initialize', state: 'completed' },
{ indicator: '2', labelTitle: 'Configure', state: 'completed' },
{ indicator: '3', labelTitle: 'Build', state: 'completed' },
{ indicator: '4', labelTitle: 'Test', state: 'completed' },
{ indicator: '5', labelTitle: 'Deploy', state: 'active' },
{ indicator: '6', labelTitle: 'Monitor', state: 'pending' }
];
return (
<div
ref={containerRef}
className="stepper"
data-type="default"
data-layout="snake"
data-steps={JSON.stringify(steps)}
/>
);
}
<template>
<div
ref="stepperRef"
class="stepper"
data-type="default"
data-layout="snake"
:data-steps="JSON.stringify(steps)"
/>
</template>
<script setup>
import { ref, onMounted } from 'vue';
const stepperRef = ref(null);
const steps = ref([
{ indicator: '1', labelTitle: 'Initialize', state: 'completed' },
{ indicator: '2', labelTitle: 'Configure', state: 'completed' },
{ indicator: '3', labelTitle: 'Build', state: 'completed' },
{ indicator: '4', labelTitle: 'Test', state: 'completed' },
{ indicator: '5', labelTitle: 'Deploy', state: 'active' },
{ indicator: '6', labelTitle: 'Monitor', state: 'pending' }
]);
onMounted(() => {
if (stepperRef.value) {
new MonochromeEdge.Stepper(stepperRef.value, {
type: 'default',
layout: 'snake'
});
}
});
</script>
// Initialize snake stepper
const stepper = new MonochromeEdge.Stepper($('.stepper')[0], {
type: 'default',
layout: 'snake'
});
// Advance to next step
function advanceStep() {
const activeIdx = stepper.steps.findIndex(s => s.state === 'active');
if (activeIdx >= 0 && activeIdx < stepper.steps.length - 1) {
stepper.updateStep(activeIdx, { state: 'completed' });
stepper.updateStep(activeIdx + 1, { state: 'active' });
}
}
<div class="stepper"
data-type="text"
data-layout="horizontal"
data-steps='[
{"labelTitle":"Personal Information","state":"completed"},
{"labelTitle":"Shipping Address Details","state":"active"},
{"labelTitle":"Payment Method","state":"pending"},
{"labelTitle":"Order Confirmation","state":"pending"}
]'>
</div>
import { useEffect, useRef } from 'react';
function TextStepperDemo() {
const containerRef = useRef(null);
useEffect(() => {
if (containerRef.current) {
new MonochromeEdge.Stepper(containerRef.current, {
type: 'text',
layout: 'horizontal'
});
}
}, []);
const steps = [
{ labelTitle: 'Personal Info', state: 'completed' },
{ labelTitle: 'Address', state: 'active' },
{ labelTitle: 'Payment', state: 'pending' },
{ labelTitle: 'Confirm', state: 'pending' }
];
return (
<div
ref={containerRef}
className="stepper"
data-type="text"
data-layout="horizontal"
data-steps={JSON.stringify(steps)}
/>
);
}
<template>
<div
ref="stepperRef"
class="stepper"
data-type="text"
data-layout="horizontal"
:data-steps="JSON.stringify(steps)"
/>
</template>
<script setup>
import { ref, onMounted } from 'vue';
const stepperRef = ref(null);
const steps = ref([
{ labelTitle: 'Personal Info', state: 'completed' },
{ labelTitle: 'Address', state: 'active' },
{ labelTitle: 'Payment', state: 'pending' },
{ labelTitle: 'Confirm', state: 'pending' }
]);
onMounted(() => {
if (stepperRef.value) {
new MonochromeEdge.Stepper(stepperRef.value, {
type: 'text',
layout: 'horizontal'
});
}
});
</script>
// Initialize text stepper
const stepper = new MonochromeEdge.Stepper($('.stepper')[0], {
type: 'text',
layout: 'horizontal'
});
<div class="stepper"
data-type="text"
data-layout="vertical"
data-steps='[
{"labelTitle":"Setup","state":"completed","title":"Initial Setup","desc":"Configure basic settings"},
{"labelTitle":"Connect","state":"active","title":"Connect Account","desc":"Link your account"},
{"labelTitle":"Verify","state":"pending","title":"Verification","desc":"Verify your identity"},
{"labelTitle":"Complete","state":"pending","title":"Completion","desc":"Finish setup"}
]'>
</div>
import { useEffect, useRef } from 'react';
function TextVerticalStepperDemo() {
const containerRef = useRef(null);
useEffect(() => {
if (containerRef.current) {
new MonochromeEdge.Stepper(containerRef.current, {
type: 'text',
layout: 'vertical'
});
}
}, []);
const steps = [
{ labelTitle: 'Setup', state: 'completed', title: 'Initial Setup', desc: 'Configure basic settings' },
{ labelTitle: 'Connect', state: 'active', title: 'Connect Account', desc: 'Link your account' },
{ labelTitle: 'Verify', state: 'pending', title: 'Verification', desc: 'Verify your identity' },
{ labelTitle: 'Complete', state: 'pending', title: 'Completion', desc: 'Finish setup' }
];
return (
<div
ref={containerRef}
className="stepper"
data-type="text"
data-layout="vertical"
data-steps={JSON.stringify(steps)}
/>
);
}
<template>
<div
ref="stepperRef"
class="stepper"
data-type="text"
data-layout="vertical"
:data-steps="JSON.stringify(steps)"
/>
</template>
<script setup>
import { ref, onMounted } from 'vue';
const stepperRef = ref(null);
const steps = ref([
{ labelTitle: 'Setup', state: 'completed', title: 'Initial Setup', desc: 'Configure basic settings' },
{ labelTitle: 'Connect', state: 'active', title: 'Connect Account', desc: 'Link your account' },
{ labelTitle: 'Verify', state: 'pending', title: 'Verification', desc: 'Verify your identity' },
{ labelTitle: 'Complete', state: 'pending', title: 'Completion', desc: 'Finish setup' }
]);
onMounted(() => {
if (stepperRef.value) {
new MonochromeEdge.Stepper(stepperRef.value, {
type: 'text',
layout: 'vertical'
});
}
});
</script>
// Initialize text vertical stepper
const stepper = new MonochromeEdge.Stepper($('.stepper')[0], {
type: 'text',
layout: 'vertical'
});
<div class="stepper"
data-type="text"
data-layout="snake"
data-steps='[
{"labelTitle":"Plan","state":"completed"},
{"labelTitle":"Design","state":"completed"},
{"labelTitle":"Develop","state":"active"},
{"labelTitle":"Test","state":"pending"},
{"labelTitle":"Deploy","state":"pending"},
{"labelTitle":"Monitor","state":"pending"}
]'>
</div>
import { useEffect, useRef } from 'react';
function TextSnakeStepperDemo() {
const containerRef = useRef(null);
useEffect(() => {
if (containerRef.current) {
new MonochromeEdge.Stepper(containerRef.current, {
type: 'text',
layout: 'snake'
});
}
}, []);
const steps = [
{ labelTitle: 'Plan', state: 'completed' },
{ labelTitle: 'Design', state: 'completed' },
{ labelTitle: 'Develop', state: 'active' },
{ labelTitle: 'Test', state: 'pending' },
{ labelTitle: 'Deploy', state: 'pending' },
{ labelTitle: 'Monitor', state: 'pending' }
];
return (
<div
ref={containerRef}
className="stepper"
data-type="text"
data-layout="snake"
data-steps={JSON.stringify(steps)}
/>
);
}
<template>
<div
ref="stepperRef"
class="stepper"
data-type="text"
data-layout="snake"
:data-steps="JSON.stringify(steps)"
/>
</template>
<script setup>
import { ref, onMounted } from 'vue';
const stepperRef = ref(null);
const steps = ref([
{ labelTitle: 'Plan', state: 'completed' },
{ labelTitle: 'Design', state: 'completed' },
{ labelTitle: 'Develop', state: 'active' },
{ labelTitle: 'Test', state: 'pending' },
{ labelTitle: 'Deploy', state: 'pending' },
{ labelTitle: 'Monitor', state: 'pending' }
]);
onMounted(() => {
if (stepperRef.value) {
new MonochromeEdge.Stepper(stepperRef.value, {
type: 'text',
layout: 'snake'
});
}
});
</script>
// Initialize text snake stepper
const stepper = new MonochromeEdge.Stepper($('.stepper')[0], {
type: 'text',
layout: 'snake'
});
Standardized tags and badges used throughout the UI library for categorization, status indication, and metadata display.
<!-- Individual Tags -->
<span class="component-tag component-tag-css">CSS</span>
<span class="component-tag component-tag-js">JS</span>
<span class="component-tag component-tag-svg">SVG</span>
<span class="component-tag component-tag-canvas">CANVAS</span>
<!-- Combined Tags (for section headers) -->
<span class="component-tags">
<span class="component-tag component-tag-css">CSS</span>
<span class="component-tag component-tag-js">JS</span>
</span>
/* Base Tag Styles */
.component-tag {
display: inline-block;
padding: 0.125rem 0.5rem;
font-size: 0.625rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
border-radius: calc(var(--border-radius) / 2);
border: 1px solid var(--theme-border);
}
/* Type Variants */
.component-tag-css {
background-color: var(--theme-bg);
color: var(--theme-text-secondary);
}
.component-tag-js {
background-color: var(--theme-accent);
color: var(--theme-accent-contrast);
}
.component-tag-svg {
background-color: var(--theme-surface);
color: var(--theme-text-primary);
border-color: var(--theme-accent);
}
.component-tag-canvas {
background-color: var(--theme-accent);
color: var(--theme-accent-contrast);
opacity: 0.9;
}
/* Container for multiple tags */
.component-tags {
display: inline-flex;
gap: 0.375rem;
align-items: center;
}
<!-- Semantic Badges -->
<span class="badge badge-success">Success</span>
<span class="badge badge-warning">Warning</span>
<span class="badge badge-error">Error</span>
<span class="badge badge-info">Info</span>
<!-- Sizes -->
<span class="badge badge-xs">Extra Small</span>
<span class="badge badge-sm">Small</span>
<span class="badge">Medium</span>
<span class="badge badge-lg">Large</span>
<!-- Outline Variants -->
<span class="badge badge-outline badge-success">Success</span>
<span class="badge badge-outline badge-error">Error</span>
/* Base Badge */
.badge {
display: inline-block;
padding: 0.25rem 0.625rem;
font-size: 0.75rem;
font-weight: 600;
border-radius: calc(var(--border-radius) * 0.75);
background-color: var(--theme-surface);
color: var(--theme-text-primary);
border: 1px solid var(--theme-border);
}
/* Semantic Colors */
.badge-success {
background-color: var(--color-success);
color: white;
border-color: var(--color-success);
}
.badge-warning {
background-color: var(--color-warning);
color: var(--theme-text-primary);
border-color: var(--color-warning);
}
.badge-error {
background-color: var(--color-error);
color: white;
border-color: var(--color-error);
}
.badge-info {
background-color: var(--color-info);
color: white;
border-color: var(--color-info);
}
/* Sizes */
.badge-xs {
padding: 0.125rem 0.375rem;
font-size: 0.625rem;
}
.badge-sm {
padding: 0.1875rem 0.5rem;
font-size: 0.6875rem;
}
.badge-lg {
padding: 0.375rem 0.875rem;
font-size: 0.875rem;
}
/* Outline Variant */
.badge-outline {
background-color: transparent;
}
.badge-outline.badge-success {
color: var(--color-success);
}
.badge-outline.badge-warning {
color: var(--color-warning);
}
.badge-outline.badge-error {
color: var(--color-error);
}
.badge-outline.badge-info {
color: var(--color-info);
}
Use component-tag for showing tech stack/type in docs
Use badge for showing status, notifications, counts
Use badge-outline for categories, tags, filters
Badge: Interactive or semantic status | Text/Small: Descriptive or static text
<!-- 1. Component Tags (for documentation) -->
<!-- Use component-tag for showing tech stack/type in docs -->
<h2>
Component Name
<span class="component-tags">
<span class="component-tag component-tag-css">CSS</span>
<span class="component-tag component-tag-js">JS</span>
</span>
</h2>
<!-- 2. Status Badge (for UI state) -->
<!-- Use badge for showing status, notifications, counts -->
<div class="card">
<div style="display: flex; justify-content: space-between;">
<h4>Task Item</h4>
<span class="badge badge-success">Completed</span>
</div>
</div>
<!-- 3. Metadata Tags (for categories/labels) -->
<!-- Use badge-outline for categories, tags, filters -->
<div style="display: flex; gap: 0.5rem; align-items: center;">
<span class="badge badge-xs badge-outline badge-info">React</span>
<span class="badge badge-xs badge-outline badge-info">TypeScript</span>
<small class="text-muted">• 5 min read</small>
</div>
<!-- 4. Badge vs Text -->
<!-- Badge: Interactive or semantic status -->
<span class="badge badge-warning">Pending</span>
<!-- Text/Small: Descriptive or static text -->
<small class="text-muted">Secondary information</small>
Expandable content panels for organizing information hierarchically.
<div class="accordion">
<div class="accordion-item">
<div class="accordion-header">
<button class="accordion-button">
<span>Question Title</span>
<span class="accordion-icon">▼</span>
</button>
</div>
<div class="accordion-collapse">
<div class="accordion-body">
Answer content goes here.
</div>
</div>
</div>
</div>
<script>
// Toggle accordion
$('.accordion-button').on('click', function() {
$(this).toggleClass('active');
$(this).closest('.accordion-item')
.find('.accordion-collapse')
.toggleClass('show');
});
</script>
import { Accordion } from '@monochrome-edge/react';
function FAQ() {
const items = [
{ id: '1', title: 'What is Monochrome Edge?', content: 'A minimalist UI library...' },
{ id: '2', title: 'How to get started?', content: 'Install via npm...' },
{ id: '3', title: 'Is it responsive?', content: 'Yes, fully responsive.' }
];
return (
<Accordion
items={items}
defaultExpandedIds={['1']}
allowMultiple={false}
onChange={(expandedIds) => console.log(expandedIds)}
/>
);
}
<template>
<Accordion
:items="items"
:default-expanded-ids="['1']"
:allow-multiple="false"
@change="handleChange"
/>
</template>
<script setup>
import { Accordion } from '@monochrome-edge/vue';
const items = [
{ id: '1', title: 'What is Monochrome Edge?', content: 'A minimalist UI library...' },
{ id: '2', title: 'How to get started?', content: 'Install via npm...' },
{ id: '3', title: 'Is it responsive?', content: 'Yes, fully responsive.' }
];
const handleChange = (expandedIds) => {
console.log('Expanded:', expandedIds);
};
</script>
<mce-accordion id="faq" allow-multiple="false">
<div data-accordion-id="1" data-accordion-title="What is Monochrome Edge?">
A minimalist UI library...
</div>
<div data-accordion-id="2" data-accordion-title="How to get started?">
Install via npm...
</div>
</mce-accordion>
<script type="module">
import '@monochrome-edge/web-components';
document.getElementById('faq').addEventListener('change', (e) => {
console.log('Expanded:', e.detail.expandedIds);
});
</script>
$('#accordion-container').accordion({
allowMultiple: false,
defaultExpandedIds: ['1'],
onChange: (expandedIds) => {
console.log('Expanded:', expandedIds);
}
});
// HTML structure
<div id="accordion-container">
<div data-accordion-id="1" data-accordion-title="What is Monochrome Edge?">
A minimalist UI library...
</div>
<div data-accordion-id="2" data-accordion-title="How to get started?">
Install via npm...
</div>
</div>
Alert and notification components with semantic color variants.
This is an informational callout with important details about the feature.
Your changes have been saved successfully!
Please review your settings before proceeding.
An error occurred while processing your request.
<!-- Info Callout -->
<div class="callout callout-info">
<div class="callout-icon">
<!-- Icon SVG -->
</div>
<div class="callout-content">
<div class="callout-title">Info</div>
<p>Informational message here.</p>
</div>
</div>
<!-- Success Callout -->
<div class="callout callout-success">
<div class="callout-icon">
<!-- Icon SVG -->
</div>
<div class="callout-content">
<div class="callout-title">Success</div>
<p>Success message here.</p>
</div>
</div>
<!-- Warning Callout -->
<div class="callout callout-warning">
<div class="callout-icon">
<!-- Icon SVG -->
</div>
<div class="callout-content">
<div class="callout-title">Warning</div>
<p>Warning message here.</p>
</div>
</div>
<!-- Error Callout -->
<div class="callout callout-error">
<div class="callout-icon">
<!-- Icon SVG -->
</div>
<div class="callout-content">
<div class="callout-title">Error</div>
<p>Error message here.</p>
</div>
</div>
Real-world examples of Monochrome components in different frameworks.
import React from 'react';
import { Button } from '@monochrome-edge'; // TSX components from main package
function App() {
return (
<div>
<Button variant="primary" size="large">
Primary Button
</Button>
<Button variant="secondary" loading>
Loading...
</Button>
<Button variant="ghost" onClick={() => alert('Clicked!')}>
Ghost Button
</Button>
</div>
);
}
<template>
<div>
<mce-button variant="primary" size="large">
Primary Button
</mce-button>
<mce-button variant="secondary" :loading="true">
Loading...
</mce-button>
<mce-button variant="ghost" @click="handleClick">
Ghost Button
</mce-button>
</div>
</template>
<script setup lang="ts">
import { MonochromeButton } from '@monochrome-edge/vue';
const handleClick = () => {
alert('Clicked!');
};
</script>
import { MonochromeComponents } from '@monochrome-edge/vanilla';
// Create buttons
const primaryBtn = MonochromeComponents.button('Primary Button', {
variant: 'primary',
size: 'large'
});
const loadingBtn = MonochromeComponents.button('Loading...', {
variant: 'secondary',
loading: true
});
const ghostBtn = MonochromeComponents.button('Ghost Button', {
variant: 'ghost',
onClick: (e) => alert('Clicked!')
});
// Append to container
const container = document.getElementById('button-container');
container.appendChild(primaryBtn);
container.appendChild(loadingBtn);
container.appendChild(ghostBtn);
// Initialize buttons with jQuery
$('#primary-btn').monochromeButton({
variant: 'primary',
size: 'large'
});
$('#loading-btn').monochromeButton({
variant: 'secondary',
loading: true
});
$('#ghost-btn').monochromeButton({
variant: 'ghost'
}).on('click', function() {
alert('Clicked!');
});
// Or create dynamically
$('<button>Dynamic Button</button>')
.monochromeButton({ variant: 'primary' })
.appendTo('#container');
<!-- Import Web Components first -->
<script type="module">
import '@monochrome-edge/wc';
</script>
<!-- Use as custom elements -->
<mce-button variant="primary" size="large">
Primary Button
</mce-button>
<mce-button variant="secondary" loading="true">
Loading...
</mce-button>
<mce-button variant="ghost" id="ghost-btn">
Ghost Button
</mce-button>
<script>
// Add event listener
document.getElementById('ghost-btn')
.addEventListener('click', () => alert('Clicked!'));
// Or create dynamically
const btn = document.createElement('monochrome-button');
btn.setAttribute('variant', 'primary');
btn.textContent = 'Dynamic Button';
document.body.appendChild(btn);
</script>
import React, { useState } from 'react';
import { FormGroup, Label, Input, Button } from '@monochrome-edge'; // TSX components
function LoginForm() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
return (
<form onSubmit={(e) => e.preventDefault()}>
<FormGroup>
<Label htmlFor="email" required>Email</Label>
<Input
id="email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="you@example.com"
/>
</FormGroup>
<FormGroup>
<Label htmlFor="password" required>Password</Label>
<Input
id="password"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
</FormGroup>
<Button type="submit" variant="primary">
Sign In
</Button>
</form>
);
}
<template>
<form @submit.prevent="handleSubmit">
<form-group>
<form-label for="email" required>Email</form-label>
<form-input
id="email"
type="email"
v-model="email"
placeholder="you@example.com"
/>
</form-group>
<form-group>
<form-label for="password" required>Password</form-label>
<form-input
id="password"
type="password"
v-model="password"
/>
</form-group>
<mce-button type="submit" variant="primary">
Sign In
</mce-button>
</form>
</template>
<script setup>
import { ref } from 'vue';
const email = ref('');
const password = ref('');
const handleSubmit = () => {
console.log('Login:', email.value, password.value);
};
</script>
import { MonochromeComponents } from '@monochrome-edge/vanilla';
// Create form elements
const emailInput = MonochromeComponents.input({
type: 'email',
placeholder: 'you@example.com',
onChange: (value) => console.log('Email:', value)
});
const passwordInput = MonochromeComponents.input({
type: 'password',
onChange: (value) => console.log('Password:', value)
});
const emailGroup = MonochromeComponents.formGroup(
'Email',
emailInput,
{ required: true }
);
const passwordGroup = MonochromeComponents.formGroup(
'Password',
passwordInput,
{ required: true }
);
const submitBtn = MonochromeComponents.button('Sign In', {
variant: 'primary',
onClick: () => {
console.log('Form submitted');
}
});
// Create form
const form = document.createElement('form');
form.appendChild(emailGroup);
form.appendChild(passwordGroup);
form.appendChild(submitBtn);
document.getElementById('form-container').appendChild(form);
// Enhance existing form
$('#login-form').monochromeForm();
// Handle form submission
$('#login-form').on('submit', function(e) {
e.preventDefault();
const formData = {
email: $('#email').val(),
password: $('#password').val()
};
console.log('Login:', formData);
});
// Add validation
$('#email').on('blur', function() {
const $input = $(this);
if (!$input.val().includes('@')) {
$input.addClass('is-error');
$input.after('<span class="helper-text error">Invalid email</span>');
} else {
$input.removeClass('is-error');
$input.next('.helper-text').remove();
}
});
<form id="login-form">
<div class="form-group">
<label class="label" for="email">
Email <span class="text-danger">*</span>
</label>
<input
type="email"
id="email"
class="input"
placeholder="you@example.com"
required
>
</div>
<div class="form-group">
<label class="label" for="password">
Password <span class="text-danger">*</span>
</label>
<input
type="password"
id="password"
class="input"
required
>
</div>
<mce-button type="submit" variant="primary">
Sign In
</mce-button>
</form>
<script>
document.getElementById('login-form')
.addEventListener('submit', (e) => {
e.preventDefault();
const formData = new FormData(e.target);
console.log('Login:', Object.fromEntries(formData));
});
</script>
import React from 'react';
import { Card, CardHeader, CardBody, StatCard } from '@monochrome-edge';
function DashboardCards() {
return (
<div className="grid grid-cols-3 gap-4">
{/* Basic Card */}
<Card>
<CardHeader>
<h3>Basic Card</h3>
</CardHeader>
<CardBody>
<p>This is a basic card with header and body.</p>
</CardBody>
</Card>
{/* Stat Card */}
<StatCard
title="Total Revenue"
value="$12,426"
change="+12.5%"
trend="up"
period="from last month"
/>
{/* Custom Styled Card */}
<Card className="border-highlight">
<CardBody>
<div className="flex items-center justify-between">
<span className="text-secondary">Active Users</span>
<span className="text-2xl font-bold">1,234</span>
</div>
</CardBody>
</Card>
</div>
);
}
<template>
<div class="grid grid-cols-3 gap-4">
<!-- Basic Card -->
<mce-card>
<template #header>
<h3>Basic Card</h3>
</template>
<p>This is a basic card with header and body.</p>
</mce-card>
<!-- Stat Card -->
<stat-card
title="Total Revenue"
value="$12,426"
change="+12.5%"
trend="up"
period="from last month"
/>
<!-- Custom Styled Card -->
<mce-card class="border-highlight">
<div class="flex items-center justify-between">
<span class="text-secondary">Active Users</span>
<span class="text-2xl font-bold">1,234</span>
</div>
</mce-card>
</div>
</template>
<script setup>
import { MonoCard, StatCard } from '@monochrome-edge/vue';
</script>
import { MonochromeComponents } from '@monochrome-edge/vanilla';
// Create basic card
const basicCard = MonochromeComponents.card({
title: 'Basic Card',
content: 'This is a basic card with header and body.'
});
// Create stat card
const statCard = MonochromeComponents.statCard({
title: 'Total Revenue',
value: '$12,426',
change: '+12.5%',
trend: 'up',
period: 'from last month'
});
// Create custom card
const customCard = document.createElement('div');
customCard.className = 'card border-highlight';
customCard.innerHTML = `
<div class="card-body">
<div class="flex items-center justify-between">
<span class="text-secondary">Active Users</span>
<span class="text-2xl font-bold">1,234</span>
</div>
</div>
`;
// Append to container
const container = document.getElementById('card-container');
container.appendChild(basicCard);
container.appendChild(statCard);
container.appendChild(customCard);
// Create cards with jQuery
const cards = [
{
type: 'basic',
title: 'Basic Card',
content: 'This is a basic card with header and body.'
},
{
type: 'stat',
title: 'Total Revenue',
value: '$12,426',
change: '+12.5%',
trend: 'up'
}
];
// Initialize cards
cards.forEach(card => {
if (card.type === 'basic') {
const $card = $('<div class="card"></div>');
$card.append(`
<div class="card-header">${card.title}</div>
<div class="card-body">${card.content}</div>
`);
$('#card-container').append($card);
} else if (card.type === 'stat') {
const $statCard = $('<div class="stat-card"></div>');
$statCard.html(`
<h4 class="stat-title">${card.title}</h4>
<div class="stat-value">${card.value}</div>
<div class="stat-change ${card.trend}">${card.change}</div>
`);
$('#card-container').append($statCard);
}
});
<!-- Basic Card -->
<div class="card">
<div class="card-header">
<h3>Basic Card</h3>
</div>
<div class="card-body">
<p>This is a basic card with header and body.</p>
</div>
</div>
<!-- Stat Card -->
<div class="stat-card">
<h4 class="stat-title">Total Revenue</h4>
<div class="stat-value">$12,426</div>
<div class="stat-change positive">
<span class="arrow">↑</span> +12.5%
<span class="period">from last month</span>
</div>
</div>
<!-- Custom Card with Web Components -->
<mce-card title="Active Users">
<div class="flex items-center justify-between">
<span class="text-secondary">Current</span>
<span class="text-2xl font-bold">1,234</span>
</div>
</mce-card>
import React, { useState } from 'react';
import { Modal, Button } from '@monochrome-edge';
function ModalExample() {
const [isOpen, setIsOpen] = useState(false);
const [confirmOpen, setConfirmOpen] = useState(false);
return (
<>
<Button onClick={() => setIsOpen(true)}>
Open Modal
</Button>
<Modal
isOpen={isOpen}
onClose={() => setIsOpen(false)}
title="Sample Modal"
size="medium"
>
<p>This is a modal dialog with custom content.</p>
<div className="modal-footer">
<Button variant="ghost" onClick={() => setIsOpen(false)}>
Cancel
</Button>
<Button variant="primary" onClick={() => setIsOpen(false)}>
Confirm
</Button>
</div>
</Modal>
{/* Confirmation Modal */}
<Modal
isOpen={confirmOpen}
onClose={() => setConfirmOpen(false)}
title="Confirm Action"
size="small"
>
<p>Are you sure you want to proceed?</p>
<div className="modal-footer">
<Button variant="ghost" onClick={() => setConfirmOpen(false)}>
No
</Button>
<Button variant="danger" onClick={() => setConfirmOpen(false)}>
Yes, Delete
</Button>
</div>
</Modal>
</>
);
}
<template>
<div>
<mce-button @click="isOpen = true">
Open Modal
</mce-button>
<mce-modal
:is-open="isOpen"
@close="isOpen = false"
title="Sample Modal"
size="medium"
>
<p>This is a modal dialog with custom content.</p>
<template #footer>
<mce-button variant="ghost" @click="isOpen = false">
Cancel
</mce-button>
<mce-button variant="primary" @click="isOpen = false">
Confirm
</mce-button>
</template>
</mce-modal>
<!-- Confirmation Modal -->
<mce-modal
:is-open="confirmOpen"
@close="confirmOpen = false"
title="Confirm Action"
size="small"
>
<p>Are you sure you want to proceed?</p>
<template #footer>
<mce-button variant="ghost" @click="confirmOpen = false">
No
</mce-button>
<mce-button variant="danger" @click="confirmOpen = false">
Yes, Delete
</mce-button>
</template>
</mce-modal>
</div>
</template>
<script setup>
import { ref } from 'vue';
import { MonoModal, MonochromeButton } from '@monochrome-edge/vue';
const isOpen = ref(false);
const confirmOpen = ref(false);
</script>
import { MonochromeComponents } from '@monochrome-edge/vanilla';
// Create modal instance
const modal = MonochromeComponents.modal({
title: 'Sample Modal',
content: 'This is a modal dialog with custom content.',
size: 'medium',
buttons: [
{
text: 'Cancel',
variant: 'ghost',
onClick: () => modal.close()
},
{
text: 'Confirm',
variant: 'primary',
onClick: () => {
console.log('Confirmed!');
modal.close();
}
}
]
});
// Create trigger button
const openButton = MonochromeComponents.button('Open Modal', {
onClick: () => modal.open()
});
// Confirmation modal helper
function confirmAction(message, onConfirm) {
const confirmModal = MonochromeComponents.modal({
title: 'Confirm Action',
content: message,
size: 'small',
buttons: [
{
text: 'No',
variant: 'ghost',
onClick: () => confirmModal.close()
},
{
text: 'Yes, Delete',
variant: 'danger',
onClick: () => {
onConfirm();
confirmModal.close();
}
}
]
});
confirmModal.open();
}
document.body.appendChild(openButton);
// Initialize modal
$('#modal-trigger').on('click', function() {
$.monochromeModal({
title: 'Sample Modal',
content: 'This is a modal dialog with custom content.',
size: 'medium',
buttons: [
{
text: 'Cancel',
class: 'btn btn-ghost',
click: function(modal) {
modal.close();
}
},
{
text: 'Confirm',
class: 'btn btn-primary',
click: function(modal) {
console.log('Confirmed!');
modal.close();
}
}
]
});
});
// Confirmation modal helper
$.confirm = function(options) {
return $.monochromeModal({
title: options.title || 'Confirm Action',
content: options.message,
size: 'small',
buttons: [
{
text: 'No',
class: 'btn btn-ghost',
click: function(modal) {
if (options.onCancel) options.onCancel();
modal.close();
}
},
{
text: options.confirmText || 'Yes',
class: 'btn btn-danger',
click: function(modal) {
if (options.onConfirm) options.onConfirm();
modal.close();
}
}
]
});
};
// Usage
$('#delete-btn').on('click', function() {
$.confirm({
message: 'Are you sure you want to delete this item?',
confirmText: 'Yes, Delete',
onConfirm: function() {
console.log('Item deleted');
}
});
});
<!-- Modal HTML Structure -->
<button id="modal-trigger" class="btn btn-primary">
Open Modal
</button>
<div class="modal" id="sample-modal">
<div class="modal-backdrop"></div>
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title">Sample Modal</h3>
<button class="modal-close">×</button>
</div>
<div class="modal-body">
<p>This is a modal dialog with custom content.</p>
</div>
<div class="modal-footer">
<button class="btn btn-ghost">Cancel</button>
<button class="btn btn-primary">Confirm</button>
</div>
</div>
</div>
<script>
// Modal controller
class ModalController {
constructor(modalId) {
this.modal = document.getElementById(modalId);
this.backdrop = this.modal.querySelector('.modal-backdrop');
this.closeBtn = this.modal.querySelector('.modal-close');
this.cancelBtn = this.modal.querySelector('.btn-ghost');
this.bindEvents();
}
bindEvents() {
this.backdrop.addEventListener('click', () => this.close());
this.closeBtn.addEventListener('click', () => this.close());
this.cancelBtn.addEventListener('click', () => this.close());
}
open() {
this.modal.classList.add('is-open');
}
close() {
this.modal.classList.remove('is-open');
}
}
// Initialize
const modal = new ModalController('sample-modal');
document.getElementById('modal-trigger')
.addEventListener('click', () => modal.open());
</script>
import React, { useState } from 'react';
import { Table, TableHeader, TableBody, TableRow, TableCell } from '@monochrome-edge';
function DataTable() {
const [data] = useState([
{ id: 1, name: 'John Doe', email: 'john@example.com', status: 'active' },
{ id: 2, name: 'Jane Smith', email: 'jane@example.com', status: 'inactive' },
{ id: 3, name: 'Bob Johnson', email: 'bob@example.com', status: 'active' },
]);
return (
<Table>
<TableHeader>
<TableRow>
<TableCell header>Name</TableCell>
<TableCell header>Email</TableCell>
<TableCell header>Status</TableCell>
<TableCell header>Actions</TableCell>
</TableRow>
</TableHeader>
<TableBody>
{data.map(row => (
<TableRow key={row.id}>
<TableCell>{row.name}</TableCell>
<TableCell>{row.email}</TableCell>
<TableCell>
<span className={`badge badge-${row.status}`}>
{row.status}
</span>
</TableCell>
<TableCell>
<button className="btn btn-sm btn-ghost">Edit</button>
<button className="btn btn-sm btn-danger">Delete</button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
);
}
<template>
<mce-table>
<mce-table-header>
<mce-table-row>
<mce-table-cell header>Name</mce-table-cell>
<mce-table-cell header>Email</mce-table-cell>
<mce-table-cell header>Status</mce-table-cell>
<mce-table-cell header>Actions</mce-table-cell>
</mce-table-row>
</mce-table-header>
<mce-table-body>
<mce-table-row v-for="row in data" :key="row.id">
<mce-table-cell>{{ row.name }}</mce-table-cell>
<mce-table-cell>{{ row.email }}</mce-table-cell>
<mce-table-cell>
<span :class="`badge badge-${row.status}`">
{{ row.status }}
</span>
</mce-table-cell>
<mce-table-cell>
<button class="btn btn-sm btn-ghost" @click="editRow(row)">
Edit
</button>
<button class="btn btn-sm btn-danger" @click="deleteRow(row)">
Delete
</button>
</mce-table-cell>
</mce-table-row>
</mce-table-body>
</mce-table>
</template>
<script setup>
import { ref } from 'vue';
const data = ref([
{ id: 1, name: 'John Doe', email: 'john@example.com', status: 'active' },
{ id: 2, name: 'Jane Smith', email: 'jane@example.com', status: 'inactive' },
{ id: 3, name: 'Bob Johnson', email: 'bob@example.com', status: 'active' },
]);
const editRow = (row) => console.log('Edit:', row);
const deleteRow = (row) => console.log('Delete:', row);
</script>
import { MonochromeComponents } from '@monochrome-edge/vanilla';
const data = [
{ id: 1, name: 'John Doe', email: 'john@example.com', status: 'active' },
{ id: 2, name: 'Jane Smith', email: 'jane@example.com', status: 'inactive' },
{ id: 3, name: 'Bob Johnson', email: 'bob@example.com', status: 'active' },
];
// Create table
const table = MonochromeComponents.table({
columns: [
{ key: 'name', label: 'Name' },
{ key: 'email', label: 'Email' },
{
key: 'status',
label: 'Status',
render: (value) => {
const badge = document.createElement('span');
badge.className = `badge badge-${value}`;
badge.textContent = value;
return badge;
}
},
{
key: 'actions',
label: 'Actions',
render: (_, row) => {
const container = document.createElement('div');
const editBtn = document.createElement('button');
editBtn.className = 'btn btn-sm btn-ghost';
editBtn.textContent = 'Edit';
editBtn.onclick = () => console.log('Edit:', row);
const deleteBtn = document.createElement('button');
deleteBtn.className = 'btn btn-sm btn-danger';
deleteBtn.textContent = 'Delete';
deleteBtn.onclick = () => console.log('Delete:', row);
container.appendChild(editBtn);
container.appendChild(deleteBtn);
return container;
}
}
],
data: data,
sortable: true,
filterable: true
});
document.getElementById('table-container').appendChild(table);
// Initialize data table with jQuery
const data = [
{ id: 1, name: 'John Doe', email: 'john@example.com', status: 'active' },
{ id: 2, name: 'Jane Smith', email: 'jane@example.com', status: 'inactive' },
{ id: 3, name: 'Bob Johnson', email: 'bob@example.com', status: 'active' },
];
// Create table HTML
const tableHtml = `
<table class="table" id="data-table">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody></tbody>
</table>
`;
$('#table-container').html(tableHtml);
// Populate table
data.forEach(row => {
const $row = $('<tr></tr>');
$row.append(`<td>${row.name}</td>`);
$row.append(`<td>${row.email}</td>`);
$row.append(`<td><span class="badge badge-${row.status}">${row.status}</span></td>`);
$row.append(`
<td>
<button class="btn btn-sm btn-ghost edit-btn" data-id="${row.id}">Edit</button>
<button class="btn btn-sm btn-danger delete-btn" data-id="${row.id}">Delete</button>
</td>
`);
$('#data-table tbody').append($row);
});
// Event handlers
$(document).on('click', '.edit-btn', function() {
const id = $(this).data('id');
console.log('Edit row:', id);
});
$(document).on('click', '.delete-btn', function() {
const id = $(this).data('id');
if (confirm('Delete this row?')) {
$(this).closest('tr').fadeOut(() => {
$(this).remove();
});
}
});
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="table-body"></tbody>
</table>
<script>
// Data
const data = [
{ id: 1, name: 'John Doe', email: 'john@example.com', status: 'active' },
{ id: 2, name: 'Jane Smith', email: 'jane@example.com', status: 'inactive' },
{ id: 3, name: 'Bob Johnson', email: 'bob@example.com', status: 'active' },
];
// Render table rows
const tbody = document.getElementById('table-body');
data.forEach(row => {
const tr = document.createElement('tr');
// Name cell
const nameCell = document.createElement('td');
nameCell.textContent = row.name;
tr.appendChild(nameCell);
// Email cell
const emailCell = document.createElement('td');
emailCell.textContent = row.email;
tr.appendChild(emailCell);
// Status cell
const statusCell = document.createElement('td');
const badge = document.createElement('span');
badge.className = `badge badge-${row.status}`;
badge.textContent = row.status;
statusCell.appendChild(badge);
tr.appendChild(statusCell);
// Actions cell
const actionsCell = document.createElement('td');
const editBtn = document.createElement('button');
editBtn.className = 'btn btn-sm btn-ghost';
editBtn.textContent = 'Edit';
editBtn.onclick = () => console.log('Edit:', row);
const deleteBtn = document.createElement('button');
deleteBtn.className = 'btn btn-sm btn-danger';
deleteBtn.textContent = 'Delete';
deleteBtn.onclick = () => {
if (confirm('Delete this row?')) {
tr.remove();
}
};
actionsCell.appendChild(editBtn);
actionsCell.appendChild(deleteBtn);
tr.appendChild(actionsCell);
tbody.appendChild(tr);
});
</script>
import React, { useState } from 'react';
import { Select } from '@monochrome-edge';
function SelectExample() {
const [selectedValue, setSelectedValue] = useState('option2');
const [multiValue, setMultiValue] = useState(['option1', 'option3']);
return (
<div>
{/* Basic Select */}
<div className="form-group">
<label className="label">Basic Select</label>
<select
className="select"
value={selectedValue}
onChange={(e) => setSelectedValue(e.target.value)}
>
<option value="">Choose an option</option>
<option value="option1">Option 1</option>
<option value="option2">Option 2</option>
<option value="option3">Option 3</option>
</select>
</div>
{/* Multiple Select */}
<div className="form-group">
<label className="label">Multiple Select</label>
<select
className="select"
multiple
size={4}
value={multiValue}
onChange={(e) => {
const selected = Array.from(e.target.selectedOptions, option => option.value);
setMultiValue(selected);
}}
>
<option value="option1">Option 1</option>
<option value="option2">Option 2</option>
<option value="option3">Option 3</option>
<option value="option4">Option 4</option>
</select>
</div>
</div>
);
}
<template>
<div>
<!-- Basic Select -->
<div class="form-group">
<label class="label">Basic Select</label>
<select class="select" v-model="selectedValue">
<option value="">Choose an option</option>
<option value="option1">Option 1</option>
<option value="option2">Option 2</option>
<option value="option3">Option 3</option>
</select>
</div>
<!-- Multiple Select -->
<div class="form-group">
<label class="label">Multiple Select</label>
<select class="select" v-model="multiValue" multiple size="4">
<option value="option1">Option 1</option>
<option value="option2">Option 2</option>
<option value="option3">Option 3</option>
<option value="option4">Option 4</option>
</select>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue';
const selectedValue = ref('option2');
const multiValue = ref(['option1', 'option3']);
</script>
import { MonochromeComponents } from '@monochrome-edge/vanilla';
// Create simple dropdown
const dropdown = MonochromeComponents.dropdown({
options: [
{ value: 'option1', label: 'Option 1' },
{ value: 'option2', label: 'Option 2' },
{ value: 'option3', label: 'Option 3' },
],
placeholder: 'Select an option',
onChange: (value) => {
console.log('Selected:', value);
}
});
// Create action dropdown
const actionDropdown = MonochromeComponents.dropdown({
label: 'Actions',
type: 'menu',
items: [
{
label: 'Edit',
onClick: () => console.log('Edit')
},
{
label: 'Duplicate',
onClick: () => console.log('Duplicate')
},
{ type: 'divider' },
{
label: 'Delete',
danger: true,
onClick: () => console.log('Delete')
}
]
});
// Custom dropdown implementation
class CustomDropdown {
constructor(element) {
this.element = element;
this.button = element.querySelector('.dropdown-button');
this.panel = element.querySelector('.dropdown-panel');
this.isOpen = false;
this.bindEvents();
}
bindEvents() {
this.button.addEventListener('click', () => this.toggle());
document.addEventListener('click', (e) => {
if (!this.element.contains(e.target)) {
this.close();
}
});
}
toggle() {
this.isOpen = !this.isOpen;
this.element.classList.toggle('is-open', this.isOpen);
this.panel.classList.toggle('is-open', this.isOpen);
}
close() {
this.isOpen = false;
this.element.classList.remove('is-open');
this.panel.classList.remove('is-open');
}
}
// Initialize
document.getElementById('dropdown-container').appendChild(dropdown);
document.getElementById('dropdown-container').appendChild(actionDropdown);
// jQuery dropdown plugin
$.fn.monochromeDropdown = function(options) {
const settings = $.extend({
placeholder: 'Select...',
onChange: function() {}
}, options);
return this.each(function() {
const $dropdown = $(this);
const $button = $dropdown.find('.dropdown-button');
const $panel = $dropdown.find('.dropdown-panel');
// Toggle dropdown
$button.on('click', function(e) {
e.stopPropagation();
$dropdown.toggleClass('is-open');
$panel.toggleClass('is-open');
});
// Select option
$panel.find('.dropdown-option').on('click', function() {
const value = $(this).data('value');
const text = $(this).text();
$button.find('span:first').text(text);
$panel.find('.dropdown-option').removeClass('is-selected');
$(this).addClass('is-selected');
$dropdown.removeClass('is-open');
$panel.removeClass('is-open');
settings.onChange(value);
});
});
};
// Initialize dropdowns
$('.dropdown').monochromeDropdown({
onChange: function(value) {
console.log('Selected:', value);
}
});
// Action dropdown
$('#action-dropdown').on('click', '.dropdown-option', function() {
const action = $(this).data('action');
switch(action) {
case 'edit':
console.log('Edit action');
break;
case 'duplicate':
console.log('Duplicate action');
break;
case 'delete':
if (confirm('Are you sure?')) {
console.log('Delete action');
}
break;
}
$('#action-dropdown').removeClass('is-open');
});
<!-- Basic Select -->
<div class="form-group">
<label class="label">Basic Select</label>
<select class="select" id="basic-select">
<option value="">Choose an option</option>
<option value="option1">Option 1</option>
<option value="option2" selected>Option 2</option>
<option value="option3">Option 3</option>
</select>
</div>
<!-- Multiple Select -->
<div class="form-group">
<label class="label">Multiple Select</label>
<select class="select" id="multi-select" multiple size="4">
<option value="option1">Option 1</option>
<option value="option2">Option 2</option>
<option value="option3">Option 3</option>
<option value="option4">Option 4</option>
</select>
</div>
<script>
// Dropdown controller
class DropdownController {
constructor(element) {
this.element = element;
this.button = element.querySelector('.dropdown-button');
this.panel = element.querySelector('.dropdown-panel');
this.options = element.querySelectorAll('.dropdown-option');
this.init();
}
init() {
// Toggle dropdown
this.button.addEventListener('click', (e) => {
e.stopPropagation();
this.toggle();
});
// Handle option clicks
this.options.forEach(option => {
option.addEventListener('click', (e) => {
this.handleSelect(e.target);
});
});
// Close on outside click
document.addEventListener('click', () => {
this.close();
});
}
toggle() {
this.element.classList.toggle('is-open');
this.panel.classList.toggle('is-open');
}
close() {
this.element.classList.remove('is-open');
this.panel.classList.remove('is-open');
}
handleSelect(option) {
const value = option.dataset.value || option.dataset.action;
if (option.dataset.value) {
// Update button text for select dropdown
this.button.querySelector('span:first-child').textContent = option.textContent;
// Update selected state
this.options.forEach(opt => opt.classList.remove('is-selected'));
option.classList.add('is-selected');
}
console.log('Selected:', value);
this.close();
}
}
// Initialize dropdowns
new DropdownController(document.getElementById('simple-dropdown'));
new DropdownController(document.getElementById('action-dropdown'));
</script>
Interactive code editor with live preview and theme customization.
Edit HTML and CSS in real-time with instant preview. Perfect for testing components.
Open Editor →Learn how to install and use Monochrome Edge in your project with CDN or npm.
Comprehensive installation guide with CDN, npm, and framework examples.
View Integration Guide →Blog and portfolio section containers for organizing content with headings, subtitles, and dividers.
Thoughts on design, development, and everything in between
This is a blog section component that provides structured content layout with clear hierarchy and spacing. Use it to organize your blog posts, articles, or any content that requires clean sectioning.
<section class="blog-section">
<div class="blog-section-header">
<h2 class="blog-section-title">Recent Articles</h2>
<p class="blog-section-subtitle">
Thoughts on design, development, and everything in between
</p>
</div>
<hr class="blog-section-divider" />
<div class="blog-content">
<!-- Section content -->
</div>
</section>
import React from 'react';
import { BlogSection } from '@monochrome-edge';
function BlogPage() {
return (
<BlogSection
title="Recent Articles"
subtitle="Thoughts on design, development, and everything in between"
>
<div className="blog-content">
<p>Your blog content goes here...</p>
</div>
</BlogSection>
);
}
<template>
<blog-section
title="Recent Articles"
subtitle="Thoughts on design, development, and everything in between"
>
<div class="blog-content">
<p>Your blog content goes here...</p>
</div>
</blog-section>
</template>
<script setup lang="ts">
import { BlogSection } from '@monochrome-edge/vue';
</script>
Article and project card components for showcasing blog posts, portfolio items, and case studies.
A comprehensive guide to creating reusable, theme-aware components with CSS variables.
Deep dive into using custom properties for dynamic, flexible design systems.
<article class="article-card">
<div class="article-card-image article-card-image-grayscale">
<img src="..." alt="Article thumbnail" />
</div>
<div class="article-card-content">
<span class="article-card-category">Design Systems</span>
<h3 class="article-card-title">Building Modern UI Components</h3>
<p class="article-card-description">
A comprehensive guide to creating reusable components...
</p>
<div class="article-card-meta">
<span class="article-card-date">Oct 7, 2025</span>
<div class="article-card-tags">
<span class="article-card-tag">CSS</span>
<span class="article-card-tag">Design</span>
</div>
</div>
</div>
</article>
import React from 'react';
import { ArticleCard } from '@monochrome-edge';
function BlogGrid() {
return (
<div className="article-card-grid">
<ArticleCard
category="Design Systems"
title="Building Modern UI Components"
description="A comprehensive guide to creating reusable components..."
date="Oct 7, 2025"
tags={['CSS', 'Design']}
image="/path/to/image.jpg"
grayscale
/>
</div>
);
}
<template>
<div class="article-card-grid">
<article-card
category="Design Systems"
title="Building Modern UI Components"
description="A comprehensive guide to creating reusable components..."
date="Oct 7, 2025"
:tags="['CSS', 'Design']"
image="/path/to/image.jpg"
grayscale
/>
</div>
</template>
<script setup lang="ts">
import { ArticleCard } from '@monochrome-edge/vue';
</script>
Exploring the challenges and solutions in creating a flexible design system that supports multiple themes and contexts.
Best practices for organizing and structuring reusable UI components.
Deep dive into using custom properties for dynamic, flexible design systems.
How to create clean, functional interfaces that prioritize user experience.
<ul class="article-list">
<li class="article-list-item">
<a href="#" class="article-list-link">
<span class="article-list-date">Oct 7, 2025</span>
<h4 class="article-list-title">Building a Dual-Theme Design System</h4>
<p class="article-list-excerpt">
Exploring the challenges and solutions...
</p>
<div class="article-card-tags">
<span class="article-card-tag">Design</span>
<span class="article-card-tag">CSS</span>
</div>
</a>
</li>
</ul>
import React from 'react';
import { ArticleList } from '@monochrome-edge';
function RecentPosts() {
return (
<ArticleList compact>
<ArticleList.Item
date="Oct 7, 2025"
title="Understanding Design Systems"
excerpt="Exploring the challenges and solutions..."
tags={['Design', 'CSS']}
href="/posts/design-systems"
/>
</ArticleList>
);
}
<template>
<article-list compact>
<article-list-item
date="Oct 7, 2025"
title="Understanding Design Systems"
excerpt="Exploring the challenges and solutions..."
:tags="['Design', 'CSS']"
href="/posts/design-systems"
/>
</article-list>
</template>
<script setup lang="ts">
import { ArticleList, ArticleListItem } from '@monochrome-edge/vue';
</script>
Modern minimalist UI component library with dual-theme system.
<article class="project-card">
<div class="project-card-image project-card-image-grayscale">
<img src="..." alt="Project thumbnail" />
</div>
<div class="project-card-content">
<div class="project-card-header">
<h3 class="project-card-title">Monochrome Edge</h3>
<span class="project-card-status project-card-status-active">Active</span>
</div>
<p class="project-card-description">
Modern minimalist UI component library...
</p>
<div class="project-card-stats">
<div class="project-card-stat">
<svg class="project-card-stat-icon">...</svg>
<span class="project-card-stat-value">1.2k</span>
</div>
</div>
<div class="project-card-tags">
<span class="project-card-tag">CSS</span>
<span class="project-card-tag">TypeScript</span>
</div>
</div>
</article>
Track all notable changes to Monochrome Edge UI Components.