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 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.
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.
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%) |
Use the font
property to set a custom font for use throughout the SDK.
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 |
The primitives
object contains base values which are used throughout the SDK.
Primitives | Description | Default Value | Dark Value |
---|---|---|---|
theme.primitives.cornerRadius |
Default corner radius used for Rectangular Tiles, Buttons and Poll/Quiz answers. | 4 dp |
The lists
customize properties of the various list types available from the SDK.
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 first row. | 0 dp | |
theme.lists.grid.bottomInset |
The space at the bottom of the last row. | 0 dp | |
theme.lists.home.startInset |
Space to the left of Storyteller Home | 12 dp | |
theme.lists.home.endInset |
Space to the right of Storyteller Home | 12 dp | |
theme.lists.home.heading.font |
The only font that can vary from theme.font, defines the font for the heading | System Font | |
theme.lists.home.heading.textColor |
Color of heading text in Storyteller Home | {theme.colors.black.primary} | {theme.colors.white.primary} |
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} |
The storyTiles property
can be used to customize the appearance of the 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.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.title.show |
Show or hide the title text in all tiles. | TRUE | |
theme.storyTiles.circularTile.title.unreadTextColor |
Text color of Circular Story Tile Titles in unread state | {theme.colors.black.primary} | {theme.colors.white.primary} |
theme.storyTiles.circularTile.title.readTextColor |
Text color of Circular Story Tile Titles in read state | {theme.colors.black.tertiary} | {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.circularTile.liveChip.unreadImage |
Image to be used in place of default unread Live Indicator. | default icon | |
theme.storyTiles.circularTile.liveChip.readImage |
Image to be used in place of default read Live Indicator. | default icon | |
theme.storyTiles.circularTile.liveChip.unreadBackgroundColor |
Background color of the Live Indicator when the Story contains unread pages. | {theme.colors.alert} | |
theme.storyTiles.circularTile.liveChip.readBackgroundColor |
Background color of the Live Indicator when all pages have been read. | {theme.colors.black.tertiary} | |
theme.storyTiles.circularTile.liveChip.textColor |
Text color of the Live Indicator. | {theme.colors.white.primary} | |
theme.storyTiles.circularTile.unreadBorderWidth |
Width of Circular Story Tile ring border in unread state | 2dp | |
theme.storyTiles.circularTile.readBorderWidth |
Width of Circular Story Tile ring border in read state | 2dp | |
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 | |
theme.storyTiles.rectangularTile.liveChip.unreadImage |
Image to be used in place of default unread Live Indicator. | default icon | |
theme.storyTiles.rectangularTile.liveChip.readImage |
Image to be used in place of default read Live Indicator. | default icon | |
theme.storyTiles.rectangularTile.liveChip.unreadBackgroundColor |
Background color of the Live Indicator when the Story contains unread pages. | {theme.colors.alert} | |
theme.storyTiles.rectangularTile.liveChip.readBackgroundColor |
Background color of the Live Indicator when all pages have been read. | {theme.colors.black.tertiary} | |
theme.storyTiles.rectangularTile.liveChip.textColor |
Text color of the Live Indicator. | {theme.colors.white.primary} |
The player
property is used to customize properties relating to the Story 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.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 | |
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 |
The buttons
property applies customizations to buttons which appear throughout the SDK.
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.colors.black.primary} | |
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} |
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.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} |
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. | {theme.colors.white.tertiary} | |
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.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.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 builder is initialized by the buildTheme(context)
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(context) {
light.colors.primary = ofHexCode("#FF00FF")
}
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.
In the builder context two variants are present:
Although they have an 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 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 StorytellerListView 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(context) {
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.
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.
For the properties accepting Drawable type, any Drawable extending object can be provided.
val myTheme = buildTheme(context){
light.engagementUnits.poll.selectedAnswerBorderImage = GradientDrawable(
GradientDrawable.Orientation.BOTTOM_TOP,
intArrayOf(Color.CYAN, Color.MAGENTA)
)
}
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.
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>
theme.font
font = ResourcesCompat.getFont(ctx, R.font.custom_font)
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
}