I don’t think this is an epidemic or anything, but I’ve seen it done a few times and even advocated for. This is what I mean…
You go to Google Fonts and pick a font like Open Sans, and it gives you either a <link> or an @import with a URL there in which to ready this font for usage on your site.
You can take a peek in there and see what it returns…
It’s just some @font-face declarations, of course!
Now your performance-minded brain kicks off. Wait. So, I make one HTTP request for this stylesheet, and then it makes more HTTP requests for those woff2 files it’s linking up. Screw the middle man here, why not just copy those @font-face blocks right out of here and use them.
You can! But!
The issue is that Google does fancy Google things here and the contents of that original stylesheet changes based on the browser requesting it. That screenshot above is Chrome 66. Here’s Firefox 20 on Windows 7:
It’s different! It’s only got woff, not woff2. If we open that URL in IE 8, we’d get an @font-face block that includes the eot format!
The point is, what that URL gives is very specific to what the current browser needs. That’s a pretty cool thing to abstract away and not worry about. Should new browsers have new formats and new CSS syntax needed, that’ll just come along for the ride.
Not that Google Fonts is perfect with this stuff. For example, by not controlling your own @font-face blocks, you can’t take advantage of font-display, which is a shame. Maybe we’ll get that someday, or maybe it’s worth self-hosting your Google Fonts, which is another whole thing we’ll get into someday.
We’ve been working with Jetpack around here as a sponsor. It’s a great match because as someone with a bunch of self-hosted WordPress sites, Jetpack is one of those no-brainer plugins for me. Jetpack can do a ton of good things for any site in a variety of very different ways. Here’s one way to think about it: it brings the power of WordPress’ own massive servers to you.
For now, let’s just focus on one angle of what Jetpack can do for you: image performance. Jetpack does a ton for you in this regard, solving some non-trivial performance upgrades. Let’s take a look at what I see as the four big boosts you get from Jetpack on your images.
1) WordPress does responsive images for you
OK, I cheated with the first one because you don’t actually need Jetpack to benefit from this. But it’s an important and foundational concept for fast images. Just by using WordPress, you get basic responsive images for free.
If you already know what I’m talking about, here’s an example of the output you’ll see in the DOM of a published WordPress post with an image in it uploaded via the Media Uploader:
It’s wonderful to get this for free, as writing out responsive images syntax by hand is quite cumbersome.
If you are new to the idea of responsive images, the big idea is this: rather than a single image going to any browser visiting your website, you have multiple images in different sizes and the most-correct one is delivered. Imagine instead of a mobile phone downloading a 1600 pixel wide image (way bigger than it needs), it only downloads a 320-pixel wide image, saving a ton of downloading time.
Read a bit about web performance and you’ll be unanimously told: „use a CDN.” A CDN is a Content Delivery Network, essentially web servers designed specifically to make serving assets like images super fast. They call it a network because it isn’t just one server, it’s many servers physically located all over the world so that when your website is requested from different locations all around the world, the files being sent back come from geographically closer locations (faster!). Not to mention it does other clever things like not requiring cookies for each web request like your own server probably does.
Literally, flip a switch in Jetpack and you’ll be using an image CDN:
Site speed is impacted by many factors and one of them is content delivery. Using what is referred to as a content delivery network (or CDN) helps by:
Delivering your content from high-speed and dedicated data centers.
More files can be downloaded simultaneously by the browser.
Distributed data centers (ie in different geographic locations) improve download speeds and provide redundancy.
By distributing load and save bandwidth you reduce your existing hosting costs (or keep them in check).
3) You get optimization
Una Kravets calls image optimization an easy performance win for designers. It’s an easy thing to see. Try taking a screenshot of something, exporting something from Photoshop, or grabbing some stock photography. Then drop it onto a tool like ImageOptim and watch the bytes fall away as it optimizes it. Massive savings.
But wouldn’t it be nice if it wasn’t on you to manually optimize all your images before using them? Computers are supposed to help us with menial tasks, right?! When you flip on the CDN feature of Jetpack, your images are now hosted on Photon, and you can see in the Photon docs how it handles things like resizing and quality for you.
4) You get lazy loading
Lazy loading is the idea that you don’t load anything at all unless you need it. In the case of images, don’t download the image unless it’s visible on the page. As in, don’t download an image that is three quarter down an article that a user might never scroll down to, but if they do, then download it.
You know what they say, the fastest web request is one that is never made.Jeremy Wagner, for Google, says:
When we lazy load images and video, we reduce initial page load time, initial page weight, and system resource usage, all of which have positive impacts on performance.
This is another flip-a-switch feature that works on any theme. Turn it on, you got lazy loading.
All Together Now
You get responsive images with WordPress, which by itself can be a major performance win.
With Jetpack, those responsive images are CDN-hosted, providing a speed boost and great caching for the images that are downloaded.
Just because you’re using responsive images and a CDN doesn’t automatically mean those images are optimized, but they are on Photon.
Last, nothing is downloaded at all unless the images are in view (lazy loading), which is the most efficient thing you can do.
Welcome to Part 2 of this mini-series on building a RSS viewer with Vue. In the last post, I walked through how I built my demo using Vue.js and Vuetify on the front end and Webtask on the back end. When I built that initial version, I knew it was exactly thatmdash;an „initial” version. I took some time to work on a few updates, and while I won’t dare call this a „perfect” version, I do think I’ve made some improvements and I’d like to share them with you.
Starting to switch to components in the layout. (Well, I was already using Vuetify components, but I meant custom components for my application.)
Using IndexedDB to store feed items for quicker access and offline support.
That was the plan, and like most plans, I wasn’t necessarily able to hit everything in this update (and I’ll explain why at the end). But hopefully you’ll see the improvements as a general „moving in the right direction” for the application. With that out of the way, let’s get started!
Implementing Vuex
I’ll start off discussing the biggest change to the application, the addition of Vuex. As I said in the previous post, Vuex describes itself as a „state management pattern + library” on their „What is Vuex” page. No offense to their documentation, but I had a difficult time wrapping my head around exactly what this meant, from a practical sense.
After having using it in a few small projects now, I’m coming to appreciate what it provides. To me, the core benefit is providing a central interface to your data. If I’ve got a basic Vue app working with an array of values, I may have multiple different methods that modify it. What happens when I begin to have certain rules that must be applied before the data changes? As a simple example, imagine an array of RSS feeds. Before I add a new one, I want to ensure it doesn’t already exist in the list. If I have one method that adds to the feed list, that isn’t a problem, but if I have more, it may become cumbersome to keep that logic in sync across the different methods. I could simply build a utility to do this, but what happens when I have other components in play as well?
While it is absolutely not a one-to-one comparison, I feel like Vuex reminds me of how Providers or Services work in Angular. If I ever want to do work with any data, I’ll ensure I use a central provider to handle all access to that data. That’s how I look at Vuex.
So the big change in this application was to migrate all the data related items to a store. I began by adding the library to my HTML:
<script src="https://unpkg.com/vuex"></script>
Woot! Half-way done! (OK maybe not.)
I then created an instance of my store in my JavaScript file:
const feedStore = new Vuex.Store({
// lots of stuff here
});
and included it in my Vue app:
let app = new Vue({
el: '#app',
store:feedStore,
// lots of stuff here too...
});
Now comes the interesting part. Any time my Vue application needs data, which primarily consists of the list of feeds and the items from those feeds, it’s going to ask the store for them. So, for example, my feeds value is now computed:
feeds() {
return feedStore.state.feeds;
},
This is now defined in the state portion of my store:
Notice that feeds defaults to an empty array. I had previously used the created event of my Vue app to read in the data from localStorage. Now, I ask the store to do that:
Back in the store, the logic is pretty much the same:
restoreFeeds(context) {
let feedsRaw = window.localStorage.getItem('feeds');
if(feedsRaw) {
try {
let feeds = JSON.parse(feedsRaw);
context.state.feeds = feeds;
context.state.feeds.forEach(f => {
context.dispatch('loadFeed', f);
});
} catch(e) {
console.error('Error restoring feed json'+e);
// bad json or other issue, nuke it
window.localStorage.removeItem('feeds');
}
}
},
I say „pretty much the same” except now I’m doing a bit of error-checking on the value read in from localStorage. But here’s the crucial bit. I already said I failed in terms of switching to IndexedDB, but in theory, I could build a third version of this application with an updated store and my Vue app won’t know the difference. And that’s where I started to get really excited. The more I worked, the more „dumb” my Vue app became and the less tied it was to any particular implementation of storage. Let’s look at the complete Vue app now:
What you’ll notice is that pretty much all of the actual logic is now gone and all I’m really doing here is UI stuff. Open a modal here, add an error there, and so forth.
You can view the complete store here, although I apologize for lumping everything together in one file.
Adding a Component
One of the other changes I mentioned was beginning to „component-ize” the view layer. I ended up only making one component, feed-item. This reduced the total number of lines in the HTML a bit:
It isn’t a huge change by any means, but it did make it bit easier for me when I started working on the feed display. As I’m not using a fancy builder yet, I defined my component straight in JavaScript like so:
I’m not doing anything at all fancy in heremdash;there’s no dynamic logic or events or anything like that, but I could certainly add that later where it makes sense. I did finally get around to adding the date and time of posting. If you’re curious about how I built the formatter used for it, read my article Build A i18n Filter Using Vue.js & Native Web Specs.”
The Power of Delete!
Oh, and I finally added a way to delete feeds:
Trash can icon, FTW!
This just fires off a method on the Vue object that, in turn, fires off a call to the store that takes care of removing the feed and items from the UI and then persisting it. A small thing, but, wow, did I wish I had that in the first version when testing. And here is a final shot of everything:
The awesome app in all it’s awesomeness
Next Steps… and What Happened to IndexedDB?
As I said in the beginning, this version is still not perfect but I definitely feel better about it. I highly encourage you to share tips, suggestions, and bug reports in the comments below or on the GitHub repo.
So what happened to IndexedDB support? The issue I ran into was how to properly initialize the database. Vuex stores don’t have a concept of a created process. I could have done something like this:
// dummy code for getting feeds
dispatch('getDB')
.then(() =>
// do stuff
);
Where the getDB action returns a promise and handles doing a one-time IndexedDB opening and storing the value in the state. I may give this a shot later, and again, what I love about Vuex is that I know I can safely do that without interfering with the rest of the application.
Open the 71.jpg image from the Realistic Textile Backgrounds 5 pack. Go to Image > Image Size, change the Width value to 1170, and click OK.
Step 2
Go to Edit > Define Pattern, and click OK.
Do that for the rest of the pattern images to add them to the Patterns preset.
2. How to Create the Background and Text Shape Layers
Step 1
Create a new 1000 x 1000 px document. Click the Create new fill or adjustment layer icon at the bottom of the Layers panel, choose Solid Color, and use the Color#d6dbdb.
Step 2
Create the text using the font The Next Font. Set the Size to 550 pt and the Tracking to 50.
Step 3
Duplicate the text layer and hide it, and right-click the copy to choose Convert to Shape.
Rename the shape layer to Text Shape.
Step 4
Duplicate the Text Shape layer 3 times and hide it.
Rename the copy layers from top to bottom to Fill, Outer Stroke, and Stroke.
Keep in mind that you can turn these layers’ visibility on and off during the tutorial to better see what you’re doing with each one of them.
Step 5
For both the Stroke and Outer Stroke layers, select each of them, and pick the Direct Selection Tool (A) to adjust their shape attributes in the Options bar.
Change the Fill to None, the Stroke Color to any bright color, and the Size to 35. Then click the Set shape stroke type icon to change the Align to Center and the Corners to Round.
3. How to Style the Stroke Layer
Double-click the Stroke layer to apply the following layer style:
Step 1
Add a Bevel and Emboss with these settings:
Size: 0
Uncheck the Use Global Light box
Angle: 40
Altitude: 48
Check the Anti-aliased box
Highlight Mode: Overlay
Opacity: 60%
Shadow Mode: Linear Burn
Opacity: 35%
Step 2
Add a Texture with these settings:
Pattern: Rough Cloth
Scale: 100%
Depth: 1000%
Check the Invert box
Step 3
Add a Pattern Overlay with these settings:
Pattern: 71.jpg
Step 4
Change the Stroke layer’s Fill value to 0.
4. How to Style the Outer Stroke Layer
Double-click the Outer Stroke layer to apply the following layer style:
Step 1
Add a Bevel and Emboss with these settings:
Style: Stroke Emboss
Size: 29
Check the Anti-aliased box
Highlight Mode: Soft Light
Opacity: 35%
Shadow Mode: Linear Burn
Opacity: 35%
Step 2
Add a Contour with these settings:
Contour: Cove – Deep
Check the Anti-aliased box.
Step 3
Add a Texture with these settings:
Pattern: Project Papper
Check the Invert box
Step 4
Add a Stroke with these settings:
Size: 3
Position: Outside
Fill Type: Pattern
Pattern: 71.jpg
Step 5
Change the Outer Stroke layer’s Fill value to 0.
5. How to Style the Text Shape Layer
Make the Text Shape layer visible, and double-click it to apply the following layer style:
Step 1
Add a Pattern Overlay with these settings:
Pattern: Human Skin
Scale: 50%
Step 2
Add a Color Overlay with these settings:
Color: #edece9
Blend Mode: Multiply
You can use any other color you like depending on the result you want.
This will style the Text Shape layer.
6. How to Create and Style Eyelets
Step 1
Pick the Ellipse Tool and create a 30 x 30 px circle where you want to place the first eyelet.
Then, press-hold the Option key, and create a smaller 17 x 17 px circle inside the one you already have to subtract it and get the main eyelet shape.
You can use the Path Selection Tool to separately select and move the smaller circle inside the bigger one if needed.
Right-click the shape layer and choose Convert to Smart Object.
Double-click the eyelet shape layer to apply the following layer style:
Step 2
Add a Bevel and Emboss with these settings:
Size: 7
Uncheck the Use Global Light box
Angle: 40
Altitude: 48
Check the Anti-aliased box
Highlight Mode: Linear light
Opacity: 60%
Shadow Mode: Linear Burn
Opacity: 35%
Step 3
Add a Contour with these settings:
Contour: Cone
Check the Anti-aliased box.
Step 4
Add a Color Overlay with these settings:
Color: #969c9d
Step 5
Add a Drop Shadow with these settings:
Opacity: 100%
Distance: 2
Size: 3
Step 6
Add another Drop Shadow effect instance with these settings:
Opacity: 60%
Distance: 3
Size: 7
Step 7
Press-hold the Option key and click-drag the styled eyelet shape to duplicate it. You can press-hold the Shift key while doing so to constrain the movement.
7. How to Modify a Ribbon Image
Step 1
Open the Ribbons image, and use the Quick Selection Tool to select the black ribbon.
Step 2
Press Command-J to duplicate the selected ribbon into a new layer, and use the Rectangular Marquee Tool to select a portion you like of the duplicated ribbon.
Press Command-J again to duplicate that portion in a new layer, and hide the other layers you have.
Step 3
Use the Rectangular Marquee Tool to select the right half of the ribbon, pick the Move Tool, and press the Left Arrow key a couple of times to move the selected part inwards, making the ribbon thinner without losing texture or details.
You might need to repeat the process a couple of times to get the result you like.
Once you do, duplicate the layer to the original text document.
8. How to Add a Ribbon to an Eyelet
Step 1
The ribbon should be thin enough to fit inside the eyelet shape you have. If it’s not, keep selecting one half of it and moving it inwards until you get the right size.
Step 2
Double-click the ribbon layer to apply an Inner Shadow effect with these settings:
Color: #020302
Opacity: 100%
Distance: 0
Size: 5
Step 3
Convert the ribbon layer to a Smart Object.
Step 4
Press Command-T to enter Free Transform Mode, rotate the ribbon to an angle you like based on the distance you want to create between the eyelet rows, and place one of its ends inside the first eyelet.
You can also resize the ribbon slightly if needed.
Step 5
Press the Warp icon in the Options bar to enter Warp Mode, and click-drag the ribbon’s tip to create a small arc and give the illusion of it being folded into the eyelet.
Press the Return key to commit the changes.
9. How to Build a Laced-Up Effect
Step 1
Duplicate both eyelet shape layers and drag them downwards. Adjust their position as well as the ribbon’s end so that they meet where you’d like them to.
Step 2
Duplicate the ribbon layer, and go to Edit > Transform > Flip Horizontal.
Place the flipped ribbon’s tip inside the empty top eyelet.
Step 3
With the ribbon layer selected, click the Add layer mask icon at the bottom of the Layers panel, and select the mask’s thumbnail.
Pick the Brush Tool, set the Foreground Color to Black, choose a hard round brush tip with the same Size as the eyelet, and click on the bottom eyelet to erase the ribbon covering it.
Then, erase the extra parts of the ribbon, and repeat that for the other one.
Step 4
Double-click the ribbon’s Smart Object thumbnail to open it, add a Hue/Saturation adjustment layer, and change both the Hue and Saturation values to -60.
Save and close the file.
Step 5
To continue building the laced-up effect, select both the ribbon layers and the bottom eyelet layers, duplicate them, and drag them downwards.
Step 6
Keep doing that for the straight parts of the letters. Once you reach a curved area, duplicate one of the ribbon layers, rotate it so that it’s horizontal again, and place it on top of the last pair of eyelets you have.
You might need to delete the layer mask in order to transform the ribbon, so do that, and add in a new mask to get rid of any extra parts.
Step 7
Take the time to build up the effect, and play around with the placement of the elements to fit each letter’s shape.
Step 8
Once you’re done, put all the eyelet and ribbon layers in a group and name it Lace.
10. How to Cover Empty Text Areas
Step 1
Select the Fill layer and change its Fill value to 0.
Step 2
Pick the Rectangle Tool, press-hold the Option key, and create rectangles that cover the laced-up parts of the letters you have to remove them.
Step 3
Change the Fill layer’s Fill value back to 100%, and click the Path operations icon in the Options bar to choose Merge Shape Components.
Step 4
Pick the Add Anchor Point Tool, and click where you want to extend the shape to cover any more areas.
Step 5
Pick the Convert Point Tool and click any anchor points you added to convert them to corner points.
Step 6
Use the Direct Selection Tool to select and drag the anchor points to create a final shape you like.
Step 7
Duplicate the Fill shape layer and rename the copy to Stitches.
Step 8
In the Options bar, change the Fill to None, the Stroke Size to 2, click the Set shape stroke type icon, and choose the dashed preset.
11. How to Style Stitches
Step 1
Right-click the Stroke layer, choose Copy Layer Style, right-click the Fill layer, and choose Paste Layer Style.
Double-click the Stitches layer to apply the following layer style:
Step 2
Add a Bevel and Emboss with these settings:
Size: 1
Check the Anti-aliased box
Highlight Mode: Linear Light
Opacity: 82%
Shadow Mode: Linear Burn
Opacity: 35%
Step 3
Add a Contour with these settings:
Contour: Gaussian
Check the Anti-aliased box.
Step 4
Add a Color Overlay with these settings:
Color: #b5b4b2
Step 5
Add a Drop Shadow with these settings:
Opacity: 100%
Distance: 2
Spread: 5
Size: 5
This will style the stitches.
12. How to Add Edges
Step 1
Duplicate the Outer Stroke Layer and change its Fill value to 100%.
Step 2
Convert the copy layer to a Smart Object and Command-click its thumbnail to load a selection.
Step 3
Open the Paths panel, Option-click the Make work path from selection icon at the bottom of the panel, set the Radius to 0.8, and click OK.
Step 4
Hide the Outer Stroke copy layer, and create a new layer on top of it with the name Edges.
13. How to Create an Edges Brush
Step 1
Pick the Brush Tool and open the Brush Settings panel.
Choose the Dune Grass tip and adjust its settings as below:
Brush Tip Shape
Shape Dynamics
Scattering
Color Dynamics
Step 2
Set the Foreground Color to #212121 and the Background Color to #3f3f3f, and hit the Return key a couple of times until you like the stroke.
Step 3
Place all the layers you have, except for the Background and Solid Color layers, in a group and name it Text.
14. How to Add Drop Shadow and Noise
Step 1
Double-click the Text group to apply Drop Shadow effects with these settings:
Opacity: 50%
Distance: 2
Spread: 5
Size: 5
Step 2
Opacity: 30%
Distance: 30
Size: 50
This will add the final shadows.
Step 3
Place the 1.jpg image from the Abstract Spotlight Backgrounds pack on top of the Solid Color layer, and resize it to fit within the document so that its bottom part isn’t showing.
Rename the layer to BG Texture and change its Blend Mode to Soft Light.
Step 4
Create a new layer, name it BG Noise, and go to Edit > Fill.
Change the Contents to 50% Gray and click OK.
Step 5
Convert the BG Noise layer to a Smart Object, and change its Blend Mode to Soft Light.
Step 6
Go to Filter > Noise > Add Noise, change the Amount to 10 and the Distribution to Uniform, and check the Monochromatic box.
15. How to Make the Final Changes
Step 1
Add a Levels adjustment layer on top of the Text group, change its Blend Mode to Luminosity, and change the Shadows value to 5 and the Highlights to 237.
Step 2
Add another Levels adjustment layer on top of all layers, choose the Blue channel, and change the Output Shadows value to 7.
Step 3
Create a new layer on top of all layers, name it High Pass, and press Command-Option-Shift-E to create a stamp.
Convert the layer to a Smart Object and change its Blend Mode to Soft Light.
Step 4
Go to Filter > Other > High Pass, and change the Radius value to 1.
Step 5
Duplicate the BG Noise layer, place the copy on top of all layers, expand the Smart Filters list, and double-click the Noise tab to change the Amount to 7.
Congratulations, You’re Done!
In this tutorial, we created a couple of shape layers and adjusted their shape attributes to create the different text parts.
Then, we styled the layers, created the eyelets, and added the ribbon.
After that, we worked with the different elements to build the laced-up effect, and created a brush tip to add some fuzzy edges.
Finally, we made some final adjustments to finish off the effect.
Please feel free to leave your comments, suggestions, and outcomes below.