|
| 1 | +# ADR-02 - Accessor Strategy for Component Styling |
| 2 | + |
| 3 | +## Decision and justification |
| 4 | +We need to decide on an accessor strategy for styling components, such as adaptive color or density, throughout our Jetpack Compose library and the app-project. |
| 5 | + |
| 6 | +## Problem description and context |
| 7 | +We require a flexible and consistent method to apply styling to our Compose components. The settings, adaptive color and density, must be adjustable and independently set. The structure for colors and themes should be easy to understand and maintain. Apply another density results in adaptive typography and dimensions, such as sizing, spacing and border. |
| 8 | + |
| 9 | +## General conditions and decision criteria |
| 10 | + |
| 11 | +### General conditions |
| 12 | +- The styling approach must allow easy adjustment of color and density parameters. |
| 13 | +- Each styling parameter should be set independently. |
| 14 | +- The styling approach must cosider all adaptive tokens: color, typography and dimensions. |
| 15 | +- Components should support adaptive identifier that use specific color schemes based on the current theme. |
| 16 | +- The solution should balance performance, scalability, flexibility, and maintainability. |
| 17 | + |
| 18 | +### Decision criteria |
| 19 | +- **Performance:** Minimal performance overhead. |
| 20 | +- **Scalability:** Capable of future extensions. |
| 21 | +- **Maintainability:** Easy to maintain and understand. |
| 22 | +- **Consistency:** Reliable and consistent application of styles. |
| 23 | +- **Usability:** Simple for developers to use and integrate with existing Jetpack Compose components. |
| 24 | + |
| 25 | +## Alternatives |
| 26 | + |
| 27 | +### A - Hardcoded Styling in Components |
| 28 | + |
| 29 | +#### Evaluation |
| 30 | +- **Performance:** High, no additional processing required. |
| 31 | +- **Scalability:** Poor, difficult to extend. |
| 32 | +- **Maintainability:** Low, hard to manage and update styles. |
| 33 | +- **Consistency:** Low, prone to discrepancies. |
| 34 | +- **Usability:** Low, not flexible for developers. |
| 35 | + |
| 36 | +### B - Centralized Style Accessors |
| 37 | + |
| 38 | +#### Evaluation |
| 39 | +- **Performance:** Moderate, slight overhead for accessing centralized styles. |
| 40 | +- **Scalability:** High, easy to extend with new styles. |
| 41 | +- **Maintainability:** High, centralized management of styles. |
| 42 | +- **Consistency:** High, uniform application of styles. |
| 43 | +- **Usability:** High, provides a flexible and consistent styling interface for developers. |
| 44 | +- **Inheritance:** Moderate, needs explicit handling of inheritance for adaptive flags. |
| 45 | + |
| 46 | +### C - Dynamic Styling via Composition Local |
| 47 | + |
| 48 | +#### Evaluation |
| 49 | +- **Performance:** Moderate, with minor overhead from Composition Local lookups. |
| 50 | +- **Scalability:** High, easily extendable by adding more Composition Local providers. |
| 51 | +- **Maintainability:** High, centralized management of styles with local overrides as needed. |
| 52 | +- **Consistency:** High, ensures uniform styling with the flexibility of local overrides. |
| 53 | +- **Usability:** High, provides a flexible and consistent interface for developers. |
| 54 | +- **Inheritance:** High, naturally supports hierarchical inheritance and adaptive flags. |
| 55 | + |
| 56 | +## Decision |
| 57 | +We choose **Alternative C - Dynamic Styling via Composition Local**. This approach provides the best support for hierarchical inheritance and adaptive flags, ensuring consistent and flexible styling management. It leverages the Composition Local mechanism for efficient state management and propagation through the Compose tree. |
| 58 | + |
| 59 | +## Consequences |
| 60 | +- **Positive:** Utilizes Composition Local to provide dynamic and inheritable styling. It simplifies the process of applying complex styling configurations and allows for easy updates and extensions. Ensures adaptive flags are consistently applied through the hierarchy. |
| 61 | +- **Negative:** There could be minor performance overhead due to Composition Local lookups, but this is offset by the flexibility and maintainability benefits. |
| 62 | + |
| 63 | +## Links |
| 64 | +- [Theme-Builder GitHub Repository](https://github.com/db-ui/theme-builder) |
| 65 | +- [Jetpack Compose Theming Documentation](https://developer.android.com/jetpack/compose/themes) |
| 66 | +- [Composition Local Documentation](https://developer.android.com/jetpack/compose/compositionlocal) |
| 67 | + |
| 68 | +## Sample Code |
| 69 | + |
| 70 | +1. **Define Composition Local Providers:** |
| 71 | + ```kotlin |
| 72 | + val LocalColors = staticCompositionLocalOf { getColorSchemeLight() } |
| 73 | + val LocalActiveColor = staticCompositionLocalOf { getColorSchemeLight().neutral } |
| 74 | + ``` |
| 75 | + |
| 76 | +2. **Make it accessible via theme accessor:** |
| 77 | + ```kotlin |
| 78 | + object DesignSystemTheme { |
| 79 | + val colors: DesignSystemColorScheme |
| 80 | + @Composable |
| 81 | + @ReadOnlyComposable |
| 82 | + get() = LocalColors.current |
| 83 | + |
| 84 | + val activeColor: DSColorVariant |
| 85 | + @Composable |
| 86 | + @ReadOnlyComposable |
| 87 | + get() = LocalActiveColor.current |
| 88 | + |
| 89 | + // Same for other stylings, such as typography and dimensions |
| 90 | + } |
| 91 | + |
| 92 | + @Composable |
| 93 | + fun DesignSystemTheme( |
| 94 | + density: DSDensity = DSDensity.REGULAR, |
| 95 | + darkTheme: Boolean = isSystemInDarkTheme(), |
| 96 | + content: @Composable () -> Unit |
| 97 | + ) { |
| 98 | + // Use density to choose correct style for typography and dimensions |
| 99 | + |
| 100 | + // colors |
| 101 | + val colorScheme: DesignSystemColorScheme = when { |
| 102 | + darkTheme -> getColorSchemeDark() |
| 103 | + else -> getColorSchemeLight() |
| 104 | + } |
| 105 | + CompositionLocalProvider( |
| 106 | + LocalColors provides colorScheme, |
| 107 | + LocalActiveColor provides colorScheme.neutral, |
| 108 | + // Same for typography and dimensions |
| 109 | + ) { |
| 110 | + content() |
| 111 | + } |
| 112 | + } |
| 113 | + ``` |
| 114 | + |
| 115 | +3. **Apply Styles in Composable Functions:** |
| 116 | + ```kotlin |
| 117 | + @Composable |
| 118 | + fun StyledText(text: String) { |
| 119 | + Box( |
| 120 | + modifier = Modifier.padding(DesignSystemTheme.dimensions.spacing.fixedMd), |
| 121 | + ) { |
| 122 | + Text( |
| 123 | + text = text, |
| 124 | + color = DesignSystemTheme.activeColor.onBgBasicEmphasis100Default, |
| 125 | + ) |
| 126 | + } |
| 127 | + } |
| 128 | + ``` |
| 129 | + |
| 130 | +4. **Provide Composable to switch between styles:** |
| 131 | + ```kotlin |
| 132 | + @Composable |
| 133 | + fun AdaptiveLayout( |
| 134 | + density: DSDensity = DSDensity.REGULAR, |
| 135 | + activeColor: DSColorVariant = DesignSystemTheme.colors.neutral, |
| 136 | + content: @Composable () -> Unit, |
| 137 | + ) { |
| 138 | + // Same logic for typography and density as shown in 2. |
| 139 | + |
| 140 | + CompositionLocalProvider( |
| 141 | + LocalActiveColor provides activeColor, |
| 142 | + // Same for typography and dimensions |
| 143 | + ) { |
| 144 | + content() |
| 145 | + } |
| 146 | + } |
| 147 | + ``` |
| 148 | + |
| 149 | +5. **Provide Adaptive Values in the Composition:** |
| 150 | + ```kotlin |
| 151 | + @Composable |
| 152 | + fun CriticalExpressiveView() { |
| 153 | + AdaptiveLayout( |
| 154 | + density = DSDensity.EXPRESSIVE, |
| 155 | + activeColor = DesignSystemTheme.colors.critical, |
| 156 | + ) { |
| 157 | + StyledText("Hello world!") |
| 158 | + } |
| 159 | + } |
| 160 | + ``` |
0 commit comments