Smart Frames
Overview
A Smart Frame is a thin decorative border drawn around a post or around one of your room's modules. There are 61 frames in twelve families, from a calm hairline to botanical sprigs, retro stars, typewriter marks and circuit traces.
A frame is decoration only. It never changes what a post says, who sees it, or where it appears. Nothing is counted, ranked or suggested: "No frame" is always first in the list and is what every post and module starts with, and the frames are listed in a fixed order by family (never by popularity).
Frames are drawn at the exact size of the thing they surround, so the lines stay the same thickness on a tall post, a short one or a phone. They sit behind the words and are never read out by a screen reader.
How frames get their colours
You never pick a frame's colours.
- On a post, the frame takes the colour of the post's intent: a Thinking Out Loud post gets a dusty denim frame, a Just for Fun post a mustard one, and so on. Change the intent and the frame follows. A post that has no intent, such as a quotation or a loop, gets a calm neutral slate frame, never an intent colour.
- On a room module, the frame takes its colours from your room's own palette, so it always looks like part of the room. If you set the room to match each visitor's light or dark preference, the frame follows the visitor.
Colours are chosen so the frame stays readable against the card behind it, in light and dark.
Add a frame to a post
- Open Write and choose your post type and intent.
- Under the writing area, open Frame (it reads "Frame: No frame" until you choose one).
- Pick a family if you want to narrow the list, then pick a frame. The post preview shows it straight away. Pick No frame to remove it.
- Post as usual.
Quotations and loops have no intent, so the Frame section is there from the start and uses the neutral frame colour.
To change or remove the frame on a post you have already made, open the post's Post settings (the ... menu on the post, or the Settings button on the post page), choose a frame, and select Save frame. Changing the frame does not mark the post as edited.
Five of the louder frames (Nouveau whiplash, Groovy, Marquee, Synth and Hazard) are for rooms only and are not offered on posts, so a feed of ten neighbours stays calm.
Default frames for each intent
If you like the same frame on every post of one kind, set it once in Account, Settings, Default frames. There is one row per intent. Choose a frame (or "No frame" to clear it) and new posts with that intent start with it already chosen.
A default is only a starting point. If you pick a different frame, or "No frame", while writing, your pick wins. Existing posts are never changed by a default.
Frames on room modules
In your room, open a module's editor and go to the Style pane, then Frame. All 61 frames are available there, including the room-only ones.
- A cell (a module, or a group of up to six tabbed modules) has one frame, set on its first tab. It goes around the whole cell, whichever tab is showing. On any later tab the editor tells you where to set it.
- Small modules that are too small for a frame simply draw without one.
- Visitors see your frames; only you can change them.
- Copy styles and room Looks do not carry frames.
For developers
Where the code is
| What | Where |
|---|---|
| Engine (pure, builds SVG for an exact size) and library (61 frames) | src/lib/frames/ |
| Draws a frame behind a card | src/components/frames/PostFrame.jsx |
| Picker | src/components/frames/FramePicker.jsx |
| Sanitising (DOMPurify) and the markup cache | src/lib/frames/sanitize-frame.js, frame-markup-cache.js |
| Ink colours | src/styles/frame-inks.css |
| Developer gallery (dev builds, admins only) | /dev/frames |
A post stores posts.frame = { "id": "sprig" } (or NULL), written only through the set_post_frame function. A room module stores home_room_modules.config_json.frame = { "id": "sprig" }. An id the library does not know draws nothing, so a frame can be retired without breaking old posts.
Adding a frame to the library
- Add an entry to
defsinsrc/lib/frames/library.jswith a new lowercase id. Never rename an existing id; deprecate it instead. - Draw the corner around (M, M) and the edge along +x, with outward = -y. Keep the total thickness within 5 px of the centreline unless you set
reachIn(how far the frame reaches toward the content) orcornerDepth(the minimum safe inset for a large corner). - Use exactly two native colours (ink and accent): three or more turn muddy when tinted. Use the
tiles,halves,waveDorroughDhelpers. For randomness usectx.rndorctx.nz, neverMath.random, and setseeded: trueso the frame is seeded by the post's id. - Run
npm run test:framesandnpx vitest run src/components/frames. The suites check clean output at many sizes, determinism, seed variation, valid XML, tile flex limits, that DOMPurify leaves the drawing unchanged, and that the frame never reaches into the content's safe area (safeInset). - Open
/dev/framesin a dev build as an admin, look at the frame at several sizes in light and dark and in every intent, and check it at 360 px wide.
Mark a frame as room-only with allowedIn: ['homeroom'] if it is too loud for a feed.