Skip to content

feat(nuget): ship Tailwind safelist for NuGet consumers, bump to 4.3.1#20

Merged
Shewart merged 19 commits into
mainfrom
feat/nuget-css-pipeline
Jul 1, 2026
Merged

feat(nuget): ship Tailwind safelist for NuGet consumers, bump to 4.3.1#20
Shewart merged 19 commits into
mainfrom
feat/nuget-css-pipeline

Conversation

@Shewart

@Shewart Shewart commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes the documented gap for NuGet consumers: dotnet add package ShellUI.Components now produces styled components when the consumer's project has Tailwind set up. The package ships a shellui-classes.txt safelist that Tailwind scans at build time via one @source directive — tree-shaken, only classes actually used end up in compiled CSS. Also bumps Tailwind to 4.3.1.

Changes

  • Tailwind 4.1.18 → 4.3.1 centralized in new TailwindConstants class (single source of truth — downloader, npm install, config default, docs all derive from it)
  • New tools/ShellUI.SafelistGenerator console tool — scans .razor files, extracts every Tailwind utility class (handles arbitrary values, modifiers, opacity, important), emits sorted-unique list
  • src/ShellUI.Components/wwwroot/shellui-classes.txt — 307 classes from 147 razor files. Ships as a NuGet contentFile; restore copies it into the consumer's wwwroot/ where Tailwind's @source directive can scan it
  • SafelistDriftTests — fails CI with a precise diff if anyone updates a component but forgets to regenerate
  • CI smoke step asserts dotnet add package lands the safelist + a sanity-check grep
  • README rewrite — honest "two install paths" (NuGet + Tailwind vs CLI) replacing the previous "CLI is the only supported path" warning

Verification

  • 52/52 unit tests passing on .NET 10
  • dotnet pack produces nupkg with safelist at contentFiles/any/net10.0/wwwroot/shellui-classes.txt
  • Safelist contains modifiers (hover:, dark:, focus-visible:), arbitrary values (data-[state=open]:bg-accent, ease-[cubic-bezier(...)]), opacity (bg-background/80), important (!flex)

Shewart added 19 commits June 21, 2026 22:17
…installation

- Introduced `SafelistDriftTests` to ensure the safelist matches generated Tailwind classes from Razor components, preventing missing styles in NuGet packages.
- Enhanced CI workflow to verify the safelist is correctly included in the NuGet package and contains essential Tailwind classes after installation.
- Updated project references in the test project to include the `ShellUI.SafelistGenerator` for safelist generation.
- Introduced a new CI job to validate that the safelist file `shellui-classes.txt` is correctly included in the NuGet package after installation.
- The job checks for the presence of the safelist and verifies it contains essential Tailwind classes, ensuring proper integration for consumers using the package.
- Introduced a new file `shellui-classes.txt` containing a comprehensive list of Tailwind CSS classes to be used in the project.
- This addition ensures that essential styles are available for components, enhancing the UI consistency and functionality.
- Replaced hardcoded Tailwind CSS version with constants from TailwindConstants for better maintainability.
- Updated the installation command to use dynamic versioning, ensuring compatibility with future updates.
- Enhanced console output to reflect the installed version dynamically, improving user feedback during installation.
- Updated all references to Tailwind CSS from v4.1.18 to v4.3.1 in README.md and src/ShellUI.Components/README.md.
- Adjusted installation instructions to reflect the new version and ensure compatibility with existing setups.
- Enhanced clarity on the two installation paths for users, emphasizing the benefits of each approach.
- Added a using directive for ShellUI.Core.
- Updated TailwindConfig to use TailwindConstants.Version instead of a hardcoded string for the Tailwind CSS version, improving maintainability.
- Introduced a new tool in `ShellUI.SafelistGenerator` to scan .razor files for Tailwind utility classes and generate a unique safelist.
- Added `TailwindConstants` to centralize the Tailwind CSS versioning, ensuring consistency across the project.
- This addition enhances the maintainability and usability of Tailwind CSS within the ShellUI framework.
- Introduced a new project `ShellUI.SafelistGenerator` within the `tools` folder to facilitate the generation of Tailwind CSS safelists.
- Updated the solution file to include the new project, enhancing the overall tooling for Tailwind CSS integration in the ShellUI framework.
- Updated references to Tailwind CSS from v4.1.18 to v4.3.1 in COMPONENT_ROADMAP.md and PROJECT_STATUS.md.
- Ensured consistency across documentation regarding the latest Tailwind CSS version, enhancing clarity for users.
- Updated the CI configuration to use a dedicated NuGet.config that restricts package sources to a local feed, preventing fallback to older versions from nuget.org.
- Added checks to ensure the local feed exists and contains the expected package before proceeding with the installation.
- Improved validation of the safelist file `shellui-classes.txt` to ensure it is correctly generated and contains essential Tailwind classes after the NuGet restore process.
- Introduced `ShellUI.Components.targets` to facilitate the automatic copying of the Tailwind safelist file `shellui-classes.txt` into the consumer's `wwwroot/` during the build process.
- Updated the project file to include the new targets and safelist file, ensuring seamless integration for users leveraging Tailwind CSS within the ShellUI framework.
…oject build

