feat(theme): tabular figures on the roles that carry a running number
M0 said the big-readout typography would be settled once there was a stopwatch to settle it against. `ClockulaTypography` now puts `tnum` on the display, headline and title roles, which is every role a counting number uses, and `ClockulaReadoutDefaults` names the three sizes the readouts share. Digits stop changing width as they change, so the timer row, the setup panel and the live pill stop twitching too — none of them needed a call-site change to get it.
This commit is contained in:
@@ -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()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user