Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
bcac221
docs: clarify Markdown and Highlight landing pages
tannerlinsley Oct 1, 2026
78f9d5e
Preserve informal landing voice and route writing skills
tannerlinsley Oct 1, 2026
a9150a3
Show Markdown and Highlight v1 docs in version picker
tannerlinsley Oct 1, 2026
8a84921
Type version-route smoke test responses
tannerlinsley Oct 1, 2026
af6f38d
Use approved streaming landing copy
tannerlinsley Oct 1, 2026
288f771
Use approved conversational syntax heading
tannerlinsley Oct 1, 2026
10f250b
Use approved syntax section description
tannerlinsley Oct 1, 2026
633c94a
Use approved two-line Markdown safety heading
tannerlinsley Oct 1, 2026
8246513
Use approved Markdown safety description
tannerlinsley Oct 1, 2026
2b4f5d2
Keep two-line safety heading within narrow screens
tannerlinsley Oct 1, 2026
984c407
Use approved browser-parsing marketing copy
tannerlinsley Oct 1, 2026
3160b66
Use approved highlighting integration and hero copy
tannerlinsley Oct 1, 2026
62fcac1
Use approved selective-language copy
tannerlinsley Oct 1, 2026
bd3148b
Use approved CSS theme marketing copy
tannerlinsley Oct 1, 2026
05794a5
Use approved mixed-language section copy
tannerlinsley Oct 1, 2026
ba88c02
Use approved annotation wordplay and explanation
tannerlinsley Oct 1, 2026
4c90ecf
Remove unnecessary Highlight performance marketing section
tannerlinsley Oct 1, 2026
8c6b542
Remove duplicated highlighter comparison section
tannerlinsley Oct 1, 2026
e64e758
Use approved Markdown integration copy
tannerlinsley Oct 1, 2026
84ab22a
Use final approved integration flexibility wording
tannerlinsley Oct 1, 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
19 changes: 19 additions & 0 deletions .agents/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,22 @@ Inspired by https://github.com/DietrichGebert/ponytail
- [UI Style Guide](./ui-style.md): Visual design principles for 2026
- [Workflow](./workflow.md): Build commands, debugging, Playwright
- [Analytics](./analytics.md): GA4 event taxonomy, funnel definition, custom dimensions

## Writing and documentation

Use `tanner-writing-style` for public front-page headings and marketing copy,
and `editorial-restraint` for clarity and grounded claims, when those skills
are available. Front-page copy should be personable, punchy, and informal.
Lead with the benefit and preserve supplied sales emphasis while polishing
awkward phrasing. Concrete grounding does not require API names in every
headline or subhead; examples and linked documentation can carry that detail.

Keep API references and technical guides precise and approachable. Preserve
identifiers and explain actual behavior and relevant limitations without
imposing the marketing voice on reference documentation.

Read relevant implementation, contracts, and examples before changing product
claims. Keep capabilities, measurements, guarantees, and comparisons accurate.
If a consequential claim is unsupported, offer a stronger accurate alternative
briefly and continue within the requested scope. Subjective praise and useful
promotional hooks do not need to become dry technical prose.
170 changes: 33 additions & 137 deletions src/components/landing/HighlightLanding.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as React from 'react'
import { Link } from '@tanstack/react-router'
import { ArrowRightIcon, CheckIcon } from '@phosphor-icons/react'
import { ArrowRightIcon } from '@phosphor-icons/react'

import { LibraryWordmark } from '~/components/LibraryWordmark'
import { getLibrary } from '~/libraries'
Expand All @@ -24,24 +24,24 @@ const bundleProfiles = [
name: 'core',
detail: 'no languages',
size: '1.82 KB',
width: 'w-[22%]',
width: 'w-[17%]',
},
{
name: 'tsx',
detail: 'core + TSX',
size: '4.03 KB',
width: 'w-[49%]',
size: '4.29 KB',
width: 'w-[40%]',
},
{
name: 'docs',
detail: '9 languages',
size: '5.97 KB',
width: 'w-[72%]',
size: '6.22 KB',
width: 'w-[58%]',
},
{
name: 'all',
detail: '26 languages',
size: '8.29 KB',
detail: '30 languages',
size: '10.79 KB',
width: 'w-full',
},
]
Expand All @@ -60,7 +60,7 @@ export default function HighlightLanding() {
<LibraryLandingShell
libraryId="highlight"
headline="Highlighting built for the web."
description="Register the languages your docs use, highlight synchronously, and ship one compact semantic HTML tree that every theme can share."
description="Fast syntax highlighting that keeps your bundles small and your code looking good."
hero={
<CodeLab
isLightTheme={isLightTheme}
Expand All @@ -73,8 +73,8 @@ export default function HighlightLanding() {
<LandingSection tone="accent">
<div className="grid items-center gap-12 lg:grid-cols-[0.7fr_1.3fr] lg:gap-16">
<LandingSectionIntro
title="The registry is the bundle plan."
body="The core knows no languages. Direct imports make the site’s language set explicit and let the bundler discard everything else."
title="Only pay for the languages you need."
body="Start with a tiny core, bring the languages you actually use, and leave the rest out of your bundle. Change your mind about the colors? CSS has you covered."
/>
<BundleDial />
</div>
Expand All @@ -83,15 +83,15 @@ export default function HighlightLanding() {
<LandingSection tone="ink">
<div className="grid items-center gap-12 lg:grid-cols-[0.76fr_1.24fr] lg:gap-16">
<LandingSectionIntro
title="Change the palette. Keep the markup."
body="Tokens carry stable semantic classes instead of theme colors. CSS variables recolor the same tree without a second highlighting pass."
title="A fresh coat of paint."
body="Go dark, go light, or offer a whole menu of themes with CSS variables, all using the same markup without shipping extra copies or highlighting everything again."
/>
<ThemeContract />
</div>
<div className="mt-12 grid items-center gap-12 border-t border-border-subtle pt-12 lg:grid-cols-[0.7fr_1.3fr] lg:gap-16">
<LandingSectionIntro
title="Web languages rarely stay in their lane."
body="HTML, Vue, Svelte, EJS, and Markdown delegate embedded regions only when the nested language is registered. JavaScript and TypeScript handle their own template interpolation."
title="Go ahead, mix your languages."
body="Put JavaScript in your HTML, CSS in your Vue components, or mix them together like you normally do, and we’ll highlight the languages right where they are."
/>
<div>
<EmbeddedLanguageMap />
Expand All @@ -115,44 +115,17 @@ export default function HighlightLanding() {
<div className="grid items-center gap-12 lg:grid-cols-[1.2fr_0.8fr] lg:gap-16">
<AnnotationPanel />
<LandingSectionIntro
title="Annotate the lesson, not the token stream."
body="Highlight lines, exact character ranges, insertions, deletions, focus, errors, and warnings without changing the source or tokenizer."
title="Dim the lights and get your point across… or underlined, too."
body="Make changed lines pop, fade everything around them, or underline the bit you’re explaining with annotations you control and CSS that makes it yours."
/>
</div>
<div className="mt-12 grid items-center gap-12 border-t border-border-subtle pt-12 lg:grid-cols-[0.7fr_1.3fr] lg:gap-16">
<LandingSectionIntro
title="Tuned against the docs it will render."
body="Release checks cover source preservation, deterministic HTML, bundle sizes, and throughput across more than 10,000 code blocks, including numbered and decorated blocks."
/>
<BenchmarkTape />
</div>
</LandingSection>

<LandingSection tone="accent">
<div className="grid items-start gap-12 lg:grid-cols-[0.72fr_1.28fr] lg:gap-16">
<LandingSectionIntro
title="A docs highlighter is not an editor highlighter."
body="Highlight is optimized for known web languages and compact page output, not TextMate completeness, automatic detection, or incremental editor state."
/>
<div className="overflow-hidden rounded-xl border border-border-subtle bg-background-surface">
<ChoiceRow
name="TanStack Highlight"
useWhen="Known docs languages, compact HTML, CSS themes, and annotations matter most."
/>
<ChoiceRow
name="Shiki"
useWhen="TextMate and VS Code fidelity, broad language coverage, and editor-grade themes are the job."
/>
<ChoiceRow
name="Sugar High"
useWhen="The smallest straightforward JavaScript and TypeScript path is enough."
/>
</div>
</div>
<div className="mt-12 grid items-center gap-12 border-t border-border-subtle pt-12 lg:grid-cols-[1.08fr_0.92fr] lg:gap-16">
<div className="grid items-center gap-12 lg:grid-cols-[1.08fr_0.92fr] lg:gap-16">
<LandingSectionIntro
title="Drop it into Markdown without hiding the language set."
body="TanStack Markdown, Remark, Rehype, and Octane MDX adapters all take an explicit highlighter, so you control which languages ship."
title="BYO Markdown."
body="Add syntax highlighting to TanStack Markdown, Remark, Rehype, Octane MDX, or whatever you’re building with, using a ready-made integration or wiring it up your way."
/>
<Link
to="/markdown/$version"
Expand All @@ -163,8 +136,8 @@ export default function HighlightLanding() {
<LibraryWordmark library={markdownLibrary} />
</div>
<p className="mt-3 text-ds-body-sm text-text-primary/55">
A serializable document model that keeps highlighting at an
explicit boundary.
Parse and render Markdown, then add syntax highlighting with a
callback.
</p>
<span className="mt-6 inline-flex items-center gap-2 text-ds-label-md">
Explore Markdown
Expand Down Expand Up @@ -359,7 +332,7 @@ function BundleDial() {
return (
<div className="rounded-xl border border-border-subtle bg-background-surface p-5 sm:p-6">
<div className="flex items-center justify-between gap-4 border-b border-border-subtle pb-3 font-ds-mono text-ds-mono-caps-xs uppercase text-text-primary/45">
<span>0.0.11 · gzip · KB = 1,000 bytes</span>
<span>1.0.0 · gzip · KB = 1,000 bytes</span>
</div>
<div className="mt-5 space-y-5">
{bundleProfiles.map((profile) => (
Expand All @@ -379,6 +352,16 @@ function BundleDial() {
</div>
))}
</div>
<p className="mt-5 border-t border-border-subtle pt-3 text-ds-body-xs text-text-primary/45">
Minified browser source-entry builds, measured October 1, 2026 with Node
26.3.1. Sizes vary with imports and build tools.{' '}
<a
href="https://github.com/TanStack/highlight/blob/v1.0.0/scripts/measure-size.mjs"
className="underline"
>
Measurement details
</a>
</p>
</div>
)
}
Expand Down Expand Up @@ -550,90 +533,3 @@ function AnnotatedLine({
</div>
)
}

function BenchmarkTape() {
return (
<div className="overflow-hidden rounded-xl border border-border-subtle bg-background-surface">
<div className="grid grid-cols-[1fr_auto_auto] gap-4 border-b border-border-subtle px-4 py-3 font-ds-mono text-ds-mono-caps-xs uppercase text-text-primary/45">
<span>334 docs fixtures</span>
<span>warmed time</span>
<span>HTML</span>
</div>
<BenchmarkRow
name="TanStack Highlight"
time="4.6 ms"
output="365 KiB"
emphasis
/>
<BenchmarkRow
name="Shiki 4.3.1"
time="182 ms"
output="1,257 KiB"
emphasis={false}
/>
<p className="border-t border-border-subtle px-4 py-3 text-ds-body-xs text-text-primary/45">
Local measurements from the{' '}
<Link
to="/$libraryId/$version/docs/$"
params={{
libraryId: 'highlight',
version: 'latest',
_splat: 'comparison',
}}
className="underline"
>
project benchmark report
</Link>
. Shiki initialization and language loading are measured separately;
EJS, ENV, and TSRX use its plaintext fallback. Timings vary by machine,
and the tools don’t provide equivalent grammar accuracy.
</p>
</div>
)
}

function BenchmarkRow({
emphasis,
name,
output,
time,
}: {
emphasis: boolean
name: string
output: string
time: string
}) {
return (
<div
className={`grid grid-cols-[1fr_auto_auto] gap-4 border-b border-border-subtle px-4 py-4 font-ds-mono text-ds-mono-xs last:border-b-0 ${emphasis ? 'bg-[color:rgb(var(--landing-glow)/0.1)]' : ''}`}
>
<span
className={
emphasis
? 'font-black text-[var(--landing-accent-bright)]'
: 'text-text-primary/55'
}
>
{name}
</span>
<span className="font-black">{time}</span>
<span className="w-20 text-right font-black">{output}</span>
</div>
)
}

function ChoiceRow({ name, useWhen }: { name: string; useWhen: string }) {
return (
<div className="grid gap-2 border-b border-border-subtle px-5 py-5 last:border-b-0 sm:grid-cols-[12rem_1fr] sm:gap-5">
<div className="inline-flex items-center gap-2 font-black">
<CheckIcon
size={15}
aria-hidden="true"
className="text-[var(--landing-accent-bright)]"
/>{' '}
{name}
</div>
<p className="text-ds-body-sm text-text-primary/55">{useWhen}</p>
</div>
)
}
2 changes: 1 addition & 1 deletion src/components/landing/LibraryLanding.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@ export function LandingSectionIntro({
centered?: boolean
eyebrow?: string
icon?: React.ReactNode
title: string
title: React.ReactNode
}) {
return (
<div className={centered ? 'mx-auto max-w-208 text-center' : 'max-w-168'}>
Expand Down
Loading
Loading