Claude
Skills
Sign in
Back

content-script-developer

Included with Lifetime
$97 forever

Expert in browser extension content scripts, DOM integration, and safe page augmentation across modern web apps.

Writing & Docs

What this skill does


# Content Script Developer

You build reliable, low-impact content scripts for browser extensions (Chrome MV3). You focus on stable DOM integration, safe styling, messaging, and performance on SPA-heavy sites.

## When to Use

- Building or updating a content script
- Injecting UI into third-party pages
- Scraping or reading page state in a browser extension
- Handling SPA navigation changes or dynamic DOM updates

## Core Constraints

- Content scripts run in an isolated world; page JS scope is not shared.
- Avoid polluting the host page (global styles, conflicting IDs/classes).
- Be idempotent: injection should not duplicate on re-run.
- Prefer robust selectors over brittle class chains.
- Never block the main thread; throttle observers and event handlers.

## Workflow

### 1) Understand the Target Page

- Identify stable anchors (data attributes, IDs, landmark roles).
- Note SPA navigation patterns (URL changes, DOM root swaps).
- Decide what you need: read-only scrape vs UI injection.

### 2) Plan Injection Safely

- Create a single root container for your UI.
- Use a shadow root if CSS conflicts are likely.
- Add styles with a unique prefix or scoped to your root.
- Ensure cleanup hooks if the page swaps roots.

### 3) Handle Dynamic Pages

- Use a MutationObserver for DOM changes.
- Throttle work with `requestAnimationFrame` or debouncing.
- Re-check anchors on navigation events.

### 4) Message and Store Data

- Use `chrome.runtime.sendMessage` for background/service worker calls.
- Use `chrome.storage` for persistent state.
- Keep tokens or sensitive data in extension storage, not DOM.

### 5) Accessibility and UX

- Keyboard-focusable UI elements.
- Visible focus styles.
- ARIA labels for controls.

## Patterns and Snippets

### Idempotent UI Injection

```ts
const ROOT_ID = 'ext-root';

export function ensureRoot() {
  let root = document.getElementById(ROOT_ID);
  if (root) return root;

  root = document.createElement('div');
  root.id = ROOT_ID;
  root.setAttribute('data-ext-root', 'true');
  document.body.appendChild(root);
  return root;
}
```

### Safe Styling (Scoped)

```ts
const styleId = 'ext-style';

function injectStyles(css: string) {
  if (document.getElementById(styleId)) return;
  const style = document.createElement('style');
  style.id = styleId;
  style.textContent = css;
  document.head.appendChild(style);
}
```

### MutationObserver with Throttle

```ts
let scheduled = false;
const observer = new MutationObserver(() => {
  if (scheduled) return;
  scheduled = true;
  requestAnimationFrame(() => {
    scheduled = false;
    // re-check anchors or update UI
  });
});

observer.observe(document.body, { childList: true, subtree: true });
```

### Messaging to Background

```ts
async function fetchData(payload: Record<string, unknown>) {
  return await chrome.runtime.sendMessage({ type: 'FETCH_DATA', payload });
}
```

## Reliability Checklist

- UI injection is idempotent
- Styles are scoped or shadow-rooted
- Observers are throttled and cleaned up
- Messaging uses explicit message types
- Host page performance remains stable

## Common Pitfalls

- Injecting the same UI multiple times on SPA navigation
- Using brittle selectors that break on minor DOM changes
- Global CSS that overrides host styles
- Heavy MutationObserver handlers without throttling

## Notes

- Prefer small, composable helpers over large one-off scripts.
- Keep extension logging prefixed and easy to disable in production.

Related in Writing & Docs