If you're doing repetitive tasks such as creating logos for clients and yourself in Figma, you can automate the whole process with bots.
Follow along to learn how to create this bot that can create and export a new logo in less than 30 seconds:
Here are the steps we're going to take:
Let's create a logo for a client. They are asking for a logo for their website cashewcoffee.com
Here's what we'll use:
To be able to interact with websites with Selenium, we need to locate the full XPath of each element we want to interact with.
If you like to type your email in the text field where you're writing your email, you need to locate the XPath for that particular text field.
The first step is installing and then importing all the Selenium packages. You'll find installation guides here https://pypi.org/project/selenium/
Once you have Selenium and a Chrome driver, go ahead and import the packages:
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support import expected_conditions as EC
Before we start the browser and log in to our Figma account, let's create 2 functions for common operations that we'll use throughout the automation; writing in text areas and clicking on buttons.
This function will locate an element according to its full XPath, click in the text area, and type each word in a string with a 400-millisecond delay.
def type_in_text_area(element_xpath, text_string, delay=2):
"""
Type words into a text area
"""
# Find text area with its XPath
text_area = WebDriverWait(driver, delay).until(EC.presence_of_element_located((By.XPATH, element_xpath)))
# Start an action chain
action = ActionChains(driver)
action.move_to_element(text_area).click().perform()
sleep(0.4)
for letter in text_string:
text_area.send_keys(letter)
sleep(0.009)
This function will locate an element according to its full XPath and hold-click on it.
def click_hold_button(element_xpath, delay=2):
"""
Click and hold a button
"""
button = WebDriverWait(driver, delay).until(EC.presence_of_element_located((By.XPATH, element_xpath)))
webdriver.ActionChains(driver).click_and_hold(button).perform()
sleep(0.5)
webdriver.ActionChains(driver).release().perform()
Start the web driver; this will open a new Chrome window:
driver = webdriver.Chrome()
Define the pages you like to route to when the web driver is up and visit the page:
page = "https://www.figma.com/files/drafts"
driver.get(page)
driver.get(page)
will take you to the login page of Figma. To log in and start the project, create two variables with your login credentials:
email = "YOUR_FIGMA_EMAIL"
passw = "YOUR_FIGMA_PASSWORD"
You can now type your email into the email text field by locating the XPath for the text field. Locate the XPath by inspecting the text field. Start by right-clicking on the element and choosing inspect in the menu.
If the inspector happens to open up to the right (or left) - make sure to dock it to the bottom:
Here's how the inspector should be docked to the bottom, and you'll also see a banner saying that Chrome is being controlled by automated test software:
Inspect the text area by first clicking on (1) and then click on (2):
Once you have clicked on the element you'd like to inspect, you'll see the element highlighted in the inspector:
To get the full XPath of the inspected element, right-click in the highlighted area, hover Copy, and finally click on Copy full XPath
Now that you have the full XPath copied, return to the code and create a variable with the path:
email_path = "/html/body/div[1]/div/div[1]/form/input[1]"
Do the same for the XPath of the password text field:
Add the copied XPath of the password text field to the code:
passw_path = "/html/body/div[1]/div/div[1]/form/input[2]"
The last element needed to be able to log in is the XPath of the log-in button. Follow the same procedure and copy the XPath of the login button. The final code snippet with the 3 XPaths should look something like this:
email_path = "/html/body/div[1]/div/div[1]/form/input[1]"
passw_path = "/html/body/div[1]/div/div[1]/form/input[2]"
login_button = "/html/body/div[1]/div/div[1]/form/button[2]"
Next, write your email address and password to the text fields and click on the login button. For this, we'll use the two functions we created earlier.
Run this snippet, and you should see the text fields get populated with your email and password, and then the log-in button gets clicked.
email = "YOUR_FIGMA_EMAIL"
passw = "YOUR_FIGMA_PASSWORD"
email_path = "/html/body/div[1]/div/div[1]/form/input[1]"
passw_path = "/html/body/div[1]/div/div[1]/form/input[2]"
login_button = "/html/body/div[1]/div/div[1]/form/button[2]"
type_in_text_area(email_path, email)
type_in_text_area(passw_path, passw)
sleep(1)
click_hold_button(login_button)
Run the code and you should now see your Figma dashboard:
Let's head over to the next part, creating a new design file and a logo.
The first step of creating a logo now that we're logged in is to create a new design file. The full XPath to create a new file is /html/body/div[2]/div/div/div[1]/div[3]/div[5]/div[3]/a[1]
combine that with clicking on the button like this:
# Create new design file
new_design_file_xpath = "/html/body/div[2]/div/div/div[1]/div[3]/div[5]/div[3]/a[1]"
click_hold_button(new_design_file_xpath)
sleep(1)
Now that we have a new design file, let's use the Figma shortcut T
to switch to the text tool and create a new text.
# Pick the text tool
text_tool_xpath = "/html/body/div[2]/div/div/div[1]/div/div[2]/div/div[2]/div[5]"
click_hold_button(text_tool_xpath)
sleep(1)
When the text tool is chosen, go ahead and click on the canvas and start typing the logo text:
# Click on canvas
canvas_xpath = "/html/body/div[2]/div/div/div[1]/div/div[1]/div/div/div/div[1]/div/div/canvas"
click_hold_button(canvas_xpath)
sleep(1)
# Write logo text
logo_text = "LOGO_TEXT"
ActionChains(driver).send_keys(logo_text).perform()
sleep(1)
Time to change the font size and pick the font you want to use for the logo. I'll go with 100 pt and the font Mama and Papa.
Highlight the logo text first. This snippet runs the command ⌘ + a
- if you're in Windows, switch Keys.COMMAND
to Keys.CONTROL
instead.
# Highlight logo text
# Use Keys.CONTROL if on Windows, Keys.COMMAND is for MacOS
ActionChains(driver).key_down(Keys.COMMAND).send_keys('a').key_up(Keys.COMMAND).perform()
Click on the font size and delete the current size:
# Click on font size
font_size_xpath = "/html/body/div[2]/div/div/div[1]/div/div[10]/div/div/div[2]/div/div/div/div[1]/span/span[2]/div[5]/div/div[1]/div[3]/div[2]/div[2]/div/label/input"
font_size_area = driver.find_element(By.XPATH, font_size_xpath)
sleep(1)
# Delete font size
font_size_area.send_keys(Keys.DELETE)
Choose a new font size and finish by pressing the return key:
# Choose a new font size
size = "100"
type_in_text_area(font_size_element, size)
sleep(1)
# Confirm new font size
ActionChains(driver).send_keys(Keys.ENTER).perform()
Font size is updated, now change to the font you want to use for the logo:
# Click on font
font_xpath = "/html/body/div[2]/div/div/div[1]/div/div[10]/div/div/div[2]/div/div[1]/div/div[1]/span/span[2]/div[5]/div/div[1]/div[3]/div[1]/div/input"
click_hold_button(font_xpath)
font_textfield = driver.find_element(By.XPATH, font_name_input)
sleep(1)
# Delete the current font
font_textfield.send_keys(Keys.BACK_SPACE)
sleep(1)
font_name = "YOUR_FONT"
# Type new font name
font_name_area.send_keys(font_name)
font_name_area.send_keys(Keys.ENTER)
sleep(1)
So far, we have written the logo text and changed the size and font. The last step is to vectorize the text and duplicate the layer into 3 colors.
Start with hitting the escape key to deselect the logo layer:
ActionChains(driver).key_down(Keys.ESCAPE).key_up(Keys.ESCAPE).perform()
sleep(1)
Then vectorize the text layer and duplicate the flattened layer:
# Vectroize text layer by flatten the text
ActionChains(driver).key_down(Keys.COMMAND).send_keys("e").perform()
sleep(1)
# Duplicate once
ActionChains(driver).key_down(Keys.COMMAND).send_keys("d").perform()
sleep(1)
# Duplicate again
ActionChains(driver).key_down(Keys.COMMAND).send_keys("d").perform()
sleep(1)
Used shortcuts:
⌘ + e
= flatten text layer to vectorize
⌘ + d
= duplicate layer
⌘ + r
= rename layer
⌘ + a
= select all layers
If you're on Windows, switch all Keys.COMMAND
to Keys.CONTROL
We duplicated the layer to create 3 different versions of the logo, one in white, black, and color of your choice. Let's rename the layers to keep track of each one:
# First layer is already selected
# Change name to white
# Use Keys.CONTROL if on Windows, Keys.COMMAND is for MacOS
ActionChains(driver).key_down(Keys.COMMAND).send_keys("r").key_up(Keys.COMMAND).send_keys(Keys.ARROW_RIGHT).send_keys(' white').send_keys(Keys.ENTER).perform()
sleep(1)
# Change vector color to white
color_picker_xpath = "/html/body/div[2]/div/div/div[1]/div/div[10]/div/div/div[2]/div/div/div/div[1]/span/span[2]/div[8]/div/div/div[3]/div/div/div/div/div/div/div[1]/div/div/div"
click_hold_button(color_picker_xpath)
ActionChains(driver).send_keys("ffffff").perform()
click_hold_button(color_picker_xpath)
# Click on second layer
vector_layer_2 = "/html/body/div[2]/div/div/div[1]/div/div[9]/div[1]/div/div[2]/div/div/div/div/div[1]/div[2]/span[3]"
click_hold_button(vector_layer_2)
sleep(1)
# Change name to black
ActionChains(driver).key_down(Keys.COMMAND).send_keys("r").key_up(Keys.COMMAND).send_keys(Keys.ARROW_RIGHT).send_keys(' black').send_keys(Keys.ENTER).perform()
# Change vector color to black
click_hold_button(color_picker_xpath)
ActionChains(driver).send_keys("000000").perform()
click_hold_button(color_picker_xpath)
# Click last layer
vector_layer_3 = "/html/body/div[2]/div/div/div[1]/div/div[9]/div[1]/div/div[2]/div/div/div/div/div[1]/div[1]/span[3]"
click_hold_button(vector_layer_3)
sleep(1)
# Recolor last layer
logo_color = "YOUR_LOGO_COLOR"
click_hold_button(color_picker_xpath)
ActionChains(driver).send_keys(logo_color).perform()
click_hold_button(color_picker_xpath)
sleep(1)
The logo and its different color variants are all set. The last step is exporting the logo in 3 different file formats: PNG, JPG, and SVG.
Start with selecting all the layers to export all of them at once:
# Select all layers
# Use Keys.CONTROL if on Windows, Keys.COMMAND is for MacOS
ActionChains(driver).key_down(Keys.COMMAND).send_keys('a').key_up(Keys.COMMAND).perform()
sleep(1)
Next up is clicking on the export panel to start exporting:
export_panel_xpath = "/html/body/div[2]/div/div/div[1]/div/div[10]/div/div/div[2]/div/div/div/div[1]/span/span[2]/div[13]/div"
click_hold_button(export_panel_xpath)
sleep(1)
The first format is now png. Let's create another file format by clicking on the plus sign:
plus_button_xpath = "/html/body/div[2]/div/div/div[1]/div/div[10]/div/div/div[2]/div/div/div/div[1]/span/span[2]/div[13]/div/div[1]/div[1]/div/div[2]/span[2]"
click_hold_button(plus_button_xpath)
sleep(1)
This will automatically add a version 2x the size and with a pre-populated suffix. I want all the files in the same magnification, so let's change back to 1x size:
size_xpath = "/html/body/div[2]/div/div/div[1]/div/div[10]/div/div/div[2]/div/div/div/div[1]/span/span[2]/div[13]/div/div[1]/div[2]/div[1]/div/div/div[2]/input"
click_hold_button(size_xpath)
ActionChains(driver).send_keys("1x").send_keys(Keys.ENTER).perform()
sleep(1)
Go ahead and delete the suffix as well:
# Click on the suffix
suffix_xpath = "/html/body/div[2]/div/div/div[1]/div/div[10]/div/div/div[2]/div/div/div/div[1]/span/span[2]/div[13]/div/div[1]/div[2]/div[1]/div/div/input"
click_hold_button(suffix_xpath)
# Delete suffix
ActionChains(driver).send_keys(Keys.BACKSPACE).send_keys(Keys.BACKSPACE).send_keys(Keys.BACKSPACE).perform()
sleep(1)
Choose JPG for the file type:
# Choose JPG as file type
dropdown_file_type_xpath = "/html/body/div[2]/div/div/div[1]/div/div[10]/div/div/div[2]/div/div/div/div[1]/span/span[2]/div[13]/div/div[1]/div[2]/div[1]/div/div/div[3]/div[2]/span[1]"
dropdown_button = WebDriverWait(driver, 4).until(EC.presence_of_element_located((By.XPATH, dropdown_file_type_xpath)))
dropdown_button.click()
sleep(1)
# Use arrows to navigate in the dropdown
webdriver.ActionChains(driver).send_keys(Keys.ARROW_DOWN).send_keys(Keys.ENTER).perform()
sleep(1)
Lastly, add a new export file and choose SVG as the file type:
# Add new file type
click_hold_button(plus_button_xpath)
sleep(1)
# Change suffix for SVG
click_hold_button(suffix_xpath)
sleep(1)
# Delete the suffix
ActionChains(driver).send_keys(Keys.BACK_SPACE).send_keys(Keys.BACK_SPACE).send_keys(Keys.BACK_SPACE).send_keys(Keys.ENTER).perform()
sleep(1)
# Use arrow keys to navigate in dropdown list
dropdown_button = WebDriverWait(driver, 4).until(EC.presence_of_element_located((By.XPATH, dropdown_file_type_xpath)))
dropdown_button.click()
webdriver.ActionChains(driver).send_keys(Keys.ARROW_DOWN).send_keys(Keys.ARROW_DOWN).send_keys(Keys.ENTER).perform()
Finally, click on export:
export_button_xpath = "/html/body/div[2]/div/div/div[1]/div/div[10]/div/div/div[2]/div/div/div/div[1]/span/span[2]/div[13]/div/div[2]/div/button"
click_hold_button(export_button_xpath)
Here's what the complete walkthrough looks like when you run all the code:
And here's the final logo I created for the client:
Here is the repo with a Jupyter notebook with all the source code if you'd like to implement this on your own ⬇️
https://github.com/norahsakal/automate-logo-creation-in-figma
Shoot me a DM on Twitter, I'd love to help out @norahsakal
If you liked the logo, you might enjoy my weekly newsletter; Daily Domain Ideas
Every Monday you'll get 3 business ideas with expired .com domains
The domain cashewcoffee.com
is actually expired and was part of the newsletter issue #016
Idea: A site for finding recipes to make a rich, foamy, creamy cashew coffee ⬇️
Originally published at https://norahsakal.com/blog/automate-logo-creation-in-figma
This comment was deleted 2 years ago.
Thanks for your feedback, I really appreciate it!
I'm using Selenium for all my end-to-end testing, and unless you change the elements you're testing, it's pretty stable.
Totally agree with you regarding
sleep()
, especially with production code.Thanks again for your thoughts!