diff --git a/app/src/main/java/de/jeanlucmakiola/clockula/ui/theme/Type.kt b/app/src/main/java/de/jeanlucmakiola/clockula/ui/theme/Type.kt index 5f89ce9..cbbceeb 100644 --- a/app/src/main/java/de/jeanlucmakiola/clockula/ui/theme/Type.kt +++ b/app/src/main/java/de/jeanlucmakiola/clockula/ui/theme/Type.kt @@ -1,10 +1,71 @@ package de.jeanlucmakiola.clockula.ui.theme +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Typography +import androidx.compose.runtime.Composable +import androidx.compose.runtime.ReadOnlyComposable +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontWeight + +/** Fixed-width digits. One string, one place, and a build rule that keeps it there (M7 D12). */ +private const val TABULAR_FIGURES = "tnum" + +private fun TextStyle.tabular(): TextStyle = copy(fontFeatureSettings = TABULAR_FIGURES) /** - * Default Material 3 Expressive typography. A clock app is mostly one enormous - * number, so the readout scale is the real typographic decision here — it gets - * settled against a working stopwatch in M7, not guessed at in the scaffolding. + * The Material 3 Expressive default with **tabular figures** on the display, + * headline and title roles. A clock app is mostly one enormous number, and a + * readout whose glyph columns change width as its digits change jitters on + * every tick — so every existing readout (the timer row's `headlineLarge`, the + * setup panel's `displayMedium`, the live pill's `titleMedium`) is fixed here, + * with no call-site change anywhere. + * + * Body and label roles keep proportional figures: prose is not a column of + * numbers, and proportional digits read better inside a sentence. */ -val ClockulaTypography = Typography() +val ClockulaTypography: Typography = Typography().run { + copy( + displayLarge = displayLarge.tabular(), + displayMedium = displayMedium.tabular(), + displaySmall = displaySmall.tabular(), + headlineLarge = headlineLarge.tabular(), + headlineMedium = headlineMedium.tabular(), + headlineSmall = headlineSmall.tabular(), + titleLarge = titleLarge.tabular(), + titleMedium = titleMedium.tabular(), + titleSmall = titleSmall.tabular(), + ) +} + +/** + * The three roles a readout uses, named once for the whole app — M0's written + * promise that the big-readout scale would be settled against a working + * stopwatch rather than guessed at in the scaffolding (M7 D12). + * + * The M3 `*Defaults` shape — composable getters over `MaterialTheme.typography` + * rather than constants — so a theme change still flows through them. No new + * font, no size outside the M3 scale, and no weight above Medium: `PLAN.md` §8's + * "Expressive ≠ big or bold" applies hardest to the one screen most tempted to + * break it. + */ +object ClockulaReadoutDefaults { + + /** The stopwatch's big number. */ + val Hero: TextStyle + @Composable @ReadOnlyComposable + get() = MaterialTheme.typography.displayLarge.copy(fontWeight = FontWeight.Medium) + + /** + * The hundredths beside [Hero], about half its size. A readout whose + * hundredths are as large as its seconds makes the fastest-changing glyphs + * the loudest ones and drags the eye off the number that matters (D5). + */ + val HeroFraction: TextStyle + @Composable @ReadOnlyComposable + get() = MaterialTheme.typography.headlineMedium + + /** A figure inside a table row — the one place body-scale text *is* a column of numbers. */ + val Figure: TextStyle + @Composable @ReadOnlyComposable + get() = MaterialTheme.typography.bodyLarge.tabular() +}