Distopia / Design System
M3 Baseline
Design System

Distopia

A Kotlin Multiplatform (KMP) design system built on top of Material Design 3 baseline. Targets Android, iOS, Desktop (JVM), and Web (JS/WASM) through Compose Multiplatform. All tokens are derived from the M3 specification and exposed via WriteopiaTheme.

Primary
#6750A4
Secondary Container
#E8DEF8
Background
#FFFBFE
Getting Started

How to use

Wrap your root composable with WriteopiaTheme. It injects both the M3 MaterialTheme and the custom WriteopiaColors semantic layer.

Theme setup Theme.kt
@Composable
fun App() {
    WriteopiaTheme {
        YourContent()
    }
}

// Access M3 tokens
val primary = MaterialTheme.colorScheme.primary

// Access Writeopia semantic tokens
val colors = WriteopiaTheme.colorScheme
val bg     = colors.globalBackground

Foundations

Color

28-token M3 baseline palette. Switch between light and dark using the toggle above — these values mirror Theme.kt exactly.

Primary
primary
onPrimary
primaryContainer
onPrimaryContainer
Secondary
secondary
onSecondary
secondaryContainer
onSecondaryContainer
Tertiary
tertiary
onTertiary
tertiaryContainer
onTertiaryContainer
Error
error
onError
errorContainer
onErrorContainer
Surface & Background
background
onBackground
surface
onSurface
surfaceVariant
onSurfaceVariant
outline
outlineVariant
inverseSurface
inverseOnSurface
inversePrimary
Foundations

Semantic tokens

WriteopiaColors is a custom semantic layer on top of M3 accessed via WriteopiaTheme.colorScheme. Use these tokens to stay consistent across the app without reaching into raw M3 roles.

Token Light Dark Usage
globalBackground
#FFFBFE
#1C1B1F
App-wide canvas background
lightBackground
#E7E0EC
#49454F
Sidebar, panel backgrounds
textLight
#1C1B1F
#E6E1E5
Primary text color
textLighter
#49454F
#CAC4D0
Secondary / subdued text
highlight
#E7E0EC
#49454F
Hover / highlight surfaces
selectedBg
primaryContainer 50%
primaryContainer 35%
Selected list item background
dividerColor
#CAC4D0
#49454F
Horizontal/vertical dividers
linkColor
#6750A4
#D0BCFF
Hyperlinks and inline actions
defaultButton
#6750A4
#D0BCFF
Default/fallback button color
cardBg
surfaceContainer
surfaceContainer
Note card background color
searchBackground
#FFFBFE
#49454F
Search bar input background
Foundations

Typography

Full M3 type scale — 15 styles. Font: Roboto (system default on Android, KMP default elsewhere). Defined in Type.kt and applied via MaterialTheme.typography.

TokenSampleSpec
displayLargeAa57sp · 64sp · -0.25sp · Regular
displayMediumAa45sp · 52sp · 0sp · Regular
displaySmallAa36sp · 44sp · 0sp · Regular
headlineLargeAa32sp · 40sp · 0sp · Regular
headlineMediumAa28sp · 36sp · 0sp · Regular
headlineSmallAa24sp · 32sp · 0sp · Regular
titleLargeTitle Large22sp · 28sp · 0sp · Regular
titleMediumTitle Medium16sp · 24sp · 0.15sp · Medium
titleSmallTitle Small14sp · 20sp · 0.1sp · Medium
bodyLargeBody Large text sample16sp · 24sp · 0.5sp · Regular
bodyMediumBody Medium text sample14sp · 20sp · 0.25sp · Regular
bodySmallBody Small text sample12sp · 16sp · 0.4sp · Regular
labelLargeLabel Large14sp · 20sp · 0.1sp · Medium
labelMediumLabel Medium12sp · 16sp · 0.5sp · Medium
labelSmallLabel Small11sp · 16sp · 0.5sp · Medium
Foundations

Shape

M3 baseline shape scale. Applied via MaterialTheme.shapes. Defined in Shape.kt.

extraSmall
4 dp
small
8 dp
medium
12 dp
large
16 dp
extraLarge
28 dp

Components

Buttons

WButton wraps all five M3 button roles plus a Destructive variant for error-state actions. Use WButtonVariant to select the emphasis level.

All variants WButton.kt
// WButtonVariant
WButton(text = "Save", onClick = { },
    variant = WButtonVariant.Filled)

WButton(text = "Cancel", onClick = { },
    variant = WButtonVariant.Tonal)

WButton(text = "Learn more", onClick = { },
    variant = WButtonVariant.Outlined)

WButton(text = "Skip", onClick = { },
    variant = WButtonVariant.Text)

WButton(text = "Delete", onClick = { },
    variant = WButtonVariant.Destructive)
With leading icon WButton.kt · leadingIcon
WButton(
    text = "New note",
    onClick = { },
    variant = WButtonVariant.Filled,
    leadingIcon = Icons.Default.Add,
    leadingIconDescription = "Add",
)
Components

