Tailwind CSS
I love using Tailwind for CSS. If you've never used it before, or maybe only heard of it, you might... hate it at first. That's because you use classes inside of HTML to define everything. And so your HTML can end up looking, well, a bit crazy. But give it a chance. I've absolutely fallen in love with it. And, instead of this looking ugly to me, it looks descriptive.
Tailwind Requires Building!
Tailwind isn't your traditional CSS behemoth where you download a giant CSS file and include it. Instead, Tailwind has a binary that parses all of your templates, finds the classes you're using, and then dumps a final CSS containing just those classes. So it keeps your final CSS as small as possible.
But to do this, duh duh duh! Tailwind requires a build step. And that's okay. Just because we don't have a build step for our entire JavaScript system doesn't mean we can't opt into a small one for a specific purpose.
Installing symfonycasts/tailwind-bundle
And there's a super-easy bundle to help us do this with AssetMapper. It's called symfonycasts/tailwind-bundle. Hey, I've heard of them!
Head down here, go to the documentation... and I'll copy the composer require line. Spin over and run that:
composer require symfonycasts/tailwind-bundle
This bundle will help us run the build command in the background and serve up the final file. We point it at a real CSS file, then it'll sneak in the dynamic content. You'll see.
While we're here, run:
php bin/console debug:config symfonycasts_tailwind
to see the default configuration for the bundle. By default, the file that it "builds" is assets/styles/app.css... which is great because we already have an assets/styles/app.css file!
To get things set up, run a command from the bundle:
php bin/console tailwind:init
Tip
If using the Symfony CLI, you can add a build command as a worker to be started whenever you run the Symfony web server:
# .symfony.local.yaml
workers:
tailwind:
cmd: ['symfony', 'console', 'tailwind:build', '--watch']
See the docs for more information.
This downloads the Tailwind binary in the background, which is awesome. That binary is standalone and doesn't require Node. It just works. The command also did two other things. First: it added the three lines needed inside of app.css:
| @tailwind base; | |
| @tailwind components; | |
| @tailwind utilities; | |
| // ... lines 4 - 8 |
When this file is built, these will be replaced by the actual CSS we need. Second, this created a tailwind.config.js file:
| /** @type {import('tailwindcss').Config} */ | |
| module.exports = { | |
| content: [ | |
| "./assets/**/*.js", | |
| "./templates/**/*.html.twig", | |
| ], | |
| theme: { | |
| extend: {}, | |
| }, | |
| plugins: [], | |
| } |
This tells Tailwind where to look for all the classes we'll use. This will find any classes in our JavaScript files or our templates.
To execute Tailwind, run:
php bin/console tailwind:build -w
For watch. That builds... then hangs out, waiting for future changes.
So... what did that do? Remember: we're already including app.css on our page. When we refresh, woh! It looks a bit different! The reason is, if you view the page source, and click to open the app.css file, it's full of Tailwind code! Our app.css file is no longer this exact source file! Behind the scenes, the Tailwind binary parses our templates, dumps a final version of this file, which is then returned. This file already contains a bunch of code because I filled the CRUD templates with Tailwind classes before we started the tutorial.
Using Tailwind
But let's see this in action for real. If we refresh the page, this is my h1. It's small and sad. Open templates/main/homepage.html.twig. On the h1, add class="text-3xl":
| // ... lines 1 - 4 | |
| {% block body %} | |
| <h1 class="text-3xl">Space Inviters: Plan your voyage and come in peace!</h1> | |
| {% endblock %} |
Now, refresh. It works! If that text-3xl wasn't in the app.css file before, Tailwind just added it because it's running in the background.
Pasting in The Layout
So Tailwind is working! To celebrate, let's paste in a proper layout. If you downloaded the course code, you should have a tutorial/ directory with a couple of files. Move base.html.twig into templates:
| <html> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |
| <title>{% block title %}Space Inviters!{% endblock %}</title> | |
| <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 128 128%22><text y=%221.2em%22 font-size=%2296%22>⚫️</text></svg>"> | |
| {% block stylesheets %} | |
| {% endblock %} | |
| {% block javascripts %} | |
| {{ importmap('app') }} | |
| {% endblock %} | |
| </head> | |
| <body class="bg-black text-white font-mono"> | |
| <div class="container mx-auto min-h-screen flex flex-col"> | |
| <header class="my-8 px-4"> | |
| <nav class="flex items-center justify-between mb-4"> | |
| <div class="flex items-center"> | |
| <a href="{{ path('app_homepage') }}"> | |
| <img src="{{ asset('images/logo.png') }}" width="50" alt="Space Inviters Logo" > | |
| </a> | |
| <a href="{{ path('app_homepage') }}" class="text-xl ml-3">Space Inviters</a> | |
| <a href="{{ path('app_voyage_index') }}" class="ml-6 hover:text-gray-400">Voyages</a> | |
| <a href="{{ path('app_planet_index') }}" class="ml-4 hover:text-gray-400">Planets</a> | |
| </div> | |
| <div | |
| class="hidden md:flex pr-10 items-center space-x-2 border-2 border-gray-900 rounded-lg p-2 bg-gray-800 text-white cursor-pointer" | |
| > | |
| <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-500" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M10 10m-7 0a7 7 0 1 0 14 0a7 7 0 1 0 -14 0"/><path d="M21 21l-6 -6"/></svg> | |
| <span class="pl-2 pr-10 text-gray-500">Search Cmd+K</span> | |
| </div> | |
| </nav> | |
| </header> | |
| <!-- Make sure the main tag takes up the remaining height --> | |
| <main class="flex-grow">{% block body %}{% endblock %}</main> | |
| <!-- Footer --> | |
| <footer class="py-4 mt-6 bg-gray-800 text-center"> | |
| <div class="text-sm"> | |
| With <svg class="inline-block w-4 h-4 text-red-600 fill-current" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M10 3.22l-.61-.6a5.5 5.5 0 00-7.78 7.78l7.39 7.4 7.39-7.4a5.5 5.5 0 00-7.78-7.78l-.61.61z"/></svg> from Symfonycasts. | |
| </div> | |
| </footer> | |
| </div> | |
| </body> | |
| </html> |
And these other two go into the main/ directory:
| {% extends 'base.html.twig' %} | |
| {% block title %}Space Inviters!{% endblock %} | |
| {% block body %} | |
| <div class="flex"> | |
| <aside class="hidden md:block md:w-64 bg-gray-900 px-2 py-6"> | |
| <h2 class="text-xl text-white font-semibold mb-6 px-2">Planets</h2> | |
| <div> | |
| {{ include('main/_planet_list.html.twig') }} | |
| </div> | |
| </aside> | |
| <section class="flex-1 ml-10"> | |
| <form | |
| method="GET" | |
| action="{{ path('app_homepage') }}" | |
| class="mb-6 flex justify-between" | |
| > | |
| <input | |
| type="search" | |
| name="query" | |
| value="{{ app.request.query.get('query') }}" | |
| aria-label="Search voyages" | |
| placeholder="Search voyages" | |
| class="w-1/3 px-4 py-2 rounded bg-gray-800 text-white placeholder-gray-400" | |
| > | |
| <div class="whitespace-nowrap m-2 mr-4">{{ voyages|length }} results</div> | |
| </form> | |
| <div class="bg-gray-800 p-4 rounded"> | |
| <table class="w-full text-white"> | |
| <thead> | |
| <tr> | |
| <th class="text-left py-2">Purpose</th> | |
| <th class="text-left py-2 pr-4">Planet</th> | |
| <th class="text-left py-2">Departing</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| {% for voyage in voyages %} | |
| <tr class="border-b border-gray-700 {% if loop.index is odd %} bg-gray-800 {% else %} bg-gray-700 {% endif %}"> | |
| <td class="p-4">{{ voyage.purpose }}</td> | |
| <td class="px-2 whitespace-nowrap"> | |
| <img | |
| src="{{ asset('images/'~voyage.planet.imageFilename) }}" | |
| alt="Image of {{ voyage.planet.name }}" | |
| class="inline-block w-8 h-8 rounded-full bg-gray-600 ml-2" | |
| > | |
| </td> | |
| <td class="px-2 whitespace-nowrap">{{ voyage.leaveAt|date('Y-m-d') }}</td> | |
| </tr> | |
| {% endfor %} | |
| </tbody> | |
| </table> | |
| </div> | |
| <div class="flex items-center mt-6 space-x-4"> | |
| <a href="#" class="block py-2 px-4 bg-gray-700 text-white rounded hover:bg-gray-600">Previous</a> | |
| <a href="#" class="block py-2 px-4 bg-gray-700 text-white rounded hover:bg-gray-600">Next</a> | |
| </div> | |
| </section> | |
| </div> | |
| {% endblock %} |
| <ul> | |
| {% for planet in planets %} | |
| <li class="mb-4 group"> | |
| <a href="{{ path('app_planet_show', { | |
| 'id': planet.id, | |
| }) }}" class="block transform transition duration-300 ease-in-out hover:translate-x-1 hover:bg-gray-700 rounded"> | |
| <div class="flex justify-between items-start p-2"> | |
| <div class="pr-3"> | |
| <span class="text-white">{{ planet.name }}</span> | |
| <span class="block text-gray-400 text-sm">{{ planet.lightYearsFromEarth|round|number_format }} light years</span> | |
| </div> | |
| <img | |
| class="flex-shrink-0 w-8 h-8 bg-gray-600 rounded-full group-hover:bg-gray-500 transition duration-300 ease-in-out" | |
| src="#" | |
| alt="Image of {{ planet.name }}" | |
| > | |
| </div> | |
| </a> | |
| </li> | |
| {% endfor %} | |
| </ul> |
Refresh. Huh, no difference. That's because, at least on a Mac, because I moved and overwrote those files, Twig didn't notice that they were updated... so its cache is out-of-date.
Open a fresh terminal tab and run:
php bin/console cache:clear
Then... woo! Welcome to Space Inviters! Styled up and ready to go! But there's nothing special about the new templates. We do have a list of voyages... but it's all boring, normal Twig code with Tailwind classes.
Referencing Assets Dynamically
We do have some broken planet images though. To fix those, go into the tutorial/assets/ directory... and move all of those planets into assets/images/. Delete the tutorial/ folder.
That broken img tag comes from the _planet_list.html.twig partial. Here it is:
| <ul> | |
| {% for planet in planets %} | |
| <li class="mb-4 group"> | |
| <a href="{{ path('app_planet_show', { | |
| 'id': planet.id, | |
| }) }}" class="block transform transition duration-300 ease-in-out hover:translate-x-1 hover:bg-gray-700 rounded"> | |
| <div class="flex justify-between items-start p-2"> | |
| // ... lines 8 - 11 | |
| <img | |
| // ... line 13 | |
| src="#" | |
| // ... line 15 | |
| > | |
| </div> | |
| </a> | |
| </li> | |
| {% endfor %} | |
| </ul> |
I left it for us to finish! How nice of me! We know that we can do {{ assets() }} then something like images/planets-1.png. That would work. But this time, the planet-1.png part is a dynamic property on the Planet entity. So, instead say ~ then planet.imageFilename:
| <ul> | |
| {% for planet in planets %} | |
| <li class="mb-4 group"> | |
| <a href="{{ path('app_planet_show', { | |
| 'id': planet.id, | |
| }) }}" class="block transform transition duration-300 ease-in-out hover:translate-x-1 hover:bg-gray-700 rounded"> | |
| <div class="flex justify-between items-start p-2"> | |
| // ... lines 8 - 11 | |
| <img | |
| // ... line 13 | |
| src="{{ asset('images/'~planet.imageFilename) }}" | |
| // ... line 15 | |
| > | |
| </div> | |
| </a> | |
| </li> | |
| {% endfor %} | |
| </ul> |
And... pretty! Yea, I know that's not what Earth and Saturn look like - I've got some randomness in my fixtures - but they're fun to look at!
Using KnpTimeBundle
Since day 6 is the "making everything look awesome day", let's do two more things. To start, I don't love this date. It's boring! I want a cool looking date.
So, install one of my favorite bundles:
composer require knplabs/knp-time-bundle
This gives us a handy ago filter. So as soon as this finishes, spin over and open homepage.html.twig. Search for leaveAt, here we go. Replace that date filter with ago:
| // ... lines 1 - 4 | |
| {% block body %} | |
| <div class="flex"> | |
| // ... lines 7 - 13 | |
| <section class="flex-1 ml-10"> | |
| // ... lines 15 - 29 | |
| <div class="bg-gray-800 p-4 rounded"> | |
| <table class="w-full text-white"> | |
| // ... lines 32 - 38 | |
| <tbody> | |
| {% for voyage in voyages %} | |
| <tr class="border-b border-gray-700 {% if loop.index is odd %} bg-gray-800 {% else %} bg-gray-700 {% endif %}"> | |
| // ... lines 42 - 49 | |
| <td class="px-2 whitespace-nowrap">{{ voyage.leaveAt|ago }}</td> | |
| </tr> | |
| {% endfor %} | |
| </tbody> | |
| </table> | |
| </div> | |
| // ... lines 56 - 59 | |
| </section> | |
| </div> | |
| {% endblock %} |
And... much cooler!
What else? Go check out the CRUD areas. These were generated via MakerBundle... but... I did preload them with Tailwind classes so they look good. Wow, there is a lot of celebrating right now. I'm not complaining.
But... if you go to a form, it looks terrible! Why? The form markup comes from Symfony's form theme... which outputs the fields without Tailwind classes.
Flowbite for Tailwind Examples
So what do we do? Do we need to create a form theme? Fortunately, no. One of the great things about Tailwind is there's an entire ecosystem set up around it. For example, Flowbite is a site with a mixture of open source examples and pro, paid features. On the open source side of things, you can, for example, find an "Alert" page with different markup to make great-looking alerts. And, you don't need to install anything with Flowbite. These classes are all native Tailwind. You can copy this markup into your project and refresh. Nothing special. And I love it.
Tip
Flowbite does also come with some JavaScript & a Tailwind Plugin. Check out the bonus chapter for the details!
This also has a forms section where it shows how we can make forms look really nice. In theory, if we could make our forms output like this, they would look great.
Adding a Tailwind Form Theme
And fortunately, there's a bundle that can help us. It's called tales-from-a-dev/flowbite-bundle. Click "Installation" and copy the composer require line. Then run it:
composer require tales-from-a-dev/flowbite-bundle
We're getting a lot of stuff installed today! This asks if we want to install the contrib recipe. Let's say yes, permanently. Cool!
Back on the installation instructions, we don't need to register the bundle - that happens automatically - but we do need to copy this line for the tailwind configuration file.
Open up tailwind.config.js, and paste that:
| /** @type {import('tailwindcss').Config} */ | |
| module.exports = { | |
| content: [ | |
| // ... lines 4 - 5 | |
| "./vendor/tales-from-a-dev/flowbite-bundle/templates/**/*.html.twig", | |
| ], | |
| // ... lines 8 - 11 | |
| } |
This bundle comes with its own form theme template with its own Tailwind classes. So we want to make sure that Tailwind sees those and outputs the CSS for them.
The last step over on the docs is to tell our system to use this form theme by default. This happens over in config/packages/twig.yaml. I'll paste... then fix the indentation:
Tip
Starting in version 0.4.0, use @TalesFromADevFlowbite/form/default.html.twig.
| twig: | |
| // ... line 2 | |
| form_themes: ['@Flowbite/form/default.html.twig'] | |
| // ... lines 4 - 8 |
That's it. Go back, refresh and eureka! In just over 10 minutes, we installed Tailwind and started using it everywhere.
Tomorrow, we'll turn back to JavaScript and leverage Stimulus to write reliable JavaScript code that we can love.
59 Comments
Hi !
Thank you very much for your work.
To get tailwind completion working in twig template with PhpStorm :
PhpStorm documentation
After step 2
npm install -D tailwindcssYou don't need to do step 3 because
tailwind.config.jsfile already exists (aftertailwind:init),but you need to add
"twig": "html"to theincludeLanguageslist in IDE "Settings" -> "Languages & Frameworks" -> "Style Sheets" -> "Tailwind CSS"That's kind of a non-sense. We want to avoid installing node, but we need it just for completion...
Hey AntoineRm,
Agree, that's not ideal, though at least you need it only for localhost where you're coding... no need of NodeJS on prod, that is still good. Or if you don't care about autocompletion - you can just ignore it.
Cheers!
Hey ThierryGTH,
Thank you for sharing this information with others!
Cheers!
I just did this, and the tutorial shows this:
but I get an error about a missing Flowbite namespace.
Doing a
bin/console debug:twigshows:which makes sense.
Changing '@Flowbite' to '@TalesFromADevFlowbite' makes it work.
Change since making this tutorial? (The world moves fast :wink:).
Or did I miss an update somewhere?
Lol, yup - a chance since yesterday! https://github.com/tales-from-a-dev/flowbite-bundle/pull/20
Their docs are already updated, but I'll add a note to the tutorial.
Thanks!
FOR THOSE WANTING TO INSTALL FLOWBITE
Some Flowbite components require custom javascript to work as intended. It is also part of the installation instructions for the flowbite-bundle. So if you are using that bundle, like this course does, I would highly recommend Flowbite. The link above links to the install instructions for Symfony. However, they aren't quite what we need when using Asset Mapper, but they are close.
1) Make sure you have node/npm installed on your local machine.
2) Install Flowbite with
npm install --dev flowbite(or the equivalent command for whatever node package manager you prefer)3) Update your
tailwind.config.js:4) Restart
symfony console tailwind:build -w5) Install Flowbite again for asset mapper use via
symfony console importmap:require flowbite6) Update
app.jsto import Flowbite js:I tested this out with their accordion component and it seemed to be working as intended.
One issue you may notice if doing this along with the course code is that the form theme will not match the dark styling of the site. You can resolve this by adding the
darkclass to the body of the base template. Tailwind, Flowbite, and the Flowbite Bundle all support light and dark mode. Adding thedarkclass applies any dark mode specific styles to its children.@ik3rib asked a question regarding using the flowbite-datepicker package. I tried to figure this out for a little while, but couldn't get it working.
Aside
Developing an app without Tailwind is not a pleasant thought for me, so I'm glad this course is covering how to use it. It is disappointing that it requires a build step though, since as I see it, the whole point of this stack, and asset mapper in general, are to avoid that. As soon as we have to add a build step, albeit a small one, it starts to negate the point of using the stack. However, at this point I can accept adding a small build step requirement as it is a necessary evil for an essential tool.
With that said, once you start having to install node packages separate from asset mapper (via npm, yarn, etc.), this stack starts to loose a lot more value for me. Especially when you consider that we have to use a
require()statement in the tailwind config file, which is not really a valid command in our stack. But it works because like @weaverryan mentioned in an earlier comment, the tailwind binary is a wrapped node app. So it works.. buuut it adds additional mental overhead when trying to reason about the application setup. Every time you see that squiggly you have to remember why therequire()is not erroring out there, but would elsewhere.I do love the spirit of this stack though, and will continue learning more about i! Even though I have some concerns with it, that is the case with every other front end framework I've worked in 😅 Mostly the JS world is just so much messier and hard to keep up with when compared to PHP and Symfony. Thanks for the work on this course and everyone's work on everything Symfony-related!
Hey @jdevine!
Thanks for posing this! Because of this - and similar questions from others - I'm adding a bonus chapter at the end about Flowbite JavaScript - https://symfonycasts.com/screencast/last-stack/flowbite
But the tl;dr:
flowbiteJavaScript (and I do in the bonus chapter), but we don't use it for Symfonycasts - and in my perfect world, we're all sharing tiny Stimulus controllers to handle things like drop-downs, instead of including big JS libraries like Flowbite.Cheers!
Thanks Ryan! I'll check that out.
The main reason I wanted to include the flowbite js was because it is mentioned as a requirement in the flowbite-bundle used in this lesson. Personally, I use flowbite for reference to create templates at work, but don't use their js. Although the reason in that case is because it conflicts with some of our legacy front-end code. So we just build our own interactive functionality.
However, I think the two areas in frontend that are worth including third party (possibly large) js libraries are tailwind and flowbite (or a similar component library). It is a huuuuuge pain in the butt the constantly have to rewrite functionality for something like combobox, especially if you want to get all the accessibility stuff right. It would be nice if there was a bundle that provided flowbite components, but with the interactivity built with stimulus. I'm not sure what that would look like, as I am not as familiar with stimulus. But if it's possible, maybe that's something I'll work on building.
Hey @jdevine!
Ah, all the more reason for us to have the extra, clarifying chapter then :)
💯
💯 again! This year, I'd like to have a place where we can grab "UI components" (i.e. HTML markup + Stimulus controllers or even a Twig component instead of direct HTML markup) and put them into our project. I think these would be copy-pastable things, though I think we should try to package and use reusable Stimulus controllers whenever possible (e.g. you might copy some HTML markup for a popover, then run the
importmap:require stimulus-popoverto get that reusable Stimulus controller). Heck, maybe there is even a little script -bin/console ui:install popover- to grab the stuff for you. I've been bouncing this around in my head since Oct and the value continues to be reinforced. I'd love to know what would be most useful for you. And, I would love to collaborate ;) - having more people with more real-world needs would help a lot.Cheers!
Glad to hear you thinking something like that is worth building too. In both my professional work and personal work I like the idea of being able to grab a premade component as a complete package (markup, styling, and interactivity), or some subset of those options.
It probably makes most sense to just provide a kind of "headless" component like you mentioned where it just installs the controller (logic).
In a dream scenario, a user could even install a full-blown component with a command
bin/console ui:install popoverlike you mentioned, but with some additional optional parameters. For the sake of this example, I'm going to say the defaults for this command install the stimulus controller and an unstyled twig template with the necessary html and variables (basically functioning as props in other frontend frameworks). A parameter like--headlessor--controller-onlywould only install the stimulus controller. Another parameter--theme THEME_NAMEwould install the component, twig template, and provide styling based on preset themes (Flowbite, DaisyUI, Bootstrap, etc.) In terms of templates, perhaps this could draw inspiration from thesymfony/formpackage.This is just a first off the cuff reaction to imagining how a user might want to work with these components. I haven't given a whole lot of consideration to the architecture, but have a vague idea of how it might work. Although, I don't have experience building reusable Symfony bundles. It seems like the "headless" part of it is definitely doable, the template part isn't as clear. And even if the template part works, it would be considering whether Symfony would want to actually maintain that piece or hand it off to individual bundle developers. I would argue that it would be worth maintaining, because in my opinion, it is the one missing piece to making Symfony a truly all-in-one solution (well.. aside from probably still needing Tailwind). At the same time, it would only be worth it if there was a good chance it would be a long-living solution and unlike so many frontend frameworks/libraries that come and go.
This granularity of this solution would be great because it could be used for new projects to get up and running quickly, or in a legacy codebase (which is my situation). Vue and React are great options for a lot of cases, but a massive headache to start to implement into an existing legacy codebase (although I have done that with Vue via the stimulus bridge at work).
Not sure what your thoughts are on any of that. I would be more than happy to collaborate on something like that or whatever you had in mind.
Hey @jdevine!
Thanks for the detailed thoughts! We've started working internally on this... I'm not sure when we'll have something to show, but it'll likely show up on the symfony/ux repository. Open questions are:
A) Exactly how Flexible we make this. Initially, it will probably be Tailwind only. But it could be feasible to have an unstyled version or a version where the CSS is also provided (but not via Tailwind).
B) We are definitely going to give these to the developers and not maintain them. Especially for HTML markup, devs will need to customize. But for Stimulus controllers, if it's large enough, those could be turned into their own packages.
Anyway, hopefully we'll open up the work soon and people can collaborate - not quite there yet!
Cheers!
There have been some changes in tailwind since this tutorial came out. In addition to add the class 'dark' to body (thanks to jdevine for his comment), you also must add the line
to the tailwind.config.js. It should look like
Thanks for the great tutorial and helpfull comments!
This is not working for me, what is changed since then?
Hey Omer,
Since Tailwind CSS v4 that
tailwind.config.jsis redundant now and you should declare your variants in the CSS:And then make sure you have
<body class="dark">to apply the dark theme.I hope that helps!
Cheers!
[ERROR] Tailwind CSS init failed: see output above.i tried to isnstall tailwind and during installation i ran into a timeout. after the timeout i could not run:
php bin/console tailwind:initwithout hte error above.
deleting the
var/tailwindfolder did the trick ...Hey @Ek24
That's unexpected. I wonder if you have a time limit to execute scripts on your local machine. Anyway, Thank you sharing your solutions
Cheers!
With Symfony 7, I have no completion in my twig files with PHPstorm ( it works with VSC). I did add "twig": "html" to the includeLanguages list but nothing change. I have to work in VSC because it's really not convenient to work without completion.
Edit: After hours of research, it seems that it's a very old issues of JetBrain IDE. The only way is to install tailwind with npm. Now that symfony push for avoiding node, i hope they will take care of that, or every tailwind lover will switch to VSC.
Hey AntiineR,
Glad to hear you were able to find the solution. And thanks for sharing it with others.
Yeah, I hope that issue will be pushed forward too!
Cheers!
Yes. I will just install npm in local to get the completion.
I'm unable to set dark mode with flowbite forms with tailwind-version-4. I tried to set in the main body tag class = "dark" and in the tailwind.config.js
darkMode: 'selector',, but those didn't do the trick.Hey Omer,
Yeah, in Tailwind v4, dark mode is no longer configured in
tailwind.config.js. Having that file with Tailwing v4 is not the best practice anymore. Now you can do it in the CSS file where you import Tailwind CSS and Flowbite:And after this make sure your CSS is recompiled and try to add dark CSS class to the body tag:
In short, you should declare your custom variants directly in the CSS using special tag instead of doing it in
tailwind.config.jsas it was in Tailwind v3.I hope that helps!
Cheers!
Hey Victor,
Indeed I tried this with version 7.4 Symfony and the tailwind.config.js is not used. Is there a way to get @plugin "@flowbite/plugin"; with assetmapper or are node_modules inevitable. What I did now is get default.css file from the flowbite repository and include this in my app.css and that worked but I dont understand why the tales-form-a-dev is not including this css file.
note: Im considering learning webpack-encore
Hey Omer,
First of all, we have a bonus chapter about Flowbite at the end of this course: https://symfonycasts.com/screencast/last-stack/flowbite - I would recommend you to check it in case you're interested in Flowbite integration. I hope that will help you with some possible questions. I personally don't use Flowbite much, so it's hard to me to give you decent advice on it.
I think the short answer is yes, Node modules are inevitable if you want Tailwind plugins like Flowbite to work as plugins, because those plugins are not just CSS. What you did (copying default.css) works only because you bypassed the plugin system entirely.
Webpack Encore should be a good option for solving this. And we have a separate coruse about it here: https://symfonycasts.com/screencast/webpack-encore . Or you can take a look at the modern Vite.js tool instead, which has become popular lately, though we still don't have a course about it yet.
Cheers!
Hello when i loop over "planets" i realize the array is empty, was there a step before to populate the db ?
Hey Yoyo,
Did you load the fixtures? :) When you download and unzip the course code - you will find the README.md file inside with all the instructions you have to do. On of them will be load the fixtures with:
symfony console doctrine:fixtures:loadcommand. Just run this command and try again ;)Cheers!
Hi. I looked all over and couldn't find a fix this
composer require symfonycasts/tailwind-bundle./composer.json has been updated
Running composer update symfonycasts/tailwind-bundle
Loading composer repositories with package information
Updating dependencies
Nothing to modify in lock file
Writing lock file
Installing dependencies from lock file (including require-dev)
In VersionParser.php line 191:
Invalid version string "9999999.9999999" `
I also tried
composer require symfonycasts/tailwind-bundle:dev-mainbut the same result!
Hey Valentin,
That's kinda weird. Try clearing composer's cache
composer clear-cacheand even upgrading composercomposer self-upgradeif that doesn't work, try reinstalling your vendors
Cheers!
Hi!
Thank you for the course)
Have a question about Tailwind 4 setup in the context of symfonycasts bundle.
According to Tailwind docs, the config file
tailwind.config.jsis not required for the version 4 and above. Andbin/console tailwind:initcommand has the next output:So, how to deal with flowbite config in this situation? Where the flowbite templates path should be added if
tailwind.config.jsdoesn`t exists?Thanks.
Hey @Olexandr,
In Tailwind 4, you use the
@plugindirective directly in your CSS file. See the Flowbite getting started guide to see how this works.Alternatively, you can still use the v3
tailwind.config.jsbut now you define it in your CSS file also, with the@configdirective.Hope that helps!
Kevin
The simplest solution is to set a previous version of Tailwind in
symfonycasts_tailwind.yaml:Thank you, @kbond.
When I use Flowbite and want to edit a planet, the error appears: Unknown "tailwind_merge" filter
Hey @giorgiocba!
Is it a PHP/Twig error?
I believe installing this package will solve. It adds the
tailwind_mergefilter.Let me know if that doesn't help.
--Kevin
Problem solved. Thank you very much!
When I went to use the SymfonyCasts/tailwind-bundle in an API Platform project using docker containers it took me a while to discover that I had to go into the docker container and run the
bin/console tailwind:buildcommand inside the docker container because running it at the WSL(host operating system) level wasn't updating the vendor folder inside the container because the compose.override.yaml has a volume entry for- /app/varthat causes the var directory to not map.Perhaps the documentation at https://symfony.com/bundles/TailwindBundle/current/index.html#watch-mode-in-docker-with-windows-host could be augmented to help diagnose that issue as it only covers situations where the Windows /var directory is mapped into the docker container. It could also be a place to add instructions for modifying a docker compose.yaml file to include the proper watch mode usage of the
bin/console tailwind:buildcommand for dev and the compile version for production.The error message one gets if they are in this situation is "Built Tailwind CSS file does not exist". Which is true in the docker container, even if the file exists outside of the docker container.
Hey @Jason-Aller
Thank you for the detailed explanation, and I agree that this could be mentioned in the Symfony docs. Have you considered proposing a PR?
https://github.com/SymfonyCasts/tailwind-bundle/edit/main/doc/index.rst
Cheers!
I tried to use complete updated software (including Symfony 7) for these casts, but then as it comes to this module where we have the first access to DB, DB access fails complete. It is that the whole zenstruck part is depecreated and not working anymore with zenstruck/foundry >= ^2. I do not see any database item at all.
Hey @astrodev,
Yeah, foundry 2 added a bunch of breaking changes - there is a migration path for 1.x-2.x you can take a look at. In the meantime, no problem staying on foundry 1.x for this tutorial.
My form looks slightly different than in the video:
E.g.
var(--tw-text-opacity)seens to be1, so thecoloroflabelandinputelements isrgb(17, 24, 39)(dark color).var(--tw-bg-opacity)also seems to be1, which make thebackground-colorforinputelementsrgb(249, 250, 251)(light color).E.g.
dark:text-whitedoes not seem to have an effect on thelabelelements.Any idea what the reason could be?
Okay, as soon as I configure Firefox to use dark mode the form looks the same. But it looks broken when I use light mode in Firefox.
You got it :). I should have mentioned this. I didn't style for light mode. So if you ARE in light mode, then everything still looks dark (from my design) except for the form elements, which are properly styled for both. So you get "light mode" forms... on my dark background.
good morninig all, i don't have any idea, why this error showed up
In bb90e00c589eb640d305e9be5c5614e2.php line 1295:
syntax error, unexpected variable "$__internal_6f47bbe9983af81f1e..."Hey Ayoub,
What PHP version are you using? For this project you need PHP 8.2+. Also, try to clear the cache, i.e. manually remove it with
rm -rf var/cache/and try again. Does it help? The file you linked sounds like a cached file, right? So cache clear should probably help.Cheers!
thank you
i fix it already with the composer.json i put this versions in my composer and i run
composer updatethank you again
Thanks for sharing it with others!
Cheers!
Hello Ryan! Amazing course!
I'm all new to php frameworks and your lessons are really valuable, thanks a lot!
Talking about tailwindcss, it supports plugins, like daisyui. I can't make tailwind find daisyui source code when i require the pack using "symfony console importmap:require daisyui". It's because symfony's asset mapper installs the lib inside "assets/vendor/daisyui" and tailwindcss search for plugins in the "nome_modules/" folder, which is only created thru npm install.
What's the recomended approach reagaarding this issue?
Thank you!
Tarik Tarilonte.
Yo @Tarik!
Thank you! This is a fun one!
I haven't had a chance to try it yet, but I believe (i.e. I have been told by one person), that you can accomplish plugins with Tailwind (using the standalone binary that the bundle downloads) by exactly following the existing documentation. So, for Daisy - https://daisyui.com/docs/install/ - you should be able to do exactly what they show:
A)
npm i -D daisyui@latestB)
plugins: [require("daisyui")],intailwind.config.jsThe standalone
tailwindbinary we download is a Node app... but it's wrapped up in a way that makes it standalone and not require node. But then if you need to add a 3rd party plugin to it, then you should still install it via node. And that's ok! Not needing to use node/npm is nice, but it's also totally great to be able to use it to grab something you need.Btw, if you try this and it works, will you let me know? It's something we will need to add to the TailwindBundle documentation to help others.
Cheers!
Hi Ryan! Hope all is well! Been praying for you.
I have installed the daisyui plugin as suggested above. And it works great! Thanks so much for helping with this!
I do have another question, though. Is there a way to pass in a config? I tried and had to format it different, but I don't thinks it's taking effect - or rather, I don't think the config is being passed to the plugin. I've just started with this so, of course, it's likely that I simply don't know how to check if the config is working or not ;-) I tried to exclude the "button" and the btn classes still worked.
This is the format that passed the tailwind build process (the excludes were only for testing):
`
`
If you have some time, any help with this would be greatly appreciated.
Again, thanks for all the videos and helpful comments!
I haven't tested it yet, but I believe I've found the solution.
https://v4.daisyui.com/docs/config/
Here's their sample configuration:
`
`
The reason is that the tailwind-bundle defaults to Tailwind version 3, but the docs for DaisyUO assumed Tailwind version 4. However, v4 of DaisyUI assumes Tailwind version 3.
Hey @JeffJones
You have a small detail in your config, move your config options outside the
pluginskey. Like this:Cheers!
"Houston: no signs of life"
Start the conversation!