web development

How to set up local development for Kirby and Tailwind, Version 2

A step-by-step beginner’s guide, using the command line on macOS — now updated for Tailwind 3.0 and Kirby 3.6

I wrote the first version of this post in September 2020 — as a reminder to my future self — and as a starting point for others, who have struggled (like me) with this complicated process.

Creating that original post (as a beginner) took me weeks of trial and error to get everything to work, and weeks more to write.

To my surprise, this — my most difficult post to write — soon became the most popular post on my website.

But, since then, Tailwind has had a major upgrade in version 3.0 — with a new ‘Just-in-Time engine’ replacing its original ‘purge’ process — making it much simpler to run, but significantly different to install and set up.

As my original post still gets daily traffic, I’ve been feeling guilty for months, that it’s become so out-of-date.

So, at last, I’ve installed the latest versions of Tailwind and Kirby, on a new 14 inch MacBook Pro, and on my original iMac. Enabling me to test and re-write this step-by-step beginner’s guide, version 2.

Despite my best efforts, it’s a long and complicated post. But, I hope it might still be helpful…

Step 1  Set up Apple’s Terminal app + install Xcode command line tools

ZSH is the default shell that Apple recommends in its Terminal app, since macOS Catalina. Apple’s Support site has more details, including these instructions for resetting the Terminal to use ZSH:

  1. Open the Terminal app, and after the prompt, paste or key in: chsh -s /bin/zsh Then press return to run that command.

  2. Then check if Apple’s Xcode command line tools need to be installed, by entering the command: xcode-select --install and follow the instructions to install them, if prompted. Wait until everything has downloaded (around 20 minutes with a 20 Mbps connection).

Step 2  Practice using the command-line

If your command-line knowledge is as sketchy as mine was, I recommend Launch School’s excellent free guide, Introduction to the Command Line. I learned tons from this, and I wish I’d found it before I started. Its short section on $PATH and Executables is especially useful.

Also recommended, but for different reasons, is Tracy Osborn’s Really Friendly Command Line Intro. Tracy’s hand-drawn zine is only a starting point for beginners — but if you’re in a hurry, it might be just enough to get you through my step-by-step instructions below.

In fact, you might even manage with just these two little lists:

Step 3  Install Homebrew

Homebrew is a package manager for macOS, needed below to install PHP, Composer, and Node/NPM packages.

  1. To install Homebrew, paste and enter the command at the top of Homebrew's front page. Currently, this is: /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

  2. On Intel Macs, that should be all you need to do – the latest version of Homebrew should install itself into the /usr/local/bin directory, which should already be configured in your macOS default PATH environment variable.

  3. But, on Silicon-powered Macs, Homebrew puts its files in a new location: /opt/homebrew/bin/brew and PATH variables need to be added in a different hidden file: .zprofile instead of .zshrc

  4. Luckily, the latest version of Homebrew provides the correct commands needed, helpfully displayed in the Terminal a few lines above the prompt line. These commands are personalised for your user name and your Mac, so all you need to do is copy and paste each command.

  5. For my Silicon-powered MacBook, these two commands read: echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> /Users/my-user-name/.zprofile
    eval "$(/opt/homebrew/bin/brew shellenv)"
    (Different from the export PATH… commands I’m now familiar with. Nevertheless, they worked first time.)

  6. To check that Homebrew is successfully installed, enter the command brew -v to output Homebrew’s version info.

  7. To upgrade or delete Homebrew’s packages, checkout the glossary and instructions on their FAQ page. Importantly, this means first updating Homebrew, using the command brew update, before upgrading a package, eg: brew upgrade php

Step 4  Install PHP

To install Kirby's recommended version of PHP (currently 8.0), using Homebrew:

  1. Enter the command brew install php@8.0

  2. Then enter the command to start PHP running in the background (now, and automatically at every future macOS startup): brew services start php@8.0

  3. To confirm that this newly-installed PHP version is now active, enter php -v, to confirm that it’s version 8.0, not the previous installed version, or the macOS default.

Step 5  Install Composer

Composer is a dependency manager for PHP (like a package manager, but per-project instead of system-wide). It’s needed below to install Kirby and Laravel Valet.

To install Composer I used Homebrew again (simpler to install and upgrade, than the instructions on Composer’s website):

  1. Enter the command: brew install composer

  2. To make sure packages installed by Composer get found by Terminal commands, create another PATH variable by entering: echo 'export PATH="~/.composer/vendor/bin:$PATH"'>>~/.zprofile for Silicon Macs, or:
    echo 'export PATH="~/.composer/vendor/bin:$PATH"'>>~/.zshrc for Intel Macs.

  3. Enter the command source ~/.zprofile or source ~/.zshrc to activate the PATH.

  4. To confirm all is well, enter: composer -v to output Composer’s version info.

