BeaconCMS / beacon

Open-source content management system (CMS) built with Phoenix LiveView. Faster render times to boost SEO performance, even for the most content-heavy pages.
https://beaconcms.org
MIT License
1.02k stars 100 forks source link

BUG: Tailwind runtime.css potential problem? #135

Closed iceshatt3r closed 5 months ago

iceshatt3r commented 1 year ago

Hello Guys,

I am not sure if this is a bug, but it seems that when I try to recreate the code from the Flowbite website for a solid background navigation bar in my Phoenix and Tailwind CSS project, it is not working correctly for beacon Layout. Normal phx homepage works fine. It seems that Tailwind is not generating responsiveness or ordering the CSS properly.

What is happening?

I am copying the Tailwind code from the following website: https://flowbite.com/docs/components/navbar/#solid-background and trying to implement it as a navbar in my layout. However, the responsiveness or CSS order seems to be incorrect.

Example:

Resolution above 1400px width

alt text

Same code, same resolution but page generated by homepage.html.heex

alt text

iceshatt3r commented 1 year ago

Ok for future reference it's escaping issue in Code.compile_string https://github.com/BeaconCMS/beacon/blob/823cfa135acd47bb740f1f6c513f91616a0e5a7a/lib/beacon/loader/module_loader.ex#L6

How tailwind code should look like

@media (min-width: 768px) {
    .md\:text-red-400 {
       --tw-text-opacity: 1;
        color: rgb(248 113 113 / var(--tw-text-opacity));
      }
  }

"""

Example to reproduce

code_string = """
defmodule ExampleCss do
  def to_code do
    \"""
      @media (min-width: 768px) {
        .md\:text-red-400 {
          --tw-text-opacity: 1;
          color: rgb(248 113 113 / var(--tw-text-opacity));
        }
      }
    \"""
  end
end
"""

Code.compile_string(code_string)
ExampleCss.to_code 

Result

 @media (min-width: 768px) {\n    .md:text-red-400 {\n      --tw-text-opacity: 1;\n      color: rgb(248 113 113 / var(--tw-text-opacity));\n    }\n  }\n

Formated for preview:

  @media (min-width: 768px) {
    .md:text-red-400 {
      --tw-text-opacity: 1;
      color: rgb(248 113 113 / var(--tw-text-opacity));
    }
  }

.md:text-red-400 lacking the \

leandrocp commented 1 year ago

Hey @iceshatt3r I'm assuming that you're passing the pasted code into Stylesheets.create_stylesheet/1 or any other create function, which will remove the \ when it gets to the database because that's the backslash escape character, that's expected. In order to preserve it you should use \\ instead, ie: you have to escape the backslash escape character.

Of course that will lead to confusion and isn't a good behavior so I'm inclined to fix it by automatically replacing \ with \\ on all internal changesets, unless @TheFirstAvenger has any objection.

TheFirstAvenger commented 1 year ago

@leandrocp that makes sense to me, no objections

iceshatt3r commented 1 year ago

@leandrocp This code is generated by https://github.com/BeaconCMS/beacon/blob/main/lib/beacon/css_compiler.ex#L27 and I assume it goes here https://github.com/BeaconCMS/beacon/blob/main/lib/beacon/loader/layout_module_loader.ex#L46

Somewhere in between loosing single backslash since the output of md:\display is actually md:display in <style></style>

Its most likely somewhere in layout_module_loader.ex the tailwind output.css file looks ok.

leandrocp commented 1 year ago

This code is generated by https://github.com/BeaconCMS/beacon/blob/main/lib/beacon/css_compiler.ex#L27 and I assume it goes here https://github.com/BeaconCMS/beacon/blob/main/lib/beacon/loader/layout_module_loader.ex#L46

@iceshatt3r that's correct 👍🏻

Can you please explain how you're creating the pages? If you're following the normal procedure of running seeds (calling the create functions) then the issue here is that Beacon stores everything in database and it has to be stored properly in order to get it loaded correctly. In short the overall workflow is:

1) create layouts, pages, stylesheets by storing on DB

2) fetch all components from DB https://github.com/BeaconCMS/beacon/blob/aa3c454564c1163ce02064f8b7206f5edcde7ac2/lib/beacon/loader/db_loader.ex#L11

3) for each component it eventually calls Code.compile_string for the content/body https://github.com/BeaconCMS/beacon/blob/aa3c454564c1163ce02064f8b7206f5edcde7ac2/lib/beacon/loader/module_loader.ex#L6

When you call "create something" (pass 1) to save content on DB, if you pass md\:text it will be stored as md:text (that's expected), so when Beacon fetches from DB (pass 2) it gets returned as md:text and it's impossible to recover that old value, which means it has to be stored with the backslash character (\) escaped (store as \\ instead of \) and everything else should work as you expect, ie: on pass 2 and 3 it keeps md\:text.

Makes sense?

leandrocp commented 1 year ago

@iceshatt3r to give you a bit more context, the css compiler input comes from the db in the same workflow I've explained in my previous comment.

iceshatt3r commented 1 year ago

Thanks for clarifying :)

The tailwind code from screenshot is saved in Beacon.Layouts.Layout body, the page has only

inside.

I created layout with tailwind code inside first and after that created page and assigned layout_id to id, releaded pages in Admin panel.

Now going to sleep so if you will have more question I can reply tomorrow!

APB9785 commented 6 months ago

@leandrocp After a bit of investigation, I'm not so sure this is a case of replacing a single-backslash with a double-backslash, because there's really no such thing as a single-backslash (in a String). What we would need to do to solve the original case is replace \: (a single character, escaped colon) with two characters, \\: (escaped backslash, regular colon). But this would only solve a narrow piece of the overall issue, because the colon is arbitrary, it could be any other character which was escaped by the preceding backslash, and we'd have to account for all of them. HOWEVER, we might be intentionally escaping a character... So I don't think we can blindly replace all escaped characters. 🤔 I can only think of \n as an example, but unless we can minify the stylesheet, there will always be newlines.

Possible solutions could be:

leandrocp commented 5 months ago

As far as I can tell this issue is only relevant when creating your own stylesheet, which in Beacon is done by calling Beacon.Content.create_stylesheet/1

There's no need to escape : on pages and layouts, you can just write <span class="md:text-red-400"> but on a stylesheet the : must be escaped, ie: it should look like:

    @media (min-width: 768px) {
      .md\:text-red-400 {
        color: blue;
      }
    }

That's something we'll have to deal with on beacon_live_admin when we expose an admin page to create stylesheets, but for now you have to preserve the escape char yourself when passing a string to Beacon.Content.create_stylesheet/1 and that can be done using the ~S sigil, eg:

  Beacon.Content.create_stylesheet(%{
    site: :dev,
    name: "test",
    content: ~S"""
    @media (min-width: 768px) {
      .md\:text-red-400 {
        color: blue;
      }
    }
    """
  })