tl;dr: Rik works out how to add Facebook share, and Twitter tweet, buttons to the RikVerse site - while not breaking any nasty Cookie laws
This is the penultimate 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 blog post can be found on GitHub.
Where were we?
In the previous post I built a book store for my free publications. Now I need to add social media buttons to the site so people can spread the good news!
Here’s a quick reminder of my goals for this site rebuild:
- [This post] Add social media share buttons to each page
- [This post] Fix the metadata
- [Next post] Index of all the poems available to read
- [Next post] Tag filtering functionality on the poems index page
- [Next post] Easy for the user to access a poem’s associated media files
- [Next 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![Done] Add in poetry-and-writing-related blog posts[Done] Get rid of the database![Done] Keep the donate button; make it More Fun To Use[Done] Add in cookie consent (meeting UK ICO guidance for explicit opt-in)[Done] Make each publication page that book’s keystone page[Done] Let people read the books in-site
What do we need to do to be social?
There’s many, many different ways to be social. Each social media channel has its own way of doing things and, as the years have rolled by, each channel has developed a variety of ways to achieve pretty much the same thing: publish details of a page on a website, that a user is currently viewing, into that user’s news channel in the social media site.
Facebook paved the way with their share buttons - snippets of code people could add to their website that displayed a Facebook-branded button which, when clicked by a visitor, would share that page on the visitor’s Facebook News Feed. Heady and magical stuff!
The price to pay? Nothing was as easy as Facebook claimed it would be. The code could fail for many reasons, and for no reason at all. Not forgetting: social graphs; tracking cookies; targeted adverts; Big Brother!
It turns out that Facebook has published many different types of ‘share-like’ buttons over the years: Share, Like, Send, Save, Quote … which send stuff to different places such as a user’s news feed. Or their story. Or a group they manage.
Confusion alert! … So what is it that I actually want to do?
I want a Facebook share button, customised for every page of my site - just as they do for the big news channels sites like the BBC or the Guardian or the Washington Post. Click the button and a Facebook box appears with the news story’s title and pretty image loaded, everything ready for the user to add comments and click ‘share’.
And Facebook is not the only social media platform out there. You want some share buttons? Here’s some share buttons for you:
It’s no surprise that companies have come along to build a business out of simplifying the whole sharing process for website owners and web developers. For instance, ShareThis. I’ve used this company’s services in the past and can testify that their code has saved me from many headaches: it really is just copy-and-forget stuff.
But everything - even “free” services - comes at a price. ShareThis make their money by collecting and selling the aggregated data that passes through their servers - as they make clear in their (not easy to find) privacy statement, publisher information and terms of use pages.
And everything runs on cookies. Which is now a problem, given the UK’s Information Commissioner’s Office’s updated guidance on what website owners need to do when asking users to agree to cookies - via the Dreaded Cookie Banner - if they want to avoid (potentially massive) fines.
The upshot of all this is that I won’t be using a service like ShareThis on the RikVerse site. I’m going to have to do the coding myself.
The practicalities of adding Facebook and Twitter share buttons to a site
The constraints for this work are as follows:
Users must agree to Facebook and/or Twitter cookies before they can share a RikVerse page to Facebook Friends and/or Twitter Followers.
Cookie agreement should be sought only once per session, or less if the user explicitly agrees the RikVerse can remember their choice for future visits.
Facebook and Twitter share buttons must appear on every page on the site.
Facebook and/or Twitter buttons should NOT appear on pages if the user decides not to allow the related cookies onto their browser or device.
The shares should include text and images relevant to the page being shared - not a single, generic site-landing-page share that appears on every page.
The good news is I’ve already done the work required for the first two constraints. All I need to do now is code up the buttons and associated metadata, making sure that my code doesn’t sneak cookies onto the user’s browser or device before they’ve agreed to them.
I went away and did some research: Facebook and Twitter developer-related pages; Google searches for best practices; reviews of other sites (BBC, Guardian, Washington Post, etc) to see how their developers had coded up this stuff. Here’s what I discovered:
Facebook requires that I register the RikVerse website as an “app” before I can attempt to share anything on their site. Twitter has no such requirement.
Both Facebook and Twitter require that
<metadata>
tags be added to each web page’s<head>
element - these tags hold information such as page title, full URL, description, URL of related images, etc.Both networks do their magic stuff through Javascript libraries (SDK software development kits) which need to be added to the page via
<script>
elements; Facebook also requires that an additional function should be added to the browser’swindow
object before its SDK loads.The networks will add cookies to the user’s browser/device only after their SDK libraries are initialized.
Finally, while both companies have brand guidelines about how their share buttons should look, I’m free to use my own button designs (within limits).
Fun with Metadata
I added the required <metadata>
elements to the RikVerse’s index.html
page (which lives at ./public/index.html
). They look like this:
1 |
|
While it looks complex and horrid, I quickly realised that I only needed to supply five pieces of information to individualise each RikVerse page:
1 | -> title: // a page name, or blogpost title, or book title, or poem title, |
… Like this:
1 | <!-- general page metadata --> |
A small Svelte gotcha!
My first attempt to do this work … was a failure. Svelte includes a markup gizmo called <svelte:head>
. This does an excellent job of allowing me to update the <title>
element in each page’s <head>
element, so the text that appears in the user’s browser’s tab changes according to the page they’re viewing:
1 | <svelte:head> |
I thought … if I made a new Svelte <Metadata>
component to hold all the metadata tags, then included that component in the <svelte:head>
component, it would work:
1 | <svelte:head> |
It does work! Except it worked by adding, instead of replacing, the metadata elements to the index page <head>
; after spending some minutes on the site, browsing between various pages, the <head>
element became the proud parent of several hundred <metadata>
elements.
… This is not the functionality I was looking for.
Doing metadata with a JS module
For my second attempt to solve the problem, I developed a new Javascript module file at ./src/handleMetadata.js
- similar to the module I developed to help handle cookie functionality. Here I did things the oldskool way, getting handles to each of the <metadata>
elements and updating their content=
attributes according to the data supplied to the function:
1 | // JS object which will hold handles to DOM metadata elements |
The data I need for each different page … I’m already pulling that data into the page from the appropriate data .mjs
file (./src/data/pageData.mjs
, ./src/data/blogpostData.mjs
, ./src/data/bookData.mjs
).
Thus I can update each of my Svelte page components (in the ./src/pages/
directory) with code to update the metadata each time the client-side Page.js router loads the page into the index.html
file. For example:
1 | import pageData from '../data/pageData.mjs'; |
Don’t forget the semi-static pages!
Both Facebook and Twitter employ web crawlers as part of their sharing functionalities. These spiders will, when a request to share a page is received by the social media servers, go away and investigate the page - in particular to retrieve data from the <metadata>
elements in the page’s <head>
element.
The web crawlers will have no idea that the page to be shared is part of a Svelte Single Page Application (SPA), which only has an index.html
page on the server. Instead they will visit the Absolute URL address supplied as part of the share request; if the request results in a “404 Page Not Found” response, the share attempt will fail.
… I’ve already addressed the 404 situation, as I explained in my third post - The client is not the server - 404 errors. To summarise: I’m not able to access my web host’s shared server to add redirect rules to it, and I’m not prepared to put in the effort to code up, using tech like a headless browser, a development toolchain to generate full, standalone static site pages for every end point URL.
Instead, the RikVerse is a semi-static site with a single-point-entry SPA page (index.html
) and a static “redirect” page for every end point in the site.
My developer toolchain solution was a Node script which loads up the metadata modules - ./src/data/pageData.mjs
, ./src/data/blogpostData.mjs
, ./src/data/bookData.mjs
- and uses them to generate the semi-static pages (every time I run yarn build
in the terminal).
Those modules already contain all the data I need to generate page-specific <metadata>
elements. So I adapted my script - ./pageBuilder.mjs
- to do exactly that:
1 | console.log('page builder called'); |
(Happy dance) … Problem solved!
Adding the Javascript SDK libraries
For both Facebook and Twitter, I don’t intend to add in their SDK libraries until after the user has agreed to cookies. Once that happens, I get Svelte to serve up different share buttons (see below) which include appropriate <script>
tags, which then load the libraries.
For Twitter, this is all that needs to happen.
The Facebook SDK, however, expects an additional piece of code to already be in place: a function attached to the window
object which the SDK can invoke to get everything set up for sharing and stuff.
Adding this code to the ./public/index.html
file does not, in itself, trigger the generation of unexpected cookies; that only happens after the function is invoked. So I went ahead and updated the index file:
1 |
|
And finally - the share buttons!
I was adamant that share buttons should appear on every single page in the RikVerse site. So the obvious place to add them is in Svelte’s root component - ./src/App.svelte
:
1 | <style global> |
<SocialMedia>
- a container for the share buttons - is a new Svelte component, whose file can be found at ./src/components/SocialMedia.svelte
:
1 | <script> |
The Facebook share button
Each share button lives inside its own Svelte component. This allows me to contain the functionality associated with each inside the component.
Both components make use of the Svelte cookie stores I set up in my Fun with Cookies! post.
The button designs - I appropriated these from the BBC website. I pay my TV licence thus, in my view, I’m entitled! I tweaked the buttons so that when a user first visits the RikVerse they appear greyed out - because: user has not yet agreed to cookies. Once they agree to cookies the buttons will appear in their brand colours.
If the user actively refuses to have the cookies in their browser/device, the code will not generate the buttons, and no cookies will be added.
Here’s the code for the Facebook button - ./src/components/FacebookButton.svelte
:
1 | <script> |
The Twitter share button
While the Facebook share button uses a <button>
element, Twitter has to be different. For their button they insist on using an anchor <a>
element with a truly scary href
attribute.
The href
attribute is scary because, URL-encoded into it, is the following information:
- The page’s title;
- The page’s description; and
- The page’s absolute URL address
The address data is easy enough to find - we can use window.location.href
.
The other data, however, we need to obtain from the current page’s metadata. And the simplest way to do that is to stuff the data into a Svelte store when we’re doing the metadata update work, and import those stores into the <TwitterButton>
component.
I adapted the .src/handleMetadata.js
file to make this happen:
1 | import { writable } from 'svelte/store'; |
Now we can build the component ./src/components/TwitterButton.svelte
file:
1 | <script> |
To save you from scrolling the code window, here’s the anchor’s href
attribute:
href="https://twitter.com/intent/tweet
?text={encodeURIComponent(`${$metaTitle} - ${$metaDescription}`)}
&url={encodeURIComponent(window.location.href)}"
… And that brings me to the end of this penultimate post in my Rebuild the RikVerse series. The final post will be about something that’s currently missing from the site … something that’s pretty fundamental: the poems!