Slotty 1.1.0

dotnet add package Slotty --version 1.1.0
                    
NuGet\Install-Package Slotty -Version 1.1.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Slotty" Version="1.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Slotty" Version="1.1.0" />
                    
Directory.Packages.props
<PackageReference Include="Slotty" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Slotty --version 1.1.0
                    
#r "nuget: Slotty, 1.1.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Slotty@1.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Slotty&version=1.1.0
                    
Install as a Cake Addin
#tool nuget:?package=Slotty&version=1.1.0
                    
Install as a Cake Tool

🎯 Slotty

<div align="center">

Slotty Logo

A modern, flexible content slot system for ASP.NET Core that replaces the limitations of @RenderSection

NuGet Version NuGet Downloads Build Status License .NET

</div>


🚀 Why Slotty?

Tired of the rigid, error-prone @RenderSection system? Slotty brings modern content management to ASP.NET Core with a flexible, component-based approach that enables true component distribution.

The Problem with @RenderSection

// ❌ Old way: Rigid, error-prone, limited
@RenderSection("Scripts", required: false)
@RenderSection("Styles", required: false)

// Must define sections in exact layout hierarchy
// No dynamic content injection
// Poor error handling
// Can't distribute layouts via NuGet packages
// Limited extensibility

The Slotty Solution


<slot name="head"></slot>
<slot name="nav-primary"></slot>
<slot name="scripts"></slot>


<slot name="sidebar">
    <div>Default sidebar content</div>
</slot>



🎯 Game Changer: Ship complete admin themes, component libraries, and plugins as NuGet packages that consuming applications can customize and extend!

✨ Features

  • 🎯 Multiple Content Sources - Add content to slots from anywhere in your application
  • 🔧 Slot Extensions - Automatic :before and :after extension points
  • 🛡️ Type Safety - Built-in validation with helpful error messages
  • 🎨 Development Tools - Visual slot debugging with Alt+S keyboard shortcut
  • 📦 NuGet Distribution - Ship themes and components as distributable packages
  • 🚀 High Performance - Request-scoped content management
  • 🔄 Backward Compatible - Migrate gradually from @RenderSection

📋 Table of Contents

📦 Installation

Package Manager Console

Install-Package Slotty

.NET CLI

dotnet add package Slotty

PackageReference

<PackageReference Include="Slotty" Version="1.1.0" />

⚡ Quick Start

1. Register Services

// Program.cs
builder.Services.AddSlotty();
app.UseSlotty(); // For automatic dev tools injection

2. Define Slots in Layout


<!DOCTYPE html>
<html>
<head>
    <slot name="head">
        
        <title>Default Title</title>
    </slot>
</head>
<body>
    <header>
        <slot name="header"></slot>
    </header>
    
    <main>
        @RenderBody()
    </main>
    
    <slot name="scripts"></slot>
</body>
</html>

3. Fill Slots from Views


<fill slot="head">
    <title>Home Page</title>
    <meta name="description" content="Welcome to our site">
</fill>

<fill slot="header">
    <nav>Navigation content</nav>
</fill>

<h1>Welcome!</h1>

<fill slot="scripts">
    <script src="~/js/home.js"></script>
</fill>

⚙️ Configuration

Basic Configuration

// Use default settings (recommended)
builder.Services.AddSlotty();

Advanced Configuration

// Program.cs - Programmatic configuration
builder.Services.AddSlotty(options =>
{
    options.ValidationMode = SlottyValidationMode.Log; // Silent, Log, Throw
    options.DevToolsInjection = SlottyDevToolsInjectionMode.Auto; // Auto, Always, Never
});

Configuration via appsettings.json

{
  "Slotty": {
    "ValidationMode": "Log",
    "DevToolsInjection": "Auto"
  }
}
// Program.cs
builder.Services.AddSlotty(builder.Configuration.GetSection("Slotty"));

Validation Modes

Mode Description Use Case
Silent Basic slot name validation only Production environments
Log Validation with warning logs Staging/testing environments
Throw Strict validation with exceptions Development environments

📖 Basic Usage

Simple Slots


<slot name="sidebar"></slot>


<fill slot="sidebar">
    <div class="widget">Content here</div>
</fill>

Slots with Fallbacks


<slot name="breadcrumbs">
    <nav>
        <a href="/">Home</a>
    </nav>
</slot>


<fill slot="breadcrumbs">
    <nav>
        <a href="/">Home</a> > 
        <a href="/products">Products</a> > 
        <span>iPhone</span>
    </nav>
</fill>

Multiple Content Blocks


<fill slot="styles">
    <link rel="stylesheet" href="~/css/components.css">
</fill>

<fill slot="styles">
    <link rel="stylesheet" href="~/css/page-specific.css">
</fill>


<slot name="styles"></slot>

Slot Extensions

Every slot automatically gets :before and :after extension points:


<slot name="content"></slot>


<fill slot="content:before">
    <div class="alert">Important notice!</div>
</fill>

<fill slot="content">
    <h1>Main Content</h1>
</fill>

<fill slot="content:after">
    <div class="related-links">Related: ...</div>
</fill>

🔥 Advanced Examples

📦 Distributable NuGet Themes & Plugins

One of Slotty's most powerful features is enabling component distribution via NuGet packages. Ship complete layouts, themes, and plugins that consuming applications can extend and customize.

Admin Theme NuGet Package

Create a reusable admin theme package:


<!DOCTYPE html>
<html>
<head>
    <slot name="head">
        <title>Admin Dashboard</title>
        <link rel="stylesheet" href="~/css/admin-theme.css">
    </slot>
</head>
<body class="admin-layout">
    <header class="admin-header">
        <slot name="header">
            <h1>Admin Portal</h1>
        </slot>
        <slot name="user-menu"></slot>
    </header>
    
    <div class="admin-container">
        <aside class="admin-sidebar">
            <nav class="admin-nav">
                <slot name="navigation:before"></slot>

                
                <a href="/admin" class="nav-item">
                    <i class="icon-dashboard"></i> Dashboard
                </a>
                <a href="/admin/users" class="nav-item">
                    <i class="icon-users"></i> Users
                </a>
                
                
                <slot name="navigation-menu"></slot>
                
                <div class="nav-sticky-bottom">
                    
                    <slot name="navigation-footer"></slot>

                    <a href="/admin/logout" class="nav-item">
                        <i class="icon-logout"></i> Logout
                    </a>
                </div>
            </nav>
        </aside>
        
        <main class="admin-main">
            <slot name="breadcrumbs"></slot>
            <slot name="page-header"></slot>
            
            @RenderBody()
            
            <slot name="page-footer"></slot>
        </main>
    </div>
    
    <slot name="modals"></slot>
    <slot name="scripts"></slot>
</body>
</html>
Client Application Usage

Install and use the admin theme:

# Client application installs the theme
dotnet add package MyCompany.AdminTheme

@{
    Layout = "_AdminLayout"; // From NuGet package
}

<fill slot="head">
    <title>Dashboard - My App Admin</title>
    <link rel="stylesheet" href="~/css/custom-admin.css">
</fill>

<fill slot="user-menu">
    <div class="user-dropdown">
        <span>Welcome, @User.Identity.Name</span>
        <a href="/logout">Logout</a>
    </div>
</fill>

<fill slot="breadcrumbs">
    <nav>Home > Dashboard</nav>
</fill>


<div class="dashboard">
    <h1>Dashboard</h1>
    
</div>
Plugin NuGet Packages

Create plugins that extend the admin theme:


@{
    // This partial is included by the plugin
}

<fill slot="nav-primary">
    <div class="nav-section">
        <span class="nav-section-title">Inventory</span>
        <a href="/admin/products" class="nav-item">
            <i class="icon-box"></i> Products
        </a>
        <a href="/admin/categories" class="nav-item">
            <i class="icon-tags"></i> Categories
        </a>
        <a href="/admin/inventory" class="nav-item">
            <i class="icon-warehouse"></i> Stock Levels
        </a>
    </div>
</fill>

<fill slot="nav-secondary">
    <div class="nav-section">
        <span class="nav-section-title">Reports</span>
        <a href="/admin/reports/sales" class="nav-item">
            <i class="icon-chart"></i> Sales Reports
        </a>
        <a href="/admin/reports/analytics" class="nav-item">
            <i class="icon-analytics"></i> Analytics
        </a>
    </div>
</fill>
Client Application Integration

The client app includes navigation from all installed plugins:


@using MyCompany.AdminTheme
@addTagHelper *, MyCompany.AdminTheme


