I built a simple API that makes building Design Sy...
# compose
a
I built a simple API that makes building Design Systems in Compose super simple. It lets you define your own 🎨 design tokens and 📐 properties and then creates a
@Composable
Theme function. You can then access those properties from any child composable of the theme function. (code example in 🧵 thread) It features: 🤸‍♀️ Flexibility to its core: you control both how your app and API looks Beautiful defaults: comes with a set of colors, text styles and shapes to build your design system with (which you can customize) 🙅 An API, NOT a design system: Instead of giving you components with required properties, it gives you the API that your components can use to be styled. Checkout the source code & more examples at: https://github.com/composablehorizons/composetheme
Here is how to create & use a theme with custom colors:
Copy code
// define your tokens
val primary = DesignToken<Color>("primary")
val background = DesignToken<Color>("background")

// creates a @Composable Theme function
val Theme = buildComposeTheme {
    colors = mapOf(
        primary to Color.Red,
        background to Color.Gray,
    )
}


@Composable
fun App() {

    // use the ComposeTheme object to access the respective token values
    Theme {
        Box(Modifier.fillMaxSize().background(ComposeTheme.colors[background])) {
            Box(Modifier.size(56.dp).background(ComposeTheme.colors[primary]))
        }
    }

}
k
cool but how would you use this w/ the material components? Or would you need to build every component from scratch?
a
you can either use this to define new properties and tokens which you can use with material components, or use this to build your own components from scratch without to rely on Material compose. What the material components do is they use the
MaterialTheme
object to get the Material's design system properties. If you are building anything that is slightly off Material, you end up building your own components anyway, and this is where ComposeTheme is handy
@kenkyee here is an example on how you would make your own button based off material:
Copy code
val accent = DesignToken<Color>("accent")
val text = DesignToken<Color>("text")

val NewTheme = buildComposeTheme {
    colors = mapOf(
        accent to Color.Yellow,
        text to Color.Yellow,
    )
}

@Composable
fun UsingWithMaterialDemo() {
    NewTheme {
        CustomButton(onClick = {}) {
            BasicText("Click me")
        }
    }
}

@Composable
fun CustomButton(onClick: () -> Unit, content: @Composable RowScope.() -> Unit) {
    // this is the Material 3 button
    Button(
        onClick = onClick,
        content = content,
        colors = ButtonColors(
            containerColor = ComposeTheme.colors[accent],
            disabledContainerColor = ComposeTheme.colors[accent].copy(alpha = 0.33f),
            contentColor = ComposeTheme.colors[text],
            disabledContentColor = ComposeTheme.colors[text].copy(alpha = 0.33f)
        )
    )
}
k
Yep...seems like this is only for pure custom components unless you have a MaterialTheme generator on this. That's what I was trying to confirm 🙂
a
it is indeed built with pure custom components in mind yes as that's the end goal of teams usually. I can see people starting off using material components though. can u clarify what you mean by material theme generator?
k
fun ComposeTheme.toMaterialTheme(): MaterialTheme { ... }
a
@kenkyee gotcha. would love to support that
👍 1
j
this is quite cool Alex, nice one! I agree with Ken, I think if you add an adapter to MaterialTheme you’ll gain a ton of traction with this because it’ll be super useful for teams who have large token-based design systems and custom UIs as well as those who are happy to build on top of material if you do add an adapter, perhaps ship it as a separate artefact like
composetheme-material3
so those who have 100% custom components aren’t forced to depend on material libs excellent library though, great work
👌 1
1
a
thanks folks. you gave me a some good ideas on how to achieve this and i am already working on a design
🚀 1
New version is up folks 🚀 thanks for the feedback You can now extend your compose theme with any existing design system. I made sure to include 2 new modules (thanks for the suggestion @james) that allow you to extend your theme using material compose. This way you get the flexibility of Compose Theme and add any tokens you like, while keeping the rest of your code base as is (and continue using the material components):
Copy code
val buttonLabel = DesignToken<TextStyle>("buttonLabel")

val Material3ThemeExtended = buildComposeTheme {
    textStyles = DesignTokens(
       buttonLabel to TextStyle(fontSize = 12.sp, lineHeight = 16.sp)
    )

   extendMaterial3 {
      colorScheme = lightColorScheme(
         primary = Color.Red,
      )
      typography = Typography()
      shapes = Shapes()
   }
}

@Composable
fun App() {
   Material3ThemeExtended {  
      Button(onClick = { }) { // this button is rendered Red
         Text("Click me!", style = ComposeTheme.textStyles[buttonLabel])
      }
   }
}
A bunch of other goodies added, such as being able to use any kind of Type you want for your design system. More info at https://github.com/composablehorizons/composetheme
👍 2
🚀 1