Phoenix 1.6.0 LiveView + esbuild + Tailwind JIT + AlpineJS - A brief tutorial

I couldn’t find any guides that worked well with Phoenix 1.6.0 and esbuild. I hope this helps people test the waters and eases you into the next-gen of Phoenix!

71 Likes

:pray:

3 Likes

How do we handle fonts using this build chain?
For example Inter | Fontsource - how do we add and use?

2 Likes

In your app.css you just import and use normally.

@import url("../vendor/fonts/Inter Web/inter.css");

html {
  font-family: "Inter var";
}

And then in your endpoint watcher config for esbuild in dev.exs you have to pass the loaders to the command.

  watchers: [
    # Start the esbuild watcher by calling Esbuild.install_and_run(:default, args)
    esbuild: {Esbuild, :install_and_run, [:default, ~w(--sourcemap=inline --watch --loader:.woff=file --loader:.woff2=file)]}
  ]

You could also use dataurl instead of file and esbuild will embed the fonts encoded as base64 (not recommended, my css file went to more than 6MB with dataurls).

Edit: Sorry, didn’t read the entire post, they remove the css processing from esbuild and handle it with postcss, in this case what I would do is ignore the esbuild base config of phoenix, install it manually and use the api to load the esbuild postcss plugin (esbuild plugins aren’t available through the cli), but in this case you will have to make a tradeoff between simplicity (no need for node, npm or anything else in phoenix 1.6) and power.

2 Likes

not quite as easy as @thomas.fortes suggests…

but close if you are OK with a seperate css file for the font (face) and thus using esbuild for it…

  • download the font Inter font family
  • copy the Inter Web folder into say /assets/vendor/fonts and rename the folder to InterWeb (need to avoid spaces!)
  • change the esbuild args in config.exs to:
    ~w(js/app.js vendor/fonts/InterWeb/inter.css --bundle --loader:.woff2=file --loader:.woff=file --target=es2016 --outdir=../priv/static/assets),
  • notice that the app.js is now outputted inside a js folder
    go to your layout and change the js script src to Routes.static_path(@conn, "/assets/js/app.js")
  • above your app.css stylesheet do a:
    <link phx-track-static rel="stylesheet" href="<%= Routes.static_path(@conn, "/assets/vendor/fonts/InterWeb/inter.css") %>"/>

for bonus points:
add https://github.com/semencov/tailwindcss-font-inter by doing:
npm install --save-dev tailwindcss-font-inter (in the assets folder)
and then add the plugin in the tailwind config:

  plugins: [require('tailwindcss-font-inter')({ 
    importFontFace: false,
  })]

now you can use the css class font-inter wherever you want…

5 Likes

will Tailwind be also default for Phoenix 1.6?

No, in this article you can see he is installing it from NPM:

npm install autoprefixer postcss postcss-import postcss-cli tailwindcss --save-dev

1 Like

woo nice! Quick question - how would I enable scss ?

1 Like

Going with dart-sass npm package is one alternative. But, if you only need sass - GitHub - CargoSense/dart_sass: An installer for sass is a very good alternative to consider.

1 Like

Great job @sergio! :clap:

Now that Phoenix 1.6.0-rc.0 is out, I tried to migrate my app and this other post also provided me some insights: https://www.mitchellhanberg.com/how-i-handle-static-assets-in-my-phoenix-apps.

I ended up with something like this:

package.json

"scripts": {
    "deploy": "npm-run-all --parallel deploy-styles deploy-files",
    "deploy-styles": "NODE_ENV=production tailwindcss -i css/app.css -o ../priv/static/assets/app.css --minify",
    "deploy-files": "cpx 'static/**/*' ../priv/static",
    "watch": "npm-run-all watch-styles watch-files",
    "watch-styles": "tailwindcss --input=css/app.css --output=../priv/static/assets/app.css --postcss --watch",
    "watch-files": "cpx 'static/**/*' ../priv/static --watch"
  }
[...]
"devDependencies": {
    "autoprefixer": "^10.2.6",
    "npm-run-all": "^4.1.5",
    "postcss-import": "^14.0.2",
    "tailwindcss": "^2.2.4"
  }

dev.exs

watchers: [
    esbuild: {Esbuild, :install_and_run, [:default, ~w(--sourcemap=inline --watch)]},
    npm: ["run", "watch", cd: Path.expand("../assets", __DIR__)]
  ]

PS.: Even though there’s too much configuration for my taste still, the recompilations are fast, really really fast now with esbuild.

Update: BTW: It seems you don’t need to have postcss installed at all if you are using just Tailwind for your styles. You can replace the deploy script with: NODE_ENV=production tailwindcss -i css/app.css -o ../priv/static/assets/app.css --minify

5 Likes

This guide was super helpful; thank you! I noticed that it doesn’t minify CSS. If you want that, it’s very easy to add if you’re already using PostCSS:

  1. Install CSSNano: npm --prefix assets install --save-dev cssnano
  2. Add it to your postcss.config.js:
module.exports = {
  plugins: {
    "postcss-import": {},
    tailwindcss: {},
    autoprefixer: {},
    cssnano: {
      preset: "default",
    },
  },
}

esbuild also supports Typescript out of the box; just make sure to read the caveats if you’re going to use it.

2 Likes

Just wanted to add a thank you for the guide—was very helpful.

1 Like

Just to offer an alternative solution how to add a font file, you can also include the font using the postcss-url plugin as follows. (cc @cvkmohan )

1. Place the font in assets/fonts

I only need the one variable font file in WOFF2 format so for me it’s at assets/fonts/Inter.var.woff2.

2. Add postcss-url dependency

npm install postcss-url --save-dev

3. Add the plugin to assets/postcss.config.js

Mine looks like this after adding it:

module.exports = {
  plugins: {
    'postcss-import': {},
    'postcss-url': { url: 'inline' },
    tailwindcss: {},
    autoprefixer: {},
  }
}

4. Add the @font-face definition to your app.css

@font-face {
  font-family: 'Inter';
  font-weight: 1 999;
  src:
    url('../fonts/Inter.var.woff2') format('woff2-variations');
}

The above configuration will inline the font into your CSS like this:

@font-face { font-family: "Inter"; src: url("data:font/woff2;base64,d09G[...]d6eMC") format("woff2-variations"); font-weight: 1 999; }

If you prefer to keep the font(s) in separate files, use copy instead of inline.

6 Likes

i have successfully made new phoenix 1.6.0 app with esbuild+tailwind+alpinejs everything fine even tailwind but tailwind-extension in vscode is not working the auto suggestion’s for tailwind are not showing up, i think it’s not working because of heex file extension.

2 Likes

Have you tried changing the extensions in your config? There’s a tailwindCSS.includeLanguages setting. If that doesn’t help you should probably open an issue on Tailwind’s extension repository.

1 Like

Thank you
I tried that setting but no luck it’s still not working,
I’ll create a GitHub issue in their repository

1 Like

If anyone has trouble getting this to work on Windows this did the trick for me.

watchers: [
    # Start the esbuild watcher by calling Esbuild.install_and_run(:default, args)
    esbuild: {Esbuild, :install_and_run, [:default, ~w(--sourcemap=inline --watch)]},
    node: [
      "node_modules/tailwindcss/lib/cli.js",
      "--input=css/app.css",
      "--output=../priv/static/assets/app.css",
      "--postcss",
      "--watch",
      cd: Path.expand("../assets", __DIR__)
    ]
  ]
2 Likes

You can also do cmd: ["/c", "npx", ... ] to run npx and other things that are .cmd files, which Erlang doesn’t seem to be able to open a port to.

2 Likes

Never considered that. Very helpful.

Are there any tricks/methods for the additional css files you could use as an addition to tailwindcss. Like for example adding @import "./custom-base-styles.css"; to the assets/css/app.css file. Would this still work and where do I need to drop these additional files. When trying via the assets/css/ location doesn’t work.