Skip to content

Instantly share code, notes, and snippets.

@marshalhayes
Last active July 21, 2025 06:23
Show Gist options
  • Save marshalhayes/8bfb1fe2ea519727b4d74bfb0a21e672 to your computer and use it in GitHub Desktop.
Save marshalhayes/8bfb1fe2ea519727b4d74bfb0a21e672 to your computer and use it in GitHub Desktop.
Using Tailwind the right way for .NET
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- This file exposes the following parameters -->
<!-- TailwindVersion: The version of the Tailwind Standalone CLI to download. -->
<!-- TailwindDownloadPath: The path to where to download the Tailwind Standalone CLI. This property is optional, and defaults to %LOCALAPPDATA% on Windows, and $XDG_CACHE_HOME on Linux and MacOS. -->
<!-- TailwindInputStyleSheetPath: The path to the input stylesheet. -->
<!-- TailwindOutputStyleSheetPath: The path to the output stylesheet. -->
<!-- TailwindOptimizeOutputStyleSheet: Whether to optimize the output stylesheet. This property is optional, and defaults to false. -->
<!-- TailwindMinifyOutputStyleSheet: Whether to minify the output stylesheet. This property is optional, and defaults to false when Configuration is Debug, and true when Configuration is Release. -->
<!-- TailwindDownloadUrl: The URL to the Tailwind Standalone CLI. This property is optional, and defaults to downloading the specified version from GitHub. -->
<!-- To override these properties, create a PropertyGroup in the csproj file -->
<!-- For example: -->
<!-- <PropertyGroup> -->
<!-- <TailwindVersion>v4.0.14</TailwindVersion> -->
<!-- <TailwindInputStyleSheetPath>Styles/main.css</TailwindVersion> -->
<!-- <TailwindOutputStyleSheetPath>wwwroot/main.css</TailwindVersion> -->
<!-- </PropertyGroup -->
<PropertyGroup>
<TailwindOptimizeOutputStyleSheet Condition="'$(TailwindOptimizeOutputStyleSheet)' == ''">false</TailwindOptimizeOutputStyleSheet>
<TailwindMinifyOutputStyleSheet Condition="$(TailwindMinifyOutputStyleSheet) == '' And '$(Configuration)' == 'Debug'">false</TailwindMinifyOutputStyleSheet>
<TailwindMinifyOutputStyleSheet Condition="$(TailwindMinifyOutputStyleSheet) == '' And '$(Configuration)' == 'Release'">true</TailwindMinifyOutputStyleSheet>
<!-- The path to where Tailwind should be downloaded to -->
<!-- This should be a path that is writable by the current user, as well as one that is accessible in CI/CD pipelines -->
<!-- By default, this is set to the local app data folder on Windows, and $XDG_CACHE_HOME on Linux and MacOS -->
<!-- On Linux and MacOS, use $XDG_CACHE_HOME or $HOME/.cache -->
<TailwindDownloadPath Condition="'$(TailwindDownloadPath)' == '' And ($([System.OperatingSystem]::IsLinux()) Or $([System.OperatingSystem]::IsMacOS()))">$([MSBuild]::ValueOrDefault($([System.Environment]::GetEnvironmentVariable('XDG_CONFIG_HOME')), $([System.IO.Path]::Combine($([System.Environment]::GetEnvironmentVariable('HOME')), '.cache'))))</TailwindDownloadPath>
<!-- On Windows, use local app data (%LOCALAPPDATA%) -->
<TailwindDownloadPath Condition="'$(TailwindDownloadPath)' == '' And $([System.OperatingSystem]::IsWindows())">$(LOCALAPPDATA)</TailwindDownloadPath>
</PropertyGroup>
<!-- Validate the parameters before download or building -->
<Target Name="ValidateParameters" BeforeTargets="DownloadTailwind; Tailwind">
<!-- Ensure the version is specified -->
<Error Condition="'$(TailwindVersion)' == ''" Text="Tailwind version not specified. Please specify the version. For example: &lt;PropertyGroup&gt;&lt;TailwindVersion&gt;v4.0.14&lt;/TailwindVersion&gt;&lt;/PropertyGroup&gt;"/>
<!-- Ensure the input stylesheet path is specified & the file exists -->
<Error Condition="'$(TailwindInputStyleSheetPath)' == ''" Text="Tailwind input stylesheet not specified. Please specify the path to the input stylesheet in the csproj file. For example: &lt;PropertyGroup&gt;&lt;TailwindInputStyleSheetPath&gt;Styles/main.css&lt;/TailwindInputStyleSheetPath&gt;&lt;/PropertyGroup&gt;"/>
<Error Condition="!Exists('$(TailwindInputStyleSheetPath)')" Text="Tailwind input stylesheet '$(TailwindInputStyleSheetPath)' does not exist. Please specify a path to a stylesheet. For example: &lt;PropertyGroup&gt;&lt;TailwindInputStyleSheetPath&gt;Styles/main.css&lt;/TailwindInputStyleSheetPath&gt;&lt;/PropertyGroup&gt;"/>
<!-- Ensure the output stylesheet path is specified -->
<Error Condition="'$(TailwindOutputStyleSheetPath)' == ''" Text="Tailwind output stylesheet not specified. Please specify the path to the output stylesheet in the csproj file. For example: &lt;PropertyGroup&gt;&lt;TailwindOutputStyleSheetPath&gt;Styles/main.css&lt;/TailwindOutputStyleSheetPath>&lt;/PropertyGroup&gt;"/>
<!-- Ensure the download path is specified -->
<Error Condition="'$(TailwindDownloadPath)' == ''" Text="Tailwind download path not specified. Please specify the download path in the csproj file. For example: &lt;PropertyGroup&gt;&lt;TailwindDownloadPath&gt;/tmp&lt;/TailwindDownloadPath&gt;&lt;/PropertyGroup&gt;"/>
</Target>
<!-- This line supports hot reload by instructing dotnet watch to be aware of modifications to the input stylesheet -->
<ItemGroup Condition="Exists('$(TailwindInputStyleSheetPath)')">
<Watch Include="$(TailwindInputStyleSheetPath)"/>
</ItemGroup>
<Target Name="DownloadTailwind">
<PropertyGroup>
<!-- Determine which version of Tailwind to use based on the current OS & architecture -->
<TailwindReleaseName Condition="$([System.OperatingSystem]::IsLinux()) And $([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture) == X64">tailwindcss-linux-x64</TailwindReleaseName>
<TailwindReleaseName Condition="$([System.OperatingSystem]::IsLinux()) And $([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture) == Armv7">tailwindcss-linux-armv7</TailwindReleaseName>
<TailwindReleaseName Condition="$([System.OperatingSystem]::IsMacOS()) And $([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture) == X64">tailwindcss-macos-x64</TailwindReleaseName>
<TailwindReleaseName Condition="$([System.OperatingSystem]::IsMacOS()) And $([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture) == Arm64">tailwindcss-macos-arm64</TailwindReleaseName>
<TailwindReleaseName Condition="$([System.OperatingSystem]::IsWindows()) And $([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture) == X64">tailwindcss-windows-x64.exe</TailwindReleaseName>
<TailwindReleaseName Condition="$([System.OperatingSystem]::IsWindows()) And $([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture) == Arm64">tailwindcss-windows-arm64.exe</TailwindReleaseName>
<TailwindDownloadUrl Condition="'$(TailwindDownloadUrl)' == '' And $(TailwindVersion) != 'latest'">https://github.com/tailwindlabs/tailwindcss/releases/download/$(TailwindVersion)/$(TailwindReleaseName)</TailwindDownloadUrl>
<TailwindDownloadUrl Condition="'$(TailwindDownloadUrl)' == '' And $(TailwindVersion) == 'latest'">https://github.com/tailwindlabs/tailwindcss/releases/latest/download/$(TailwindReleaseName)</TailwindDownloadUrl>
</PropertyGroup>
<!-- Download the file -->
<DownloadFile DestinationFolder="$([System.IO.Path]::Combine('$(TailwindDownloadPath)', 'Tailwind', '$(TailwindVersion)'))"
DestinationFileName="$(TailwindReleaseName)"
SourceUrl="$(TailwindDownloadUrl)"
SkipUnchangedFiles="true"
Retries="3">
<Output TaskParameter="DownloadedFile" PropertyName="TailwindCliPath"/>
</DownloadFile>
<!-- On unix systems, make the file executable -->
<Exec Condition="Exists('$(TailwindCliPath)') And ($([System.OperatingSystem]::IsLinux()) Or $([System.OperatingSystem]::IsMacOS()))" Command="chmod +x '$(TailwindCliPath)'"/>
</Target>
<!-- When building the project, run the Tailwind CLI -->
<!-- This target can also be executed manually. For example, with dotnet watch: `dotnet watch msbuild /t:Tailwind` -->
<!-- In order to use hot reload, run both `dotnet watch run` and `dotnet watch msbuild /t:Tailwind` -->
<Target Name="Tailwind" DependsOnTargets="DownloadTailwind" BeforeTargets="Build">
<PropertyGroup>
<!-- Normalize the paths provided -->
<TailwindCliPath>$([MSBuild]::NormalizePath('$(TailwindCliPath)'))</TailwindCliPath>
<TailwindInputStyleSheetPath>$([MSBuild]::NormalizePath('$(TailwindInputStyleSheetPath)'))</TailwindInputStyleSheetPath>
<TailwindOutputStyleSheetPath>$([MSBuild]::NormalizePath('$(TailwindOutputStyleSheetPath)'))</TailwindOutputStyleSheetPath>
<TailwindBuildCommand>"$(TailwindCliPath)" -i "$(TailwindInputStyleSheetPath)" -o "$(TailwindOutputStyleSheetPath)"</TailwindBuildCommand>
<!-- Add optimize flag if specified -->
<TailwindBuildCommand Condition="'$(TailwindOptimizeOutputStyleSheet)' == 'true'">$(TailwindBuildCommand) --optimize</TailwindBuildCommand>
<!-- Add minify flag if specified -->
<TailwindBuildCommand Condition="'$(TailwindMinifyOutputStyleSheet)' == 'true'">$(TailwindBuildCommand) --minify</TailwindBuildCommand>
</PropertyGroup>
<Exec Command="$(TailwindBuildCommand)"/>
</Target>
</Project>
@marshalhayes
Copy link
Author

