I've been trying to collect one common place for how to work with Color Variables as they involve a fair amount of the JS API space to get right. Thought I would share some basics for how to work with them, some cocoascript alternatives where it comes up a lil short, and some general thoughts.
What are Color Variables?
Color Variables lets designers use one color object in many different places! It's a more direct comparison with how colors are traditionally defined in code. Set them up once and you can apply them to all sort of things: fills, text colors, tints, etc.
So what does a Color Variable look like with the Sketch JS API?
Color Variables are represented with a Swatch
object. A swatch object has two properties a name and a color. You can create one of these objects in the same way you can create a ColorAsset
or GradientAsset
.
let sketch = require('sketch')
let mySwatch = sketch.Swatch.from({
name: 'Safety Orange',
color: '#ff6600'
})
https://developer.sketch.com/reference/api/#swatch
How do I apply a color variable (swatch) to a layer?
You might think that you could create a swatch and just apply it to a layer like this
let sketch = require('sketch')
let doc = sketch.getSelectedDocument()
let mySwatch = sketch.Swatch.from({
name: 'Safety Orange',
color: '#ff6600'
})
let selectedLayer = doc.selectedLayers.layers[0]
selectedLayer.style.fills[0].color = mySwatch.referencingColor
And while Sketch does look like this is the correct behavior (the color well turns purple), if you try to create a new layer and attempt to apply this new color variable to a new layer you will find that its not available for you to reuse.
We are missing a key step. We need to add the swatch to the collection of swatches belonging to the document.
let sketch = require('sketch')
let doc = sketch.getSelectedDocument()
let mySwatch = sketch.Swatch.from({
name: 'Safety Orange',
color: '#ff6600'
})
doc.swatches.push(mySwatch)
let selectedLayer = doc.selectedLayers.layers[0]
selectedLayer.style.fills[0].color = mySwatch.referencingColor
A couple subtle things are happening here. You probably have noticed mySwatch.referencingColor
. You cannot pass a Swatch
as a color object. You'll need to use the referencingColor
method to pass the native MSColor
object to the color.
TIL: You can pass a native MSColor
instead of a js color object https://github.com/sketch-hq/SketchAPI/blob/develop/Source/dom/assets/ColorAsset.js#L10
The other subtle thing that is happening is that document.colors
is being depreciated. When you open a file for the first time in Sketch 69 it will move all your colors to a local library. It's still around and you can write to it but it won't be displayed anywhere in the Sketch UI anymore.
How do I update color variables programmatically?
You might first try to just update the document swatch directly like this
let sketch = require('sketch')
let doc = sketch.getSelectedDocument()
let myNewSwatch = sketch.Swatch.from({
name: "Ocean Blue",
color: "#0094FF"
})
doc.swatches[0].name = "Ocean Blue"
doc.swatches[0].color = "#0094FF"
console.log(doc.swatches[0])
Unfortunately, you’ll find that you can easily change the name of the Swatch
however changing the color isn’t working.
You also can't just replace the old swatch with a new swatch
let sketch = require('sketch')
let doc = sketch.getSelectedDocument()
let myNewSwatch = sketch.Swatch.from({
name: "Ocean Blue",
color: "#0094FF"
})
doc.swatches[0] = myNewSwatch
So to update both the color and the name of a swatch you will need to drop down to the native Sketch API
let sketch = require('sketch')
let doc = sketch.getSelectedDocument()
let myNewSwatch = sketch.Swatch.from({
name: "Ocean Blue",
color: "#0094FF"
})
doc.swatches[0].sketchObject.updateWithColor(myNewSwatch.referencingColor)
doc.swatches[0].name = myNewSwatch.name
If you don't want to mess with creating the myNewSwatch
object you can do this a bit more directly like so:
let sketch = require('sketch')
let doc = sketch.getSelectedDocument()
doc.swatches[0].sketchObject.updateWithColor(MSColor.colorWithHex_alpha("#0094FF", 1))
doc.swatches[0].name = "Ocean Blue"
You'll also notice something funny with our orange rectangle layer that is still on the canvas... Well its still ORANGE not blue! The name gets updated immediately to all the instances but the colors do not (pretty sure its a bug that the names get updated but thats a separate issue).
Updating all the layers with the new color
At the time of writing you cannot do this with the JS API. You'll need to briefly drop down to the native API.
Unfortunately, doc.swatches.sketchObject
doesn't return the native equivalent and we will need to go back to the document to get the native object that we need.
The native Sketch class we need is the SketchModel.MSSwatchContainer
(I believe its written in Swift which is why you have the SketchModel prefix)
To get at this object we can use
doc.sketchObject.documentData().sharedSwatches()
Now that we have this we can put it all together and update an existing swatch and all layers with that color variable.
let sketch = require('sketch')
let doc = sketch.getSelectedDocument()
let mySwatch = doc.swatches[0]
mySwatch.sketchObject.updateWithColor(MSColor.colorWithHex_alpha("#0094FF", 1))
mySwatch.name = "Ocean Blue"
let swatchContainer = doc.sketchObject.documentData().sharedSwatches()
swatchContainer.updateReferencesToSwatch(mySwatch.sketchObject)
Handy methods on SketchModel.MSSwatchContainer
Check out the methods on this class here https://github.com/abynim/Sketch-Headers/blob/master/Headers/_TtC11SketchModel17MSSwatchContainer.h
Some notable methods are
- (id)swatchWithID:(id)arg1;
- (void)removeSwatchWithSwatch:(id)arg1;
- (id)allColorReferencesTo:(id)arg1;
- (void)updateReferencesToSwatch:(id)arg1; // we just used this!
- (id)addSwatchWithName:(id)arg1 color:(id)arg2;
Swatches (err Color Variables) and Libraries
When you first update to Sketch 69 and you find that all the document colors you have defined previously are now color variables you are probably wanting to interact with these colors in some way.
You'll want to think of these Swatches much in the same way as other ImportableObject
s in the JS API.
For a quick refresher check out the documentation https://developer.sketch.com/reference/api/#importable-object or just follow along.
To get at one of the libraries you can use the getLibraries()
top level function on Sketch
let sketch = require('sketch')
let libraries = sketch.getLibraries()
Select the library that you would like to use
let myLibrary = libraries.find(library => library.name == 'Migrated Global Colors')
This next step is key. We don't want to access the document of the Library but rather need to get the Shared Swatch References of the Library. Read more about this detail here https://developer.sketch.com/reference/api/#get-the-shared-swatches-that-can-be-imported
let doc = sketch.getSelectedDocument()
let swatchRefs = myLibrary.getImportableSwatchReferencesForDocument(doc)
We can now filter this list for the specific color that we are looking for
let importableSwatch = swatchRefs.find(swatch => swatch.name = "1. green-brand")
// you'll want to update the string here or just use swatchRefs[0]
Now we need to import the ImportableObject
let mySwatch = importableSwatch.import()
And we can apply the swatch to a layer like so
let selectedLayer = doc.selectedLayers.layers[0]
selectedLayer.style.fills[0].color = mySwatch.referencingColor
Note that here we don't need to add this swatch to the document like we did much earlier on. This swatch is referencing an external document (the library) and if changes are made in that document we will be notified of them in our document.
Putting it all together it looks like this:
let sketch = require('sketch')
let doc = sketch.getSelectedDocument()
let libraries = sketch.getLibraries()
let myLibrary = libraries.find(library => library.name == 'Migrated Global Colors')
let swatchRefs = myLibrary.getImportableSwatchReferencesForDocument(doc)
let importableSwatch = swatchRefs.find(swatch => swatch.name = "1. green-brand")
let mySwatch = importableSwatch.import()
let selectedLayer = doc.selectedLayers.layers[0]
selectedLayer.style.fills[0].color = mySwatch.referencingColor
Hope that all helps! If you have any questions, noticed any mistakes, or have any suggestions for improvement do comment below.