Vector images (resource/vectors/, the vector piece)

Like resource/images/ but resolution-independent: SVG glyphs staged per backend into whatever form its toolkit loads natively, surfaced through typed constants and one piece:

vector(res::vectors::home).tint(color).frame(24.0, 24.0)

res::vectors::… constants are generated by day-build (a VectorName per file — a typo is a compile error, presence is guaranteed). Vector and image names share one per-backend resolution namespace (From<VectorName> for ImageName), so the name-based channels — nav-item icons, tab icons, toolbar_button(...).image(…), bar_action — accept vectors unchanged.

Source forms

  • Plain .svg — the whole document is the glyph (raw Material Symbols downloads work as-is).
  • SF Symbol template .svg — the SF Symbols app’s export (and third-party generators of the same shape): #Symbols holding Weight-Scale variant groups. Day reduces it to the canonical Regular variant for non-Apple targets (cut out textually, re-boxed to its measured content).
  • .symbolset/ bundle — an Xcode custom-symbol bundle; its inner template SVG routes through the same reduction.

Text must be outlined (<text> is a hard build error; shaping is not compiled in).

Per-backend staging (§18.3)

At build, every glyph gets a raster cache PNG (build/day/vectors/raster/, 256 px), a staged glyph SVG (build/day/vectors/svg/), and — where the art converts — XAML geometry (build/day/vectors/xaml/).

The raster cache is a build INPUT, not a shipping form. What a target carries is build/day/vectors/fallback/<toolkit>/: on a toolkit with no vector arm (gtk, qt) that is every glyph, and on one that draws vectors it is only the art the vector pipeline could not express — usually nothing. Bundling the rest would ship a second copy of every glyph AND let a broken vector path go unnoticed behind a raster that still looks right, which is how two XAML bugs survived their first review. day build reports the split per target (Vectors xaml: 81/81 glyph(s) vector). What ships on top:

BackendNative form
android-mdcVectorDrawable in res/drawable/ (solid fills/strokes, both fill rules; gradients/clips/masks/filters fall back to the raster at xxxhdpi, loudly)
macos-appkitthe staged SVG via DAY_VECTOR_SVG_ROOT (NSImage renders SVG files at display size on macOS 11+); other desktop dev launches use the raster cache via DAY_VECTOR_RASTER_ROOT (probed by resolve_image_file)
ios-uikitthe SVG in a DayPieces asset-catalog imageset with preserves-vector-representation (Xcode 12+), so UIImage(named:) renders at display size
web-domthe SVG beside assets/images/ — day-dom asks for .svg for the names in the page’s window.__DAY_VECTORS list (via the shim’s vector: env keys) and the browser renders it; the raster stays beside it as the older-host fallback
harmony-arkuithe SVG in rawfile day/ (same stem as the raster) — day-arkui probes day/<name>.svg via the resource manager and ArkUI’s Image renders it natively; raster names keep the png
windows-xamlXAML geometry via DAY_VECTOR_XAML_ROOT — a Path in a scaling Viewbox in page content, a PathIcon in the nav pane’s icon slot, so the glyph is redrawn at every size rather than scaled from a cache. Emitted by the CLI (day_vector::to_xaml_geometry) as absolute M/L/C/Z only: the grammar is SVG’s, but a command XAML’s parser rejects fails the WHOLE geometry, so quadratics are elevated to cubics (exact) and arcs are flattened upstream by usvg. Same subset as the VectorDrawable emission — gradients/clips/masks/filters stage no geometry and fall back to the raster

Packed desktop apps carry the same forms without the launch env: a .app ships Contents/Resources/vectors/{svg,raster} (probed exe-relative — the SVGs by resolve_vector_svg, the rasters by resolve_image_file); an AppImage/flatpak ships share/<name>/vectors/{raster,svg} with the launcher exporting the same DAY_VECTOR_*_ROOT roots a dev launch would; the Windows payload merges the raster cache into the exe-relative images/, where both the piece resolution and the nav rows’ ms-appx:///images/ loads already look, and ships vectors/xaml beside the exe — geometry specs are not loadable images, so they cannot merge into images/. The glyph SVGs do not ship to Windows at all: that backend draws the geometry and nothing there reads an SVG.

Weights

Every vector stages three weight variants: the canonical Regular under its own name, and __light/__bold suffixed names behind vector(...).weight(VectorWeight::Light | Bold). Template-form sources (SF template SVGs, .symbolset bundles) contribute true per-weight art (their Light/Bold variants, with the template’s own fallback ladder for sparse exports); plain SVGs alias all three to the same glyph, so .weight(…) degrades to Regular rather than to a missing asset — on every backend, since the suffixed names ride the same staging.

Tint

.tint(color) recolors a monochrome glyph where the backend can: template rendering + contentTintColor on AppKit, alwaysTemplate + tintColor on UIKit, setImageTintList on Android, pixel recolor on GTK, SVG fill color (NODE_IMAGE_FILL_COLOR) on ArkUI, and a brush on the Path shapes on XAML — composed over the geometry when the glyph is realized, so one staged glyph serves every tint at every size with no second asset and no recoloured copy. Backends without a tint arm yet (Qt, web) draw the authored colours — the coverage-honest degradation. None (and every raster image(…)) means “as authored”.

A tint follows the art it was authored with: the colour fills where the glyph filled and strokes where it stroked, so an outline glyph stays an outline rather than becoming a silhouette. Where XAML has no geometry to draw (art outside the convertible subset) the tint degrades to a monochrome BitmapIcon over the raster — still tinted, but from the 256 px cache.

Nav-menu rows have their own arm, item(…).icon_tint(color) (docs/navigation.md): per-row recolor on AppKit (contentTintColor), UIKit (tintColor), GTK (pixel recolor), Qt (QPainter SourceIn over the pixmap), Android (compound-drawable setTint, best-effort after the nav host mounts), ArkUI (SVG fill color), XAML (Foreground on the row’s PathIcon), and web (the mask painted with the tint instead of currentColor). Untinted rows keep each backend’s template default (theme foreground / secondary label); ArkUI’s untinted raster rows draw as authored (fill color is SVG-only).

Lint

day lint validates every vector source: unreadable/unparseable art, glyph-embedded <text> (a template’s Notes/Guides documentation text is fine — only the extracted glyph matters), an empty .symbolset, a template whose Regular variant fails extraction, and — when android-mdc is a declared target — art outside the VectorDrawable subset (day::lint::vector-raster-fallback, a heads-up that Android ships the raster).

Not yet

Apple-native symbol weights (the .symbolset catalog staging that would unlock them — staged weights are template-extracted everywhere today, Apple included), and the opt-in runtime rasterizer for downloaded SVGs (day-piece-remote-image’s planned svg arm).