@Html.Partial("~/Views/Shared/_InventoryNavigation.cshtml") 
@Html.Partial("~/Views/Shared/_ReportsNavigation.cshtml")
Benefits of This Architecture

Modular Development: Teams can work on separate plugins independently
Consistent UI: All plugins use the same admin theme
Easy Installation: dotnet add package InventoryPlugin
Customizable: Client apps can override any slot with their own content
Maintainable: Theme updates automatically benefit all plugins

E-commerce Product Pages


<fill slot="head">
    <title>@Model.Name - My Store</title>
    <meta name="description" content="@Model.Description">
</fill>

<fill slot="breadcrumbs">
    <nav>
        <a href="/">Home</a> > 
        <a href="/products">Products</a> > 
        @Model.Category.Name
    </nav>
</fill>


<div class="product-details">
    <h1>@Model.Name</h1>
    <div class="price">$@Model.Price</div>
</div>

<fill slot="page-footer">
    <section class="related-products">
        <h2>You Might Also Like</h2>
        
    </section>
</fill>

<fill slot="scripts">
    <script src="~/js/product-gallery.js"></script>
</fill>

Component Libraries with Slots


<div class="product-card">
    <slot name="product-image">
        <img src="@Model.DefaultImage" alt="@Model.Name">
    </slot>
    
    <div class="product-info">
        <h3>@Model.Name</h3>
        <div class="price">$@Model.Price</div>
        
        <slot name="product-actions">
            <button class="btn btn-primary">Add to Cart</button>
        </slot>
    </div>
    
    <slot name="product-badges"></slot>
</div>


@Html.Partial("_ProductCard", product)

<fill slot="product-badges">
    @if (product.IsOnSale)
    {
        <span class="badge sale">50% OFF</span>
    }
</fill>

<fill slot="product-actions">
    <button class="btn btn-primary" data-product="@product.Id">
        Quick Add - $@product.Price
    </button>
    <a href="#" class="wishlist-btn">♡ Save</a>
</fill>

🛠️ Development Tools

Visual Slot Debugging

Slotty includes powerful development tools that make slot debugging intuitive and visual:

Slotty Development Tools Placeholder image - Visual overlay showing slot boundaries and names

Interactive Overlay System

Keyboard Shortcut: Alt + S

  • Single press: Show slot overlays while keys are held
  • Double press: Toggle permanent visibility (persists until page reload)
  • Visual indicators: Each slot shows its name, content status, and boundaries

<script>
// Alt+S keyboard shortcut for slot visualization
// Double-press for permanent visibility
// Session storage persistence
</script>

Customizable Overlay Styling

Override the default blue theme with CSS variables:

/* Change the entire overlay theme with one variable */
:root {
  --slotty-overlay-color: red;                    /* Base color */
  --slotty-overlay-transition-duration: 300ms;    /* Animation speed */
  --slotty-overlay-border-radius: 8px;           /* Corner rounding */
}

/* Dark mode support */
@media (prefers-color-scheme: dark) {
  :root {
    --slotty-overlay-color: crimson;
  }
}

Available CSS Variables:

  • --slotty-overlay-color - Base color (auto-generates other colors)
  • --slotty-overlay-border - Border style (e.g., 2px solid red)
  • --slotty-overlay-background-color - Fill opacity
  • --slotty-overlay-pill-background - Name pill background
  • --slotty-overlay-pill-color - Name pill text color
  • --slotty-overlay-transition-duration - Animation timing
  • --slotty-overlay-border-radius - Corner rounding
  • --slotty-overlay-sheen-color - Highlight animation color

Head-Safe Debugging

Slots in <head> use HTML comments for valid markup:

<head>
    
    <title>My Page</title>
    <meta charset="utf-8">
    
</head>

🔄 Migration Guide

From @RenderSection

Before:


@RenderSection("Scripts", required: false)
@RenderSection("Styles", required: false)


@section Scripts {
    <script src="~/js/page.js"></script>
}

@section Styles {
    <link rel="stylesheet" href="~/css/page.css">
}

After:


<slot name="scripts"></slot>
<slot name="styles"></slot>


<fill slot="scripts">
    <script src="~/js/page.js"></script>
</fill>

<fill slot="styles">
    <link rel="stylesheet" href="~/css/page.css">
</fill>

Benefits of Migration

@RenderSection Slotty Benefit
Single content per section Multiple fills per slot ✅ Flexible content composition
Must be defined in hierarchy Fill from anywhere ✅ True component architecture
Required/optional only Rich validation modes ✅ Better error handling
No extensibility :before and :after extensions ✅ Built-in extensibility
No default content Fallback content support ✅ Progressive enhancement

📚 API Reference

TagHelpers

<slot>

Defines a content slot that can be filled by <fill> tags.

Attributes:

  • name (required): The unique name of the slot

Example:

<slot name="sidebar">
    <div>Default content</div>
</slot>
<fill>

Adds content to a named slot.

Attributes:

  • slot (required): The name of the slot to fill

Example:

<fill slot="sidebar">
    <div>Custom sidebar content</div>
</fill>

Configuration

SlottyOptions
Property Type Default Description
ValidationMode SlottyValidationMode Silent Validation behavior
DevToolsInjection SlottyDevToolsInjectionMode Auto Dev tools injection mode
SlottyValidationMode
Value Description
Silent Basic validation only
Log Validation with warning logs
Throw Strict validation with exceptions
SlottyDevToolsInjectionMode
Value Description
Auto Inject in development environment only
Always Always inject dev tools
Never Never inject dev tools

Service Registration

// Basic registration
services.AddSlotty();

// With configuration
services.AddSlotty(options => { /* configure */ });

// From configuration section
services.AddSlotty(configuration.GetSection("Slotty"));

Middleware Registration

// Enable automatic dev tools injection
app.UseSlotty();

🤝 Contributing

We love contributions! Please see our Contributing Guide for details.

Development Setup

git clone https://github.com/AaronLayton/slotty.git
cd slotty
dotnet restore
dotnet build
dotnet test

Running Examples

dotnet run --project Slotty.Example

🔧 Development & Release Workflow

Development Process

  1. Make changes to the codebase
  2. Run tests to ensure everything works:
    dotnet test --configuration Release
    
  3. Build and test locally:
    dotnet build --configuration Release
    dotnet pack Slotty --configuration Release
    
  4. Commit and push changes:
    git add .
    git commit -m "feat: your feature description"
    git push
    

Release Process

When ready to release a new version:

  1. Update CHANGELOG.md with new version details
  2. Commit changelog updates:
    git add CHANGELOG.md
    git commit -m "docs: update changelog for v1.0.x"
    git push
    
  3. Create and push version tag:
    # For patch releases (bug fixes)
    git tag -a v1.0.3 -m "Release v1.0.3 - Bug fixes"
    git push origin v1.0.3
    
    # For minor releases (new features)
    git tag -a v1.1.0 -m "Release v1.1.0 - New features"
    git push origin v1.1.0
    
    # For major releases (breaking changes)
    git tag -a v2.0.0 -m "Release v2.0.0 - Breaking changes"
    git push origin v2.0.0
    
  4. GitHub Actions automatically:
    • Builds the project
    • Runs all tests
    • Validates the NuGet package
    • Publishes to NuGet.org (if tag starts with v)

Version Management

  • MinVer automatically generates versions based on git tags
  • Development commits (after a tag) generate alpha versions: 1.0.3-alpha.0.1
  • Tagged releases generate clean versions: 1.0.3
  • Tag format: Always use v prefix (e.g., v1.0.3, v1.1.0, v2.0.0)
How to Determine Next Version Number

Before creating a release, check what version to use:

# 1. What's the current latest tag?
git describe --tags --abbrev=0

# 2. What commits have been made since the last tag?
git log --oneline $(git describe --tags --abbrev=0)..HEAD

# 3. What will MinVer suggest for the next version?
dotnet build Slotty --configuration Release --verbosity detailed | Select-String -Pattern "MinVerVersion"

Decision Guide:

  • Patch (v1.0.3): Bug fixes, small improvements
  • Minor (v1.1.0): New features, backwards compatible
  • Major (v2.0.0): Breaking changes, API changes

Verification

After pushing a tag, verify the release:

  1. Check GitHub Actions for successful build
  2. Check NuGet.org for the new version
  3. Test installation: dotnet add package Slotty

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.

🌟 Support


<div align="center">

Made with ❤️ by Aaron Layton • Follow on X

⬆ Back to Top

</div>

Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net6.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.1.0 522 7/22/2025
1.0.2 151 7/16/2025
0.0.0-alpha.0.4 121 7/16/2025

See CHANGELOG.md for detailed release notes