ResolvedStyle Struct

#include <vstyle.h>

Public Members

std::string fill
bool fillIsGradient { false }
std::string borderFill
bool borderIsGradient { false }
double borderThickness { 0 }
double radiusTL { 0 }
double radiusTR { 0 }
double radiusBL { 0 }
double radiusBR { 0 }
std::string textColor
double marginL { 0 }
double marginT { 0 }
double marginR { 0 }
double marginB { 0 }
double paddingL { 0 }
double paddingT { 0 }
double paddingR { 0 }
double paddingB { 0 }
std::string fontFamily
double fontSize { 0 }
double fontWeight { 0 }
std::string direction
std::string crossAlign
std::string justify
std::string scroll
double spacing { 0 }
double grow { 0 }
double shrink { -1 }
double width { 0 }
double height { 0 }
double maxWidth { 0 }
double aspectRatio { 0 }
double opacity { 1.0 }
bool visible { true }
bool collapsed { false }
double translateX { 0 }
double translateY { 0 }
double rotation { 0 }
std::string intent
std::string overlay
bool secret = false
bool multiline = false
bool readOnly = false
bool wrap = true
std::string fit
bool autoplay = false
bool loop = false
bool muted = false
bool controls = false
std::string text
std::string placeholder
std::string iconSource
std::string mediaSource
std::string qrData
std::string href
std::string tag
std::string anchor
std::string bind
double heightVp = 0
std::string codeBg

Detailed Description

Fully-resolved styling for a single box/text node. Colors are hex strings ("#rrggbb" / "#aarrggbb"). Gradients are stored as comma-joined stops ("0:#3a3c42,1:#42454d") with the matching *IsGradient flag set.

Note: Content inset = borderThickness + padding.

Member Variable Documentation

std::string fill

Color or gradient that fills the widget

bool fillIsGradient { false }

True when fill is a gradient

std::string borderFill

Color or gradient that fills the widget's border

bool borderIsGradient { false }

True when borderFill is a gradient

double borderThickness { 0 }

The widget's border thickness (px)

double radiusTL { 0 }

Top-left corner radius of the widget (px)

double radiusTR { 0 }

Top-right corner radius of the widget (px)

double radiusBL { 0 }

Bottom-left corner radius of the widget (px)

double radiusBR { 0 }

Bottom-right corner radius of the widget (px)

std::string textColor

Color of text and icons

double marginL { 0 }

Left space outside the widget (handled by parent layout)

double marginT { 0 }

Top space outside the widget (handled by parent layout)

double marginR { 0 }

Right space outside the widget (handled by parent layout)

double marginB { 0 }

Bottom space outside the widget (handled by parent layout)

double paddingL { 0 }

Left space between the widget's border and its content

double paddingT { 0 }

Top space between the widget's border and its content

double paddingR { 0 }

Right space between the widget's border and its content

double paddingB { 0 }

Bottom space between the widget's border and its content

std::string fontFamily

Font family name for text. The generic "monospace" maps to the platform's default. "" is the reconciler default.

double fontSize { 0 }

Pixel size for text; 0 is reconciler default

double fontWeight { 0 }

Numeric text weight (100-900); 0 is reconciler default

std::string direction

Direction that children are laid out.

Option Default Orients children...
"Column" ☑ vertically
"Column-Reverse" vertically in reversed order
"Row" horizontally
"Row-Reverse" horizontally in reversed order

std::string crossAlign

Cross-axis alignment of children. If direction == "Row", then this controls the vertical alignment, whereas direction == "Column" means this controls the horizontal alignment.

The associated margins of the children are factored into each result. For example, if direction == "Row" and this == "Start", the children are aligned to the top-side offset by their individual marginT distances.

Option Default Aligns children...
"Start" along the top ("Row") or left ("Column") edge
"Center" ☑ centered between the top & bottom ("Row") or left & right ("Column") edges
"End" along the bottom ("Row") or right ("Column") edge
"Stretch" filling the available height ("Row") or width ("Column")

std::string justify

Main-axis placement of the child block, when there's leftover space.

Unlike the cross-axis where children are aligned within their own lanes, on the main-axis, children sit one after another, and justify shifts the whole group. If direction == "Row" and there is free space available:

