skills/fund-slides/references/html-template.md
wangyitong a65adcc2e5 Initial commit: merged, deduplicated, and vetted skill collection
Sources: extracted from two upstream archives (skill-repo, skills-main),
merged with the following policy:

- 15 broken symlinks (pointing to /Users/jameslee/.cc-switch/skills or
  ../../.agents/skills on a foreign machine) discarded
- 3 real name collisions with identical content (ai-pair, ifind-http-api,
  zhipu-websearch) kept as one copy
- Functional overlaps deduped keeping the strongest variant:
  - docx family: kept docx (official, full toolchain) + docx-cn
    (GB/T 9704 Chinese official-document constants),
    dropped docx_writer (no scripts, name collided with docx)
  - humanizer family: kept humanizer-zh (6 zh reference docs),
    dropped humanizer (en, redundant for CN workflow)
- Skills that only ran in a foreign environment removed:
  ablemind-ops, app-publish, hlb-design-system, openclaw-adj-skill,
  claude-driver
- alphapai excluded from this public repo because its SKILL.md hard-coded
  live credentials

Result: 25 skills, 572 files, ~7.5 MB.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 14:47:12 +08:00

13 KiB

HTML Presentation Template

Reference architecture for generating slide presentations. Every presentation follows this structure.

Base HTML Structure

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Presentation Title</title>

    <!-- Fonts: Google Fonts with Chinese font families -->
    <!-- Always include at least one Chinese font (Noto Sans SC / Noto Serif SC / LXGW WenKai) -->
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;700&display=swap" rel="stylesheet">

    <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() {
                // 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:

<div class="edit-hotzone"></div>
<button class="edit-toggle" id="editToggle" title="Edit mode (E)">
    <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor"><path d="M12.1 1.3a1.5 1.5 0 0 1 2.1 2.1L5.6 12 2 14l2-3.6 8.1-9.1z"/></svg>
</button>

CSS (visibility controlled by JS classes only):

/* 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):

// 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();
    }
});

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

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:

<img src="assets/logo_round.png" alt="Logo" class="slide-image logo">
<img src="assets/screenshot.png" alt="Screenshot" class="slide-image screenshot">
.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).


Financial Data Integration

When the presentation includes financial data (charts, KPIs, tables), add these utilities:

Chinese Number Formatting (Required for Financial Slides)

/* Include in <script> when any slide contains financial data */
function formatCN(num, decimals = 1) {
    if (num === null || num === undefined) return '--';
    const abs = Math.abs(num);
    const sign = num < 0 ? '-' : '';
    if (abs >= 1e8) return sign + (abs / 1e8).toFixed(decimals).replace(/\.0$/, '') + '\u4ebf';
    if (abs >= 1e4) return sign + (abs / 1e4).toFixed(decimals).replace(/\.0$/, '') + '\u4e07';
    return sign + abs.toLocaleString('zh-CN');
}
function formatPct(num, decimals = 2) {
    if (num === null || num === undefined) return '--';
    return (num * 100).toFixed(decimals) + '%';
}

ECharts Integration (Optional)

<!-- Add before </body> when charts are needed -->
<script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script>

Chart containers must init on slide visibility. See financial-charts.md for complete configuration reference.

Trend Indicators (CSS Only, No Emoji)

/* Up/down arrows as CSS pseudo-elements — never use emoji */
.trend-up::before {
    content: '';
    display: inline-block;
    width: 0; height: 0;
    border-left: 4px solid transparent;
    border-right: 4px solid transparent;
    border-bottom: 6px solid var(--color-positive, #dc2626);
    margin-right: 0.3em;
    vertical-align: middle;
}
.trend-down::before {
    content: '';
    display: inline-block;
    width: 0; height: 0;
    border-left: 4px solid transparent;
    border-right: 4px solid transparent;
    border-top: 6px solid var(--color-negative, #16a34a);
    margin-right: 0.3em;
    vertical-align: middle;
}
.trend-flat::before {
    content: '--';
    color: var(--text-secondary);
    margin-right: 0.3em;
}

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)
  • lang="zh-CN" on <html> for Chinese presentations

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/