Pattern Fills
Ripl supports repeating pattern paint strings directly in fill and stroke properties. A pattern string describes a small square tile (diagonal lines, cross-hatching, dots) that repeats across the shape. The string is parsed at render time and converted to the appropriate native pattern for the current context (Canvas or SVG).
Patterns keep shapes distinguishable without relying on color alone, which helps with accessibility and print-friendly output: two series can share a hue and still read differently.
Demo
NOTE
For the full API, see the Core API Reference.
Pattern Syntax
pattern(<type>[, <foreground>[, <background>[, <size>]]])Only the type is required; the remaining arguments default sensibly when omitted:
| Argument | Default | Description |
|---|---|---|
type | required | One of the built-in pattern types below |
foreground | #000000 | CSS color used to draw the tile's lines or dots |
background | transparent | CSS color painted behind the motif (transparent leaves the tile see-through) |
size | 8 | Width and height of the square repeating tile, in pixels (an optional px suffix is accepted) |
// Black diagonal lines on a transparent tile
'pattern(diagonal)';
// Pink dots, transparent background
'pattern(dots, #ff006e)';
// Blue grid on a light background
'pattern(cross-hatch, #3a86ff, #eff6ff)';
// Explicit 12px tile, px suffix optional
'pattern(horizontal, #8338ec, transparent, 12px)';A string that does not conform to the grammar (unknown type, empty argument, zero or negative size) is not treated as a pattern: parsing fails silently and no pattern is drawn.
Pattern Types
| Type | Tile |
|---|---|
diagonal | Parallel lines at 45 degrees |
cross-hatch | A grid of crossing horizontal and vertical lines |
dots | A single filled dot centered in the tile |
horizontal | A horizontal line through the middle of the tile |
vertical | A vertical line through the middle of the tile |
Line thickness and dot radius scale with the tile: lines are drawn at size / 8 pixels thick and dots at a radius of size / 6 pixels, each with a 1 pixel minimum. A larger tile therefore produces a coarser, bolder motif, and a smaller tile a finer one.
Using Patterns
Patterns work anywhere a fill or stroke color is accepted, including both properties at once:
const rect = createRect({
fill: 'pattern(diagonal, #3a86ff, #eff6ff, 8)',
stroke: '#1a56db',
lineWidth: 2,
x: 50,
y: 50,
width: 200,
height: 120,
});Because chart options that select colors are ultimately applied as fills and strokes, pattern strings can be used there too, for example as a series color in @ripl/charts.
Patterns tile in user space (aligned to the canvas, not to each shape), so adjacent shapes sharing the same pattern string keep their tiles in phase across the seam.
How It Works
When Ripl encounters a pattern string in a style property:
- The string is parsed into a structured
Patternobject (type, foreground, background, size) - The shared tile geometry is resolved: the primitive lines and dots that make up one tile, so every context draws the same motif
- The context materializes a native pattern:
- Canvas: draws the tile to an offscreen canvas and creates a repeating
CanvasPattern, cached per pattern string - SVG: creates a
<pattern>element in a<defs>block (tiled in user space) and references it viaurl(#id), updating the definition in place when the paint changes
- Canvas: draws the tile to an offscreen canvas and creates a repeating
- The native pattern is applied as the fill or stroke style
Pattern Interpolation
Like gradients, pattern paints animate during transitions. When both values are patterns of the same tile type, renderer.transition() interpolates their foreground color, background color, and tile size:
// A diagonal pattern eases its colors and tile size toward the target.
await renderer.transition(rect, {
duration: 1000,
state: {
fill: 'pattern(diagonal, #ff006e, #fff0, 16)',
},
});The tile type is the pattern's signature. When the two patterns share a type they interpolate smoothly; when the types differ (say, diagonal to dots) there is no meaningful in-between motif, so the paint snaps from one to the other at the transition midpoint. The same snap applies when only one endpoint is a pattern, since a repeating tile cannot morph into a plain color or a gradient.
NOTE
The literal transparent keyword is not a parseable color, so a transparent background snaps at the midpoint rather than fading. Use a zero-alpha color such as #fff0 or rgba(255, 255, 255, 0) for a smooth fade.
Working with Patterns Programmatically
The parsing utilities behind pattern strings are exported from @ripl/core:
import {
isPatternString,
parsePattern,
serializePattern,
} from '@ripl/core';
isPatternString('pattern(dots)'); // true
const pattern = parsePattern('pattern(diagonal, #1a6, #fff0, 8)');
// {
// type: 'diagonal',
// foreground: '#1a6',
// background: '#fff0',
// size: 8,
// }
if (pattern) {
serializePattern(pattern); // 'pattern(diagonal, #1a6, #fff0, 8)'
}parsePattern(value)parses a pattern string into aPatternobject, applying defaults for omitted arguments, and returnsnullwhen the string is invalid.serializePattern(pattern)turns aPatternobject back into its canonical string form.isPatternString(value)is a cheap shape check (does the string look likepattern(...)); useparsePatternto validate fully.PATTERN_TYPESlists the validPatternTypevalues, useful for building pattern pickers.DEFAULT_PATTERN_FOREGROUND,DEFAULT_PATTERN_BACKGROUND, andDEFAULT_PATTERN_SIZEexpose the defaults applied when arguments are omitted.
Custom contexts can call getPatternTileGeometry(pattern) to obtain the renderer-agnostic tile geometry (the tile size plus the lines and dots to draw within it) and reproduce the same motifs in their own backend.