Skip to content

Jannchie/text-trace

Repository files navigation

Text Trace

Demo

Text Trace demo

Packages

  • @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.

Usage

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>

Fonts

@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.

Accessibility

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.

Per-Glyph Styles

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"
      }
    }
  ]
});

SVG Paths

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));

Development

pnpm install
pnpm dev

Build all packages and the playground:

pnpm build

Timing

@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
});

About

Create Keynote-style SVG text trace animations

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors