Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Tailwind CSS

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

The HTML on our site is already styled with Tailwind: all the classes you see here come from it. So if we can get Tailwind installed, we should have a much less ugly site.

Tailwind is interesting because it's not just a CSS file you include: it requires a build step. And that's totally fine! Even though we don't have a build system for everything doesn't mean we can't choose to add one for some specific things.

Using TailwindBundle

Before we dive in... about a week after I recorded this, we created a bundle that makes it super easy to add Tailwind. It's called, creatively, TailwindBundle! Seeing how you can set up a small build system is still interesting - but if you want to skip this chapter and head over to that bundle instead, it won't hurt my feelings. The bundle basically automates what we're about to do.

Downloading the Standalone Executable

To get all of this working, we need the Tailwind binary file. As we see here, we could install it with Node... and that's a really flexible option. You would have a package.json file... but instead of it containing WebpackEncore and a ton of other stuff, it would just have Tailwind.

The other option, which avoids the need for Node entirely, is to use the standalone executable. Click the "Standalone CLI build" to go to the Tailwind release page. Find the version you need: for me, it's "tailwind-macos-arm64". You can download that here, but I'll copy the link address... so I can download it fancily via curl: curl -slO then paste!

It doesn't matter where you put this, but I'm going to move it into the bin/ directory and rename it to tailwindcss... instead of that long name. Finally, because other machines - like the computers of our co-workers or the machine that deploys our site - might need a different version of this file, let's ignore it.

16 lines .gitignore
... lines 1 - 14
/bin/tailwindcss

So yes, this does mean that everyone will need to download their own Tailwind binary.

The very last step is to make this executable. On a Linux-based system, that's:

chmod +x bin/tailwindcss

Oh, and there is an extra, very-very last step if you're on a Mac. Run:

open bin/tailwindcss

If this is the first time you've downloaded the file, it will ask you to verify that you do want to open it from a security standpoint.

Initializing Tailwind

Okay! We now have the bin/tailwindcss executable, which does not require Node. From here, we can follow the normal docs. This is something I really like about the new frontend philosophy. If you do need a build system, you can just use Tailwind's build system directly and follow their instructions: no need for a Symfony-specific solution.

Here, it says that we need to run:

tailwindcss init

So let's do that!

./bin/tailwindcss init

This creates a shiny new tailwind.config.js file. Let's go check it out!

/** @type {import('tailwindcss').Config} */
module.exports = {
content: [],
theme: {
extend: {},
},
plugins: [],
}
... lines 9 - 10

The most important thing is to configure the content key. This tells Tailwind where it should look for HTML that may contain Tailwind classes. Search for their Symfony-specific documentation. Down here, they have exactly what we want! Copy the content key... then paste! I mean... paste it in the correct spot!

... line 1
module.exports = {
content: [
"./assets/**/*.js",
"./templates/**/*.html.twig",
],
... lines 7 - 10
}
... lines 12 - 13

The last step is to copy the three base directive lines for Tailwind... and put those inside app.css. I'll remove the Bootstrap stuff... but keep a little bit of our custom code down here. Nice!

@tailwind base;
@tailwind components;
@tailwind utilities;
... lines 4 - 17

Building the CSS File

Finally, we're ready to build! At your command line, run bin/tailwind, use -i to point to the input assets/styles/app.css file, then -o to tell it where to output the final code. Use assets/styles/app.tailwind.css so it's in the same directory, which is important so that any relative image paths will still work. At the end, add -w so it will keep running and watching for changes:

./bin/tailwindcss -i assets/styles/app.css -o assets/styles/app.tailwind.css -w

And that's it! Built! Over here, we have an app.tailwind.css file containing all the goodies. Awesome!

In base.html.twig, instead of pointing at app.css - which is now kind of an "internal" source file - point this at app.tailwind.css.

... lines 1 - 2
<head>
... lines 4 - 11
{% block stylesheets %}
... lines 13 - 14
<link rel="stylesheet" href="{{ asset('styles/app.tailwind.css') }}">
{% endblock %}
... lines 17 - 21
</head>
... lines 23 - 75

Moment of truth. Back to the browser! Refresh. Our site is styled! That means we can get rid of the Bootstrap stuff: remove the Bootstrap CDN link... since we were just demonstrating how that works... and also the button down here.

That looks good!

Ignoring the Built File

But what about this app.tailwind.css built file? Do we ignore that from git? Do we commit it? It's up to you! We can commit it - it would make deploying easier, but we generally don't want to commit built stuff. I will ignore it... then we'll see how that works into our deployment process a bit later.

17 lines .gitignore
... lines 1 - 15
/assets/styles/app.tailwind.css

Ok, done! Next: Let's turn to JavaScript.

Leave a comment!

0
Login or Register to join the conversation
Cat in space

"Houston: no signs of life"
Start the conversation!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=8.1",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "babdev/pagerfanta-bundle": "^4.0", // v4.2.0
        "doctrine/doctrine-bundle": "^2.7", // 2.10.0
        "doctrine/doctrine-migrations-bundle": "^3.2", // 3.2.4
        "doctrine/orm": "^2.12", // 2.15.2
        "knplabs/knp-time-bundle": "^1.18", // v1.20.0
        "pagerfanta/doctrine-orm-adapter": "^4.0", // v4.1.0
        "pagerfanta/twig": "^4.0", // v4.1.0
        "stof/doctrine-extensions-bundle": "^1.7", // v1.7.1
        "symfony/asset": "6.3.*", // v6.3.0
        "symfony/asset-mapper": "6.3.*", // v6.3.0
        "symfony/console": "6.3.*", // v6.3.0
        "symfony/dotenv": "6.3.*", // v6.3.0
        "symfony/flex": "^2", // v2.3.1
        "symfony/framework-bundle": "6.3.*", // v6.3.0
        "symfony/http-client": "6.3.*", // v6.3.0
        "symfony/monolog-bundle": "^3.0", // v3.8.0
        "symfony/proxy-manager-bridge": "6.3.*", // v6.3.0
        "symfony/runtime": "6.3.*", // v6.3.0
        "symfony/stimulus-bundle": "^2.9", // v2.9.1
        "symfony/twig-bundle": "6.3.*", // v6.3.0
        "symfony/ux-turbo": "^2.9", // v2.9.1
        "symfony/web-link": "6.3.*", // v6.3.0
        "symfony/yaml": "6.3.*", // v6.3.0
        "twig/extra-bundle": "^2.12|^3.0", // v3.6.1
        "twig/twig": "^2.12|^3.0" // v3.6.1
    },
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.4", // 3.4.4
        "symfony/debug-bundle": "6.3.*", // v6.3.0
        "symfony/maker-bundle": "^1.41", // v1.49.0
        "symfony/stopwatch": "6.3.*", // v6.3.0
        "symfony/web-profiler-bundle": "6.3.*", // v6.3.0
        "zenstruck/foundry": "^1.21" // v1.33.0
    }
}
userVoice