Skip to main content

Theming

Make the community look like your app: its colors, fonts, logo, light or dark mode and the title bar of the main feed. To replace the SDK icons, see Icons and images.

Before you begin​

  • Complete the Quickstart: the SDK is initialized and the community screen opens in your app.
  • Collect your brand values: a primary color with a lighter and a darker variation, the color of text shown over the primary color, and your fonts and logo as app resources.

How it works​

The theme is a set of optional values. A value you do not set keeps the SDK default, so you only override what differs from your brand. Pass colors without transparency.

PlatformWhere the theme is set
AndroidThe OctopusTheme composable that wraps the Octopus screens, usually in the container of octopusComposables.
iOSThe \.octopusTheme environment value on OctopusHomeScreen.
FlutterThe theme parameter of OctopusHomeScreen or showOctopusHomeScreen().
React NativeThe theme object passed to initialize().
UnityThe Octopus SDK > Theme Configuration window of the Editor, or OctopusSDK.SetTheme(...) at runtime.

The screens below show where each color is used, in light and dark mode:

Light Mode Dark Mode


1. Set the colors​

The color scheme has four main values: the primary color, its low-contrast and high-contrast variations, and the color of content shown over the primary color.

When no colorScheme is given, the SDK derives its palette from your app's MaterialTheme.colorScheme. To set the colors yourself, start from octopusLightColorScheme() or octopusDarkColorScheme() and override the values you need. Their defaults are the Octopus palette: a 0xFF141414 primary in light mode, a white primary in dark mode.

octopusComposables(
navController = navController,
container = { _, content ->
OctopusTheme(
colorScheme = if (isSystemInDarkTheme()) {
octopusDarkColorScheme(
primary = yourDarkPrimaryColor,
primaryLow = yourDarkLowContrastColor,
primaryHigh = yourDarkHighContrastColor,
onPrimary = yourDarkOnPrimaryColor,
)
} else {
octopusLightColorScheme(
primary = yourLightPrimaryColor,
primaryLow = yourLightLowContrastColor,
primaryHigh = yourLightHighContrastColor,
onPrimary = yourLightOnPrimaryColor,
)
},
content = content,
)
},
)
tip

The OctopusThemeConfigurator shows a live preview of the Octopus screens while you pick your colors.

Android availableiOS ≥ 1.13.0Flutter ≥ 1.13.1React Native ≥ 1.13.0Unity ≥ 1.12.8

Two more optional colors: background colors every community screen, and link colors the URLs shown in posts and comments. An unset value keeps the SDK default.

The default link color is 0xFF1D9BD1 in both schemes. The default background is white in light mode and 0xFF141414 in dark mode.

octopusLightColorScheme(
background = yourBackgroundColor,
link = yourLinkColor,
)

2. Set the fonts​

The SDK uses six text styles: title1, title2, body1, body2, caption1 and caption2. Their default sizes are 26, 22, 18, 16, 14 and 12. The picture below shows where each style is used:

Text Styles

Each style is a Compose TextStyle. The default title1 also sets a 36 sp line height.

OctopusTheme(
typography = OctopusTypographyDefaults.typography(
title1 = TextStyle(fontSize = 24.sp, lineHeight = 32.sp),
body1 = TextStyle(fontSize = 17.sp),
),
content = content,
)

Font family and weight​

Android availableiOS availableFlutter ≥ 1.13.1React Native ≥ 1.13.0Unity available

Use your own font family and weight for every SDK text. The SDK renders its screens natively, so the font must exist as a native resource of your app: a font resource under res/font/ on Android, and a font declared under UIAppFonts in Info.plist on iOS, referenced by its PostScript name. A name that does not resolve is logged as a warning, and the SDK keeps its default font.

Set fontFamily and fontWeight on each TextStyle.

val brand = FontFamily(Font(R.font.inter_semibold, FontWeight.SemiBold))

OctopusTypographyDefaults.typography(
title1 = TextStyle(fontFamily = brand, fontWeight = FontWeight.SemiBold, fontSize = 26.sp),
body1 = TextStyle(fontFamily = brand, fontSize = 18.sp),
)

Android not availableiOS availableFlutter ≥ 1.13.1React Native ≥ 1.13.0Unity available

The theme can set a dedicated font for the navigation-bar items. When it is not set, navigation-bar items follow body1.

Navigation-bar item size is not yet available on Android.