Start:   [A][B][C]·········
Center:  ····[A][B][C]·····
End:     ·········[A][B][C]

If any of the children in the block are set to grow, the justify field is irrelevant because a growing child leaves no free space.

Start:   [A][B][C·········]  // Since `C` grows, justify is irrelevant
Center:  [A][B][C·········]
End:     [A][B][C·········]
Option Default Shifts child block...
"Start" to the left ("Row") or top ("Column") edge
"Center" ☑ centered between the left & right ("Row") or top & bottom ("Column") edges
"End" to the right ("Row") or bottom ("Column") edge

std::string scroll

Enabled scrolling directions.

Option Default Scrolling is...
"" (empty) ☑ disabled
"Vertical" enabled for vertically overflowing content
"Horizontal" enabled for horizontally overflowing content
"Both" enabled for both vertically & horizontally overflowing content

double spacing { 0 }

Gap between children (px)

double grow { 0 }

Growth factor of this child along the parent's main-axis. When applied, the parent's leftover space is split between this child and any other growing siblings. No factor (the default value of 0) means the node keeps its content size.

The leftover space consumed by this child is calculated using a ratio of its factor divided by the factor total of all the parent's growing children:

ratio   = factor / total
consume = ratio * leftover

Two Grow: 1 children divide the space evenly (both get 1/2). If they are Grow: 1 and Grow: 2, then the space is divided into thirds (1/3 and 2/3). A single growing child takes all of the leftover space regardless of its growth factor (a lone Grow: 3 produces 3/3).

[A......][B......]  // `A/Grow: 1` & `B/Grow: 1`
[A....][B........]  // `A/Grow: 1` & `B/Grow: 2`
[A...............]  // `A/Grow: 3`

If any of the children are set to grow, justify is irrelevant because a growing child leaves no free space, unless the growing children have their sizes capped.

double shrink { -1 }

Static-web only: Prevents shrinking below the content size when set to a value of 0.

double width { 0 }

Fixed width (px).

A value of 0 means the width is unfixed (the width of the content unless the node grows or is stretched).

double height { 0 }

Fixed height (px).

A value of 0 means the height is unfixed (the height of the content unless the node grows or is stretched).

double maxWidth { 0 }

Fills the width up to the set value (px), or up to the available space if it's narrower.

A value of 0 means no constraint is applied. If a fixed width is set, then this field is ignored.

double aspectRatio { 0 }

Image/Video nodes only Ratio of horizontal-to-vertical pixels used to derive a height value by dividing the width by the ratio.

If an aspect ratio is set, then height is ignored. A value of 0 means no ratio is applied.

ratio  = horizontal pixels / vertical pixels
height = width / ratio

// For example: a width of `1920` & a desired 16:9 aspect ratio
ratio  = 16 / 9          // 1.7778; This is what you set `aspectRatio` to
height = 1920 / ratio    // 1080

The aspect ratio frames the rendering of an Image/Video node, whereas fit controls how the actual image/video fits within that frame.

double opacity { 1.0 }

Render-time opacity; value in the range [0,1]

bool visible { true }

When false, the node is dropped from the render tree

bool collapsed { false }

When true, the reconciler animates the node's height to 0, and then back to the natural content height when false (un-collapsed).

Unlike visible which removes the node from the tree, a collapsed node stays in the tree to permit the collapsing/expanding animation.

double translateX { 0 }

Sliding overlays only: Render-time horizontal offset expressed as a fraction of the node's width (a value of 0 keeps the node in place, while a value of 1 fully shifts it by its own width).

double translateY { 0 }

Sliding overlays only: Render-time vertical offset expressed as a fraction of the node's height (a value of 0 keeps the node in place, while a value of 1 fully shifts it by its own height).

double rotation { 0 }

Icon nodes only: Render-time clockwise rotation in degrees.

std::string intent

Optional semantic action for binding a platform behavior, like "window:close" on an exit button or "window:drag" on a titlebar box. Platforms ignore intents that they don't handle.

std::string overlay

Overlay mode. "" == this node lays out normally in its parent's flow. "Modal" == the reconciler lifts this node out of the flow and presents it as a dialog centered over a darkening backdrop covering the rest of the UI (desktop = a movable, modal frameless window; web/Android = a fixed, centered element). The node is shown/hidden via the normal visibility overlay (bind_visible), so opening/closing the dialog rides the diff as a single Insert / Remove. "PinnedBottom" == the reconciler floats this node over the content, anchored to the bottom edge (rather than occupying a slot in the flow), so it can slide off-screen via translateY without reflowing content. Used by the titlebar in @Narrow (where the bar sits at the bottom) to drive hide-away- on-scroll. Empty for most nodes. "BottomSheet" == like PinnedBottom (floated at the anchor's bottom edge, centered up to Size.Max Width), but reserves NO bottom space: the node OVERLAYS the content instead of pushing it. Used by the dashboard drawers, which peek as just a header (their body carries a Visible "@Expanded" variant) and grow upward over the list when expanded; the list reserves the collapsed peek with its own bottom padding. "SideSheet" == a modal detail panel floated at the anchor's RIGHT edge, full anchor height, right-aligned and capped at Size.Max Width (0 == fill, i.e. edge-to-edge in @Narrow). The reconciler draws a darkening scrim over the rest of the anchor (click-to-dismiss; dispatches a "click" to THIS node's path) and slides the panel in/out horizontally via translateX (rest at 1 off-screen; an @Open variant pulls it to 0), the scrim's opacity tracking the slide. Stays in the tree across open/close (state-driven, like the titlebar's translateY hide-away) so the slide animates both ways. Used by the profile post-detail.

bool secret = false

Editor nodes: mask the typed value (password field) when true. Defaults false (a normal visible field).

bool multiline = false

Editor nodes: a multi-line text box (wrapping, top-aligned) instead of the single-line field. The floating placeholder is a single-line affordance, so multi-line fields show a STATIC placeholder that fills the box while empty. Height comes from the style's Size.Height (no auto-grow). Defaults false.

bool readOnly = false

Editor nodes: a display surface the user can select/copy from but not type into (Docuriser's streamed build log). The value still moves via app rebinds (bind_text); a read-only multi-line editor additionally pins its scroll to the bottom on each rebind — log semantics. Defaults false.

bool wrap = true

Text nodes wrap long lines to the available width by default (like web + Android); "Wrap": 0 keeps them single-line. Editor nodes (multi-line): wrap long lines to the box width (the default). "Wrap": 0 disables wrapping and scrolls horizontally instead — the log/code look (long paths stay on one line). Single-line fields ignore it.

std::string fit

Image / Video nodes: how the media fills its box when its aspect ratio differs from the box's — "Cover" (fill + crop) or "Contain" (fit whole, letterbox). Maps to CSS object-fit. Empty == the reconciler's default (Cover). Ignored by non-media nodes.

bool autoplay = false

Video nodes only: playback flags. autoplay starts on load (browsers require muted to honor it), loop restarts at the end, muted starts silent, controls shows the native scrubber/volume UI. All default false. Ignored elsewhere.

std::string text

Text nodes: static, design-owned literal copy authored directly in the style (mirrors iconSource for icons). The base value for node.text; a runtime bind_text() value or a dynamic "Bind" override takes precedence. Empty when the style supplies no literal (text then comes from code/bind). NOTE: literal copy here is not localizable — when i18n lands this should become a string-table key (e.g. "@app.title") resolved against app data.

std::string placeholder

Editor nodes: the floating placeholder copy — shown filling the field while it is empty and unfocused, animating up + smaller to a label once the user focuses or types (the legacy desktop line-editor behavior, now portable to every reconciler). The editor's INITIAL/default value rides the normal text field (the style's literal Text, overridable via bind_text); the user's typed value is owned by the reconciler and read back via VApp::editor_text(). Empty for non-editor nodes.

std::string iconSource

Icon nodes: the .svg asset name (no extension) the reconciler loads and recolors (to textColor). Empty for non-icon nodes.

std::string mediaSource

Image / Video nodes: the media file path or URL the reconciler loads and displays (a photo, an mp4, an SVG logo). Raster/video render verbatim; an SVG Image renders as a live vector and — unlike a photo, but like an icon — recolors to textColor when the style sets Text Color (else full color is preserved). Resolves the same "Source" attribute icons use; the reconciler reads whichever of iconSource/mediaSource matches the node type. Empty otherwise.

