Skip to content

Paint & Shared Paint Props ​

Every shape (and Canvas/Group) accepts the shared GraphicProps — the paint and compositing attributes that control how a node draws. This page documents that shared surface, plus <Paint>, the declarative child that overrides a shape's fill or stroke paint.

Multi-pass painting — concentric strokes and a fill + halo stroke pass
tsx
import { Paint } from "@scumble/react";

Shared graphic props ​

All shape components document only their geometry; these props (GraphicProps) are available on every one of them. Geometry is authored in the canvas coordinate space (logical pixels when viewPort is set).

PropTypeDefaultDescription
colorColor—Fill or stroke color — any CSS color string ("red", "#fff", "rgb(…)", …), a packed 0xAARRGGBB number, an { r, g, b, a? } object, or an [r, g, b, a?] tuple. Omit for a transparent (no-op) shape.
style"fill" | "stroke""fill"Whether color fills or strokes the shape. Line, Polyline, and Points default to "stroke".
strokeWidthnumber—Stroke width (dp). Stroke-only.
strokeCap"butt" | "round" | "square"—Cap style for the open endpoints of a stroke. Stroke-only.
strokeJoin"miter" | "round" | "bevel"—Join style for the corners of a stroke. Stroke-only.
strokeMiternumber—Miter limit for "miter" joins. Stroke-only.
opacitynumber—Shape opacity, 0–1, folded into the paint's color alpha. Inheritable from a Group.
dashnumber[]—Stroke dash intervals in px — [on, off, on, off, …]. An odd array is repeated once to make it even (SVG stroke-dasharray semantics). An invalid pattern (empty / negative values / zero sum) is dropped — the stroke stays solid. Stroke-only.
dashOffsetnumber—Phase offset into the dash pattern (px).
blendModeBlendMode—Blend mode (Skia's 28 modes) — how the shape composites onto what is below it. Applied to the shape's fill and stroke paints; inheritable from a Group.
zIndexnumber—z-index, accepted for parity; native z-ordering follows tree order today.
transformTransformProp—Transform applied to this node's own drawing (shapes) and its whole subtree (groups). Nested transforms cascade. See transform forms.
animateAnimationSpec | AnimationSpec[] | null—Declarative native animations — one track per property, many tracks per node. null/false array entries are filtered; an empty array (or null) clears the node's animations. See Animation.
childrenReactNode—Declarative children consumed by the shape's paint: shaders (gradients, ImageShader), <Paint> overrides, and filters.

BlendMode values ​

BlendMode mirrors Skia/skity's 28 modes (kebab-case): clear, src, dst, src-over, dst-over, src-in, dst-in, src-out, dst-out, src-atop, dst-atop, xor, plus, modulate, screen, overlay, darken, lighten, color-dodge, color-burn, hard-light, soft-light, difference, exclusion, multiply, hue, saturation, color, luminosity.

Transform forms ​

transform takes a single op object, a 4×4 column-major matrix, or an array of ops composed left-to-right ([translate, rotate] translates first, then rotates — Skia canvas semantics).

FormTypeNotes
translate{ translateX?: number; translateY?: number }Defaults 0.
scale{ scaleX?: number; scaleY?: number }Each defaults to the other, or 1.
rotate{ rotate: number; x?: number; y?: number }rotate is degrees (not radians); x/y are an optional pivot (native rotate-with-center support pending verification).
matrixnumber[]A 4×4 column-major matrix — 16 numbers, m[col*4 + row]; the 2D affine part is m0, m1, m4, m5, m12, m13.
arrayTransform[]Ops composed left-to-right; a matrix inside an array composes like any other op.
tsx
<Rect width={80} height={80} color="#f59e0b" transform={[{ translateX: 100 }, { rotate: 45 }]} />

<Paint> ​

Declarative paint override — a data-only child of a shape. It overrides the paint properties for one style, and shaders nested inside it apply to that paint.

tsx
<Circle cx={100} cy={100} radius={70}>
  <LinearGradient start={vec(30, 30)} end={vec(170, 170)} colors={["#f00", "#00f"]} />
  <Paint style="stroke" strokeWidth={8}>
    <SweepGradient c={vec(100, 100)} colors={["#0f0", "#00f"]} />
  </Paint>
</Circle>

Here the <LinearGradient> (placed directly under the shape) fills the circle, while the <SweepGradient> inside <Paint style="stroke"> strokes it. The component renders nothing itself — the parent shape reads its props and merges them into its native paint props.

Properties given here override the shape-level ones; properties omitted fall back to the shape level.

Native paint-slot limits

At most one fill paint + one stroke paint per shape (a later <Paint> of the same style wins), and opacity is not honored here — opacity is a single node-level channel.

Props ​

PropTypeDefaultDescription
style"fill" | "stroke""fill"Which paint this declaration targets.
colorColor—Paint color; overrides the shape's color for this style.
blendModeBlendMode—Overrides the shape's blendMode. Natively one blend mode is shared by the fill and stroke paints — the last declaration wins.
strokeWidthnumber—Stroke width. Stroke-only.
strokeCap"butt" | "round" | "square"—Stroke cap. Stroke-only.
strokeJoin"miter" | "round" | "bevel"—Stroke join. Stroke-only.
strokeMiternumber—Miter limit. Stroke-only.
dashnumber[]—Dash intervals — see the shared dash. Stroke-only.
dashOffsetnumber—Phase offset into the dash pattern. Stroke-only.
childrenReactNode—Shader children (e.g. <LinearGradient>) applied to this paint.

Released under the Apache License 2.0.