Engineering

Building a SpectaQL theme for your GraphQL documentation

Headshot, Ben Ogle
By Ben Ogle

SpectaQL is an auto-generator for GraphQL documentation. We'll cover the ins and outs of building out a theme to make your API docs shine.

Back to all articles
Everything you want to know about building a SpectaQL theme.
Engineering

Building a SpectaQL theme for your GraphQL documentation

Headshot, Ben Ogle
By Ben Ogle

SpectaQL is an auto-generator for GraphQL documentation. We'll cover the ins and outs of building out a theme to make your API docs shine.

We recently released SpectaQL 1.0 to the world. One of the major improvements in 1.0 was the addition of a brand new, more customizable theme system. This post is here to show you how to get the most of that theme system, and to generate docs that are exactly as you envisioned.

Not sure what SpectaQL is? It's a documentation generator for your GraphQL APIs. Give it a GraphQL endpoint or SDL file, and it will output a single-page static HTML site with your GraphQL documentation. The default theme looks like this:

spectaql default theme SpectaQL generates GraphQL documentation

The new theme system is quite powerful. You can override a single color, or go whole-hog and completely replace all styles and templates. In this post, we'll start simple, then work our way up to more complex customizations. Let's get to it!

Setup

First thing we'll need to do is set up our project to use SpectaQL. Assuming you're using JavaScript / Node, you just need to install it into your dev dependencies:

$ yarn add -D spectaql
# OR
$ npm install --dev spectaql

Next, set up a config. The key config property you need to be aware of for theming is themeDir. Point themeDir to a directory in your project. Your custom theme directory can be anywhere, and it can be empty for now:

spectaql:
  # Use the theme from node_modules
  themeDir: ./docs/my-custom-theme
  # The rest of your config...

See the example config.yml for a full config example.

Great, time to run it! Running spectaql with the -D option will start a development server at localhost:4400:

# View docs with a development server
$ yarn spectaql ./spectaql-config.yml -D
# OR
$ npx spectaql ./spectaql-config.yml -D

As a shortcut, we've created an example node repo using SpectaQL and a custom theme. Feel free to fork it and follow along!

Theme 1: Override styles

Now you're all set up and it's time to do some theming. We'll start with the simplest kind of theme: overriding variables and styles from the default theme. This approach is likely all you will need. Overriding variables and styles can get you quite far even if the theme you envisioned is significantly different than the default theme.

The default theme defines a number of variables that can be overridden in your custom theme. The default theme styling is minimal as well; you can add your flair without rewriting a whole lot, and you'll get the benefit of upstream improvements to the default theme.

Let's make a dark theme. This is our goal:

spectaql dark theme Our goal dark theme

The first thing we'll do is add a stylesheets/custom.scss file to our custom theme's directory. custom.scss is a special file that will be imported by the theme system and will contain all of our overrides. Now your theme's directory structure will look like this:

my-custom-theme/
└── stylesheets/
     └── custom.scss

Next we'll add a few variable overrides. You can see the full list of available variables to override in SpectaQL's custom.scss. We'll pick out a couple obvious ones to get us 99% of the way there:

// custom.scss
$background: #222222;

$text-color: white;
$text-color-subtle: #808080;

$link-color: #66d9ef;
$link-color-hover: #83dced;

$border-color: #565656;
$border-color-subtle: #404040;

$background-code-block-heading: #000000;
$background-code-block: #000000;
$background-arguments: #000000;

We have mostly what we want in only a few lines of SCSS!

You can use custom.scss to override styles as well. For example, this box of arguments has a border that we'd like to get rid of.

Arguments with border Oof, what an ugly border

A well placed border: none; fixes it:

// custom.scss
// ....Variable overrides

// Style rule overrides
#spectaql {
  .row-field-arguments .field-arguments {
    border: none;
  }
}

Arguments with border No more border!

That's it! Only ~30 lines of code to significantly change the default theme.