The logo is shown on the main feed and on the profile creation screen. Without one, the SDK keeps its default. To replace the SDK icons, see Icons and images.

logo takes a composable lambda that returns a Painter.

OctopusTheme(
images = OctopusImagesDefaults.images(
logo = { painterResource(R.drawable.your_logo) },
),
content = content,
)

4. Choose light or dark mode​

By default, the community follows the appearance of the device. You can give separate colors for light and dark mode, or force one mode for the community only.

The theme is a composable, so pick the scheme with isSystemInDarkTheme() or with your own app setting, as in Set the colors.

OctopusTheme(
colorScheme = if (useDarkCommunity) octopusDarkColorScheme() else octopusLightColorScheme(),
content = content,
)

5. Customize the top app bar​

The top app bar of the main feed shows a title: your logo or a short text. Keep a text title under 18 characters. You can also align it and, where available, color the bar with the primary color. The other screens keep their own titles.

OctopusHomeScreen takes titleText, titleCentered (default false) and logo, a composable lambda that overrides the theme logo. For the bar colors and title style, pass OctopusTopAppBarDefaults.topAppBar(...) to the topAppBar parameter of OctopusTheme (see Theme each screen).

OctopusHomeScreen(
navController = navController,
titleCentered = true,
logo = { painterResource(R.drawable.your_logo) },
)

Leading navigation button​

Android ≥ 1.12.3iOS ≥ 1.12.2Flutter availableReact Native ≥ 1.13.0Unity not available

When you present the community in your own container, such as a modal, a bottom sheet or a pushed route, show a close or back button on its root screen. The button calls your code, and your code dismisses the container.

leadingNavigationIcon takes NavigationIconType.Close or NavigationIconType.Back, calls onBack on tap, and overrides backIcon.

OctopusHomeScreen(
navController = navController,
leadingNavigationIcon = NavigationIconType.Close,
onBack = { /* dismiss your modal or pop your route */ },
)

Navigation container​

Android not availableiOS ≥ 1.12.2Flutter availableReact Native ≥ 1.13.0Unity ≥ 1.13.0

The SDK can drive its iOS screens with a NavigationStack (iOS 16 and later) or with its automatic container. Choose navigationStack when you present the community in a modal; keep the automatic container in a tab or on an existing navigation stack.

Navigation container is not yet available on Android.

Root screen leading icon​

Android availableiOS ≥ 1.12.2Flutter availableReact Native ≥ 1.13.1Unity not available

On the root screen, the leading icon has nothing left to go back to inside the community, so it calls your code. On the other screens, it goes back inside the community and does not call you.

onBack is called on the root screen. Its default is navController.navigateUp().

OctopusHomeScreen(
navController = navController,
onBack = { navController.popBackStack() },
)

6. Force the community orientation​

Android not availableiOS not availableFlutter not availableReact Native not availableUnity ≥ 1.12.5

Show the community in a fixed orientation, for example a portrait community inside a landscape game. The setting applies to the community only: your game keeps its own orientation, which comes back when the community closes.

Force the community orientation is not yet available on Android.


7. Theme each screen​

Android availableiOS not availableFlutter not availableReact Native not availableUnity not available

Change any part of the theme (colors, typography, images, top app bar) depending on the screen shown. The container of octopusComposables receives the back stack entry of each screen.

hasRoute comes from androidx.navigation. OctopusTopAppBarDefaults.topAppBar needs @OptIn(ExperimentalMaterial3Api::class).

octopusComposables(
navController = navController,
container = { backStackEntry, content ->
OctopusTheme(
colorScheme = when {
backStackEntry.destination.hasRoute<OctopusDestination.PostDetails>() ->
octopusColorScheme().copy(
background = if (isSystemInDarkTheme()) Color.Black else Color.White
)
else -> octopusColorScheme()
},
topAppBar = when {
backStackEntry.destination.hasRoute<OctopusDestination.Home>() ->
OctopusTopAppBarDefaults.topAppBar(
title = { _ -> OctopusTopAppBarTitle(text = "My Community") }
)
else -> OctopusTopAppBarDefaults.topAppBar()
},
content = content,
)
},
)

Behavior and limits​

  • Every theme value is optional. An unset value keeps the SDK default, and an explicit font size is a base size that still follows the reader's text-size setting.
  • Colors must be opaque. Transparent colors are not supported.
  • Icons, reaction images and screen-state illustrations are set in the same theme, on the Icons and images page.

Next steps​