@text-trace/core: framework-agnostic SVG text trace animation.@text-trace/vue: Vue binding.@text-trace/react: React binding.text-trace-playground: native TypeScript playground for debugging@text-trace/core.
import { createTextTrace } from "@text-trace/core";
const svg = document.querySelector<SVGSVGElement>("svg")!;
const trace = createTextTrace(svg, {
text: "Snowcake47",
ariaLabel: "Snowcake47"
});
await trace.render();React:
import { TextTrace } from "@text-trace/react";
export function Logo() {
return <TextTrace text="Snowcake47" aria-label="Snowcake47" />;
}Vue:
<script setup lang="ts">
import { TextTrace } from "@text-trace/vue";
</script>
<template>
<TextTrace text="Snowcake47" aria-label="Snowcake47" />
</template>Nuxt:
<script setup lang="ts">
import { TextTrace } from "@text-trace/vue";
</script>
<template>
<ClientOnly>
<TextTrace text="Snowcake47" aria-label="Snowcake47" />
</ClientOnly>
</template>@text-trace/core does not bundle font files. The default font presets use CDN URLs for quick demos, so production pages should pass a font hosted by your own app.
Pass one local font with fontSource:
import brandFontUrl from "./brand-font.woff?url";
createTextTrace(svg, {
text: "Snowcake47",
fontKey: "brand",
fontSource: brandFontUrl
});Or pass multiple local fonts with fontSources:
createTextTrace(svg, {
text: "Snowcake47",
fontKey: "brand",
fontSources: {
brand: "/fonts/brand.woff",
display: "/fonts/display.woff"
}
});WOFF2 fonts are supported, but the decompressor must be provided explicitly. Install wawoff2 if you want to use its browser binding, and host that binding with your app too:
import brandFontUrl from "./brand-font.woff2?url";
import wawoff2Url from "wawoff2/build/decompress_binding.js?url";
createTextTrace(svg, {
text: "Snowcake47",
fontKey: "brand",
fontSource: brandFontUrl,
wawoff2Url
});fontUrls is still supported as a backwards-compatible alias for URL-only sources. The CDN presets are exported as TEXT_TRACE_FONT_URLS and TEXT_TRACE_CDN_FONT_URLS for demos or quick experiments.
The SVG uses role="img" and an accessible name by default. The name comes from ariaLabel / aria-label, falling back to text. Core also inserts a <title> element as an SVG fallback.
Use decorative: true when the animation is purely decorative and real text is already present nearby.
Use glyphStyles to override colors for specific character indexes. from is inclusive and to is exclusive.
createTextTrace(svg, {
text: "Snowcake47",
textColor: "#111827",
guideColor: "#111827",
glyphStyles: [
{
at: [8, 9],
style: {
textColor: "#2563eb",
guideColor: "#2563eb"
}
},
{
from: 4,
to: 8,
style: {
textColor: "#dc2626"
}
}
]
});Use getTextTracePaths when you only need the generated glyph paths:
import { getTextTracePaths } from "@text-trace/core";
const result = await getTextTracePaths({
text: "Snowcake47",
fontKey: "inter"
});
console.log(result.viewBox, result.paths.map((path) => path.d));pnpm install
pnpm devBuild all packages and the playground:
pnpm build@text-trace/core accepts a duration option in milliseconds and a timing option with phase positions from 0 to 1:
createTextTrace(svg, {
text: "Hello, world!",
textColor: "#111827",
guideColor: "#111827",
duration: 1000,
timing: {
horizontal: 0,
guide: 0.1,
stroke: 0.4,
fill: 0.8,
erase: 1
},
verticalGuideOvershoot: 28,
verticalGuideProbability: 0.45,
mergeOverlappingShapes: true
});