How to use setBoundVariableForPaint

I would appreciate some guidance on figma.variables. setBoundVariableForPaint method.

I would like to run a simple script to bind a variable to the existing style. This is what I tried

// get a style ref
let style = figma.getLocalPaintStyles()[0];
// get a var ref
let variable = figma.variables.getVariableById("VariableID:1:53");
// bind?
figma.variables.setBoundVariableForPaint(style.paints[0], 'color', variable);

It returns a solid paint indeed, however, it has no effect on a given style. Confusing…

1 Like

Alright, seem like it does not change the pain itself but returns a new one.

const styles = figma.getLocalPaintStyles();
const variables = figma.variables.getLocalVariables();

let getStyleByName = (name) => {
    return styles.find((style) => style.name === name);
};

variables.forEach(variable => {
    const name = variable.name;
    let style = getStyleByName(name);
    if(style) {
        let newPaints = style.paints.map(paint => {
            return figma.variables.setBoundVariableForPaint(paint, 'color', variable);
        });
        style.paints = newPaints;
    }
});
1 Like

Very confusing API indeed. I think there’s an issue with your code, afaik you can’t update paints of existing styles, which has been a shortcoming I haven’t been able to overcome and I think is not possible (no updateLocalStyles for instance only create). The line style.paints = newPaints; shouldn’t work I believe, although confusingly it doesn’t give a warning when using TypeScript, the type is typed as readonly Paint[].

Update:
To my surprise it’s possible to update paints by reassigning, I wish I tried this approach before! I was able to create the styles to variables functionality similar to Styles-to-Variables-Converter.

My code is as follows that might benefit others:

const localPaintStyles = figma
    .getLocalPaintStyles()
    .filter((style) => style.paints[0].type === "SOLID");

const date = new Date();
const collectionName = `Colors (imported at ${date.toLocaleString()})`;
const collection = figma.variables.createVariableCollection(collectionName);
collection.name = collectionName;

for (const paintStyle of localPaintStyles) {
    // Create variable.
    const colorVariable = figma.variables.createVariable(
        paintStyle.name,
        collection.id,
        "COLOR"
    );

  	// This only works for single color per style.
    const solidPaint = paintStyle.paints[0] as SolidPaint

    // Set the current color to the first mode.
    colorVariable.setValueForMode(
        collection.modes[0].modeId,
        solidPaint.color
    );

    // Also port the description of the paint style.
    colorVariable.description = paintStyle.description

    // Update the color style to use the variable.
    const newPaint = figma.variables.setBoundVariableForPaint(
        solidPaint,
        "color",
        colorVariable
    );

    // Change the paint on the existing style to use the variable.
    paintStyle.paints = [newPaint];
}
2 Likes

Well, shortly after my discovery I found this - https://www.figma.com/plugin-docs/working-with-variables/#example-binding-a-variable-to-a-node-or-style

Official code that reassign paints, yay! I swear it wasn’t there before :wink:

Seems using

const colorVar = {
    "type": "VARIABLE_ALIAS",
    "id": "VariableID:dc7eae0065a4fa2e06bdb4ffa9bf4a755a8232d3/3:77"
  }

const fillsCopy = clone(node.fills)
fillsCopy[0] = figma.variables.setBoundVariableForPaint(fillsCopy[0], 'color', colorVar)
node.fills = fillsCopy

throws a ts error for colorVar in setBoundVariableForPaint. Any ideas as to why? The Variable is getting fetched from a variable file that is available to the file :confused:

Your colorVar isn’t variable yet, it’s an object that you can feed into setValueForMode(modeId: string, newValue: [VariableValue])

And then do as usual

I’m trying to apply these new color variables by key when their name matches a currently used fillStyle.

So I want to push something like this in that colorVar property?

{
  key: "0cf002c6d23cdefe1209c2caa26a954ca94081ba"
  name: "brand/background"
  resolvedType: "COLOR"
}

Ultimately I’m trying to swap FillStyles with Color Variables is the goal. Seems like setBoundVariableForPaint is the obvious way to do that but I’m not 100% clear on the types of the properties in that method. Like what goes is the third param expecting here?

paintsCopy[0] = figma.variables.setBoundVariableForPaint(paintsCopy[0], 'color', colorVariable)

It takes time to figure out, I found out that reading the docs, digesting and fair bit of experiments to work well :sunglasses:

There is a code sample above that does exactly that - How to use setBoundVariableForPaint - #2 by Pavel_Kiselev

It loops through local variables, checks local colour styles and bind them together whenever name matches

And another one that goes other way around - it takes local colour styles and makes variables out of them

This is great! I had been creating blank colour variables, turns out I was missing setValueForMode. Thanks for this!