- Modified the CI configuration to build the project before checking for the presence of the `shellui-classes.txt` safelist file in the `wwwroot/` directory.
- Added error handling to provide clearer feedback if the safelist file is not found, ensuring better diagnostics for users integrating Tailwind CSS.
- Added a new test to verify that the `build/ShellUI.Components.targets` file contains the same classes as the safelist, ensuring consistency for NuGet consumers.
- Introduced a constant for the regeneration command to improve clarity and maintainability in error messages.
- Updated existing tests to utilize the new constant for better readability.
- Updated `ShellUI.Components.targets` to embed the Tailwind safelist inline, eliminating the need for a separate `shellui-classes.txt` file.
- Modified project file to reflect the changes, ensuring seamless integration for consumers without dependency on external files.
- Enhanced documentation within the targets file to clarify the auto-generation process and usage.
- Incremented the Tailwind CSS version from 4.3.1 to 4.3.2 in the TailwindConstants class to reflect the latest release, ensuring consistency across the project.
- Updated the `ShellUI.SafelistGenerator` to generate both a sorted list of Tailwind utility classes and a build targets file for NuGet integration.
- Modified command-line arguments to accept separate output paths for the text file and targets file.
- Improved documentation within the code to clarify usage and the purpose of generated files.
- Introduced a new test in `SafelistDriftTests` to validate that the generated `ShellUI.Components.targets` file is well-formed XML, ensuring compatibility with strict MSBuild parsers.
- Updated the `ShellUI.SafelistGenerator` to include comments that avoid invalid XML sequences, enhancing robustness in generated output.
- Improved documentation within the targets file to clarify the auto-generation process and potential pitfalls.
- Updated comments in `ShellUI.Components.targets` to enhance clarity regarding the auto-generation process and the embedding of the Tailwind safelist.
- Simplified language to improve readability and understanding for future maintainers.
…iance

- Enhanced comments in `Program.cs` to clarify the auto-generation process and avoid invalid XML sequences, specifically the `--` sequence in XML comments.
- Simplified language for better readability and understanding, ensuring future maintainers can easily grasp the purpose and usage of the generated targets file.
Copilot AI review requested due to automatic review settings July 1, 2026 21:33
@Shewart Shewart merged commit 3f57d8b into main Jul 1, 2026
2 checks passed
@Shewart Shewart deleted the feat/nuget-css-pipeline branch July 1, 2026 21:35

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR aims to make the NuGet-only consumption path for ShellUI.Components produce correctly styled components by shipping a Tailwind safelist that can be scanned during a consumer build, while also centralizing/bumping the Tailwind version and adding CI/test coverage to prevent safelist drift.

