DEV Community

Ben Sinclair
Ben Sinclair

Posted on

I made my DEV articles into a blog using the Forem API

Other people have done this before, of course. I'm not breaking new ground here.

It's a couple of API calls and a styling exercise. All the actual work is done by DEV.

It lives at moopet.net/blog.

So why did I do this?

I lost my job a few weeks ago1 and decided it was time to make my personal website, which was pretty much a one-shot joke at the time, into something I could show recruiters. A links page, a CV, a portfolio, and... a blog. You know, something to show I was an "active member of society" or whatever.

The obvious choice was to use DEV, since I've made quite a few posts here over the years, and everything I've posted elsewhere is, let's say, "not representative of the opinion of my employers".

I also decided to do it with Svelte, because I'm getting into that, too. So birds, stones, etc., you know the drill.

What did I want it to look like?

I love gruvbox as a colour scheme, and I wanted something kind of retro. Something that wasn't another swish corporate-looking effort full of stock images but that also didn't look too playful.

Technical details

Dark mode

I wanted to support dark- and light-modes based on the system setting followed by the user's preference:

import { browser } from '$app/environment';

const defaultColorMode: string = 'light';

const getBrowserColorMode: string = () => {
  if (!browser) {
    return defaultColorMode;
  }

  if (window.localStorage) {
    const userSavedMode = window.localStorage.getItem('dark-mode');

    if (userSavedMode) {
      return userSavedMode;
    }
  }

  if (window.matchMedia) {
    if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
      return 'dark';
    }
  }

  return defaultColorMode;
};

let isDarkMode = $state(getBrowserColorMode() === 'dark');

$effect(() => {
  if (isDarkMode) {
    document.body.classList.add('dark-mode');
    window.localStorage.setItem('dark-mode', 'dark');
  }
  else {
    document.body.classList.remove('dark-mode');
    window.localStorage.setItem('dark-mode', 'light');
  }
});
Enter fullscreen mode Exit fullscreen mode
<div>
  <img src="/images/icons/dark-mode.png" alt="" />
  <input id="dark-mode-toggle" type="checkbox" bind:checked={isDarkMode} />
  <label for="dark-mode-toggle">Dark mode</label>
</div>
Enter fullscreen mode Exit fullscreen mode

Code blocks

For the code blocks, I decided to not change the style when switching between dark- and light-mode, because I think the dark block looks good against either background.

But while I was styling it, I noticed the syntax highlighter used on DEV handily includes the language in the wrapping element's class.

And I can do something cool with that!

