Skip to content

App Style Guide

francelu edited this page Dec 6, 2024 · 33 revisions

App Style Guide

The purpose of this page is to

  • serve as a reference for the style that the app should follow
  • be consistent with the app style

This page can be constantly updated, modified and re-discussed along the development of the app!

Padding

Variable Name Default Value Description/Usage
extraSmall 0.dp Not yet used.
small1 0.dp Base spacing value used for scaling other dimensions.
small2 small1 * 2 Scaffold's LazyColumn vertically spacedBy().
Add spacing to the top of a component on a screen (see ProfileSection).
In a button, space between the icon and the text.
small3 small1 * 4 Scaffold's LazyColumn vertical padding.
Card's horizontal padding (and spacing for alert item).
medium1 small1 * 6 Auth screens' in-between padding.
Auth's Box horizontal padding.
medium2 small1 * 8 Not yet used.
medium3 small1 * 10 Scaffold's LazyColumn horizontal padding.
large small1 * 15 Auth screen's LazyColumn's horizontal padding.
borderLine 1.dp Auth's Box's border.
HorizontalDivider thickness.
If needed, a Button's border.
buttonHeight 40.dp Not yet used, but should be used (?).
cardElevation 4.dp Card's elevation = CardDefaults.cardElevation(...).
cardRoundedSize small1 * 3 Card's shape is RoundedCornerShape(size = ...).
iconSize 0.dp Normal Icon's Modifier.size(...).
iconSizeSmall iconSize * 2 / 3 Smaller Icons' size (e.g., alert's cards).
iconButtonSize iconSize * 2 The size of the button around an icon (for IconButton).
profilePictureSize small1 * 50 GlideImage's Modifier.size(...).
roundedPercent 50 Round perfectly the left and right edges RoundedCornerShape(%) of a component.

Typography

The typographies font size are adapted to the screens' size. However, there are only a few ones that have been set from the Typography:

Text Style Usage Example
headlineMedium Big Buttons CreateAlertScreen's Button
headlineSmall TabRow's Tab text Tabs in AlertListsScreen
titleLarge Extra big textx AuthenticationWelcomeText
titleMedium TopAppBar title TopAppBar's title Text
titleSmall "Sections" on screens "Name" field on ProfileScreen, ProfileSection
bodyLarge Short and big instructions on screens Authentication screens' instruction texts
bodyMedium Smaller / long instructions on screens
Normal buttons
Profile's description and save button
labelLarge placeholder in OutlinedTextField + DropDownMenus OutlinedTextFields, DropdownMenus
Alert's name
labelMedium label in OutlinedTextField + DropDownMenus OutlinedTextFields, DropdownMenus
Alert's data & edit button text
labelSmall BottomNavigationBars texts NavigationBarItem's label's text

If further typographies will be needed, the above will be updated.

Example templates

IMPORTANT: the layout of the scaffold

// Screen
Scaffold(
    modifier = Modifier.fillMaxSize().testTag(CreateAlertScreen.SCREEN),
    topBar = { TopAppBar(title = SCREEN_TITLE) },
    bottomBar = {
      BottomNavigationMenu(
          onTabSelect = { route -> navigationActions.navigateTo(route) },
          tabList = LIST_TOP_LEVEL_DESTINATION,
          selectedItem = navigationActions.currentRoute(),
      )
    },
) { paddingValues ->
  Column(
      modifier =
          Modifier.fillMaxSize()
              .padding(paddingValues)
              .padding(
                  horizontal = MaterialTheme.dimens.medium3,
                  vertical = MaterialTheme.dimens.small3,
              )
              .verticalScroll(rememberScrollState()),
      horizontalAlignment = Alignment.CenterHorizontally,
      verticalArrangement =
          Arrangement.spacedBy(MaterialTheme.dimens.small2, Alignment.CenterVertically),
  ) {
    // Components on the screen should have no padding.
    // However, e.g. profile section, if you need more padding, it should be as follow
    Text(
      text = ...,
      modifier = Modifier.padding(vertical = MaterialTheme.dimens.small1),
    )
  }
}

Other components

OutlinedTextFields

// Example with message field
var isFocused by remember { mutableStateOf(false) }
OutlinedTextField(
    modifier =
        Modifier.fillMaxWidth()
            .wrapContentHeight()
            .testTag(CreateAlertScreen.MESSAGE_FIELD)
            .onFocusEvent { focusState -> isFocused = focusState.isFocused },
    value = message,
    onValueChange = { message = it },
    textStyle = MaterialTheme.typography.labelLarge,
    label = {
      Text(
          text = MESSAGE_FIELD_LABEL,
          style =
              if (isFocused || message.isNotEmpty()) MaterialTheme.typography.labelMedium
              else MaterialTheme.typography.labelLarge)
    },
    placeholder = {
      Text(text = MESSAGE_FIELD_PLACEHOLDER, style = MaterialTheme.typography.labelLarge)
    },
    minLines = 3,
)

