svg panel

goo renders vector art through the SvgPanel blob. give it a path to an imported .svg file and it fills its panel bounds with that drawing at any resolution, pixel-perfect. one optional property tints the whole image with a CSS color string.

the basics

import your .svg into the s&box editor first, the same way you would import a texture. then point Path at the mounted asset path. Width and Height control the rendered size. set both or use flex to fill the parent.

new SvgPanel
{
    Path   = "ui/icon.svg",
    Width  = 64,
    Height = 64,
}

omit Width and Height and the panel takes zero size, so the drawing disappears. give it explicit dimensions or let a flex parent size it via FlexGrow.

tinting with Color

the Color property accepts any CSS color string. the engine multiplies that color against the SVG's own pixel values, so the result is a tinted version of the art.

new SvgPanel
{
    Path   = "ui/icon.svg",
    Color  = "#6FA6DE",
    Width  = 64,
    Height = 64,
}

omit Color entirely to render the SVG's original colors untouched. pass "white" to leave the colors unchanged and control opacity through Opacity instead.

the style surface

SvgPanel subscribes to the sizing, margin, flex, background, opacity, transform, and positioning subsets, plus PointerEvents. that means Position, Top, Left, Right, Bottom, and BorderRadius (the shorthand and all four per-corner variants) work directly on the blob, no wrapper needed:

new SvgPanel
{
    Path         = "ui/icon.svg",
    Width        = 64,
    Height       = 64,
    Position     = PositionMode.Absolute,
    Left         = 20,
    Top          = 30,
    BorderRadius = 8,
}

it does not expose Padding, true border color or width, ZIndex, Overflow, or the rest of the container-only family (see styles for the full list). wrap the blob in a Container only when you need one of those:

new Container
{
    ZIndex   = 1,
    Children = { new SvgPanel { Path = "ui/icon.svg", Width = 64, Height = 64 } },
}

the full property surface SvgPanel exposes through its style facade, covering sizing, margins, flex, background, opacity, transform, positioning, and the eight mouse handlers, is in the svgpanel reference.

wrapper pattern for container-only properties

the most common reason to wrap an SvgPanel is Overflow.Hidden: clipping the rendered icon to a rounded box. BorderRadius alone does not clip content, on SvgPanel or on Container, so pair it with Overflow.Hidden on the wrapper:

new Container
{
    Width        = 96,
    Height       = 96,
    BorderRadius = 24,
    Overflow     = OverflowMode.Hidden,
    Children     = { new SvgPanel { Path = "ui/files.svg", Width = 96, Height = 96 } },
}

the outer container carries the layout, radius, and clip; the inner blob carries the rendering.

layering SVGs with LayeredSvg

LayeredSvg.Of stacks multiple SVGs into a single Container. it takes a width, a height, and a params array of SvgLayer records. each layer becomes one absolutely-positioned Container wrapping an SvgPanel: the wrapper carries Position, ZIndex, and Transform, and the inner SvgPanel carries the path, color, key, and mouse handlers. layers paint in array order, so later entries sit on top unless a layer sets an explicit ZIndex.

var art = LayeredSvg.Of(200, 200,
    new SvgLayer { Path = "ui/background.svg" },
    new SvgLayer { Path = "ui/icon.svg", Color = "#FF6B6B" });

each SvgLayer record carries:

member type purpose
Path string? the SVG asset path
Color string? optional CSS color tint
Transform PanelTransform? per-layer translate, rotate, or scale (see panel transform)
ZIndex int? paint order, defaults to the layer's array index
Key string? reconciler identity when a layer moves within the list

SvgLayer carries six of SvgPanel's eight mouse handlers: OnClick, OnMouseEnter, OnMouseLeave, OnMouseDown, OnMouseUp, OnMouseMove. it does not expose OnRightClick or OnMiddleClick.

the Transform on each SvgLayer is the animation seam. it takes a PanelTransform (see panel transform for the full op vocabulary), and LayeredSvg.Of applies it to the layer's wrapper Container, not the inner SvgPanel, so a rotate or translate moves the whole layer intact. for continuous motion, advance the angle in a Tick(float dt) override and return true while the animation runs; the panel rebuilds each frame for you, no manual Rebuild() loop (see build method for the Tick contract):

float _angle;

protected override bool Tick( float dt )
{
    _angle += dt * 60f;  // degrees per second
    return true;         // keep rebuilding while animating
}

// in Build():
var art = LayeredSvg.Of(200, 200,
    new SvgLayer { Path = "ui/base.svg" },
    new SvgLayer
    {
        Path      = "ui/needle.svg",
        Transform = PanelTransform.Rotate(_angle),
    });

PanelTransform.Rotate(float) takes degrees (see panel transform for the rest of the op table: translate, scale, skew, perspective, matrix3D). LayeredSvg.Of runs from scratch on every call, rebuilding the wrapper tree from the SvgLayer array, and the reconciler (see dynamic children for the full matching rules) lines up old and new layers by Key if present, or by array position otherwise. matching by Key keeps the underlying engine panel alive across the move; matching by position destroys and recreates it whenever an unkeyed layer's index shifts. set a Key on any layer that might change index between rebuilds so it survives.

in the wild

Code/Demo/DocsProbes/DocsShapesSvgProbeUI.cs was UAT-blessed at d91fa44 and exercises three patterns on this page: Color tint, LayeredSvg.Of with a base and a tinted layer, and the Overflow.Hidden wrapper clip. it does not exercise the rotating-needle animation above: the probe's own rotation (Radar()) applies PanelTransform.Rotate to a plain Container, not to an SvgLayer.Transform, and the file has picked up unrelated edits since the bless. treat the tint/layer/clip snippets as a pointer rather than a verified transcript.

see also