🎨 Templates & Theming

PhPstrap uses Bootstrap 5. Keep all visual overrides in /css/custom.css so core and modules can update safely. This guide shows the load order, token strategy, dark mode, and patterns you can reuse.

TL;DR: Load Bootstrap first, then custom.css. Set colors via CSS variables. Avoid editing vendor files. Prefer utility classes; only add minimal custom classes.

1) Where styles live

<!-- includes/header.php -->
<link rel="stylesheet" href="/assets/bootstrap.min.css">
<link rel="stylesheet" href="/css/custom.css">

2) Color system & tokens

Expose a small set of brand variables and let Bootstrap map them to components.

/* /css/custom.css */

/* 2.1 Brand tokens */
:root{
  --brand-primary: #0d6efd; /* THEME_COLOR fallback */
  --brand-secondary: #6c757d;
  --brand-accent: #6f42c1;
  --brand-success: #198754;
  --brand-warning: #ffc107;
  --brand-danger:  #dc3545;
  --brand-info:    #0dcaf0;

  /* Surface & text */
  --surface-0: #ffffff;
  --surface-1: #f8f9fa;
  --text-0:    #111318;
  --text-1:    #495057;

  /* 2.2 Bootstrap CSS vars override (affects components) */
  --bs-primary: var(--brand-primary);
  --bs-secondary: var(--brand-secondary);
  --bs-link-color: var(--brand-primary);
  --bs-link-hover-color: color-mix(in oklab, var(--brand-primary) 80%, black);
}

/* 2.3 Dark mode (auto or attribute) */
@media (prefers-color-scheme: dark){
  :root{
    --surface-0: #0f1115;
    --surface-1: #151922;
    --text-0:    #e8eaed;
    --text-1:    #b8c0cc;
  }
}
[data-bs-theme="dark"]{
  --surface-0:#0f1115; --surface-1:#151922;
  --text-0:#e8eaed; --text-1:#b8c0cc;
  --bs-body-bg: var(--surface-0);
  --bs-body-color: var(--text-0);
}

3) Typography & spacing

Use Bootstrap utilities first (.fs-*, .fw-*, .lh-*, .mb-*). Add minimal global tweaks:

body{ background-color: var(--bs-body-bg, var(--surface-0)); color:var(--bs-body-color, var(--text-0)); }
.lead{ color: var(--text-1); }
/* Optional tighter containers on large screens */
@media (min-width:1200px){ .container{ max-width: 1120px; } }

4) Component polish

Small, reusable touches for cards, buttons, forms.

/* Cards */
.card{ border:1px solid var(--bs-border-color, #dee2e6); }
.card.shadow-sm{ transition: transform .18s ease, box-shadow .18s ease; }
.card.shadow-sm:hover{ transform: translateY(-2px); }

/* Buttons */
.btn-primary{ box-shadow: 0 1px 0 rgba(0,0,0,.05); }
.btn-outline-primary:hover{ background:color-mix(in oklab, var(--brand-primary) 12%, white); }

/* Forms */
.form-control::placeholder{ color: color-mix(in oklab, var(--text-1) 80%, white); }
.input-group-text{ background: transparent; }

/* Badges */
.badge{ letter-spacing:.02em; }

5) Accessibility defaults

:focus-visible{
  outline: 3px solid color-mix(in oklab, var(--brand-primary) 70%, white);
  outline-offset: 2px;
  border-radius: .5rem;
}

6) Styling the non-logged LexBoard template

The sample landing (public) page uses a few helpers. Move them into custom.css and keep the page clean.

/* === LexBoard public template helpers === */

/* 6.1 Region cards */
.region-card{ border:0; }
.region-card .card-body{ padding:1rem 1.1rem; }
.region-card:hover{ transform: translateY(-2px); }
.region-disabled{ opacity:.55; filter: grayscale(30%); pointer-events:none; }

/* 6.2 KB badge size */
.kb-badge{ font-size:.8rem; letter-spacing:.02em; }

/* 6.3 Hero panel */
.search-hero{
  background:
    radial-gradient(1200px 600px at 50% -10%, color-mix(in oklab, var(--brand-primary) 8%, transparent), transparent 60%),
    linear-gradient(135deg, var(--bs-body-bg) 0%, var(--bs-body-bg) 60%);
  border: 1px solid var(--bs-border-color, #dee2e6);
}

/* 6.4 Input group icon alignment */
#lex-search-addon .fas{ opacity:.7; }

Then remove the inline <style> block from the PHP template and keep only semantic markup.


7) Page layout patterns


8) Dark mode

Support both OS preference and manual toggle. Add a simple body attribute switcher if desired.

// Example toggle (place in a small JS file)
const tgl = document.querySelector('[data-theme-toggle]');
if (tgl){
  tgl.addEventListener('click', () => {
    const dark = document.documentElement.getAttribute('data-bs-theme') === 'dark';
    document.documentElement.setAttribute('data-bs-theme', dark ? 'light' : 'dark');
    localStorage.setItem('theme', dark ? 'light' : 'dark');
  });
}
(function init(){
  const saved = localStorage.getItem('theme');
  if (saved) document.documentElement.setAttribute('data-bs-theme', saved);
})();

.site-logo{ height: 28px; }
[data-bs-theme="dark"] .site-logo--primary{ display:none; }
[data-bs-theme="dark"] .site-logo--inverted{ display:inline-block; }

10) Performance tips


11) Theming checklist


12) FAQ

Q: Can I override Bootstrap variables with SCSS?
A: Yes, but to keep things simple, start with plain CSS variables in custom.css. If you move to SCSS, compile to a single CSS file and keep the same load order.

Questions or ideas? Open a discussion or issue: GitHub Issues.