global topbar #6

Merged
archvillainette merged 6 commits from feat/global-topbar-20260626 into main 2026-06-26 08:27:49 +00:00
8 changed files with 33 additions and 225 deletions
Showing only changes of commit b23720c42d - Show all commits
+1 -1
View File
@@ -2,7 +2,7 @@
## Project
This repository is `nodebb-theme-westgate`, a NodeBB theme for the Shadows Over Westgate forum.
This repository is `nodebb-theme-westgate`, a NodeBB theme for the Shadows Over Westgate forum and website.
It is forked from `nodebb-theme-quickstart` and should remain a small, focused NodeBB child theme rather than a full replacement for NodeBB or Harmony.
@@ -39,10 +39,9 @@ drafts, and authentication entry points.
- `theme.json` declares `baseTheme: "nodebb-theme-harmony"`.
- `theme.scss` imports Harmony first, then focused Westgate partials under
`scss/westgate/`.
- `templates/header.tpl` currently matches the Harmony layout pattern: a
`layout-container`, `partials/sidebar-left.tpl`, `#panel`, and
`partials/header/brand.tpl`.
- Harmony's `footer.tpl` imports `partials/sidebar-right.tpl`.
- `templates/header.tpl` currently mounts the Westgate topbar inside the
`layout-container` before `#panel`.
- `templates/footer.tpl` no longer imports Harmony's right sidebar.
- Harmony's sidebar partials under
`/home/vicky/Repositories/nodebb-theme-harmony/templates/partials/sidebar/`
own the live controls for navigation, user menu, search, notifications, chat,
-6
View File
@@ -115,11 +115,6 @@ library.defineWidgetAreas = async function (areas) {
template: 'global',
location: 'sidebar-footer',
},
{
name: 'Brand Header',
template: 'global',
location: 'brand-header',
},
{
name: 'About me (before)',
template: 'account/profile.tpl',
@@ -182,4 +177,3 @@ library.filterMiddlewareRenderHeader = async function (hookData) {
hookData.templateData.bootswatchSkinOptions = await meta.css.getSkinSwitcherOptions(hookData.req.uid);
return hookData;
};
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 MiB

-187
View File
@@ -1,187 +0,0 @@
.westgate-brand-hero {
position: relative;
overflow: hidden;
margin-top: 0.25rem;
border: 1px solid rgba(194, 163, 90, 0.22);
border-radius: 10px;
padding: 1.2rem 1.05rem;
filter: saturate(1) contrast(1) brightness(1);
background-color: #16121b;
background:
linear-gradient(90deg, rgba(5, 4, 7, 0.82) 0%, rgba(7, 6, 9, 0.72) 22%, rgba(9, 7, 11, 0.48) 34%, rgba(10, 8, 12, 0.18) 43%, rgba(10, 8, 12, 0) 52%),
url("./plugins/nodebb-theme-westgate/images/plum-header-bg.png") center 50% / cover no-repeat;
box-shadow:
inset 0 1px 0 rgba(255, 242, 204, 0.08),
inset 0 0 0 1px rgba(33, 21, 28, 0.5),
0 18px 42px rgba(0, 0, 0, 0.3);
&::before {
content: "";
position: absolute;
inset: 0;
background:
radial-gradient(circle at 62% 100%, rgba(88, 38, 86, 0.26), rgba(88, 38, 86, 0.13) 20%, transparent 44%),
linear-gradient(155deg, transparent 58%, rgba(55, 24, 53, 0.08) 78%, rgba(78, 33, 75, 0.14) 100%);
pointer-events: none;
}
}
.westgate-brand-hero__veil,
.westgate-brand-hero__inner {
position: relative;
z-index: 1;
}
.westgate-brand-hero__veil {
display: none;
}
.westgate-brand-hero__inner {
align-items: center;
justify-content: flex-start;
gap: 0;
min-height: 142px;
width: 100%;
z-index: 2;
}
.westgate-brand-hero__brand {
width: 100%;
flex-wrap: wrap;
min-height: inherit;
padding: 0 0 0 clamp(1rem, 4vw, 3rem);
justify-content: flex-start;
text-align: left;
&:hover,
&:focus-within {
background: transparent;
}
}
.westgate-brand-hero__brand :where([component="brand/anchor"], [component="siteTitle"]) {
color: inherit;
text-decoration: none;
&:hover,
&:focus {
color: inherit;
text-decoration: none;
background: transparent;
box-shadow: none;
outline: none;
}
}
.westgate-brand-hero__title-shell {
width: 100%;
padding: 0;
min-height: inherit;
justify-content: flex-start;
text-align: left;
color: inherit;
position: relative;
z-index: 3;
text-decoration: none;
&:hover,
&:focus {
color: inherit;
text-decoration: none;
background: transparent;
box-shadow: none;
outline: none;
}
}
.westgate-brand-hero__title-block {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 0.35rem;
text-align: left;
}
.westgate-brand-hero__brand [component="siteTitle"] :where(h1, .fs-6) {
font-family: var(--wg-font-display);
font-size: clamp(1.25rem, 2.1vw, 1.9rem) !important;
line-height: 0.92;
font-weight: 600;
letter-spacing: 0.1em;
text-transform: uppercase;
text-shadow:
0 1px 0 rgba(255, 222, 142, 0.05),
0 2px 0 rgba(52, 37, 15, 0.92),
0 4px 0 rgba(16, 11, 6, 0.96),
0 8px 14px rgba(0, 0, 0, 0.72),
0 14px 26px rgba(0, 0, 0, 0.46),
0 0 16px rgba(48, 10, 26, 0.24);
opacity: 0.82;
mix-blend-mode: screen;
}
.westgate-brand-hero__subtitle {
color: #c2a35a;
font-family: var(--wg-font-display);
font-size: 0.64rem;
font-weight: 600;
letter-spacing: 0.22em;
line-height: 1;
text-transform: uppercase;
text-shadow:
0 1px 0 rgba(0, 0, 0, 0.92),
0 3px 8px rgba(0, 0, 0, 0.34);
}
.westgate-brand-hero__widgets {
position: relative;
z-index: 4;
color: var(--wg-text);
text-shadow: 0 1px 0 rgba(0, 0, 0, 0.74);
width: 100%;
padding-left: 0;
border-left: 0;
text-align: center;
margin-top: 0.5rem;
> :last-child {
margin-bottom: 0;
}
}
@media (max-width: 991.98px) {
.westgate-brand-hero {
margin-top: 0;
padding: 1.05rem 0.9rem;
background-position: center 46%;
}
.westgate-brand-hero__inner {
min-height: 118px;
flex-wrap: wrap;
}
.westgate-brand-hero__brand {
min-height: 0;
padding: 0 0 0 0.65rem;
}
.westgate-brand-hero__title-block {
gap: 0.28rem;
}
.westgate-brand-hero__brand [component="siteTitle"] :where(h1, .fs-6) {
font-size: clamp(1rem, 3.9vw, 1.35rem) !important;
letter-spacing: 0.07em;
}
.westgate-brand-hero__subtitle {
font-size: 0.56rem;
letter-spacing: 0.18em;
}
.westgate-brand-hero__widgets {
border-top: 1px solid rgba(216, 194, 138, 0.12);
padding-top: 0.85rem;
}
}
-26
View File
@@ -1,26 +0,0 @@
{{{ if (brand:logo || (config.showSiteTitle || widgets.brand-header.length)) }}}
<div class="container-lg px-md-4 brand-container">
<div class="westgate-brand-hero">
<div class="westgate-brand-hero__veil"></div>
<div class="d-flex westgate-brand-hero__inner justify-content-start">
{{{ if config.showSiteTitle }}}
<div component="brand/wrapper" class="d-flex align-items-center justify-content-start align-content-stretch westgate-brand-hero__brand">
<a component="siteTitle" class="align-self-stretch align-items-center d-flex justify-content-start westgate-brand-hero__title-shell" href="{{{ if title:url }}}{title:url}{{{ else }}}{relative_path}/{{{ end }}}">
<span class="westgate-brand-hero__title-block">
<h1 class="fs-6 fw-bold text-body mb-0">{config.siteTitle}</h1>
<span class="westgate-brand-hero__subtitle">Forums</span>
</span>
</a>
</div>
{{{ end }}}
{{{ if widgets.brand-header.length }}}
<div data-widget-area="brand-header" class="flex-fill gap-3 align-self-center westgate-brand-hero__widgets">
{{{each widgets.brand-header}}}
{{./html}}
{{{end}}}
</div>
{{{ end }}}
</div>
</div>
</div>
{{{ end }}}
+29
View File
@@ -10,6 +10,11 @@ function read(relativePath) {
return fs.readFileSync(filePath, 'utf8');
}
function assertMissing(relativePath, message) {
const filePath = path.join(__dirname, '..', relativePath);
assert(!fs.existsSync(filePath), message || `${relativePath} should not exist`);
}
function assertIncludes(haystack, needle, message) {
assert(
haystack.includes(needle),
@@ -30,6 +35,7 @@ const topbar = read('templates/partials/header/topbar.tpl');
const theme = read('theme.scss');
const stylesheet = read('scss/westgate/_topbar.scss');
const client = read('public/client.js');
const themeHooks = read('lib/theme.js');
assertIncludes(
header,
@@ -51,6 +57,11 @@ assertIncludes(
'@import "./scss/westgate/topbar";',
'theme.scss should import the focused topbar partial'
);
assertExcludes(
theme,
'@import "./scss/westgate/header";',
'theme.scss should not import the removed brand banner stylesheet'
);
assertIncludes(
stylesheet,
'.wg-topbar',
@@ -94,3 +105,21 @@ assertIncludes(topbar, '{{{ if config.loggedIn }}}', 'Topbar should branch on th
assertIncludes(topbar, '{{{ if canChat }}}', 'Chat controls should remain permission-gated');
assertIncludes(topbar, '{{{ if allowRegistration }}}', 'Registration should remain server-gated');
assertIncludes(topbar, '{{{ each navigation }}}', 'Topbar should render ACP Navigation items');
assertMissing(
'templates/partials/header/brand.tpl',
'Theme should inherit Harmony brand markup instead of shipping the removed Westgate brand banner override'
);
assertMissing(
'scss/westgate/_header.scss',
'Removed brand banner styles should not remain in the theme'
);
assertMissing(
'public/images/plum-header-bg.png',
'Removed brand banner background should not remain in static assets'
);
assertExcludes(
themeHooks,
'brand-header',
'Removed brand banner widget area should not remain registered'
);
-1
View File
@@ -1,7 +1,6 @@
@import "../nodebb-theme-harmony/theme";
@import "./scss/westgate/tokens";
@import "./scss/westgate/wiki-prose";
@import "./scss/westgate/header";
@import "./scss/westgate/topbar";
@import "./scss/westgate/surfaces";
@import "./scss/westgate/categories";