Changes:

  • Adds a SafelistGenerator tool and commits generated artifacts (shellui-classes.txt and an auto-imported .targets) to support Tailwind scanning for NuGet consumers.
  • Centralizes the Tailwind version into TailwindConstants and wires CLI/config defaults/docs to that single source of truth.
  • Adds drift tests + a CI NuGet-smoke step to ensure the safelist continues to ship and stays in sync.

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
tools/ShellUI.SafelistGenerator/ShellUI.SafelistGenerator.csproj New console tool project for generating the safelist + targets artifact.
tools/ShellUI.SafelistGenerator/Program.cs Implements safelist extraction + .targets generation.
src/ShellUI.Core/TailwindConstants.cs Introduces centralized Tailwind version constants.
src/ShellUI.Core/Models/ShellUIConfig.cs Uses TailwindConstants.Version as the default Tailwind version.
src/ShellUI.Components/wwwroot/shellui-classes.txt Committed safelist text file generated from component sources.
src/ShellUI.Components/ShellUI.Components.csproj Packs build/ShellUI.Components.targets into the NuGet package.
src/ShellUI.Components/README.md Documents the new “NuGet + existing Tailwind” install path.
src/ShellUI.Components/build/ShellUI.Components.targets Auto-imported MSBuild target that writes the safelist into a consumer’s wwwroot/ at build time.
src/ShellUI.CLI/Services/TailwindDownloader.cs Switches Tailwind download tag to TailwindConstants.GitHubTag.
src/ShellUI.CLI/Services/InitService.cs Switches npm install/version defaults to TailwindConstants.
ShellUI.Tests/ShellUI.Tests.csproj Adds test-time reference to the safelist generator tool.
ShellUI.Tests/SafelistDriftTests.cs Adds drift tests for safelist + targets XML validity and sync.
ShellUI.slnx Adds the new tool project to the solution.
README.md Updates top-level documentation for the two install paths + Tailwind version.
docs/PROJECT_STATUS.md Updates Tailwind version references.
docs/COMPONENT_ROADMAP.md Updates Tailwind version references.
.github/workflows/ci.yml Adds a NuGet-only smoke step verifying safelist emission in a consumer build.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +41 to +45
Directory.CreateDirectory(Path.GetDirectoryName(txtOut)!);
File.WriteAllLines(txtOut, classes);

Directory.CreateDirectory(Path.GetDirectoryName(targetsOut)!);
File.WriteAllText(targetsOut, BuildTargetsFileContent(classes));
Comment on lines +114 to +123
// Class-attribute extraction — handles literal strings, Razor expressions, nested
// Shell.Cn("foo", …) literals. Razor variable parts (`@Class`) are dropped because
// Tailwind would not see those at build time anyway.
private static readonly Regex ClassAttributeRegex = new(
"class\\s*=\\s*\"(?<value>[^\"]*)\"",
RegexOptions.Compiled);

private static readonly Regex StringLiteralRegex = new(
"\"(?<lit>[^\"]*)\"",
RegexOptions.Compiled);
Comment on lines +138 to +154
// A token qualifies as a Tailwind utility class if it contains at least one of
// `-`, `:`, `[`, `/` AND starts with a lowercase letter. Excludes C# identifiers
// (`Class`, `Variant`) caught by our literal extraction.
private static void HarvestTokens(string text, SortedSet<string> sink)
{
var tokens = text.Split(new[] { ' ', '\t', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var raw in tokens)
{
var token = raw.Trim();
if (token.Length < 2) continue;
if (!char.IsLower(token[0])) continue;
if (!token.Contains('-') && !token.Contains(':') && !token.Contains('[') && !token.Contains('/')) continue;
token = token.TrimEnd(',', ';', ')', '"');
if (token.Length < 2) continue;
sink.Add(token);
}
}
Comment on lines +6 to +11
public static class TailwindConstants
{
public const string Version = "4.3.2";
public const string GitHubTag = "v" + Version;
public const string NpmRange = "^" + Version;
}
Comment on lines +88 to +89
var msg = "Safelist is out of date.\n";
msg += "Regenerate with:\n dotnet run --project tools/ShellUI.SafelistGenerator -- src/ShellUI.Components/Components src/ShellUI.Components/wwwroot/shellui-classes.txt\n\n";
Comment thread README.md
```

That's it. Components render styled out of the box. Re-run `shellui add` whenever you want more.
The package ships a `shellui-classes.txt` safelist at `wwwroot/shellui-classes.txt` in your project. Wire Tailwind to scan it:
Comment on lines +26 to +38
After restore, the safelist appears at `wwwroot/shellui-classes.txt` in your project. In your `wwwroot/input.css`:

```css
@import "tailwindcss";
@source "./shellui-classes.txt";

/* ... your theme variables, custom layers, etc. ... */
```

Then in your `App.razor` or `_Host.cshtml`:
```html
@using ShellUI.Components
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants