ThemeProviderEnhanced theme system that provides design tokens, CSS variables, and theme composition support.
☰Navigation

ThemeProvider

Enhanced theme system that provides design tokens, CSS variables, and theme composition support.

Components

ThemeProvider

Provides theme configuration to child components and optionally injects CSS variables.

@Composable
fun ThemeProvider(
    theme: EnhancedThemeConfig = EnhancedThemeConfig(),
    content: @Composable () -> Unit
)

Parameters:

  • theme - The theme configuration to provide
  • content - The content to render with the theme

useTheme

Hook to access the current theme configuration.

@Composable
fun useTheme(): EnhancedThemeConfig

Returns: The current theme configuration

EnhancedThemeConfig

Enhanced theme configuration that extends the base theme with additional design tokens.

data class EnhancedThemeConfig(
    val primaryColor: String = "#007bff",
    val secondaryColor: String = "#6c757d",
    val backgroundColor: String = "#ffffff",
    val textColor: String = "#212529",
    val borderColor: String = "#dee2e6",
    val isDarkMode: Boolean? = null,
    val designTokens: Map<String, String>? = null,
    val breakpoints: Map<String, String>? = null,
    val typography: Map<String, String>? = null
)

Properties:

  • primaryColor - Primary brand color
  • secondaryColor - Secondary brand color
  • backgroundColor - Default background color
  • textColor - Default text color
  • borderColor - Default border color
  • isDarkMode - Whether this is a dark mode theme
  • designTokens - CSS custom properties to inject
  • breakpoints - Responsive breakpoint definitions
  • typography - Typography scale definitions

Basic Usage

Simple Theme

@Composable
fun App() {
    val theme = EnhancedThemeConfig(
        primaryColor = "#007bff",
        secondaryColor = "#6c757d",
        backgroundColor = "#ffffff",
        textColor = "#333333"
    )

    ThemeProvider(theme = theme) {
        // Your app content
        HomePage()
    }
}

@Composable
fun HomePage() {
    val theme = useTheme()

    Box(
        modifier = Modifier()
            .style("background-color", theme.backgroundColor)
            .style("color", theme.textColor)
    ) {
        Text("Welcome to our app!")
    }
}

Theme with Design Tokens

@Composable
fun App() {
    val theme = EnhancedThemeConfig(
        primaryColor = "var(--color-primary)",
        secondaryColor = "var(--color-secondary)",
        backgroundColor = "var(--color-background)",
        designTokens = mapOf(
            // Colors
            "--color-primary" to "#007bff",
            "--color-primary-hover" to "#0056b3",
            "--color-secondary" to "#6c757d",
            "--color-background" to "#ffffff",
            "--color-surface" to "#f8f9fa",
            "--color-text" to "#333333",
            "--color-text-muted" to "#6c757d",

            // Spacing
            "--space-1" to "0.25rem",
            "--space-2" to "0.5rem",
            "--space-3" to "0.75rem",
            "--space-4" to "1rem",
            "--space-5" to "1.25rem",
            "--space-6" to "1.5rem",
            "--space-8" to "2rem",
            "--space-10" to "2.5rem",
            "--space-12" to "3rem",

            // Typography
            "--font-size-xs" to "0.75rem",
            "--font-size-sm" to "0.875rem",
            "--font-size-base" to "1rem",
            "--font-size-lg" to "1.125rem",
            "--font-size-xl" to "1.25rem",
            "--font-size-2xl" to "1.5rem",
            "--font-size-3xl" to "1.875rem",
            "--font-size-4xl" to "2.25rem",

            // Border radius
            "--radius-sm" to "0.125rem",
            "--radius-base" to "0.25rem",
            "--radius-md" to "0.375rem",
            "--radius-lg" to "0.5rem",
            "--radius-xl" to "0.75rem",
            "--radius-2xl" to "1rem",
            "--radius-full" to "9999px",

            // Shadows
            "--shadow-sm" to "0 1px 2px 0 rgba(0, 0, 0, 0.05)",
            "--shadow-base" to "0 1px 3px 0 rgba(0, 0, 0, 0.1)",
            "--shadow-md" to "0 4px 6px -1px rgba(0, 0, 0, 0.1)",
            "--shadow-lg" to "0 10px 15px -3px rgba(0, 0, 0, 0.1)",
            "--shadow-xl" to "0 20px 25px -5px rgba(0, 0, 0, 0.1)"
        )
    )

    ThemeProvider(theme = theme) {
        MainApp()
    }
}