For future reference, this dark theme is available for use in your own projects. The code is on GitHub at anvilco/spectaql-dark-theme and on NPM as spectaql-dark-theme. You can also check out the example node repo using spectaql-dark-theme.

Theme 1.1: Override syntax styles

In addition to the main styles, you may want to tweak the syntax highlighting colors. Styling the example code blocks works similarly to using custom.scss as described above. You'll add a new file called syntax-highlighting.scss. Once the new file is added, your theme's directory structure will look like this:

my-custom-theme/
└── stylesheets/
     ├── custom.scss
     └── syntax-highlighting.scss NEW!

SpectaQL uses Highlight.js to render code blocks and supports all of their themes. You can check out the theme demos then grab the whichever theme styles you'd like. By default, SpectaQL ships with the Monokai theme:

Monokai theme Monokai: the default syntax theme

Paste the tomorrow night theme css into your syntax-highlighting.scss and voila, a fresh look:

Tomorrow night theme Tomorrow night syntax theme

Theme structure

A lot more customization is possible beyond the overrides described above. Before digging any deeper, it's helpful to understand how the theme system operates.

When you specify a theme directory to SpectaQL, the files contained in your directory will be overlaid on top of the default theme. That means when the system imports stylesheets/syntax-highlighting.scss and your theme directory contains a stylesheets/syntax-highlighting.scss file, it will use your file instead of the file from theme default theme.

The default theme is made up of several directories structured roughly like this:

default/
├── javascripts/
│
├── stylesheets/
│    ├── custom.scss
│    ├── syntax-highlighting.scss
│    └── main.scss
│
├── views/
│    ├── partials/
│    ├── main.hbs
│    └── embedded.hbs
│
└── helpers/

The default theme directory structure

In the dark theme we built in the previous sections, we specified a couple files: custom.scss and syntax-highlighting.scss. When generating the docs, our custom files were simply used instead of the files in the default theme.

my-custom-theme/
└── stylesheets/
     ├── custom.scss
     └── syntax-highlighting.scss

You can extend this concept to any files in the default theme—stylesheets of course, but also javascripts, views, and even helpers. You can even override main files for full control. For example you could create a directory structure with only main.scss:

my-custom-theme/
└── stylesheets/
     └── main.scss

This structure would totally replace all styles in the default theme with your own custom main.scss file.

Theme 2: Fully custom styling

It's possible that you want total control over styling—you don't want to override and undo a bunch of built-in styles. Let's use the structure described at the end of the last section:

my-custom-theme/
└── stylesheets/
     └── main.scss

Our main.scss can start out with very little in it:

// main.scss
#spectaql {
  background: white;
}

Well, it isn't very pretty, but you're the captain now!

no theme Custom theme with no styling at all

You can start from complete scratch like this if you want. However, SpectaQL provides a base.scss file with structural styling. Using it is super easy, just add @import 'base'; to your main file to get structural styling from the default theme. base.scss will set up the sidebar, mobile drawer styling, side-by-side code blocks, etc. but provide no visual styling.

// main.scss
@import 'base';

#spectaql {
  background: white;
}

Readable, but still not attactive!

base styling Custom main file using base.scss

base.scss contains a number of variables you can override. See base.scss for full details. For demo purposes, I'll just override the $code-background then import the default syntax-highlighting styles:

// main.scss

// Override variables from base
$code-background: #222222;

@import 'base';
@import 'syntax-highlighting';

#spectaql {
  background: white;
}

Alright, now we're cooking with gas:

base theme with syntax highlighting Custom theme using base and a syntax theme

At this stage you can do whatever you'd like. It may be useful to look at the styles from the default theme's main.scss, feel free to use bits of it in your own theme.

Overriding templates

So far, we've focused entirely on styling your theme. What if the HTML is not structured to your liking? Maybe you want to totally restructure the intro block? Maybe you don't like how resolver arguments are displayed? You can override templates just like you override stylesheets: by providing an override file in your custom theme for anything in the views directory.

