tl;dr: Rik adds a blog post reader to the new RikVerse website
This is the fourth in a series of blog posts detailing my journey to rebuild my poetry website - The RikVerse - using Svelte.js, Page.js and Tailwind CSS. Other posts in this series:
- Introduction to the project
- Setting up the shiny
- The client is not the server - 404 errors
- Building the blog reader
- Fun with Cookies!
- The book store bits
- Sharing is Caring - social media share buttons
- … And finally: the poems!
The code developed in this blogpost can be found here on GitHub.
Where were we?
In the previous post I investigated and fixed a major issue which was causing “404 Page Not Found” errors when users attempted to refresh a page on the new RikVerse site. Now that that issue has been resolved, we can move on to more fun stuff!
Here’s a quick reminder of my thirteen goals for this site rebuild:
- [This post] Add in poetry-and-writing-related blog posts
- [This post] Get rid of the database!
- [Next post] Keep the donate button; make it More Fun To Use
- [Next post] Add in cookie consent (meeting UK ICO guidance for explicit opt-in)
- [Future post] Make each publication page that book’s keystone page
- [Future post] Let people read the books in-site
- [Future post] Index of all the poems available to read
- [Future post] Tag filtering functionality on the poems index page
- [Future post] Easy for the user to access a poem’s associated media files (images, audio, video)
- [Future post] The landing page should display a randomly selected poem
[Done] Simpler, more minimal design; better fonts[Done] Navigation needs to be massively simplified[Done] Avoid 404 Page Not Found errors at all costs!
Question: what is a “blog reader”?
A traditional blog site, or blogging platform, comes in two parts:
the “writer” part, usually some form of console where users can log in to compose and publish blog posts; and
the “reader” part, where everyone else can see a list of blog posts, and read individual articles.
The RikVerse is, primarily, a site where I can showcase my books and poems. It is not a blogging platform. However I have, over the years, offered opinions in various venues on the state of poetry, writing and publishing. As part of this website rebuild I wanted to gather those witterings together in one place so that complete strangers can discover them for their own entertainment.
I also have an aim for this rebuild to rid myself of a database on the back end. The site’s content is (mostly) static: it only gets updated when I feel an urge to write a poem, or publish a book, or offer an opinion on something related to the terrible state of English literature.
Thus my thinking is: I can store the poems, book details and blog posts in .js
and .html
files, which can be fetched by the front end whenever a page or person has a need for them.
… Yes, it’s a simplistic architecture. But it fits the requirements this particular website. It is enough to meet the client’s (in other words, my) wishes!
Finalizing the design
At the moment, the site does not look pretty. We need to change this. But before we get onto coding up the header and footer components I need to correct a small bug.
One of the screen responsiveness things I’m doing is increasing the base font size as we move through the breakpoints. I was doing this by setting font sizes in px
on the body
element for each @media
block (this is all happening in ./src/App.svelte
).
Tailwind CSS very sensibly does its font sizing magic in rem
units. These units are relative to the font size of the entire document - which needs to be set on the html
element.
… All fixed now!
Finalizing the Navigation.svelte component
My Navigation component acts both as the site’s banner, and as the main site navigation - which will be a horizontal line of anchor links. I didn’t want to go overboard on designing this component, but I did want it to stand out.
I made a decision to rescue the “RikVerse” banner image from the old site. This - alongside the .ico
image that goes in the browser’s tab - was the only old site furniture I planned to reuse.
For the navigation links, I decided to go super-simple.
I know every modern site in the world likes to collapse its main navigation into a ‘hamburger’ link for small screens. I also know that hiding the navigation bar (with its ‘home’ and ‘hamburger’ icons) when the user scrolls down the screen, but showing it fixed at the top of the screen when the user scrolls back up, remains “all the rage” in UX circles.
I made some design decisions:
- No icons in the navigation - links to be text-only.
- No slippy-slidey collapsing navigation bar which only shows up on mobile screens.
- Navigation links to be duplicated in both the Navigation and the Footer components.
… I think this will work well for the RikVerse site, where I need to keep distractions to a minimum. The user has arrived at the site to read poems, not to be pulled away from the words by animated shiny!
Here is the complete code for ./src/components/Navigation.svelte
1 | <script> |
And here’s the code for ./src/components/NavigationLinks.svelte
1 | <script></script> |
How many CSS classes can you see in that code?
This is where the magic of combining Svelte with Tailwind CSS really shines!
Tailwind makes it super-easy to apply classes to elements through its @apply
function. Where Tailwind is lacking a class for something - such as for styling a grid - we can add in that styling using normal CSS.
And then Svelte goes away and does its magic to make sure that the styles applied to the elements in a component remain local to that component! The styling that the Navigation component adds to its div
and a
elements is completely separated from the styling applied by the NavigationLinks component to its own equivalent elements.
Also, adding a component to another component is really easy in Svelte - import
the component in the script
element (note: paths are relative to the receiving component), then use it wherever needed in the layout.
Finalizing the Footer.svelte component
The Footer component is a little more interesting because it includes a Button
element:
The idea is that if people want to give me some £money, they can click on that button and make a donation via PayPal. However to do this, users need to agree to allow PayPal to add cookies to the site. Only after they agree will they be able to make a donation.
… Yeah. It’s a palaver. But it’s also the law. I’ll be describing how I handle cookie consents in the next post. For now, clicking on the button just takes the user to the Cookie Consents page.
Here’s the entire code for ./src/components/Footer.svelte
1 | <script> |
The button
element is not an anchor - it doesn’t have an href=
attribute. Instead, we have to let Svelte know what to do when a user clicks on it. We do this through the on:click={setupPayPalAction}
attribute - see the Svelte documentation for more information on handling events in Svelte components.
Building the RikVerse Blog reader
A blog post consists (for me, at least) of two parts:
The bit you read - the “copy”, and
The various bits of data surrounding it such as title, publication date, tags, etc - the “meta”
Normally all of this stuff gets added to a database as a single record.
For the RikVerse, I wanted to display a list of blog posts which, when clicked on, open up the selected post in a different display. Nothing surprising here.
Again, normal procedure is to send a request to the backend to get all the data required to display the list (from each record, for example: title, summary, publication date, link to article, etc). Then to read a particular post, another request gets sent to the backend to get all the data contained in the record for that post.
It’s a sensible way to do these things.
But it does mean that some date (title, summary, publication date, etc) gets sent over the network twice. Even in these days of super-fast broadband and 5G wireless, it feels a bit wasteful to me.
Also, I’m trying to break free from paying for a database on the back end. Because: nothing in this world is truly free.
So I decided to split the blog post data. For each post:
The “copy” part of the post is saved in its own file (in plain html) in the
./public/posts/
directory - which makes sense as this comprises the vast bulk of the post and should only be sent over the wires when required; whileThe “meta” part of the post (everything that is not “copy”) gets stored as an object in an array in a different file -
./src/data/blogpostData.mjs
Stay with me here … I do have excellent reasons for doing it this way!
This is a snippet of the blogpostData.mjs
file. As you can see, the object attributes are (pretty much) the same as the ones used in the pageData.mjs
file.
1 | const blogpostData = [ |
This is all the information we need to start constructing the blog posts listing page. And, because the file is a Javascript module, we can use it in the same was as we use the pageData module.
The blog post listings page
Here’s the entire code for the ./src/pages/Blog.svelte
component:
1 | <script> |
The interesting thing here is the Svelte markup - #each … - which we use to iterate through the two arrays we created. We pass this data onto a new component called BlogListing; this is the component which does all the work of laying out the data in a nice way on the page.
This is the ./src/component/BlogListing.svelte
code:
1 | <script> |
There’s nothing remarkable in this code - except for the wierd export let listing;
line. As ever, the Svelte documentation does a far better job than I can to explain how it passes data between components using attributes and properties.
The anchor link <a href="/blog/{listing.id}">
- this is a new route that we need to add to our router code. I updated the ./src/routes.js
file as follows:
1 | const routes = [ |
Now we’re in Page.js territory. The client-side router’s documentation is less than useful if you don’t know what you’re looking for. However Jack Whiting’s blog post, which inspired this rebuild exercise, details how we can access that mysterious :slug
value (which will be the blog post’s id string) in the component that Page routes us to.
The blog article display page
So the user has wandered into the RikVerse, discovered the blog listings and clicked on one of the links. Now we have to display that article to them.
Page.js tells Svelte to load up the BlogPost page. The code for this Svelte component is located in ./src/pages/BlogPost.svelte
:
1 | <script> |
The ./src/utilities.js
file is a small collection of functions which I can import and use in various places across the code base:
prettifyDate()
makes a YYYY-MM-DD date string more readable - I could have used the moment.js library (which is fantastic!) but I only need a couple of date conversion functions on this site so I’m saving some bandwidth by coding up the functions myself.scrollToTopOnLoad()
triggers a browser scroll action to the top of the page.navigateTo()
- I refactored the Footer’s button redirection code into the utilities file so I could use it in other components, like here.
The Svelte commands #if ... /if
and @html ...
are properly explained in the Svelte documentation.
… And that is pretty much it!
There’s just one last thing to do before I wrap up this post …
Danger! Danger! 404 Page Not Found!
As it stands, every blog post page will error if the user clicks on their browser’s refresh button. We need to generate some static redirect pages for all the post pages.
I updated the ./pageBuilder.mjs
file to automagically do this work for us:
1 | // Process the router base pages index files |
… And that really is is for this post. In the next post I shall be having Fun With Cookies!