Technical, journey, and philosophy posts sourced from the Calendula issue threads, reviewed for source-accuracy, standards, ethics, and voice. All draft:true with staggered future pubDates; they stay hidden in prod until approved (draft:false) and due. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4.3 KiB
title, description, pubDate, tags, draft
| title | description | pubDate | tags | draft | ||||
|---|---|---|---|---|---|---|---|---|
| Why your CalDAV events show sixty shades of the same blue | Two open colour reports on Calendula come from one root — an overstuffed picker on CalDAV calendars and hard-to-read dark event titles — and both trace back to how event colour flows through Android's calendar provider. | 2026-07-08 |
|
true |
Two colour reports landed on Calendula in the same week, and both are still open. One is a bug: the colour picker on a CalDAV calendar shows a full screen of colours, many of them near-duplicates (#22). The other is a request: make event titles readable on dark backgrounds so busy weeks don't turn into a smear (#21). They look unrelated. They come from the same place — how event colour actually flows through Android's calendar provider — which is why I'm writing up the diagnosis before I write the fix.
Palette vs. free-for-all
Android's
CalendarContract
has two ways to colour an event, and which one you get depends on the account.
Google and local calendars expose a palette: a small, indexed set of colours
through
CalendarContract.Colors.
An event stores a color_key, the provider maps it to a swatch, and a picker can
show exactly those choices — a tidy dozen.
CalDAV is different. The iCalendar
COLOR property is
defined as a CSS3 colour name — a set of about 150, already far bigger than a
palette — and in practice clients push arbitrary hex through vendor extensions
like X-APPLE-CALENDAR-COLOR. Either way, by the time it reaches the provider
there's no color_key and no shared index: the event just carries a raw
EVENT_COLOR. So a picker that tries to build a palette from what it finds
ends up gathering every distinct value that ever appeared, including a dozen
blues that differ by a rounding error you can't see. That's the "too many
colours" bug: it isn't showing junk, it's faithfully showing an unbounded space
as though it were a fixed menu. The fix is to stop pretending CalDAV has a
palette — offer a sane curated set and map to the nearest real colour, rather
than enumerating every ghost.
The contrast problem is the same problem
Once an event can be any colour, you can no longer assume the text on top is readable. A pale event with dark text is fine; a deep navy block with the same dark text is a smear. Right now Calendula draws event titles in a fixed near- black — which is exactly the assumption that breaks. Google Calendar flips to white text on dark fills, and #21 asks for the same.
The rule for "is this colour dark" isn't the average of its channels — the eye isn't equally sensitive to red, green and blue. The WCAG relative luminance formula weights them the way perception does (green counts far more than blue), linearises each channel, and yields a single brightness figure. Pick a threshold, and below it the title renders white (or off-white), above it near-black. It's a few lines of maths applied to the same event colour the picker just handed you — which is the point. The overstuffed picker and the unreadable title are two ends of one pipe: the moment colour stops being a fixed palette and becomes arbitrary, both the input and the output need taming. One end is a bug to close; the other is a threshold to add. Same pipe.
Standard first, taste second
The tidy path would be to store my own colour for every event and never touch the
provider's mess. I don't — for the same reason Calendula owns no other part of
your data. The COLOR on a CalDAV event belongs to the event, and the sync
adapter carries it out to every other client that reads your calendar; if I
overwrote it with a private value, that portability would be gone. My job isn't
to replace the colour. It's to present a bounded, readable view of it — a
curated picker on the way in, a luminance-aware title on the way out — while the
value itself stays yours and portable. Two open issues, one fix: let you read
your own calendar without taking it over.