Summary#
An SVG asset is a file in your app, not data. Put a .svg in model/art/ and it becomes part of your app's
vocabulary:
model/
art/
hexgrid.svg
hero.svg
logo-full.svg[Composable]
component Splash() {
render {
Box(position: Position.Relative, minH: "100vh") {
Svg(Art.Hexgrid, position: Position.Absolute, inset: 0, z: "-1"); // a full-bleed background
Text("Welcome");
}
}
}This compiles against the drop-ship-order sample, whose art/ folder really does contain hexgrid.svg
— the name is checked against the files the app ships.
Adding an asset is dropping a file in. There is nothing to register and nothing to import.
Signature#
Svg(Art.Hexgrid) · Svg(Art.Hexgrid, position: Position.Absolute, inset: 0) — a named SVG asset, placed with style propsDescription#
Asset or icon?#
Reach for an asset when the artwork carries its own colours — an illustration, a background pattern, a hero image, a full-colour logo. Reach for an icon when it's a single-colour glyph that should follow the surrounding text colour.
Icon(name) | Svg(name) | |
|---|---|---|
| Lives in | model/icons/ | model/art/ |
| Colour | recoloured to the current text colour | its own colours, gradients, <pattern>s |
| Sizing | an em square (size:) | placed with style props (w:/h:/position:/…) |
| For | UI glyphs | illustrations, backgrounds, multi-colour logos |
The name is checked#
Svg(Art.Hexgrid) names the asset by a bare identifier, checked against the assets your app actually declares. A
typo is a compile error with a suggestion:
unknown SVG asset 'hexgrd' (declared assets: hero, hexgrid, logo). Did you mean 'hexgrid'?Because the name is an identifier, an asset's file name must be one too — logo_full.svg, not
logo-full.svg. A kebab-case file is rejected with the rename to make.
The name is never an expression. A local variable called hexgrid does not change what Svg(Art.Hexgrid) means —
the asset vocabulary always wins. An asset chosen at runtime is a content concern, not chrome: use Image(src)
for that.
Sizing and placing an asset — ordinary style props#
An asset keeps its own colours, so there is no size:/color:. Instead it takes the ordinary
style props, so you place it like any other element — a sized inline logo, or a full-bleed
background behind a card:
[Composable]
component Wordmark() {
render { Svg(Art.LogoFull, w: 140); } // an inline, fixed-width logo
}
[Composable]
component Patterned() {
render {
Box(position: Position.Relative) {
Svg(Art.Hexgrid, position: Position.Absolute, inset: 0, z: "-1"); // tiles behind the box's content
Slot;
}
}
}By default an asset fills the box you give it, so a full-bleed background is position: Position.Absolute; inset: 0 on a
position: Position.Relative parent, and a fixed-size asset is just w:/h:. Anything other than the asset name and style
props is a compile error.
What an asset may contain#
An asset is drawing: shapes, groups, and the paint machinery that gives it colour — linearGradient,
radialGradient, pattern, clipPath, mask, and a <defs> block, with in-document fill="url(#…)" references
to them. Its colours are kept exactly as drawn.
Anything that could run, load, or reach outside the file is a compile error, naming the file:
'evil.svg' contains a <script> element — scripting, styling, embedding, external
references and animation are not allowed in an SVG asset.That covers <script>, <style>, <image>, <use>, <a>, animation elements, any on… handler, and any URL
that leaves the document — an external or data: image, a javascript: link, a url(https://…). A url(#id)
that points inside the same asset (a gradient or pattern fill) is fine; each asset's ids are kept separate, so
two assets that happen to use the same id never collide. An SVG is a place scripts can hide, and your assets are
placed directly into your pages — so the rule is an allow-list, and it is not negotiable.
Assets from a UI kit#
A kit's assets land in your app tree alongside your own and are picked up the same way. Two files claiming the same name is an error naming both, so an asset always resolves to exactly one file.
How assets are delivered — inline, on the first byte#
A server-rendered page paints its assets inline on the very first byte — no request, no flash. Change an asset and the delivered copy changes with it; leave it alone and browsers keep the copy they already have.
Custom glob, if model/art/ doesn't suit you:
app Admin {
model "model/**/*.osy";
svg "assets/art/*.svg";
}Passing an asset around — Art is a type#
Art is a type, so an SVG asset can be a component parameter, a return value or a stored field:
[Composable]
component Badge(string label, Art art) {
render {
Stack {
Svg(art, w: 120);
Text(label);
}
}
}
// at the call site
Badge("Grid", Art.Hexgrid);The vocabulary is built from the files themselves, so there is nothing to declare — and an app that ships no
model/art/ files simply has no Art type yet, which the compiler says in those words.
See also#
- icons — the single-colour glyph counterpart, recoloured to the current text colour.
- style props — the
position/inset/w/hprops that place an asset. - theme tokens — the tokens the surrounding layout resolves against.