# How to Monitor Hallmark Performance: Complete Guide to Static Site Telemetry

> Learn how to monitor Hallmark performance with our complete guide to static site telemetry. Capture FCP, LCP, and animation frame durations using the browser's Performance API.

- Repository: [Hassan El Mghari/hallmark](https://github.com/Nutlope/hallmark)
- Tags: how-to-guide
- Published: 2026-07-26

---

**Use the browser's native Performance API to capture runtime metrics like First Contentful Paint (FCP), Largest Contentful Paint (LCP), and animation frame durations, while adhering to the GPU-composited animation rules defined in the Hallmark skill.**

Hallmark is a design skill in the **Nutlope/hallmark** repository that generates static HTML and CSS pages governed by 57 "slop-test" performance gates. Because the output is purely client-side markup without server-side processing, monitoring Hallmark performance requires leveraging native browser APIs like `performance.now()` and `PerformanceObserver` rather than backend telemetry. This guide covers the exact implementation patterns found in the source code to measure, log, and enforce performance standards.

## Why Monitor Hallmark Performance?

Hallmark outputs static pages, which means performance bottlenecks arise from **client-side rendering** rather than database queries or server response times. The primary concerns involve **First Contentful Paint (FCP)**, **Largest Contentful Paint (LCP)**, and **animation frame rates**. 

According to the source analysis, the repository enforces strict performance boundaries through "slop-test" gates defined in [`SKILL.md`](https://github.com/Nutlope/hallmark/blob/main/SKILL.md). Monitoring ensures your generated pages stay within these thresholds and maintain the performance-first philosophy documented in [`skills/hallmark/references/motion.md`](https://github.com/Nutlope/hallmark/blob/main/skills/hallmark/references/motion.md).

## Core Monitoring Techniques

### Capturing Runtime Timestamps

The simplest way to monitor Hallmark performance is recording timestamps at key lifecycle moments. In [`site/js/main.js`](https://github.com/Nutlope/hallmark/blob/main/site/js/main.js), the codebase demonstrates this pattern by capturing `performance.now()` at the start of animation loops to calculate elapsed time.

```javascript
// Record when the page starts loading
const pageStart = performance.now();

// Measure specific operation duration
const start = performance.now();
// ... animation or layout operation ...
const duration = performance.now() - start;
console.log('Operation took:', duration.toFixed(2), 'ms');

```

This approach provides microsecond-precision timing for any arbitrary code block without external dependencies.

### Observing Paint Metrics (FCP and LCP)

For Web Vitals monitoring, use `PerformanceObserver` to capture paint metrics as they occur. This pattern detects when critical content becomes visible to users.

```javascript
// Listen for First Contentful Paint
new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    if (entry.name === 'first-contentful-paint') {
      console.log('FCP:', entry.startTime.toFixed(2), 'ms');
    }
  }
}).observe({ type: 'paint', buffered: true });

// Capture overall load time at page exit
window.addEventListener('beforeunload', () => {
  const loadTime = performance.now() - pageStart;
  console.log('Total page load time:', loadTime.toFixed(2), 'ms');
});

```

### Measuring Animation Frame Rates

Hallmark emphasizes smooth animations, making frame timing critical. Use `requestAnimationFrame` combined with `performance.now()` to detect jank or dropped frames.

```javascript
function logAnimationFrame() {
  const start = performance.now();
  requestAnimationFrame(() => {
    const frameDuration = performance.now() - start;
    console.log('Animation frame duration:', frameDuration.toFixed(2), 'ms');
    // Target: ~16.67ms for 60fps
    if (frameDuration > 16.67) {
      console.warn('Frame dropped');
    }
  });
}

```

## Performance-First Animation Rules

As documented in [`skills/hallmark/references/motion.md`](https://github.com/Nutlope/hallmark/blob/main/skills/hallmark/references/motion.md), Hallmark follows a **GPU-composited only** policy. This means animations must exclusively use `transform` and `opacity` properties. Any animation involving `width`, `height`, `margin`, or other layout-triggering properties causes reflows and degrades frame rates.

```css
/* ✅ GPU-accelerated: Uses transform and opacity only */
.optimal-animation {
  animation: slide 1s infinite;
}

@keyframes slide {
  from { transform: translateX(0); }
  to { transform: translateX(100px); }
}

/* ❌ Avoid: Triggers layout and paint */
.poor-performance {
  animation: expand 1s infinite;
}

@keyframes expand {
  from { width: 100px; }
  to { width: 200px; }
}

```

Adhering to these rules is essential because monitoring reveals problems that these animation constraints are designed to prevent.

## Implementing a Monitoring Pipeline

### Client-Side Instrumentation

Combine timestamp capture with metric reporting to build a complete monitoring solution. The following implementation mirrors the pattern found at line 990 of [`site/js/main.js`](https://github.com/Nutlope/hallmark/blob/main/site/js/main.js):

```javascript
// Comprehensive performance monitoring
const perfMetrics = {
  pageStart: performance.now(),
  fcp: null,
  lcp: null
};

// Observe paint metrics
new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    if (entry.name === 'first-contentful-paint') {
      perfMetrics.fcp = entry.startTime;
    }
  }
}).observe({ type: 'paint', buffered: true });

// Long task detection (helps identify blocking JavaScript)
new PerformanceObserver((list) => {
  list.getEntries().forEach(entry => {
    console.warn('Long task detected:', entry.duration.toFixed(2), 'ms');
  });
}).observe({ type: 'longtask', buffered: true });

// Report on unload
window.addEventListener('beforeunload', () => {
  const totalTime = performance.now() - perfMetrics.pageStart;
  // Send to analytics endpoint or logging service
  console.log('Performance Report:', {
    totalLoadTime: totalTime,
    fcp: perfMetrics.fcp,
    timestamp: new Date().toISOString()
  });
});

```

### Automated CI with Lighthouse

Because Hallmark generates static files under `site/_tests/`, you can automate performance monitoring using **Lighthouse CI**. Configure your [`package.json`](https://github.com/Nutlope/hallmark/blob/main/package.json) scripts to run audits against generated pages and enforce the slop-test thresholds.

```json
{
  "scripts": {
    "perf:audit": "lighthouse-ci --config=lighthouserc.js"
  }
}

```

This ensures that FCP and LCP metrics remain below the gate thresholds defined in the skill rules before deployment.

## Key Source Files for Performance Monitoring

- **[`site/js/main.js`](https://github.com/Nutlope/hallmark/blob/main/site/js/main.js)**: Contains the reference implementation using `performance.now()` for animation frame timing (around line 990).
- **[`skills/hallmark/references/motion.md`](https://github.com/Nutlope/hallmark/blob/main/skills/hallmark/references/motion.md)**: Documents the strict animation guidelines limiting effects to GPU-composited properties.
- **[`SKILL.md`](https://github.com/Nutlope/hallmark/blob/main/SKILL.md)**: Defines the 57 slop-test performance gates and thresholds that your monitoring should validate against.
- **[`README.md`](https://github.com/Nutlope/hallmark/blob/main/README.md)**: Provides installation steps and overview of the performance monitoring philosophy.
- **[`package.json`](https://github.com/Nutlope/hallmark/blob/main/package.json)**: Houses npm scripts extensible for Lighthouse CI integration.

## Summary

- **Use `performance.now()`** to capture microsecond-precision timestamps at page load and animation start points, following the pattern in [`site/js/main.js`](https://github.com/Nutlope/hallmark/blob/main/site/js/main.js).
- **Monitor Web Vitals** (FCP, LCP) using `PerformanceObserver` to track when critical content renders.
- **Restrict animations** to `transform` and `opacity` only, as required by [`skills/hallmark/references/motion.md`](https://github.com/Nutlope/hallmark/blob/main/skills/hallmark/references/motion.md), to prevent layout thrashing.
- **Implement Long Task observation** to detect blocking JavaScript that degrades frame rates.
- **Automate validation** with Lighthouse CI against the slop-test thresholds defined in [`SKILL.md`](https://github.com/Nutlope/hallmark/blob/main/SKILL.md).

## Frequently Asked Questions

### How do I measure First Contentful Paint in Hallmark projects?

Use the `PerformanceObserver` API to listen for paint events. Because Hallmark outputs static HTML, FCP occurs as soon as the browser renders the first text or image element. The observer pattern with `{ type: 'paint', buffered: true }` captures this metric even if your script loads after the initial paint.

### What is the "slop-test" performance gate mentioned in the Hallmark skill?

The slop-test refers to 57 specific performance thresholds defined in [`SKILL.md`](https://github.com/Nutlope/hallmark/blob/main/SKILL.md) that govern acceptable latency, bundle size, and rendering metrics. Monitoring tools should validate that FCP, LCP, and animation frame rates stay within these boundaries to ensure the generated site meets the skill's quality standards.

### Why can't I animate properties like width or height in Hallmark?

As documented in [`skills/hallmark/references/motion.md`](https://github.com/Nutlope/hallmark/blob/main/skills/hallmark/references/motion.md), animating layout properties triggers browser reflow and repaint cycles, which degrade frame rates on static sites. Hallmark enforces a GPU-composited-only policy (`transform` and `opacity`) to maintain 60fps performance. Monitoring will reveal these bottlenecks if you accidentally use non-compliant CSS.

### Can I use third-party libraries like web-vitals with Hallmark?

Yes. While the core examples use native browser APIs, you can import libraries like `web-vitals` to simplify metric collection. Since Hallmark generates standard static HTML, any client-side performance library works, though the native `PerformanceObserver` and `performance.now()` methods provide sufficient granularity for most use cases without adding bundle weight.