Step 6  Install Kirby CMS

First we set up a folder structure for local development files, and then use Composer to install Kirby CMS.

Inside our home folder we’ll create a local development folder I’ve called ‘Sites’, and inside that we’ll use Composer to create (and populate) a folder for the new Kirby site, that I’ve called ‘mysitename’.

This site folder name will later be used by Laravel Valet to serve its local URL, at http://mysitename.test so it would be simplest for you to make ‘mysitename’ exactly the same as your new site’s domain name.

  1. Enter the command: cd ~ to make sure you’re in your user home directory, then: mkdir Sites followed by: cd Sites

  2. Then install either Kirby’s Starterkit or Plainkit, based on Kirby’s Starting a new project recipe — using the command: composer create-project getkirby/starterkit mysitename or:
    composer create-project getkirby/plainkit mysitename

  3. In addition to Kirby's folder structure, this will also create a couple of extra files needed to control future updates using Composer: composer.json and composer.lock

  4. As described in more detail in Updating Kirby, the command to update the version specified in composer.json is: composer update getkirby/cms or:
    composer update getkirby/cms -w to include all dependencies.

Step 7  Install Laravel Valet

Laravel Valet is a virtual web server that runs ‘locally’ on your computer, in the background on macOS.

After installing Valet using Composer, and setting up the valet park command, every project folder inside the ‘Sites’ folder becomes accessible to any browser on your computer, at http://mysitename-1.test and http://mysitename-2.test etc.

You only need to set up Valet once — after that everything runs automatically each time macOS restarts — and, for me, so far it’s been completely transparent, and trouble-free.

  1. To install Laravel and Valet, enter the command: composer global require laravel/valet

  2. Then enter: valet install

  3. To check Valet is installed, and accessible, enter: valet -v to get its version info.

  4. And then confirm that its server is running, with the command: ping valet.test, which should return

  5. Enter: control-c to stop the pinging!

  6. To set up Valet to serve all the project folders inside the Sites folder, first make sure you are working inside the Sites folder with the command: cd ~/Sites

  7. Then enter the command: valet park

  8. That’s it! Because all of Kirby’s content is stored as text files in macOS’s folder hierarchy, there’s no need to set up a database. So, you should now be able to access your newly installed Kirby site, live in any browser on your Mac, at: http://mysitename.test

  9. You can drag and drop any other Kirby project folder into your Sites folder, anytime in the future, and it will be automatically served by Valet, with no other commands needed.

  10. To get a list of all the projects served from the Sites folder, enter valet parked, then your macOS account password when prompted.

  11. Checkout Laravel's excellent documentation for more info on updating, maintaining, customising, and troubleshooting Valet.

If you don’t want to install TailwindCSS, but you do want to run Browser­sync, you should first install Node and NPM in Step 8 and then skip to Step 16

Step 8  Install Node and NPM

Node.js is an important JavaScript runtime environment, and NPM is its package manager. Both are needed below to set up TailwindCSS processing, and then to install Browsersync. I installed them using Homebrew (I’d read here that this avoids some permissions complications of using Node’s official installer).

  1. To install Node and NPM together using Homebrew, simply enter: brew install node

  2. You can confirm that these are correctly installed, and available to Terminal commands, by entering: node -v or: npm -v to output their version numbers.

Step 9  Read Tailwind’s latest documentation

Tailwind's website, and its Getting Started documentation, are both terrific, and well worth studying in detail — with a coffee — before proceeding.

There are three alternative installation methods listed in Tailwind’s instructions: Tailwind CLI (the simplest first-party method), PostCSS (the original method), and some Framework guides.

My instructions below are based on the PostCSS method, which provides the option of adding other useful PostCSS plugins.

Update, 10 June 2022: Released only a couple of weeks after writing this post, the latest Tailwind version 3.1 now includes PostCSS-import, PostCSS-nested, and CSSnano — all built-in to the simpler Tailwind CLI installation method. However, I’ve used PostCSS-nesting extensively in my coding (with a different syntax from PostCSS-nested), so I’ll stick with this more complicated PostCSS method, for now.

Step 10  Install and set up Tailwind CSS + PostCSS + Autoprefixer

PostCSS and Autoprefixer are Node packages that Tailwind uses to process its files, and then output them to CSS.

  1. Enter the command cd ~/Sites/mysitename to change directory to the correct project folder. Don’t forget to stay in this folder for the steps below!

  2. Create an empty package.json file in the mysitename folder by entering the command: npm init -y

  3. Then install the packages needed, by entering the command: npm install -D tailwindcss postcss postcss-cli autoprefixer
    This creates a new folder called node_modules inside the mysitename folder, to store these in.
    (For some reason postcss-cli isn’t included in Tailwind’s PostCSS install instructions, but my install refused to work without it!)

  4. Create an empty tailwind.config.js file in the mysitename folder, by entering the command: npx tailwind init

  5. Create a new postcss.config.js file in the same folder, by entering the command: touch ~/Sites/mysitename/postcss.config.js 

  6. Open postcss.config.js in a text editor, and list the plugins we want to use, by pasting or keying in the following:

module.exports = {
  plugins: [

Step 11 (Optional)  Add PostCSS-nesting + PostCSS-import + CSSnano plugins

The PostCSS-nesting plugin enables W3C Working Group’s CSS-nesting syntax in custom CSS and Tailwind components stylesheets.

Most projects (especially if they use Tailwind’s Typography plugin) won’t need this. However I used CSS-nesting extensively for my site's Markdown and KirbyText content, using (super-powerful) coding patterns like this:

.markdown {
  & h1,
  & h2,
  & h3 {
    @apply mt-6 sm:mt-8;
    & + * {
      @apply mt-2 sm:mt-3;

The PostCSS-import plugin allows several CSS files to be imported and combined into a single CSS file. This helped me to manage my site’s complicated stylesheets, as detailed in Step 13 below.

Finally, as my compiled stylesheet grew ever-larger, CSSnano was a no-brainer: it achieved a saving of over 20%, just by removing white space and comments.

  1. Make sure you’re still in the mysitename folder, by entering the command: cd ~/Sites/mysitename

  2. Enter the command: npm install -D postcss-import postcss-nesting cssnano (but, omitting the plugins you don’t want).

  3. Open postcss.config.js in a text editor, and insert a new line for each plugin you’ve just installed, in order, exactly as below:

module.exports = {
  plugins: [

Or, you can use this alternative syntax…

…which was introduced (confusingly) in Tailwind 3.0’s installation instructions:

module.exports = {
  plugins: {
    'postcss-import': {},
    'tailwindcss/nesting': 'postcss-nesting',
    tailwindcss: {},
    autoprefixer: {},
    'cssnano': {},

Step 12  Set up a ‘main’ CSS file to import Tailwind files, and add your custom CSS and components

Here we’ll set up the main CSS file needed to generate a production-ready stylesheet from Tailwind’s Node files, customised with your own CSS and Tailwind @apply components:

  1. Create a new folder, which I’ve called stylesheets inside your Kirby site’s Site folder, by entering the command: mkdir ~/Sites/mysitename/site/stylesheets

  2. Create a new file, which I’ve called main.css inside this new folder, by entering the command: touch ~/Sites/mysitename/site/stylesheets/main.css

  3. Open main.css in a text editor, and set up Tailwind’s processing directives, by pasting or keying in the following:

@tailwind base;
@tailwind components;

/* ---- custom css and @apply components go here ---- */

@tailwind utilities;

Step 13 (Optional)  Set up files to structure your custom CSS

If you’ve installed the PostCSS-import plugin, in Step 11, to structure and manage your custom CSS:

  1. Create new files, which I’ve called custom-1.css and custom-2.css etc, inside the ‘stylesheets’ folder, by entering the commands:
    touch ~/Sites/mysitename/site/stylesheets/custom-1.css and:
    touch ~/Sites/mysitename/site/stylesheets/custom-2.css etc.

  2. Cut all your custom CSS code out of main.css and paste it into custom-1.css and custom-2.css etc, splitting it into your own logical structure.

  3. Open main.css in a text editor, and replace the code you’ve just removed, with the following:

@import "tailwindcss/base";
@import "tailwindcss/components";

@import "custom-1.css";
@import "custom-2.css";
/* ---- etc ---- */

@import "tailwindcss/utilities";

Step 14  Set up ‘Just-in-Time’ PostCSS processing

The latest Tailwind version 3 incorporates a new ‘Just-in-Time engine’ to generate a single CSS file from all your PHP templates, Tailwind utilities, and custom CSS files.

This JIT-engine works in the background, and so by incorporating CSSnano, it can generate a fully production-ready stylesheet, continuously during development — greatly simplifying the production process.

First we’ll create a new style.css file. Then we’ll insert a ‘watch’ script into the package.json file, to process the main.css file and output its compiled CSS into the new style.css file. And finally, in the tailwind.config file, we’ll specify which PHP and CSS files will trigger the watch script:

  1. If you’re using Kirby’s PlainKit, you’ll need to create a new folder, called assets inside the mysitename folder, by entering the command: mkdir ~/Sites/mysitename/assets

  2. Create a new file, which I’ve called style.css inside the assets folder, by entering the command: touch ~/Sites/mysitename/assets/style.css

  3. Depending on your own customising, the folder structure for your new Kirby site should now include all the files and folders shown in the screenshot alongside.

  4. Next, open the package.json file in a text editor and insert the following ‘watch’ script, on a new line immediately after the opening { bracket:
    "scripts": { "watch": "postcss site/stylesheets/main.css -o assets/style.css --watch", },

  5. Finally, open tailwind.config.js in a text editor, and paste or key in the script below (based on the file locations shown in the screenshot above):

module.exports = {
    content: [
    theme: {},
    variants: {},
    plugins: [],

Step 15  Link Tailwind to your Kirby templates, and start the ‘watch’ script to process its files

Process all the Tailwind directives, custom CSS, and @apply components from your main.css file, and save them into your style.css file:

  1. Using Kirby's CSS helper syntax, link the new style.css file to your Kirby templates, by adding this new line to their <head> elements: <?= css('assets/style.css') ?>

  2. Make sure you’re still in the mysitename folder, by entering the command cd ~/Sites/mysitename — it’s easy to forget this command, when you’re busy coding!

  3. Enter the command npm run watch to run the watch script, automatically, every time you save a change to your PHP and CSS files.

  4. To stop the watch command, enter: control-c

Generating this production stylesheet normally only takes a few seconds, and it runs continuously in the background.

Then, because Kirby is file-based, it’s quick and easy to synchronise the local site with its ‘live’ internet server — using an FTP client such as Forklift, or Panic’s Transmit, or Panic Nova’s super-slick Publish workflow.

Step 16  Set up and run Browsersync

Browsersync also runs in the background, watching for changes to your files during local development, and automatically triggers a refresh in any open browser window. It’s easy to install using NPM — and for me, it’s always been completely reliable and trouble-free.

  1. Open a new Terminal window, and make sure you’re in your site’s folder by entering the command: cd ~/sites/mysitename

  2. To install Browsersync locally, enter the command: npm install browser-sync --save-dev

  3. Then, to run Browsersync in the background during development, you enter something similar to this example command, but adjusted to your site’s specific file structure: browser-sync start --proxy "mysitename.test" --files "assets/style.css, site/snippets/*.php, site/templates/*.php, content/**/*.txt"

  4. This example watches for changes to style.css in the assets folder; to PHP files in the snippets and templates folders; and to text files which are any number of folders deep in the content folder.

  5. As soon as it’s run, this command will automatically open your site in a new browser tab, at localhost:3000 and this will refresh every time you save one of your watched files during development.

  6. To stop Browsersync, enter: control-c

  7. To preview edits made in Kirby’s content panel, open http://mysitename.test/panel/page-name in one browser window, and localhost:3000/page-name in another.

  8. But avoid using localhost:3000/panel/page-name for the panel edits, as this (annoyingly) scrolls back to the top of the panel, every time you save.

  9. To simplify running the complicated script in command 3 above, I’ve been using Panic Nova’s Custom Task workflow, which saves the script in a Nova project, so it can be started and stopped with a single click.

You’ve reached the end…

Well done, and thanks for reading!

Readers’ comments

  • Luke

    09 Dec 2022

    Thank you for this! Really appreciate the hard work to update the prior post <3

  • ian hobbs

    14 Feb 2023

    This is a great summary for beginners setting up mac enviros. It’s important to note that codekit does suppport Tailwind3 if you still want to use Codekit. I can’t use tailwind alone there’s too much other goodies out there which I like to use. I’m a huge fan of Blyth and Utopia modular scales. Thanks Ian

Add your comment…

Available formatting commands

Use Markdown commands or their HTML equivalents to add simple formatting to your comment:

Text markup
*italic*, **bold**, ~~strikethrough~~, `code` and <mark>marked text</mark>.
- Unordered item 1
- Unordered list item 2
1. Ordered list item 1
2. Ordered list item 2
> Quoted text
Code blocks
// A simple code block
// Some PHP code
[Link text](https://example.com)
Full URLs are automatically converted into links.