S(GH)PA: The Single-Page App Hack for GitHub Pages

SPA woes

For some time now I have wanted the ability to route paths for a gh-pages site to its index.html for handling as a single-page app. This ability is table stakes for single-page apps because you need all requests to be routed to one HTML file, unless you want to copy the same file across all your routes every time you make a change to your project. Currently GitHub Pages doesn’t offer a route handling solution; the Pages system is intended to be a flat, simple mechanism for serving basic project content.

If you weren’t aware, GitHub does provide one morsel of customization for your project site: the ability to add a 404.html file and have it served as your custom error page. I took a first stab at doing an SPA hack by simply copying my index.html file and renaming the copy to 404.html. Turns out many folks have experienced the same issue with GitHub Pages and liked the general idea: https://twitter.com/csuwildcat/status/730558238458937344. The issue that some folks on Twitter correctly raised was that the 404.html page is still served with a status code of 404, which is no bueno for crawlers. The gauntlet had been thrown down, but I decided to answer, and answer with vigor!

One more time, with feeling

After sleeping on it, I thought to myself: “Self, we’re deep in fuck-it territory, so why don’t I make this hack even dirtier?!” To that end, I developed an even better hack that provides the same functionality and simplicity, while also preserving your site’s crawler juice – and you don’t even need to waste time copying your index.html file to a 404.html file anymore! The following solution should work in all modern desktop and mobile browsers (Edge, Chrome, Firefox, Safari), and Internet Explorer 10+.

That’s so META

The first thing I did was investigate other options for getting the browser to redirect to the index.html page. That part was pretty straight forward, you basically have three options: server config, JavaScript location manipulation, or a meta refresh tag. The first one is obviously a no-go for GitHub pages, and JavaScript is basically the same as a refresh, but arguably worse for crawler indexing, so that leaves us with the meta tag. Setting a meta tag with a refresh of 0 appears to be treated as a 301 redirect by search engines, which works out well for this use-case.

You’ll need to start by adding a 404.html file to your gh-pages repo that contains an empty HTML document inside it – but your document must total more than 512 bytes (explained below). Next put the following markup in your 404.html page’s head element:

<script>
  sessionStorage.redirect = location.href;
</script>
<meta http-equiv="refresh" content="0;URL='http://MY_PROJECT_HERE.github.io'"></meta>

This code sets the attempted entrance URL to a variable on the standard sessionStorage object and immediately redirects to your project’s index.html page using a meta refresh tag.

Customizing your route handling

If you want more elaborate route handling, just include some additional JavaScript logic in the script tag shown above to tweak things like: the composition of the href you pass to the index.html page, which pages should remain on the 404 page (via dynamic removal of the meta tag), and any other logic you want to put in place to dictate what content is shown based on the inbound route.

512 magical bytes:

This is hands down one of the strangest quirks I have ever encountered in web development: You must ensure the total size of your 404.html page is greater than 512 bytes, because if it isn’t IE will disregard it and show a generic browser 404 page instead. When I finally figured this out, I had to crack a beer to help cope with the amount of time it took.

Let’s make history

In order to capture and restore the URL the user initially navigated to, you’ll need to add the following script tag to the head of your index.html page before any other JavaScript acts on the page’s current state:

<script>
  (function(){
    var redirect = sessionStorage.redirect;
    delete sessionStorage.redirect;
    if (redirect && redirect != location.href) {
      history.replaceState(null, null, redirect);
    }
  })();
</script>

This bit of JavaScript retrieves the URL we cached in sessionStorage over on the 404.html page and replaces the current history entry with it. However you choose to handle things from there is up to you, but I’d use popstate and hashchange if you can.


Well folks, that’s it – now go hug it out and celebrate by writing some single-page apps on GitHub Pages!

GIF

The Web Beyond: How blockchain identity will transform our world

Imagine

Jane wakes up to the sound of her alarm clock – it’s 6:13 AM. “Oh great, what am I in for today,” she thinks. Jane’s alarm clock is normally set for 6:30 AM, but her identity agent detected a traffic accident that is projected to add 17 minutes to her commute. Acting in agency on Jane’s behalf, her identity agent changed her alarm while she was sleeping. All three – Jane’s identity, the identity of her alarm clock, and the identity of her agent – are connected via a standard, worldwide, decentralized, blockchain-based identity system.

