Skip to main content

Shapes

Define reusable shape styles under theme.shapes, then reference them with a shapeStyleKey:

{
"theme": {
"shapes": {
"card": {
"kind": "roundRect",
"fillColor": "#173B63",
"cornerRadius": [20, 20, 36, 20],
"strokeColor": "#65C7F7",
"strokeWidth": 2,
"shadows": [
{
"x": 0,
"y": 8,
"blur": 20,
"spread": 0,
"color": "#00000066"
}
]
}
}
}
}
Shape({
width: 420,
height: 220,
shapeStyleKey: 'card',
})

The current shape kind is roundRect.

Shape fields

FieldTypePurpose
kind"roundRect"Geometry kind.
fillColorcolor stringInterior color.
cornerRadiusnumber, "pill", or four numbersAll corners or [topLeft, topRight, bottomRight, bottomLeft].
strokeColorcolor stringOptional outline color.
strokeWidthnumberOutline width.
shadowsarrayOuter or inset shadow definitions.
shapeStyleKeystringInherit another shape style before applying local fields.
fallbackUristringOptional fallback asset used by shape resolution.
width, heightnumberReference dimensions required for some Roku preprocessing.
maskbooleanOpt the style into use as a generated mask source.

A shadow contains x, y, blur, color, and optional spread and inset.

Inherit and override a shape

{
"theme": {
"shapes": {
"card": {
"kind": "roundRect",
"fillColor": "#173B63",
"cornerRadius": 20
},
"focusedCard": {
"shapeStyleKey": "card",
"fillColor": "#245B91",
"strokeColor": "#FFFFFF",
"strokeWidth": 3
}
}
}
}

The referenced style is resolved first; local fields override it.

Platform compilation

Shape declarations resolve shared inheritance before branching into direct web or native rendering and Roku cached image generationShape declarations resolve shared inheritance before branching into direct web or native rendering and Roku cached image generation

BaseApp.loadShapes() runs during launch and stores the resolved forms under theme.compiledShapes.

  • Web and native-drawing platforms keep the resolved shape fields and render them with the platform shape node.
  • Roku sends shape requests to the imageGenerator task, writes generated nine-patch images to cachefs:/hosanna-shapes, and renders them as posters.

This is a runtime launch step. The current source does not support per-shape delivery values or an hst app-config:compile shape-delivery workflow.

Reference-size requirements

On Roku, a "pill" radius needs positive width and height in the theme entry; without them, preprocessing warns and produces square corners. A style with mask: true also needs positive reference dimensions so its mask image can be generated.

Shapes inside fragments

Fragment trees can contain hs_shape and MaskGroup children. Give each child a stable ID so status overlays and the provider can address it:

{
"$supportsDataMap": true,
"views": {
"base": [
{
"id": "surface",
"subType": "hs_shape",
"shapeStyleKey": "card",
"width": 384,
"height": 216
},
{
"id": "mask",
"subType": "MaskGroup",
"shapeStyle": "card",
"maskSize": [384, 216],
"children": [
{
"id": "poster",
"subType": "Poster",
"uri": "${data.imageUrl}",
"width": 384,
"height": 216
}
]
}
],
"normal": {
"surface": { "shapeStyleKey": "card" }
},
"focused": {
"surface": { "shapeStyleKey": "focusedCard" }
}
}
}

shapeStyleKey selects the style for an hs_shape node. shapeStyle selects the geometry used by a MaskGroup; a maskUri can remain as a bitmap fallback.

The fragment provider handles these fields differently by platform:

  • On Roku it turns hs_shape into compiled poster/nine-patch fields, resolves MaskGroup.shapeStyle to the compiled mask image, and scales maskSize to output-resolution pixels.
  • On web, Apple, and Android it leaves the shape metadata and design-point mask size for the platform bridge to render.
  • A status overlay that changes shapeStyleKey is translated to the corresponding compiled Roku poster fields.

Load shapes before acquiring fragment instances. If a fragment was hydrated before its shapes compiled, the provider marks that pooled instance stale and rebuilds it on a later acquisition.

See Fragment Callbacks and Lifecycle for pooled cleanup rules and Fragment Performance and Debugging for shape diagnostics.

Reloading config

Replacing AppConfig clears its computed theme.compiledShapes branch. Call loadShapes() again after a config reload, and recreate fragments that were hydrated before the new shapes finished loading.

Talk to us