Skip to content

Repository files navigation

@privacy-thing/brand

Framework-agnostic Privacy Thing logos and the animated Thing runtime.

The package exposes a native Web Component. It does not depend on React, Vue, Tailwind, or an application-specific design system.

Install

pnpm add @privacy-thing/brand

The package is published publicly on npm, so it needs no registry configuration and no token. Releases are built and published from this repository's CI with provenance, so npm records which workflow and commit produced each tarball.

The tarball ships the compiled dist, so nothing is built when you install it.

Register and render

Registration is explicit so importing the package remains safe during SSR.

import { definePrivacyThingLogo } from "@privacy-thing/brand";

definePrivacyThingLogo();
<privacy-thing-logo
  variant="horizontal"
  label="Privacy Thing"
  animate-cursor
  animate-icon
  hover-reaction="boop"
  tap-reaction="jelly"
></privacy-thing-logo>

The element is inline-sized. Set its size and color with normal CSS:

privacy-thing-logo {
  width: 155px;
  color: #111115;
  --privacy-thing-cursor-color: #0f8d70;
}

Variants

variant Content Animated Thing support Cursor support
thing Thing only Direct look-around, blink, tracking and hover controls No
icon Static signet No No
logo Stacked signet and wordmark animate-icon animate-cursor
horizontal Horizontal signet and wordmark animate-icon animate-cursor
wordmark Horizontal wordmark only No No

Attributes and properties

Boolean options are enabled by attribute presence. Every option is also available as a typed JavaScript property and through configure().

Attribute Property Values / default Purpose
variant variant variants above / logo Selects the visual composition.
label label string / Privacy Thing Accessible name when the element is not decorative.
decorative decorative boolean / false Removes the image role and sets aria-hidden.
color color CSS color / inherited currentColor Sets the signet and wordmark color.
animate-cursor animateCursor boolean / false Blinks the wordmark cursor.
animate-icon animateIcon boolean / false Replaces a composite logo's static signet with animated Thing.
pose pose pose below / idle Selects a fixed gaze or the Zz scene.
look-around lookAround boolean / false Enables the independent look-around loop for thing.
look-around-directions lookAroundDirections two directions / west east Selects the two directional poses visited by the look-around loop.
blink blink boolean / false Enables the independent blink loop for thing.
track-pointer trackPointer boolean / false Tracks document pointer direction in eight sectors.
hover-reaction hoverReaction none, boop / none Boops on entry, then directly follows the pointer until exit.
tap-reaction tapReaction none, jelly / none Reacts to touch taps on Thing and across its owner document.
reduce-motion reduceMotion boolean / false Disables SMIL motion and pointer tracking.

Composite logo and horizontal variants enable look-around and blink when animate-icon is set. Their pose, look-around directions, pointer tracking, hover reaction, timing, tap reaction, and reduced-motion rules still apply.

look-around-directions accepts exactly two space-separated directional poses. idle, zz, missing values, and extra values fall back to west east. JavaScript consumers can use the typed tuple instead:

logo?.configure({
  lookAroundDirections: ["south-west", "south-east"],
});

Touch reaction

tap-reaction="jelly" listens only for a primary touch pointer. A tap directly on Thing produces a centered squash-and-wobble. A tap elsewhere in the owner document sends a directional impulse from the tap position toward Thing. Mouse, pen, long presses, scrolling, and gestures that move more than 12 CSS pixels do not trigger the reaction. The listener is passive and never changes the native behavior of the tapped page element.

Jelly is disabled by reduce-motion and by the authoritative zz pose. Its motion is deliberately part of the brand runtime rather than a host-configurable timing surface.

Poses

idle, north, north-east, east, south-east, south, south-west, west, north-west, and zz.

The zz pose has priority over pointer tracking and hover behavior. It disables look-around, blinking, and the wordmark cursor while rendering the sleep scene.

Timing

Timing is an object, so it is configured as a JavaScript property rather than an HTML attribute:

const logo = document.querySelector("privacy-thing-logo");

logo?.configure({
  timing: {
    lookAround: { cycleMs: 60_000, holdMs: 1_064 },
    blink: { cycleMs: 15_000, durationMs: 320, eyeStaggerMs: 25 },
    pointer: {
      directionDelayMs: 2_700,
      idleHoldMs: 7_500,
      transitionMs: 420,
      inactivityTimeoutMs: 15_000,
    },
  },
});

Pointer tracking always passes through idle when changing direction. The direction delay controls when a new direction is accepted, the idle hold keeps the neutral pose between directions, and the inactivity timeout returns Thing to neutral when the pointer stops moving. During the post-boop hover phase, Thing follows the pointer directly without those delays or the center dead zone.

JavaScript API

import {
  createPrivacyThingLogo,
  definePrivacyThingLogo,
  type PrivacyThingLogoElement,
} from "@privacy-thing/brand";

definePrivacyThingLogo();

const logo = document.querySelector<PrivacyThingLogoElement>(
  "privacy-thing-logo",
);
if (logo) {
  logo.variant = "horizontal";
  logo.animateIcon = true;
  logo.animateCursor = true;
  logo.hoverReaction = "boop";
  logo.tapReaction = "jelly";
  logo.configure({
    trackPointer: true,
    timing: { pointer: { inactivityTimeoutMs: 15_000 } },
  });
}

const icon = createPrivacyThingLogo({
  variant: "thing",
  pose: "north-east",
  lookAround: true,
  blink: true,
});

definePrivacyThingLogo() is idempotent and accepts an optional custom element registry. createPrivacyThingLogo() creates and configures an element in an optional owner document.

Event commands

Dispatch privacy-thing:command directly on a logo element to control its animated Thing without replacing the SVG. The package helper keeps the event name and payload typed:

import {
  dispatchPrivacyThingCommand,
  type PrivacyThingLogoElement,
} from "@privacy-thing/brand";

const logo = document.querySelector<PrivacyThingLogoElement>(
  "privacy-thing-logo",
);

if (logo) {
  dispatchPrivacyThingCommand(logo, {
    type: "look",
    direction: "north-east",
  });

  dispatchPrivacyThingCommand(logo, { type: "reset" });
}

look accepts idle and all eight directions. It uses the configured pose transition and can be superseded by later pointer interaction. reset returns to the declarative pose. A configured zz scene remains authoritative and ignores look commands.

Styling and composition

  • The complete logo uses currentColor.
  • --privacy-thing-cursor-color can color only #cursor.
  • ::part(base), ::part(thing), and ::part(svg) expose the principal Shadow DOM layers for deliberate host-side styling.
  • Link behavior is intentionally not built in. Wrap the element in an <a> so the host application retains navigation and focus semantics.
  • The open Shadow DOM isolates the animation styles while remaining inspectable.

Static assets and SSR

Static source SVG files are exported under @privacy-thing/brand/assets/*. renderStaticLogo(variant) returns their normalized markup for server-rendered fallbacks. The thing variant uses the static icon as its fallback.

Importing the package does not read window, document, or customElements. Call registration only in a browser lifecycle. The animation uses native SVG SMIL in Chromium and Firefox; reduce-motion provides the explicit static mode.

About

Framework-agnostic Privacy Thing brand components and animation runtime.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages