Skip to main content

Command Palette

Search for a command to run...

Fixing the Invisible Spinner: How I Made My First Open Source Contribution to Ghost

When a tiny invisible spinner taught me the real power of open source and attention to detail.

Updated
4 min read
Fixing the Invisible Spinner: How I Made My First Open Source Contribution to Ghost

Introduction

When you think about open source, you might imagine developers fixing huge bugs, adding features, or diving into complex backend systems.

But my first contribution to Ghost, an open-source publishing platform, was not about any of that.

It was about a spinner, a tiny loading indicator that almost no one noticed... because it was invisible.

That small bug became my gateway into open source, teaching me about debugging, design systems, accessibility, and collaboration.

How It Started

While exploring Ghost’s GitHub issues, I came across Issue #24706:

“Spinner color contrast issue in Share Profile.”

It did not sound glamorous, just a UI detail, but something about it caught my attention.
If users cannot see feedback while an action is happening, the experience feels broken.

So I decided, this is the issue I’ll fix.

Setting Up My Environment

Before touching any code, I had to set up Ghost locally, a challenge in itself.

It involved:

  • Forking and cloning the repo

  • Installing dependencies using yarn install

  • Setting up MySQL, Redis, and Caddy (for ActivityPub)

  • Enabling ActivityPub in Ghost’s configuration

It took patience, multiple retries, and a lot of reading through Ghost’s contributor docs. But once I saw Ghost running locally, I knew I was ready.

Reproducing the Bug

Inside Ghost’s admin panel, under:
Network → Preferences → Share your profile,
Users can generate shareable profile images with three backgrounds:

  • Light mode

  • Dark mode

  • Accent colour mode

When I clicked “Copy Image”, I noticed the issue immediately:
The spinner appeared... but it was barely visible, sometimes completely invisible.

ModeButton BackgroundSpinner ColorVisibility
LightBlackBlack❌ Barely visible
DarkWhiteWhite❌ Invisible
AccentBlackBlack❌ Barely visible

A tiny problem, but one that breaks the feedback loop for users.

Tracing the Root Cause

The issue came from a simple logic flaw in Profile.tsx:

<LoadingIndicator 
    color={`${backgroundColor === 'dark' ? 'dark' : 'light'}`} 
    size='sm' 
/>

It based the spinner colour on the page background, not the button it was sitting on.

Result: perfect logic, wrong context.

The Fix

I wrote a small helper function that picked the spinner colour based on the actual button colour:

const getSpinnerColorForButton = (backgroundColor: 'light' | 'dark' | 'accent') => {
    switch (backgroundColor) {
        case 'light': return 'light'; // White spinner on dark button
        case 'dark': return 'dark';  // Black spinner on white button
        case 'accent': return 'light'; // White spinner on dark button
        default: return 'dark';
    }
};

Then replaced the inline ternary with:

<LoadingIndicator color={getSpinnerColorForButton(backgroundColor)} size='sm' />

A two-line change, but now, the spinner adapted perfectly in all modes.

Testing the Solution

After rebuilding Ghost locally, I tested all three modes again.

ModeExpectedResult
LightWhite spinner✅ Visible
DarkBlack spinner✅ Visible
AccentWhite spinner✅ Visible

Success 🎉

What once was an invisible spinner now gave clear feedback across every theme.

Submitting My Pull Request

Following Ghost’s commit guidelines, I wrote this commit message:

Fixed spinner visibility in Share Profile copy button

closes https://github.com/TryGhost/Ghost/issues/24706

Added getSpinnerColorForButton() helper function to ensure optimal contrast 
between spinner and button backgrounds, improving UX in ActivityPub Share Profile.

Created a branch, pushed changes, and submitted my first ever PR to an open-source project.

Why This Small Fix Mattered

This was not just a cosmetic change.
It improved accessibility, user trust, and consistency subtly, but meaningfully.

It taught me that:

  1. Even the smallest UI bug affects user confidence.

  2. Simplicity is powerful when done with intention.

  3. Contributing isn’t about complexity, it’s about care.

Lessons I Learned

  • Observe before coding — clarity saves hours.

  • Context matters — a “dark” mode doesn’t mean everything is dark.

  • Readable code is lasting code — functions tell stories too.

  • Follow project conventions — every repo has its rhythm.

  • Small steps create big confidence.

The Real Impact

This one-line fix made Ghost’s ActivityPub experience smoother for every user.
But more importantly, it made me realise something:

Open source isn’t just about contributing code; it’s about contributing care.

That small act of improving a spinner made me part of a community that values every detail.

Final Thoughts

If you’re new to open source, start small.
Find a bug you understand.
Fix it. Document it. Share it.

You’ll learn, grow, and feel the same joy I did seeing your name appear in the contributors list of a project you love.

Resources

Have you made your first open source contribution yet? Share your story in the comments — I’d love to hear it!