Code Comparison
Code Comparison stacks two code blocks in the same grid cell and clips the top one at the divider, so the panes always line up regardless of how long either snippet is. Dragging the divider wipes from one version to the other, which makes a refactor readable in place instead of in two separate blocks.
The divider is a real range input: pointer drag, click-to-jump and arrow keys all work, and the value is exposed through v-model. Each pane can be filled from a prop or from its own slot when you want to drop a syntax-highlighted block inside.
Usage
const total = items.filter(item => item.active).map(item => item.price)
const tax = total.reduce((sum, price) => sum + price * 0.2, 0)
const receipt = { total, tax, paid: total + tax, currency: 'USD' }const total = items.filter(({ active }) => active).map(({ price }) => price)
const tax = total.reduce((sum, price) => sum + price * 0.2, 0)
const receipt = { total, tax, paid: total + tax, currency: 'USD' }<script setup lang="ts">
const before = `const total = items.filter(item => item.active).map(item => item.price)
const tax = total.reduce((sum, price) => sum + price * 0.2, 0)
const receipt = { total, tax, paid: total + tax, currency: 'USD' }`
const after = `const total = items.filter(({ active }) => active).map(({ price }) => price)
const tax = total.reduce((sum, price) => sum + price * 0.2, 0)
const receipt = { total, tax, paid: total + tax, currency: 'USD' }`
</script>
<template>
<MCodeComparison
before-label="Before"
after-label="After"
:before="before"
:after="after"
/>
</template>
Examples
Customize
const total = items.filter(item => item.active).map(item => item.price)
const tax = total.reduce((sum, price) => sum + price * 0.2, 0)
const receipt = { total, tax, paid: total + tax, currency: 'USD' }const total = items.filter(({ active }) => active).map(({ price }) => price)
const tax = total.reduce((sum, price) => sum + price * 0.2, 0)
const receipt = { total, tax, paid: total + tax, currency: 'USD' }Customize
Props
| Prop | Type | Default | Description |
|---|---|---|---|
before | string | '' | Text for the left-hand pane. Ignored when the before slot is filled. |
after | string | '' | Text for the right-hand pane. Ignored when the after slot is filled. |
beforeLabel | string | undefined | Label shown in a header above the left-hand pane. Leave it out for no header. |
afterLabel | string | undefined | Label shown in a header above the right-hand pane. |
modelValue | number | 50 | Where the divider sits, from 0 to 100. Pair it with v-model to read the position back. |
ariaLabel | string | 'Reveal amount' | Accessible name of the divider's range input. |
class | string | '' | CSS classes for the frame. |
Slots
| Slot | Props | Description |
|---|---|---|
before | - | Content of the left-hand pane. Use it to nest a highlighted code block instead of plain text. |
after | - | Content of the right-hand pane. |
UI
This component's theme exposes the slots below. Override any of them per instance with the ui prop, or app-wide under ui.codeComparison in app.config.ts.
| Prop | Type | Default | Description |
|---|---|---|---|
ui | object | - | Per-slot classes merged over the theme defaults. |
| Slot | Props | Description |
|---|---|---|
root | - | relative grid overflow-hidden rounded-xl border border-default bg-elevated |
pane | - | col-start-1 row-start-1 min-w-0 overflow-hidden |
header | - | flex items-center justify-between gap-2 border-b border-default px-4 py-2 text-xs font-medium text-muted |
code | - | block overflow-x-auto p-4 font-mono text-xs leading-relaxed text-highlighted |
divider | - | pointer-events-none absolute inset-y-0 z-10 w-px -translate-x-1/2 bg-primary |
handle | - | absolute left-1/2 top-1/2 flex size-8 -translate-x-1/2 -translate-y-1/2 items-center justify-center rounded-full border border-default bg-default text-muted shadow-md |
slider | - | absolute inset-0 z-20 size-full cursor-ew-resize appearance-none bg-transparent opacity-0 |
Accessibility
The divider is an <input type="range"> with a label, not a decorative handle. A keyboard user reaches it with Tab and moves it with the arrow keys, and assistive technology announces it as a slider with a value between 0 and 100. The visible divider and grip are aria-hidden so the input is the only thing announced, and both panes stay in the document, so their text is readable by a screen reader and selectable by a search engine either way.