Skip to content

Conversation

evnchn
Copy link
Collaborator

@evnchn evnchn commented May 28, 2025

Motivation

As discovered in #4802, the client-side Tailwind JavaScript library is CPU resource intensive (and network resource intensive, if you don't consider cachine), taking up over 60% of the page load time.

Provided that the user compromise by declaring classes they want to use ahead-of-time, it was discovered that we can run the Tailwind JIT engine on the server, significantly speeding up the JavaScript execution needed to get the page ready and interactable.

Implementation

  • Let pass tailwind='jit' for ui.run() and ui.run_with(), alongside existing True/False
  • Maintain set ALL_CLASSES_EVER_USED, which is used for:
    • Generating the tailwind.jit.css
    • Cachebusting said file when the hash of the frozenset of that set changes (set elements changed)
  • Pass additionally tailwind_jit and tailwind_jit_cachebusting to the index.html template, and display accordingly.
  • Now expect tailwindcss.exe as downloaded in https://v3.tailwindcss.com/docs/installation in the NiceGUI storage folder (default .nicegui for processing
  • Default ignore the files generated in the Tailwind JIT process in uvicorn ignores, so as to avoid server repeated restart.

Progress

  • I chose a meaningful title that completes the sentence: "If applied, this PR will..."
  • The implementation is complete.
  • Pytests have been added (or are not necessary).
  • Documentation has been added (or is not necessary).
  • Make cross-platform (other platforms don't have .exe suffix, after all) @falkoschindler do you think you can do that ASAP so that you can test? Or otherwise I need to get my Chromebook ready...
  • Should we warn the user, if they dynamically add new classes while in the middle of page execution, which would break since there's no page reload?
  • Should we use --watch mode for faster Tailwind JIT completion times, eliminating the cold start?
  • Should we serve tailwind.jit.css as critical CSS (inlined in index.html)? (I think not, since we have cachebusting and aggressive cache... Uncheck if you'd like to discuss)

Performance uplift

As from #4802 (comment)

Results are in!

Test code:

from nicegui import ui

ui.label('Hello, NiceGUI!').classes('text-2xl text-blue-500')

ui.run(tailwind=True) # Overriden False: Tailwind JIT instead of full Tailwind CDN JS

Without Tailwind JIT:

image

Scripting: 2415ms

With Tailwind JIT:

image

Scripting: 672ms

So the new loading time is only 25% of original. In other words, that's 4 times faster! 🚀

@evnchn evnchn requested a review from Copilot May 28, 2025 13:23
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces experimental support for running Tailwind JIT on the server, thereby reducing the CPU and network load caused by the client-side Tailwind library.

  • Parameter types allowing tailwind to be set to True/False or 'jit' have been updated in multiple functions and configuration files.
  • A caching mechanism and endpoint for serving the Tailwind JIT generated CSS have been introduced, and the template has been updated to load the generated CSS.

Reviewed Changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
nicegui/ui_run_with.py Updated tailwind parameter type and documentation to support 'jit'.
nicegui/ui_run.py Adjusted tailwind parameter type and updated reload exclusions to avoid restarting the server for generated files.
nicegui/templates/index.html Added conditional loading of tailwind_jit css based on configuration.
nicegui/nicegui.py Introduced caching for Tailwind JIT CSS generation and added a dedicated API endpoint.
nicegui/client.py Updated response payload to include tailwind_jit flags and cache busting value.
nicegui/classes.py Updated ALL_CLASSES_EVER_USED to include new classes for cache busting.
nicegui/app/app_config.py Updated tailwind parameter type to support 'jit'.

Comment on lines +111 to +118


@functools.lru_cache(maxsize=1)
def _get_tailwind_jit_csstext(classes: frozenset) -> Optional[str]:
"""Get the result of the Tailwind JIT compilation for the given classes."""
global _tailwind_jit_call_count # noqa: PLW0603
_tailwind_jit_call_count += 1
if _tailwind_jit_call_count > 1:
Copy link
Preview

Copilot AI May 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider reviewing the use of the global _tailwind_jit_call_count within the cached function for potential thread-safety issues if the function is ever called in a concurrent context.

Suggested change
@functools.lru_cache(maxsize=1)
def _get_tailwind_jit_csstext(classes: frozenset) -> Optional[str]:
"""Get the result of the Tailwind JIT compilation for the given classes."""
global _tailwind_jit_call_count # noqa: PLW0603
_tailwind_jit_call_count += 1
if _tailwind_jit_call_count > 1:
_tailwind_jit_lock = asyncio.Lock() # lock to ensure thread-safe access
@functools.lru_cache(maxsize=1)
def _get_tailwind_jit_csstext(classes: frozenset) -> Optional[str]:
"""Get the result of the Tailwind JIT compilation for the given classes."""
global _tailwind_jit_call_count # noqa: PLW0603
async with _tailwind_jit_lock: # ensure thread-safe access
_tailwind_jit_call_count += 1
if _tailwind_jit_call_count > 1:

Copilot uses AI. Check for mistakes.

'tailwind': core.app.config.tailwind,
'tailwind': core.app.config.tailwind is True,
'tailwind_jit': core.app.config.tailwind == 'jit',
'tailwind_jit_cachebusting': hash(frozenset(ALL_CLASSES_EVER_USED)) if core.app.config.tailwind == 'jit' else None,
Copy link
Preview

Copilot AI May 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using Python's built-in hash may result in non-deterministic values between sessions; consider using a stable hash function (e.g., hashlib.md5) to ensure reliable cache busting.

Suggested change
'tailwind_jit_cachebusting': hash(frozenset(ALL_CLASSES_EVER_USED)) if core.app.config.tailwind == 'jit' else None,
'tailwind_jit_cachebusting': hashlib.md5(str(sorted(ALL_CLASSES_EVER_USED)).encode()).hexdigest() if core.app.config.tailwind == 'jit' else None,

Copilot uses AI. Check for mistakes.

@evnchn evnchn added feature Type/scope: New feature or enhancement 🟡 medium Priority: Relevant, but not essential labels May 28, 2025
@rodja
Copy link
Member

rodja commented May 28, 2025

Thanks @evnchn. While performance improvements are great, they come at the cost of added complexity. This PR looks not to complicated but still it will add a maintainability burden. Besides that I have two questions for now:

  • The list of classes is only generated when a page is visited. How would --watch mode work in such cases? Would you auto-update the build css after JIT is done?
  • How long does the JIT process take up on the server?

@evnchn
Copy link
Collaborator Author

evnchn commented May 28, 2025

I think that, yes, it will add a maintainability burden, but I think it can be partially mitigated with a "beta rollout" strategy, in that NiceGUI does NOT take responsibility if the Tailwind JIT grinds to a halt, until we have enough brave users writing in that they have good experience with the feature, then we mark this as stable. Consider that, if you think this is too risky.

While for your points:

  • In the situation that the classes set grew (user did not follow advice and dynamically added classes, causing said class to fly under our radar), and we need to re-generate Tailwind JIT CSS, --watch would allow us to skip cold-starting tailwindcss.exe, since it is already running and will automatically generate the updated CSS once we put the new HTML in. But we need to handle how to detect the CSS has been written (since the process finishing is no longer a hint) and maybe edge cases such as tailwindcss.exe stopp because of problems in the exe itself, OOM, or any other weird situation.
  • JIT process takes 300ms in a cold start on my machine, but 10ms if we use --watch (time provided from the console output of tailwindcss.exe

@evnchn
Copy link
Collaborator Author

evnchn commented May 28, 2025

I want to emphasize that, no matter how slow tailwindcss.exe may be (I imagine it'd be slower on less performant machines, with more styles), that it is still worth it.

Since, given the @functools.lru_cache(maxsize=1), it runs Tailwind JIT once and once only, provided you don't dynamically add class. Even if you did add class, it will run once and once more only until you add another class (the previous class is also covered)

Therefore, it seems that for serving large websites, the pros outweight the cons.

Notably, I tried it with NiceGUI website, and it works flawlessly to my eyes.

Only case which this PR breaks: Creating elements mid-execution (ui.button('Surprise', on_click=lambda: ui.label('BOLD').classes('font-bold'))). Then, the label will not be bold, until the next time you refresh the page and do it again (warnings emitted in server console output)

@rodja
Copy link
Member

rodja commented May 29, 2025

it runs Tailwind JIT once and once only, provided you don't dynamically add class.

On a single page this might only happen when dynamically adding content. But the re-run must also happen if a new page is first accessed which contains not-before-used tailwind classes.

Therefore, it seems that for serving large websites, the pros outweight the cons.

Why are the benefits bigger for large websites? The performance boost for smaller sites should be more significant because the full tailwind js takes up a bigger portion of the overall page, or not?

Only case which this PR breaks: Creating elements mid-execution (ui.button('Surprise', on_click=lambda: ui.label('BOLD').classes('font-bold'))). Then, the label will not be bold, until the next time you refresh the page and do it again (warnings emitted in server console output)

Yes in such cases the developer should be warned.

I have still not found the time to try it myself. Maybe that would help me to decide whether I agree with your overall feeling that "the pros outweight the cons".

@evnchn
Copy link
Collaborator Author

evnchn commented May 29, 2025

re-run must also happen if a new page is first accessed which contains not-before-used tailwind classes

Yes you are right. But I am thinking if potentially a solution similar to #4799 (decorator which executes the page function right away the first time around) can eliminate this issue. Then, we can collect all classes and run the JIT once.

I am thinking about a more explicit API:

ui.tailwind_jit('font-bold') # registers the class for tailwind JIT

ui.label().tailwind_jit('font-bold') # this time around, we throw an error if unregistered classes are used

benefits bigger for large websites, considering that the Tailwind JS file takes up less portion of the page if the page itself is bigger

You have to bear in mind, that the tailwind client-side is a JS file, and code execution goes on.

While chatting with my friend about this PR, we noticed something:

image

Chunk marked in red: Vue (and possibly Quasar)

Chunk marked in blue: Definitely Tailwind CSS JS

So, we can visually see the speedup.

Applying this to a larger website, like NiceGUI documentation:

We can drop these blue chunks from the loading time, which can be huge as well.

image


Bottom line, I think trying this would make it more obvious how the pros and the cons are, and which outweights which (or it's a 50:50). But, right now, it is designed for Windows only, since it expects a .exe.

I think it could be possible for Linux and macOS users to rename their tailwind CLI standalone installation to a file ending in .exe, but I am not sure, and we need a slight bit of work in order that this PR's functionality can work on those platforms.

@falkoschindler
Copy link
Contributor

Ok, I see the potential benefit. But, the three main questions I have at the moment (apart from added complexity):

  • "user did not follow advice and dynamically added classes" - To me this seems like a very common case. Imagine a ui.log which might show errors with "text-red-500". This will only happen after some time when an error occurs. Or ui.refreshable constantly re-generating parts of the UI, possibly with classes based on the current app state. Or tiles representing database entries that only appear after the user created a first entry. Or dialogs and other dynamically created elements. Such cases are countless and an app with a constant set of Tailwind classes is - in my point of view - rather an exception. So will the new "jit" option be limited to such rare apps? Or is the user required to manually register all possible classes (ui.tailwind_jit(...))? Both options don't seem very handy.

  • How will the tailwind executable be distributed? As far as I can tell, 1) we either need to add executables for different architectures to our PyPI package (increasing the package size for everyone), 2) ask the users to add it themselves (complicating the NiceGUI installation process) or 3) somehow fetch it automatically on first run or during some kind of setup process (requires internet access and a reliable source).

  • Is there a simpler alternative? E.g. can we strip parts of Tailwind that are definitely not used in NiceGUI apps? And what about Tailwind 4.0? Will it be more or less expensive to run in our setup? Maybe they already improved their performance?

@evnchn
Copy link
Collaborator Author

evnchn commented May 30, 2025

Case of dynamically added classes

So, I have thought about it for a long time, and I think that the current implementation of applying Tailwind JIT to ALL pages is a bit harsh on the developer, especially if they want to turn on said functionality for commonly-visited pages only.

Consider that:

  • Homepage has mostly static styles, but the subpages with dynamic functionality will have wildly static styles, complex code paths, and other unpredictable things.
  • We need only the homepage to have a low First Contentful Paint (FCP), since that is the most jarring one. Imagine navigating from another website to a NiceGUI website's homepage. If that takes a long time, it's bad; Meanwhile for subpages, FCP matters less, and full functionality matters more. While we're at it, we can have the homepage fetch the Tailwind JS in the background, to drive down the FCP of subsequent non-JIT pages.

So, I would call for "Tailwind JIT engine only for selected pages with static styles, high requirement on low FCP, and are initially visited assuming normal browsing behaviour". See if this is a better balance.

How will the tailwind executable be distributed?

In this PR, the user is responsible for downloading the executable to .tailwind_jit folder. I imagine this is a low-frequency operation, and not all users will need it, so I tend to go against "add executables to PyPI", and "automatically fetch from internet". However, a helper function (imagine ui.download_tailwind_jit() which does the download for you) could be handy, and the user's responsible for deciding when to call the function, if at all.

simpler alternative

  • Strip parts of Tailwind that are definitely not used in NiceGUI apps: No. This would be a misunderstanding of the problem at hand. It is not possible to do any stripping, since the client-side Tailwind JS is non-customizable and monolithic. You can't remove parts of it that you don't use as a result.
    • I have thought of tree-shaking that JS using Webpack, but it'd break more things I imagine, since the code path of that isn't predictable at all.
  • What about Tailwind 4.0: No. They didn't pay attention to making client-side Tailwind JS faster. It seems equally as bulky and slow to me, from my browser profiling. Not only that, they now print a warning.
{FDB9D2F1-3631-4B6C-9944-42C6FE66F3EF}

Summary: Propose per-page JIT setting so as to make it fast when speed is needed, make it compatible when compatibility needed. Tailwind executable distribution needs to be de-coupled from the main library, but we can offer helper functions to download it. There is no simplier alternative since it's a client-side JS we're speaking - in fact Tailwind 4.0 actively prints a warning.

@EmberLightVFX
Copy link
Contributor

Don't get me wrong, I love to squeeze out every bit of speedup for NiceGUI!

A while ago I found this project:
https://github.com/THEGOLDENPRO/fastapi-tailwind
Maybe it would be possible to rewrite/that insperation from that project to write a tailwind compiler for NiceGUI where we crawl through all python files, find all places where tailwindcss is used and use those to compile the tailwind.css file. For tailwind classes that might not get fetched up, maybe we can add those to a nicegui.dynamic_tailwind_classes("") that can be catched.

Completely different from this PR but just a thought about tailwind speedup.

@evnchn
Copy link
Collaborator Author

evnchn commented May 30, 2025

@EmberLightVFX Surprise to no one, they are also calling the Tailwind Standalone executable.

https://github.com/THEGOLDENPRO/fastapi-tailwind/blob/4b5fca5ea34f541009c987ccb7ec3902d40546a8/fastapi_tailwind/tailwind.py#L51-L54

Not sure if we can use that library to make our work easier, but eventually I guess if we want JIT we'll use that executable one way or another.

@rodja
Copy link
Member

rodja commented May 30, 2025

Another idea I want to share: would it be possible to load all tailwind classes progressive? I mean:

  • ship no treeshaked tailwind css and no tailwind js
  • have the full tailwind css available on the server
  • in our outbox, monitor which classes are send out
  • augment the delivery with the necessary css
  • the browser then needs to dynamically also add the new css when processing the incoming messages

@evnchn
Copy link
Collaborator Author

evnchn commented May 30, 2025

Interesting idea, but there will be difficulty in these places:

  • "full tailwind css": There is no such thing, because Tailwind supports stuff like w-[377px], where what's in the bracket is unpredictable. Not to mention the selectors and stuff. This gets worse with Tailwind 4 since there are no more fixed classes at all, so this solution would break when we come around and upgrade Tailwind later.

Tailwind 3: Note the h-0, h-0.5, h-1 classes
{3EE91F0A-1B52-47B9-94BB-8E310E174632}

Tailwind 4: It's all h-<number> now
{6935A0B0-66B5-407C-81B6-8B78A1F57BC5}

  • "outbox monitor class and augment CSS": As mentioned, the JIT takes 300ms since we don't use --watch and keep the Tailwind executable running, rather we start-and-stop it per JIT. It would make the outbox unbearably slow (hence why my stance towards new classes in Outbox has been, so far, "you shouldn't do that and I'll just let the classes fail")

  • "augment the delivery with the necessary css": I haven't got around to taking a look at how Tailwind generate its CSS. It may or may not be possible to serve only the difference, and the lag would be awful if we send the whole thing over again and again.

@evnchn
Copy link
Collaborator Author

evnchn commented Jun 1, 2025

Now I'm not saying that JIT can definitely solve it, but:

{BD30CEA8-4981-475B-A272-74B499B30B7B}

Addressing #4822, note how every animation frame, Tailwind client-side JS does something with its MutationObserver

@falkoschindler falkoschindler added 🌳 advanced Difficulty: Requires deep knowledge of the topic analysis Status: Requires team/community input labels Jun 2, 2025
@falkoschindler
Copy link
Contributor

Ok, to summarize where we're at: We're looking for a way to convert HTML classes to CSS definitions, ideally

  1. incrementally without the need to pre-define anything,
  2. on the client to avoid additional CPU load on the server,
  3. without an extra binary the user needs to install,
  4. with a small (JS) size and CPU usage on the client.

This is basically what we're already doing - up to point 4. The current TailwindCSS is pretty large and costly.

So we're thinking: Maybe there are low-weight alternatives like Twind, UnoCSS, Windi CSS, or Master CSS? If we can live without some features (like plugin support), we could switch in 3.0 (or let the user choose between two Tailwind implementations) and we're done.

@evnchn
Copy link
Collaborator Author

evnchn commented Jun 2, 2025

Thanks for pitching the other alternatives. I have analyzed them, and tabulated as such.

Incrementally w/o pre-define On-client save server CPU No extra binary to install Small footprint on client
1. TailwindCSS JS YES YES YES NO (Mutation Observer)
2. TailwindCSS JIT MAYBE (unlikely) NO NO YES
3. Twind, etc… Need wrap tw YES YES YES
4. UnoCSS MAYBE NO NO + npm only YES
5. MasterCSS JS Need change syntax YES YES NO (Mutation Observer)
6. MasterCSS Hybrid YES (But need change syntax) NO NO + npm only PARTLY (Mutation Observer only after initial page load)

(WindiCSS is sunsetting, and is similar to UnoCSS, so I am not considering it)

JIT and AOT definition between TailwindCSS and MasterCSS

  • TailwindCSS’s definition of JIT: Run TailwindCSS’s exe for serving the CSS
  • MasterCSS’s definition of AOT: Run MasterCSS’s library for serving the CSS

So, TailwindCSS’s JIT = MasterCSS’s AOT, MasterCSS’s JIT is TailwindCSS’s Play CDN mode, and my PR referring to “Tailwind JIT engine” means the former (TailwindCSS's JIT, MasterCSS's AOT)

Reviewing the options

Choice 2: For TailwindCSS JIT (what this PR is about), incremental change is not a guarantee, and is likely not going to work, otherwise MasterCSS will not put its Hybrid Rendering (AOT + JIT) as its selling point. Even if it did work, 300ms is a lot (assuming cold start), and keeping the executable in memory may be tricky. I don’t think lack of support for incremental change and demanding pre-definition of all styles is a deal-breaker, but it’s a tough pill to swallow nonetheless.

Choice 3: Twind looks very interesting, since it gets rid of the Mutation Observer by having you wrap classes in tw function. In essense, there is no need for mutation observer, if you notify Twind of any mutations! Let me look into it!

Choice 4: UnoCSS is itself a server-side library. Not only that, unlike Tailwind CSS, it does not have a standalone executable, and whoever likes to use it must install via npm or something. This is a deal-breaker and we’re unlikely to choose it.

Choice 5: MasterCSS client-side JS will likely suffer from the same performance issue as TailwindCSS JS, since it also uses Mutation Observer to observe the DOM tree to look for new classes. Since it’s likely equally as slow and requires new syntax, we’re unlikely to choose it.

Choice 6: MasterCSS Hybrid Render looks interesting, but run-time wise, it’ll still be slow since it needs to use Mutation Observer. It is only faster for FCP since the initial load already has the CSS baked in from server, but like UnoCSS there is no standalone executable. In conclusion, we’re unlikely to choose it.

So far, this leaves Choice 3 Twind as a strong contender, Choice 2 Tailwind JIT engine if you can deal with pre-defining all your classes / try figure out incremental diffing.

See if you agree. Thanks!

@rodja
Copy link
Member

rodja commented Jun 2, 2025

UnoCSS can also run on the client: https://unocss.dev/integrations/runtime

@evnchn
Copy link
Collaborator Author

evnchn commented Jun 2, 2025

Ah, I see.

But it does it by "detect the DOM changes and generate the styles on the fly", and I'd bet Mutation Observer was used somewhere.

At the end of the day, we'd like to move away from Mutation Observer, because it's slow, as seen #4806 (comment) (initial page load) and #4806 (comment) (CSS animations and anything which frequently changes the DOM in some way that the Mutation Observer would pick up)

@falkoschindler
Copy link
Contributor

Unfortunately, Twind seems to be pretty stale (last commit 3 years ago). 😕

@evnchn
Copy link
Collaborator Author

evnchn commented Jun 2, 2025

@rodja I took another look at the UnoCSS runtime at https://unocss.dev/integrations/runtime

Even though it may not immediately make things super fast by dropping all client-side computation, it does grant us a lot more control as we can controls the preset:

  • We can potentially let users enjoy Tailwind V4-like functionality in NiceGUI 2.x, or stay on Tailwind V3-like functionality in NiceGUI 3.x,
  • By choosing a light-weight preset (the "mini" one, for example), we can make the performance faster for general users
  • For advance users, they can ship their own minimal preset, which makes it as fast as it can be, for a client-side solution.

I'll tend to prioritize investigating that, over Twind (stale) and Tailwind JIT engine (has its limitations)

@evnchn
Copy link
Collaborator Author

evnchn commented Jun 2, 2025

Not considering Twind since it is stale. Notably, 950 colors are missing. tw-in-js/twind#520 is sitting there for quite a while...

Let's wait for unocss/unocss#4700, since I think Tailwind V4 is absent from UnoCSS Runtime Preset, since I don't quite feel comfortable jumping on the UnoCSS train, when it doesn't have Tailwind V4 which we will upgrade to in NiceGUI 3.0 eventually.

@evnchn evnchn mentioned this pull request Jun 3, 2025
7 tasks
@evnchn
Copy link
Collaborator Author

evnchn commented Jun 3, 2025

UnoCSS's results are out in #4832

By now it is a draft PR, you can imagine it didn't go too well, then. Quite a lot of things break, and it is by no means a painless transition.

Let's discuss UnoCSS from that thread going forward!

@evnchn
Copy link
Collaborator Author

evnchn commented Jun 3, 2025

I know this sounds dumb at first, but hear me out.

NiceGUI uses TailwindCSS V3 since v1.0.0, as far as I can tell. Before then, it was using some sort of tailwind.css, so I'd assume it's the browser CSS build at Tailwind V2 (or possibly earlier) https://v2.tailwindcss.com/docs/installation#using-tailwind-via-cdn

f6194c0#diff-4e4cc16e68c714c6a8433c24c00c6bc837f4f7ed51259f6e4bff356931927ed0

Takeaway: For performance-focused crowds, they can probably get away with a performance uplift, if they turn off Tailwind via ui.run, and then include back the V2 CSS.

Besides limited features, I don't see much downsides. CSS isn't a programming language, so if it's old we won't get any security vulnerabilities or anything. If anything, browser support is even better.

@falkoschindler
Copy link
Contributor

Oh wow, that's an important insight! I wasn't aware (or I forgot) that we switched from a CSS file to JS in 1.0.

There's no way to make a sensible CSS-based CDN build for Tailwind CSS v3.0 so we had to do something different — we built a JavaScript library.

https://tailwindcss.com/blog/tailwindcss-v3?utm_source=chatgpt.com#play-cdn

I wonder if we could auto-generate our own tailwind.css with all well-known classes from tailwind.py? Then the user can choose between full or limited Tailwind support. Unfortunately the "limited" version would lack quite a lot of useful features.

@evnchn
Copy link
Collaborator Author

evnchn commented Jun 3, 2025

I just swapped out NiceGUI documentation's Tailwind from V3 (JS) to V2 (CSS)

Many stuff breaks. Namely

  • No more dark:, or any other prefix, really
  • No more text-[your_color_here], the defaults are what you got

And so, our tailwind.py, short of parsing the user's CSS and dynamically generating the CSS styles on-the-fly (much like the Tailwind JIT), will never be even close to feature-complete to Tailwind V3. As such, we should not bother, and we can let people who truly need that speed benefit use Tailwind V2 CSS.

I am in the process of seeing what happens when I convert my project to Tailwind V2 CSS. See how many syntax changes I need to make.

@evnchn
Copy link
Collaborator Author

evnchn commented Jun 3, 2025

Moving to Tailwind V2 CSS isn't that hard, if you already have a colors.py defining all the colors centrally.

{C58000E9-674C-464D-9318-4E49CB801FF4}

-> Some Tailwind but not too much

{622F47C2-A245-4082-B7A0-B0A995FB8519}

-> No Tailwind at all

@evnchn
Copy link
Collaborator Author

evnchn commented Jun 11, 2025

Would like to focus effort on UnoCSS (#4832) instead of this. I can't find UnoCSS on the performance timeline but I think it should be really fast compared to Tailwind client-side library already.

UnoCSS:

{4BD8815F-A08C-4401-A803-414459CCA50B}

TailwindCSS client-side library:

{80A4DFBF-A6C4-4363-8572-887A15BAE2A3}

@evnchn evnchn marked this pull request as draft June 11, 2025 07:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🌳 advanced Difficulty: Requires deep knowledge of the topic analysis Status: Requires team/community input feature Type/scope: New feature or enhancement 🟡 medium Priority: Relevant, but not essential
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants