{"id":67814,"date":"2019-07-06T04:19:27","date_gmt":"2019-07-06T11:19:27","guid":{"rendered":"http:\/\/www.taterboy.com\/blog\/2019\/07\/how-to-build-a-sketch-plugin-with-javascript-html-and-css-part-1\/"},"modified":"2019-07-06T04:19:27","modified_gmt":"2019-07-06T11:19:27","slug":"how-to-build-a-sketch-plugin-with-javascript-html-and-css-part-1","status":"publish","type":"post","link":"https:\/\/www.taterboy.com\/blog\/2019\/07\/how-to-build-a-sketch-plugin-with-javascript-html-and-css-part-1\/","title":{"rendered":"How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 1)"},"content":{"rendered":"<link rel=\"canonical\" href=\"https:\/\/www.smashingmagazine.com\/2019\/07\/build-sketch-plugin-javascript-html-css-part-1\/\"><title>How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 1)<\/title><\/p>\n<article>\n<header>\n<h1>How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 1)<\/h1>\n<address>Matt Curtis<\/address>\n<p>                  <time datetime=\"2019-07-05T12:30:59+02:00\">2019-07-05T12:30:59+02:00<\/time><time datetime=\"2019-07-06T11:06:39+00:00\">2019-07-06T11:06:39+00:00<\/time><\/header>\n<p>This tutorial is intended for people who know and use the Sketch app and are not afraid of dabbling with code. To profit from it the most, you will need to have at least some basic experience writing JavaScript (and, optionally, HTML\/CSS).<\/p>\n<p>The plugin we\u2019ll be creating is called \u201cMosaic\u201d. In part one, we\u2019ll learn about the basic files that make up a Sketch plugin; we\u2019ll write some JavaScript and create a user interface for our plugin with the help of some HTML and CSS. The next article will be about how to connect the user interface to the core plugin code, how to implement the plugin\u2019s main features, and at the end of it, you will also learn how to optimize the code and the way the plugin works.<\/p>\n<p>I\u2019ll also be sharing the plugin\u2019s code (JS, HTML, CSS) and files which you\u2019ll be able to examine and use for learning purposes.<\/p>\n<h3>What Are Sketch Plugins, And How Do They Work?<\/h3>\n<p>In Sketch, plugins are a way to add features and functionality that aren\u2019t present in Sketch \u201cout of the box.\u201d Considering that there\u2019s almost always going to be some missing feature or integration in any given program (especially given the vast number of needs any individual designer might have!), one can begin to imagine how plugins might be especially useful and powerful. Sketch plugins are able to do pretty much everything you\u2019d expect, like manipulating the color, shape, size, order, style, grouping, and effects of layers, but also able to do things like make requests to internet resources, present a user interface, and much, much more!<\/p>\n<p>On the programming side, all Sketch plugins are written in JavaScript code. Well, actually, that\u2019s not <em>entirely<\/em> true. It\u2019s more accurate to say that <em>most<\/em> Sketch plugins are written in JavaScript, as it\u2019s also possible to write a Sketch plugin in one of Apple\u2019s programming languages, <a href=\"https:\/\/en.wikipedia.org\/wiki\/Objective-C\">Objective-C<\/a> and <a href=\"https:\/\/developer.apple.com\/swift\/\">Swift<\/a>, though even they require a small amount of JavaScript knowledge.<\/p>\n<p>Don\u2019t worry though. In this article, we\u2019ll focus on how to build Sketch plugins using JavaScript, HTML, and CSS <em>alone<\/em>. We won\u2019t be going over the basics of HTML, CSS, or JavaScript \u2014 this article assumes at least some knowledge and experience with all of these three. The MDN developer website provides a great place to <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Learn\">learn more about web development<\/a>.<\/p>\n<div data-component=\"FeaturePanel\" data-audience=\"non-subscriber\" data-remove=\"true\"><\/div>\n<h3>Let\u2019s Get Started!<\/h3>\n<h4>Firstly, What Are We Making?<\/h4>\n<p>In this tutorial, I\u2019ll teach you how to build a basic, beginner-friendly plugin that will be able to create, duplicate, and modify layers, as well as present the user with a nice user interface. By doing so, my goal is to establish a fundamental knowledge on which you can build on and use it to create your own plugins.<\/p>\n<p>The plugin we\u2019ll be building is called <em>Mosaic,<\/em> and is effectively a \u201cpattern generator\u201d. Feed it your layers, tweak a few settings, and it\u2019ll create a pattern:<\/p>\n<figure><a href=\"https:\/\/i0.wp.com\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/5cd14d15-fa84-48ee-b2d4-24f3aee88efc\/ui-pattern-examples.png?ssl=1\"><\/p>\n<p>    <img data-recalc-dims=\"1\" decoding=\"async\" srcset=\"http:\/\/www.taterboy.com\/blog\/wp-content\/uploads\/2019\/07\/ui-pattern-examples.png 400w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_800\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/5cd14d15-fa84-48ee-b2d4-24f3aee88efc\/ui-pattern-examples.png 800w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1200\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/5cd14d15-fa84-48ee-b2d4-24f3aee88efc\/ui-pattern-examples.png 1200w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1600\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/5cd14d15-fa84-48ee-b2d4-24f3aee88efc\/ui-pattern-examples.png 1600w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_2000\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/5cd14d15-fa84-48ee-b2d4-24f3aee88efc\/ui-pattern-examples.png 2000w\" src=\"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2019\/07\/ui-pattern-examples.png?w=900\" sizes=\"100vw\" alt=\"Image showing the Mosaic plugin\u2019s UI, and a few example patterns.\"><\/a><figcaption>\n      The Mosaic\u2019s UI, and some examples of patterns made with it. (<a href=\"https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/5cd14d15-fa84-48ee-b2d4-24f3aee88efc\/ui-pattern-examples.png\">Large preview<\/a>)<br \/>\n    <\/figcaption><\/figure>\n<p>If you\u2019d like to install and play around with Mosaic, you can <a href=\"https:\/\/github.com\/matt-curtis\/mosaic\">download the completed plugin<\/a> from GitHub.<\/p>\n<p>A bit of history: Mosaic is inspired in large part by an old-school <a href=\"https:\/\/www.smashingmagazine.com\/2013\/12\/present-future-adobe-fireworks\/\">Adobe Fireworks<\/a> plugin called <em>Twist-and-Fade<\/em>. Twist-and-Fade was pretty powerful, able to duplicate a layer any number of times while adjusting its hue, position, rotation, size, and opacity. The plugin was even able to generate animated GIFs, like <a href=\"https:\/\/dribbble.com\/shots\/147212-Tape-Cassette-Rotating\">this one<\/a>, where it created the frames for the two rotating elements in the cassette tape:<\/p>\n<figure><a href=\"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2019\/07\/spinning-cassette.gif\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2019\/07\/spinning-cassette.gif?resize=400%2C300\" width=\"400\" height=\"300\" alt=\"Image showing a music cassette tape with rotating drums\"><\/a><figcaption>Animated cassette tape (<a href=\"https:\/\/dribbble.com\/shots\/147212-Tape-Cassette-Rotating\">source<\/a>). (<a href=\"http:\/\/www.taterboy.com\/blog\/wp-content\/uploads\/2019\/07\/spinning-cassette.gif\">Large preview<\/a>)<\/figcaption><\/figure>\n<p>(Here\u2019s a <a href=\"https:\/\/www.youtube.com\/watch?v=VPQIZ_Pz-II&#038;feature=youtu.be&#038;t=161\">video demoing Twist and Fade<\/a> if you\u2019re interested in seeing exactly how it worked.)<\/p>\n<p>For the purposes of this tutorial, we\u2019ll be building a somewhat similar plugin for Sketch, though intentionally simplified so as to keep the tutorial as accessible as possible. Specifically, our plugin will be able to:<\/p>\n<ul>\n<li>Duplicate any Sketch layer (bitmap or vector) and tweak the duplicates\u2019 layer\u2019s position, rotation, and opacity. This will give us an introduction to manipulating layers using <a href=\"https:\/\/developer.sketch.com\/reference\/api\/\">Sketch\u2019s JavaScript APIs<\/a>.<\/li>\n<li>Display a user interface created using HTML, CSS, and JS, which will teach you about how to easily create an interface for the plugin, by using web technologies that you may already be familiar with. The plugin interface is pretty important since it\u2019s how we\u2019ll gather the user\u2019s inputs regarding how the user wants the resulting mosaic image to look.<\/li>\n<\/ul>\n<div><\/div>\n<h4>Creating Our Base Plugin In Ten Seconds Flat<\/h4>\n<p>First, we\u2019ll be creating the \u201cbase\u201d (or template) for the plugin we want to build. We could create all the necessary files and folders that make up a plugin manually, but luckily we don\u2019t have to \u2014 because Sketch can do it for us. After we\u2019ve generated the template plugin, we\u2019ll be able to customize it as we see fit.<\/p>\n<p>There\u2019s a really quick and easy technique we can use to create the template plugin, which is pretty much my go-to method when I need to whip a plugin together to solve whatever problem I\u2019m dealing with at a given moment. Here\u2019s how it works:<\/p>\n<p>With Sketch open, check the menu bar at the top of the screen and click <code>Plugins -> Run Script<\/code>. This will open up a dialog box that we can use to test and run the code. We can also save any code we enter in it as a plugin, which is the part we\u2019re specifically interested in right now.<\/p>\n<p>Clear whatever code is already in this dialog and replace it with the following demo code:<\/p>\n<div>\n<pre><code>const UI = require(\"sketch\/ui\");\n\nUI.message(\"? Hey there, you fantastic plugin developer you! This is your plugin! Talking to you from the digital computer screen! In Sketch! Simply stupendous!\");\n<\/code><\/pre>\n<\/div>\n<p>Next, hit <code>Save Script as Plugin<\/code> in the bottom-left of the window, enter whatever name you\u2019d like for this plugin to have (in our case, this is \u201cMosaic\u201d), then <code>Save Script as Plugin<\/code> once more.<\/p>\n<figure><a href=\"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2019\/07\/sketch-plugin-save-name.gif\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2019\/07\/sketch-plugin-save-name.gif?resize=668%2C518\" width=\"668\" height=\"518\" alt=\"\"><\/a><figcaption>Press \u201cSave\u201d in the bottom-left of the window and enter whatever name you&#8217;d like for this plugin to have. (<a href=\"http:\/\/www.taterboy.com\/blog\/wp-content\/uploads\/2019\/07\/sketch-plugin-save-name.gif\">Large preview<\/a>)<\/figcaption><\/figure>\n<p>Believe it or not, we\u2019re already done \u2014 all that\u2019s left is to eat the cake we just baked. Here comes the fun part. Opening the Plugins menu once again, you should see something like this: your brand-spanking-new plugin listed as \u201cMosaic\u201d! Click on it!<\/p>\n<figure><a href=\"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2019\/07\/hello-world-tooltip.gif\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2019\/07\/hello-world-tooltip.gif?resize=900%2C84\" width=\"900\" height=\"84\" alt=\"\"><\/a><figcaption>(<a href=\"http:\/\/www.taterboy.com\/blog\/wp-content\/uploads\/2019\/07\/hello-world-tooltip.gif\">Large preview<\/a>)<\/figcaption><\/figure>\n<p>Congratulations, you\u2019ve just written your first Sketch plugin!<\/p>\n<p>What you should see after clicking \u201cMosaic\u201d should be like the short video above, with an unobtrusive tooltip message appearing at the bottom of the screen beginning with the words \u201cHey there\u2026\u201d \u2014 which is exactly what the code we pasted in tells it to do. This is what it makes this technique so great: it makes it <strong>easy to paste, modify and test code without having to build a plugin from scratch.<\/strong> If you\u2019re familiar with or have ever played with your browser\u2019s web console, this is basically that. Having this tool in your back pocket as you build and test code is a must-have.<\/p>\n<p>Let\u2019s do a quick rundown of what the code you added does:<\/p>\n<p>First, it imports the <a href=\"https:\/\/developer.sketchapp.com\/reference\/api\/#ui\"><code>sketch\/ui<\/code><\/a> <a href=\"https:\/\/developer.sketchapp.com\/reference\/api\/#ui\">module<\/a> of <a href=\"https:\/\/github.com\/BohemianCoding\/SketchAPI\">Sketch\u2019s built-in JS library<\/a>, and assigns it to the <code>UI<\/code> variable. This module contains a couple of useful interface-related methods, one of which we\u2019ll use:<\/p>\n<pre><code>const UI = require(\"sketch\/ui\");\n<\/code><\/pre>\n<p>Next, it calls the <code>message<\/code> method (which is part of the <code>sketch\/ui<\/code>  module) with the string of text we want displayed in the tooltip we saw:<\/p>\n<div>\n<pre><code>UI.message(\"? Hey there, you fantastic plugin developer you! This is your plugin! Talking to you from the digital computer screen! In Sketch! Simply stupendous!\");\n<\/code><\/pre>\n<\/div>\n<p>The <code>message()<\/code> method provides a great way to present an unobtrusive message to the user; it\u2019s great for cases where you don\u2019t need to steal focus (non-modal) and don\u2019t need any fancy buttons or text fields. There\u2019s also other ways to <a href=\"https:\/\/developer.sketchapp.com\/reference\/api\/#ui\">present common UI elements<\/a> like alerts, prompts, and such, some of which we\u2019ll be using as we build Mosaic.<\/p>\n<h4>Customizing Our Plugin\u2019s Metadata<\/h4>\n<p>We now have a basic plugin to start from, but we still need to tweak it further and make it truly ours. Our next step will be to change the plugin\u2019s metadata.<\/p>\n<p>For this step, we\u2019ll need to peek into what\u2019s called the <em>plugin bundle<\/em>. When you hit save in the \u2018Run Script\u2019 window, Sketch saved your plugin as a folder named <code>Mosaic.sketchplugin<\/code> that you can find in the<br \/>\n<code>~\/Library\/Application Support\/com.bohemiancoding.sketch3\/Plugins<\/code> directory. That\u2019s a bit long and annoying to remember; as a shortcut, you can also pull it up via <code>Plugins -> Manage Plugins -> (right-click your plugin) -> Reveal Plugins Folder<\/code>. Even though it appears in Finder as a single file, it\u2019s actually a folder containing everything our plugin needs for Sketch to run it. The reason it appears as a single file <em>despite<\/em> being a folder is because when you first installed Sketch, Sketch registered the <code>.sketchplugin<\/code> extension as a \u201cbundle\u201d (a special kind of folder that appears as a file) and asked for it to automatically open in Sketch when opened.<\/p>\n<p>Let\u2019s take a peek inside. Right-click <code>Mosaic.sketchplugin<\/code>, then click \u201cShow Package Contents\u201d. Inside, you should see the following directory structure:<\/p>\n<pre><code>Contents\/\n? Resources\/\n? Sketch\/\n  ? manifest.json\n  ? script.cocoascript\n<\/code><\/pre>\n<p>You might be wondering why there\u2019s a file in there with the extension <code>.cocoascript<\/code>. Don\u2019t worry \u2014 it\u2019s just a regular JavaScript file, and only contains the code we entered earlier. Go ahead and rename this file to <code>index.js<\/code>, which will change the directory structure to look like the one below:<\/p>\n<pre><code>Contents\/\n? Resources\/\n? Sketch\/\n  ? manifest.json\n  ? index.js\n<\/code><\/pre>\n<p>The most common way of organizing the files inside a plugin bundle is as follows: your code (JavaScript) and <code>manifest.json<\/code> belong in <code>Sketch\/<\/code>, and resources (think images, audio files, text files, etc.) belong in <code>Resources\/<\/code>.<\/p>\n<p>Let\u2019s start by tweaking the file named <code>manifest.json<\/code>. Open it inside your favorite code editor, such as <a href=\"https:\/\/code.visualstudio.com\/\">Visual Studio Code<\/a> or <a href=\"https:\/\/atom.io\/\">Atom<\/a>.<\/p>\n<p>You\u2019ll see that at the moment there\u2019s relatively little inside here, but we\u2019ll add more soon. The plugin manifest serves primarily two purposes:<\/p>\n<ol>\n<li>First, it provides metadata that describes the plugin to the user \u2014 things like its name, version, the author\u2019s name, and so on. Sketch uses this information in the <code>Sketch -> Preferences -> Plugins<\/code> dialog to create a listing and description for your plugin.<\/li>\n<li>Second, it also tells Sketch about how to get down to your business; that is, it tells Sketch how you\u2019d like your plugin\u2019s menu to look, what hotkeys to assign to your plugin, and where your plugin\u2019s code lives (so Sketch can run it).<\/li>\n<\/ol>\n<p>Considering purpose #1, describing the plugin to the user, you\u2019ll probably notice that right now there\u2019s no description <em>or<\/em> author given, which would be confusing for the user and make the plugin difficult to identify. Let\u2019s fix that by adjusting the relevant keys\u2019 values to:<\/p>\n<div>\n<pre><code>{\n        \"description\": \"Generate awesome designs and repeating patterns from your layers!\",\n        \"author\": \"=> Your name here <\/code><\/pre>\n<\/div>\n<p>Next, let\u2019s adjust the plugin\u2019s identifier. This identifier uses what is called a \u201creverse domain notation\u201d which is a really concise (or boring, take your pick) way to say \u201ctake your site\u2019s domain, reverse the order, then put your product\u2019s name at the end.\u201d This will come out something like: <code>com.your-company-or-your-name-its-not-that-big-a-deal.yourproduct<\/code>.<\/p>\n<p>You don\u2019t have to stick to this naming convention \u2014 you can put whatever you want here, so long as it\u2019s unique enough to avoid conflicts with other plugins (though it\u2019s <em>probably<\/em> a good idea to stick to the RDN format, especially as it provides a simple, reusable system for your plugin identifiers).<\/p>\n<p>To that effect, change your identifier to <code>com.your-name.mosaic<\/code>:<\/p>\n<pre><code>{\n    \"identifier\": \"com.your-name.mosaic\"\n}\n<\/code><\/pre>\n<p>I personally like to take all metadata related keys (title, author, identifier, etc.) and group them near the top of the manifest so they\u2019re not spread out all over the place and help preserve my sanity when I need to find them.<\/p>\n<p>Next, let\u2019s take a look at the <code>menu<\/code> and <code>commands<\/code> keys. These two are responsible for telling Sketch what code to call and in response to what.<\/p>\n<p>If you look at the <code>menu<\/code> key, you\u2019ll see it contains a <code>title<\/code> key, whose value is the name our plugin will show up with in the <code>Plugins<\/code> menu. It also has an <code>items<\/code> key, which is a list of <em>command identifiers<\/em>:<\/p>\n<pre><code>{\n  \"menu\": {\n    \"title\": \"Mosaic\",\n    \"items\": [\n        \"com.bohemiancoding.sketch.runscriptidentifier\"\n    ]\n  }\n}\n<\/code><\/pre>\n<p>Right now there\u2019s only one command identifier in this list, <code>\"com.bohemiancoding.sketch.runscriptidentifier\"<\/code>. Command identifiers always point to a command in the <code>commands<\/code> list. Right now our plugin only has one command, which is the one with this identifier:<\/p>\n<pre><code>{\n  \"commands\": [\n    {\n      \"script\" : \"script.cocoascript\",\n      \"name\" : \"Mosaic\",\n      \"handlers\" : {\n              \"run\" : \"onRun\"\n      },\n      \"identifier\" : \"com.bohemiancoding.sketch.runscriptidentifier\"\n    }\n  ]\n}\n<\/code><\/pre>\n<p>Whenever you add a command identifier to a <code>menu<\/code> entry, Sketch will look up the command entry that has that identifier and will display the value of its <code>name<\/code> key (which in this case is \u201cMosaic\u201d) and will show it in your plugin\u2019s menu instead of the identifier.<\/p>\n<p>As for the role commands play, we can think of a command entry as a way to tell Sketch what function in our plugin\u2019s JavaScript code we want to run when that command is invoked, the \u201cinvocation\u201d usually being the user\u2019s click on the associated menu item. The command entry doesn\u2019t do anything on its own, it\u2019s just JSON \u2014 it simply provides a <em>description<\/em> to Sketch of where to look for the JavaScript it needs to run when the command is invoked.<\/p>\n<p>So far, we\u2019ve talked about what a command\u2019s <code>name<\/code> and <code>identifier<\/code> keys do, but there are two other keys in a command that need to be addressed: <code>script<\/code> and <code>handlers<\/code>.<\/p>\n<p>The <code>script<\/code> key tells Sketch where the JavaScript file that it should run is. Note how Sketch assumes that the script file in question is in the <code>Sketch\/<\/code> folder, which is why for simplicity\u2019s sake you\u2019ll want to make sure all your JavaScript code lives somewhere under the <code>Sketch\/<\/code> folder. Before we move on from this key <strong>it\u2019s important<\/strong> that you make sure you change this key\u2019s value to <code>index.js<\/code>, just like we renamed the file earlier. Otherwise, Sketch won\u2019t be able to find and run your JavaScript file.<\/p>\n<p>The value of the <code>handlers<\/code> key is what Sketch looks at to determine what function in your JavaScript to call. Here, we only have one handler set: <code>run<\/code>, with the value <code>onRun<\/code>. <code>run<\/code> is the name of a predefined, built-in Sketch <strong>action<\/strong>. This <code>run<\/code>  action will always be called when a user clicks a menu item that references this command. <code>onRun<\/code> is the <strong>name<\/strong> of a function in the auto-generated <code>script.cocoascript<\/code> file (which we renamed to <code>index.js<\/code>), and the function we want to be called when the <code>run<\/code>  event occurs, i.e., when the user clicks the menu item.<\/p>\n<div><\/div>\n<p>In the example we have so far, this process plays out something like this:<\/p>\n<ol>\n<li>The user clicks our menu item.<\/li>\n<li>Sketch finds the command associated with that menu item.<\/li>\n<li>Sketch finds the script file the command refers to and runs it (which in this case means it executes the JavaScript in <code>index.js<\/code>).<\/li>\n<li>Since this command was invoked by a menu item click, it\u2019s considered a <code>run<\/code> action. That means Sketch will look at the command\u2019s <code>handlers.run<\/code> value for the function to call next, which in this case is <code>onRun<\/code>.<\/li>\n<li>Sketch calls the <code>onRun<\/code> function.<\/li>\n<\/ol>\n<p>Commands are most commonly called in response to a user clicking on one of your menu items, but they can also be called in response to other user actions, such as the user changing the selection or a property on a layer. However, for this plugin, we won\u2019t be using any of these other actions. (You can learn more about actions and how they work in the <a href=\"https:\/\/developer.sketch.com\/plugins\/actions\">Action API<\/a> help page.)<\/p>\n<p>Before we move on from this manifest, we\u2019ll want to make two other tweaks. Right now, our menu has the structure:<\/p>\n<pre><code>Mosaic\n? Mosaic\n<\/code><\/pre>\n<figure><a href=\"https:\/\/i0.wp.com\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/9271bfb2-04e9-455c-8d79-daa080625b34\/redundant-plugin-menu.png?ssl=1\"><\/p>\n<p>    <img data-recalc-dims=\"1\" decoding=\"async\" srcset=\"http:\/\/www.taterboy.com\/blog\/wp-content\/uploads\/2019\/07\/redundant-plugin-menu.png 400w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_800\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/9271bfb2-04e9-455c-8d79-daa080625b34\/redundant-plugin-menu.png 800w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1200\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/9271bfb2-04e9-455c-8d79-daa080625b34\/redundant-plugin-menu.png 1200w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1600\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/9271bfb2-04e9-455c-8d79-daa080625b34\/redundant-plugin-menu.png 1600w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_2000\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/9271bfb2-04e9-455c-8d79-daa080625b34\/redundant-plugin-menu.png 2000w\" src=\"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2019\/07\/redundant-plugin-menu.png?w=900\" sizes=\"100vw\" alt=\"Image showing Mosaic menu item redundantly nested inside another menu named Mosaic\"><\/a><figcaption>\n      Pretty redundant, right? (<a href=\"https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/9271bfb2-04e9-455c-8d79-daa080625b34\/redundant-plugin-menu.png\">Large preview<\/a>)<br \/>\n    <\/figcaption><\/figure>\n<p>\u2026which is a bit redundant since our plugin only has one menu item. It also adds a bit of unnecessary friction for our user as our plugin now takes two clicks to invoke rather than one. We can fix this by adding <code>isRoot: true<\/code> to our <code>menu<\/code>:<\/p>\n<pre><code>{\n  \"menu\": {\n    \"title\" : \"Mosaic\",\n    \"items\" : [\n            \"com.bohemiancoding.sketch.runscriptidentifier\"\n    ],\n    \"isRoot\": true\n}\n}\n<\/code><\/pre>\n<p>This tells Sketch to place the first level of menu items directly under the <code>Plugins<\/code> menu, rather than nesting them under the menu\u2019s <code>title<\/code>.<\/p>\n<p>Hit save and return to Sketch. You should see that now <code>Mosaic -> Mosaic<\/code> has been replaced by just <code>Mosaic<\/code> \u2014 perfect!<\/p>\n<figure><a href=\"https:\/\/i0.wp.com\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/303674a2-f96b-42cb-9aa4-05c5d51da78c\/non-redudant-plugin-menu.png?ssl=1\"><\/p>\n<p>    <img data-recalc-dims=\"1\" decoding=\"async\" srcset=\"http:\/\/www.taterboy.com\/blog\/wp-content\/uploads\/2019\/07\/non-redudant-plugin-menu.png 400w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_800\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/303674a2-f96b-42cb-9aa4-05c5d51da78c\/non-redudant-plugin-menu.png 800w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1200\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/303674a2-f96b-42cb-9aa4-05c5d51da78c\/non-redudant-plugin-menu.png 1200w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1600\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/303674a2-f96b-42cb-9aa4-05c5d51da78c\/non-redudant-plugin-menu.png 1600w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_2000\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/303674a2-f96b-42cb-9aa4-05c5d51da78c\/non-redudant-plugin-menu.png 2000w\" src=\"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2019\/07\/non-redudant-plugin-menu.png?w=900\" sizes=\"100vw\" alt=\"Image showing the Mosaic plugin\u2019s UI\"><\/a><figcaption>\n      Mosaic\u2019s UI. (<a href=\"https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/303674a2-f96b-42cb-9aa4-05c5d51da78c\/non-redudant-plugin-menu.png\">Large preview<\/a>)<br \/>\n    <\/figcaption><\/figure>\n<p>As for our second tweak, let\u2019s go ahead and rename this command identifier to something less unwieldy. Since command identifiers only need to be unique within the context of an individual plugin, we can safely rename it to something more concise and obvious, like <code>\"open\"<\/code>:<\/p>\n<pre><code>{\n  \"commands\": [\n    {\n            ...\n            \"identifier\" : \"open\"\n    }\n],\n\n\"menu\": {\n    ...\n    \"items\" : [\n            \"open\"\n    ]\n  }\n}\n<\/code><\/pre>\n<p>Before we move on, it\u2019s useful to note that menus can contain also other menus. You could easily create a sub-menu by nesting another <code>{ title: ..., items: ... }<\/code> entry inside another menu\u2019s <code>items<\/code> list:<\/p>\n<pre><code>{\n  \"menu\": {\n    \"title\" : \"Mosaic\",\n    \"items\" : [\n      \"open\",\n      {\n        \"title\" : \"I'm a sub-menu!\",\n        \"items\" : [\n                \"another-command-identifier\"\n        ]\n      }\n    ]\n  }\n}\n<\/code><\/pre>\n<h3>Building The Plugin\u2019s User Interface<\/h3>\n<p>So far, we\u2019ve written some demo code and customized our plugin\u2019s manifest. We\u2019ll now move on to creating its user interface, which is essentially a web page embedded in a window (similarly to the browsers you\u2019re familiar with using):<\/p>\n<figure><a href=\"https:\/\/i0.wp.com\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/7787048a-2129-4888-b681-1bd72735084e\/ui-window.png?ssl=1\"><\/p>\n<p>    <img data-recalc-dims=\"1\" decoding=\"async\" srcset=\"http:\/\/www.taterboy.com\/blog\/wp-content\/uploads\/2019\/07\/ui-window-800px.png 400w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_800\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/fd705576-21ed-4ede-8c89-2bb55b4abce6\/ui-window-800px.png 800w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1200\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/fd705576-21ed-4ede-8c89-2bb55b4abce6\/ui-window-800px.png 1200w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1600\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/fd705576-21ed-4ede-8c89-2bb55b4abce6\/ui-window-800px.png 1600w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_2000\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/fd705576-21ed-4ede-8c89-2bb55b4abce6\/ui-window-800px.png 2000w\" src=\"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2019\/07\/ui-window-800px.png?w=900\" sizes=\"40vw\" alt=\"\"><\/a><figcaption>\n      The plugin\u2019s window. (<a href=\"https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/7787048a-2129-4888-b681-1bd72735084e\/ui-window.png\">Large preview<\/a>)<br \/>\n    <\/figcaption><\/figure>\n<figure><a href=\"https:\/\/i0.wp.com\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/939e8d32-ba10-4f93-bd82-7edd941d7b21\/interface-components-illustration.png?ssl=1\"><\/p>\n<p>    <img data-recalc-dims=\"1\" decoding=\"async\" srcset=\"http:\/\/www.taterboy.com\/blog\/wp-content\/uploads\/2019\/07\/interface-components-illustration.png 400w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_800\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/939e8d32-ba10-4f93-bd82-7edd941d7b21\/interface-components-illustration.png 800w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1200\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/939e8d32-ba10-4f93-bd82-7edd941d7b21\/interface-components-illustration.png 1200w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1600\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/939e8d32-ba10-4f93-bd82-7edd941d7b21\/interface-components-illustration.png 1600w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_2000\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/939e8d32-ba10-4f93-bd82-7edd941d7b21\/interface-components-illustration.png 2000w\" src=\"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2019\/07\/interface-components-illustration.png?w=900\" sizes=\"100vw\" alt=\"Image showing the components making up our plugin\u2019s interface: window and web view\"><\/a><figcaption>\n      The components making up our plugin. (<a href=\"https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/939e8d32-ba10-4f93-bd82-7edd941d7b21\/interface-components-illustration.png\">Large preview<\/a>)<br \/>\n    <\/figcaption><\/figure>\n<h4>The Window<\/h4>\n<p>Mosaic\u2019s user interface design has its own window, which we can consider the most basic component; we\u2019ll start with it. In order to create and display a window, we\u2019ll have to make use of a class that\u2019s built into macOS by default, called <a href=\"https:\/\/gist.github.com\/matt-curtis\/documentation\"><code>NSWindow<\/code><\/a>. Over the remainder of this tutorial, we\u2019ll actually be doing this quite a bit (using built-in APIs like <code>NSWindow<\/code>) which might seem a little daunting if you\u2019re unfamiliar with it, but don\u2019t worry \u2014 I\u2019ll explain everything along the way!<\/p>\n<p><strong>Note:<\/strong> While we\u2019re talking about built-in APIs, the reason we\u2019re able to use this class <em>at all<\/em> is thanks to a <a href=\"https:\/\/github.com\/logancollins\/Mocha\">bridge<\/a> present in the JavaScript runtime used by Sketch plugins. This bridge automatically imports these built-in classes, methods, and functions that would normally only be available to native applications.<\/p>\n<p>Open <code>Sketch\/index.js<\/code> in your code editor, delete what\u2019s already there, and paste in the following:<\/p>\n<div>\n<pre><code>function onRun(context){\n  const window = NSWindow.alloc().initWithContentRect_styleMask_backing_defer_(\n    NSMakeRect(0, 0, 145, 500),\n    NSWindowStyleMaskClosable | NSWindowStyleMaskTitled | NSWindowStyleMaskResizable,\n    NSBackingStoreBuffered,\n    false\n  );\n\n  window.releasedWhenClosed = false;\n\n  window.makeKeyAndOrderFront(nil);\n};\n<\/code><\/pre>\n<\/div>\n<p>Let\u2019s take a look at what this first bit of code does:<\/p>\n<pre><code>function onRun(context){\n<\/code><\/pre>\n<p>Remember earlier when we talked about commands and how they function, and we told Sketch to call in response to a menu-click was called <code>onRun<\/code>? (If you need a refresher, revisit that part above, then come back.) All this bit does is create that function. You\u2019ll also notice our <code>onRun<\/code> function takes a <code>context<\/code> argument. This is an argument Sketch will call your command handlers with that can provide us with certain information. Later on, we\u2019ll use it in order to get the URL of our plugin bundle on the user\u2019s computer.<\/p>\n<div>\n<pre><code>const window = NSWindow.alloc().initWithContentRect_styleMask_backing_defer(\n  NSMakeRect(0, 0, 145, 500),\n  NSWindowStyleMaskClosable | NSWindowStyleMaskTitled | NSWindowStyleMaskResizable,\n  NSBackingStoreBuffered,\n  false\n);\n<\/code><\/pre>\n<\/div>\n<p>Here we\u2019re actually doing a few things:<\/p>\n<ol>\n<li>First, we call <code>alloc()<\/code> on <code>NSWindow<\/code>; this basically means \u201cset aside some memory for an instance of NSWindow\u201d. It\u2019s sufficient to know that you\u2019ll have to do this for <strong>every instance<\/strong> of a native class you want to create. The <a href=\"https:\/\/developer.apple.com\/documentation\/objectivec\/nsobject\/1571958-alloc?language=objc\"><code>alloc<\/code><\/a> method is available in every native class.<\/li>\n<li>Next, we call <code>NSWindow<\/code>\u2019s initializer method (that is, the method that actually creates an instance of <code>NSWindow<\/code>), which is named <a href=\"https:\/\/developer.apple.com\/documentation\/appkit\/nswindow\/1419477-initwithcontentrect?language=objc\"><code>initWithContentRect:styleMask:backing:defer:<\/code><\/a>. You\u2019ll notice that\u2019s different from what we call in our code above \u2014 it\u2019s got a bunch of colons (<code>:<\/code>) between every argument. Since we can\u2019t use that syntax in JavaScript, Sketch conveniently renames it to something we <em>can<\/em> actually use by replacing the colons with underscores, which is how we get its JS name: <code>initWithContentRect_styleMask_backing_defer<\/code>.<\/li>\n<li>Next, we pass in each of the arguments the method needs. For the first argument, <code>contentRect<\/code>, we supply a rectangle with a size large enough for our user interface.<\/li>\n<li>For <code>styleMask<\/code>, we use a <a href=\"https:\/\/codeburst.io\/using-javascript-bitwise-operators-in-real-life-f551a731ff5\">bitmask<\/a> which says that we want our window to have a close button, a title bar, and to be resizable.<\/li>\n<li>The next two arguments, <code>backing<\/code> and <code>defer<\/code>, are <strong>always<\/strong> going to be set to <code>NSBackingStoreBuffered<\/code> and <code>false<\/code>, so we don\u2019t really need to worry about them. (The <a href=\"https:\/\/developer.apple.com\/documentation\/appkit\/nswindow\/1419477-initwithcontentrect?language=objc\">documentation for this method<\/a> goes into further detail as to why this is.)<\/li>\n<\/ol>\n<pre><code>window.releasedWhenClosed = false;\n\nwindow.makeKeyAndOrderFront(null);\n<\/code><\/pre>\n<p>Here we set <code>NSWindow<\/code>\u2019s <a href=\"https:\/\/developer.apple.com\/documentation\/appkit\/nswindow\/1419062-releasedwhenclosed?language=objc\"><code>releasedWhenClosed<\/code><\/a> property to <code>false<\/code>, which means: \u201cHey! don\u2019t delete this window from memory just because the user closes it.\u201d Then we call <a href=\"https:\/\/developer.apple.com\/documentation\/appkit\/nswindow\/1419208-makekeyandorderfront?language=objc\"><code>makeKeyAndOrderFront<\/code><\/a><code>(null)<\/code> which means: \u201cMove this window to the forefront, and give it keyboard focus.\u201d<\/p>\n<h4>Web View: The Interface<\/h4>\n<p>To make things easier, I\u2019ve already written the HTML and CSS code of the plugin\u2019s web user interface we\u2019re going to be using; the only remaining code we\u2019re going to have to add to it will deal with making sure we\u2019re able to communicate between it and our Sketch plugin code.<\/p>\n<p>Next, <a href=\"http:\/\/provide.smashingmagazine.com\/build-sketch-plugin-javascript-html-css-part-1.zip\">download the HTML and CSS code<\/a>. Once you\u2019ve downloaded it, extract it, then move the folder named \u201cweb-ui\u201d into our plugin\u2019s Resources folder.<\/p>\n<p><strong>Note<\/strong>: <em>Writing and optimizing the actual HTML\/CSS code is outside of the scope of this tutorial, as its focus is on JavaScript which powers the plugin\u2019s core features; but there are <a href=\"http:\/\/www.google.com\/search?q=HTML%2FCSS+for+user+interfaces\">a ton of tutorials on the web<\/a> on this topic, should you want to learn more.<\/em><\/p>\n<p>If you run our plugin now, you\u2019ll see that it shows a window \u2014 yay, progress! But it\u2019s empty, without a title, and not particularly useful yet. We need to get it to show our web interface. In order to do that, we\u2019ll need to use another native class, <a href=\"https:\/\/developer.apple.com\/documentation\/webkit\/wkwebview?language=objc\"><code>WKWebView<\/code><\/a>, which is a view specifically made for displaying web content.<\/p>\n<p>We\u2019ll add the code needed to create our <code>WKWebView<\/code> beneath the code we wrote for our window:<\/p>\n<div>\n<pre><code>function onRun(context){\n    \/\/        Create window\n\n    const window = NSWindow.alloc().initWithContentRect_styleMask_backing_defer(\n            NSMakeRect(0, 0, 145, 500),\n            NSWindowStyleMaskClosable | NSWindowStyleMaskTitled | NSWindowStyleMaskResizable,\n            NSBackingStoreBuffered,\n            false\n    );\n\n    window.releasedWhenClosed = false;\n\n    \/\/        Create web view, and set it as the view for our window to display\n\n    const webView = WKWebView.alloc().init();\n\n    window.contentView = webView;\n\n    \/\/        Load our UI into the web view\n\n    const webUIFolderURL = context.scriptURL\n            .URLByDeletingLastPathComponent()\n            .URLByAppendingPathComponent(\"..\/Resources\/web-ui\/\");\n\n    const indexURL = webUIFolderURL.URLByAppendingPathComponent(\"index.html\");\n\n    webView.loadFileURL_allowingReadAccessToURL(indexURL, webUIFolderURL);\n\n    \/\/        Make window key and move to front\n\n    window.makeKeyAndOrderFront(nil);\n};\n<\/code><\/pre>\n<\/div>\n<p>If we run our plugin now, we\u2019ll see that now we\u2019ve got a window open that displays our web user interface. Success!<\/p>\n<p>Again, before moving on, let\u2019s examine what the code we added does:<\/p>\n<pre><code>const webView = WKWebView.alloc().init();\n<\/code><\/pre>\n<p>This should look familiar \u2014 it\u2019s basically the same as what we did when we made our <code>NSWindow<\/code>: allocate memory for a web view, then initialize it.<\/p>\n<pre><code>window.contentView = webView;\n<\/code><\/pre>\n<p>This line of code tells our window to display the web view we just made.<\/p>\n<pre><code>const webUIFolderURL = context.scriptURL\n  .URLByDeletingLastPathComponent()\n  .URLByAppendingPathComponent(\"..\/Resources\/web-ui\/\");\n<\/code><\/pre>\n<p>Here our goal is to create a URL that points to the <code>web-ui<\/code> folder that we made earlier. In order to get that URL, we need some way to figure out where our plugin\u2019s bundle is in the user\u2019s filesystem. Here we use the <code>context.scriptURL<\/code> property, which gives us the URL of the <em>currently running script<\/em>. However, this doesn\u2019t give us a JavaScript <code>String<\/code> as you might expect, but an instance of a native class, <a href=\"https:\/\/developer.apple.com\/documentation\/foundation\/nsurl?language=objc\"><code>NSURL<\/code><\/a>, that has a few methods on it that make manipulating URL strings easier.<\/p>\n<p>We need to turn what <code>context.scriptURL<\/code> gives us \u2014<\/p>\n<pre><code>file:\/\/path-to-your-plugin\/Contents\/Sketch\/index.js\n<\/code><\/pre>\n<p>\u2014 into:<\/p>\n<pre><code>file:\/\/path-to-your-plugin\/Contents\/Resources\/web-ui\/\n<\/code><\/pre>\n<p>Step by step:<\/p>\n<ol>\n<li>Calling <a href=\"https:\/\/developer.apple.com\/documentation\/foundation\/nsurl\/1411592-urlbydeletinglastpathcomponent?language=objc\"><code>URLByDeletingLastPathComponent()<\/code><\/a> the first time gives us <code>file:\/\/path-to-your-plugin\/Contents\/Sketch\/<\/code><\/li>\n<li>Calling <code>URLByDeletingLastPathComponent()<\/code> again gives us <code>file:\/\/path-to-your-plugin\/Contents\/<\/code><\/li>\n<li>And lastly, adding <code>Resources\/web-ui\/<\/code> onto the end using <a href=\"https:\/\/developer.apple.com\/documentation\/foundation\/nsurl\/1410614-urlbyappendingpathcomponent?language=objc\"><code>URLByAppendingPathComponent<\/code><\/a><code>(\"Resources\/web-ui\/\")<\/code> gives us <code>file:\/\/path-to-your-plugin\/Contents\/Resources\/web-ui\/<\/code><\/li>\n<\/ol>\n<p>We also need to create a second URL that points directly to the <code>index.html<\/code> file:<\/p>\n<div>\n<pre><code>const indexURL = webUIFolderURL.URLByAppendingPathComponent(\"index.html\");\n<\/code><\/pre>\n<\/div>\n<p>Finally, we tell our web view to load <code>index.html<\/code> and give it access to the contents of the <code>web-ui<\/code> folder:<\/p>\n<div>\n<pre><code>webView.loadFileURL_allowingReadAccessToURL(indexURL, webUIFolderURL);\n<\/code><\/pre>\n<\/div>\n<p>Alright. So far, we have a window that displays our web user interface, just like we wanted. However, it\u2019s not quite yet complete \u2014 our original design doesn\u2019t have a title bar (or \u201c<a href=\"https:\/\/en.wikipedia.org\/wiki\/Graphical_user_interface#User_interface_and_interaction_design\">chrome<\/a>\u201d), but our current window does. There\u2019s also the fact that when we click inside a Sketch document, that document moves in front of our window, which isn\u2019t what we want \u2014 we want the user to be able to interact with the plugin window <em>and<\/em> the Sketch document without having to constantly refocus from one window to the other.<\/p>\n<p>To fix this, we first need to get rid of the default window chrome and keep only the buttons. Adding the two lines of code below will get rid of the title bar.<\/p>\n<p><strong>Note:<\/strong> <em>Like before, all of the properties and methods we\u2019re using below are documented in <a href=\"https:\/\/developer.apple.com\/documentation\/appkit\/nswindow?language=objc\"><code>NSWindow<\/code><\/a><a href=\"https:\/\/developer.apple.com\/documentation\/appkit\/nswindow?language=objc\">\u2019s documentation<\/a> page.<\/em><\/p>\n<pre><code>window.titlebarAppearsTransparent = true;\nwindow.titleVisibility = NSWindowTitleHidden;\n<\/code><\/pre>\n<p>These next two lines of code will remove the window buttons (also known as \u201ctraffic lights\u201d in MacOS lingo) that we don\u2019t need \u2014 \u201czoom\u201d and \u201cminimize\u201d \u2014 leaving only the \u201cclose\u201d button:<\/p>\n<div>\n<pre><code>window.standardWindowButton(NSWindowZoomButton).hidden = true;\nwindow.standardWindowButton(NSWindowMiniaturizeButton).hidden = true;\n<\/code><\/pre>\n<\/div>\n<p>While we\u2019re at it, let\u2019s also go ahead and change the window\u2019s background color to match that of our web UI:<\/p>\n<div>\n<pre><code>window.backgroundColor = NSColor.colorWithRed_green_blue_alpha(1, 0.98, 0.98, 1);\n<\/code><\/pre>\n<\/div>\n<p>Next, we need to do something to keep our floating plugin window on top of other windows, so the user can interact with their Sketch documents without having to worry about the Mosaic\u2019s window disappearing. We can use a special type of <code>NSWindow<\/code> for this, called <code>NSPanel<\/code>, which is able to \u201cstay on top\u201d of other windows. All that\u2019s needed for this is to change <code>NSWindow<\/code> to <a href=\"https:\/\/developer.apple.com\/documentation\/appkit\/nspanel?language=objc\"><code>NSPanel<\/code><\/a>, which is a single-line code change:<\/p>\n<div>\n<pre><code>const window = NSPanel.alloc().initWithContentRect_styleMask_backing_defer(\n<\/code><\/pre>\n<\/div>\n<p>Now we tell our panel window to float (stay on top of all others), and only take keyboard\/mouse focus when necessary:<\/p>\n<pre><code>window.floatingPanel = true;\nwindow.becomesKeyOnlyIfNeeded = true;\n<\/code><\/pre>\n<p>We can also tweak our window so that it automatically reopens in the last position it was at:<\/p>\n<pre><code>window.frameAutosaveName = \"mosaic-panel-frame\";\n<\/code><\/pre>\n<p>This line basically says \u201cremember this window\u2019s position by saving it with Sketch\u2019s preferences under the key <code>mosaic-panel-frame<\/code>\u201d.<\/p>\n<p>All together, we now have the following code:<\/p>\n<div>\n<pre><code>function onRun(context){\n    \/\/        Create window\n\n    const window = NSPanel.alloc().initWithContentRect_styleMask_backing_defer(\n            NSMakeRect(0, 0, 145, 500),\n            NSWindowStyleMaskClosable | NSWindowStyleMaskTitled | NSWindowStyleMaskResizable,\n            NSBackingStoreBuffered,\n            false\n    );\n\n    window.becomesKeyOnlyIfNeeded = true;\n    window.floatingPanel = true;\n\n    window.frameAutosaveName = \"mosaic-panel-frame\";\n\n    window.releasedWhenClosed = false;\n\n    window.standardWindowButton(NSWindowZoomButton).hidden = true;\n    window.standardWindowButton(NSWindowMiniaturizeButton).hidden = true;\n\n    window.titlebarAppearsTransparent = true;\n    window.titleVisibility = NSWindowTitleHidden;\n\n    window.backgroundColor = NSColor.colorWithRed_green_blue_alpha(1, 0.98, 0.98, 1);\n\n    \/\/        Create web view, and set it as the view for our window to display\n\n    const webView = WKWebView.alloc().init();\n\n    window.contentView = webView;\n\n    \/\/        Load our UI into the webview\n\n    const webUIFolderURL = context.scriptURL\n            .URLByDeletingLastPathComponent()\n            .URLByAppendingPathComponent(\"..\/Resources\/web-ui\/\");\n\n    const indexURL = webUIFolderURL.URLByAppendingPathComponent(\"index.html\");\n\n    webView.loadFileURL_allowingReadAccessToURL(indexURL, webUIFolderURL);\n\n    \/\/        Make window key and move to front\n\n    window.makeKeyAndOrderFront(nil);\n};\n<\/code><\/pre>\n<\/div>\n<h4>Organizing The Code<\/h4>\n<p>Before we move to the next part, it\u2019s a good idea to organize our code so that it\u2019s easier to navigate and tweak. Since we still have a lot more code to add and we want to avoid <code>index.js<\/code> becoming a messy dumping ground for all of our code, let\u2019s split things up a bit and move our UI-specific code into a file named <code>ui.js<\/code>, under the <code>Sketch<\/code> folder. We\u2019ll also extract some of the UI tasks we do, like creating the web view and window, into their own functions.<\/p>\n<p>Create a new file called <code>ui.js<\/code> and insert the code below inside it:<\/p>\n<div>\n<pre><code>\/\/        Private\n\nvar _window;\n\nfunction createWebView(pageURL){\n        const webView = WKWebView.alloc().init();\n\n        webView.loadFileURL_allowingReadAccessToURL(pageURL, pageURL.URLByDeletingLastPathComponent());\n\n        return webView;\n};\n\nfunction createWindow(){\n        const window = NSPanel.alloc().initWithContentRect_styleMask_backing_defer(\n                NSMakeRect(0, 0, 420, 646),\n                NSWindowStyleMaskClosable | NSWindowStyleMaskTitled | NSWindowStyleMaskResizable,\n                NSBackingStoreBuffered,\n                false\n        );\n\n        window.becomesKeyOnlyIfNeeded = true;\n        window.floatingPanel = true;\n\n        window.frameAutosaveName = \"mosaic-panel-frame\";\n\n        window.releasedWhenClosed = false;\n\n        window.standardWindowButton(NSWindowZoomButton).hidden = true;\n        window.standardWindowButton(NSWindowMiniaturizeButton).hidden = true;\n\n        window.titlebarAppearsTransparent = true;\n        window.titleVisibility = NSWindowTitleHidden;\n        \n        window.backgroundColor = NSColor.colorWithRed_green_blue_alpha(1, 0.98, 0.98, 1);\n\n        return window;\n};\n\nfunction showWindow(window){\n        window.makeKeyAndOrderFront(nil);\n};\n\n\/\/        Public\n\nfunction loadAndShow(baseURL){\n        if(_window){\n                showWindow(_window);\n\n                return;\n        }\n\n        const pageURL = baseURL\n                .URLByDeletingLastPathComponent()\n                .URLByAppendingPathComponent(\"..\/Resources\/web-ui\/index.html\");\n\n        const window = createWindow();\n        const webView = createWebView(pageURL);\n\n        window.contentView = webView;\n        \n        showWindow(_window);\n\n        _window = window;\n};\n\nfunction cleanup(){\n        if(_window){\n                _window.orderOut(nil);\n                _window = null;\n        }\n};\n\n\/\/        Export\n\nmodule.exports = { loadAndShow, cleanup };\n<\/code><\/pre>\n<\/div>\n<p>There are a couple of key changes we made here that are important to note. Besides the fact that we\u2019ve created specific functions for the creation, hiding and showing of our window and its web view, we\u2019ve also <em>modularized<\/em> our user interface code.<\/p>\n<p>Notice the <code>module.exports = { loadAndShow, cleanup }<\/code> line at the bottom? This is a way for us to specify exactly what objects and functions scripts who import this UI code can use (and hiding those we don\u2019t want to them to worry about), which means we now have a more organized API for interacting with, showing and destroying our UI.<\/p>\n<p><strong>Recommended reading<\/strong>: <em><a href=\"https:\/\/www.smashingmagazine.com\/2017\/04\/symbols-sketch\/\">Unleashing The Full Potential Of Symbols In Sketch<\/a><\/em><\/p>\n<p>Let\u2019s see what this looks like in practice. Back in <code>index.js<\/code>, remove the old code and add the following:<\/p>\n<pre><code>const UI = require(\".\/ui\");\n\nfunction onRun(context){\n        UI.loadAndShow(context.scriptURL);\n};\n<\/code><\/pre>\n<p>We\u2019re using a special function that Sketch automatically makes available to us, <code>require<\/code>, to import our <code>ui.js<\/code> code and assign the returned module to the <code>UI<\/code> variable. This gives us access to a simplified API for triggering our user interface. Things are much tidier now and easy to find!<\/p>\n<h3>Conclusion<\/h3>\n<p>Well done \u2014 you\u2019ve come far! In the next part of this tutorial, we\u2019ll give our web UI the ability to send us a message when the \u201cApply\u201d button is clicked, and we\u2019ll focus on the main plugin functionality: actually generating layer mosaics!<\/p>\n<div>\n  <img data-recalc-dims=\"1\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2019\/07\/logo-red-4.png?w=900\" alt=\"Smashing Editorial\"><span>(mb, yk, il)<\/span>\n<\/div>\n<\/article>\n<p class=\"wpematico_credit\"><small>Powered by <a href=\"http:\/\/www.wpematico.com\" target=\"_blank\">WPeMatico<\/a><\/small><\/p>\n","protected":false},"excerpt":{"rendered":"<p>How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 1) How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 1) Matt Curtis 2019-07-05T12:30:59+02:002019-07-06T11:06:39+00:00 This tutorial is intended for people who know and use the Sketch app and are not afraid of dabbling with code. To profit from it the&#8230;<a class=\"moretag\" href=\"https:\/\/www.taterboy.com\/blog\/2019\/07\/how-to-build-a-sketch-plugin-with-javascript-html-and-css-part-1\/\"> Read the full article&#8230;<\/a><\/p>\n","protected":false},"author":4,"featured_media":67815,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[371],"tags":[],"class_list":["post-67814","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-user-experience"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.0.1 - aioseo.com -->\n\t<meta name=\"description\" content=\"How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 1)How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 1) Matt Curtis 2019-07-05T12:30:59+02:002019-07-06T11:06:39+00:00This tutorial is intended for people who know and use the Sketch app and are not afraid of dabbling with code. To profit from it the most, you\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Guest Contribution\"\/>\n\t<meta name=\"keywords\" content=\"user experience\" \/>\n\t<link rel=\"canonical\" href=\"https:\/\/www.taterboy.com\/blog\/2019\/07\/how-to-build-a-sketch-plugin-with-javascript-html-and-css-part-1\/\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.0.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"en_US\" \/>\n\t\t<meta property=\"og:site_name\" content=\"Design for Immersive Technologies | User Experience for Games, Virtual Reality (VR) and Augmented\/Mixed Reality (AR\/MR)\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 1) | Design for Immersive Technologies\" \/>\n\t\t<meta property=\"og:description\" content=\"How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 1)How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 1) Matt Curtis 2019-07-05T12:30:59+02:002019-07-06T11:06:39+00:00This tutorial is intended for people who know and use the Sketch app and are not afraid of dabbling with code. To profit from it the most, you\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/www.taterboy.com\/blog\/2019\/07\/how-to-build-a-sketch-plugin-with-javascript-html-and-css-part-1\/\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2019-07-06T11:19:27+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2019-07-06T11:19:27+00:00\" \/>\n\t\t<meta name=\"twitter:card\" content=\"summary\" \/>\n\t\t<meta name=\"twitter:title\" content=\"How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 1) | Design for Immersive Technologies\" \/>\n\t\t<meta name=\"twitter:description\" content=\"How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 1)How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 1) Matt Curtis 2019-07-05T12:30:59+02:002019-07-06T11:06:39+00:00This tutorial is intended for people who know and use the Sketch app and are not afraid of dabbling with code. To profit from it the most, you\" \/>\n\t\t<script type=\"application\/ld+json\" class=\"aioseo-schema\">\n\t\t\t{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2019\\\/07\\\/how-to-build-a-sketch-plugin-with-javascript-html-and-css-part-1\\\/#article\",\"name\":\"How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 1) | Design for Immersive Technologies\",\"headline\":\"How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 1)\",\"author\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/author\\\/guestcontribution\\\/#author\"},\"publisher\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/#organization\"},\"image\":{\"@type\":\"ImageObject\",\"url\":\"https:\\\/\\\/i0.wp.com\\\/www.taterboy.com\\\/blog\\\/wp-content\\\/uploads\\\/2019\\\/07\\\/ui-pattern-examples.png?fit=400%2C401&ssl=1\",\"width\":400,\"height\":401},\"datePublished\":\"2019-07-06T04:19:27-07:00\",\"dateModified\":\"2019-07-06T04:19:27-07:00\",\"inLanguage\":\"en-US\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2019\\\/07\\\/how-to-build-a-sketch-plugin-with-javascript-html-and-css-part-1\\\/#webpage\"},\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2019\\\/07\\\/how-to-build-a-sketch-plugin-with-javascript-html-and-css-part-1\\\/#webpage\"},\"articleSection\":\"User Experience\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2019\\\/07\\\/how-to-build-a-sketch-plugin-with-javascript-html-and-css-part-1\\\/#breadcrumblist\",\"itemListElement\":[{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog#listItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.taterboy.com\\\/blog\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/category\\\/user-experience\\\/#listItem\",\"name\":\"User Experience\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/category\\\/user-experience\\\/#listItem\",\"position\":2,\"name\":\"User Experience\",\"item\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/category\\\/user-experience\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2019\\\/07\\\/how-to-build-a-sketch-plugin-with-javascript-html-and-css-part-1\\\/#listItem\",\"name\":\"How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 1)\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog#listItem\",\"name\":\"Home\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2019\\\/07\\\/how-to-build-a-sketch-plugin-with-javascript-html-and-css-part-1\\\/#listItem\",\"position\":3,\"name\":\"How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 1)\",\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/category\\\/user-experience\\\/#listItem\",\"name\":\"User Experience\"}}]},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/#organization\",\"name\":\"Design for Immersive Technologies\",\"description\":\"User Experience for Games, Virtual Reality (VR) and Augmented\\\/Mixed Reality (AR\\\/MR)\",\"url\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/author\\\/guestcontribution\\\/#author\",\"url\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/author\\\/guestcontribution\\\/\",\"name\":\"Guest Contribution\",\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2019\\\/07\\\/how-to-build-a-sketch-plugin-with-javascript-html-and-css-part-1\\\/#authorImage\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/d2aad73eb0f48c67b141e9fc978a0e498969791ed623346e361d02d19775b0bb?s=96&d=mm&r=g\",\"width\":96,\"height\":96,\"caption\":\"Guest Contribution\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2019\\\/07\\\/how-to-build-a-sketch-plugin-with-javascript-html-and-css-part-1\\\/#webpage\",\"url\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2019\\\/07\\\/how-to-build-a-sketch-plugin-with-javascript-html-and-css-part-1\\\/\",\"name\":\"How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 1) | Design for Immersive Technologies\",\"description\":\"How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 1)How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 1) Matt Curtis 2019-07-05T12:30:59+02:002019-07-06T11:06:39+00:00This tutorial is intended for people who know and use the Sketch app and are not afraid of dabbling with code. To profit from it the most, you\",\"inLanguage\":\"en-US\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/#website\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2019\\\/07\\\/how-to-build-a-sketch-plugin-with-javascript-html-and-css-part-1\\\/#breadcrumblist\"},\"author\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/author\\\/guestcontribution\\\/#author\"},\"creator\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/author\\\/guestcontribution\\\/#author\"},\"image\":{\"@type\":\"ImageObject\",\"url\":\"https:\\\/\\\/i0.wp.com\\\/www.taterboy.com\\\/blog\\\/wp-content\\\/uploads\\\/2019\\\/07\\\/ui-pattern-examples.png?fit=400%2C401&ssl=1\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2019\\\/07\\\/how-to-build-a-sketch-plugin-with-javascript-html-and-css-part-1\\\/#mainImage\",\"width\":400,\"height\":401},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2019\\\/07\\\/how-to-build-a-sketch-plugin-with-javascript-html-and-css-part-1\\\/#mainImage\"},\"datePublished\":\"2019-07-06T04:19:27-07:00\",\"dateModified\":\"2019-07-06T04:19:27-07:00\"},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/\",\"name\":\"Design for Immersive Technologies\",\"description\":\"User Experience for Games, Virtual Reality (VR) and Augmented\\\/Mixed Reality (AR\\\/MR)\",\"inLanguage\":\"en-US\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/#organization\"}}]}\n\t\t<\/script>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 1) | Design for Immersive Technologies","description":"How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 1)How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 1) Matt Curtis 2019-07-05T12:30:59+02:002019-07-06T11:06:39+00:00This tutorial is intended for people who know and use the Sketch app and are not afraid of dabbling with code. To profit from it the most, you","canonical_url":"https:\/\/www.taterboy.com\/blog\/2019\/07\/how-to-build-a-sketch-plugin-with-javascript-html-and-css-part-1\/","robots":"max-image-preview:large","keywords":"user experience","webmasterTools":{"miscellaneous":""},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.taterboy.com\/blog\/2019\/07\/how-to-build-a-sketch-plugin-with-javascript-html-and-css-part-1\/#article","name":"How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 1) | Design for Immersive Technologies","headline":"How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 1)","author":{"@id":"https:\/\/www.taterboy.com\/blog\/author\/guestcontribution\/#author"},"publisher":{"@id":"https:\/\/www.taterboy.com\/blog\/#organization"},"image":{"@type":"ImageObject","url":"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2019\/07\/ui-pattern-examples.png?fit=400%2C401&ssl=1","width":400,"height":401},"datePublished":"2019-07-06T04:19:27-07:00","dateModified":"2019-07-06T04:19:27-07:00","inLanguage":"en-US","mainEntityOfPage":{"@id":"https:\/\/www.taterboy.com\/blog\/2019\/07\/how-to-build-a-sketch-plugin-with-javascript-html-and-css-part-1\/#webpage"},"isPartOf":{"@id":"https:\/\/www.taterboy.com\/blog\/2019\/07\/how-to-build-a-sketch-plugin-with-javascript-html-and-css-part-1\/#webpage"},"articleSection":"User Experience"},{"@type":"BreadcrumbList","@id":"https:\/\/www.taterboy.com\/blog\/2019\/07\/how-to-build-a-sketch-plugin-with-javascript-html-and-css-part-1\/#breadcrumblist","itemListElement":[{"@type":"ListItem","@id":"https:\/\/www.taterboy.com\/blog#listItem","position":1,"name":"Home","item":"https:\/\/www.taterboy.com\/blog","nextItem":{"@type":"ListItem","@id":"https:\/\/www.taterboy.com\/blog\/category\/user-experience\/#listItem","name":"User Experience"}},{"@type":"ListItem","@id":"https:\/\/www.taterboy.com\/blog\/category\/user-experience\/#listItem","position":2,"name":"User Experience","item":"https:\/\/www.taterboy.com\/blog\/category\/user-experience\/","nextItem":{"@type":"ListItem","@id":"https:\/\/www.taterboy.com\/blog\/2019\/07\/how-to-build-a-sketch-plugin-with-javascript-html-and-css-part-1\/#listItem","name":"How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 1)"},"previousItem":{"@type":"ListItem","@id":"https:\/\/www.taterboy.com\/blog#listItem","name":"Home"}},{"@type":"ListItem","@id":"https:\/\/www.taterboy.com\/blog\/2019\/07\/how-to-build-a-sketch-plugin-with-javascript-html-and-css-part-1\/#listItem","position":3,"name":"How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 1)","previousItem":{"@type":"ListItem","@id":"https:\/\/www.taterboy.com\/blog\/category\/user-experience\/#listItem","name":"User Experience"}}]},{"@type":"Organization","@id":"https:\/\/www.taterboy.com\/blog\/#organization","name":"Design for Immersive Technologies","description":"User Experience for Games, Virtual Reality (VR) and Augmented\/Mixed Reality (AR\/MR)","url":"https:\/\/www.taterboy.com\/blog\/"},{"@type":"Person","@id":"https:\/\/www.taterboy.com\/blog\/author\/guestcontribution\/#author","url":"https:\/\/www.taterboy.com\/blog\/author\/guestcontribution\/","name":"Guest Contribution","image":{"@type":"ImageObject","@id":"https:\/\/www.taterboy.com\/blog\/2019\/07\/how-to-build-a-sketch-plugin-with-javascript-html-and-css-part-1\/#authorImage","url":"https:\/\/secure.gravatar.com\/avatar\/d2aad73eb0f48c67b141e9fc978a0e498969791ed623346e361d02d19775b0bb?s=96&d=mm&r=g","width":96,"height":96,"caption":"Guest Contribution"}},{"@type":"WebPage","@id":"https:\/\/www.taterboy.com\/blog\/2019\/07\/how-to-build-a-sketch-plugin-with-javascript-html-and-css-part-1\/#webpage","url":"https:\/\/www.taterboy.com\/blog\/2019\/07\/how-to-build-a-sketch-plugin-with-javascript-html-and-css-part-1\/","name":"How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 1) | Design for Immersive Technologies","description":"How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 1)How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 1) Matt Curtis 2019-07-05T12:30:59+02:002019-07-06T11:06:39+00:00This tutorial is intended for people who know and use the Sketch app and are not afraid of dabbling with code. To profit from it the most, you","inLanguage":"en-US","isPartOf":{"@id":"https:\/\/www.taterboy.com\/blog\/#website"},"breadcrumb":{"@id":"https:\/\/www.taterboy.com\/blog\/2019\/07\/how-to-build-a-sketch-plugin-with-javascript-html-and-css-part-1\/#breadcrumblist"},"author":{"@id":"https:\/\/www.taterboy.com\/blog\/author\/guestcontribution\/#author"},"creator":{"@id":"https:\/\/www.taterboy.com\/blog\/author\/guestcontribution\/#author"},"image":{"@type":"ImageObject","url":"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2019\/07\/ui-pattern-examples.png?fit=400%2C401&ssl=1","@id":"https:\/\/www.taterboy.com\/blog\/2019\/07\/how-to-build-a-sketch-plugin-with-javascript-html-and-css-part-1\/#mainImage","width":400,"height":401},"primaryImageOfPage":{"@id":"https:\/\/www.taterboy.com\/blog\/2019\/07\/how-to-build-a-sketch-plugin-with-javascript-html-and-css-part-1\/#mainImage"},"datePublished":"2019-07-06T04:19:27-07:00","dateModified":"2019-07-06T04:19:27-07:00"},{"@type":"WebSite","@id":"https:\/\/www.taterboy.com\/blog\/#website","url":"https:\/\/www.taterboy.com\/blog\/","name":"Design for Immersive Technologies","description":"User Experience for Games, Virtual Reality (VR) and Augmented\/Mixed Reality (AR\/MR)","inLanguage":"en-US","publisher":{"@id":"https:\/\/www.taterboy.com\/blog\/#organization"}}]},"og:locale":"en_US","og:site_name":"Design for Immersive Technologies | User Experience for Games, Virtual Reality (VR) and Augmented\/Mixed Reality (AR\/MR)","og:type":"article","og:title":"How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 1) | Design for Immersive Technologies","og:description":"How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 1)How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 1) Matt Curtis 2019-07-05T12:30:59+02:002019-07-06T11:06:39+00:00This tutorial is intended for people who know and use the Sketch app and are not afraid of dabbling with code. To profit from it the most, you","og:url":"https:\/\/www.taterboy.com\/blog\/2019\/07\/how-to-build-a-sketch-plugin-with-javascript-html-and-css-part-1\/","article:published_time":"2019-07-06T11:19:27+00:00","article:modified_time":"2019-07-06T11:19:27+00:00","twitter:card":"summary","twitter:title":"How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 1) | Design for Immersive Technologies","twitter:description":"How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 1)How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 1) Matt Curtis 2019-07-05T12:30:59+02:002019-07-06T11:06:39+00:00This tutorial is intended for people who know and use the Sketch app and are not afraid of dabbling with code. To profit from it the most, you"},"aioseo_meta_data":{"post_id":"67814","title":null,"description":null,"keywords":null,"keyphrases":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_custom_url":null,"og_image_custom_fields":null,"og_custom_image_width":null,"og_custom_image_height":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"location":null,"local_seo":null,"created":"2021-02-07 19:04:26","updated":"2026-09-01 14:44:25","focus_keyword":null,"additional_keywords":null,"truseo_locale":null,"primary_term":null,"og_image_url":null,"og_image_width":null,"og_image_height":null,"twitter_image_url":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"Article","isEnabled":true},"graphs":[]},"limit_modified_date":false,"ai":null,"breadcrumb_settings":null,"seo_analyzer_scan_date":null},"aioseo_breadcrumb":"<div class=\"aioseo-breadcrumbs\"><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/www.taterboy.com\/blog\" title=\"Home\">Home<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">&raquo;<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/www.taterboy.com\/blog\/category\/user-experience\/\" title=\"User Experience\">User Experience<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">&raquo;<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\tHow To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 1)\n\t\t<\/span><\/div>","aioseo_breadcrumb_json":[{"label":"Home","link":"https:\/\/www.taterboy.com\/blog"},{"label":"User Experience","link":"https:\/\/www.taterboy.com\/blog\/category\/user-experience\/"},{"label":"How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 1)","link":"https:\/\/www.taterboy.com\/blog\/2019\/07\/how-to-build-a-sketch-plugin-with-javascript-html-and-css-part-1\/"}],"jetpack_publicize_connections":[],"jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p8wzr5-hDM","jetpack_featured_media_url":"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2019\/07\/ui-pattern-examples.png?fit=400%2C401&ssl=1","_links":{"self":[{"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/posts\/67814","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/comments?post=67814"}],"version-history":[{"count":0,"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/posts\/67814\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/media\/67815"}],"wp:attachment":[{"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/media?parent=67814"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/categories?post=67814"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/tags?post=67814"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}