Add heart shape and document kind 0 shape extension in NIP.md

- Add 'heart' as the 8th avatar shape using SVG path() clip-path with cubic beziers
- Document the kind 0 'shape' metadata extension in NIP.md with all 8 defined values,
  client behavior rules, and forward-compatibility guidance
This commit is contained in:
Alex Gleason
2026-03-13 17:25:50 -05:00
parent 0781b1a87c
commit 61ea5ff1fe
2 changed files with 50 additions and 0 deletions
+44
View File
@@ -251,3 +251,47 @@ After resolution (assuming `$follows` = `["pk1", "pk2"]`):
- To **clear** all tabs: publish a kind 16769 event with no `tab` tags (only `alt`).
- Clients MUST filter by `authors: [pubkey]` when querying to prevent spoofing.
- `var` tags are shared across all `tab` tags in the same event.
---
## 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. Clients that support this extension render the avatar using the specified geometric shape instead of the default circle.
### Metadata Field
The `shape` field is added to the JSON content of a kind 0 event alongside standard fields like `name`, `picture`, etc.
```json
{
"kind": 0,
"content": "{\"name\":\"Alice\",\"picture\":\"https://example.com/alice.jpg\",\"shape\":\"hexagon\"}"
}
```
### Defined Shape Values
| Value | Description |
|----------------------|----------------------------------------------------------------|
| `circle` | Standard circular avatar (the default when `shape` is absent) |
| `triangle` | Equilateral triangle pointing up |
| `inverted-triangle` | Equilateral triangle pointing down |
| `hexagon` | Regular hexagon (6-sided polygon) |
| `star` | 5-pointed star |
| `inverted-star` | 5-pointed star pointing down |
| `hexagram` | 6-pointed star (Star of David) |
| `heart` | Heart shape with curved lobes |
### Client Behavior
- When `shape` is absent or set to `"circle"`, clients SHOULD render the avatar as a circle (the current universal default).
- When `shape` is set to a recognized value, clients SHOULD apply the corresponding geometric mask (e.g. via CSS `clip-path`) to the avatar image and fallback.
- When `shape` is set to an **unrecognized** value, clients MUST fall back to `"circle"`. This ensures forward compatibility as new shapes are added.
- 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.
### Implementation Notes
Non-circle shapes are best implemented using CSS `clip-path` with `polygon()` for straight-edged shapes and `path()` for curved shapes (e.g. heart). The `circle` shape should continue to use `border-radius: 50%` (or equivalent) for simplicity and compatibility.
+6
View File
@@ -7,6 +7,7 @@ export const AVATAR_SHAPES = [
'star',
'inverted-star',
'hexagram',
'heart',
] as const;
export type AvatarShape = (typeof AVATAR_SHAPES)[number];
@@ -28,6 +29,7 @@ export function getAvatarShapeLabel(shape: AvatarShape): string {
case 'star': return 'Star';
case 'inverted-star': return 'Inv. Star';
case 'hexagram': return 'Hexagram';
case 'heart': return 'Heart';
}
}
@@ -95,5 +97,9 @@ export function getAvatarClipPath(shape: AvatarShape | undefined): string | unde
case 'hexagram':
return starPolygon(6, 0.577, -90);
case 'heart':
// SVG path normalized to a 1×1 viewBox. Uses cubic beziers for the curved lobes.
return 'path("M0.5 0.92 C0.28 0.75, 0 0.56, 0 0.34 C0 0.16, 0.13 0.04, 0.28 0.04 C0.37 0.04, 0.45 0.09, 0.5 0.17 C0.55 0.09, 0.63 0.04, 0.72 0.04 C0.87 0.04, 1 0.16, 1 0.34 C1 0.56, 0.72 0.75, 0.5 0.92 Z")';
}
}