Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update tailwind to version 4.0.0 #107

Merged
merged 3 commits into from
Feb 3, 2025

Conversation

aptinio
Copy link
Contributor

@aptinio aptinio commented Jan 25, 2025

This PR is made on top of @leifmetcalf's #103. It bumps the versions now that Tailwind CSS v4.0 is out.

leifmetcalf and others added 2 commits January 25, 2025 08:18
Previously the README recommended running the tailwind executable
from the assets directory in the `mix tailwind` task. Since
tailwind 4.0.0 searches for source files in the directory it is run
from, we instead recommend running the executable from the parent
directory.
Also, bump old version used to test updates.
It might help if GitHub does any sort of caching of release assets.
@snewcomer
Copy link

@aptinio 🙏. Is there any breaking changes that need to be documented in this repo specifically in the README?

README.md Outdated Show resolved Hide resolved
@aptinio
Copy link
Contributor Author

aptinio commented Jan 26, 2025

@aptinio 🙏. Is there any breaking changes that need to be documented in this repo specifically in the README?

@snewcomer, moving to Tailwind v4's CSS configuration is a breaking change if an earlier Tailwind version is chosen by the user. Tailwind v4 does provide the @config directive for compatibility with Tailwind v3.x, but it doesnn't support all the JavaScript-based config options.

We could release a 0.3 version for Tailwind v4 support. What do you think?

@aptinio
Copy link
Contributor Author

aptinio commented Jan 27, 2025

I've tested this branch to work with phoenixframework/phoenix#5990 with the following changes to that PR:

  • bumped Tailwind version from 4.0.0-beta.3 to 4.0.0
  • removed @plugin "./tailwind_heroicons.js"; from app.css
  • installed watchman to get the Tailwind cli watcher to work

@SteffenDE
Copy link
Contributor

SteffenDE commented Jan 27, 2025

@aptinio interesting, I didn't need to install watchman (running on macOS). Which OS are you running on? And is this something that was also needed with v3?

{:phoenix, "~> 1.6"},
{:tailwind, "~> 0.1.8", runtime: Mix.env() == :dev}
{:phoenix, "~> 1.7"},
{:tailwind, "~> 0.2.4", runtime: Mix.env() == :dev}
Copy link
Member

Choose a reason for hiding this comment

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

Let's ship it as 0.3.0 once we have these changes in.

@stefanchrobot
Copy link

stefanchrobot commented Jan 27, 2025

@aptinio interesting, I didn't need to install watchman (running on macOS).

