- Implement full web interface with Go html/template server - Add GX component library (buttons, dialogs, tables, forms, etc.) - Create scene/performer/studio/movie detail and listing pages - Add Adult Empire scraper for additional metadata sources - Implement movie support with database schema - Add import and sync services for data management - Include comprehensive API and frontend documentation - Add custom color scheme and responsive layout 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
30 KiB
Goondex HTML/CSS Templates Guide
For Frontend Developers - HTML/CSS Focus
This guide provides complete, ready-to-use HTML templates with Bootstrap styling. All JavaScript is included as simple copy-paste snippets with detailed comments.
Getting Started
All you need to know:
- Copy the HTML template you need
- The JavaScript is already included in
<script>tags - no modifications needed - Customize the CSS/Bootstrap classes for styling
- Use Bootstrap 5 components for quick UI elements
Table of Contents
- Complete Search Page
- Performer Import Form
- Bulk Import with Progress
- Simple Data Display
- Bootstrap Component Reference
Complete Search Page
File: search.html
Copy this entire file - it's ready to use!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Goondex - Search</title>
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Custom CSS -->
<style>
body {
background-color: #f8f9fa;
}
.search-container {
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
}
.performer-card {
transition: transform 0.2s;
height: 100%;
}
.performer-card:hover {
transform: translateY(-5px);
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
}
.performer-image {
height: 300px;
object-fit: cover;
}
.no-results {
text-align: center;
padding: 3rem;
color: #6c757d;
}
</style>
</head>
<body>
<!-- Navigation -->
<nav class="navbar navbar-dark bg-dark">
<div class="container-fluid">
<a class="navbar-brand" href="/">Goondex</a>
</div>
</nav>
<!-- Main Content -->
<div class="search-container">
<h1 class="mb-4">Search</h1>
<!-- Search Form -->
<form id="search-form" class="mb-5">
<div class="input-group input-group-lg">
<input
type="text"
id="search-input"
class="form-control"
placeholder="Search performers, studios, scenes..."
required
>
<button type="submit" class="btn btn-primary px-5">
Search
</button>
</div>
</form>
<!-- Loading Spinner (hidden by default) -->
<div id="loading" class="text-center" style="display: none;">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
<p class="mt-2">Searching...</p>
</div>
<!-- Results Container -->
<div id="results"></div>
</div>
<!-- JavaScript -->
<script>
// Configuration - change this if your server runs on a different port
const API_BASE = 'http://localhost:8080';
// Get elements
const form = document.getElementById('search-form');
const input = document.getElementById('search-input');
const loading = document.getElementById('loading');
const results = document.getElementById('results');
// When form is submitted
form.addEventListener('submit', async function(event) {
event.preventDefault(); // Don't reload the page
const searchTerm = input.value.trim();
if (!searchTerm) return;
// Show loading, hide results
loading.style.display = 'block';
results.innerHTML = '';
try {
// Make API request
const response = await fetch(
`${API_BASE}/api/search?q=${encodeURIComponent(searchTerm)}`
);
const data = await response.json();
// Hide loading
loading.style.display = 'none';
// Check if successful
if (!data.success) {
results.innerHTML = `
<div class="alert alert-danger">
${data.message}
</div>
`;
return;
}
// Display results
displayResults(data.data);
} catch (error) {
loading.style.display = 'none';
results.innerHTML = `
<div class="alert alert-danger">
Error: ${error.message}
</div>
`;
}
});
// Function to display search results
function displayResults(data) {
const totalResults = data.total;
if (totalResults === 0) {
results.innerHTML = `
<div class="no-results">
<h3>No results found</h3>
<p>Try a different search term</p>
</div>
`;
return;
}
let html = `<h2 class="mb-4">${totalResults} Results Found</h2>`;
// Performers Section
if (data.performers && data.performers.length > 0) {
html += `
<h3 class="mt-4 mb-3">Performers (${data.performers.length})</h3>
<div class="row row-cols-1 row-cols-md-3 row-cols-lg-4 g-4 mb-5">
`;
data.performers.forEach(performer => {
const imageUrl = performer.image_url || '/static/placeholder.jpg';
const nationality = performer.nationality ? getFlagEmoji(performer.nationality) : '';
html += `
<div class="col">
<div class="card performer-card">
<img
src="${imageUrl}"
class="card-img-top performer-image"
alt="${performer.name}"
onerror="this.src='/static/placeholder.jpg'"
>
<div class="card-body">
<h5 class="card-title">${performer.name}</h5>
<p class="card-text">
${nationality} ${performer.gender || ''}
</p>
<a href="/performers/${performer.id}" class="btn btn-sm btn-primary">
View Profile
</a>
</div>
</div>
</div>
`;
});
html += `</div>`;
}
// Studios Section
if (data.studios && data.studios.length > 0) {
html += `
<h3 class="mt-4 mb-3">Studios (${data.studios.length})</h3>
<div class="list-group mb-5">
`;
data.studios.forEach(studio => {
html += `
<a href="/studios/${studio.id}" class="list-group-item list-group-item-action">
<div class="d-flex w-100 justify-content-between">
<h5 class="mb-1">${studio.name}</h5>
</div>
${studio.description ? `<p class="mb-1">${studio.description}</p>` : ''}
</a>
`;
});
html += `</div>`;
}
// Scenes Section
if (data.scenes && data.scenes.length > 0) {
html += `
<h3 class="mt-4 mb-3">Scenes (${data.scenes.length})</h3>
<div class="list-group mb-5">
`;
data.scenes.forEach(scene => {
html += `
<a href="/scenes/${scene.id}" class="list-group-item list-group-item-action">
<div class="d-flex w-100 justify-content-between">
<h5 class="mb-1">${scene.title}</h5>
${scene.date ? `<small>${scene.date}</small>` : ''}
</div>
${scene.description ? `<p class="mb-1">${scene.description.substring(0, 200)}...</p>` : ''}
</a>
`;
});
html += `</div>`;
}
results.innerHTML = html;
}
// Helper function: Convert country code to flag emoji
function getFlagEmoji(countryCode) {
const codePoints = countryCode
.toUpperCase()
.split('')
.map(char => 127397 + char.charCodeAt());
return String.fromCodePoint(...codePoints);
}
</script>
</body>
</html>
Performer Import Form
File: import-performer.html
Simple form to search and import performers from TPDB.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Import Performer - Goondex</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
.import-container {
max-width: 600px;
margin: 3rem auto;
padding: 2rem;
}
.success-message {
animation: slideDown 0.3s ease-out;
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>
</head>
<body>
<nav class="navbar navbar-dark bg-dark">
<div class="container-fluid">
<a class="navbar-brand" href="/">Goondex</a>
</div>
</nav>
<div class="import-container">
<h1 class="mb-4">Import Performer</h1>
<div class="card">
<div class="card-body">
<p class="card-text text-muted">
Search for a performer by name. All matching results from TPDB will be imported.
</p>
<form id="import-form">
<div class="mb-3">
<label for="performer-name" class="form-label">Performer Name</label>
<input
type="text"
class="form-control form-control-lg"
id="performer-name"
placeholder="Enter performer name..."
required
>
</div>
<button type="submit" class="btn btn-primary btn-lg w-100" id="submit-btn">
<span id="button-text">Import Performer</span>
<span id="button-spinner" class="spinner-border spinner-border-sm" style="display: none;"></span>
</button>
</form>
<!-- Result message (hidden by default) -->
<div id="result-message" class="mt-3" style="display: none;"></div>
</div>
</div>
</div>
<script>
const API_BASE = 'http://localhost:8080';
const form = document.getElementById('import-form');
const input = document.getElementById('performer-name');
const submitBtn = document.getElementById('submit-btn');
const buttonText = document.getElementById('button-text');
const buttonSpinner = document.getElementById('button-spinner');
const resultMessage = document.getElementById('result-message');
form.addEventListener('submit', async function(event) {
event.preventDefault();
const performerName = input.value.trim();
if (!performerName) return;
// Show loading state
submitBtn.disabled = true;
buttonText.textContent = 'Importing...';
buttonSpinner.style.display = 'inline-block';
resultMessage.style.display = 'none';
try {
// Make API request
const response = await fetch(`${API_BASE}/api/import/performer`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
query: performerName
})
});
const data = await response.json();
// Reset button
submitBtn.disabled = false;
buttonText.textContent = 'Import Performer';
buttonSpinner.style.display = 'none';
// Show result
if (data.success) {
resultMessage.className = 'alert alert-success success-message mt-3';
resultMessage.innerHTML = `
<strong>Success!</strong> ${data.message}<br>
<small>Imported ${data.data.imported} out of ${data.data.found} found</small>
`;
resultMessage.style.display = 'block';
// Clear form
input.value = '';
} else {
resultMessage.className = 'alert alert-danger mt-3';
resultMessage.innerHTML = `<strong>Error:</strong> ${data.message}`;
resultMessage.style.display = 'block';
}
} catch (error) {
submitBtn.disabled = false;
buttonText.textContent = 'Import Performer';
buttonSpinner.style.display = 'none';
resultMessage.className = 'alert alert-danger mt-3';
resultMessage.innerHTML = `<strong>Error:</strong> ${error.message}`;
resultMessage.style.display = 'block';
}
});
</script>
</body>
</html>
Bulk Import with Progress
File: bulk-import.html
Import all performers with a real-time progress bar.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bulk Import - Goondex</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
.import-container {
max-width: 800px;
margin: 3rem auto;
padding: 2rem;
}
.progress {
height: 30px;
}
.progress-bar {
font-size: 14px;
line-height: 30px;
}
.import-stats {
display: none;
margin-top: 1rem;
}
.stat-box {
text-align: center;
padding: 1rem;
background: #f8f9fa;
border-radius: 8px;
}
.stat-number {
font-size: 2rem;
font-weight: bold;
color: #0d6efd;
}
.stat-label {
color: #6c757d;
font-size: 0.875rem;
}
</style>
</head>
<body>
<nav class="navbar navbar-dark bg-dark">
<div class="container-fluid">
<a class="navbar-brand" href="/">Goondex</a>
</div>
</nav>
<div class="import-container">
<h1 class="mb-4">Bulk Import Performers</h1>
<div class="card">
<div class="card-body">
<p class="text-muted">
Import all performers from your database. This will fetch full metadata from TPDB.
</p>
<!-- Start Button -->
<button id="start-btn" class="btn btn-primary btn-lg w-100 mb-4">
Start Import
</button>
<!-- Progress Bar (hidden initially) -->
<div id="progress-container" style="display: none;">
<div class="progress mb-2">
<div
id="progress-bar"
class="progress-bar progress-bar-striped progress-bar-animated"
role="progressbar"
style="width: 0%"
>
0%
</div>
</div>
<div id="status-text" class="alert alert-info">
Preparing to import...
</div>
<!-- Statistics (shown on completion) -->
<div id="import-stats" class="import-stats">
<div class="row g-3">
<div class="col-md-3">
<div class="stat-box">
<div id="stat-total" class="stat-number">0</div>
<div class="stat-label">Total</div>
</div>
</div>
<div class="col-md-3">
<div class="stat-box">
<div id="stat-imported" class="stat-number">0</div>
<div class="stat-label">Imported</div>
</div>
</div>
<div class="col-md-3">
<div class="stat-box">
<div id="stat-skipped" class="stat-number">0</div>
<div class="stat-label">Skipped</div>
</div>
</div>
<div class="col-md-3">
<div class="stat-box">
<div id="stat-errors" class="stat-number">0</div>
<div class="stat-label">Errors</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
const API_BASE = 'http://localhost:8080';
const startBtn = document.getElementById('start-btn');
const progressContainer = document.getElementById('progress-container');
const progressBar = document.getElementById('progress-bar');
const statusText = document.getElementById('status-text');
const importStats = document.getElementById('import-stats');
startBtn.addEventListener('click', function() {
// Hide button, show progress
startBtn.style.display = 'none';
progressContainer.style.display = 'block';
importStats.style.display = 'none';
// Create EventSource to listen for progress updates
const eventSource = new EventSource(
`${API_BASE}/api/import/all-performers/progress`
);
// When we receive a message
eventSource.onmessage = function(event) {
const update = JSON.parse(event.data);
// Check for error
if (update.error) {
statusText.className = 'alert alert-danger';
statusText.textContent = `Error: ${update.error}`;
progressBar.className = 'progress-bar bg-danger';
progressBar.style.width = '100%';
progressBar.textContent = 'Error';
startBtn.style.display = 'block';
eventSource.close();
return;
}
// Check for completion
if (update.complete) {
const result = update.result;
statusText.className = 'alert alert-success';
statusText.innerHTML = `
<strong>Import Complete!</strong><br>
Successfully imported ${result.imported} out of ${result.total} performers
`;
progressBar.className = 'progress-bar bg-success';
progressBar.style.width = '100%';
progressBar.textContent = '100%';
// Show statistics
document.getElementById('stat-total').textContent = result.total;
document.getElementById('stat-imported').textContent = result.imported;
document.getElementById('stat-skipped').textContent = result.skipped;
document.getElementById('stat-errors').textContent = result.errors;
importStats.style.display = 'block';
startBtn.textContent = 'Import Again';
startBtn.style.display = 'block';
eventSource.close();
return;
}
// Progress update
const percent = Math.round((update.current / update.total) * 100);
progressBar.style.width = `${percent}%`;
progressBar.textContent = `${percent}%`;
statusText.className = 'alert alert-info';
statusText.innerHTML = `
<strong>Importing:</strong> ${update.name}<br>
<small>Progress: ${update.current} / ${update.total}</small>
`;
};
// Handle connection errors
eventSource.onerror = function() {
statusText.className = 'alert alert-danger';
statusText.textContent = 'Connection error. Please try again.';
progressBar.className = 'progress-bar bg-danger';
startBtn.style.display = 'block';
eventSource.close();
};
});
</script>
</body>
</html>
Simple Data Display
File: performers-list.html
Display a list of performers from the database.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Performers - Goondex</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
.performer-grid {
max-width: 1400px;
margin: 2rem auto;
padding: 0 2rem;
}
.performer-card {
height: 100%;
transition: transform 0.2s;
}
.performer-card:hover {
transform: translateY(-5px);
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
.performer-image {
width: 100%;
height: 350px;
object-fit: cover;
}
.loading {
text-align: center;
padding: 3rem;
}
</style>
</head>
<body>
<nav class="navbar navbar-dark bg-dark">
<div class="container-fluid">
<a class="navbar-brand" href="/">Goondex</a>
</div>
</nav>
<div class="performer-grid">
<h1 class="mb-4">All Performers</h1>
<!-- Loading indicator -->
<div id="loading" class="loading">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
<p class="mt-2">Loading performers...</p>
</div>
<!-- Performers grid -->
<div id="performers-grid" class="row row-cols-1 row-cols-md-3 row-cols-lg-5 g-4"></div>
</div>
<script>
const API_BASE = 'http://localhost:8080';
// Load performers when page loads
window.addEventListener('load', loadPerformers);
async function loadPerformers() {
try {
// Fetch from search API with empty query (returns all)
const response = await fetch(`${API_BASE}/api/search?q=`);
const data = await response.json();
// Hide loading
document.getElementById('loading').style.display = 'none';
if (!data.success) {
alert('Error: ' + data.message);
return;
}
// Display performers
displayPerformers(data.data.performers || []);
} catch (error) {
document.getElementById('loading').style.display = 'none';
alert('Error loading performers: ' + error.message);
}
}
function displayPerformers(performers) {
const grid = document.getElementById('performers-grid');
if (performers.length === 0) {
grid.innerHTML = `
<div class="col-12 text-center text-muted py-5">
<h3>No performers found</h3>
<p>Import some performers to get started</p>
</div>
`;
return;
}
let html = '';
performers.forEach(performer => {
const imageUrl = performer.image_url || '/static/placeholder.jpg';
const flag = performer.nationality ? getFlagEmoji(performer.nationality) : '';
const age = calculateAge(performer.birthday);
html += `
<div class="col">
<div class="card performer-card">
<img
src="${imageUrl}"
class="performer-image"
alt="${performer.name}"
onerror="this.src='/static/placeholder.jpg'"
>
<div class="card-body">
<h5 class="card-title">${performer.name}</h5>
<p class="card-text">
${flag} ${performer.gender || ''}<br>
${age ? `Age: ${age}` : ''}
</p>
<a href="/performers/${performer.id}" class="btn btn-primary btn-sm w-100">
View Profile
</a>
</div>
</div>
</div>
`;
});
grid.innerHTML = html;
}
// Helper: Convert country code to flag emoji
function getFlagEmoji(countryCode) {
const codePoints = countryCode
.toUpperCase()
.split('')
.map(char => 127397 + char.charCodeAt());
return String.fromCodePoint(...codePoints);
}
// Helper: Calculate age from birthday
function calculateAge(birthday) {
if (!birthday) return null;
const birthDate = new Date(birthday);
const today = new Date();
let age = today.getFullYear() - birthDate.getFullYear();
const monthDiff = today.getMonth() - birthDate.getMonth();
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
age--;
}
return age;
}
</script>
</body>
</html>
Bootstrap Component Reference
Quick reference for Bootstrap 5 components to use in your pages.
Buttons
<!-- Primary button -->
<button class="btn btn-primary">Primary</button>
<!-- Success button -->
<button class="btn btn-success">Success</button>
<!-- Danger button -->
<button class="btn btn-danger">Danger</button>
<!-- Large button -->
<button class="btn btn-primary btn-lg">Large Button</button>
<!-- Small button -->
<button class="btn btn-primary btn-sm">Small Button</button>
<!-- Full width button -->
<button class="btn btn-primary w-100">Full Width</button>
<!-- Disabled button -->
<button class="btn btn-primary" disabled>Disabled</button>
Alerts
<!-- Success alert -->
<div class="alert alert-success">
<strong>Success!</strong> Your action was successful.
</div>
<!-- Error alert -->
<div class="alert alert-danger">
<strong>Error!</strong> Something went wrong.
</div>
<!-- Warning alert -->
<div class="alert alert-warning">
<strong>Warning!</strong> Please be careful.
</div>
<!-- Info alert -->
<div class="alert alert-info">
<strong>Info:</strong> Here's some information.
</div>
<!-- Dismissible alert -->
<div class="alert alert-success alert-dismissible fade show">
Success message
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
Cards
<!-- Basic card -->
<div class="card">
<img src="image.jpg" class="card-img-top" alt="...">
<div class="card-body">
<h5 class="card-title">Card Title</h5>
<p class="card-text">Card description goes here.</p>
<a href="#" class="btn btn-primary">Go somewhere</a>
</div>
</div>
<!-- Card with header -->
<div class="card">
<div class="card-header">
Featured
</div>
<div class="card-body">
<h5 class="card-title">Special title</h5>
<p class="card-text">Card content</p>
</div>
</div>
Forms
<!-- Basic form -->
<form>
<div class="mb-3">
<label for="input1" class="form-label">Email address</label>
<input type="email" class="form-control" id="input1">
</div>
<div class="mb-3">
<label for="input2" class="form-label">Password</label>
<input type="password" class="form-control" id="input2">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<!-- Large input -->
<input type="text" class="form-control form-control-lg" placeholder="Large input">
<!-- Small input -->
<input type="text" class="form-control form-control-sm" placeholder="Small input">
<!-- Select dropdown -->
<select class="form-select">
<option selected>Choose...</option>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
</select>
Progress Bars
<!-- Basic progress bar -->
<div class="progress">
<div class="progress-bar" style="width: 50%">50%</div>
</div>
<!-- Colored progress bars -->
<div class="progress">
<div class="progress-bar bg-success" style="width: 75%"></div>
</div>
<div class="progress">
<div class="progress-bar bg-danger" style="width: 100%"></div>
</div>
<!-- Striped and animated -->
<div class="progress">
<div class="progress-bar progress-bar-striped progress-bar-animated" style="width: 75%"></div>
</div>
Spinners
<!-- Basic spinner -->
<div class="spinner-border" role="status">
<span class="visually-hidden">Loading...</span>
</div>
<!-- Colored spinners -->
<div class="spinner-border text-primary"></div>
<div class="spinner-border text-success"></div>
<div class="spinner-border text-danger"></div>
<!-- Small spinner -->
<div class="spinner-border spinner-border-sm"></div>
<!-- Growing spinner -->
<div class="spinner-grow" role="status">
<span class="visually-hidden">Loading...</span>
</div>
Lists
<!-- Basic list group -->
<ul class="list-group">
<li class="list-group-item">Item 1</li>
<li class="list-group-item">Item 2</li>
<li class="list-group-item">Item 3</li>
</ul>
<!-- Clickable list -->
<div class="list-group">
<a href="#" class="list-group-item list-group-item-action">
Clickable item
</a>
<a href="#" class="list-group-item list-group-item-action active">
Active item
</a>
</div>
Grid System
<!-- Responsive grid (1 column on mobile, 3 on tablet, 4 on desktop) -->
<div class="row row-cols-1 row-cols-md-3 row-cols-lg-4 g-4">
<div class="col">Column 1</div>
<div class="col">Column 2</div>
<div class="col">Column 3</div>
<div class="col">Column 4</div>
</div>
<!-- Two column layout -->
<div class="row">
<div class="col-md-6">Left column</div>
<div class="col-md-6">Right column</div>
</div>
<!-- Sidebar layout -->
<div class="row">
<div class="col-md-3">Sidebar</div>
<div class="col-md-9">Main content</div>
</div>
CSS Tips
Spacing Utilities
<!-- Margin -->
<div class="m-3">Margin on all sides</div>
<div class="mt-3">Margin top</div>
<div class="mb-3">Margin bottom</div>
<div class="mx-3">Margin left and right</div>
<div class="my-3">Margin top and bottom</div>
<!-- Padding -->
<div class="p-3">Padding on all sides</div>
<div class="pt-3">Padding top</div>
<div class="pb-3">Padding bottom</div>
<div class="px-3">Padding left and right</div>
<div class="py-3">Padding top and bottom</div>
<!-- Sizes: 0, 1, 2, 3, 4, 5 (0 = 0, 5 = 3rem) -->
Text Utilities
<!-- Alignment -->
<div class="text-start">Left aligned</div>
<div class="text-center">Center aligned</div>
<div class="text-end">Right aligned</div>
<!-- Colors -->
<p class="text-primary">Primary text</p>
<p class="text-success">Success text</p>
<p class="text-danger">Danger text</p>
<p class="text-muted">Muted text</p>
<!-- Font weight -->
<p class="fw-bold">Bold text</p>
<p class="fw-normal">Normal text</p>
<p class="fw-light">Light text</p>
<!-- Font size -->
<p class="fs-1">Very large</p>
<p class="fs-5">Medium</p>
Display Utilities
<!-- Show/Hide -->
<div class="d-none">Hidden</div>
<div class="d-block">Shown as block</div>
<div class="d-inline-block">Inline block</div>
<div class="d-flex">Flexbox container</div>
<!-- Responsive display -->
<div class="d-none d-md-block">Hidden on mobile, shown on tablet+</div>
Quick JavaScript Snippets
Show/Hide Elements
// Hide element
document.getElementById('myElement').style.display = 'none';
// Show element
document.getElementById('myElement').style.display = 'block';
// Toggle visibility
const el = document.getElementById('myElement');
el.style.display = el.style.display === 'none' ? 'block' : 'none';
Change Text Content
document.getElementById('myElement').textContent = 'New text';
document.getElementById('myElement').innerHTML = '<strong>HTML</strong> text';
Add/Remove CSS Classes
// Add class
document.getElementById('myElement').classList.add('active');
// Remove class
document.getElementById('myElement').classList.remove('active');
// Toggle class
document.getElementById('myElement').classList.toggle('active');
Get Form Values
const inputValue = document.getElementById('myInput').value;
const selectValue = document.getElementById('mySelect').value;
Need Help?
All the JavaScript in these templates is ready to use - just copy and paste! If you need to customize something:
- Look for comments in the JavaScript (
// like this) - The
API_BASEconstant at the top controls the server URL - Use AI assistance to modify the JavaScript parts
- Focus on the HTML/CSS which you're already great at!
The templates are fully functional and follow Bootstrap best practices.