Downloading Vercel Geist Icons

1 April 2024

I wanted to be able to download all the Vercel Geist icons however I didn't see an option to do this on the page (may exist now of course). I decided to write my own script in the browser to do it for me.

Vercel Geist Icons

If you're using Chrome and want follow along with this you'll need to enable pasting within the console otherwise you'll be hit with a warning.

Browser console
allow pasting

Getting a single icon

To start I identified the svg element of the icon them gathered them all with a node list.

Browser console
const allIcons = document.querySelectorAll("div[class='-mt-1.5'] > div > svg");

I thought I would experiment and start with one. I want to target the outerHtml property of the first element in the node list. This will give me the .svg as a string.

Browser console
const newIcon = allIcons[0].outerHTML;

Then we need to create a function that turns our Icon into a file. This will allow us to use it as an object we can download from the browser.

Furthermore we'll need to programmatically enable the download of this file by creating an a element and setting some of it's attributes.

Browser console
function downloadBlob(dataFile, filename) {

  //Create the blob of the SVG file
  const file = new File([dataFile], "icon.svg", {
	type: "image/svg+xml",
  });

  // Create an object URL for the blob object
  const url = URL.createObjectURL(file);

  // Create a new anchor element
  const a = document.createElement('a');

  // Set the href and download attributes for the anchor element
  a.href = url;
  a.download = filename || 'download';

  // Click handler that releases the object URL after the element has been clicked
  // This is required for one-off downloads of the blob content
  const clickHandler = () => {
    setTimeout(() => {
      URL.revokeObjectURL(url);
      removeEventListener('click', clickHandler);
    }, 150);
  };

  // Return the anchor element
  return a;
}

We return the new a from the function using the passed dataFile and fileName arguments.

Browser console
const newLink = downloadBlob(file, "test.svg");

As we how have an element that is usable within the DOM we can simulate a click on our returned a element.

Browser console
newLink.click()

Getting all icons at once

Re-using our function from before I want to target the outer divs of the svgs so that we can determine the right name for the downloaded files.

Browser console
const allIconGroups = document.querySelectorAll("div[class='hover:bg-background-100 flex h-28 w-full cursor-pointer flex-col items-center rounded px-4 text-gray-900']")

I then need to turn the node list into an array to be able to iterate over it.

Browser console
const iconGroupArr = Array.from(allIconGroups)

This works. Chrome (and other browsers) have a max sequential download limit of 10 within a second, I create a small 1 second wait after each 10 are downloaded so as not to be blocked by the browser.

Browser console

function download(el) {
  const name = el.querySelector('p').innerHTML;
  const svgText = el.querySelector("div[class='-mt-1.5'] > div > svg").outerHTML;
	
  const file = new File([svgText], name+".svg", {
    type: "image/svg+xml",
  });

  // Create an object URL for the blob object
  const url = URL.createObjectURL(file);

  // Create a new anchor element
  const a = document.createElement('a');

  // Set the href and download attributes for the anchor element
  a.href = url;
  a.download = name || 'download';

  // Click handler that releases the object URL after the element has been clicked
  // This is required for one-off downloads of the blob content
  const clickHandler = () => {
    setTimeout(() => {
      URL.revokeObjectURL(url);
      removeEventListener('click', clickHandler);
    }, 150);
  };

  // Add the click event listener on the anchor element
  a.addEventListener('click', clickHandler, false);
  a.click();
}

function pause(msec) {
    return new Promise(
        (resolve, reject) => {
            setTimeout(resolve, msec || 1000);
        }
    );
}

async function downloadAll(elements) {
    var count = 0;
    for (var e in elements) {
        download(elements[e]);
        if (++count >= 10) {
            await pause(1000);
            count = 0;
        }
    }
}

I then call my function with the array of icons to download them all. It may take ~30 seconds.

Browser console
downloadAll(iconGroupArr);

I hope this was useful!


Useful links