NIPs by PolleramaCommunity NIPs, surfaced by trustConnect
npub1q3sle0kvfse...

Profile Themes

Published Jun 28, 2026
kind 16767 · Active Profile Themekind 36767 · Shareable Profile Theme

Profile Themes

This NIP defines custom profile themes as seen in Ditto. It includes shareable theme events (kind 36767) as well as the user's current profile theme (kind 16767). Additionally, kind 0 events are extended with a "shape" field that may be used to mask the user's avatar with an emoji silhouette instead of a standard circle.

Kind 36767: Theme Definition

Summary

Addressable event kind for publishing shareable custom UI themes. A single user may publish multiple themes, each identified by a unique d tag.

A theme consists of colors, optional fonts, and an optional background. Colors are stored in c tags, fonts in f tags, and background in a bg tag.

Event Structure

{
  "kind": 36767,
  "content": "",
  "tags": [
    ["d", "mk-dark-theme"],
    ["c", "#1a1a2e", "background"],
    ["c", "#e0e0e0", "text"],
    ["c", "#6c3ce0", "primary"],
    ["f", "Inter", "https://example.com/inter.woff2", "body"],
    ["f", "Playfair Display", "https://example.com/playfair.woff2", "title"],
    ["bg", "url https://example.com/bg.jpg", "mode cover", "m image/jpeg", "dim 1920x1080"],
    ["title", "MK Dark Theme"],
    ["alt", "Custom theme: MK Dark Theme"]
  ]
}

Content

The content field is unused and MUST be an empty string ("").

Tags

TagRequiredDescription
dYesUnique identifier (slug) for this theme, e.g. "mk-dark-theme"
cYes (×3)Hex color with marker. See [Color Tags](#color-tags).
fNoFont declaration. See [Font Tag](#font-tag).
bgNoBackground media. See [Background Tag](#background-tag).
titleYesHuman-readable theme name
altYesNIP-31 human-readable fallback

Multiple Themes Per User

Since kind 36767 is addressable, a user can publish multiple themes by using different d tag values. Publishing a new event with the same d tag replaces the previous version (this is how editing works).


Kind 16767: Active Profile Theme

Summary

Replaceable event that represents the user's currently active profile theme. Only one per user. When other users visit a profile, they query this kind to determine what theme to display.

Event Structure

{
  "kind": 16767,
  "content": "",
  "tags": [
    ["c", "#1a1a2e", "background"],
    ["c", "#e0e0e0", "text"],
    ["c", "#6c3ce0", "primary"],
    ["f", "Inter", "https://example.com/inter.woff2", "body"],
    ["f", "Playfair Display", "https://example.com/playfair.woff2", "title"],
    ["bg", "url https://example.com/bg.jpg", "mode cover", "m image/jpeg"],
    ["title", "MK Dark Theme"],
    ["alt", "Active profile theme"]
  ]
}

Content

The content field is unused and MUST be an empty string ("").

Tags

TagRequiredDescription
cYes (×3)Hex color with marker. See [Color Tags](#color-tags).
fNoFont declaration. See [Font Tag](#font-tag).
bgNoBackground media. See [Background Tag](#background-tag).
titleNoHuman-readable name for the theme
altYesNIP-31 human-readable fallback

Client Behavior

  • When visiting a profile, clients query { kinds: [16767], authors: [pubkey], limit: 1 } to get the active theme.
  • Clients read the c tags to extract colors, f tags for fonts, and bg tag for the background.
  • Setting a new active theme publishes a new kind 16767 event (replacing the old one).
  • To remove the active theme, publish a kind 5 deletion event targeting kind 16767.

Shared Tag Definitions

The following tag definitions apply to both kind 36767 and kind 16767.

Color Tags

Format: ["c", "#rrggbb", "<marker>"]

IndexRequiredDescription
0YesTag name: "c"
1YesLowercase 6-digit hex color code including the # sign (e.g. "#ff0000")
2YesColor role marker: one of "primary", "text", or "background"
  • All three markers ("primary", "text", "background") MUST be present.
  • Only one c tag per marker is allowed.

Font Tag

Format: ["f", "<family>", "<url>", "<role>"]

IndexRequiredDescription
0YesTag name: "f"
1YesCSS font-family name (e.g. "Inter")
2YesDirect URL to a font file (.woff2, .ttf, .otf)
3YesFont role: "body" or "title"

Roles:

RoleApplies to
"body"All text globally (body, headings, UI elements)
"title"The user's profile display name

Rules:

  • The f tag is optional on the event.
  • At most one f tag per role is allowed (i.e. one body font and one title font).
  • The "body" font tag MUST be ordered before the "title" font tag. This ensures backward-compatible clients that only read the first f tag will pick up the body font.
  • If the URL fails to load, the client SHOULD fall back to a default font gracefully.
  • Clients that do not recognize a role SHOULD ignore that f tag.
  • Legacy events with an f tag that has no role marker (only 3 elements) SHOULD be treated as "body".
  • Variable font files (covering multiple weights in a single file) are preferred.

Background Tag

The bg tag uses an imeta-style variadic format where each entry (after the tag name) is a space-delimited key/value pair.

Format: ["bg", "url <url>", "mode <mode>", "m <mime-type>", ...]

KeyRequiredDescription
urlYesURL to an image or video file
modeYesDisplay mode: "cover" or "tile"
mYesMIME type (e.g. "image/jpeg", "image/png", "video/mp4")
dimNoDimensions in pixels: "<width>x<height>" (e.g. "1920x1080")
blurhashNoBlurhash placeholder string for progressive loading
  • At most one bg tag is allowed per event.
  • Clients MAY choose not to render video backgrounds for performance or bandwidth reasons.
  • Unknown keys SHOULD be ignored for forward compatibility.

Kind 0 Extension: Avatar Shape

Summary

An optional shape property on kind 0 (profile metadata) that controls how the user's avatar is masked/clipped when displayed. The value is an emoji character whose silhouette is used as a mask over the avatar image. When absent, the avatar renders as the standard circle.

Metadata Field

The shape field is added to the JSON content of a kind 0 event alongside standard fields like name, picture, etc. Its value is a single emoji character (including multi-codepoint emoji such as flags, ZWJ sequences, and skin-tone variants).

{
  "kind": 0,
  "content": "{\"name\":\"Luna\",\"shape\":\"🌙\",\"picture\":\"https://example.com/luna.jpg\"}"
}

Client Behavior

  • When shape is absent, clients SHOULD render the avatar as a circle (the current universal default).
  • When shape is a valid emoji, clients SHOULD use the emoji's silhouette as an alpha mask over the avatar image. The specific rendering technique is platform-dependent (see below).
  • When shape is set to an unrecognized or invalid value, clients MUST fall back to a circle. This ensures forward compatibility.
  • The shape field is purely cosmetic and has no protocol-level significance.
  • Clients MAY choose not to support this extension, in which case avatars render as circles as usual.