OnboardingScaffold (scrollable hero + body, CTAs pinned to the bottom), BenefitRow and the OnboardingSpace scale — identical across Agendula and Calendula. The hero is a slot, so each app keeps its own mark. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
floret-kit
The shared Material 3 Expressive design system + plumbing for the Floret family of Android apps (Calendula, Agendula, and future siblings).
"Floret" is the family's design language: a Calendula flower head is a cluster of many small florets, and each app is one of them. This repo is the bloom they share — so a new app doesn't reinvent a settings screen, an edit form, or a theme; it draws from here.
How it's consumed
Each app embeds this repo as a git submodule and wires it in with a Gradle composite build, so the kit is always built from source (no published artifacts — keeps every app reproducible for F-Droid):
// app's settings.gradle.kts
includeBuild("floret-kit")
// app module's build.gradle.kts
implementation("de.jeanlucmakiola.floret:core-time")
Gradle's dependency substitution maps de.jeanlucmakiola.floret:<module> to the
local source module — edit the kit and the app picks it up immediately.
Modules
| Module | What it holds |
|---|---|
core-time |
Pure-Kotlin date/time helpers: local-day windows (DayWindow) for smart-list logic and locale/zone-aware display formatting (Instant.formatDate() / formatTime() / formatDateTime()). No Android, no deps. |
core-reminders |
Pure-Kotlin reminder-lead plumbing: the lead-time unit model (ReminderUnit, decomposeReminderMinutes), the per-target override model (ReminderOverride + reminderLeadFor / applyReminderOverride) and the stored-format codec (ReminderOverrideCodec, separators configurable per app). No Android, no deps. Each app layers its own DataStore + string labels on top. |
core-locale |
Per-app language plumbing (AppLanguage): read the shipped languages from the app's res/xml/locales_config.xml, get/set the applied language via AppCompatDelegate, and render each language's autonym. The app passes its own locales_config resource id; appcompat only, no Compose. |
More modules (identity/theme, components, screen recipes, provider/prefs/crash plumbing) land as they're extracted from the apps.
Build
./gradlew :core-time:test