-
Notifications
You must be signed in to change notification settings - Fork 0
Using Design System
Ali Sadeghi edited this page Jan 27, 2026
·
5 revisions
Auto-activates when: Working in feature/*/ui/ directories, creating Composables, mentions "UI", "screen", "component"
Ensures X-components are used instead of Material3, preventing design drift.
// ❌ Material3 // ✅ X-Components
Scaffold → XScaffold
Button → XButton
OutlinedButton → XOutlinedButton
TextButton → XTextButton
Text → XText
TextField → XTextField
OutlinedTextField → XOutlinedTextField
Card → XCard
Icon → XIcon
IconButton → XIconButton
Switch → XSwitch
Checkbox → XCheckbox
RadioButton → XRadioButton
Divider → XDivider
CircularProgressIndicator → XLoadingIndicator> Create a login form with email, password, and submit buttonAI generates:
@Composable
fun LoginForm(
email: String,
password: String,
onEmailChange: (String) -> Unit,
onPasswordChange: (String) -> Unit,
onSubmit: () -> Unit
) {
Column {
XTextField(
value = email,
onValueChange = onEmailChange,
label = { XText("Email") }
)
XTextField(
value = password,
onValueChange = onPasswordChange,
label = { XText("Password") },
visualTransformation = PasswordVisualTransformation()
)
XButton(
onClick = onSubmit,
text = "Login"
)
}
}> Create a product card component with image, title, price, and add to cart buttonAI generates using XCard, XText, XButton (not Material3 components)
// ✅ Correct (in feature modules)
XScaffold {
// Feature content - NO theme wrapper needed
}
// ❌ Wrong (in feature modules)
XTheme { // XTheme is app-level only!
XScaffold { }
}
// ❌ Wrong
MaterialTheme {
Scaffold { }
}Note: XTheme is applied at the App level (composeApp). Feature modules should NOT wrap their screens in XTheme.
All screens must follow the ScreenRoot pattern:
// {Feature}Screen - ViewModel wrapper, collects state
@Composable
fun ProductListScreen(
viewModel: ProductListViewModel = koinViewModel(),
onProductClick: (String) -> Unit,
onBackClick: () -> Unit
) {
val state by viewModel.state.collectAsState()
ProductListScreenRoot(
uiState = state,
onProductClick = onProductClick,
onBackClick = onBackClick,
onRetry = viewModel::loadProducts
)
}
// {Feature}ScreenRoot - ViewModel-independent, takes UiState + callbacks (TESTABLE)
@Composable
fun ProductListScreenRoot(
uiState: ProductListUiState,
onProductClick: (String) -> Unit,
onBackClick: () -> Unit,
onRetry: () -> Unit
) {
// UI implementation
}Key benefits:
- ScreenRoot is testable without ViewModel mocking
- Clear separation of concerns
- UI tests target ScreenRoot with fixture-based UiState
When spec exists at .claude/docs/{feature}/spec.md:
- Validates 4-state pattern matches spec's State Management section
- Ensures UI flows match spec's Navigation section
- Verifies all documented callbacks exist in ScreenRoot
- Reports drift if UI implementation differs from spec
Back to Skills