Skip to content

Find a better way to preview Tailwind #114

Open

Description

@davestewart

I was wondering if it was possible to open Tailwind code in Tailwind Playground

Whilst not possible to do directly, a simple Chrome extension could act as a bridge.

The steps would be as follows:

  • Figma plugin opens new window, either the Tailwind Playground URL directly (or maybe a chrome extension page)
  • the Tailwind HTML would be encoded in the URL
  • the extension could (I think?) run javascript in the main page using the MAIN execution context:
const url = new URL(location.href)
const html = url.searchParams.get('html')
if (html) {
  MonacoEditor.getModel().setValue(html)
}

Have done a quick experiment, and the code does set the Tailwind Playground code, would just have to confirm the extension could access the MonacoEditor global.

Activity

  1. bernaferrari commented on Jul 10, 2024

    @bernaferrari
    Owner

    Maybe tailwind playground is not doable, but codepen/codesandbox/replit/others would be doable?

  2. davestewart commented on Jul 13, 2024

    @davestewart
    ContributorAuthor

    I've been thinking through this a bit more, and actually it might just be better to render Tailwind directly in the responsive preview.

    See also:

  3. bernaferrari commented on Jul 13, 2024

    @bernaferrari
    Owner

    If you know how, I would accept.. But I don't know how.

  4. davestewart commented on Jul 13, 2024

    @davestewart
    ContributorAuthor

    I think it might be possible using the Play CDN:

    For arbitrary rules, a custom config can be created injected (see further down page).

    I don't know if it's possible to run inside the Figma environment, but might be an option.

    Have also seen some solutions where classes were generated, but it adds about 1.2MB to the size of the CSS. For arbitrary classes (size, color) could probably parse the markup and add styles dynamically.

    Another option might be to see how Tailwind Config Viewer does it:

    Happy to take a look.

  5. bernaferrari commented on Jul 13, 2024

    @bernaferrari
    Owner
  6. davestewart commented on Jul 15, 2024

    @davestewart
    ContributorAuthor

    I've had a few attempts to make this work and coming up short.

    Attempt 1: Use a precompiled Tailwind v2 source

    I grabbed the Tailwind 2 CSS from a CDN.

    This mainly works, but I know there's a few changes from v2 to v3, esp with colors.

    CleanShot 2024-07-15 at 14 07 30

    Attempt 2: Compile Tailwind using Post CSS

    I got Tailwind compiling with JIT via Post CSS in Node, but I can't get it to run in the Figma build env.

    CleanShot 2024-07-15 at 13 51 53

    Seems to be problems with built-ins like FS which I can't seem to get around, even though I've tried multiple polyfills etc.

    Going to pause effort on this for now.

    Attempt 3:

    Not tried this yet, but think could be an option to:

    • identify the finite classes needed, such as layout, default colours, etc
    • build this set of classes using some of the techniques above
    • also possibly leverage Tailwind's default config (see packages/backend/node_modules/tailwindcss/stubs/config.full.js)
    • create additional arbitrary classes (size, colors) on the fly

    I think for now – as I need to get back to work – I'm going to pause this, but think it's worth exploring. 

    If you fancy generating a base set of Tailwind class names (colors, sizes, borders, etc) that you think FigmaToCode can output, I can PR the script above, and we could at least use that as a build step to generate a TW config that we could load into the Plugin UI, as step 1.

    Might even be easier to do this with code, seeing as you have a lot of the sizes already mapped.

    LMK and I can PR the core code.

  7. changed the title [-]Open Tailwind code in Tailwind Playground[/-] [+]Find a better way to preview Tailwind[/+] on Jul 15, 2024
  8. bernaferrari commented on Jul 15, 2024

    @bernaferrari
    Owner
  9. davestewart commented on Jul 15, 2024

    @davestewart
    ContributorAuthor

    Check my updated comment above. I think we could successfully leverage Post CSS to build just the classes we're using in F2C.

  10. bernaferrari commented on Jul 15, 2024

    @bernaferrari
    Owner

    I liked approach 3 because if we make html output Tailwind colors somehow, then it is very easy. Only code would be potentially ugly.

  11. davestewart commented on Jul 15, 2024

    @davestewart
    ContributorAuthor

    OK. I propose the PR would look something like this:

    Build time:

    • I push a script with the PostCSS generation code
    • you work on the permutations for possible classes
    • final script outputs all these classes to a static file which is imported in the Plugin UI (we could even prefix these classes with something like .preview, so .preview .bg-red-500 { ... }

    Run time:

    • design regexp to grab arbitrary class names (text|bg)-\[(.+?)\] (perhaps you can advise on TW class names)
    • add functions to generate arbitrary classes (should be straightforward to copy TW code)
    • process HTML code to grab tokens
    • pass Tailwind code and additional Tailwind classes to Plugin UI from backend

    I'm happy to take the Post CSS stuff if you want to take more of the client code?

  12. davestewart commented on Jul 15, 2024

    @davestewart
    ContributorAuthor

    It looks like additional processing will also be needed for transparent colors too, as not only can /25 not be passed as a token to the compiler, but passing all those transparency variants would balloon the file size.

    I think we can get somewhere which will work, but indeed, Tailwind is tricky!

  13. davestewart commented on Jul 15, 2024

    @davestewart
    ContributorAuthor

    Some additional tweaks needed, but do seem to be getting somewhere:

    CleanShot 2024-07-15 at 18 01 42

  14. bernaferrari commented on Jul 15, 2024

    @bernaferrari
    Owner

    Yeah, I can help. Whatever you do, make it easy to update, because Tailwind 4 will come one day and it is going to be big.

    So, are we kind of reverse-engineering tailwind to output html? Does it add 1mb or not?

  15. davestewart commented on Jul 15, 2024

    @davestewart
    ContributorAuthor

    So, are we kind of reverse-engineering tailwind to output html

    Kind of:

    • pre-compiling 90% of the base classes
    • generating the remaining 10% (mechanism TBC)

    Suspect will need to generate from markup:

    • arbitrary values
    • arbitrary colors
    • transparent colors

    Have been tweaking classes in the devtools and it feels way better to be able to toggle TW classes than guess what they might be once they are copied and pasted elsewhere. Not perfect compared to an actual JIT compiler, but feels like it could certainly be better than the HTML stand-in.

    FWIW, final-generated classes are:

    /**
     * Add all combinations of class names
     *
     * Listed in order of Tailwind docs
     */
    function makeClasses() {
      // layout
      add("block inline-block inline flex inline-flex grid inline-grid hidden"); // https://tailwindcss.com/docs/display
      add("static fixed absolute relative"); // https://tailwindcss.com/docs/position
      add("top", config.layoutSize); // https://tailwindcss.com/docs/top-right-bottom-left
      add("right", config.layoutSize);
      add("bottom", config.layoutSize);
      add("left", config.layoutSize);
      add("start", config.layoutSize);
      add("end", config.layoutSize);
    
      // flexbox
      add("basis", config.layoutSize); // https://tailwindcss.com/docs/flex-basis
      add("flex", "row row-reverse col col-reverse"); // https://tailwindcss.com/docs/flex-direction
      add("flex", "wrap"); // https://tailwindcss.com/docs/flex-wrap
      add("grow grow-0"); //https://tailwindcss.com/docs/flex-grow
      add("shrink shrink-0"); //https://tailwindcss.com/docs/flex-shrink
      add("gap", config.layoutSize); // https://tailwindcss.com/docs/gap
      add("gap-x", config.layoutSize);
      add("gap-y", config.layoutSize);
    
      // flexbox align
      add("justify", "normal start end center between around evenly stretch"); // https://tailwindcss.com/docs/justify-content
      add("justify-items", "start end center stretch"); // https://tailwindcss.com/docs/justify-items
      add("justify-self", "auto start end center stretch"); // https://tailwindcss.com/docs/justify-self
      add("content", "normal center start end between around evenly baseline stretch"); // https://tailwindcss.com/docs/align-content
      add("items", "start end center baseline stretch"); // https://tailwindcss.com/docs/align-items
      add("self", "auto start end center stretch baseline"); // https://tailwindcss.com/docs/align-self
    
      // spacing
      add("p", config.layoutSize); // https://tailwindcss.com/docs/padding
      add("px", config.layoutSize);
      add("py", config.layoutSize);
      add("m", config.layoutSize); // https://tailwindcss.com/docs/margin
      add("mx", config.layoutSize);
      add("my", config.layoutSize);
      add("space-x", config.layoutSize); // https://tailwindcss.com/docs/space
      add("space-y", config.layoutSize);
    
      // sizing
      add("w", config.layoutSize); // https://tailwindcss.com/docs/width
      add("min-w", config.layoutSize); // https://tailwindcss.com/docs/min-width
      add("max-w", config.layoutSize); // https://tailwindcss.com/docs/max-width
      add("h", config.layoutSize); // https://tailwindcss.com/docs/height
      add("min-h", config.layoutSize); // https://tailwindcss.com/docs/min-height
      add("max-h", config.layoutSize); // https://tailwindcss.com/docs/max-height
      add("size", config.layoutSize); // https://tailwindcss.com/docs/size
    
      // typography
      add("text", config.fontSize); // https://tailwindcss.com/docs/font-size
      add("italic not-italic"); // https://tailwindcss.com/docs/font-style
      add("font", config.fontWeight); // https://tailwindcss.com/docs/font-weight
      add("tracking", config.letterSpacing); // https://tailwindcss.com/docs/letter-spacing
      add("leading", config.lineHeight); // https://tailwindcss.com/docs/line-height
      add("text", "left center right justify start end"); // https://tailwindcss.com/docs/text-align
      add("text", config.color); // https://tailwindcss.com/docs/text-color
      add("uppercase lowercase capitalize normal-case"); // https://tailwindcss.com/docs/text-transform
      add("text", "wrap nowrap balance pretty"); // https://tailwindcss.com/docs/text-wrap
      add("align", "top middle bottom"); // https://tailwindcss.com/docs/vertical-align
    
      // backgrounds
      add("bg", config.color); // https://tailwindcss.com/docs/background-color
    
      // borders
      add("rounded", config.borderRadius); // https://tailwindcss.com/docs/border-radius
      add("border", " 0 2 4 8"); // https://tailwindcss.com/docs/border-width
      add("border", config.color); // https://tailwindcss.com/docs/border-color
      add("border", "solid dashed dotted double hidden none"); // https://tailwindcss.com/docs/border-style
    
      // effects
      add("shadow", config.shadow); // https://tailwindcss.com/docs/box-shadow
      add("shadow", config.color); // https://tailwindcss.com/docs/box-shadow-color
      add("opacity", config.opacity); //https://tailwindcss.com/docs/opacity
    
      // filters
      add("blur", config.blur); // https://tailwindcss.com/docs/blur
    }

    This basically generates custom @apply classes which are then extracted and renamed as per the original TW classes and output as a CSS file:

    #preview .border-slate-950 {
      --tw-border-opacity: 1;
      border-color: rgb(2 6 23 / var(--tw-border-opacity));
    }
    
    #preview .border-gray-50 {
      --tw-border-opacity: 1;
      border-color: rgb(249 250 251 / var(--tw-border-opacity));
    }
    
    ...
    
    #preview .px-0\.5 {
      padding-left: 0.125rem;
      padding-right: 0.125rem;
    }
    
    #preview .px-1 {
      padding-left: 0.25rem;
      padding-right: 0.25rem;
    }
    
    #preview .px-1\.5 {
      padding-left: 0.375rem;
      padding-right: 0.375rem;
    }
    
    ...

    Does it add 1mb or not?

    178K at the mo, which is manageable.

    it is going to be big

    Big, in what way?

  16. davestewart commented on Jul 16, 2024

    @davestewart
    ContributorAuthor

    Morning update...

    Tailwind classes:

    • have added all supported TW classes; TW size is now 178K
    • could potentially output CSS as JSON, and add classes to preview on demand (vs adding the whole stylesheet)
    • could also potentially generate the current tailwindConfig.ts file using values (though, hardcoded is still fine)

    Preview generation:

    • am successfully sending Tailwind preview markup from backend
    • have arbitrary colors, opacities and sizes (w h p m px py mx my gap) working

    Here's how it's looking right now (outline just for debugging purposes):

    CleanShot 2024-07-16 at 13 13 04

    Next steps:

  17. bernaferrari commented on Jul 16, 2024

    @bernaferrari
    Owner
  18. davestewart commented on Jul 16, 2024

    @davestewart
    ContributorAuthor

    Probably for QA!

    Also, hoping to get all three over the line ASAP. How are you fixed for reviews in the next day or so?

    And, would be great to get the various updates published sooner rather than later so my team can use them.

  19. davestewart commented on Jul 17, 2024

    @davestewart
    ContributorAuthor

    Question regarding left and top values.

    In the tailwind builder, size values pass through the conversion tables / rounding function, but position values do not.

    Just checking that this is intentional?

    (I wonder if at some point in the future there might be more granular control over which properties get rounded).

  20. davestewart commented on Jul 19, 2024

    @davestewart
    ContributorAuthor

    OK, so FINALLY got all the flexbox layout code working, along with the new Tailwind renderer, and updated preview renderer:

    CleanShot 2024-07-19 at 19 37 55

    It's pretty cool; the new renderer now shows real Tailwind, with the rounding of sizes, colors, as well as user colors (variables).

    The reason why user colors are showing as stripes above, is because I'm working to add Figma's codeSyntax property, which will mean we won't have to fall back to deriving TW names names from variable names, the user will be able to specify them in the UI:

    image

    Overall... very nearly there!

  21. bernaferrari commented on Jul 19, 2024

    @bernaferrari
    Owner

    Amazing!

  22. bernaferrari commented on Jul 19, 2024

    @bernaferrari
    Owner

    In the tailwind builder, size values pass through the conversion tables / rounding function, but position values do not.

    I don't know lol I think it wasn't intentional, but maybe I found it easier that way, because you have top-4 left-[17px], quickly becomes weird, could be that.. I don't remember. Feel free to fix it!

    Edit: oh, I think I know. There wasn't top-[17px] before, so it was too much work to switch between JSX and Tailwind, I simplified.

  23. davestewart commented on Jul 19, 2024

    @davestewart
    ContributorAuthor

    With the initial work on variables done, I'll look to implement them for numbers soon, then the choice to use exact values or rounded values can be chosen per-property.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions