Collective #542

Inspirational Website of the Week: Igor Mahr A web design full of lively motion with a truly unique feel. Our pick this week. Get inspired This content is sponsored via Syndicate Ads Easily Secure and Manage All Your Apple Devices in Minutes Jamf Now Read more

How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 2)

dreamt up by webguru in Uncategorized | Comments Off on How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 2)

How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 2)

How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 2)

Matt Curtis



As mentioned in part 1, this tutorial is intended for people who know and use the Sketch app and are not afraid of dabbling with code as well. To profit from it the most, you will need to have at least some basic experience writing JavaScript (and, optionally, HTML/CSS).

In the previous part of this tutorial, we learned about the basic files that make up a plugin, and how to create the plugin’s user interface. In this second and final part, we’ll learn how to connect the user interface to the core plugin code and how to implement the plugin’s main features. Last but not least, we’ll also learn how to optimize the code and the way the plugin works.

Building The Plugin’s User Interface: Making Our Web Interface And The Sketch Plugin Code “Talk” To Each Other

The next thing we need to do is to set up communication between our web interface and the Sketch plugin.

We need to be able to send a message from our web interface to the Sketch plugin when the “Apply” button in our web interface is clicked. This message needs to tell us about what settings the user has input — like the number of steps, rotation amount, the number of duplicates to create, and so on.

WKWebView makes this task a little bit easier for us: we can send messages to our Sketch plugin from our web interface’s JavaScript code by using the window.webkit.messageHandlers API.

On our Sketch code’s side, we can use another method, addScriptMessageHandler:name: (or addScriptMessageHandler_name) to register a message handler that will be called whenever it receives a message sent from our plugin web interface.

Let’s start by making sure we can receive messages from our web UI. Head over to our ui.js file’s createWebView function, and add the following:

function createWebView(pageURL){
        const webView = WKWebView.alloc().init();

        //        Set handler for messages from script

        const userContentController = webView.configuration().userContentController();
        
        const ourMessageHandler = ...

        userContentController.addScriptMessageHandler_name(
                ourMessageHandler, "sketchPlugin"
        );

        //        Load page into web view

        webView.loadFileURL_allowingReadAccessToURL(pageURL, pageURL.URLByDeletingLastPathComponent());

        return webView;
};

Here we use the web view’s userContentController property to add a message handler we’ve named “sketchPlugin”. This “user content controller” is the bridge that ensures messages get across from our web view.

You might have noticed something odd about the above code: the object we’re adding as the message handler, ourMessageHandler, doesn’t exist yet! Unfortunately, we can’t just use a regular JavaScript object or function as the handler, as this method expects a certain kind of native object.

Luckily for us, we can get around this limitation by using MochaJSDelegate, a mini-library I wrote that makes it possible to create the kind of native object we need using regular ol’ JavaScript. You’ll need to manually download and save it in your plugin bundle under Sketch/MochaJSDelegate.js.

In order to use it, we’ll need to first import it into ui.js. Add the following at the top of the file:

const MochaJSDelegate = require("./MochaJSDelegate");

Now we can use MochaJSDelegate to create the type of message handler addScriptMessageHandler:name: is expecting:

function createWebView(pageURL){
        const webView = WKWebView.alloc().init();

        //        Set handler for messages from script

        const userContentController = webView.configuration().userContentController();

        const scriptMessageHandler = new MochaJSDelegate({
                "userContentController:didReceiveScriptMessage:": (_, wkMessage) => {
                        /* handle message here */
                }
        }).getClassInstance();

        userContentController.addScriptMessageHandler_name(
                scriptMessageHandler, "sketchPlugin"
        );

        //        Load page into web view

        webView.loadFileURL_allowingReadAccessToURL(pageURL, pageURL.URLByDeletingLastPathComponent());

        return webView;
};

The code we just added creates the native object we need. It also defines a method on that object named userContentController:didReceiveScriptMessage: — this method is then called with the message we want as the second argument. Since we’re not actually sending any messages yet, we’ll have to come back here at a later time and add some code to actually parse and handle the messages we receive.

Next, we need to add some code to our web interface to send us those messages. Head over to /Resources/web-ui/script.js. You’ll find I’ve already written most of the code that handles retrieving the values of the HTML <inputs /> the user will enter their options into.

What’s still left for us to do is to add the code that actually sends the values over to our Sketch code:

Find the apply function and add the following to the end of it:

//        Send user inputs to sketch plugin

window.webkit.messageHandlers.sketchPlugin.postMessage(JSON.stringify({
        stepCount, startingOptions, stepOptions
}));

Here we use window.webkit.messageHandlers API we mentioned earlier to access the message handler we registered above as sketchPlugin. Then send a message to it with a JSON string containing the user’s inputs.

Let’s make sure everything is set up properly. Head back to /Sketch/ui.js. In order to make sure we’re getting messages as we expect, we’ll modify the method we defined earlier so that it displays a dialog when we get a message:

function createWebView(pageURL){
        // ...

        const scriptMessageHandler = new MochaJSDelegate({
                "userContentController:didReceiveScriptMessage:": (_, wkMessage) => {
                        const UI = require("sketch/ui");
                        
                        UI.alert("Hey, a message!", wkMessage.body());
                }
        }).getClassInstance();

        userContentController.addScriptMessageHandler_name(
                scriptMessageHandler, "sketchPlugin"
        );

        // ...
};

Now run the plugin (you may need to first close any existing Mosaic window you have opened), enter some values, then click “Apply”. You should see an alert like the one below — this means everything is wired up correctly and our message went through successfully! If not, go back over the previous steps and ensure that everything was done as described.

Image showing the dialog you should see after clicking the ‘apply’ button in the plugin’s UI.

The dialog you should see appear once you click Apply. (Large preview)

Now that we’re able to send messages from our interface to our plugin, we can move on to writing the code that actually does something useful with that information: generating our layer mosaics.

Generating The Layer Mosaics

Let’s take stock of what’s necessary in order to make this happen. Simplifying things a bit, what our code needs to do is:

  1. Find the current document.
  2. Find the current document’s selected layer.
  3. Duplicate the selected layer (we’ll call it the template layer) x number of times.
  4. For each duplicate, tweak its position, rotation, opacity, etc., by the specific values (amounts) set by the user.

Now that we’ve got a reasonable plan, let’s continue writing. Sticking with our pattern of modularizing our code, let’s create a new file, mosaic.js in the Sketch/ folder, and add to it the following code:

function mosaic(options){

};

module.export = mosaic;

We’ll use this function as the only export of this module since it makes for a simpler API to use once we import it — we can just call mosaic() with whatever options we get from the web interface.

The first two steps we need to take are getting the current document, and then its selected layer. The Sketch API has a built-in library for document manipulation which we can get access to by importing the sketch/dom module. We only need the Document object right now, so we’ll pull it out explicitly. At the top of the file, add:

const { Document } = require("sketch/dom");

The Document object has a method specifically for accessing the current document we can use, called getSelectedDocument(). Once we have the current document instance, we can access whatever layers the user has selected via the document’s selectedLayers property. In our case, though, we only care about single-layer selections, so we’ll only grab the first layer the user has selected:

function mosaic(options){
        const document = Document.getSelectedDocument();
        const selectedLayer = document.selectedLayers.layers[0];
};

module.export = mosaic;

Note: You might have been expecting selectedLayers itself to be an array, but it’s not. Instead, it’s an instance of the Selection class. There’s a reason for this: the Selection class contains a bunch of useful helper methods for manipulating the selection like clear, map, reduce, and forEach. It exposes the actual layer array via the layer property.

Let’s also add some warning feedback in case the user forgets to open a document or to select something:

const UI = require("sketch/ui");

function mosaic(options){
        const document = Document.getSelectedDocument();

        //        Safety check:

        if(!document){
                UI.alert("Mosaic", "⚠️ Please select/focus a document.");

                return;
        }

        //        Safety check:

        const selectedLayer = document.selectedLayers.layers[0];

        if(!selectedLayer){
                UI.alert("Mosaic", "⚠️ Please select a layer to duplicate.");
                
                return;
        }
};

module.export = mosaic;

Now that we’ve written the code for steps 1 and 2 (finding the current document and selected layer), we need to address steps 3 and 4:

  • Duplicate the template layer x number of times.
  • For each duplicate, tweak its position, rotation, opacity, etc., by the specific values set by the user.

Let’s start by pulling all the relevant information we need out of options: the number of times to duplicate, starting options, and step options. We can once again use destructuring (like we did earlier with Document) to pull those properties out of options:

function mosaic(options) {
        //        ...
        
        //        Destructure options:

        var { stepCount, startingOptions, stepOptions } = options;
}

Next, let’s sanitize our inputs and ensure that step count is always at least 1:

function mosaic(options) {
        //        ...
        
        //        Destructure options:
        
        var { stepCount, startingOptions, stepOptions } = options;
        
        stepCount = Math.max(1, stepCount);
}

Now we need to make sure that the template layer’s opacity, rotation, etc., all match up with the user’s desired starting values. Since applying the user’s options to a layer is going to be something we’ll do a lot of, we’ll move this work into its own method:

function configureLayer(layer, options, shouldAdjustSpacing){
        const { opacity, rotation, direction, spacing } = options;

        layer.style.opacity = opacity / 100;
        layer.transform.rotation = rotation;

        if(shouldAdjustSpacing){
                const directionAsRadians = direction * (Math.PI / 180);
                const vector = {
                        x: Math.cos(directionAsRadians),
                        y: Math.sin(directionAsRadians)
                };

                layer.frame.x += vector.x * spacing;
                layer.frame.y += vector.y * spacing;
        }
};

And because spacing only needs to be applied in-between the duplicates and not the template layer, we’ve added a specific flag, shouldAdjustSpacing, that we can set to true or false depending on whether we’re applying options to a template layer or not. That way we can ensure that rotation and opacity will be applied to the template, but not spacing.

Back in the mosaic method, let’s now ensure that the starting options are applied to the template layer:

function mosaic(options){
        //        ...
        
        //        Configure template layer

        var layer = group.layers[0];
        
        configureLayer(layer, startingOptions, false);
}

Next, we need to create our duplicates. First, let’s create a variable that we can use to track what the options for the current duplicate are:

function mosaic(options){
        //        ...

        var currentOptions; // ...
}

Since we already applied the starting options to the template layer, we need to take those options we just applied and add the relative values of stepOptions in order to get the options to apply to the next layer. Since we’ll also be doing this several more times in our loop, we’ll also move this work into a specific method, stepOptionsBy:

function stepOptionsBy(start, step){
        const newOptions = {};
        
        for(let key in start){
                newOptions[key] = start[key] + step[key];
        }

        return newOptions;
};

After that, we need to write a loop that duplicates the previous layer, applies the current options to it, then offsets (or “steps”) the current options in order to get the options for the next duplicate:

function mosaic(options) {
        //        ...
        
        var currentOptions = stepOptionsBy(startingOptions, stepOptions);

        for(let i = 0; i < (stepCount - 1); i++){
                let duplicateLayer = layer.duplicate();

                configureLayer(duplicateLayer, currentOptions, true);

                currentOptions = stepOptionsBy(currentOptions, stepOptions);
                layer = duplicateLayer;
        }
}

All done — we’ve successfully written the core of what our plugin is supposed to do! Now, we need to wire things up so that when the user actually clicks the “Apply” button our mosaic code is invoked.

Let’s head back to ui.js and adjust our message handling code. What we’ll need to do is parse the JSON string of options we’re getting so that they’re turned into an object we can actually use. Once we have these options, we can then call the mosaic function with them.

First, parsing. We’ll need to update our message handling function to parse the JSON message we get:

function createWebView(pageURL){
        //        ...
        
        const scriptMessageHandler = new MochaJSDelegate({
                "userContentController:didReceiveScriptMessage:": (_, wkMessage) => {
                        const message = JSON.parse(wkMessage.body());
                }
        });
}

Next, we’ll need to pass this over to our mosaic function. However, this isn’t really something our code in ui.js should be doing — it’s supposed to be primarily concerned with what’s necessary to display interface-related things on screen — not creating mosaics itself. To keep these responsibilities separate, we’ll add a second argument to createWebView that takes a function, and we’ll call that function whenever we receive options from the web interface.

Let’s name this argument onApplyMessage:

function createWebView(pageURL, onApplyMessage){
        // ...
        
        const scriptMessageHandler = new MochaJSDelegate({
                "userContentController:didReceiveScriptMessage:": (_, wkMessage) => {
                        const message = JSON.parse(wkMessage.body());
                        
                        onApplyMessage(message);
                }
        });
}

We’ll also need to modify our exported method, loadAndShow, to take this onApplyMessage argument as well and pass it off to createWebView:

function loadAndShow(baseURL, onApplyMessage){
        //        ...
        
        const webView = createWebView(pageURL, onApplyMessage);
}

Finally, head over to main.js. We now need to import our mosaic function, and call it with the options we receive from the plugin’s user interface:

const mosaic = require("./mosaic");

function onRun(context){
        UI.loadAndShow(context.scriptURL, options => {
                mosaic(options);
        });
};

We’re almost done!

However, if we ran our code now and clicked the “Apply” button in the plugin interface, nothing would happen. Why? The reason is due to how Sketch scripts are run: by default, they “live” only until the bottom of your script is reached, after which Sketch destroys it and frees up whatever resources it was using.

This is a problem for us since it means that anything we need to have asynchronously happen (in this case, that’s after the bottom of our code is reached), like receiving messages, cannot, because our script has been destroyed. This means we wouldn’t get any of our messages from the web interface since we’re not around to receive and respond to them!

There’s a way to signal to Sketch that we need our script to stay alive beyond this point, using Fibers. By creating a Fiber, we tell Sketch that something asynchronous is happening and that it needs to keep our script around. Sketch will then only destroy our script when absolutely necessary (like the user closing Sketch, or when the Mosaic plugin needs to be updated):

//        ...

const Async = require("sketch/async");

var fiber;

function onRun(context){
    if(!fiber){
        fiber = Async.createFiber();
        fiber.onCleanup(() => {
            UI.cleanup();
        });
    }
    
    UI.loadAndShow(context.scriptURL, options => {
        mosaic(options);
    });
};

Voilà! Let’s try out our plugin now. With a layer selected in Sketch, enter some settings, then click apply:

Let’s try out our plugin now — with a layer selected in Sketch, enter some settings, then click “Apply.”

Final Improvements

Now that we’ve got the majority of our plugin’s functionality implemented, we can try to “zoom out” a bit and take a look at the big picture.

Improving The User’s Experience

If you’ve played around with the plugin in its current state, you might’ve noticed that one of the biggest points of friction appears when you try to edit a Mosaic. Once you create one, you have to hit undo, adjust the options, then click ‘Apply’ (or press Enter). It also makes it harder to edit a Mosaic after you’ve left your document and returned to it later, since your undo/redo history will have been wiped out, leaving you to manually delete the duplicate layers yourself.

In a more ideal flow, the user could just select a Mosaic group, adjust options and watch the Mosaic update until they get the exact arrangement they’re looking for. To implement this, we have two problems to solve:

  1. First, we’ll need a way to group the duplicates that make up a Mosaic together. Sketch provides the concept of Groups, which we can use to solve this problem.
  2. Second, we’ll need a way to tell the difference between a normal, user-created group and a Mosaic group. Sketch’s API also gives us a way to store information on any given layer, which we can use as a way tag and later identify a group as one of our ‘special’ Mosaic groups.

Let’s revisit the logic we wrote in the previous section to address this. Our original code follows the following steps:

  1. Find the current document.
  2. Find the current document’s selected layer.
  3. Duplicate the selected layer (we’ll call it the template layer) x number of times.
  4. For each duplicate, tweak its position, rotation, opacity, etc., by the specific values (amounts) set by the user.

In order to make our new user flow possible, we need to change these steps to:

  1. Grab the current document.
  2. Grab the current document’s selected layer.
  3. Determine whether the selected layer is a Mosaic group or not.
    • If it’s some other layer, use it as the template layer and go to step 4.
    • If it is a Mosaic group, consider the first layer in it as the template layer, and go to step 5.
  4. Wrap the template layer inside a group, and mark that group as a Mosaic group.
  5. Remove all layers from inside the group except the template layer.
  6. Duplicate the template layer x number of times.
  7. For each duplicate, tweak its position, rotation, opacity, etc., by the specific values set by the user.

We’ve got three new steps. For the first new step, step 3, we’ll create a function named findOrMakeSpecialGroupIfNeeded that will look at the layer passed to it to determine whether or not it’s a Mosaic group. If it is, we’ll just return it. Since the user could potentially select a sublayer nested deep in a Mosaic group, we’ll also need to check the parents of the selected layer to tell if they’re one of our Mosaic groups as well:

