1

I'm currently trying to set up a flask project using tailwindcss 3.0.23. For templating I'm using jinja. Furthermore yarn is used. During previous projects when working on frontend components I was used to an automatic adoption of styling by the usage of inline HTML classes. As I worked myself through this tutorial, I just realized I have to re-run npx tailwindcss -i ./static/src/style.css -o ./static/css/main.css to generate the most recent version of my tailwind css classes, that I defined in my style.css. As I'm now a lazy developer I would like to configure the project in a way that introduces two things.

#1 automatic generation of most recent css

This should allow me to add tailwind classes, which are automatically applied after saving my .css file and reloading my localhost:3000/index page.

#2 inline tailwind html classes for styling

As described earlier, I need to put all my tailwind classes into the style.css file which looks like the following code snippet, to define a class todo-text that is then later used in my templates/index.html. Instead I would be more flexible and also be able to add tailwind classes to my exisitng index.html like this. <p class="text-xl font-mono font-bold">text</p>

@tailwind base;
@tailwind components;

.todo-text {
  @apply text-xl font-mono font-bold;
}

@tailwind utilities;

I have already read about the just-in-time engine of tailwind, but I'm not really sure how to configure my project so that it will work using tailwind 3.0.23. I further do not want to use a CDN as solution and I would appreciate anybody that would also add some explanation about the inner workings, why my current process is so cumbersome and furthermore, which role nodejs plays in this whole topic. Lastly, I've heard of the Flask Assets package but I'm not sure if this is even an option to solve my issues.


Config: My tailwind.config.js looks like this:

module.exports = {
  content: ["./templates/*.{html,js,jsx}"],
  theme: {
    extend: {},
  },
  plugins: [],
};

Update: As a limited answer to "Why node? What is node used for?" I want to reference this post. But want to encourage you to add more elaborate sources to understand the background of using nodejs better.

2 Answers 2

2

I came across the -watch flag that automatically regenerates the latest .css after changes occur.

So just open a new terminal and run npx tailwindcss -i ./static/src/input.css -o ./static/dist/output.css --watch to activate automatic updates after changes in your template files.

Hope that helps!

Sign up to request clarification or add additional context in comments.

Comments

0

I was recently struggling with the same problem and was determined to get the compiler automatically started. After some research I found a way to plug-in a subprocess that is automatically torn down when the flask server shuts down.

The compiler is configured to only start in debug mode (flask --debug run).

Assumptions for this code to work:

  • tailwind was installed with npm
  • your npm package.json and tailwind.config.js are located in the parent folder of your flask app folder
  • you define the command to run tailwindcss (e.g. npx tailwindcss -i {src} -o {dst} --minify --watch) within your package.json file as a child of scripts.

package.json

{
    "dependencies": {. . .},
    "scripts": {
        "watch": "npx tailwindcss -i ./app/static/src/main.css -o ./app/static/dist/main.min.css --minify --watch"
    }
}

app/utils/compiler.py

import atexit
import json
import os
import shlex
import subprocess
from pathlib import Path
from typing import Optional

import flask
import werkzeug


class TailwindCompiler:

    proc: Optional[subprocess.Popen] = None

    def __init__(
        self,
        app: flask.Flask,
        npm_script_name: str,
        debugmode_only: bool = True,
    ):
        """Start subprocess to compile TailwindCSS on-the-fly on change.

        Prerequisites: flask app is run in debug mode & second instance started
        by `werkzeug` is running (this second instance is needed in debug mode
        to watch for changes). This ensures that the subprocess is only started
        once.
        """
        self.app = app
        debugmode = app.config["DEBUG"]
        is_reloader = werkzeug.serving.is_running_from_reloader()

        if debugmode and is_reloader:
            self.run(npm_script_name)
        elif not debugmode and not debugmode_only:
            self.run(npm_script_name)
        else:
            pass

    def run(self, npm_script_name):
        """Run TailwindCSS Compiler as subprocess.

        Store the current working dir and assume that tailwind, configs,
        etc. are in the apps parent dir. Change the working directory to the
        parent dir. Get the command for running tailwind from the package.json.
        Start the subprocess. Then change back to the original working dir.
        Finally register the subprocess so that it can be shut down on exit.

        Parameters
        ----------
        npm_script_name : str
            The script that should be run must be defined in a `package.json`
            file as a child of the `scripts` key like so:
              "scripts": {
                "watch": "npx tailwindcss -i ./app/static/src/main.css -o ./app/static/dist/main.min.css --minify --watch"
                }
        """

        print("=== Starting TailwindCSS Compiler ===")

        cwd = os.getcwd()
        app_parent_dir = str(Path(self.app.root_path).parent)

        os.chdir(app_parent_dir)
        with open("package.json") as f:
            package = json.load(f)
            try:
                cmd = shlex.split(package["scripts"][npm_script_name])
            except KeyError:
                raise ValueError(
                    f"No script with name '{npm_script_name}' "
                    "found in `package.json`."
                )

        TailwindCompiler.proc = subprocess.Popen(cmd)
        os.chdir(cwd)

        atexit.register(TailwindCompiler.terminate_on_exit)

    @classmethod
    def terminate_on_exit(cls):
        print("=== Closing TailwindCSS Compiler ===")
        TailwindCompiler.proc.terminate()

It is now very easy to use! Initialize the class after you created the app object.

app.py

from app import create_app
from app.utils import TailwindCompiler

app = create_app()

# Run TailwindCSS as subprocess; watch for changes; build css on-the-fly
TailwindCompiler(app, npm_script_name="watch", debugmode_only=True)

The output of the compiler is presented between the normal flask server logs.

flask --debug run

 * Serving Flask app 'wsgi.py'
 * Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:5000
Press CTRL+C to quit
 * Restarting with stat
=== Starting TailwindCSS Compiler ===
 * Debugger is active!
 * Debugger PIN: 143-120-061

Rebuilding...

Done in 168ms.
127.0.0.1 - - [05/Dec/2022 11:48:21] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [05/Dec/2022 11:48:21] "GET /static/dist/main.min.css HTTP/1.1" 200 -
. . .
^C=== Closing TailwindCSS Compiler ===

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.