Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions .github/workflows/terraform.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: terraform (cloudflare)

# DNS/redirects change rarely, so apply is manual (a button). PRs that touch
# terraform/ get a plan for review.
on:
pull_request:
paths: ["terraform/**"]
workflow_dispatch:
inputs:
action:
description: "plan or apply"
type: choice
options: [plan, apply]
default: plan

permissions:
contents: read

concurrency:
group: terraform-cloudflare
cancel-in-progress: false

jobs:
terraform:
runs-on: ubuntu-latest
defaults:
run:
working-directory: terraform
env:
# Cloudflare (your repo secrets)
TF_VAR_cloudflare_api_token: ${{ secrets.CF_API }}
TF_VAR_account_id: ${{ secrets.CF_ACCOUNT }}
# Terraform Cloud holds the state (free tier). Needs a TF_API_TOKEN secret
# and a TF_CLOUD_ORG variable; see terraform/README.md.
TF_TOKEN_app_terraform_io: ${{ secrets.TF_API_TOKEN }}
steps:
- uses: actions/checkout@v4

- name: Enable remote state (Terraform Cloud)
run: sed "s/__TF_CLOUD_ORG__/${{ vars.TF_CLOUD_ORG }}/" backend.tf.example > backend.tf

- uses: hashicorp/setup-terraform@v3

- run: terraform init -input=false
- run: terraform fmt -check
- run: terraform validate

- name: Plan
if: ${{ github.event_name == 'pull_request' || inputs.action == 'plan' }}
run: terraform plan -input=false

- name: Apply
if: ${{ github.event_name == 'workflow_dispatch' && inputs.action == 'apply' }}
run: terraform apply -input=false -auto-approve
24 changes: 22 additions & 2 deletions assets/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,25 @@
/* ---------- three.js hero ---------- */
var hero = { renderer: null, raf: null, running: false };

// A soft circular sprite so the particles render as round dots, not squares.
var _sprite = null;
function makeRoundSprite() {
if (_sprite) return _sprite;
var c = document.createElement("canvas");
c.width = c.height = 64;
var g = c.getContext("2d");
var grd = g.createRadialGradient(32, 32, 0, 32, 32, 32);
grd.addColorStop(0, "rgba(255,255,255,1)");
grd.addColorStop(0.45, "rgba(255,255,255,0.85)");
grd.addColorStop(1, "rgba(255,255,255,0)");
g.fillStyle = grd;
g.beginPath();
g.arc(32, 32, 32, 0, Math.PI * 2);
g.fill();
_sprite = new THREE.CanvasTexture(c);
return _sprite;
}

