If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeThe 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.
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.
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.
... 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.
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 |
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!
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.
... lines 1 - 15 | |
/assets/styles/app.tailwind.css |
Ok, done! Next: Let's turn to JavaScript.
"Houston: no signs of life"
Start the conversation!
// 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
}
}