content-script-developer
from shipshitdev/library
Claude, Cursor, Codex skills and commands
3 stars0 forksUpdated Jan 25, 2026
npx skills add https://github.com/shipshitdev/library --skill content-script-developerSKILL.md
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
requestAnimationFrameor debouncing. - Re-check anchors on navigation events.
4) Message and Store Data
- Use
chrome.runtime.sendMessagefor background/service worker calls. - Use
chrome.storagefor 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
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)
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
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
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.
Repository
shipshitdev/libraryParent repository
Repository Stats
Stars3
Forks0