From 1af24841ae8e670fb608253086f8c702d4f7ba09 Mon Sep 17 00:00:00 2001 From: Jean-Luc Makiola Date: Wed, 9 Sep 2026 15:17:56 +0200 Subject: [PATCH] feat(components): a named header above a run of grouped rows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A settings page with a dozen categories in one flat run has no way to say which of them are about how the app behaves and which are about the app itself. GroupedSectionHeader is that label: it starts on GroupedListInset, the edge the cards it names start on, and owns the gap to the group above, so a page writes header-then-rows and nothing else. primary rather than onSurfaceVariant — after the Android-15 settings pattern Position already follows, and because a header that matches the summaries underneath it stops separating anything. --- CHANGELOG.md | 7 +++++ .../floret/components/GroupedList.kt | 27 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b2b1f51..01f0cb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,13 @@ are no binary releases; "version" tracks the shared `version` in the root build ### Added +- **`components`** — `GroupedSectionHeader`, the label above a run of + `GroupedRow`s. Starts on `GroupedListInset` and owns the gap to the group + above, so a page writes header-then-rows and nothing else; `primary`, after + the Android-15 settings pattern the row system already follows. Wanted by + Agendula's settings hub, which had grown a flat run of a dozen rows with + nothing separating how the app behaves from what it is. + - **`components`** — `GroupedRow` takes a `contentColor`, the pair to the existing `container`: a row given a tint now names the `on*` role that goes with it instead of leaving its text on `onSurface`, which only happens to diff --git a/components/src/main/kotlin/de/jeanlucmakiola/floret/components/GroupedList.kt b/components/src/main/kotlin/de/jeanlucmakiola/floret/components/GroupedList.kt index a60e628..33c8799 100644 --- a/components/src/main/kotlin/de/jeanlucmakiola/floret/components/GroupedList.kt +++ b/components/src/main/kotlin/de/jeanlucmakiola/floret/components/GroupedList.kt @@ -53,6 +53,33 @@ fun positionOf(index: Int, count: Int): Position = when { */ val GroupedListInset = 16.dp +/** + * The label above a run of [GroupedRow]s, for a page whose groups are named + * rather than merely spaced apart — a settings hub that separates how the app + * behaves from what it is. + * + * Starts on [GroupedListInset], the same edge as the cards it names, and owns + * the space that separates its group from the one above, so a caller writes + * header-then-rows and nothing else. Coloured `primary` after the Android-15 + * settings pattern this row system already follows: the headers are what makes + * the grouping readable at a glance, and `onSurfaceVariant` sinks them into the + * summaries. + */ +@Composable +fun GroupedSectionHeader(text: String, modifier: Modifier = Modifier) { + Text( + text = text, + style = MaterialTheme.typography.titleSmall, + color = MaterialTheme.colorScheme.primary, + modifier = modifier.padding( + start = GroupedListInset, + end = GroupedListInset, + top = 24.dp, + bottom = 8.dp, + ), + ) +} + /** Corner radii for a grouped segment: full at the group's outer edges, small between. */ fun groupedShape(position: Position, full: Dp, small: Dp): Shape = when (position) { Position.Alone -> RoundedCornerShape(full)