Custom Themes#
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 StorytellerLists or StorytellerListViews (legacy).
Showcase examples#
UiTheme is a 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#
The colors property on theme is used to establish a set of base colors for the SDK to use.
| 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, e.g. the time stamp in the player header. Also used for the selected Poll option border. | rgba({theme.colors.white.primary}, 70%) | |
theme.colors.black.primary |
Used for primary text in light mode by default. | #1A1A1A | |
theme.colors.black.secondary |
Used for secondary text in light mode. | rgba({theme.colors.black.primary}, 85%) | |
theme.colors.black.tertiary |
Used for tertiary text and minor UI elements in light mode. | rgba({theme.colors.black.primary}, 70%) |
Font#
Use the font property to set a custom font for use throughout the UI.
| Font | Description | Default Value | Dark Value |
|---|---|---|---|
theme.font |
Font to be used throughout the UI, 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#
The primitives object contains base values which are used throughout the UI.
| Primitives | Description | Default Value | Dark Value |
|---|---|---|---|
theme.primitives.cornerRadius |
Default corner radius used for Rectangular Tiles, Buttons and Poll/Quiz answers. | 8 dp |
Lists#
The lists customize properties of the various list types available from the SDK.
| Lists | Description | Default Value | Dark Value |
|---|---|---|---|
theme.lists.title |
The style of the title on Story or Clip tile. | 8 dp | |
theme.lists.row.tileSpacing |
The space between each Story and Clip 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 and Clip 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 first row in a grid. | 0 dp | |
theme.lists.grid.bottomInset |
The space at the bottom of the last row in a grid. | 0 dp | |
theme.lists.backgroundColor |
Required for outline on Live chip and fade to the side of the row | {theme.colors.white.primary} | {theme.colors.black.primary} |
theme.lists.animateTilesOnReorder |
This option allows you to enable animation in lists when updating items | true | true |
theme.lists.enablePlayerOpen |
Controls whether the SDK opens the player when a tile is tapped. When set to false, the SDK will not open the player and the app must handle tile taps via StorytellerDelegates.onTileTapped(tileType: StorytellerTileType) |
true | true |
Gradient#
The Gradient data class allows for the creation of a color gradient, with options to customize both the colors and the positions at which the gradient starts and ends.
| Property | Default Value | Data Type | Description |
|---|---|---|---|
startColor |
null |
ColorInt |
The color where the gradient begins. |
endColor |
null |
ColorInt |
The color where the gradient ends. |
startPosition |
null |
GradientPosition |
The position indicating where the gradient starts. |
endPosition |
null |
GradientPosition |
The position indicating where the gradient ends. |
Enum: GradientPosition#
Defines positions for starting and ending points of the gradient.
| Value | Data Type | Description |
|---|---|---|
GradientPosition.BottomLeft |
enum |
Bottom left corner of the gradient area. |
GradientPosition.BottomCenter |
enum |
Bottom center edge of the gradient area. |
GradientPosition.BottomRight |
enum |
Bottom right corner of the gradient area. |
GradientPosition.CenterLeft |
enum |
Center left edge of the gradient area. |
GradientPosition.CenterCenter |
enum |
Center of the gradient area. |
GradientPosition.CenterRight |
enum |
Center right edge of the gradient area. |
GradientPosition.TopLeft |
enum |
Top left corner of the gradient area. |
GradientPosition.TopCenter |
enum |
Top center edge of the gradient area. |
GradientPosition.TopRight |
enum |
Top right corner of the gradient area. |
Story and Clip Tiles#
The tiles property can be used to customize the appearance of the Story and Clip Tiles.
| Story and Clip Tiles | Description | Default Value | Dark Value |
|---|---|---|---|
theme.tiles.chip.textSize |
Text size for the New Indicator and Live Indicator. | 11 sp | |
theme.tiles.chip.borderColor |
Border color for the New Indicator and Live Indicator. | null | |
theme.tiles.chip.show |
Show or hide the New/Live chip indicator on tiles. | true | |
theme.tiles.title.textSize |
Size of the Story Title on a Tile. | 11 sp | |
theme.tiles.title.lineHeight |
Line height of the Story Title on a Tile. | 13 sp | |
theme.tiles.title.alignment |
Alignment of the text in a tile, can be Gravity.START/CENTER/END | center | |
theme.tiles.circularTile.title.unreadTextColor |
Text color of Circular Story and Clip Tile Titles in unread state | {theme.colors.black.primary} | {theme.colors.white.primary} |
theme.tiles.circularTile.title.readTextColor |
Text color of Circular Story and Clip Tile Titles in read state | {theme.colors.black.tertiary} | {theme.colors.white.tertiary} |
theme.tiles.circularTile.unreadIndicatorColor |
The color of the ring around Circular Story and Clip Tiles. | {theme.colors.primary} | |
theme.tiles.circularTile.readIndicatorColor |
The color of the ring around Circular Story and Clip Tiles in read state | #C5C5C5 (taken from app layout) | |
theme.tiles.circularTile.unreadIndicatorBorderColor |
The color of the border of the ring around Circular Story and Clip Tiles | null | |
theme.tiles.circularTile.readIndicatorBorderColor |
The color of the border of the ring around Circular Story and Clip Tiles in read state | null | |
theme.tiles.circularTile.unreadBorderWidth |
Width of Circular Story and Clip Tile ring border in unread state | 2dp | |
theme.tiles.circularTile.readBorderWidth |
Width of Circular Story and Clip Tile ring border in read state | 2dp | |
theme.tiles.circularTile.unreadIndicatorGradient |
The gradient of the ring around a circular tile when the story or the clip is unread. If set, overrides circularTile.unreadIndicatorColor. |
null | |
theme.tiles.circularTile.liveChip.unreadImage |
Image resource to be used in place of default unread Live Indicator. If set, overrides theme.tiles.circularTile.liveChip.unreadBackgroundGradient. |
null | |
theme.tiles.circularTile.liveChip.unreadBackgroundGradient |
The gradient of the ring around a live tile and background of the Live Indicator. If set, overrides circularTile.liveChip.unreadBackgroundColor. |
null | |
theme.tiles.circularTile.liveChip.unreadBackgroundColor |
Background color of the Live Indicator when the story contains unread pages or the clip has not been viewed. | {theme.colors.alert} | |
theme.tiles.circularTile.liveChip.unreadBorderColor |
Border color of the Live Indicator when the story contains unread pages or the clip has not been viewed. | null | |
theme.tiles.circularTile.liveChip.unreadTextColor |
Text color of the Live Indicator when the story contains unread pages or the clip has not been viewed. | {theme.colors.white.primary} | |
theme.tiles.circularTile.liveChip.readImage |
Image resource to be used in place of default read Live Indicator. | null | |
theme.tiles.circularTile.liveChip.readBackgroundColor |
Background color of the Live Indicator when all pages have been read or the clip has been viewed. | {theme.colors.black.tertiary} | |
theme.tiles.circularTile.liveChip.readBorderColor |
Border color of the Live Indicator when all pages have been read or the clip has been viewed. | null | |
theme.tiles.circularTile.liveChip.readTextColor |
Text color of the Live Indicator when all story pages have been read or the clip has been viewed. | {theme.colors.white.primary} | |
theme.tiles.rectangularTile.title.textColor |
Text color of the Story Title in Rectangular Tiles. | {theme.colors.white.primary} | |
theme.tiles.rectangularTile.chip.alignment |
Alignment of the New Indicator and Live Indicator in Rectangular Tiles, can be start, center or end. | end | |
theme.tiles.rectangularTile.padding |
Internal padding for Rectangular Story and Clip Tiles, creates space between Story Name or New Indicator and tile edge. | 8 dp | |
theme.tiles.rectangularTile.unreadIndicator.image |
Image resource to be used in place of default New Indicator on Rectangular Tiles. | null | |
theme.tiles.rectangularTile.unreadIndicator.backgroundColor |
Background color of the New Indicator. | {theme.colors.primary} | |
theme.tiles.rectangularTile.unreadIndicator.borderColor |
Border color of the New Indicator. | null | |
theme.tiles.rectangularTile.unreadIndicator.textColor |
The text color of the unread indicator for a rectangular tile. | {theme.colors.white.primary} | |
theme.tiles.rectangularTile.unreadIndicator.textSize |
Text size for the New Indicator. | 11 sp | |
theme.tiles.rectangularTile.liveChip.unreadImage |
Image resource to be used in place of default unread Live Indicator. If set, overrides theme.tiles.circularTile.liveChip.unreadBackgroundGradient. |
null | |
theme.tiles.rectangularTile.liveChip.unreadBackgroundGradient |
The gradient of the ring around a live tile and background of the Live Indicator. If set, overrides circularTile.liveChip.unreadBackgroundColor. |
null | |
theme.tiles.rectangularTile.liveChip.unreadBackgroundColor |
Background color of the Live Indicator when the story contains unread pages or the clip has not been viewed. | {theme.colors.alert} | |
theme.tiles.rectangularTile.liveChip.unreadTextColor |
Text color of the Live Indicator when the story contains unread pages or the clip has not been viewed. | {theme.colors.white.primary} | |
theme.tiles.rectangularTile.liveChip.readImage |
Image resource to be used in place of default read Live Indicator. | null | |
theme.tiles.rectangularTile.liveChip.readBackgroundColor |
Background color of the Live Indicator when all pages have been read or the clip has been viewed. | {theme.colors.black.tertiary} | |
theme.tiles.rectangularTile.liveChip.readBorderColor |
Border color of the Live Indicator when all pages have been read or the clip has been viewed. | null | |
theme.tiles.rectangularTile.liveChip.unreadBorderColor |
Border color of the Live Indicator when the story contains unread pages or the clip has not been viewed. | null | |
theme.tiles.rectangularTile.liveChip.readTextColor |
Text color of the Live Indicator when all story pages have been read or the clip has been viewed. | {theme.colors.white.primary} |


Player#
The player property is used to customize properties relating to the Stories and Clips Player.
| 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.liveChip.image |
Image used in place of Live Chip before Live Story or Clip Titles. If set, it overrides liveChip.backgroundGradient |
null | |
theme.player.liveChip.backgroundGradient |
Background gradient of the badge for Live Story or Clip. If set, it overrides liveChip.backgroundColor |
null | |
theme.player.liveChip.backgroundColor |
Background color of the Live chip Story or Clip | {theme.colors.alert} | |
theme.player.liveChip.textColor |
Text color used for badge label for Live Story or Clip | {theme.colors.white.primary} | |
theme.player.liveChip.borderColor |
Color used for border of badge label for Live Story or Clip | null | |
theme.player.icons.share |
Share button image to be used in place of default share icon. | default icon | |
theme.player.icons.refresh |
Refresh button image to be used in place of default refresh icon. | default icon | |
theme.player.icons.close |
Close button image to be used in place of default close icon. | default icon | |
theme.player.icons.like.initial |
Initial like button image to be used in place of default initial like icon. | default icon | |
theme.player.icons.like.liked |
Liked button image to be used in place of default liked icon. | default icon | |
theme.player.showLikeButton |
Shows the Like button on Clips. | TRUE |

Buttons#
The buttons property applies customizations to buttons which appear throughout the UI.
| Buttons | Description | Default Value | Dark Value |
|---|---|---|---|
theme.buttons.backgroundColor |
Background color of buttons including: share buttons at the end of Quizzes, primary action buttons in Clips, and action buttons in the Following empty state. | {theme.colors.white.primary} | |
theme.buttons.textColor |
Text color of buttons including: share buttons at the end of Quizzes, primary action buttons in Clips, and action buttons in the Following empty state. | {theme.colors.black.primary} | |
theme.buttons.textCase |
Sets the text case for the button on the Instructions Screen, share buttons at the end of Quizzes, primary action buttons in Clips, and action buttons in the Following empty state (TextCase.UPPER/DEFAULT/LOWER). | default | |
theme.buttons.cornerRadius |
Sets the corner radius for buttons including: Instructions Screen button, share buttons at the end of Quizzes, primary action buttons in Clips, and action buttons in the Following empty state. Any value greater than half the height of the button will create a pill shape. | {theme.primitives.cornerRadius} |
Instructions#
Use the instructions property to customize the appearance of the instructions screen.
| Instructions | Description | Default Value | Dark Value |
|---|---|---|---|
theme.instructions.show |
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.black.primary} | {theme.colors.white.primary} |
theme.instructions.headingTextCase |
Sets the text case for the heading on the Instructions Screen. (TextCase.UPPER/DEFAULT/LOWER). | TextCaseTheme.DEFAULT | |
theme.instructions.headingFont |
Font to be used on the Instuctions Screen for headers, defaults to the system font | System font | |
theme.instructions.subHeadingColor |
Subheading color of the text used on the Instructions Screen. | {theme.colors.black.secondary} | {theme.colors.white.secondary} |
theme.instructions.backgroundColor |
Background color of the Instructions Screen. | {theme.colors.white.primary} | {theme.colors.black.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.black.primary} | {theme.colors.white.primary} |
theme.instructions.button.textColor |
Text color of the button used on the Instructions Screen. | {theme.colors.white.primary} | {theme.colors.black.primary} |

