CSS Houdini Collection

CSS Paint API: Part 4 Custom Arguments

Not only do we have access to our own custom properties, but we can also pass in our own custom arguments to the paint() function as well.

We add these extra arguments when we call the function in the CSS. Let's say we want to sometimes stroke our background instead of fill it, let's pass in an extra argument for this occasion.

h1 {
	background-image: paint(headerHighlight, stroke);
}

My Cool Header

Now we can use the inputArguments() method in the registerPaint() class, to get a list of these custom arguments we have added to our paint() function:

static get inputArguments() { return ['*']; }
paint(ctx, size, props, args) {

	// use our custom arguments
	const hasStroke = args[0].toString();

	// if stroke arg is stroke, don't fill
	if (hasStroke === 'stroke') {
		ctx.fillStyle = 'transparent';
		ctx.strokeStyle = colour;
	}
	...
}

In this case we have just added the string 'stroke', but there's more — if the custom argument is a CSS value, for instance a unit, we can invoke Typed OM CSSStyleValue class (and sub classes) by using the value type keyword when we retrieve it in the registerPaint() function.

Let's say we add a second argument with how many pixels wide we want the stroke to be:

h1 {
	background-image: paint(headerHighlight, stroke, 3px);
}

When we get our list of argument values, we ask specifically for a <length> unit.

static get inputArguments() { return ['*', '<length>']; }

Now we can access the type and value properties, meaning we can get the number of pixels and a number type right out of the box!

paint(ctx, size, props, args) {

		const strokeWidth = args[1];

		if (strokeWidth.unit === 'px') {
			ctx.lineWidth = strokeWidth.value;
		} else {
			ctx.lineWidth = 1.0;
		}

	...
}

Note: It's worth noting the difference between using custom properties to control different parts of this worklet and the arguments set out here. The situation will depend on this: Custom properties (and in fact any properties on the style map) are global — they can be used elsewhere within our CSS (and JS). You may for example have a --mainColor, which will be useful for setting the color within a paint() function, but also sets colors elsewhere in your CSS. If you wanted to change it specifically for paint, it could prove difficult, so this is where thinking about custom arguments may come in. Another way to think about it is that arguments are set to control what you are actually drawing, whereas properties are set to control styling.

Now we can really start to see the benefits of this API, if we can control a myriad of drawing parameters from our CSS through both custom properties and extra paint() function arguments, then we can really start to build reusable and infinitely controlled styling elements.

In the next part we take a look at building such a styling element and how we can configure it to be used throughout our code. Carry on with CSS Paint in the next part here.