> ## Documentation Index
> Fetch the complete documentation index at: /llms.txt
> Use this file to discover all available pages before exploring further.

# Typewriter

> Character-by-character text reveal with a deterministic blinking cursor.

# Typewriter

Character-by-character text reveal with a deterministic blinking cursor.

**Typewriter**

```html typewriter.html theme={null}
<!--
  Typewriter - typography primitive for HyperFrames.

  Paste the markup, CSS and script into a composition. Drive the timeline
  integration from a paused GSAP timeline so renders remain deterministic.

  Variables. The script reads each one, falls back to the declared default on
  anything missing or unrecognised, and writes the result either into the
  markup or into a custom property the CSS above consumes. The timeline recipe
  below is untouched by them: it keeps driving one type progress and one caret
  opacity, and these decide what is typed, in what colour, and behind which
  caret.

    - text (string, default hyperframes add typewriter): the line that types
      itself out. It is written into the existing span, so the reveal keeps
      running character by character over whatever is set. Anything that is
      not a non-empty string keeps the shipped line.
    - tone (ink | paper | accent, default ink): colour of both the text and
      the caret. ink is near-black for light frames, paper near-white for dark
      ones, accent rides --brand.
    - caret (line | block | none, default line): caret shape. line is the
      shipped 3px rule, block is a 22px slab in the style of a terminal
      cursor, none removes the caret entirely and closes the gap after the
      text.

  On every default the result is identical to the original: a near-black
  monospace line typing itself out behind a 3px caret.
-->

<div
  class="hf-catalog-typewriter"
  data-composition-variables='[
    { "id": "text", "type": "string", "role": "content", "label": "Text", "description": "Line that types itself out character by character.", "default": "hyperframes add typewriter" },
    { "id": "tone", "type": "enum", "role": "style", "label": "Tone", "description": "Colour of the text and the caret: ink for light frames, paper for dark ones, accent rides --brand.", "default": "ink", "options": [{ "value": "ink", "label": "Ink" }, { "value": "paper", "label": "Paper" }, { "value": "accent", "label": "Accent" }] },
    { "id": "caret", "type": "enum", "role": "style", "label": "Caret", "description": "Caret shape: a thin rule, a terminal-style block, or no caret at all.", "default": "line", "options": [{ "value": "line", "label": "Line" }, { "value": "block", "label": "Block" }, { "value": "none", "label": "None" }] }
  ]'
>
  <span>hyperframes add typewriter</span><i></i>
</div>

<style>
  .hf-catalog-typewriter {
    color: var(--hf-type-color, #18181b);
    font-family: Inter, system-ui, sans-serif;
    font-weight: 900;
    letter-spacing: -0.04em;
  }
  .hf-catalog-typewriter {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font:
      800 42px/1 ui-monospace,
      SFMono-Regular,
      Menlo,
      monospace;
  }
  .hf-catalog-typewriter span {
    display: inline-block;
    max-width: calc(var(--hf-type-progress, 1) * 100%);
    overflow: hidden;
    white-space: nowrap;
  }
  .hf-catalog-typewriter i {
    display: var(--hf-caret-display, block);
    width: var(--hf-caret-width, 3px);
    height: 42px;
    border-radius: 999px;
    background: var(--hf-type-color, #18181b);
    opacity: var(--hf-caret-opacity, 1);
  }
</style>

<script>
  (function () {
    "use strict";

    var vars =
      window.__hyperframes && window.__hyperframes.getVariables
        ? window.__hyperframes.getVariables()
        : {};

    // Unrecognised overrides return to their declared defaults.
    var tones = {
      ink: "#18181b",
      paper: "#fafafa",
      accent: "var(--brand, #34d399)",
    };
    var carets = {
      line: { width: "3px", display: "block" },
      block: { width: "22px", display: "block" },
      none: { width: "3px", display: "none" },
    };

    function pick(table, value, fallback) {
      return Object.prototype.hasOwnProperty.call(table, value) ? value : fallback;
    }

    var tone = tones[pick(tones, vars.tone, "ink")];
    var caret = carets[pick(carets, vars.caret, "line")];
    var text = typeof vars.text === "string" && vars.text !== "" ? vars.text : null;

    var roots = document.querySelectorAll(".hf-catalog-typewriter");
    for (var i = 0; i < roots.length; i += 1) {
      var root = roots[i];
      root.style.setProperty("--hf-type-color", tone);
      root.style.setProperty("--hf-caret-width", caret.width);
      root.style.setProperty("--hf-caret-display", caret.display);

      var line = root.querySelector("span");
      if (text !== null && line) {
        line.textContent = text;
      }
    }
  })();
</script>

<!--
  Timeline integration:
  tl.fromTo('.hf-catalog-typewriter', { '--hf-type-progress': 0 }, { '--hf-type-progress': 1, duration: 1.2, ease: 'steps(28)' }, startTime); tl.to('.hf-catalog-typewriter i', { '--hf-caret-opacity': 0.2, duration: 0.24, repeat: 7, yoyo: true, ease: 'none' }, startTime + 0.2);
-->
```

Copy npx hyperframes add typewriter to the clipboard

That writes one file: `compositions/components/typewriter.html`.

## Paste it into your composition

Open `compositions/components/typewriter.html` and copy what is inside into your own composition.
The file opens with a comment header that walks you through it.

A component has no size or duration of its own. It takes both from the composition
you paste it into.

## Variables

Every one of these has a default, so the piece works untouched. Set the ones you
want to change on the element:

| Variable | Default | Accepts | What it does |
| - | - | - | - |
| `text` | `hyperframes add typewriter` | string | Line that types itself out character by character. |
| `tone` | `ink` | `ink`, `paper`, `accent` | Colour of the text and the caret: ink for light frames, paper for dark ones, accent rides --brand. |
| `caret` | `line` | `line`, `block`, `none` | Caret shape: a thin rule, a terminal-style block, or no caret at all. |

Set them with `data-variable-values` on the element that mounts it. These are the
defaults, so this behaves exactly like the preview above until you change one:

```html wrap theme={null}
<div
  data-composition-id="typewriter"
  data-composition-src="compositions/components/typewriter.html"
  data-variable-values='{"text":"hyperframes add typewriter","tone":"ink","caret":"line"}'
></div>
```

Tagged `video-primitive` `motion-primitive` `typography`.

## Related topics

* [Browse the complete Catalog](/catalog.md)
* [Add assets and Catalog items in the browser editor](/studio/assets-and-blocks.md)
* [Put blocks together into one video](/concepts/compositions.md)


## Related topics

- [Marker Checklist Card](/catalog/components/marker-checklist-card.md)
- [Comment Cards](/catalog/blocks/yt-comment-card.md)
- [Streaming Text](/catalog/components/streaming-text.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.