std::string qrData

QR nodes: the payload string encoded into the QR matrix (a node URL, a key blob, …). Resolves the "Data" attribute (a separate attribute from Source, so a QR and an icon/image can't be confused). The matrix is derived from this on demand — by the core when serializing for web/Android, by the desktop reconciler directly — so only the payload rides the resolved style. Empty for non-QR nodes (an empty payload paints just the box fill).

std::string href

Link nodes: the target URL (the "Href" attribute; overridable at runtime via bind_href and per-row via a "Href:" bind). The static-web emitter writes it as the ; runtime reconcilers ignore it.

std::string tag

Document tag hint (the "Tag" attribute): the semantic HTML element the static-web emitter uses instead of div/span — "h1".."h6", "nav", "main", "header", "footer", "section", "article", "ul", "li", … Authored per style for crawlable, reader-mode-friendly generated pages. Runtime reconcilers ignore it. Empty = the emitter's default element.

std::string anchor

Fragment anchor (the "Anchor" attribute): the emitted element's id, the target of "#anchor" deep links in generated documentation. Runtime reconcilers ignore it. Empty = no id.

std::string bind

Data binding handled in the core during tree-build (consumed there, never sent to reconcilers): "ThemeList" makes a container generate one button per loaded theme (from its first child as a template); "ActiveThemeName" / "ActiveThemePublisher" set a text node to the active theme's name/publisher; "ThemeName" / "ThemePublisher" do the same for the theme of the enclosing generated theme button (its primary label + publisher subtext), so each button reads its own theme rather than the active one. Row-data forms (inside a "List:" container's row template): "List:" generates one row per app-supplied entry (first child as the template; an optional SECOND child is a separator template, stamped between consecutive rows — list chrome built without row context, keyed to the row it follows so mid-list edits diff minimally; each row's subtree also resolves with a "@First"/"@Last" state folded in at the ends, so a template can restyle end rows — round only the outer corners, hide an embedded divider on the last row, etc.); "Field:" fills a node from the row's field — text for most nodes, the media Source for an Image/Video (an empty value collapses it); "Placeholder:" sets an Editor's placeholder from a row field — the row's identity rides the editor's floating field label, reading exactly like a static form field's authored Placeholder (absent/empty leaves the authored one, so a template can carry a static default); "Opacity:" fades a node from a boolean-ish row field (empty/"0"/"false" → transparent) WITHOUT collapsing its slot, to gate a per-row affordance while preserving layout (e.g. the feed vote arrows); "MaxWidth:" caps Size.Max Width from a numeric row field (non-positive → uncapped, fills the cross axis), e.g. an inline embed's pixel cap; "Aspect:" sets a media node's Aspect Ratio from a numeric row field (non-positive/absent → the authored ratio), e.g. a feed post's media at its own clamped shape; "Indent:" sets the node's LEFT MARGIN from a numeric row field (non-positive → flush), insetting the whole node (fill included) — used to step threaded comment replies in by depth. Row-data binds compose: a "Bind" value may carry SEVERAL of them separated by ';' (e.g. "Field:image; Aspect:aspect" — one media node bound to both its per-row source and its per-row shape). The structural binds (List:, ThemeList — they generate children) are whole-value only.

double heightVp = 0

Height as a FRACTION of the VIEWPORT height ("Size.Height Viewport", 0 == unset) — the viewport cousin of translateY's self-fraction. Folded into height at build time against the platform-reported viewport (VApp::set_viewport_height), so reconcilers only ever see the concrete px value; an authored Size.Height doubles as the fallback when no viewport height has been reported. A fraction (not a px length), so the mobile metric scale leaves it alone. Sizes the scan drawer's camera to the legacy "70% of the screen" drawer proportions.

std::string codeBg

Markdown nodes only: the resolved Theme/Secondary hex used as the surface color behind inline + fenced code (so code reads as a tinted pill/block over the body). Resolved in core from the active theme — not an author-facing attribute — so all three reconcilers theme code identically. Empty for every non-Markdown node.

Appearance
Theme
—