Book of dark arts: selective disclosure with CSS

Pentagram, source Wikipedia.

The previous chapter of the book of dark showed a quick & dirty way to store data in a web pages’s HTML. In this chapter I’ll write about showing and hiding pages and page elements based on state changes in the single page application (SPA) by using CSS rules.


The gist:

  • The page HTML describes in the DOM all forms and fields (elements, for brevity) that would ever be necessary
  • The elements that are visible depending on application state (SDE for selective disclosure elements, in short) are hidden with CSS by default
  • Application state is reflected by one or more top-level CSS classs (e.g. on the <form> element or even the <body> element) which unveil their respective SDEs.

Let’s see how this works in detail. Let’s try to model an address book SPA with two screens: an overview of the stored address entries and a detail view.


Address book SPA with two screens

The (single) HTML page contains markup for both screens and some CSS that hides them on condition:

<div id=container>      
<form class="hidden show-on-overview">
<h1>addressbook overview</h1>
<div class=list>
<a href="#details_1">Alice Crypter</a>
<a href="#details_2">Bob Crypter</a>
<a href="#details_3">John Doe</a>
<a href="#details_4">Dr. Oz</a>
</div>
</form>

<form class="hidden show-on-details">
<a href="#overview">back</a>
<h1>entry details</h1>
<label>Name <input id=name type=text/></label>
<label>Street <input id=street type=text/></label>
<label>Postal <input id=postal type=text/></label>
</form>
</div>

The interesting CSS for this page is:

.hidden{
display:none;
}

.state-overview .show-on-overview{
display:block;
}

.state-details .show-on-details{
display:block;
}

Per default, both the overview and details screens are hidden and the page looks blank. The overview screen becomes visible by adding the “state-overview” class to container <div>. Likewise a transition to the details screen involves:

  • remove the state-overview class from the container, which hides the overview screen
  • add the state-details class to the container, which shows the details screen

More advanced functionality, like showing an edit button depending on roles, would work similarly:

.role-editor .needs-role-editor{
display:inline;
}

...
<body class="role-editor ...">
....
<button onclick="editField('name')" class="hidden needs-role-editor">Edit</button>
...

</body>

The best way to keep track of state is by using business logic events. Whenever a relevant action happens, an event is fired which can be consumed by code that modifies the UI, cleanly decoupling logic from view.

The address book app has two states which are modelled by URL hashes, #overview and #details_ID (where ID depends on the entry inspected). The events are implicitly fired by clicking on <a href=”#hash”> links and are processed by listening to them. A jquery implementation would look like this:

$(window).on("hashchange",function(){
let token = window.location.hash.substring(1, window.location.hash);
if (token == "overview") $(window).trigger("overview");
if (token.indexOf("details_")!=-1)
$(window).trigger("details", {id:token.substring("details"_.length)};
});

$(window).on("overview",function(){
$(".container").attr("class","state-overview");
});
$(window).on("detailsfunction(){
$(".container").attr("class","state-details");
$.ajax(....) //load data into the details page
});

A component view of the same constellation: a controller consumes events (browser and domain) and assigns CSS classes to a container document which reflect the application state:


The SPA discussed can be played with over here: https://ggeorgovassilis.github.io/public/blog/dark-arts-selective-view.html

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.