Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
81f8ede
feat(docs): redesign Highlights playground
mdo Sep 24, 2026
b6a0516
feat(docs): add Highlights benchmark evidence
mdo Sep 24, 2026
a4ea9a4
docs(diffs): simplify Highlights hero links
mdo Sep 24, 2026
3e08ae8
fix(docs): tighten mobile benchmark charts
mdo Sep 28, 2026
b74ca17
feat(docs): add the Pierre letterhead to product homes
mdo Sep 29, 2026
dfed065
feat(docs): unify product navigation
mdo Sep 30, 2026
f4abec5
feat(docs): refine Highlights landing page
mdo Sep 30, 2026
22c5d5d
feat(docs): streamline product navigation
mdo Sep 30, 2026
8166e14
feat(docs): showcase Highlights browser performance
mdo Sep 30, 2026
8d49a2f
fix(docs): use the duotone Wasm icon in the gallery hero
mdo Sep 30, 2026
e6587a9
feat(docs): showcase Highlights HTML performance
mdo Sep 30, 2026
bc0bc22
feat(docs): add Highlights install guidance
mdo Sep 30, 2026
c8d8e10
feat(docs): polish Highlights benchmark visuals
mdo Sep 30, 2026
42612bf
feat(docs): add Highlights agent setup flow
mdo Sep 30, 2026
069103a
fix(docs): declare SVG asset imports
mdo Sep 30, 2026
4d1d5c9
feat(docs): animate the Highlights hero gradient
mdo Sep 30, 2026
3909518
fix(docs): clarify Highlights benchmark scales
mdo Oct 1, 2026
e4d6a38
docs(docs): render the Highlights install command as a file
mdo Oct 1, 2026
21b4f41
feat(docs): refine install and mobile navigation
mdo Oct 1, 2026
9f23e97
fix(docs): improve benchmark responsiveness
mdo Oct 1, 2026
d4ef406
feat(docs): brighten the dark Highlights hero
mdo Oct 1, 2026
d32dbc3
feat(docs): refine Highlights benchmarks
mdo Oct 1, 2026
d92a6e3
feat(docs): refine Highlights demo surfaces
mdo Oct 1, 2026
8bf3aca
fix(docs): tighten benchmark duration labels
mdo Oct 1, 2026
fda42d3
feat(docs): add Highlights social card
mdo Oct 2, 2026
0ea1d55
fix(docs): align Highlights metadata title
mdo Oct 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions apps/docs/app/(diffs)/_docs/DocsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ import {
HIGHLIGHTS_DUAL_THEMES,
HIGHLIGHTS_DUAL_THEMES_CSS,
HIGHLIGHTS_HTML,
HIGHLIGHTS_INSTALL,
HIGHLIGHTS_LIVE,
HIGHLIGHTS_STREAM,
HIGHLIGHTS_STREAM_PIPE,
Expand Down Expand Up @@ -658,6 +659,7 @@ async function ThemingSection() {

async function HighlightsHighlighterSection() {
const [
highlightsInstall,
highlightsHtml,
highlightsTokens,
highlightsStreamPipe,
Expand All @@ -672,6 +674,7 @@ async function HighlightsHighlighterSection() {
highlightsApiRuntime,
highlightsApiTypes,
] = await Promise.all([
preloadCodeExample(HIGHLIGHTS_INSTALL),
preloadCodeExample(HIGHLIGHTS_HTML),
preloadCodeExample(HIGHLIGHTS_TOKENS),
preloadCodeExample(HIGHLIGHTS_STREAM_PIPE),
Expand All @@ -689,6 +692,7 @@ async function HighlightsHighlighterSection() {
const content = await renderMDX({
filePath: '(diffs)/docs/HighlightsHighlighter/content.mdx',
scope: {
highlightsInstall,
highlightsHtml,
highlightsTokens,
highlightsStreamPipe,
Expand Down
12 changes: 12 additions & 0 deletions apps/docs/app/(diffs)/docs/HighlightsHighlighter/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,17 @@ const options = {
} as const;

export const HIGHLIGHTS_HIGHLIGHTER_EXAMPLES = {
highlightsInstall: {
file: {
name: 'pnpm.sh',
contents: 'pnpm add @pierre/highlights',
},
options: {
theme: { dark: 'pierre-dark', light: 'pierre-light' },
disableFileHeader: true,
unsafeCSS: CustomScrollbarCSS,
},
},
highlightsHtml: {
file: {
name: 'html.ts',
Expand Down Expand Up @@ -306,6 +317,7 @@ export const {
highlightsDualThemes: HIGHLIGHTS_DUAL_THEMES,
highlightsDualThemesCss: HIGHLIGHTS_DUAL_THEMES_CSS,
highlightsHtml: HIGHLIGHTS_HTML,
highlightsInstall: HIGHLIGHTS_INSTALL,
highlightsLive: HIGHLIGHTS_LIVE,
highlightsStream: HIGHLIGHTS_STREAM,
highlightsStreamPipe: HIGHLIGHTS_STREAM_PIPE,
Expand Down
4 changes: 1 addition & 3 deletions apps/docs/app/(diffs)/docs/HighlightsHighlighter/content.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@ without downloading language grammars.

Install Highlights from npm:

```bash
pnpm add @pierre/highlights
```
<DocsCodeExample {...highlightsInstall} />

The package runs in Node.js, browsers, and Cloudflare Workers. Its conditional
exports select and initialize the matching WebAssembly loader automatically.
Expand Down
76 changes: 76 additions & 0 deletions apps/docs/app/(diffs)/highlights/BenchmarkBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import type { CSSProperties } from 'react';

import styles from './HighlightsRainbow.module.css';
import { cn } from '@/lib/utils';

interface BenchmarkBarStyle extends CSSProperties {
'--benchmark-gradient-end'?: string;
}

interface BenchmarkBarProps {
value: number;
maximum: number;
highlighted?: boolean;
minimumWidth?: number;
scaleFloor?: number;
}

// Reserves a fixed visible baseline, then maps the ratio proportionally across
// the remaining track width.
function getScaledWidth(value: number, maximum: number, scaleFloor: number) {
const ratio = value / maximum;
return `calc(${scaleFloor * (1 - ratio)}px + ${ratio * 100}%)`;
}

// Spans four chart tracks in each direction, then shifts by the exact mirrored
// period so every bar reveals the same calm slice and loops without a jump.
function getBenchmarkGradientStyle(
value: number,
maximum: number
): BenchmarkBarStyle {
const imageScale = 8 * (maximum / value);

return {
backgroundSize: `${imageScale * 100}% 100%`,
'--benchmark-gradient-end': `${(imageScale / (imageScale - 1)) * 100}%`,
};
}

export function BenchmarkBar({
value,
maximum,
highlighted = false,
minimumWidth,
scaleFloor,
}: BenchmarkBarProps) {
const benchmarkGradientStyle =
highlighted && value > 0
? getBenchmarkGradientStyle(value, maximum)
: undefined;
const barClassName = cn(
'h-full rounded-[4px]',
highlighted ? styles.benchmarkRainbow : 'bg-foreground/30'
);

return (
<div aria-hidden="true" className="h-3 w-full">
<div
className={cn(
'transition-[width] duration-500 ease-out motion-reduce:transition-none',
barClassName
)}
style={{
width:
value > 0 && scaleFloor !== undefined
? getScaledWidth(value, maximum, scaleFloor)
: `${(value / maximum) * 100}%`,
minWidth:
value > 0 && minimumWidth !== undefined
? `${minimumWidth}px`
: undefined,
...benchmarkGradientStyle,
}}
/>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.card {
--color-foreground: var(--foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);

background-color: var(--background);
}

/* The card's local `.dark` class must not trigger this outer-page override. */
/* stylelint-disable-next-line selector-pseudo-class-no-unknown */
:global(:root.dark) .card {
background-color: color-mix(in oklch, var(--muted) 50%, transparent);
}
150 changes: 150 additions & 0 deletions apps/docs/app/(diffs)/highlights/HighlightsBenchmarks.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import { BenchmarkBar } from './BenchmarkBar';
import styles from './HighlightsBenchmarkCard.module.css';
import benchmark from '@/public/highlights/benchmark-browser.json';

// Keep the chart tied to the complete recorded browser run. Every engine
// processes the same source, so elapsed times can share one direct scale.
const rows = [...benchmark.rows].sort(
(left, right) => left.milliseconds - right.milliseconds
);
const maximumMilliseconds = Math.max(
...rows.map(({ milliseconds }) => milliseconds)
);

const subTenSecondsFormatter = new Intl.NumberFormat('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
});
const longerSecondsFormatter = new Intl.NumberFormat('en-US', {
minimumFractionDigits: 1,
maximumFractionDigits: 1,
});

function formatDuration(milliseconds: number) {
const seconds = milliseconds / 1000;
const displayedSeconds = (
seconds < 10 ? subTenSecondsFormatter : longerSecondsFormatter
)
.format(seconds)
.replace(/^0/, '');

return `${displayedSeconds}s`;
}

function formatOutput(output: string) {
return output === 'tokens' ? 'Tokens' : output === 'spans' ? 'Spans' : output;
}

export function HighlightsBenchmarks() {
return (
<section
id="performance"
aria-labelledby="highlights-performance"
className="scroll-mt-20 space-y-6 pb-16 md:pb-24"
>
<div className="max-w-2xl">
<h2
id="highlights-performance"
className="text-2xl font-semibold tracking-tight"
>
Compared with other highlighters
</h2>
<p className="text-muted-foreground text-pretty">
Highlights processes 5.56 million characters of JavaScript in{' '}
<strong className="text-foreground font-semibold">.09 s</strong>.
Here’s how it compares with Shu Ding’s{' '}
<a
href="https://github.com/vercel-labs/gpu-lexer"
target="_blank"
rel="noopener noreferrer"
className="styled-link styled-link-muted"
>
gpu-lexer
</a>{' '}
and other syntax highlighters in the same browser run.
</p>
</div>

<figure
className={`${styles.card} dark text-foreground min-w-0 rounded-lg p-4 pb-3 sm:p-8 sm:pb-5 lg:p-10 lg:pb-6`}
>
<figcaption
id="highlights-benchmark-caption"
className="text-muted-foreground mb-5 flex flex-col text-left text-sm md:flex-row md:items-baseline md:gap-1"
>
<strong>Processing three.min.js 10&times;</strong>
<span aria-hidden="true" className="hidden md:inline">
·
</span>
<span>Elapsed time, shorter is faster</span>
</figcaption>
<dl
aria-labelledby="highlights-benchmark-caption"
className="space-y-5 tabular-nums sm:space-y-6"
>
{rows.map(({ name, version, milliseconds, output }) => {
const isHighlights = name === 'Highlights';

return (
<div
key={name}
className="grid grid-cols-[minmax(0,1fr)_4.75rem] items-center gap-x-3 gap-y-1.5 sm:grid-cols-[minmax(0,1fr)_7.5rem]"
>
<dt className="text-muted-foreground col-start-1 row-start-2 flex min-w-0 flex-wrap items-baseline gap-x-1 text-xs leading-tight">
<span className="text-foreground text-sm font-medium">
{name}
</span>
<span>
{version} · {formatOutput(output)}
</span>
</dt>
<dd className="contents">
<div className="col-start-1 row-start-1">
<BenchmarkBar
value={milliseconds}
maximum={maximumMilliseconds}
highlighted={isHighlights}
scaleFloor={6}
/>
</div>
<span
className={
isHighlights
? 'col-start-2 row-span-2 row-start-1 self-center text-right text-2xl font-semibold whitespace-nowrap sm:text-3xl'
: 'text-foreground/50 col-start-2 row-span-2 row-start-1 self-center text-right text-2xl font-normal whitespace-nowrap sm:text-3xl'
}
>
<span className="sr-only">Elapsed time: </span>
{formatDuration(milliseconds)}
</span>
</dd>
</div>
);
})}
</dl>
</figure>

<div className="text-muted-foreground max-w-3xl space-y-2 text-xs leading-relaxed">
<p>
Recorded September 15, 2026 · Chromium 152 · Apple M4 Pro. Median of
three samples, each with one warm-up in a fresh worker. Bar lengths
encode elapsed time; shorter is faster. Every nonzero bar reserves a
6px visibility floor, with elapsed-time percentages mapped
proportionally across the remaining width. Highlights and Shiki return
themed tokens; gpu-lexer returns classified spans. The other libraries
return HTML or a HAST tree. These results measure speed on this
JavaScript workload, not highlighting quality.{' '}
<a
href="/highlights/benchmark-browser.json"
className="styled-link styled-link-muted"
target="_blank"
rel="noopener noreferrer"
>
View versions, samples, and run conditions
</a>
.
</p>
</div>
</section>
);
}
Loading