Pond-level branding: override instance logo and favicon per pond #307
Labels
No Label
area:auth
area:docs
area:export
area:ops
area:storage
area:supply-chain
auth
backend
blocked
collab
deployment
docs
effort:L
effort:M
effort:S
frontend
plugins
qa
vs-nfd
vs-nfd:blocker
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: stwaidele/dorfteich#307
Loading…
Reference in New Issue
Block a user
No description provided.
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Depends on #306.
A pond admin should be able to override the instance logo and favicon in
the pond settings, so a pond carries its own identity.
Resolution order
One place decides which asset applies, extending what #306 built:
instance.nameas text(logo).
A logo set belongs to one level — variants are never mixed across
levels. A pond that uploads only a light logo uses that logo in dark
mode too. It does not fall back to the instance's dark variant.
This is deliberate (decided 2026-08-01): a logo silently swapping to a
different image when the viewer switches theme is a change nobody
ordered. A design that looks wrong is more honest than one that is
quietly substituted, and the pond admin can see and fix it.
The pond settings screen warns when the dark variant is missing, exactly
as the instance screen does (#306). The warning is advisory — it names
the consequence, and the pond admin may ignore it. Nothing is blocked.
Only when a pond sets no logo at all does the instance's set apply,
again as a set.
Worth an explicit test in both directions: pond-light-only stays on its
own logo in dark mode, and a pond with nothing set inherits both instance
variants.
Storage and quota
Pond assets live under the branding directory established in #306,
scoped per pond, and are covered by the same backup registration.
They count against the pond's storage quota, charged before the write
the way attachments are (
apps/api/src/files/files.service.ts:153).Without that, branding uploads would be a way around the quota — and
replacing a logo repeatedly would let a pond admin consume disk with no
ceiling.
Purging a pond must remove its branding assets too. The purge standard is
absolute: after it, nothing referencing the pond survives, rows or files
(
apps/api/src/trash/pond-purge.e2e.db.test.ts). Extend that test ratherthan trusting the new code path.
Pond favicon
The favicon is a document-level resource, so a pond's favicon is applied
by swapping the
<link rel="icon">href when the pond route is entered,and restoring the instance favicon on leaving.
The limitation is accepted and should be recorded in the code comment:
the swap necessarily happens after first paint, so opening a pond
link directly shows the instance favicon briefly before it changes.
Avoiding that would require server-rendering
index.html, which is #179'sterritory and deliberately out of scope here. In a pinned tab — where
distinguishing ponds matters most — the tab is already open, so the swap
is the normal case rather than the exception.
Take care that the swap is driven by the resolved pond, not by the raw
route parameter, so an unreadable or non-existent pond slug does not leave
a stale icon in the tab.
Permissions
Pond admins manage their own pond's branding, in the existing pond
settings screen. Follow the project's 404/403 policy: a user who cannot
see the pond gets 404, one who can see but not administer it gets 403.
Security
Unchanged from #306 and non-negotiable at this level too, since the
uploader is now an ordinary pond admin rather than the operator: SVG
rejected, magic bytes validated server-side, size caps enforced, content
type pinned on serving, no image parsing in the api.
Accessibility
context — it must not silently keep announcing the instance name.
including its keyboard operability; it must not grow a second,
drag-only cropper.
empty file field.
apps/web/e2e/a11y.spec.tsper the standing rule.Acceptance criteria
back to the default — verified at each level.
and the settings screen warns about the missing dark variant without
blocking anything.
one.
limit is refused with a clear message.
purge test.
pnpm lint,pnpm typecheck,pnpm i18n:check, api suite and thea11y spec pass.