identity: shared M3 Expressive theme factory + nav slide spec

FloretExpressiveTheme(lightScheme, darkScheme, darkTheme, dynamicColor,
typography) holds the family's theme mechanics — dynamic-colour resolution
(API 31+), system light/dark fallback, and the STANDARD motion scheme — while
each app supplies its own seed-derived colour schemes, so the look stays
per-app and the machinery is shared. rememberNavSlideSpec() shares the fast
spatial nav slide. android-library, Compose-only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
makiolajandClaude Opus 4.8 committed 2026-06-28 12:57:27 +02:00
1 parent 19c2f8a677
commit dccf934172
4 files changed
+111

No files matched your search

@@ -0,0 +1,19 @@
package de.jeanlucmakiola.floret.identity
import androidx.compose.animation.core.FiniteAnimationSpec
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.IntOffset
/**
* The horizontal slide spec for screen-to-screen navigation: the *fast*
* spring-physics spec from the active motion scheme — snappy with a subtle
* springy settle, rather than a fixed easing curve. Shared so the family's apps
* move the same way. Read in composable scope so the non-composable `NavHost`
* transition lambdas can capture it.
*/
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun rememberNavSlideSpec(): FiniteAnimationSpec<IntOffset> =
MaterialTheme.motionScheme.fastSpatialSpec()
@@ -0,0 +1,52 @@
package de.jeanlucmakiola.floret.identity
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.ColorScheme
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.MaterialExpressiveTheme
import androidx.compose.material3.MotionScheme
import androidx.compose.material3.Typography
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
/**
* The family's Material 3 Expressive theme factory. Honours system light/dark
* and Material You dynamic colour (API 31+), falling back to the app-supplied
* [lightScheme] / [darkScheme] otherwise. Routes all component + custom motion
* through `MaterialTheme.motionScheme` with the **standard** scheme — spring
* choreography without the overshoot, the family's deliberate motion choice.
*
* The mechanics are shared; the look is not. Each app keeps its identity by
* passing its own hand-tuned, seed-derived [lightScheme] / [darkScheme] and an
* optional [typography]. A thin per-app wrapper (e.g. `AgendulaTheme`) supplies
* those and forwards [darkTheme] / [dynamicColor] from settings.
*/
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun FloretExpressiveTheme(
lightScheme: ColorScheme,
darkScheme: ColorScheme,
darkTheme: Boolean = isSystemInDarkTheme(),
dynamicColor: Boolean = true,
typography: Typography = Typography(),
content: @Composable () -> Unit,
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val ctx = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(ctx) else dynamicLightColorScheme(ctx)
}
darkTheme -> darkScheme
else -> lightScheme
}
MaterialExpressiveTheme(
colorScheme = colorScheme,
typography = typography,
motionScheme = MotionScheme.standard(),
content = content,
)
}