aeroheim-logoblog
projects
about

back to blog

Behind Aeroheim.moe: Intro & Frontend

AUG 7, 2017first part of a technical overview
    metadev

Behind Aeroheim.moe: Intro & Frontend


The idea of creating this aeroheim.moe first came to mind back in late 2016, roughly a year after I graduated college and started working.

While I had always been interested in web development, my job unfortunately didn’t provide much exposure to it. Seeing the rise and growth of fluid and responsive single page apps (SPAs) and web-based desktop apps (e.g Slack, Discord) however, I decided it was finally the right time to pick up and learn web development.

After much research and studying with about a months worth of development time, I launched aeroheim.moe on June 2017. By the time of release, I had gained much familiarity with the following languages/concepts:

  • HTML5
  • CSS
  • Javascript (ES6)
  • Node.js
  • NoSQL DBs
  • React
  • Webpack

Below is an intro and overview of the architecture and design considerations behind the frontend of aeroheim.moe. A technical overview on the backend of aeroheim.moe will follow at a later date.


Specification

The primary requirements of this site:

  • backend server with RESTful API
  • CMS with version-controlled content
  • fluid and responsive SPA

The purpose of this site is to serve as platform for personal blog posts as well arbitrary content that I plan to add in the future. Content is intended to be version-controlled to mirror a standard developer workflow. For larger binary content such as media, Git Large File Storage (LFS) will be utilized to avoid bloating the content git repository.


Frontend

Most of the user interfaces I’ve written in the past were for desktop apps in C# (WPF/Xaml), so I was most familiar with declarative and object-oriented styles for writing UIs. Coincidentally at the time, using direct DOM manipulation for UIs (e.g JQuery) was also falling out of favor against component-based frameworks (Angular, React, Vue) that were very similar to the traditional object-oriented UI programming mentioned above.

Single Page App Using React

Of the various component-based Javascript UI frameworks available at the time, I ended up choosing React due to its powerful ecosystem (JSX, webpack, etc) and how natural and intuitive writing HTML using Javascript (JSX) felt. While the initial learning curve involved in learning React was extremely steep for a newbie, the ease of use afterwards once I was familiar with it and the powerful tools available (e.g Webpack’s Hot Module Replacement) allowed for extremely rapid development.

Using React, views like the blog page can be composed intuitively using JSX:

// blog.js
class Blog extends React.Component
{
    // ...
    render()
    {
        return (
            <div className={styles.page}>
                <div className={`${styles.content} ${transitionStyles['content']}`} onTransitionEnd={onTransitionEnd}>
                    <PageHeader className={styles.headerStyle} color={styles.headerColor} show={match}>BLOG</PageHeader>
                    // a list of BlogListItem components is generated dynamically
                    <div className={styles.postsContent}>
                        <ul className={`${styles.posts} ${transitionStyles['posts']}`} onTransitionEnd={onTransitionEnd}>
                            {this.props.posts.map((post) => <BlogListItem key={post._id} post={post} show={match}/>)}
                        </ul>
                    </div>
                </div>
            </div>
        );
    }
}

// blog-list-item.js
class BlogListItem extends React.Component
{
    // ...
    render()
    {
        return (
            <li className={`${styles.post} ${transitionStyles['post']}`} onTransitionEnd={onTransitionEnd}>
                <LinkButton link={`/blog/${post._id}`} className={styles.linkButton}>
                    <div className={styles.postColorBar}/>
                    <div className={styles.postText}>
                        <h2 className={styles.postTitle}>{post.title}</h2>
                        <span className={styles.postDate}>{monthFormatter.format(post.date).toUpperCase()} {post.date.getDate()}<br/>{post.date.getFullYear()}</span>
                    </div>
                    <p className={styles.postDescription}>{post.description}</p>
                </LinkButton>
            </li>
        );
    }
}

 

Application State Management Using Redux

React manages data using a unidirectional dataflow, where data flows strictly from the root component down the component tree towards children. Each component has a props object that receives data from its parent as well as a state object used exclusively by the component itself to manage its data. Any updates to these objects causes React to re-render respective components.

One issue that I encountered while architecting the frontend was how often deeply nested components required data to be transferred across multiple levels in the component tree, therefore bloating many components with unnecessary data. This was a sign to me that my frontend now required an application state independent of component states.

To solve this issue, I ended up using Redux, which introduces the concept of data stores called reducers. Reducers store data and define how data is written using pure functions called actions. Components can connect to reducers to add additional data to their props as well as invoke actions to change data stored inside reducers. Keep in mind that Redux exists as an additional layer of data on top of React’s already existing state and props.

Without going into too many details, here’s a very simple use case of using Redux to control the main scrollbar owned by the root component.

// aeroheim.js
class Root extends React.Component
{
    // ...
    render()
    {
        return (
            // this.props.scrollbarVisible is read and updated from a redux reducer.
            <div className={styles.background} style={!this.props.scrollbarVisible ? { overflowY: 'hidden' } : null}>
                // rest of the app...
            </div>
        );
    }
}

// Some child component that needs to hide the scrollbar
import { setScrollbarVisibility } from '../actions/app-actions';
class Component extends React.Component
{
    hideScrollbar()
    {
        // invoke the action which triggers a change in the redux reducer.
        this.props.setScrollbarVisibility(false);
    }
}

 

Page Transitions With React Router

As I mentioned before, React has a powerful ecosystem behind it. One of the neat libraries inside of that ecosystem is React Router, which provides a declarative component-based approach towards routing.

With React Router, I was able to write all of my routes declaratively and scoped to the components that they belonged to. This fits in nicely with React’s component-based paradigm, as routing is essential in determining when a component needs to be rendered.

Taking a look at the Root component example shown earlier, here’s how React Router fits into the picture:

// aeroheim.js
import Route from 'react-router-dom';

class Root extends React.Component
{
    // ...
    render()
    {
        return (
            <div className={styles.background} style={!this.props.scrollbarVisible ? { overflowY: 'hidden' } : null}>
                // Define routes and the components that should be rendered at those routes.
                <Route exact path='/' children={(props) => <Home {...props} path='/'/>}/>
                <Route exact path='/blog' children={(props) => <Blog {...props} path='/blog'/>}/>
                <Route exact path='/about' children={(props) => <About {...props} path='/about'/>}/>
                // rest of app...
            </div>
        );
    }
}

An example of a nested route that is scoped to the component it belongs to:

// blog.js
class Blog extends React.Component
{
    // ...
    render()
    {
        return (
            // The Blog component defines the nested route '/blog/:id, where 'id' is the name of the blog post.
            // A BlogPost component is rendered when a valid '/blog/:id/' route is entered.
            <Route exact path={`${props.path}/:id`} children={(props) => <BlogPost { ...props } path={`${props.path}/:id`}/>}/>
            // rest of blog...
        );
    }
}

 

Component-based Styling With CSS Modules

React provides support for inlining CSS styles using Javascript. This has led to some approaches that push the component-based paradigm to the extreme by merging both the structure and presentation of a component together using JSX and inline styles. I’m personally not a fan of these approaches as it drastically increases the complexity of the code without any real benefits that better alternatives can’t provide. Separation of concerns is important in keeping code clean and maintanable.

To adopt a more maintanable component-based approach towards writing my stylesheets, I decided to use CSS Modules in conjunction with a preprocessor called PostCSS. CSS Modules allows you to import stylesheets into components and reference them programmatically using Javascript, making style dependencies explicit and removing human error when it comes to naming and referencing styles. It also uniquefies all selector names to prevent global conflicts, therefore removing global scope from CSS and in effect scoping styles to just the components that import them. PostCSS provides support for plugins such as sharing values between CSS files, auto-prefixing vendor prefixes for CSS rules, etc.

Stylesheets for aeroheim.moe are written as regular CSS files that leverage the additional features that PostCSS provides:

// about.css

// PostCSS values plugin to import values from other stylesheets
@value aboutColor from "../global/colors.css";
@value easeOutCubic from "../global/animations.css";
@value small, large from '../global/breakpoints.css';
@value headerMargin from "./header.css";

@value sideMargin: 15px;

.page
{
    display: flex;

    position: absolute;
    top: 0;
    left: 0;
    right: 0;

    min-height: calc(100% - 2 * headerMargin);
    padding: headerMargin sideMargin headerMargin sideMargin;
}

// rest of about.css...

Components for aeroheim.moe can then pull these stylesheets in as dependencies just like regular ES6 modules:

// about.js
import styles from '../static/styles/components/about.css';

class About extends React.Component
{
    // ...
    render()
    {
        // styles can be referred to programmatically as properties of the imported stylesheet.
        return (
            <div className={styles.page}>
                <div className={styles.content}>
                    <PageHeader className={styles.headerStyle} color={styles.headerColor} show={this.props.match !== null}>ABOUT</PageHeader>
                    <article className={`${styles.bio} ${transitionStyles['bio']}`} onTransitionEnd={onTransitionEnd}>
                        // rest of about...
                    </article>
                </div>
            </div>
        );
    }
}

 

Bundling It All Together

To create a frontend SPA, my backend needed to serve a static page that includes a Javascript file with all of my modules and dependencies. In addition, I also needed a way to compile my source code with the various transpilers/preprocessors that I was using (Babel for JSX & ES6, PostCSS, etc).

Fortunately, investing in the React ecosystem paid off handsomely here as Webpack, an excellent module bundler, is used heavily by it. Webpack generates a dependency tree of all of my modules and assets from my root source file, runs the appropriate transpilers/preprocessors on the nodes, and then outputs the transformed tree as a minified and gzipped Javascript file.

This single bundled Javascript file is included by my static index.html:

// index.html

<html lang="en">
// rest of index.html...
<script src="/bundle.js"></script>
</html>

React then generates the rest of the HTML using my components when this Javascript file is run by the browser.


Conclusion

While there’s still a lot more deeper stuff involved in the frontend that I haven’t mentioned yet, I hope this post was able to at least provide a good general technical overview on the design and architecture behind the frontend of aeroheim.moe.

As is common in the world of web development, the technologies used here may very well be outdated in just a few years, but this post should still serve as a usable reference for this point in time.

Stay tuned for the next post on the technical overview of the backend behind aeroheim.moe!