Same here. Everything just works on macOS (including the Heroicons plugin; EDIT: no, the plugin doesn't work).

@SteffenDE
Copy link
Contributor

Same here. Everything just works on macOS (including the Heroicons plugin).

@stefanchrobot Do you have a node_modules folder with tailwindcss directory inside? I tried the PR in Phoenix yesterday and I could reproduce the heroicons plugin not working out of the box.

@stefanchrobot
Copy link

Same here. Everything just works on macOS (including the Heroicons plugin).

@stefanchrobot Do you have a node_modules folder with tailwindcss directory inside? I tried the PR in Phoenix yesterday and I could reproduce the heroicons plugin not working out of the box.

Sorry, I'm actually experiencing the same thing. The plugin is not working. I had node_modules temporarily to run the v3 to v4 upgrade tool and was mislead to believe that the plugins work.

@aptinio
Copy link
Contributor Author

aptinio commented Jan 27, 2025

@aptinio interesting, I didn't need to install watchman (running on macOS). Which OS are you running on? And is this something that was also needed with v3?

I'm running on Linux. When I run mix phx.server without watchman installed, changes do not trigger a rebuild and I see this in the logs:

sh: line 1: watchman: command not found

And before I remove the heroicons plugin and run mix tailwind profile_name, I get:

** (Mix) `mix tailwind profile_name` exited with 1

Running:

./_build/tailwind-linux-x64 --input=assets/css/app.css --output=priv/static/assets/app.css

exits with 1 with no output.

There's a missing parenthesis in the heroicons plugin in phoenixframework/phoenix#5990, but fixing it doesn't help with the exit code 1.

@aptinio
Copy link
Contributor Author

aptinio commented Jan 27, 2025

I think both of these are upstream issues in the tailwind cli linux binary. I can file them later upstream.

@SteffenDE
Copy link
Contributor

@aptinio there's already an upstream issue here: tailwindlabs/tailwindcss#15235, but feel free to open one for the missing watchman!

@aptinio
Copy link
Contributor Author

aptinio commented Jan 29, 2025

Reported tailwindlabs/tailwindcss#15703.

Apparently, watching still works even if the following is included in the output:

sh: line 1: watchman: command not found

@Flo0807
Copy link

Flo0807 commented Feb 1, 2025

Apparently, watching still works even if the following is included in the output:
sh: line 1: watchman: command not found

I don't have this issue on macOS, but I am also experiencing the following error related to the tailwind_heroicons plugin:

** (Mix) `mix tailwind default` exited with 1` error.

Everything works fine if I remove the plugin.

I noticed that the root cause of the error might be the following line of the plugin:

const plugin = require('tailwindcss/plugin')

Unfortunately, you don't need this because you can just export a basic function (no need to wrap it into plugin). I don't know if this is something Tailwind changed with v4 (?)

So this seems to be a valid plugin in v4:

# plugin.js
module.exports = function({ addVariant }) {
  addVariant('inverted', '@media (inverted-colors: inverted)')
  addVariant('hocus', ['&:focus', '&:hover'])
}

Therefore, exporting a basic function in tailwind_heroicons.js seems to fix the issue. I can successfully execute mix tailwind default and the icons are displayed correctly on my pages.

Here is the full tailwind_heroicons.js file I used:

const fs = require("fs")
const path = require("path")

module.exports = function ({ matchComponents, theme }) {
  const iconsDir = path.join(__dirname, '../../deps/heroicons/optimized')
  const values = {}
  const icons = [
    ['', '/24/outline'],
    ['-solid', '/24/solid'],
    ['-mini', '/20/solid'],
    ['-micro', '/16/solid']
  ]
  icons.forEach(([suffix, dir]) => {
    fs.readdirSync(path.join(iconsDir, dir)).forEach(file => {
      const name = path.basename(file, '.svg') + suffix
      values[name] = { name, fullPath: path.join(iconsDir, dir, file) }
    })
  })
  matchComponents({
    hero: ({ name, fullPath }) => {
      const content = fs.readFileSync(fullPath).toString().replace(/\r?\n|\r/g, '')
      let size = theme('spacing.6')
      if (name.endsWith('-mini')) {
        size = theme('spacing.5')
      } else if (name.endsWith('-micro')) {
        size = theme('spacing.4')
      }
      return {
        [`--hero-${name}`]: `url('data:image/svg+xml;utf8,${content}')`,
        '-webkit-mask': `var(--hero-${name})`,
        mask: `var(--hero-${name})`,
        'mask-repeat': 'no-repeat',
        'background-color': 'currentColor',
        'vertical-align': 'middle',
        display: 'inline-block',
        width: size,
        height: size
      }
    }
  }, { values })
}

@stefanchrobot
Copy link

stefanchrobot commented Feb 2, 2025

It looks like this is a valid way to build plugins: https://github.com/tailwindlabs/tailwindcss/blob/main/packages/internal-example-plugin/index.js

A plugin seems to be just this:

export type PluginFn = (api: PluginAPI) => void

and the plugin helper does this:

function createPlugin(handler: PluginFn, config?: Partial<Config>): PluginWithConfig {
  return {
    handler,
    config,
  }
}

which is just:

export type PluginWithConfig = {
  handler: PluginFn
  config?: UserConfig

  /** @internal */
  reference?: boolean
}

But I think this is a workaround, so it still needs fixing upstream.

@stefanchrobot
Copy link

Related PR: tailwindlabs/tailwindcss#15934

@stefanchrobot
Copy link

stefanchrobot commented Feb 2, 2025

Found another issue: the prebuilt packages have changed between v3 and v4.

tailwindcss-linux-arm64
tailwindcss-linux-armv7
tailwindcss-linux-x64
tailwindcss-macos-arm64
tailwindcss-macos-x64
tailwindcss-windows-arm64.exe
tailwindcss-windows-x64.exe

tailwindcss-linux-arm64
tailwindcss-linux-arm64-musl
tailwindcss-linux-x64
tailwindcss-linux-x64-musl
tailwindcss-macos-arm64
tailwindcss-macos-x64
tailwindcss-windows-x64.exe

This requires updating this function. I think this would definitely warrant a version bump of :tailwind.

More importantly, though, we need to figure out a way to pick musl vs non-musl binaries. Things are currently failing for me when trying to build the app on Alpine in Docker. Not sure if there is a way to detect musl. Should we have another option in the config? Something along the lines of:

config :tailwind,
  version: "4.0.3",
  musl: true # only applies if target is "linux"; default is false

If someone was developing on non-musl and building on musl, they could probably control this with an ENV var.

@stefanchrobot
Copy link

More importantly, though, we need to figure out a way to pick musl vs non-musl binaries. Things are currently failing for me when trying to build the app on Alpine in Docker. Not sure if there is a way to detect musl. Should we have another option in the config? Something along the lines of:

config :tailwind,
  version: "4.0.3",
  musl: true # only applies if target is "linux"; default is false

If someone was developing on non-musl and building on musl, they could probably control this with an ENV var.

Maybe a better approach would be to provide target option in the config. This would allow to:

  • Override the inferred target to handle the abovementioned case,
  • Enable targets not supported by :tailwind without the need to wait for a PR/release.

@SteffenDE
Copy link
Contributor

@stefanchrobot we can automatically detect musl - I opened #109 which also adds a config option to override if necessary.

@SteffenDE
Copy link
Contributor

@Flo0807 @stefanchrobot I can confirm that the heroicons plugin works when we directly export the function. If this is officially supported by Tailwind, we could go forward with that change. Sadly, v4 doesn't seem to have any documentation on plugins, while v3 had https://v3.tailwindcss.com/docs/plugins, so I'm not sure :(

@stefanchrobot
Copy link

@stefanchrobot we can automatically detect musl - I opened #109 which also adds a config option to override if necessary.

This is great! I think it would be best to incorporate your PR into this one and also update the list of targets: the musl binaries are only available for v4; also v4 no longer provides targets like "linux-armv7".

@stefanchrobot
Copy link

@Flo0807 @stefanchrobot I can confirm that the heroicons plugin works when we directly export the function. If this is officially supported by Tailwind, we could go forward with that change. Sadly, v4 doesn't seem to have any documentation on plugins, while v3 had https://v3.tailwindcss.com/docs/plugins, so I'm not sure :(

I'd vote for moving forward with the change so that this gets unblocked. We could revert to using plugin() once that gets fixed upstream.

@SteffenDE
Copy link
Contributor

I think it would be best to incorporate your PR into this one and also update the list of targets: the musl binaries are only available for v4; also v4 no longer provides targets like "linux-armv7".

I think we can merge them separately as well, and then do a new release with all changes incorporated.

I'm not sure if we should remove the armv7 targets. You could use the new library version with Tailwind v3 as well. There's also the freebsd targets which aren't officially provided by Tailwind. If we do remove it, I think we should document which versions of Tailwind are expected to work with which version of the library.

I'd vote for moving forward with the change so that this gets unblocked. We could revert to using plugin() once that gets fixed upstream.

The heroicon plugin doesn't block the change here in the tailwind repo. It's only an issue for Phoenix 1.8 :)

@josevalim
Copy link
Member

This looks good to go my only concern is that, as soon as update the README, people will start relying on that. So we need to either postpone the README changes or hold merging this branch.

@SteffenDE
Copy link
Contributor

@josevalim so you're worried people will try to follow the Adding to Phoenix steps for apps generated with older Phoenix versions using Tailwind 3? As mentioned above, I don't feel like Phoenix 1.8 should block this. Maybe we can add a warning to check for the correct Tailwind version and link to the v0.2 README?

@josevalim
Copy link
Member

Compromise: let's add a "Updating to Tailwind v4" section to the README, explaining that main/v0.3+ is Tailwind v4 and with the steps to migrate?

@SteffenDE
Copy link
Contributor

I'll merge and then send a new PR targeting main!

@SteffenDE SteffenDE merged commit df61ebc into phoenixframework:main Feb 3, 2025
2 checks passed
@SteffenDE
Copy link
Contributor

Thank you everyone! 🙌🏻

@aptinio
Copy link
Contributor Author

aptinio commented Feb 3, 2025

@SteffenDE, @josevalim, for what it's worth, I think it'll be more future-proof to tell people that v0.3 only supports Tailwind v4 and link them to v0.2 docs for Tailwind < v4. I think it'll also keep the README neat and tidy.

@aptinio aptinio deleted the tailwind-v4 branch February 3, 2025 11:15
@stefanchrobot
Copy link

Thanks! Running latest main with Tailwind v4.0.3 on production.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants