diff --git a/package.json b/package.json
index cc3d92ff..53a54059 100644
--- a/package.json
+++ b/package.json
@@ -41,6 +41,7 @@
"@storybook/theming": "6.5.9",
"@sveltejs/vite-plugin-svelte": "^1.0.1",
"@tsconfig/svelte": "^3.0.0",
+ "@types/gtag.js": "^0.0.12",
"@types/proper-url-join": "^2.1.1",
"@types/react-syntax-highlighter": "^15.5.4",
"@typescript-eslint/eslint-plugin": "^5.33.1",
@@ -100,6 +101,10 @@
"./actions/cssVariables": "./dist/actions/cssVariables/index.js",
"./actions/resizeObserver": "./dist/actions/resizeObserver/index.js",
"./components/@types/global.ts": "./dist/components/@types/global.ts",
+ "./components/Analytics/Analytics.svelte": "./dist/components/Analytics/Analytics.svelte",
+ "./components/Analytics/providers/chartbeat.ts": "./dist/components/Analytics/providers/chartbeat.ts",
+ "./components/Analytics/providers/ga.ts": "./dist/components/Analytics/providers/ga.ts",
+ "./components/Analytics/providers/index.ts": "./dist/components/Analytics/providers/index.ts",
"./components/Article/Article.svelte": "./dist/components/Article/Article.svelte",
"./components/BeforeAfter/BeforeAfter.svelte": "./dist/components/BeforeAfter/BeforeAfter.svelte",
"./components/Block/Block.svelte": "./dist/components/Block/Block.svelte",
@@ -131,10 +136,6 @@
"./components/ReutersGraphicsLogo/ReutersGraphicsLogo.svelte": "./dist/components/ReutersGraphicsLogo/ReutersGraphicsLogo.svelte",
"./components/ReutersLogo/ReutersLogo.svelte": "./dist/components/ReutersLogo/ReutersLogo.svelte",
"./components/SEO/SEO.svelte": "./dist/components/SEO/SEO.svelte",
- "./components/SEO/analytics/chartbeat": "./dist/components/SEO/analytics/chartbeat.js",
- "./components/SEO/analytics/ga": "./dist/components/SEO/analytics/ga.js",
- "./components/SEO/analytics": "./dist/components/SEO/analytics/index.js",
- "./components/SEO/analytics/publisherTags": "./dist/components/SEO/analytics/publisherTags.js",
"./components/Scroller/Background.svelte": "./dist/components/Scroller/Background.svelte",
"./components/Scroller/Embedded/Background.svelte": "./dist/components/Scroller/Embedded/Background.svelte",
"./components/Scroller/Embedded/Foreground.svelte": "./dist/components/Scroller/Embedded/Foreground.svelte",
@@ -206,6 +207,7 @@
"./components/Video/Controls.svelte": "./dist/components/Video/Controls.svelte",
"./components/Video/Video.svelte": "./dist/components/Video/Video.svelte",
"./components/Visible/Visible.svelte": "./dist/components/Visible/Visible.svelte",
+ "./globals.d.ts": "./dist/globals.d.ts",
"./scss/mixins": "./dist/scss/_mixins.scss",
"./scss/variables": "./dist/scss/_variables.scss",
"./scss/bootstrap/main": "./dist/scss/bootstrap/_main.scss",
diff --git a/src/components/Analytics/Analytics.stories.svelte b/src/components/Analytics/Analytics.stories.svelte
new file mode 100644
index 00000000..c3263416
--- /dev/null
+++ b/src/components/Analytics/Analytics.stories.svelte
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+ Nothing to see here
+
+
+
+
+
+
+ Nothing to see here
+
+
+
+
+ Nothing to see here
+
diff --git a/src/components/Analytics/Analytics.svelte b/src/components/Analytics/Analytics.svelte
new file mode 100644
index 00000000..3d8512f0
--- /dev/null
+++ b/src/components/Analytics/Analytics.svelte
@@ -0,0 +1,29 @@
+
+
+
+
diff --git a/src/components/Analytics/providers/chartbeat.ts b/src/components/Analytics/providers/chartbeat.ts
new file mode 100644
index 00000000..4d88c65c
--- /dev/null
+++ b/src/components/Analytics/providers/chartbeat.ts
@@ -0,0 +1,41 @@
+// Reuters Chartbeat UID
+const UID = 52639;
+
+const URL = '//static.chartbeat.com/js/chartbeat.js';
+
+const attachScript = () => {
+ // If script is already attached, skip
+ if (document.querySelector(`script[src="${URL}"]`)) return;
+ // ... else attach it.
+ const e = document.createElement('script');
+ const n = document.getElementsByTagName('script')[0];
+ e.type = 'text/javascript';
+ e.async = true;
+ e.src = URL;
+ n.parentNode.insertBefore(e, n);
+};
+
+export default (authors: { name: string }[]) => {
+ window._sf_async_config = {
+ uid: UID,
+ domain: 'reuters.com',
+ flickerControl: false,
+ useCanonical: true,
+ useCanonicalDomain: true,
+ sections: 'Graphics',
+ authors: authors.map((a) => a?.name || '').join(','),
+ ...(window._sf_async_config || {}),
+ };
+
+ try {
+ attachScript();
+ } catch (e) { console.warn(`Error initialising Chartbeat Analytics: ${e}`); }
+};
+
+export const registerPageview = () => {
+ if (typeof window === 'undefined' || !window.pSUPERFLY) return;
+ window.pSUPERFLY({
+ path: window.location.pathname,
+ title: document?.title,
+ });
+};
diff --git a/src/components/Analytics/providers/ga.ts b/src/components/Analytics/providers/ga.ts
new file mode 100644
index 00000000..cdfbd24f
--- /dev/null
+++ b/src/components/Analytics/providers/ga.ts
@@ -0,0 +1,43 @@
+// Reuters Google Tag ID
+const GOOGLE_TAG_ID = 'G-W3Q2X6NTNM';
+
+const URL = `https://www.googletagmanager.com/gtag/js?id=${GOOGLE_TAG_ID}`;
+
+const attachScript = () => {
+ // If script is already attached, skip
+ if (document.querySelector(`script[src="${URL}"]`)) return;
+ // ... else attach it.
+ const e = document.createElement('script');
+ const n = document.getElementsByTagName('script')[0];
+ e.type = 'text/javascript';
+ e.async = true;
+ e.src = URL;
+ n.parentNode.insertBefore(e, n);
+};
+
+export default () => {
+ try {
+ window.dataLayer = window.dataLayer || [];
+ if (!window.gtag) {
+ attachScript();
+ /** @type {Gtag.Gtag} */
+ window.gtag = (...args) => {
+ window.dataLayer.push(...args);
+ };
+ window.gtag('js', new Date());
+ // config event registers a pageview by default
+ window.gtag('config', GOOGLE_TAG_ID, {
+ send_page_view: false,
+ });
+ registerPageview();
+ }
+ } catch (e) { console.warn(`Error initialising Google Analytics: ${e}`); }
+};
+
+export const registerPageview = () => {
+ if (typeof window === 'undefined' || !window.gtag) return;
+ window.gtag('event', 'page_view', {
+ page_location: window.location.origin + window.location.pathname,
+ page_title: document?.title,
+ });
+};
diff --git a/src/components/Analytics/providers/index.ts b/src/components/Analytics/providers/index.ts
new file mode 100644
index 00000000..9b2fdba9
--- /dev/null
+++ b/src/components/Analytics/providers/index.ts
@@ -0,0 +1,2 @@
+export { default as ga } from './ga';
+export { default as chartbeat } from './chartbeat';
diff --git a/src/components/Analytics/stories/docs/component.md b/src/components/Analytics/stories/docs/component.md
new file mode 100644
index 00000000..1ddd7b8e
--- /dev/null
+++ b/src/components/Analytics/stories/docs/component.md
@@ -0,0 +1,11 @@
+Add Google and Chartbeat analytics to your page.
+
+```svelte
+
+
+
+```
diff --git a/src/components/Analytics/stories/docs/environments.md b/src/components/Analytics/stories/docs/environments.md
new file mode 100644
index 00000000..74089219
--- /dev/null
+++ b/src/components/Analytics/stories/docs/environments.md
@@ -0,0 +1,17 @@
+Generally, you only want to send page analytics in production environments.
+
+In a SvelteKit context, you can use `$app` stores to restrict when you send analytics.
+
+For example, the following excludes analytics from pages in development or hosted on our preview server:
+
+```svelte
+
+
+{#if !dev && $page.url?.hostname !== 'graphics.thomsonreuters.com'}
+
+{/if}
+```
diff --git a/src/components/Analytics/stories/docs/multipage.md b/src/components/Analytics/stories/docs/multipage.md
new file mode 100644
index 00000000..b8336dda
--- /dev/null
+++ b/src/components/Analytics/stories/docs/multipage.md
@@ -0,0 +1,32 @@
+If you're using analytics to measure a multipage newsapp that uses [client-side routing](https://kit.svelte.dev/docs/glossary#routing), then you may need to trigger analytics after virtual page navigation.
+
+This component also exports a function you can call to register pageviews.
+
+For example, here's how you can use SvelteKit's [`afterNavigate`](https://kit.svelte.dev/docs/modules#$app-navigation-afternavigate) lifecycle to capture additional pageviews:
+
+```svelte
+
+
+
+```
diff --git a/src/components/SEO/SEO.svelte b/src/components/SEO/SEO.svelte
index 9c9e49a5..4b4a0720 100644
--- a/src/components/SEO/SEO.svelte
+++ b/src/components/SEO/SEO.svelte
@@ -1,15 +1,9 @@