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.
pnpm add @privacy-thing/brandThe 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.
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;
}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 |
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"],
});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.
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 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.
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.
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.
- The complete logo uses
currentColor. --privacy-thing-cursor-colorcan 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 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.