Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 40 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -482,18 +482,51 @@ Now write raw HTML with ShellUI's Tailwind classes:

---

### Using tweakcn themes with `shellui theme` (new in 0.4.x)

[tweakcn.com](https://tweakcn.com) is a visual theme editor for shadcn-style CSS variables. ShellUI's `theme` subcommand fetches a theme by URL and bakes it into your project at **build time** — no runtime dep, works offline, exactly-what-you-see-is-what-ships.

```bash
# One-shot: init a fresh project AND apply a theme (Path C setup)
shellui theme init https://tweakcn.com/themes/<id>

# Existing project: apply a theme to your input.css (Path B/C)
shellui theme apply https://tweakcn.com/themes/<id>

# Existing project: emit standalone override CSS (Path A/D)
shellui theme apply https://tweakcn.com/themes/<id> --emit-override wwwroot/theme.css

# Re-fetch the theme recorded in shellui.theme.lock
shellui theme update
```

Each `apply` writes a `shellui.theme.lock` file recording the source URL + SHA-256, so `shellui theme update` refreshes from the same source without you having to remember the URL.

**How it drops into each install path:**

| Path | Command | Where the theme lands |
|---|---|---|
| **A** — precompiled bundle | `shellui theme apply <url> --emit-override wwwroot/theme.css`, then `<link>` it after `shellui-all.css` | Standalone override CSS; wins the cascade over the baked bundle |
| **B** — safelist | `shellui theme apply <url>` | Sentinel-marked region in your `wwwroot/input.css`; user content outside markers survives re-applies |
| **C** — CLI | `shellui theme init <url>` (fresh) or `shellui theme apply <url>` (existing) | Sentinel-marked region in the `input.css` `shellui init` created; Tailwind rebuilds on `dotnet build` |
| **D** — CDN | `shellui theme apply <url> --emit-override theme.css`, then `<link>` it after the CDN link | Same override pattern as Path A |

The apply is **idempotent** — re-running with the same URL produces byte-identical output. Custom utilities, `@source` directives, and imports you added around the theme block are preserved verbatim.

---

### Theming across paths

All four paths use the same CSS variable system, so theming works uniformly — just with different edit surfaces:

| Path | Where the theme lives | How you edit it |
|---|---|---|
| A | Baked in `shellui-all.css` | Override CSS vars in a `<style>` block *after* the link |
| B | Your `wwwroot/input.css` | Edit directly, paste tweakcn output over `:root`/`.dark` blocks |
| C | `wwwroot/input.css` created by `shellui init` | Edit directly — Tailwind auto-rebuilds on `dotnet build` |
| D | Baked in the CDN-served CSS | Override in a `<style>` tag |
| Path | Where the theme lives | How you edit it | Auto-import from tweakcn |
|---|---|---|---|
| A | Baked in `shellui-all.css` | Override CSS vars in a `<style>` block *after* the link | `shellui theme apply <url> --emit-override wwwroot/theme.css` |
| B | Your `wwwroot/input.css` | Edit directly, paste tweakcn output over `:root`/`.dark` blocks | `shellui theme apply <url>` |
| C | `wwwroot/input.css` created by `shellui init` | Edit directly — Tailwind auto-rebuilds on `dotnet build` | `shellui theme init <url>` or `shellui theme apply <url>` |
| D | Baked in the CDN-served CSS | Override in a `<style>` tag | `shellui theme apply <url> --emit-override theme.css` |

Change one CSS variable (e.g. `--primary`) and every component updates immediately. See each install-path README under `shellui-installation-tests/` for concrete theming examples.
Change one CSS variable (e.g. `--primary`) and every component updates immediately.

### Configure Tailwind CSS manually (advanced)
Create/update `wwwroot/tailwind.config.js`:
Expand Down
40 changes: 40 additions & 0 deletions src/ShellUI.CLI/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,51 @@ static async Task<int> Main(string[] args)
static Command CreateThemeCommand()
{
var theme = new Command("theme", "Fetch and apply themes from tweakcn.com at build time.");
theme.AddCommand(CreateThemeInitCommand());
theme.AddCommand(CreateThemeApplyCommand());
theme.AddCommand(CreateThemeUpdateCommand());
return theme;
}

static Command CreateThemeInitCommand()
{
var command = new Command("init", "Initialize ShellUI in a fresh Blazor project and bake in a tweakcn theme in one shot.");

var urlArg = new Argument<string>("url",
"tweakcn URL or theme id. Accepts https://tweakcn.com/themes/<id>, the raw <id>, or the public https://tweakcn.com/r/themes/<id> endpoint.");
command.AddArgument(urlArg);

var forceOpt = new Option<bool>("--force", "Reinitialize even if already initialized");
var styleOpt = new Option<string>("--style", getDefaultValue: () => "default",
"Component style: default, new-york, minimal");
var tailwindOpt = new Option<string>("--tailwind", getDefaultValue: () => "standalone",
"Tailwind method: standalone, npm");
var yesOpt = new Option<bool>("--yes", "Non-interactive mode with default options");
command.AddOption(forceOpt);
command.AddOption(styleOpt);
command.AddOption(tailwindOpt);
command.AddOption(yesOpt);

command.SetHandler(async (url, force, style, tailwind, nonInteractive) =>
{
try
{
AnsiConsole.MarkupLine("[cyan]Step 1/2:[/] initializing ShellUI…");
await InitService.InitializeAsync(style, force, tailwind, nonInteractive);
AnsiConsole.MarkupLine("");
AnsiConsole.MarkupLine("[cyan]Step 2/2:[/] applying tweakcn theme…");
await ApplyThemeAsync(url, emitOverride: null);
}
catch (Exception ex)
{
AnsiConsole.MarkupLine($"[red]Error:[/] {ex.Message.Replace("[", "[[").Replace("]", "]]")}");
Environment.Exit(1);
}
}, urlArg, forceOpt, styleOpt, tailwindOpt, yesOpt);

return command;
}

static Command CreateThemeApplyCommand()
{
var command = new Command("apply", "Fetch a theme from tweakcn and bake it into wwwroot/input.css");
Expand Down
28 changes: 27 additions & 1 deletion src/ShellUI.CLI/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,13 @@ dotnet tool install -g ShellUI.CLI
dotnet shellui init
```

This automatically:
Or in one shot with a tweakcn theme baked in:

```bash
dotnet shellui theme init https://tweakcn.com/themes/<id> --yes
```

`init` automatically:
- ✅ Downloads Tailwind CSS CLI (standalone, no Node.js required) — or uses your npm install if you prefer
- ✅ Writes the full default theme (`:root`, `.dark`, `@theme inline`) to `wwwroot/input.css`
- ✅ Patches `Components/App.razor` with `@rendermode="InteractiveServer"`, a theme-bootstrap `<script>` (no light-flash on dark pages), and the `<script src="shellui.js">` tag
Expand Down Expand Up @@ -105,6 +111,26 @@ dotnet shellui remove button input
**Options:**
- `--all` - Remove all installed components

### `theme` — bake tweakcn themes at build time

Fetch a theme from [tweakcn.com](https://tweakcn.com) and write it into your project. No runtime fetch, works offline, exactly-what-you-see-is-what-ships.

```bash
# Fresh project — init + apply theme in one step
dotnet shellui theme init https://tweakcn.com/themes/<id>

# Existing project — apply theme to wwwroot/input.css
dotnet shellui theme apply https://tweakcn.com/themes/<id>

# Emit standalone override CSS (for Path A/D consumers who use the precompiled bundle)
dotnet shellui theme apply https://tweakcn.com/themes/<id> --emit-override wwwroot/theme.css

# Re-fetch the theme recorded in shellui.theme.lock
dotnet shellui theme update
```

Each `apply` writes a `shellui.theme.lock` file (source URL + SHA-256) so `update` can refresh from the same source without you having to remember the URL. Re-applies are idempotent — user content outside the sentinel-marked region survives verbatim.

## Available Components

### Form Components
Expand Down
42 changes: 36 additions & 6 deletions src/ShellUI.Components/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,15 @@ Done. `<Button>`, `<Card>`, `<Dialog>` all render styled.

**Trade-offs:**
- ❌ No tree-shaking — you pay ~77KB even if you use 3 components (negligible for most sites)
- ❌ Theme customization is via override only — theme vars are baked in. You add a `<style>` block *after* the link:
- ❌ Theme customization is via override only — theme vars are baked in. You add a `<style>` block *after* the link, or (recommended) generate one automatically from a tweakcn theme with the CLI:
```bash
dotnet tool install -g ShellUI.CLI
dotnet shellui theme apply https://tweakcn.com/themes/<id> --emit-override wwwroot/theme.css
```
Then link the override AFTER `shellui-all.css` so it wins the cascade:
```html
<link href="_content/ShellUI.Components/shellui-all.css" rel="stylesheet" />
<style>
:root { --primary: oklch(0.55 0.15 250); } /* your color */
</style>
<link href="theme.css" rel="stylesheet" />
```

**Best for:** new projects, prototypes, teams without existing Tailwind, "just get me components" scenarios.
Expand Down Expand Up @@ -93,6 +96,13 @@ The package copies `shellui-classes.txt` (auto-generated safelist of every Tailw
}
```

Or auto-import a tweakcn theme into the `:root` / `.dark` blocks (idempotent, preserves your custom utilities and imports around it):

```bash
dotnet tool install -g ShellUI.CLI
dotnet shellui theme apply https://tweakcn.com/themes/<id>
```

```razor
@* _Imports.razor *@
@using ShellUI.Components
Expand Down Expand Up @@ -122,6 +132,9 @@ The package copies `shellui-classes.txt` (auto-generated safelist of every Tailw
dotnet tool install -g ShellUI.CLI
shellui init # one-time
shellui add button card dialog # any time you want more components

# Or one-shot: init + tweakcn theme
shellui theme init https://tweakcn.com/themes/<id> --yes
```

`shellui init` automatically:
Expand Down Expand Up @@ -259,12 +272,29 @@ ShellUI components work alongside Bootstrap. You can:

### 🎨 Theme Customization

**Easily customize themes with [tweakcn](https://tweakcn.com/):**
**Easiest — auto-import a [tweakcn](https://tweakcn.com/) theme with the CLI:**

```bash
dotnet tool install -g ShellUI.CLI

# Path B / Path C — write into wwwroot/input.css between sentinel markers
dotnet shellui theme apply https://tweakcn.com/themes/<id>

# Path A — emit standalone override CSS, link AFTER shellui-all.css
dotnet shellui theme apply https://tweakcn.com/themes/<id> --emit-override wwwroot/theme.css

# Refresh later from the same URL (recorded in shellui.theme.lock)
dotnet shellui theme update
```

Re-runs are idempotent. Your custom utilities, `@source` directives, and imports outside the sentinel block are preserved verbatim.

**Manual alternative:**

1. Design your perfect theme on tweakcn
2. Copy the generated CSS variables
3. Paste into `wwwroot/input.css`
4. All ShellUI components update automatically!
4. All ShellUI components update automatically

**Custom Fonts:** Add Google Fonts links and update `--font-*` variables in your CSS.

Expand Down
Loading