Component Examples
Real-world component implementations you can use as templates.
Hello Card (Minimal)
A minimal component showing the basics.
Use case: Learning component structure
manifest.json:
json
{
"name": "hello-card",
"version": "1.0.0",
"description": "Minimal hello world component",
"entry": "index.html",
"component": {
"width": 300,
"height": 200,
"schema": {
"type": "object",
"properties": {
"message": {
"type": "string",
"default": "Hello World!"
}
}
}
}
}index.html:
html
<!DOCTYPE html>
<html>
<head>
<style>
body {
margin: 0;
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
font-family: sans-serif;
}
</style>
</head>
<body>
<div>
<h1 id="message">Hello!</h1>
<button onclick="sayHello()">Click Me</button>
</div>
<script>
const props = window.componentProps || {};
document.getElementById('message').textContent = props.message || 'Hello World!';
function sayHello() {
alert('Hello! 👋');
}
</script>
</body>
</html>Vocabulary Card
Interactive flashcard for language learning.
Use case: Educational platforms, language schools
Features:
- Front/back flip animation
- Audio pronunciation
- Progress tracking
- Difficulty levels
manifest.json:
json
{
"name": "vocabulary-card",
"version": "1.0.0",
"description": "Interactive vocabulary flashcard",
"entry": "index.html",
"component": {
"width": 300,
"height": 200,
"schema": {
"type": "object",
"properties": {
"word": { "type": "string", "required": true },
"translation": { "type": "string" },
"example": { "type": "string" },
"audioUrl": { "type": "string", "format": "uri" },
"difficulty": {
"type": "string",
"enum": ["easy", "medium", "hard"],
"default": "medium"
}
}
}
}
}index.html:
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
width: 100%;
height: 100%;
}
.card {
width: 100%;
height: 100%;
perspective: 1000px;
}
.card-inner {
position: relative;
width: 100%;
height: 100%;
transition: transform 0.6s;
transform-style: preserve-3d;
}
.card.flipped .card-inner {
transform: rotateY(180deg);
}
.card-face {
position: absolute;
width: 100%;
height: 100%;
backface-visibility: hidden;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 20px;
border-radius: 12px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
.card-front {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.card-back {
background: white;
color: #333;
transform: rotateY(180deg);
}
.word {
font-size: 32px;
font-weight: bold;
margin-bottom: 10px;
}
.example {
font-size: 14px;
opacity: 0.9;
font-style: italic;
margin-top: 10px;
}
.translation {
font-size: 24px;
color: #667eea;
margin-bottom: 15px;
}
.difficulty {
position: absolute;
top: 10px;
right: 10px;
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
text-transform: uppercase;
}
.difficulty-easy { background: #10b981; color: white; }
.difficulty-medium { background: #f59e0b; color: white; }
.difficulty-hard { background: #ef4444; color: white; }
.audio-button {
margin-top: 15px;
padding: 8px 16px;
background: rgba(255,255,255,0.2);
border: 2px solid white;
border-radius: 20px;
color: white;
cursor: pointer;
font-size: 14px;
}
.audio-button:hover {
background: rgba(255,255,255,0.3);
}
</style>
</head>
<body>
<div class="card" id="card">
<div class="card-inner">
<!-- Front -->
<div class="card-face card-front">
<div class="difficulty" id="difficulty"></div>
<div class="word" id="word">Word</div>
<div class="example" id="example"></div>
<button class="audio-button" id="audioBtn" onclick="playAudio()">
🔊 Pronounce
</button>
</div>
<!-- Back -->
<div class="card-face card-back">
<div class="translation" id="translation">Translation</div>
<button class="audio-button" onclick="flipCard()">
← Back
</button>
</div>
</div>
</div>
<script>
const props = window.componentProps || {};
let flipped = false;
function init() {
// Set content
document.getElementById('word').textContent = props.word || 'Word';
document.getElementById('translation').textContent = props.translation || 'Translation';
document.getElementById('example').textContent = props.example || '';
// Set difficulty badge
const difficulty = props.difficulty || 'medium';
const badge = document.getElementById('difficulty');
badge.textContent = difficulty;
badge.className = `difficulty difficulty-${difficulty}`;
// Hide audio button if no URL
if (!props.audioUrl) {
document.getElementById('audioBtn').style.display = 'none';
}
}
function flipCard() {
const card = document.getElementById('card');
flipped = !flipped;
card.classList.toggle('flipped', flipped);
// Emit event
window.parent.postMessage({
type: 'component-event',
event: 'card-flipped',
data: { word: props.word, flipped }
}, '*');
}
function playAudio() {
if (props.audioUrl) {
const audio = new Audio(props.audioUrl);
audio.play();
}
}
// Flip on click (front only)
document.querySelector('.card-front').addEventListener('click', (e) => {
if (e.target.tagName !== 'BUTTON') {
flipCard();
}
});
init();
</script>
</body>
</html>Usage:
json
{
"components": [
{
"type": "vocabulary-card",
"props": {
"word": "Hello",
"translation": "Привет",
"example": "Hello, how are you?",
"audioUrl": "https://example.com/audio/hello.mp3",
"difficulty": "easy"
}
}
]
}Todo Card
Simple task card with checkbox.
Use case: Project management, Kanban boards
index.html:
html
<!DOCTYPE html>
<html>
<head>
<style>
body {
margin: 0;
padding: 15px;
font-family: sans-serif;
background: white;
border: 1px solid #e5e7eb;
border-radius: 8px;
}
.todo {
display: flex;
align-items: flex-start;
gap: 10px;
}
.checkbox {
margin-top: 2px;
width: 20px;
height: 20px;
cursor: pointer;
}
.content {
flex: 1;
}
.title {
font-size: 16px;
font-weight: 500;
margin-bottom: 5px;
}
.title.completed {
text-decoration: line-through;
color: #9ca3af;
}
.description {
font-size: 14px;
color: #6b7280;
}
.priority {
display: inline-block;
padding: 2px 8px;
border-radius: 12px;
font-size: 12px;
margin-top: 5px;
}
.priority-high { background: #fee2e2; color: #dc2626; }
.priority-medium { background: #fef3c7; color: #d97706; }
.priority-low { background: #dbeafe; color: #2563eb; }
</style>
</head>
<body>
<div class="todo">
<input type="checkbox" class="checkbox" id="checkbox" onchange="toggleComplete()">
<div class="content">
<div class="title" id="title">Task title</div>
<div class="description" id="description"></div>
<span class="priority" id="priority"></span>
</div>
</div>
<script>
const props = window.componentProps || {};
let completed = props.completed || false;
function init() {
document.getElementById('title').textContent = props.title || 'Untitled Task';
document.getElementById('description').textContent = props.description || '';
const priority = props.priority || 'medium';
const badge = document.getElementById('priority');
badge.textContent = priority.toUpperCase();
badge.className = `priority priority-${priority}`;
document.getElementById('checkbox').checked = completed;
updateUI();
}
function toggleComplete() {
completed = !completed;
updateUI();
// Emit event
window.parent.postMessage({
type: 'component-event',
event: 'task-completed',
data: { taskId: props.id, completed }
}, '*');
}
function updateUI() {
const title = document.getElementById('title');
title.classList.toggle('completed', completed);
}
init();
</script>
</body>
</html>Counter Widget
Real-time counter with increment/decrement.
Use case: Live polls, voting, scorekeeping
html
<!DOCTYPE html>
<html>
<head>
<style>
body {
margin: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
color: white;
font-family: sans-serif;
}
.count {
font-size: 64px;
font-weight: bold;
margin: 20px 0;
}
.buttons {
display: flex;
gap: 10px;
}
button {
width: 50px;
height: 50px;
border: none;
border-radius: 50%;
background: white;
color: #f5576c;
font-size: 24px;
cursor: pointer;
transition: transform 0.1s;
}
button:active {
transform: scale(0.95);
}
</style>
</head>
<body>
<p>Count</p>
<div class="count" id="count">0</div>
<div class="buttons">
<button onclick="decrement()">−</button>
<button onclick="increment()">+</button>
</div>
<script>
let count = window.componentProps?.count || 0;
function updateDisplay() {
document.getElementById('count').textContent = count;
}
function increment() {
count++;
updateDisplay();
broadcastChange();
}
function decrement() {
count--;
updateDisplay();
broadcastChange();
}
function broadcastChange() {
window.parent.postMessage({
type: 'component-event',
event: 'counter-changed',
data: { count }
}, '*');
}
// Listen for changes from other instances
window.addEventListener('message', (e) => {
if (e.data.type === 'board-event' && e.data.event === 'counter-changed') {
count = e.data.data.count;
updateDisplay();
}
});
updateDisplay();
</script>
</body>
</html>More Examples
Educational
- Quiz Question - Multiple choice with timer
- Progress Bar - Student completion tracking
- Grade Calculator - Automatic grade computation
Business
- Pipeline Card - Sales stage tracking
- Time Tracker - Log hours worked
- Customer Card - Contact info display
Creative
- Color Picker - Collaborative color selection
- Mood Board Image - Annotated image card
- Sticky Note - Classic sticky note
Need Help?
Can't find what you're looking for?