redesign
This commit is contained in:
parent
f6577d5018
commit
4c98640a08
12 changed files with 232 additions and 285 deletions
|
|
@ -60,7 +60,11 @@ export class BlogController {
|
||||||
return createdBlogPost;
|
return createdBlogPost;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAllBlogPosts(): Promise<Array<BlogPostListItem | BookReviewListItem | SnoutStreetStudiosPostListItem>> {
|
async getAllBlogPosts(
|
||||||
|
pageSize?: number
|
||||||
|
): Promise<Array<BlogPostListItem | BookReviewListItem | SnoutStreetStudiosPostListItem>> {
|
||||||
|
console.log('getAllBlogPosts')
|
||||||
|
console.log({ pageSize });
|
||||||
const blogPosts = await this._markdownRepository.blogPosts;
|
const blogPosts = await this._markdownRepository.blogPosts;
|
||||||
|
|
||||||
const bookReviews = await this._markdownRepository.bookReviews;
|
const bookReviews = await this._markdownRepository.bookReviews;
|
||||||
|
|
@ -79,9 +83,16 @@ export class BlogController {
|
||||||
(post) => this.snoutStreetStudiosPostToSnoutStreetStudiosPostListItem(post)
|
(post) => this.snoutStreetStudiosPostToSnoutStreetStudiosPostListItem(post)
|
||||||
);
|
);
|
||||||
|
|
||||||
return [...blogPostListItems, ...bookReviewListItems, ...snoutStreetStudiosPostListItems].sort((a, b) =>
|
const allBlogPosts = [...blogPostListItems, ...bookReviewListItems, ...snoutStreetStudiosPostListItems].sort((a, b) =>
|
||||||
a.date > b.date ? -1 : 1
|
a.date > b.date ? -1 : 1
|
||||||
);
|
)
|
||||||
|
|
||||||
|
if (pageSize === undefined) {
|
||||||
|
console.log('returning all blog posts')
|
||||||
|
return allBlogPosts;
|
||||||
|
}
|
||||||
|
|
||||||
|
return allBlogPosts.slice(0, pageSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bookReviewToBookReviewListItem(bookReview: BookReview): BookReviewListItem {
|
private bookReviewToBookReviewListItem(bookReview: BookReview): BookReviewListItem {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,23 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { colourSchemeStore, lightColourScheme, darkColourScheme } from "../../stores/colourSchemeStore";
|
||||||
|
|
||||||
|
function onColourSchemeChange() {
|
||||||
|
if ($colourSchemeStore.name === 'dark') {
|
||||||
|
colourSchemeStore.set(lightColourScheme)
|
||||||
|
} else {
|
||||||
|
colourSchemeStore.set(darkColourScheme)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
<nav>
|
<nav>
|
||||||
<div class="left">
|
<div class="left">
|
||||||
<a href="/" class="home">Thomas Wilson</a>
|
<a href="/" class="home">Thomas Wilson</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<a href="/blog" class="blog">Blog</a>
|
<button class="colour-theme-toggle" on:click={onColourSchemeChange}>Toggle Colour Scheme</button>
|
||||||
|
<a href="/blog" class="blog">/blog</a>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
|
@ -15,6 +28,7 @@
|
||||||
max-width: 100vw;
|
max-width: 100vw;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
min-height: var(--navbar-height);
|
min-height: var(--navbar-height);
|
||||||
|
font-family: var(--font-family-mono);
|
||||||
}
|
}
|
||||||
|
|
||||||
.left {
|
.left {
|
||||||
|
|
@ -22,10 +36,12 @@
|
||||||
flex-grow: 0;
|
flex-grow: 0;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
padding: var(--spacing-base);
|
padding: var(--spacing-base);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.home {
|
.home {
|
||||||
color: var(--brand-orange);
|
color: var(--brand-orange);
|
||||||
|
font-family: inherit;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -42,17 +58,25 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
gap: 1rem;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
padding: var(--spacing-base);
|
padding: var(--spacing-base);
|
||||||
}
|
}
|
||||||
|
|
||||||
.blog {
|
.colour-theme-toggle {
|
||||||
font-size: 1.4rem;
|
display: flex;
|
||||||
padding: 0;
|
align-items: center;
|
||||||
color: var(--gray-1000);
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.blog {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.blog:visited {
|
.blog:visited {
|
||||||
color: var(--gray-1000);
|
color: var(--colour-scheme-text);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,36 @@
|
||||||
<script>
|
<script>
|
||||||
import "../styles/thomaswilson.css";
|
import "../styles/thomaswilson.css";
|
||||||
|
import { colourSchemeStore } from "../stores/colourSchemeStore";
|
||||||
|
import { browser } from "$app/environment";
|
||||||
|
|
||||||
|
colourSchemeStore.subscribe((value) => {
|
||||||
|
if (browser) {
|
||||||
|
document.documentElement.style.setProperty(
|
||||||
|
"--colour-scheme-background",
|
||||||
|
value.background
|
||||||
|
);
|
||||||
|
|
||||||
|
document.documentElement.style.setProperty(
|
||||||
|
"--colour-scheme-text",
|
||||||
|
value.text
|
||||||
|
);
|
||||||
|
|
||||||
|
document.documentElement.style.setProperty(
|
||||||
|
"--colour-scheme-background-accent",
|
||||||
|
value.backgroundAccent
|
||||||
|
);
|
||||||
|
|
||||||
|
document.documentElement.style.setProperty(
|
||||||
|
"--colour-scheme-text-accent",
|
||||||
|
value.textAccent
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>Thomas Wilson</title>
|
<title>Thomas Wilson</title>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
|
|
||||||
<slot />
|
<slot />
|
||||||
|
|
|
||||||
16
src/routes/+page.server.ts
Normal file
16
src/routes/+page.server.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { BlogController } from '../lib/blog/BlogController.js';
|
||||||
|
import type { PageServerLoad } from './$types';
|
||||||
|
|
||||||
|
export const load: PageServerLoad = async () => {
|
||||||
|
try {
|
||||||
|
const controller = await BlogController.singleton();
|
||||||
|
const latestBlogPosts = await controller.getAllBlogPosts(3);
|
||||||
|
return { latestBlogPosts }
|
||||||
|
} catch (error) {
|
||||||
|
console.error({
|
||||||
|
message: `Caught error in GET /api/blog.json`,
|
||||||
|
error: JSON.stringify(error),
|
||||||
|
});
|
||||||
|
return { latestBlogPosts: [] }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { slide } from "svelte/transition";
|
|
||||||
import Navbar from "$lib/components/Navbar.svelte";
|
import Navbar from "$lib/components/Navbar.svelte";
|
||||||
|
import HomepageHeader from "./home/HomepageHeader.svelte";
|
||||||
|
|
||||||
|
export let data = { latestBlogPosts: [] }
|
||||||
|
|
||||||
let isWorkExpanded = false;
|
|
||||||
let isPersonalExpanded = false;
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
|
|
@ -12,250 +12,6 @@
|
||||||
|
|
||||||
<Navbar />
|
<Navbar />
|
||||||
|
|
||||||
<main class="thomaswilson-container">
|
<main class="home">
|
||||||
<section class="section thomaswilson-strapline">
|
<HomepageHeader latestBlogPosts={data.latestBlogPosts} />
|
||||||
<h1 class="title">Thomas Wilson</h1>
|
</main>
|
||||||
<p>
|
|
||||||
<span class="strapline-animated" id="statement-1">I am a software engineer who loves the craft of building quality software, </span>
|
|
||||||
<span class="strapline-animated" id="statement-2">the messy-ness of working with people,</span>
|
|
||||||
<span class="strapline-animated" id="statement-3">and the balance between <i>minimal</i> and <i>complete</i> products.</span>
|
|
||||||
<span class="strapline-summary">I build good things with good people for good companies.</span>
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="work section">
|
|
||||||
<h2>My work</h2>
|
|
||||||
{#if isWorkExpanded}
|
|
||||||
<ul transition:slide="{{ duration: 220 }}">
|
|
||||||
<li>Right now I am a Senior Software Engineer at <a href="https://www.laka.co.uk" target="_blank" rel="noopener noreferrer">Laka</a>. We're building web tools to un<span class="censor">fuck</span> insurance</li>
|
|
||||||
<li>
|
|
||||||
I spent three years building and leading the Software Engienering team at<a
|
|
||||||
class="oxwash"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
href="https://www.oxwash.com">Oxwash</a
|
|
||||||
>, where we built tools for the sustainable and scalable future of laundry.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
I build full-stack software for the web, especially with TypeScript,
|
|
||||||
Node, Svelte, and React.
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li>
|
|
||||||
I like domain driven design, and agile (with a little 'a')
|
|
||||||
opinions about delivery and collaboration.
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li>
|
|
||||||
I've previously built software with data privacy, ed-tech, and
|
|
||||||
sustainability companies.
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li>
|
|
||||||
I have a <a
|
|
||||||
class="thesis"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
href="https://eprints.soton.ac.uk/418168/"
|
|
||||||
>Ph.D. in education technology</a
|
|
||||||
>, where I looked at how we can use tech in the teaching lab to
|
|
||||||
promote and evidence understanding.
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li>
|
|
||||||
I have only ever worked with companies actively solving environment
|
|
||||||
and societal problems.
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li>
|
|
||||||
<button on:click="{() => (isWorkExpanded = false)}">
|
|
||||||
Ew, show less
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
{:else}
|
|
||||||
<p>
|
|
||||||
I build software that makes complicated things more human-friendly, and
|
|
||||||
lead teams to do the same.
|
|
||||||
</p>
|
|
||||||
<button on:click="{() => (isWorkExpanded = true)}">
|
|
||||||
Wow, Show more
|
|
||||||
</button>
|
|
||||||
{/if}
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="section personal">
|
|
||||||
<h2>Me</h2>
|
|
||||||
{#if isPersonalExpanded}
|
|
||||||
<ul transition:slide="{{ duration: 210 }}">
|
|
||||||
<li>
|
|
||||||
I try to write at least once a month on my <a
|
|
||||||
class="thesis"
|
|
||||||
href="/blog">blog</a
|
|
||||||
>.
|
|
||||||
</li>
|
|
||||||
<li>I'm learning to sew my own clothes</li>
|
|
||||||
<li>I love fruity coffees and botanical gins</li>
|
|
||||||
<li>I used to dance and produce with a contemporary dance company</li>
|
|
||||||
<li>
|
|
||||||
<button on:click="{() => (isPersonalExpanded = false)}"
|
|
||||||
>Cool, show less</button
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
{:else}
|
|
||||||
<p>
|
|
||||||
I like to write on my <a href="/blog">blog</a> and ride bikes. I'm learning
|
|
||||||
to sew my own clothes, and my French is terrible.
|
|
||||||
</p>
|
|
||||||
<button on:click="{() => (isPersonalExpanded = true)}"
|
|
||||||
>Oh, cool, tell me more</button
|
|
||||||
>
|
|
||||||
{/if}
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="section links">
|
|
||||||
<h2>Links</h2>
|
|
||||||
<ul>
|
|
||||||
<li>I write here, <a href="/blog">on my blog</a></li>
|
|
||||||
<li>
|
|
||||||
And on <a href="https://www.github.com/thomaswilsonxyz">GitHub</a>
|
|
||||||
</li>
|
|
||||||
<li>I stopped checking Twitter when... you know.</li>
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
section {
|
|
||||||
--link-transition: 0.2s;
|
|
||||||
--link-padding: 4px;
|
|
||||||
--link-border-radius: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
section ul {
|
|
||||||
margin-top: 0;
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
section p {
|
|
||||||
padding-top: 0;
|
|
||||||
padding-bottom: var(--spacing-md);
|
|
||||||
font-size: 1.3rem;
|
|
||||||
color: var(--gray-700);
|
|
||||||
}
|
|
||||||
|
|
||||||
.thomaswilson-strapline > p {
|
|
||||||
padding-top: 4px;
|
|
||||||
padding-bottom: 1rem;
|
|
||||||
font-size: 1.65rem;
|
|
||||||
|
|
||||||
color: var(--gray-700);
|
|
||||||
}
|
|
||||||
|
|
||||||
.oxwash {
|
|
||||||
padding: var(--link-padding);
|
|
||||||
transition: var(--link-transition);
|
|
||||||
border-radius: var(--link-border-radius);
|
|
||||||
border: 1px solid #0256f2;
|
|
||||||
color: #0256f2;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.oxwash:hover {
|
|
||||||
background: #6fbbec;
|
|
||||||
border: 1px solid #6fbbec;
|
|
||||||
text-decoration: underline;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.thesis {
|
|
||||||
padding: var(--link-padding);
|
|
||||||
transition: var(--link-transition);
|
|
||||||
border-radius: var(--link-border-radius);
|
|
||||||
border: 1px solid var(--brand-orange);
|
|
||||||
color: var(--brand-orange);
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.thesis:hover {
|
|
||||||
background: var(--brand-orange);
|
|
||||||
color: white;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.links a {
|
|
||||||
color: var(--brand-orange);
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 2.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
font-family: var(--font-family-title);
|
|
||||||
color: var(--gray-800);
|
|
||||||
}
|
|
||||||
|
|
||||||
li {
|
|
||||||
padding-bottom: 0.3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Create a class called censor which places a black bar over the text*/
|
|
||||||
.censor {
|
|
||||||
position: relative;
|
|
||||||
color: transparent;
|
|
||||||
text-shadow: 0 0 3px rgba(0,0,0,0.7);
|
|
||||||
}
|
|
||||||
|
|
||||||
.censor::after {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: 55%;
|
|
||||||
width: 100%;
|
|
||||||
height: 8px;
|
|
||||||
background-color: black;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@keyframes statementAppear {
|
|
||||||
0% {
|
|
||||||
opacity: 0.1;
|
|
||||||
transform: translateY(-20px)
|
|
||||||
}
|
|
||||||
|
|
||||||
100% {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0px)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.strapline-summary {
|
|
||||||
font-weight: 400;
|
|
||||||
}
|
|
||||||
.strapline-animated {
|
|
||||||
animation-fill-mode: forwards;
|
|
||||||
opacity: 0.15;
|
|
||||||
transform: translateY(-10px)
|
|
||||||
}
|
|
||||||
|
|
||||||
#statement-1 {
|
|
||||||
animation-name: statementAppear;
|
|
||||||
animation-delay: 0.5s;
|
|
||||||
animation-duration: 1.5s;
|
|
||||||
}
|
|
||||||
|
|
||||||
#statement-2 {
|
|
||||||
animation-name: statementAppear;
|
|
||||||
animation-delay: 1.4s;
|
|
||||||
animation-duration: 1.5s;
|
|
||||||
}
|
|
||||||
|
|
||||||
#statement-3 {
|
|
||||||
animation-name: statementAppear;
|
|
||||||
animation-delay: 2.3s;
|
|
||||||
animation-duration: 1.5s;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
export const prerender = false;
|
|
||||||
|
|
@ -3,9 +3,7 @@ import { BlogController } from '../../../lib/blog/BlogController.js';
|
||||||
|
|
||||||
export const GET = async () => {
|
export const GET = async () => {
|
||||||
try {
|
try {
|
||||||
console.log(`GET /api/blog.json`);
|
|
||||||
const controller = await BlogController.singleton();
|
const controller = await BlogController.singleton();
|
||||||
console.log(`Controller instantiated.`);
|
|
||||||
const blogPosts = await controller.getAllBlogPosts();
|
const blogPosts = await controller.getAllBlogPosts();
|
||||||
return json({ posts: blogPosts });
|
return json({ posts: blogPosts });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
||||||
|
|
@ -97,10 +97,6 @@
|
||||||
grid-template-columns: 100%;
|
grid-template-columns: 100%;
|
||||||
gap: var(--spacing-base);
|
gap: var(--spacing-base);
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
|
|
||||||
@media screen and (min-width: 768px) {
|
|
||||||
grid-template-columns: repeat(2, 1fr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.days-since {
|
.days-since {
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,18 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { intlFormat } from "date-fns";
|
import { format as formatDate } from "date-fns";
|
||||||
|
|
||||||
export let index: number;
|
export let index: number;
|
||||||
export let numberOfPosts: number;
|
export let numberOfPosts: number;
|
||||||
export let book_review: boolean;
|
export let book_review: boolean;
|
||||||
export let title: string;
|
export let title: string;
|
||||||
export let preview: string;
|
export let preview: string;
|
||||||
export let slug: string;
|
export let slug: string
|
||||||
export let date: string;
|
export let date: string;
|
||||||
export let content_type: "blog" | "book_review" | "snout_street_studios";
|
export let content_type: "blog" | "book_review" | "snout_street_studios";
|
||||||
|
|
||||||
$: formattedDate = intlFormat(
|
$: formattedDate = formatDate(
|
||||||
new Date(date),
|
new Date(date),
|
||||||
{ day: "2-digit", month: "long", year: "numeric" },
|
'yyyy-mm-dd',
|
||||||
{ locale: "en-GB" }
|
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -58,10 +57,10 @@
|
||||||
|
|
||||||
.post:hover {
|
.post:hover {
|
||||||
color: var(--brand-orange);
|
color: var(--brand-orange);
|
||||||
background-color: white;
|
background-color: var(--colour-scheme-background-accent);
|
||||||
border: 1px solid var(--brand-orange);
|
border: 1px solid var(--brand-orange);
|
||||||
scale: 1.02;
|
scale: 1.02;
|
||||||
box-shadow: 10px 10px 10px 10px var(--gray-200);
|
box-shadow: 10px 10px 10px 10px rgba(0, 0, 0, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.post a {
|
.post a {
|
||||||
|
|
|
||||||
86
src/routes/home/HomepageHeader.svelte
Normal file
86
src/routes/home/HomepageHeader.svelte
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { format as formatDate } from "date-fns";
|
||||||
|
// TODO: move somewhere common
|
||||||
|
interface BlogPost {
|
||||||
|
title: string;
|
||||||
|
slug: string;
|
||||||
|
date: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export let latestBlogPosts: BlogPost[] = [];
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<section class="homepage-header">
|
||||||
|
<h1 class="title">(Thomas) Wilson</h1>
|
||||||
|
<p class="body">
|
||||||
|
I love the craft of well-built things: mostly software (~8 years), but also
|
||||||
|
clothes (~2 years).
|
||||||
|
</p>
|
||||||
|
<p class="body">
|
||||||
|
I work against systems which rely on exploitation, excessive waste, and
|
||||||
|
dishonesty. I'm trying to build a kinder and fairer world, and I find that
|
||||||
|
hard sometimes.
|
||||||
|
</p>
|
||||||
|
<p class="body">
|
||||||
|
I try to think and be curious, that's why I keep a <a href="/blog">weblog</a
|
||||||
|
>. And (equally embarrassing to say) probably what got me through a
|
||||||
|
<a
|
||||||
|
class="thesis"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
href="https://eprints.soton.ac.uk/418168/"
|
||||||
|
>Ph.D. in Education Technology</a
|
||||||
|
>.
|
||||||
|
</p>
|
||||||
|
<p class="body">Here are some things I've written recently:</p>
|
||||||
|
|
||||||
|
<ol>
|
||||||
|
{#each latestBlogPosts as post}
|
||||||
|
<li>
|
||||||
|
<a href="/blog/{post.slug}">{post.title}</a> ({formatDate(
|
||||||
|
new Date(post.date),
|
||||||
|
"yyyy-MM-dd"
|
||||||
|
)})
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<p class="body">
|
||||||
|
Right now I am a Senior Software engineer at <a
|
||||||
|
href="https://www.laka.co.uk"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer">Laka</a
|
||||||
|
>, building tools actually fairer insurance. Before that, I was Head of
|
||||||
|
Software Engineering at
|
||||||
|
<a href="https://www.oxwash.com" target="_blank" rel="noopener noreferrer"
|
||||||
|
>Oxwash</a
|
||||||
|
>, building tools for actually sustainable laundry.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.homepage-header {
|
||||||
|
font-family: monospace;
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
a,
|
||||||
|
ol,
|
||||||
|
li,
|
||||||
|
p {
|
||||||
|
color: var(--text-color);
|
||||||
|
font-family: inherit;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 60ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-weight: 400;
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.body {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
35
src/stores/colourSchemeStore.ts
Normal file
35
src/stores/colourSchemeStore.ts
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
import { writable } from "svelte/store";
|
||||||
|
|
||||||
|
type ColourSchemeName = 'light' | 'dark';
|
||||||
|
|
||||||
|
interface ColourScheme {
|
||||||
|
name: ColourSchemeName;
|
||||||
|
background: string;
|
||||||
|
backgroundAccent: string;
|
||||||
|
text: string;
|
||||||
|
textAccent: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const lightColourScheme: ColourScheme = {
|
||||||
|
name: 'light',
|
||||||
|
background: 'white',
|
||||||
|
backgroundAccent: '#f8f9fa',
|
||||||
|
text: '#212529',
|
||||||
|
textAccent: '#495057'
|
||||||
|
};
|
||||||
|
|
||||||
|
export const darkColourScheme: ColourScheme = {
|
||||||
|
name: 'dark',
|
||||||
|
background: '#212529',
|
||||||
|
backgroundAccent: '#343a40',
|
||||||
|
text: '#f8f9fa',
|
||||||
|
textAccent: '#ced4da'
|
||||||
|
};
|
||||||
|
|
||||||
|
export const colourSchemes: Record<ColourSchemeName, ColourScheme> = {
|
||||||
|
light: lightColourScheme,
|
||||||
|
dark: darkColourScheme
|
||||||
|
};
|
||||||
|
|
||||||
|
export const colourSchemeStore = writable<ColourScheme>(darkColourScheme);
|
||||||
|
|
||||||
|
|
@ -21,6 +21,7 @@
|
||||||
--gray-900: #212529;
|
--gray-900: #212529;
|
||||||
--gray-950: #1a1e23;
|
--gray-950: #1a1e23;
|
||||||
--gray-1000: #0a0c0e;
|
--gray-1000: #0a0c0e;
|
||||||
|
--font-family-mono: monospace;
|
||||||
--font-family-title: 'FivoSansModern-Regular', sans-serif;
|
--font-family-title: 'FivoSansModern-Regular', sans-serif;
|
||||||
--font-family-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans',
|
--font-family-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans',
|
||||||
sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
|
sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
|
||||||
|
|
@ -53,9 +54,12 @@
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: var(--font-family-sans);
|
font-family: var(--font-family-sans);
|
||||||
color: var(--gray-900);
|
|
||||||
line-height: var(--line-height-md);
|
line-height: var(--line-height-md);
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
|
background-color: var(--colour-scheme-background);
|
||||||
|
color: var(--colour-scheme-text);
|
||||||
|
transition: 0.3s ease;
|
||||||
|
transition-property: background-color, color;
|
||||||
}
|
}
|
||||||
|
|
||||||
.thomaswilson-container {
|
.thomaswilson-container {
|
||||||
|
|
@ -65,7 +69,6 @@ body {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
min-height: calc(100vh - var(--navbar-height) - calc(2 * var(--container-padding)));
|
min-height: calc(100vh - var(--navbar-height) - calc(2 * var(--container-padding)));
|
||||||
background-color: var(--gray-100);
|
|
||||||
padding: var(--container-padding);
|
padding: var(--container-padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -76,7 +79,6 @@ body {
|
||||||
font-size: 1.19rem;
|
font-size: 1.19rem;
|
||||||
line-height: var(--line-height-md);
|
line-height: var(--line-height-md);
|
||||||
padding-bottom: var(--spacing-base);
|
padding-bottom: var(--spacing-base);
|
||||||
color: var(--gray-700);
|
|
||||||
padding-bottom: 2rem;
|
padding-bottom: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -85,14 +87,13 @@ body {
|
||||||
font-size: var(--font-size-base);
|
font-size: var(--font-size-base);
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
color: var(--gray-800);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.thomaswilson-strapline p {
|
.thomaswilson-strapline p {
|
||||||
font-size: 1.6rem;
|
font-size: 1.4rem;
|
||||||
line-height: var(--line-height-md);
|
line-height: var(--line-height-md);
|
||||||
letter-spacing: -0.25px;
|
letter-spacing: -0.25px;
|
||||||
font-weight: 250;
|
font-weight: 200;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1,
|
h1,
|
||||||
|
|
@ -105,7 +106,7 @@ h6 {
|
||||||
padding-bottom: 0.25rem;
|
padding-bottom: 0.25rem;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
color: var(--gray-800);
|
color: var(--colour-scheme-text);
|
||||||
padding-top: 8px;
|
padding-top: 8px;
|
||||||
padding-bottom: 6px;
|
padding-bottom: 6px;
|
||||||
line-height: var(--line-height);
|
line-height: var(--line-height);
|
||||||
|
|
@ -116,9 +117,9 @@ li,
|
||||||
a {
|
a {
|
||||||
font-size: var(--font-size);
|
font-size: var(--font-size);
|
||||||
line-height: var(--line-height-lg);
|
line-height: var(--line-height-lg);
|
||||||
font-family: var(--font-family-sans);
|
font-family: var(--font-family-mono);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
color: var(--gray-800);
|
color: var(--colour-scheme-text);
|
||||||
padding: 8px 0;
|
padding: 8px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue