Relaunching my website using Swift

by Maximilian Götzfried on

I recently rebuilt my website using Swift and John Sundell's Publish framework and i can't recommend it enough. It was a lot of fun.

It's just great to be able to use the familiar DateFormatter to format dates of blog posts. Or to use a forEach loop to create a list of posts that are sorted using a key path:

let items = context.allItems(sortedBy: \.date, order: .descending)
return HTML(
    .body(
        .ul(
            .id("posts"),
            .forEach(items) { item in
                .li(
                    .article(
                        .a(.href(item.path), .text(item.title)),
                        .p(.text(item.description))
                    )
                )
            }
        )
    )
)

Installation and setup

After following the instructions you end up with two things:

If you look at my code you see that the final result is pretty straightforward and well organized. I chose a very reduced approach. That's why there are only three Swift files in my package.

Adding additional resources

Publish requires that resources (images, stylesheets, …) are put in a separate folder Resources, outside of the Content folder:

Publish requires that resources are put in a separate folder Resources

During the publishing process it copies the resources to the root level of the Output folder:

During the publishing process resources are copied to the root level of the Output folder

This is implemented in PublishingStep.copyResources(at:to:includingFolder:).

Adding images to posts when editing

I found that Typora is the best solution to write new posts, especially when it comes to dealing with images. Typora supports both custom root urls and copying images to custom folders.

To support Publish's resource structure i add these two lines to the YAML front matter of the markdown document of each post:

typora-copy-images-to: ../../Resources/images
typora-root-url: ../../Resources

So whenever i add an image to a post, for example by drag & drop, it automatically copies the image to the Resources folder of my Publish repository and sets the image url to /images/<filename>. The only requirement is that the markdown document needs to be saved to the posts folder before adding images.

By the way: These custom metadata fields are ignored during the publishing process because i don't define them in the WebsiteItemMetadata struct of my site definition. So they don't show up in the generated HTML.

Flashing the posts list

I decided to put the list of blog posts at the end of the index page. I know, this is weird. But at the moment it is more important to present me as a freelancer than list my posts.

I thought it would be nice to flash the posts list when a visitor follows the link in the description that jumps to that list, similar to what Stack Overflow is doing when linking to answers.

To do this, i added .id("posts") to the posts list in MyHTMLFactory.makeIndexHTML(for:context:). Then, to flash the posts list when the anchor is clicked, i added an animation to .posts:target in the CSS:

#posts:target {
    animation: flash 2s;
}
@keyframes flash {
   from { background: rgba(254, 255, 0, 0.40); }
   to { background: transparent; }
}

This solution is similar to this CodePen.

Supporting dark mode

To support dark mode i first tell the browser in the stylesheet that my site supports both light and dark mode. It will then redraw the site when the user switches between these modes:

:root {
    color-scheme: light dark;
}

Then, i defined the colors using a @media query:

@media (prefers-color-scheme: dark) { … }

Highlighting syntax

Publish has a plug-in for John Sundell's Splash, a Swift syntax highlighter that adds colors to your code blocks by adding CSS classes to your HTML:

<span class="keyword">func</span> hello(world: <span class="type">String</span>) -> <span class="type">Int</span>

Adding this plugin is pretty simple because you just have to add one line of code to your main publish call:

try MySite().publish(
    withTheme: .myTheme,
    additionalSteps: [],
    plugins: [
        .splash(withClassPrefix: "")
    ]
)

For the colors i just use Xcode's default colors:

/* light colors */
pre code .keyword { color: #9b2393; }
pre code .type { color: #0b4f79; }
pre code .call { color: #326d74; }
pre code .property { color: #326d74; }
pre code .number { color: #1c00cf; }
pre code .string { color: #c41a16; }
pre code .comment { color: #5d6c79; }
pre code .dotAccess { color: #326d74; }
pre code .preprocessing { color: #643820; }

/* dark colors */
pre code .keyword { color: #fc5fA3; }
pre code .type { color: #5dd8ff; }
pre code .call { color: #67b7a4; }
pre code .property { color: #67b7a4; }
pre code .number { color: #d0bf69; }
pre code .string { color: #fc6a5d; }
pre code .comment { color: #6c7986; }
pre code .dotAccess { color: #67b7a4; }
pre code .preprocessing { color: #fd8f3f; }

The code

You can find the Swift package for my website on GitHub at mxgzf/mxgzf.com and the generated website at mxgzf/mxgzf.github.io.

More

Check out the following links if you want to learn more about creating and maintaining websites using Swift and Publish:

This list of pull requests gives you a good overview what others contributed to Publish.

Two plug-ins for Publish:

Other sites built with Publish: