The appearance of the SDK can be customized by setting the Storyteller.theme global property or StorytellerListView.theme view property. This requires a UiTheme configuration object.

Note: global theme should be set before any of the Storyteller list views are inflated. Note: this property will be used as fallback theming style used to render story items in lists and activities launched from list. See StorytellerListView.

UiTheme is data structure containing tree of the parameters. While it's possible to manually declare a whole structure of this data object, for convenience it's recommended to use UiTheme DSL for that purpose.

Defining a theme

A Theme contains various settings which change visual aspects of the Storyteller SDK. All properties are optional. In general, it should be possible to obtain a custom look by only using the colors properties - however, if you require more fine-grained control, that is also available.


Colors Description Default Value Dark Value
theme.colors.primary Default accent color used throughout the UI. Usually the primary brand color. Used for: Unread Indicators, loading progress bars and spinners on Android. #1C62EB
theme.colors.success Used to indicate correct answers in Quizzes. #3BB327
theme.colors.alert Used to indicate wrong answers in quizzes and for the 'Live' indicator on Live stories. #E21219
theme.colors.white.primary Used for story names on rectangular tiles and in the player. Also used for all primary text in dark mode by default. #ffffff
theme.colors.white.secondary Used for secondary text in dark mode. rgba({theme.colors.white.primary}, 85%)
theme.colors.white.tertiary Used for tertiary text in dark mode, eg the time stamp in the player header. Also used for the selected poll option border. rgba({theme.colors.white.primary}, 70%) Used for primary text in light mode by default. #1A1A1A Used for secondary text in light mode. rgba({}, 85%) Used for tertiary text and minor UI elements in light mode. rgba({}, 70%)


Font Description Default Value Dark Value
theme.font Font to be used throughout the SDK, defaults to the system font on each platform or inherits the container font on web. Font definition requires access to different weights to work properly. System Font


Primitives Description Default Value Dark Value
theme.primitives.cornerRadius Default corner radius used for Rectangular Tiles, Buttons and Poll/Quiz answers. 4 dp


Lists Description Default Value Dark Value
theme.lists.row.tileSpacing The space between each Story Tile in a row. 8 dp
theme.lists.row.startInset The space before the first tile in a row. 12 dp
theme.lists.row.endInset The space after the last tile in a row. 12 dp
theme.lists.grid.tileSpacing The space between Story Tiles in a grid, both vertically and horizontally. 8 dp
theme.lists.grid.columns Number of columns in the grid. 2 dp
theme.lists.grid.topInset The space at the top of the grid 12 dp
theme.lists.grid.bottomInset The space at the bottom of the grid 12 dp

Story tiles

Story Tiles Description Default Value Dark Value
theme.storyTiles.chip.textSize Text size for the New Indicator and Live Indicator. 11 sp
theme.storyTiles.liveChip.unreadImage Image to be used in place of default unread Live Indicator. default icon
theme.storyTiles.liveChip.readImage Image to be used in place of default read Live Indicator. default icon
theme.storyTiles.liveChip.unreadBackgroundColor Background color of the Live Indicator when the story contains unread pages. {theme.colors.alert}
theme.storyTiles.liveChip.readBackgroundColor Background color of the Live Indicator when all pages have been read. {}
theme.storyTiles.liveChip.textColor Text color of the Live Indicator. {theme.colors.white.primary}
theme.storyTiles.title.textSize Size of the Story Title on a Tile. 11 sp
theme.storyTiles.title.lineHeight Line height of the Story Title on a Tile. 13 sp
theme.storyTiles.title.alignment Alignment of the text in a tile, can be Gravity.START/CENTER/END center
theme.storyTiles.circularTile.title.unreadTextColor Text color of Circular Story Tile Titles in unread state {} {theme.colors.white.primary}
theme.storyTiles.circularTile.title.readTextColor Text color of Circular Story Tile Titles in read state {} {theme.colors.white.tertiary}
theme.storyTiles.circularTile.unreadIndicatorColor The color of the ring around Circular Story Tiles. {theme.colors.primary}
theme.storyTiles.circularTile.readIndicatorColor The color of the ring around Circular Story Tiles in read state #C5C5C5 (taken from app layout)
theme.storyTiles.rectangularTile.title.textColor Text color of the Story Title in Rectangular Tiles. {theme.colors.white.primary}
theme.storyTiles.rectangularTile.chip.alignment Alignment of the New Indicator and Live Indicator in Rectangular Tiles, can be start, center or end. end
theme.storyTiles.rectangularTile.padding Internal padding for Rectangular Story Tiles, creates space between Story Name or New Indicator and tile edge. 8 dp
theme.storyTiles.rectangularTile.unreadIndicator.image Image to be used in place of default New Indicator on Rectangular Tiles. null
theme.storyTiles.rectangularTile.unreadIndicator.backgroundColor Background color of the New Indicator. {theme.colors.primary}
theme.storyTiles.rectangularTile.unreadIndicator.textColor Text color of the New Indicator. {theme.colors.white.primary}
theme.storyTiles.rectangularTile.unreadIndicator.textSize Text size for the New Indicator. 11 sp


Player Description Default Value Dark Value
theme.player.showStoryIcon Shows the circular Story icon before the Story Name in the Player. FALSE
theme.player.showTimestamp Shows a timestamp after the Story Name in the Player, eg “2h” to show the Story was published 2 hours ago. TRUE
theme.player.showShareButton Shows the Share button in the Player, applies to all Page types and Engagement Units. Setting to FALSE entirely disables sharing in Storyteller. TRUE
theme.player.liveChipImage Image used in place of Live Chip before Live Story Titles. default icon
theme.player.icons.share Share button image to be used in place of default share icon. default icon


Buttons Description Default Value Dark Value
theme.buttons.backgroundColor Background color of the button for Share buttons at the end of Quizzes. {theme.colors.white.primary}
theme.buttons.textColor Text color of the button used on the share button at the end of quizzes. {}
theme.buttons.textCase Sets the text case for the button on the Instructions Screen and share buttons at the end of Quizzes. (TextCase.UPPER/DEFAULT/LOWER default
theme.buttons.cornerRadius Sets the corner radius for the button on the Instructions Screen and share buttons at the end of Quizzes. Any value greater than half the height of the button will create a pill shape. {theme.primitives.cornerRadius}


Instructions Description Default Value Dark Value Show the Instructions Screen the first time a user opens Storyteller. Set to FALSE to entirely hide the Instructions screen. TRUE
theme.instructions.headingColor Heading color of the text used on the Instructions Screen. {} {theme.colors.white.primary}
theme.instructions.subHeadingColor Subheading color of the text used on the Instructions Screen. {} {theme.colors.white.secondary}
theme.instructions.backgroundColor Background color of the Instructions Screen. {theme.colors.white.primary} {}
theme.instructions.icons A set of icons used for each instruction on the Instructions Screen default set of icons
theme.instructions.button.backgroundColor Background color of the button used on the Instructions Screen. {} {theme.colors.white.primary}
theme.instructions.button.textColor Text color of the button used on the Instructions Screen. {theme.colors.white.primary} {}

Engagement units

Engagement Units Description Default Value Dark Value
theme.engagementUnits.poll.answerTextColor Answer text color used in Poll Answers. {}
theme.engagementUnits.poll.percentBarColor Background color of the Percentage Bar in Poll Answers. #CDD0DC
theme.engagementUnits.poll.selectedAnswerBorderColor Border color added to the selected Poll Answer. {theme.colors.white.tertiary}
theme.engagementUnits.poll.answeredMessageTextColor Color of the vote count or “Thanks for Voting!” message shown to users. {theme.colors.white.tertiary}
theme.engagementUnits.poll.selectedAnswerBorderImage Border image used for the selected Poll Answer. Overwrites selectedAnswerBorderColor and can be used to create a shimmer animation as the border image is rotated in when an answer is selected. null
theme.engagementUnits.poll.showVoteCount Shows the approximate number of Poll responses after a user selects an answer. If FALSE we show a 'Thanks for voting!' message. TRUE
theme.engagementUnits.poll.showPercentBarBackground Adds a striped background under the Percentage Bar in Poll Answers. FALSE
theme.engagementUnits.triviaQuiz.correctColor Color used for correct answers in Trivia Quizzes. {theme.colors.success}
theme.engagementUnits.triviaQuiz.incorrectColor Color used for incorrect answers in Trivia Quizzes. {theme.colors.alert}

Theme DSL usage

Theme builder initialization

Theme builder is initialized by the buildTheme(context) method lambda passed after the method acts in the builder scope.


Storyteller.theme = buildTheme(context) {
  light.colors.primary = ofHexCode("#FF00FF")

Accessing properties in the builder scope

There are 2 equivalent ways of accessing builder properties:

  • By scopes
    buildTheme(context) {
    light {
      instructions {
        button {
          backgroundColor = ofHexCode("#00FF00")
  • By properties
    buildTheme(context) {
      light.instructions.button.backgroundColor = ofHexCode("#00FF00")

Both approaches produce the same effect.

Light and Dark builder variants

In the builder context two variants are present:

  • light
  • dark

Although they have identical structure they are build with the different set of fallbacks. for instance default theme.storyTiles.circularTile.title.unreadTextColor will fallback to the default value of in the light mode or theme.colors.white.primary in the dark mode.

Selection of active theme will be done using current phone UI mode and StorytellerListView.uiStyle property value. See StorytellerListView

For coding convenience if you do not intent use light and dark mode and relay on default fallback you can use from inline method to copy already set values from one theme to the other.

buildTheme(context) {
  light {
    colors {
      primary = ofHexCode("#FF00FF")
      success = ofHexCode("#00FF00")
  dark from light

Above code will set all properties of dark to the current state of light builder. This method is useful to avoid lengthy typing - common parameter can be assigned one and copied to the other variant.

Setting properties of particular type


Color properties are expected to be android @ColorINt. They can be initialized with anything that return such type e.g they can be resolved color from resources, Color.argb() Color.BLUE and so on for convenience ofHexColor(string) method was provided - it accepts 6 or 8 hex digits prefixed by the #

val red = ofHexColor("#FF0000")
val semiTransparentRed = ofHexColor("#55FF0000")

Note: when using resources colors mind that they are resolved at the moment of building theme, NOT at the moment of accessing.


For the properties accepting Drawable type any Drawable extending object can be provided.

val myTheme = buildTheme(context){
  light.engagementUnits.poll.selectedAnswerBorderImage = GradientDrawable(
    intArrayOf(Color.CYAN, Color.MAGENTA)


val storyRowView = StoryRowView(context)

storyRowView.theme = buildTheme(context) {
      light {
        colors {
          primary = ofHexCode("#FF0000")
          success = ofHexCode("#00FF00")
          alert = ofHexCode("#C50511")

        font = ResourcesCompat.getFont(ctx, R.font.custom_font)

        lists {
          row {
            tileSpacing = 8
            startInset = 12
            endInset = 12
          grid {
            tileSpacing = 8
            columns = 2

        storyTiles {
          title {
            textSize = 11
            lineHeight = 13
            alignment = Gravity.START
          rectangularTile {
            padding = 8
            unreadIndicator.alignment = Gravity.END
            unreadIndicator.textColor = ofHexCode("#000FF")
            unreadIndicator.textSize = 11
        buttons.cornerRadius = 24
        buttons.textCase = TextCase.UPPER
        instructions {
          icons {
            forward = ContextCompat.getDrawable(ctx, R.drawable.ic_forward_light)
            pause = ContextCompat.getDrawable(ctx, R.drawable.ic_pause_light)
            back = ContextCompat.getDrawable(ctx, R.drawable.ic_back_light)
            move = ContextCompat.getDrawable(ctx, R.drawable.ic__swipe_light)
      dark from light
Forward Arrow