// Variable Collection CRUD
interface CreateVariableCollectionParams {
name: string;
initialModes?: string[];
}
interface UpdateVariableCollectionParams {
collectionId: string;
name?: string;
}
interface DeleteVariableCollectionParams {
collectionId: string;
}
interface GetVariableCollectionParams {
collectionId: string;
}
// Variable CRUD
interface CreateVariableParams {
collectionId: string;
name: string;
resolvedType: "BOOLEAN" | "COLOR" | "FLOAT" | "STRING";
scopes?: VariableScope[];
description?: string;
}
interface UpdateVariableParams {
variableId: string;
name?: string;
scopes?: VariableScope[];
description?: string;
}
interface DeleteVariableParams {
variableId: string;
}
interface GetVariablesInCollectionParams {
collectionId: string;
}
// Mode Management
interface GetVariableModesParams {
collectionId: string;
}
interface CreateVariableModeParams {
collectionId: string;
name: string;
}
interface RenameVariableModeParams {
collectionId: string;
modeId: string;
name: string;
}
interface DeleteVariableModeParams {
collectionId: string;
modeId: string;
}
// Value Management
interface SetVariableValueForModeParams {
variableId: string;
modeId: string;
value: boolean | number | string | RGBA | VariableAlias;
}
// Binding
interface BindVariableToNodeParams {
nodeId: string;
variableId: string;
field: string;
}
interface UnbindVariableFromNodeParams {
nodeId: string;
field: string;
}
// Extended Collection params
interface CreateExtendedCollectionParams {
collectionKey: string;
name: string;
}
interface GetExtendedCollectionParams {
collectionId: string;
}
interface SetExtendedCollectionOverrideParams {
collectionId: string;
variableId: string;
modeId: string;
value: boolean | number | string | RGBA | VariableAlias;
}
interface RemoveExtendedCollectionOverrideParams {
collectionId: string;
variableId: string;
}
// ============ Collection CRUD ============
/**
* Get all local variable collections
*/
export async function getLocalVariableCollections(): Promise<{
collections: Array<{
id: string;
name: string;
key: string;
modes: Array<{ modeId: string; name: string }>;
variableCount: number;
}>;
}> {
const collections = await figma.variables.getLocalVariableCollectionsAsync();
return {
collections: collections.map((c) => ({
id: c.id,
name: c.name,
key: c.key,
modes: c.modes.map((m) => ({ modeId: m.modeId, name: m.name })),
variableCount: c.variableIds.length,
})),
};
}
/**
* Get a specific variable collection by ID
*/
export async function getVariableCollection(
params: GetVariableCollectionParams,
): Promise<{
id: string;
name: string;
key: string;
modes: Array<{ modeId: string; name: string }>;
variableIds: string[];
}> {
const { collectionId } = params;
const collection =
await figma.variables.getVariableCollectionByIdAsync(collectionId);
if (!collection) {
throw new Error(`Variable collection not found: ${collectionId}`);
}
return {
id: collection.id,
name: collection.name,
key: collection.key,
modes: collection.modes.map((m) => ({ modeId: m.modeId, name: m.name })),
variableIds: collection.variableIds,
};
}
/**
* Create a new variable collection
*/
export async function createVariableCollection(
params: CreateVariableCollectionParams,
): Promise<{
id: string;
name: string;
key: string;
modes: Array<{ modeId: string; name: string }>;
}> {
const { name, initialModes } = params;
const collection = figma.variables.createVariableCollection(name);
// Rename or add modes if specified
if (initialModes && initialModes.length > 0) {
// Rename the default mode
collection.renameMode(collection.modes[0].modeId, initialModes[0]);
// Add additional modes
for (let i = 1; i < initialModes.length; i++) {
collection.addMode(initialModes[i]);
}
}
return {
id: collection.id,
name: collection.name,
key: collection.key,
modes: collection.modes.map((m) => ({ modeId: m.modeId, name: m.name })),
};
}
/**
* Update a variable collection
*/
export async function updateVariableCollection(
params: UpdateVariableCollectionParams,
): Promise<{
success: boolean;
id: string;
name: string;
}> {
const { collectionId, name } = params;
const collection =
await figma.variables.getVariableCollectionByIdAsync(collectionId);
if (!collection) {
throw new Error(`Variable collection not found: ${collectionId}`);
}
if (name) {
collection.name = name;
}
return {
success: true,
id: collection.id,
name: collection.name,
};
}
/**
* Delete a variable collection
*/
export async function deleteVariableCollection(
params: DeleteVariableCollectionParams,
): Promise<{
success: boolean;
deletedId: string;
}> {
const { collectionId } = params;
const collection =
await figma.variables.getVariableCollectionByIdAsync(collectionId);
if (!collection) {
throw new Error(`Variable collection not found: ${collectionId}`);
}
collection.remove();
return {
success: true,
deletedId: collectionId,
};
}
// ============ Variable CRUD ============
/**
* Get variables in a collection
*/
export async function getVariablesInCollection(
params: GetVariablesInCollectionParams,
): Promise<{
collectionId: string;
variables: Array<{
id: string;
name: string;
key: string;
resolvedType: VariableResolvedDataType;
description: string;
scopes: VariableScope[];
}>;
}> {
const { collectionId } = params;
const collection =
await figma.variables.getVariableCollectionByIdAsync(collectionId);
if (!collection) {
throw new Error(`Variable collection not found: ${collectionId}`);
}
const variables = await Promise.all(
collection.variableIds.map((id) =>
figma.variables.getVariableByIdAsync(id),
),
);
return {
collectionId,
variables: variables
.filter((v): v is Variable => v !== null)
.map((v) => ({
id: v.id,
name: v.name,
key: v.key,
resolvedType: v.resolvedType,
description: v.description,
scopes: v.scopes,
})),
};
}
/**
* Create a variable
*/
export async function createVariable(params: CreateVariableParams): Promise<{
id: string;
name: string;
key: string;
resolvedType: VariableResolvedDataType;
}> {
const { collectionId, name, resolvedType, scopes, description } = params;
const collection =
await figma.variables.getVariableCollectionByIdAsync(collectionId);
if (!collection) {
throw new Error(`Variable collection not found: ${collectionId}`);
}
const variable = figma.variables.createVariable(
name,
collection,
resolvedType,
);
if (scopes) {
variable.scopes = scopes;
}
if (description) {
variable.description = description;
}
return {
id: variable.id,
name: variable.name,
key: variable.key,
resolvedType: variable.resolvedType,
};
}
/**
* Update a variable
*/
export async function updateVariable(params: UpdateVariableParams): Promise<{
success: boolean;
id: string;
name: string;
}> {
const { variableId, name, scopes, description } = params;
const variable = await figma.variables.getVariableByIdAsync(variableId);
if (!variable) {
throw new Error(`Variable not found: ${variableId}`);
}
if (name) {
variable.name = name;
}
if (scopes) {
variable.scopes = scopes;
}
if (description !== undefined) {
variable.description = description;
}
return {
success: true,
id: variable.id,
name: variable.name,
};
}
/**
* Delete a variable
*/
export async function deleteVariable(params: DeleteVariableParams): Promise<{
success: boolean;
deletedId: string;
}> {
const { variableId } = params;
const variable = await figma.variables.getVariableByIdAsync(variableId);
if (!variable) {
throw new Error(`Variable not found: ${variableId}`);
}
variable.remove();
return {
success: true,
deletedId: variableId,
};
}
// ============ Mode Management ============
/**
* Get variable modes in a collection
*/
export async function getVariableModes(
params: GetVariableModesParams,
): Promise<{
collectionId: string;
modes: Array<{ modeId: string; name: string }>;
}> {
const { collectionId } = params;
const collection =
await figma.variables.getVariableCollectionByIdAsync(collectionId);
if (!collection) {
throw new Error(`Variable collection not found: ${collectionId}`);
}
return {
collectionId,
modes: collection.modes.map((m) => ({ modeId: m.modeId, name: m.name })),
};
}
/**
* Create a variable mode
*/
export async function createVariableMode(
params: CreateVariableModeParams,
): Promise<{
success: boolean;
modeId: string;
name: string;
}> {
const { collectionId, name } = params;
const collection =
await figma.variables.getVariableCollectionByIdAsync(collectionId);
if (!collection) {
throw new Error(`Variable collection not found: ${collectionId}`);
}
const modeId = collection.addMode(name);
return {
success: true,
modeId,
name,
};
}
/**
* Rename a variable mode
*/
export async function renameVariableMode(
params: RenameVariableModeParams,
): Promise<{
success: boolean;
modeId: string;
name: string;
}> {
const { collectionId, modeId, name } = params;
const collection =
await figma.variables.getVariableCollectionByIdAsync(collectionId);
if (!collection) {
throw new Error(`Variable collection not found: ${collectionId}`);
}
collection.renameMode(modeId, name);
return {
success: true,
modeId,
name,
};
}
/**
* Delete a variable mode
*/
export async function deleteVariableMode(
params: DeleteVariableModeParams,
): Promise<{
success: boolean;
deletedModeId: string;
}> {
const { collectionId, modeId } = params;
const collection =
await figma.variables.getVariableCollectionByIdAsync(collectionId);
if (!collection) {
throw new Error(`Variable collection not found: ${collectionId}`);
}
collection.removeMode(modeId);
return {
success: true,
deletedModeId: modeId,
};
}
// ============ Value Management ============
/**
* Set variable value for a specific mode
*/
export async function setVariableValueForMode(
params: SetVariableValueForModeParams,
): Promise<{
success: boolean;
variableId: string;
modeId: string;
}> {
const { variableId, modeId, value } = params;
const variable = await figma.variables.getVariableByIdAsync(variableId);
if (!variable) {
throw new Error(`Variable not found: ${variableId}`);
}
variable.setValueForMode(modeId, value);
return {
success: true,
variableId,
modeId,
};
}
// ============ Binding ============
/**
* Bind variable to node property
*/
export async function bindVariableToNode(
params: BindVariableToNodeParams,
): Promise<{
success: boolean;
nodeId: string;
variableId: string;
field: string;
}> {
const { nodeId, variableId, field } = params;
const node = await figma.getNodeByIdAsync(nodeId);
if (!node) {
throw new Error(`Node not found: ${nodeId}`);
}
const variable = await figma.variables.getVariableByIdAsync(variableId);
if (!variable) {
throw new Error(`Variable not found: ${variableId}`);
}
// Bind variable to the specified field
if ("setBoundVariable" in node) {
const bindableNode = node as SceneNode & {
setBoundVariable: (field: string, variable: Variable) => void;
};
bindableNode.setBoundVariable(field as VariableBindableNodeField, variable);
} else {
throw new Error(`Node does not support variable binding: ${nodeId}`);
}
return {
success: true,
nodeId,
variableId,
field,
};
}
/**
* Unbind variable from node property
*/
export async function unbindVariableFromNode(
params: UnbindVariableFromNodeParams,
): Promise<{
success: boolean;
nodeId: string;
field: string;
}> {
const { nodeId, field } = params;
const node = await figma.getNodeByIdAsync(nodeId);
if (!node) {
throw new Error(`Node not found: ${nodeId}`);
}
// Unbind variable from the specified field
if ("setBoundVariable" in node) {
const bindableNode = node as SceneNode & {
setBoundVariable: (field: string, variable: null) => void;
};
bindableNode.setBoundVariable(field as VariableBindableNodeField, null);
} else {
throw new Error(`Node does not support variable binding: ${nodeId}`);
}
return {
success: true,
nodeId,
field,
};
}
// ============ Extended Collections ============
/**
* Get all extended variable collections
*/
export async function getExtendedVariableCollections(): Promise<{
collections: Array<{
id: string;
name: string;
key: string;
isExtension: boolean;
parentVariableCollectionId: string;
modes: Array<{ modeId: string; name: string }>;
variableCount: number;
}>;
}> {
const allCollections =
await figma.variables.getLocalVariableCollectionsAsync();
// Filter to only extended collections
const extendedCollections = allCollections.filter(
(c): c is ExtendedVariableCollection =>
"isExtension" in c && c.isExtension === true,
);
return {
collections: extendedCollections.map((c) => ({
id: c.id,
name: c.name,
key: c.key,
isExtension: true,
parentVariableCollectionId: c.parentVariableCollectionId,
modes: c.modes.map((m) => ({ modeId: m.modeId, name: m.name })),
variableCount: c.variableIds.length,
})),
};
}
/**
* Create an extended variable collection from a library or local collection
*/
export async function createExtendedCollection(
params: CreateExtendedCollectionParams,
): Promise<{
id: string;
name: string;
key: string;
isExtension: boolean;
parentVariableCollectionId: string;
modes: Array<{ modeId: string; name: string }>;
}> {
const { collectionKey, name } = params;
const extendedCollection =
await figma.variables.extendLibraryCollectionByKeyAsync(
collectionKey,
name,
);
return {
id: extendedCollection.id,
name: extendedCollection.name,
key: extendedCollection.key,
isExtension: true,
parentVariableCollectionId: extendedCollection.parentVariableCollectionId,
modes: extendedCollection.modes.map((m) => ({
modeId: m.modeId,
name: m.name,
})),
};
}
/**
* Get extended collection details including overrides
*/
export async function getExtendedCollection(
params: GetExtendedCollectionParams,
): Promise<{
id: string;
name: string;
key: string;
isExtension: boolean;
parentVariableCollectionId: string;
modes: Array<{ modeId: string; name: string }>;
variableIds: string[];
variableOverrides: Record<string, Record<string, VariableValue>>;
}> {
const { collectionId } = params;
const collection =
await figma.variables.getVariableCollectionByIdAsync(collectionId);
if (!collection) {
throw new Error(`Variable collection not found: ${collectionId}`);
}
if (!("isExtension" in collection) || !collection.isExtension) {
throw new Error(
`Collection is not an extended collection: ${collectionId}`,
);
}
const extendedCollection = collection as ExtendedVariableCollection;
return {
id: extendedCollection.id,
name: extendedCollection.name,
key: extendedCollection.key,
isExtension: true,
parentVariableCollectionId: extendedCollection.parentVariableCollectionId,
modes: extendedCollection.modes.map((m) => ({
modeId: m.modeId,
name: m.name,
})),
variableIds: extendedCollection.variableIds,
variableOverrides: extendedCollection.variableOverrides,
};
}
/**
* Set a variable override in an extended collection
*/
export async function setExtendedCollectionOverride(
params: SetExtendedCollectionOverrideParams,
): Promise<{
success: boolean;
collectionId: string;
variableId: string;
modeId: string;
}> {
const { collectionId, variableId, modeId, value } = params;
const collection =
await figma.variables.getVariableCollectionByIdAsync(collectionId);
if (!collection) {
throw new Error(`Variable collection not found: ${collectionId}`);
}
if (!("isExtension" in collection) || !collection.isExtension) {
throw new Error(
`Collection is not an extended collection: ${collectionId}`,
);
}
const variable = await figma.variables.getVariableByIdAsync(variableId);
if (!variable) {
throw new Error(`Variable not found: ${variableId}`);
}
// Set the override value for this variable/mode combination
variable.setValueForMode(modeId, value);
return {
success: true,
collectionId,
variableId,
modeId,
};
}
/**
* Remove all overrides for a variable in an extended collection
*/
export async function removeExtendedCollectionOverride(
params: RemoveExtendedCollectionOverrideParams,
): Promise<{
success: boolean;
collectionId: string;
variableId: string;
}> {
const { collectionId, variableId } = params;
const collection =
await figma.variables.getVariableCollectionByIdAsync(collectionId);
if (!collection) {
throw new Error(`Variable collection not found: ${collectionId}`);
}
if (!("isExtension" in collection) || !collection.isExtension) {
throw new Error(
`Collection is not an extended collection: ${collectionId}`,
);
}
const extendedCollection = collection as ExtendedVariableCollection;
const variable = await figma.variables.getVariableByIdAsync(variableId);
if (!variable) {
throw new Error(`Variable not found: ${variableId}`);
}
// Remove all overrides for this variable
extendedCollection.removeOverridesForVariable(variable);
return {
success: true,
collectionId,
variableId,
};
}