Texts

// General instruction texts, example from auth instruction text
Text(
    modifier =
        Modifier.fillMaxWidth().wrapContentHeight().testTag(SignInScreen.INSTRUCTION_TEXT),
    text = SIGN_IN_INSTRUCTION,
    textAlign = TextAlign.Center,
    style = MaterialTheme.typography.bodyLarge,
)
// Longer instruction texts, example from create alert
Text(
    text = INSTRUCTION_TEXT,
    modifier = Modifier.testTag(CreateAlertScreen.INSTRUCTION_TEXT),
    textAlign = TextAlign.Center,
    style = MaterialTheme.typography.bodyMedium,
)

Buttons

// Buttons
Button(
    modifier = Modifier.wrapContentSize().testTag(testTag),
    onClick = ...,
    enable = true,
) {
  Text(text = text, style = MaterialTheme.typography.bodyMedium)
}

Cards

// Example from alert lists' [NoAlertDialog]
Card(
    modifier = Modifier.wrapContentSize().testTag(AlertListsScreen.NO_ALERTS_CARD),
    shape = RoundedCornerShape(size = MaterialTheme.dimens.cardRoundedSize),
    elevation = CardDefaults.cardElevation(defaultElevation = MaterialTheme.dimens.cardElevation),
) {
  Column(
      modifier = Modifier.wrapContentSize().padding(MaterialTheme.dimens.small2),
      horizontalAlignment = Alignment.CenterHorizontally,
      verticalArrangement =
          Arrangement.spacedBy(MaterialTheme.dimens.small2, Alignment.CenterVertically),
  ) {
   ...
  }
}

Color Scheme

Here's a link to Color system.
For best practices, see Color roles or see this comment in issue #179.

Primary Secondary Tertiary Error
primary secondary tertiary error
onPrimary onSecondary onTertiary onError
primaryContainer secondaryContainer tertiaryContainer errorContainer
onPrimaryContainer onSecondaryContainer onTertiaryContainer onErrorContainer
Background ? Surface Outline Scrim Inverse
background surface outline scrim inverseSurface
onBackground onSurface - - inverserOnSurface
- surfaceVariant outlineVariant - inversePrimary
- onSurfaceVariant - - -
- sufaceDim - - -
- surfaceBright - - -

surfaceContainer-Lowest, Low, -, High, Highest

Basic Rules

Note: This wiki page is mostly a paraphrasing and summary of the style guide found in https://m3.material.io/.

These are the very basic rules to follow when implementing the design.

Standard way of implementing the UI

Contrast

  • Use high contrast.
  • Contrast ratios: represent how different one color is from another color. The greater the difference in relative luminance between the colors, the greater the difference between the colors.
Text type Color contrast ratio
Large text (at 14 pt bold/18 pt regular and up) and graphics 3:1 against the background
Small text 4.5:1 against the background

Structure

  • Place important actions at the top or bottom of the screen (reachable with shortcuts)
  • Place related items of a similar hierarchy next to each other

Headings

  • Identify headings based on content hierarchy, rather than visual styling
  • Headings should not skip a level, for example, don't go from H2 to H4 without using an H3
  • Map content on your pages to headings (H1–H6) in sequential order based on the hierarchy of your content
  • A single H1 for the page title is recommended
Heading Font size (in dp)
H1 32
H2 24
H3 21
H4 16
H5 12
H6 11

Target sizes

  • Touch targets are the parts of the screen that respond to user input, extending beyond the visual bounds of an element.

Design Tokens

  • Design tokens point to style values like colors, fonts, and measurements
  • Use design tokens instead of hardcoded values
  • Each token is named for how or where it’s used (for example, md.fab.container.color sets the container color for a FAB)
  • Even if a token’s end value is changed, its name and use remain the same
  • They ensure consistency between the mock-up and the implementation
  • A token consists of:
    • A code-like name, such as md.ref.palette.secondary90
    • An associated value, such as #E8DEF8
  • Types of tokens:
    • Reference tokens: All available tokens with associated values
    • System tokens: Decisions and roles that give the design system its character, from color and typography, to elevation and shape
    • Component tokens: The design attributes assigned to elements in a component, such as the color of a button icon

Theme

From #16 comments