Skip to content
Back to skills

Userscripts

ASecurity

Use when writing Tampermonkey/Violentmonkey/Greasemonkey userscripts, modifying website behavior or appearance, or working with .user.js files.

  • 87 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 5, 2026
toolsjavascriptgojavanodetestingdebugginggitapi

Works with

  • cli
  • api

Security analysis

A100/100

Pro scans all 3 files and shows the line behind each finding

Scanned September 22, 2026

npx -y skills add denolfe/dotfiles --skill userscripts --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Userscripts?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Userscripts
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/denolfe-userscripts/badge)](https://www.skillsdirectory.com/skills/denolfe-userscripts)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: userscripts
description: Use when writing Tampermonkey/Violentmonkey/Greasemonkey userscripts, modifying website behavior or appearance, or working with .user.js files.
---

# Userscript Development

Guide for writing Tampermonkey/Violentmonkey userscripts.

## Workflow

### Phase 1: Requirements
Ask one at a time:
1. **Target site** - URL pattern (e.g., `https://example.com/*`)
2. **Goal** - CSS styling, add feature, remove element, keyboard shortcut, etc.
3. **Output location** - Where to save the `.user.js` file

### Phase 2: DOM Inspection

**Option A: Fetch URL directly (try first for public sites)**
- Fetch the page HTML
- Search for relevant selectors, CSS variables, data attributes
- Works for public sites not behind login

**Option B: User provides MHTML/HTML file**
- If fetch fails (login required, dynamic content), instruct: "Save the page (Cmd+S / Ctrl+S) as MHTML or 'Webpage, Complete'"
- Search the HTML for patterns: `class=`, `data-`, `id=`, `--` (CSS variables)
- Prefer stable selectors: `data-testid`, `data-*`, semantic IDs over generated class names

**Option C: User provides selectors**
- Guide user to inspect element in browser devtools
- Ask for: element selector, parent structure, relevant CSS variables

### Phase 3: Implementation
1. Choose `@run-at` timing based on task type
2. Choose `@grant` level (start with `none`)
3. Write script using metadata template below
4. Save to user-specified location

### Phase 4: Testing
- Instruct: "Install in Tampermonkey/Violentmonkey and reload the page"
- If issues: iterate on selectors or timing

---

## Metadata Template

```javascript
// ==UserScript==
// @name            Script Name
// @namespace       your-namespace
// @version         0.0.1
// @description     Brief description
// @icon            https://www.google.com/s2/favicons?sz=64&domain=example.com
// @homepage        https://github.com/username/repo
// @match           https://example.com/*
// @exclude         https://example.com/admin/*
// @author          Author Name
// @run-at          document-end
// @noframes
// @grant           none
// ==/UserScript==
```

---

## Full Templates

### CSS Override (copy-paste ready)
```javascript
// ==UserScript==
// @name            Site Name - Custom Styles
// @namespace       your-namespace
// @version         0.0.1
// @description     Override styles on example.com
// @icon            https://www.google.com/s2/favicons?sz=64&domain=example.com
// @match           https://example.com/*
// @author          Your Name
// @run-at          document-start
// @grant           none
// ==/UserScript==

;(() => {
  'use strict'

  const style = document.createElement('style')
  style.textContent = `
    /* Hide element */
    .annoying-banner {
      display: none !important;
    }

    /* Override CSS variable */
    :root {
      --site-font: -apple-system, BlinkMacSystemFont, system-ui, sans-serif !important;
    }
  `
  document.documentElement.appendChild(style)
})()
```

### DOM Manipulation (copy-paste ready)
```javascript
// ==UserScript==
// @name            Site Name - DOM Tweaks
// @namespace       your-namespace
// @version         0.0.1
// @description     Modify DOM on example.com
// @icon            https://www.google.com/s2/favicons?sz=64&domain=example.com
// @match           https://example.com/*
// @author          Your Name
// @run-at          document-end
// @noframes
// @grant           none
// ==/UserScript==

;(() => {
  'use strict'

  // Remove elements
  document.querySelectorAll('.unwanted').forEach(el => el.remove())

  // Modify elements
  document.querySelectorAll('a.external').forEach(link => {
    link.setAttribute('target', '_blank')
  })

  // Add keyboard shortcut
  document.addEventListener('keydown', e => {
    if (e.target.matches('input, textarea, [contenteditable]')) return

    if (e.key === 'j' && !e.ctrlKey && !e.metaKey && !e.altKey) {
      e.preventDefault()
      // action here
    }
  })
})()
```

### Dynamic Content (copy-paste ready)
```javascript
// ==UserScript==
// @name            Site Name - Dynamic Handler
// @namespace       your-namespace
// @version         0.0.1
// @description     Handle dynamically loaded content on example.com
// @icon            https://www.google.com/s2/favicons?sz=64&domain=example.com
// @match           https://example.com/*
// @author          Your Name
// @run-at          document-end
// @noframes
// @grant           none
// ==/UserScript==

;(() => {
  'use strict'

  const processElement = el => {
    // modify element
  }

  // Process existing elements
  document.querySelectorAll('.target').forEach(processElement)

  // Watch for new elements
  const observer = new MutationObserver(mutations => {
    for (const mutation of mutations) {
      for (const node of mutation.addedNodes) {
        if (node.nodeType !== Node.ELEMENT_NODE) continue
        if (node.matches('.target')) processElement(node)
        node.querySelectorAll?.('.target').forEach(processElement)
      }
    }
  })

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

---

## @run-at Timing

| Value | Use Case |
|-------|----------|
| `document-start` | CSS injection, intercept before render |
| `document-end` | DOM manipulation (DOM ready, images loading) |
| `document-idle` | Non-critical, after page fully loads |

---

## @grant Options

| Value | When to Use |
|-------|-------------|
| `none` | Simple scripts, no sandbox, direct page access |
| `GM_addStyle` | CSS injection helper function |
| `GM_getValue` / `GM_setValue` | Persistent storage across sessions |
| `GM_xmlhttpRequest` | Cross-origin requests (also needs `@connect`) |
| `GM_registerMenuCommand` | Add items to extension popup menu |
| `GM_setClipboard` | Copy to clipboard |
| `unsafeWindow` | Access page's JavaScript context |

---

## Common Patterns

### CSS Injection
```javascript
// @run-at document-start
// @grant none

const style = document.createElement('style')
style.textContent = `
  .selector {
    display: none !important;
  }
`
document.documentElement.appendChild(style)
```

### CSS Variable Override
```javascript
// @run-at document-start

const style = document.createElement('style')
style.textContent = `
  :root {
    --custom-property: value !important;
  }
`
document.documentElement.appendChild(style)
```

### DOM Query + Modify
```javascript
// @run-at document-end

document.querySelectorAll('.selector').forEach(el => {
  el.style.display = 'none'
})
```

### Keyboard Shortcut
```javascript
// @run-at document-end

document.addEventListener('keydown', e => {
  // Skip if typing in input/textarea
  if (e.target.matches('input, textarea, [contenteditable]')) return

  if (e.key === 'j' && !e.ctrlKey && !e.metaKey && !e.altKey) {
    e.preventDefault()
    // action here
  }
})
```

### MutationObserver (Dynamic Content)
```javascript
// @run-at document-end

const observer = new MutationObserver(mutations => {
  for (const mutation of mutations) {
    for (const node of mutation.addedNodes) {
      if (node.matches?.('.target-selector')) {
        // handle new element
      }
    }
  }
})

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

### GM_addStyle (with grant)
```javascript
// @grant GM_addStyle

GM_addStyle(`
  .selector {
    display: none !important;
  }
`)
```

---

## Advanced Patterns

See `@references/advanced-patterns.md` for full implementations:

| Pattern | Use Case |
|---------|----------|
| Wait for element | Poll with `requestAnimationFrame` until element exists |
| isTyping helper | Skip shortcuts when user is in input/textarea |
| Scroll with offset | Smooth scroll accounting for fixed headers |
| Dark mode | CSS media query, meta tag, or CSS variables |
| URL parsing | Extract domain/path with `new URL()` |
| GM_addElement | Programmatic element creation |
| SPA navigation | Handle YouTube events, History API |
| Debug logging | Toggleable console output |
| Focus management | Track selection with keyboard nav |
| TreeWalker | Iterate text nodes for search/highlight |
| CSS Highlight API | Highlight without DOM modification |
| Canvas overlay | Draw scroll position markers |

---

## Selector Strategy

**Prefer (stable):**
- `data-testid="value"` - test IDs rarely change
- `data-*` attributes - semantic, intentional
- Semantic IDs - `#main-content`, `#sidebar`
- Tag + role - `button[role="tab"]`

**Avoid (fragile):**
- Generated classes - `.css-1a2b3c`, `.sc-abc123`
- Deep nesting - `div > div > div > span`
- Index-based - `:nth-child(3)`

---

## Debugging Tips

1. **Test selectors in console first** - `document.querySelectorAll('.selector')`
2. **Check timing** - if elements missing, try `document-idle` or MutationObserver
3. **Inspect network** - for dynamic content, elements may load after initial render
4. **CSS specificity** - use `!important` to override site styles
5. **Console errors** - check devtools console for script errors

---

## Reference

- `@references/api-reference.md` - All metadata headers, @match patterns, GM_* functions
- `@references/advanced-patterns.md` - SPA handling, dark mode, scroll markers, TreeWalker, etc.

Files in this skill

  • SKILL.md9 KB
  • references/advanced-patterns.md7.7 KB
  • references/api-reference.md9.7 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.