function startHero() {
var canvas = document.getElementById("hero-canvas");
if (!canvas || typeof THREE === "undefined" || hero.running) return;
Expand Down Expand Up @@ -90,8 +109,9 @@
geo.setAttribute("position", new THREE.BufferAttribute(positions, 3));
geo.setAttribute("color", new THREE.BufferAttribute(colors, 3));
var mat = new THREE.PointsMaterial({
size: 0.16, vertexColors: true, transparent: true, opacity: 0.85,
depthWrite: false, blending: THREE.AdditiveBlending
size: 0.22, vertexColors: true, transparent: true, opacity: 0.9,
depthWrite: false, blending: THREE.AdditiveBlending,
map: makeRoundSprite(), alphaTest: 0.02
});
var points = new THREE.Points(geo, mat);
scene.add(points);
Expand Down
85 changes: 76 additions & 9 deletions assets/scss/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@
--accent: #ffd166; /* neurodiversity gold — focus + highlights */
--accent-ink: #1a1430; /* text on accent */
--shadow: 0 14px 40px rgba(0, 0, 0, 0.45);
--coffee: #d8a47f; /* warm latte — the "and Coffee" nod */
--pride: linear-gradient(90deg, #e63946, #f4a261, #2a9d8f, #457b9d, #9d4edd);
/* Brighter pride stops so every word stays legible on the dark background. */
--hero-gradient: linear-gradient(90deg, #ff8fa3, #ffd166, #6ee7d2, #8ecae6, #d0b3ff);
color-scheme: dark;
}

Expand All @@ -33,6 +36,9 @@
--accent: #b8860b; /* darker gold for contrast on light */
--accent-ink: #1c1633; /* dark text on gold — AA on light theme */
--shadow: 0 14px 40px rgba(40, 30, 80, 0.14);
--coffee: #8a4b2f; /* deeper roast for contrast on cream */
/* Deeper, saturated stops that stay readable on the cream background. */
--hero-gradient: linear-gradient(90deg, #d62839, #c25e00, #1a7f6b, #2a6f97, #7b2fd4);
color-scheme: light;
}

Expand Down Expand Up @@ -175,12 +181,15 @@ img { max-width: 100%; height: auto; display: block; }
.hero__title {
font-size: clamp(2.1rem, calc(1.3rem + 3.6vw), 4rem);
margin: 0 0 1.2rem;
background: var(--pride);
-webkit-background-clip: text; background-clip: text; color: transparent;
background: var(--hero-gradient);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
color: var(--text); /* fallback colour if clip is unsupported */
}
/* Keep a solid fallback so the headline is always legible if clip is unsupported. */
@supports not (background-clip: text) {
.hero__title { color: var(--text); background: none; }
/* If neither prefixed nor unprefixed clip is supported, show solid readable text. */
@supports not ((-webkit-background-clip: text) or (background-clip: text)) {
.hero__title { -webkit-text-fill-color: var(--text); background: none; }
}
.hero__lede { font-size: 1.15rem; color: var(--text-muted); margin: 0 auto 2rem; max-width: 40rem; }
.hero__actions { display: flex; gap: 0.8rem; justify-content: center; flex-wrap: wrap; }
Expand Down Expand Up @@ -217,6 +226,43 @@ img { max-width: 100%; height: auto; display: block; }
.focus-card__title { margin: 0 0 0.5rem; font-size: 1.25rem; }
.focus-card p { margin: 0; color: var(--text-muted); }

/* =========================================================================
Two-flavor split (home) + landing pages (/dev, /audhd)
========================================================================= */
.flavors { max-width: $maxw; margin-inline: auto; padding: 3.5rem 1.25rem 1rem; }
.flavors__grid {
display: grid; gap: 1.4rem;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}
.flavor-card {
display: flex; flex-direction: column; gap: 0.6rem;
background: var(--surface); border: 1px solid var(--border);
border-radius: $radius; padding: 1.8rem; box-shadow: var(--shadow);
border-top: 4px solid var(--border);
}
.flavor-card--dev { border-top-color: var(--coffee); }
.flavor-card--audhd { border-top-color: var(--link); }
.flavor-card__kicker {
margin: 0; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
font-size: 0.78rem; color: var(--text-muted);
}
.flavor-card__title { margin: 0; font-size: 1.5rem; }
.flavor-card__lede { margin: 0; color: var(--text-muted); }
.flavor-card__list { margin: 0.2rem 0 1rem; padding-left: 1.1rem; color: var(--text-muted); }
.flavor-card__list li { margin: 0.15rem 0; }
.flavor-card .btn { align-self: flex-start; margin-top: auto; }

.landing__head { max-width: $readw; margin-bottom: 1rem; }
.landing__kicker {
margin: 0 0 0.5rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
font-size: 0.82rem; color: var(--text-muted);
}
.landing__title { font-size: clamp(1.9rem, calc(1.3rem + 2.4vw), 3rem); margin: 0 0 0.8rem; }
.landing__lede { font-size: 1.2rem; color: var(--text-muted); margin: 0 0 1.4rem; }
.landing__cta { display: flex; gap: 0.7rem; flex-wrap: wrap; margin: 0 0 1rem; }
.landing--dev .landing__title { border-left: 5px solid var(--coffee); padding-left: 0.7rem; }
.landing--audhd .landing__title { border-left: 5px solid var(--link); padding-left: 0.7rem; }

/* =========================================================================
Post cards / grid
========================================================================= */
Expand Down Expand Up @@ -311,26 +357,47 @@ img { max-width: 100%; height: auto; display: block; }
.page-head__desc { color: var(--text-muted); margin: 0; }

.notfound { text-align: center; }
.notfound__code { font-size: clamp(4rem, 10vw, 8rem); margin: 0; background: var(--pride); -webkit-background-clip: text; background-clip: text; color: transparent; }
.notfound__code { font-size: clamp(4rem, 10vw, 8rem); margin: 0; background: var(--hero-gradient); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; color: var(--text); }
.notfound__msg { color: var(--text-muted); font-size: 1.2rem; margin: 0 0 1.5rem; }

/* =========================================================================
Footer
========================================================================= */
.site-footer { border-top: 1px solid var(--border); background: var(--bg-elev); margin-top: 3rem; }
.site-footer__inner { max-width: $maxw; margin-inline: auto; padding: 2.5rem 1.25rem; display: grid; gap: 1.2rem; }
.site-footer__title { font-family: $font-display; font-size: 1.3rem; margin: 0; }
.site-footer__title { font-family: $font-display; font-size: 1.3rem; margin: 0; display: inline-flex; align-items: center; gap: 0.5rem; }
.site-footer__coffee { color: var(--coffee); display: inline-flex; }
.site-footer__bio { color: var(--text-muted); margin: 0.4rem 0 0; max-width: 40rem; }
.social__list { list-style: none; display: flex; gap: 0.5rem; padding: 0; margin: 0; flex-wrap: wrap; }
.social__link {
width: 2.6rem; height: 2.6rem; border-radius: 50%;
border: 1px solid var(--border); background: var(--surface);
display: inline-flex; align-items: center; justify-content: center;
color: var(--text); font-size: 1.1rem;
color: var(--text); transition: color 0.2s $ease, border-color 0.2s $ease, transform 0.2s $ease;
}
.social__link:hover { color: var(--link); border-color: var(--link); }
.social__link:hover { color: var(--link); border-color: var(--link); transform: translateY(-2px); }
.social__icon { width: 1.2rem; height: 1.2rem; display: block; }
.site-footer__copyright { color: var(--text-muted); font-size: 0.9rem; margin: 0; }
.site-footer__copyright a { color: var(--link); }
.site-footer__brewed { color: var(--text-muted); font-size: 0.82rem; margin: 0; opacity: 0.85; font-style: italic; }

/* =========================================================================
Coffee cup (the "and Coffee" motif) — header brand + footer
========================================================================= */
.coffee-cup { display: block; }
.coffee-cup__steam { opacity: 0.75; }
.brand__coffee { color: var(--coffee); display: inline-flex; }
.brand__coffee .coffee-cup { width: 22px; height: 22px; }
@media (prefers-reduced-motion: no-preference) {
[data-motion="on"] .coffee-cup__steam {
animation: coffee-steam 3.2s ease-in-out infinite;
transform-origin: center;
}
}
@keyframes coffee-steam {
0%, 100% { opacity: 0.35; transform: translateY(0); }
50% { opacity: 0.85; transform: translateY(-1px); }
}

/* =========================================================================
Author aside (siteinfo)
Expand Down
28 changes: 14 additions & 14 deletions config.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
######################## default configuration ####################
baseURL: /
baseURL: https://kellyand.coffee/
title: Kelly and Coffee
locale: en-US
defaultContentLanguage: en
Expand All @@ -15,18 +15,18 @@ removePathAccents: true # Because I may type accents
# sectionPagesMenu: "main"
menu:
main:
- name: Posts
url: /posts
- name: Tech
url: /dev/
weight: 1
- name: About
url: /about
- name: Advocacy
url: /audhd/
weight: 2
- name: Tags
url: /tags
- name: Blog
url: /posts/
weight: 3
- name: About
url: /about/
weight: 4
- name: Resume
url: /resume
weight: 5

#################### Taxonomies;grouping ####################
# Must include default tags and categories
Expand All @@ -43,15 +43,15 @@ params:
Notes from a neurodivergent, queer DevOps engineer on tech, accessibility, and living as your whole self.
subtitle: Neurodivergent · LGBTQIA+ · Tech advocate
author: Kelly Crabbé
heroTitle: Building a kinder, more accessible corner of tech.
heroTitle: Automation by day. Advocacy out loud.
heroLede: >-
I'm Kelly — a DevOps engineer, advocate, and writer. I share what I'm learning about living and working as a neurodivergent, queer person in technology, and how we can build things that include everyone.
I'm Kelly — a DevOps & automation engineer who also writes and speaks about being AuDHD and queer in tech. Two sides of the same person. Pick a door.
avatar: true
bio: >-
I am a geeky, happily married, neurodivergent and queer woman who works in DevOps after a 15-year affair with system administration. I write about tech, accessibility, and showing up as my whole self — alongside my wife, our plus-daughter, and a very particular English Cocker Spaniel named Lou.
googleAnalitycs: G-WYTPMHCV3B
mobile: +32479695560
email: kelly.crabbe@gmail.com
email: kelly@kellyand.coffee
location: "Neerlinter,België"
copyright: >-
| copyright © 2022 [Kelly Crabbé](https://kellyand.coffee)
Expand All @@ -67,7 +67,7 @@ params:
- title: twitter
link: "https://twitter.com/kaymoonwitch"
- title: instagram
link: "https://https://instagram.com/kaymoonwitch"
link: "https://instagram.com/kaymoonwitch"
- title: github
link: "https://github.com/moonwitch"
- title: linkedin
Expand Down
28 changes: 28 additions & 0 deletions content/audhd.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
title: "Neurodiversity & Advocacy"
description: "Writing and public speaking on being AuDHD and queer in tech — accessibility, masking, and showing up as your whole self."
layout: landing
flavor: audhd
kicker: "Neurodivergent · LGBTQIA+ · in tech"
cta:
text: "Invite me to speak"
url: "mailto:kelly@kellyand.coffee?subject=Speaking%20invitation"
cta2:
text: "Read the blog"
url: "/posts/"
---

I'm autistic and ADHD — AuDHD — and queer, and I work in tech. For a long time I kept those parts of myself folded away at work. I don't anymore, and I've started writing and speaking about it, because the version of me who needed to hear "you're not broken, you're just wired differently" didn't have anyone saying it.

## What I talk about

- **Being AuDHD in a technical career** — the real accommodations, executive function, and the cost of masking — not the inspirational-poster version.
- **Accessibility & inclusive defaults** — why building for the edges makes things better for everyone, and how teams can do it without a big budget.
- **Showing up whole** — being openly queer *and* neurodivergent at work, and what managers can do to make that safe.
- **The honest middle of a neurodivergent career** — the wins, the wall, and the bits nobody puts on LinkedIn.

## Bring me to your stage

Meetup, conference track, panel, internal lunch-and-learn — if you want someone to talk plainly and warmly about neurodivergence and queerness in tech, I'd love to. I'm not here to be the polished expert with everything figured out; I'm here as someone a few steps down the same road, turning around to say *it's walkable, and here's where the potholes are.*

I write about all of this on [the blog](/posts/), too. You bring the audience — I'll bring the honesty (and, let's be real, the coffee).
27 changes: 27 additions & 0 deletions content/dev.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
title: "Kelly & Coffee — Tech & Cloud"
description: "You bring the coffee, I'll bring the automation. DevOps, cloud engineering, and low-code workflows that get your platform running."
layout: landing
flavor: dev
kicker: "Automation · Cloud · DevOps"
cta:
text: "Book a chat"
url: "mailto:kelly@kellyand.coffee?subject=Kelly%20%26%20Coffee%20%E2%80%94%20let's%20talk"
cta2:
text: "GitHub"
url: "https://github.com/moonwitch"
---

I'm a DevOps and automation engineer. I like taking the fiddly, repetitive, error-prone parts of running software off people's plates so they can get back to the work that actually needs a human. The unglamorous plumbing — pipelines, infrastructure, the workflow that quietly runs at 3am so nobody has to — is genuinely my favourite thing to build.

## What I do

- **CI/CD & DevOps** — build, test, and deployment pipelines that are boring in the best way: predictable, repeatable, and hard to break.
- **Cloud engineering on GCP** — infrastructure as code, sensible defaults, and platforms that scale without drama.
- **Low-code & n8n automation** — wiring your tools together so the busywork happens on its own. If you're doing the same thing by hand twice a week, it probably shouldn't be you doing it.

## How I work

Plainly, and without jargon for its own sake. I'd rather explain a thing clearly than sound clever about it. I document as I go, I automate the boring bits, and I leave systems in a state the next person (often future-me) can actually understand.

If you've got a platform that needs taming, a pile of manual steps that should be a workflow, or a cloud setup that's grown into a thicket — let's have a coffee about it.
Loading
Loading