Creative & Technical Web Content: How We Redesigned Neo4j’s Developer Guides

As with all web pages, style and content need refreshed with new features and user-friendly functionality. Our team decided to tackle this and present users with an all-new design of Neo4j’s developer guide pages. The team has worked hard to bring a polished look to content on concepts, explanations, and walkthroughs for topics surrounding Neo4j products, tools and libraries, and development with Neo4j.

Web development is never as simple as hoped. We learned a lot in the process and were able to tackle some very specific goals to minimize support, increase maintainability, and (hopefully!) improve user experience. Our 2 main goals for the project were the following:

  1. Provide a functional, useful interface for learning Neo4j. We wanted the changes to be valuable for users interacting with the webpages to learn about our products and tools. We needed to ensure navigation was simple and easy to use and users could leave and come back to content easily. Also, users should be able to see the full list of all the content to find information on specific topics.
  2. Increase attractiveness and positive user interaction through styles and coloring. Content should be helpful for learning objectives, but it should also appeal to the user. Menus and other supporting components needed to be unobtrusive and colors should highlight important content and reduce distractions. We wanted a clean, simple design that didn’t obstruct the user from learning Neo4j.

To accomplish these goals, there were a few components where we made changes. In the end, we were able to go live with an improved version that serves as the foundation for additional changes in the near future. Let’s take a look at what we did and how we got there!

Structure and layout of page

As a tech company, we needed to structure components for a variety of learning approaches. Some users walk through page-by-page and studiously follow an outline, while others search for adhoc topics or answers. We needed to provide for these two extremes, as well as for a mix of scenarios and users.

This is where our multi-section layout is based. Along the top of the page, there is a header with our company/product logo, along with key resources for developers to learn about our technology. Beneath the header is the core of our material in 3 columns — left navigation menu for topic sections, middle column for core content, and right menu for outline of core contents.

To build this frame, we used the CSS flexbox, which helps design a flexible and responsive structure (W3Schools CSS Flexbox). Our HTML outline looks something like below.

<header>....</header>
<article id="content" role="main">
<div class="content-wrapper">
    <div class="dev-menu-nav large-2 hide-for-medium-only hide-for-small-only columns">....</div>
    <div class="guide-section-content large-8 columns">....</div
    <div class="guide-toc large-2 hide-for-medium-only hide-for-small-only columns">....</div>
  </div>
</article>
<footer>....</footer>

And the CSS properties, then, use the display: flex; property on the parent <div> with a few other properties set on the child elements.

.content-wrapper {
display: flex;
align-items: flex-start;
}
.dev-menu-nav {
flex: 1.5;
align-self: stretch;
}
.guide-section-content {
float: left;
}
.guide-toc {
position: sticky;
flex: 1;
}

As you can see, the content-wrapper div sets the child components as flexible and also aligns the items at the start of the container. Each of the child items can then be customized and adjusted to the desired spot on the page.

The dev-menu-nav element is the left navigation menu. This should take up the entire left side of the page until it reaches the footer, which extends across the bottom. We defined the flex property with a value of 1.5, which means the column can grow up to 1.5x times its size before it stops expanding. We also set the align-self property to stretch, so that the menu will extend to the bottom of the parent container and not stop at content height.

The middle column is for our main content. It kept a pretty simple layout without much configuration. We only defined it to float to the left in our CSS, and this section extends the length of the contents in the guide.

The final column is for the hovering right menu. This was a bit tricky to figure out, especially in tandem with the left menu. While the left menu extended the full parent height and menu contents remain static, the right menu should only be the size of its contents and scroll with the page. To accomplish this, we set the position to sticky, allowing the container to stick to the viewport and move separately from the page contents (letting it hover when scrolling) and set the flex value at 1, so that the menu size didn’t fluctuate with window size.

While it seems rather simple, it took us some time to do the research and understand how to make the components somewhat independent, as well as somewhat aligned with each other.

Page and content width with adaptable sizing

Users read and learn in all kinds of formats, including from phones, tablets, and desktop monitors. To support and improve experience from different devices, we hid non-essential content from the page in a mobile view and maximized content on the page for wider or larger screens. The design needed to be flexible to adjust to screen size and content.

Since our layout used the flex display property, the columns scale according to the content width and the flex values we set. We set the left menu to scale up to 1.5 (flex: 1.5) and the right menu to grow up to 1 (no scaling). Our middle section, however, scaled too much on a large screen. This made the text sparse and difficult to read, so we needed to tell this section when to stop extending. It should also adjust menu width to the content for menu items with short or long strings of text.

Our team’s general rule is about 120 characters per line. We can use the max-width property to set a maximum size for certain components to grow, but there wasn’t a way to use characters for the value of that property. Using a converter, 120 characters in our line calculated to 960px, which we adjusted slightly for font and size.

.dev-menu-nav {
max-width: 290px;
}
.guide-section-content {
max-width: 920px;
}
.guide-toc {
max-width: 300px;
}

These values ensured that all three sections scaled, but only to a certain point. This allowed text, videos, diagrams, and images in the middle section to efficiently use screen real estate without stretching and making it unreadable.