Jane gets ready and grabs a yogurt from the fridge as she heads out the door. The yogurt was delivered yesterday after her fridge detected she was out. Her fridge’s identity has been granted limited access to initiate purchases for her. In this case, Jane has opted to be notified for confirmation of any purchases her fridge initiates; yesterday Jane swiped “Confirm” when her identity agent asked her if the fridge could execute a purchase of some groceries. The fridge executed a payment over the blockchain between Jane’s identity-linked blockchain wallet and the wallet specified on the grocery store’s blockchain identity. That’s right, the grocery store has a blockchain identity as well. Starting to get the picture?

Jane needs to get to a downtown office building where she is scheduled to meet a contact on the 12th floor. Jane doesn’t have a car so she asks her identity agent to fetch her one by leveraging the many blockchain crawlers dedicated to indexing sharing economy identity data. These crawlers are always hard at work, real-time indexing the blockchain identity object changes of every person, place, device, and intangible entity on Earth. In this case, there are hundreds of drivers in Jane’s general vicinity who have granted popular ride sharing agents access to read and update their identity’s ride sharing fields. Jane uses her preferred crawler’s app to send signed requests directly to providers of sharing economy services. The crawler identifies a driver whose blockchain identity shows a ride sharing status of “Available,” with a geolocation value that indicates he is close to Jane. Jane taps “Request a Ride” on the app and it immediately sends a signed message to the communication endpoint listed on the driver’s blockchain identity. The driver’s blockchain sharing economy app alerts him that a new ride request was received and asks whether he wants to accept. The driver accepts and is sent Jane’s current geolocation.

Upon arriving at her destination, Jane authorizes a payment of her driver’s identity-linked blockchain wallet. She enters the office building and heads directly for the elevators, bypassing a lengthy check-in procedure in the ground floor lobby. Jane taps her phone against an NFC pad, which instantly identifies her via a challenge/response verification of her blockchain identity. The elevator system’s blockchain identity has been given access to the appointment schedules of the various software systems used by the companies that reside in the building. It uses Jane’s blockchain identity to locate the appointment entry, which was created by her contact. Within this entry is a signed directive to allow Jane’s identity to access the elevator and take it to the 12th floor. Jane enters the elevator and the button for the 12th floor is already lit up. Just for fun Jane tries hitting other buttons. But alas, she was not granted access to other floors, so the buttons don’t light up and she isn’t able to access those floors.

Jane walks up to the front desk and alerts the attendant that she has arrived for her meeting. The attendant directs her to verify her identity once more, via the guest terminal. She verifies her identity using her blockchain identity, of course. Jane is greeted by her contact and smiles at the thought of how efficient and interoperable the world has become, thanks to the blockchain.

Understand

A blockchain is a decentralized system of accounting for and storing cryptographically verifiable bits of data to a distributed ledger, synced to computers worldwide. Blockchains represent an unprecedented opportunity to create standard, decentralized systems that handle complex activities in a more efficient, automated, programmable way than ever before. One of the most interesting applications of blockchains is in the area of identity. Identity has never – never – had a good solution. Humanity has built countless centralized systems, federation schemes, and every hybrid of the two you can imagine. With a worldwide, decentralized blockchain of identity, that all ends.

Each transaction on the blockchain allows for a small amount of data to be stored with it. For the purpose of identity, this data can be encoded with two things:

  1. A registration for an ID (a friendly or unfriendly name), that is verifiable and indexable
  2. A pointer to off-blockchain data that describes the identity attached to the ID

Whoever possesses the private key for one of these blockchain ID transactions controls the identity data attached to it. This allows us to do interesting things, like:

  • Lookup IDs on a cacheable index of the global ledger
  • CRUD identity data connected to an ID at real-time speed
  • Prove ownership of an ID, or verify data has been signed/sent by an ID’s owner, using standard cryptographic methods

Build

With a global blockchain of identity, we can dramatically transform almost every product or service that relies on interactions between living, non-living, and intangible things. Here are a few examples of what it will do:

  • Allows users to directly expose products or services to real-time crawlers and indexes, which can disintermediate centralized products/services in every vertical.
  • Provides a means to lookup and contact anyone on the planet via the exposure of public or private (access limited) communication endpoints
  • Simplifies service access and accounting schemes, like registering for API keys, leaky URL params, etc.
  • Provides better mechanisms for verifying access/ownership of digital goods
  • Solves the fundamental issues with provisioning, security, and access control for the IoT ecosystem

Here are a few developer-enabling features, APIs, and tools we can build into existing platforms to more rapidly realize this blockchain-based future:

  • Create a new protocol (chain:, bid: ?) that allows for CRUD and search of blockchain transactions/identities
  • Build cloud services that make blockchain identity agents, and their bots, as easy to develop as all the social/messaging bot frameworks of today
  • Develop new Web standards and browser features that integrate a more secure, more powerful blockchain-based system of authentication and identity into common flows, like login and request signing
  • We may want to reuse/augment some existing mechanism, like the FIDO flow, etc.

^ This is the future we deserve – a standard, generative, user-sovereign world of identity that will fundamentally change the way we interface with every person and object around us.

A Renewed Call for App-to-App Interaction APIs

Cow, come in cow

The battle ground of app-to-app interaction history is littered with abandoned ideas, half-solutions, and unimplemented APIs. The current, consumer/provider interaction paradigm for apps and services is a mess of one-off, provider-defined systems that each use their own transaction mechanisms and custom data structures. This makes it hard to do simple things across N providers, like save something to a user’s preferred storage service without jumping through provider-specific code and UX hoops.

I’d like to restart the conversation about bringing legit, app-to-app interaction APIs to the Web. There have been past spec attempts, namely Web Activities and Web Intents, but I’ll argue that while they get a lot right, they all fail to deliver an A+ solution.

Continue reading

Element Queries, From the Feet Up

Everybody’s looking for Element Queries

What are Element Queries? At a high level, I’d describe them as pure, unfiltered, rocket fuel for hyper-responsive layouts and components. More technically, they are Media Queries scoped to individual elements. Element Queries would allow you to attach Media Query break-points based on the dimensions and characteristics of an element itself, instead of the page’s viewport.

Developers have wanted Element Queries for a long time. Here’s a list of articles that convey the need, along with a few attempts to make them real:

Continue reading

Cross-Browser, Event-based, Element Resize Detection

UPDATE: This post has seen a significant change from the first version of the code. It now relies on a much simpler method: a hidden object element that relays its resize event to your listeners.

DOM Elements! Y U No Resize Event?

During your coding adventures, you may have run into occasions where you wanted to know when an element in your document changed dimensions – basically the window resize event, but on regular elements. Element size changes can occur for many reasons: modifications to CSS width, height, padding, as a response to changes to a parent element’s size, and many more. Before today, you probably thought this was mere unicorn lore, an impossible feat – well buckle up folks, we’re about to throw down the gauntlet.

Continue reading

The Oft-Overlooked Overflow and Underflow Events

A Primer on Overflow and Underflow

To level-set, I’ll define and describe what overflow and underflow are in the context of the web. Overflow is a rather simple concept you’re probably familiar with: when an element’s content takes up more space than it allows, given style or box model constraints, it causes a scrollbar to appear or the content to be cut off from view (if you set overflow: hidden;). Underflow is the less common case you probably don’t think about: an element currently in an overflown state leaves that state as a result of the element growing in size or a reduction of the amount of content within it – visually, the scrollbars disappear and all content is visible within the element. As it turns out, Firefox and WebKit browsers offer events that alert you of changes between these two flow states.

What if I told you

Continue reading

FlightDeck and MetaLab: Bad Messaging Leads to Bad Times

NOTE: I spoke with Andrew Wilkinson (CEO of MetaLab) prior to releasing this post.

The Back Story

I arrived at Mozilla 4 years ago at age of 26 with a passion for the web. Like many Mozillians, my previous job was with a private company. Mozilla was radically different than any work environment I had ever been in. Not only is Mozilla open source, it’s also open meeting, open planning, open specs, open mockups, open bug lists – yeah, lots of open. I wasn’t used to this, not that I shied away from openness or wanted to be secretive, it simply took a while to acclimate myself.

One of the first projects I was tasked with when I arrived was Add-on Builder. It was to be a lightweight code environment for Firefox add-ons – mostly for beginners and people who wanted to test their add-ons in a collaborative way (think jsFiddle for Firefox Add-ons). Unfortunately, it was also the source of the most frustrating, painful event of my professional career. Given Add-on Builder was end-of-life’d a few months ago to free up resources for other developer-facing products, I thought I’d finally write about the event and what actually happened. As it turns out, it was far less interesting than the woefully inaccurate fable it mutated into. Here goes:

Continue reading

MVCR – Minimum Viable CSS Ribbon

