plugins/content-management-system/skills/design-theme-system/SKILL.md
Design token and theming architecture for multi-site/multi-tenant CMS. Includes CSS variables, Tailwind, and design system integration.
npx skillsauth add melodic-software/claude-code-plugins design-theme-systemInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Design a comprehensive theming architecture with design tokens and multi-site support.
/cms:design-theme-system --format tokens
/cms:design-theme-system --format css-vars --multi-tenant
/cms:design-theme-system --format tailwind
/cms:design-theme-system --format all
Extract format and multi-tenant option from command.
Use AskUserQuestion to understand:
Invoke relevant skills:
design-token-management - Token architecturemulti-site-theming - Multi-tenant patternsToken Hierarchy:
tokens:
# Primitive tokens (raw values)
primitive:
colors:
blue:
50: "#eff6ff"
100: "#dbeafe"
500: "#3b82f6"
600: "#2563eb"
900: "#1e3a8a"
gray:
50: "#f9fafb"
100: "#f3f4f6"
500: "#6b7280"
900: "#111827"
success: "#22c55e"
warning: "#f59e0b"
error: "#ef4444"
spacing:
0: "0"
1: "0.25rem"
2: "0.5rem"
4: "1rem"
8: "2rem"
16: "4rem"
typography:
font_families:
sans: "Inter, system-ui, sans-serif"
serif: "Merriweather, Georgia, serif"
mono: "JetBrains Mono, monospace"
font_sizes:
xs: "0.75rem"
sm: "0.875rem"
base: "1rem"
lg: "1.125rem"
xl: "1.25rem"
2xl: "1.5rem"
4xl: "2.25rem"
font_weights:
normal: 400
medium: 500
semibold: 600
bold: 700
radii:
none: "0"
sm: "0.125rem"
md: "0.375rem"
lg: "0.5rem"
full: "9999px"
# Semantic tokens (purpose-driven)
semantic:
colors:
background:
primary: "{primitive.colors.gray.50}"
secondary: "{primitive.colors.gray.100}"
inverse: "{primitive.colors.gray.900}"
text:
primary: "{primitive.colors.gray.900}"
secondary: "{primitive.colors.gray.500}"
inverse: "{primitive.colors.gray.50}"
brand:
primary: "{primitive.colors.blue.600}"
primary_hover: "{primitive.colors.blue.700}"
secondary: "{primitive.colors.blue.100}"
feedback:
success: "{primitive.colors.success}"
warning: "{primitive.colors.warning}"
error: "{primitive.colors.error}"
border:
default: "{primitive.colors.gray.200}"
focus: "{primitive.colors.blue.500}"
spacing:
content_padding: "{primitive.spacing.4}"
section_gap: "{primitive.spacing.8}"
container_max: "1280px"
typography:
body:
family: "{primitive.typography.font_families.sans}"
size: "{primitive.typography.font_sizes.base}"
weight: "{primitive.typography.font_weights.normal}"
line_height: "1.5"
heading:
family: "{primitive.typography.font_families.sans}"
weight: "{primitive.typography.font_weights.bold}"
# Component tokens
component:
button:
primary:
background: "{semantic.colors.brand.primary}"
text: "{semantic.colors.text.inverse}"
border_radius: "{primitive.radii.md}"
padding_x: "{primitive.spacing.4}"
padding_y: "{primitive.spacing.2}"
font_weight: "{primitive.typography.font_weights.medium}"
secondary:
background: "transparent"
text: "{semantic.colors.brand.primary}"
border: "1px solid {semantic.colors.brand.primary}"
card:
background: "{semantic.colors.background.primary}"
border: "1px solid {semantic.colors.border.default}"
border_radius: "{primitive.radii.lg}"
padding: "{primitive.spacing.4}"
shadow: "0 1px 3px rgba(0,0,0,0.1)"
input:
background: "{semantic.colors.background.primary}"
border: "1px solid {semantic.colors.border.default}"
border_radius: "{primitive.radii.md}"
padding: "{primitive.spacing.2} {primitive.spacing.4}"
focus_ring: "0 0 0 2px {semantic.colors.border.focus}"
CSS Output:
/* Base theme (light mode) */
:root {
/* Primitive colors */
--color-blue-50: #eff6ff;
--color-blue-500: #3b82f6;
--color-blue-600: #2563eb;
--color-gray-50: #f9fafb;
--color-gray-500: #6b7280;
--color-gray-900: #111827;
/* Semantic colors */
--color-bg-primary: var(--color-gray-50);
--color-bg-secondary: var(--color-gray-100);
--color-text-primary: var(--color-gray-900);
--color-text-secondary: var(--color-gray-500);
--color-brand-primary: var(--color-blue-600);
--color-border-default: var(--color-gray-200);
/* Typography */
--font-family-sans: 'Inter', system-ui, sans-serif;
--font-size-base: 1rem;
--font-weight-normal: 400;
--font-weight-bold: 700;
/* Spacing */
--spacing-1: 0.25rem;
--spacing-2: 0.5rem;
--spacing-4: 1rem;
--spacing-8: 2rem;
/* Component tokens */
--button-primary-bg: var(--color-brand-primary);
--button-primary-text: white;
--button-radius: var(--radius-md);
--card-bg: var(--color-bg-primary);
--card-border: 1px solid var(--color-border-default);
--card-radius: var(--radius-lg);
}
/* Dark mode */
:root[data-theme="dark"],
.dark {
--color-bg-primary: var(--color-gray-900);
--color-bg-secondary: var(--color-gray-800);
--color-text-primary: var(--color-gray-50);
--color-text-secondary: var(--color-gray-400);
--color-border-default: var(--color-gray-700);
}
/* Brand override example */
:root[data-brand="acme"] {
--color-brand-primary: #ff6b35;
--color-brand-primary-hover: #e85a2a;
--font-family-sans: 'Poppins', sans-serif;
}
Tailwind Configuration:
// tailwind.config.js
const tokens = require('./tokens.json');
module.exports = {
content: ['./src/**/*.{html,js,jsx,ts,tsx,razor}'],
theme: {
colors: {
transparent: 'transparent',
current: 'currentColor',
// Map CSS variables for runtime theming
brand: {
primary: 'var(--color-brand-primary)',
'primary-hover': 'var(--color-brand-primary-hover)',
secondary: 'var(--color-brand-secondary)',
},
bg: {
primary: 'var(--color-bg-primary)',
secondary: 'var(--color-bg-secondary)',
inverse: 'var(--color-bg-inverse)',
},
text: {
primary: 'var(--color-text-primary)',
secondary: 'var(--color-text-secondary)',
inverse: 'var(--color-text-inverse)',
},
border: {
DEFAULT: 'var(--color-border-default)',
focus: 'var(--color-border-focus)',
},
// Static palette for non-themed colors
...tokens.primitive.colors,
},
fontFamily: {
sans: 'var(--font-family-sans)',
serif: 'var(--font-family-serif)',
mono: 'var(--font-family-mono)',
},
extend: {
spacing: tokens.primitive.spacing,
borderRadius: tokens.primitive.radii,
},
},
plugins: [
require('@tailwindcss/forms'),
require('@tailwindcss/typography'),
],
};
Theme Resolution Service:
public class ThemeService
{
private readonly ITenantResolver _tenantResolver;
private readonly IThemeRepository _themeRepository;
private readonly IMemoryCache _cache;
public async Task<ThemeConfiguration> GetThemeAsync()
{
var tenant = await _tenantResolver.ResolveAsync();
var cacheKey = $"theme:{tenant.Id}";
return await _cache.GetOrCreateAsync(cacheKey, async entry =>
{
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5);
// Load theme hierarchy
var baseTheme = await _themeRepository.GetBaseThemeAsync();
var brandTheme = await _themeRepository.GetBrandThemeAsync(tenant.BrandId);
var siteTheme = await _themeRepository.GetSiteThemeAsync(tenant.SiteId);
// Merge with precedence: site > brand > base
return MergeThemes(baseTheme, brandTheme, siteTheme);
});
}
public string GenerateCssVariables(ThemeConfiguration theme)
{
var sb = new StringBuilder();
sb.AppendLine(":root {");
foreach (var token in theme.FlattenTokens())
{
sb.AppendLine($" --{token.Key}: {token.Value};");
}
sb.AppendLine("}");
return sb.ToString();
}
}
Theme API Endpoint:
[HttpGet("theme.css")]
[ResponseCache(Duration = 300, VaryByHeader = "Host")]
public async Task<IActionResult> GetThemeCss()
{
var theme = await _themeService.GetThemeAsync();
var css = _themeService.GenerateCssVariables(theme);
return Content(css, "text/css");
}
Theme Customization API:
theme_editor:
sections:
- name: Colors
fields:
- key: brand_primary
label: Primary Brand Color
type: color
path: semantic.colors.brand.primary
- key: brand_secondary
label: Secondary Color
type: color
path: semantic.colors.brand.secondary
- name: Typography
fields:
- key: font_family
label: Primary Font
type: font_picker
path: primitive.typography.font_families.sans
- key: heading_font
label: Heading Font
type: font_picker
path: semantic.typography.heading.family
- name: Layout
fields:
- key: border_radius
label: Corner Roundness
type: slider
min: 0
max: 24
path: primitive.radii.md
preview:
components: [Button, Card, Input, Typography]
live_update: true
design-token-management - Token architecturemulti-site-theming - Multi-tenant patternsdevelopment
Search Milan Jovanovic's .NET blog for Clean Architecture, DDD, CQRS, EF Core, and ASP.NET Core patterns. Use for finding applicable patterns, code examples, and architecture guidance. Invoke when working with .NET projects that could benefit from proven architectural patterns.
tools
Install and configure Data API Builder (DAB) for production SQL Server MCP access with RBAC
tools
Manage MssqlMcp servers - status, rebuild, and upstream updates
tools
Developer environment setup guides for Windows, macOS, Linux, and WSL. Use when setting up development machines, installing tools, configuring environments, or following platform-specific setup guides. Covers package management, shell/terminal, code editors, AI tooling, containerization, databases, and more.