Advanced Features

Responsive Breakpoints

@Composable
fun ResponsiveTheme() {
    val theme = EnhancedThemeConfig(
        breakpoints = mapOf(
            "sm" to "640px",
            "md" to "768px",
            "lg" to "1024px",
            "xl" to "1280px",
            "2xl" to "1536px"
        ),
        designTokens = mapOf(
            "--breakpoint-sm" to "640px",
            "--breakpoint-md" to "768px",
            "--breakpoint-lg" to "1024px",
            "--breakpoint-xl" to "1280px",
            "--breakpoint-2xl" to "1536px"
        )
    )

    ThemeProvider(theme = theme) {
        GlobalStyle(
            """
            .container {
                width: 100%;
                padding: 0 var(--space-4);
            }
            
            @media (min-width: var(--breakpoint-sm)) {
                .container { max-width: 640px; margin: 0 auto; }
            }
            
            @media (min-width: var(--breakpoint-md)) {
                .container { max-width: 768px; }
            }
            
            @media (min-width: var(--breakpoint-lg)) {
                .container { max-width: 1024px; }
            }
        """
        )

        YourApp()
    }
}

Typography System

@Composable
fun TypographyTheme() {
    val theme = EnhancedThemeConfig(
        typography = mapOf(
            "fontFamily" to "'Inter', -apple-system, BlinkMacSystemFont, sans-serif",
            "fontFamilyMono" to "'JetBrains Mono', 'Monaco', monospace",
            "fontWeight-light" to "300",
            "fontWeight-normal" to "400",
            "fontWeight-medium" to "500",
            "fontWeight-semibold" to "600",
            "fontWeight-bold" to "700",
            "lineHeight-tight" to "1.25",
            "lineHeight-normal" to "1.5",
            "lineHeight-relaxed" to "1.75"
        ),
        designTokens = mapOf(
            "--font-family" to "'Inter', -apple-system, BlinkMacSystemFont, sans-serif",
            "--font-family-mono" to "'JetBrains Mono', 'Monaco', monospace",
            "--font-weight-light" to "300",
            "--font-weight-normal" to "400",
            "--font-weight-medium" to "500",
            "--font-weight-semibold" to "600",
            "--font-weight-bold" to "700",
            "--line-height-tight" to "1.25",
            "--line-height-normal" to "1.5",
            "--line-height-relaxed" to "1.75"
        )
    )

    ThemeProvider(theme = theme) {
        GlobalStyle(
            """
            body {
                font-family: var(--font-family);
                line-height: var(--line-height-normal);
            }
            
            .text-xs { font-size: var(--font-size-xs); }
            .text-sm { font-size: var(--font-size-sm); }
            .text-base { font-size: var(--font-size-base); }
            .text-lg { font-size: var(--font-size-lg); }
            .text-xl { font-size: var(--font-size-xl); }
            .text-2xl { font-size: var(--font-size-2xl); }
            
            .font-light { font-weight: var(--font-weight-light); }
            .font-normal { font-weight: var(--font-weight-normal); }
            .font-medium { font-weight: var(--font-weight-medium); }
            .font-semibold { font-weight: var(--font-weight-semibold); }
            .font-bold { font-weight: var(--font-weight-bold); }
        """
        )

        YourApp()
    }
}

Dark Mode Support

@Composable
fun DarkModeTheme() {
    val lightTheme = EnhancedThemeConfig(
        primaryColor = "#3b82f6",
        backgroundColor = "#ffffff",
        textColor = "#1f2937",
        isDarkMode = false,
        designTokens = mapOf(
            "--color-primary" to "#3b82f6",
            "--color-background" to "#ffffff",
            "--color-surface" to "#f9fafb",
            "--color-text" to "#1f2937",
            "--color-text-muted" to "#6b7280",
            "--color-border" to "#e5e7eb"
        )
    )

    val darkTheme = EnhancedThemeConfig(
        primaryColor = "#60a5fa",
        backgroundColor = "#111827",
        textColor = "#f9fafb",
        isDarkMode = true,
        designTokens = mapOf(
            "--color-primary" to "#60a5fa",
            "--color-background" to "#111827",
            "--color-surface" to "#1f2937",
            "--color-text" to "#f9fafb",
            "--color-text-muted" to "#d1d5db",
            "--color-border" to "#374151"
        )
    )

    // Detect system preference or use user setting
    val isDarkMode = remember { /* detect dark mode */ false }
    val currentTheme = if (isDarkMode) darkTheme else lightTheme

    ThemeProvider(theme = currentTheme) {
        GlobalStyle(
            """
            body {
                background-color: var(--color-background);
                color: var(--color-text);
                transition: background-color 0.2s ease, color 0.2s ease;
            }
        """
        )

        YourApp()
    }
}

Theme Composition and Nesting

Nested Themes

@Composable
fun NestedThemeExample() {
    val mainTheme = EnhancedThemeConfig(primaryColor = "#007bff")

    ThemeProvider(theme = mainTheme) {
        Column {
            Text("Main theme color: ${useTheme().primaryColor}")

            // Override theme for specific section
            val cardTheme = EnhancedThemeConfig(primaryColor = "#28a745")
            ThemeProvider(theme = cardTheme) {
                Card {
                    Text("Card theme color: ${useTheme().primaryColor}")
                }
            }

            // Main theme is restored
            Text("Back to main theme: ${useTheme().primaryColor}")
        }
    }
}

Extending Themes

@Composable
fun ExtendedTheme() {
    val baseTheme = EnhancedThemeConfig(
        primaryColor = "#007bff",
        designTokens = mapOf(
            "--color-primary" to "#007bff",
            "--space-4" to "1rem"
        )
    )

    ThemeProvider(theme = baseTheme) {
        // Extend with additional tokens
        val extendedTheme = useTheme().copy(
            designTokens = useTheme().designTokens.orEmpty() + mapOf(
                "--color-accent" to "#ff6b6b",
                "--space-custom" to "1.25rem"
            )
        )

        ThemeProvider(theme = extendedTheme) {
            // Use extended theme
            YourComponent()
        }
    }
}

Theme-Aware Modifiers

@Composable
fun ThemedButton(text: String) {
    Button(
        modifier = Modifier().themeColor("primary").themeBorder(),
        onClick = { /* handle click */ }
    ) {
        Text(text)
    }
}

@Composable
fun ThemedCard() {
    val theme = useTheme()

    Card(
        modifier = Modifier()
            .style("background-color", theme.backgroundColor)
            .style("border", "1px solid ${theme.borderColor}")
            .themeSpacing("md")
    ) {
        Text(
            "Card content",
            modifier = Modifier().style("color", theme.textColor)
        )
    }
}

Integration with GlobalStyle

@Composable
fun ThemedApp() {
    val theme = EnhancedThemeConfig(
        designTokens = mapOf(
            "--primary" to "#007bff",
            "--secondary" to "#6c757d",
            "--radius" to "8px",
            "--space-4" to "1rem"
        )
    )

    ThemeProvider(theme = theme) {
        // Design tokens are automatically injected as CSS variables

        GlobalStyle(
            """
            .btn {
                background: var(--primary);
                border-radius: var(--radius);
                padding: var(--space-4);
                border: none;
                color: white;
                cursor: pointer;
                transition: all 0.2s ease;
            }
            
            .btn:hover {
                filter: brightness(1.1);
            }
            
            .btn-secondary {
                background: var(--secondary);
            }
        """
        )

        // Use themed styles
        Button(
            modifier = Modifier().style("class", "btn"),
            onClick = { /* action */ }
        ) {
            Text("Primary Button")
        }

        Button(
            modifier = Modifier().style("class", "btn btn-secondary"),
            onClick = { /* action */ }
        ) {
            Text("Secondary Button")
        }
    }
}

Best Practices

1. Design System Organization

object DesignTokens {
    // Colors
    val colors = mapOf(
        "--color-primary-50" to "#eff6ff",
        "--color-primary-100" to "#dbeafe",
        "--color-primary-500" to "#3b82f6",
        "--color-primary-600" to "#2563eb",
        "--color-primary-900" to "#1e3a8a"
    )

    // Spacing scale
    val spacing = mapOf(
        "--space-0" to "0",
        "--space-px" to "1px",
        "--space-0_5" to "0.125rem",
        "--space-1" to "0.25rem",
        "--space-2" to "0.5rem",
        "--space-3" to "0.75rem",
        "--space-4" to "1rem"
    )

    // Typography scale
    val typography = mapOf(
        "--text-xs" to "0.75rem",
        "--text-sm" to "0.875rem",
        "--text-base" to "1rem",
        "--text-lg" to "1.125rem",
        "--text-xl" to "1.25rem"
    )

    val all = colors + spacing + typography
}

@Composable
fun DesignSystemTheme(content: @Composable () -> Unit) {
    val theme = EnhancedThemeConfig(
        designTokens = DesignTokens.all
    )

    ThemeProvider(theme = theme) {
        content()
    }
}

2. Consistent Color Usage

@Composable
fun ConsistentColors() {
    val theme = useTheme()

    // Good: Use theme colors consistently
    Box(
        modifier = Modifier()
            .style("background-color", theme.primaryColor)
            .style("color", theme.backgroundColor)
    ) {
        Text("Consistent theming")
    }

    // Avoid: Hardcoded colors
    // Box(modifier = Modifier().style("background-color", "#007bff"))
}

3. Responsive Design with Themes

@Composable
fun ResponsiveComponent() {
    ThemeProvider(
        theme = EnhancedThemeConfig(
            breakpoints = mapOf("md" to "768px"),
            designTokens = mapOf("--breakpoint-md" to "768px")
        )
    ) {
        GlobalStyle(
            """
            .responsive-text {
                font-size: 1rem;
            }
            
            @media (min-width: var(--breakpoint-md)) {
                .responsive-text {
                    font-size: 1.25rem;
                }
            }
        """
        )

        Text(
            "Responsive text",
            modifier = Modifier().style("class", "responsive-text")
        )
    }
}

Platform Support

  • Common: Type-safe theme configuration and composition
  • JVM: CSS variable injection into HTML head
  • JS: Dynamic CSS variable updates in the DOM

Migration from Legacy Theme

// Legacy Theme usage
val legacyTheme = Theme.ThemeConfig(
    primaryColor = "#007bff",
    secondaryColor = "#6c757d"
)
Theme.setTheme(legacyTheme)

// New Enhanced Theme usage  
val enhancedTheme = EnhancedThemeConfig(
    primaryColor = "#007bff",
    secondaryColor = "#6c757d",
    designTokens = mapOf(
        "--color-primary" to "#007bff",
        "--color-secondary" to "#6c757d"
    )
)

ThemeProvider(theme = enhancedTheme) {
    // Components automatically have access to both
    // legacy Theme.getTheme() and useTheme()
}
Architected in Kotlin. Rendered with Materia. Powered by Aether.
© 2026 Yousef.