function findOrMakeSpecialGroupIfNeeded(layer){
        //        Loop up through the parent hierarchy, looking for a special group

        var layerToCheck = layer;

        while(layerToCheck){
                if(/* TODO: is mosaic layer? */){
                        return layerToCheck;
                }

                layerToCheck = layerToCheck.parent;
        }
};

If we weren’t able to find a Mosaic group we’ll simply wrap the layer we were passed inside a Group, then tag it as a Mosaic group.

Back at the top of the file, we’ll need to pull out the Group class now too:

const { Document, Group } = require("sketch/dom");
function findOrMakeSpecialGroupIfNeeded(layer){
        //        Loop up through the parent hierarchy, looking for a special group

        var layerToCheck = layer;

        while(layerToCheck){
                if(/* TODO: is mosaic layer? */){
                        return layerToCheck;
                }

                layerToCheck = layerToCheck.parent;
        }
        
        //        Group

        const destinationParent = layer.parent;
        const group = new Group({
                name: "Mosaic Group",
                layers: [ layer ],
                parent: destinationParent
        });
        
        /* TODO: mark group as mosaic layer */
        
        return group;
};

Now we need to fill in the gaps (todo’s). To begin with, we need a means of identifying whether or not a group is one of the special groups belonging to us or not. Here, the Settings module of the Sketch library comes to our rescue. We can use it to store custom information on a particular layer, and also to read it back.

Once we import the module at the top of the file:

const Settings = require("sketch/settings");

We can then use two key methods it provides, setLayerSettingForKey and layerSettingForKey, to set and read data off a layer:

function findOrMakeSpecialGroupIfNeeded(layer){
        const isSpecialGroupKey = "is-mosaic-group";

        //        Loop up through the parent hierarchy, looking for a special group

        var layerToCheck = layer;

        while(layerToCheck){
                let isSpecialGroup = Settings.layerSettingForKey(layerToCheck, isSpecialGroupKey);

                if(isSpecialGroup) return layerToCheck;

                layerToCheck = layerToCheck.parent;
        }
        
        //        Group

        const destinationParent = layer.parent;

       layer.remove(); // explicitly remove layer from it’s existing parent before adding it to group

        const group = new Group({
                name: "Mosaic Group",
                layers: [ layer ],
                parent: destinationParent
        });
        
        Settings.setLayerSettingForKey(group, isSpecialGroupKey, true);

        return group;
};

Now that we’ve got a method that handles wrapping a layer in a mosaic group (or, if already a mosaic group, just returns it) we can now plug it into our main mosaic method just after our safety checks:

function mosaic(options){
        //       ... safety checks ...

        //        Group selection if needed:

        const group = findOrMakeSpecialGroupIfNeeded(selectedLayer);
}

Next we’ll add a loop to remove all layers from the group except the template layer (which is the first):

function mosaic(options) {
        //        ...
        
        //        Remove all layers except the first:
        
        while(group.layers.length > 1){
                group.layers[group.layers.length - 1].remove();
        }
}

Lastly, we’ll make sure that the group’s size is fitted to its new contents since the user might have originally selected a layer nested within the old group (a layer that we might have removed).

We’ll also need to make sure to set the current selection to our mosaic group itself. This will ensure that if the user is making a bunch of rapid changes to the same mosaic group it won’t become deselected. After the code we already wrote to duplicate a layer, add:

function mosaic(options) {
        //        ...
        
        //        Fit group to duplicates

        group.adjustToFit();

        //        Set selection to the group

        document.selectedLayers.clear();
        group.selected = true;
}

Try out the plugin again. You should find that editing a mosaic is much smoother now!

Improving The Interface

One other thing you might notice is the lack of synchronization between the display window and the interface inside it, in terms of them both becoming visible at the same time. This is due to the fact that when we display the window, the web interface isn’t guaranteed to have finished loading, so sometimes it’ll “pop” or “flash in” afterwards.

One way to fix this is by listening for when the web interface has finished loading, and only then show our window. There is a method, webView:didFinishNavigation:, that WKWebView will call when the current page has finished loading. We can use it to get exactly the notification we’re looking for.

Back in ui.js, we’ll extend the MochaJSDelegate instance we created to implement this method, which will in turn call the onLoadFinish argument we’ll pass to createWebView:

function createWebView(pageURL, onApplyMessage, onLoadFinish){
        const webView = WKWebView.alloc().init();

        //        Create delegate

        const delegate = new MochaJSDelegate({
                "webView:didFinishNavigation:": (webView, navigation) => {
                        onLoadFinish();
                },
                "userContentController:didReceiveScriptMessage:": (_, wkMessage) => {
                        const message = JSON.parse(wkMessage.body());
                        
                        onApplyMessage(message);
                }
        }).getClassInstance();

        //        Set load complete handler

        webView.navigationDelegate = delegate;

        //        Set handler for messages from script

        const userContentController = webView.configuration().userContentController();

        userContentController.addScriptMessageHandler_name(delegate, "sketchPlugin");

        //        Load page into web view

        webView.loadFileURL_allowingReadAccessToURL(pageURL, pageURL.URLByDeletingLastPathComponent());

        return webView;
};

And back in the loadAndShow method, we’ll adjust it so that it only shows the window once the web view has loaded:

function loadAndShow(baseURL, onApplyMessage){
        //        ...

        const window = createWindow();
        const webView = createWebView(pageURL, onApplyMessage, () => {
                showWindow(window);
        });

        window.contentView = webView;

        _window = window;
};

Bingo! Now our window displays only when the web view has finished loading, avoiding that annoying visual flicker.

Conclusion

Congratulations, you’ve built your first Sketch plugin! 🎉

If you’d like to install and play around with Mosaic, you can download the complete plugin from GitHub. And before you go, here are a few resources that might be handy during the rest of your journey:

  • developer.sketchapp.com
    The official resource regarding Sketch plugin development. Contains several useful guides, as well as an API reference for the Sketch JavaScript library.
  • sketchplugins.com
    A fantastic and helpful community of Sketch plugin developers. Great for getting all your burning questions answered.
  • github.com/sketchplugins/plugin-directory
    Official, central GitHub repository of Sketch plugins. You can submit your plugins here and share them with the rest of the Sketch community!
Smashing Editorial
(mb, yk, il)

Source: Smashing Magazine, How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 2)

How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 1)

dreamt up by webguru in Uncategorized | Comments Off on 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)

How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 1)

Matt Curtis



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).

The plugin we’ll be creating is called “Mosaic”. In part one, we’ll learn about the basic files that make up a Sketch plugin; we’ll 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’s main features, and at the end of it, you will also learn how to optimize the code and the way the plugin works.

I’ll also be sharing the plugin’s code (JS, HTML, CSS) and files which you’ll be able to examine and use for learning purposes.

What Are Sketch Plugins, And How Do They Work?

In Sketch, plugins are a way to add features and functionality that aren’t present in Sketch “out of the box.” Considering that there’s 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’d 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!

On the programming side, all Sketch plugins are written in JavaScript code. Well, actually, that’s not entirely true. It’s more accurate to say that most Sketch plugins are written in JavaScript, as it’s also possible to write a Sketch plugin in one of Apple’s programming languages, Objective-C and Swift, though even they require a small amount of JavaScript knowledge.

Don’t worry though. In this article, we’ll focus on how to build Sketch plugins using JavaScript, HTML, and CSS alone. We won’t be going over the basics of HTML, CSS, or JavaScript — this article assumes at least some knowledge and experience with all of these three. The MDN developer website provides a great place to learn more about web development.

Let’s Get Started!

Firstly, What Are We Making?

In this tutorial, I’ll 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.

The plugin we’ll be building is called Mosaic, and is effectively a “pattern generator”. Feed it your layers, tweak a few settings, and it’ll create a pattern:

Image showing the Mosaic plugin’s UI, and a few example patterns.

The Mosaic’s UI, and some examples of patterns made with it. (Large preview)

If you’d like to install and play around with Mosaic, you can download the completed plugin from GitHub.

A bit of history: Mosaic is inspired in large part by an old-school Adobe Fireworks plugin called Twist-and-Fade. 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 this one, where it created the frames for the two rotating elements in the cassette tape:

Image showing a music cassette tape with rotating drums
Animated cassette tape (source). (Large preview)

(Here’s a video demoing Twist and Fade if you’re interested in seeing exactly how it worked.)

For the purposes of this tutorial, we’ll 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:

  • Duplicate any Sketch layer (bitmap or vector) and tweak the duplicates’ layer’s position, rotation, and opacity. This will give us an introduction to manipulating layers using Sketch’s JavaScript APIs.
  • 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’s how we’ll gather the user’s inputs regarding how the user wants the resulting mosaic image to look.

Creating Our Base Plugin In Ten Seconds Flat

