feat: update GTM skills triad with field-tested improvements
gtm-audit (60): - Added Mode D rule: check elements for IDs before designing CSS triggers gtm-editor (61): - Added "Tagging Workflow: dataLayer First" section — always suggest dataLayer push code for developers before creating GTM triggers - Added multi-language snippets (JS, React, Vue, PHP) - Added "Trigger Design: IDs First" section — prefer element IDs, ask user to add IDs before falling back to CSS selectors - Added ID naming convention: [section]-[element]-[action] - Added decision tree for trigger selection gtm-validator (62): - Flag CSS-based triggers as fragility risk during QA - Recommend adding IDs instead of finding alternative CSS selectors Based on field testing with ourdigital.org GTM-N9TPJW container. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -136,5 +136,6 @@ One GA4 page_view fires to multiple endpoints — this is normal:
|
|||||||
- Wait for page to fully load before capturing network requests
|
- Wait for page to fully load before capturing network requests
|
||||||
- Use `evaluate_script` to check consent mode before interpreting missing tags
|
- Use `evaluate_script` to check consent mode before interpreting missing tags
|
||||||
- Orphan count may be high due to GA4 multi-endpoint — note this in reports
|
- Orphan count may be high due to GA4 multi-endpoint — note this in reports
|
||||||
|
- **In Mode D (tag design), check elements for `id` attributes** — if missing, recommend adding IDs before designing CSS-based triggers. Suggest naming: `[section]-[element]-[action]`
|
||||||
- Hand off tag creation work to `gtm-editor` skill
|
- Hand off tag creation work to `gtm-editor` skill
|
||||||
- Hand off QA/validation work to `gtm-validator` skill
|
- Hand off QA/validation work to `gtm-validator` skill
|
||||||
|
|||||||
@@ -34,6 +34,89 @@ Create, modify, and deploy GTM configurations via API. Generates ES5-compliant C
|
|||||||
### Reporting (Notion MCP)
|
### Reporting (Notion MCP)
|
||||||
- Write implementation plans and tag configurations to Notion
|
- Write implementation plans and tag configurations to Notion
|
||||||
|
|
||||||
|
## Tagging Workflow: dataLayer First (MANDATORY)
|
||||||
|
|
||||||
|
**Always push complexity into the dataLayer, not GTM triggers.**
|
||||||
|
|
||||||
|
### Step 1: Design the dataLayer push FIRST
|
||||||
|
|
||||||
|
Before creating any GTM configuration, design the `dataLayer.push()` that the website should implement. Present this to the user as a code snippet matching their tech stack:
|
||||||
|
|
||||||
|
**For vanilla JS / static HTML (ES5):**
|
||||||
|
```javascript
|
||||||
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
window.dataLayer.push({
|
||||||
|
'event': 'generate_lead',
|
||||||
|
'lead_type': document.getElementById('requestType').value,
|
||||||
|
'form_id': 'contact-form'
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**For React / Next.js / TypeScript:**
|
||||||
|
```typescript
|
||||||
|
declare global { interface Window { dataLayer: Record<string, any>[]; } }
|
||||||
|
|
||||||
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
window.dataLayer.push({
|
||||||
|
event: 'generate_lead',
|
||||||
|
lead_type: selectedType,
|
||||||
|
form_id: 'contact-form',
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**For Vue:**
|
||||||
|
```javascript
|
||||||
|
window.dataLayer?.push({
|
||||||
|
event: 'generate_lead',
|
||||||
|
lead_type: this.selectedType,
|
||||||
|
form_id: 'contact-form',
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**For PHP / WordPress:**
|
||||||
|
```php
|
||||||
|
add_action('wp_footer', function() { ?>
|
||||||
|
<script>
|
||||||
|
document.getElementById('contact-form').addEventListener('submit', function() {
|
||||||
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
window.dataLayer.push({
|
||||||
|
'event': 'generate_lead',
|
||||||
|
'lead_type': document.getElementById('requestType').value
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<?php });
|
||||||
|
```
|
||||||
|
|
||||||
|
**ASK the user:** "Here's the dataLayer code for your developers. Can they add this to the page? What's your tech stack?"
|
||||||
|
|
||||||
|
### Step 2: Create simple GTM config
|
||||||
|
|
||||||
|
Once dataLayer push is agreed:
|
||||||
|
- **Trigger:** Custom Event `{{_event}}` equals `generate_lead` (simple, robust)
|
||||||
|
- **Variables:** DataLayer Variable for each parameter (e.g., `dlv - lead_type`)
|
||||||
|
- **Tag:** GA4 Event with `measurementIdOverride` + DLV parameters
|
||||||
|
|
||||||
|
### Step 3: cHTML fallback (LAST RESORT)
|
||||||
|
|
||||||
|
Only if user confirms they CANNOT modify the website code:
|
||||||
|
1. Create a Custom HTML tag that listens for DOM events
|
||||||
|
2. The cHTML pushes to dataLayer internally
|
||||||
|
3. A Custom Event trigger picks it up (same clean pattern)
|
||||||
|
|
||||||
|
**All cHTML must be ES5-compatible** (see ES5 section below).
|
||||||
|
|
||||||
|
### Decision Tree
|
||||||
|
```
|
||||||
|
Track user action?
|
||||||
|
├─ Can devs add dataLayer.push()? → YES → Simple CE trigger ✅
|
||||||
|
├─ Can't modify code, element has ID? → Use Click ID trigger ✅
|
||||||
|
├─ No ID? → Ask user to add one first
|
||||||
|
└─ Nothing possible? → cHTML + CE trigger (last resort)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Core Capabilities
|
## Core Capabilities
|
||||||
|
|
||||||
### 1. Tag Creation via GTM API
|
### 1. Tag Creation via GTM API
|
||||||
@@ -175,10 +258,35 @@ When creating tags that should trigger FB CAPI:
|
|||||||
4. **Verify**: Hand off to gtm-validator for QA
|
4. **Verify**: Hand off to gtm-validator for QA
|
||||||
5. **Document**: Write implementation details to Notion
|
5. **Document**: Write implementation details to Notion
|
||||||
|
|
||||||
|
## Trigger Design: IDs First (MANDATORY)
|
||||||
|
|
||||||
|
**Always prefer element `id` over CSS selectors.** IDs survive redesigns; CSS selectors break.
|
||||||
|
|
||||||
|
**Before creating ANY click/form trigger:**
|
||||||
|
1. Check if the target element has an `id` attribute via Chrome DevTools `evaluate_script`
|
||||||
|
2. **If it has an ID** → use `Click ID` or `Form ID` filter
|
||||||
|
3. **If it does NOT have an ID** → **ASK the user** before falling back to CSS:
|
||||||
|
> "This element doesn't have an ID. Can you add one to the website/app code?
|
||||||
|
> Suggested: `id="[section]-[element]-[action]"` e.g., `id="hero-btn-consult"`"
|
||||||
|
4. Only use CSS selectors when the user explicitly confirms they cannot modify the HTML
|
||||||
|
|
||||||
|
**Priority order:**
|
||||||
|
1. Element ID (`Click ID equals "hero-btn-consult"`)
|
||||||
|
2. Data attribute (`Click Element matches CSS [data-track="consult"]`)
|
||||||
|
3. Form ID (`Form ID equals "contact-form"`)
|
||||||
|
4. CSS selector (last resort — document which selectors are used)
|
||||||
|
|
||||||
|
**Suggested ID naming:** `[section]-[element]-[action]`
|
||||||
|
- Navigation: `nav-about`, `nav-services`, `nav-insights`
|
||||||
|
- CTAs: `hero-btn-consult`, `cta-btn-booking`
|
||||||
|
- Forms: `contact-form`, `newsletter-form`
|
||||||
|
- Footer: `footer-social-linkedin`, `footer-email`
|
||||||
|
|
||||||
## Rules
|
## Rules
|
||||||
|
|
||||||
- Always use `dtm_status` first to verify auth and active container
|
- Always use `dtm_status` first to verify auth and active container
|
||||||
- Always use `measurementIdOverride` for GA4 event tags (NOT `measurementId`)
|
- Always use `measurementIdOverride` for GA4 event tags (NOT `measurementId`)
|
||||||
|
- **Always prefer element IDs for triggers — ask user to add IDs before using CSS selectors**
|
||||||
- All Custom HTML must be ES5-compatible
|
- All Custom HTML must be ES5-compatible
|
||||||
- Use `--dry-run` conceptually — verify trigger selectors via Chrome DevTools before creating
|
- Use `--dry-run` conceptually — verify trigger selectors via Chrome DevTools before creating
|
||||||
- Rate limit: space API calls 1 second apart in batch operations
|
- Rate limit: space API calls 1 second apart in batch operations
|
||||||
|
|||||||
@@ -220,5 +220,6 @@ Compare tags between two container versions to identify changes.
|
|||||||
- Test on both desktop and mobile viewports when possible
|
- Test on both desktop and mobile viewports when possible
|
||||||
- Check consent mode state before flagging missing tags
|
- Check consent mode state before flagging missing tags
|
||||||
- Document every test result with screenshots or network request evidence
|
- Document every test result with screenshots or network request evidence
|
||||||
- If a trigger selector doesn't match, suggest a fix and hand off to gtm-editor
|
- **When a trigger uses CSS selectors instead of IDs, flag it as a fragility risk** — recommend adding `id` attributes to the HTML
|
||||||
|
- If a trigger selector doesn't match, suggest adding an ID to the element first (not a different CSS selector) and hand off to gtm-editor
|
||||||
- GTM questions, best practices, and architecture advice are also in scope
|
- GTM questions, best practices, and architecture advice are also in scope
|
||||||
|
|||||||
Reference in New Issue
Block a user