{"id":"68f30820511acc176f0cd0a96bfc7899bc2ea07e5c411b41fa5509e4c713ce2e","pubkey":"0461fcbecc4c3374439932d6b8f11269ccdb7cc973ad7a50ae362db135a474dd","created_at":1782666455,"kind":30817,"tags":[["d","profile-themes"],["title","Profile Themes"],["alt","Nostr Implementation Possibility: Profile Themes"],["k","16767","Active Profile Theme"],["k","36767","Shareable Profile Theme"],["client","NostrHub"]],"content":"# Profile Themes\n\nThis NIP defines custom profile themes as seen in [Ditto](https://ditto.pub/). 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.\n\n## Kind 36767: Theme Definition\n\n### Summary\n\nAddressable event kind for publishing shareable custom UI themes. A single user may publish multiple themes, each identified by a unique `d` tag.\n\nA 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.\n\n### Event Structure\n\n```json\n{\n  \"kind\": 36767,\n  \"content\": \"\",\n  \"tags\": [\n    [\"d\", \"mk-dark-theme\"],\n    [\"c\", \"#1a1a2e\", \"background\"],\n    [\"c\", \"#e0e0e0\", \"text\"],\n    [\"c\", \"#6c3ce0\", \"primary\"],\n    [\"f\", \"Inter\", \"https://example.com/inter.woff2\", \"body\"],\n    [\"f\", \"Playfair Display\", \"https://example.com/playfair.woff2\", \"title\"],\n    [\"bg\", \"url https://example.com/bg.jpg\", \"mode cover\", \"m image/jpeg\", \"dim 1920x1080\"],\n    [\"title\", \"MK Dark Theme\"],\n    [\"alt\", \"Custom theme: MK Dark Theme\"]\n  ]\n}\n```\n\n### Content\n\nThe `content` field is unused and MUST be an empty string (`\"\"`).\n\n### Tags\n\n| Tag     | Required | Description                                                                           |\n|---------|----------|---------------------------------------------------------------------------------------|\n| `d`     | Yes      | Unique identifier (slug) for this theme, e.g. `\"mk-dark-theme\"`                      |\n| `c`     | Yes (×3) | Hex color with marker. See [Color Tags](#color-tags).                                 |\n| `f`     | No       | Font declaration. See [Font Tag](#font-tag).                                          |\n| `bg`    | No       | Background media. See [Background Tag](#background-tag).                              |\n| `title` | Yes      | Human-readable theme name                                                             |\n| `alt`   | Yes      | NIP-31 human-readable fallback                                                        |\n\n### Multiple Themes Per User\n\nSince 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).\n\n---\n\n## Kind 16767: Active Profile Theme\n\n### Summary\n\nReplaceable 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.\n\n### Event Structure\n\n```json\n{\n  \"kind\": 16767,\n  \"content\": \"\",\n  \"tags\": [\n    [\"c\", \"#1a1a2e\", \"background\"],\n    [\"c\", \"#e0e0e0\", \"text\"],\n    [\"c\", \"#6c3ce0\", \"primary\"],\n    [\"f\", \"Inter\", \"https://example.com/inter.woff2\", \"body\"],\n    [\"f\", \"Playfair Display\", \"https://example.com/playfair.woff2\", \"title\"],\n    [\"bg\", \"url https://example.com/bg.jpg\", \"mode cover\", \"m image/jpeg\"],\n    [\"title\", \"MK Dark Theme\"],\n    [\"alt\", \"Active profile theme\"]\n  ]\n}\n```\n\n### Content\n\nThe `content` field is unused and MUST be an empty string (`\"\"`).\n\n### Tags\n\n| Tag     | Required | Description                                                                           |\n|---------|----------|---------------------------------------------------------------------------------------|\n| `c`     | Yes (×3) | Hex color with marker. See [Color Tags](#color-tags).                                 |\n| `f`     | No       | Font declaration. See [Font Tag](#font-tag).                                          |\n| `bg`    | No       | Background media. See [Background Tag](#background-tag).                              |\n| `title` | No       | Human-readable name for the theme                                                     |\n| `alt`   | Yes      | NIP-31 human-readable fallback                                                        |\n\n### Client Behavior\n\n- When visiting a profile, clients query `{ kinds: [16767], authors: [pubkey], limit: 1 }` to get the active theme.\n- Clients read the `c` tags to extract colors, `f` tags for fonts, and `bg` tag for the background.\n- Setting a new active theme publishes a new kind 16767 event (replacing the old one).\n- To remove the active theme, publish a kind 5 deletion event targeting kind 16767.\n\n---\n\n## Shared Tag Definitions\n\nThe following tag definitions apply to both kind 36767 and kind 16767.\n\n### Color Tags\n\nFormat: `[\"c\", \"#rrggbb\", \"<marker>\"]`\n\n| Index | Required | Description                                                                                   |\n|-------|----------|-----------------------------------------------------------------------------------------------|\n| 0     | Yes      | Tag name: `\"c\"`                                                                               |\n| 1     | Yes      | Lowercase 6-digit hex color code including the `#` sign (e.g. `\"#ff0000\"`)                    |\n| 2     | Yes      | Color role marker: one of `\"primary\"`, `\"text\"`, or `\"background\"`                            |\n\n- All three markers (`\"primary\"`, `\"text\"`, `\"background\"`) MUST be present.\n- Only one `c` tag per marker is allowed.\n\n### Font Tag\n\nFormat: `[\"f\", \"<family>\", \"<url>\", \"<role>\"]`\n\n| Index | Required | Description                                                                                   |\n|-------|----------|-----------------------------------------------------------------------------------------------|\n| 0     | Yes      | Tag name: `\"f\"`                                                                               |\n| 1     | Yes      | CSS `font-family` name (e.g. `\"Inter\"`)                                                       |\n| 2     | Yes      | Direct URL to a font file (`.woff2`, `.ttf`, `.otf`)                                          |\n| 3     | Yes      | Font role: `\"body\"` or `\"title\"`                                                              |\n\n**Roles:**\n\n| Role      | Applies to                                      |\n|-----------|--------------------------------------------------|\n| `\"body\"`  | All text globally (body, headings, UI elements)  |\n| `\"title\"` | The user's profile display name                  |\n\n**Rules:**\n\n- The `f` tag is optional on the event.\n- At most one `f` tag per role is allowed (i.e. one body font and one title font).\n- 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.\n- If the URL fails to load, the client SHOULD fall back to a default font gracefully.\n- Clients that do not recognize a role SHOULD ignore that `f` tag.\n- Legacy events with an `f` tag that has no role marker (only 3 elements) SHOULD be treated as `\"body\"`.\n- Variable font files (covering multiple weights in a single file) are preferred.\n\n### Background Tag\n\nThe `bg` tag uses an `imeta`-style variadic format where each entry (after the tag name) is a space-delimited key/value pair.\n\nFormat: `[\"bg\", \"url <url>\", \"mode <mode>\", \"m <mime-type>\", ...]`\n\n| Key         | Required | Description                                                                              |\n|-------------|----------|------------------------------------------------------------------------------------------|\n| `url`       | Yes      | URL to an image or video file                                                            |\n| `mode`      | Yes      | Display mode: `\"cover\"` or `\"tile\"`                                                      |\n| `m`         | Yes      | MIME type (e.g. `\"image/jpeg\"`, `\"image/png\"`, `\"video/mp4\"`)                            |\n| `dim`       | No       | Dimensions in pixels: `\"<width>x<height>\"` (e.g. `\"1920x1080\"`)                          |\n| `blurhash`  | No       | Blurhash placeholder string for progressive loading                                      |\n\n- At most one `bg` tag is allowed per event.\n- Clients MAY choose not to render video backgrounds for performance or bandwidth reasons.\n- Unknown keys SHOULD be ignored for forward compatibility.\n\n---\n\n## Kind 0 Extension: Avatar Shape\n\n### Summary\n\nAn 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.\n\n### Metadata Field\n\nThe `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).\n\n```json\n{\n  \"kind\": 0,\n  \"content\": \"{\\\"name\\\":\\\"Luna\\\",\\\"shape\\\":\\\"🌙\\\",\\\"picture\\\":\\\"https://example.com/luna.jpg\\\"}\"\n}\n```\n\n### Client Behavior\n\n- When `shape` is absent, clients SHOULD render the avatar as a circle (the current universal default).\n- 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).\n- When `shape` is set to an unrecognized or invalid value, clients MUST fall back to a circle. This ensures forward compatibility.\n- The `shape` field is purely cosmetic and has no protocol-level significance.\n- Clients MAY choose not to support this extension, in which case avatars render as circles as usual.","sig":"5baa362832f940dfbd858208702d90ad861d90786d6a23479b4f865f88f235315c73843aa36d93fe0df365f5c1c3f7a73900049100938fa1d2d8723af3423ebc"}