🎨 Theme Customization
Create beautiful custom themes for Xiuno BBS
Theme Overview
Default Themes
Xiuno BBS includes several themes out of the box:
- default: Clean modern Bootstrap 5 theme
- flat: Minimal flat design
- dark: Dark mode theme
- classic: Traditional forum layout
Themes are located in view/ directory. Each theme contains template files (*.htm) and assets (CSS, JS, images).
Theme Structure
view/
└── mytheme/
├── css/
│ ├── bootstrap.min.css
│ ├── style.css # Main theme styles
│ └── custom.css # Your customizations
├── js/
│ ├── bootstrap.min.js
│ └── theme.js # Theme JavaScript
├── img/
│ ├── logo.png
│ ├── favicon.ico
│ └── backgrounds/
├── htm/
│ ├── header.htm # Global header
│ ├── footer.htm # Global footer
│ ├── index.htm # Homepage template
│ ├── thread.htm # Thread list
│ ├── thread_show.htm # Single thread view
│ ├── user.htm # User profile
│ └── ...
└── conf.json # Theme metadata
Creating a Custom Theme
Step 1: Copy Base Theme
# Copy default theme as starting point
cd /home/xiuno/public_html/view
cp -r default mytheme
# Update theme metadata
nano mytheme/conf.json
Step 2: Theme Metadata
Edit view/mytheme/conf.json:
{
"name": "My Custom Theme",
"version": "1.0.0",
"author": "Your Name",
"brief": "A beautiful custom theme",
"bbs_version": "4.0.0",
"screenshot": "screenshot.png",
"responsive": true,
"dark_mode": false
}
Step 3: Customize Styles
Edit view/mytheme/css/style.css:
/* Custom Color Scheme */
:root {
--primary-color: #667eea;
--secondary-color: #764ba2;
--text-color: #2d3748;
--bg-color: #f7fafc;
--border-color: #e2e8f0;
--success-color: #48bb78;
--danger-color: #f56565;
--warning-color: #ed8936;
}
/* Header Styling */
.header {
background: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%);
padding: 20px 0;
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
}
.logo {
font-size: 28px;
font-weight: 700;
color: white;
text-decoration: none;
}
/* Navigation */
.nav-menu {
display: flex;
gap: 30px;
align-items: center;
}
.nav-menu a {
color: rgba(255,255,255,0.9);
text-decoration: none;
font-weight: 500;
transition: all 0.3s;
}
.nav-menu a:hover {
color: white;
transform: translateY(-2px);
}
/* Card Design */
.thread-card {
background: white;
border-radius: 12px;
padding: 20px;
margin-bottom: 15px;
box-shadow: 0 2px 10px rgba(0,0,0,0.05);
transition: transform 0.3s, box-shadow 0.3s;
}
.thread-card:hover {
transform: translateY(-3px);
box-shadow: 0 8px 25px rgba(0,0,0,0.1);
}
/* Buttons */
.btn-primary {
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
border: none;
padding: 12px 30px;
border-radius: 8px;
color: white;
font-weight: 600;
cursor: pointer;
transition: transform 0.2s;
}
.btn-primary:hover {
transform: scale(1.05);
}
/* User Avatar */
.avatar {
width: 50px;
height: 50px;
border-radius: 50%;
border: 3px solid var(--primary-color);
}
/* Footer */
.footer {
background: #2d3748;
color: rgba(255,255,255,0.8);
padding: 40px 0;
margin-top: 60px;
}
Template Customization
Header Template
Edit view/mytheme/htm/header.htm:
<!DOCTYPE html>
<html lang="<?php echo $lang; ?>">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo $title; ?> - <?php echo $conf['sitename']; ?></title>
<!-- Meta Tags -->
<meta name="description" content="<?php echo $description; ?>">
<meta name="keywords" content="<?php echo $keywords; ?>">
<!-- Stylesheets -->
<link rel="stylesheet" href="<?php echo $conf['siteurl']; ?>view/mytheme/css/bootstrap.min.css">
<link rel="stylesheet" href="<?php echo $conf['siteurl']; ?>view/mytheme/css/style.css">
<!-- Favicon -->
<link rel="icon" href="<?php echo $conf['siteurl']; ?>view/mytheme/img/favicon.ico">
</head>
<body>
<header class="header">
<div class="container">
<div class="nav-wrapper">
<a href="<?php echo $conf['siteurl']; ?>" class="logo">
<img src="<?php echo $conf['siteurl']; ?>view/mytheme/img/logo.png" alt="Logo">
<?php echo $conf['sitename']; ?>
</a>
<nav class="nav-menu">
<a href="<?php echo $conf['siteurl']; ?>">Home</a>
<a href="<?php echo $conf['siteurl']; ?>docs/">Docs</a>
<a href="<?php echo $conf['siteurl']; ?>downloads/">Downloads</a>
<a href="<?php echo $conf['siteurl']; ?>forum.php">Forum</a>
<?php if($uid): ?>
<a href="?user-<?php echo $uid; ?>.htm"><?php echo $user['username']; ?></a>
<a href="?user-logout.htm">Logout</a>
<?php else: ?>
<a href="?user-login.htm">Login</a>
<a href="?user-register.htm">Register</a>
<?php endif; ?>
</nav>
</div>
</div>
</header>
<main class="main-content">
Thread List Template
Edit view/mytheme/htm/thread.htm:
<div class="container">
<div class="forum-header">
<h1><?php echo $forum['name']; ?></h1>
<p><?php echo $forum['brief']; ?></p>
</div>
<div class="thread-list">
<?php foreach($threads as $thread): ?>
<div class="thread-card">
<div class="thread-header">
<div class="user-info">
<img src="<?php echo user_avatar($thread['uid']); ?>" class="avatar">
<div>
<strong><?php echo $thread['username']; ?></strong>
<small><?php echo humantime($thread['create_date']); ?></small>
</div>
</div>
</div>
<h3 class="thread-title">
<a href="?thread-<?php echo $thread['tid']; ?>.htm">
<?php if($thread['top']): ?>
<span class="badge badge-top">📌 Pinned</span>
<?php endif; ?>
<?php echo $thread['subject']; ?>
</a>
</h3>
<div class="thread-meta">
<span>💬 <?php echo $thread['posts']; ?> replies</span>
<span>👁️ <?php echo $thread['views']; ?> views</span>
<span>📅 <?php echo humantime($thread['last_date']); ?></span>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
Responsive Design
Mobile-First CSS
/* Base styles (mobile) */
.container {
width: 100%;
padding: 0 15px;
}
.thread-card {
padding: 15px;
}
/* Tablet (768px+) */
@media (min-width: 768px) {
.container {
max-width: 750px;
margin: 0 auto;
}
.nav-menu {
display: flex;
}
}
/* Desktop (1024px+) */
@media (min-width: 1024px) {
.container {
max-width: 1200px;
}
.thread-list {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20px;
}
}
/* Hide mobile menu on desktop */
.mobile-menu-toggle {
display: block;
}
@media (min-width: 768px) {
.mobile-menu-toggle {
display: none;
}
}
Color Schemes
Purple Gradient (Current)
Primary
#667eea
Secondary
#764ba2
Alternative Color Schemes
/* Blue Professional */
:root {
--primary-color: #2563eb;
--secondary-color: #1e40af;
}
/* Green Nature */
:root {
--primary-color: #10b981;
--secondary-color: #059669;
}
/* Orange Energy */
:root {
--primary-color: #f97316;
--secondary-color: #ea580c;
}
/* Red Bold */
:root {
--primary-color: #ef4444;
--secondary-color: #dc2626;
}
Dark Mode Theme
Dark Mode CSS
/* Dark mode color scheme */
body.dark-mode {
--text-color: #e2e8f0;
--bg-color: #1a202c;
--card-bg: #2d3748;
--border-color: #4a5568;
}
body.dark-mode .thread-card {
background: var(--card-bg);
color: var(--text-color);
}
body.dark-mode .header {
background: #2d3748;
border-bottom: 1px solid var(--border-color);
}
/* Dark mode toggle button */
.dark-mode-toggle {
cursor: pointer;
padding: 10px;
border-radius: 50%;
background: rgba(255,255,255,0.1);
}
body.dark-mode .dark-mode-toggle {
background: rgba(255,255,255,0.2);
}
Dark Mode JavaScript
Add to view/mytheme/js/theme.js:
// Dark mode toggle
const darkModeToggle = document.getElementById('dark-mode-toggle');
const body = document.body;
// Load saved preference
const darkMode = localStorage.getItem('darkMode');
if (darkMode === 'enabled') {
body.classList.add('dark-mode');
}
// Toggle dark mode
darkModeToggle.addEventListener('click', () => {
body.classList.toggle('dark-mode');
// Save preference
if (body.classList.contains('dark-mode')) {
localStorage.setItem('darkMode', 'enabled');
} else {
localStorage.setItem('darkMode', 'disabled');
}
});
Custom Fonts
Using Google Fonts
<!-- In header.htm -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap" rel="stylesheet">
/* In style.css */
body {
font-family: 'Poppins', sans-serif;
}
h1, h2, h3, h4, h5, h6 {
font-family: 'Poppins', sans-serif;
font-weight: 600;
}
code, pre {
font-family: 'JetBrains Mono', monospace;
}
Advanced Customization
Custom Homepage
Create view/mytheme/htm/index_custom.htm:
<div class="hero-section">
<div class="container">
<h1>Welcome to Xiuno Wiki</h1>
<p>The fastest, most powerful PHP forum platform</p>
<div class="hero-buttons">
<a href="/docs/" class="btn btn-primary">Get Started</a>
<a href="/downloads/" class="btn btn-secondary">Download</a>
</div>
</div>
</div>
<div class="features-section">
<div class="container">
<div class="feature-grid">
<div class="feature-card">
<div class="feature-icon">⚡</div>
<h3>Lightning Fast</h3>
<p>Optimized for speed with built-in caching</p>
</div>
<div class="feature-card">
<div class="feature-icon">🔒</div>
<h3>Secure</h3>
<p>Built with security best practices</p>
</div>
<div class="feature-card">
<div class="feature-icon">🎨</div>
<h3>Customizable</h3>
<p>Themes and plugins for endless possibilities</p>
</div>
</div>
</div>
</div>
Adding Animations
/* Fade in animation */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.thread-card {
animation: fadeIn 0.5s ease-out;
}
/* Smooth transitions */
* {
transition: all 0.3s ease;
}
/* Hover effects */
.thread-card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 30px rgba(0,0,0,0.15);
}
Theme Testing
Browser Testing
Test your theme in:
- Chrome/Edge (Chromium)
- Firefox
- Safari (macOS/iOS)
- Mobile browsers (iOS Safari, Chrome Mobile)
Responsive Testing
# Test different screen sizes
Desktop: 1920x1080, 1366x768
Tablet: 1024x768, 768x1024
Mobile: 375x667, 414x896, 360x640
Performance Optimization
/* Minify CSS */
npm install -g clean-css-cli
cleancss -o style.min.css style.css
/* Optimize images */
# Use tools like TinyPNG or ImageOptim
/* Lazy load images */
<img src="placeholder.jpg" data-src="image.jpg" class="lazy-load">
<script>
document.addEventListener("DOMContentLoaded", function() {
const lazyImages = document.querySelectorAll('.lazy-load');
const imageObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
imageObserver.unobserve(img);
}
});
});
lazyImages.forEach(img => imageObserver.observe(img));
});
</script>
Publishing Your Theme
- Test thoroughly on multiple devices
- Create screenshot.png (1200x800px)
- Write README.md with:
- Theme description and features
- Installation instructions
- Customization options
- Browser compatibility
- Changelog
- Package as ZIP:
mytheme-1.0.0.zip - Submit to Theme Directory
📦 Package Structure:
ZIP should contain theme folder directly:
ZIP should contain theme folder directly:
✓ mytheme/css/style.css❌ mytheme-1.0/mytheme/css/style.css
⚠️ Best Practices:
• Keep CSS organized and well-commented
• Use CSS variables for easy customization
• Optimize images and minimize HTTP requests
• Test on slow connections (throttle network)
• Validate HTML and CSS
• Ensure accessibility (WCAG 2.1)
• Support RTL languages if possible
• Keep CSS organized and well-commented
• Use CSS variables for easy customization
• Optimize images and minimize HTTP requests
• Test on slow connections (throttle network)
• Validate HTML and CSS
• Ensure accessibility (WCAG 2.1)
• Support RTL languages if possible
Resources
- Download Themes - Browse available themes
- Theme Forum - Get help and share themes
- Bootstrap Documentation - CSS framework
- Google Fonts - Free web fonts