--- title: Why your CalDAV events show sixty shades of the same blue description: >- 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. pubDate: 2026-07-08 tags: [android, calendula, caldav, accessibility] draft: 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`](https://developer.android.com/reference/android/provider/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`](https://developer.android.com/reference/android/provider/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`](https://datatracker.ietf.org/doc/html/rfc7986#section-5.9) 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](https://www.w3.org/TR/WCAG21/#dfn-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. [#21]: https://codeberg.org/jlmakiola/calendula/issues/21 [#22]: https://codeberg.org/jlmakiola/calendula/issues/22