• Hi All,

    I thought that seeing as I’ve been quiet for a while that I’d start a new thread that will detail the journey Sudonix is taking to reach BS5, v3 of NodeBB, and the adoption of the Harmony theme. I’ve been playing with the “Nord” theme, and am pleased to report that the CSS needed since the move from standard CSS to LESS / SCSS has been remarkably reduced. Presently, I’m at 324 lines - a huge difference to the 4,479 (yes, that’s right…) currently in use here.

    And no doubt you’re curious as to what v3.beta1 of NodeBB looks like, combined with Harmony, and the theme I’m working on?

    Here’s what the standard iteration looks like via https://community.nodebb.org

    8afb6691-68ef-4c34-8de8-088808181b75-image.png

    And here’s what the Nord theme on https://sudonix.dev looks like

    WARNING: Extreme sexiness ahead 🙂

    6f377bf7-f1c8-4908-a18c-cdbf473d2d3e-image.png

    cfbb2f07-1549-41fe-a937-3ccffdead9c4-image.png

    3c2cc439-bfbb-49f7-82b5-488740c93d5b-image.png

    The Swatch script is also functional

    fd3020e9-ec4f-4df8-9d79-9ca8262fe005-image.png

    Interestingly, this differs heavily from v2 (mostly because the jump from BS3 to BS5 is significant). Currently, it looks like this in v2

    // ------------------------------------------
    // Swatch Applet
    // ------------------------------------------
    
    
    
    
    $(document).ready(function() {
    	function generateRandomString(length) {
    
    		var text = "";
    		var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    
    		for (var i = 0; i < length; i++) {
    			text += possible.charAt(Math.floor(Math.random() * possible.length));
    		}
    
    		return text;
    	}
    	var string = generateRandomString(10);
    	$("#random_string").text(string);
    	var whichTheme = localStorage.getItem("theme");
    	var activeTheme = localStorage.getItem("activeTheme");
    	// If no theme is detected (for example, a new visitor), then set this to default
    	if (!whichTheme) {
    		// dark-mode media query matched or not
    		let matched = window.matchMedia('(prefers-color-scheme: dark)').matches;
    		//var override = getUrlParameter('override');
    
    		if (matched) {
    			// Offer the mifnight theme by default
    			whichTheme = "midnight";
    			activeTheme = "/assets/customcss/midnight.css?version=" + string;
    			//$("link[rel=stylesheet]").attr('href' , thishref + "?version=" + string + "");
    		} else {
    			// Leave the default theme intact
    			whichTheme = "default";
    			activeTheme = "/assets/customcss/daylight.css?version=" + string;
    		}
    		// See if override has been enabled
    		if (whichTheme === 'default') {
    			// Sudonix is overriding operating system settings and will force dark scheme
    			activeTheme = "/assets/customcss/daylight.css?version=" + string;
    		}
    		if (whichTheme === 'daylight') {
    			// $('[component="post"]').addClass("background");
    			$('li.self-post .content:not(.isSolved [component="post/content"]').addClass("response");
    		} else {
    			// Nothing to do :)
    		}
    	}
    
    	/*$(".forum-logo").attr("src","/assets/uploads/system/sl_" + whichTheme + ".webp?version=" + string + ""); */
    	var panel = $('<li id="switcher" class="dropdown"> \
    <a title="" data-original-title="Swatch" class="navigation-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> \
    <i id="ticon" class="fa fa-light fa-swatchbook" data-content="" aria-hidden="true"></i> \
    <span class="visible-xs-inline">Swatch</span> \
    <i class="fa fa-caret-down" aria-hidden="true"></i> \
    </a> \
    <ul id="theme" class="dropdown-menu"> \
    <li><a id="default" href="#" rel="/assets/customcss/daylight.css">Default</a></li> \
    <li><a id="anthracite" href="#" rel="/assets/customcss/anthracite.css">Anthracite</a></li> \
    <li><a id="darkly" href="#" rel="/assets/customcss/darkly.css">Darkly</a></li> \
    <li><a id="daylight" href="#" rel="/assets/customcss/daylight.css">Daylight</a></li> \
    <li><a id="facetube" href="#" rel="/assets/customcss/facetube.css">FaceTube</a></li> \
    <li><a id="greybird" href="#" rel="/assets/customcss/greybird.css">Greybird</a></li> \
    <li><a id="midnight" href="#" rel="/assets/customcss/midnight.css">Midnight</a></li> \
    <li><a id="nord" href="#" rel="/assets/customcss/nord.css">Nord</a></li> \
    <li><a id="slate" href="#" rel="/assets/customcss/slate.css">Slate</a></li> \
    <li><a id="superhero" href="#" rel="/assets/customcss/superhero.css">Superhero</a></li> \
    <li><a id="tempest" href="#" rel="/assets/customcss/tempest.css">Tempest</a></li> \
    <li><a id="twitter" href="#" rel="/assets/customcss/twitter.css">Twitter</a></li> \
    </ul> \
    </li> \
    </div> \
    ');
    
    
    	if (whichTheme) {
    		$.get(activeTheme, function(css) {
    			$('<style type="text/css"></style>')
    				.html(css)
    				.appendTo("head");
    		});
    	} else {}
    
    	$('#main-nav').append(panel);
    
    	if (utils.findBootstrapEnvironment() === 'xs') {
    		$('#main-nav').append(panel);
    	}
    
    	$(document).ready(function() {
    
    		$("body").on("click change", "#theme li a", function() {
    			var thishref = $(this).attr('rel') + '?version=' + string;
    			$.get(thishref, function(css) {
    				$('<style type="text/css"></style>')
    					.html(css)
    					.appendTo("head");
    			});
    			console.log("Applying swatch " + thishref);
    			//location.reload();
    			var selected = $(this).attr("id");
    			var theTheme = $(this).attr("rel");
    			if (selected === 'default') {
    				localStorage.setItem("theme", selected);
    				localStorage.setItem("activeTheme", "/assets/customcss/daylight.css?version=" + string);
    				//location.reload();
    			} else {
    				localStorage.setItem("theme", selected);
    				localStorage.setItem("activeTheme", theTheme);
    			}
    			return false;
    		});
    	});
    });
    

    Now in v3, it looks VERY different

    var mobiledropdown = $('<li class="nav-item  dropend" title="Swatch"> \
    	<a class="nav-link nav-btn navigation-link px-3 py-2 dropdown-toggle" href="#" role="button" data-bs-toggle="collapse" data-bs-target="#theme" onclick="event.stopPropagation();"> \
    		<span class="d-inline-flex justify-content-between align-items-center w-100"> \
    			<span class="text-nowrap truncate-open"> \
    				<i class="fa fa-fw fa-painbrush" data-content="" aria-hidden="true"></i>
    				<span class="nav-text visible-open px-2 fw-semibold">More</span>
    			</span>
    			<span component="navigation/count" class="visible-open badge rounded-1 bg-primary hidden"></span>
    		</span>
    	</a>
    	<div class="ps-3">
    		<ul id="theme" class="collapse list-unstyled ps-3"> \
    			<li><a id="default" class="dropdown-item rounded-1" href="#" rel="/assets/customcss/daylight.css">Default</a></li> \
    			<li><a id="anthracite" class="dropdown-item rounded-1" href="#" rel="/assets/customcss/anthracite.css">Anthracite</a></li> \
    			<li><a id="darkly" class="dropdown-item rounded-1" href="#" rel="/assets/customcss/darkly.css">Darkly</a></li> \
    			<li><a id="daylight" class="dropdown-item rounded-1" href="#" rel="/assets/customcss/daylight.css">Daylight</a></li> \
    			<li><a id="facetube" class="dropdown-item rounded-1" href="#" rel="/assets/customcss/facetube.css">FaceTube</a></li> \
    			<li><a id="greybird" class="dropdown-item rounded-1" href="#" rel="/assets/customcss/greybird.css">Greybird</a></li> \
    			<li><a id="midnight" class="dropdown-item rounded-1" href="#" rel="/assets/customcss/midnight.css">Midnight</a></li> \
    			<li><a id="nord" class="dropdown-item rounded-1" href="#" rel="/assets/customcss/nord.css">Nord</a></li> \
    			<li><a id="slate" class="dropdown-item rounded-1" href="#" rel="/assets/customcss/slate.css">Slate</a></li> \
    			<li><a id="superhero" class="dropdown-item rounded-1" href="#" rel="/assets/customcss/superhero.css">Superhero</a></li> \
    			<li><a id="tempest" class="dropdown-item rounded-1" href="#" rel="/assets/customcss/tempest.css">Tempest</a></li> \
    			<li><a id="twitter" class="dropdown-item rounded-1" href="#" rel="/assets/customcss/twitter.css">Twitter</a></li> \
    		</ul>
    	</div>
    	
    </li>
    ');
    
    var desktopnavbar = $('<li id="switcher" class="nav-item mx-2 dropend" title="Swatch"> \
    	<a class="nav-link nav-btn navigation-link d-flex gap-2 justify-content-between align-items-center dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> \
    					<span class="d-flex gap-2 align-items-center text-nowrap truncate-open"> \
    						<span class="position-relative"> \
    							<i class="fa fa-fw fa-paintbrush" data-content="" aria-hidden="true"></i> \
    							<span component="navigation/count" class="visible-closed position-absolute top-0 start-100 translate-middle badge rounded-1 bg-primary hidden"></span> \
    						</span> \
    						<span class="nav-text small visible-open fw-semibold text-truncate">Swatch</span> \
    					</span> \
    					<span component="navigation/count" class="visible-open badge rounded-1 bg-primary hidden"></span> \
    	</a> \
    	<ul id="theme" class="dropdown-menu overflow-auto" data-popper-placement="right-end"> \
    	<li><a id="default" class="dropdown-item rounded-1" href="#" rel="/assets/customcss/daylight.css">Default</a></li> \
    	<li><a id="anthracite" class="dropdown-item rounded-1" href="#" rel="/assets/customcss/anthracite.css">Anthracite</a></li> \
    	<li><a id="darkly" class="dropdown-item rounded-1" href="#" rel="/assets/customcss/darkly.css">Darkly</a></li> \
    	<li><a id="daylight" class="dropdown-item rounded-1" href="#" rel="/assets/customcss/daylight.css">Daylight</a></li> \
    	<li><a id="facetube" class="dropdown-item rounded-1" href="#" rel="/assets/customcss/facetube.css">FaceTube</a></li> \
    	<li><a id="greybird" class="dropdown-item rounded-1" href="#" rel="/assets/customcss/greybird.css">Greybird</a></li> \
    	<li><a id="midnight" class="dropdown-item rounded-1" href="#" rel="/assets/customcss/midnight.css">Midnight</a></li> \
    	<li><a id="nord" class="dropdown-item rounded-1" href="#" rel="/assets/customcss/nord.css">Nord</a></li> \
    	<li><a id="slate" class="dropdown-item rounded-1" href="#" rel="/assets/customcss/slate.css">Slate</a></li> \
    	<li><a id="superhero" class="dropdown-item rounded-1" href="#" rel="/assets/customcss/superhero.css">Superhero</a></li> \
    	<li><a id="tempest" class="dropdown-item rounded-1" href="#" rel="/assets/customcss/tempest.css">Tempest</a></li> \
    	<li><a id="twitter" class="dropdown-item rounded-1" href="#" rel="/assets/customcss/twitter.css">Twitter</a></li> \
    	</ul> \
    	</li> \
    	</div> \
    	');
    

    I’ve yet to decide how many of the v2 swatches will make their way into v3 on Sudonix -likely candidates will be

    • Anthracite
    • Darkly
    • Facetube
    • Midnight
    • Nord
    • Slate
    • Superhero
    • Tempest
    • Twitter

    Also, the “conversational / messenger view” is destined to be scrapped. It looked good at the time, and I know a number of you adopted it for your own forums, but it’s not really “today’s standard” - and, a LOT of effort to code for very little cosmetic return.

    Stay tuned for more updates 🙂


  • Let me know if you have any specific requests around v3 theming 🙂


  • Can’t wait to try this 🙂


  • The colour palette used in the Nord theme (see first post here) is taken from this excellent resource


  • @phenomlab

    How to use it ?
    PM if you want.


  • @DownPW You just use the hex codes it provides. Nothing more to it than that.


  • @phenomlab

    On ACP/JS ?

    Nothing on ACP/general/navigation ?


  • @DownPW sorry, I thought you were referring to the Nord theme URL I posted. The Swatch code isn’t ready for use yet.


  • @phenomlab Ok bro. No problem 🙂


  • I’ve made significant progress with the first Swatch template. However, it’s a little way off being completely ready as there are a number of js functions that need to be completed (or in some cases, rewritten) first before the alpha phase of testing can start.

    In addition, there’s the rapid succession of changes and updates to the bootstrap core in NodeBB v3, plus updates to the harmony theme itself, It’s hard to determine the impact this will have on the Swatch code itself. I’ve already found an issue in the tooltip function - as the Swatch code runs after the harmony code, it means that the same function for the tooltip has to be run again.

    This then causes an error to be generated in the console at random intervals. I know why this is - essentially, it’s because destroy as a JS function was removed in BS 4.1 and replaced with dispose - see the below thread raised in the NodeBB forum.

    The advice here is to start NodeBB using Grunt so that the JS remains uncompressed - which should make the destroy function easier to track. None of this is the “end of the world” - it’s merely cosmetic, but could cause issues further down the line, so it’s best to isolate this issue now and remediate - unless there’s a better way to activate the tooltip function again without it throwing the same error.

    Another option here is to use the navigation menu in the ACP which means that the tooltip function should fire as the menu items are already in the DOM. However, taking this route comes with a number of drawbacks in the sense that it isn’t as flexible as adding arbitrary code.

    This is something I’ll need to investigate further but taking this route places additional burden on those using the Swatch code which obviously negates the user experience.


  • More styling work completed today. Started looking at an decent alternative to the scroll top navigator featured on this site. I designed this a while ago and it’s designed to mimic the post scroller in Persona. With a little bit of modification, it would fit harmony very well in my view.

    Currently the code I wrote for this is lying dormant on a VM I created for v3 development but since abandoned when the announcement around the harmony theme was announced. Its going to be hard to walk away from what constitutes around 100 hours of new CSS and js but this was designed for Persona, and my thoughts are given all the fanfare with Harmony, the previous staple theme will fade into obscurity - perhaps no longer developed, meaning I’d have to both fork the last version and maintain it going forward.

    Honestly, I just don’t have time to commit to that, so the decision to turn my back on Persona development has been made, and it’s all harmony from now on. Sadly, this does mean the death knell for the conversation style CSS I developed. I won’t be including this in harmony as it’s difficult to support given it needs to add various new CSS classes into the DOM as posts are being loaded by Ajax. Clearly, this isn’t exactly optimum, and in fact, will slow down the rendering process if poorly coded.

    Not sure if it’s my imagination, but v3 seems “oh so slightly” slower than v2. Literally nothing in it, but noticeable to me so clearly, there’s some room for tuning here which I think will be at the nginx level.

    Stay tuned.


  • @phenomlab said in v3 / Harmony diary:

    perhaps no longer developed

    well, although I understand your choice with Harmony, I believe Persona has its charm and will continue to be developed. you do not think so?


  • @phenomlab it seems i gonne use v2 end of this year. because i see that v2 very fast than v3 i mean page speed, user test etc.


  • @crazycells said in v3 / Harmony diary:

    I believe Persona has its charm and will continue to be developed. you do not think so?

    It’s a feeling I have, and in my experience, something I’ve seen many times. Persona was the default theme shipped with v2, and has been the mainstay of that version for a long time. However, things change, and by NodeBB’s own admission, it needed an update to bring it in line with modern layouts and mobile design. As you know, Harmony is not a fork, but a completely new design that also requires v3 for it to work at all.

    To me, this is the final whistle for Persona. Harmony will supersede it in every way, and eventually, development will stall for the former product with all the focus being on Harmony itself. My view is that Persona will become a community fork to stop it from disappearing completely, but in the same vein, I think most (like me) will jump ship and move to Harmony.


  • @cagatay said in v3 / Harmony diary:

    it seems i gonne use v2 end of this year. because i see that v2 very fast than v3 i mean page speed, user test etc

    Yes, I’m glad I’m not the only one to witness this. I am going to raise this with the NodeBB team

    EDIT - raised in the v3 Bug thread


  • Ok, spun up the dev VM I started working on

    9097e069-c077-40e4-a949-0b4aeacecca2-image.png

    And here’s a video of the reworked pagination / scroller utility I wrote that fires on pages

    This was originally designed to work in tandem with the Persona scroller / navigation, but would be simple to convert to Harmony and BS5.


  • Today’s playground 🙂

    c67e0b6c-b534-4306-8e58-dbe77e30c6a0-image.png

    Here’s a video… still needs a bit more work, but… 🙂 Notice the newer scroll and progress bar I was talking about earlier…



Discover More

  • 19
  • 3
  • 6
  • 2
  • 38
  • 5
  • 29
  • 9