I thought I’d jot down a snippet of CSS I came up with recently that I use to generate UI ribbons. The code uses :before/:after pseudo elements, which means it works in IE8+ and all the other not-shitty browsers. To ensure the content of the ribbon can be modified dynamically using JavaScript, I’ve set the pseudo element’s content property with the value attr(ribbon). The attr() content function grabs the parent element’s ribbon HTML attribute string (example: ribbon="SomeText") and uses it for the ribbon’s content. I’m pretty sure this is one of, if not the, shortest bit of code required to create CSS ribbons, but perhaps you all can improve upon it:

.ribbon:before {
	display: block;
	content: attr(ribbon);
	background: #eee;
}
	
.ribbon:after {
	display: block;
	content: " ";
	border-left: 5px dotted transparent;
	border-top: 5px solid #ccc;
	height: 0;
	width: 0;
}

CSS Selector Listeners

The following article explores a new event mechanism that I like to call CSS Selector Listeners. Selector listeners are completely bad ass, and sufficiently indistinguishable from magic, as you will soon find out.

Rise of the Mutants

Developers recently were empowered with a new way to listen for changes in the DOM: DOM Level 4 Mutation Observers. Mutation Observers emit fairly general events that tell you basic things, like when a new element has entered the DOM, or an attribute has changed on an element or subtree you are observing.

As a result of their relatively low detail resolution, developers have created libraries to help make Mutation Observers more useful for developers. Rafael Weinstein of Google recently introduced a library called Mutation Summary, which lets you watch the DOM through the lens of a limited subset of CSS selectors using a combination of Mutation Observers and its own custom logic layer to filter and interpret mutation events. The concept of listening to selector matches is intriguing because selectors inherently require the parser to quickly assess complex DOM conditions and perform selector-related logic to check for matches. Having the ability to listen for when selector states become active would be a powerful tool indeed!

A Hidden World of Events

Mutation Summary is pretty cool, but what if I told you there was a hidden world of arcane CSS selector event magic living in your browser that offered unlimited, detailed insight into the state of the DOM? Well there is, and technically there always was, in every style sheet you’ve ever written. All we needed was the right key to access this world…well, a Keyframe, to be exact.

Piggybacking the Parser, with Keyframes

I recently posted a method for using CSS Animation Keyframes to detect node insertions via the animationstart event of a dummy keyframe animation. Now let’s take a second to realize what this hack really is at its core: the ability to listen for any selector-based matches the CSS parser makes anywhere in the DOM. The Selector Listener code I’ve developed provides two methods, addSelectorListener and removeSelectorListener. These methods are available at both document and element level, and allow you to listen for any selector match, regardless of complexity. Once the parser detects a matched selector, the event bubbles up the DOM from the matched target element to the element or document the selector listener is attached to. Here’s what it looks like in action:

// Some action to perform when a match occurs
var sequenceMatch = function(){
  alert("Selector listeners, they're easy as A, B, C!");
};

// Attaching your selector listener to the document or an element
document.addSelectorListener('.one + .two + .three', sequenceMatch);

// Remove the selector listener when it is no longer needed
document.removeSelectorListener('.one + .two + .three', sequenceMatch);

The Goods: Code & Demo

The code that provides all this new hotness, as well as more examples of what’s possible, is available on Github here: SelectorListener Code Repo

You can also play around a bit with this demo page: SelectorListener Demo

A Modal Cure in Pure CSS – No Wrappers, no JavaScript, no BS

Modals. They’ve been the subject of countless hacks over the years (I did a cross-browser one a while ago here), but due to cross-browser considerations, they usually are less than elegant in their implementation.

Well, if you don’t care about IE < 9, things are about to get much, much easier. This little trick is so simple it’s going to make you cry, in fact it’s so easy, it may cause you to audibly curse IE louder and more passionately than you ever have:

<div id="modal">
    <!-- YOUR CONTENT HERE (seriously, it's just one div) -->
</div>
#modal {
    display: block;
    position: fixed;
    top: 50%;
    left: 50%;
    box-sizing: border-box;
    transform: translate(-50%, -50%);
}

Here’s a fiddle so you can see it in action:

Yeah I know, all those hours wasted on wrapper divs, tables, crazy CSS, and performance-robbing JS, all to get hoodwinked by some top/left positioning and a transform property, go figure.

© 2016 Back Alley Coder

Theme by Anders NorenUp ↑