mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-05-15 13:23:13 +08:00
docs: salvage focused stale PR contributions
- add Vite and Redis pattern skills from closed stale PRs - add frontend-slides support assets - port skill-comply runner fixes and LLM prompt/provider regressions - harden agent frontmatter validation and sync catalog counts
This commit is contained in:
committed by
Affaan Mustafa
parent
d8f879e671
commit
b39d2244cf
419
skills/frontend-slides/html-template.md
Normal file
419
skills/frontend-slides/html-template.md
Normal file
@@ -0,0 +1,419 @@
|
||||
# HTML Presentation Template
|
||||
|
||||
Reference architecture for generating slide presentations. Every presentation follows this structure.
|
||||
|
||||
## Base HTML Structure
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Presentation Title</title>
|
||||
|
||||
<!-- Fonts: use Fontshare or Google Fonts — never system fonts -->
|
||||
<link rel="stylesheet" href="https://api.fontshare.com/v2/css?f[]=..." />
|
||||
|
||||
<style>
|
||||
/* ===========================================
|
||||
CSS CUSTOM PROPERTIES (THEME)
|
||||
Change these to change the whole look
|
||||
=========================================== */
|
||||
:root {
|
||||
/* Colors — from chosen style preset */
|
||||
--bg-primary: #0a0f1c;
|
||||
--bg-secondary: #111827;
|
||||
--text-primary: #ffffff;
|
||||
--text-secondary: #9ca3af;
|
||||
--accent: #00ffcc;
|
||||
--accent-glow: rgba(0, 255, 204, 0.3);
|
||||
|
||||
/* Typography — MUST use clamp() */
|
||||
--font-display: "Clash Display", sans-serif;
|
||||
--font-body: "Satoshi", sans-serif;
|
||||
--title-size: clamp(2rem, 6vw, 5rem);
|
||||
--subtitle-size: clamp(0.875rem, 2vw, 1.25rem);
|
||||
--body-size: clamp(0.75rem, 1.2vw, 1rem);
|
||||
|
||||
/* Spacing — MUST use clamp() */
|
||||
--slide-padding: clamp(1.5rem, 4vw, 4rem);
|
||||
--content-gap: clamp(1rem, 2vw, 2rem);
|
||||
|
||||
/* Animation */
|
||||
--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
|
||||
--duration-normal: 0.6s;
|
||||
}
|
||||
|
||||
/* ===========================================
|
||||
BASE STYLES
|
||||
=========================================== */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* --- PASTE viewport-base.css CONTENTS HERE --- */
|
||||
|
||||
/* ===========================================
|
||||
ANIMATIONS
|
||||
Trigger via .visible class (added by JS on scroll)
|
||||
=========================================== */
|
||||
.reveal {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
transition:
|
||||
opacity var(--duration-normal) var(--ease-out-expo),
|
||||
transform var(--duration-normal) var(--ease-out-expo);
|
||||
}
|
||||
|
||||
.slide.visible .reveal {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
/* Stagger children for sequential reveal */
|
||||
.reveal:nth-child(1) {
|
||||
transition-delay: 0.1s;
|
||||
}
|
||||
.reveal:nth-child(2) {
|
||||
transition-delay: 0.2s;
|
||||
}
|
||||
.reveal:nth-child(3) {
|
||||
transition-delay: 0.3s;
|
||||
}
|
||||
.reveal:nth-child(4) {
|
||||
transition-delay: 0.4s;
|
||||
}
|
||||
|
||||
/* ... preset-specific styles ... */
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Optional: Progress bar -->
|
||||
<div class="progress-bar"></div>
|
||||
|
||||
<!-- Optional: Navigation dots -->
|
||||
<nav class="nav-dots"><!-- Generated by JS --></nav>
|
||||
|
||||
<!-- Slides -->
|
||||
<section class="slide title-slide">
|
||||
<h1 class="reveal">Presentation Title</h1>
|
||||
<p class="reveal">Subtitle or author</p>
|
||||
</section>
|
||||
|
||||
<section class="slide">
|
||||
<div class="slide-content">
|
||||
<h2 class="reveal">Slide Title</h2>
|
||||
<p class="reveal">Content...</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- More slides... -->
|
||||
|
||||
<script>
|
||||
/* ===========================================
|
||||
SLIDE PRESENTATION CONTROLLER
|
||||
=========================================== */
|
||||
class SlidePresentation {
|
||||
constructor() {
|
||||
this.slides = document.querySelectorAll(".slide");
|
||||
this.currentSlide = 0;
|
||||
this.setupIntersectionObserver();
|
||||
this.setupKeyboardNav();
|
||||
this.setupTouchNav();
|
||||
this.setupProgressBar();
|
||||
this.setupNavDots();
|
||||
}
|
||||
|
||||
setupIntersectionObserver() {
|
||||
// Add .visible class when slides enter viewport
|
||||
// Triggers CSS animations efficiently
|
||||
}
|
||||
|
||||
setupKeyboardNav() {
|
||||
// Arrow keys, Space, Page Up/Down
|
||||
}
|
||||
|
||||
setupTouchNav() {
|
||||
// Touch/swipe support for mobile
|
||||
}
|
||||
|
||||
setupProgressBar() {
|
||||
// Update progress bar on scroll
|
||||
}
|
||||
|
||||
setupNavDots() {
|
||||
// IMPORTANT: Always clear before building — if outerHTML was
|
||||
// captured while dots were rendered, re-opening the file would
|
||||
// append a duplicate set on top of the existing ones.
|
||||
this.navDotsContainer.innerHTML = "";
|
||||
// Generate and manage navigation dots
|
||||
}
|
||||
}
|
||||
|
||||
new SlidePresentation();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
## Required JavaScript Features
|
||||
|
||||
Every presentation must include:
|
||||
|
||||
1. **SlidePresentation Class** — Main controller with:
|
||||
- Keyboard navigation (arrows, space, page up/down)
|
||||
- Touch/swipe support
|
||||
- Mouse wheel navigation
|
||||
- Progress bar updates
|
||||
- Navigation dots
|
||||
|
||||
2. **Intersection Observer** — For scroll-triggered animations:
|
||||
- Add `.visible` class when slides enter viewport
|
||||
- Trigger CSS transitions efficiently
|
||||
|
||||
3. **Optional Enhancements** (match to chosen style):
|
||||
- Custom cursor with trail
|
||||
- Particle system background (canvas)
|
||||
- Parallax effects
|
||||
- 3D tilt on hover
|
||||
- Magnetic buttons
|
||||
- Counter animations
|
||||
|
||||
4. **Inline Editing** (only if user opted in during Phase 1 — skip entirely if they said No):
|
||||
- Edit toggle button (hidden by default, revealed via hover hotzone or `E` key)
|
||||
- Auto-save to localStorage
|
||||
- Export/save file functionality
|
||||
- See "Inline Editing Implementation" section below
|
||||
|
||||
## Inline Editing Implementation (Opt-In Only)
|
||||
|
||||
**If the user chose "No" for inline editing in Phase 1, do NOT generate any edit-related HTML, CSS, or JS.**
|
||||
|
||||
**Do NOT use CSS `~` sibling selector for hover-based show/hide.** The CSS-only approach (`edit-hotzone:hover ~ .edit-toggle`) fails because `pointer-events: none` on the toggle button breaks the hover chain: user hovers hotzone -> button becomes visible -> mouse moves toward button -> leaves hotzone -> button disappears before click.
|
||||
|
||||
**Required approach: JS-based hover with 400ms delay timeout.**
|
||||
|
||||
HTML:
|
||||
|
||||
```html
|
||||
<div class="edit-hotzone"></div>
|
||||
<button class="edit-toggle" id="editToggle" title="Edit mode (E)">Edit</button>
|
||||
```
|
||||
|
||||
CSS (visibility controlled by JS classes only):
|
||||
|
||||
```css
|
||||
/* Do NOT use CSS ~ sibling selector for this!
|
||||
pointer-events: none breaks the hover chain.
|
||||
Must use JS with delay timeout. */
|
||||
.edit-hotzone {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
z-index: 10000;
|
||||
cursor: pointer;
|
||||
}
|
||||
.edit-toggle {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.3s ease;
|
||||
z-index: 10001;
|
||||
}
|
||||
.edit-toggle.show,
|
||||
.edit-toggle.active {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
```
|
||||
|
||||
JS (three interaction methods):
|
||||
|
||||
```javascript
|
||||
// 1. Click handler on the toggle button
|
||||
document.getElementById("editToggle").addEventListener("click", () => {
|
||||
editor.toggleEditMode();
|
||||
});
|
||||
|
||||
// 2. Hotzone hover with 400ms grace period
|
||||
const hotzone = document.querySelector(".edit-hotzone");
|
||||
const editToggle = document.getElementById("editToggle");
|
||||
let hideTimeout = null;
|
||||
|
||||
hotzone.addEventListener("mouseenter", () => {
|
||||
clearTimeout(hideTimeout);
|
||||
editToggle.classList.add("show");
|
||||
});
|
||||
hotzone.addEventListener("mouseleave", () => {
|
||||
hideTimeout = setTimeout(() => {
|
||||
if (!editor.isActive) editToggle.classList.remove("show");
|
||||
}, 400);
|
||||
});
|
||||
editToggle.addEventListener("mouseenter", () => {
|
||||
clearTimeout(hideTimeout);
|
||||
});
|
||||
editToggle.addEventListener("mouseleave", () => {
|
||||
hideTimeout = setTimeout(() => {
|
||||
if (!editor.isActive) editToggle.classList.remove("show");
|
||||
}, 400);
|
||||
});
|
||||
|
||||
// 3. Hotzone direct click
|
||||
hotzone.addEventListener("click", () => {
|
||||
editor.toggleEditMode();
|
||||
});
|
||||
|
||||
// 4. Keyboard shortcut (E key, skip when editing text)
|
||||
document.addEventListener("keydown", (e) => {
|
||||
if (
|
||||
(e.key === "e" || e.key === "E") &&
|
||||
!e.target.getAttribute("contenteditable")
|
||||
) {
|
||||
editor.toggleEditMode();
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
**CRITICAL: `exportFile()` must strip edit state before capturing outerHTML.**
|
||||
|
||||
When the user presses Ctrl+S in edit mode, `document.documentElement.outerHTML` captures the live DOM —
|
||||
including `body.edit-active`, `contenteditable="true"` on every text element, and `.active`/`.show` classes on
|
||||
the toggle button and banner. Anyone opening the saved file sees dashed outlines, a checkmark button, and an
|
||||
edit banner, as if permanently stuck in edit mode.
|
||||
|
||||
Always implement `exportFile()` like this:
|
||||
|
||||
```javascript
|
||||
exportFile() {
|
||||
// Temporarily strip edit state so the saved file opens cleanly
|
||||
const editableEls = Array.from(document.querySelectorAll('[contenteditable]'));
|
||||
editableEls.forEach(el => el.removeAttribute('contenteditable'));
|
||||
document.body.classList.remove('edit-active');
|
||||
|
||||
// Also strip UI classes from toggle button and banner
|
||||
const editToggle = document.getElementById('editToggle');
|
||||
const editBanner = document.querySelector('.edit-banner');
|
||||
editToggle?.classList.remove('active', 'show');
|
||||
editBanner?.classList.remove('active', 'show');
|
||||
|
||||
const html = '<!DOCTYPE html>\n' + document.documentElement.outerHTML;
|
||||
|
||||
// Restore edit state so the user can keep editing
|
||||
document.body.classList.add('edit-active');
|
||||
editableEls.forEach(el => el.setAttribute('contenteditable', 'true'));
|
||||
editToggle?.classList.add('active');
|
||||
editBanner?.classList.add('active');
|
||||
|
||||
const blob = new Blob([html], { type: 'text/html' });
|
||||
const a = document.createElement('a');
|
||||
a.href = URL.createObjectURL(blob);
|
||||
a.download = 'presentation.html';
|
||||
a.click();
|
||||
URL.revokeObjectURL(a.href);
|
||||
}
|
||||
```
|
||||
|
||||
## Image Pipeline (Skip If No Images)
|
||||
|
||||
If user chose "No images" in Phase 1, skip this entirely. If images were provided, process them before generating HTML.
|
||||
|
||||
**Dependency:** `pip install Pillow`
|
||||
|
||||
### Image Processing
|
||||
|
||||
```python
|
||||
from PIL import Image, ImageDraw
|
||||
|
||||
# Circular crop (for logos on modern/clean styles)
|
||||
def crop_circle(input_path, output_path):
|
||||
img = Image.open(input_path).convert('RGBA')
|
||||
w, h = img.size
|
||||
size = min(w, h)
|
||||
left, top = (w - size) // 2, (h - size) // 2
|
||||
img = img.crop((left, top, left + size, top + size))
|
||||
mask = Image.new('L', (size, size), 0)
|
||||
ImageDraw.Draw(mask).ellipse([0, 0, size, size], fill=255)
|
||||
img.putalpha(mask)
|
||||
img.save(output_path, 'PNG')
|
||||
|
||||
# Resize (for oversized images that inflate HTML)
|
||||
def resize_max(input_path, output_path, max_dim=1200):
|
||||
img = Image.open(input_path)
|
||||
img.thumbnail((max_dim, max_dim), Image.LANCZOS)
|
||||
img.save(output_path, quality=85)
|
||||
```
|
||||
|
||||
| Situation | Operation |
|
||||
| -------------------------------- | ----------------------------- |
|
||||
| Square logo on rounded aesthetic | `crop_circle()` |
|
||||
| Image > 1MB | `resize_max(max_dim=1200)` |
|
||||
| Wrong aspect ratio | Manual crop with `img.crop()` |
|
||||
|
||||
Save processed images with `_processed` suffix. Never overwrite originals.
|
||||
|
||||
### Image Placement
|
||||
|
||||
**Use direct file paths** (not base64) — presentations are viewed locally:
|
||||
|
||||
```html
|
||||
<img src="assets/logo_round.png" alt="Logo" class="slide-image logo" />
|
||||
<img
|
||||
src="assets/screenshot.png"
|
||||
alt="Screenshot"
|
||||
class="slide-image screenshot"
|
||||
/>
|
||||
```
|
||||
|
||||
```css
|
||||
.slide-image {
|
||||
max-width: 100%;
|
||||
max-height: min(50vh, 400px);
|
||||
object-fit: contain;
|
||||
border-radius: 8px;
|
||||
}
|
||||
.slide-image.screenshot {
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
.slide-image.logo {
|
||||
max-height: min(30vh, 200px);
|
||||
}
|
||||
```
|
||||
|
||||
**Adapt border/shadow colors to match the chosen style's accent.** Never repeat the same image on multiple slides (except logos on title + closing).
|
||||
|
||||
**Placement patterns:** Logo centered on title slide. Screenshots in two-column layouts with text. Full-bleed images as slide backgrounds with text overlay (use sparingly).
|
||||
|
||||
---
|
||||
|
||||
## Code Quality
|
||||
|
||||
**Comments:** Every section needs clear comments explaining what it does and how to modify it.
|
||||
|
||||
**Accessibility:**
|
||||
|
||||
- Semantic HTML (`<section>`, `<nav>`, `<main>`)
|
||||
- Keyboard navigation works fully
|
||||
- ARIA labels where needed
|
||||
- `prefers-reduced-motion` support (included in viewport-base.css)
|
||||
|
||||
## File Structure
|
||||
|
||||
Single presentations:
|
||||
|
||||
```
|
||||
presentation.html # Self-contained, all CSS/JS inline
|
||||
assets/ # Images only, if any
|
||||
```
|
||||
|
||||
Multiple presentations in one project:
|
||||
|
||||
```
|
||||
[name].html
|
||||
[name]-assets/
|
||||
```
|
||||
Reference in New Issue
Block a user