From all-skills
Embeds interactive Vue components, HTML5 iframes, and animations in Slidev presentations. Use for clickable prototypes, workflows synced with v-clicks, and mock UIs in slides.
npx claudepluginhub vinnie357/claude-skills --plugin alliumThis skill uses the workspace's default tool permissions.
Guide for embedding interactive demos, mock UIs, and animated workflows in Slidev presentations.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Checks Next.js compilation errors using a running Turbopack dev server after code edits. Fixes actionable issues before reporting complete. Replaces `next build`.
Guide for embedding interactive demos, mock UIs, and animated workflows in Slidev presentations.
Choose based on portability and integration requirements:
| Concern | Vue Components | Iframe / HTML5 |
|---|---|---|
| Slidev integration | Tight — shares state, v-click, themes | Loose — isolated sandbox |
| Portability | Requires Slidev project | Self-contained HTML file |
| Reactivity | Full Vue 3 reactivity | Vanilla JS or any framework |
| Styling | Inherits slide theme | Independent CSS |
| Hot reload | Yes (Vite HMR) | Requires manual refresh |
| Best for | Demos that respond to clicks, slide context | Standalone prototypes, third-party widgets |
For Vue component patterns, see references/vue-components.md.
For iframe and standalone HTML patterns, see references/iframe-mocks.md.
For animation patterns, see references/animation-patterns.md.
Place .vue files in components/ — Slidev auto-imports them by filename:
components/
├── LoginForm.vue
├── ApiExplorer.vue
└── DashboardWidget.vue
Use directly in slides without import statements:
---
layout: default
---
# Live Demo
<LoginForm />
Use the Composition API for state management:
<script setup>
import { ref, computed } from 'vue'
const step = ref(0)
const steps = ['Input', 'Validate', 'Submit', 'Success']
const currentStep = computed(() => steps[step.value])
function advance() {
if (step.value < steps.length - 1) step.value++
}
function reset() {
step.value = 0
}
</script>
Wrap components in v-click for progressive reveal, or use $clicks for step-aware behavior:
<v-click>
<ApiExplorer />
</v-click>
<!-- Or bind clicks inside the component -->
<WorkflowDemo :step="$clicks" />
Inside the component, receive step as a prop and render accordingly:
<script setup>
const props = defineProps({ step: { type: Number, default: 0 } })
</script>
<template>
<div class="workflow">
<StepItem v-for="(s, i) in steps" :key="i"
:active="i === props.step"
:done="i < props.step" />
</div>
</template>
Use <style scoped> to prevent leaking styles into the slide:
<style scoped>
.demo-container {
border: 1px solid #e2e8f0;
border-radius: 8px;
padding: 1.5rem;
}
.active-step {
background: var(--slidev-theme-primary, #4f46e5);
color: white;
}
</style>
Access navigation state inside components using the useSlideContext composable:
<script setup>
import { useSlideContext } from '@slidev/client'
const { $slidev } = useSlideContext()
// Current slide number
const slideNo = computed(() => $slidev.nav.currentPage)
// Total clicks on this slide
const clicks = computed(() => $slidev.nav.clicks)
// Check if presenting (vs. browsing)
const isPresenting = computed(() => $slidev.nav.isPresenter)
</script>
Place self-contained mocks in public/mocks/:
public/
└── mocks/
├── dashboard/
│ ├── index.html
│ ├── style.css
│ └── app.js
└── login-flow/
└── index.html # inline CSS/JS preferred for portability
Use built-in layouts to embed mocks:
---
layout: iframe
url: /mocks/dashboard/index.html
---
Split layouts for side-by-side content:
---
layout: iframe-right
url: /mocks/login-flow/index.html
---
## Login Flow
Walk through the authentication steps:
1. User enters credentials
2. Client validates format
3. Server returns JWT
4. Redirect to dashboard
To embed an existing standalone HTML prototype:
public/mocks/<name>//mocks/<name>/index.htmlurl: /mocks/<name>/index.html in slide frontmatterAsset path checklist:
./style.css works when style.css is in the same directory../shared/reset.css works if the shared/ directory is within public/Send data from the iframe to the parent slide:
// Inside the mock (iframe)
function notifyParent(event, payload) {
window.parent.postMessage({ event, payload }, '*')
}
// Example: notify parent when user completes a step
document.querySelector('#submit-btn').addEventListener('click', () => {
notifyParent('form-submitted', { user: 'demo@example.com' })
})
Listen in the slide (embed in a Vue component):
<script setup>
import { onMounted, onUnmounted, ref } from 'vue'
const lastEvent = ref(null)
function handleMessage(e) {
if (e.data?.event) lastEvent.value = e.data
}
onMounted(() => window.addEventListener('message', handleMessage))
onUnmounted(() => window.removeEventListener('message', handleMessage))
</script>
Demonstrate authentication UX with three states: input, loading, success.
<!-- components/LoginDemo.vue -->
<script setup>
import { ref } from 'vue'
const state = ref('idle') // idle | loading | success | error
const email = ref('')
const password = ref('')
async function submit() {
state.value = 'loading'
await new Promise(r => setTimeout(r, 1200))
state.value = email.value.includes('@') ? 'success' : 'error'
}
</script>
<template>
<form class="login-demo" @submit.prevent="submit"
aria-label="Login demonstration form">
<div v-if="state === 'success'" role="status" aria-live="polite">
Signed in as {{ email }}
</div>
<template v-else>
<label for="demo-email">Email</label>
<input id="demo-email" v-model="email" type="email"
:disabled="state === 'loading'"
aria-required="true" />
<label for="demo-password">Password</label>
<input id="demo-password" v-model="password" type="password"
:disabled="state === 'loading'"
aria-required="true" />
<button type="submit" :disabled="state === 'loading'"
:aria-busy="state === 'loading'">
{{ state === 'loading' ? 'Signing in…' : 'Sign in' }}
</button>
<p v-if="state === 'error'" role="alert" class="error">
Invalid email address — include an @ symbol
</p>
</template>
</form>
</template>
Display metric cards with live-updating values:
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
const metrics = ref([
{ label: 'Requests/s', value: 0 },
{ label: 'Error rate', value: 0 },
{ label: 'P95 latency', value: 0, unit: 'ms' },
])
let interval
onMounted(() => {
interval = setInterval(() => {
metrics.value[0].value = Math.floor(800 + Math.random() * 400)
metrics.value[1].value = (Math.random() * 2).toFixed(2)
metrics.value[2].value = Math.floor(45 + Math.random() * 60)
}, 1500)
})
onUnmounted(() => clearInterval(interval))
</script>
<template>
<div class="dashboard" role="region" aria-label="Live metrics dashboard">
<div v-for="m in metrics" :key="m.label"
class="metric-card" aria-live="polite" aria-atomic="true">
<span class="label">{{ m.label }}</span>
<span class="value">{{ m.value }}<span v-if="m.unit" class="unit">{{ m.unit }}</span></span>
</div>
</div>
</template>
Show an HTTP interaction step by step:
<script setup>
import { ref } from 'vue'
const phase = ref('idle') // idle | request | response | error
const responseData = ref(null)
async function sendRequest() {
phase.value = 'request'
await new Promise(r => setTimeout(r, 900))
responseData.value = { id: 42, name: 'Widget', status: 'active' }
phase.value = 'response'
}
</script>
<template>
<div class="api-demo" role="region" aria-label="API request demonstration">
<div class="request-panel">
<code>GET /api/widgets/42</code>
<button @click="sendRequest" :disabled="phase === 'request'">Send</button>
</div>
<div v-if="phase !== 'idle'" class="response-panel"
aria-live="polite" :aria-busy="phase === 'request'">
<span v-if="phase === 'request'">Sending…</span>
<pre v-else>{{ JSON.stringify(responseData, null, 2) }}</pre>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const steps = ['Account', 'Profile', 'Review', 'Done']
const current = ref(0)
const isLast = computed(() => current.value === steps.length - 1)
function next() { if (!isLast.value) current.value++ }
function back() { if (current.value > 0) current.value-- }
</script>
<template>
<div class="wizard" role="region" aria-label="Multi-step form">
<nav aria-label="Progress">
<ol>
<li v-for="(s, i) in steps" :key="s"
:aria-current="i === current ? 'step' : undefined">
{{ s }}
</li>
</ol>
</nav>
<div :key="current" class="step-content">
<h3>Step {{ current + 1 }}: {{ steps[current] }}</h3>
<p>Demo content for {{ steps[current] }} step.</p>
</div>
<div class="wizard-nav">
<button @click="back" :disabled="current === 0">Back</button>
<button @click="next" :disabled="isLast">
{{ isLast ? 'Finish' : 'Next' }}
</button>
</div>
</div>
</template>
Pair a Mermaid workflow diagram with an interactive demo — diagram shows structure, demo lets audience walk through it.
---
layout: two-cols
---
## Auth Flow Diagram
```mermaid
sequenceDiagram
Client->>Server: POST /login
Server->>DB: Validate credentials
DB-->>Server: User record
Server-->>Client: JWT token
::right::
Always add annotation bullets below each Mermaid block:
All interactive demos must meet these requirements. Load core:accessibility for full WCAG guidance.
tabindex="0" and handle keydown for Enter/Spaceoutline without a replacement<div role="button" tabindex="0"
@click="activate"
@keydown.enter="activate"
@keydown.space.prevent="activate">
Activate
</div>
role="region" with aria-label on demo containersaria-live="polite" for updates that don't interrupt (metric changes, status)aria-live="assertive" only for critical errors requiring immediate attentionrole="alert" for error messagesrole="status" for success confirmationsaria-hidden="true"<label> via for/id or aria-labelaria-required="true"aria-busy="true" on the button or containeraria-describedby to link error text to its input@media (prefers-reduced-motion: reduce) checksSee references/animation-patterns.md for motion-safe animation examples.
$slidev.nav.isPresenter to detect export mode and disable timers<v-if="!$slidev.nav.isExporting"> to hide demo-only elements from exportsreferences/vue-components.md — Detailed Vue 3 component patterns, worked examplesreferences/iframe-mocks.md — Standalone HTML5 mock patterns and postMessage examplesreferences/animation-patterns.md — CSS transitions, Vue Transition, reduced-motion patterns