To handle the screen sizing for varying devices, we used standard column classes to show and hide certain elements.

<div class="dev-menu-nav large-2 hide-for-medium-only hide-for-small-only columns">....</div>
<div class="guide-section-content large-8 columns">....</div>
<div class="guide-toc large-2 hide-for-medium-only hide-for-small-only columns">....</div>

The small/medium/large-# tells the browser how many columns at each size scale that the div should take up out of the 12 in a web page. Our left menu div (dev-menu-nav) has large-2 to take 2 columns in a large size, then hide-for-medium-only and hide-for-small-only, so it will be hidden on medium and small screen formats. Note: medium screen size is smaller than anticipated. ;)

The middle column (guide-section-content) has large-8 to use 8 columns at large scale. Since we don’t specify medium or small size classes, it will maintain that scale in all formats. The last div for our right menu (guide-toc) has the same classes that our left menu does — 2 columns at large scale, then hidden at both smaller sizes.

The classes and max-width CSS that we added here ensured both menus are hidden on a mobile phone or narrow window, but that all elements scaled well on a large screen, with most screen real estate and scaling flexibility given to the main content in the middle.

Fully-expandable left navigation menu

We needed to enable the left menu to expand and collapse all sections at once to allow users to view all of the guides and topics. A simple Javascript function was able to handle this for us, with a function call added to the html element.

HTML:

<dl class="menu-list">
<dd onclick="expandMenu(this)" class="accordion-dev-nav">Getting Started</dd>
  <div class="panel-dev-nav">
<ul>
<li><a href="/developer/get-started/">Getting Started</a></li>
...
</ul>
</div>
</dl>

Javascript:

function expandMenu(domElement) {
$( domElement ).toggleClass("active");
var elem = $( domElement ).next('.panel-dev-nav');
elem.toggleClass("active");
  if (elem.css('display') == 'block') {
elem.css('display', 'none');
} else {
elem.css('display', 'block');
}
  if (elem.css('max-height') != '0px') {
elem.css('max-height', '0px');
} else {
elem.css('max-height', elem.prop('scrollHeight')+'px');
}
}

The expandMenu function is called in the <dd> tag of each menu list section and passes the current element (this) to the function. The function defined in our Javascript file toggles an active class on both the current element (<dd>), as well as the next element (panel-dev-nav). The function also shows and hides the inner panel with all the links in the section (panel-dev-nav) and lets the section expand to the scroll height for varying numbers of links in each section. CSS handles the active class styling for change in color, altering the + icon to a - when it’s open, and dropdown speed.

Page content outline in right menu

Table of contents sections are helpful to see what a page covers or to find a specific topic. We created a hovering menu on the right side of the page with an unobtrusive, bookmark-like style. As a plus, it follows the window scrolling and highlights the current section in the view.

Coding this involved setting up section headers with anchor tags. Most of our pages are written in Asciidoc with some Ruby templates and converters to render the Asciidoc to HTML. It’s simple enough to use an anchor in Asciidoc. You just need two items. 1. Some configurations at the top of the file for the renderer to include section link anchors, a table of contents (toc), the header of the toc, and the number nested levels anchored.

:sectanchors:
:toc:
:toc-title: Contents
:toclevels: 1

2. The [#tag] syntax above each header at the specified level. Note: what you put in the brackets will be the id value of that element in the HTML.

[#starting-neo4j]
Taking the first steps with Neo4j?

In our Ruby template, we pull the tag from current page and set the id of the HTML element to that retrieved tag value, like below.

toc_id = @id
<div<%= toc_id && %(id="#{toc_id}") %>>

Finally, all that is left to do is track the scroll in the webpage and highlight each section as it hits the viewport. This seems fancy, but it was easily handled with some Javascript and CSS. There are a few libraries that implement this functionality, as well, but many seemed to bundle unnecessary dependencies that bloated size and complexity. First, let’s look at the Javascript.

var visDict = {};
var handleIntersect = function(entries, observer) {
  entries.forEach(function(entry) {
visDict[ entry.target.id ] = entry.intersectionRatio;
var sortable = [];

for (var vis in visDict) {
sortable.push([vis, visDict[vis]]);
}
  sortable.sort(function(a, b) {
return b[1] - a[1];
});
  var mostVisibleDivId = sortable[0][0];
$('.sectlevel1').find('li').removeClass("active");

$('.sectlevel1').find('[href^="#' +
mostVisibleDivId + '"]').parent().addClass("active");
});
}
var setObserver = function () {
var observer;
var sections = document.querySelectorAll(".sect1");
  var options = {
root: null,
rootMargin: "0px",
threshold: [0.25, 0.5, 0.6, 0.8, 0.9, 1.0]
};
  observer = new IntersectionObserver(handleIntersect, options);
sections.forEach(section => {
observer.observe(section);
});
}
jQuery(document).ready(function() {
setObserver();
});

The first section of the code finds which div is most visible in the viewport and adds an active class to that element. The next code paragraph sets up the observer and its options on sensitivity for how much each section needs to be in the viewport before changing whether it’s active or not. Then, the next block loops through sections in the page, and the final block is the jQuery(document).ready() that calls the observer function when the document changes (including on scroll).

For the highlighting, we simply add a block to our CSS file that changes the text color and font weight of the elements when the active class is present. This is what the CSS looks like.

.toc li.active a {
color: #428bca;
font-weight: 600;
}

Focusing the header menu

When looking at technical content, we wanted to keep options succinct to reduce distractions and decisions on where to go or what to look at. To meet this, we created a developer header with only the key developer-related resources. We felt that these links represented resources that users would frequent, as well as places we wanted users to go for learning or knowledge-sharing.

<header role="banner" class="global-header control-menu developer-header">
  <div class="top-bar-container row column">
<nav class="top-bar primary-nav" role="navigation" id="responsive-menu">
<div class="top-bar-left">...</div>
<div class="top-bar-right">...</div>
</nav>
</div>
</header>

On this container, we borrowed most of the styles from the rest of our neo4j.com header, but we altered a couple of things. We wanted a mental and visual separation/shift between main site content and technical guide content. Using a change in color helped us cleanly differentiate between the two, as well as create stark contrast between menus and guide text (dark menu background vs light guide background).

Just as with our page content, this menu needed to be adaptable for various sizes, as well. Though a lot of the styling was defined from the main site, we altered one small CSS property on the <ul> elements within the top-bar-right div. This ensured that the page would not wrap the menu on a narrow window. On small screens, the wrapping left an ugly white margin on the right and cut off the bottom of wrapped content.

ul.menu.large-horizontal {
flex-wrap: unset;
}

Color and style contrast

In the paragraphs above, we touched on the fact that we wanted separation between technical and other sections of the Neo4j website, with color shifts as a way to accomplish this. There were a few other factors at work in this decision, as well as some reasoning about color choices.

One of the general things we noted is that dark mode themes for screens, editors, and other developer interfaces are very popular, so this styling mimics what many developers are already familiar with and may prefer. However, dark text on light backgrounds is still the standard way to read material (books, webpages, etc), so we blended the two. Since darker elements tend to draw less attention, this seemed like a good fit for the menus and side elements. This also allowed us to contrast the darker trim with a lighter guide in the middle and draw focus to the learning material.

We were very specific in the colors that we chose. The grey colors are the same ones that appear in our products like Neo4j Desktop and Browser. This created a consistent visual sense, whether interacting with our products or learning how to use them. These colors have also already been tested for user experience and compatibility.

The dark styling extended to our code blocks, as well. We used a dark background and text highlighting similar to IntelliJ IDEA’s darcula theme. This has the additional benefit of familiar code styling that pops against the main content text throughout many pages. We used CodeMirror library’s out-of-the-box darcula mode to apply this style to the code blocks. To implement this, we included the darcula.css file in our project, then used uglify to bundle the styles and programming language modes into a Javascript file.

uglify -s ./lib/codemirror.js,./mode/clike/clike.js, ./mode/cypher/cypher.js, ./mode/javascript/javascript.js, ./mode/shell/shell.js, ./mode/xml/xml.js, addon/runmode/runmode.js, ./mode/sql/sql.js, ./mode/go/go.js, ./mode/python/python.js, ./mode/php/php.js, ./mode/ruby/ruby.js -o ./neo4j-cm.js

With these dependencies and the CSS classes on the appropriate HTML elements, we have code blocks that now look like this!

Section anchor links

In a previous section, we used anchor links for the right menu to track and highlight where the viewport was in the content. It’s also helpful for another thing, though — leaving and returning to a specific section within a page. The anchor link marks the location of tagged sections and adds the tag to the URL so the browser window displays that particular section.

This avoids having to send a page link to a colleague or friend and telling them which paragraph or section to look at for the desired information. Just click the anchor link icon next to the related headline, copy the browser url (which has the anchor tag at the end), and send it along. The receiving person can open the page in the subsection that the sharer wanted.

Our quick (not necessarily permanent) solution was to do this in Javascript. We definitely plan to explore other ways for a simpler and more elegant solution. Let’s take a look at what we have.

jQuery(document).ready(function() {
  $('h2,h3').css('display', 'inline')
.after($('<i class="fa fa-link" aria-hidden="true" style="margin-left: 20px; color: lightgrey">')
.wrap( $('<a>') ).parent() ).next()
.attr('href', function () {
return $(this).prev().children().first().attr('href')
});
});

What’s coming next?

Over time, our team will adjust a few styles and make updates. We will work (as always) on better and cleaner solutions and hope to continue improving the user experience Neo4j’s developer content.

If you have any comments, feedback, or content you’d like to see added on the developer guides, feel free to post on the Community Site, and we will work on incorporating and personalizing it. Best wishes in your learning journey!


Creative & Technical Web Content: How We Redesigned Neo4j’s Developer Guides was originally published in Neo4j Developer Blog on Medium, where people are continuing the conversation by highlighting and responding to this story.

1 Like