APL Style Definition and Evaluation (APL 1.1)
(This is not the most recent version of APL. Use the Other Versions option to see the documentation for the most recent version of APL)
A style is a named entity that defines a set of visual properties. You can use the style to consistently set those properties on a component. Styles can include conditional logic and can use component state. For example, a style could change text color depending on whether the component's state is checked.
Style definitions
A style definition specifies the name of the style, a list of one or more parent styles to inherit from, and an ordered list of conditionally-applied property definitions. Here is a style definition title suitable for use by the Text component. It sets the size of the font and the color. The color changes based on the state of the Text component and current theme.
"styles": {
"baseText": {
"values": [
{
"fontFamily": "Amazon Ember",
"color": "${viewport.theme == 'dark' ? 'white' : 'black' }"
},
{
"when": "${state.karaoke}",
"color": "blue"
}
]
},
"title": {
"extends": "baseText",
"values": [
{
"fontWeight": 700,
"fontSize": "${viewport.height > 400 ? 30 : 25}"
}
]
}
}
For example, the title style on a device with a large viewport, dark theme, and focused state evaluates to:
{
"fontFamily": "Amazon Ember Display",
"color": "blue",
"fontWeight": 700,
"fontSize": 30
}
Not all component properties can be set with styles. See Styled Properties for a reference to all styled properties across all components. For an individual component, note the table of available component properties. Properties that you can set in a style for that component are indicated with "Yes" in the "Styled" column of the table. For example, see the Text properties table and note that all properties except
text are styled.Definition properties
Each style definition has the following properties.
| Property | Type | Required | Description |
|---|---|---|---|
| description | String | No | A description of this style |
| extend | Array of style names | No | List the styles that this style inherits from. Order is important; later items override earlier. |
| values | Array of VALUE objects | No | An array of objects |
The extend array contains an ordered list of styles that this style inherits from. The access property controls whether or not this style is available outside of the defining package.
The values array contains an ordered list of value objects to apply. Each value object has the form:
{
"when": EXPRESSION,
NAME: VALUE,
NAME: VALUE,
:
}
The when property is optional and defaults to true if it is not defined. The defined properties are expected to be the names of valid styled properties (they are ignored if they are invalid names). The data-binding context for the when clause contains the viewport, environment, resource definitions, and styles that appear earlier in the document or in imported packages.
Component state
Each component has a state. A state is a collection of defined Boolean
properties that are changed as the user interacts with the APL document.
The following table lists all state properties:
| Property | Definition |
|---|---|
checked |
The component has been checked or toggled on |
disabled |
Component has been disabled (many components have a "disabled" property) |
focused |
Component has the keyboard input focus |
karaoke |
The component is being spoken by a speech command |
karaokeTarget |
An element within the component is the specific target for a current speech command. |
pressed |
Mouse or touch input is active |
Each component state is a Boolean property. All component states are
applicable to all components; for example, a
Container has a checked and
disabled state even though they are likely to have no effect on the
container.
When a style is evaluated, the component states are exposed in the data-binding context as a map of Boolean values under the state keyword. For example, you can access the checked state as ${state.checked}.
The component states are set during component creation and may change based on user interaction, such as by touching the screen or using a keyboard, or with commands. For example, the TouchWrapper component responds to touch or keyboard events by setting the pressed state. The combination of component states and styles is used to change the visual appearance of a component.
Changing the state of a component does not change the state of the children in a component unless you set the inheritParentState property. Here is a sample Text component that changes color when it is pushed. The Text component is wrapped in TouchWrapper and is set to inherit the state of the TouchWrapper:
Definition of a style:
{
"textStylePressable": {
"values": [
{
"color": "white"
},
{
"when": "${state.pressed}",
"color": "green"
}
]
}
}
Use of the style in a layout:
{
"type": "TouchWrapper",
"item": {
"type": "Text",
"inheritParentState": true,
"style": "textStylePressable",
"text": "Push Me!"
}
}
Note these rules around component state:
- Use the
SetValuecommand to change to change thecheckedanddisabledstates. - A
disabledcomponent can't also have the statepressedorfocused. Setting thedisabledstate on a component clears thepressedandfocusedstates.
Component State Control
The following table summarizes state values and changes:
| Concept | checked | disabled | focused | karaoke | karaokeTarget | pressed |
|---|---|---|---|---|---|---|
| Initialized by property | Yes | Yes | -- | -- | -- | -- |
| Controlled by SetValue | Yes | Yes | -- | -- | -- | -- |
| Controlled by SetFocus | -- | -- | Yes | -- | -- | -- |
| Controlled by ClearFocus | -- | -- | Yes | -- | -- | -- |
| Changed by touch | -- | -- | -- | -- | -- | Yes |
| Changed by keyboard events | -- | -- | Yes | -- | -- | Yes |
| Controlled by SpeakItem/List | -- | -- | -- | Yes | Yes | -- |
| Appears in event.source.value | Yes | -- | -- | -- | -- | -- |
| Appears in visual context | Yes | Yes | Yes | -- | -- | -- |
The following sections detail state behavior.
checked
Components use the checked state to behave like a check box or radio button. You can control the checked state for a component. It has the following characteristics:
- You can initialize the
checkedstate in the component definition. - You can change
checkeddynamically with theSetValuecommand. - The visual context reports the checked state.
When pressed, the TouchWrapper component reports the checked state of the component in the event.source.value property. Use the checked state to toggle check boxes as shown in the following example.
{
"onPress": {
"type": "SetValue",
"property": "checked",
"value": "${!event.source.value}"
}
}
Touching a component on the screen does not automatically set or clear the checked state. You must change the state with the SetValue command.
disabled
The disabled state indicates that a component which normally responds to touch or keyboard events is not available.
- You can initialize the
disabledstate in the component definition. - You can change the
disabledstate dynamically with theSetValuecommand. - The visual context reports the disabled state.
A disabled component may not receive focus and cannot be pressed. Setting the disabled state on a component will clear the pressed and focused states.
Disabling a component that contains children has no
effect on the children of the disabled component unless they inherit
their parent's state. For example, an enabled TouchWrapper placed inside of a
disabled Container displays normally and responds to
touch events.
focused
The focused state reflects if the component currently has active
focus in the runtime. The focused state only applies to actionable
components which can be controlled by a keyboard. The actionable
components are: TouchWrapper, Pager, ScrollView, and Sequence.
- The focused state defaults to
falsewhen the component is initialized. - You can change the
focuseddynamically with theSetFocusandClearFocuscommands. - The visual context reports the focused state.
The focused state is controlled only by user navigation with keyboard
events, OR through use of the SetFocus and ClearFocus commands. The SetValue command can't change the focused state.
One parent component at a time has focused state set to true. Child components with the inheritParentState property set are assigned the focused state for visual display, but do not receive keyboard events.
karaoke/karaokeTarget
The karaoke state highlights a component that is being spoken or
described by Alexa. The karaokeTarget state highlights a subsection of
the component that is being spoken or described by Alexa.
The karaoke state is controlled by the SpeakList and SpeakItem commands. The SetValue
command can't change the karaoke state.
For more about how to define karaoke styling, see Karaoke style calculations.
pressed
The pressed state highlights a component that is being touched either
directly by the user or indirectly by pressing the "enter" key on a
keyboard. The pressed state only applies to the
TouchWrapper.
The TouchWrapper component sets the pressed state when the user touches
the component and releases the pressed state when the user either
releases the touch or drags outside of the component bounds. The pressed
state is re-entered if the user drags out and then drags back in.
The onPress event handler for TouchWrapper only fires if the user presses and releases in
the same component. The onPress command fires after the pressed state has been released.
Evaluation of a style
Each component references a named style either explicitly or implicitly (device runtimes
have default styles for each component). The style evaluation occurs in a restricted data-binding context with just the viewport,state, and resource definitions.
The calculated style is a function of the viewport, resources, and state. The calculation algorithm can be approximated with this pseudo-code:
function _calcInternal(style, context):
result = {};
// Walk the extend array
foreach style.extend as name:
result += _calcInternal( getStyleByName(name), context)
// Walk the values array
foreach values as value:
if evaluate(value.when):
result += evaluateEach(value)
return result
function calculateStyle(style, context, state):
workingContext = extendContext(context, { state: state })
return _calcInternal(style, workingContext)
The extend array is calculated first, followed by the values blocks (in order).
Karaoke style calculations
The karaoke state property is set by the commands SpeakItem and SpeakList
commands. In
most cases the karaoke state behaves just like any other state where
all styled properties may be modified freely. There are two points for
karaoke mode that should be kept in mind.
First, the SpeakItem and SpeakList commands scroll content into view
before karaoke styling is applied. If the karaoke
styling makes dramatic visual changes to the screen, the content may move
unexpectedly after the scrolling has finished. Therefore,
minimize changes that alter the position of components on the
screen. This includes changing the size of components, text, fonts, and borders.
Second, the SpeakItem
property may be set to "highlight" individual lines of text during
karaoke. When this highlighting mode is selected, the block of text is
assigned the karaoke state and the active line of text being spoken is
assigned both the karaoke state and the karaokeTarget state. The
karaokeTarget state can only change the color of the highlighted text.
For example:
{
"styles": {
"karaokeText": {
"values": [
{
"color": "#fafafa",
"fontWeight": 300
},
{
"when": "${state.karaoke}",
"color": "blue",
"fontWeight": 700
},
{
"when": "${state.karaokeTarget}",
"color": "white",
"fontWeight": 100
}
]
}
}
}
In this example, the normal text color is #FAFAFA (a light grey) with a
fontWeight of 300. During a SpeakItem command with line-by-line
highlighting, the fontWeight of the entire text block is 700. The
highlighted line is white (it has karaokeTarget=True); the other
lines are blue. Note that the fontWeight setting for the karaoke
target line was ignored since remember that currently APL supports
changing the color of the highlighted line only.
Line-by-line highlighting does not mean that there is
always at least one visible highlighted line of text. If the
minimumDwellTime on the SpeakList command
property is set to longer than the speech duration, there will be a
period where no lines are highlighted but the component still has the
karaoke state set. Following the example above, during this "extra"
time all of the lines of text will be blue with a fontWeight of 700.
Finally, if block highlighting mode is selected instead
of line-by-line highlighting, all of the text will be blue. The
karaokeTarget state is not activated in block highlighting mode.
Last updated: Nov 28, 2023