slidev-interactive
Guide for embedding interactive demos and mock UIs in Slidev presentations. Use when adding clickable prototypes, animated workflows, Vue components, or standalone HTML5 mock pages to slide decks.
What this skill does
# Slidev Interactive Demos
Guide for embedding interactive demos, mock UIs, and animated workflows in Slidev presentations.
## Two Integration Paths
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`.
## Vue Component Path
### Auto-Import
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:
```markdown
---
layout: default
---
# Live Demo
<LoginForm />
```
### Reactive State with Vue 3
Use the Composition API for state management:
```vue
<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>
```
### Integration with v-click
Wrap components in `v-click` for progressive reveal, or use `$clicks` for step-aware behavior:
```markdown
<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:
```vue
<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>
```
### Scoped Styles
Use `<style scoped>` to prevent leaking styles into the slide:
```vue
<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>
```
### Slide-Aware Behavior with $slidev
Access navigation state inside components using the `useSlideContext` composable:
```vue
<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>
```
## Iframe / HTML5 Path
### Directory Structure
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
```
### Slidev iframe Layouts
Use built-in layouts to embed mocks:
```markdown
---
layout: iframe
url: /mocks/dashboard/index.html
---
```
Split layouts for side-by-side content:
```markdown
---
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
```
### Importing Existing Mocks
To embed an existing standalone HTML prototype:
1. Copy the mock to `public/mocks/<name>/`
2. Fix relative asset paths — all paths must work from `/mocks/<name>/index.html`
3. Bundle external CSS/JS inline or copy dependencies into the mock directory
4. Reference via `url: /mocks/<name>/index.html` in slide frontmatter
Asset path checklist:
- `./style.css` works when `style.css` is in the same directory
- `../shared/reset.css` works if the `shared/` directory is within `public/`
- Absolute CDN URLs work if network is available during presentation
- Prefer inline styles/scripts for fully offline presentations
### postMessage Communication
Send data from the iframe to the parent slide:
```javascript
// 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: '[email protected]' })
})
```
Listen in the slide (embed in a Vue component):
```vue
<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>
```
## Common Mock Templates
### Login Flow
Demonstrate authentication UX with three states: input, loading, success.
```vue
<!-- 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>
```
### Dashboard Widget
Display metric cards with live-updating values:
```vue
<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>
```
### API Request/Response
Show an HTTP interaction step by step:
```vue
<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(Related in Web Dev
generating-lwc-components
IncludedLightning Web Components with PICKLES methodology and 165-point scoring. Use this skill when the user creates or edits LWC components, builds wire service patterns, or writes Jest tests for LWC. TRIGGER when: user creates/edits LWC components, touches lwc/**/*.js, .html, .css, .js-meta.xml files, or asks about wire service, SLDS, or Jest LWC tests. DO NOT TRIGGER when: Apex classes (use generating-apex), Aura components, or Visualforce.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Set up queries with useQuery, mutations with useMutation, configure QueryClient caching strategies, implement optimistic updates, and handle infinite scroll with useInfiniteQuery. Use when: setting up data fetching in React projects, migrating from v4 to v5, or fixing object syntax required errors, query callbacks removed issues, cacheTime renamed to gcTime, isPending vs isLoading confusion, keepPreviousData removed problems.
document-processor-api
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
nutrient-document-processing
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Covers useMutationState, simplified optimistic updates, throwOnError, network mode (offline/PWA), and infiniteQueryOptions. Use when setting up data fetching, fixing v4→v5 migration errors (object syntax, gcTime, isPending, keepPreviousData), or debugging SSR/hydration issues with streaming server components.
accelint-nextjs-best-practices
IncludedNext.js performance optimization and best practices. Use when writing Next.js code (App Router or Pages Router); implementing Server Components, Server Actions, or API routes; optimizing RSC serialization, data fetching, or server-side rendering; reviewing Next.js code for performance issues; fixing authentication in Server Actions; or implementing Suspense boundaries, parallel data fetching, or request deduplication.