First, we’ll be creating the “base” (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’t have to — because Sketch can do it for us. After we’ve generated the template plugin, we’ll be able to customize it as we see fit.

There’s 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’m dealing with at a given moment. Here’s how it works:

With Sketch open, check the menu bar at the top of the screen and click Plugins -> Run Script. 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’re specifically interested in right now.

Clear whatever code is already in this dialog and replace it with the following demo code:

const UI = require("sketch/ui");

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!");

Next, hit Save Script as Plugin in the bottom-left of the window, enter whatever name you’d like for this plugin to have (in our case, this is “Mosaic”), then Save Script as Plugin once more.

Press “Save” in the bottom-left of the window and enter whatever name you’d like for this plugin to have. (Large preview)

Believe it or not, we’re already done — all that’s 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 “Mosaic”! Click on it!

(Large preview)

Congratulations, you’ve just written your first Sketch plugin!

What you should see after clicking “Mosaic” should be like the short video above, with an unobtrusive tooltip message appearing at the bottom of the screen beginning with the words “Hey there…” — 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 easy to paste, modify and test code without having to build a plugin from scratch. If you’re familiar with or have ever played with your browser’s web console, this is basically that. Having this tool in your back pocket as you build and test code is a must-have.

Let’s do a quick rundown of what the code you added does:

First, it imports the sketch/ui module of Sketch’s built-in JS library, and assigns it to the UI variable. This module contains a couple of useful interface-related methods, one of which we’ll use:

const UI = require("sketch/ui");

Next, it calls the message method (which is part of the sketch/ui module) with the string of text we want displayed in the tooltip we saw:

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!");

The message() method provides a great way to present an unobtrusive message to the user; it’s great for cases where you don’t need to steal focus (non-modal) and don’t need any fancy buttons or text fields. There’s also other ways to present common UI elements like alerts, prompts, and such, some of which we’ll be using as we build Mosaic.

Customizing Our Plugin’s Metadata

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’s metadata.

For this step, we’ll need to peek into what’s called the plugin bundle. When you hit save in the ‘Run Script’ window, Sketch saved your plugin as a folder named Mosaic.sketchplugin that you can find in the
~/Library/Application Support/com.bohemiancoding.sketch3/Plugins directory. That’s a bit long and annoying to remember; as a shortcut, you can also pull it up via Plugins -> Manage Plugins -> (right-click your plugin) -> Reveal Plugins Folder. Even though it appears in Finder as a single file, it’s actually a folder containing everything our plugin needs for Sketch to run it. The reason it appears as a single file despite being a folder is because when you first installed Sketch, Sketch registered the .sketchplugin extension as a “bundle” (a special kind of folder that appears as a file) and asked for it to automatically open in Sketch when opened.

Let’s take a peek inside. Right-click Mosaic.sketchplugin, then click “Show Package Contents”. Inside, you should see the following directory structure:

Contents/
└ Resources/
└ Sketch/
  └ manifest.json
  └ script.cocoascript

You might be wondering why there’s a file in there with the extension .cocoascript. Don’t worry — it’s just a regular JavaScript file, and only contains the code we entered earlier. Go ahead and rename this file to index.js, which will change the directory structure to look like the one below:

Contents/
└ Resources/
└ Sketch/
  └ manifest.json
  └ index.js

The most common way of organizing the files inside a plugin bundle is as follows: your code (JavaScript) and manifest.json belong in Sketch/, and resources (think images, audio files, text files, etc.) belong in Resources/.

Let’s start by tweaking the file named manifest.json. Open it inside your favorite code editor, such as Visual Studio Code or Atom.

You’ll see that at the moment there’s relatively little inside here, but we’ll add more soon. The plugin manifest serves primarily two purposes:

  1. First, it provides metadata that describes the plugin to the user — things like its name, version, the author’s name, and so on. Sketch uses this information in the Sketch -> Preferences -> Plugins dialog to create a listing and description for your plugin.
  2. Second, it also tells Sketch about how to get down to your business; that is, it tells Sketch how you’d like your plugin’s menu to look, what hotkeys to assign to your plugin, and where your plugin’s code lives (so Sketch can run it).

Considering purpose #1, describing the plugin to the user, you’ll probably notice that right now there’s no description or author given, which would be confusing for the user and make the plugin difficult to identify. Let’s fix that by adjusting the relevant keys’ values to:

{
        "description": "Generate awesome designs and repeating patterns from your layers!",
        "author": "=> Your name here <="
}

Next, let’s adjust the plugin’s identifier. This identifier uses what is called a “reverse domain notation” which is a really concise (or boring, take your pick) way to say “take your site’s domain, reverse the order, then put your product’s name at the end.” This will come out something like: com.your-company-or-your-name-its-not-that-big-a-deal.yourproduct.

You don’t have to stick to this naming convention — you can put whatever you want here, so long as it’s unique enough to avoid conflicts with other plugins (though it’s probably a good idea to stick to the RDN format, especially as it provides a simple, reusable system for your plugin identifiers).

To that effect, change your identifier to com.your-name.mosaic:

{
    "identifier": "com.your-name.mosaic"
}

I personally like to take all metadata related keys (title, author, identifier, etc.) and group them near the top of the manifest so they’re not spread out all over the place and help preserve my sanity when I need to find them.

Next, let’s take a look at the menu and commands keys. These two are responsible for telling Sketch what code to call and in response to what.

If you look at the menu key, you’ll see it contains a title key, whose value is the name our plugin will show up with in the Plugins menu. It also has an items key, which is a list of command identifiers:

{
  "menu": {
    "title": "Mosaic",
    "items": [
        "com.bohemiancoding.sketch.runscriptidentifier"
    ]
  }
}

Right now there’s only one command identifier in this list, "com.bohemiancoding.sketch.runscriptidentifier". Command identifiers always point to a command in the commands list. Right now our plugin only has one command, which is the one with this identifier:

{
  "commands": [
    {
      "script" : "script.cocoascript",
      "name" : "Mosaic",
      "handlers" : {
              "run" : "onRun"
      },
      "identifier" : "com.bohemiancoding.sketch.runscriptidentifier"
    }
  ]
}

Whenever you add a command identifier to a menu entry, Sketch will look up the command entry that has that identifier and will display the value of its name key (which in this case is “Mosaic”) and will show it in your plugin’s menu instead of the identifier.

As for the role commands play, we can think of a command entry as a way to tell Sketch what function in our plugin’s JavaScript code we want to run when that command is invoked, the “invocation” usually being the user’s click on the associated menu item. The command entry doesn’t do anything on its own, it’s just JSON — it simply provides a description to Sketch of where to look for the JavaScript it needs to run when the command is invoked.

So far, we’ve talked about what a command’s name and identifier keys do, but there are two other keys in a command that need to be addressed: script and handlers.

The script 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 Sketch/ folder, which is why for simplicity’s sake you’ll want to make sure all your JavaScript code lives somewhere under the Sketch/ folder. Before we move on from this key it’s important that you make sure you change this key’s value to index.js, just like we renamed the file earlier. Otherwise, Sketch won’t be able to find and run your JavaScript file.

The value of the handlers key is what Sketch looks at to determine what function in your JavaScript to call. Here, we only have one handler set: run, with the value onRun. run is the name of a predefined, built-in Sketch action. This run action will always be called when a user clicks a menu item that references this command. onRun is the name of a function in the auto-generated script.cocoascript file (which we renamed to index.js), and the function we want to be called when the run event occurs, i.e., when the user clicks the menu item.

In the example we have so far, this process plays out something like this:

  1. The user clicks our menu item.
  2. Sketch finds the command associated with that menu item.
  3. Sketch finds the script file the command refers to and runs it (which in this case means it executes the JavaScript in index.js).
  4. Since this command was invoked by a menu item click, it’s considered a run action. That means Sketch will look at the command’s handlers.run value for the function to call next, which in this case is onRun.
  5. Sketch calls the onRun function.

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’t be using any of these other actions. (You can learn more about actions and how they work in the Action API help page.)

Before we move on from this manifest, we’ll want to make two other tweaks. Right now, our menu has the structure:

Mosaic
└ Mosaic

Image showing Mosaic menu item redundantly nested inside another menu named Mosaic

Pretty redundant, right? (Large preview)

…which 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 isRoot: true to our menu:

{
  "menu": {
    "title" : "Mosaic",
    "items" : [
            "com.bohemiancoding.sketch.runscriptidentifier"
    ],
    "isRoot": true
}
}

This tells Sketch to place the first level of menu items directly under the Plugins menu, rather than nesting them under the menu’s title.

Hit save and return to Sketch. You should see that now Mosaic -> Mosaic has been replaced by just Mosaic — perfect!

Image showing the Mosaic plugin’s UI

Mosaic’s UI. (Large preview)

As for our second tweak, let’s 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 "open":

{
  "commands": [
    {
            ...
            "identifier" : "open"
    }
],

"menu": {
    ...
    "items" : [
            "open"
    ]
  }
}

Before we move on, it’s useful to note that menus can contain also other menus. You could easily create a sub-menu by nesting another { title: ..., items: ... } entry inside another menu’s items list:

{
  "menu": {
    "title" : "Mosaic",
    "items" : [
      "open",
      {
        "title" : "I'm a sub-menu!",
        "items" : [
                "another-command-identifier"
        ]
      }
    ]
  }
}

Building The Plugin’s User Interface

So far, we’ve written some demo code and customized our plugin’s manifest. We’ll now move on to creating its user interface, which is essentially a web page embedded in a window (similarly to the browsers you’re familiar with using):

The plugin’s window. (Large preview)

Image showing the components making up our plugin’s interface: window and web view

The components making up our plugin. (Large preview)

The Window

Mosaic’s user interface design has its own window, which we can consider the most basic component; we’ll start with it. In order to create and display a window, we’ll have to make use of a class that’s built into macOS by default, called NSWindow. Over the remainder of this tutorial, we’ll actually be doing this quite a bit (using built-in APIs like NSWindow) which might seem a little daunting if you’re unfamiliar with it, but don’t worry — I’ll explain everything along the way!

Note: While we’re talking about built-in APIs, the reason we’re able to use this class at all is thanks to a bridge 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.

Open Sketch/index.js in your code editor, delete what’s already there, and paste in the following:

function onRun(context){
  const window = NSWindow.alloc().initWithContentRect_styleMask_backing_defer_(
    NSMakeRect(0, 0, 145, 500),
    NSWindowStyleMaskClosable | NSWindowStyleMaskTitled | NSWindowStyleMaskResizable,
    NSBackingStoreBuffered,
    false
  );

  window.releasedWhenClosed = false;

  window.makeKeyAndOrderFront(nil);
};

Let’s take a look at what this first bit of code does:

function onRun(context){

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 onRun? (If you need a refresher, revisit that part above, then come back.) All this bit does is create that function. You’ll also notice our onRun function takes a context argument. This is an argument Sketch will call your command handlers with that can provide us with certain information. Later on, we’ll use it in order to get the URL of our plugin bundle on the user’s computer.

const window = NSWindow.alloc().initWithContentRect_styleMask_backing_defer(
  NSMakeRect(0, 0, 145, 500),
  NSWindowStyleMaskClosable | NSWindowStyleMaskTitled | NSWindowStyleMaskResizable,
  NSBackingStoreBuffered,
  false
);

Here we’re actually doing a few things:

  1. First, we call alloc() on NSWindow; this basically means “set aside some memory for an instance of NSWindow”. It’s sufficient to know that you’ll have to do this for every instance of a native class you want to create. The alloc method is available in every native class.
  2. Next, we call NSWindow’s initializer method (that is, the method that actually creates an instance of NSWindow), which is named initWithContentRect:styleMask:backing:defer:. You’ll notice that’s different from what we call in our code above — it’s got a bunch of colons (:) between every argument. Since we can’t use that syntax in JavaScript, Sketch conveniently renames it to something we can actually use by replacing the colons with underscores, which is how we get its JS name: initWithContentRect_styleMask_backing_defer.
  3. Next, we pass in each of the arguments the method needs. For the first argument, contentRect, we supply a rectangle with a size large enough for our user interface.
  4. For styleMask, we use a bitmask which says that we want our window to have a close button, a title bar, and to be resizable.
  5. The next two arguments, backing and defer, are always going to be set to NSBackingStoreBuffered and false, so we don’t really need to worry about them. (The documentation for this method goes into further detail as to why this is.)
window.releasedWhenClosed = false;

window.makeKeyAndOrderFront(null);

Here we set NSWindow’s releasedWhenClosed property to false, which means: “Hey! don’t delete this window from memory just because the user closes it.” Then we call makeKeyAndOrderFront(null) which means: “Move this window to the forefront, and give it keyboard focus.”

Web View: The Interface

To make things easier, I’ve already written the HTML and CSS code of the plugin’s web user interface we’re going to be using; the only remaining code we’re going to have to add to it will deal with making sure we’re able to communicate between it and our Sketch plugin code.

Next, download the HTML and CSS code. Once you’ve downloaded it, extract it, then move the folder named “web-ui” into our plugin’s Resources folder.

Note: 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’s core features; but there are a ton of tutorials on the web on this topic, should you want to learn more.

If you run our plugin now, you’ll see that it shows a window — yay, progress! But it’s 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’ll need to use another native class, WKWebView, which is a view specifically made for displaying web content.

We’ll add the code needed to create our WKWebView beneath the code we wrote for our window:

function onRun(context){
    //        Create window

    const window = NSWindow.alloc().initWithContentRect_styleMask_backing_defer(
            NSMakeRect(0, 0, 145, 500),
            NSWindowStyleMaskClosable | NSWindowStyleMaskTitled | NSWindowStyleMaskResizable,
            NSBackingStoreBuffered,
            false
    );

    window.releasedWhenClosed = false;

    //        Create web view, and set it as the view for our window to display

    const webView = WKWebView.alloc().init();

    window.contentView = webView;

    //        Load our UI into the web view

    const webUIFolderURL = context.scriptURL
            .URLByDeletingLastPathComponent()
            .URLByAppendingPathComponent("../Resources/web-ui/");

    const indexURL = webUIFolderURL.URLByAppendingPathComponent("index.html");

    webView.loadFileURL_allowingReadAccessToURL(indexURL, webUIFolderURL);

    //        Make window key and move to front

    window.makeKeyAndOrderFront(nil);
};

If we run our plugin now, we’ll see that now we’ve got a window open that displays our web user interface. Success!

Again, before moving on, let’s examine what the code we added does:

const webView = WKWebView.alloc().init();

This should look familiar — it’s basically the same as what we did when we made our NSWindow: allocate memory for a web view, then initialize it.

window.contentView = webView;

This line of code tells our window to display the web view we just made.

const webUIFolderURL = context.scriptURL
  .URLByDeletingLastPathComponent()
  .URLByAppendingPathComponent("../Resources/web-ui/");

Here our goal is to create a URL that points to the web-ui folder that we made earlier. In order to get that URL, we need some way to figure out where our plugin’s bundle is in the user’s filesystem. Here we use the context.scriptURL property, which gives us the URL of the currently running script. However, this doesn’t give us a JavaScript String as you might expect, but an instance of a native class, NSURL, that has a few methods on it that make manipulating URL strings easier.

We need to turn what context.scriptURL gives us —

file://path-to-your-plugin/Contents/Sketch/index.js

— into:

file://path-to-your-plugin/Contents/Resources/web-ui/

Step by step:

  1. Calling URLByDeletingLastPathComponent() the first time gives us file://path-to-your-plugin/Contents/Sketch/
  2. Calling URLByDeletingLastPathComponent() again gives us file://path-to-your-plugin/Contents/
  3. And lastly, adding Resources/web-ui/ onto the end using URLByAppendingPathComponent("Resources/web-ui/") gives us file://path-to-your-plugin/Contents/Resources/web-ui/

We also need to create a second URL that points directly to the index.html file:

const indexURL = webUIFolderURL.URLByAppendingPathComponent("index.html");

Finally, we tell our web view to load index.html and give it access to the contents of the web-ui folder:

webView.loadFileURL_allowingReadAccessToURL(indexURL, webUIFolderURL);

Alright. So far, we have a window that displays our web user interface, just like we wanted. However, it’s not quite yet complete — our original design doesn’t have a title bar (or “chrome”), but our current window does. There’s also the fact that when we click inside a Sketch document, that document moves in front of our window, which isn’t what we want — we want the user to be able to interact with the plugin window and the Sketch document without having to constantly refocus from one window to the other.

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.

Note: Like before, all of the properties and methods we’re using below are documented in NSWindow’s documentation page.

window.titlebarAppearsTransparent = true;
window.titleVisibility = NSWindowTitleHidden;

These next two lines of code will remove the window buttons (also known as “traffic lights” in MacOS lingo) that we don’t need — “zoom” and “minimize” — leaving only the “close” button:

window.standardWindowButton(NSWindowZoomButton).hidden = true;
window.standardWindowButton(NSWindowMiniaturizeButton).hidden = true;

While we’re at it, let’s also go ahead and change the window’s background color to match that of our web UI:

window.backgroundColor = NSColor.colorWithRed_green_blue_alpha(1, 0.98, 0.98, 1);

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’s window disappearing. We can use a special type of NSWindow for this, called NSPanel, which is able to “stay on top” of other windows. All that’s needed for this is to change NSWindow to NSPanel, which is a single-line code change:

const window = NSPanel.alloc().initWithContentRect_styleMask_backing_defer(

Now we tell our panel window to float (stay on top of all others), and only take keyboard/mouse focus when necessary:

window.floatingPanel = true;
window.becomesKeyOnlyIfNeeded = true;

We can also tweak our window so that it automatically reopens in the last position it was at:

window.frameAutosaveName = "mosaic-panel-frame";

This line basically says “remember this window’s position by saving it with Sketch’s preferences under the key mosaic-panel-frame”.

All together, we now have the following code:

function onRun(context){
    //        Create window

    const window = NSPanel.alloc().initWithContentRect_styleMask_backing_defer(
            NSMakeRect(0, 0, 145, 500),
            NSWindowStyleMaskClosable | NSWindowStyleMaskTitled | NSWindowStyleMaskResizable,
            NSBackingStoreBuffered,
            false
    );

    window.becomesKeyOnlyIfNeeded = true;
    window.floatingPanel = true;

    window.frameAutosaveName = "mosaic-panel-frame";

    window.releasedWhenClosed = false;

    window.standardWindowButton(NSWindowZoomButton).hidden = true;
    window.standardWindowButton(NSWindowMiniaturizeButton).hidden = true;

    window.titlebarAppearsTransparent = true;
    window.titleVisibility = NSWindowTitleHidden;

    window.backgroundColor = NSColor.colorWithRed_green_blue_alpha(1, 0.98, 0.98, 1);

    //        Create web view, and set it as the view for our window to display

    const webView = WKWebView.alloc().init();

    window.contentView = webView;

    //        Load our UI into the webview

    const webUIFolderURL = context.scriptURL
            .URLByDeletingLastPathComponent()
            .URLByAppendingPathComponent("../Resources/web-ui/");

    const indexURL = webUIFolderURL.URLByAppendingPathComponent("index.html");

    webView.loadFileURL_allowingReadAccessToURL(indexURL, webUIFolderURL);

    //        Make window key and move to front

    window.makeKeyAndOrderFront(nil);
};

Organizing The Code

Before we move to the next part, it’s a good idea to organize our code so that it’s easier to navigate and tweak. Since we still have a lot more code to add and we want to avoid index.js becoming a messy dumping ground for all of our code, let’s split things up a bit and move our UI-specific code into a file named ui.js, under the Sketch folder. We’ll also extract some of the UI tasks we do, like creating the web view and window, into their own functions.

Create a new file called ui.js and insert the code below inside it:

//        Private

var _window;

function createWebView(pageURL){
        const webView = WKWebView.alloc().init();

        webView.loadFileURL_allowingReadAccessToURL(pageURL, pageURL.URLByDeletingLastPathComponent());

        return webView;
};

function createWindow(){
        const window = NSPanel.alloc().initWithContentRect_styleMask_backing_defer(
                NSMakeRect(0, 0, 420, 646),
                NSWindowStyleMaskClosable | NSWindowStyleMaskTitled | NSWindowStyleMaskResizable,
                NSBackingStoreBuffered,
                false
        );

        window.becomesKeyOnlyIfNeeded = true;
        window.floatingPanel = true;

        window.frameAutosaveName = "mosaic-panel-frame";

        window.releasedWhenClosed = false;

        window.standardWindowButton(NSWindowZoomButton).hidden = true;
        window.standardWindowButton(NSWindowMiniaturizeButton).hidden = true;

        window.titlebarAppearsTransparent = true;
        window.titleVisibility = NSWindowTitleHidden;
        
        window.backgroundColor = NSColor.colorWithRed_green_blue_alpha(1, 0.98, 0.98, 1);

        return window;
};

function showWindow(window){
        window.makeKeyAndOrderFront(nil);
};

//        Public

function loadAndShow(baseURL){
        if(_window){
                showWindow(_window);

                return;
        }

        const pageURL = baseURL
                .URLByDeletingLastPathComponent()
                .URLByAppendingPathComponent("../Resources/web-ui/index.html");

        const window = createWindow();
        const webView = createWebView(pageURL);

        window.contentView = webView;
        
        showWindow(_window);

        _window = window;
};

function cleanup(){
        if(_window){
                _window.orderOut(nil);
                _window = null;
        }
};

//        Export

module.exports = { loadAndShow, cleanup };

There are a couple of key changes we made here that are important to note. Besides the fact that we’ve created specific functions for the creation, hiding and showing of our window and its web view, we’ve also modularized our user interface code.

Notice the module.exports = { loadAndShow, cleanup } 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’t want to them to worry about), which means we now have a more organized API for interacting with, showing and destroying our UI.

Recommended reading: Unleashing The Full Potential Of Symbols In Sketch

Let’s see what this looks like in practice. Back in index.js, remove the old code and add the following:

const UI = require("./ui");

function onRun(context){
        UI.loadAndShow(context.scriptURL);
};

We’re using a special function that Sketch automatically makes available to us, require, to import our ui.js code and assign the returned module to the UI variable. This gives us access to a simplified API for triggering our user interface. Things are much tidier now and easy to find!

Conclusion

Well done — you’ve come far! In the next part of this tutorial, we’ll give our web UI the ability to send us a message when the “Apply” button is clicked, and we’ll focus on the main plugin functionality: actually generating layer mosaics!

Smashing Editorial
(mb, yk, il)

Source: Smashing Magazine, How To Build A Sketch Plugin With JavaScript, HTML And CSS (Part 1)

Collective #529

dreamt up by webguru in Uncategorized | Comments Off on Collective #529



C523_divi

Our Sponsor

The Future of WYSIWYG

Divi takes WordPress to a whole new level with its incredibly advanced visual builder technology. You’ve never built a WordPress website like this before.

Try it

















C529_lunacy4

Lunacy 4.0

Lunacy 4.0 is a full-fledged graphic editor with icons, photos, masked images and illustrations integrated and ready to use. It’s Sketch for Windows with design assets.

Check it out



C529_templates

CodeSandbox Templates

If you work with CodeSandbox, then your prototyping workflow will certainly benefit from the new option to create and use templates.

Read it




Collective #529 was written by Pedro Botelho and published on Codrops.


Source: Codrops, Collective #529

How To Integrate Social Media Into Mobile Web Design

dreamt up by webguru in Uncategorized | Comments Off on How To Integrate Social Media Into Mobile Web Design

How To Integrate Social Media Into Mobile Web Design

How To Integrate Social Media Into Mobile Web Design

Suzanne Scacca



There are a number of reasons why social media is a popular form of socialization and communication today. For starters, it gives us a chance to connect with exponentially more people than in-person communities allow for (which is fantastic for both consumer and business). It’s also encouraged a new style of communication where brevity and visual storytelling rules.

So, why aren’t we using more social media-like features in mobile web design?

To be clear, I’m not referring to the kinds of social media elements we already see on websites, like:

  • Social logins
  • Social follow icons
  • Social share icons
  • Social feeds
  • <li.Instagram galleries

  • YouTube embeds
  • Pinnable images.

I’m talking about drawing inspiration from the abbreviated way in which we talk to others there. After all, mobile websites, PWAs, and native apps give us very little space to tell a story and engage an audience with it. Adopting a social media-inspired design would be helpful in quickly getting our message across on mobile screens.

These elements, in particular, are worth exploring in your mobile design work.

1. Use a Notification Symbol to Direct Visitors to Action

You don’t have much space or time to waste on mobile, so why literally spell out what kind of action you want visitors to take? A lot of the symbols we use on social media can easily be repurposed for mobile websites.

One such symbol is a notification ticker, a lot like the ones we’re accustomed to seeing in the header or footer of social media apps when there’s a message or reminder we need to be made aware of. Like this blue one found in the bottom bar on Facebook:

Facebook menu notification

Facebook uses a small blue dot to signify that a new notification awaits you. (Source: Facebook) (Large preview)

On occasion, the Red Bull website will put a similar-looking red ticker on its hamburger menu icon (this one is flashing though):

Red Bull menu notification

The Red Bull mobile website displays a flashing red ticker on the hamburger menu. (Source: Red Bull) (Large preview)

This flashing notification has nothing to do with alerting me to account activity (because I’m not logged into the website). However, it does certainly draw my eye to the navigation before anything else.

If you have something pertinent you want visitors to see in the navigation or header of the site, a notification icon is a good way to grab their attention fast. And it doesn’t have to be a colored dot the way Facebook or Red Bull have handled it. You could also use a number counter like the ones that appear when items sit in a shopping cart or emails in an inbox.

2. Boost Branding with Hashtags and Handles

The way we talk to one another on social media is quite unique. Not only do many people use acronyms to truncate how much they say in a tiny amount of space, but we’ve also adopted a quicker way of getting our messages in front of target users.

Take, for example, this message I’ve written on Twitter about a post Jad Joubran recently wrote about PWAs.

Basic Twitter message

Example of a basic message shared on Twitter. (Source: Twitter) (Large preview)

Now, I could leave my message as is and hope that Jad runs across the mention or that people interested in learning how to build PWAs find it. But there’s just too much noise on social media, which is why the usage of the handle (@) and hashtag (#) symbols has become helpful in getting our messages in front of the right people:

Twitter message with hashtags and handles

Example of a Twitter message with hashtags and handles. (Source: Twitter) (Large preview)

Take a look at the message above and note the differences. First, I’ve included hashtags in this post for #pwa and #progressivewebapp. That way, if someone is interested in related topics, it’ll be easier to locate this post now.

In addition, I’ve tagged Jad. This way, he’ll see my shout-out. I’ve also tagged Smashing Magazine since that’s the magazine in which the article appeared. This is good for them since it’ll increase the visibility of the brand while helping people who encounter the post make a direct connection between #pwa and @smashingmag.

The hashtag and handle have become such an innate part of how we communicate online, that I’m surprised more brands don’t use them on their websites. And I’m not talking about listing social media handles on a page. I mean actually integrating branded hashtags or handles within their designs as eos does here:

eos website hashtag

eos includes a branded hashtag within an image. (Source: eos) (Large preview)

A couple of years back, I saw more hashtags and handles incorporated into web designs. Today, I don’t see it as much.

My guess is that designers have moved away from placing handles and hashtags into designs since visitors can’t readily copy or click and do anything with them. But there is value in placing branded handles and hashtags into your designs on mobile.

Think of it as a watermark for your brand images. Like the example above, people won’t just walk away thinking about how cool that chapstick “egg” looks. They’ll take note of the hashtag sitting perpendicular to it, too. And for those that are on their way to becoming loyal eos customers, that hashtag will stick with them.

3. Add Trust Marks into the Design

There are certain social media platforms that give brands and public figures the ability to verify their identity. That way, anyone who wants to follow them will know who the person on the other side of the profile actually is (instead of, say, a bot or someone pretending to be them).

Rather than slap a “Verified Profile” label on these profiles, platforms like Twitter and Instagram use a tiny symbol that lets people know it’s okay to trust that the user is who they say they are. And it comes in the form of a small blue checkmark next to their username.

Instagram trust mark

Instagram uses a small blue checkmark to let people know it’s a real profile.. (Source: Instagram) (Large preview)

Trust marks are already used on the web to let visitors know when a site is safe enough to engage with and make a purchase through. However, they usually come in the form of big security provider logos like Norton Security that build consumer trust just before checkout.

Instead, you should find ways to leverage smaller trust marks to reinforce trust throughout the entire experience. Here’s how Sephora does this for its store:

Sephora trust marks

Sephora uses the same trust mark stamps to call attention to more notable products. (Source: Sephora) (Large preview)

There are two trust marks present in the screenshot above.

The GlamGlow product has a red-and-white stamp on it that says “Allure Readers’ Choice Award Winner”. You already know how much people trust in the reviews and recommendations of other consumers, so you can imagine what this sort of symbol does to pique their interest and build trust in the product.

The Farmacy product has a green-and-white stamp on it that says “Clean at Sephora”. This is a label Sephora slaps onto any product that’s free of questionable ingredients. For customers that are extra conscious about where their products come from and what they’re made from, this symbol will quickly direct their attention to the kinds of products they’re looking for.

4. Lead with a Singular Memorable Image

When you scroll through your social media feed, you’re not likely to see post after post with galleries of images attached to them. Unless, that is, someone just went on vacation and they want to show off everything they did or saw.

Usually, most of the social media posts you encounter keep it simple: a text message with a complementary image attached to it. And if the image didn’t come courtesy of the link they’re promoting, then they’ll attach something equally as memorable and relevant.

Social media feeds are full of posts from the people we’re connected to, so it’s important to have one strong image that helps ours stand out among the crowd. A mobile website would benefit from that same approach.

In this case, I’m going to point you to this promotional element on the West Elm mobile website for the approaching July 4th holiday in the United States:

West Elm GIF

A sparkler GIF used to promote West Elm’s July 4th promotion. (Source: West Elm) (Large preview)

Since sparklers are a big part of how we celebrate Independence Day, this sparkler GIF is an incredibly effective, yet simplistic way to draw visitors’ attention to the relevant promotion.

Not only is the GIF memorable, but it’s very relevant to what online shoppers are looking for at the moment. Plus, it all fits within the space of a smartphone screen, which makes it a more convenient way to read about the promotion before clicking through to claim the 20% offer.

5. Make Your Brand Meme-like

When brands use social media, they have to think about more than just pairing a carefully crafted message with an engaging image. They also have to consider the consistency of their posts. In some cases, brands will use color to tie their posts together. Others will rely on the messaging itself to create a cohesive identity.

Then, there are brands that have turned their brands into memes. Old Spice’s “The Man Your Man Could Smell Like” or Terry Crews campaigns are some of the more successful and memorable examples of how to do this.

It’s not just brands like Old Spice or Dollar Shave Club whose humorous advertising become memes that can pull this off. Take a brand like Oreo, for example.

Oreo is a household name. And, yet, would anyone have considered sharing posts about Oreo cookies with others? Before 2013, I’m not so sure. After Oreo published its now-famous post during the Super Bowl blackout, though, things changed.

Oreo Super Bowl meme

Oreo gave a boost to its already iconic image during the 2013 Super Bowl. (Source: Oreo) (Large preview)

This might not be a meme on the level of Old Spice. However, it most certainly is on a similar level of trendiness and shareability.

Oreo has continued to find ways to turn its iconic Oreo cookie image into a meme-like status symbol.

The Oreo cookie meme

Oreo has turned its cookie into a meme of its own. (Source: Oreo) (Large preview)

You know how everyone and their mother has been sharing cookies notices on their websites, thanks to GDPR? Oreo utilizes its brand image to have a little fun with it.

As you can see here, the drop-down creatively uses the Oreo image while also playing on the word “cookie” itself. It’s a fantastic example of how to be creative, even in the smallest of spaces and for a limited amount of time. It certainly grabbed my attention enough to deem it worth sharing here.

6. Embrace the Selfie

I recognize that selfies don’t always have the most positive of connotations, but it’s really difficult to have a discussion about using social media-like elements in design without addressing them. So, I’m going to go out on a limb here and say that mobile websites would benefit from including selfie-like portraits… when not used in a phony or obnoxious context.

I mean, that’s the main complaint about selfies, right? They’re staged and not at all realistic of a person’s actual life. Or they’re not staged because they’re taken at the worst possible time and in the least professional of contexts.

That said, selfies do stand out from super-glossy headshots and other staged company photos. And when you present them on a mobile interface, it gives a website (and the brand behind it) a more social-like presence. In other words, you’re inviting people to engage with you, not to just casually glance through copy and photos on their way to mindlessly converting. There’s something more to this experience when a selfie is present.

One example of this that I particularly love comes from Aleyda Solis, an SEO consultant and author.

Aleyda Solis selfies

Aleyda Solis uses selfies as promotional images on her website. (Source: Aleyda Solis) (Large preview)

Just looking at the above screenshot out of context, I feel like I’m scrolling through her personal feed on Instagram. And because I’m so accustomed to seeing interesting-looking photos on Instagram and then immediately seeking out the captions for more information on them, I’m compelled to do the same here. Talk about Pavlov’s dog, right?

For brands with a recognizable figure or team behind them, a selfie could be a great way to up your visitors’ engagement with a website. Just make sure it paints the person or people in a positive light.

7. Use Filters to Tell Individual Stories

Way, way back in the day, we used to take photos using film and pray that the images came out okay and would be acceptable enough to frame. Then, we got digital cameras that allowed us to see what our pictures looked like in real-time, though it often led to too many rounds of picture-taking to try to capture the perfect lighting or shot.

Nowadays, our smartphones and social media platforms make all of this so much simpler. We can take photos wherever we are, whenever we feel like it and many of the tools we need to clean up a shot and apply an attractive filter are already built into our apps.

Needless to say, filters are a big part of what makes sharing photos on social media so appealing to users.

Now, it’s not as though brands haven’t utilized filters before to spruce up their branding or photography. However, have you considered what using different filters in different contexts could do for your mobile website? They could effectively turn different pages or products into unique experiences or stories of their own.

Abel is an online perfume shop that sells a small collection of perfumes, with scents like pink iris, red santal and green cedar. Notice the trend? Each scent is described by a distinct color, as this example of golden neroli demonstrates:

Abel’s golden neroli scent

An example of one of Abel’s product pages. (Source: Abel) (Large preview)

Although the perfume itself is clear in color, the design of this image gives the bottle a golden color to match its namesake. Now, here’s where it gets interesting:

Abel golden filter

Abel applies a color-specific filter to images on each product page. (Source: Abel) (Large preview)

This is one of the images lower on the product’s page. While I suspect the sunset captured in the photo is real, it’s obvious that a filter has been applied to lighten and warm the overall tone of the image.

The same is done with another image further down on the page:

Abel image filters

Another filter-enhanced image on Abel’s product page. (Source: Abel) (Large preview)

Again, the emphasis is placed on making the image lighter and warmer in tone. And if you look at the snippet of text just above, you can see that Abel even compares the sensation of this perfume to “the warm warm blanket of the night falling over Lisbon”.

So, rather than get hung up on designing your mobile website with one singular color palette, consider the individual emotion and identity of each of its pages or elements. You may find that filters and colors help you tell a stronger and unique story for each than the words alone could.

The Benefits Of A Social Media-Inspired Mobile Design

As we attempt to unlock more ways to design for mobile without stifling creativity or overwhelming the user experience, I think it’s important to look to other kinds of media for inspiration.

It’s not like we haven’t already found ways to tell a visual story through design on smaller or more isolated screens. Just look at video game design. Social media is another type of media that’s successfully carved out a style of its own. And considering how addictive and engaging social media often is for consumers, it’s a good platform to turn to for design inspiration.

Smashing Editorial
(ra, yk, il)

Source: Smashing Magazine, How To Integrate Social Media Into Mobile Web Design

Using Slots In Vue.js

dreamt up by webguru in Uncategorized | Comments Off on Using Slots In Vue.js

Using Slots In Vue.js

Using Slots In Vue.js

Joseph Zimmerman



With the recent release of Vue 2.6, the syntax for using slots has been made more succinct. This change to slots has gotten me re-interested in discovering the potential power of slots to provide reusability, new features, and clearer readability to our Vue-based projects. What are slots truly capable of?

If you’re new to Vue or haven’t seen the changes from version 2.6, read on. Probably the best resource for learning about slots is Vue’s own documentation, but I’ll try to give a rundown here.

What Are Slots?

Slots are a mechanism for Vue components that allows you to compose your components in a way other than the strict parent-child relationship. Slots give you an outlet to place content in new places or make components more generic. The best way to understand them is to see them in action. Let’s start with a simple example:

// frame.vue
<template>
  
</template>

This component has a wrapper div. Let’s pretend that div is there to create a stylistic frame around its content. This component is able to be used generically to wrap a frame around any content you want. Let’s see what it looks like to use it. The frame component here refers to the component we just made above.

// app.vue
<template>
  <frame><img src="an-image.jpg"></frame>
</template>

The content that is between the opening and closing frame tags will get inserted into the frame component where the slot is, replacing the slot tags. This is the most basic way of doing it. You can also specify default content to go into a slot simply by filling it in:

// frame.vue
<template>
  
This is the default content if nothing gets specified to go here
</template>

So now if we use it like this instead:

// app.vue
<template>
  <frame />
</template>

The default text of “This is the default content if nothing gets specified to go here” will show up, but if we use it as we did before, the default text will be overridden by the img tag.

Multiple/Named Slots

You can add multiple slots to a component, but if you do, all but one of them is required to have a name. If there is one without a name, it is the default slot. Here’s how you create multiple slots:

// titled-frame.vue
<template>
  

Title

This is the default content if nothing gets specified to go here
</template>

We kept the same default slot, but this time we added a slot named header where you can enter a title. You use it like this:

// app.vue
<template>
  <titled-frame>
    <template v-slot:header>
      <!-- The code below goes into the header slot -->
      My Image’s Title
    </template>
    <!-- The code below goes into the default slot -->
    <img src="an-image.jpg">
  </titled-frame>
</template>

Just like before, if we want to add content to the default slot, just put it directly inside the titled-frame component. To add content to a named slot, though, we needed to wrap the code in a template tag with a v-slot directive. You add a colon (:) after v-slot and then write the name of the slot you want the content to be passed to. Note that v-slot is new to Vue 2.6, so if you’re using an older version, you’ll need to read the docs about the deprecated slot syntax.

Scoped Slots

One more thing you’ll need to know is that slots can pass data/functions down to their children. To demonstrate this, we’ll need a completely different example component with slots, one that’s even more contrived than the previous one: let’s sorta copy the example from the docs by creating a component that supplies the data about the current user to its slots:

// current-user.vue
<template>
  <span>
    <slot v-bind:user="user">
      {{ user.lastName }}
    </slot>
  </span>
</template>


export default {
  data () {
    return {
      user: ...
    }
  }
}

This component has a property called user with details about the user. By default, the component shows the user’s last name, but note that it is using v-bind to bind the user data to the slot. With that, we can use this component to provide the user data to its descendant:

// app.vue
<template>
  <current-user>
    <template v-slot:default="slotProps">{{ slotProps.user.firstName }}</template>    
  </current-user>
</template>

To get access to the data passed to the slot, we specify the name of the scope variable with the value of the v-slot directive.

There are a few notes to take here:

  • We specified the name of default, though we don’t need to for the default slot. Instead we could just use v-slot="slotProps".
  • You don’t need to use slotProps as the name. You can call it whatever you want.
  • If you’re only using a default slot, you can skip that inner template tag and put the v-slot directive directly onto the current-user tag.
  • You can use object destructuring to create direct references to the scoped slot data rather than using a single variable name. In other words, you can use v-slot="{user}" instead of v-slot="slotProps" and then you can use user directly instead of slotProps.user.

Taking those notes into account, the above example can be rewritten like this:

// app.vue
<template>
  <current-user v-slot="{user}">
    {{ user.firstName }}
  </current-user>
</template>

A couple more things to keep in mind:

  • You can bind more than one value with v-bind directives. So in the example, I could have done more than just user.
  • You can pass functions to scoped slots too. Many libraries use this to provide reusable functional components as you’ll see later.
  • v-slot has an alias of #. So instead of writing v-slot:header="data", you can write #header="data". You can also just specify #header instead of v-slot:header when you’re not using scoped slots. As for default slots, you’ll need to specify the name of default when you use the alias. In other words, you’ll need to write #default="data" instead of #="data".

There are a few more minor points you can learn about from the docs, but that should be enough to help you understand what we’re talking about in the rest of this article.

What Can You Do With Slots?

Slots weren’t built for a single purpose, or at least if they were, they’ve evolved way beyond that original intention to be a powerhouse tool for doing many different things.

Reusable Patterns

Components were always designed to be able to be reused, but some patterns aren’t practical to enforce with a single “normal” component because the number of props you’ll need in order to customize it can be excessive or you’d need to pass large sections of content and potentially other components through the props. Slots can be used to encompass the “outside” part of the pattern and allow other HTML and/or components to placed inside of them to customize the “inside” part, allowing the component with slots to define the pattern and the components injected into the slots to be unique.

For our first example, let’s start with something simple: a button. Imagine you and your team are using Bootstrap*. With Bootstrap, your buttons are often strapped with the base `btn` class and a class specifying the color, such as `btn-primary`. You can also add a size class, such as `btn-lg`.

* I neither encourage nor discourage you from doing this, I just needed something for my example and it’s pretty well known.

Let’s now assume, for simplicity’s sake that your app/site always uses btn-primary and btn-lg. You don’t want to always have to write all three classes on your buttons, or maybe you don’t trust a rookie to remember to do all three. In that case, you can create a component that automatically has all three of those classes, but how do you allow customization of the content? A prop isn’t practical because a button tag is allowed to have all kinds of HTML in it, so we should use a slot.

<!-- my-button.vue -->
<template>
  <button class="btn btn-primary btn-lg">
    <slot>Click Me!</slot>
  </button>
</template>

Now we can use it everywhere with whatever content you want:

<!-- somewhere else, using my-button.vue -->
<template>
  <my-button>
    <img src="https//vuejs.org/img/awesome-icon.jpg"> SMASH THIS BUTTON TO BECOME AWESOME FOR ONLY $500!!!
  </my-button>
</template>

Of course, you can go with something much bigger than a button. Sticking with Bootstrap, let’s look at a modal, or least the HTML part; I won’t be going into functionality… yet.

<!-- my-modal.vue -->
<template>

Now, let’s use this:

<!-- somewhere else, using my-modal.vue -->
<template>
  <my-modal>
    <template #header><!-- using the shorthand for `v-slot` -->
      <h5>Awesome Interruption!</h5>
    </template>
    <template #body>
      <p>We interrupt your use of our application to
      let you know that this application is awesome 
      and you should continue using it every day for 
      the rest of your life!</p>
    </template>
    <template #footer>
      <em>Now back to your regularly scheduled app usage</em>
    </template>
  </my-modal>
</template>

The above type of use case for slots is obviously very useful, but it can do even more.

Reusing Functionality

Vue components aren’t all about the HTML and CSS. They’re built with JavaScript, so they’re also about functionality. Slots can be useful for creating functionality once and using it in multiple places. Let’s go back to our modal example and add a function that closes the modal:

<!-- my-modal.vue -->
<template>

Now when you use this component, you can add a button to the footer that can close the modal. Normally, in the case of a Bootstrap modal, you could just add data-dismiss="modal" to a button, but we want to hide Bootstrap specific things away from the components that will be slotting into this modal component. So we pass them a function they can call and they are none the wiser about Bootstrap’s involvement:

<!-- somewhere else, using my-modal.vue -->
<template>
  <my-modal>
    <template #header><!-- using the shorthand for `v-slot` -->
      <h5>Awesome Interruption!</h5>
    </template>
    <template #body>
      <p>We interrupt your use of our application to
      let you know that this application is awesome 
      and you should continue using it every day for 
      the rest of your life!</p>
    </template>
    <!-- pull in `closeModal` and use it in a button’s click handler -->
    <template #footer="{closeModal}">
      <button @click="closeModal">
        Take me back to the app so I can be awesome
      </button>
    </template>
  </my-modal>
</template>

Renderless Components

And finally, you can take what you know about using slots to pass around reusable functionality and strip practically all of the HTML and just use the slots. That’s essentially what a renderless component is: a component that provides only functionality without any HTML.

Making components truly renderless can be a little tricky because you’ll need to write render functions rather than using a template in order to remove the need for a root element, but it may not always be necessary. Let’s take a look at a simple example that does let us use a template first, though:

<template>
  <transition name="fade" v-bind="$attrs" v-on="$listeners">
    <slot></slot>
  </transition>
</template>
<style>
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.3s;
}
.fade-enter, .fade-leave-to {
  opacity: 0;
}
</style>

This is an odd example of a renderless component because it doesn’t even have any JavaScript in it. That’s mostly because we’re just creating a pre-configured reusable version of a built-in renderless function: transition.

Yup, Vue has built-in renderless components. This particular example is taken from an article on reusable transitions by Cristi Jora and shows a simple way to create a renderless component that can standardize the transitions used throughout your application. Cristi’s article goes into a lot more depth and shows some more advanced variations of reusable transitions, so I recommend checking it out.

For our other example, we’ll create a component that handles switching what is shown during the different states of a Promise: pending, successfully resolved, and failed. It’s a common pattern and while it doesn’t require a lot of code, it can muddy up a lot of your components if the logic isn’t pulled out for reusability.

<!-- promised.vue -->
<template>
  <span>
    <slot  name="rejected"  v-if="error" :error="error"></slot>
    <slot  name="resolved"  v-else-if="resolved" :data="data"></slot>
    <slot  name="pending"  v-else></slot>
  </span>
</template>


export  default {
  props: {
    promise:  Promise
  },

  data: () => ({
    resolved:  false,
    data:  null,
    error:  null
  }),  

  watch: {
    promise: {
      handler (promise) {
        this.resolved  =  false
        this.error  =  null

        if (!promise) {
          this.data  =  null
          return
        }

        promise.then(data  => {
          this.data  =  data
          this.resolved  =  true
        })
        .catch(err  => {
          this.error  =  err
          this.resolved  =  true
        })
      },
      immediate:  true
    }
  }
}

So what is going on here? First, note that we are receiving a prop called promise that is a Promise. In the watch section we watch for changes to the promise and when it changes (or immediately on component creation thanks to the immediate property) we clear the state, and call then and catch on the promise, updating the state when it either finishes successfully or fails.

Then, in the template, we show a different slot based on the state. Note that we failed to keep it truly renderless because we needed a root element in order to use a template. We’re passing data and error to the relevant slot scopes as well.

And here’s an example of it being used:

<template>
  
Resolved: {{ data }} Rejected: {{ error }} Working on it...
</template> ...

We pass in somePromise to the renderless component. While we’re waiting for it to finish, we’re displaying “Working on it…” thanks to the pending slot. If it succeeds we display “Resolved:” and the resolution value. If it fails we display “Rejected:” and the error that caused the rejection. Now we no longer need to track the state of the promise within this component because that part is pulled out into its own reusable component.

So, what can we do about that span wrapping around the slots in promised.vue? To remove it, we’ll need to remove the template portion and add a render function to our component:

render () {
  if (this.error) {
    return this.$scopedSlots['rejected']({error: this.error})
  }

  if (this.resolved) {
    return this.$scopedSlots['resolved']({data: this.data})
  }

  return this.$scopedSlots['pending']()
}

There isn’t anything too tricky going on here. We’re just using some if blocks to find the state and then returning the correct scoped slot (via this.$scopedSlots['SLOTNAME'](...)) and passing the relevant data to the slot scope. When you’re not using a template, you can skip using the .vue file extension by pulling the JavaScript out of the script tag and just plunking it into a .js file. This should give you a very slight performance bump when compiling those Vue files.

This example is a stripped-down and slightly tweaked version of vue-promised, which I would recommend over using the above example because they cover over some potential pitfalls. There are plenty of other great examples of renderless components out there too. Baleada is an entire library full of renderless components that provide useful functionality like this. There’s also vue-virtual-scroller for controlling the rendering of list item based on what is visible on the screen or PortalVue for “teleporting” content to completely different parts of the DOM.

I’m Out

Vue’s slots take component-based development to a whole new level, and while I’ve demonstrated a lot of great ways slots can be used, there are countless more out there. What great idea can you think of? What ways do you think slots could get an upgrade? If you have any, make sure to bring your ideas to the Vue team. God bless and happy coding.

Smashing Editorial
(dm, il)

Source: Smashing Magazine, Using Slots In Vue.js

How Sitejet Helps Your Agency Design Websites Faster Than Ever

dreamt up by webguru in Uncategorized | Comments Off on How Sitejet Helps Your Agency Design Websites Faster Than Ever

How Sitejet Helps Your Agency Design Websites Faster Than Ever

How Sitejet Helps Your Agency Design Websites Faster Than Ever

Suzanne Scacca



(This is a sponsored article.) Spend enough time in a Facebook group for professional web developers and designers and the question will inevitably come up:

“I was approached by a small client with a small budget. Should I work with them?”

Those that have been around a long time will probably scream “No!” and tell you that you’re under no obligation to work with smaller clients. The reasoning being that it’s impossible to make a worthwhile profit on those kinds of projects and that many of the clients end up being difficult to work with.

But is the problem really with the quality of clients with small- and medium-sized businesses? In some cases, that may be so — though, honestly, that’s really only the case if you attract and agree to work with discount seekers (i.e. “How much can you give me for $X?”). In most cases, though, the underlying issue is that your process isn’t efficient enough to design high-quality websites for SMBs at a price point they can afford.

Which is why Sitejet as an all-in-one web design platform is so exciting.

In almost any economy, it’s companies with 10 employees or less that make up well over 80% of the total number of businesses. Although it may not have seemed like an attractive segment of businesses to target in the past, Sitejet makes it not only an option worth considering, but an attractive one at that.

Sitejet gives you a way to design beautiful, feature-rich and responsive websites for SMBs without killing your profits. In the following post, I’m going to show you how Sitejet makes that possible.

Why Sitejet Decided to Share Its Internal Software with the World

In 2013, a German web design agency called Websitebutler was formed.

the website for the Websitebutler design agency

This is the website for the Websitebutler design agency. (Source: Websitebutler) (Large preview)

Their business model is this:

  • Give small companies the chance to have a high-quality website even without an agency-sized budget.
  • Design professional-looking websites for SMBs.
  • Charge them a monthly subscription fee for the website, maintenance, website updates, domain, hosting and so on. Websitebutler would take care of the rest.

The thing is, their cheapest subscription plan starts at € 29.99.

The subscription plans and pricing for Websitebutler’s website-as-a-service

The subscription plans and pricing for Websitebutler’s website-as-a-service. (Source: Websitebutler) (Large preview)

It soon became clear, however, that they couldn’t afford to charge SMBs so little.

While it was a website-as-a-service offering, they weren’t willing to cut corners. Websitebutler still took the time to understand what the client wanted in order to build the right solution.

Because that approach was so time- and resource-consuming, they either needed to:

  1. Reevaluate their pricing,
  2. Phase out smaller clients who couldn’t afford it,
  3. Find a more efficient way to work.

Sitejet Was Born

The Websitebutler team decided to go with option #3:

Find a more efficient way to work.

When I spoke to Hendrik Köhler, co-founder and the lead of Marketing & Product for Sitejet, he said:

“It took five years to create the perfect tool for ourselves.”

It also took building over 4,000 SMB websites in-house before they were happy with the refinements they’d made to their internal solution. That’s when they started thinking about sharing their high-performance content and project management system with other web designers and design agencies. And why wouldn’t they?

Sitejet enabled Websitebutler to decrease the time spent on web design projects by 70%.

As I’ll show you in just a few moments, Sitejet gives designers a way to effectively manage everything from one place while still being able to develop fast, beautiful and responsive websites.

Time-Savings For Your Web Design Workflow With Sitejet

Think about your design process: realistically, there probably isn’t much more you could shave off of it without compromising the quality of your work. If you were to pick up speed in your workflow it would have to be in client communication and project management, right?

That’s why Sitejet deserves to stand out from other site builder solutions. It’s not that they’re the first to create an easy-to-use visual editor. However, they are the first to successfully combine project management, communication, and web design into one tool.

A quick look into the Sitejet website and project management dashboard

A quick look into the Sitejet website and project management dashboard. (Source: Sitejet) (Large preview)

One thing to note before I give you a look at how Sitejet saves you time is that I’m not going to focus too much on how to design with Sitejet. If you’ve worked with enough site builders, then you have a good idea of what you can do with Sitejet. So, what I’m going to focus on are the key differentiators that make this a powerhouse in terms of managing web design projects.

Before we get started looking into this platform, though, here is a highlight reel of how Sitejet revolutionizes your workflows:

Now, let’s take a closer look at what you need to know about this web design tool built by web designers for web designers.

1. Built-In Project Management

Sitejet is like other site builders in that it allows you to build and manage multiple websites from a single dashboard. However, nothing else about this dashboard is like the rest:

A close-up of the project management panel in Sitejet

A close-up of the project management panel in Sitejet. (Source: Sitejet) (Large preview)

If you’re currently managing multiple website projects simultaneously, my guess is that you’re using a project management platform like Asana, Trello or Basecamp for collecting files, communicating with clients and managing your task list.

But that creates an additional step for you, right? Plus, you have to account for the errant email or voicemail from clients that fail to use the project management system as it was intended.

There’s just too much logging in and out platforms and hunting around for all of the information and assets you need when you work with two systems for the same workflow.

With Sitejet, that’s no longer a problem as project management is automatically built in.

2. Faster Communications

You know how clients are, especially small business owners who don’t have a dedicated team member whose sole job it is to help you to create their website. They don’t have the patience for all these systems (especially when they’re complicated to use), which is why they end up emailing and calling even though you’ve pleaded with them to use the tools you’ve given them.

So, you end up throwing away a bunch of time dealing with these unexpected and disorganized communications.

In Sitejet, that’s not a problem. All communication takes place in the same system that everything else does. This is what the email system looks like from your end:

Sitejet makes email communications much easier to organize and execute

Sitejet makes email communications much easier to organize and execute. (Source: Sitejet) (Large preview)

You can message team members privately, insert messages or information from your client or send them a new one. You can also create email templates to reuse as certain phases of each project are completed.

Sitejet reduces your workload even further by automatically assigning new emails to your customers to ensure they never miss an important communication. In addition, any files that are attached to emails are automatically uploaded to your file management center — just one less asset you have to worry about moving from Point A to Point B and then over to Point C.

From the client’s perspective, this is great, too. Here’s a message as seen from the client’s Gmail account:

Sitejet emails end up in the client’s own email account and not lost in the system

Sitejet emails end up in the client’s own email account and not lost in the system. (Source: Sitejet) (Large preview)

If a response is needed, the client can reply directly from their email provider and not have to worry about logging back into Sitejet if no other actions are needed there (like a simple “Yes”, “No”, “I approve this”). If action is needed, they’ll simply click on the link and do as you’ve requested.

Otherwise, all of the communication — emails, task assignments, and feedback — is handled within Sitejet.

3. Smart Project Status System

One of the features to pay close attention to in Sitejet is the first column in the dashboard:

Sitejet’s project status list for more effective project management

Sitejet’s project status list for more effective project management. (Source: Sitejet) (Large preview)

This isn’t some meaningless system that helps you know which stage each project is in either. It actually opens up new functionality in Sitejet for you and your clients.

The Customer Portal is an incredibly powerful tool for Sitejet users and their clients.

Unlike many page builder tools and content management systems which require your clients to work in the same exact interface as you do, Sitejet has created a simpler and more intuitive interface for clients. This way, you don’t have to spend your time spelling out what each area of the portal means or demonstrating what they need to do inside of it.

For example, this is what the Customer Portal looks like when a website is in the Preparation phase:

This is the Sitejet Preparation phase, as seen from the client

This is the Sitejet Preparation phase, as seen from the client. (Source: Sitejet) (Large preview)

They’re asked to:

  • Set up their user account and data,
  • Upload files like photos, videos, documents and much more,
  • Provide info on their “Wishes” for the website.

After completing their portion of the “Preparation” phase, the system automatically notifies you. That way, you don’t have to chase down the client or try to gather all of those details from other platforms.

Managing phase completion from the Sitejet dashboard

Managing phase completion from the Sitejet dashboard. (Source: Sitejet) (Large preview)

With confirmation that they’ve completed their tasks, you can then get to work on subsequent phases. As you complete new ones, their dashboard will continue to transform.

For example, here is what they see when you enter the Feedback phase:

A new option appears for clients as you enter new phases in Sitejet

A new option appears for clients as you enter new phases in Sitejet. (Source: Sitejet) (Large preview)

Notice how there’s now a prompt for feedback. When they click on either of those links, they’re taken into the website where they can leave notes on the website you’ve designed.

For many clients, project management tools are often overwhelming. With Sitejet, however, you control what they focus on to ensure that you get the right information without any issue. As a bonus, you’re able to stay abreast of any and all changes they make on their end, so you can more quickly get your websites through all project phases.

4. Remove The Client Bottleneck

When it comes to building websites, there are a number of ways in which your clients can become the bottleneck in your workflow. Like in the matter of content collection.

When you work with SMBs, it’s common to leave it to the clients to provide content for their sites. It’s then your responsibility to put it together and shape a great-looking design around it.

Despite knowing that they’re accountable for the content, many designers still have issues getting it from clients. But as we’ve seen already, Sitejet makes it easy to inform clients when certain actions are needed. It also makes the act of content collection one that won’t make you want to tear your hair out.

Remember the Preparation phase we saw earlier from the client’s perspective? This is what they find under File Management:

An example of files uploaded to the Sitejet file management tool

An example of files uploaded to the Sitejet file management tool. (Source: Sitejet) (Large preview)

This is where they can upload any kind of data (e.g. image files, PDF documents, and more) they owe you for the project. They may be photographs to use on the site (as in the example above). Or partner or customer logos they want to showcase. Or even style guides or other PDFs that’ll guide you along as you design their website.

For those of you who convert your files from one format to another, Sitejet allows you to do that from within the platform, too. Let’s say you want to reduce the size of an image by going from PNG to JPG or you want to turn a PDF into a JPG. There’s no need to leave Sitejet to do this.

In traditional website workflows, you’d have your clients upload their content to the project management platform or to a file sharing system. You’d then download each of the files and re-upload them into your site builder. This removes the middle man.

Then, there’s the Wishes section of the Client Portal:

Sitejet lets your client define what they “wish” you’ll include on their website

Sitejet lets your client define what they “wish” you’ll include on their website. (Source: Sitejet) (Large preview)

This system retrieves all of the details you need from clients in order to build their websites:

  • What other websites are they a fan of and why?
  • Do they want a single-page or multi-page site?
  • Do they have a logo or need one created?
  • Do they have a color palette or shall you create it for them?
  • Is content for the website ready to be uploaded or is there an old website you can retrieve it from?
  • What is the business’s contact information to show on the website?
  • Are there any special legal notices they want to be included?

You can also fill in as much of it as you can before they ever get there. For instance, say you already know you’re going to create a multi-page website that includes an About page, a Menu page and a Contact page. You can add that information into the Construction and Content tabs for them.

It’s also worth mentioning that the client portal is a great sales tool as well. Because, not only can you create accounts for current clients, you can do so for prospective clients. They can upload files and data, and email you from within the platform, all while you’re still in the early stages of talking.

If you want to build trust with clients early on, a branded Client Portal that simplifies all of your exchanges would help immensely. Again, this would reduce the amount of time you have to spend hand holding clients or manually taking them through the process.

5. Control Client Activity On The Site

Let’s say you want to give your clients the option to edit their website while you’re working on it. This can be risky at any stage as clients are often easily overwhelmed by what they see when they have a brand new website. They also have a tendency to hurriedly make changes and then act surprised when something looks bad or “breaks”.

There’s a feature you can use called “Editable for customers” to cut down on the overwhelm many clients face when confronted with a site builder. This’ll also cut down on the time you have to spend repairing something they “didn’t mean to do”.

To activate it, step inside the Sitejet builder. Next, right-click on an element you want them to be able to edit. If you want them to be able to edit more than that, right-click anywhere on the page.

Sitejet’s “Editable for customer” setting lets you control how much of the site the client can alter

Sitejet’s “Editable for customer” setting lets you control how much of the site the client can alter. (Source: Sitejet) (Large preview)

You’ll see three options:

  1. This element.
  2. All elements of the same type.
  3. All elements on the page.

If you only want the client to edit a particular section (say, if you’re into your final round of revisions and don’t want them to backtrack and try to edit something already finalized), this would really come in handy.

Or, if you don’t trust them to make edits to the site at all, you can opt to collect their feedback instead.

The second you change the Project Status to “Feedback”, your client will then see the Feedback option open up on their end along with a link to the site builder. Just be sure to send them an email to let them know they can step inside.

The first time the client steps into the editor, they’ll see this message

The first time the client steps into the editor, they’ll see this message. (Source: Sitejet) (Large preview)

As they hover over different parts of the web page, the element will display a blue overlay.

Leaving feedback on websites with Sitejet is easy for clients

Leaving feedback on websites with Sitejet is easy for clients. (Source: Sitejet) (Large preview)

When they click on the element, a new note field will appear where they can leave their feedback. The note will show up on their feedback sidebar like this:

All feedback notes show up in the sidebar until the client is ready to submit

All feedback notes show up in the sidebar until the client is ready to submit. (Source: Sitejet) (Large preview)

Once they’ve completed adding their notes, they can click the “Submit” button which will then push new to-dos (one for each note) over to your queue.

A list of sample to-dos inside the web designer’s dashboard of Sitejet

A list of sample to-dos inside the web designer’s dashboard of Sitejet. (Source: Sitejet) (Large preview)

This way, you don’t have to copy down all of the feedback from an email, text message or phone call with the client and try to decipher where they meant for you to make a change. Their notes happen directly on the website and end up as alerts in your to-do box as well as inside the page editor tool where they reveal themselves within the context of the page.

Collaboration isn’t just easier with your clients either. Sitejet simplifies collaboration with team members as well as external service providers. You can assign them to-dos as well as define permissions and roles which restrict the type of actions they can or cannot take within the platform.

6. Faster Website Generation

With Sitejet, you have numerous options to build a website for your clients. However, if you really want to save time up front, you’ll want to use the platform’s templates, matching presets and website generator.

Templates

Sitejet has dozens of beautifully made templates for you to start with:

Sitejet has dozens of website templates

Sitejet has dozens of website templates. (Source: Sitejet) (Large preview)

What’s nice about this — and something you don’t often find with site builders — is that the best templates aren’t hidden behind a paywall. You have access to all of their templates without any additional fees.

What’s more, because this platform was made to cater to the SMB client, you’re going to find templates built specifically for those niches. Restaurants. Bars. Salons. Real estate agents. And much more.

If you’re normally spending too much time trying to find the right starter design, having to customize the ones available or search hours for the right template on marketplaces, Sitejet is a way out of that time-consuming cycle. You can also create and save your own template if there’s a specific style of site you tend to use for your niche.

Matching Presets

There’s a related feature Sitejet includes that you should also know about. I wouldn’t call it a section template tool, though it kind of looks like one at first glance. Let me show you.

This is the Sitejet builder. You can edit universal website settings, add elements and drag-and-drop elements around as you like:

A look inside the Sitejet builder and editor interface

A look inside the Sitejet builder and editor interface. (Source: Sitejet) (Large preview)

Now, let’s say that this great-looking template took care of most of the work for you. However, you’re not 100% satisfied with the layout of this section. What you could do is customize it using the builder’s system of elements and the edit container. Or…

You can use what’s called “Find Matching Presets”:

Sitejet’s Matching Presets feature is a huge time-saver

Sitejet’s Matching Presets feature is a huge time-saver. (Source: Sitejet) (Large preview)

When you right-click on any section, you’re given a bunch of options for adding elements, editing them, as well as customizing the code. You will also find matching presets.

At the bottom of the screen, the Preset Wizard appears:

This is the Sitejet Preset Wizard when you want to redesign a section

This is the Sitejet Preset Wizard when you want to redesign a section. (Source: Sitejet) (Large preview)

When you select one of the Presets, this allows you to change the structure of the section without losing any of the content. For example:

Restructuring the layout and design of a section can happen in seconds

Restructuring the layout and design of a section can happen in seconds. (Source: Sitejet) (Large preview)

It’s an insanely quick way to edit your designs without having to make more than a couple of clicks.

Website Generator

If you really want to save yourself time building websites and you’ve left the content piece in your clients’ hands, have a look at the website generation tool.

To use this tool, you first need to add a new website. When you’re asked to choose a template in the next screen, you’ll click “Choose template later”. This way, all you’ve done is create an empty shell of a website.

Then, locate “More” options next to that website in your dashboard:

Use Sitejet’s website generator to speed up design

Use Sitejet’s website generator to speed up design. (Source: Sitejet) (Large preview)

The “Generate website” tool will then pull all of the “Wishes” images, information and content from the Client Portal into the new website. You’ll see a quick preview of those details and can make last-minute changes before letting Sitejet auto-generate the new website for you:

You get a chance to review and edit the information and content provided by the client

You get a chance to review and edit the information and content provided by the client. (Source: Sitejet) (Large preview)

When it’s done, you’ll have a brand new website filled with all of the content the client sent to you:

An example of an auto-generated website with Sitejet

An example of an auto-generated website with Sitejet. (Source: Sitejet) (Large preview)

If you find that anything’s not in the right place or that there are some missing spots that need filling in, you can edit the website as you would one you built from scratch or from a template. At least this way you have a running head start.

Things I Didn’t Talk About

As I mentioned at the beginning of this roundup, I didn’t want to dig too deeply into all of the features of Sitejet since it would take too much time. That said, there are so many great features here that ensure that you can do everything you can with other website builder tools — and more.

For example:

  • Manage hosting and domains.
  • Automate backups.
  • Set up mail transfers.
  • Whitelabel the CMS with your branding.
  • Perform website checks.
  • Design using the page builder or with code.
  • Track time.
  • Manage users and permissions.
  • Review website statistics.

Clearly, it’s a robust all-in-one platform that takes into account every aspect of your workflow, simplifying as much as possible so you can work faster and more efficiently.

Wrapping Up

Let’s be real: when looking for new clients, you’re probably focused on the big dogs with the big budgets. Because those have traditionally been the only ones you could make a hefty profit on.

If you’ve ever felt bad about turning away small businesses, Sitejet makes it possible for you to start saying “yes” to them.

Not only is that great for the smaller players who otherwise wouldn’t have the means to get a high-performance website for their business, this is great for you because it means you can exponentially increase your client base. You can still take on big projects and then fill in the gaps with a bunch of smaller ones who’ll take significantly less time now thanks to Sitejet.

Smashing Editorial
(ms, il)

Source: Smashing Magazine, How Sitejet Helps Your Agency Design Websites Faster Than Ever

Collective #528

dreamt up by webguru in Uncategorized | Comments Off on Collective #528

C528_bound

Bounds.js

Asynchronous boundary detection for lazy-loading, infinite scroll and more. By Christopher Cavalea.

Check it out



C528_number

Numverify

A global phone number validation and lookup JSON API that supports 232 countries.

Check it out


C528_animapi

CSS Animation Worklet API

The first draft of the CSS Animation Worklet API that allows for running scripted animations without impacting the main thread.

Read it


C528_imageformat

Using Basis Textures in Three.js

Learn about the new Basis Universal image format that was designed to produce very small file sizes and to be decoded on graphic cards instead of the CPU. An article by Ada Rose Cannon.

Read it



C528_customprop

CSS Custom Properties In The Cascade

In this article, Miriam Suzanne takes a deeper dive into the ‘CSS Custom Properties for Cascading Variables’ specification to ask, “Why are they called custom properties, how do they work in the cascade, and what else can we do with them?”

Read it








C528_trees

How accessibility trees inform assistive tech

Hidde de Vries takes a look at how “good” client-side code improves the experience of users of assistive technologies, and how we can use accessibility trees to help verify our work on the user experience.

Read it


C528_statuscodes

HTTP Cats

In case you didn’t know about it: easy HTTP status code pages with meaningful cat images.

Check it out



C528_urlpage

URL Pages

URL Pages is a proof of concept that works by storing the entire contents of a web page in the URL.

Check it out




C528_gan

GANPaint Studio

GANPaint Studio is a glimpse into the future of creative tools. It provides semantic photo manipulation with a generative image prior.

Check it out



C528_synth

Learning Synths

Learn about synthesizers via Ableton’s interactive website. Play with a synth in your browser and learn to use the various parts of a synth to make your own sounds.

Check it out



Collective #528 was written by Pedro Botelho and published on Codrops.


Source: Codrops, Collective #528

CSS Custom Properties In The Cascade

dreamt up by webguru in Uncategorized | Comments Off on CSS Custom Properties In The Cascade

CSS Custom Properties In The Cascade

CSS Custom Properties In The Cascade

Miriam Suzanne



Last month, I had a conversation on Twitter about the difference between “scoped” styles (generated in a build process) and “nested” styles native to CSS. I asked why, anecdotally, developers avoid the specificity of ID selectors, while embracing “scoped styles” generated by JavaScript? Keith Grant suggested that the difference lies in balancing the cascade* and inheritance, i.e. giving preference to proximity over specificity. Let’s take a look.

The Cascade

The CSS cascade is based on three factors:

  1. Importance defined by the !important flag, and style origin (user > author > browser)
  2. Specificity of the selectors used (inline > ID > class > element)
  3. Source Order of the code itself (latest takes precedence)

Proximity is not mentioned anywhere — the DOM-tree relationship between parts of a selector. The paragraphs below will both be red, even though #inner p describes a closer relationship than #outer p for the second paragraph:

See the Pen [Cascade: Specificity vs Proximity](https://codepen.io/smashingmag/pen/OexweJ/) by Miriam Suzanne.

See the Pen Cascade: Specificity vs Proximity by Miriam Suzanne.
<section id="outer">
  <p>This text is red</p>
  

This text is also red!

</section>
#inner p {
  color: green;
}

#outer p {
  color: red;
}

Both selectors have the same specificity, they are both describing the same p element, and neither is flagged as !important — so the result is based on source-order alone.

BEM And Scoped Styles

Naming conventions like BEM (“Block__Element—Modifier”) are used to ensure that each paragraph is “scoped” to only one parent, avoiding the cascade entirely. Paragraph “elements” are given unique classes specific to their “block” context:

See the Pen [BEM Selectors & Proximity](https://codepen.io/smashingmag/pen/qzPyeM/) by Miriam Suzanne.

See the Pen BEM Selectors & Proximity by Miriam Suzanne.
<section class="outer">
  <p class="outer__p">This text is red</p>
  

This text is green!

</section>
.inner__p {
  color: green;
}

.outer__p {
  color: red;
}

These selectors still have the same relative importance, specificity, and source order — but the results are different. “Scoped” or “modular” CSS tools automate that process, re-writing our CSS for us, based on the HTML. In the code below, each paragraph is scoped to its direct parent:

See the Pen [Scoped Style Proximity](https://codepen.io/smashingmag/pen/NZaLWN/) by Miriam Suzanne.

See the Pen Scoped Style Proximity by Miriam Suzanne.
<section outer-scope>
  <p outer-scope>This text is red</p>
  

This text is green!

</section>
p[inner-scope] {
  color: green
}

p[outer-scope] {
  color: red;
}

Inheritance

Proximity is not part of the cascade, but it is part of CSS. That’s where inheritance becomes important. If we drop the p from our selectors, each paragraph will inherit a color from its closest ancestor:

See the Pen [Inheritance: Specificity vs Proximity](https://codepen.io/smashingmag/pen/mZBGyN/) by Miriam Suzanne.

See the Pen Inheritance: Specificity vs Proximity by Miriam Suzanne.
#inner {
  color: green;
}

#outer {
  color: red;
}

Since #inner and #outer describe different elements, our div and section respectively, both color properties are applied without conflict. The nested p element has no color specified, so the results are determined by inheritance (the color of the direct parent) rather than cascade. Proximity takes precedence, and the #inner value overrides the #outer.

But there’s a problem: In order to use inheritance, we are styling everything inside our section and div. We want to target the paragraph color specifically.

(Re-)Introducing Custom Properties

Custom properties provide a new, browser-native solution; they inherit like any other property, but they don’t have to be used where they are defined. Using plain CSS, without any naming conventions or build tools, we can create a style that is both targeted and contextual, with proximity taking precedence over the cascade:

See the Pen [Custom Props: Specificity vs Proximity](https://codepen.io/smashingmag/pen/gNGdaO/) by Miriam Suzanne.

See the Pen Custom Props: Specificity vs Proximity by Miriam Suzanne.
p {
  color: var(--paragraph);
}

#inner {
  --paragraph: green;
}

#outer {
  --paragraph: red;
}

The custom --paragraph property inherits just like the color property, but now we have control over exactly how and where that value is applied. The --paragraph property acts similar to a parameter that can be passed into the p component, either through direct selection (specificity-rules) or context (proximity-rules).

I think this reveals a potential for custom properties that we often associate with functions, mixins, or components.

Custom “Functions” And Parameters

Functions, mixins, and components are all based on the same idea: reusable code, that can be run with various input parameters to get consistent-but-configurable results. The distinction is in what they do with the results. We’ll start with a striped-gradient variable, and then we can extend it into other forms:

html {
  --stripes: linear-gradient(
    to right,
    powderblue 20%, pink 20% 40%, white 40% 60%, pink 60% 80%, powderblue 80%
  );
}

That variable is defined on the root html element (could also use :root, but that adds unnecessary specificity), so our striped variable will be available everywhere in the document. We can apply it anywhere gradients are supported:

See the Pen [Custom Props: Variable](https://codepen.io/smashingmag/pen/NZwrrm/) by Miriam Suzanne.

See the Pen Custom Props: Variable by Miriam Suzanne.
body {
  background-image: var(--stripes);
}

Adding Parameters

Functions are used like variables, but define parameters for changing the output. We can update our --stripes variable to be more function-like by defining some parameter-like variables inside it. I’ll start by replacing to right with var(--stripes-angle), to create an angle-changing parameter:

html {
  --stripes: linear-gradient(
    var(--stripes-angle),
    powderblue 20%, pink 20% 40%, white 40% 60%, pink 60% 80%, powderblue 80%
  );
}

There are other parameters we could create, depending on what purpose the function is meant to serve. Should we allow users to pick their own stripe colors? If so, does our function accept 5 different color parameters or only 3 that will go outside-in like we have now? Do we want to create parameters for color-stops as well? Every parameter we add provides more customization at the cost of simplicity and consistency.

There is no universal right answer to that balance — some functions need to be more flexible, and others need to be more opinionated. Abstractions exist to provide consistency and readability in your code, so take a step back and ask what your goals are. What really needs to be customizable, and where should consistency be enforced? In some cases, it might be more helpful to have two opinionated functions, rather than one fully-customizable function.

To use the function above, we need to pass in a value for the --stripes-angle parameter, and apply the output to a CSS output property, like background-image:

/* in addition to the code above… */
html {
  --stripes-angle: 75deg;
  background-image: var(--stripes);
}

See the Pen [Custom Props: Function](https://codepen.io/smashingmag/pen/BgwOjj/) by Miriam Suzanne.

See the Pen Custom Props: Function by Miriam Suzanne.

Inherited Versus Universal

I defined the --stripes function on the html element out of habit. Custom properties inherit, and I want my function available everywhere, so it makes some sense to put it on the root element. That works well for inheriting variables like --brand-color: blue, so we might also expect it to work for our “function” as well. But if we try to use this function again on a nested selector, it won’t work:

See the Pen [Custom Props: Function Inheritance Fail](https://codepen.io/smashingmag/pen/RzjRrM/) by Miriam Suzanne.

See the Pen Custom Props: Function Inheritance Fail by Miriam Suzanne.
div {
  --stripes-angle: 90deg;
  background-image: var(--stripes);
}

The new --stripes-angle is ignored entirely. It turns out we can’t rely on inheritance for functions that need to be re-calculated. That’s because each property value is computed once per element (in our case, the html root element), and then the computed value is inherited. By defining our function at the document root, we don’t make the entire function available to descendants — only the computed result of our function.

That makes sense if you frame it in terms of the cascading --stripes-angle parameter. Like any inherited CSS property, it is available to descendants but not ancestors. The value we set on a nested div is not available to a function we defined on the html root ancestor. In order to create a universally-available function that will re-calculate on any element, we have to define it on every element:

See the Pen [Custom Props: Universal Function](https://codepen.io/smashingmag/pen/agLaNj/) by Miriam Suzanne.

See the Pen Custom Props: Universal Function by Miriam Suzanne.
* {
  --stripes: linear-gradient(
    var(--stripes-angle),
    powderblue 20%, pink 20% 40%, white 40% 60%, pink 60% 80%, powderblue 80%
  );
}

The universal selector makes our function available everywhere, but we can define it more narrowly if we want. The important thing is that it can only re-calculate where it is explicitly defined. Here are some alternatives:

/* make the function available to elements with a given selector */
.stripes { --stripes: /* etc… */; } 

/* make the function available to elements nested inside a given selector */
.stripes * { --stripes: /* etc… */; } 

/* make the function available to siblings following a given selector */
.stripes ~ * { --stripes: /* etc… */; } 

See the Pen [Custom Props: Scoped Function](https://codepen.io/smashingmag/pen/JQMvGM/) by Miriam Suzanne.

See the Pen Custom Props: Scoped Function by Miriam Suzanne.

This can be extended with any selector logic that doesn’t rely on inheritance.

Free Parameters And Fallback Values

In our example above, var(--stripes-angle) has no value and no fallback. Unlike Sass or JS variables that must be defined or instantiated before they are called, CSS custom properties can be called without ever being defined. This creates a “free” variable, similar to a function parameter that can be inherited from the context.

We can eventually define the variable on html or :root (or any other ancestor) to set an inherited value, but first we need to consider the fallback if no value is defined. There are several options, depending on exactly what behavior we want

  1. For “required” parameters, we don’t want a fallback. As-is, the function will do nothing until --stripes-angle is defined.
  2. For “optional” parameters, we can provide a fallback value in the var() function. After the variable-name, we add a comma, followed by the default value:
var(--stripes-angle, 90deg)

Each var() function can only have one fallback — so any additional commas will be part of that value. That makes it possible to provide complex defaults with internal commas:

html {
  /* Computed: Hevetica, Ariel, sans-serif */
  font-family: var(--sans-family, Hevetica, Ariel, sans-serif);

  /* Computed: 0 -1px 0 white, 0 1px 0 black */
  test-shadow: var(--shadow, 0 -1px 0 white, 0 1px 0 black);
} 

We can also use nested variables to create our own cascade rules, giving different priorities to the different values:

var(--stripes-angle, var(--global-default-angle, 90deg))
  1. First, try our explicit parameter (--stripes-angle);
  2. Fallback to a global “user default” (--user-default-angle) if it’s available;
  3. Finally, fallback to our “factory default” (90deg).

See the Pen [Custom Props: Fallback Values](https://codepen.io/smashingmag/pen/jjGvVm/) by Miriam Suzanne.

See the Pen Custom Props: Fallback Values by Miriam Suzanne.

By setting fallback values in var() rather than defining the custom property explicitly, we ensure that there are no specificity or cascade restrictions on the parameter. All the *-angle parameters are “free” to be inherited from any context.

Browser Fallbacks Versus Variable Fallbacks

When we’re using variables, there are two fallback paths we need to keep in mind:

  1. What value should be used by browsers without variable support?
  2. What value should be used by browsers that support variables, when a particular variable is missing or invalid?
p {
  color: blue;
  color: var(--paragraph);
}

While old browsers will ignore the variable declaration property, and fallback to blue — modern browsers will read both and use the latter. Our var(--paragraph) might not be defined, but it is valid and will override the previous property, so browsers with variable support will fallback to the inherited or initial value, as if using the unset keyword.

That may seem confusing at first, but there are good reasons for it. The first is technical: browser engines handle invalid or unknown syntax at “parse time” (which happens first), but variables are not resolved until “computed-value time” (which happens later).

  1. At parse time, declarations with invalid syntax are ignored completely — falling back on earlier declarations. This is the path that old browsers will follow. Modern browsers support the variable syntax, so the previous declaration is discarded instead.
  2. At computed-value time the variable is compiled as invalid, but it’s too late — the previous declaration was already discarded. According to the spec, invalid variable values are treated the same as unset:

See the Pen [Custom Props: Invalid/Unsupported vs Undefined](https://codepen.io/smashingmag/pen/VJMGbJ/) by Miriam Suzanne.

See the Pen Custom Props: Invalid/Unsupported vs Undefined by Miriam Suzanne.
html {
  color: red;
  
  /* ignored as *invalid syntax* by all browsers */
  /* - old browsers: red */
  /* - new browsers: red */
  color: not a valid color; 
  color: var(not a valid variable name); 
  
  /* ignored as *invalid syntax* by browsers without var support */
  /* valid syntax, but invalid *values* in modern browsers */
  /* - old browsers: red */
  /* - new browsers: unset (black) */
  --invalid-value: not a valid color value;
  color: var(--undefined-variable);
  color: var(--invalid-value);
}

This is also good for us as authors, because it allows us to play with more complex fallbacks for the browsers that support variables, and provide simple fallbacks for older browsers. Even better, that allows us to use the null/undefined state to set required parameters. This becomes especially important if we want to turn a function into a mixin or component.

Custom Property “Mixins”

In Sass, the functions return raw values, while mixins generally return actual CSS output with property-value pairs. When we define a universal --stripes property, without applying it to any visual output, the result is function-like. We can make that behave more like a mixin, by defining the output universally as well:

* {
  --stripes: linear-gradient(
    var(--stripes-angle),
    powderblue 20%, pink 20% 40%, white 40% 60%, pink 60% 80%, powderblue 80%
  );
  background-image: var(--stripes);
}

As long as --stripes-angle remains invalid or undefined, the mixin fails to compile, and no background-image will be applied. If we set a valid angle on any element, the function will compute and give us a background:

div {
  --stripes-angle: 30deg; /* generates the background */
}

Unfortunately, that parameter-value will inherit, so the current definition creates a background on the div and all descendants. To fix that, we have to make sure the --stripes-angle value doesn’t inherit, by resting it to initial (or any invalid value) on every element. We can do that on the same universal selector:

See the Pen [Custom Props: Mixin](https://codepen.io/smashingmag/pen/ZdXMJx/) by Miriam Suzanne.

See the Pen Custom Props: Mixin by Miriam Suzanne.
* {
  --stripes-angle: initial;
  --stripes: /* etc… */;
  background-image: var(--stripes);
}

Safe Inline Styles

In some cases, we need the parameter to be set dynamically from outside CSS — based on data from a back-end server or front-end framework. With custom properties, we can safely define variables in our HTML without worrying about the usual specificity issues:

See the Pen [Custom Props: Mixin + Inline Style](https://codepen.io/smashingmag/pen/qzPMPv/) by Miriam Suzanne.

See the Pen Custom Props: Mixin + Inline Style by Miriam Suzanne.
...

Inline styles have a high specificity, and are very hard to override — but with custom properties, we we have another option: ignore it. If we set the div to background-image: none (for example) that inline variable will have no impact. To take it even farther, we can create an intermediate variable:

* { --stripes-angle: var(--stripes-angle-dynamic, initial); }

Now we have the option to define --stripes-angle-dynamic in the HTML, or ignore it, and set --stripes-angle directly in our stylesheet.

See the Pen [Custom Props: Mixin + Inline / Override](https://codepen.io/smashingmag/pen/ZdXMao/) by Miriam Suzanne.

See the Pen Custom Props: Mixin + Inline / Override by Miriam Suzanne.

Preset Values

For more complex values, or common patterns we want to re-use, we can also provide a few preset variables to choose from:

* {
  --tilt-down: 6deg;
  --tilt-up: -6deg;
}

And use those presets, rather than setting the value directly:

...

See the Pen [Custom Props: Mixin + Presets](https://codepen.io/smashingmag/pen/LKemZm/) by Miriam Suzanne.

See the Pen Custom Props: Mixin + Presets by Miriam Suzanne.

This is great for creating charts and graphs based on dynamic data, or even laying out a day planner.

See the Pen [Bar chart in CSS grid + variables](https://codepen.io/smashingmag/pen/wLrEyg/) by Miriam Suzanne.

See the Pen Bar chart in CSS grid + variables by Miriam Suzanne.

Contextual Components

We can also re-frame our “mixin” as a “component” by applying it to an explicit selector, and making the parameters optional. Rather than relying on the presence-or-absence of --stripes-angle to toggle our output, we can rely on the presence-or-absence of a component selector. That allows us to set fallback values safely:

See the Pen [Custom Props: Component](https://codepen.io/smashingmag/pen/QXqVmM/) by Miriam Suzanne.

See the Pen Custom Props: Component by Miriam Suzanne.
[data-stripes] {
  --stripes: linear-gradient(
    var(--stripes-angle, to right),
    powderblue 20%, pink 20% 40%, white 40% 60%, pink 60% 80%, powderblue 80%
  );
  background-image: var(--stripes);
}

By putting the fallback inside the var() function, we can leave --stripes-angle undefined and “free” to inherit a value from outside the component. This is a great way to expose certain aspects of a component style to contextual input. Even “scoped” styles generated by a JS framework (or scoped inside the shadow-DOM, like SVG icons) can use this approach to expose specific parameters for outside influence.

Isolated Components

If we don’t want to expose the parameter for inheritance, we can define the variable with a default value:

[data-stripes] {
  --stripes-angle: to right;
  --stripes: linear-gradient(
    var(--stripes-angle, to right),
    powderblue 20%, pink 20% 40%, white 40% 60%, pink 60% 80%, powderblue 80%
  );
  background-image: var(--stripes);
}

These components would also work with a class, or any other valid selector, but I chose the data-attribute to create a namespace for any modifiers we want:

[data-stripes='vertical'] { --stripes-angle: to bottom; }
[data-stripes='horizontal'] { --stripes-angle: to right; }
[data-stripes='corners'] { --stripes-angle: to bottom right; }

See the Pen [Custom Props: Isolated Components](https://codepen.io/smashingmag/pen/agLaGX/) by Miriam Suzanne.

See the Pen Custom Props: Isolated Components by Miriam Suzanne.

Selectors and Parameters

I often wish I could use data-attributes to set a variable — a feature supported by the CSS3 attr() specification, but not yet implemented in any browsers (see the resources tab for linked issues on each browser). That would allow us to more closely associate a selector with a particular parameter:

...
/* Part of the CSS3 spec, but not yet supported */ /* attr( , ) */ [data-stripes] { --stripes-angle: attr(data-stripes angle, to right); }

In the meantime, we can achieve something similar by using the style attribute:

See the Pen [Custom Props: Style Selectors](https://codepen.io/smashingmag/pen/PrJdBG/) by Miriam Suzanne.

See the Pen Custom Props: Style Selectors by Miriam Suzanne.
...
/* The `*=` atttribute selector will match a string anywhere in the attribute */ [style*='--stripes-angle'] { /* Only define the function where we want to call it */ --stripes: linear-gradient(…); }

This approach is most useful when we want to include other properties in addition to the parameter being set. For example, setting a grid area could also add padding and background:

[style*='--grid-area'] {
  background-color: white;
  grid-area: var(--grid-area, auto / 1 / auto / -1);
  padding: 1em;
}

Conclusion

When we start to put all these pieces together, it becomes clear that custom properties go far beyond the common variable use-cases we’re familiar with. We’re not only able to store values, and scope them to the cascade — but we can use them to manipulate the cascade in new ways, and create smarter components directly in CSS.

This calls for us to re-think many of the tools we’ve relied on in the past — from naming conventions like SMACSS and BEM, to “scoped” styles and CSS-in-JS. Many of those tools help work around specificity, or manage dynamic styles in another language — use-cases that we can now address directly with custom properties. Dynamic styles that we’ve often calculated in JS, can now be handled by passing raw data into the CSS.

At first, these changes may be seen as “added complexity” — since we’re not used to seeing logic inside CSS. And, as with all code, over-engineering can be a real danger. But I’d argue that in many cases, we can use this power not to add complexity, but to move complexity out of third-party tools and conventions, back into the core language of web design, and (more importantly) back into the browser. If our styles require calculation, that calculation ought to live inside our CSS.

All of these ideas can be taken much further. Custom properties are just starting to see wider adoption, and we’ve only begun to scratch the surface of what’s possible. I’m excited to see where this goes, and what else people come up with. Have fun!

Further Reading

Smashing Editorial
(dm, il)

Source: Smashing Magazine, CSS Custom Properties In The Cascade

The Joys Of July (2019 Wallpapers Edition)

dreamt up by webguru in Uncategorized | Comments Off on The Joys Of July (2019 Wallpapers Edition)

The Joys Of July (2019 Wallpapers Edition)

The Joys Of July (2019 Wallpapers Edition)

Cosima Mielke



A scoop of their favorite ice cream, a bike ride in the summer rain, or listening to the frog concert by the nearby lake — a lot of little things and precious moments have inspired the design community to create a wallpaper this July.

This monthly wallpapers challenge has been going on for more than nine years now, and each time anew, artists and designers from across the globe take it as an occasion to tickle their creativity and cater for beautiful, unique, and thought-provoking wallpaper designs. Wallpapers that are bound to get your ideas sparking, too.

The wallpapers in this collection all come in versions with and without a calendar for July 2019 and can be downloaded for free. A big thank-you to everyone who submitted their designs! At the end of this post, we also compiled some July favorites from past years’ editions that are just too good to be forgotten. Enjoy!

Please note that:

  • All images can be clicked on and lead to the preview of the wallpaper,
  • You can feature your work in our magazine, too. So if you have an idea for an August wallpaper design, please don’t hesitate to submit it. We’d love to see what you’ll come up with.

Further Reading on SmashingMag:

Hello, Strawberry Sundae!

“Is there anything more refreshing (and more delicious!) than a strawberry sundae on a hot summer day? Well, we don’t think so. And did you know that strawberry celebration is on its way in the U.S. Oh, yes! July 7th is the National Strawberry Sundae Day, and we predict that it’s going to be sweet and yummy. So, make your favorite dessert and start preparing yourself for the festive July days.” — Designed by PopArt Studio from Serbia.

Hello, Strawberry Sundae!

Riding In The Drizzle

“Rain has come, showering the existence with new seeds of life… Everywhere life is blooming, as if they were asleep and the falling music of raindrops have awakened them… Feel the drops of rain… Feel this beautiful mystery of life… Listen to its music, melt into it…” — Designed by DMS Software from India.

Riding In The Drizzle

We All Scream For Ice Cream

“There are two things that come to mind when I think about July: ice cream and the Fourth of July!” — Designed by Alyssa Merkel from the United States.

We All Scream For Ice Cream

My July

Designed by Cátia Pereira from Portugal.

My July

Alentejo Plain

“Based on the Alentejo region, south of Portugal, where there are large plains used for growing wheat. It thus represents the extensions of the fields of cultivation and their simplicity. Contrast of the plain with the few trees in the fields. Storks that at this time of year predominate in this region, being part of the Alentejo landscape and mentioned in the singing of Alentejo.” — Designed by José Guerra from Portugal.

Alentejo Plain

Frogs In The Night

“July is coming and the nights are warmer. Frogs look at the moon while they talk about their day.” — Designed by Veronica Valenzuela from Spain.

Frogs In The Night

July Rocks!

Designed by Joana Moreira from Portugal.

July Rocks!

Plastic Bag Free Day

“The objective of this date is to draw attention to the production and over-consumption of plastic bags worldwide, presenting alternatives to solve this serious environmental problem. It is urgent to change the behavior of all human beings regarding the use of plastic bags. For the preservation of the environment, we should use the same plastic bag for shopping, recycling or use paper bags. In this wallpaper I drew a plastic bag with a turtle inside it, as if it was imprisoned by its own bag, as if the ocean was reduced to a plastic bag, emphasizing the seriousness of this environmental problem, which has tortured both turtles and many others marine species.” — Designed by Carolina Santos from Portugal.

Plastic Bag Free Day

Save The Tigers

“Global Tiger Day, often called International Tiger Day, is an annual celebration to raise awareness for tiger conservation, held annually on July 29. It was created in 2010 at the Saint Petersburg Tiger Summit. The goal of the day is to promote a global system for protecting the natural habitats of tigers and to raise public awareness and support for tiger conservation issues.” — Designed by Athulya from Calicut.

Save The Tigers

Palms

“I was inspired by Hawaii type of scenarios with some reference to surf.” — Designed by Sónia Fernandes from Portugal.

Palms

Friendship Day

“The lower part of the image is represented in a more realistic and detailed form and represents the basis of the friendship, conveying a strong connection both in the simplicity and strength of the gestures, hands and medals that both children wear around their necks are symbols of this union. In terms of color I chose to use the ash scale, referring to the old photograph, implying the idea of a long duration of the relations of friendships and the memories we keep of our childhood friends, the illustration represented portrays a memory of mine, which makes it very personal. At the top of the wallpaper, the identity of the two characters appears pixelated, suggesting an idea of building deconstruction, through colored squares that come together and move away, as a symbol of connections and the sharing of emotions, which build and strengthen bonds of friendship.” — Designed by Carolina Santos from Portugal.

Friendship Day

Yellow Lemon Tree

“Summer is here, enjoy it and cool and stay hydrated!” — Designed by Melissa Bogemans from Belgium.

Yellow Lemon Tree

Summer Energy

Designed by IQUADART from Belarus.

Summer Energy

Heat Wave

Designed by Ricardo Gimenes from Sweden.

Heat Wave

Season Of Wind

“Summer is the season of wind. I like to stand on top of the mountains, hearing the song of the wind flying through the meadow.” — Designed by Anh Nguyet Tran from Vietnam.

Season Of Wind

Oldies But Goodies

Let’s go an a journey back in time: Down in our wallpaper archives, we rediscovered some July classics that are just too good to gather dust. May we present… (Please note that these designs don’t come with a calendar.)

Fire Camp

“What’s better than a starry summer night with an (unexpected) friend around a fire camp with some marshmallows? Happy July!” — Designed by Etienne Mansard from the UK.

Fire Camp

Heated Mountains

“Warm summer weather inspired the color palette.” — Designed by Marijana Pivac from Croatia.

Heated Mountains

Tutti Frutti

“July is National Ice Cream Month and who needs an invitation for a scoop or two, or three! Lacking the real thing, our Tutti Frutti wallpaper can satisfy until your next creamy indulgence.” — Designed by Karen Frolo from the United States.

Tutti Frutti

Memories In July

“Words are few, thoughts are deep, memories of you we’ll always keep.” — Designed by Suman Sil from India.

Memories In July

Cactus Hug

Designed by Ilaria Bagnasco from Italy.

Cactus Hug

Keep Moving Forward

“Snails can be inspiring! If you keep heading towards your goal, even if it is just tiny steps, enjoy the journey and hopefully it will be worth the effort.” — Designed by Glynnis Owen from Australia.

Keep Moving Forward

Sand And Waves

“What do you do in summer? You go to the beach. Even if you can’t go — feel the waves and sand and sun through this funny wallpaper.” — Designed by Olga Lepaeva from Russia.

Sand And Waves

Island River

“Make sure you have a refreshing source of ideas, plans and hopes this July. Especially if you are to escape from urban life for a while.” — Designed by Igor Izhik from Canada.

Island River

Ice Cream vs. Hot Dog

“It’s both ‘National Ice Cream Month’ and ‘National Hot Dog Month’ over in the US, which got me thinking — which is better? With this as your wallpaper, you can ponder the question all month!” — Designed by James Mitchell from the UK.

Ice Cream vs. Hot Dog

Summer Heat

Designed by Xenia Latii from Berlin, Germany.

Summer Heat

Peaceful Memories

“July is one of the most beautiful months of the year. Enjoy every day, every hour, which gives us this month!” — Designed by Nikolay Belikov from Russia.

Peaceful Memories

Floral Thing

“The wallpaper which I created consists of my personal sketches of Polish herbs and flowers and custom typography. I wanted it to be light and simple with a hint of romantic feeling. I hope you’ll enjoy it!” — Designed by Beata Kurek from Poland.

Smashing Desktop Wallpapers - July 2012

An Intrusion Of Cockroaches

“Ever watched Joe’s Apartment when you were a kid? Well that movie left a soft spot in my heart for the little critters. Don’t get me wrong: I won’t invite them over for dinner, but I won’t grab my flip flop and bring the wrath upon them when I see one running in the house. So there you have it… three roaches… bringing the smack down on that pesky human… ZZZZZZZAP!!” — Designed by Wonderland Collective from South Africa.

An Intrusion Of Cockroaches

Celebrate Freedom

“This wallpaper encourages you to appreciate and celebrate the country’s freedom as well as your own!” — Designed by Marina Zhukov from the USA.

Desktop Wallpaper

Hot Air Balloon

Designed by Studcréa from France

Hot Air Balloon

Only One

Designed by Elise Vanoorbeek from Belgium

Only one

Cool Summer

“Even though it is not summer in my country, I made a summer theme. A cool approach to summer themes, tough, very fresh and ‘twilighty’.” — Designed by Marcos Sandrini from Brazil.

Smashing Desktop Wallpapers - July 2012

Birdie Nam Nam

“I have created a pattern that has a summer feeling. For me July and summer is bright color, joy and lots of different flowers and birds. So naturally I incorporated all these elements in a crazy pattern.” — Designed by Lina Karlsson, Idadesign Ab from Sweden.

Smashing Desktop Wallpapers - July 2012

Sun In July

”…enjoy the sun in July!” — Designed by Marco Palma from Italy/Germany.

Smashing Desktop Wallpapers - July 2012

Join In Next Month!

Please note that we respect and carefully consider the ideas and motivation behind each and every artist’s work. This is why we give all artists the full freedom to explore their creativity and express emotions and experience throughout their works. This is also why the themes of the wallpapers weren’t anyhow influenced by us but rather designed from scratch by the artists themselves.

Thank you to all designers for their participation. Join in next month!

Smashing Editorial
(il)

Source: Smashing Magazine, The Joys Of July (2019 Wallpapers Edition)

The Joys Of July (2019 Wallpapers Edition)

dreamt up by webguru in Uncategorized | Comments Off on The Joys Of July (2019 Wallpapers Edition)

The Joys Of July (2019 Wallpapers Edition)

The Joys Of July (2019 Wallpapers Edition)

Cosima Mielke



A scoop of their favorite ice cream, a bike ride in the summer rain, or listening to the frog concert by the nearby lake — a lot of little things and precious moments have inspired the design community to create a wallpaper this July.

This monthly wallpapers challenge has been going on for more than nine years now, and each time anew, artists and designers from across the globe take it as an occasion to tickle their creativity and cater for beautiful, unique, and thought-provoking wallpaper designs. Wallpapers that are bound to get your ideas sparking, too.

The wallpapers in this collection all come in versions with and without a calendar for July 2019 and can be downloaded for free. A big thank-you to everyone who submitted their designs! At the end of this post, we also compiled some July favorites from past years’ editions that are just too good to be forgotten. Enjoy!

Please note that:

  • All images can be clicked on and lead to the preview of the wallpaper,
  • You can feature your work in our magazine, too. So if you have an idea for an August wallpaper design, please don’t hesitate to submit it. We’d love to see what you’ll come up with.

Further Reading on SmashingMag:

Hello, Strawberry Sundae!

“Is there anything more refreshing (and more delicious!) than a strawberry sundae on a hot summer day? Well, we don’t think so. And did you know that strawberry celebration is on its way in the U.S. Oh, yes! July 7th is the National Strawberry Sundae Day, and we predict that it’s going to be sweet and yummy. So, make your favorite dessert and start preparing yourself for the festive July days.” — Designed by PopArt Studio from Serbia.

Hello, Strawberry Sundae!

Riding In The Drizzle

“Rain has come, showering the existence with new seeds of life… Everywhere life is blooming, as if they were asleep and the falling music of raindrops have awakened them… Feel the drops of rain… Feel this beautiful mystery of life… Listen to its music, melt into it…” — Designed by DMS Software from India.

Riding In The Drizzle

We All Scream For Ice Cream

“There are two things that come to mind when I think about July: ice cream and the Fourth of July!” — Designed by Alyssa Merkel from the United States.

We All Scream For Ice Cream

My July

Designed by Cátia Pereira from Portugal.

My July

Alentejo Plain

“Based on the Alentejo region, south of Portugal, where there are large plains used for growing wheat. It thus represents the extensions of the fields of cultivation and their simplicity. Contrast of the plain with the few trees in the fields. Storks that at this time of year predominate in this region, being part of the Alentejo landscape and mentioned in the singing of Alentejo.” — Designed by José Guerra from Portugal.

Alentejo Plain

Frogs In The Night

“July is coming and the nights are warmer. Frogs look at the moon while they talk about their day.” — Designed by Veronica Valenzuela from Spain.

Frogs In The Night

July Rocks!

Designed by Joana Moreira from Portugal.

July Rocks!

Plastic Bag Free Day

“The objective of this date is to draw attention to the production and over-consumption of plastic bags worldwide, presenting alternatives to solve this serious environmental problem. It is urgent to change the behavior of all human beings regarding the use of plastic bags. For the preservation of the environment, we should use the same plastic bag for shopping, recycling or use paper bags. In this wallpaper I drew a plastic bag with a turtle inside it, as if it was imprisoned by its own bag, as if the ocean was reduced to a plastic bag, emphasizing the seriousness of this environmental problem, which has tortured both turtles and many others marine species.” — Designed by Carolina Santos from Portugal.

Plastic Bag Free Day

Save The Tigers

“Global Tiger Day, often called International Tiger Day, is an annual celebration to raise awareness for tiger conservation, held annually on July 29. It was created in 2010 at the Saint Petersburg Tiger Summit. The goal of the day is to promote a global system for protecting the natural habitats of tigers and to raise public awareness and support for tiger conservation issues.” — Designed by Athulya from Calicut.

Save The Tigers

Palms

“I was inspired by Hawaii type of scenarios with some reference to surf.” — Designed by Sónia Fernandes from Portugal.

Palms

Friendship Day

“The lower part of the image is represented in a more realistic and detailed form and represents the basis of the friendship, conveying a strong connection both in the simplicity and strength of the gestures, hands and medals that both children wear around their necks are symbols of this union. In terms of color I chose to use the ash scale, referring to the old photograph, implying the idea of a long duration of the relations of friendships and the memories we keep of our childhood friends, the illustration represented portrays a memory of mine, which makes it very personal. At the top of the wallpaper, the identity of the two characters appears pixelated, suggesting an idea of building deconstruction, through colored squares that come together and move away, as a symbol of connections and the sharing of emotions, which build and strengthen bonds of friendship.” — Designed by Carolina Santos from Portugal.

Friendship Day

Yellow Lemon Tree

“Summer is here, enjoy it and cool and stay hydrated!” — Designed by Melissa Bogemans from Belgium.

Yellow Lemon Tree

Summer Energy

Designed by IQUADART from Belarus.

Summer Energy

Heat Wave

Designed by Ricardo Gimenes from Sweden.

Heat Wave

Season Of Wind

“Summer is the season of wind. I like to stand on top of the mountains, hearing the song of the wind flying through the meadow.” — Designed by Anh Nguyet Tran from Vietnam.

Season Of Wind

Oldies But Goodies

Let’s go an a journey back in time: Down in our wallpaper archives, we rediscovered some July classics that are just too good to gather dust. May we present… (Please note that these designs don’t come with a calendar.)

Fire Camp

“What’s better than a starry summer night with an (unexpected) friend around a fire camp with some marshmallows? Happy July!” — Designed by Etienne Mansard from the UK.

Fire Camp

Heated Mountains

“Warm summer weather inspired the color palette.” — Designed by Marijana Pivac from Croatia.

Heated Mountains

Tutti Frutti

“July is National Ice Cream Month and who needs an invitation for a scoop or two, or three! Lacking the real thing, our Tutti Frutti wallpaper can satisfy until your next creamy indulgence.” — Designed by Karen Frolo from the United States.

Tutti Frutti

Memories In July

“Words are few, thoughts are deep, memories of you we’ll always keep.” — Designed by Suman Sil from India.

Memories In July

Cactus Hug

Designed by Ilaria Bagnasco from Italy.

Cactus Hug

Keep Moving Forward

“Snails can be inspiring! If you keep heading towards your goal, even if it is just tiny steps, enjoy the journey and hopefully it will be worth the effort.” — Designed by Glynnis Owen from Australia.

Keep Moving Forward

Sand And Waves

“What do you do in summer? You go to the beach. Even if you can’t go — feel the waves and sand and sun through this funny wallpaper.” — Designed by Olga Lepaeva from Russia.

Sand And Waves

Island River

“Make sure you have a refreshing source of ideas, plans and hopes this July. Especially if you are to escape from urban life for a while.” — Designed by Igor Izhik from Canada.

Island River

Ice Cream vs. Hot Dog

“It’s both ‘National Ice Cream Month’ and ‘National Hot Dog Month’ over in the US, which got me thinking — which is better? With this as your wallpaper, you can ponder the question all month!” — Designed by James Mitchell from the UK.

Ice Cream vs. Hot Dog

Summer Heat

Designed by Xenia Latii from Berlin, Germany.

Summer Heat

Peaceful Memories

“July is one of the most beautiful months of the year. Enjoy every day, every hour, which gives us this month!” — Designed by Nikolay Belikov from Russia.

Peaceful Memories

Floral Thing

“The wallpaper which I created consists of my personal sketches of Polish herbs and flowers and custom typography. I wanted it to be light and simple with a hint of romantic feeling. I hope you’ll enjoy it!” — Designed by Beata Kurek from Poland.

Smashing Desktop Wallpapers - July 2012

An Intrusion Of Cockroaches

“Ever watched Joe’s Apartment when you were a kid? Well that movie left a soft spot in my heart for the little critters. Don’t get me wrong: I won’t invite them over for dinner, but I won’t grab my flip flop and bring the wrath upon them when I see one running in the house. So there you have it… three roaches… bringing the smack down on that pesky human… ZZZZZZZAP!!” — Designed by Wonderland Collective from South Africa.

An Intrusion Of Cockroaches

Celebrate Freedom

“This wallpaper encourages you to appreciate and celebrate the country’s freedom as well as your own!” — Designed by Marina Zhukov from the USA.

Desktop Wallpaper

Hot Air Balloon

Designed by Studcréa from France

Hot Air Balloon

Only One

Designed by Elise Vanoorbeek from Belgium

Only one

Cool Summer

“Even though it is not summer in my country, I made a summer theme. A cool approach to summer themes, tough, very fresh and ‘twilighty’.” — Designed by Marcos Sandrini from Brazil.

Smashing Desktop Wallpapers - July 2012

Birdie Nam Nam

“I have created a pattern that has a summer feeling. For me July and summer is bright color, joy and lots of different flowers and birds. So naturally I incorporated all these elements in a crazy pattern.” — Designed by Lina Karlsson, Idadesign Ab from Sweden.

Smashing Desktop Wallpapers - July 2012

Sun In July

”…enjoy the sun in July!” — Designed by Marco Palma from Italy/Germany.

Smashing Desktop Wallpapers - July 2012

Join In Next Month!

Please note that we respect and carefully consider the ideas and motivation behind each and every artist’s work. This is why we give all artists the full freedom to explore their creativity and express emotions and experience throughout their works. This is also why the themes of the wallpapers weren’t anyhow influenced by us but rather designed from scratch by the artists themselves.

Thank you to all designers for their participation. Join in next month!

Smashing Editorial
(il)

Source: Smashing Magazine, The Joys Of July (2019 Wallpapers Edition)