As an example, let's restructure the introduction section. By default it looks like this:

default introduction section The default introduction section

The introduction and its side-by-side markup is controlled by the welcome.hbs handlebars template in the views/partials/layout/content/introduction directory. Custom themes can override anything in the views directory, so all we need to do is mirror this directory structure in our theme, then supply a new welcome.hbs:

my-custom-theme/
├── stylesheets/
│   └── custom.scss # Our custom style overrides
└── views/
    └── partials/
        └── layout/
            └── content/
                └── introduction/
                    └── welcome.hbs

Dir structure to override the welcome partial

<!-- views/partials/layout/content/introduction/welcome.hbs -->
<div id="welcome">
  {{md info.description}}
  <p>Custom HTML, I do what I want!</p>
</div>

Our new welcome partial

You can also create new partials and use them from your overridden template. Our docs could use a little spicing up, how about a kitten in a new partial:

my-custom-theme/
├── stylesheets/
│   └── custom.scss # Our custom style overrides
└── views/
    └── partials/
        └── layout/
            └── content/
                └── introduction/
                    ├── kitten.hbs # NEW!
                    └── welcome.hbs

A new kitten partial we'll use in the welcome template

<!-- views/partials/layout/content/introduction/welcome.hbs -->
<div id="welcome">
  {{md info.description}}
  <p>Custom HTML, I do what I want!</p>
  <!-- Import our new partial -->
  {{>layout/content/introduction/kitten}}
</div>

Using the new kitten partial in our welcome template

<!--
  views/partials/layout/content/introduction/kitten.hbs
  New template used by welcome.hbs
-->
<img src="https://placekitten.com/g/400/300">

The new kitten partial HTML

And the final result:

new intro Cute, but you're about to get attacked

Overriding JavaScript

By default, SpectaQL outputs your docs with minimal JavaScript; there are only two behaviors: a menu toggler to open the navigation menu in mobile, and a scroll-spy to highlight the part of the navbar the user is viewing. These scripts are written in vanilla JavaScript with no dependencies. SpectaQL bundles them up into a single tiny JS file for easy importing.

Like stylesheets or templates, the site's JavaScript is part of a theme. You can customize the JavaScript bundle from your theme in two ways:

  1. By providing an extra JavaScript file with your new custom features.
  2. By overriding a JavaScript file from the default theme.

Adding a custom JavaScript file

SpectaQL will pull any files from your theme's javascripts directory into the bundle. This lets you keep the default nav toggle and scroll spy, but add your own custom behaviors with a brand new file. You can name new files anything you'd like—we'll use custom.js for consistency. With the new JS file, our theme's directory structure would look like this:

my-custom-theme/
├── stylesheets/
│   └── custom.scss
└── javascripts/
    └── custom.js # Name it anything you want!

And our new file

// javascripts/custom.js
console.log('Magically loaded')

Overriding an existing JavaScript file

You can also change behavior of the existing scripts by overriding any file in the javascripts directory. Maybe you want custom scroll spy behavior? You can provide your own javascripts/scroll-spy.js file:

my-custom-theme/
├── stylesheets/
│   └── custom.scss
└── javascripts/
    └── scroll-spy.js # Override the default scroll-spy
// javascripts/scroll-spy.js
function scrollSpy () {
  console.log('Custom spy')
}

Or maybe you want full control. You can override the entire bundle by specifying a main.js:

my-custom-theme/
├── stylesheets/
│   └── custom.scss
└── javascripts/
    └── main.js # Override the entire bundle
// javascripts/main.js
window.addEventListener('DOMContentLoaded', (event) => {
  console.log('I do what I want')
})

Fin.

That's it! Hopefully this post helps you get the most out of your theme. A major goal with SpectaQL 1.0 was simple customization. We wanted it to have a theme system allowing you generate docs that fit your style, vision, and brand with minimal effort.

If you have questions or issues, open an issue on SpectaQL or reach out to us at developers@useanvil.com. We'd love to hear from you!