NAP-THEME / Merged specification
Shell-Provided Theming
Full specification from the fetched upstream revision ↗. The document below is rendered from its original Markdown. Follow the latest source on GitHub ↗.
NAP-THEME
Shell-Provided Theming
draft
NAP ID: NAP-THEME
Domain: theme
Depends: none.
Web binding (NIP-5D): window.napplet.theme; domain presence signals availability.
Description
NAP-THEME provides napplets with read-only access to the shell's active theme. The shell owns the theme and delivers it as a typed payload — napplets have no knowledge of where the theme originates or how it is stored. The theme includes colors (required), as well as optional fonts, background media, and a human-readable title. Napplets can query the current theme on demand and receive automatic notifications when the shell's active theme changes.
API Surface
| Operation | Parameters | Result | Wire |
|---|---|---|---|
get |
none | Theme |
theme.get / theme.get.result |
onChanged |
handler for Theme |
Subscription handle |
theme.changed |
Schemas
ThemeColors fields:
| Field | Required | Type | Notes |
|---|---|---|---|
background |
yes | text | Hex color, e.g. #1a1a2e. |
text |
yes | text | Hex color. |
primary |
yes | text | Hex color. |
ThemeFont fields:
| Field | Required | Type | Notes |
|---|---|---|---|
name |
yes | text | Font family name. |
url |
yes | text | URL to font file. |
ThemeBackground fields:
| Field | Required | Type | Notes |
|---|---|---|---|
url |
yes | text | URL to background image or media. |
mode |
yes | text | CSS background-size value, e.g. cover. |
mime |
yes | text | MIME type, e.g. image/jpeg. |
ThemeFonts fields:
| Field | Required | Type |
|---|---|---|
body |
no | ThemeFont |
title |
no | ThemeFont |
Theme fields:
| Field | Required | Type | Notes |
|---|---|---|---|
colors |
yes | ThemeColors |
Active color set. |
fonts |
no | ThemeFonts |
Optional font set. |
background |
no | ThemeBackground |
Optional background media. |
title |
no | text | Human-readable theme name. |
onChanged(handler) registers a handler for Theme updates and returns a
Subscription handle. Closing the handle removes the local handler. There is no
wire subscribe or unsubscribe operation; the runtime-provided binding receives
theme.changed for this domain and dispatches it to registered handlers.
Wire Protocol
theme.* messages use the NIP-5D wire format ({ "type": "domain.action", ...payload }).
| Type | Direction | Payload fields |
|---|---|---|
theme.get |
napplet -> shell | id |
theme.get.result |
shell -> napplet | id, theme (Theme object) |
theme.changed |
shell -> napplet | theme (Theme object) |
Key design notes:
theme.get/theme.get.resultuseidfor correlation.theme.changedhas noid— it is a shell-initiated push with no napplet request to correlate.- There is no subscribe or unsubscribe. Every napplet to which the runtime exposes
themeautomatically receivestheme.changedwhen the active theme changes. ManifestRandOtags are declarations, not grants; neither is required for receiving updates when the domain is exposed.
Examples
Get active theme (full payload):
-> { "type": "theme.get", "id": "t1" }
<- { "type": "theme.get.result", "id": "t1", "theme": {
"colors": { "background": "#1a1a2e", "text": "#e0e0e0", "primary": "#6c3ce0" },
"fonts": {
"body": { "name": "Inter", "url": "https://example.com/inter.woff2" },
"title": { "name": "Playfair Display", "url": "https://example.com/playfair.woff2" }
},
"background": { "url": "https://example.com/bg.jpg", "mode": "cover", "mime": "image/jpeg" },
"title": "MK Dark Theme"
}}
Get active theme (minimal — colors only):
-> { "type": "theme.get", "id": "t2" }
<- { "type": "theme.get.result", "id": "t2", "theme": {
"colors": { "background": "#ffffff", "text": "#333333", "primary": "#0066cc" }
}}
Theme changed (auto-push from shell, no id):
<- { "type": "theme.changed", "theme": {
"colors": { "background": "#0d1117", "text": "#c9d1d9", "primary": "#58a6ff" },
"title": "GitHub Dark"
}}
Error Handling
Any result message MAY include an error field (string). When error is present, other result fields are undefined.
<- { "type": "theme.get.result", "id": "t1", "error": "no active theme" }
Shell Behavior
- The shell MUST respond to
theme.getwith atheme.get.resultcarrying the sameid. - The shell MUST include
colorswith all three fields (background,text,primary) in every theme payload. Thefonts,background, andtitlefields are optional and MAY be omitted. - The shell MUST broadcast
theme.changedwhen the active theme changes to all napplets to which it exposestheme. It MUST NOT deliver these messages to napplets without that domain. It MUST NOT infer grants fromRorOtags. - The shell MAY source the theme from any origin: a Nostr kind 16767 event, a user preference, a hardcoded default, or any other mechanism. The theme source is an implementation detail invisible to napplets.
Kind 16767 Mapping
Shells that source themes from Nostr kind 16767 events (active profile themes, as used by Ditto and compatible clients) SHOULD map event tags to the typed theme payload as follows:
| Kind 16767 Tag | Theme Payload Field |
|---|---|
["c", "<hex>", "background"] |
colors.background |
["c", "<hex>", "text"] |
colors.text |
["c", "<hex>", "primary"] |
colors.primary |
["f", "<name>", "<url>", "body"] |
fonts.body.name, fonts.body.url |
["f", "<name>", "<url>", "title"] |
fonts.title.name, fonts.title.url |
["bg", "url <url>", "mode <mode>", "m <mime>"] |
background.url, background.mode, background.mime |
["title", "<name>"] |
title |
The bg tag uses positional string prefixes (url , mode , m ). Shells MUST strip these prefixes when extracting values before placing them in the payload.
This mapping is informational guidance for shells that choose to source from kind 16767 events. It is not a requirement — shells that source themes from other origins MUST still deliver a valid Theme payload.
Security Considerations
- Theme data is read-only. Napplets cannot modify the shell's active theme via this interface.
- Font and background URLs do not authorize network access. Shells SHOULD validate them and MAY provide resources through their mediated resource policy. Napplets MUST NOT assume external URLs are directly loadable; they SHOULD fall back to colors and local fonts when optional media is unavailable.
- The shell controls theme delivery. A napplet cannot influence what theme other napplets receive.
- Color values are strings. Napplets SHOULD validate hex color format (e.g.,
#rrggbb) before applying values to prevent injection via malformed color strings. - The shell MAY enforce ACL checks before responding to
theme.getin restricted environments.
Implementations
- (none yet)
Changelog
780d7ba- Introduced shell-provided themes and automatic change notifications.4f92e9a- Bound automatic theme delivery to exposed domains, defined onChanged, and clarified optional media under runtime network policy.