Skip to content
react-wrapped

Marks

A mark is the logo in the bottom right corner of the frame: what the year is about. It is not pasted on like a sticker. The stage presses it out of the palette's own colours, lit from the top left, so it belongs to the frame in every theme. Set it with subject.mark, from the marks package or from a path of your own.

Pick a mark and look at the bottom right corner of the frame.

Installation

bash
pnpm add @react-wrapped/marks @react-wrapped/core

The package is data only: path strings, no components and no stylesheet. @react-wrapped/kit includes it.

Usage

tsx
import type { WrappedSubject } from "@react-wrapped/core";
import { markOf } from "@react-wrapped/marks";

export const subject: WrappedSubject = {
  handle: "ada",
  name: "Ada",
  avatarUrl: null,
  caption: "Orbit Wrapped",
  period: "Oct 2025 to Oct 2026",
  mark: markOf("github"),
};

Leave mark out and the corner keeps its plain pressed discs. The component below is the preview above: every mark as a small SVG, and the picked one pressed into a player's frame.

mark-picker.tsx
import { type CSSProperties, useMemo, useState } from "react";

import type { SceneInstance, WrappedConfig, WrappedSubject } from "@react-wrapped/core";
import { type MarkId, markOf, MARKS } from "@react-wrapped/marks";
import { WrappedPlayer } from "@react-wrapped/player";

const ROOM: CSSProperties = { display: "grid", height: 520, marginTop: 16 };

const MARK_IDS = Object.keys(MARKS) as MarkId[];

export function MarkPicker({
  subject,
  seed,
  scenes,
  config,
}: {
  subject: WrappedSubject;
  seed: string;
  scenes: readonly SceneInstance[];
  config: WrappedConfig;
}) {
  const [picked, setPicked] = useState<MarkId>(MARK_IDS[0]);
  // The mark is part of who the video is about. `markOf` hands back one path, or one per shape of the mark.
  const marked = useMemo(() => ({ ...subject, mark: markOf(picked) }), [subject, picked]);
  return (
    <div>
      <div role="group" aria-label="Marks">
        {MARK_IDS.map((id) => (
          <button key={id} type="button" aria-pressed={id === picked} onClick={() => setPicked(id)}>
            {/* Every mark is path data in a 24 by 24 box, so it draws as a plain SVG too. */}
            <svg viewBox="0 0 24 24" width="20" height="20" aria-hidden>
              <path d={MARKS[id]} fill="currentColor" />
            </svg>
            {id}
          </button>
        ))}
      </div>
      <div style={ROOM}>
        <WrappedPlayer subject={marked} seed={seed} scenes={scenes} config={config} />
      </div>
    </div>
  );
}

Your own mark

A mark is SVG path data drawn in a box of 24 by 24: the d of a path. Pass your logo's path as a string. For a logo made of several shapes that should stay apart, pass an array with one path per shape, and each is pressed out on its own.

tsx
import type { StageMark } from "@react-wrapped/core";

// One shape: a ring with a dot in it. Fill is not needed: the stage colours and lights the mark itself.
export const ORBIT: StageMark =
  "M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm0 3a7 7 0 1 1 0 14 7 7 0 0 1 0-14zm0 4a3 3 0 1 0 0 6 3 3 0 0 0 0-6z";

// Two shapes kept apart: each gets its own edge.
export const TWO_BARS: StageMark = ["M4 4h6v16H4z", "M14 9h6v11h-6z"];
  • Use a single colour version of the logo. Colours in the artwork are ignored.
  • Fill the box. A logo drawn small in the middle of its 24 by 24 comes out small in the corner.
  • Keep shapes bold. Hairlines vanish once the mark is pressed into the background.

What the package exports

ExportWhat it is
MARKSEvery mark by id, each as one path string. There are 14.
MarkIdThe ids, as a type.
markOf(id)A mark ready for subject.mark: one path, or one path per shape for a mark made of several.
MARK_LAYERSThe marks that are more than one flat shape, back to front, each layer with its own fill for drawing the mark in its own colours. Today: search-console.

Every mark

The ids in MARKS: github, claude, openai, cursor, npm, stripe, lemon-squeezy, polar, search-console, hacker-news, gemini, x, bluesky, reddit. The picker at the top of this page draws each of them.

Last updated . Read this page as Markdown.