diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 78413ae9..3822e414 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -20,7 +20,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- ruby: ["3.1", "3.2", "3.3", "head"]
+ ruby: ["3.1", "3.2", "3.3", "3.4", "head"]
steps:
- uses: actions/checkout@v4
- run: rm Gemfile.lock
@@ -32,7 +32,7 @@ jobs:
- name: Run tests
run: bin/test
- user-journey:
+ user-install:
strategy:
fail-fast: false
matrix:
@@ -42,7 +42,22 @@ jobs:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
- ruby-version: "3.2"
+ ruby-version: "3.4"
bundler: latest
- - run: test/integration/user_journey_test.sh
+ - run: test/integration/user_install_test.sh
+ shell: bash
+
+ user-upgrade:
+ strategy:
+ fail-fast: false
+ matrix:
+ plat: ["ubuntu", "macos"] # TODO: on windows the tailwind upgrader tests are failing
+ runs-on: ${{matrix.plat}}-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: ruby/setup-ruby@v1
+ with:
+ ruby-version: "3.4"
+ bundler: latest
+ - run: test/integration/user_upgrade_test.sh
shell: bash
diff --git a/.github/workflows/upstream.yml b/.github/workflows/upstream.yml
index 0ba5507a..e1b12dea 100644
--- a/.github/workflows/upstream.yml
+++ b/.github/workflows/upstream.yml
@@ -34,14 +34,14 @@ jobs:
- name: Run tests
run: bin/test
- user-journey:
- name: "user-journey (rails ${{ matrix.ref }})"
+ user-install:
+ name: "user-install (rails ${{ matrix.ref }})"
runs-on: ${{matrix.plat}}-latest
strategy:
fail-fast: false
matrix:
plat: ["ubuntu"]
- ref: ["7-2-stable", "v8.0.0.beta1", "main"]
+ ref: ["7-2-stable", "8-0-stable", "main"]
env:
RAILSOPTS: --git=https://github.com/rails/rails --ref=${{ matrix.ref }}
steps:
@@ -50,5 +50,24 @@ jobs:
with:
ruby-version: "3.3"
bundler: latest
- - run: test/integration/user_journey_test.sh
+ - run: test/integration/user_install_test.sh
+ shell: bash
+
+ user-upgrade:
+ name: "user-upgrade (rails ${{ matrix.ref }})"
+ runs-on: ${{matrix.plat}}-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ plat: ["ubuntu"]
+ ref: ["7-2-stable", "8-0-stable", "main"]
+ env:
+ RAILSOPTS: --git=https://github.com/rails/rails --ref=${{ matrix.ref }}
+ steps:
+ - uses: actions/checkout@v4
+ - uses: ruby/setup-ruby@v1
+ with:
+ ruby-version: "3.3"
+ bundler: latest
+ - run: test/integration/user_upgrade_test.sh
shell: bash
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 26d72880..9ad013b2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,84 @@
-## v3.0.0
+# `tailwindcss-rails` Changelog
+
+## v4.2.0 / 2025-03-02
+
+### Features
+
+* Improve the view templates to display better on mobile devices. #503 @patriciomacadden
+* Support for environment variable `TAILWINDCSS_DEBUG` to turn off CSS minification. #504 @r-sierra
+
+
+## v4.1.0 / 2025-02-19
+
+### View template improvements
+
+* Field outlines are no longer hidden, and the focus border is brighter. #489 @rubys
+* Boolean fields are improved (checkbox labels aligned, "Yes"/"No" instead of "true"/"false"). #454 @patriciomacadden
+* Attachment links are consistently spaced and styled. #460 @patriciomacadden
+* Index page links to Show, Edit, and Destroy for each resource. #460 @patriciomacadden @flavorjones
+* Turbo confirm prompt added to Destroy links. #498 @patriciomacadden
+
+
+## v4.0.0 / 2025-02-01
+
+### Upgrade to Tailwind CSS v4
+
+General changes:
+
+- The dependency on `tailwindcss-ruby` is set to `~> 4.0`.
+- The location of (optional) `postcss.config.js` has moved from the `config/` directory to the app root.
+- The input file `app/assets/tailwind/application.tailwind.css` has been renamed to `app/assets/tailwind/application.css`.
+- If Propshaft is being used, `app/assets/tailwind` will be excluded from its asset handling.
+- The Inter font is no longer packaged with the gem.
+- Some Tailwind class names in the generated ERB templates are updated for v4.
+- The README is updated to contain verbose instructions on upgrading.
+
+Changes to the `tailwindcss:install` task:
+
+- The `tailwindcss:install` task no longer installs `config/tailwind.config.js`, as v4 recommends placing Tailwind configuration in the CSS file.
+- The Inter font is no longer configured in the application layout.
+- The "tailwind" stylesheet link tag will only be added to the application layout if Propshaft isn't in use and therefore already handling `app/assets/build/tailwind.css`. Previously it was always injected, resulting in the tag being rendered twice if Propshaft was in use.
+
+New task `tailwindcss:upgrade` upgrades many apps cleanly:
+
+- Cleans up `config/tailwind.config.js` and references it from the CSS file as recommended for v4 upgrades.
+- Runs the upstream upgrader (note: requires `npx` to run the one-time upgrade, but highly recommended).
+- Removes configuration for the Inter font from the application layout.
+- If present, moves `config/postcss.config.js` to the root directory.
+- The "tailwind" stylesheet link tag will be removed if Propshaft is in use and already handling `app/assets/build/tailwind.css`.
+- The input file `app/assets/tailwind/application.tailwind.css` will be moved to `app/assets/tailwind/application.css`.
+
+Thanks to @EricGusmao, @patriciomacadden, @excid3, and @brunoprietog for their feedback, contributions, and advice on v4 support.
+
+### Other changes
+
+- The gem's Rails generators are now hidden in the `rails g --help` output. #483 @patriciomacadden
+
+## v3.3.1 / 2025-01-23
+
+* Pin the dependency on `tailwindcss-ruby` to `~> 3.0` to prevent users from upgrading Tailwind while still on v3 of this gem.
+
+ While it was useful during the Tailwind v4 beta period to allow users to float this dependency to try upgrading, we know (now that v4.0.0.rc1 of this gem is out) that not everything will work well if combining Tailwind v4 with `tailwindcss-rails` v3. Pinning this dependency should protect developers against unexpected issues.
+
+
+## v3.3.0 / 2025-01-19
+
+* Add support for using the puma plugin in a standalone puma process (outside of `rails server`). (#458) @flavorjones
+
+
+## v3.2.0 / 2025-01-10
+
+* Improve the scaffold views by making positions, padding, and sizes more consistent, add titles to all pages, add hover states and semantic colors to buttons and links, and change border and focus colors on fields with errors. (#452) @patriciomacadden
+
+
+## v3.1.0 / 2024-12-29
+
+### Notable changes
+
+The tailwindcss plugins "form", "typography", and "container-queries" have been dropped from the default generated `tailwind.config.js` file. If you'd like to use them, you can re-add them to your project by uncommenting the appropriate lines in your config file. (#446) @flavorjones
+
+
+## v3.0.0 / 2024-10-15
### Notable changes
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index a4040dfa..c2e8746f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -9,7 +9,7 @@ This doc is a brief introduction on modifying and maintaining this gem.
The unit tests are run with `bundle exec rake test`
-There is an additional integration test which runs in CI, `test/integration/user_journey_test.sh` which you may also want to run.
+There is an additional integration test which runs in CI, `test/integration/user_install_test.sh` which you may also want to run.
### Testing in a Rails app
diff --git a/Gemfile.lock b/Gemfile.lock
index 96696921..730f94fb 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,42 +1,42 @@
PATH
remote: .
specs:
- tailwindcss-rails (3.0.0)
+ tailwindcss-rails (4.1.0)
railties (>= 7.0.0)
- tailwindcss-ruby
+ tailwindcss-ruby (~> 4.0)
GEM
remote: https://rubygems.org/
specs:
- actionmailer (7.2.1)
- actionpack (= 7.2.1)
- actionview (= 7.2.1)
- activejob (= 7.2.1)
- activesupport (= 7.2.1)
+ actionmailer (8.0.1)
+ actionpack (= 8.0.1)
+ actionview (= 8.0.1)
+ activejob (= 8.0.1)
+ activesupport (= 8.0.1)
mail (>= 2.8.0)
rails-dom-testing (~> 2.2)
- actionpack (7.2.1)
- actionview (= 7.2.1)
- activesupport (= 7.2.1)
+ actionpack (8.0.1)
+ actionview (= 8.0.1)
+ activesupport (= 8.0.1)
nokogiri (>= 1.8.5)
- racc
- rack (>= 2.2.4, < 3.2)
+ rack (>= 2.2.4)
rack-session (>= 1.0.1)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6)
useragent (~> 0.16)
- actionview (7.2.1)
- activesupport (= 7.2.1)
+ actionview (8.0.1)
+ activesupport (= 8.0.1)
builder (~> 3.1)
erubi (~> 1.11)
rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6)
- activejob (7.2.1)
- activesupport (= 7.2.1)
+ activejob (8.0.1)
+ activesupport (= 8.0.1)
globalid (>= 0.3.6)
- activesupport (7.2.1)
+ activesupport (8.0.1)
base64
+ benchmark (>= 0.3)
bigdecimal
concurrent-ruby (~> 1.0, >= 1.3.1)
connection_pool (>= 2.2.5)
@@ -46,28 +46,31 @@ GEM
minitest (>= 5.1)
securerandom (>= 0.3)
tzinfo (~> 2.0, >= 2.0.5)
+ uri (>= 0.13.1)
base64 (0.2.0)
- bigdecimal (3.1.8)
+ benchmark (0.4.0)
+ bigdecimal (3.1.9)
builder (3.3.0)
- concurrent-ruby (1.3.4)
- connection_pool (2.4.1)
+ concurrent-ruby (1.3.5)
+ connection_pool (2.5.0)
crass (1.0.6)
- date (3.3.4)
- debug (1.9.2)
+ date (3.4.1)
+ debug (1.10.0)
irb (~> 1.10)
reline (>= 0.3.8)
drb (2.2.1)
- erubi (1.13.0)
+ erubi (1.13.1)
globalid (1.2.1)
activesupport (>= 6.1)
- i18n (1.14.6)
+ i18n (1.14.7)
concurrent-ruby (~> 1.0)
- io-console (0.7.2)
- irb (1.14.1)
+ io-console (0.8.0)
+ irb (1.15.1)
+ pp (>= 0.6.0)
rdoc (>= 4.0.0)
reline (>= 0.4.2)
- logger (1.6.1)
- loofah (2.22.0)
+ logger (1.6.6)
+ loofah (2.24.0)
crass (~> 1.0.2)
nokogiri (>= 1.12.0)
mail (2.8.1)
@@ -76,74 +79,79 @@ GEM
net-pop
net-smtp
mini_mime (1.1.5)
- minitest (5.25.1)
- net-imap (0.4.16)
+ mini_portile2 (2.8.8)
+ minitest (5.25.4)
+ net-imap (0.5.6)
date
net-protocol
net-pop (0.1.2)
net-protocol
net-protocol (0.2.2)
timeout
- net-smtp (0.5.0)
+ net-smtp (0.5.1)
net-protocol
- nokogiri (1.16.7-aarch64-linux)
+ nokogiri (1.18.3)
+ mini_portile2 (~> 2.8.2)
racc (~> 1.4)
- nokogiri (1.16.7-arm-linux)
+ nokogiri (1.18.3-aarch64-linux-gnu)
racc (~> 1.4)
- nokogiri (1.16.7-arm64-darwin)
+ nokogiri (1.18.3-arm-linux-gnu)
racc (~> 1.4)
- nokogiri (1.16.7-x86-linux)
+ nokogiri (1.18.3-arm64-darwin)
racc (~> 1.4)
- nokogiri (1.16.7-x86_64-darwin)
+ nokogiri (1.18.3-x86_64-darwin)
racc (~> 1.4)
- nokogiri (1.16.7-x86_64-linux)
+ nokogiri (1.18.3-x86_64-linux-gnu)
racc (~> 1.4)
- psych (5.1.2)
+ pp (0.6.2)
+ prettyprint
+ prettyprint (0.2.0)
+ psych (5.2.3)
+ date
stringio
racc (1.8.1)
- rack (3.1.7)
- rack-session (2.0.0)
+ rack (3.1.10)
+ rack-session (2.1.0)
+ base64 (>= 0.1.0)
rack (>= 3.0.0)
- rack-test (2.1.0)
+ rack-test (2.2.0)
rack (>= 1.3)
- rackup (2.1.0)
+ rackup (2.2.1)
rack (>= 3)
- webrick (~> 1.8)
rails-dom-testing (2.2.0)
activesupport (>= 5.0.0)
minitest
nokogiri (>= 1.6)
- rails-html-sanitizer (1.6.0)
+ rails-html-sanitizer (1.6.2)
loofah (~> 2.21)
- nokogiri (~> 1.14)
- railties (7.2.1)
- actionpack (= 7.2.1)
- activesupport (= 7.2.1)
+ nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
+ railties (8.0.1)
+ actionpack (= 8.0.1)
+ activesupport (= 8.0.1)
irb (~> 1.13)
rackup (>= 1.0.0)
rake (>= 12.2)
thor (~> 1.0, >= 1.2.2)
zeitwerk (~> 2.6)
rake (13.2.1)
- rdoc (6.7.0)
+ rdoc (6.12.0)
psych (>= 4.0.0)
- reline (0.5.10)
+ reline (0.6.0)
io-console (~> 0.5)
- securerandom (0.3.1)
- stringio (3.1.1)
- tailwindcss-ruby (3.4.13)
- tailwindcss-ruby (3.4.13-aarch64-linux)
- tailwindcss-ruby (3.4.13-arm-linux)
- tailwindcss-ruby (3.4.13-arm64-darwin)
- tailwindcss-ruby (3.4.13-x86_64-darwin)
- tailwindcss-ruby (3.4.13-x86_64-linux)
+ securerandom (0.4.1)
+ stringio (3.1.3)
+ tailwindcss-ruby (4.0.7)
+ tailwindcss-ruby (4.0.7-aarch64-linux-gnu)
+ tailwindcss-ruby (4.0.7-arm64-darwin)
+ tailwindcss-ruby (4.0.7-x86_64-darwin)
+ tailwindcss-ruby (4.0.7-x86_64-linux-gnu)
thor (1.3.2)
- timeout (0.4.1)
+ timeout (0.4.3)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
- useragent (0.16.10)
- webrick (1.8.2)
- zeitwerk (2.6.18)
+ uri (1.0.2)
+ useragent (0.16.11)
+ zeitwerk (2.7.2)
PLATFORMS
aarch64-linux
@@ -159,4 +167,4 @@ DEPENDENCIES
tailwindcss-rails!
BUNDLED WITH
- 2.5.20
+ 2.5.23
diff --git a/README.md b/README.md
index 2887ab3f..f7afe7a8 100644
--- a/README.md
+++ b/README.md
@@ -9,12 +9,18 @@
- [Installation](#installation)
* [Choosing a specific version of `tailwindcss`](#choosing-a-specific-version-of-tailwindcss)
* [Using a local installation of `tailwindcss`](#using-a-local-installation-of-tailwindcss)
+- [Upgrading your application from Tailwind v3 to v4](#upgrading-your-application-from-tailwind-v3-to-v4)
+ * [You don't _have_ to upgrade](#you-dont-_have_-to-upgrade)
+ * [Upgrade steps](#upgrade-steps)
+ * [Troubleshooting a v4 upgrade](#troubleshooting-a-v4-upgrade)
+ * [Updating CSS class names for v4](#updating-css-class-names-for-v4)
- [Developing with Tailwindcss](#developing-with-tailwindcss)
* [Configuration and commands](#configuration-and-commands)
* [Building for production](#building-for-production)
* [Building for testing](#building-for-testing)
* [Building unminified assets](#building-unminified-assets)
* [Live rebuild](#live-rebuild)
+ * [Using Tailwind plugins](#using-tailwind-plugins)
* [Using with PostCSS](#using-with-postcss)
* [Custom inputs or outputs](#custom-inputs-or-outputs)
- [Troubleshooting](#troubleshooting)
@@ -24,24 +30,23 @@
* [Class names must be spelled out](#class-names-must-be-spelled-out)
* [`ERROR: Cannot find the tailwindcss executable` for supported platform](#error-cannot-find-the-tailwindcss-executable-for-supported-platform)
* [Using asset-pipeline assets](#using-asset-pipeline-assets)
- * [Conflict with pre-existing asset pipeline stylesheets](#conflict-with-pre-existing-asset-pipeline-stylesheets)
- [License](#license)
## Installation
-With Rails 7 you can generate a new application preconfigured with Tailwind by using `--css tailwind`. If you're adding Tailwind later, you need to:
+With Rails 7 you can generate a new application preconfigured with Tailwind CSS by using `--css tailwind`. If you're adding Tailwind later, you need to:
1. Run `./bin/bundle add tailwindcss-rails`
2. Run `./bin/rails tailwindcss:install`
-This gem depends on the `tailwindcss-ruby` gem to install a working tailwind executable.
+This gem depends on the `tailwindcss-ruby` gem to install a working Tailwind CLI executable.
### Choosing a specific version of `tailwindcss`
-The `tailwindcss-ruby` gem is declared as a floating dependency of this gem, so by default you will get the most recent stable version. However, you can select a specific version of tailwind by pinning that gem to the analogous version in your application's `Gemfile`. For example,
+The `tailwindcss-ruby` gem is declared as a floating dependency of this gem, so by default you will get the most recent stable version. However, you can select a specific version of Tailwind CSS by pinning that gem to the analogous version in your application's `Gemfile`. For example,
``` ruby
gem "tailwindcss-rails"
@@ -55,21 +60,201 @@ gem "tailwindcss-ruby", "3.4.13"
You can also use a local (npm-based) installation if you prefer, please go to https://github.com/flavorjones/tailwindcss-ruby for more information.
+## Upgrading your application from Tailwind v3 to v4
+
+v4.x of this gem has been updated to work with Tailwind v4, including providing some help with upgrading your application.
+
+A full explanation of a Tailwind CSS v4 upgrade is out of scope for this README, so we **strongly urge** you to read the [official Tailwind CSS v4 upgrade guide](https://tailwindcss.com/docs/upgrade-guide) before embarking on an upgrade to an existing large app.
+
+This gem will help with some of the mechanics of the upgrade:
+
+- update some generated files to handle breaking changes in v4 of this gem,
+- update some local project files to meet some Tailwind CSS v4 conventions,
+- attempt to run the [upstream v4 upgrade tool](https://tailwindcss.com/docs/upgrade-guide#using-the-upgrade-tool).
+
+
+### You don't _have_ to upgrade
+
+Keep in mind that you don't _need_ to upgrade. You can stay on Tailwind CSS v3 for the foreseeable future if you prefer not to migrate now, or if your migration runs into problems.
+
+If you don't want to upgrade, then pin your application to v3.3.1 of this gem:
+
+``` ruby
+# Gemfile
+gem "tailwindcss-rails", "~> 3.3.1" # which transitively pins tailwindcss-ruby to v3
+```
+
+If you're on an earlier version of this gem, `<= 3.3.0`, then make sure you're pinning the version of **both** `tailwindcss-rails` and `tailwindcss-ruby`:
+
+``` ruby
+# Gemfile
+gem "tailwindcss-rails", "~> 3.3"
+gem "tailwindcss-ruby", "~> 3.4" # only necessary with tailwindcss-rails <= 3.3.0
+```
+
+
+### Upgrade steps
+
+> [!WARNING]
+> In applications using Tailwind plugins without JavaScript tooling, these upgrade steps may fail to fully migrate `tailwind.config.js` because the upstream upgrade tool needs the Tailwind plugins to be installed and available through a JavaScript package manager. If you see errors from the upstream upgrade tool, you should try following the additional steps in [Updating CSS class names for v4](#updating-css-class-names-for-v4) which will help you install (temporarily!) the necessary packages and clean up afterwards.
+
+First, update to `tailwindcss-rails` v4.0.0 or higher. This will also ensure you're transitively depending on `tailwindcss-ruby` v4.
+
+```html
+# Gemfile
+gem "tailwindcss-rails", "~> 4.0" # which transitively pins tailwindcss-ruby to v4
+```
+
+**Update** path references to any existing css files imported in `app/assets/stylesheets/application.tailwind.css` so that they will resolve when the file is moved to `app/assets/tailwind/application.css`.
+
+```diff
+-@import "pagy.css";
++@import "../stylesheets/pagy.css";
+```
+
+If you want to migrate CSS class names for v4 (this is an optional step!), jump to [Updating CSS class names for v4](#updating-css-class-names-for-v4) before continuing.
+
+Then, run `bin/rails tailwindcss:upgrade`. Among other things, this will try to run the official Tailwind upgrade utility. It requires `npx` in order to run, but it's a one-time operation and is *highly recommended* for a successful upgrade.
+
+
+Here's a detailed list of what the upgrade task does.
+
+- Cleans up some things in the generated `config/tailwind.config.js`.
+- If present, moves `config/postcss.config.js` to the root directory.
+- If present, moves `app/assets/stylesheets/application.tailwind.css` to `app/assets/tailwind/application.css`.
+- Removes unnecessary `stylesheet_link_tag "tailwindcss"` tags from the application layout.
+- Removes references to the Inter font from the application layout.
+- Runs the upstream upgrader (note: requires `npx` to run the one-time upgrade, but highly recommended).
+
+
+
+
+Here's what that upgrade looks like on a vanilla Rails app.
+
+``` sh
+$ bin/rails tailwindcss:upgrade
+ apply /path/to/tailwindcss-rails/lib/install/upgrade_tailwindcss.rb
+ Removing references to 'defaultTheme' from /home/user/myapp/config/tailwind.config.js
+ gsub config/tailwind.config.js
+ Strip Inter font CSS from application layout
+ gsub app/views/layouts/application.html.erb
+ Remove unnecessary stylesheet_link_tag from application layout
+ gsub app/views/layouts/application.html.erb
+ Moving /home/user/myapp/app/assets/stylesheets/application.tailwind.css to /home/user/myapp/app/assets/tailwind/application.css
+ create app/assets/tailwind/application.css
+ remove app/assets/stylesheets/application.tailwind.css
+10.9.0
+ Running the upstream Tailwind CSS upgrader
+ run npx @tailwindcss/upgrade@next --force --config /home/user/myapp/config/tailwind.config.js from "."
+≈ tailwindcss v4.0.0
+│ Searching for CSS files in the current directory and its subdirectories…
+│ ↳ Linked `./config/tailwind.config.js` to `./app/assets/tailwind/application.css`
+│ Migrating JavaScript configuration files…
+│ ↳ The configuration file at `./config/tailwind.config.js` could not be automatically migrated to the new CSS
+│ configuration format, so your CSS has been updated to load your existing configuration file.
+│ Migrating templates…
+│ ↳ Migrated templates for configuration file: `./config/tailwind.config.js`
+│ Migrating stylesheets…
+│ ↳ Migrated stylesheet: `./app/assets/tailwind/application.css`
+│ ↳ No PostCSS config found, skipping migration.
+│ Updating dependencies…
+│ Could not detect a package manager. Please manually update `tailwindcss` to v4.
+│ Verify the changes and commit them to your repository.
+ Compile initial Tailwind build
+ run rails tailwindcss:build from "."
+≈ tailwindcss v4.0.0
+Done in 56ms
+ run bundle install --quiet
+```
+
+
+
+If this doesn't succeed, it's likely that you've customized your Tailwind configuration and you'll need to do some work to make sure your application upgrades. Please read the [official upgrade guide](https://tailwindcss.com/docs/upgrade-guide) and try following the additional steps in [Updating CSS class names for v4](#updating-css-class-names-for-v4).
+
+
+### Troubleshooting a v4 upgrade
+
+You may want to check out [TailwindCSS v4 - upgrade experience report · rails/tailwindcss-rails · Discussion #450](https://github.com/rails/tailwindcss-rails/discussions/450) if you're having trouble upgrading.
+
+We know there are some cases we haven't addressed with the upgrade task:
+
+- In applications using Tailwind plugins without JavaScript tooling, these upgrade steps may fail to fully migrate `tailwind.config.js` because the upstream upgrade tool needs the Tailwind plugins to be installed and available through a JavaScript package manager. If you see errors from the upstream upgrade tool, you should try following the additional steps in [Updating CSS class names for v4](#updating-css-class-names-for-v4) which will help you install (temporarily!) the necessary packages and clean up afterwards.
+
+We'll try to improve the upgrade process over time, but for now you may need to do some manual work to upgrade.
+
+
+### Updating CSS class names for v4
+
+> [!NOTE]
+> If you'd like to help automate these steps, please drop a note to the maintainers in [this discussion thread](https://github.com/rails/tailwindcss-rails/discussions/450).
+
+With some additional manual work the upstream upgrade tool will update your application's CSS class names to v4 conventions. **This is an optional step that requires a JavaScript toolchain.**
+
+**Add** the following line to the `.gitignore` file, to prevent the upstream upgrade tool from accessing node_modules files.
+
+```gitignore
+/node_modules
+```
+
+**Create** or **update** a `package.json` in the root of the project:
+
+```jsonc
+{
+ "name": "app_name",
+ "version": "1.0.0",
+ "dependencies": {
+ "tailwindcss": "^3.4.17", // Mandatory!!
+ // Install all plugins and modules that are referenced in tailwind.config.js
+ "@tailwindcss/aspect-ratio": "^0.4.2",
+ "@tailwindcss/container-queries": "^0.1.1",
+ "@tailwindcss/forms": "^0.5.10",
+ "@tailwindcss/typography": "^0.5.16"
+ // And so on...
+ }
+}
+```
+
+**Run** `npm install` (or `yarn install` if using `yarn`)
+
+**Update** `config/tailwind.config.js` and temporarily change the `content` part to have an additional `.` on all paths so they are relative to the config file:
+
+```js
+ content: [
+ '../public/*.html',
+ '../app/helpers/**/*.rb',
+ '../app/javascript/**/*.js',
+ '../app/views/**/*.{erb,haml,html,slim}'
+ ],
+```
+
+(Just add an additional `.` to all the paths referenced)
+
+**Run** the upstream upgrader as instructed above.
+
+Then, once you've run that successfully, clean up:
+
+- **Review** `package.json` to remove unnecessary modules.
+ - This includes modules added for the period of upgrade.
+ - If you don't need any modules besides `tailwindcss` itself, **delete** `package.json`, `node_modules/` and `package-lock.json` (or `yarn.lock`), plus remove `/node_modules` from `.gitignore`.
+- **Go** to your CSS file and remove the following line (if present):
+ ```css
+ @plugin '@tailwindcss/container-queries';
+ ```
+- **Revert** the changes to `config/tailwind.config.js` so that paths are once again relative to the application root.
+
## Developing with Tailwindcss
### Configuration and commands
-#### Configuration file: `config/tailwind.config.js`
+#### Input file: `app/assets/tailwind/application.css`
-You can customize the Tailwind build through the `config/tailwind.config.js` file, just like you would if Tailwind was running in a traditional node installation. All the first-party plugins are supported.
+The `tailwindcss:install` task will generate a Tailwind input file in `app/assets/tailwind/application.css`. This is where you import the plugins you want to use and where you can setup your custom `@apply` rules.
-#### Input file: `app/assets/stylesheets/application.tailwind.css`
-
-The installer will generate a Tailwind input file in `app/assets/stylesheets/application.tailwind.css`. This is where you import the plugins you want to use, and where you can setup your custom `@apply` rules.
+⚠ The location of this file changed in v4, from `app/assets/stylesheets/application.tailwind.css` to `app/assets/tailwind/application.css`. The `tailwindcss:upgrade` task will move it for you.
#### Output file: `app/assets/builds/tailwind.css`
-When you run `rails tailwindcss:build`, the input file will be used to generate the output in `app/assets/builds/tailwind.css`. That's the output CSS that you'll include in your app (the installer automatically configures this, alongside the Inter font as well).
+When you run `rails tailwindcss:build`, the input file will be used to generate the output in `app/assets/builds/tailwind.css`. That's the output CSS that you'll include in your app.
#### Commands
@@ -91,7 +276,6 @@ This gem also makes available a Puma plugin to manage a live rebuild process whe
This gem also generates a `Procfile.dev` file which will run both the rails server and a live rebuild process (see "Live Rebuild" section below).
-
### Building for production
The `tailwindcss:build` is automatically attached to `assets:precompile`, so before the asset pipeline digests the files, the Tailwind output will be generated.
@@ -104,8 +288,12 @@ The `tailwindcss:build` task is automatically attached to the `test:prepare` Rak
### Building unminified assets
-If you want unminified assets, you can pass a `debug` argument to the rake task, i.e. `rails tailwindcss:build[debug]` or `rails tailwindcss:watch[debug]`.
+If you want unminified assets, you can:
+- pass a `debug` argument to the rake task, i.e. `rails tailwindcss:build[debug]` or `rails tailwindcss:watch[debug]`.
+- set an environment variable named `TAILWINDCSS_DEBUG` with a non-blank value
+
+If both values are set, the environment variable will take precedence over the rake task argument.
### Live rebuild
@@ -124,7 +312,7 @@ This gem ships with a Puma plugin. To use it, add this line to your `puma.rb` co
plugin :tailwindcss if ENV.fetch("RAILS_ENV", "development") == "development"
```
-and then running `rails server` will run the Tailwind watch process in the background
+and then running `rails server` (or just `puma`) will run the Tailwind watch process in the background.
#### Run `rails tailwindcss:watch`
@@ -149,24 +337,65 @@ If you are running `rails tailwindcss:watch` in a docker container without a tty
Running `bin/dev` invokes Foreman to start both the Tailwind watch process and the rails server in development mode based on your `Procfile.dev` file.
+### Using Tailwind plugins
+
+If you want to use Tailwind plugins, they can be installed using `package.json`.
+
+Using Yarn:
+
+``` sh
+[ ! -f package.json ] && yarn init
+yarn add daisyui # example
+```
+
+Using npm:
+
+``` sh
+npm init
+npm add daisyui # example
+```
+
+Than use `@plugin` annotation in `app/assets/tailwind/application.css`:
+
+``` css
+@import "tailwindcss";
+@plugin "daisyui";
+```
+
+
### Using with PostCSS
-If you want to use PostCSS as a preprocessor, create a custom `config/postcss.config.js` and it will be loaded automatically.
+If you want to use PostCSS as a preprocessor, create a custom `postcss.config.js` in your project root directory, and that file will be loaded by Tailwind automatically.
For example, to enable nesting:
```js
-// config/postcss.config.js
-module.exports = {
+// postcss.config.js
+export default {
plugins: {
- 'postcss-import': {},
- 'tailwindcss/nesting': {},
- tailwindcss: {},
- autoprefixer: {},
- },
+ "@tailwindcss/postcss": {},
+ }
+}
+```
+
+⚠ Note that PostCSS is a JavaScript tool with its own prerequisites! By default `tailwindcss-rails` does not require any JavaScript tooling, so in order to use PostCSS, a `package.json` with dependencies for your plugins and a package manager like `yarn` or `npm` is required, for example:
+
+```json
+// package.json
+{
+ "name": "my app",
+ "private": true,
+ "dependencies": {
+ "@tailwindcss/postcss": "^4.0.0",
+ "tailwindcss": "^4.0.0",
+ "postcss": "^8.5.1"
+ }
}
```
+Then you can use yarn or npm to install the dependencies.
+
+
### Custom inputs or outputs
If you need to use a custom input or output file, you can run `bundle exec tailwindcss` to access the platform-specific executable, and give it your own build options.
@@ -200,7 +429,7 @@ See https://github.com/flavorjones/tailwindcss-ruby for help.
### Using asset-pipeline assets
-In Rails, you want to use [assets from the asset pipeline to get fingerprinting](https://guides.rubyonrails.org/asset_pipeline.html#what-is-fingerprinting-and-why-should-i-care-questionmark). However, Tailwind isn't aware of those assets.
+In Rails, you want to use [assets from the asset pipeline to get fingerprinting](https://guides.rubyonrails.org/asset_pipeline.html#fingerprinting-versioning-with-digest-based-urls). However, Tailwind isn't aware of those assets.
To use assets from the pipeline, use `url(image.svg)`. [Since Sprockets v3.3.0](https://github.com/rails/sprockets-rails/pull/476) `url(image.svg)` is rewritten to `/path/to/assets/image-7801e7538c6f1cc57aa75a5876ab0cac.svg` so output CSS will have the correct path to those assets.
@@ -222,18 +451,6 @@ The inline version also works:
Has the image as it's background
```
-### Conflict with pre-existing asset pipeline stylesheets
-
-If you get a warning `Unrecognized at-rule or error parsing at-rule ‘@tailwind’.` in the browser console after installation, you are incorrectly double-processing `application.tailwind.css`. This is a misconfiguration, even though the styles will be fully effective in many cases.
-
-The file `application.tailwind.css` is installed when running `rails tailwindcss:install` and is placed alongside the common `application.css` in `app/assets/stylesheets`. Because the `application.css` in a newly generated Rails app includes a `require_tree .` directive, the asset pipeline incorrectly processes `application.tailwind.css`, where it should be taken care of by `tailwindcss`. The asset pipeline ignores TailwindCSS's at-directives, and the browser can't process them.
-
-To fix the warning, you can either remove the `application.css`, if you don't plan to use the asset pipeline for stylesheets, and instead rely on TailwindCSS completely for styles. This is what this installer assumes.
-
-Or, if you do want to keep using the asset pipeline in parallel, make sure to remove the `require_tree .` line from the `application.css`.
-
-
## License
Tailwind for Rails is released under the [MIT License](https://opensource.org/licenses/MIT).
-The Inter font is released under the [SIL Open Font License, Version 1.1](https://github.com/rsms/inter/blob/master/LICENSE.txt).
diff --git a/app/assets/fonts/Inter-italic.alternates.var.woff2 b/app/assets/fonts/Inter-italic.alternates.var.woff2
deleted file mode 100644
index cbeb7846..00000000
Binary files a/app/assets/fonts/Inter-italic.alternates.var.woff2 and /dev/null differ
diff --git a/app/assets/fonts/Inter-italic.cyrillic.var.woff2 b/app/assets/fonts/Inter-italic.cyrillic.var.woff2
deleted file mode 100644
index 90930a09..00000000
Binary files a/app/assets/fonts/Inter-italic.cyrillic.var.woff2 and /dev/null differ
diff --git a/app/assets/fonts/Inter-italic.extra.var.woff2 b/app/assets/fonts/Inter-italic.extra.var.woff2
deleted file mode 100644
index cbeb7846..00000000
Binary files a/app/assets/fonts/Inter-italic.extra.var.woff2 and /dev/null differ
diff --git a/app/assets/fonts/Inter-italic.greek.var.woff2 b/app/assets/fonts/Inter-italic.greek.var.woff2
deleted file mode 100644
index c0684d11..00000000
Binary files a/app/assets/fonts/Inter-italic.greek.var.woff2 and /dev/null differ
diff --git a/app/assets/fonts/Inter-italic.latin-ext.var.woff2 b/app/assets/fonts/Inter-italic.latin-ext.var.woff2
deleted file mode 100644
index be543c42..00000000
Binary files a/app/assets/fonts/Inter-italic.latin-ext.var.woff2 and /dev/null differ
diff --git a/app/assets/fonts/Inter-italic.latin.var.woff2 b/app/assets/fonts/Inter-italic.latin.var.woff2
deleted file mode 100644
index 5066cfdf..00000000
Binary files a/app/assets/fonts/Inter-italic.latin.var.woff2 and /dev/null differ
diff --git a/app/assets/fonts/Inter-italic.symbols.var.woff2 b/app/assets/fonts/Inter-italic.symbols.var.woff2
deleted file mode 100644
index 1542bc95..00000000
Binary files a/app/assets/fonts/Inter-italic.symbols.var.woff2 and /dev/null differ
diff --git a/app/assets/fonts/Inter-italic.vietnamese.var.woff2 b/app/assets/fonts/Inter-italic.vietnamese.var.woff2
deleted file mode 100644
index badcf325..00000000
Binary files a/app/assets/fonts/Inter-italic.vietnamese.var.woff2 and /dev/null differ
diff --git a/app/assets/fonts/Inter-roman.alternates.var.woff2 b/app/assets/fonts/Inter-roman.alternates.var.woff2
deleted file mode 100644
index f2ee9eeb..00000000
Binary files a/app/assets/fonts/Inter-roman.alternates.var.woff2 and /dev/null differ
diff --git a/app/assets/fonts/Inter-roman.cyrillic.var.woff2 b/app/assets/fonts/Inter-roman.cyrillic.var.woff2
deleted file mode 100644
index a9378e78..00000000
Binary files a/app/assets/fonts/Inter-roman.cyrillic.var.woff2 and /dev/null differ
diff --git a/app/assets/fonts/Inter-roman.extra.var.woff2 b/app/assets/fonts/Inter-roman.extra.var.woff2
deleted file mode 100644
index f2ee9eeb..00000000
Binary files a/app/assets/fonts/Inter-roman.extra.var.woff2 and /dev/null differ
diff --git a/app/assets/fonts/Inter-roman.greek.var.woff2 b/app/assets/fonts/Inter-roman.greek.var.woff2
deleted file mode 100644
index 8604c8a0..00000000
Binary files a/app/assets/fonts/Inter-roman.greek.var.woff2 and /dev/null differ
diff --git a/app/assets/fonts/Inter-roman.latin-ext.var.woff2 b/app/assets/fonts/Inter-roman.latin-ext.var.woff2
deleted file mode 100644
index ac92ffba..00000000
Binary files a/app/assets/fonts/Inter-roman.latin-ext.var.woff2 and /dev/null differ
diff --git a/app/assets/fonts/Inter-roman.latin.var.woff2 b/app/assets/fonts/Inter-roman.latin.var.woff2
deleted file mode 100644
index 9503ba17..00000000
Binary files a/app/assets/fonts/Inter-roman.latin.var.woff2 and /dev/null differ
diff --git a/app/assets/fonts/Inter-roman.symbols.var.woff2 b/app/assets/fonts/Inter-roman.symbols.var.woff2
deleted file mode 100644
index c8da0889..00000000
Binary files a/app/assets/fonts/Inter-roman.symbols.var.woff2 and /dev/null differ
diff --git a/app/assets/fonts/Inter-roman.vietnamese.var.woff2 b/app/assets/fonts/Inter-roman.vietnamese.var.woff2
deleted file mode 100644
index b65ab546..00000000
Binary files a/app/assets/fonts/Inter-roman.vietnamese.var.woff2 and /dev/null differ
diff --git a/app/assets/stylesheets/inter-font.css b/app/assets/stylesheets/inter-font.css
deleted file mode 100644
index ddeabaf5..00000000
--- a/app/assets/stylesheets/inter-font.css
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- *= link_directory ../fonts
- */
-
-@font-face {
- font-family: 'Inter var';
- font-style: italic;
- font-weight: 100 900;
- font-display: swap;
- src: url('Inter-italic.extra.var.woff2') format('woff2');
- unicode-range: U+E000,U+E002-E081,U+E093-E097,U+E0A5-E0E7,U+E0F3-E11D,U+E11E-E165,U+EE01,U+F6C3;
- font-named-instance: 'Italic';
-}
-/* alternates */
-@font-face {
- font-family: 'Inter var';
- font-style: italic;
- font-weight: 100 900;
- font-display: swap;
- src: url('Inter-italic.alternates.var.woff2') format('woff2');
- unicode-range: U+E000,U+E002-E081,U+E093-E097,U+E0A5-E0E7,U+E0F3-E11D,U+E11E-E165,U+EE01,U+F6C3;
- font-named-instance: 'Italic';
-}
-/* symbols */
-@font-face {
- font-family: 'Inter var';
- font-style: italic;
- font-weight: 100 900;
- font-display: swap;
- src: url('Inter-italic.symbols.var.woff2') format('woff2');
- unicode-range: U+20DD-20DF,U+2190-219A,U+21A9-21AB,U+21B0-21B2,U+21B3-21B6,U+21BA-21BC,U+21D0,U+21D2,U+21D4,U+21DE-21E0,U+21E4-21E6,U+21E7,U+21EA,U+2303,U+2305,U+2318,U+2325-2328,U+232B,U+2380,U+2387,U+238B,U+23CE-23D0,U+2460-2469,U+24B6-24D0,U+24EA,U+25A0-25A3,U+25B2-25B4,U+25B6-25B8,U+25BA-25BE,U+25C0-25C2,U+25C4-25C8,U+25CB,U+25CF,U+25EF,U+2600,U+2605-2607,U+263C,U+2661,U+2665,U+26A0,U+2713,U+2717,U+2756,U+2764,U+2780-2789,U+27F5-27FB,U+2B06,U+2B12-2B14,U+2B1C,U+E000,U+E12B-E164,U+1F130-1F14A,U+1F850,U+1F852;
- font-named-instance: 'Italic';
-}
-
-/* extra */
-@font-face {
- font-family: 'Inter var';
- font-style: italic;
- font-weight: 100 900;
- font-display: swap;
- src: url('Inter-italic.extra.var.woff2') format('woff2');
- unicode-range: U+E000,U+E002-E081,U+E093-E097,U+E0A5-E0E7,U+E0F3-E11D,U+E11E-E165,U+EE01,U+F6C3;
- font-named-instance: 'Italic';
-}
-/* alternates */
-@font-face {
- font-family: 'Inter var';
- font-style: italic;
- font-weight: 100 900;
- font-display: swap;
- src: url('Inter-italic.alternates.var.woff2') format('woff2');
- unicode-range: U+E000,U+E002-E081,U+E093-E097,U+E0A5-E0E7,U+E0F3-E11D,U+E11E-E165,U+EE01,U+F6C3;
- font-named-instance: 'Italic';
-}
-/* symbols */
-@font-face {
- font-family: 'Inter var';
- font-style: italic;
- font-weight: 100 900;
- font-display: swap;
- src: url('Inter-italic.symbols.var.woff2') format('woff2');
- unicode-range: U+20DD-20DF,U+2190-219A,U+21A9-21AB,U+21B0-21B2,U+21B3-21B6,U+21BA-21BC,U+21D0,U+21D2,U+21D4,U+21DE-21E0,U+21E4-21E6,U+21E7,U+21EA,U+2303,U+2305,U+2318,U+2325-2328,U+232B,U+2380,U+2387,U+238B,U+23CE-23D0,U+2460-2469,U+24B6-24D0,U+24EA,U+25A0-25A3,U+25B2-25B4,U+25B6-25B8,U+25BA-25BE,U+25C0-25C2,U+25C4-25C8,U+25CB,U+25CF,U+25EF,U+2600,U+2605-2607,U+263C,U+2661,U+2665,U+26A0,U+2713,U+2717,U+2756,U+2764,U+2780-2789,U+27F5-27FB,U+2B06,U+2B12-2B14,U+2B1C,U+E000,U+E12B-E164,U+1F130-1F14A,U+1F850,U+1F852;
- font-named-instance: 'Italic';
-}
-/* cyrillic */
-@font-face {
- font-family: 'Inter var';
- font-style: italic;
- font-weight: 100 900;
- font-display: swap;
- src: url('Inter-italic.cyrillic.var.woff2') format('woff2');
- unicode-range: U+0400-049E,U+04A0-0500,U+052F,U+20B4,U+2116,U+2DFF,U+A69F;
- font-named-instance: 'Italic';
-}
-/* greek */
-@font-face {
- font-family: 'Inter var';
- font-style: italic;
- font-weight: 100 900;
- font-display: swap;
- src: url('Inter-italic.greek.var.woff2') format('woff2');
- unicode-range: U+0370-0378,U+037A-0380,U+0384-038B,U+038C,U+038E-03A2,U+03A3-03E2,U+03F0-0400,U+1F00-1F16,U+1F18-1F1E,U+1F20-1F46,U+1F48-1F4E,U+1F50-1F58,U+1F59,U+1F5B,U+1F5D,U+1F5F-1F7E,U+1F80-1FB5,U+1FB6-1FC5,U+1FC6-1FD4,U+1FD6-1FDC,U+1FDD-1FF0,U+1FF2-1FF5,U+1FF6-1FFF;
- font-named-instance: 'Italic';
-}
-/* vietnamese */
-@font-face {
- font-family: 'Inter var';
- font-style: italic;
- font-weight: 100 900;
- font-display: swap;
- src: url('Inter-italic.vietnamese.var.woff2') format('woff2');
- unicode-range: U+0102-0104,U+0110-0112,U+0128-012A,U+0168-016A,U+01A0-01A2,U+01AF-01B1,U+1EA0-1EFA,U+20AB;
- font-named-instance: 'Italic';
-}
-/* latin-ext */
-@font-face {
- font-family: 'Inter var';
- font-style: italic;
- font-weight: 100 900;
- font-display: swap;
- src: url('Inter-italic.latin-ext.var.woff2') format('woff2');
- unicode-range: U+0100-0149,U+014A-01C4,U+01C5-0250,U+0259,U+1E00-1F00,U+2020,U+20A0-20AC,U+20AD-20C0,U+2113,U+2C7C,U+2C7F,U+A7FF;
- font-named-instance: 'Italic';
-}
-/* latin */
-@font-face {
- font-family: 'Inter var';
- font-style: italic;
- font-weight: 100 900;
- font-display: swap;
- src: url('Inter-italic.latin.var.woff2') format('woff2');
- unicode-range: U+0000-007F,U+00A0-0100,U+0131,U+0152-0154,U+02BB-02BD,U+02C6,U+02DA,U+02DC,U+2000-200C,U+2010-2028,U+202F-2060,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+FEFF;
- font-named-instance: 'Italic';
-}
-/* extra */
-@font-face {
- font-family: 'Inter var';
- font-style: normal;
- font-weight: 100 900;
- font-display: swap;
- src: url('Inter-roman.extra.var.woff2') format('woff2');
- unicode-range: U+E000,U+E002-E081,U+E093-E097,U+E0A5-E0E7,U+E0F3-E11D,U+E11E-E165,U+EE01,U+F6C3;
- font-named-instance: 'Regular';
-}
-/* alternates */
-@font-face {
- font-family: 'Inter var';
- font-style: normal;
- font-weight: 100 900;
- font-display: swap;
- src: url('Inter-roman.alternates.var.woff2') format('woff2');
- unicode-range: U+E000,U+E002-E081,U+E093-E097,U+E0A5-E0E7,U+E0F3-E11D,U+E11E-E165,U+EE01,U+F6C3;
- font-named-instance: 'Regular';
-}
-/* symbols */
-@font-face {
- font-family: 'Inter var';
- font-style: normal;
- font-weight: 100 900;
- font-display: swap;
- src: url('Inter-roman.symbols.var.woff2') format('woff2');
- unicode-range: U+20DD-20DF,U+2190-219A,U+21A9-21AB,U+21B0-21B2,U+21B3-21B6,U+21BA-21BC,U+21D0,U+21D2,U+21D4,U+21DE-21E0,U+21E4-21E6,U+21E7,U+21EA,U+2303,U+2305,U+2318,U+2325-2328,U+232B,U+2380,U+2387,U+238B,U+23CE-23D0,U+2460-2469,U+24B6-24D0,U+24EA,U+25A0-25A3,U+25B2-25B4,U+25B6-25B8,U+25BA-25BE,U+25C0-25C2,U+25C4-25C8,U+25CB,U+25CF,U+25EF,U+2600,U+2605-2607,U+263C,U+2661,U+2665,U+26A0,U+2713,U+2717,U+2756,U+2764,U+2780-2789,U+27F5-27FB,U+2B06,U+2B12-2B14,U+2B1C,U+E000,U+E12B-E164,U+1F130-1F14A,U+1F850,U+1F852;
- font-named-instance: 'Regular';
-}
-/* cyrillic */
-@font-face {
- font-family: 'Inter var';
- font-style: normal;
- font-weight: 100 900;
- font-display: swap;
- src: url('Inter-roman.cyrillic.var.woff2') format('woff2');
- unicode-range: U+0400-049E,U+04A0-0500,U+052F,U+20B4,U+2116,U+2DFF,U+A69F;
- font-named-instance: 'Regular';
-}
-/* greek */
-@font-face {
- font-family: 'Inter var';
- font-style: normal;
- font-weight: 100 900;
- font-display: swap;
- src: url('Inter-roman.greek.var.woff2') format('woff2');
- unicode-range: U+0370-0378,U+037A-0380,U+0384-038B,U+038C,U+038E-03A2,U+03A3-03E2,U+03F0-0400,U+1F00-1F16,U+1F18-1F1E,U+1F20-1F46,U+1F48-1F4E,U+1F50-1F58,U+1F59,U+1F5B,U+1F5D,U+1F5F-1F7E,U+1F80-1FB5,U+1FB6-1FC5,U+1FC6-1FD4,U+1FD6-1FDC,U+1FDD-1FF0,U+1FF2-1FF5,U+1FF6-1FFF;
- font-named-instance: 'Regular';
-}
-/* vietnamese */
-@font-face {
- font-family: 'Inter var';
- font-style: normal;
- font-weight: 100 900;
- font-display: swap;
- src: url('Inter-roman.vietnamese.var.woff2') format('woff2');
- unicode-range: U+0102-0104,U+0110-0112,U+0128-012A,U+0168-016A,U+01A0-01A2,U+01AF-01B1,U+1EA0-1EFA,U+20AB;
- font-named-instance: 'Regular';
-}
-/* latin-ext */
-@font-face {
- font-family: 'Inter var';
- font-style: normal;
- font-weight: 100 900;
- font-display: swap;
- src: url('Inter-roman.latin-ext.var.woff2') format('woff2');
- unicode-range: U+0100-0149,U+014A-01C4,U+01C5-0250,U+0259,U+1E00-1F00,U+2020,U+20A0-20AC,U+20AD-20C0,U+2113,U+2C7C,U+2C7F,U+A7FF;
- font-named-instance: 'Regular';
-}
-/* latin */
-@font-face {
- font-family: 'Inter var';
- font-style: normal;
- font-weight: 100 900;
- font-display: swap;
- src: url('Inter-roman.latin.var.woff2') format('woff2');
- unicode-range: U+0000-007F,U+00A0-0100,U+0131,U+0152-0154,U+02BB-02BD,U+02C6,U+02DA,U+02DC,U+2000-200C,U+2010-2028,U+202F-2060,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+FEFF;
- font-named-instance: 'Regular';
-}
\ No newline at end of file
diff --git a/lib/generators/tailwindcss/authentication/authentication_generator.rb b/lib/generators/tailwindcss/authentication/authentication_generator.rb
index aa6fc23b..756f1d7b 100644
--- a/lib/generators/tailwindcss/authentication/authentication_generator.rb
+++ b/lib/generators/tailwindcss/authentication/authentication_generator.rb
@@ -3,6 +3,8 @@
module Tailwindcss
module Generators
class AuthenticationGenerator < Erb::Generators::AuthenticationGenerator
+ hide!
+
source_root File.expand_path("templates", __dir__)
end
end
diff --git a/lib/generators/tailwindcss/authentication/templates/app/views/passwords/edit.html.erb b/lib/generators/tailwindcss/authentication/templates/app/views/passwords/edit.html.erb
index 939b0b8d..7096a5bc 100644
--- a/lib/generators/tailwindcss/authentication/templates/app/views/passwords/edit.html.erb
+++ b/lib/generators/tailwindcss/authentication/templates/app/views/passwords/edit.html.erb
@@ -7,15 +7,15 @@
<%%= form_with url: password_path(params[:token]), method: :put, class: "contents" do |form| %>
- <%%= form.password_field :password, required: true, autocomplete: "new-password", placeholder: "Enter new password", maxlength: 72, class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
+ <%%= form.password_field :password, required: true, autocomplete: "new-password", placeholder: "Enter new password", maxlength: 72, class: "block shadow-sm rounded-md border border-gray-400 focus:outline-solid focus:outline-blue-600 px-3 py-2 mt-2 w-full" %>
- <%%= form.password_field :password_confirmation, required: true, autocomplete: "new-password", placeholder: "Repeat new password", maxlength: 72, class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
+ <%%= form.password_field :password_confirmation, required: true, autocomplete: "new-password", placeholder: "Repeat new password", maxlength: 72, class: "block shadow-sm rounded-md border border-gray-400 focus:outline-solid focus:outline-blue-600 px-3 py-2 mt-2 w-full" %>
- <%%= form.submit "Save", class: "rounded-lg py-3 px-5 bg-blue-600 text-white inline-block font-medium cursor-pointer" %>
+ <%%= form.submit "Save", class: "w-full sm:w-auto text-center rounded-md px-3.5 py-2.5 bg-blue-600 hover:bg-blue-500 text-white inline-block font-medium cursor-pointer" %>
<%% end %>
diff --git a/lib/generators/tailwindcss/authentication/templates/app/views/passwords/new.html.erb b/lib/generators/tailwindcss/authentication/templates/app/views/passwords/new.html.erb
index e5d88197..36dff837 100644
--- a/lib/generators/tailwindcss/authentication/templates/app/views/passwords/new.html.erb
+++ b/lib/generators/tailwindcss/authentication/templates/app/views/passwords/new.html.erb
@@ -7,11 +7,11 @@
<%%= form_with url: passwords_path, class: "contents" do |form| %>
- <%%= form.email_field :email_address, required: true, autofocus: true, autocomplete: "username", placeholder: "Enter your email address", value: params[:email_address], class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
+ <%%= form.email_field :email_address, required: true, autofocus: true, autocomplete: "username", placeholder: "Enter your email address", value: params[:email_address], class: "block shadow-sm rounded-md border border-gray-400 focus:outline-solid focus:outline-blue-600 px-3 py-2 mt-2 w-full" %>
- <%%= form.submit "Email reset instructions", class: "rounded-lg py-3 px-5 bg-blue-600 text-white inline-block font-medium cursor-pointer" %>
+ <%%= form.submit "Email reset instructions", class: "w-full sm:w-auto text-center rounded-lg px-3.5 py-2.5 bg-blue-600 hover:bg-blue-500 text-white inline-block font-medium cursor-pointer" %>
<%% end %>
diff --git a/lib/generators/tailwindcss/authentication/templates/app/views/sessions/new.html.erb b/lib/generators/tailwindcss/authentication/templates/app/views/sessions/new.html.erb
index a4751541..30fb0d91 100644
--- a/lib/generators/tailwindcss/authentication/templates/app/views/sessions/new.html.erb
+++ b/lib/generators/tailwindcss/authentication/templates/app/views/sessions/new.html.erb
@@ -11,20 +11,20 @@
<%%= form_with url: session_url, class: "contents" do |form| %>
- <%%= form.email_field :email_address, required: true, autofocus: true, autocomplete: "username", placeholder: "Enter your email address", value: params[:email_address], class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
+ <%%= form.email_field :email_address, required: true, autofocus: true, autocomplete: "username", placeholder: "Enter your email address", value: params[:email_address], class: "block shadow-sm rounded-md border border-gray-400 focus:outline-blue-600 px-3 py-2 mt-2 w-full" %>
- <%%= form.password_field :password, required: true, autocomplete: "current-password", placeholder: "Enter your password", maxlength: 72, class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
+ <%%= form.password_field :password, required: true, autocomplete: "current-password", placeholder: "Enter your password", maxlength: 72, class: "block shadow-sm rounded-md border border-gray-400 focus:outline-blue-600 px-3 py-2 mt-2 w-full" %>
- <%%= form.submit "Sign in", class: "rounded-lg py-3 px-5 bg-blue-600 text-white inline-block font-medium cursor-pointer" %>
+ <%%= form.submit "Sign in", class: "w-full sm:w-auto text-center rounded-md px-3.5 py-2.5 bg-blue-600 hover:bg-blue-500 text-white inline-block font-medium cursor-pointer" %>
- <%%= link_to "Forgot password?", new_password_path, class: "text-gray-700 underline" %>
+ <%%= link_to "Forgot password?", new_password_path, class: "text-gray-700 underline hover:no-underline" %>
<%% end %>
diff --git a/lib/generators/tailwindcss/authentication/templates/views/passwords/edit.html.erb b/lib/generators/tailwindcss/authentication/templates/views/passwords/edit.html.erb
deleted file mode 100644
index 939b0b8d..00000000
--- a/lib/generators/tailwindcss/authentication/templates/views/passwords/edit.html.erb
+++ /dev/null
@@ -1,21 +0,0 @@
-
- <%% if alert = flash[:alert] %>
-
<%%= alert %>
- <%% end %>
-
-
Update your password
-
- <%%= form_with url: password_path(params[:token]), method: :put, class: "contents" do |form| %>
-
- <%%= form.password_field :password, required: true, autocomplete: "new-password", placeholder: "Enter new password", maxlength: 72, class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
-
-
-
- <%%= form.password_field :password_confirmation, required: true, autocomplete: "new-password", placeholder: "Repeat new password", maxlength: 72, class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
-
-
-
- <%%= form.submit "Save", class: "rounded-lg py-3 px-5 bg-blue-600 text-white inline-block font-medium cursor-pointer" %>
-
- <%% end %>
-
diff --git a/lib/generators/tailwindcss/authentication/templates/views/passwords/new.html.erb b/lib/generators/tailwindcss/authentication/templates/views/passwords/new.html.erb
deleted file mode 100644
index e5d88197..00000000
--- a/lib/generators/tailwindcss/authentication/templates/views/passwords/new.html.erb
+++ /dev/null
@@ -1,17 +0,0 @@
-
- <%% if alert = flash[:alert] %>
-
<%%= alert %>
- <%% end %>
-
-
Forgot your password?
-
- <%%= form_with url: passwords_path, class: "contents" do |form| %>
-
- <%%= form.email_field :email_address, required: true, autofocus: true, autocomplete: "username", placeholder: "Enter your email address", value: params[:email_address], class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
-
-
-
- <%%= form.submit "Email reset instructions", class: "rounded-lg py-3 px-5 bg-blue-600 text-white inline-block font-medium cursor-pointer" %>
-
- <%% end %>
-
diff --git a/lib/generators/tailwindcss/authentication/templates/views/sessions/new.html.erb b/lib/generators/tailwindcss/authentication/templates/views/sessions/new.html.erb
deleted file mode 100644
index a4751541..00000000
--- a/lib/generators/tailwindcss/authentication/templates/views/sessions/new.html.erb
+++ /dev/null
@@ -1,31 +0,0 @@
-
- <%% if alert = flash[:alert] %>
-
<%%= alert %>
- <%% end %>
-
- <%% if notice = flash[:notice] %>
-
<%%= notice %>
- <%% end %>
-
-
Sign in
-
- <%%= form_with url: session_url, class: "contents" do |form| %>
-
- <%%= form.email_field :email_address, required: true, autofocus: true, autocomplete: "username", placeholder: "Enter your email address", value: params[:email_address], class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
-
-
-
- <%%= form.password_field :password, required: true, autocomplete: "current-password", placeholder: "Enter your password", maxlength: 72, class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
-
-
-
-
- <%%= form.submit "Sign in", class: "rounded-lg py-3 px-5 bg-blue-600 text-white inline-block font-medium cursor-pointer" %>
-
-
-
- <%%= link_to "Forgot password?", new_password_path, class: "text-gray-700 underline" %>
-
-
- <%% end %>
-
diff --git a/lib/generators/tailwindcss/scaffold/templates/_form.html.erb.tt b/lib/generators/tailwindcss/scaffold/templates/_form.html.erb.tt
index 487f2f5f..e50790ce 100644
--- a/lib/generators/tailwindcss/scaffold/templates/_form.html.erb.tt
+++ b/lib/generators/tailwindcss/scaffold/templates/_form.html.erb.tt
@@ -1,9 +1,9 @@
<%%= form_with(model: <%= model_resource_name %>, class: "contents") do |form| %>
<%% if <%= singular_table_name %>.errors.any? %>
-
+
<%%= pluralize(<%= singular_table_name %>.errors.count, "error") %> prohibited this <%= singular_table_name %> from being saved:
-
+
<%% <%= singular_table_name %>.errors.each do |error| %>
- <%%= error.full_message %>
<%% end %>
@@ -12,32 +12,32 @@
<%% end %>
<% attributes.each do |attribute| -%>
-
+
">
<% if attribute.password_digest? -%>
<%%= form.label :password %>
- <%%= form.password_field :password, class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
-
+ <%%= form.password_field :password, class: ["block shadow-sm rounded-md border px-3 py-2 mt-2 w-full", {"border-gray-400 focus:outline-blue-600": <%= model_resource_name %>.errors[:password].none?, "border-red-400 focus:outline-red-600": <%= model_resource_name %>.errors[:password].any?}] %>
+
-
+
<%%= form.label :password_confirmation %>
- <%%= form.password_field :password_confirmation, class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
+ <%%= form.password_field :password_confirmation, class: ["block shadow-sm rounded-md border px-3 py-2 mt-2 w-full", {"border-gray-400 focus:outline-blue-600": <%= model_resource_name %>.errors[:password_confirmation].none?, "border-red-400 focus:outline-red-600": <%= model_resource_name %>.errors[:password_confirmation].any?}] %>
<% elsif attribute.attachments? -%>
<%%= form.label :<%= attribute.column_name %> %>
- <%%= form.<%= attribute.field_type %> :<%= attribute.column_name %>, multiple: true, class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
+ <%%= form.<%= attribute.field_type %> :<%= attribute.column_name %>, multiple: true, class: ["block shadow-sm rounded-md border px-3 py-2 mt-2 w-full", {"border-gray-400 focus:outline-blue-600": <%= model_resource_name %>.errors[:password].none?, "border-red-400 focus:outline-red-600": <%= model_resource_name %>.errors[:password].any?}] %>
<% else -%>
<%%= form.label :<%= attribute.column_name %> %>
<% if attribute.field_type == :textarea || attribute.field_type == :text_area -%>
- <%%= form.<%= attribute.field_type %> :<%= attribute.column_name %>, rows: 4, class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
+ <%%= form.<%= attribute.field_type %> :<%= attribute.column_name %>, rows: 4, class: ["block shadow-sm rounded-md border px-3 py-2 mt-2 w-full", {"border-gray-400 focus:outline-blue-600": <%= model_resource_name %>.errors[:<%= attribute.column_name %>].none?, "border-red-400 focus:outline-red-600": <%= model_resource_name %>.errors[:<%= attribute.column_name %>].any?}] %>
<% elsif attribute.field_type == :checkbox || attribute.field_type == :check_box -%>
- <%%= form.<%= attribute.field_type %> :<%= attribute.column_name %>, class: "block mt-2 h-5 w-5" %>
+ <%%= form.<%= attribute.field_type %> :<%= attribute.column_name %>, class: ["block shadow-sm rounded-md border order-first h-5 w-5", {"border-gray-400 focus:outline-blue-600": <%= model_resource_name %>.errors[:<%= attribute.column_name %>].none?, "border-red-400 focus:outline-red-600": <%= model_resource_name %>.errors[:<%= attribute.column_name %>].any?}] %>
<% else -%>
- <%%= form.<%= attribute.field_type %> :<%= attribute.column_name %>, class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
+ <%%= form.<%= attribute.field_type %> :<%= attribute.column_name %>, class: ["block shadow-sm rounded-md border px-3 py-2 mt-2 w-full", {"border-gray-400 focus:outline-blue-600": <%= model_resource_name %>.errors[:<%= attribute.column_name %>].none?, "border-red-400 focus:outline-red-600": <%= model_resource_name %>.errors[:<%= attribute.column_name %>].any?}] %>
<% end -%>
<% end -%>
<% end -%>
- <%%= form.submit class: "rounded-lg py-3 px-5 bg-blue-600 text-white inline-block font-medium cursor-pointer" %>
+ <%%= form.submit class: "w-full sm:w-auto rounded-md px-3.5 py-2.5 bg-blue-600 hover:bg-blue-500 text-white inline-block font-medium cursor-pointer" %>
<%% end %>
diff --git a/lib/generators/tailwindcss/scaffold/templates/edit.html.erb.tt b/lib/generators/tailwindcss/scaffold/templates/edit.html.erb.tt
index fab779d1..08d92d3a 100644
--- a/lib/generators/tailwindcss/scaffold/templates/edit.html.erb.tt
+++ b/lib/generators/tailwindcss/scaffold/templates/edit.html.erb.tt
@@ -1,8 +1,10 @@
-
+<%% content_for :title, "Editing <%= human_name.downcase %>" %>
+
+
Editing <%= human_name.downcase %>
<%%= render "form", <%= singular_table_name %>: @<%= singular_table_name %> %>
- <%%= link_to "Show this <%= human_name.downcase %>", <%= model_resource_name(prefix: "@") %>, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
- <%%= link_to "Back to <%= human_name.pluralize.downcase %>", <%= index_helper(type: :path) %>, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
+ <%%= link_to "Show this <%= human_name.downcase %>", <%= model_resource_name(prefix: "@") %>, class: "w-full sm:w-auto text-center mt-2 sm:mt-0 sm:ml-2 rounded-md px-3.5 py-2.5 bg-gray-100 hover:bg-gray-50 inline-block font-medium" %>
+ <%%= link_to "Back to <%= human_name.pluralize.downcase %>", <%= index_helper(type: :path) %>, class: "w-full sm:w-auto text-center mt-2 sm:mt-0 sm:ml-2 rounded-md px-3.5 py-2.5 bg-gray-100 hover:bg-gray-50 inline-block font-medium" %>
diff --git a/lib/generators/tailwindcss/scaffold/templates/index.html.erb.tt b/lib/generators/tailwindcss/scaffold/templates/index.html.erb.tt
index 65cf06b5..848c97fb 100644
--- a/lib/generators/tailwindcss/scaffold/templates/index.html.erb.tt
+++ b/lib/generators/tailwindcss/scaffold/templates/index.html.erb.tt
@@ -1,21 +1,31 @@
+<%% content_for :title, "<%= human_name.pluralize %>" %>
+
<%% if notice.present? %>
-
<%%= notice %>
+
<%%= notice %>
<%% end %>
- <%% content_for :title, "<%= human_name.pluralize %>" %>
-
<%= human_name.pluralize %>
- <%%= link_to "New <%= human_name.downcase %>", new_<%= singular_route_name %>_path, class: "rounded-lg py-3 px-5 bg-blue-600 text-white block font-medium" %>
+ <%%= link_to "New <%= human_name.downcase %>", new_<%= singular_route_name %>_path, class: "rounded-md px-3.5 py-2.5 bg-blue-600 hover:bg-blue-500 text-white block font-medium" %>
-
- <%% @<%= plural_table_name %>.each do |<%= singular_table_name %>| %>
- <%%= render <%= singular_table_name %> %>
-
- <%%= link_to "Show this <%= human_name.downcase %>", <%= model_resource_name(singular_table_name) %>, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
-
+
+ <%% if @<%= plural_table_name %>.any? %>
+ <%% @<%= plural_table_name %>.each do |<%= singular_table_name %>| %>
+
+ <%%= render <%= singular_table_name %> %>
+
+ <%%= link_to "Show", <%= model_resource_name(singular_table_name) %>, class: "w-full sm:w-auto text-center rounded-md px-3.5 py-2.5 bg-gray-100 hover:bg-gray-50 inline-block font-medium" %>
+ <%%= link_to "Edit", <%= edit_helper(singular_table_name, type: :path) %>, class: "w-full sm:w-auto text-center rounded-md px-3.5 py-2.5 bg-gray-100 hover:bg-gray-50 inline-block font-medium" %>
+
+ <%%= button_to "Destroy", <%= model_resource_name %>, method: :delete, class: "w-full sm:w-auto rounded-md px-3.5 py-2.5 text-white bg-red-600 hover:bg-red-500 font-medium cursor-pointer", data: { turbo_confirm: "Are you sure?" } %>
+
+
+
+ <%% end %>
+ <%% else %>
+
No <%= human_name.downcase.pluralize %> found.
<%% end %>
diff --git a/lib/generators/tailwindcss/scaffold/templates/new.html.erb.tt b/lib/generators/tailwindcss/scaffold/templates/new.html.erb.tt
index 665532e5..32ad9b83 100644
--- a/lib/generators/tailwindcss/scaffold/templates/new.html.erb.tt
+++ b/lib/generators/tailwindcss/scaffold/templates/new.html.erb.tt
@@ -1,7 +1,9 @@
-
+<%% content_for :title, "New <%= human_name.downcase %>" %>
+
+
New <%= human_name.downcase %>
<%%= render "form", <%= singular_table_name %>: @<%= singular_table_name %> %>
- <%%= link_to "Back to <%= human_name.pluralize.downcase %>", <%= index_helper(type: :path) %>, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
+ <%%= link_to "Back to <%= human_name.pluralize.downcase %>", <%= index_helper(type: :path) %>, class: "w-full sm:w-auto text-center mt-2 sm:mt-0 sm:ml-2 rounded-md px-3.5 py-2.5 bg-gray-100 hover:bg-gray-50 inline-block font-medium" %>
diff --git a/lib/generators/tailwindcss/scaffold/templates/partial.html.erb.tt b/lib/generators/tailwindcss/scaffold/templates/partial.html.erb.tt
index e1a45470..433b7a33 100644
--- a/lib/generators/tailwindcss/scaffold/templates/partial.html.erb.tt
+++ b/lib/generators/tailwindcss/scaffold/templates/partial.html.erb.tt
@@ -1,17 +1,18 @@
-
+
<% attributes.reject(&:password_digest?).each do |attribute| -%>
-
+
<%= attribute.human_name %>:
<% if attribute.attachment? -%>
- <%%= link_to <%= singular_name %>.<%= attribute.column_name %>.filename, <%= singular_name %>.<%= attribute.column_name %> if <%= singular_name %>.<%= attribute.column_name %>.attached? %>
+ <%%= link_to <%= singular_name %>.<%= attribute.column_name %>.filename, <%= singular_name %>.<%= attribute.column_name %>, class: "text-gray-700 underline hover:no-underline" if <%= singular_name %>.<%= attribute.column_name %>.attached? %>
<% elsif attribute.attachments? -%>
<%% <%= singular_name %>.<%= attribute.column_name %>.each do |<%= attribute.singular_name %>| %>
-
<%%= link_to <%= attribute.singular_name %>.filename, <%= attribute.singular_name %> %>
+
<%%= link_to <%= attribute.singular_name %>.filename, <%= attribute.singular_name %>, class: "text-gray-700 underline hover:no-underline" %>
<%% end %>
+<% elsif attribute.type == :boolean -%>
+ <%%= <%= singular_name %>.<%= attribute.column_name %>? ? "Yes" : "No" %>
<% else -%>
<%%= <%= singular_name %>.<%= attribute.column_name %> %>
<% end -%>
-
-
+
<% end -%>
diff --git a/lib/generators/tailwindcss/scaffold/templates/show.html.erb.tt b/lib/generators/tailwindcss/scaffold/templates/show.html.erb.tt
index 17d0febf..b6f153c2 100644
--- a/lib/generators/tailwindcss/scaffold/templates/show.html.erb.tt
+++ b/lib/generators/tailwindcss/scaffold/templates/show.html.erb.tt
@@ -1,15 +1,17 @@
-
-
- <%% if notice.present? %>
-
<%%= notice %>
- <%% end %>
-
- <%%= render @<%= singular_table_name %> %>
-
- <%%= link_to "Edit this <%= human_name.downcase %>", <%= edit_helper(type: :path) %>, class: "mt-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
- <%%= link_to "Back to <%= human_name.pluralize.downcase %>", <%= index_helper %>_path, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
-
- <%%= button_to "Destroy this <%= human_name.downcase %>", <%= model_resource_name(prefix: "@") %>, method: :delete, class: "mt-2 rounded-lg py-3 px-5 bg-gray-100 font-medium" %>
-
+<%% content_for :title, "Showing <%= human_name.downcase %>" %>
+
+
+ <%% if notice.present? %>
+
<%%= notice %>
+ <%% end %>
+
+
Showing <%= human_name.downcase %>
+
+ <%%= render @<%= singular_table_name %> %>
+
+ <%%= link_to "Edit this <%= human_name.downcase %>", <%= edit_helper(type: :path) %>, class: "w-full sm:w-auto text-center rounded-md px-3.5 py-2.5 bg-gray-100 hover:bg-gray-50 inline-block font-medium" %>
+ <%%= link_to "Back to <%= human_name.pluralize.downcase %>", <%= index_helper %>_path, class: "w-full sm:w-auto text-center mt-2 sm:mt-0 sm:ml-2 rounded-md px-3.5 py-2.5 bg-gray-100 hover:bg-gray-50 inline-block font-medium" %>
+
+ <%%= button_to "Destroy this <%= human_name.downcase %>", <%= model_resource_name(prefix: "@") %>, method: :delete, class: "w-full rounded-md px-3.5 py-2.5 text-white bg-red-600 hover:bg-red-500 font-medium cursor-pointer", data: { turbo_confirm: "Are you sure?" } %>
diff --git a/lib/install/application.css b/lib/install/application.css
new file mode 100644
index 00000000..f1d8c73c
--- /dev/null
+++ b/lib/install/application.css
@@ -0,0 +1 @@
+@import "tailwindcss";
diff --git a/lib/install/application.tailwind.css b/lib/install/application.tailwind.css
deleted file mode 100644
index 8666d2f3..00000000
--- a/lib/install/application.tailwind.css
+++ /dev/null
@@ -1,13 +0,0 @@
-@tailwind base;
-@tailwind components;
-@tailwind utilities;
-
-/*
-
-@layer components {
- .btn-primary {
- @apply py-2 px-4 bg-blue-200;
- }
-}
-
-*/
diff --git a/lib/install/tailwindcss.rb b/lib/install/install_tailwindcss.rb
similarity index 63%
rename from lib/install/tailwindcss.rb
rename to lib/install/install_tailwindcss.rb
index 2c2ba50c..3d471d07 100644
--- a/lib/install/tailwindcss.rb
+++ b/lib/install/install_tailwindcss.rb
@@ -1,19 +1,23 @@
APPLICATION_LAYOUT_PATH = Rails.root.join("app/views/layouts/application.html.erb")
CENTERING_CONTAINER_INSERTION_POINT = /^\s*<%= yield %>/.freeze
+TAILWIND_ASSET_PATH = Rails.root.join("app/assets/tailwind/application.css")
if APPLICATION_LAYOUT_PATH.exist?
- say "Add Tailwindcss include tags and container element in application layout"
- insert_into_file APPLICATION_LAYOUT_PATH.to_s, <<~ERB.indent(4), before: /^\s*<%= stylesheet_link_tag/
- <%= stylesheet_link_tag "tailwind", "inter-font", "data-turbo-track": "reload" %>
- ERB
+ unless File.read(APPLICATION_LAYOUT_PATH).match?(/stylesheet_link_tag :app/)
+ say "Add Tailwindcss include tags in application layout"
+ insert_into_file APPLICATION_LAYOUT_PATH.to_s, <<~ERB.indent(4), before: /^\s*<%= stylesheet_link_tag/
+ <%= stylesheet_link_tag "tailwind", "data-turbo-track": "reload" %>
+ ERB
+ end
+ say "Add Tailwindcss container element in application layout"
if File.open(APPLICATION_LAYOUT_PATH).read =~ /\n\s*<%= yield %>\n\s*<\/body>/
insert_into_file APPLICATION_LAYOUT_PATH.to_s, %(
\n ), before: CENTERING_CONTAINER_INSERTION_POINT
insert_into_file APPLICATION_LAYOUT_PATH.to_s, %(\n ), after: CENTERING_CONTAINER_INSERTION_POINT
end
else
say "Default application.html.erb is missing!", :red
- say %( Add <%= stylesheet_link_tag "tailwind", "inter-font", "data-turbo-track": "reload" %> within the tag in your custom layout.)
+ say %( Add <%= stylesheet_link_tag "tailwind", "data-turbo-track": "reload" %> within the tag in your custom layout.)
end
say "Build into app/assets/builds"
@@ -28,14 +32,9 @@
append_to_file(".gitignore", %(\n/app/assets/builds/*\n!/app/assets/builds/.keep\n))
end
-unless Rails.root.join("config/tailwind.config.js").exist?
- say "Add default config/tailwindcss.config.js"
- copy_file "#{__dir__}/tailwind.config.js", "config/tailwind.config.js"
-end
-
-unless Rails.root.join("app/assets/stylesheets/application.tailwind.css").exist?
- say "Add default app/assets/stylesheets/application.tailwind.css"
- copy_file "#{__dir__}/application.tailwind.css", "app/assets/stylesheets/application.tailwind.css"
+unless TAILWIND_ASSET_PATH.exist?
+ say "Add default #{TAILWIND_ASSET_PATH}"
+ copy_file "#{__dir__}/application.css", TAILWIND_ASSET_PATH
end
if Rails.root.join("Procfile.dev").exist?
diff --git a/lib/install/tailwind.config.js b/lib/install/tailwind.config.js
deleted file mode 100644
index d6ad82c0..00000000
--- a/lib/install/tailwind.config.js
+++ /dev/null
@@ -1,22 +0,0 @@
-const defaultTheme = require('tailwindcss/defaultTheme')
-
-module.exports = {
- content: [
- './public/*.html',
- './app/helpers/**/*.rb',
- './app/javascript/**/*.js',
- './app/views/**/*.{erb,haml,html,slim}'
- ],
- theme: {
- extend: {
- fontFamily: {
- sans: ['Inter var', ...defaultTheme.fontFamily.sans],
- },
- },
- },
- plugins: [
- require('@tailwindcss/forms'),
- require('@tailwindcss/typography'),
- require('@tailwindcss/container-queries'),
- ]
-}
diff --git a/lib/install/upgrade_tailwindcss.rb b/lib/install/upgrade_tailwindcss.rb
new file mode 100644
index 00000000..6266583a
--- /dev/null
+++ b/lib/install/upgrade_tailwindcss.rb
@@ -0,0 +1,63 @@
+TAILWIND_CONFIG_PATH = Rails.root.join("config/tailwind.config.js")
+APPLICATION_LAYOUT_PATH = Rails.root.join("app/views/layouts/application.html.erb")
+POSTCSS_CONFIG_PATH = Rails.root.join("config/postcss.config.js")
+OLD_TAILWIND_ASSET_PATH = Rails.root.join("app/assets/stylesheets/application.tailwind.css")
+TAILWIND_ASSET_PATH = Rails.root.join("app/assets/tailwind/application.css")
+
+unless TAILWIND_CONFIG_PATH.exist?
+ say "Default tailwind.config.js is missing!", :red
+ abort
+end
+
+if File.read(TAILWIND_CONFIG_PATH).match?(/defaultTheme/)
+ say "Removing references to 'defaultTheme' from #{TAILWIND_CONFIG_PATH}"
+ gsub_file TAILWIND_CONFIG_PATH.to_s, /^(.*defaultTheme)/, "// \\1"
+end
+
+if POSTCSS_CONFIG_PATH.exist?
+ say "Moving PostCSS configuration to application root directory"
+ copy_file POSTCSS_CONFIG_PATH, Rails.root.join("postcss.config.js")
+ remove_file POSTCSS_CONFIG_PATH
+end
+
+if APPLICATION_LAYOUT_PATH.exist?
+ if File.read(APPLICATION_LAYOUT_PATH).match?(/"inter-font"/)
+ say "Strip Inter font CSS from application layout"
+ gsub_file APPLICATION_LAYOUT_PATH.to_s, %r{, "inter-font"}, ""
+ else
+ say "Inter font CSS not detected.", :green
+ end
+
+ if File.read(APPLICATION_LAYOUT_PATH).match?(/stylesheet_link_tag :app/) &&
+ File.read(APPLICATION_LAYOUT_PATH).match?(/stylesheet_link_tag "tailwind"/)
+ say "Remove unnecessary stylesheet_link_tag from application layout"
+ gsub_file APPLICATION_LAYOUT_PATH.to_s, %r{^\s*<%= stylesheet_link_tag "tailwind".*%>$}, ""
+ end
+else
+ say "Default application.html.erb is missing!", :red
+ say %( Please check your layouts and remove any "inter-font" stylesheet links.)
+end
+
+if OLD_TAILWIND_ASSET_PATH.exist?
+ say "Moving #{OLD_TAILWIND_ASSET_PATH} to #{TAILWIND_ASSET_PATH}"
+ copy_file OLD_TAILWIND_ASSET_PATH, TAILWIND_ASSET_PATH
+ remove_file OLD_TAILWIND_ASSET_PATH
+end
+
+if system("npx --version")
+ say "Running the upstream Tailwind CSS upgrader"
+ command = Shellwords.join(["npx", "@tailwindcss/upgrade@next", "--force", "--config", TAILWIND_CONFIG_PATH.to_s])
+ success = run(command, abort_on_failure: false)
+ unless success
+ say "The upgrade tool failed!", :red
+ say %( You probably need to update your configuration. Please read the error messages,)
+ say %( and check the Tailwind CSS upgrade guide at https://tailwindcss.com/docs/upgrade-guide.)
+ abort
+ end
+else
+ say "Could not run the Tailwind upgrade tool. Please see https://tailwindcss.com/docs/upgrade-guide for manual instructions.", :red
+ abort
+end
+
+say "Compile initial Tailwind build"
+run "rails tailwindcss:build"
diff --git a/lib/puma/plugin/tailwindcss.rb b/lib/puma/plugin/tailwindcss.rb
index e614d62a..4624d7e8 100644
--- a/lib/puma/plugin/tailwindcss.rb
+++ b/lib/puma/plugin/tailwindcss.rb
@@ -1,4 +1,5 @@
require "puma/plugin"
+require "tailwindcss/commands"
Puma::Plugin.create do
attr_reader :puma_pid, :tailwind_pid, :log_writer
@@ -11,8 +12,11 @@ def start(launcher)
# Using IO.popen(command, 'r+') will avoid watch_command read from $stdin.
# If we use system(*command) instead, IRB and Debug can't read from $stdin
# correctly bacause some keystrokes will be taken by watch_command.
- IO.popen(Tailwindcss::Commands.watch_command, 'r+') do |io|
- IO.copy_stream(io, $stdout)
+ begin
+ IO.popen(Tailwindcss::Commands.watch_command, 'r+') do |io|
+ IO.copy_stream(io, $stdout)
+ end
+ rescue Interrupt
end
end
diff --git a/lib/tailwindcss/commands.rb b/lib/tailwindcss/commands.rb
index 26c5178a..bef42fd2 100644
--- a/lib/tailwindcss/commands.rb
+++ b/lib/tailwindcss/commands.rb
@@ -4,16 +4,18 @@ module Tailwindcss
module Commands
class << self
def compile_command(debug: false, **kwargs)
+ debug = ENV["TAILWINDCSS_DEBUG"].present? if ENV.key?("TAILWINDCSS_DEBUG")
+ rails_root = defined?(Rails) ? Rails.root : Pathname.new(Dir.pwd)
+
command = [
Tailwindcss::Ruby.executable(**kwargs),
- "-i", Rails.root.join("app/assets/stylesheets/application.tailwind.css").to_s,
- "-o", Rails.root.join("app/assets/builds/tailwind.css").to_s,
- "-c", Rails.root.join("config/tailwind.config.js").to_s,
+ "-i", rails_root.join("app/assets/tailwind/application.css").to_s,
+ "-o", rails_root.join("app/assets/builds/tailwind.css").to_s,
]
command << "--minify" unless (debug || rails_css_compressor?)
- postcss_path = Rails.root.join("config/postcss.config.js")
+ postcss_path = rails_root.join("postcss.config.js")
command += ["--postcss", postcss_path.to_s] if File.exist?(postcss_path)
command
diff --git a/lib/tailwindcss/engine.rb b/lib/tailwindcss/engine.rb
index 4b9b9fdc..7b88c5f1 100644
--- a/lib/tailwindcss/engine.rb
+++ b/lib/tailwindcss/engine.rb
@@ -2,14 +2,16 @@
module Tailwindcss
class Engine < ::Rails::Engine
- initializer "tailwindcss.assets" do
- Rails.application.config.assets.precompile += %w( inter-font.css )
- end
-
initializer "tailwindcss.disable_generator_stylesheets" do
Rails.application.config.generators.stylesheets = false
end
+ initializer "tailwindcss.exclude_asset_path", before: "propshaft.append_assets_path" do
+ if Rails.application.config.assets.excluded_paths # the app may not be using Propshaft
+ Rails.application.config.assets.excluded_paths << Rails.root.join("app/assets/tailwind")
+ end
+ end
+
config.app_generators do |g|
g.template_engine :tailwindcss
end
diff --git a/lib/tailwindcss/version.rb b/lib/tailwindcss/version.rb
index 2bc244fc..a172caa0 100644
--- a/lib/tailwindcss/version.rb
+++ b/lib/tailwindcss/version.rb
@@ -1,3 +1,3 @@
module Tailwindcss
- VERSION = "3.0.0"
+ VERSION = "4.2.0"
end
diff --git a/lib/tasks/install.rake b/lib/tasks/install.rake
index 2a571cad..f94ec2a1 100644
--- a/lib/tasks/install.rake
+++ b/lib/tasks/install.rake
@@ -1,6 +1,6 @@
namespace :tailwindcss do
desc "Install Tailwind CSS into the app"
task :install do
- system "#{RbConfig.ruby} ./bin/rails app:template LOCATION=#{File.expand_path("../install/tailwindcss.rb", __dir__)}"
+ system "#{RbConfig.ruby} ./bin/rails app:template LOCATION=#{File.expand_path("../install/install_tailwindcss.rb", __dir__)}"
end
end
diff --git a/lib/tasks/upgrade.rake b/lib/tasks/upgrade.rake
new file mode 100644
index 00000000..05047523
--- /dev/null
+++ b/lib/tasks/upgrade.rake
@@ -0,0 +1,6 @@
+namespace :tailwindcss do
+ desc "Upgrade app from Tailwind CSS v3 to v4"
+ task :upgrade do
+ system "#{RbConfig.ruby} ./bin/rails app:template LOCATION=#{File.expand_path("../install/upgrade_tailwindcss.rb", __dir__)}"
+ end
+end
diff --git a/tailwindcss-rails.gemspec b/tailwindcss-rails.gemspec
index 8d893029..45c19c28 100644
--- a/tailwindcss-rails.gemspec
+++ b/tailwindcss-rails.gemspec
@@ -19,5 +19,19 @@ Gem::Specification.new do |spec|
spec.files = Dir["{app,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.md"]
spec.add_dependency "railties", ">= 7.0.0"
- spec.add_dependency "tailwindcss-ruby"
+ spec.add_dependency "tailwindcss-ruby", "~> 4.0"
+
+ # TODO: remove this after a suitable period of time after the v4.0.0 release.
+ spec.post_install_message = <<~TEXT
+ == Upgrading to Tailwind CSS v4 ==
+
+ If you are upgrading to tailwindcss-rails 4.x, please read the upgrade guide at:
+
+ https://github.com/rails/tailwindcss-rails/blob/main/README.md#upgrading-your-application-from-tailwind-v3-to-v4
+
+ If you're not ready to upgrade yet, please pin to version 3 in your Gemfile:
+
+ gem "tailwindcss-rails", "~> 3.3.1"
+
+ TEXT
end
diff --git a/test/integration/user_journey_test.sh b/test/integration/user_install_test.sh
similarity index 60%
rename from test/integration/user_journey_test.sh
rename to test/integration/user_install_test.sh
index 00818f2d..6f4ca301 100755
--- a/test/integration/user_journey_test.sh
+++ b/test/integration/user_install_test.sh
@@ -7,9 +7,10 @@ set -eux
# set up dependencies
rm -f Gemfile.lock
-bundle remove actionmailer
+bundle remove actionmailer || true
+bundle remove rails || true
bundle add rails --skip-install ${RAILSOPTS:-}
-bundle install
+bundle install --prefer-local
# do our work a directory with spaces in the name (#176, #184)
rm -rf "My Workspace"
@@ -18,24 +19,26 @@ pushd "My Workspace"
# create a rails app
bundle exec rails -v
-bundle exec rails new test-app --skip-bundle
-pushd test-app
+bundle exec rails new test-install --skip-bundle
+pushd test-install
# make sure to use the same version of rails (e.g., install from git source if necessary)
bundle remove rails --skip-install
bundle add rails --skip-install ${RAILSOPTS:-}
# use the tailwindcss-rails under test
-bundle add tailwindcss-rails --path="../.."
-bundle install
-bundle show --paths
+bundle add tailwindcss-rails --skip-install --path="../.."
+bundle add tailwindcss-ruby --skip-install ${TAILWINDCSSOPTS:-}
+bundle install --prefer-local
+bundle show --paths | fgrep tailwind
bundle binstubs --all
# install tailwindcss
bin/rails tailwindcss:install
# TEST: tailwind was installed correctly
-grep -q tailwind app/views/layouts/application.html.erb
+grep -q "
> Rakefile
@@ -44,6 +47,10 @@ task :still_here do
end
EOF
+cat >> app/assets/tailwind/application.css < "Rails 8.0.0.beta" ]] ; then
@@ -54,4 +61,13 @@ fi
# TEST: presence of the generated file
bin/rails generate scaffold post title:string body:text published:boolean
-grep -q "Show this post" app/views/posts/index.html.erb
+grep -q "Show" app/views/posts/index.html.erb
+
+# TEST: contents of the css file
+bin/rails tailwindcss:build[verbose]
+grep -q "py-2" app/assets/builds/tailwind.css
+
+# TEST: contents include application.css directives
+grep -q "#abc12399" app/assets/builds/tailwind.css
+
+echo "OK"
diff --git a/test/integration/user_upgrade_test.sh b/test/integration/user_upgrade_test.sh
new file mode 100755
index 00000000..d6017efc
--- /dev/null
+++ b/test/integration/user_upgrade_test.sh
@@ -0,0 +1,90 @@
+#! /usr/bin/env bash
+# reproduce the documented user journey for installing and running tailwindcss-rails
+# this is run in the CI pipeline, non-zero exit code indicates a failure
+
+set -o pipefail
+set -eux
+
+# set up dependencies
+rm -f Gemfile.lock
+bundle remove actionmailer || true
+bundle remove rails || true
+bundle add rails --skip-install ${RAILSOPTS:-}
+bundle install --prefer-local
+
+# do our work a directory with spaces in the name (#176, #184)
+rm -rf "My Workspace"
+mkdir "My Workspace"
+pushd "My Workspace"
+
+# create a rails app
+bundle exec rails -v
+bundle exec rails new test-upgrade --skip-bundle
+pushd test-upgrade
+
+# make sure to use the same version of rails (e.g., install from git source if necessary)
+bundle remove rails --skip-install
+bundle add rails --skip-install ${RAILSOPTS:-}
+
+# set up app with tailwindcss-rails v3 and tailwindcss-ruby v3
+bundle add tailwindcss-rails --skip-install --version 3.3.0
+bundle add tailwindcss-ruby --skip-install --version 3.4.17
+bundle install --prefer-local
+bundle show --paths | fgrep tailwind
+bundle binstubs --all
+
+# install tailwindcss
+bin/rails tailwindcss:install
+grep -q inter-font app/views/layouts/application.html.erb
+
+if [[ $(rails -v) > "Rails 8.0.0.beta" ]] ; then
+ # install auth templates
+ bin/rails generate authentication
+ grep -q PasswordsController app/controllers/passwords_controller.rb
+fi
+
+# install scaffold templates
+bin/rails generate scaffold post title:string body:text published:boolean
+grep -q "Show this post" app/views/posts/index.html.erb
+
+# upgrade time!
+bundle remove tailwindcss-rails --skip-install
+bundle remove tailwindcss-ruby --skip-install
+
+bundle add tailwindcss-rails --skip-install --path="../.."
+bundle add tailwindcss-ruby --skip-install ${TAILWINDCSSOPTS:---version 4.0.0}
+
+bundle install --prefer-local
+bundle show --paths | fgrep tailwind
+bundle binstubs --all
+
+# create a postcss file
+cat < config/postcss.config.js
+module.exports = {
+ plugins: {
+ autoprefixer: {},
+ },
+}
+EOF
+
+bin/rails tailwindcss:upgrade
+
+# TEST: removal of inter-font CSS
+if grep -q inter-font app/views/layouts/application.html.erb ; then
+ echo "FAIL: inter-font CSS not removed"
+ exit 1
+fi
+
+# TEST: moving the postcss file
+test ! -a config/postcss.config.js
+test -a postcss.config.js
+
+# TEST: moving application.tailwind.css
+test ! -a app/assets/stylesheets/application.tailwind.css
+test -a app/assets/tailwind/application.css
+
+# generate CSS
+bin/rails tailwindcss:build[verbose]
+grep -q "py-2" app/assets/builds/tailwind.css
+
+echo "OK"
diff --git a/test/lib/tailwindcss/commands_test.rb b/test/lib/tailwindcss/commands_test.rb
index a273e45e..d09481a4 100644
--- a/test/lib/tailwindcss/commands_test.rb
+++ b/test/lib/tailwindcss/commands_test.rb
@@ -10,6 +10,16 @@ def setup
end
test ".compile_command" do
+ Rails.stub(:root, File) do # Rails.root won't work in this test suite
+ actual = Tailwindcss::Commands.compile_command
+ assert_kind_of(Array, actual)
+ assert_equal(executable, actual.first)
+ assert_includes(actual, "-i")
+ assert_includes(actual, "-o")
+ end
+ end
+
+ test ".compile_command debug flag" do
Rails.stub(:root, File) do # Rails.root won't work in this test suite
actual = Tailwindcss::Commands.compile_command
assert_kind_of(Array, actual)
@@ -23,6 +33,32 @@ def setup
end
end
+ test ".compile_command debug environment variable" do
+ begin
+ Rails.stub(:root, File) do # Rails.root won't work in this test suite
+ ENV["TAILWINDCSS_DEBUG"] = ""
+ actual = Tailwindcss::Commands.compile_command
+ assert_kind_of(Array, actual)
+ assert_includes(actual, "--minify")
+
+ actual = Tailwindcss::Commands.compile_command(debug: true)
+ assert_kind_of(Array, actual)
+ assert_includes(actual, "--minify")
+
+ ENV["TAILWINDCSS_DEBUG"] = "any non-blank value"
+ actual = Tailwindcss::Commands.compile_command
+ assert_kind_of(Array, actual)
+ refute_includes(actual, "--minify")
+
+ actual = Tailwindcss::Commands.compile_command(debug: true)
+ assert_kind_of(Array, actual)
+ refute_includes(actual, "--minify")
+ end
+ ensure
+ ENV.delete('TAILWINDCSS_DEBUG')
+ end
+ end
+
test ".compile_command when Rails compression is on" do
Rails.stub(:root, File) do # Rails.root won't work in this test suite
Tailwindcss::Commands.stub(:rails_css_compressor?, true) do
@@ -47,8 +83,7 @@ def setup
assert_equal(executable, actual.first)
refute_includes(actual, "--postcss")
- config_file = Rails.root.join("config/postcss.config.js")
- FileUtils.mkdir_p(Rails.root.join("config"))
+ config_file = Rails.root.join("postcss.config.js")
FileUtils.touch(config_file)
actual = Tailwindcss::Commands.compile_command
assert_kind_of(Array, actual)