A code block, with the language (in this case

.highlight {
    background-color: var(--color-gruvbox-black);
    color: var(--syntax-text-color);
    border-radius: 6px;
    padding-inline-start: 1rem;
    padding-inline-end: 1rem;
    padding-block-start: 1rem;
    padding-block-end: 1rem;
    margin-block-end: 0;

    & + .highlight {
      margin-block-start: 1.5rem;
    }

    &:has(pre) {
      position: relative;

      &:after {
        position: absolute;
        top: -0.75em;
        font-weight: bold;
        text-transform: uppercase;
        font-family: monospace;
        right: 1rem;
        padding-inline-start: 0.5rem;
        padding-inline-end: 0.5rem;
        padding-block-start: 0.25rem;
        padding-block-end: 0.25rem;
        border-width: 1px;
        border-inline-start-width: 1px;
        border-inline-end-width: 1px;
        border-block-start-width: 1px;
        border-block-end-width: 1px;
        border-color: var(--color-gruvbox-white);
        border-style: solid;
        background-color: var(--color-gruvbox-brown);
        border-radius: var(--border-radius);
        content: "source";
      }
    }

    &:has(pre.plaintext):after {
      content: none;
    }

    &:has(pre.html):after {
      content: "html";
    }

    &:has(pre.javascript):after {
      content: "js";
    }

    &:has(pre.shell):after {
      content: "shell";
    }

    &:has(pre.sql):after {
      content: "sql";
    }

    &:has(pre.yaml):after {
      content: "yaml";
    }
  }
Enter fullscreen mode Exit fullscreen mode

Inline code with backticks

I decided to style inline code the opposite way, with a light background. Dark didn't look good when the site was in dark mode and I wanted to do more than just use a monospace font.

Some unimportant blog text demonstrating the use of inline code, rendered with a different (lighter) background

What problems did I find?

Honestly, very few. Most were because I was a Svelte beginner - barely using anything beyond the router - and I've either fixed them since or decided they're fine as-is.

I couldn't figure out how to get pagination to work in Svelte without loading all my posts and slicing the array. Doesn't matter since there aren't enough posts to make it an issue. The API payload is small enough.

I realised too late that some posts had generic DEV-themed cover images (well, social_image really) because I hadn't uploaded one2.
I decided to make the text full-width if there was no cover_image set instead:

Close-up of two blog post cards, one with a teaser image and one without

... and I kinda like it. It's big and bold and goes with the big links and colours I've used elsewhere.

When faced with posts containing code blocks, I could have rendered the markdown myself and styled it (the API provides posts both as markdown and HTML) but then I'd have needed to handle liquid tags and all sorts of other content. So I took the HTML version and styled it up myself.

Similarly, links were something I needed to fudge, since they were often relative. I could add routes in Svelte to match the post slugs and show my articles, but I'd need to do that just for my own articles. Anyone else's and I'd need to redirect to dev.to.

Some elements in DEV's HTML (from liquid tags, mostly) don't always contain what I'd guess. For example, an {% embed ... %} tag for another user's post will render with an image and a description, which I styled up fine. It wasn't until I went through all the posts to double-check that I discovered that under some circumstances there can be more than one image. I had to style pretty defensively.

So am I pleased with the result?

Yeah. Yeah, I am. I have a website which doesn't look too modern, and uses proper HTML (it doesn't rely on any weird utility classes).

Grandpa Simpson saying,

I wanted to make a blog that felt like it fit in without having to rethink all the design decisions I'd made for the rest of the site. Or maybe it was the other way around. Whatever, I wanted it to go nicely together.

I wanted it to be as simple and extendable as I could make it, with bold, accessible colours and spacing, yet still cram in all the different things you can do in a DEV post.

I learnt a buttload about Svelte. Most significantly I learnt that the things I don't like about React are suddenly not a problem with Svelte. I'm not an expert with either so there's room to change my mind.

So what's a good example post?

Try moopet.net/blog/98475. It's an older post, but it checks out.

It doesn't particularly matter what the it's about; it showcases blockquotes, code blocks, inline code, different heading levels, images and tags, so it covers most of the things I've styled.

Is there anything left to do?

There's always something to improve, right?

  • make the router work with slugs instead of/as well as article IDs. Done!
  • read the highlighter source code to find what other languages it supports and add them in.
  • make it update without having to push to the repo :)
  • probably fix a million silly little styling bugs on different devices.

Final notes and engagement ploy

There are a couple of posts here on DEV where people have shown how they integrated posts in their own website, most of which go into a lot more detail than I have.

But I'd love to see some comments about your own examples, showing off how you made it look!


  1. It's ok, I got a new one. 

  2. In fact, this post you're reading now doesn't have a cover image, and it's because I couldn't think of something that would work and wouldn't be a recursive image of the blog itself! 

Top comments (4)

Collapse
 
jess profile image
Jess Lee

Yes!!!

Collapse
 
shricodev profile image
Shrijal Acharya

The site looks awesome, though. I have also built something similar for my portfolio using Hashnode GraphQL API.

Collapse
 
moopet profile image
Ben Sinclair

Hey thanks :) Yours is on techwithshrijal.com, right? It's a really good idea putting the links to DEV and Hashnode "for full engagement", that's something I should probably get round to as well, otherwise nobody will ever be able to comment!

Collapse
 
shricodev profile image
Shrijal Acharya

Adding that disclaimer can be helpful. I've added a similar disclaimer in the projects section as well, as it fetches the README content once using GitHub Actions, and if I ever change the README, it's not updated. It's better to give an initial heads-up.

I'm glad I was helpful to you, even in the slightest way.