@okjavis/nodebb-theme-javis 1.0.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/README.md ADDED
@@ -0,0 +1,201 @@
1
+ # JAVIS Community Theme
2
+
3
+ A modern, premium NodeBB theme for JAVIS Community. Inspired by the clean aesthetics of Apple, Stripe, and Linear design systems.
4
+
5
+ ## Features
6
+
7
+ - **Design System First**: Built on a comprehensive design token system for consistency
8
+ - **Modern Aesthetics**: Clean, premium look with smooth interactions
9
+ - **Fully Customizable**: Organized LESS files make customization easy
10
+ - **Responsive**: Works beautifully on all devices
11
+ - **Performance Optimized**: Clean, efficient CSS with no bloat
12
+
13
+ ## Design System
14
+
15
+ The JAVIS design system includes:
16
+
17
+ - **Typography**: SF Pro-based system font stack
18
+ - **Color Palette**: Primary blue (#0051ff) with neutral grays
19
+ - **Spacing**: 8pt grid system (4px, 8px, 12px, 16px, 20px, 24px)
20
+ - **Border Radius**: Consistent radii (4px, 8px, 12px, 16px, pill)
21
+ - **Shadows**: Soft, subtle elevation system
22
+
23
+ ## Installation & Development
24
+
25
+ ### 1. Install Dependencies
26
+
27
+ ```bash
28
+ cd /Users/chiragdugar/Desktop/Javis/javis-community-theme
29
+ npm install
30
+ ```
31
+
32
+ ### 2. Build the Theme
33
+
34
+ ```bash
35
+ npm run build
36
+ ```
37
+
38
+ This compiles `less/theme.less` into `static/lib/theme.css`.
39
+
40
+ ### 3. Development Mode (Watch for Changes)
41
+
42
+ ```bash
43
+ npm run dev
44
+ ```
45
+
46
+ This watches your LESS files and rebuilds automatically on changes.
47
+
48
+ ## Deployment to NodeBB Cloud
49
+
50
+ ### Option 1: Direct Upload via ACP (Recommended)
51
+
52
+ 1. **Package the theme**:
53
+ ```bash
54
+ cd /Users/chiragdugar/Desktop/Javis
55
+ zip -r javis-community-theme.zip javis-community-theme/ -x "*/node_modules/*" "*/.git/*"
56
+ ```
57
+
58
+ 2. **Upload to NodeBB Cloud**:
59
+ - Go to your NodeBB Admin Panel (ACP)
60
+ - Navigate to: **Extend → Install Plugins**
61
+ - Click "Upload Plugin"
62
+ - Select `javis-community-theme.zip`
63
+ - Wait for installation to complete
64
+ - Rebuild NodeBB when prompted
65
+
66
+ 3. **Activate the theme**:
67
+ - Go to: **Appearance → Themes**
68
+ - Select "JAVIS Community Theme"
69
+ - Click "Apply" and rebuild
70
+
71
+ ### Option 2: Git Repository (For Version Control)
72
+
73
+ 1. **Initialize git** (if not already):
74
+ ```bash
75
+ cd /Users/chiragdugar/Desktop/Javis/javis-community-theme
76
+ git init
77
+ git add .
78
+ git commit -m "Initial JAVIS theme setup"
79
+ ```
80
+
81
+ 2. **Push to GitHub**:
82
+ ```bash
83
+ git remote add origin https://github.com/yourusername/javis-community-theme.git
84
+ git push -u origin main
85
+ ```
86
+
87
+ 3. **Install from GitHub in NodeBB**:
88
+ - NodeBB ACP → Extend → Install Plugins
89
+ - Enter: `yourusername/javis-community-theme`
90
+ - Click "Install"
91
+
92
+ ## File Structure
93
+
94
+ ```
95
+ javis-community-theme/
96
+ ├── less/
97
+ │ ├── theme.less # Main entry point
98
+ │ ├── _variables.less # Design tokens
99
+ │ ├── _base.less # Global styles & typography
100
+ │ ├── _buttons.less # Button system
101
+ │ ├── _forms.less # Input & form controls
102
+ │ ├── _cards.less # Feed post cards
103
+ │ ├── _sidebar.less # Sidebar widgets
104
+ │ └── _categories.less # Category list styling
105
+ ├── static/
106
+ │ └── lib/
107
+ │ ├── theme.css # Compiled CSS (generated)
108
+ │ └── theme.js # Client-side JS
109
+ ├── templates/ # Custom template overrides (if needed)
110
+ ├── theme.js # Server-side theme logic
111
+ ├── plugin.json # NodeBB theme manifest
112
+ └── package.json # NPM configuration
113
+ ```
114
+
115
+ ## Customization Guide
116
+
117
+ ### Changing Colors
118
+
119
+ Edit `less/_variables.less`:
120
+
121
+ ```less
122
+ @jv-primary: #0051ff; // Your primary brand color
123
+ @jv-primary-hover: #0044dd; // Hover state
124
+ ```
125
+
126
+ ### Adjusting Spacing
127
+
128
+ Modify the spacing scale in `less/_variables.less`:
129
+
130
+ ```less
131
+ @jv-space-2: 4px;
132
+ @jv-space-4: 8px;
133
+ // ... etc
134
+ ```
135
+
136
+ ### Component Styling
137
+
138
+ Each component has its own file:
139
+ - **Buttons**: `less/_buttons.less`
140
+ - **Forms**: `less/_forms.less`
141
+ - **Cards**: `less/_cards.less`
142
+ - **Sidebar**: `less/_sidebar.less`
143
+ - **Categories**: `less/_categories.less`
144
+
145
+ ### Adding Custom Components
146
+
147
+ 1. Create a new LESS file: `less/_yourcomponent.less`
148
+ 2. Import it in `less/theme.less`:
149
+ ```less
150
+ @import "_yourcomponent";
151
+ ```
152
+ 3. Rebuild: `npm run build`
153
+
154
+ ## Maintenance
155
+
156
+ ### After Making Changes
157
+
158
+ 1. Edit LESS files in the `less/` directory
159
+ 2. Run `npm run build` to compile
160
+ 3. If deployed, re-upload the theme to NodeBB Cloud
161
+ 4. Rebuild NodeBB in the ACP
162
+
163
+ ### Updating NodeBB
164
+
165
+ Since this is a standalone theme, NodeBB updates won't affect your design. However:
166
+ - Test the theme after major NodeBB version updates
167
+ - Check for any DOM structure changes that might need CSS adjustments
168
+
169
+ ## Troubleshooting
170
+
171
+ ### Theme Not Applying
172
+
173
+ 1. Check that the theme is activated in ACP → Appearance → Themes
174
+ 2. Make sure you rebuilt NodeBB after installation
175
+ 3. Clear browser cache (Cmd+Shift+R)
176
+
177
+ ### Styles Not Updating
178
+
179
+ 1. Make sure you ran `npm run build` after editing LESS files
180
+ 2. Check that `static/lib/theme.css` was generated
181
+ 3. Rebuild NodeBB in the ACP
182
+
183
+ ### Missing Dependencies
184
+
185
+ ```bash
186
+ npm install
187
+ ```
188
+
189
+ ## Support
190
+
191
+ For issues or questions:
192
+ - Check NodeBB documentation: https://docs.nodebb.org
193
+ - NodeBB Community: https://community.nodebb.org
194
+
195
+ ## License
196
+
197
+ MIT License - feel free to customize for your needs!
198
+
199
+ ---
200
+
201
+ **Built with ❤️ for JAVIS Community**
@@ -0,0 +1,70 @@
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
+ }
@@ -0,0 +1,90 @@
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
+ }
@@ -0,0 +1,118 @@
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
+ }
@@ -0,0 +1,121 @@
1
+ // ===========================================================
2
+ // CATEGORIES – Enterprise Clean Style (Linear-inspired)
3
+ // ===========================================================
4
+
5
+ // Category Header
6
+ .feed h2 {
7
+ font-size: 16px;
8
+ font-weight: 600;
9
+ margin: 0 0 @jv-space-6 0;
10
+ padding: 0;
11
+ color: @jv-text-main;
12
+ letter-spacing: -0.2px;
13
+ }
14
+
15
+ // Wrapper list spacing
16
+ ul.categories-list.list-unstyled {
17
+ margin: 0;
18
+ padding: 0;
19
+ display: flex;
20
+ flex-direction: column;
21
+ gap: 0;
22
+ }
23
+
24
+ // ===========================================================
25
+ // PARENT CATEGORY BLOCK
26
+ // ===========================================================
27
+
28
+ li[component="categories/category"] {
29
+ padding: 16px 0;
30
+ border-bottom: 1px solid #f1f1f1;
31
+ transition: background 120ms ease;
32
+
33
+ &:last-child {
34
+ border-bottom: none;
35
+ }
36
+
37
+ &:hover {
38
+ background: rgba(0, 0, 0, 0.02);
39
+ }
40
+
41
+ // Parent title
42
+ .title {
43
+ font-size: @jv-font-size-base;
44
+ font-weight: 600;
45
+ color: @jv-text-main;
46
+ margin-bottom: 4px;
47
+ display: flex;
48
+ align-items: center;
49
+ gap: 6px;
50
+
51
+ a {
52
+ text-decoration: none;
53
+ color: inherit;
54
+
55
+ &:hover {
56
+ color: @jv-primary;
57
+ }
58
+ }
59
+
60
+ i {
61
+ font-size: @jv-font-size-base;
62
+ opacity: 0.95;
63
+ }
64
+ }
65
+
66
+ // Category description
67
+ .description {
68
+ font-size: @jv-font-size-sm;
69
+ color: @jv-text-muted;
70
+ line-height: 1.45;
71
+ margin-bottom: 10px;
72
+ max-width: 92%;
73
+ }
74
+ }
75
+
76
+ // ===========================================================
77
+ // CHILD CATEGORY ITEMS – Linear-style tiny dot list
78
+ // ===========================================================
79
+
80
+ li[component="categories/category"] .category-children {
81
+ display: flex;
82
+ flex-direction: column;
83
+ gap: 6px;
84
+ margin-top: 4px;
85
+ }
86
+
87
+ .category-children-item {
88
+ small > .d-flex {
89
+ display: flex;
90
+ align-items: center;
91
+ gap: 6px;
92
+ font-size: 14px;
93
+ color: #333;
94
+ }
95
+
96
+ // Dot (neutral by default)
97
+ i {
98
+ font-size: 6px;
99
+ color: #999;
100
+ margin-top: 1px;
101
+ }
102
+
103
+ // Child link
104
+ a {
105
+ text-decoration: none;
106
+ font-size: 14px;
107
+ color: #333;
108
+ transition: color 120ms ease, transform 80ms ease;
109
+ }
110
+
111
+ // Hover state: blue dot + blue text
112
+ &:hover {
113
+ i {
114
+ color: @jv-primary;
115
+ }
116
+
117
+ a {
118
+ color: @jv-primary;
119
+ }
120
+ }
121
+ }
@@ -0,0 +1,104 @@
1
+ // ===========================================================
2
+ // INPUT SYSTEM – Global Form Controls
3
+ // ===========================================================
4
+
5
+ .form-control,
6
+ input[type="text"],
7
+ input[type="search"],
8
+ input[type="email"],
9
+ input[type="password"],
10
+ textarea,
11
+ .form-select {
12
+ background: @jv-surface;
13
+ border: 1px solid @jv-border-subtle;
14
+ border-radius: @jv-radius-pill;
15
+ padding: 10px 16px;
16
+ font-size: @jv-font-size-base;
17
+ color: @jv-text-main;
18
+ line-height: @jv-line-height-tight;
19
+ box-shadow: none;
20
+ transition: all 0.15s ease;
21
+
22
+ &::placeholder {
23
+ color: @jv-text-muted;
24
+ }
25
+
26
+ &:focus {
27
+ outline: none;
28
+ border-color: @jv-primary;
29
+ box-shadow: 0 0 0 3px @jv-primary-soft;
30
+ }
31
+ }
32
+
33
+ // Textarea specific
34
+ textarea {
35
+ border-radius: @jv-radius-md;
36
+ resize: vertical;
37
+ min-height: 100px;
38
+ }
39
+
40
+ // ===========================================================
41
+ // SEARCH BAR
42
+ // ===========================================================
43
+
44
+ .search-widget .input-group {
45
+ .form-control {
46
+ border-top-right-radius: 0;
47
+ border-bottom-right-radius: 0;
48
+ }
49
+
50
+ .btn-primary {
51
+ border-top-left-radius: 0;
52
+ border-bottom-left-radius: 0;
53
+ padding: 0 18px;
54
+ }
55
+ }
56
+
57
+ // ===========================================================
58
+ // COMPOSER TRIGGER (Write a Post…)
59
+ // ===========================================================
60
+
61
+ .jlc-trigger {
62
+ width: 100%;
63
+ background: @jv-surface;
64
+ border: 1px solid @jv-border-subtle;
65
+ border-radius: @jv-radius-pill;
66
+ padding: 12px 18px;
67
+ font-size: @jv-font-size-base;
68
+ font-weight: 400;
69
+ text-align: left;
70
+ color: @jv-text-muted;
71
+ box-shadow: @jv-shadow-soft;
72
+ transition: all 0.15s ease;
73
+
74
+ &:hover,
75
+ &:focus {
76
+ background: @jv-surface;
77
+ border-color: @jv-primary;
78
+ color: @jv-primary;
79
+ box-shadow: @jv-shadow-soft;
80
+ outline: none;
81
+ }
82
+
83
+ &:active {
84
+ transform: scale(0.99);
85
+ }
86
+
87
+ // Override Bootstrap button states
88
+ &.btn,
89
+ &.btn:focus,
90
+ &.btn:hover,
91
+ &.btn:active {
92
+ background: @jv-surface;
93
+ border-radius: @jv-radius-pill;
94
+ box-shadow: @jv-shadow-soft;
95
+ border-color: @jv-border-subtle;
96
+ outline: none;
97
+ }
98
+
99
+ &.btn:hover,
100
+ &.btn:focus {
101
+ border-color: @jv-primary;
102
+ color: @jv-primary;
103
+ }
104
+ }
@@ -0,0 +1,110 @@
1
+ // ===========================================================
2
+ // SIDEBAR SYSTEM – Trending Tags + Trending Now
3
+ // ===========================================================
4
+
5
+ // Sidebar Widget Titles
6
+ div[data-widget-area="right"] {
7
+ h5, h8 {
8
+ font-size: 16px;
9
+ font-weight: 600;
10
+ margin-bottom: @jv-space-4;
11
+ color: @jv-text-main;
12
+ }
13
+ }
14
+
15
+ // ===========================================================
16
+ // TRENDING TAGS
17
+ // ===========================================================
18
+
19
+ .popular-tags {
20
+ gap: 6px;
21
+
22
+ .tag-list > div {
23
+ background: @jv-surface;
24
+ border: 1px solid @jv-border-subtle;
25
+ border-radius: 10px;
26
+ padding: 10px 12px;
27
+ font-size: 14px;
28
+ color: @jv-text-main;
29
+ display: flex;
30
+ flex-direction: column;
31
+ gap: 2px;
32
+ cursor: pointer;
33
+ transition: border-color 0.15s ease, background 0.15s ease;
34
+
35
+ &:hover {
36
+ border-color: rgba(0,0,0,0.12);
37
+ background: rgba(0,0,0,0.03);
38
+ }
39
+
40
+ small {
41
+ color: @jv-text-muted;
42
+ font-size: @jv-font-size-xs;
43
+ }
44
+ }
45
+ }
46
+
47
+ // ===========================================================
48
+ // TRENDING NOW
49
+ // ===========================================================
50
+
51
+ #recent_posts {
52
+ display: flex;
53
+ flex-direction: column;
54
+ gap: 10px;
55
+ margin-top: @jv-space-6;
56
+
57
+ li.widget-posts {
58
+ background: @jv-surface;
59
+ border: 1px solid rgba(0,0,0,0.05);
60
+ border-radius: @jv-radius-md;
61
+ padding: 10px 14px;
62
+ display: flex;
63
+ flex-direction: column;
64
+ gap: @jv-space-4;
65
+ transition: box-shadow 0.15s ease, border 0.15s ease;
66
+
67
+ &:hover {
68
+ border-color: rgba(0,0,0,0.08);
69
+ box-shadow: @jv-shadow-soft;
70
+ }
71
+
72
+ // Meta info
73
+ .d-flex.gap-2,
74
+ .timeago {
75
+ font-size: @jv-font-size-xs;
76
+ color: @jv-text-muted;
77
+ }
78
+
79
+ // Title inside mini post
80
+ a {
81
+ font-size: 14px;
82
+ font-weight: 500;
83
+ color: @jv-text-main;
84
+ text-decoration: none;
85
+ line-height: @jv-line-height-tight;
86
+
87
+ &:hover {
88
+ color: @jv-primary;
89
+ }
90
+ }
91
+
92
+ // 2-line clamp for content preview
93
+ p,
94
+ .content {
95
+ display: -webkit-box;
96
+ -webkit-line-clamp: 2;
97
+ -webkit-box-orient: vertical;
98
+ overflow: hidden;
99
+ font-size: @jv-font-size-sm;
100
+ color: @jv-text-muted;
101
+ margin: 0;
102
+ }
103
+
104
+ // Read more alignment
105
+ a.stretched-link {
106
+ font-size: @jv-font-size-xs;
107
+ color: @jv-primary;
108
+ }
109
+ }
110
+ }
@@ -0,0 +1,54 @@
1
+ // ============================================
2
+ // JAVIS Design System – Tokens
3
+ // ============================================
4
+
5
+ // Brand Colors
6
+ @jv-primary: #0051ff;
7
+ @jv-primary-hover: #0044dd;
8
+ @jv-primary-soft: rgba(0, 81, 255, 0.12);
9
+
10
+ // Neutrals
11
+ @jv-bg: #f5f6f8; // app background
12
+ @jv-surface: #ffffff; // cards, panels
13
+ @jv-border-subtle: rgba(0,0,0,0.06);
14
+ @jv-border-strong: rgba(0,0,0,0.12);
15
+
16
+ // Text
17
+ @jv-text-main: #111111;
18
+ @jv-text-muted: #6b7280;
19
+ @jv-text-soft: #9ca3af;
20
+
21
+ // Radii
22
+ @jv-radius-xs: 4px;
23
+ @jv-radius-sm: 8px;
24
+ @jv-radius-md: 12px;
25
+ @jv-radius-lg: 16px;
26
+ @jv-radius-pill: 999px;
27
+
28
+ // Shadows
29
+ @jv-shadow-soft: 0 2px 8px rgba(0,0,0,0.04);
30
+ @jv-shadow-card: 0 4px 10px rgba(0,0,0,0.05);
31
+ @jv-shadow-button: 0 4px 12px rgba(0, 81, 255, 0.25);
32
+ @jv-shadow-button-hover: 0 6px 16px rgba(0, 81, 255, 0.35);
33
+
34
+ // Spacing (8pt-ish scale)
35
+ @jv-space-2: 4px;
36
+ @jv-space-4: 8px;
37
+ @jv-space-6: 12px;
38
+ @jv-space-8: 16px;
39
+ @jv-space-10: 20px;
40
+ @jv-space-12: 24px;
41
+
42
+ // Typography
43
+ @jv-font-sans: -apple-system, BlinkMacSystemFont, "SF Pro Text", system-ui, "Helvetica Neue", Arial, sans-serif;
44
+ @jv-font-size-base: 15px;
45
+ @jv-font-size-sm: 13px;
46
+ @jv-font-size-xs: 12px;
47
+ @jv-font-size-lg: 18px;
48
+ @jv-font-size-xl: 22px;
49
+ @jv-font-size-xxl: 32px;
50
+
51
+ // Line Heights
52
+ @jv-line-height-base: 1.52;
53
+ @jv-line-height-tight: 1.4;
54
+ @jv-line-height-relaxed: 1.6;
@@ -0,0 +1,18 @@
1
+ // ============================================
2
+ // JAVIS Community Theme
3
+ // Modern, premium NodeBB theme
4
+ // Inspired by Apple, Stripe, and Linear
5
+ // ============================================
6
+
7
+ // Core Design System
8
+ @import "_variables";
9
+
10
+ // Base Styles
11
+ @import "_base";
12
+
13
+ // Components
14
+ @import "_buttons";
15
+ @import "_forms";
16
+ @import "_cards";
17
+ @import "_sidebar";
18
+ @import "_categories";
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@okjavis/nodebb-theme-javis",
3
+ "version": "1.0.0",
4
+ "description": "Modern, premium NodeBB theme for JAVIS Community - Extends Harmony with custom styling",
5
+ "main": "theme.js",
6
+ "scripts": {
7
+ "build": "npm run build:less",
8
+ "build:less": "lessc less/theme.less static/lib/theme.css --clean-css",
9
+ "watch": "npm-watch",
10
+ "dev": "npm run watch",
11
+ "prepublishOnly": "npm run build"
12
+ },
13
+ "watch": {
14
+ "build:less": {
15
+ "patterns": [
16
+ "less"
17
+ ],
18
+ "extensions": "less",
19
+ "quiet": false
20
+ }
21
+ },
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/javis-admin/nodebb-community-theme.git"
25
+ },
26
+ "bugs": {
27
+ "url": "https://github.com/javis-admin/nodebb-community-theme/issues"
28
+ },
29
+ "homepage": "https://github.com/javis-admin/nodebb-community-theme#readme",
30
+ "keywords": [
31
+ "nodebb-theme",
32
+ "nodebb",
33
+ "theme",
34
+ "javis",
35
+ "community",
36
+ "harmony"
37
+ ],
38
+ "author": "Chirag Dugar",
39
+ "license": "MIT",
40
+ "peerDependencies": {
41
+ "nodebb-theme-harmony": "^1.0.0"
42
+ },
43
+ "devDependencies": {
44
+ "less": "^4.2.0",
45
+ "less-plugin-clean-css": "^1.5.1",
46
+ "npm-watch": "^0.11.0"
47
+ },
48
+ "nbbpm": {
49
+ "compatibility": "^3.0.0"
50
+ },
51
+ "files": [
52
+ "less/",
53
+ "static/",
54
+ "templates/",
55
+ "theme.js",
56
+ "plugin.json"
57
+ ]
58
+ }
package/plugin.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "id": "@okjavis/nodebb-theme-javis",
3
+ "name": "JAVIS Community Theme",
4
+ "description": "Modern, premium NodeBB theme for JAVIS Community - Extends Harmony with custom styling and selective template overrides",
5
+ "url": "https://github.com/javis-admin/nodebb-community-theme",
6
+ "library": "./theme.js",
7
+ "baseTheme": "nodebb-theme-harmony",
8
+ "staticDirs": {
9
+ "static": "./static"
10
+ },
11
+ "less": [
12
+ "less/theme.less"
13
+ ],
14
+ "scripts": [
15
+ "static/lib/theme.js"
16
+ ],
17
+ "templates": "templates",
18
+ "screenshot": "screenshot.png",
19
+ "version": "1.0.0",
20
+ "nbbpm": {
21
+ "compatibility": "^3.0.0"
22
+ }
23
+ }
@@ -0,0 +1 @@
1
+ body,html{font-family:-apple-system,BlinkMacSystemFont,"SF Pro Text",system-ui,"Helvetica Neue",Arial,sans-serif;font-size:15px;line-height:1.52;background-color:#f5f6f8;color:#111;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;margin:0;padding:0}p{margin:0 0 12px 0}h1{font-size:32px;font-weight:600;letter-spacing:-.02em;margin-bottom:16px}h2{font-size:22px;font-weight:600;letter-spacing:-.01em;margin-bottom:12px}h3{font-size:22px;font-weight:600;margin-bottom:12px}h4{font-size:20px;font-weight:600;margin-bottom:12px}h5{font-size:17px;font-weight:600;margin-bottom:12px}h6{font-size:15px;font-weight:600;margin-bottom:8px}a{color:#0051ff;text-decoration:none;transition:color .15s ease}a:hover{color:#04d}.btn{border-radius:999px;padding:6px 16px;font-size:14px;font-weight:500;line-height:1.32;transition:all .15s ease;border-width:1px;cursor:pointer}.btn-primary{background-color:#0051ff;border-color:#0051ff;color:#fff;box-shadow:0 4px 12px rgba(0,81,255,.25)}.btn-primary:focus,.btn-primary:hover{background-color:#04d;border-color:#04d;box-shadow:0 6px 16px rgba(0,81,255,.35);transform:translateY(-1px);color:#fff}.btn-primary:active{transform:translateY(0)}.btn-outline-primary{background-color:transparent;border-color:#0051ff;color:#0051ff}.btn-outline-primary:focus,.btn-outline-primary:hover{background-color:rgba(0,81,255,.12);border-color:#0051ff;color:#0051ff}.btn-default,.btn-secondary{background-color:#f7f8fa;border-color:rgba(0,0,0,.06);color:#111}.btn-default:focus,.btn-default:hover,.btn-secondary:focus,.btn-secondary:hover{background-color:#eef0f3;border-color:rgba(0,0,0,.06);color:#111}.btn-link{color:#0051ff;font-weight:500;padding:0}.btn-link:hover{text-decoration:underline;color:#04d}.btn-sm{padding:4px 12px;font-size:13px}.btn-lg{padding:10px 22px;font-size:15px;border-radius:999px}.form-control,.form-select,input[type=email],input[type=password],input[type=search],input[type=text],textarea{background:#fff;border:1px solid rgba(0,0,0,.06);border-radius:999px;padding:10px 16px;font-size:15px;color:#111;line-height:1.4;box-shadow:none;transition:all .15s ease}.form-control::placeholder,.form-select::placeholder,input[type=email]::placeholder,input[type=password]::placeholder,input[type=search]::placeholder,input[type=text]::placeholder,textarea::placeholder{color:#6b7280}.form-control:focus,.form-select:focus,input[type=email]:focus,input[type=password]:focus,input[type=search]:focus,input[type=text]:focus,textarea:focus{outline:0;border-color:#0051ff;box-shadow:0 0 0 3px rgba(0,81,255,.12)}textarea{border-radius:12px;resize:vertical;min-height:100px}.search-widget .input-group .form-control{border-top-right-radius:0;border-bottom-right-radius:0}.search-widget .input-group .btn-primary{border-top-left-radius:0;border-bottom-left-radius:0;padding:0 18px}.jlc-trigger{width:100%;background:#fff;border:1px solid rgba(0,0,0,.06);border-radius:999px;padding:12px 18px;font-size:15px;font-weight:400;text-align:left;color:#6b7280;box-shadow:0 2px 8px rgba(0,0,0,.04);transition:all .15s ease}.jlc-trigger:focus,.jlc-trigger:hover{background:#fff;border-color:#0051ff;color:#0051ff;box-shadow:0 2px 8px rgba(0,0,0,.04);outline:0}.jlc-trigger:active{transform:scale(.99)}.jlc-trigger.btn,.jlc-trigger.btn:active,.jlc-trigger.btn:focus,.jlc-trigger.btn:hover{background:#fff;border-radius:999px;box-shadow:0 2px 8px rgba(0,0,0,.04);border-color:rgba(0,0,0,.06);outline:0}.jlc-trigger.btn:focus,.jlc-trigger.btn:hover{border-color:#0051ff;color:#0051ff}li[component=post].posts-list-item{background:#fff;border-radius:16px;border:1px solid rgba(0,0,0,.05);box-shadow:0 4px 10px rgba(0,0,0,.05);padding:24px 22px 20px 22px;margin-bottom:24px;transition:box-shadow .15s ease,border-color .15s ease}li[component=post].posts-list-item:hover{box-shadow:0 6px 14px rgba(0,0,0,.08);border-color:rgba(0,0,0,.08)}li[component=post] .overflow-hidden,li[component=post] img.w-100{border-radius:16px}li[component=post] .post-body{display:flex;flex-direction:column;gap:12px;padding-top:4px}li[component=post] .topic-title{display:block;margin-bottom:6px;font-size:18px;font-weight:600;letter-spacing:-.01em;line-height:1.4;color:#111}li[component=post] .topic-title a{color:inherit;text-decoration:none}li[component=post] .topic-title a:hover{color:#0051ff}li[component=post] .d-flex.gap-2{margin-bottom:10px}li[component=post] .post-meta a,li[component=post] .timeago,li[component=post] .topic-meta{font-size:13px;color:#6b7280}li[component=post] .content p{font-size:15px;line-height:1.52;margin-bottom:18px;color:#111}li[component=post] hr{margin:16px 0;border-color:rgba(0,0,0,.06)}li[component=post] .d-flex.justify-content-between{margin-top:6px;padding-bottom:2px}li[component=post] .d-flex.gap-3 a{background:0 0;border:none;padding:0;font-size:13px;color:#6b7280;opacity:.85;display:flex;align-items:center;gap:6px;box-shadow:none;text-decoration:none;transition:color .15s ease,opacity .15s ease}li[component=post] .d-flex.gap-3 a:hover{color:#0051ff;opacity:1}li[component=post] .d-flex.gap-3 a i{font-size:15px;color:inherit}div[data-widget-area=right] h5,div[data-widget-area=right] h8{font-size:16px;font-weight:600;margin-bottom:8px;color:#111}.popular-tags{gap:6px}.popular-tags .tag-list>div{background:#fff;border:1px solid rgba(0,0,0,.06);border-radius:10px;padding:10px 12px;font-size:14px;color:#111;display:flex;flex-direction:column;gap:2px;cursor:pointer;transition:border-color .15s ease,background .15s ease}.popular-tags .tag-list>div:hover{border-color:rgba(0,0,0,.12);background:rgba(0,0,0,.03)}.popular-tags .tag-list>div small{color:#6b7280;font-size:12px}#recent_posts{display:flex;flex-direction:column;gap:10px;margin-top:12px}#recent_posts li.widget-posts{background:#fff;border:1px solid rgba(0,0,0,.05);border-radius:12px;padding:10px 14px;display:flex;flex-direction:column;gap:8px;transition:box-shadow .15s ease,border .15s ease}#recent_posts li.widget-posts:hover{border-color:rgba(0,0,0,.08);box-shadow:0 2px 8px rgba(0,0,0,.04)}#recent_posts li.widget-posts .d-flex.gap-2,#recent_posts li.widget-posts .timeago{font-size:12px;color:#6b7280}#recent_posts li.widget-posts a{font-size:14px;font-weight:500;color:#111;text-decoration:none;line-height:1.4}#recent_posts li.widget-posts a:hover{color:#0051ff}#recent_posts li.widget-posts .content,#recent_posts li.widget-posts p{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;font-size:13px;color:#6b7280;margin:0}#recent_posts li.widget-posts a.stretched-link{font-size:12px;color:#0051ff}.feed h2{font-size:16px;font-weight:600;margin:0 0 12px 0;padding:0;color:#111;letter-spacing:-.2px}ul.categories-list.list-unstyled{margin:0;padding:0;display:flex;flex-direction:column;gap:0}li[component="categories/category"]{padding:16px 0;border-bottom:1px solid #f1f1f1;transition:background 120ms ease}li[component="categories/category"]:last-child{border-bottom:none}li[component="categories/category"]:hover{background:rgba(0,0,0,.02)}li[component="categories/category"] .title{font-size:15px;font-weight:600;color:#111;margin-bottom:4px;display:flex;align-items:center;gap:6px}li[component="categories/category"] .title a{text-decoration:none;color:inherit}li[component="categories/category"] .title a:hover{color:#0051ff}li[component="categories/category"] .title i{font-size:15px;opacity:.95}li[component="categories/category"] .description{font-size:13px;color:#6b7280;line-height:1.45;margin-bottom:10px;max-width:92%}li[component="categories/category"] .category-children{display:flex;flex-direction:column;gap:6px;margin-top:4px}.category-children-item small>.d-flex{display:flex;align-items:center;gap:6px;font-size:14px;color:#333}.category-children-item i{font-size:6px;color:#999;margin-top:1px}.category-children-item a{text-decoration:none;font-size:14px;color:#333;transition:color 120ms ease,transform 80ms ease}.category-children-item:hover i{color:#0051ff}.category-children-item:hover a{color:#0051ff}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * JAVIS Community Theme - Client-side JavaScript
3
+ * Handles theme-specific interactions and behaviors
4
+ */
5
+
6
+ (function() {
7
+ 'use strict';
8
+
9
+ // Theme initialization
10
+ $(document).ready(function() {
11
+ console.log('JAVIS Community Theme initialized');
12
+
13
+ // Add any custom client-side interactions here
14
+ // Example: smooth scrolling, animations, custom widgets, etc.
15
+ });
16
+
17
+ })();
@@ -0,0 +1,38 @@
1
+ {{{
2
+ JAVIS Community Theme - Post List Item
3
+
4
+ This template overrides Harmony's posts_list_item.tpl
5
+ Used on: Recent, Popular, Unread pages (feed view)
6
+
7
+ To customize: Edit the HTML structure below
8
+ Original: https://github.com/NodeBB/nodebb-theme-harmony/blob/master/templates/partials/posts_list_item.tpl
9
+ }}}
10
+ <li component="post" class="posts-list-item {{{ if ./deleted }}} deleted{{{ else }}}{{{ if ./topic.deleted }}} deleted{{{ end }}}{{{ end }}}{{{ if ./topic.scheduled }}} scheduled{{{ end }}}" data-pid="{./pid}" data-uid="{./uid}">
11
+
12
+ <a class="topic-title fw-semibold fs-5 mb-2 text-reset text-break d-block" href="{config.relative_path}/post/{encodeURIComponent(./pid)}">
13
+ {{{ if ./isMainPost }}}<i class="fa fa-book text-muted" title="[[topic:topic]]"></i> {{{ end }}}{./topic.title}
14
+ </a>
15
+
16
+ <div class="post-body d-flex flex-column gap-1 mb-2">
17
+ <div class="d-flex gap-2 post-info text-sm align-items-center">
18
+ <div class="post-author d-flex align-items-center gap-1">
19
+ <a class="lh-1 text-decoration-none" href="{config.relative_path}/user/{./user.userslug}">{buildAvatar(./user, "16px", true, "not-responsive")}</a>
20
+ <a class="lh-1 fw-semibold" href="{config.relative_path}/user/{./user.userslug}">{../user.displayname}</a>
21
+ </div>
22
+ <span class="timeago text-muted lh-1" title="{./timestampISO}"></span>
23
+ </div>
24
+
25
+ <div component="post/content" class="content text-sm text-break">
26
+ {./content}
27
+ </div>
28
+ </div>
29
+
30
+ <div class="mb-3 d-flex flex-wrap gap-1 w-100">
31
+ {buildCategoryLabel(./category, "a", "border")}
32
+ <span data-tid="{./topic.tid}" component="topic/tags" class="lh-1 tag-list d-flex flex-wrap gap-1 {{{ if !./topic.tags.length }}}hidden{{{ end }}}">
33
+ {{{ each ./topic.tags }}}
34
+ <a href="{config.relative_path}/tags/{./valueEncoded}"><span class="badge border border-gray-300 fw-normal tag tag-class-{./class}" data-tag="{./value}">{./valueEscaped}</span></a>
35
+ {{{ end }}}
36
+ </span>
37
+ </div>
38
+ </li>
package/theme.js ADDED
@@ -0,0 +1,38 @@
1
+ /**
2
+ * JAVIS Community Theme
3
+ * Main theme configuration and initialization
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const theme = {};
9
+
10
+ theme.defineWidgetAreas = async (areas) => {
11
+ // Define custom widget areas for JAVIS theme
12
+ areas = areas.concat([
13
+ {
14
+ name: 'JAVIS Right Sidebar',
15
+ template: 'categories.tpl',
16
+ location: 'sidebar-right',
17
+ },
18
+ {
19
+ name: 'JAVIS Left Sidebar',
20
+ template: 'categories.tpl',
21
+ location: 'sidebar-left',
22
+ },
23
+ ]);
24
+
25
+ return areas;
26
+ };
27
+
28
+ theme.addAdminNavigation = (header) => {
29
+ header.plugins.push({
30
+ route: '/plugins/javis-community-theme',
31
+ icon: 'fa-paint-brush',
32
+ name: 'JAVIS Theme',
33
+ });
34
+
35
+ return header;
36
+ };
37
+
38
+ module.exports = theme;