WHITE LABEL COMPLETE - nself-org/nchat GitHub Wiki
The nself-chat platform includes a comprehensive white-label system that allows complete customization of branding, themes, and user experience. This document covers Phase 15 implementation (Tasks 109-113).
- Architecture
- Template System
- Theme Customization
- Branding Management
- Domain Configuration
- API Reference
- Usage Guide
src/
├── lib/white-label/
│ ├── tenant-branding.ts # Branding service & persistence
│ ├── branding-schema.ts # Type definitions & validation
│ └── branding-export.ts # Export/import functionality
├── templates/
│ ├── types.ts # Template type definitions
│ ├── index.ts # Template registry & loading
│ ├── default/ # nself default template
│ ├── slack/ # Slack template
│ ├── discord/ # Discord template
│ ├── telegram/ # Telegram template
│ └── whatsapp/ # WhatsApp template
├── components/white-label/
│ ├── theme-editor.tsx # Visual theme editor with live preview
│ ├── template-selector.tsx # Template selection interface
│ └── branding-dashboard.tsx # Complete branding control center
└── app/api/tenants/[id]/branding/
├── route.ts # GET/PATCH branding config
├── upload/route.ts # Logo upload
├── template/route.ts # Template switching
├── css/route.ts # Custom CSS
├── domain/route.ts # Domain configuration
├── export/route.ts # Export configuration
└── import/route.ts # Import configuration
- Description: Modern, professional design combining best of Slack, Discord, and Telegram
- Colors: nself cyan (#00D4FF), Protocol zinc
- Best For: Teams wanting modern communication platform
- Features: Full feature set, flexible layouts
- Description: Classic Slack-style interface with aubergine accents
- Colors: Aubergine (#4A154B), Green (#007A5A)
- Best For: Professional teams and enterprises
- Features: Channel-based, threads, integrations
- Description: Discord-style dark theme with blurple accents
- Colors: Blurple (#5865F2), Dark gray (#202225)
- Best For: Gaming communities and social groups
- Features: Server hierarchy, voice channels, rich embeds
- Description: Clean, fast Telegram-style interface
- Colors: Blue (#34B7F1), White
- Best For: Privacy-focused teams
- Features: Secret chats, channels, bots
- Description: WhatsApp-style chat bubbles with green theme
- Colors: Green (#25D366), Teal (#128C7E)
- Best For: Personal and small team communication
- Features: Bubble messages, voice notes, double checkmarks
interface PlatformTemplate {
// Identity
id: TemplateId
name: string
description: string
version: string
// Theme colors (light & dark)
theme: {
light: ThemeColors
dark: ThemeColors
defaultMode: 'light' | 'dark' | 'system'
}
// Layout configuration
layout: LayoutConfig
// Feature configuration
features: FeatureConfig
// Terminology customization
terminology: TerminologyConfig
// Animations
animations: AnimationConfig
// Custom CSS
customCSS?: string
}import { loadTemplate, templateRegistry } from '@/templates'
// Load specific template
const template = await loadTemplate('whatsapp')
// Get all available templates
const templates = Object.values(templateRegistry)
// Load from environment
const envTemplate = await loadEnvTemplate()Each template includes 24+ color properties for both light and dark modes:
Primary Colors:
primaryColorsecondaryColoraccentColor
Background Colors:
backgroundColorsurfaceColorcardColorpopoverColor
Text Colors:
textColortextMutedColortextInverseColor
Border Colors:
borderColorborderMutedColor
Button Colors:
buttonPrimaryBgbuttonPrimaryTextbuttonSecondaryBgbuttonSecondaryTextbuttonGhostHover
Status Colors:
successColorwarningColorerrorColorinfoColor
Special Colors:
linkColorfocusRingColorselectionBghighlightBg
The theme editor provides real-time preview:
import { ThemeEditor } from '@/components/white-label/theme-editor'
;<ThemeEditor
tenantId="your-tenant-id"
initialColors={{
light: { primaryColor: '#3B82F6' },
dark: { primaryColor: '#60A5FA' },
}}
onSave={(theme) => {
// Save theme
}}
/>Features:
- Color picker for each property
- Live preview with sample UI
- Light/dark mode toggle
- Export/import theme JSON
- Reset to defaults
import { tenantBrandingService } from '@/lib/white-label/tenant-branding'
// Get branding configuration
const branding = await tenantBrandingService.getTenantBranding('tenant-id')
// Update branding
await tenantBrandingService.updateTenantBranding(
'tenant-id',
{
appInfo: { appName: 'My App' },
colors: { primary: '#3B82F6' },
},
'user-id'
)
// Upload logo
const { url, storageKey } = await tenantBrandingService.uploadLogo('tenant-id', file, 'primary')
// Switch template
await tenantBrandingService.switchTemplate(
'tenant-id',
'slack',
'user-id',
true // preserve customizations
)
// Export/import
const blob = await tenantBrandingService.exportBranding('tenant-id')
await tenantBrandingService.importBranding('tenant-id', file, 'user-id')Upload and manage three logo types:
- Primary Logo: Main logo (recommended: 200x60px)
- Square Logo: Icon version (recommended: 512x512px)
- Favicon: Browser tab icon (recommended: 32x32px)
// Upload logo
const formData = new FormData()
formData.append('file', logoFile)
formData.append('type', 'primary')
formData.append('tenantId', 'tenant-id')
const response = await fetch('/api/tenants/tenant-id/branding/upload', {
method: 'POST',
body: formData,
})
const { url, storageKey } = await response.json()Add custom styles to override or extend template styles:
await tenantBrandingService.applyCustomCSS(
'tenant-id',
`
/* Custom styles */
.custom-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.message-bubble {
border-radius: 16px;
}
`,
'user-id'
)Security Notes:
- CSS is sanitized on the server
- No
<script>tags allowed - Limited to CSS properties only
- Configure Domain:
const { dnsRecords, verificationToken } = await tenantBrandingService.configureDomain(
'tenant-id',
'chat.yourdomain.com',
'user-id'
)- Add DNS Records:
Type: CNAME
Name: chat.yourdomain.com
Value: tenant-id.nself.app
Type: TXT
Name: _nself-verification.chat.yourdomain.com
Value: [verification-token]
- Verify Domain:
const { verified, errors } = await tenantBrandingService.verifyDomain(
'tenant-id',
'chat.yourdomain.com'
)Each tenant automatically gets a subdomain:
- Format:
{tenantId}.nself.app - SSL: Automatic via Let's Encrypt
- No configuration required
Fetch tenant branding configuration.
Response:
{
"tenantId": "string",
"templateId": "default | slack | discord | telegram | whatsapp",
"customTemplate": {},
"customCSS": "string",
"logos": {
"primary": { "url": "string", "storageKey": "string" },
"square": { "url": "string", "storageKey": "string" },
"favicon": { "url": "string", "storageKey": "string" }
},
"domains": {
"primary": "string",
"custom": ["string"],
"subdomain": "string"
},
"themeOverrides": {
"light": {},
"dark": {}
}
}Update tenant branding configuration.
Request:
{
"updates": {
"appInfo": { "appName": "My App" },
"colors": { "primary": "#3B82F6" }
},
"userId": "string"
}Upload logo file.
Request: FormData with file, type, tenantId
Response:
{
"url": "string",
"storageKey": "string"
}Switch template.
Request:
{
"templateId": "slack",
"userId": "string",
"preserveCustomizations": true
}Apply custom CSS.
Request:
{
"css": "string",
"userId": "string"
}Configure custom domain.
Request:
{
"domain": "chat.yourdomain.com",
"userId": "string"
}Response:
{
"dnsRecords": [
{ "type": "CNAME", "name": "...", "value": "..." },
{ "type": "TXT", "name": "...", "value": "..." }
],
"verificationToken": "string"
}Export branding configuration as JSON.
Response: JSON file download
Import branding configuration from JSON.
Request: FormData with file, userId
Navigate to: /admin/branding
- Click "Template" tab
- Review available templates
- Preview each template
- Click "Apply Template"
- Confirm template switch
- Click "Theme" tab
- Switch between Light/Dark mode
- Adjust colors using color pickers
- Preview changes in real-time
- Click "Save Theme"
- Click "Logos" tab
- Upload primary logo (200x60px recommended)
- Upload square logo/icon (512x512px)
- Upload favicon (32x32px)
- Click "Domain" tab
- Enter custom domain
- Click "Configure"
- Add DNS records to your domain
- Click "Verify Domain"
- Click "Custom CSS" tab
- Write custom CSS
- Preview changes
- Click "Save CSS"
- Click "Export" button
- Save JSON file
- Use for backup or migration
import { loadTemplate, customizeTemplate } from '@/templates'
// Load base template
const baseTemplate = await loadTemplate('slack')
// Customize template
const customTemplate = customizeTemplate(baseTemplate, {
theme: {
light: {
primaryColor: '#FF0000',
},
},
layout: {
sidebarWidth: 280,
},
})import { tenantBrandingService } from '@/lib/white-label/tenant-branding'
// Listen for changes
const unsubscribe = tenantBrandingService.onBrandingChange((tenantId, branding) => {
console.log('Branding updated:', tenantId, branding)
// Update UI
})
// Cleanup
unsubscribe()import { generateCSSVariables, generateTemplateCSS } from '@/templates'
const template = await loadTemplate('whatsapp')
// Generate CSS variables
const cssVars = generateCSSVariables(template.theme.light)
// Generate complete CSS
const css = generateTemplateCSS(template)| Feature | Default | Slack | Discord | Telegram | |
|---|---|---|---|---|---|
| Threads | ✅ | ✅ | ✅ | ❌ | ❌ |
| Reactions | ✅ | ✅ | ✅ | ✅ | ✅ |
| Voice Messages | ✅ | ❌ | ✅ | ✅ | ✅ |
| Code Blocks | ✅ | ✅ | ✅ | ✅ | ❌ |
| Link Previews | ✅ | ✅ | ✅ | ✅ | ✅ |
| Read Receipts | ✅ | ✅ | ❌ | ✅ | ✅ |
| Typing Indicators | ✅ | ✅ | ✅ | ✅ | ✅ |
| Channel Categories | ✅ | ✅ | ✅ | ❌ | ❌ |
| Message Bubbles | ❌ | ❌ | ❌ | ✅ | ✅ |
| Server Hierarchy | ❌ | ❌ | ✅ | ❌ | ❌ |
- Maintain Contrast: Ensure text is readable on backgrounds (WCAG AA minimum)
- Consistent Colors: Use primary color consistently across buttons and links
- Dark Mode: Test both light and dark modes thoroughly
- Accessibility: Consider colorblind users (don't rely solely on color)
- Primary Logo: Horizontal logo with text, transparent background
- Square Logo: Icon-only version, centered, transparent background
- Favicon: Simple, recognizable at small sizes
- File Formats: PNG for raster, SVG for vector
- File Size: Keep under 500KB for performance
- Scope Selectors: Use specific selectors to avoid conflicts
- Test Thoroughly: Check in both light and dark modes
- Performance: Avoid expensive selectors (nested, attribute)
- Maintenance: Comment your code for future reference
- Validation: Use CSS linters before applying
- DNS Propagation: Allow 24-48 hours for DNS changes
- SSL Certificates: Automatic via Let's Encrypt (no action needed)
- Subdomains: Use for staging/testing environments
- Email: Configure SPF/DKIM records separately
Problem: Template changes don't appear
Solutions:
- Clear browser cache
- Check localStorage:
localStorage.removeItem('template-cache') - Verify API response: Check Network tab
- Check console for errors
Problem: Logo upload fails
Solutions:
- Check file size (must be < 5MB)
- Check file format (PNG, JPG, SVG, WebP only)
- Verify storage permissions
- Check server logs for errors
Problem: Custom domain won't verify
Solutions:
- Wait for DNS propagation (up to 48 hours)
- Verify DNS records with
digornslookup - Check for typos in DNS records
- Ensure no conflicting records
Problem: Custom CSS doesn't take effect
Solutions:
- Check for CSS syntax errors
- Ensure selectors are specific enough
- Clear browser cache
- Check if styles are being overridden
- Use
!importantsparingly
The white-label system is new in v0.9.0. To migrate:
-
Backup Current Configuration:
curl https://your-instance.com/api/config > config-backup.json -
Choose Template:
- Review available templates
- Select one that matches your current design
- Default template is closest to v0.8.0
-
Import Configuration:
# Via API curl -X POST \ -F "[email protected]" \ -F "userId=admin" \ https://your-instance.com/api/tenants/YOUR_ID/branding/import
-
Customize:
- Adjust colors to match old branding
- Upload existing logos
- Apply any custom CSS
-
Test Thoroughly:
- Check all pages
- Test both light and dark modes
- Verify mobile responsiveness
- All CSS is sanitized on the server
- No
<script>tags allowed - No
@importfrom external sources - No
url()to external resources - Limited to safe CSS properties
- TXT record verification prevents domain hijacking
- CNAME must point to platform domain
- SSL certificates issued after verification
- Regular verification checks
- Files scanned for malware
- Strict file type validation
- Size limits enforced
- Isolated storage per tenant
- CDN delivery for performance
- Templates lazy-loaded on demand
- Cached after first load
- Minimal bundle size impact
- Code splitting per template
- CSS variables for runtime switching
- Minimal DOM manipulation
- Hardware-accelerated animations
- Critical CSS inlined
- Optimized image formats (WebP)
- Responsive images with srcset
- CDN caching
- Lazy loading below the fold
For issues or questions:
- Documentation: Check this guide first
- Community: Discord channel #white-label
- Support: [email protected]
- Enterprise: [email protected]
- ✨ Initial white-label system release
- ✨ 5 pre-built templates
- ✨ Visual theme editor
- ✨ Logo management
- ✨ Custom domain support
- ✨ Custom CSS injection
- ✨ Export/import functionality
- ✨ Complete branding dashboard
This white-label system is part of nself-chat and subject to the same license terms.
Last Updated: February 3, 2026 Version: 0.9.0 Author: nself team