SvelteKit Tutorial: Build a Svelte MDsveX Blog Site

🧑🏽‍🎓 SvelteKit Tutorial

SvelteKit Tutorial: with all the buzz around SvelteKit, we'll run through how to sep up an accessible, fast and secure Svelte Blog using SvelteKit. Our posts will be powered by Svelte in markdown (MDsveX). Essentially this new project is an extension on the recent blog post where we looked at the SvelteKit MDsveX Blog starter. This time we are taking a more hands on approach, customising the site and adding some MDsveX to the blog posts. If that sounds like the kind of thing you can get exited about, let's get going!

🚀 Let's Get the Ball Rolling!

To hit the ground running, we will be using the MDsvex Blog Starter. So, the first thing we need to do is clone it. I will be using pnpm as the package manager, but if you prefer npm, just drop the first p from all the pnpm commands. Let's clone the repo and set it up at the command line:

git clone https://github.com/rodneylab/sveltekit-blog-mdx.git sveltekit-tutorial
cd sveltekit-tutorial
pnpm install
cp .env.EXAMPLE .env
pnpm run dev

If this is your first time using SvelteKit, you might find it useful to skim through the post on Getting Started with SvelteKit. However, if anything is unclear, please get in touch (see details below), as it could be something I forgot to mention! Now in your browser go to localhost:3000.

As you have the site open, have a look though the pages. You will see there is some demo content in there already. In this post we will:

  • update styling,
  • change fonts,
  • add a new Svelte component and use it in an MDsveX blog post.

If you're as keen as I am, then let's jump on to the next part and see how you update the styling in the starter.

đź’„ Styling

We use SCSS for styling in the starter. Much of the styling is set by variables in two files: src/lib/styles/styles.scss and src/lib/styles/variables.scss. Styles propagate globally from there. This makes it easy to update the site's styling. Let's start by changing the colours up. Edit src/lib/styles/variables.scss:

// Colours
$color-theme-1: #e63946; // imperial red
$color-theme-2: #a8dadc; // powder blue
$color-theme-3: #457b9d; // celadon blue
$color-theme-4: #1d3557; // prussian blue

$color-primary: #005b99;
$color-text: #0d1821; // rich black FOGRA 29
$color-heading: $color-theme-4;
$color-heading-black: $color-theme-4;
$color-accent: #f1faee; // honeydew
$color-success: #2ec4b6; // tiffany blue
$color-danger: #e71d36; // rose madder

Excuse the inconsistent spelling of the word “colour”! Do other non-Americans do this, or is it just me?

Next, let's remove the italic styling for level 1 headings, deleting that line so that src/lib/styles/styles.scss ends up looking like this:

h1 {
  margin-top: 0;
  font-weight: $font-weight-black;
  font-size: $font-size-6;
  color: $color-heading-black;
}

No peeking until we have finished! Next we will change the BlogPostSummary and Card components:

...
    <span id={idString} aria-hidden="true" class="read-more-text">Read more {H_ELLIPSIS_ENTITY}</span>
  </div>
</div>
.content {
    width: 100%;
    border-radius: $spacing-2;
    margin: $spacing-0 auto;
    padding: $spacing-4 $spacing-0;

    h3 {
      margin: $spacing-0 $spacing-2;
    }
    p {
      color: $color-theme-3;
      font-size: $mobile-font-size-2;
      margin: $spacing-2;
    }
    .read-more-text {
      margin: $spacing-2;
    }
  }

  .content:hover {
    h3 {
      color: $color-accent;
    }
    p {
      color: $color-theme-2;
    }
.content {
    width: 100%;
    background-color: $color-theme-2;
    border-radius: $spacing-1;
    margin: $spacing-0 auto;
    padding: $spacing-4;
  }

Then finally, let's spruce up the home page, whose code is at src/routes/index.svelte. We're changing up the title and restyling the “About Me” card:

<header>
  <h1>My Film Photography Blog</h1>
</header>
<Card>
  <div class="card">
    <h2><span>About me</span></h2>
    <p>
      I live and breathe analogue photography. I show you my favourite analogue film cameras on this
      site. Hopefully if you are not into analogue photography yet, some of my enthusiasm will rub off
      onto you.
    </p>
  </div>
</Card>
<BlogRoll {posts} />

<style lang="scss">
  .card h2 { margin-top: $spacing-0; }
</style>

OK, you can take a look in the browser now. What do you think? Change it up a little more if it's not yet your cup of tea! When you're ready to move on, we'll change the fonts.

đź–‹ Fonts

The starter uses self-hosted fonts. This allows the page to load faster, improving user experience. One thing you need to remember is to install the new fonts when you change them. Let's start by uninstalling the starter fonts which we no longer want at the command line:

pnpm uninstall @fontsource/lora @fontsource/source-sans-pro

Next, as you probably guessed, we will install a replacement font:

pnpm install @fontsource/slabo-27px

We will also use Lato, but that is already installed. Slabo 27px will be used for headings and Lato for other elements. Most free fonts are available as Fontsource packages, you see the Fontsource website for more details. Be sure to check which weights and styles are supported for your chosen font.

Now we have the fonts installed, we need to import them and then set them as our chosen fonts in the SCSS variables file. Let's start with the variables file:

// Fonts
$font-family-serif: 'Lato', 'Lustria', 'Noto Serif', 'Merriweather', 'Georgia', 'Cambria',
  'Times New Roman', 'Times', serif;
$font-body: $font-family-serif;
$font-heading: 'Slabo 27px', $font-family-serif;

Let's carry on to the final step; importing the fonts. Since all pages use our global layout file (src/routes/__layout.svelte), we will import fonts there removing the original import:

<script>
    // Lato - supported variants:
    // weights: [100, 300, 400, 700, 900]
    // styles: [italic, normal]
    import '@fontsource/lato/400.css';

    // Slabo 27px - supported variants:
    // weights: [400]
    // styles: [normal]
    import '@fontsource/slabo-27px/400.css'
    ...

⚓️ Headings with Scroll to Anchor

Sadly we're almost finished. Let's carry on with the next step. I promised we would create a new svelte component and add it to out blog post markup. We will do exactly that now. We will implement scroll to anchor. You have probably been on websites on which if you hover over a title a little link icon appears. You can copy that link to reference it from somewhere else, or just click it so the section you are reading scrolls to the top of the screen. That's just what we will set up now. Let's start by creating the new Heading component. Make a new file at src/lib/components/Heading.svelte:

<script>
    import LinkIcon from '$lib/components/Icons/Link.svelte';

    export let id;

    $: showLink = false;

    const handleMouseEnter = (event) => {
        showLink = true;
    }

    const handleMouseLeave = (event) => {
        showLink = false;
    }
</script>

<h2
    {id}
    on:mouseenter={handleMouseEnter}
    on:mouseleave={handleMouseLeave}
>
    <slot />
    {#if showLink}
    <a href={\`#\${id}\`}>
        <LinkIcon />
    </a>
    {/if}
</h2>

That code fragment is packed full of sveltisms. There's a bit too much to go into here. It's worth checking the Svelte tutorial for explanations, though also feel free to get in touch if I can help. We rely on default browser behaviour for the scrolling. An id will be supplied by the component consumer. We label out heading with that id. Because of that, the browser knows where to scroll when we specify that id in the anchor element.

This is quite a basic version to demonstrate MDsveX. You can go to town here when you implement it on your own site. For example you might want to generate the ids automatically or have custom heading levels. There is so much you can do with this one tiny element.

You will notice we imported a Link icon which we need to add the icon to our project. Let's get cracking on that.

Link Icon

Create a new file at src/lib/components/Icons/Link.svelte and give it the following content to import the corresponding Feather icons icon:

<script>
  import { DEFAULT_ICON_SIZE } from './index.js';
  import LinkIcon from 'svelte-feather-icons/src/icons/LinkIcon.svelte';
  export let size = DEFAULT_ICON_SIZE;
</script>

<LinkIcon {size} />

Our final step is to import the component in a blog post and use it. Open up src/routes/best-medium-format-camera-for-starting-out/index.md. The file is a little messy, with a lot of front matter because of a temporary workaround for the Netlify adapter. That aside, let's import and use the Heading element:

<script>
  import ExternalLink from '$lib/components/ExternalLink.svelte';
  import Heading from '$lib/components/Heading.svelte';
  import Link from '$lib/components/Link.svelte';
</script>

<Heading id="whatIsAMediumFormatCamera">What is a Medium Format Camera? </Heading>
...

Now go to your browser and hover over the link and the link icon should appear, you can click it to scroll to anchor. When you move the mouse away from the link, it should disappear. Hope it's all working. What do you think?

🙌🏽 SvelteKit Tutorial: Wrapup

That's it for now. Normally I would also run automated accessibility tests in Cypress using Axe. Unfortunately, Cypress is not currently compatible with SvelteKit out of the box, though there are a few workarounds you can try. As an alternative, you can install the Axe browser addin then open up Axe from your browser Dev Tools.

I am keen to hear how you will extend what we have run though here. What will you blog about?

Sveltekit Tutorial: Feedback

Please send me feedback! Have you found the post useful? Would you like to see posts on another topic instead? Get in touch with ideas for new posts. Also if you like my writing style, get in touch if I can write some posts for your company site on a consultancy basis. Read on to find ways to get in touch, further below. If you want to support posts similar to this one and can spare a couple of dollars, rupees, euros or pounds, please consider supporting me through Buy me a Coffee.

Finally, feel free to share the post on your social media accounts for all your followers who will find it useful. As well as leaving a comment below, you can get in touch via @askRodney on Twitter and also askRodney on Telegram. Also, see further ways to get in touch with Rodney Lab. I post regularly on SvelteKit as well as Gatsby JS among other topics. Also subscribe to the newsletter to keep up-to-date with our latest projects.

27