FAB

Floating Action Button for primary actions. Use M3 FloatingActionButton or ExtendedFloatingActionButton directly.

Variants FloatingActionButton
FloatingActionButton(onClick = { }) {
    Icon(Icons.Default.Add, contentDescription = "Add")
}

ExtendedFloatingActionButton(
    onClick = { },
    icon = { Icon(Icons.Default.Add, contentDescription = null) },
    text = { Text("New") }
)
Components

Chips

Four chip roles: Assist, Filter, Input, Suggestion. Use M3 AssistChip, FilterChip, InputChip, SuggestionChip.

Chip roles *Chip
AssistChip(onClick = { }, label = { Text("Assist") })

FilterChip(selected = true, onClick = { }, label = { Text("Filter") })

InputChip(selected = false, onClick = { }, label = { Text("Input") })

SuggestionChip(onClick = { }, label = { Text("Suggestion") })
Components

Text Fields

Filled and Outlined variants. Use M3 TextField and OutlinedTextField.

Filled & Outlined TextField · OutlinedTextField
var text by remember { mutableStateOf("") }

TextField(
    value = text,
    onValueChange = { text = it },
    label = { Text("Label") }
)

OutlinedTextField(
    value = text,
    onValueChange = { text = it },
    label = { Text("Label") }
)
Components

Cards

Three card styles: Elevated (default shadow), Filled (surface variant), Outlined (border only). Use M3 Card, ElevatedCard, OutlinedCard.

Card variants Card · ElevatedCard · OutlinedCard
Elevated
Default shadow. For primary content surfaces.
Filled
Surface variant background. No elevation.
Outlined
Subtle border. Low emphasis container.
ElevatedCard(modifier = Modifier.width(200.dp)) {
    Text("Elevated", style = MaterialTheme.typography.titleMedium)
    Text("Content", style = MaterialTheme.typography.bodyMedium)
}

OutlinedCard(modifier = Modifier.width(200.dp)) {
    Text("Outlined")
}
Components

List Items

Use M3 ListItem. The selected state uses WriteopiaTheme.colorScheme.selectedBg.

List with selected state ListItem
Design tokens
Updated 2 min ago
•••
Component library
Updated 1 hr ago
•••
Meeting notes
Updated yesterday
•••
ListItem(
    headlineContent = { Text("Design tokens") },
    supportingContent = { Text("Updated 2 min ago") },
    leadingContent = {
        Icon(Icons.Default.Description, contentDescription = null)
    },
    modifier = Modifier.background(WriteopiaTheme.colorScheme.selectedBg)
)
Components

Switch

Toggle binary states. Use M3 Switch.

Switch Switch
Off
On
var checked by remember { mutableStateOf(false) }

Switch(
    checked = checked,
    onCheckedChange = { checked = it }
)
Components

Dialog

Confirmation and alert dialogs. Use M3 AlertDialog.

Alert Dialog AlertDialog
Delete note?
This note will be permanently deleted. You can recover it from Trash within 30 days.
AlertDialog(
    onDismissRequest = { showDialog = false },
    title = { Text("Delete note?") },
    text  = { Text("This note will be permanently deleted.") },
    confirmButton = {
        WButton(text = "Delete", onClick = { },
            variant = WButtonVariant.Destructive)
    },
    dismissButton = {
        WButton(text = "Cancel", onClick = { },
            variant = WButtonVariant.Text)
    }
)
Components

Snackbar

Brief messages with an optional action. Use M3 Snackbar via SnackbarHost.

Snackbar with action Snackbar · SnackbarHost
Note moved to Trash Undo
val snackbarHostState = remember { SnackbarHostState() }

Scaffold(
    snackbarHost = { SnackbarHost(snackbarHostState) }
) {
    // trigger:
    LaunchedEffect(Unit) {
        snackbarHostState.showSnackbar(
            message = "Note moved to Trash",
            actionLabel = "Undo"
        )
    }
}
Components

Progress Indicators

Linear and Circular. Use M3 LinearProgressIndicator and CircularProgressIndicator.

Linear & Circular LinearProgressIndicator · CircularProgressIndicator
Linear — determinate (60%)
Linear — indeterminate
Circular
// Determinate
LinearProgressIndicator(progress = { 0.6f })

// Indeterminate
LinearProgressIndicator()

// Circular
CircularProgressIndicator()
Components

Badge

Notification indicators — dot or count. Use M3 BadgedBox.

Dot & Count BadgedBox · Badge
5
// Dot badge
BadgedBox(badge = { Badge() }) {
    Icon(Icons.Default.Email, contentDescription = "Email")
}

// Count badge
BadgedBox(badge = { Badge { Text("5") } }) {
    Icon(Icons.Default.Notifications, contentDescription = "Notifications")
}