Engagement Units#
The engagementUnits property can be used to customize properties relating to Polls and Quizzes.
| Engagement Units | Description | Default Value | Dark Value |
|---|---|---|---|
theme.engagementUnits.poll.answerTextColor |
Answer text color used in Poll Answers. | {theme.colors.black.primary} | |
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. Inherits colors.primary |
{theme.colors.primary} | |
theme.engagementUnits.poll.answeredMessageTextColor |
Color of the vote count or "Thanks for Voting!" message shown to users. | {theme.colors.white.primary} | |
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.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} |

Search#
The search property applies customizations to the Search component.
| Title | Description | Default Value | Dark Value |
|---|---|---|---|
theme.search.heading |
Styling of the Filters title in the Filter View |
theme.lists.title |
theme.lists.title |
theme.search.backIcon |
Image to be used as a back icon in the Search UI | default icon |
Home#
| Title | Description | Default Value | Dark Value |
|---|---|---|---|
theme.home.headerTitle.font |
Font to be used throughout the UI, defaults to the system font | System font | |
theme.home.headerTitle.textSize |
Size of the Home Header Title | 22sp | |
theme.home.headerTitle.lineHeight |
Line height of the Home Header Title | 25sp | |
theme.home.headerTitle.textCase |
Sets the text case for the Home Header Title. (TextCase.UPPER/DEFAULT/LOWER). | TextCaseTheme.DEFAULT | |
theme.home.headerTitle.textColor |
Text color of the Home Title | colors.black.primary | |
theme.home.circularTitle.textSize |
The size of the title for Circular tiles | lists.title | |
theme.home.circularTitle.lineHeight |
The line height of the title for Circular tiles | lists.title | |
theme.home.gridTitle.textSize |
The size of the title for tiles in Grid | 16sp | |
theme.home.gridTitle.lineHeight |
The line height of the title for Grid tiles | 22sp | |
theme.home.singletonTitle.textSize |
The size of the title for Singleton tile | 22sp | |
theme.home.singletonTitle.lineHeight |
The line height of the title for Singleton tiles | 28sp | |
theme.home.rectangularTitle.smallTitle.textSize |
The size of the title for small Rectangular row tiles | lists.title | |
theme.home.rectangularTitle.smallTitle.lineHeight |
The title line height for the small Rectangular row tiles | lists.title | |
theme.home.rectangularTitle.mediumTitle.textSize |
The size of the title for medium Rectangular row tiles | 16sp | |
theme.home.rectangularTitle.mediumTitle.lineHeight |
The title line height for the medium Rectangular row tiles | 22sp | |
theme.home.rectangularTitle.largeTitle.textSize |
The size of the title for large Rectangular row tiles | 18sp | |
theme.home.rectangularTitle.largeTitle.lineHeight |
The title line height for the large Rectangular row tiles | 24sp |
Theme Builder Initialization#
Theme builder is initialized by the buildTheme method lambda passed after the method acts
in the builder scope.
import com.storyteller.domain.theme.builders.buildTheme
import com.storyteller.domain.theme.builders.ofHexCode
Storyteller.theme = buildTheme {
light.colors.primary = ofHexCode("#FF00FF")
}
Accessing Properties in the Builder Scope#
There are 2 equivalent ways of accessing builder properties:
- By scopes
buildTheme {
light {
instructions {
button {
backgroundColor = ofHexCode("#00FF00")
}
}
}
}
- By properties
buildTheme {
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 an identical structure, they are build with the different set of fallbacks. For
instance, default theme.tiles.circularTile.title.unreadTextColor will fallback to the default
value of theme.colors.black.primary in the light mode or theme.colors.white.primary in the dark
mode.
The selection of active themes will be done using current phone UI mode and StorytellerListView.uiStyle
property value. See StorytellerListViews for more details.
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 {
light {
colors {
primary = ofHexCode("#FF00FF")
success = ofHexCode("#00FF00")
}
}
dark from light
}
The above code will set all properties of dark to the current state of the light builder. This method is useful to avoid lengthy typing - a common parameter can be assigned one and copied to the other variant.
Setting Properties of Particular Type#
Colors#
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 is 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.
Drawables#
For the properties accepting StorytellerDrawable type. You can use drawableRes helper method to set a drawable resource.
val myTheme = buildTheme {
light.engagementUnits.poll.selectedAnswerBorderImage = drawableRes(R.drawable.gradient_border)
}
Fonts#
To support multiple weights for fonts, a font family xml resource is required. The SDK will automatically select a font for the appropriate weight when needed.
- Creating a font family resource
custom_font.xml
<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:android="http://schemas.android.com/apk/res/android">
<font android:fontWeight="200" android:font="@font/my_font_regular" />
<font android:fontWeight="700" android:font="@font/my_font_bold" />
</font-family>
- Setting the font family as custom font property
theme.fontusing the helper methodfontRes
font = fontRes(R.font.custom_font)
Example#
val storyRowView = StoryRowView(context)
storyRowView.theme = buildTheme {
light {
colors {
primary = ofHexCode("#FF0000")
success = ofHexCode("#00FF00")
alert = ofHexCode("#C50511")
}
font = fontRes(R.font.custom_font)
lists {
row {
tileSpacing = 8
startInset = 12
endInset = 12
}
grid {
tileSpacing = 8
columns = 2
}
enablePlayerOpen = false // Handle tile taps manually via StorytellerDelegates
}
tiles {
title {
textSize = 11
lineHeight = 13
alignment = Gravity.START
}
circularTile {
unreadIndicatorGradient = UiTheme.Theme.Gradient(
startColor = getColor(R.color.gradient_start),
endColor = getColor(R.color.gradient_end),
startPosition = UiTheme.Theme.Gradient.GradientPosition.CenterLeft,
endPosition = UiTheme.Theme.Gradient.GradientPosition.CenterRight,
)
liveChip {
unreadBackgroundGradient = UiTheme.Theme.Gradient(
startColor = getColor(R.color.gradient_start),
endColor = getColor(R.color.gradient_end),
startPosition = UiTheme.Theme.Gradient.GradientPosition.CenterLeft,
endPosition = UiTheme.Theme.Gradient.GradientPosition.CenterRight,
)
readTextColor = getColor(R.color.read_live_text)
unreadTextColor = getColor(R.color.unread_live_text)
}
}
rectangularTile {
padding = 8
unreadIndicator.alignment = Gravity.END
unreadIndicator.textColor = ofHexCode("#000FF")
unreadIndicator.textSize = 11
liveChip {
readTextColor = getColor(R.color.read_live_text)
unreadTextColor = getColor(R.color.unread_live_text)
}
}
}
buttons.cornerRadius = 24
buttons.textCase = TextCase.UPPER
instructions {
icons {
forward = drawableRes(R.drawable.ic_forward_light)
pause = drawableRes(R.drawable.ic_pause_light)
back = drawableRes(R.drawable.ic_back_light)
move = drawableRes(R.drawable.ic_move_light)
}
}
home {
title {
font = fontRes(R.font.font)
textCase = TextCaseTheme.LOWER
textSize = 28
lineHeight = 28
textColor = getColor(R.color.rams_storyteller_primary)
}
}
}
dark from light
}