AS.
← Back to work

03 / 11

Browser Extension

GitHubDefectMarker

Make "what kind of bug is this?" a one-click decision, right inside the PR comment box.

ROLE
Full-stack developer, solo project
BUILT FOR
A personal/portfolio tool aimed at developers doing code review on GitHub
STACK
Vanilla JS, CSS and HTML — no framework, no build step, no dependencies
PLATFORM
Chrome extension, Manifest V3 (content script + toolbar popup, zero permissions)
GitHub Defect Marker — sign in

THE PROBLEM

Code review comments on GitHub are free text. "This is wrong" and "nit: rename this" get written in the same box, with no structured signal for how serious an issue is or what kind of defect it represents. That makes it hard to triage a review at a glance, hard to build tooling or metrics on top of review comments, and easy for reviewers to be inconsistent with each other about what counts as "Major" versus "Minor."

Teams that want this kind of classification usually solve it with a convention — "please prefix your comment with a severity label" — enforced by nothing but habit. Conventions like that decay the moment someone is in a hurry, reviewing from a phone, or new to the team.

THE IDEA

Put the classification UI where the habit actually has to happen: inside the comment editor itself, at the moment of writing. Make the two choices (severity, defect type) a pair of one-click pill groups rather than a markdown convention to remember, auto-build a machine-readable label (#Major:Missing) from those choices, and block submission until the label is present.

The extension does nothing else — no server, no account, no settings to configure.

WHAT IT DOES

01

Inline defect panel

A compact panel is injected into every comment editor on a PR page — inline review comments, the main conversation box, replies, and the review summary box — right between the textarea and its footer buttons. Ticking "Mark as Defect" expands it.

defect-marker.vercel.app
GitHub Defect Marker — Inline defect panel

02

One-click severity, type, and a live label preview

Severity (Major, Minor, Cosmetic) and Defect Type (Missing, Extra, Risk-prone, Ambiguous, Inconsistent, Improvement, Factually Incorrect) render as radio-pill groups instead of dropdowns. As pills are picked, a preview chip builds the label (#Major:… → #Major:Missing) and writes it straight into the comment. If the label gets deleted by accident, the chip turns into a one-click re-insert button instead of silently failing.

defect-marker.vercel.app
GitHub Defect Marker — One-click severity, type, and a live label preview

HOW IT WORKS

GitHub PR page (github.com/*)
        │
        ▼
content.js (content script, document_idle)
  ├─ scan()            → finds comment textareas via layered,
  │                       hash-resistant selectors
  ├─ MutationObserver   → catches lazily-rendered editors
  ├─ focusin listener   → fallback: attach on click-into
  └─ turbo:load/popstate → re-scan after GitHub's soft navigation
        │
        ▼
DefectController (one per open editor)
  ├─ owns: panel UI, label state, validation, submit gating
  ├─ writes label text via native HTMLTextAreaElement setter
  │   + dispatched input event (React-safe)
  └─ watches for editor clear/destroy → resets after submit

React fights naive DOM writes

GitHub's comment boxes are React-controlled components, so setting textarea.value directly is silently dropped by React's internal value tracker. The label engine instead writes through the native HTMLTextAreaElement.prototype.value setter and dispatches a real input event, which both the legacy and React-based editors honor.

Surviving GitHub's soft navigation

GitHub navigates between a repo page and a PR page via Turbo (soft) transitions, so a content script scoped to */pull/* would frequently never fire. The fix is to match all of github.com and gate on location.pathname in code, re-scanning on turbo:load and popstate.

One controller per editor

A PR page can have many open editors simultaneously (several inline comments plus the conversation box), so each gets its own DefectController owning its own panel, textarea reference and validation state — editors can't cross-contaminate, and controllers are pruned once GitHub removes their editor from the DOM.

Conservative submit gating

Only buttons that look like submit actions are disabled, and only the ones the extension itself disabled get re-enabled — so it never overrides GitHub's own button-state logic, like disabling on an empty comment.

CHALLENGES AND WHAT I LEARNED

GitHub's DOM is a moving target

The README documents a complete rewrite (v1.5.2 → v2.4.0), with the old version kept in archive/v1.5.2/ for reference — a sign the original selector/styling approach didn't hold up as GitHub's UI evolved. The current version leans on [class*="…"] prefix selectors specifically to survive GitHub's hashed CSS-module class names, and layers three discovery mechanisms (initial scan, debounced MutationObserver, focusin fallback) rather than trusting any single one.

Resisting scope creep

The README explicitly frames "no permissions, no storage, no background worker" as a design decision, not an oversight — a disciplined choice given how easy it would be to add chrome.storage for remembering defaults or a badge count.

Detecting "did the comment actually post?" without a dedicated API

Submission is a network round trip with no direct completion signal from the content script's vantage point, so the controller polls briefly for the editor being cleared or destroyed by GitHub, rather than relying on a fixed timer.

RESULTS

Ships as a Manifest V3 extension with a single content script and a purely informational popup — no background service worker.

Requests zero Chrome permissions; only a content_scripts match on https://github.com/*.

No network requests, no chrome.storage usage, no analytics — confirmed by both the manifest and PRIVACY.md.

No build step: plain JS/CSS/HTML, loaded directly via chrome://extensions → "Load unpacked."

AND ACROSS THE APP

GitHub Defect Marker — screen 1

STACK

JavaScriptManifest V3GitHub Primer CSSNext.js 15

LINKS

NEXT PROJECT

PR Analysis Dashboard

→

05 / CONTACT

Let'sbuild
somethinggreat.