@okjavis/nodebb-theme-javis 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +6 -22
- package/plugin.json +4 -3
- package/scss/_base.scss +71 -0
- package/scss/_buttons.scss +410 -0
- package/scss/_cards.scss +434 -0
- package/{less/_categories.less → scss/_categories.scss} +10 -10
- package/scss/_forms.scss +502 -0
- package/scss/_sidebar-user.scss +150 -0
- package/scss/_sidebar.scss +539 -0
- package/scss/_variables.scss +54 -0
- package/scss/overrides.scss +39 -0
- package/static/lib/theme.js +39 -2
- package/templates/partials/posts_list_item.tpl +3 -7
- package/templates/partials/sidebar/user-menu-dropdown.tpl +99 -0
- package/templates/partials/sidebar-left.tpl +77 -0
- package/templates/partials/sidebar-right.tpl +7 -0
- package/templates/partials/topics_list.tpl +111 -0
- package/theme.js +49 -7
- package/theme.json +8 -0
- package/theme.scss +20 -0
- package/less/_base.less +0 -70
- package/less/_buttons.less +0 -90
- package/less/_cards.less +0 -118
- package/less/_forms.less +0 -104
- package/less/_sidebar.less +0 -110
- package/less/_variables.less +0 -54
- package/less/theme.less +0 -18
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
<ul id="user-control-list" component="header/usercontrol" class="overscroll-behavior-contain user-dropdown dropdown-menu shadow p-1 text-sm ff-base" role="menu">
|
|
2
|
+
<li>
|
|
3
|
+
<a class="dropdown-item rounded-1 d-flex align-items-center gap-2" component="header/profilelink" href="{relative_path}/user/{user.userslug}" role="menuitem" aria-label="[[user:profile]]">
|
|
4
|
+
<span component="user/status" class="flex-shrink-0 border border-white border-2 rounded-circle status {user.status}"><span class="visually-hidden">[[global:{user.status}]]</span></span>
|
|
5
|
+
<span class="fw-semibold" component="header/username">{user.username}</span>
|
|
6
|
+
</a>
|
|
7
|
+
</li>
|
|
8
|
+
<li role="presentation" class="dropdown-divider"></li>
|
|
9
|
+
<li><h6 class="dropdown-header text-xs">[[global:status]]</h6></li>
|
|
10
|
+
<li>
|
|
11
|
+
<a href="#" class="dropdown-item rounded-1 user-status d-flex align-items-center gap-2 {{{ if user.online }}}selected{{{ end }}}" data-status="online" role="menuitem">
|
|
12
|
+
<span component="user/status" class="flex-shrink-0 border border-white border-2 rounded-circle status online"></span>
|
|
13
|
+
<span class="flex-grow-1">[[global:online]]</span>
|
|
14
|
+
<i class="fa-solid fa-check text-secondary flex-shrink-0" aria-label="[[global:selected]]"></i>
|
|
15
|
+
</a>
|
|
16
|
+
</li>
|
|
17
|
+
<li>
|
|
18
|
+
<a href="#" class="dropdown-item rounded-1 user-status d-flex align-items-center gap-2 {{{ if user.away }}}selected{{{ end }}}" data-status="away" role="menuitem">
|
|
19
|
+
<span component="user/status" class="flex-shrink-0 border border-white border-2 rounded-circle status away"></span>
|
|
20
|
+
<span class="flex-grow-1">[[global:away]]</span>
|
|
21
|
+
<i class="fa-solid fa-check text-secondary flex-shrink-0"><span class="visually-hidden">[[global:selected]]</span></i>
|
|
22
|
+
</a>
|
|
23
|
+
</li>
|
|
24
|
+
<li>
|
|
25
|
+
<a href="#" class="dropdown-item rounded-1 user-status d-flex align-items-center gap-2 {{{ if user.dnd }}}selected{{{ end }}}" data-status="dnd" role="menuitem">
|
|
26
|
+
<span component="user/status" class="flex-shrink-0 border border-white border-2 rounded-circle status dnd"></span>
|
|
27
|
+
<span class="flex-grow-1">[[global:dnd]]</span>
|
|
28
|
+
<i class="fa-solid fa-check text-secondary flex-shrink-0"></i>
|
|
29
|
+
</a>
|
|
30
|
+
</li>
|
|
31
|
+
<li>
|
|
32
|
+
<a href="#" class="dropdown-item rounded-1 user-status d-flex align-items-center gap-2 {{{ if user.offline }}}selected{{{ end }}}" data-status="offline" role="menuitem">
|
|
33
|
+
<span component="user/status" class="flex-shrink-0 border border-white border-2 rounded-circle status offline"></span>
|
|
34
|
+
<span class="flex-grow-1">[[global:invisible]]</span>
|
|
35
|
+
<i class="fa-solid fa-check text-secondary flex-shrink-0"></i>
|
|
36
|
+
</a>
|
|
37
|
+
</li>
|
|
38
|
+
<li role="presentation" class="dropdown-divider"></li>
|
|
39
|
+
<li>
|
|
40
|
+
<a class="dropdown-item rounded-1 d-flex align-items-center gap-2" href="{relative_path}/user/{user.userslug}/bookmarks" role="menuitem">
|
|
41
|
+
<i class="fa fa-fw fa-bookmark text-secondary"></i> <span>[[user:bookmarks]]</span>
|
|
42
|
+
</a>
|
|
43
|
+
</li>
|
|
44
|
+
<li>
|
|
45
|
+
<a class="dropdown-item rounded-1 d-flex align-items-center gap-2" component="header/profilelink/edit" href="{relative_path}/user/{user.userslug}/edit" role="menuitem">
|
|
46
|
+
<i class="fa fa-fw fa-edit text-secondary"></i> <span>[[user:edit-profile]]</span>
|
|
47
|
+
</a>
|
|
48
|
+
</li>
|
|
49
|
+
<li>
|
|
50
|
+
<a class="dropdown-item rounded-1 d-flex align-items-center gap-2" component="header/profilelink/settings" href="{relative_path}/user/{user.userslug}/settings" role="menuitem">
|
|
51
|
+
<i class="fa fa-fw fa-gear text-secondary"></i> <span>[[user:settings]]</span>
|
|
52
|
+
</a>
|
|
53
|
+
</li>
|
|
54
|
+
{{{ if showModMenu }}}
|
|
55
|
+
<li role="presentation" class="dropdown-divider"></li>
|
|
56
|
+
<li><h6 class="dropdown-header text-xs">[[pages:moderator-tools]]</h6></li>
|
|
57
|
+
<li>
|
|
58
|
+
<a class="dropdown-item rounded-1 d-flex align-items-center gap-2" href="{relative_path}/flags" role="menuitem">
|
|
59
|
+
<i class="fa fa-fw fa-flag text-secondary"></i> <span>[[pages:flagged-content]]</span>
|
|
60
|
+
</a>
|
|
61
|
+
</li>
|
|
62
|
+
<li>
|
|
63
|
+
<a class="dropdown-item rounded-1 d-flex align-items-center gap-2" href="{relative_path}/post-queue" role="menuitem">
|
|
64
|
+
<i class="fa fa-fw fa-list-alt text-secondary"></i> <span>[[pages:post-queue]]</span>
|
|
65
|
+
</a>
|
|
66
|
+
</li>
|
|
67
|
+
{{{ if registrationQueueEnabled }}}
|
|
68
|
+
<li>
|
|
69
|
+
<a class="dropdown-item rounded-1 d-flex align-items-center gap-2" href="{relative_path}/registration-queue" role="menuitem">
|
|
70
|
+
<i class="fa fa-fw fa-list-alt text-secondary"></i> <span>[[pages:registration-queue]]</span>
|
|
71
|
+
</a>
|
|
72
|
+
</li>
|
|
73
|
+
{{{ end }}}
|
|
74
|
+
<li>
|
|
75
|
+
<a class="dropdown-item rounded-1 d-flex align-items-center gap-2" href="{relative_path}/ip-blacklist" role="menuitem">
|
|
76
|
+
<i class="fa fa-fw fa-ban text-secondary"></i> <span>[[pages:ip-blacklist]]</span>
|
|
77
|
+
</a>
|
|
78
|
+
</li>
|
|
79
|
+
{{{ else }}}
|
|
80
|
+
{{{ if postQueueEnabled }}}
|
|
81
|
+
<li>
|
|
82
|
+
<a class="dropdown-item rounded-1 d-flex align-items-center gap-2" href="{relative_path}/post-queue" role="menuitem">
|
|
83
|
+
<i class="fa fa-fw fa-list-alt text-secondary"></i> <span>[[pages:post-queue]]</span>
|
|
84
|
+
</a>
|
|
85
|
+
</li>
|
|
86
|
+
{{{ end }}}
|
|
87
|
+
{{{ end }}}
|
|
88
|
+
|
|
89
|
+
<li role="presentation" class="dropdown-divider"></li>
|
|
90
|
+
<li component="user/logout">
|
|
91
|
+
<form method="post" action="{relative_path}/logout" role="menuitem">
|
|
92
|
+
<input type="hidden" name="_csrf" value="{config.csrf_token}">
|
|
93
|
+
<input type="hidden" name="noscript" value="true">
|
|
94
|
+
<button type="submit" class="dropdown-item rounded-1 d-flex align-items-center gap-2">
|
|
95
|
+
<i class="fa fa-fw fa-sign-out text-secondary"></i><span>[[global:logout]]</span>
|
|
96
|
+
</button>
|
|
97
|
+
</form>
|
|
98
|
+
</li>
|
|
99
|
+
</ul>
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
<nav component="sidebar/left" class="{{{ if config.theme.openSidebars}}}open{{{ end }}} text-dark bg-light sidebar sidebar-left start-0 border-end vh-100 d-none d-lg-flex flex-column justify-content-between sticky-top">
|
|
2
|
+
<ul id="main-nav" class="list-unstyled d-flex flex-column w-100 gap-2 mt-2 overflow-y-auto">
|
|
3
|
+
{{{ each navigation }}}
|
|
4
|
+
{{{ if displayMenuItem(@root, @index) }}}
|
|
5
|
+
<li class="nav-item mx-2 {./class}{{{ if ./dropdown }}} dropend{{{ end }}}" title="{./title}">
|
|
6
|
+
<a class="nav-link navigation-link d-flex gap-2 justify-content-between align-items-center {{{ if ./dropdown }}}dropdown-toggle{{{ end }}}" {{{ if ./dropdown }}} href="#" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" {{{ else }}} href="{./route}"{{{ end }}} {{{ if ./id }}}id="{./id}"{{{ end }}}{{{ if ./targetBlank }}} target="_blank"{{{ end }}} {{{ if ./text }}}aria-label="{./text}"{{{ end }}}>
|
|
7
|
+
<span class="d-flex gap-2 align-items-center text-nowrap truncate-open">
|
|
8
|
+
<span class="position-relative">
|
|
9
|
+
{{{ if ./iconClass }}}
|
|
10
|
+
<i class="fa fa-fw {./iconClass}" data-content="{./content}"></i>
|
|
11
|
+
<span component="navigation/count" class="visible-closed position-absolute top-0 start-100 translate-middle badge rounded-1 bg-primary {{{ if !./content }}}hidden{{{ end }}}">{./content}</span>
|
|
12
|
+
{{{ end }}}
|
|
13
|
+
</span>
|
|
14
|
+
{{{ if ./text }}}<span class="nav-text small visible-open fw-semibold text-truncate">{./text}</span>{{{ end }}}
|
|
15
|
+
</span>
|
|
16
|
+
<span component="navigation/count" class="visible-open badge rounded-1 bg-primary {{{ if !./content }}}hidden{{{ end }}}">{./content}</span>
|
|
17
|
+
</a>
|
|
18
|
+
{{{ if ./dropdown }}}
|
|
19
|
+
<ul class="dropdown-menu p-1 shadow" role="menu">
|
|
20
|
+
{./dropdownContent}
|
|
21
|
+
</ul>
|
|
22
|
+
{{{ end }}}
|
|
23
|
+
</li>
|
|
24
|
+
{{{ end }}}
|
|
25
|
+
{{{ end }}}
|
|
26
|
+
</ul>
|
|
27
|
+
<div class="sidebar-toggle-container align-self-start">
|
|
28
|
+
<!-- User Profile Section (only for logged-in users) -->
|
|
29
|
+
{{{ if config.loggedIn }}}
|
|
30
|
+
<div class="sidebar-user-section mx-2 mb-2">
|
|
31
|
+
<div class="nav-item dropend usermenu">
|
|
32
|
+
<a component="header/avatar" id="sidebar_user_dropdown" href="#" role="button"
|
|
33
|
+
class="nav-link d-flex gap-2 align-items-center text-truncate sidebar-user-trigger"
|
|
34
|
+
data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"
|
|
35
|
+
aria-label="[[user:user-menu]]">
|
|
36
|
+
<span class="sidebar-avatar-wrapper position-relative">
|
|
37
|
+
{buildAvatar(user, "32px", true)}
|
|
38
|
+
<span component="user/status" class="sidebar-status-dot position-absolute border border-white border-2 rounded-circle status {user.status}"></span>
|
|
39
|
+
</span>
|
|
40
|
+
<span class="nav-text visible-open fw-semibold text-truncate">{user.username}</span>
|
|
41
|
+
</a>
|
|
42
|
+
<!-- IMPORT partials/sidebar/user-menu-dropdown.tpl -->
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
{{{ else }}}
|
|
46
|
+
<!-- Login/Register for logged-out users -->
|
|
47
|
+
<div class="sidebar-auth-section mx-2 mb-2">
|
|
48
|
+
<div class="nav-item">
|
|
49
|
+
<a class="nav-link d-flex gap-2 align-items-center" href="{relative_path}/login">
|
|
50
|
+
<i class="fa fa-fw fa-sign-in"></i>
|
|
51
|
+
<span class="nav-text small visible-open fw-semibold">[[global:login]]</span>
|
|
52
|
+
</a>
|
|
53
|
+
</div>
|
|
54
|
+
{{{ if config.allowRegistration }}}
|
|
55
|
+
<div class="nav-item mt-1">
|
|
56
|
+
<a class="nav-link d-flex gap-2 align-items-center" href="{relative_path}/register">
|
|
57
|
+
<i class="fa fa-fw fa-user-plus"></i>
|
|
58
|
+
<span class="nav-text small visible-open fw-semibold">[[global:register]]</span>
|
|
59
|
+
</a>
|
|
60
|
+
</div>
|
|
61
|
+
{{{ end }}}
|
|
62
|
+
</div>
|
|
63
|
+
{{{ end }}}
|
|
64
|
+
|
|
65
|
+
{{{ if !config.disableCustomUserSkins }}}
|
|
66
|
+
<!-- IMPORT partials/skin-switcher.tpl -->
|
|
67
|
+
{{{ end }}}
|
|
68
|
+
|
|
69
|
+
<div class="sidebar-toggle m-2 d-none d-lg-block">
|
|
70
|
+
<a href="#" role="button" component="sidebar/toggle" class="nav-link d-flex gap-2 align-items-center p-2 pointer w-100 text-nowrap" title="[[themes/harmony:expand]]" aria-label="[[themes/harmony:sidebar-toggle]]">
|
|
71
|
+
<i class="fa fa-fw fa-angles-right"></i>
|
|
72
|
+
<i class="fa fa-fw fa-angles-left"></i>
|
|
73
|
+
<span class="nav-text visible-open fw-semibold small lh-1">[[themes/harmony:collapse]]</span>
|
|
74
|
+
</a>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
</nav>
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
JAVIS Community Theme - Topics List (Reddit-style)
|
|
3
|
+
Used on: Categories, Recent, Popular, Unread pages
|
|
4
|
+
-->
|
|
5
|
+
<ul component="category" class="topics-list list-unstyled" itemscope itemtype="http://www.schema.org/ItemList" data-nextstart="{nextStart}" data-set="{set}">
|
|
6
|
+
|
|
7
|
+
{{{ each topics }}}
|
|
8
|
+
<li component="category/topic" class="topic-card {function.generateTopicClass}" <!-- IMPORT partials/data/category.tpl -->>
|
|
9
|
+
<link itemprop="url" content="{config.relative_path}/topic/{./slug}" />
|
|
10
|
+
<meta itemprop="name" content="{function.stripTags, ./title}" />
|
|
11
|
+
<meta itemprop="itemListOrder" content="descending" />
|
|
12
|
+
<meta itemprop="position" content="{increment(./index, "1")}" />
|
|
13
|
+
<a id="{./index}" data-index="{./index}" component="topic/anchor"></a>
|
|
14
|
+
|
|
15
|
+
<!-- Vote Column (Reddit-style) -->
|
|
16
|
+
{{{ if !reputation:disabled }}}
|
|
17
|
+
<div class="vote-column">
|
|
18
|
+
<button class="vote-btn vote-up" title="[[topic:upvote]]">
|
|
19
|
+
<i class="fa fa-chevron-up"></i>
|
|
20
|
+
</button>
|
|
21
|
+
<span class="vote-count" title="{./votes}">{humanReadableNumber(./votes, 0)}</span>
|
|
22
|
+
<button class="vote-btn vote-down" title="[[topic:downvote]]">
|
|
23
|
+
<i class="fa fa-chevron-down"></i>
|
|
24
|
+
</button>
|
|
25
|
+
</div>
|
|
26
|
+
{{{ end }}}
|
|
27
|
+
|
|
28
|
+
<!-- Main Content -->
|
|
29
|
+
<div class="topic-content">
|
|
30
|
+
<!-- Meta Row: Category + Author + Time -->
|
|
31
|
+
<div class="topic-meta">
|
|
32
|
+
{{{ if (!template.category || (cid != ./cid)) }}}
|
|
33
|
+
{buildCategoryLabel(./category, "a", "border")}
|
|
34
|
+
{{{ end }}}
|
|
35
|
+
<span class="meta-separator">•</span>
|
|
36
|
+
<span class="meta-author">
|
|
37
|
+
Posted by
|
|
38
|
+
<a href="{{{ if ./user.userslug }}}{config.relative_path}/user/{./user.userslug}{{{ else }}}#{{{ end }}}" class="author-link">{./user.displayname}</a>
|
|
39
|
+
</span>
|
|
40
|
+
<span class="meta-separator">•</span>
|
|
41
|
+
<span class="timeago meta-time" title="{./timestampISO}"></span>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<!-- Title -->
|
|
45
|
+
<h3 component="topic/header" class="topic-title">
|
|
46
|
+
<a href="{{{ if topics.noAnchor }}}#{{{ else }}}{config.relative_path}/topic/{./slug}{{{ if ./bookmark }}}/{./bookmark}{{{ end }}}{{{ end }}}">{./title}</a>
|
|
47
|
+
</h3>
|
|
48
|
+
|
|
49
|
+
<!-- Labels/Badges -->
|
|
50
|
+
<div component="topic/labels" class="topic-labels">
|
|
51
|
+
<span component="topic/pinned" class="topic-badge pinned {{{ if (./scheduled || !./pinned) }}}hidden{{{ end }}}">
|
|
52
|
+
<i class="fa fa-thumb-tack"></i> Pinned
|
|
53
|
+
</span>
|
|
54
|
+
<span component="topic/locked" class="topic-badge locked {{{ if !./locked }}}hidden{{{ end }}}">
|
|
55
|
+
<i class="fa fa-lock"></i> Locked
|
|
56
|
+
</span>
|
|
57
|
+
<span component="topic/scheduled" class="topic-badge scheduled {{{ if !./scheduled }}}hidden{{{ end }}}">
|
|
58
|
+
<i class="fa fa-clock-o"></i> Scheduled
|
|
59
|
+
</span>
|
|
60
|
+
{{{each ./icons}}}<span class="topic-badge">{@value}</span>{{{end}}}
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<!-- Tags -->
|
|
64
|
+
{{{ if ./tags.length }}}
|
|
65
|
+
<div data-tid="{./tid}" component="topic/tags" class="topic-tags">
|
|
66
|
+
{{{ each ./tags }}}
|
|
67
|
+
<a href="{config.relative_path}/tags/{./valueEncoded}" class="topic-tag">{./valueEscaped}</a>
|
|
68
|
+
{{{ end }}}
|
|
69
|
+
</div>
|
|
70
|
+
{{{ end }}}
|
|
71
|
+
|
|
72
|
+
<!-- Thumbnail Preview (if exists) -->
|
|
73
|
+
{{{ if ./thumbs.length }}}
|
|
74
|
+
<a class="topic-thumbnail" href="{config.relative_path}/topic/{./slug}{{{ if ./bookmark }}}/{./bookmark}{{{ end }}}">
|
|
75
|
+
<img src="{./thumbs.0.url}" alt="" loading="lazy" />
|
|
76
|
+
{{{ if ./thumbs.1 }}}
|
|
77
|
+
<span class="thumb-count">+{subtract(./thumbs.length, 1)}</span>
|
|
78
|
+
{{{ end }}}
|
|
79
|
+
</a>
|
|
80
|
+
{{{ end }}}
|
|
81
|
+
|
|
82
|
+
<!-- Teaser/Preview Content -->
|
|
83
|
+
{{{ if ./teaser.content }}}
|
|
84
|
+
<div class="topic-teaser">
|
|
85
|
+
{./teaser.content}
|
|
86
|
+
</div>
|
|
87
|
+
{{{ end }}}
|
|
88
|
+
|
|
89
|
+
<!-- Action Bar -->
|
|
90
|
+
<div class="topic-actions">
|
|
91
|
+
<a href="{config.relative_path}/topic/{./slug}" class="action-btn">
|
|
92
|
+
<i class="fa-regular fa-comment"></i>
|
|
93
|
+
<span>{humanReadableNumber(./postcount, 0)} Comments</span>
|
|
94
|
+
</a>
|
|
95
|
+
<button class="action-btn share-btn" data-url="{config.relative_path}/topic/{./slug}">
|
|
96
|
+
<i class="fa fa-share"></i>
|
|
97
|
+
<span>Share</span>
|
|
98
|
+
</button>
|
|
99
|
+
<button class="action-btn save-btn">
|
|
100
|
+
<i class="fa-regular fa-bookmark"></i>
|
|
101
|
+
<span>Save</span>
|
|
102
|
+
</button>
|
|
103
|
+
<span class="action-stat">
|
|
104
|
+
<i class="fa fa-eye"></i>
|
|
105
|
+
<span>{humanReadableNumber(./viewcount, 0)}</span>
|
|
106
|
+
</span>
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
</li>
|
|
110
|
+
{{{end}}}
|
|
111
|
+
</ul>
|
package/theme.js
CHANGED
|
@@ -8,17 +8,59 @@
|
|
|
8
8
|
const theme = {};
|
|
9
9
|
|
|
10
10
|
theme.defineWidgetAreas = async (areas) => {
|
|
11
|
-
// Define
|
|
11
|
+
// Define widget areas like Harmony does
|
|
12
|
+
const locations = ['header', 'sidebar', 'footer'];
|
|
13
|
+
const templates = [
|
|
14
|
+
'categories.tpl', 'category.tpl', 'topic.tpl', 'users.tpl',
|
|
15
|
+
'unread.tpl', 'recent.tpl', 'popular.tpl', 'top.tpl', 'tags.tpl', 'tag.tpl',
|
|
16
|
+
'login.tpl', 'register.tpl', 'world.tpl',
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
function capitalizeFirst(str) {
|
|
20
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
templates.forEach((template) => {
|
|
24
|
+
locations.forEach((location) => {
|
|
25
|
+
areas.push({
|
|
26
|
+
name: `${capitalizeFirst(template.split('.')[0])} ${capitalizeFirst(location)}`,
|
|
27
|
+
template: template,
|
|
28
|
+
location: location,
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// Additional widget areas
|
|
12
34
|
areas = areas.concat([
|
|
13
35
|
{
|
|
14
|
-
name: '
|
|
15
|
-
template: '
|
|
16
|
-
location: '
|
|
36
|
+
name: 'Main post header',
|
|
37
|
+
template: 'topic.tpl',
|
|
38
|
+
location: 'mainpost-header',
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: 'Main post footer',
|
|
42
|
+
template: 'topic.tpl',
|
|
43
|
+
location: 'mainpost-footer',
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: 'Sidebar Footer',
|
|
47
|
+
template: 'global',
|
|
48
|
+
location: 'sidebar-footer',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: 'Brand Header',
|
|
52
|
+
template: 'global',
|
|
53
|
+
location: 'brand-header',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: 'About me (before)',
|
|
57
|
+
template: 'account/profile.tpl',
|
|
58
|
+
location: 'profile-aboutme-before',
|
|
17
59
|
},
|
|
18
60
|
{
|
|
19
|
-
name: '
|
|
20
|
-
template: '
|
|
21
|
-
location: '
|
|
61
|
+
name: 'About me (after)',
|
|
62
|
+
template: 'account/profile.tpl',
|
|
63
|
+
location: 'profile-aboutme-after',
|
|
22
64
|
},
|
|
23
65
|
]);
|
|
24
66
|
|
package/theme.json
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "@okjavis/nodebb-theme-javis",
|
|
3
|
+
"name": "JAVIS Community Theme",
|
|
4
|
+
"description": "Modern, premium NodeBB theme for JAVIS Community",
|
|
5
|
+
"url": "https://github.com/javis-admin/nodebb-community-theme",
|
|
6
|
+
"screenshot": "screenshot.png",
|
|
7
|
+
"baseTheme": "nodebb-theme-harmony"
|
|
8
|
+
}
|
package/theme.scss
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// ============================================
|
|
2
|
+
// JAVIS Community Theme
|
|
3
|
+
// Extends NodeBB Harmony Theme
|
|
4
|
+
// ============================================
|
|
5
|
+
|
|
6
|
+
// Note: When using baseTheme, NodeBB loads the parent theme (Harmony) first.
|
|
7
|
+
// This file contains JAVIS-specific style overrides that are applied on top.
|
|
8
|
+
|
|
9
|
+
// Import Harmony's theme styles (required for inheritance)
|
|
10
|
+
@import "nodebb-theme-harmony/scss/harmony";
|
|
11
|
+
|
|
12
|
+
// JAVIS Custom Styles
|
|
13
|
+
@import "./scss/variables";
|
|
14
|
+
@import "./scss/base";
|
|
15
|
+
@import "./scss/buttons";
|
|
16
|
+
@import "./scss/forms";
|
|
17
|
+
@import "./scss/cards";
|
|
18
|
+
@import "./scss/sidebar";
|
|
19
|
+
@import "./scss/sidebar-user";
|
|
20
|
+
@import "./scss/categories";
|
package/less/_base.less
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
// ============================================
|
|
2
|
-
// BASE STYLES – Global Reset & Typography
|
|
3
|
-
// ============================================
|
|
4
|
-
|
|
5
|
-
html, body {
|
|
6
|
-
font-family: @jv-font-sans;
|
|
7
|
-
font-size: @jv-font-size-base;
|
|
8
|
-
line-height: @jv-line-height-base;
|
|
9
|
-
background-color: @jv-bg;
|
|
10
|
-
color: @jv-text-main;
|
|
11
|
-
-webkit-font-smoothing: antialiased;
|
|
12
|
-
-moz-osx-font-smoothing: grayscale;
|
|
13
|
-
margin: 0;
|
|
14
|
-
padding: 0;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// Paragraph
|
|
18
|
-
p {
|
|
19
|
-
margin: 0 0 @jv-space-6 0;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// Headings
|
|
23
|
-
h1 {
|
|
24
|
-
font-size: @jv-font-size-xxl;
|
|
25
|
-
font-weight: 600;
|
|
26
|
-
letter-spacing: -0.02em;
|
|
27
|
-
margin-bottom: @jv-space-8;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
h2 {
|
|
31
|
-
font-size: @jv-font-size-xl;
|
|
32
|
-
font-weight: 600;
|
|
33
|
-
letter-spacing: -0.01em;
|
|
34
|
-
margin-bottom: @jv-space-6;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
h3 {
|
|
38
|
-
font-size: @jv-font-size-xl;
|
|
39
|
-
font-weight: 600;
|
|
40
|
-
margin-bottom: @jv-space-6;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
h4 {
|
|
44
|
-
font-size: 20px;
|
|
45
|
-
font-weight: 600;
|
|
46
|
-
margin-bottom: @jv-space-6;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
h5 {
|
|
50
|
-
font-size: 17px;
|
|
51
|
-
font-weight: 600;
|
|
52
|
-
margin-bottom: @jv-space-6;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
h6 {
|
|
56
|
-
font-size: @jv-font-size-base;
|
|
57
|
-
font-weight: 600;
|
|
58
|
-
margin-bottom: @jv-space-4;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Links
|
|
62
|
-
a {
|
|
63
|
-
color: @jv-primary;
|
|
64
|
-
text-decoration: none;
|
|
65
|
-
transition: color 0.15s ease;
|
|
66
|
-
|
|
67
|
-
&:hover {
|
|
68
|
-
color: @jv-primary-hover;
|
|
69
|
-
}
|
|
70
|
-
}
|
package/less/_buttons.less
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
// ===========================================================
|
|
2
|
-
// BUTTON SYSTEM – JAVIS Design System
|
|
3
|
-
// ===========================================================
|
|
4
|
-
|
|
5
|
-
// Base Button Reset
|
|
6
|
-
.btn {
|
|
7
|
-
border-radius: @jv-radius-pill;
|
|
8
|
-
padding: 6px 16px;
|
|
9
|
-
font-size: 14px;
|
|
10
|
-
font-weight: 500;
|
|
11
|
-
line-height: 1.32;
|
|
12
|
-
transition: all 0.15s ease;
|
|
13
|
-
border-width: 1px;
|
|
14
|
-
cursor: pointer;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// PRIMARY BUTTON
|
|
18
|
-
.btn-primary {
|
|
19
|
-
background-color: @jv-primary;
|
|
20
|
-
border-color: @jv-primary;
|
|
21
|
-
color: #fff;
|
|
22
|
-
box-shadow: @jv-shadow-button;
|
|
23
|
-
|
|
24
|
-
&:hover,
|
|
25
|
-
&:focus {
|
|
26
|
-
background-color: @jv-primary-hover;
|
|
27
|
-
border-color: @jv-primary-hover;
|
|
28
|
-
box-shadow: @jv-shadow-button-hover;
|
|
29
|
-
transform: translateY(-1px);
|
|
30
|
-
color: #fff;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
&:active {
|
|
34
|
-
transform: translateY(0);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// OUTLINE PRIMARY
|
|
39
|
-
.btn-outline-primary {
|
|
40
|
-
background-color: transparent;
|
|
41
|
-
border-color: @jv-primary;
|
|
42
|
-
color: @jv-primary;
|
|
43
|
-
|
|
44
|
-
&:hover,
|
|
45
|
-
&:focus {
|
|
46
|
-
background-color: @jv-primary-soft;
|
|
47
|
-
border-color: @jv-primary;
|
|
48
|
-
color: @jv-primary;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// SECONDARY BUTTON
|
|
53
|
-
.btn-secondary,
|
|
54
|
-
.btn-default {
|
|
55
|
-
background-color: #f7f8fa;
|
|
56
|
-
border-color: @jv-border-subtle;
|
|
57
|
-
color: @jv-text-main;
|
|
58
|
-
|
|
59
|
-
&:hover,
|
|
60
|
-
&:focus {
|
|
61
|
-
background-color: #eef0f3;
|
|
62
|
-
border-color: @jv-border-subtle;
|
|
63
|
-
color: @jv-text-main;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// LINK-STYLE BUTTON
|
|
68
|
-
.btn-link {
|
|
69
|
-
color: @jv-primary;
|
|
70
|
-
font-weight: 500;
|
|
71
|
-
padding: 0;
|
|
72
|
-
|
|
73
|
-
&:hover {
|
|
74
|
-
text-decoration: underline;
|
|
75
|
-
color: @jv-primary-hover;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// SMALL BUTTONS
|
|
80
|
-
.btn-sm {
|
|
81
|
-
padding: 4px 12px;
|
|
82
|
-
font-size: @jv-font-size-sm;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// LARGE BUTTONS
|
|
86
|
-
.btn-lg {
|
|
87
|
-
padding: 10px 22px;
|
|
88
|
-
font-size: @jv-font-size-base;
|
|
89
|
-
border-radius: @jv-radius-pill;
|
|
90
|
-
}
|
package/less/_cards.less
DELETED
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
// ===========================================================
|
|
2
|
-
// CARD COMPONENT – Feed Posts
|
|
3
|
-
// Inspired by Circle.so's spacing and Apple's depth
|
|
4
|
-
// ===========================================================
|
|
5
|
-
|
|
6
|
-
// CARD WRAPPER
|
|
7
|
-
li[component="post"].posts-list-item {
|
|
8
|
-
background: @jv-surface;
|
|
9
|
-
border-radius: @jv-radius-lg;
|
|
10
|
-
border: 1px solid rgba(0,0,0,0.05);
|
|
11
|
-
box-shadow: @jv-shadow-card;
|
|
12
|
-
padding: 24px 22px 20px 22px;
|
|
13
|
-
margin-bottom: 24px;
|
|
14
|
-
transition: box-shadow 0.15s ease, border-color 0.15s ease;
|
|
15
|
-
|
|
16
|
-
&:hover {
|
|
17
|
-
box-shadow: 0 6px 14px rgba(0,0,0,0.08);
|
|
18
|
-
border-color: rgba(0,0,0,0.08);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// IMAGE BLOCK
|
|
23
|
-
li[component="post"] {
|
|
24
|
-
.overflow-hidden,
|
|
25
|
-
img.w-100 {
|
|
26
|
-
border-radius: @jv-radius-lg;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// CARD BODY LAYOUT
|
|
31
|
-
li[component="post"] .post-body {
|
|
32
|
-
display: flex;
|
|
33
|
-
flex-direction: column;
|
|
34
|
-
gap: @jv-space-6;
|
|
35
|
-
padding-top: @jv-space-2;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// TITLE
|
|
39
|
-
li[component="post"] .topic-title {
|
|
40
|
-
display: block;
|
|
41
|
-
margin-bottom: 6px;
|
|
42
|
-
font-size: @jv-font-size-lg;
|
|
43
|
-
font-weight: 600;
|
|
44
|
-
letter-spacing: -0.01em;
|
|
45
|
-
line-height: @jv-line-height-tight;
|
|
46
|
-
color: @jv-text-main;
|
|
47
|
-
|
|
48
|
-
a {
|
|
49
|
-
color: inherit;
|
|
50
|
-
text-decoration: none;
|
|
51
|
-
|
|
52
|
-
&:hover {
|
|
53
|
-
color: @jv-primary;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// META (author + time)
|
|
59
|
-
li[component="post"] .d-flex.gap-2 {
|
|
60
|
-
margin-bottom: 10px;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
li[component="post"] {
|
|
64
|
-
.timeago,
|
|
65
|
-
.post-meta a,
|
|
66
|
-
.topic-meta {
|
|
67
|
-
font-size: @jv-font-size-sm;
|
|
68
|
-
color: @jv-text-muted;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// CONTENT
|
|
73
|
-
li[component="post"] .content p {
|
|
74
|
-
font-size: @jv-font-size-base;
|
|
75
|
-
line-height: @jv-line-height-base;
|
|
76
|
-
margin-bottom: 18px;
|
|
77
|
-
color: @jv-text-main;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// DIVIDER
|
|
81
|
-
li[component="post"] hr {
|
|
82
|
-
margin: 16px 0;
|
|
83
|
-
border-color: @jv-border-subtle;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// ===========================================================
|
|
87
|
-
// ACTION ROW (quiet, modern, premium)
|
|
88
|
-
// ===========================================================
|
|
89
|
-
|
|
90
|
-
li[component="post"] .d-flex.justify-content-between {
|
|
91
|
-
margin-top: 6px;
|
|
92
|
-
padding-bottom: 2px;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
li[component="post"] .d-flex.gap-3 a {
|
|
96
|
-
background: transparent;
|
|
97
|
-
border: none;
|
|
98
|
-
padding: 0;
|
|
99
|
-
font-size: @jv-font-size-sm;
|
|
100
|
-
color: @jv-text-muted;
|
|
101
|
-
opacity: 0.85;
|
|
102
|
-
display: flex;
|
|
103
|
-
align-items: center;
|
|
104
|
-
gap: 6px;
|
|
105
|
-
box-shadow: none;
|
|
106
|
-
text-decoration: none;
|
|
107
|
-
transition: color 0.15s ease, opacity 0.15s ease;
|
|
108
|
-
|
|
109
|
-
&:hover {
|
|
110
|
-
color: @jv-primary;
|
|
111
|
-
opacity: 1;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
i {
|
|
115
|
-
font-size: 15px;
|
|
116
|
-
color: inherit;
|
|
117
|
-
}
|
|
118
|
-
}
|