# Copilot Instructions — squarespaceplugins

This file documents the coding conventions and architectural patterns used across all plugins in this repository. Follow these conventions when creating or modifying plugins.

---

## Plugin Architecture

### IIFE Pattern

All plugins are wrapped in an immediately-invoked function expression (IIFE) with strict mode:

```js
(function () {
  'use strict';
  // ... plugin code
})();
```

### Version & Name Constants

Every plugin declares two constants at the top of the IIFE:

```js
const PLUGIN_VERSION = '1.0.0';
const PLUGIN_NAME = 'PluginName'; // PascalCase — used for licensing
```

### Console Log Format

```js
console.log(`📁 ${PLUGIN_NAME} v${PLUGIN_VERSION} - Loading...`);
// ... on success:
console.log(`✅ ${PLUGIN_NAME} v${PLUGIN_VERSION} Active!`);
```

Use a relevant emoji for the loading message (e.g. `📁` for file/tab plugins, `🚀` for header plugins, `💀` for Logo Reaper).

---

## Configuration Parsing

### `document.currentScript` + URL Parameters

```js
const currentScript =
  document.currentScript ||
  (function () {
    const scripts = document.getElementsByTagName('script');
    return scripts[scripts.length - 1];
  })();

function getScriptParams() {
  const src = currentScript.src;
  const url = new URL(src);
  const params = new URLSearchParams(url.search);
  // ... parse params
}
```

### `fixColor()` / `fixHexColor()` Helper

All plugins use a color helper that adds a `#` prefix to bare hex values and handles special values:

```js
function fixColor(color) {
  if (!color || color === 'transparent') return color;
  if (color.startsWith('#') || color.startsWith('rgb')) return color;
  return '#' + color;
}
```

---

## CSS Injection

Styles are injected once with a unique style tag ID:

```js
function injectStyles() {
  const styleId = 'anavo-{plugin-name}-styles';
  if (document.getElementById(styleId)) return;

  const styles = document.createElement('style');
  styles.id = styleId;
  styles.textContent = `/* CSS here */`;
  document.head.appendChild(styles);
}
```

CSS class names use the `anavo-{plugin-name}-` prefix (e.g. `anavo-tc-wrapper`, `anavo-hp-header`).

---

## Presets System

Plugins with multiple visual styles use a `PRESETS` object mapping preset names to config overrides:

```js
const PRESETS = {
  default: { bgColor: '#FAF5EF', activeColor: '#8B7355', /* ... */ },
  minimal: { bgColor: '#ffffff', /* ... */ },
  elegant: { /* ... */ },
  bold:    { bgColor: '#1a1a1a', /* ... */ },
};
```

The active preset is merged with URL parameter overrides inside `getScriptParams()`.

---

## Licensing Integration

Licensing is loaded asynchronously, 1.5 seconds after plugin initialization, to be non-blocking:

```js
function loadLicensing() {
  setTimeout(() => {
    try {
      const licScript = document.createElement('script');
      const base = currentScript.src.replace(/[^/]+$/, '');
      licScript.src = base + '../_shared/licensing.min.js';
      licScript.onload = () => {
        if (window.AnavoLicenseManager) {
          const lm = new window.AnavoLicenseManager(PLUGIN_NAME, PLUGIN_VERSION);
          lm.init();
        }
      };
      document.head.appendChild(licScript);
    } catch (_e) {
      // licensing is non-blocking
    }
  }, 1500);
}
```

`PLUGIN_NAME` for licensing must be PascalCase (e.g. `TabbedContent`, `HeaderPro`, `LogoReaper`).

---

## DOM Ready Pattern

```js
if (document.readyState === 'loading') {
  document.addEventListener('DOMContentLoaded', init);
} else {
  init();
}
```

---

## Responsive Breakpoints

| Breakpoint | Max-Width | Label |
|------------|-----------|-------|
| Tablet | `≤900px` | Tablet |
| Mobile | `≤768px` | Mobile |
| Small mobile | `≤480px` | Small Mobile |

---

## Accessibility

- Use ARIA attributes on all interactive elements (`role`, `aria-selected`, `aria-controls`, `aria-labelledby`, `aria-expanded`)
- Inactive panels: use the `hidden` attribute (not just `display: none`)
- Keyboard navigation: implement `ArrowLeft`/`ArrowRight`/`ArrowUp`/`ArrowDown`/`Home`/`End` for tablist components
- Respect `prefers-reduced-motion`: set `animationSpeed = 0` or disable animations when the media query matches

---

## File Structure

Each plugin lives in its own directory:

```
/plugin-name/
  ├── plugin-name.js        ← Full source (unminified)
  ├── plugin-name.min.js    ← Minified (auto-generated by CI)
  ├── README.md             ← Plugin documentation
  ├── CHANGELOG.md          ← Version history (Keep a Changelog format)
  └── examples/
      └── basic.html        ← Self-contained demo page
```

- File names: **kebab-case** (`tabbed-content.js`, `logo-reaper.js`)
- Plugin `PLUGIN_NAME` constant: **PascalCase** (`TabbedContent`, `LogoReaper`)
- CSS class prefix: **kebab-case** (`anavo-tc-`, `anavo-hp-`)
- Style tag ID: `anavo-{plugin-name}-styles`

---

## CDN URLs

All CDN references use `squarespaceplugins` (no hyphen):

```
https://cdn.jsdelivr.net/gh/clonegarden/squarespaceplugins@latest/{plugin}/{plugin}.min.js
```

---

## Debug Mode

All plugins support a `?debug=false` URL parameter. When enabled, verbose logging is emitted via a `dbg()` helper:

```js
function dbg(...args) {
  if (config.debug) console.log(`[${PLUGIN_NAME}]`, ...args);
}
```
