SwiftyTailwind is a Swift package that allows you to download and run the Tailwind CSS CLI directly from a Swift project.
It is commonly used with server-side Swift frameworks such as Vapor to generate and watch Tailwind CSS styles during development.
This repository is a maintained fork adapted for modern Vapor workflows.
Add SwiftyTailwind as a dependency in your project's Package.swift:
.package(url: "https://github.com/Bilalyyy/SwiftyTailwind.git", from: "0.5.0")Then create an instance of SwiftyTailwind:
let tailwind = SwiftyTailwind(version: .latest, directory: "./cache")If you don't pass any argument, it defaults to the latest version in the system's default temporary directory. If you work in a team, we recommend fixing the version to minimize non-determinism across environments.
You can create a tailwind.config.js configuration file by running the initialize function on the SwiftyTailwind instance:
try await tailwind.initialize()Check out all the available options in the documentation.
To run Tailwind against a project, you can use the run function:
try await subject.run(input: inputCSSPath, output: outputCSSPath, options: .content("views/**/*.html"))If you'd like Tailwind to keep watching for file changes, you can pass the .watch option:
try await subject.run(input: inputCSSPath,
output: outputCSSPath,
options: .watch, .content("views/**/*.html"))Check out all the available options in the documentation.
You can integrate this with Vapor by setting up a tailwind.swift:
import SwiftyTailwind
import TSCBasic
import Vapor
func tailwind(_ app: Application) async throws {
let resourcesDirectory = try AbsolutePath(validating: app.directory.resourcesDirectory)
let publicDirectory = try AbsolutePath(validating: app.directory.publicDirectory)
let tailwind = SwiftyTailwind()
try await tailwind.run(
input: .init(validating: "Styles/app.css", relativeTo: resourcesDirectory),
output: .init(validating: "styles/app.generated.css", relativeTo: publicDirectory),
options: .content("\(app.directory.viewsDirectory)**/*.leaf")
)
}Then in configure.swift:
try await tailwind(app)
app.middleware.use(FileMiddleware(publicDirectory: app.directory.publicDirectory))And in your index.leaf:
<link rel="stylesheet" href="/styles/app.generated.css" />It can be desirable for Tailwind to watch and rebuild changes without restarting the Vapor server. It is also best to restrict this behavior for development only.
You can integrate this behavior by setting up a tailwind.swift:
#if DEBUG
import SwiftyTailwind
import TSCBasic
import Vapor
func runTailwind(_ app: Application) async throws {
let resourcesDirectory = try AbsolutePath(validating: app.directory.resourcesDirectory)
let publicDirectory = try AbsolutePath(validating: app.directory.publicDirectory)
let tailwind = SwiftyTailwind()
async let runTailwind: () = tailwind.run(
input: .init(validating: "Styles/app.css", relativeTo: resourcesDirectory),
output: .init(validating: "styles/app.generated.css", relativeTo: publicDirectory),
options: .watch, .content("\(app.directory.viewsDirectory)**/*.leaf"))
return try await runTailwind
}
#endifand then in entrypoint.swift, replace try await app.execute() with:
#if DEBUG
if (env.arguments.contains { arg in arg == "migrate" }) {
try await app.execute()
} else {
async let runApp: () = try await app.execute()
_ = await [try runTailwind(app), try await runApp]
}
#else
try await app.execute()
#endifThe check for migrate in the arguments will ensure that it doesn't run when doing migrations in development.
Additionally, it may be a good idea to setup a script to minify the CSS before deploying to production.
This project is based on the original SwiftyTailwind package by Tuist and its contributors.
This fork exists to maintain and adapt the package for modern Vapor projects.