2022 is the first year in memory that not only a single major technology ships, but a whole host of powerful technology. The result feels significant.
You see—the web typically moves slower than the 'app' world. But over the last few years, browser vendors—like Chrome, Safari, and Firefox—have been shipping behind experimental 'flags' that have enabled a more modular and faster approach to pushing out new tech.
What does this mean? These new technologies mean more expressive websites, from “scroll-linked” animation to eloquent magazine-like grids and sub-grid layouts.
This new tech is not only exciting for web developers but also for clients and, well… everyone who uses the web. For the rest of the article, I will be running through upcoming technology releases and translating what they mean into plain English.
Hat tips to Bramus Van Damme and Nicole Sullivan (both on the Google Chrome Developer Relations team).
If you're of a certain age and have ever dealt with a web developer, you might remember years ago web developers needed to design in “web-safe” colours.
At that time most consumer computer monitors could only display 256 colours, so if you designed in a broader range, most users wouldn't see the full spectrum of colours.
Colour Functions are kind of the same thing, except this time it's internet browsers holding things back, not monitors.
At the time of writing, most browsers can only access the "sRGB" (standard Red, Green, Blue) colour space, which is limited to a certain amount of colours.
There's a newer standard, known as the P3 (Protocol 3) colour space, where modern monitors and laptops can access a much richer range of colours.
]]>If you can see all four spectrums (display-p3, lch, and lab), it means you have a P3-capable browser. If you can also notice a difference between the colours, it means you have a modern screen capable of displaying colours in this expanded spectrum.
Once all browsers support P3, web designs will gradually start looking a lot more vibrant and attractive as web developers start implementing P3 colours in designs.
I'm already looking forward to making my neon-infused black/white/blue website look 26% hotter.
What are page transitions? Imagine you're on a hotel-booking app, and you click a thumbnail image of a hotel. In the next moment, information will probably slide across from the right, or maybe the hotel thumbnail will enlarge to fill the screen in one smooth motion.
You've just experienced a “page transition”—moving from one state (or page) to another in a smooth transition.
Page transitions are one of the few things that separate websites and 'apps'—apps providing a significantly smoother experience in this case.
We go from one page to the next on the web in “pop” movements without any kind of soft landing.
Transitions, on the other hand, are not only smoother and more visually appealing, but they also show us where we're going. Think of all the animations on your smartphone; when you flick up from the bottom of a screen on an iPhone, iOS shows you what happened in one smooth movement.
For web developers, native page transitions could bring us one step closer to confidently saying to clients, “you probably don't need an app”.
For what it's worth, an older colleague (sorry, Dean) pointed out that we were here decades ago with Internet Explorer 5.5. That obviously didn't last, but I hope this time is different now that we have better browser standards and collaboration between browser vendors.
Just about every client meeting I've had in the last few years can't end until a client smiles knowingly and asks for "parallax" effects.
However, implementing anything scroll-related has always been a bit hamfisted. You'll need to use some sort of script plugin to continually track where the user is on a page. The browser always struggles a little with this kind of stuff. Until now! (or the near-future, at least).
On the horizon is a “native” implementation of scroll-linked animation. “Native” means that the browser has it baked in so that you don't need to load a plugin.
Don't be fooled—this is not just developer convenience. Anything “Native” (baked into the browser) means performance will be significantly better and programming significantly simpler, leading to a much smoother and delightful implementation.
If you don't know what I'm talking about, Responsive Web Design was both a mental and technology shift to designing websites that worked on all sorts of different sized devices.
In the simpler days of web development, you'd just design a fixed-width page. With the advent of Responsive Web Design, we could suddenly query the device width. Developers started designing flexible layouts that could adjust to mobile phones, tablets, and various screen sizes.
The web will always be an inherently flexible medium. Your website can be literally accessed by thousands of different device types with wildly different capabilities. Someone could access your site on anything from a computer at CERN, to a 2008 Blackberry, to a high-specced 2022 MacBook Pro. Layout and design need to be flexible and adjusted accordingly.
So what follows Responsive Web Design? 4 years ago, Jen Simmons gave a talk called Everything You Know About Web Design Just Changed. This was around the time web developers got access to modern layout models—namely CSS Grid and CSS Flexbox—two incredibly powerful layout technologies that enabled all sorts of wonderful Graphic Design-like layouts.
Fast-forward to 2022, and we're about to get something called Container Queries, which are the crowning jewel we've been waiting for in this era of new layout technology.
Container Queries allow web developers to query the surrounding space of the component. Querying contextual space was never previously possible. Instead, you had to query the area of the whole page. As container queries are released, we can design components that adjust based on context.
Jenn Simmons has suggested a new name to mark this paradigm shift: Intrinsic Design—which indicates that we're designing inherent systems rather than pages.
Web developers have been asking for this sort of expression for years to reflect the truly modular nature of designed components, allowing for more robust designs. Imagine brand guidelines for different components, where you can specify how things will look depending on the space available to them? The Responsive Logos project comes to mind.
Subgrid means developers can create a much more robust design ecosystem, allowing for more complex designs that stand up to the scrutiny of the ever-growing list of different screen sizes and devices.
I've tried to highlight the most visually understandable browser technology changes in this article, without making it too long. Still, the reality is there are far more technical things on the immediate horizon that only developers—including me!—are excited about.
I've listed more new technologies below to give readers an idea of how much is happening in the web browser space. 2022 is probably the best time there's ever been to be involved in web design and development.
For more technical reading including the below, I'd recommend Bramus Van Damme's blog post on CSS in 2022.
Colour Functions
Viewport Units
:has()
Overscroll Behaviour
Accent Color (for form elements)
Media Query Ranges
Nesting
Scoping
Mixins
Houdini
Most of the video will cover spinning up a site and setting up blueprints. For the written article, here are the things you need to output for the RSS feed, which you'll need to give your podcast host.
Add this to your head.antlers.html
:
{{# RSS
=================================================== #}}
<link rel="alternate" type="application/rss+xml" title="Name of your podcast feed" href="{{ current_url }}/rss">
Create a new file at resources/views/rss.antlers.html.
Here is my specific implementation:
{{ xml_header }}
<rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" version="2.0">
<channel>
<title>{{ brand:name | cdata }}</title>
<itunes:title>{{ brand:name | cdata }}</itunes:title>
<link>{{ config:app:url }}</link>
<language>en-us</language>
<copyright>{{ brand:authors | cdata }}</copyright>
<itunes:author>{{ brand:authors | cdata }}</itunes:author>
<itunes:summary>{{ brand:podcast_description | cdata }}</itunes:summary>
{{# This should hold a concise, one-sentence description of your podcast. #}}
<itunes:subtitle>{{ brand:podcast_subtitle | cdata }}</itunes:subtitle>
<itunes:explicit>yes</itunes:explicit>
<description>{{ brand:podcast_description | cdata }}</description>
<itunes:owner>
<itunes:name>{{ brand:podcast_owner | cdata }}</itunes:name>
<itunes:email>{{ brand:podcast_email | cdata }}</itunes:email>
</itunes:owner>
<itunes:image href="{{ config:app:url }}{{ glide:brand:podcast_artwork square='1400' }}" />
<itunes:keywords>Games</itunes:keywords>
{{# https://www.podcastinsights.com/itunes-podcast-categories/ #}}
<itunes:category text="Leisure">
<itunes:category text="Video Games" />
</itunes:category>
<itunes:category text="News">
<itunes:category text="Tech News" />
</itunes:category>
<itunes:category text="Technology"/>
{{# PODCAST
=================================================== #}}
{{ collection:episodes as='posts' sort='date:desc' }}
{{ partial:rss-post }}
{{ /collection:episodes }}
</channel>
</rss>
In the above we're linking to an item partial, which looks like this:
{{ posts }}
<item>
<title>Ep.{{ episode_number | cdata }} {{ title | cdata }}</title>
<link>{{ config:app:url }}/episodes/{{ episode_number }}-{{ slug }}</link>
<itunes:author>{{ brand:authors | cdata }}</itunes:author>
<itunes:explicit>yes</itunes:explicit>
<itunes:subtitle>{{ content | smartypants | striptags | cdata }}</itunes:subtitle>
<itunes:summary>{{ content | smartypants | striptags | cdata }}</itunes:summary>
{{# the Description tag is requested by some hosts e.g. https://podcasters.radiopublic.com/dashboard #}}
<description>{{ content | smartypants | striptags | cdata }}</description>
<itunes:image href="{{ config:app:url }}{{ if artwork }}{{ glide:artwork square='1400' }}{{ else }}{{ glide:brand:podcast_artwork square='1400' }}{{ /if }}" />
<enclosure url="{{ audio }}" length="{{ size }}" type="audio/mpeg" />
<guid>{{ episode_number }}</guid>
<pubDate>{{ date format='D, d M Y H:i:s O' }}</pubDate>
<itunes:duration>{{ duration }}</itunes:duration>
</item>
{{ /posts }}
We need to define a route which removes the layout template and processes. Open routes/web.php
and add the following: Route::statamic('/rss', 'rss', ['layout' => '', 'content_type' => 'xml']);
What we're saying here is—When these routes are matched, use our 'rss'
Antler's template, skip layout processing (you'll notice we have a blank layout argument), then render the content as 'xml'
—which is needed for podcast feeds.
Podcast aggregators are fussy about how they receive content so it's best to be thorough. My favourites are Cast Feed Validator and Podbase
]]>What if you want to offer multiple feeds on your own site? I was stuck with this question—since I write about a variety of things. I write music reviews, film reviews, posts about Statamic, and thought-pieces. I know not everyone cares for my music taste, so I decided to figure out how to offer different RSS feeds using Statamic since this is what my site is currently built on.
The result is my Subscribe page, where I've output all the possible RSS feeds on this site so fellow RSS-lovers can pick and choose. So how did I do this?
Adding a link to the head means that visitors can just dump the page URL in an RSS reader and it'll fetch the feed automatically. The minimum amount of code you’d need to link to an RSS feed would be like this:
<link rel="alternate" type="application/rss+xml" title="Jay's Blog feed" href="{{ current_url }}/rss">
This would work well for a blog, for example.
But since we want to offer different feed titles depending on the page, we'll need to use an if statement to understand which page we're on.
We can easily set variables in Statamic at the very top of the antlers file.
At the top of my resources/views/blog/index.antlers.html
file I've added:
---
rss: blog
---
Subsequently at the top of my resources/views/listening/index.antlers.html
file I have:
---
rss: listening
---
And at the top of my resources/views/watched/index.antlers.html
file I have:
---
rss: watched
---
We now want an if statement in our head to detect the page and conditionally serve the relevant feed link. In my case I've broadly split RSS feeds into my Blog, Listening, and Watched. You could probably make this code a little leaner by simply outputting the title dynamically, but I wanted a little more control over the title so this is the way I did it.
{{ if view:rss == "blog" }}
<link rel="alternate" type="application/rss+xml" title="Jay's Blog" href="{{ current_url }}/rss">
{{ elseif view:rss == "listening" }}
<link rel="alternate" type="application/rss+xml" title="Jay George – Intrigue / Listening" href="{{ current_url }}/rss">
{{ elseif view:rss == "watched" }}
<link rel="alternate" type="application/rss+xml" title="Jay George – Intrigue / Watched" href="{{ current_url }}/rss">
{{ /if }}
Create a new file called resources/views/rss.antlers.html
This is the file we'll use to generate a dynamic RSS feed depending on the URL. For reference, here is a simple static RSS feed:
<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title>Your Page</title>
<link>https://yoursite.com/blog</link>
<description>Name of your blog</description>
<item>
<title>RSS Tutorial</title>
<link>https://somesite.com/xml/xml_rss.asp</link>
<description>New RSS tutorial on Some Site</description>
</item>
<item>
<title>XML Tutorial</title>
<link>https://somesite.com/xml</link>
<description>New XML tutorial on Some Site</description>
</item>
</channel>
</rss>
What we need to do instead is dynamically construct a feed by testing the URL “segments”. This is best understood through looking at a code example. Here is my specific implementation. FYI I've used the standard HTML comments syntax here so they stand out a bit more in the example:
{{ xml_header }}
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<!-- Construct RSS link by testing segments -->
<atom:link href="{{ config:app:url }}/{{ segment_1 }}{{ if segment_2 }}/{{ segment_2 }}{{ /if }}{{ if segment_3 }}/{{ segment_3 }}/rss{{ /if }}" rel="self" type="application/rss+xml" />
<title>{{ brand:name | cdata }} – {{ segment_1 | title }}{{ if segment_3 }} / {{ segment_3 | title }}{{ /if }}</title>
<link>{{ config:app:url }}/{{ segment_1 }}</link>
<description>{{ if segment_1 == 'blog' }}Thoughts and tutorials about web design.{{ /if }}</description>
<language>en</language>
<generator>{{ config:app:url }}</generator>
{{# BLOG
=================================================== #}}
<!-- Test segments to determine which collections and taxonomies to pull into the feed -->
{{ if segment_1 == 'blog' }}
{{ if segment_3 }}
{{ if segment_2 == 'categories' }}
{ collection:blog as='posts' limit='10' taxonomy:categories="{segment_3}" }
{{ partial:rss-post }}
{ /collection:blog }
{{ elseif segment_2 == 'tags' }}
{{ collection:blog as='posts' limit='10' taxonomy:tags="{segment_3}" }}
{{ partial:rss-post }}
{{ /collection:blog }}
{{ /if }}
{{ else }}
{{ collection:blog as='posts' limit='10' }}
{{ partial:rss-post }}
{{ /collection:blog }}
{{ /if }}
{{ /if }}
{{# LISTENING
=================================================== #}}
{{ if segment_1 == 'listening' }}
{{ collection:listening as='posts' sort="date:desc" limit='10' }}
{{ partial:rss-post-listening }}
{{ /collection:listening }}
{{ /if }}
{{# WATCHED
=================================================== #}}
{{ if segment_1 == 'watched' }}
{{ collection:watched as='posts' sort="date:desc" limit='10' }}
{{ partial:rss-post-watched }}
{{ /collection:watched }}
{{ /if }}
</channel>
</rss>
In the above we're linking to partials. As an example, here is the partial that's stored in /resources/views/_rss-post.antlers.html
:
{{ posts }}
<item>
<title>{{ title | cdata }}</title>
<link>{{ permalink }}</link>
<guid >{{ permalink }}</guid>
<description>
{{ if hero_image }}
<![CDATA[<img src="{{ config:app:url }}{{ glide:hero_image width="1920" height="1080" }}" width="1920" height="1080" alt="{{ hero_image:alt }}" />]]>
{{ /if }}
{{ main_content }}
{{ if type == 'text' }}
{{ text | full_urls | cdata }}
{{ elseif type == 'super_blockquote' }}
<blockquote>
{{ quote | full_urls | cdata }}{{ if attribution }}—{{ if attribution_link }}<a href="{{ attribution_link }}">{{ /if }}{{ attribution }}{{ attribution_link ?= '</a>' }}{{ /if }}
</blockquote>
{{ elseif type == 'inline_image' }}
{{ caption ?= '<figure>' }}
<![CDATA[<img src="{{ glide:image }}" loading="lazy" width="{{ image }}{{ width }}{{ /image }}" height="{{ image }}{{ height }}{{ /image }}" alt="{{ image:alt }}" />{{ if caption }}<figcaption>{{ caption }}</figcaption>{{ /if }}{{ caption ?= '</figure>' }}]]>
{{ elseif type == 'full-width_image' }}
{{ caption ?= '<figure>' }}
<![CDATA[<img src="{{ config:app:url }}{{ glide:image width="860" dpr="2" }}" loading="lazy" width="200" height="200" alt="{{ image:alt ?? caption }}" />{{ if caption }}<figcaption>{{ caption }}</figcaption>{{ /if }}{{ caption ?= '</figure>' }}]]>
{{ /if }}
{{ /main_content }}
</description>
{{ if categories || tags }}
{{ if categories }}
<category>
{{ categories }}
{{ title | cdata }}
{{ /categories }}
</category>
{{ /if }}
{{ if tags }}
<category>
{{ tags }}
{{ title | cdata }}
{{ /tags }}
</category>
{{ /if }}
{{ /if }}
<pubDate>{{ date format='D, d M Y H:i:s O' }}</pubDate>
</item>
{{ /posts }}
We need to define a route that removes the layout template and processes since we simply want to output the feed as an 'atom' content type, which is the preferred content type for RSS.
You can create routes with Statamic through routes/web.php
. This is the code I have in web.php:
// e.g. /blog/rss or /listening/rss
Route::statamic('{route_dir_1}/rss', 'rss', ['layout' => '', 'content_type' => 'atom']);
// e.g. /blog/categories/business or /blog/tags/dropbox
Route::statamic('{route_dir_1}/{route_dir_2}/{route_dir_3}/rss', 'rss', ['layout' => '', 'content_type' => 'atom']);
What we're saying here is—When these routes are matched, use our 'rss'
Antler's template, skip layout processing (you'll notice we have a blank layout argument), then render the content as 'atom'
—which is needed for RSS.
Some other things to note:
We have two routes in my particular example.
The order of the routes matters.
Put less specific routes first. If the URL matches the first route, the second route will effectively be ignored.
You can name the variables whatever you like, I just went with {route_directory_1}
, etc. because it made it easy for me to understand
We don't need to do it here but you can reference these route variable names in your templates if you like—although in this case, I find it easier to use the built-in Statamic's built-in {segment}
tags
Remember to clear the route cache, else the route won't work. Use the command php artisan route:clear
to do this.
Also, remember to run this command on your next deployment to your production server
Validate your RSS Feed using the W3C validator to check everything is OK.
Pop your DevTools “out”—this is a personal preference of mine. It isn't easy to get a clear view of your design when pushed to the side and surrounded by code. You can change how DevTools are displayed by going in the top right > click the vertical dots > and selecting a different docking icon.
Use the Ruler tool—this can really help when trying to make sure elements line up.
To enable the ruler tool, go to the Elements pane in DevTools, press shift + ?
to access preferences > click the show rulers
checkbox.
A little trick I've learned is highlighting an element, so the rulers are displayed, then scroll vertically-only; without moving your mouse. DevTools will continue to draw rulers as you scroll to check elements beyond the viewport are lined up.
Maintain your own base CSS file—I personally keep a core.css
file, and a non-core.css
file. I know I'll use everything in core and quite a lot of things in non-core. This can help kick-start a project, and design in the browser immediately without any set-up.
A good metaphor for thinking about automation and keyboard shortcuts is using a well-known tool like Photoshop of a Word document. If you use these all day, you'll start to get used to some shortcuts. For example, Photoshop has single-key shortcuts for things like Brush tool (b), pen tool (p), Stamp tool (s), Switch colours (x). Word has things like Bold (b), Underline (u).
If you use these tools all day long, it's difficult to do without shortcuts. I feel the same way when designing in the browser—I want as many shortcuts as I can remember to get my ideas out fast.
Here are some convenient keyboard shortcuts that are baked into DevTools:
Incrementing—whenever you're keyboard is focused in a number field you can use the up/down
arrows to increment by 1, and then combine with modifiers like shift
to increment by 10, or alt
to increment by 0.1. This works slightly differently in the Elements panel vs the Sources panel—the Elements panel being slightly superior since there is also the option to increment by 0.1.
Incidentally, you can also use your scroll wheel to go up and down values, again using the modifier keys to change the way the numbers increment.
Previous/Next edit points—Use alt & -
and alt & +
to get to where you were editing previously, or most recently. I find this one particularly useful when working with larger CSS files (like my core.css!) where I'm editing variables at the top of the file and need to get to where I was.
Switching between inspecting and editing—you can use cmd + 1-9
to jump between different panels (you can also rearrange panels to whatever order you want by dragging them around).
This is not turned on by default. To enable this, go to the Elements pane in DevTools, press shift + ?
to access preferences > click the Enable cmd + 1-9 shortcut to switch panels
checkbox.
I use these shortcuts to switch between inspect mode (the Elements panel) and authoring CSS (the Sources panel).
There are a few different automation tools around. TextExpander is probably the most famous. I really like Keyboard Maestro (macOS only, unfortunately – apparently the closest thing on Windows is AutoHotkey). Keyboard Maestro is similar to TextExpander, but its feature set is much much larger, and it pretty much lets you automate whatever you can dream up.
Keyboard Maestro is incredibly handy when it comes to speeding up the authoring experience in DevTools, and you can get a free trial from their website if you're not sure. I have macros that deal with different aspects of authoring:
Text expansions that speed up typing property/value pairs, for example, I might type ptt
to have Keyboard Maestro type out padding-block-start: var(--spacing-somevalue)
, and place my cursor in the correct position so that DevTools presents me with a list of possible variables.
Utilities such as deleting a line, moving lines up/down, commenting shortcuts, or maybe typing out a media query.
I've attached a sample of my macros below which may help you understand how to speed things up, before tailoring them to your needs.
If you'd like to understand a particular macro included in the attachment, hit me up on Twitter. Also, let me know if you have issues with any macros.
]]>Click your avatar (top right) > Profile
Settings > Theme mode > Dark
—I'm not usually a big dark-mode fan but I like it here
Backup configurations > configure these as needed
Security > Enable 2FA here
Server > Select your server > PHP
Manage Extensions
(button)
Imagick
(checkbox)
Enable OPcache
(button). If we're using Statamic and have the default php7.4-fpm reload command in our deploy script, then we don't need to worry about the warning here
If you've spent some time setting up file backups (as above) you can edit or run them manually—but the settings are in a different place. Go to Sites > Your Site > Manage > File Backups
Click your avatar (top right) > Profile
Integrations
Set this up first with whatever service you'd like. Personally I use Google Drive for backups and Telegram for notifications, so I'll include instructions for them below.
Notification Channels
For Telegram I chose a label of “Telegram” and clicked “Create”
Once you've set up this base configuration you'll need to set up notifications on a site basis.
Sites > Your Site > Notifications
Select your Telegram setup here from the dropdown
Servers > Your Server > Monitoring
> add a notification for anything over 80%. There's no basis for this number, but it felt like a good point at which I'd like to take action.
While you're here, click the channel notification to also send an alert through Telegram if this happens
Server > Manage > Automatic update notifications
> choose a service to be notified on e.g. Telegram. I tend to set up automatic server updates on a weekly basis here.
I thought it was worth mentioning how to do this since it's a brilliant feature that's not easily discoverable. Password protection is desiable if you're working on a site that is under NDA or if maybe contains confidential information while under development.
To password protect a site go to Sites > Your Site > Manage > Authentication (button)
There are a few ways to use Statamic—workflow and licence-wise. This video may be useful if you're checking out Statamic with a solo licence and need to pull down some changes from your production environment, or you may just want to make sure your server can push to your repo via SSH if you're setting up Statamic Pro Git integration.
One “Gotcha” with Ploi is if you link to a repository using the built-in “wizard” it will link to your repository using the OAuth protocol. Oath defaults to HTTPS, which means we won't be using SSH to push changes. This causes issues when trying to push back to the repository.
To solve this, when you link a site to a repository in Ploi, instead of selecting your provider, choose “Custom”. At this point, you need to ensure that you add your Ploi sever's SSH key to your account with your provider. Ploi will show you the SSH key to add when you select “Custom”. Copy the key, and (in Bitbucket, for example) go to your Personal Settings > SSH Keys > Add Key – paste in the copied SSH key and call it something like “Ploi - Name of your server”.
Go back into your repository in Bitbucket and click the “Clone” button in the top right. This will reveal the address of your Bitbucket repository. It will look something like this:
git@bitbucket.org:jaygeorge/jaygeorge.co.uk-statamic.git
Paste this into the Repository field in Ploi, select “Install composer dependencies” as usual and then click “Install Repository”. A huge thank you to Stephen Meehan for helping me out with this gotcha.
SSH into your machine, using a command like ssh ploi@yourserveripaddresshere
Make sure you're in the correct directory—with Ploi that'll probably be a command like: cd yourdomainname.com
git remote -v
this should now show you two SSH-style addresses—one for fetch and one for pull.
git status
to see if there are any changes to the production site that you can push up to Git.
Go ahead and add your changes to production e.g.
git add .
git commit -m "Content changes"
git push
You should now be able to pull your changes down to your site locally using the git pull
command.
Once you've cloned the site into your Sites
folder, it's handy to ensure that your project folder has the same name as the domain so valet can run it easily, e.g. for my site—https://jaygeorge.wip—my project folder is called jaygeorge
If you're running valet park
on your Sites
you should be able to boot up http://yoursitename.wip (or maybe http://yoursitename.test if you haven't changed the default TLD). You can go a step further and run your domain on HTTPS by typing valet secure yoursitename
Composer needs to do its thing and install the site. Run the following commands:
composer update
composer install
At this point, you need to ensure you have an environment file since these are not committed to git by default, since every machine will have a slightly different environment. I find the easiest way to add an environment file is like this:
Make sure you have hidden files revealed on macOS. You can do this by using the keyboard shortcut cmd + shift + .
in Finder to toggle hidden files.
Duplicate .env.example
using cmd + d
Rename the newly duplicated file as just .env
Now you can run the following commands:
php artisan key:generate
php artisan optimize
php please cache:clear
php please stache:refresh
]]>
You may be wondering “Why would I want to do this?” By default, like most CMS’s, Statamic generates images using software called “GD” (Graphics Draw). For most cases, this will probably be an OK default, but from my experience using ImageMagick as alternative image processing software generates superior images—especially with larger, more noticeable images.
Even if you're comfortable getting ImageMagick installed on your production server, installing locally will likely be a different process.
Open yoursite/config/statamic/assets.php
Search for driver
and change the value to imagick
Make sure you have homebrew installed. To check if you have homebrew installed, you can run brew -v
. If Terminal tells you the version number, then you're good to go. Otherwise, download homebrew from https://brew.sh/
Run brew install imagemagick
. Check for any errors. At this point in the video, I was prompted to run a command to remove some leftover files.
Run brew install pkg-config
When prompted with Please provide the prefix of Imagemagick installation [autodetect] :
just hit enter.
Once complete try running valet restart
and refreshing your site. You'll know ImageMagick is successfully installed if 1) it generates Statamic Glide images correctly, and 2) if you go to yoursite/cp/utilities/phpinfo
you should a new full section with the headline imagick
, with the first row of the section detailing the imagick module version
In my experience development environment things seldom “just work”—there's either something you forgot to set or some error you need to deal with. While ImageMagick usually runs smoothly you'll see in the video I ran into an error. The good news is you can usually Google search-engine your way out of an error when it comes to popular software—and luckily ImageMagick is relatively popular.
Before you start searching for fixes it may be worth clearing the cache first with:
php please glide:clear
This didn't help the error I ran into—which in this case was:
warning: mkdir(): File exists in System.php on line 294
I eventually found an article by Patrique Ouimet, which detailed a fix—thank you, Patrique! https://patriqueouimet.ca/tip/installing-php-and-pecl-extensions-on-macos
In this case, you needed to run:
pecl config-get ext_dir | pbcopy
then type:
mkdir -p
followed by pressing cmd + v
to paste the value you just copied to your clipboard.
Once this command has been run, and the directory has been created, try running pecl install imagick
again and maybe run php please glide:clear
to be on the safe side. Finally valet restart
and go back to your Utilities > PHP Info in your control panel. If everything's worked out, you should now see an Imagick section here.
FYI if you've decided to store your files in a Github repository instead, this process will be very similar.
Go ahead and set up a Bitbucket account if you don't already have one.
When you log in to Bitbucket, you should be in the “Repositories” dashboard. From here look in the left-hand corner where you should see your avatar. Click this and then “Personal Settings” then “SSH Keys”.
We now need to generate an SSH key on our machine. Open the Terminal application and paste in ssh-keygen -t ed25519 -C "your_email@example.com"
, substituting the email address for whatever your Bitbucket email address is.
Once a key pair has been generated, you'll be prompted to enter a file to save the key. Press enter
to accept the default location.
You'll now be prompted to generate a passphrase. This is basically a password. Please make a note of whatever you type in because you'll need it later. Terminal should now inform you that a fingerprint has been generated.
Back in Bitbucket press the add key
button
Give the key a label; for example, the name of the machine you've used to generate the key. In my case, I called it MacBook 2018
At the time of writing Bitbucket prompts you to copy the key from your machine using a command that references an older encryption method (RSA). Since we've generated a key using a newer encryption standard, an RSA copy command won't work. To be sure you get the key, you can copy it directly from the file. Go to your home directory and make sure hidden files are showing in Finder—you can toggle these on/off using the keyboard shortcut cmd + shift + .
You should see a .ssh
directory. Inside the folder look for id_ed25519.pub
. Open this in a text editor and copy the contents of this file. Paste this into the key field in Bitbucket, then press Add key
.
In the repository dashboard in Bitbucket create a new repository. You can call the Repository whatever you like, but usually, you'll call it the name of your site. I find it useful to specify the site's technology, for example for my own site I've called it “jaygeorge-statamic”. This means if I ever switch to a different CMS in future (which I have done in the past!) I can store that as a separate repository, e.g. “jaygeorge-perch”.
I find it easier to select “no” when it asks me if I'd like to create other files—I prefer to create the repo locally and manually add these things. This is just personal preference though—you may find it easier to let Bitbucket do this for you.
It's now possible to clone down the repository using the Sourcetree app. This may be the simplest way to do things, but here I'll cover creating the repository locally from scratch and linking it to Bitbucket.
If you don't already have a project in your Sites
folder go ahead and make one, e.g. create a subfolder in Sites
called test
We now need to make sure we're in that project folder in Terminal. In Finder press cmd + c
on the project folder to “copy” it. Back in Terminal type cd
(with a space afterwards) and then press cmd + v
to paste the directory path we've just copied. Hit enter
The Terminal window's top bar should now show the name of the project folder we're in.
Now we're going to create a Git project. To do this run git init
and hit enter. Now run git add .
– this command will add all your files to the repository. Now run git commit -m "Initial commit"
– this command “commits” the files you've just added. The -m
stands for “message”.
We've got our repository set up locally, and now we want to synchronise it with a host (in our case Bitbucket). Eventually, we will set up our production server to “pull” our code from Bitbucket—but that's for another day!
Once you've created a repository back on the Bitbucket site, it should give you a few different sample commands. We can skip a few of these since we've already created a git repository, added files, and committed. We need to add the Bitbucket repository we've created as a “remote” now. You should see the command we need to run on the Bitbucket site, in my example it was git remote add origin git@bitbucket.org:jaygeorge/test.git
.
push up your commits to the master branch with git push -u origin master
. If Terminal tells you the authenticity of the host can't be established say yes
to continue. At this point, you'll be prompted to enter your passphrase that we made up earlier. Do so and hit enter (it'll be invisible so don't worry if you don't see anything while you're typing)
Once you've “pushed up” the code, at this point if you go back to Bitbucket and refresh the page you should see your commit along with its message. PS If you try to push to the repository again and get a permission denied public key
message, try restarting your computer and trying again.
You typically need to run a set of commands after each deploy. These commands may do things like clear the cache, and recompile assets like Tailwind / minified CSS and JS using Webpack.
Laravel needs to be run on a folder named public
rather than the wide-standard for hosts, public_html
. This can be a change to resolve, especially on shared hosting where it's typically not even possible to rename the public_html folder.
Using a host that supports Laravel environments makes things a lot easier and I strongly recommend you go this route unless you love tinkering with servers and making your life more difficult.
N.B. If you decide to sign up to Ploi I would very much appreciate it if you used my referral code https://ploi.io/register?referrer=JjynFt5S8WmoZbiAZO3E
The two most popular “server management” solutions (note: not quite the same as a host—we’ll get to that later)—are:
Laravel Forge – built by Taylor Otwell. Taylor is the creator of the Laravel Framework and so this is a solid choice.
Ploi – Similar to Forge but more encompassing, with extra bells and whistles which make it easier to connect to repositories, and with a much nicer UI, in my opinion. Ploi also has PHPMyAdmin built-in, so if you have existing sites on other CMS's like WordPress or Perch (which I did), you can migrate them and keep everything in one place.
Whether you choose Forge or Laravel, both of these services have the same purpose – to administrate a host/server more easily. In terms of hosts, the most popular options for Statamic are Digital Ocean and Linode. Digital Ocean seems to be the favourite, and so my final recommendation would be a Statamic setup of using Ploi to manage a Digital Ocean server.
Let's start from scratch with a free Ploi trial—we'll go from signing up to a trial to getting a Statamic site live. p.s. if you decide you like Ploi and want to help a man out, please use my referral code: https://ploi.io/register?referrer=JjynFt5S8WmoZbiAZO3E
Sign up to a free trial on ploi.io
Add a Git Provider – In the Dashboard click the “Link Git Provider” button, where you'll be prompted to link to your GitHub, BitBucket, or GitLab accounts. If you're a little unsure on Git, my Working With Git video may help you.
Link to a Server Provider – Select your Server Provider. In my case, I'm using Digital Ocean.
In Digital Ocean go to “API” in the bottom of the left column.
Select “Generate a new Token”, call it something like “Ploi”.
Copy the token using the little “copy” link.
Go back to Ploi and paste the token in the “API Key” field. Label it something like “Digital Ocean” and click “Add Provider”.
Create a Server – In the Dashboard click the “Create Server” button. Select your server provider and then in the “Details” section use the Credentials dropdown to select the account you just created. Fill the in the details here. If you're just starting out the lowest server plan should be fine. You'll want to make sure you select a Server Region that's closest to your main customer base. When you're done click the “Create Server” button. Ploi will now use Digital Ocean's API to create a server for you on their platform. This might take a bit of time – go and get a cuppa tea—you deserve it! it took 20 minutes for the progress bar to reach 100% for me.
At this point you'll be e-mailed server details. Save these somewhere safe (you'll need them!). I would personally then delete the e-mail for security reasons.
Click your avatar in the top right > Profile
– set up TwoFactor Authentication here.
Settings > Theme mode
– change this to Dark mode if you're a fan.
I'd recommend you use ImageMagick to process images rather than GD, which produces better quality image processing. For more information on that see my post about getting ImageMagick working locally. To enable ImageMagick in Ploi, go to Servers > PHP > Manage Extensions
and check Imagick
In the Ploi dashboard > left hand menu > Servers > select your server
– add a domain you'd like to link here e.g. yourdomain.com
and click Add Site.
Once the site has been created, click it, and select “Git Install Repository”, choose your provider and select your repository. Make sure you select “Install composer dependencies”, and then click Install Repository.
At this point you'll be prompted to add some recommended script commands, ignore this – I'll give you some recommendations instead. Click “Ignore suggestions”
Once you can see the Deploy script, add these lines below where it says “echo "🚀 Application deployed!"”
npm install
npm run production
php artisan optimize
php please cache:clear
php please stache:refresh
php artisan route:cache
php artisan view:clear
The full deploy script should now look something like this (bear in mind your site directory will be different and your php version might be different).
cd /home/ploi/yourdomain.com
git pull origin master
composer install --no-interaction --prefer-dist --optimize-autoloader
echo "" | sudo -S service php7.4-fpm reload
echo "🚀 Application deployed!"
npm install
npm run production
php artisan optimize
php please cache:clear
php please stache:refresh
php artisan route:cache
php artisan view:clear
When you've finished editing the deploy script click “Save”
Click the “Edit Environment” button in the top right:
Change the APP_URL
to https://yourdomain.com
(rather than plain ol’ http).
Change the APP_ENV
to production
Then in the top right click “Deploy Now”. Once you've successfully deployed your repository you can remove the npm install
line.
In your domain registrar's DNS records, you should have the following three lines:
An A
record for *
with a value of your server’s IP address (which you can find in the top left of Ploi once you're in a server or its sites)
An A
record for @
with a value of your server’s IP address
A cname
record for www
with a value of yourdomain.com
Once you've gone into your site in Ploi > left hand menu > SSL
– accept the defaults here and click “Add certificate”. At this point Ploi will check with the DNS to connect. if you've correctly pointed your DNS to ploi this should work.
When you push to your installed branch, Ploi will run the deploy script for you.
A final thing to consider is whether you'd like to automatically deploy your site when you push up to Git. You may want to be careful with this if you're handling a big client but otherwise, this is a big productivity boost. Once you've gone into your site in Ploi > left hand menu > Repository
– scroll down to the Quick deploy heading and click “Enable Quick Deploy”. Another possible workflow is if you set up a staging site on Ploi and automatically deploy to that.
I've run into a problem previously where "Quick Deploy" wasn't visible in the "Repository" section. I believe this happens when you initially add the repository using the "Custom" option rather than the built-in BitBucket/GitHub integration.
It's still possible to enable quick deploy though. To get around this, in Ploi > left hand menu > Repository
– scroll down to the Repository section, and change "Custom" to "BitBucket" or "GitHub". Make sure the repository name is in the format "username/repo_name", for example "jaygeorge/london-local". The repository URL field should already automatically be in the correct format from when it was previously saved as "Custom", e.g. "git@bitbucket.org/jaygeorge/london-local.git"
Once you've turned Quick Deploy on, if you like you can switch back to "Custom" and Ploi should continue to quick-deploy.
]]>