marshalhayes commented Mar 16, 2025

If you found this useful, leave a ๐Ÿ‘ below!

@jonanderson10
Copy link

jonanderson10 commented Mar 16, 2025

๐Ÿ‘ thanks!

@spicyramen26
Copy link

Im the guy with the SpecialFolder problem on msbuild. This is how it currently looks in my code:
$([System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::LocalApplicationData))

Maybe you can try building it on your system using windows and not WSL? I'm not really sure what the problem is.

@marshalhayes
Copy link
Author

marshalhayes commented Mar 17, 2025

Absolutely!

This should work for you on Windows:

<!-- Change the download path to this  -->
<TailwindDownloadPath Condition="'$(TailwindDownloadPath)' == '' And $([System.OperatingSystem]::IsWindows())">$(LOCALAPPDATA)</TailwindDownloadPath>

<!-- Remove the quotes from the build command -->
<TailwindBuildCommand>$(TailwindCliPath) -i $(TailwindInputStyleSheetPath) -o $(TailwindOutputStyleSheetPath) --cwd $(ProjectDir)</TailwindBuildCommand>

Thank you for catching this!

Removing the quotes on the build command is slightly worrying. I'll need to look for a more permanent solution there. Until I find a permanent solution, you won't be able to use a path that includes a space anywhere.

@spicyramen26
Copy link

I'm still getting the same error unfortunately, I also tried building your mudblazor-tailwind template and I get the same error.

@marshalhayes
Copy link
Author

Just to make sure I understand, you changed the TailwindDownloadPath here to the below?

<TailwindDownloadPath Condition="'$(TailwindDownloadPath)' == '' And $([System.OperatingSystem]::IsWindows())">$(LOCALAPPDATA)</TailwindDownloadPath>

And similarly, the TailwindBuildCommand here:

<TailwindBuildCommand>$(TailwindCliPath) -i $(TailwindInputStyleSheetPath) -o $(TailwindOutputStyleSheetPath) --cwd $(ProjectDir)</TailwindBuildCommand>

@spicyramen26
Copy link

Yes I copy pasted your code and changed both, making sure that I changed the TailwindDownloadPath for windows and not linux/mac. I made sure to check that msbuild works on my mvc projects and it does.

@marshalhayes
Copy link
Author

I've provided a revised version here. I tried it myself on Windows & it works.

Let me know it if works for you as well!

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