CSS-Tricks is 12 years old! Firmly into that Early Adolescence stage, I’d say 😉 As we do each year, let’s reflect upon the past year. I’d better have something to say, right? Otherwise, John Prine would get mad at me.
How the hell can a person go to work in the morning
And come home in the evening and have nothing to say. – Angel From Montgomery
Easily the biggest change this year was design v17
We redesign most years, so it’s not terribly shocking I suppose that we did this year, but I think it’s fairly apparent that this was a big one. The biggest since v10.
The aesthetics of it still feel fresh to me, 6 months later. There are no plans at all yet for what the next version will be. I imagine this one will last a good couple of years with tweaks along the way. I’m always working on it. Just in the last few days, I have several commits cleaning things up, adding little features, and optimizing. That work is never done. v18 might just be a more thorough scrubbing of what is here. Might be a good release to focus on the back-end tech. I’ve always wanted to try some sort of MVC setup.
In a way, things feel easier.
There is a lot going right around here. We’ve got a great staff. Our editorial workflow, led by Geoff, has been smooth. There are ebbs and flows of how many great guest posts are in the pipeline, but it never seems to run dry and these days we stay more ahead than we ever have.
We stay quite organized with Notion. In fact, I still use it heavily across all the teams I’m on. It’s just as fundamental as Slack and email.
We’re still working with BuySellAds as a partner to help us sell advertising and sponsorship partnerships. We’ve worked with them for ages and they really do a good job with clean ad tech, smooth integration workflows, and finding good companies that want to run campaigns.
On the 10th anniversary I wrote:
If you do all the work, the hope is that you just keep to keep on keeping on. Everyone gets paid for their effort. This is not a hockey-stick growth kind of site. It’s a modest publication.
Yep.
Check out a year over year chart from Google Analytics:
I can look at that and celebrate the moments with growth. Long periods of 20% year over year growth, which is cool. Then if you look at just this last month, we’re more even or a little bit under 2018 (looking at only pageviews). Good to know, kinda, but I never put much stock in this kind of generic analytics. I’m glad we record them. I would want to know if we started tanking or growing hugely. But we never do. We have long slow steady growth and that is a comfortable place for me.
Thinking on ads
The world of advertising is tightly integrated around here, of course. I’m sure many of you look at this site and go JEEZ, LITTLE HEAVY ON THE ADS, EH? I hope it’s not too big a turnoff, as I really try to be tasteful with them. But another thing you should know is that the ad tech is clean. No tracking stuff. No retargetting. No mysterious third-party JavaScript. There might be an impression-tracking pixel here and there, but that’s about it. No slew of 100’s of requests doing god-knows-what.
That’s not by accident. It’s clear to me now how to go down that other road, and that road has money on it. Twice as much. But I look at it as what would be short term gains. Nobody is going to be more mad at me than you if I slap 80 tracking scripts on this site, my credibility amongst devs goes out the window along with any hopes of sustaining or growing this site. It’s no surprise to me that on sites without developers as an audience, the tendency is to go down the golden road of tracking scripts.
Even the tech is easier.
Just starting in July I’ve gotten all my sites on Flywheel hosting, and I’ve written about that here just today. Flywheel is a new sponsor here to the site, and I’m equally excited about that as I am in actually using it. Between using Local for local WordPress development, GitHub for repos, Buddy for deployment, Cloudflare for DNS/CDN… everything just feels smooth and easy right now.
The way I feel about tech at the moment is that nearly anything is doable. Even stuff that feels insurmountable. It’s just work. Do a bunch of work, get the thing done.
Fancy posts
One thing that we snuck in this year is the idea of posts that have special design applied to them. The term “Art-directed articles” seems to be the term that has stuck for that, for better or worse, and we’ve added to that.
There are posts like The Great Divide that I intentionally wanted to make stand out.
And now we’ve taken that and turned it into a template. The point of an art-directed article is to do something unique, so a template is a little antithetical to that, but I think this strikes a nice middle ground. The template assumes a big full-width header with background image under big title and then is otherwise just a centered column of type on white. The point is to use the template, then apply custom styles on top of it as needed to do something special for the post. I have a good feeling we’ll keep using it and have fun with it, and that it won’t be too burdensome for future designs.
Elsewhere
Last year at this time I was just settling into living in Bend, Oregon. It still feels that way. I’m in a new house now, that we’ve bought, and it feels like this is a very permanent living situation. But we’re less than a year into the new house so there is plenty of dust to settle. I’m still honeymooning on Bend as I just love it here so much. My daughter is just over a year and a half now so stability is very much what we’re after.
Professionally, most of my time is on CodePen, of course. There is a lot of overlap, like the fact that we work with BuySellAds on both sites and often sell across both. Plus working on CSS-Tricks always has me in CodePen anyway ;). Miraculously, Dave Rupert and I almost never miss a week on ShopTalk Show. Going strong after all these years. Never a shortage of stuff to talk about when it comes to websites.
Thank you
A bit hearty thanks from me! Y’all reading this site is what makes it possible.
I first heard of Flywheel through their product Local, which is a native app for working on WordPress sites. If you ask around for what people use for that kind of work, you’ll get all sorts of answers, but an awful lot of very strong recommendations for Local. I’ve become one of them! We ultimately did a sponsored post for Local, but that’s based on the fact that now 100% of my local WordPress development work is done using it and I’m very happy with it.
Now I’ve taken the next step and moved all my production sites to Flywheel hosting!
Full disclosure here, Flywheel is now a sponsor of CSS-Tricks. I’ve been wanting to work with them for a while. I’ve been out to visit them in Omaha! (? at Jamie, Christi, Karissa, and everybody I’ve worked with over there.) Part of our deal includes the hosting. But I was a paying customer and user of Flywheel before this on some sites, and my good experiences there are what made me want to get this sponsorship partnership cooking! There has been big recent news that Flywheel was acquired by WP Engine. I’m also a fan of WP Engine, being also a premium WordPress host that has done innovative things with hosting, so I’m optimistic that a real WordPress hosting powerhouse is being formed and I’ve got my sites in the right place.
Developing on Local is a breeze
It feels like a breath of fresh air to me, as running all the dev dependencies for WordPress has forever been a bit of a pain in the butt. Sometimes you have it going fine, but then something breaks in the most inscrutable possible way and it takes forever to get going again. Whatever, you know what I mean. At this point, I’ve been running Local for over a year and have had almost no issues with it.
There are all kinds of features worth checking out here. Here’s one that is very likely useful to bigger teams. Say you have a Flywheel account with a bunch of production sites on it. Then a new person starts working with you and they have their own computer. You connect Local to Flywheel, and you can pull down the site and have it ready to work on. That’s pretty sweet.
Local doesn’t lock you into anything either. You can use Local for local development and literally use nothing else. Local can push a site up to Flywheel hosting too, which I’ve found to be mighty useful particularly for that first deployment of a new site, but you don’t have to use that if you don’t want. I’ll cover more about workflow below.
Other features that I find worthy of note:
Spinning up a new site takes just a second. A quick walkthrough through a wizard where they ask you some login details but otherwise offer smart-but-customizable defaults.
Dealing with HTTPS locally is easy. It will create a certificate for you and trust it locally with one click.
You can flip on “Live Link”, which uses ngrok to create a live, sharable URL to your localhost site. Great for temporarily showing a client or co-worker something without having to move anything.
One click to pop open the database in Sequel Pro, my favorite free database tool. Much easier than trying to spin up phpMyAdmin or whatever on the web to manage from there.
Flywheel’s Dashboard is so clear
I love the simple UI of Local, and I really like how that same design and spirit carries over into the Flywheel hosting dashboard.
There are so many things the dashboard makes easy:
You need an SSL cert? Click some buttons.
Wanna force HTTPS? Flip a switch.
Wanna convert the site to Multisite? Hit a button.
Need to edit the database? There is a UI around it built in.
Want a CDN? Toggle a thing.
Need to invite a collaborator on a site? Go for it.
Need a backup? There are in there, download it or restore to that point.
It’s a big deal when everything is simple and works. It means you aren’t burning hours fighting with tools and you can use them doing work that pushes you forward.
Workflow
When I set up my new CSS-Tricks workflow, I had Flywheel move the site for me (thanks gang!) (no special treatment either, they’ll do that for anybody).
I’ve got Local already, so my local development process is the same. But I needed to update my deployment workflow for the new hosting. Local can push a site up to Flywheel hosting, but it just zips everything up and sends it all up. Great for first deployment but not perfect for tiny little changes like 95% of the work I do. There is a new Local for Teams feature, which uses what they call MagicSync for deployment, which only deploys changed files. That’s very cool, but I like working with a Git-based system, where ultimately merges to master are what trigger deployment of the changed files.
For years I’ve used Beanstalk for Git-based deployment over SFTP. I still am using Beanstalk for many sites and think it’s a great choice, but Beanstalk has the limitation that the Git-repo is basically a private Git repo hosted by Beanstalk itself.
During this change, I needed to switch up what the root of the repo is (more on that in a second) so I wanted to create a new repo. I figured rather than doing that on Beanstalk, I’d make a private GitHub repo and set up deployment from there. There are services like DeployHQ and DeployBot that will work well for that, but I went with Buddy, which has a really really nice UI for managing all this stuff, and is capable of much more than just deployment should I ultimately need that.
Regarding the repo itself, one thing that I’ve always done with my WordPress sites is just make the repo the whole damn thing starting at the root. I think it’s just a legacy/comfort thing. I had some files at the root I wanted to deploy along with everything else and that seemed like the easiest way. In WordPress-land, this isn’t usually how it’s done. It’s more common to have the /wp-content/ folder be the root of the repo, as those are essentially the only files unique to your installation. I can imagine setups where even down to individual themes are repos and deployed alone.
I figured I’d get on board with a more scoped deployment, but also, I didn’t have much of a choice. Flywheel literally locks down all WordPress core files, so if your deployment system tries to override them, it will just fail. That actually sounds great to me. There is no reason anyone from the outside should alter those files, might as well totally remove it as an attack vector. Flywheel itself keeps the WordPress version up to date. So I made a new repo with /wp-content/ at the root, and I figured I’d make it on GitHub instead just because that’s such an obvious hub of developer activity and keeps my options wide open for deployment choices.
For the same kind of spiritual reasons, during the the move, I moved the DNS over to Cloudflare. This gives me control over DNS from a third-party so it’s easy for me to point things where I need them. Kind of a decentralization of concerns. That’s not for everyone, but it’s great for me on this project. While now I might suffer from Cloudflare outages (rare, but it literally just happened), I benefit from all sorts of additional security and performance that Cloudflare can provide.
So the workflow is Local > GitHub > Buddy > Flywheel.
And the hosting is Cloudflare > Flywheel with image assets on Cloudinary.
And I’ve got backups from both Flywheel and Jetpack/VaultPress.
Back in April, jQuery 3.0 was released with all the pomp and ceremony of a British monarch on the 4th of July (happy Independence Day to all our American readers). There have been a flurry of minor releases since then, with the current version being 3.4.1.
We’ve had a few weeks to poke around, and check out the new features, and more importantly the now deprecated features. What we found was a modern library that defied a few of our expectations.
It’s Not Bloated
There are quite a few myths built up around jQuery, not least that it is bloated and slow. There’s some sense to this, containing as it does code that you probably won’t need. However this is true of all libraries, frameworks, and 3rd party scripts; unless you’re using something so niche that there is nothing superfluous wrapped up in the code, then there will always be a few bytes here and there that aren’t required.
drop a jpg and you’ll probably have room to spare
But let’s keep this in proportion: the raw, minified, production version of jQuery is 88kb, if you opt for the slim version without Ajax and the effects, then it’s just 71kb.
If you’re working to a strict size quota it’s relatively simple to squeeze 71kb out of a few images. Better yet, drop a jpg and you’ll probably have room to spare.
It’s (Probably) Not Cached
One of the greatest benefits of jQuery circa 2014, was that it was all but ubiquitous. Almost every site took advantage of this by linking directly to the jQuery CDN meaning that more often than not it was cached in the user’s browser – you could, in effect, use it for zero size cost.
jQuery usage has dipped in recent years, thanks in part to rival libraries, and thanks in part to the belief that vanilla JavaScript is superior. Fewer sites are linking to the CDN, fewer browsers have the file cached, and so it will probably need to be downloaded before it’s available.
The problem is compounded by the release for version 3, because although plenty of legacy sites are still loading jQuery from the CDN, by their nature they aren’t linking to the current version. The jQuery team have gone to great lengths to help you upgrade but project cycles and code adoption means that jQuery 3.n is probably at least several years away from comprehensive coverage.
You Don’t Need to Know Vanilla JavaScript
One of the most oft-repeated myths – or perhaps I should say ‘untruths’ in this case – is that you should learn vanilla JavaScript before you can use jQuery well.
View jQuery as a set of training wheels
I detest this view. I understand the logic; if you learn the underlying logic of the language, you’ll understand what’s happening behind the scenes and have a nuanced understanding of your code. People who perpetuate that view have forgotten what it feels like to be trawling Stack Overflow at three in the morning struggling to understand why document.getElementsByClassName needs a [0] on the end.
Yes, you do need to understand language basics, like variables, loops, conditionals, operators, and so forth; you’ll need to know those with or without jQuery. View jQuery as a set of training wheels, it does the hard stuff for you, so the easy stuff has a chance to become second nature.
I would argue that if you want to learn JavaScript, you should learn jQuery first. It will hold your hand through the tougher tasks (like Ajax) while you practice your fundamentals. And I say that as someone who spends a great deal of time coding vanilla JavaScript.
There’s No Longer a Rich Ecosystem
There was a time when jQuery developers were queuing up to deliver time-saving, feature-pushing, bolt-ons. That’s no longer the case.
The web has moved on, and the gun-for-hire developers are chasing more lucrative markets, like Shopify, or WordPress. Legacy jQuery plugins (should) still work, and in the case of version 2.n plugins – thanks to the jQuery team’s commitment to backwards compatibility – will (probably) not break using 3.n. (Do make sure to test with the development version of jQuery and check your console, if you’re going down this route.)
There will undoubtedly be some kind of market for jQuery 3.n plugins, and those developers who deem it financially worthwhile to update their code will do so. So for plugins, the release of jQuery 3.0 may actually prompt a long-overdue cull.
Should You be Using jQuery 3?
For many of us, jQuery is a tool we used years ago, and have since moved on. There are better tools for handling Ajax; jQuery still doesn’t play nice with SVG, making its use for DOM manipulation increasingly limited; there are copycat libraries that mimic the best aspects of jQuery, like the selectors, without packaging up everything else.
jQuery isn’t going to challenge Vue.js, or AngularJS. jQuery is no longer the must-befriend big kid on the block. What jQuery does is simplify JavaScript’s verbosity and enable inexperienced JavaScript coders to maximize their use of the language.
If you’re new to JavaScript, or if you’ve tinkered with it before without ever being able to do anything really cool, then jQuery is still exactly what you’re looking for.
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
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:
On occasion, the Red Bull website will put a similar-looking red ticker on its hamburger menu icon (this one is flashing though):
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.
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:
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:
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.
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:
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:
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.
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.
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.
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:
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:
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:
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.
Flyout menus! The second you need to implement a menu that uses a hover event to display more menu items, you’re in tricky territory. For one, they should work with clicks and taps, too. Without that, you’ve broken the menu for anyone without a mouse. That doesn’t mean you can’t also use :hover. When you use a hover state to reveal more content, that means an un-hovering state needs to hide them. Therein lies the problem.
The problem is that if a submenu pops out somewhere on hover, getting your mouse over to it might involve moving it along a fairly narrow corridor. Accidentally move outside that area, the menu can close, and it can be an extremely frustrating UX moment.
The most compelling examples that solve this issue are the ones that involve extra hidden “hit areas.” Amazon doesn’t really have menus like this anymore (that I can see), and perhaps this is one of the reasons why. But in the past, they’ve used this hit area technique. We could call them “dynamic hit areas” because they were drawn based on the position of the parent element and the submenus:
I haven’t seen a lot of implementations of this lately, but just recently, Hakim El Hattab included a modern implementation of this in his talk at CSS Day 2019. The implementation leverages drawing the areas dynamically with SVG. You don’t actually see the hit areas, but they do look like this, thus forming paths for that prevent hover-offs.
I’ll include a YouTube embed of the talk starting at that point here:
The way he draws the hit area is so fancy it makes me all kinds of happy:
Hey, let’s create a functional calendar app with the JAMstack
I’ve always wondered how dynamic scheduling worked so I decided to do extensive research, learn new things, and write about the technical part of the journey. It’s only fair to warn you: everything I cover here is three weeks of research condensed into a single article. Even though it’s beginner-friendly, it’s a healthy amount of reading. So, please, pull up a chair, sit down and let’s have an adventure.
My plan was to build something that looked like Google Calendar but only demonstrate three core features:
List all existing events on a calendar
Create new events
Schedule and email notification based on the date chosen during creation. The schedule should run some code to email the user when the time is right.
Pretty, right? Make it to the end of the article, because this is what we’ll make.
The only knowledge I had about asking my code to run at a later or deferred time was CRON jobs. The easiest way to use a CRON job is to statically define a job in your code. This is ad hoc — statically means that I cannot simply schedule an event like Google Calendar and easily have it update my CRON code. If you are experienced with writing CRON triggers, you feel my pain. If you’re not, you are lucky you might never have to use CRON this way.
To elaborate more on my frustration, I needed to trigger a schedule based on a payload of HTTP requests. The dates and information about this schedule would be passed in through the HTTP request. This means there’s no way to know things like the scheduled date beforehand.
We (my colleagues and I) figured out a way to make this work and — with the help of Sarah Drasner’s article on Durable Functions — I understood what I needed learn (and unlearn for that matter). You will learn about everything I worked in this article, from event creation to email scheduling to calendar listings. Here is a video of the app in action:
You might notice the subtle delay. This has nothing to do with the execution timing of the schedule or running the code. I am testing with a free SendGrid account which I suspect have some form of latency. You can confirm this by testing the serverless function responsible without sending emails. You would notice that the code runs at exactly the scheduled time.
Tools and architecture
Here are the three fundamental units of this project:
React Frontend: Calendar UI, including the UI to create, update or delete events.
8Base GraphQL: A back-end database layer for the app. This is where we will store, read and update our date from. The fun part is you won’t write any code for this back end.
Durable Functions: Durable functions are kind of Serverless Functions that have the power of remembering their state from previous executions. This is what replaces CRON jobs and solves the ad hoc problem we described earlier.
The rest of this post will have three major sections based on the three units we saw above. We will take them one after the other, build them out, test them, and even deploy the work. Before we get on with that, let’s setup using a starter project I made to get us started.
You can set up this project in different ways — either as a full-stack project with the three units in one project or as a standalone project with each unit living in it’s own root. Well, I went with the first because it’s more concise, easier to teach, and manageable since it’s one project.
The app will be a create-react-app project and I made a starter for us to lower the barrier to set up. It comes with supplementary code and logic that we don’t need to explain since they are out of the scope of the article. The following are set up for us:
Calendar component
Modal and popover components for presenting event forms
Event form component
Some GraphQL logic to query and mutate data
A Durable Serverless Function scaffold where we will write the schedulers
Tip: Each existing file that we care about has a comment block at the top of the document. The comment block tells you what is currently happening in the code file and a to-do section that describes what we are required to do next.
Install the npm dependencies described in the root package.json file as well as the serverless package.json:
npm install
Orchestrated Durable Functions for scheduling
There are two words we need to get out of the way first before we can understand what this term is — orchestration and durable.
Orchestration was originally used to describe an assembly of well-coordinated events, actions, etc. It is heavily borrowed in computing to describe a smooth coordination of computer systems. The key word is coordinate. We need to put two or more units of a system together in a coordinated way.
Durable is used to describe anything that has the outstanding feature of lasting longer.
Put system coordination and long lasting together, and you get Durable Functions. This is the most powerful feature if Azure’s Serverless Function. Durable Functions based in what we now know have these two features:
They can be used to assemble the execution of two or more functions and coordinate them so race conditions do not occur (orchestration).
Durable Functions remember things. This is what makes it so powerful. It breaks the number one rule of HTTP: stateless. Durable functions keep their state intact no matter how long they have to wait. Create a schedule for 1,000,000 years into the future and a durable function will execute after one million years while remembering the parameters that were passed to it on the day of trigger. That means Durable Functions are stateful.
These durability features unlock a new realm of opportunities for serverless functions and that is why we are exploring one of those features today. I highly recommend Sarah’s article one more time for a visualized version of some of the possible use cases of Durable Functions.
I also made a visual representation of the behavior of the Durable Functions we will be writing today. Take this as an animated architectural diagram:
A data mutation from an external system (8Base) triggers the orchestration by calling the HTTP Trigger. The trigger then calls the orchestration function which schedules an event. When the time for execution is due, the orchestration function is called again but this time skips the orchestration and calls the activity function. The activity function is the action performer. This is the actual thing that happens e.g. “send email notification”.
Create orchestrated Durable Functions
Let me walk you through creating functions using VS Code. You need two things:
Once you have both setup, you need to tie them together. You can do this using a VS Code extension and a Node CLI tool. Start with installing the CLItool:
npm install -g azure-functions-core-tools
# OR
brew tap azure/functions
brew install azure-functions-core-tools
Now that you have all the setup done, let’s get into creating these functions. The functions we will be creating will map to the following folders.
Folder
Function
schedule
Durable HTTP Trigger
scheduleOrchestrator
Durable Orchestration
sendEmail
Durable Activity
Start with the trigger.
Click on the Azure extension icon and follow the image below to create the schedule function
Since this is the first function, we chose the folder icon to create a function project. The icon after that creates a single function (not a project).
Click Browse and create a serverless folder inside the project. Select the new serverless folder.
Select JavaScript as the language. If TypeScript (or any other language) is your jam, please feel free.
Select Durable Functions HTTP starter. This is the trigger.
Name the first function as schedule
Next, create the orchestrator. Instead of creating a function project, create a function instead.
Click on the function icon:
Select Durable Functions orchestrator.
Give it a name, scheduleOrchestrator and hit Enter.
You will be asked to select a storage account. Orchestrator uses storage to preserve the state of a function-in-process.
Select a subscription in your Azure account. In my case, I chose the free trial subscription.
Follow the few remaining steps to create a storage account.
Finally, repeat the previous step to create an Activity. This time, the following should be different:
Select Durable Functions activity.
Name it sendEmail.
No storage account will be needed.
Scheduling with a durable HTTP trigger
The code in serverless/schedule/index.js does not need to be touched. This is what it looks like originally when the function is scaffolded using VS Code or the CLI tool.
We’re creating a durable function on the client side that is based on the context of the request.
We’re calling the orchestrator using the client’s startNew() function. The orchestrator function name is passed as the first argument to startNew() via the params object. A req.body is also passed to startNew() as third argument which is forwarded to the orchestrator.
Finally, we return a set of data that can be used to check the status of the orchestrator function, or even cancel the process before it’s complete.
The URL to call the above function would look like this:
It’s also good to know that you can change how this URL looks.
Orchestrating with a Durable Orchestrator
The HTTP trigger startNew call calls a function based on the name we pass to it. That name corresponds to the name of the function and folder that holds the orchestration logic. The serverless/scheduleOrchestrator/index.js file exports a Durable Function. Replace the content with the following:
What this line does use the Durable Function to create a timer based on the date passed in from the request body via the HTTP trigger.
When this function executes and gets here, it will trigger the timer and bail temporarily. When the schedule is due, it will come back, skip this line and call the following line which you should use in place of TODO -- 2.
When a schedule is due, the orchestrator comes back to call the activity. The activity file lives in serverless/sendEmail/index.js. Replace what’s in there with the following:
It currently imports SendGrid’s mailer and sets the API key. You can get an API Key by following these instructions.
I am setting the key in an environmental variable to keep my credentials safe. You can safely store yours the same way by creating a SENDGRID_API_KEY key in serverless/local.settings.json with your SendGrid key as the value:
This pulls out the event information from the input from the orchestrator function. The input is attached to context.bindings. payload can be anything you name it so go to serverless/sendEmail/function.json and change the name value to payload:
Deploying functions to Azure is easy. It’s merely a click away from the VS Code editor. Click on the circled icon to deploy and get a deploy URL:
Still with me this far in? You’re making great progress! It’s totally OK to take a break here, nap, stretch or get some rest. I definitely did while writing this post.
Data and GraphQL layer with 8Base
My easiest description and understanding of 8Base is “Firebase for GraphQL.” 8Base is a database layer for any kind of app you can think of and the most interesting aspect of it is that it’s based on GraphQL.
The best way to describe where 8Base fits in your stack is to paint a picture of a scenario.
Imagine you are a freelance developer with a small-to-medium scale contract to build an e-commerce store for a client. Your core skills are on the web so you are not very comfortable on the back end. though you can write a bit of Node.
Unfortunately, e-commerce requires managing inventories, order management, managing purchases, managing authentication and identity, etc. “Manage” at a fundamental level just means data CRUD and data access.
Instead of the redundant and boring process of creating, reading, updating, deleting, and managing access for entities in our backend code, what if we could describe these business requirements in a UI? What if we can create tables that allow us to configure CRUD operations, auth and access? What if we had such help and only focus on building frontend code and writing queries? Everything we just described is tackled by 8Base
Here is an architecture of a back-end-less app that relies on 8Base as it’s data layer:
Create an 8Base table for events storage and retrieval
The first thing we need to do before creating a table is to create an account. Once you have an account, create a workspace that holds all the tables and logic for a given project.
Next, create a table, name the table Events and fill out the table fields.
We need to configure access levels. Right now, there’s nothing to hide from each user, so we can just turn on all access to the Events table we created:
Setting up Auth is super simple with 8base because it integrates with Auth0. If you have entities that need to be protected or want to extend our example to use auth, please go wild.
Finally, grab your endpoint URL for use in the React app:
Testing GraphQL Queries and mutation in the playground
Just to be sure that we are ready to take the URL to the wild and start building the client, let’s first test the API with a GraphQL playground and see if the setup is fine. Click on the explorer.
Paste the following query in the editor.
query {
eventsList {
count
items {
id
title
startAt
endAt
description
allDay
email
}
}
}
I created some test data through the 8base UI and I get the result back when I run they query:
You can explore the entire database using the schema document on the right end of the explore page.
Calendar and event form interface
The third (and last) unit of our project is the React App which builds the user interfaces. There are four major components making up the UI and they include:
Calendar: A calendar UI that lists all the existing events
Event Modal: A React modal that renders EventForm component to create a component
Event Popover: Popover UI to read a single event, update event using EventForm or delete event
Event Form: HTML form for creating new event
Before we dive right into the calendar component, we need to setup React Apollo client. The React Apollo provider empowers you with tools to query a GraphQL data source using React patterns. The original provider allows you to use higher order components or render props to query and mutate data. We will be using a wrapper to the original provider that allows you query and mutate using React Hooks.
In src/index.js, import the React Apollo Hooks and the 8base client in TODO -- 1:
import { ApolloProvider } from 'react-apollo-hooks';
import { EightBaseApolloClient } from '@8base/apollo-client';
At TODO -- 2, configure the client with the endpoint URL we got in the 8base setup stage:
const URI = 'https://api.8base.com/cjvuk51i0000701s0hvvcbnxg';
const apolloClient = new EightBaseApolloClient({
uri: URI,
withAuth: false
});
Use this client to wrap the entire App tree with the provider on TODO -- 3:
Let’s add a simple error and loading handler on TODO -- 2:
if (error) return console.log(error);
if (loading)
return (
<div className="calendar">
<p>Loading...</p>
</div>
);
Notice that the Calendar component uses the EventPopover component to render a custom event. You can also observe that the Calendar component file renders EventModal as well. Both components have been setup for you, and their only responsibility is to render EventForm.
Create, update and delete events with the event form component
The component in src/components/Event/EventForm.js renders a form. The form is used to create, edit or delete an event. At TODO -- 1, import useCreateUpdateMutation and useDeleteMutation:
import {useCreateUpdateMutation, useDeleteMutation} from './eventMutationHooks'
useCreateUpdateMutation: This mutation either creates or updates an event depending on whether the event already existed.
useDeleteMutation: This mutation deletes an existing event.
A call to any of these functions returns another function. The function returned can then serve as an even handler.
Now, go ahead and replace TODO -- 2 with a call to both functions:
These are custom hooks I wrote to wrap the useMutation exposed by React Apollo Hooks. Each hook creates a mutation and passes mutation variable to the useMutation query. The blocks that look like the following in src/components/Event/eventMutationHooks.js are the most important parts:
We have spent quite some time in building the serverless structure, data storage and UI layers of our calendar app. To recap, the UI sends data to 8base for storage, 8base saves data and triggers the Durable Function HTTP trigger, the HTTP trigger kicks in orchestration and the rest is history. Currently, we are saving data with mutation but we are not calling the serverless function anywhere in 8base.
8base allows you to write custom logic which is what makes it very powerful and extensible. Custom logic are simple functions that are called based on actions performed on the 8base database. For example, we can set up a logic to be called every time a mutation occurs on a table. Let’s create one that is called when an event is created.
Start by installing the 8base CLI:
npm install -g 8base
On the calendar app project run the following command to create a starter logic:
8base init 8base
8base init command creates a new 8base logic project. You can pass it a directory name which in this case we are naming the 8base logic folder, 8base — don’t get it twisted.
Trigger scheduling logic
Delete everything in 8base/src and create a triggerSchedule.js file in the src folder. Once you have done that, drop in the following into the file:
The information about the GraphQL mutation is available on the event object as data.
Replace with the URL you got after deploying your function. You can get the URL by going to the function in your Azure URL and click “Copy URL.”
You also need to install the node-fetch module, which will grab the data from the API:
npm install --save node-fetch
8base logic configuration
The next thing to do is tell 8base what exact mutation or query that needs to trigger this logic. In our case, a create mutation on the Events table. You can describe this information in the 8base.yml file:
In a sense, this is saying, when a create mutation happens on the Events table, please call src/triggerSchedule.js after the mutation has occurred.
We want to deploy all the things
Before anything can be deployed, we need to login into the 8Base account, which we can do via command line:
8base login
Then, let’s run the deploy command to send and set up the app logic in your workspace instance.
8base deploy
Testing the entire flow
To see the app in all its glory, click on one of the days of the calendar. You should get the event modal containing the form. Fill that out and put a future start date so we trigger a notification. Try a date more than 2-5 mins from the current time because I haven’t been able to trigger a notification any faster than that.
Yay, go check your email! The email should have arrived thanks to SendGrid. Now we have an app that allows us to create events and get notified with the details of the event submission.
I was just chatting with Dave and he told me about Haunted. It’s hooks, but for native web components! Pretty cool. I think the existence of stuff like this makes using web components more and more palatable — particularly in that totally-native no-build-step-needed-at-all kinda way.
I get that there are all sorts of issues with web components, but the things that typically turn me away from them are a lack of nice templating and rerendering and no state management.
But we can knock those two out right quick these days…
import { html } from "https://unpkg.com/lit-html/lit-html.js";
import { component, useState} from "https://unpkg.com/haunted/haunted.js";
function App() {
const [name, setName] = useState("Chris");
return html`
<div class="module">
Hello, ${name}!
</div>
`;
}
customElements.define("my-app", component(App));
The CodePen Challenge this week is using the Star Wars API, so let’s make a fetch request and use that to fill state. That’s a great use case for useEffect.
Just imagine it, you walk into your office, and you get a notification: your client has just dropped everything you need into your cloud storage provider of choice. The copy brings tears of joy to your eyes, the images are crisp, clean, and huge. The logo is a work of art, and the client has sent a note saying, “Actually, we don’t need you to finish up for another three months, but why don’t I just pay you double right now?”
And then you wake up.
The truth is that your deadline is the same, but they’re “just wondering if you could speed things up a little”, the provided images are 640×480 and just blurry enough to be annoying, the logo is an abomination made in Word, and the brand’s colors remind you of those awful school uniforms you used to wear.
some people have a talent for picking the absolute worst shades of brown, yellow, and green for their brand
Okay, now I’m just being mean, but it’s a sad reality that we often have to work with sub-par assets in web design projects. Logos don’t always look great, and some people have a talent for picking the absolute worst shades of brown, yellow, and green for their brand. It’s enough to make you throw your hands in the air flamboyantly and shout, “I just can’t work with this!”
But what about all those times when you don’t have a choice, and your clients refuse to let you completely redo their branding? You know, most of the time. Well, you do have a few options.
Ugly Logos
Just kind of ignore the logo as much as you can, really. I mean, obviously, it needs to be there, probably in the upper-left, or in the upper-middle. But just sort of… leave it there. There’s not much you can really do about a logo. The users need to see it, and the client will definitely be annoyed if it’s not there.
if there was ever a time to push back when the client asks you to make the logo bigger, this is it
However, if there was ever a time to push back when the client asks you to make the logo bigger, this is it. And where a lot of sites will incorporate the logo mark into other aspects of the design, this time, it’s staying in its designated spot. Maybe if the rest of the site looks drastically better than the logo, it will give the client pause, and perhaps even a reason to get it redone.
Thankfully, few logos are ever truly that ugly, at least among clients who can afford you. Overcomplicated and hard to see at small sizes? Yes. Generic and boring? Sure. But WordArt-ugly? Thankfully that’s not as common as it once was.
Ugly Colors
Colors are another story, and for every color combination that sparks joy, Marie Kondo-style, there’s a combination that sparks nausea. But hey, brand guides are brand guides, and you gotta follow the brand guide.
The best thing to do is to use the colors, but as sparingly as possible. Many colors are only truly terrible when they’re in close proximity to each other, so use some (probably literal) white space to your advantage. Keep them apart, and use them only for emphasis.
Will your client ever demand that you make the entire background puke-green? It’s possible. Even then, maybe you could get away with using a gradient to minimize any negative effects.
Low-Quality Images
Once again, embrace the white space. If all you have to work with are a bunch of very small images, make that small size look intentional. Make those tiny images the center of attention, and pray most of your users aren’t using retina screens. You can get away with a lot if you make it look like you planned it all from the very beginning.
if Instagram has taught us anything, it’s that excessive filtering can make any old photo look like it’s supposed to be art
Another way to do this is to use the images as large, blurry backgrounds. This works best with landscapes and macro shots, though. That company “team portrait” doesn’t work as well for background material, sadly.
Lastly, if Instagram has taught us anything, it’s that excessive filtering can make any old photo look like it’s supposed to be art. Sometimes. Add some film grain, desaturate the photos, and maybe throw in some harsh, spotty lighting. Users can’t judge what can barely be seen.
I’m only half joking.
Legacy UI Elements & Widgets
Now this is a rare situation, but on occasion, a client might have some leftover UI assets that they really, really love. Maybe they designed their first shiny Web 2.0 button in Xara 3D nearly twenty years ago, and they just have to use it. Maybe they have a favorite graph showing how their business works.
Again, this isn’t common, but you may find yourself having to work around it. For the example of the graph, I’d put a full-on skeuomorphic, photo-realistic picture frame around it. I’d treat it like a piece of the company’s history. An approach like this could also work for the example of the button.
When All Else Fails, Lean Into the Ugly
Brutalism is a thing. It’s like I said before: you can get away with a lot if you make it look like you fully intended to use butt-ugly color palettes, low-quality images, or even ‘90s clipart. That’s right, you can make clipart work. I’ve seen people do it.
Embrace the aesthetic, and call it retro, or call it ironic, I don’t care. I mean—and I admit, this is the example I use every time I talk about less-than-pretty design—people still love Craigslist. Making the website work is more important than making it beautiful. Giving the user what they want, when they want it, is worth a million high-resolution photos, and Jon-Hicks-designed logos.
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:
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.
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>
<div class="frame">
<slot>This is the default content if nothing gets specified to go here</slot>
</div>
</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>
<div class="frame">
<header><h2><slot name="header">Title</slot></h2></header>
<slot>This is the default content if nothing gets specified to go here</slot>
</div>
</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:
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:
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:
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.
Now we can use it everywhere with whatever content you want:
<!-- somewhere else, using my-button.vue -->
<template>
<my-button>
<img src="/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.
<!-- 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>
<div class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<slot name="header"></slot>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<slot name="body"></slot>
</div>
<div class="modal-footer">
<!--
using `v-bind` shorthand to pass the `closeModal` method
to the component that will be in this slot
-->
<slot name="footer" :closeModal="closeModal"></slot>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
//...
methods: {
closeModal () {
// Do what needs to be done to close the modal... and maybe remove it from the DOM
}
}
}
</script>
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:
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.
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>
<div>
<promised :promise="somePromise">
<template #resolved="{ data }">
Resolved: {{ data }}
</template>
<template #rejected="{ error }">
Rejected: {{ error }}
</template>
<template #pending>
Working on it...
</template>
</promised>
</div>
</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.
Git is command-line-driven software, but that doesn’t mean you have to use the command line to make it work. There are lots of options! Some of the deepest programmer nerds I know prefer to use GUIs for Git (Graphic
User Interface, or you know, software you can see things and click stuff), and some near pure-designers I know prefer working with the command line for Git. Swear to Git.
Lemme round up what look like the major players for Git GUIs these days.
No matter how much you love the CLI, don’t GUI-shame. Lots of perfectly amazing programmers like working with GUIs, and it’s perfectly fine.
There’s some weird gatekeeping tendencies centered around the command line. #DevDiscuss
I’ve used Tower for ages and it’s the one used the most. I’m not sure the exact release dates of all these, but I feel like Tower was an early player here. They’ve been around a long time and continuously improve, which I always respect.
This is a 2.0 of the original GitHub Desktop. I had some gripes with the 1.0 version in that its terminology was weird (to me) and seemed to vastly deviate from Git, which was more confusing than it was worth (again, to me). This version cleans most of that up. It’s deeply integrated into GitHub so it makes GitHubb-y things (e.g. pull requests) feel like first-class citizens, but it will still happily work with any Git repo.
I’m pretty intrigued by this one. Upgrading (monthly cost) to get the in-app merge conflict tool seems worth it, but you also have to upgrade to access private repos. It seems highly feature rich, but I think my favorite part is the dark-with-rainbow-accent-colors theme.
You might be compelled by Sourcetree if you’re a big Bitbucket user because they are both Atlassian products. I know it works for any Git repo though. I imagine there is some smooth Bitbucket integration stuff with this, similar to the GitHub/GitHub Desktop connection.
You don’t really think of Coda as a version control tool (it’s more of a direct-to-FTP thing), and even though I’d argue the support for it is fairly half-baked, it does work! Seems likely the next evolution of Coda will address this.
Having version control right in your IDE like this, to me, feels like kind of a tweener between GUI and CLI. There are a lot of features here, but it’s not really a full-blown GUI to me, but you’ve got a terminal built in right there so it almost encourages that. A lot of Git usage is pretty basic pulling, committing, and pushing — so having this right within the app is kinda sweet.
(I imagine there are lots of other IDEs that offer version control features. PHPStorm, etc.)