feat(registry): mk / yt / hw theme families — 29 items

Adds three themed registry families contributed by @jbernard077:

- mk-*  minimal presentation  (7 blocks, 3 components)
- yt-*  retro-broadcast creator (5 blocks, 4 components)
- hw-*  hand-drawn scribble   (6 blocks, 5 components)

Consolidates PRs #1933, #1992, #1993 and #1994, cuts 5 of the original 34
items, and fixes the defects that CI never got to report.

Cut (5), each covered by something we already ship or trivial to inline:
- yt-doc-lower-third — would be the 12th name/role lower third next to the
  10 lt-* variants and lower-third-bild
- mk-logo-sting — logo-outro exists, and yt-logo-intro is the richer version
- mk-cta-button, mk-pill-callout, yt-avatar-pip — a styled button, a styled
  chip and a masked circle

Fixes:
- hw-path-text rendered as a solid black blob. #hw-pt-path is the textPath
  carrier and had no rule, so it inherited SVG's default black fill while
  only #hw-pt-guide set fill:none.
- mk-line-graph value labels collided with their own dots and each other.
  Series after the first now label downward, and the offset moved off a CSS
  transform because the entrance tween animates y and GSAP rewrote it.
- Added demo.html for the 11 components and the 3 transparent-root blocks.
  Without it generate-catalog-previews.ts skips components entirely, so the
  catalog-previews job failed for every component in the original PRs.
- Tagged hw-title, hw-pipeline and hw-path-text as overlays.

Also registers all catalog pages in docs.json, which the original PRs left
orphaned, and picks up 4 pre-existing blocks that had no catalog page
(camcorder-hud, editorial-flash-overlay, freeze-frame-dressing,
organic-light-leak-overlay) because the regenerated nav now links them.

Every item was verified by rendering it and looking at the frames.

Co-authored-by: jbernard077 <jbernard077@users.noreply.github.com>
This commit is contained in:
Miguel Angel Simon Sierra
2026-08-01 01:03:39 +02:00
co-authored by jbernard077
parent 58ef6aca67
commit f252ec6d99
57 changed files with 5287 additions and 2467 deletions
+44
View File
@@ -0,0 +1,44 @@
---
title: "Camcorder HUD"
description: "Responsive REC, battery, editable date placeholder, and timeline-driven counter overlay for creator-camera and camcorder treatments"
---
# Camcorder HUD
Responsive REC, battery, editable date placeholder, and timeline-driven counter overlay for creator-camera and camcorder treatments
`camcorder` `camera` `recording` `hud` `overlay` `creator` `media-treatment-overlay`
<video className="w-full aspect-video rounded-xl object-cover bg-zinc-100 dark:bg-zinc-800" src="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/camcorder-hud.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/camcorder-hud.png" autoPlay muted loop playsInline />
## Install
<CodeGroup>
```bash Terminal
npx hyperframes add camcorder-hud
```
</CodeGroup>
## Details
| Property | Value |
| --- | --- |
| Type | Block |
| Dimensions | 1920×1080 |
| Duration | 4s |
## Files
| File | Target | Type |
| --- | --- | --- |
| `camcorder-hud.html` | `compositions/camcorder-hud.html` | hyperframes:composition |
## Usage
After installing, add the block to your host composition:
```html
<div data-composition-id="camcorder-hud" data-composition-src="compositions/camcorder-hud.html" data-start="0" data-duration="4" data-track-index="1" data-width="1920" data-height="1080"></div>
```
@@ -0,0 +1,44 @@
---
title: "Editorial Flash Overlay"
description: "Finite neutral-warm light layers for a seek-safe camera-flash cut or social reveal"
---
# Editorial Flash Overlay
Finite neutral-warm light layers for a seek-safe camera-flash cut or social reveal
`flash` `editorial` `social` `light` `transition` `overlay` `media-treatment-overlay`
<video className="w-full aspect-video rounded-xl object-cover bg-zinc-100 dark:bg-zinc-800" src="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/editorial-flash-overlay.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/editorial-flash-overlay.png" autoPlay muted loop playsInline />
## Install
<CodeGroup>
```bash Terminal
npx hyperframes add editorial-flash-overlay
```
</CodeGroup>
## Details
| Property | Value |
| --- | --- |
| Type | Block |
| Dimensions | 1920×1080 |
| Duration | 4s |
## Files
| File | Target | Type |
| --- | --- | --- |
| `editorial-flash-overlay.html` | `compositions/editorial-flash-overlay.html` | hyperframes:composition |
## Usage
After installing, add the block to your host composition:
```html
<div data-composition-id="editorial-flash-overlay" data-composition-src="compositions/editorial-flash-overlay.html" data-start="0" data-duration="4" data-track-index="1" data-width="1920" data-height="1080"></div>
```
@@ -0,0 +1,44 @@
---
title: "Freeze-Frame Dressing"
description: "Timeline-driven paper, tape, and flash dressing for a freeze-frame or background-removed subject"
---
# Freeze-Frame Dressing
Timeline-driven paper, tape, and flash dressing for a freeze-frame or background-removed subject
`freeze-frame` `cutout` `scrapbook` `paper` `social` `overlay` `media-treatment-overlay`
<video className="w-full aspect-video rounded-xl object-cover bg-zinc-100 dark:bg-zinc-800" src="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/freeze-frame-dressing.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/freeze-frame-dressing.png" autoPlay muted loop playsInline />
## Install
<CodeGroup>
```bash Terminal
npx hyperframes add freeze-frame-dressing
```
</CodeGroup>
## Details
| Property | Value |
| --- | --- |
| Type | Block |
| Dimensions | 1920×1080 |
| Duration | 4s |
## Files
| File | Target | Type |
| --- | --- | --- |
| `freeze-frame-dressing.html` | `compositions/freeze-frame-dressing.html` | hyperframes:composition |
## Usage
After installing, add the block to your host composition:
```html
<div data-composition-id="freeze-frame-dressing" data-composition-src="compositions/freeze-frame-dressing.html" data-start="0" data-duration="4" data-track-index="1" data-width="1920" data-height="1080"></div>
```
+1
View File
@@ -34,6 +34,7 @@ npx hyperframes add hw-frame
| File | Target | Type | | File | Target | Type |
| --- | --- | --- | | --- | --- | --- |
| `hw-frame.html` | `compositions/hw-frame.html` | hyperframes:composition | | `hw-frame.html` | `compositions/hw-frame.html` | hyperframes:composition |
| `assets/fonts/Caveat-700-latin.woff2` | `assets/fonts/Caveat-700-latin.woff2` | hyperframes:asset |
## Usage ## Usage
+2 -1
View File
@@ -7,7 +7,7 @@ description: "Handwriting flowing along any SVG path with a per-character reveal
Handwriting flowing along any SVG path with a per-character reveal; the path is the only geometry input Handwriting flowing along any SVG path with a per-character reveal; the path is the only geometry input
`handwritten` `typography` `path` `handwritten` `typography` `path` `overlay`
<video className="w-full aspect-video rounded-xl object-cover bg-zinc-100 dark:bg-zinc-800" src="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/hw-path-text.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/hw-path-text.png" autoPlay muted loop playsInline /> <video className="w-full aspect-video rounded-xl object-cover bg-zinc-100 dark:bg-zinc-800" src="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/hw-path-text.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/hw-path-text.png" autoPlay muted loop playsInline />
@@ -34,6 +34,7 @@ npx hyperframes add hw-path-text
| File | Target | Type | | File | Target | Type |
| --- | --- | --- | | --- | --- | --- |
| `hw-path-text.html` | `compositions/hw-path-text.html` | hyperframes:composition | | `hw-path-text.html` | `compositions/hw-path-text.html` | hyperframes:composition |
| `assets/fonts/Caveat-700-latin.woff2` | `assets/fonts/Caveat-700-latin.woff2` | hyperframes:asset |
## Usage ## Usage
+2 -1
View File
@@ -7,7 +7,7 @@ description: "Wobbled boxes with handwritten labels joined by curved connectors,
Wobbled boxes with handwritten labels joined by curved connectors, drawing on in sequence from a node list Wobbled boxes with handwritten labels joined by curved connectors, drawing on in sequence from a node list
`handwritten` `diagram` `data-viz` `handwritten` `diagram` `data-viz` `overlay`
<video className="w-full aspect-video rounded-xl object-cover bg-zinc-100 dark:bg-zinc-800" src="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/hw-pipeline.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/hw-pipeline.png" autoPlay muted loop playsInline /> <video className="w-full aspect-video rounded-xl object-cover bg-zinc-100 dark:bg-zinc-800" src="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/hw-pipeline.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/hw-pipeline.png" autoPlay muted loop playsInline />
@@ -34,6 +34,7 @@ npx hyperframes add hw-pipeline
| File | Target | Type | | File | Target | Type |
| --- | --- | --- | | --- | --- | --- |
| `hw-pipeline.html` | `compositions/hw-pipeline.html` | hyperframes:composition | | `hw-pipeline.html` | `compositions/hw-pipeline.html` | hyperframes:composition |
| `assets/fonts/Caveat-700-latin.woff2` | `assets/fonts/Caveat-700-latin.woff2` | hyperframes:asset |
## Usage ## Usage
+1
View File
@@ -34,6 +34,7 @@ npx hyperframes add hw-text-cloud
| File | Target | Type | | File | Target | Type |
| --- | --- | --- | | --- | --- | --- |
| `hw-text-cloud.html` | `compositions/hw-text-cloud.html` | hyperframes:composition | | `hw-text-cloud.html` | `compositions/hw-text-cloud.html` | hyperframes:composition |
| `assets/fonts/Caveat-700-latin.woff2` | `assets/fonts/Caveat-700-latin.woff2` | hyperframes:asset |
## Usage ## Usage
+2 -1
View File
@@ -7,7 +7,7 @@ description: "Handwritten display text with a scalar-driven word-by-word highlig
Handwritten display text with a scalar-driven word-by-word highlight sweep and a seeded squiggle underline sized to the text; boils Handwritten display text with a scalar-driven word-by-word highlight sweep and a seeded squiggle underline sized to the text; boils
`handwritten` `typography` `title-card` `handwritten` `typography` `title-card` `overlay`
<video className="w-full aspect-video rounded-xl object-cover bg-zinc-100 dark:bg-zinc-800" src="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/hw-title.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/hw-title.png" autoPlay muted loop playsInline /> <video className="w-full aspect-video rounded-xl object-cover bg-zinc-100 dark:bg-zinc-800" src="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/hw-title.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/hw-title.png" autoPlay muted loop playsInline />
@@ -34,6 +34,7 @@ npx hyperframes add hw-title
| File | Target | Type | | File | Target | Type |
| --- | --- | --- | | --- | --- | --- |
| `hw-title.html` | `compositions/hw-title.html` | hyperframes:composition | | `hw-title.html` | `compositions/hw-title.html` | hyperframes:composition |
| `assets/fonts/Caveat-700-latin.woff2` | `assets/fonts/Caveat-700-latin.woff2` | hyperframes:asset |
## Usage ## Usage
-44
View File
@@ -1,44 +0,0 @@
---
title: "Outline Logo Sting"
description: "A hairline rounded-square outline draws on and the logo lands inside — image or text wordmark in the same shell"
---
# Outline Logo Sting
A hairline rounded-square outline draws on and the logo lands inside — image or text wordmark in the same shell
`logo` `sting` `minimal`
<video className="w-full aspect-video rounded-xl object-cover bg-zinc-100 dark:bg-zinc-800" src="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/mk-logo-sting.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/mk-logo-sting.png" autoPlay muted loop playsInline />
## Install
<CodeGroup>
```bash Terminal
npx hyperframes add mk-logo-sting
```
</CodeGroup>
## Details
| Property | Value |
| --- | --- |
| Type | Block |
| Dimensions | 1920×1080 |
| Duration | 3.5s |
## Files
| File | Target | Type |
| --- | --- | --- |
| `mk-logo-sting.html` | `compositions/mk-logo-sting.html` | hyperframes:composition |
## Usage
After installing, add the block to your host composition:
```html
<div data-composition-id="mk-logo-sting" data-composition-src="compositions/mk-logo-sting.html" data-start="0" data-duration="3.5" data-track-index="1" data-width="1920" data-height="1080"></div>
```
@@ -0,0 +1,44 @@
---
title: "Organic Light Leak Overlay"
description: "Finite CSS light-leak overlay for memory beats and motivated transitions"
---
# Organic Light Leak Overlay
Finite CSS light-leak overlay for memory beats and motivated transitions
`light-leak` `film` `memory` `transition` `overlay` `media-treatment-overlay`
<video className="w-full aspect-video rounded-xl object-cover bg-zinc-100 dark:bg-zinc-800" src="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/organic-light-leak-overlay.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/organic-light-leak-overlay.png" autoPlay muted loop playsInline />
## Install
<CodeGroup>
```bash Terminal
npx hyperframes add organic-light-leak-overlay
```
</CodeGroup>
## Details
| Property | Value |
| --- | --- |
| Type | Block |
| Dimensions | 1920×1080 |
| Duration | 4s |
## Files
| File | Target | Type |
| --- | --- | --- |
| `organic-light-leak-overlay.html` | `compositions/organic-light-leak-overlay.html` | hyperframes:composition |
## Usage
After installing, add the block to your host composition:
```html
<div data-composition-id="organic-light-leak-overlay" data-composition-src="compositions/organic-light-leak-overlay.html" data-start="0" data-duration="4" data-track-index="1" data-width="1920" data-height="1080"></div>
```
@@ -1,44 +0,0 @@
---
title: "Documentary Lower Third"
description: "Name and role lower third: accent bar draws down, text slides in, hairline rule grows; overlay and light schemes"
---
# Documentary Lower Third
Name and role lower third: accent bar draws down, text slides in, hairline rule grows; overlay and light schemes
`lower-third` `title` `creator`
<video className="w-full aspect-video rounded-xl object-cover bg-zinc-100 dark:bg-zinc-800" src="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/yt-doc-lower-third.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/yt-doc-lower-third.png" autoPlay muted loop playsInline />
## Install
<CodeGroup>
```bash Terminal
npx hyperframes add yt-doc-lower-third
```
</CodeGroup>
## Details
| Property | Value |
| --- | --- |
| Type | Block |
| Dimensions | 1920×1080 |
| Duration | 6s |
## Files
| File | Target | Type |
| --- | --- | --- |
| `yt-doc-lower-third.html` | `compositions/yt-doc-lower-third.html` | hyperframes:composition |
## Usage
After installing, add the block to your host composition:
```html
<div data-composition-id="yt-doc-lower-third" data-composition-src="compositions/yt-doc-lower-third.html" data-start="0" data-duration="6" data-track-index="1" data-width="1920" data-height="1080"></div>
```
+2 -1
View File
@@ -32,7 +32,8 @@ npx hyperframes add hw-arrow
| File | Target | Type | | File | Target | Type |
| --- | --- | --- | | --- | --- | --- |
| `hw-arrow.html` | `compositions/components/hw-arrow.html` | hyperframes:snippet | | `hw-arrow.html` | `compositions/components/hw-arrow.html` | hyperframes:snippet |
| `assets/fonts/Caveat-700-latin.woff2` | `assets/fonts/Caveat-700-latin.woff2` | hyperframes:asset |
## Usage ## Usage
Read the installed file and copy its CSS, markup pattern, and helper functions into your host composition — the parent timeline owns all timing. The file's header comment documents the wiring calls. Open `compositions/components/hw-arrow.html` and paste its contents into your composition. See the comment header in the file for detailed instructions.
+2 -1
View File
@@ -32,7 +32,8 @@ npx hyperframes add hw-boil
| File | Target | Type | | File | Target | Type |
| --- | --- | --- | | --- | --- | --- |
| `hw-boil.html` | `compositions/components/hw-boil.html` | hyperframes:snippet | | `hw-boil.html` | `compositions/components/hw-boil.html` | hyperframes:snippet |
| `assets/fonts/Caveat-700-latin.woff2` | `assets/fonts/Caveat-700-latin.woff2` | hyperframes:asset |
## Usage ## Usage
Read the installed file and copy its CSS, markup pattern, and helper functions into your host composition — the parent timeline owns all timing. The file's header comment documents the wiring calls. Open `compositions/components/hw-boil.html` and paste its contents into your composition. See the comment header in the file for detailed instructions.
+2 -1
View File
@@ -32,7 +32,8 @@ npx hyperframes add hw-box-label
| File | Target | Type | | File | Target | Type |
| --- | --- | --- | | --- | --- | --- |
| `hw-box-label.html` | `compositions/components/hw-box-label.html` | hyperframes:snippet | | `hw-box-label.html` | `compositions/components/hw-box-label.html` | hyperframes:snippet |
| `assets/fonts/Caveat-700-latin.woff2` | `assets/fonts/Caveat-700-latin.woff2` | hyperframes:asset |
## Usage ## Usage
Read the installed file and copy its CSS, markup pattern, and helper functions into your host composition — the parent timeline owns all timing. The file's header comment documents the wiring calls. Open `compositions/components/hw-box-label.html` and paste its contents into your composition. See the comment header in the file for detailed instructions.
@@ -32,7 +32,8 @@ npx hyperframes add hw-callout-circle
| File | Target | Type | | File | Target | Type |
| --- | --- | --- | | --- | --- | --- |
| `hw-callout-circle.html` | `compositions/components/hw-callout-circle.html` | hyperframes:snippet | | `hw-callout-circle.html` | `compositions/components/hw-callout-circle.html` | hyperframes:snippet |
| `assets/fonts/Caveat-700-latin.woff2` | `assets/fonts/Caveat-700-latin.woff2` | hyperframes:asset |
## Usage ## Usage
Read the installed file and copy its CSS, markup pattern, and helper functions into your host composition — the parent timeline owns all timing. The file's header comment documents the wiring calls. Open `compositions/components/hw-callout-circle.html` and paste its contents into your composition. See the comment header in the file for detailed instructions.
+2 -1
View File
@@ -32,7 +32,8 @@ npx hyperframes add hw-underline
| File | Target | Type | | File | Target | Type |
| --- | --- | --- | | --- | --- | --- |
| `hw-underline.html` | `compositions/components/hw-underline.html` | hyperframes:snippet | | `hw-underline.html` | `compositions/components/hw-underline.html` | hyperframes:snippet |
| `assets/fonts/Caveat-700-latin.woff2` | `assets/fonts/Caveat-700-latin.woff2` | hyperframes:asset |
## Usage ## Usage
Read the installed file and copy its CSS, markup pattern, and helper functions into your host composition — the parent timeline owns all timing. The file's header comment documents the wiring calls. Open `compositions/components/hw-underline.html` and paste its contents into your composition. See the comment header in the file for detailed instructions.
-38
View File
@@ -1,38 +0,0 @@
---
title: "Pill CTA Button"
description: "Rounded call-to-action button with arrow, optional outline frame with shadow, frosted variant, and an arrow-nudge helper"
---
# Pill CTA Button
Rounded call-to-action button with arrow, optional outline frame with shadow, frosted variant, and an arrow-nudge helper
`cta` `button` `minimal` `overlay`
<video className="w-full aspect-video rounded-xl object-cover bg-zinc-100 dark:bg-zinc-800" src="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/components/mk-cta-button.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/components/mk-cta-button.png" autoPlay muted loop playsInline />
## Install
<CodeGroup>
```bash Terminal
npx hyperframes add mk-cta-button
```
</CodeGroup>
## Details
| Property | Value |
| --- | --- |
| Type | Component |
## Files
| File | Target | Type |
| --- | --- | --- |
| `mk-cta-button.html` | `compositions/components/mk-cta-button.html` | hyperframes:snippet |
## Usage
Read the installed file and copy its CSS, markup pattern, and helper functions into your host composition — the parent timeline owns all timing. The file's header comment documents the wiring calls.
+1 -1
View File
@@ -35,4 +35,4 @@ npx hyperframes add mk-emphasis-type
## Usage ## Usage
Read the installed file and copy its CSS, markup pattern, and helper functions into your host composition — the parent timeline owns all timing. The file's header comment documents the wiring calls. Open `compositions/components/mk-emphasis-type.html` and paste its contents into your composition. See the comment header in the file for detailed instructions.
@@ -1,38 +0,0 @@
---
title: "Capsule Callout Chip"
description: "Rounded capsule chip for handles, tags, and URLs with solid, paper, and frosted-glass variants; slide in/out helpers for the host timeline"
---
# Capsule Callout Chip
Rounded capsule chip for handles, tags, and URLs with solid, paper, and frosted-glass variants; slide in/out helpers for the host timeline
`callout` `social` `minimal` `overlay`
<video className="w-full aspect-video rounded-xl object-cover bg-zinc-100 dark:bg-zinc-800" src="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/components/mk-pill-callout.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/components/mk-pill-callout.png" autoPlay muted loop playsInline />
## Install
<CodeGroup>
```bash Terminal
npx hyperframes add mk-pill-callout
```
</CodeGroup>
## Details
| Property | Value |
| --- | --- |
| Type | Component |
## Files
| File | Target | Type |
| --- | --- | --- |
| `mk-pill-callout.html` | `compositions/components/mk-pill-callout.html` | hyperframes:snippet |
## Usage
Read the installed file and copy its CSS, markup pattern, and helper functions into your host composition — the parent timeline owns all timing. The file's header comment documents the wiring calls.
+1 -1
View File
@@ -35,4 +35,4 @@ npx hyperframes add mk-usage-arc
## Usage ## Usage
Read the installed file and copy its CSS, markup pattern, and helper functions into your host composition — the parent timeline owns all timing. The file's header comment documents the wiring calls. Open `compositions/components/mk-usage-arc.html` and paste its contents into your composition. See the comment header in the file for detailed instructions.
-38
View File
@@ -1,38 +0,0 @@
---
title: "Circle PiP Window"
description: "Circular or rounded-rect masked picture-in-picture window with ring and shadow for a second video or image; pop in/out helpers"
---
# Circle PiP Window
Circular or rounded-rect masked picture-in-picture window with ring and shadow for a second video or image; pop in/out helpers
`pip` `overlay` `creator`
<video className="w-full aspect-video rounded-xl object-cover bg-zinc-100 dark:bg-zinc-800" src="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/components/yt-avatar-pip.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/components/yt-avatar-pip.png" autoPlay muted loop playsInline />
## Install
<CodeGroup>
```bash Terminal
npx hyperframes add yt-avatar-pip
```
</CodeGroup>
## Details
| Property | Value |
| --- | --- |
| Type | Component |
## Files
| File | Target | Type |
| --- | --- | --- |
| `yt-avatar-pip.html` | `compositions/components/yt-avatar-pip.html` | hyperframes:snippet |
## Usage
Read the installed file and copy its CSS, markup pattern, and helper functions into your host composition — the parent timeline owns all timing. The file's header comment documents the wiring calls.
+1 -1
View File
@@ -35,4 +35,4 @@ npx hyperframes add yt-camera-move
## Usage ## Usage
Read the installed file and copy its CSS, markup pattern, and helper functions into your host composition — the parent timeline owns all timing. The file's header comment documents the wiring calls. Open `compositions/components/yt-camera-move.html` and paste its contents into your composition. See the comment header in the file for detailed instructions.
@@ -35,4 +35,4 @@ npx hyperframes add yt-circle-pointer
## Usage ## Usage
Read the installed file and copy its CSS, markup pattern, and helper functions into your host composition — the parent timeline owns all timing. The file's header comment documents the wiring calls. Open `compositions/components/yt-circle-pointer.html` and paste its contents into your composition. See the comment header in the file for detailed instructions.
@@ -35,4 +35,4 @@ npx hyperframes add yt-feather-highlight
## Usage ## Usage
Read the installed file and copy its CSS, markup pattern, and helper functions into your host composition — the parent timeline owns all timing. The file's header comment documents the wiring calls. Open `compositions/components/yt-feather-highlight.html` and paste its contents into your composition. See the comment header in the file for detailed instructions.
+1 -1
View File
@@ -35,4 +35,4 @@ npx hyperframes add yt-screen-warp
## Usage ## Usage
Read the installed file and copy its CSS, markup pattern, and helper functions into your host composition — the parent timeline owns all timing. The file's header comment documents the wiring calls. Open `compositions/components/yt-screen-warp.html` and paste its contents into your composition. See the comment header in the file for detailed instructions.
+80 -56
View File
@@ -200,14 +200,14 @@
"group": "Code Animations", "group": "Code Animations",
"pages": [ "pages": [
"catalog/blocks/code-3d-extrude", "catalog/blocks/code-3d-extrude",
"catalog/blocks/code-shader-dissolve",
"catalog/blocks/code-particle-assemble",
"catalog/blocks/code-morph",
"catalog/blocks/code-snippet-flight",
"catalog/blocks/code-typing",
"catalog/blocks/code-diff", "catalog/blocks/code-diff",
"catalog/blocks/code-highlight", "catalog/blocks/code-highlight",
"catalog/blocks/code-scroll" "catalog/blocks/code-morph",
"catalog/blocks/code-particle-assemble",
"catalog/blocks/code-scroll",
"catalog/blocks/code-shader-dissolve",
"catalog/blocks/code-snippet-flight",
"catalog/blocks/code-typing"
] ]
}, },
{ {
@@ -227,7 +227,8 @@
"catalog/components/caption-particle-burst", "catalog/components/caption-particle-burst",
"catalog/components/caption-pill-karaoke", "catalog/components/caption-pill-karaoke",
"catalog/components/caption-texture", "catalog/components/caption-texture",
"catalog/components/caption-weight-shift" "catalog/components/caption-weight-shift",
"catalog/blocks/mk-callout-highlight"
] ]
}, },
{ {
@@ -251,12 +252,15 @@
{ {
"group": "Social Overlays", "group": "Social Overlays",
"pages": [ "pages": [
"catalog/blocks/editorial-flash-overlay",
"catalog/blocks/freeze-frame-dressing",
"catalog/blocks/instagram-follow", "catalog/blocks/instagram-follow",
"catalog/blocks/macos-notification", "catalog/blocks/macos-notification",
"catalog/blocks/reddit-post", "catalog/blocks/reddit-post",
"catalog/blocks/spotify-card", "catalog/blocks/spotify-card",
"catalog/blocks/tiktok-follow", "catalog/blocks/tiktok-follow",
"catalog/blocks/x-post", "catalog/blocks/x-post",
"catalog/blocks/yt-comment-card",
"catalog/blocks/yt-lower-third" "catalog/blocks/yt-lower-third"
] ]
}, },
@@ -299,6 +303,9 @@
{ {
"group": "CSS Transitions", "group": "CSS Transitions",
"pages": [ "pages": [
"catalog/blocks/hw-scribble-transition",
"catalog/blocks/mk-clone-wall-transition",
"catalog/blocks/organic-light-leak-overlay",
"catalog/blocks/transitions-3d", "catalog/blocks/transitions-3d",
"catalog/blocks/transitions-blur", "catalog/blocks/transitions-blur",
"catalog/blocks/transitions-cover", "catalog/blocks/transitions-cover",
@@ -320,54 +327,6 @@
"catalog/blocks/app-showcase", "catalog/blocks/app-showcase",
"catalog/blocks/apple-money-count", "catalog/blocks/apple-money-count",
"catalog/blocks/blue-sweater-intro-video", "catalog/blocks/blue-sweater-intro-video",
"catalog/blocks/north-korea-locked-down",
"catalog/blocks/nyc-paris-flight",
"catalog/blocks/ui-3d-reveal",
"catalog/blocks/vpn-youtube-spot"
]
},
{
"group": "Data",
"pages": [
"catalog/blocks/data-chart",
"catalog/blocks/spain-map",
"catalog/blocks/us-map",
"catalog/blocks/us-map-bubble",
"catalog/blocks/us-map-flow",
"catalog/blocks/us-map-hex",
"catalog/blocks/world-map"
]
},
{
"group": "Effects",
"pages": [
"catalog/components/grain-overlay",
"catalog/components/grid-pixelate-wipe",
"catalog/components/parallax-unzoom",
"catalog/components/parallax-zoom",
"catalog/components/shimmer-sweep",
"catalog/components/vignette"
]
},
{
"group": "Text Effects",
"pages": [
"catalog/components/caption-blend-difference",
"catalog/components/morph-text",
"catalog/components/texture-mask-text"
]
},
{
"group": "Blocks",
"pages": [
"catalog/blocks/flowchart",
"catalog/blocks/flowchart-vertical",
"catalog/blocks/logo-outro"
]
},
{
"group": "Code Snippets",
"pages": [
"catalog/blocks/code-snippet-apple-terminal-basic", "catalog/blocks/code-snippet-apple-terminal-basic",
"catalog/blocks/code-snippet-apple-terminal-clear-dark", "catalog/blocks/code-snippet-apple-terminal-clear-dark",
"catalog/blocks/code-snippet-apple-terminal-clear-light", "catalog/blocks/code-snippet-apple-terminal-clear-light",
@@ -391,7 +350,72 @@
"catalog/blocks/code-snippet-monokai", "catalog/blocks/code-snippet-monokai",
"catalog/blocks/code-snippet-solarized-light", "catalog/blocks/code-snippet-solarized-light",
"catalog/blocks/code-snippet-visual-studio-dark", "catalog/blocks/code-snippet-visual-studio-dark",
"catalog/blocks/code-snippet-visual-studio-light" "catalog/blocks/code-snippet-visual-studio-light",
"catalog/blocks/north-korea-locked-down",
"catalog/blocks/nyc-paris-flight",
"catalog/blocks/ui-3d-reveal",
"catalog/blocks/vpn-youtube-spot"
]
},
{
"group": "Data",
"pages": [
"catalog/blocks/data-chart",
"catalog/blocks/mk-line-graph",
"catalog/blocks/spain-map",
"catalog/blocks/us-map",
"catalog/blocks/us-map-bubble",
"catalog/blocks/us-map-flow",
"catalog/blocks/us-map-hex",
"catalog/blocks/world-map"
]
},
{
"group": "Effects",
"pages": [
"catalog/components/caption-blend-difference",
"catalog/components/grain-overlay",
"catalog/components/grid-pixelate-wipe",
"catalog/components/hw-arrow",
"catalog/components/hw-boil",
"catalog/components/hw-box-label",
"catalog/components/hw-callout-circle",
"catalog/components/hw-underline",
"catalog/components/mk-emphasis-type",
"catalog/components/mk-usage-arc",
"catalog/components/morph-text",
"catalog/components/motion-blur",
"catalog/components/parallax-unzoom",
"catalog/components/parallax-zoom",
"catalog/components/shimmer-sweep",
"catalog/components/texture-mask-text",
"catalog/components/vignette",
"catalog/components/yt-camera-move",
"catalog/components/yt-circle-pointer",
"catalog/components/yt-feather-highlight",
"catalog/components/yt-screen-warp"
]
},
{
"group": "Blocks",
"pages": [
"catalog/blocks/camcorder-hud",
"catalog/blocks/flowchart",
"catalog/blocks/flowchart-vertical",
"catalog/blocks/hw-frame",
"catalog/blocks/hw-path-text",
"catalog/blocks/hw-pipeline",
"catalog/blocks/hw-text-cloud",
"catalog/blocks/hw-title",
"catalog/blocks/logo-outro",
"catalog/blocks/mk-background",
"catalog/blocks/mk-placeholder-grid",
"catalog/blocks/mk-progress-stat",
"catalog/blocks/mk-specs-list",
"catalog/blocks/yt-lcd-background",
"catalog/blocks/yt-logo-intro",
"catalog/blocks/yt-prism-title",
"catalog/blocks/yt-vertical-fill"
] ]
} }
] ]
File diff suppressed because it is too large Load Diff
+196
View File
@@ -0,0 +1,196 @@
<!doctype html>
<!-- Demo plate for the catalog preview. Identical to hw-path-text.html except the
root background, which is opaque here so the transparent overlay reads.
Not installed by `hyperframes add` - registry-item.json lists hw-path-text.html only. -->
<html lang="en">
<head>
<meta charset="utf-8" />
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<style>
@font-face {
font-family: "Caveat";
src: url("assets/fonts/Caveat-700-latin.woff2") format("woff2");
font-weight: 700;
font-display: block;
}
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #17161a;
overflow: hidden;
}
#hw-pt-root {
--hw-font-print: "Caveat", cursive;
--hw-font-script: "Caveat", cursive;
--hw-ink: #f4f2ec;
position: relative;
width: 1920px;
height: 1080px;
overflow: hidden;
background: transparent;
font-family: var(--hw-font-print, "Caveat", cursive);
}
#hw-pt-svg {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
overflow: visible;
}
/* The textPath carrier is geometry only — without fill:none it inherits
SVG's default black fill and renders as a solid blob under the text. */
#hw-pt-path {
fill: none;
}
#hw-pt-guide {
fill: none;
stroke: rgba(244, 242, 236, 0.22);
stroke-width: 3;
stroke-dasharray: 2 14;
stroke-linecap: round;
}
#hw-pt-svg text {
font-family: var(--hw-font-print, "Caveat", cursive);
font-weight: 700;
fill: var(--hw-ink);
}
#hw-pt-svg tspan {
opacity: 0;
}
</style>
</head>
<body>
<div
id="hw-pt-root"
data-composition-id="hw-path-text"
data-start="0"
data-duration="6"
data-width="1920"
data-height="1080"
>
<svg id="hw-pt-svg" viewBox="0 0 1920 1080">
<path id="hw-pt-path" d=""></path>
<path id="hw-pt-guide" d=""></path>
<text id="hw-pt-text" font-size="92">
<textPath id="hw-pt-tp" href="#hw-pt-path" startOffset="4%"></textPath>
</text>
</svg>
<div
id="hw-pt-drv"
class="clip"
data-start="0"
data-duration="6"
data-track-index="0"
style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
></div>
</div>
<script>
(function () {
window.__timelines = window.__timelines || {};
/* ---- published parameters ----
Handwriting along any path — custom path text, natively via
SVG textPath (no Fusion overlay). CONFIG.d is the whole design. */
var CONFIG = {
text: "come on this wild journey with me...",
d: "M 220 700 C 520 480, 900 820, 1230 560 S 1700 380, 1780 420",
fontSize: 92,
showGuide: true, // faint dotted guide under the writing
typeCharDelay: 0.05,
seed: 12,
boil: { enabled: true, amp: 1.4, rot: 0.3, frameDrop: 3 },
animationOut: true,
};
var DUR = 6;
function clamp(v, lo, hi) {
return Math.min(hi, Math.max(lo, v));
}
var OUT = clamp(DUR * 0.06, 0.25, 0.6);
/* hw family runtime */
window.hwOnUpdate = function (tl, fn) {
if (!tl.__hwRenders) {
tl.__hwRenders = [];
tl.eventCallback("onUpdate", function () {
for (var i = 0; i < tl.__hwRenders.length; i++) tl.__hwRenders[i]();
});
}
tl.__hwRenders.push(fn);
fn();
};
window.hwHash = function (n, seed) {
var x = Math.sin(n * 127.1 + (seed || 1) * 311.7) * 43758.5453;
return (x - Math.floor(x)) * 2 - 1;
};
window.hwBoil = function (tl, target, opts) {
opts = opts || {};
var amp = opts.amp !== undefined ? opts.amp : 1.6;
var rot = opts.rot !== undefined ? opts.rot : 0.5;
var fps = opts.fps || 30;
var drop = opts.frameDrop || 3;
var seed = opts.seed || 1;
var els = gsap.utils.toArray(target);
window.hwOnUpdate(tl, function () {
var step = Math.floor((tl.time() * fps) / drop);
for (var i = 0; i < els.length; i++) {
gsap.set(els[i], {
x: window.hwHash(step * 3 + i * 97, seed) * amp,
y: window.hwHash(step * 3 + 1 + i * 97, seed) * amp,
rotation: (opts.baseRot || 0) + window.hwHash(step * 3 + 2 + i * 97, seed) * rot,
});
}
});
};
document.getElementById("hw-pt-path").setAttribute("d", CONFIG.d);
if (CONFIG.showGuide) document.getElementById("hw-pt-guide").setAttribute("d", CONFIG.d);
document.getElementById("hw-pt-text").setAttribute("font-size", CONFIG.fontSize);
/* per-char tspans along the path */
var NS = "http://www.w3.org/2000/svg";
var tp = document.getElementById("hw-pt-tp");
Array.from(CONFIG.text).forEach(function (ch) {
var t = document.createElementNS(NS, "tspan");
t.textContent = ch;
tp.appendChild(t);
});
var tl = gsap.timeline({ paused: true });
/* guide draws first, then the handwriting types along it */
var guide = document.getElementById("hw-pt-guide");
if (CONFIG.showGuide) {
var glen = guide.getTotalLength();
gsap.set(guide, { strokeDasharray: "2 14", strokeDashoffset: glen });
tl.to(guide, { strokeDashoffset: 0, duration: 0.9, ease: "power2.inOut" }, 0.2);
}
var chars = tp.querySelectorAll("tspan");
var delay = Math.min(CONFIG.typeCharDelay, 2.6 / Math.max(1, chars.length));
tl.to(chars, { opacity: 1, duration: 0.03, stagger: delay, ease: "none" }, 0.7);
if (CONFIG.boil.enabled) {
window.hwBoil(tl, "#hw-pt-svg", {
amp: CONFIG.boil.amp,
rot: CONFIG.boil.rot,
frameDrop: CONFIG.boil.frameDrop,
seed: CONFIG.seed,
});
}
if (CONFIG.animationOut) {
tl.to("#hw-pt-svg", { opacity: 0, duration: OUT, ease: "power2.in" }, DUR - OUT - 0.05);
}
tl.set("#hw-pt-root", { visibility: "hidden" }, DUR - 0.02);
window.__timelines["hw-path-text"] = tl;
})();
</script>
</body>
</html>
+5
View File
@@ -40,6 +40,11 @@
height: 100%; height: 100%;
overflow: visible; overflow: visible;
} }
/* The textPath carrier is geometry only — without fill:none it inherits
SVG's default black fill and renders as a solid blob under the text. */
#hw-pt-path {
fill: none;
}
#hw-pt-guide { #hw-pt-guide {
fill: none; fill: none;
stroke: rgba(244, 242, 236, 0.22); stroke: rgba(244, 242, 236, 0.22);
@@ -4,7 +4,7 @@
"type": "hyperframes:block", "type": "hyperframes:block",
"title": "Text Along a Path", "title": "Text Along a Path",
"description": "Handwriting flowing along any SVG path with a per-character reveal; the path is the only geometry input", "description": "Handwriting flowing along any SVG path with a per-character reveal; the path is the only geometry input",
"tags": ["handwritten", "typography", "path"], "tags": ["handwritten", "typography", "path", "overlay"],
"dimensions": { "dimensions": {
"width": 1920, "width": 1920,
"height": 1080 "height": 1080
+271
View File
@@ -0,0 +1,271 @@
<!doctype html>
<!-- Demo plate for the catalog preview. Identical to hw-pipeline.html except the
root background, which is opaque here so the transparent overlay reads.
Not installed by `hyperframes add` - registry-item.json lists hw-pipeline.html only. -->
<html lang="en">
<head>
<meta charset="utf-8" />
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<style>
@font-face {
font-family: "Caveat";
src: url("assets/fonts/Caveat-700-latin.woff2") format("woff2");
font-weight: 700;
font-display: block;
}
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #17161a;
overflow: hidden;
}
#hw-pl-root {
--hw-font-print: "Caveat", cursive;
--hw-font-script: "Caveat", cursive;
--hw-ink: #f4f2ec;
--hw-accent: #6d6dff;
position: relative;
width: 1920px;
height: 1080px;
overflow: hidden;
background: transparent; /* overlays footage or boards */
font-family: var(--hw-font-print, "Caveat", cursive);
}
#hw-pl-svg {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
overflow: visible;
}
#hw-pl-svg path {
fill: none;
stroke-linecap: round;
stroke-linejoin: round;
}
.hw-pl-box {
stroke: var(--hw-ink);
stroke-width: 6;
}
.hw-pl-conn {
stroke: var(--hw-accent);
stroke-width: 5;
}
.hw-pl-label {
position: absolute;
text-align: center;
font-weight: 700;
color: var(--hw-ink);
white-space: nowrap;
opacity: 0;
text-shadow: 0 2px 14px rgba(0, 0, 0, 0.4);
}
</style>
</head>
<body>
<div
id="hw-pl-root"
data-composition-id="hw-pipeline"
data-start="0"
data-duration="7"
data-width="1920"
data-height="1080"
>
<svg id="hw-pl-svg" viewBox="0 0 1920 1080"></svg>
<div id="hw-pl-labels"></div>
<div
id="hw-pl-drv"
class="clip"
data-start="0"
data-duration="7"
data-track-index="0"
style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
></div>
</div>
<script>
(function () {
window.__timelines = window.__timelines || {};
/* ---- published parameters ----
A flowchart "pipeline": wobbled boxes joined by curved
connectors, drawing on in sequence, all boiling. */
var CONFIG = {
nodes: [{ label: "Idea" }, { label: "Record" }, { label: "Shine!" }],
boxW: 320,
boxH: 170,
gap: 190, // px between boxes
y: 455, // top of the row; x centers automatically
fontSize: 56,
seed: 11,
boil: { enabled: true, amp: 1.6, rot: 0.4, frameDrop: 3 },
animationOut: true,
};
var DUR = 7;
function clamp(v, lo, hi) {
return Math.min(hi, Math.max(lo, v));
}
var OUT = clamp(DUR * 0.06, 0.25, 0.6);
/* hw family runtime */
window.hwOnUpdate = function (tl, fn) {
if (!tl.__hwRenders) {
tl.__hwRenders = [];
tl.eventCallback("onUpdate", function () {
for (var i = 0; i < tl.__hwRenders.length; i++) tl.__hwRenders[i]();
});
}
tl.__hwRenders.push(fn);
fn();
};
window.hwHash = function (n, seed) {
var x = Math.sin(n * 127.1 + (seed || 1) * 311.7) * 43758.5453;
return (x - Math.floor(x)) * 2 - 1;
};
window.hwBoil = function (tl, target, opts) {
opts = opts || {};
var amp = opts.amp !== undefined ? opts.amp : 1.6;
var rot = opts.rot !== undefined ? opts.rot : 0.5;
var fps = opts.fps || 30;
var drop = opts.frameDrop || 3;
var seed = opts.seed || 1;
var els = gsap.utils.toArray(target);
window.hwOnUpdate(tl, function () {
var step = Math.floor((tl.time() * fps) / drop);
for (var i = 0; i < els.length; i++) {
gsap.set(els[i], {
x: window.hwHash(step * 3 + i * 97, seed) * amp,
y: window.hwHash(step * 3 + 1 + i * 97, seed) * amp,
rotation: (opts.baseRot || 0) + window.hwHash(step * 3 + 2 + i * 97, seed) * rot,
});
}
});
};
window.hwWobbleRect = function (w, h, r, seed, amp) {
amp = amp === undefined ? 3.5 : amp;
function wob(n) {
return window.hwHash(n, seed) * amp;
}
var d = "M " + (r + wob(1)) + " " + wob(2);
d += " L " + (w / 2 + wob(3)) + " " + wob(4);
d += " L " + (w - r + wob(5)) + " " + wob(6);
d += " Q " + (w + wob(7)) + " " + wob(8) + " " + (w + wob(9)) + " " + (r + wob(10));
d += " L " + (w + wob(11)) + " " + (h / 2 + wob(12));
d += " L " + (w + wob(13)) + " " + (h - r + wob(14));
d += " Q " + (w + wob(15)) + " " + (h + wob(16)) + " " + (w - r + wob(17)) + " " + (h + wob(18));
d += " L " + (w / 2 + wob(19)) + " " + (h + wob(20));
d += " L " + (r + wob(21)) + " " + (h + wob(22));
d += " Q " + wob(23) + " " + (h + wob(24)) + " " + wob(25) + " " + (h - r + wob(26));
d += " L " + wob(27) + " " + (h / 2 + wob(28));
d += " L " + wob(29) + " " + (r + wob(30));
d += " Q " + wob(31) + " " + wob(32) + " " + (r + wob(33)) + " " + wob(34);
return d;
};
var NS = "http://www.w3.org/2000/svg";
var svg = document.getElementById("hw-pl-svg");
var labels = document.getElementById("hw-pl-labels");
var n = CONFIG.nodes.length;
var totalW = n * CONFIG.boxW + (n - 1) * CONFIG.gap;
var x0 = (1920 - totalW) / 2;
var boxes = [],
conns = [],
labelEls = [];
CONFIG.nodes.forEach(function (node, i) {
var bx = x0 + i * (CONFIG.boxW + CONFIG.gap);
/* outer g owns the translate (GSAP transforms REPLACE the SVG
transform attribute — never boil the element that carries it);
inner g is the boil target */
var gOuter = document.createElementNS(NS, "g");
gOuter.setAttribute("transform", "translate(" + bx + " " + CONFIG.y + ")");
var g = document.createElementNS(NS, "g");
g.setAttribute("id", "hw-pl-box-g-" + i);
gOuter.appendChild(g);
var p = document.createElementNS(NS, "path");
p.setAttribute("class", "hw-pl-box");
p.setAttribute("id", "hw-pl-box-" + i);
p.setAttribute("d", window.hwWobbleRect(CONFIG.boxW, CONFIG.boxH, 24, CONFIG.seed + i));
g.appendChild(p);
svg.appendChild(gOuter);
boxes.push(p);
var label = document.createElement("div");
label.className = "hw-pl-label";
label.id = "hw-pl-label-" + i;
label.textContent = node.label;
label.style.left = bx + "px";
label.style.top = CONFIG.y + CONFIG.boxH / 2 - CONFIG.fontSize * 0.62 + "px";
label.style.width = CONFIG.boxW + "px";
label.style.fontSize = CONFIG.fontSize + "px";
labels.appendChild(label);
labelEls.push(label);
if (i < n - 1) {
var cx = bx + CONFIG.boxW,
cy = CONFIG.y + CONFIG.boxH / 2;
var ex = cx + CONFIG.gap,
ey = cy;
var c = document.createElementNS(NS, "path");
c.setAttribute("class", "hw-pl-conn");
c.setAttribute("id", "hw-pl-conn-" + i);
c.setAttribute(
"d",
"M " + (cx + 10) + " " + cy +
" Q " + (cx + CONFIG.gap / 2 + window.hwHash(i * 9 + 2, CONFIG.seed) * 16) + " " + (cy - 46 - window.hwHash(i * 9 + 3, CONFIG.seed) * 12) +
" " + (ex - 10) + " " + ey +
" M " + (ex - 10) + " " + ey + " l -30 -14 M " + (ex - 10) + " " + ey + " l -26 18"
);
svg.appendChild(c);
conns.push(c);
}
});
var tl = gsap.timeline({ paused: true });
/* sequence: box draws -> label -> connector -> next box... */
var t = 0.3;
CONFIG.nodes.forEach(function (_, i) {
var p = boxes[i];
var len = p.getTotalLength();
gsap.set(p, { strokeDasharray: len, strokeDashoffset: len });
tl.to(p, { strokeDashoffset: 0, duration: 0.65, ease: "power2.inOut" }, t);
tl.to(labelEls[i], { opacity: 1, duration: 0.35, ease: "power2.out" }, t + 0.45);
t += 0.7;
if (i < conns.length) {
var c = conns[i];
var cl = c.getTotalLength();
gsap.set(c, { strokeDasharray: cl, strokeDashoffset: cl });
tl.to(c, { strokeDashoffset: 0, duration: 0.45, ease: "power2.inOut" }, t);
t += 0.5;
}
});
if (CONFIG.boil.enabled) {
var targets = boxes.map(function (_, i) {
return "#hw-pl-box-g-" + i;
});
window.hwBoil(tl, targets.concat([".hw-pl-conn", ".hw-pl-label"]), {
amp: CONFIG.boil.amp,
rot: CONFIG.boil.rot,
frameDrop: CONFIG.boil.frameDrop,
seed: CONFIG.seed,
});
}
if (CONFIG.animationOut) {
tl.to([svg, labels], { opacity: 0, duration: OUT, ease: "power2.in" }, DUR - OUT - 0.05);
}
tl.set("#hw-pl-root", { visibility: "hidden" }, DUR - 0.02);
window.__timelines["hw-pipeline"] = tl;
})();
</script>
</body>
</html>
@@ -4,7 +4,7 @@
"type": "hyperframes:block", "type": "hyperframes:block",
"title": "Hand-Drawn Flowchart", "title": "Hand-Drawn Flowchart",
"description": "Wobbled boxes with handwritten labels joined by curved connectors, drawing on in sequence from a node list", "description": "Wobbled boxes with handwritten labels joined by curved connectors, drawing on in sequence from a node list",
"tags": ["handwritten", "diagram", "data-viz"], "tags": ["handwritten", "diagram", "data-viz", "overlay"],
"dimensions": { "dimensions": {
"width": 1920, "width": 1920,
"height": 1080 "height": 1080
+246
View File
@@ -0,0 +1,246 @@
<!doctype html>
<!-- Demo plate for the catalog preview. Identical to hw-title.html except the
root background, which is opaque here so the transparent overlay reads.
Not installed by `hyperframes add` - registry-item.json lists hw-title.html only. -->
<html lang="en">
<head>
<meta charset="utf-8" />
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<style>
@font-face {
font-family: "Caveat";
src: url("assets/fonts/Caveat-700-latin.woff2") format("woff2");
font-weight: 700;
font-display: block;
}
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #17161a;
overflow: hidden;
}
#hw-t-root {
/* ---- hw tokens ---- */
--hw-font-print: "Caveat", cursive;
--hw-font-script: "Caveat", cursive;
--hw-ink: #f4f2ec;
--hw-ink-dark: #26241f;
--hw-accent-warm: #ffb020;
position: relative;
width: 1920px;
height: 1080px;
overflow: hidden;
background: transparent; /* overlays footage or a board */
font-family: var(--hw-font-print, "Caveat", cursive);
}
#hw-t-wrap {
position: absolute;
pointer-events: none;
}
#hw-t-line {
font-weight: 700;
line-height: 1.1;
white-space: nowrap;
text-shadow: 0 2px 18px rgba(0, 0, 0, 0.4);
}
#hw-t-line .w {
display: inline;
}
#hw-t-under {
overflow: visible;
display: block;
}
#hw-t-under path {
fill: none;
stroke: var(--hw-accent-warm);
stroke-width: 7;
stroke-linecap: round;
}
</style>
</head>
<body>
<div
id="hw-t-root"
data-composition-id="hw-title"
data-start="0"
data-duration="6"
data-width="1920"
data-height="1080"
>
<div id="hw-t-wrap">
<div id="hw-t-line"></div>
<svg id="hw-t-under" width="10" height="34"><path id="hw-t-under-path"></path></svg>
</div>
<div
id="hw-t-drv"
class="clip"
data-start="0"
data-duration="6"
data-track-index="0"
style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
></div>
</div>
<script>
(function () {
window.__timelines = window.__timelines || {};
/* ---- published parameters ----
Handwritten title with the highlight-sweep scalar (the shared
mk-callout-highlight mechanic) + squiggle underline + the boil.
Demonstrates the dispatcher composing sweep + boil on one
timeline (learning: one onUpdate — everything through hwOnUpdate). */
var CONFIG = {
text: "shine like the star you are",
fontSize: 120,
scheme: "chalk", // "chalk" (white, over footage) | "ink" (dark, boards)
sweep: { enabled: true, start: 0.8, end: 3.2 },
underline: { enabled: true, wobble: 6 },
boil: { enabled: true, amp: 1.4, rot: 0.3, frameDrop: 3 },
seed: 8,
x: null, // null = centered
y: null,
animationIn: true,
animationOut: true,
};
var DUR = 6;
function clamp(v, lo, hi) {
return Math.min(hi, Math.max(lo, v));
}
var IN = clamp(DUR * 0.08, 0.3, 0.8);
var OUT = clamp(DUR * 0.06, 0.25, 0.6);
/* hw family runtime */
window.hwOnUpdate = function (tl, fn) {
if (!tl.__hwRenders) {
tl.__hwRenders = [];
tl.eventCallback("onUpdate", function () {
for (var i = 0; i < tl.__hwRenders.length; i++) tl.__hwRenders[i]();
});
}
tl.__hwRenders.push(fn);
fn();
};
window.hwHash = function (n, seed) {
var x = Math.sin(n * 127.1 + (seed || 1) * 311.7) * 43758.5453;
return (x - Math.floor(x)) * 2 - 1;
};
window.hwBoil = function (tl, target, opts) {
opts = opts || {};
var amp = opts.amp !== undefined ? opts.amp : 1.6;
var rot = opts.rot !== undefined ? opts.rot : 0.5;
var fps = opts.fps || 30;
var drop = opts.frameDrop || 3;
var seed = opts.seed || 1;
var els = gsap.utils.toArray(target);
window.hwOnUpdate(tl, function () {
var step = Math.floor((tl.time() * fps) / drop);
for (var i = 0; i < els.length; i++) {
gsap.set(els[i], {
x: window.hwHash(step * 3 + i * 97, seed) * amp,
y: window.hwHash(step * 3 + 1 + i * 97, seed) * amp,
rotation: (opts.baseRot || 0) + window.hwHash(step * 3 + 2 + i * 97, seed) * rot,
});
}
});
};
var FULL = CONFIG.scheme === "ink" ? "#26241f" : "#f4f2ec";
var FAINT = CONFIG.scheme === "ink" ? "rgba(38,36,31,0.3)" : "rgba(244,242,236,0.32)";
var wrap = document.getElementById("hw-t-wrap");
var line = document.getElementById("hw-t-line");
line.style.fontSize = CONFIG.fontSize + "px";
/* word spans */
var wordEls = [];
CONFIG.text.split(/\s+/).forEach(function (w, i) {
if (i > 0) line.appendChild(document.createTextNode(" "));
var span = document.createElement("span");
span.className = "w";
span.textContent = w;
span.style.color = CONFIG.sweep.enabled ? FAINT : FULL;
line.appendChild(span);
wordEls.push(span);
});
/* measure + position; build squiggle underline to match width */
var rect = line.getBoundingClientRect();
wrap.style.left = (CONFIG.x === null ? Math.round((1920 - rect.width) / 2) : CONFIG.x) + "px";
wrap.style.top = (CONFIG.y === null ? Math.round((1080 - rect.height) / 2) - 20 : CONFIG.y) + "px";
var underPath = document.getElementById("hw-t-under-path");
if (CONFIG.underline.enabled) {
var uw = Math.round(rect.width);
var svg = document.getElementById("hw-t-under");
svg.setAttribute("width", uw);
svg.setAttribute("viewBox", "0 0 " + uw + " 34");
var segs = Math.max(6, Math.round(uw / 90));
var d = "M 2 " + (16 + window.hwHash(0, CONFIG.seed) * 4).toFixed(1);
for (var i = 1; i <= segs; i++) {
var x = (uw / segs) * i;
var y = 16 + window.hwHash(i * 5 + 1, CONFIG.seed) * CONFIG.underline.wobble;
var cxp = x - uw / segs / 2 + window.hwHash(i * 5 + 2, CONFIG.seed) * 10;
var cyp = 16 + (i % 2 ? -1 : 1) * (8 + window.hwHash(i * 5 + 3, CONFIG.seed) * 4);
d += " Q " + cxp.toFixed(1) + " " + cyp.toFixed(1) + " " + x.toFixed(1) + " " + y.toFixed(1);
}
underPath.setAttribute("d", d);
} else {
document.getElementById("hw-t-under").style.display = "none";
}
var tl = gsap.timeline({ paused: true });
if (CONFIG.animationIn) {
gsap.set(wrap, { opacity: 0 });
tl.to(wrap, { opacity: 1, duration: IN, ease: "power2.out" }, 0.1);
}
/* the sweep scalar — registered through the dispatcher so it
coexists with the boil on the same timeline */
if (CONFIG.sweep.enabled) {
var mix = gsap.utils.interpolate(FAINT, FULL);
var st = { p: 0 };
window.hwOnUpdate(tl, function () {
var k = st.p * wordEls.length;
for (var i = 0; i < wordEls.length; i++) {
var f = Math.max(0, Math.min(1, k - i));
wordEls[i].style.color = mix(f);
}
});
tl.to(st, { p: 1, duration: CONFIG.sweep.end - CONFIG.sweep.start, ease: "none" }, CONFIG.sweep.start);
}
/* underline draws as the sweep lands */
if (CONFIG.underline.enabled) {
var len = underPath.getTotalLength();
gsap.set(underPath, { strokeDasharray: len, strokeDashoffset: len });
var uAt = CONFIG.sweep.enabled ? CONFIG.sweep.end - 0.2 : 0.8;
tl.to(underPath, { strokeDashoffset: 0, duration: 0.7, ease: "power2.inOut" }, uAt);
}
if (CONFIG.boil.enabled) {
window.hwBoil(tl, "#hw-t-wrap", {
amp: CONFIG.boil.amp,
rot: CONFIG.boil.rot,
frameDrop: CONFIG.boil.frameDrop,
seed: CONFIG.seed,
});
}
if (CONFIG.animationOut) {
tl.to(wrap, { opacity: 0, duration: OUT, ease: "power2.in" }, DUR - OUT - 0.05);
}
tl.set("#hw-t-root", { visibility: "hidden" }, DUR - 0.02);
window.__timelines["hw-title"] = tl;
})();
</script>
</body>
</html>
+1 -1
View File
@@ -4,7 +4,7 @@
"type": "hyperframes:block", "type": "hyperframes:block",
"title": "Handwritten Title", "title": "Handwritten Title",
"description": "Handwritten display text with a scalar-driven word-by-word highlight sweep and a seeded squiggle underline sized to the text; boils", "description": "Handwritten display text with a scalar-driven word-by-word highlight sweep and a seeded squiggle underline sized to the text; boils",
"tags": ["handwritten", "typography", "title-card"], "tags": ["handwritten", "typography", "title-card", "overlay"],
"dimensions": { "dimensions": {
"width": 1920, "width": 1920,
"height": 1080 "height": 1080
+11 -2
View File
@@ -57,7 +57,11 @@
letter-spacing: -0.01em; letter-spacing: -0.01em;
color: var(--mk-ink); color: var(--mk-ink);
font-variant-numeric: tabular-nums; font-variant-numeric: tabular-nums;
transform: translate(-50%, -100%); /* Horizontal centring only. The label's vertical offset is baked into
`top` instead of a CSS translate, because the entrance tween animates
`y` and GSAP rewrites this transform, dropping any vertical shift. */
transform: translate(-50%, 0);
line-height: 38px;
white-space: nowrap; white-space: nowrap;
} }
.mk-lg-xlabel { .mk-lg-xlabel {
@@ -197,11 +201,16 @@
if (CONFIG.showValues) { if (CONFIG.showValues) {
var val = document.createElement("div"); var val = document.createElement("div");
/* Series after the first label downward. With every series
labelling upward the numbers collide wherever the lines run
close together. GAP clears the dot radius plus line stroke. */
var below = si > 0;
var GAP = 22;
val.className = "mk-lg-value"; val.className = "mk-lg-value";
val.id = "mk-lg-val-" + si + "-" + i; val.id = "mk-lg-val-" + si + "-" + i;
val.textContent = v; val.textContent = v;
val.style.left = px(i) + "px"; val.style.left = px(i) + "px";
val.style.top = py(v) - 20 + "px"; val.style.top = py(v) + (below ? GAP : -(GAP + 38)) + "px";
labels.appendChild(val); labels.appendChild(val);
vals.push({ el: val, si: si, i: i }); vals.push({ el: val, si: si, i: i });
} }
-174
View File
@@ -1,174 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<style>
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: transparent;
overflow: hidden;
}
#mk-ls-root {
/* ---- mk tokens: override in the host to re-skin the family ---- */
--mk-font: "Inter", -apple-system, sans-serif;
--mk-ink: #1d1d1f;
--mk-ink-dim: #6e6e73;
--mk-hairline: 3px;
position: relative;
width: 1920px;
height: 1080px;
overflow: hidden;
background: transparent; /* overlays footage or mk-background */
font-family: var(--mk-font);
}
#mk-ls-svg {
position: absolute;
inset: 0;
}
#mk-ls-outline {
fill: none;
stroke: var(--mk-ink);
stroke-width: var(--mk-hairline);
}
#mk-ls-logo {
position: absolute;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
#mk-ls-logo img {
width: 72%;
height: 72%;
object-fit: contain;
}
#mk-ls-text {
font-weight: 600;
font-size: 120px;
letter-spacing: -0.03em;
color: var(--mk-ink);
}
#mk-ls-caption {
position: absolute;
width: 100%;
text-align: center;
font-weight: 500;
font-size: 32px;
color: var(--mk-ink-dim);
}
</style>
</head>
<body>
<div
id="mk-ls-root"
data-composition-id="mk-logo-sting"
data-start="0"
data-duration="3.5"
data-width="1920"
data-height="1080"
>
<svg id="mk-ls-svg" viewBox="0 0 1920 1080">
<rect id="mk-ls-outline"></rect>
</svg>
<div id="mk-ls-logo"><span id="mk-ls-text"></span></div>
<div id="mk-ls-caption"></div>
<div
id="mk-ls-drv"
class="clip"
data-start="0"
data-duration="3.5"
data-track-index="0"
style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
></div>
</div>
<script>
(function () {
window.__timelines = window.__timelines || {};
/* ---- published parameters (inspector-style CONFIG) ----
A logo preset: hairline rounded-square outline draws on, the
logo lands inside. logoType switches drop-zone image <-> wordmark
text (same animation shell — one logoType toggle). */
var CONFIG = {
// no scheme param — scheme-agnostic; re-skin via the --mk-* tokens
logoType: "text", // "text" | "image"
text: "HF",
src: null, // image path when logoType === "image"
boxSize: 340, // outline square, px
radius: 72,
caption: "Built with HyperFrames",
animationOut: true,
};
var W = 1920,
H = 1080,
DUR = 3.5;
var root = document.getElementById("mk-ls-root");
var outline = document.getElementById("mk-ls-outline");
var logo = document.getElementById("mk-ls-logo");
var textEl = document.getElementById("mk-ls-text");
var caption = document.getElementById("mk-ls-caption");
var S = CONFIG.boxSize;
var bx = (W - S) / 2,
by = (H - S) / 2 - 40;
outline.setAttribute("x", bx);
outline.setAttribute("y", by);
outline.setAttribute("width", S);
outline.setAttribute("height", S);
outline.setAttribute("rx", CONFIG.radius);
logo.style.left = bx + "px";
logo.style.top = by + "px";
logo.style.width = S + "px";
logo.style.height = S + "px";
logo.style.borderRadius = CONFIG.radius + "px";
if (CONFIG.logoType === "image" && CONFIG.src) {
textEl.remove();
var img = document.createElement("img");
img.src = CONFIG.src;
logo.appendChild(img);
} else {
textEl.textContent = CONFIG.text;
}
caption.textContent = CONFIG.caption;
caption.style.top = by + S + 48 + "px";
var tl = gsap.timeline({ paused: true });
/* outline draws on */
var len = outline.getTotalLength();
gsap.set(outline, { strokeDasharray: len, strokeDashoffset: len });
tl.to(outline, { strokeDashoffset: 0, duration: 1.0, ease: "power2.inOut" }, 0.15);
/* logo lands inside */
gsap.set(logo, { opacity: 0, scale: 0.82 });
tl.to(logo, { opacity: 1, scale: 1, duration: 0.6, ease: "back.out(1.2)" }, 0.85);
/* caption */
gsap.set(caption, { opacity: 0, y: 12 });
tl.to(caption, { opacity: 1, y: 0, duration: 0.5, ease: "power2.out" }, 1.35);
/* exit + hard kill */
if (CONFIG.animationOut) {
tl.to([logo, caption], { opacity: 0, duration: 0.35, ease: "power2.in" }, DUR - 0.55);
tl.to(outline, { opacity: 0, duration: 0.35, ease: "power2.in" }, DUR - 0.5);
}
tl.set(root, { visibility: "hidden" }, DUR - 0.02);
window.__timelines["mk-logo-sting"] = tl;
})();
</script>
</body>
</html>
@@ -1,20 +0,0 @@
{
"$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
"name": "mk-logo-sting",
"type": "hyperframes:block",
"title": "Outline Logo Sting",
"description": "A hairline rounded-square outline draws on and the logo lands inside \u2014 image or text wordmark in the same shell",
"tags": ["logo", "sting", "minimal"],
"dimensions": {
"width": 1920,
"height": 1080
},
"duration": 3.5,
"files": [
{
"path": "mk-logo-sting.html",
"target": "compositions/mk-logo-sting.html",
"type": "hyperframes:composition"
}
]
}
@@ -1,20 +0,0 @@
{
"$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
"name": "yt-doc-lower-third",
"type": "hyperframes:block",
"title": "Documentary Lower Third",
"description": "Name and role lower third: accent bar draws down, text slides in, hairline rule grows; overlay and light schemes",
"tags": ["lower-third", "title", "creator"],
"dimensions": {
"width": 1920,
"height": 1080
},
"duration": 6,
"files": [
{
"path": "yt-doc-lower-third.html",
"target": "compositions/yt-doc-lower-third.html",
"type": "hyperframes:composition"
}
]
}
@@ -1,169 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<style>
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: transparent;
overflow: hidden;
}
#yt-lt-root {
/* ---- yt tokens ---- */
--yt-font: "Inter", -apple-system, sans-serif;
--yt-ink: #1d1d1f;
--yt-ink-dim: #5a5a5f;
/* overlay-scheme text over footage — distinct from the --yt-ink board tokens */
--yt-text: #ffffff;
--yt-text-dim: rgba(255, 255, 255, 0.75);
--yt-red: #e62e2e;
position: relative;
width: 1920px;
height: 1080px;
overflow: hidden;
background: transparent; /* overlays footage */
font-family: var(--yt-font);
}
#yt-lt-group {
position: absolute;
display: flex;
gap: 26px;
align-items: stretch;
}
#yt-lt-bar {
flex: none;
width: 7px;
border-radius: 4px;
background: var(--yt-red);
transform-origin: top center;
}
#yt-lt-name {
font-weight: 700;
font-size: 56px;
line-height: 1.05;
letter-spacing: -0.015em;
}
#yt-lt-role {
margin-top: 8px;
font-weight: 500;
font-size: 30px;
letter-spacing: 0.02em;
}
#yt-lt-rule {
margin-top: 14px;
height: 2px;
width: 320px;
border-radius: 1px;
transform-origin: left center;
}
.yt-overlay #yt-lt-name {
color: var(--yt-text);
text-shadow: 0 2px 18px rgba(0, 0, 0, 0.55);
}
.yt-overlay #yt-lt-role {
color: var(--yt-text-dim);
text-shadow: 0 2px 14px rgba(0, 0, 0, 0.5);
}
.yt-overlay #yt-lt-rule {
background: rgba(255, 255, 255, 0.35);
}
.yt-light #yt-lt-name {
color: var(--yt-ink);
}
.yt-light #yt-lt-role {
color: var(--yt-ink-dim);
}
.yt-light #yt-lt-rule {
background: rgba(0, 0, 0, 0.18);
}
</style>
</head>
<body>
<div
id="yt-lt-root"
data-composition-id="yt-doc-lower-third"
data-start="0"
data-duration="6"
data-width="1920"
data-height="1080"
>
<div id="yt-lt-group">
<div id="yt-lt-bar"></div>
<div>
<div id="yt-lt-name"></div>
<div id="yt-lt-role"></div>
<div id="yt-lt-rule"></div>
</div>
</div>
<div
id="yt-lt-drv"
class="clip"
data-start="0"
data-duration="6"
data-track-index="0"
style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
></div>
</div>
<script>
(function () {
window.__timelines = window.__timelines || {};
/* ---- published parameters ----
The documentary lower third ("Lower 03" energy): red bar draws
down, name slides in, role follows, hairline rule grows. */
var CONFIG = {
scheme: "overlay", // "overlay" (white over footage) | "light" (ink)
name: "Alex Winters",
role: "Field Producer",
x: 120,
y: 800, // top of the group
animationIn: true,
animationOut: true,
};
var DUR = 6;
function clamp(v, lo, hi) {
return Math.min(hi, Math.max(lo, v));
}
var OUT = clamp(DUR * 0.06, 0.25, 0.6);
var root = document.getElementById("yt-lt-root");
var group = document.getElementById("yt-lt-group");
root.classList.add(CONFIG.scheme === "light" ? "yt-light" : "yt-overlay");
document.getElementById("yt-lt-name").textContent = CONFIG.name;
document.getElementById("yt-lt-role").textContent = CONFIG.role;
group.style.left = CONFIG.x + "px";
group.style.top = CONFIG.y + "px";
var tl = gsap.timeline({ paused: true });
if (CONFIG.animationIn) {
gsap.set("#yt-lt-bar", { scaleY: 0 });
gsap.set(["#yt-lt-name", "#yt-lt-role"], { opacity: 0, x: -28 });
gsap.set("#yt-lt-rule", { scaleX: 0 });
tl.to("#yt-lt-bar", { scaleY: 1, duration: 0.5, ease: "power2.inOut" }, 0.15);
tl.to("#yt-lt-name", { opacity: 1, x: 0, duration: 0.55, ease: "power3.out" }, 0.4);
tl.to("#yt-lt-role", { opacity: 1, x: 0, duration: 0.55, ease: "power3.out" }, 0.55);
tl.to("#yt-lt-rule", { scaleX: 1, duration: 0.6, ease: "power2.inOut" }, 0.7);
} else {
gsap.set("#yt-lt-bar", { scaleY: 1 });
}
if (CONFIG.animationOut) {
tl.to(group, { opacity: 0, x: -20, duration: OUT, ease: "power2.in" }, DUR - OUT - 0.05);
}
tl.set(root, { visibility: "hidden" }, DUR - 0.02);
window.__timelines["yt-doc-lower-third"] = tl;
})();
</script>
</body>
</html>
+185
View File
@@ -0,0 +1,185 @@
<!doctype html>
<!-- Demo plate for the catalog preview. Identical to hw-arrow.html except the
root background, which is opaque here so the transparent overlay reads.
Not installed by `hyperframes add` - registry-item.json lists hw-arrow.html only. -->
<html lang="en">
<head>
<meta charset="utf-8" />
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<style>
@font-face {
font-family: "Caveat";
src: url("assets/fonts/Caveat-700-latin.woff2") format("woff2");
font-weight: 700;
font-display: block;
}
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #17161a;
overflow: hidden;
}
/* ---- hw-arrow ------------------------------------------------------
Hand-wobbled draw-on arrow, boiling. Position the wrapper, pick a
curve preset, call the helpers. Requires the hw-boil helpers
(hwOnUpdate / hwHash / hwBoil) in the host script. */
.hw-arrow {
position: absolute;
pointer-events: none;
}
.hw-arrow svg {
width: 100%;
height: 100%;
overflow: visible;
}
.hw-arrow path {
fill: none;
stroke: var(--hw-arrow-color, var(--hw-ink, #f4f2ec));
stroke-width: var(--hw-arrow-w, 6);
stroke-linecap: round;
stroke-linejoin: round;
}
.hw-arrow.hw-soft path {
filter: blur(0.6px);
}
/* self-preview scene */
#hw-ar-scene {
position: absolute;
inset: 0;
background: linear-gradient(135deg, #274060 0%, #123 100%);
display: flex;
align-items: center;
justify-content: center;
font-family: var(--hw-font-print, "Caveat", cursive);
font-weight: 700;
font-size: 80px;
color: rgba(244, 242, 236, 0.92);
}
</style>
</head>
<body>
<!-- Self-preview. WIRING: copy the .hw-arrow CSS, the markup pattern,
and the hwArrowPath / hwArrowOn / hwArrowOff helpers (plus the
hw-boil helpers) into your host. Then:
document.querySelector("#hw-my-arrow path")
.setAttribute("d", hwArrowPath(360, 160, "swoop", 3));
hwArrowOn(tl, "#hw-my-arrow", 2.0);
hwBoil(tl, "#hw-my-arrow", { frameDrop: 3, seed: 3 });
hwArrowOff(tl, "#hw-my-arrow", 8.0);
-->
<div
id="hw-ar-root"
data-composition-id="hw-arrow"
data-start="0"
data-duration="4"
data-width="1920"
data-height="1080"
>
<div id="hw-ar-scene"><span>point at things</span></div>
<div class="hw-arrow" id="hw-ar-demo" style="left: 560px; top: 320px; width: 380px; height: 180px">
<svg viewBox="0 0 380 180"><path id="hw-ar-demo-path"></path></svg>
</div>
<div
id="hw-ar-drv"
class="clip"
data-start="0"
data-duration="4"
data-track-index="0"
style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
></div>
</div>
<script>
(function () {
window.__timelines = window.__timelines || {};
/* ---- hw-boil helpers (shared family runtime — copy once per host) ---- */
window.hwOnUpdate = function (tl, fn) {
if (!tl.__hwRenders) {
tl.__hwRenders = [];
tl.eventCallback("onUpdate", function () {
for (var i = 0; i < tl.__hwRenders.length; i++) tl.__hwRenders[i]();
});
}
tl.__hwRenders.push(fn);
fn();
};
window.hwHash = function (n, seed) {
var x = Math.sin(n * 127.1 + (seed || 1) * 311.7) * 43758.5453;
return (x - Math.floor(x)) * 2 - 1;
};
window.hwBoil = function (tl, target, opts) {
opts = opts || {};
var amp = opts.amp !== undefined ? opts.amp : 1.6;
var rot = opts.rot !== undefined ? opts.rot : 0.5;
var fps = opts.fps || 30;
var drop = opts.frameDrop || 3;
var seed = opts.seed || 1;
var els = gsap.utils.toArray(target);
window.hwOnUpdate(tl, function () {
var step = Math.floor((tl.time() * fps) / drop);
for (var i = 0; i < els.length; i++) {
gsap.set(els[i], {
x: window.hwHash(step * 3 + i * 97, seed) * amp,
y: window.hwHash(step * 3 + 1 + i * 97, seed) * amp,
rotation: (opts.baseRot || 0) + window.hwHash(step * 3 + 2 + i * 97, seed) * rot,
});
}
});
};
// ---- hw-arrow helpers (copy these too) ----
// Wobbled arrow path in a w x h box, tail at left, head at right.
// curve: "straight" | "gentle" | "swoop"
window.hwArrowPath = function (w, h, curve, seed) {
seed = seed || 1;
var bend = curve === "swoop" ? 0.55 : curve === "gentle" ? 0.28 : 0.06;
function wob(v, n, range) {
return v + window.hwHash(n, seed) * range;
}
var x0 = wob(4, 1, 3),
y0 = wob(h * 0.72, 2, 4),
x1 = wob(w - 10, 3, 4),
y1 = wob(h * 0.3, 4, 4);
var cx1 = wob(w * 0.35, 5, 10),
cy1 = wob(h * (0.72 + bend * 0.5), 6, 8);
var cx2 = wob(w * 0.7, 7, 10),
cy2 = wob(h * (0.3 + bend), 8, 8);
var ang = Math.atan2(y1 - cy2, x1 - cx2);
function head(da, len) {
var a = ang + Math.PI + da;
return "M " + x1.toFixed(1) + " " + y1.toFixed(1) + " l " + (Math.cos(a) * len).toFixed(1) + " " + (Math.sin(a) * len).toFixed(1);
}
return (
"M " + x0.toFixed(1) + " " + y0.toFixed(1) +
" C " + cx1.toFixed(1) + " " + cy1.toFixed(1) + ", " + cx2.toFixed(1) + " " + cy2.toFixed(1) + ", " + x1.toFixed(1) + " " + y1.toFixed(1) +
" " + head(0.5, wob(34, 9, 6)) + " " + head(-0.5, wob(34, 10, 6))
);
};
window.hwArrowOn = function (tl, target, at, dur) {
var p = document.querySelector(target + " path");
var len = p.getTotalLength();
gsap.set(p, { strokeDasharray: len, strokeDashoffset: len });
tl.to(p, { strokeDashoffset: 0, duration: dur === undefined ? 0.7 : dur, ease: "power2.inOut" }, at);
};
window.hwArrowOff = function (tl, target, at) {
tl.to(target, { opacity: 0, duration: 0.3, ease: "power2.in" }, at);
};
// ---- self-preview ----
document.getElementById("hw-ar-demo-path").setAttribute("d", window.hwArrowPath(380, 180, "swoop", 7));
var tl = gsap.timeline({ paused: true });
window.hwArrowOn(tl, "#hw-ar-demo", 0.4);
window.hwBoil(tl, "#hw-ar-demo", { amp: 2, rot: 0.6, frameDrop: 3, seed: 7 });
window.hwArrowOff(tl, "#hw-ar-demo", 3.4);
tl.set("#hw-ar-root", { visibility: "hidden" }, 3.98);
window.__timelines["hw-arrow"] = tl;
})();
</script>
</body>
</html>
+218
View File
@@ -0,0 +1,218 @@
<!doctype html>
<!-- Demo plate for the catalog preview. Identical to hw-boil.html except the
root background, which is opaque here so the transparent overlay reads.
Not installed by `hyperframes add` - registry-item.json lists hw-boil.html only. -->
<html lang="en">
<head>
<meta charset="utf-8" />
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<style>
/* Caveat is NOT auto-resolved by the renderer — bundle it. Asset
paths are ROOT-RELATIVE (the project root is the base URL for all
compositions; ../ traversal is a lint error). Registry installs
ship the woff2 as an asset file targeting assets/fonts/. */
@font-face {
font-family: "Caveat";
src: url("assets/fonts/Caveat-700-latin.woff2") format("woff2");
font-weight: 700;
font-display: block;
}
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #17161a;
overflow: hidden;
}
/* ---- hw-boil -----------------------------------------------------
The hand-drawn "boil": jitter that re-poses only every N frames
(quake + frame-drop style). Pose is a pure function of quantized
timeline time through a seeded hash — fully seek-safe, renders
identically on every pass over the same frame.
RULES:
- hwBoil owns x/y/rotation of the boiled element. Entrance/exit
tweens go on a WRAPPER around it, never the boiled element.
- One timeline has ONE onUpdate — hwOnUpdate is the dispatcher
that lets the boil coexist with sweep scalars etc. Register
every onUpdate-style render through it. */
#hw-boil-root {
--hw-font-print: "Caveat", cursive;
--hw-font-script: "Caveat", cursive;
--hw-ink: #f4f2ec;
--hw-accent: #6d6dff;
position: relative;
width: 1920px;
height: 1080px;
overflow: hidden;
background: #17161a;
font-family: var(--hw-font-print, "Caveat", cursive);
}
#hw-boil-root svg {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
}
#hw-boil-root path {
fill: none;
stroke: var(--hw-ink);
stroke-width: 6;
stroke-linecap: round;
stroke-linejoin: round;
}
#hw-b-arrow {
stroke: var(--hw-accent);
stroke-width: 7;
}
.hw-b-label {
position: absolute;
font-weight: 700;
font-size: 84px;
color: var(--hw-ink);
white-space: nowrap;
}
.hw-b-tag {
position: absolute;
font-family: "Inter", ui-sans-serif, sans-serif;
font-weight: 500;
font-size: 22px;
letter-spacing: 0.12em;
color: rgba(244, 242, 236, 0.45);
}
</style>
</head>
<body>
<!-- Self-preview. WIRING: copy the hwOnUpdate / hwHash / hwBoil /
hwWobbleEllipse helpers into your host script, then:
hwBoil(tl, "#hw-my-annotation", { amp: 1.6, rot: 0.5, frameDrop: 3, seed: 2 });
frameDrop 3-4 = classic boil; 1 = frantic; boil: off = don't call it. -->
<div
id="hw-boil-root"
data-composition-id="hw-boil"
data-start="0"
data-duration="4"
data-width="1920"
data-height="1080"
>
<svg viewBox="0 0 1920 1080">
<g id="hw-b-circle-g"><path id="hw-b-circle"></path></g>
<g id="hw-b-arrow-g">
<path id="hw-b-arrow" d="M 640 640 C 760 700, 980 720, 1130 620 M 1130 620 l -64 -6 M 1130 620 l -18 58"></path>
</g>
</svg>
<div class="hw-b-label" id="hw-b-word" style="left: 1230px; top: 560px">the boil</div>
<div class="hw-b-tag" style="left: 350px; top: 782px">FRAME DROP 3</div>
<div class="hw-b-tag" style="left: 700px; top: 782px">FRAME DROP 1</div>
<div class="hw-b-tag" style="left: 1230px; top: 700px">TEXT, AMP 1.2</div>
<div
id="hw-b-drv"
class="clip"
data-start="0"
data-duration="4"
data-track-index="0"
style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
></div>
</div>
<script>
(function () {
window.__timelines = window.__timelines || {};
// ---- copy these helpers into the host script ----
// Dispatcher: a GSAP timeline has ONE onUpdate. Register every
// per-frame render (boils, sweep scalars) through this instead of
// setting eventCallback directly, and they all coexist.
window.hwOnUpdate = function (tl, fn) {
if (!tl.__hwRenders) {
tl.__hwRenders = [];
tl.eventCallback("onUpdate", function () {
for (var i = 0; i < tl.__hwRenders.length; i++) tl.__hwRenders[i]();
});
}
tl.__hwRenders.push(fn);
fn();
};
// Seeded hash -> [-1, 1]. Pure function of n — no Math.random().
window.hwHash = function (n, seed) {
var x = Math.sin(n * 127.1 + (seed || 1) * 311.7) * 43758.5453;
return (x - Math.floor(x)) * 2 - 1;
};
// The boil. Owns x/y/rotation of its targets — put entrance tweens
// on a wrapper. frameDrop = re-pose every N frames (3-4 classic).
window.hwBoil = function (tl, target, opts) {
opts = opts || {};
var amp = opts.amp !== undefined ? opts.amp : 1.6;
var rot = opts.rot !== undefined ? opts.rot : 0.5;
var fps = opts.fps || 30;
var drop = opts.frameDrop || 3;
var seed = opts.seed || 1;
var els = gsap.utils.toArray(target);
window.hwOnUpdate(tl, function () {
var step = Math.floor((tl.time() * fps) / drop);
for (var i = 0; i < els.length; i++) {
gsap.set(els[i], {
x: window.hwHash(step * 3 + i * 97, seed) * amp,
y: window.hwHash(step * 3 + 1 + i * 97, seed) * amp,
rotation: (opts.baseRot || 0) + window.hwHash(step * 3 + 2 + i * 97, seed) * rot,
});
}
});
};
// Seeded hand-wobbled ellipse path (smooth quadratic chain).
window.hwWobbleEllipse = function (cx, cy, rx, ry, seed, wobblePct) {
wobblePct = wobblePct === undefined ? 3 : wobblePct;
var N = 14,
pts = [];
for (var i = 0; i < N; i++) {
var a = (i / N) * Math.PI * 2;
var wr = 1 + window.hwHash(i * 13 + 5, seed) * (wobblePct / 100);
pts.push([cx + Math.cos(a) * rx * wr, cy + Math.sin(a) * ry * wr]);
}
var mx = (pts[0][0] + pts[N - 1][0]) / 2,
my = (pts[0][1] + pts[N - 1][1]) / 2;
var d = "M" + mx.toFixed(1) + " " + my.toFixed(1);
for (var j = 0; j < N; j++) {
var p = pts[j],
q = pts[(j + 1) % N];
d += " Q" + p[0].toFixed(1) + " " + p[1].toFixed(1) + " " + ((p[0] + q[0]) / 2).toFixed(1) + " " + ((p[1] + q[1]) / 2).toFixed(1);
}
return d;
};
// ---- self-preview (not part of the snippet you copy) ----
var circle = document.getElementById("hw-b-circle");
circle.setAttribute("d", window.hwWobbleEllipse(460, 620, 190, 130, 4, 3.2));
var tl = gsap.timeline({ paused: true });
/* draw-on entrances (strokeDashoffset — never x/y, the boil owns those) */
[circle, document.getElementById("hw-b-arrow")].forEach(function (p, i) {
var len = p.getTotalLength();
gsap.set(p, { strokeDasharray: len, strokeDashoffset: len });
tl.to(p, { strokeDashoffset: 0, duration: 0.8, ease: "power2.inOut" }, 0.3 + i * 0.35);
});
gsap.set("#hw-b-word", { opacity: 0 });
tl.to("#hw-b-word", { opacity: 1, duration: 0.5, ease: "power2.out" }, 1.1);
/* three boils with different characters, one dispatcher */
window.hwBoil(tl, "#hw-b-circle-g", { amp: 2.2, rot: 0.4, frameDrop: 3, seed: 2 });
window.hwBoil(tl, "#hw-b-arrow-g", { amp: 2.6, rot: 0.7, frameDrop: 1, seed: 5 });
window.hwBoil(tl, "#hw-b-word", { amp: 1.2, rot: 0.35, frameDrop: 3, seed: 9 });
tl.set("#hw-boil-root", { visibility: "hidden" }, 3.98);
window.__timelines["hw-boil"] = tl;
})();
</script>
</body>
</html>
+198
View File
@@ -0,0 +1,198 @@
<!doctype html>
<!-- Demo plate for the catalog preview. Identical to hw-box-label.html except the
root background, which is opaque here so the transparent overlay reads.
Not installed by `hyperframes add` - registry-item.json lists hw-box-label.html only. -->
<html lang="en">
<head>
<meta charset="utf-8" />
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<style>
@font-face {
font-family: "Caveat";
src: url("assets/fonts/Caveat-700-latin.woff2") format("woff2");
font-weight: 700;
font-display: block;
}
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #17161a;
overflow: hidden;
}
/* ---- hw-box-label ----------------------------------------------------
Rounded hand-drawn box + marker label (map-label style annotations).
Requires the hw-boil helpers in the host. */
.hw-box {
position: absolute;
pointer-events: none;
}
.hw-box svg {
width: 100%;
height: 100%;
overflow: visible;
}
.hw-box path {
fill: none;
stroke: var(--hw-box-ink, var(--hw-ink, #f4f2ec));
stroke-width: var(--hw-box-w, 6);
stroke-linecap: round;
stroke-linejoin: round;
}
.hw-box .hw-bx-label {
position: absolute;
width: 100%;
text-align: center;
font-family: var(--hw-font-print, "Caveat", cursive);
font-weight: 700;
font-size: 52px;
color: var(--hw-box-ink, var(--hw-ink, #f4f2ec));
white-space: nowrap;
opacity: 0;
text-shadow: 0 2px 14px rgba(0, 0, 0, 0.4);
}
/* self-preview scene */
#hw-bx-scene {
position: absolute;
inset: 0;
background: linear-gradient(140deg, #4a3560 0%, #1c1423 100%);
}
</style>
</head>
<body>
<!-- Self-preview. WIRING: copy the .hw-box CSS + markup pattern, the
hwWobbleRect / hwBoxBuild / hwBoxOn / hwBoxOff helpers, AND the
hw-boil helpers. Size the wrapper over your target, then:
hwBoxBuild("#hw-my-box", { seed: 3, label: "Europe", labelPos: "above" });
hwBoxOn(tl, "#hw-my-box", 2.0);
hwBoil(tl, "#hw-my-box", { frameDrop: 3, seed: 3 });
hwBoxOff(tl, "#hw-my-box", 8.0);
-->
<div
id="hw-bx-root"
data-composition-id="hw-box-label"
data-start="0"
data-duration="4"
data-width="1920"
data-height="1080"
>
<div id="hw-bx-scene"></div>
<div class="hw-box" id="hw-bx-demo" style="left: 700px; top: 420px; width: 420px; height: 230px">
<svg viewBox="0 0 420 230"><path id="hw-bx-demo-path"></path></svg>
<div class="hw-bx-label" id="hw-bx-demo-label"></div>
</div>
<div
id="hw-bx-drv"
class="clip"
data-start="0"
data-duration="4"
data-track-index="0"
style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
></div>
</div>
<script>
(function () {
window.__timelines = window.__timelines || {};
/* ---- hw-boil helpers (shared family runtime — copy once per host) ---- */
window.hwOnUpdate = function (tl, fn) {
if (!tl.__hwRenders) {
tl.__hwRenders = [];
tl.eventCallback("onUpdate", function () {
for (var i = 0; i < tl.__hwRenders.length; i++) tl.__hwRenders[i]();
});
}
tl.__hwRenders.push(fn);
fn();
};
window.hwHash = function (n, seed) {
var x = Math.sin(n * 127.1 + (seed || 1) * 311.7) * 43758.5453;
return (x - Math.floor(x)) * 2 - 1;
};
window.hwBoil = function (tl, target, opts) {
opts = opts || {};
var amp = opts.amp !== undefined ? opts.amp : 1.6;
var rot = opts.rot !== undefined ? opts.rot : 0.5;
var fps = opts.fps || 30;
var drop = opts.frameDrop || 3;
var seed = opts.seed || 1;
var els = gsap.utils.toArray(target);
window.hwOnUpdate(tl, function () {
var step = Math.floor((tl.time() * fps) / drop);
for (var i = 0; i < els.length; i++) {
gsap.set(els[i], {
x: window.hwHash(step * 3 + i * 97, seed) * amp,
y: window.hwHash(step * 3 + 1 + i * 97, seed) * amp,
rotation: (opts.baseRot || 0) + window.hwHash(step * 3 + 2 + i * 97, seed) * rot,
});
}
});
};
// ---- hw-box helpers (copy these too) ----
// Wobbled rounded-rect path in a w x h box.
window.hwWobbleRect = function (w, h, r, seed, amp) {
amp = amp === undefined ? 3.5 : amp;
function wob(n) {
return window.hwHash(n, seed) * amp;
}
var d = "M " + (r + wob(1)) + " " + wob(2);
d += " L " + (w / 2 + wob(3)) + " " + wob(4);
d += " L " + (w - r + wob(5)) + " " + wob(6);
d += " Q " + (w + wob(7)) + " " + wob(8) + " " + (w + wob(9)) + " " + (r + wob(10));
d += " L " + (w + wob(11)) + " " + (h / 2 + wob(12));
d += " L " + (w + wob(13)) + " " + (h - r + wob(14));
d += " Q " + (w + wob(15)) + " " + (h + wob(16)) + " " + (w - r + wob(17)) + " " + (h + wob(18));
d += " L " + (w / 2 + wob(19)) + " " + (h + wob(20));
d += " L " + (r + wob(21)) + " " + (h + wob(22));
d += " Q " + wob(23) + " " + (h + wob(24)) + " " + wob(25) + " " + (h - r + wob(26));
d += " L " + wob(27) + " " + (h / 2 + wob(28));
d += " L " + wob(29) + " " + (r + wob(30));
d += " Q " + wob(31) + " " + wob(32) + " " + (r + wob(33)) + " " + wob(34);
return d;
};
window.hwBoxBuild = function (target, opts) {
opts = opts || {};
var el = document.querySelector(target);
var svg = el.querySelector("svg");
var vb = svg.getAttribute("viewBox").split(" ");
var W = parseFloat(vb[2]),
H = parseFloat(vb[3]);
el.querySelector("path").setAttribute("d", window.hwWobbleRect(W, H, Math.min(26, W * 0.1), opts.seed || 1));
var label = el.querySelector(".hw-bx-label");
label.textContent = opts.label || "";
if ((opts.labelPos || "above") === "above") {
label.style.top = "-64px";
} else {
label.style.top = "50%";
label.style.transform = "translateY(-50%)";
}
};
window.hwBoxOn = function (tl, target, at) {
var p = document.querySelector(target + " path");
var len = p.getTotalLength();
gsap.set(p, { strokeDasharray: len, strokeDashoffset: len });
tl.to(p, { strokeDashoffset: 0, duration: 0.75, ease: "power2.inOut" }, at);
tl.to(document.querySelector(target + " .hw-bx-label"), { opacity: 1, duration: 0.4, ease: "power2.out" }, at + 0.55);
};
window.hwBoxOff = function (tl, target, at) {
tl.to(target, { opacity: 0, duration: 0.35, ease: "power2.in" }, at);
};
// ---- self-preview ----
window.hwBoxBuild("#hw-bx-demo", { seed: 3, label: "Europe", labelPos: "above" });
var tl = gsap.timeline({ paused: true });
window.hwBoxOn(tl, "#hw-bx-demo", 0.4);
window.hwBoil(tl, "#hw-bx-demo", { amp: 1.8, rot: 0.4, frameDrop: 3, seed: 3 });
window.hwBoxOff(tl, "#hw-bx-demo", 3.4);
tl.set("#hw-bx-root", { visibility: "hidden" }, 3.98);
window.__timelines["hw-box-label"] = tl;
})();
</script>
</body>
</html>
+277
View File
@@ -0,0 +1,277 @@
<!doctype html>
<!-- Demo plate for the catalog preview. Identical to hw-callout-circle.html except the
root background, which is opaque here so the transparent overlay reads.
Not installed by `hyperframes add` - registry-item.json lists hw-callout-circle.html only. -->
<html lang="en">
<head>
<meta charset="utf-8" />
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<style>
@font-face {
font-family: "Caveat";
src: url("assets/fonts/Caveat-700-latin.woff2") format("woff2");
font-weight: 700;
font-display: block;
}
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #17161a;
overflow: hidden;
}
/* ---- hw-callout-circle ----------------------------------------------
The full callout anatomy: wobbled ellipse outline draws around a
target, optional scribble fill sketches in, a curved connector leads
to a handwritten label. Requires the hw-boil helpers in the host. */
.hw-callout {
position: absolute;
pointer-events: none;
}
.hw-callout svg {
width: 100%;
height: 100%;
overflow: visible;
}
.hw-callout path {
fill: none;
stroke-linecap: round;
stroke-linejoin: round;
}
.hw-callout .hw-co-outline {
stroke: var(--hw-co-ink, var(--hw-ink, #f4f2ec));
stroke-width: var(--hw-co-w, 6);
}
.hw-callout .hw-co-scribble {
stroke: var(--hw-co-accent, var(--hw-accent, #6d6dff));
stroke-width: 4;
opacity: 0.7;
}
.hw-callout .hw-co-connector {
stroke: var(--hw-co-ink, var(--hw-ink, #f4f2ec));
stroke-width: 4;
}
.hw-callout .hw-co-label {
position: absolute;
font-family: var(--hw-font-print, "Caveat", cursive);
font-weight: 700;
font-size: 54px;
color: var(--hw-co-ink, var(--hw-ink, #f4f2ec));
white-space: nowrap;
opacity: 0;
text-shadow: 0 2px 14px rgba(0, 0, 0, 0.45);
}
/* self-preview scene */
#hw-co-scene {
position: absolute;
inset: 0;
background: linear-gradient(150deg, #35604a 0%, #14231c 100%);
}
#hw-co-target {
position: absolute;
left: 700px;
top: 420px;
width: 220px;
height: 150px;
border-radius: 18px;
background: rgba(244, 242, 236, 0.18);
display: flex;
align-items: center;
justify-content: center;
font-family: "Inter", ui-sans-serif, sans-serif;
font-weight: 600;
font-size: 30px;
color: rgba(244, 242, 236, 0.8);
}
</style>
</head>
<body>
<!-- Self-preview. WIRING: copy the .hw-callout CSS + markup pattern, the
hwCalloutBuild / hwCalloutOn / hwCalloutOff helpers, AND the hw-boil
helpers. Size the wrapper around your target, then:
hwCalloutBuild("#hw-my-callout", { scribble: true, seed: 4,
label: "look here!", labelAt: "right" });
hwCalloutOn(tl, "#hw-my-callout", 2.0);
hwBoil(tl, "#hw-my-callout", { frameDrop: 3, seed: 4 });
hwCalloutOff(tl, "#hw-my-callout", 8.0);
-->
<div
id="hw-co-root"
data-composition-id="hw-callout-circle"
data-start="0"
data-duration="5"
data-width="1920"
data-height="1080"
>
<div id="hw-co-scene"></div>
<div id="hw-co-target">subject</div>
<div class="hw-callout" id="hw-co-demo" style="left: 640px; top: 370px; width: 340px; height: 250px">
<svg viewBox="0 0 340 250">
<path class="hw-co-scribble" id="hw-co-demo-scribble"></path>
<path class="hw-co-outline" id="hw-co-demo-outline"></path>
<path class="hw-co-connector" id="hw-co-demo-connector"></path>
</svg>
<div class="hw-co-label" id="hw-co-demo-label"></div>
</div>
<div
id="hw-co-drv"
class="clip"
data-start="0"
data-duration="5"
data-track-index="0"
style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
></div>
</div>
<script>
(function () {
window.__timelines = window.__timelines || {};
/* ---- hw-boil helpers (shared family runtime — copy once per host) ---- */
window.hwOnUpdate = function (tl, fn) {
if (!tl.__hwRenders) {
tl.__hwRenders = [];
tl.eventCallback("onUpdate", function () {
for (var i = 0; i < tl.__hwRenders.length; i++) tl.__hwRenders[i]();
});
}
tl.__hwRenders.push(fn);
fn();
};
window.hwHash = function (n, seed) {
var x = Math.sin(n * 127.1 + (seed || 1) * 311.7) * 43758.5453;
return (x - Math.floor(x)) * 2 - 1;
};
window.hwBoil = function (tl, target, opts) {
opts = opts || {};
var amp = opts.amp !== undefined ? opts.amp : 1.6;
var rot = opts.rot !== undefined ? opts.rot : 0.5;
var fps = opts.fps || 30;
var drop = opts.frameDrop || 3;
var seed = opts.seed || 1;
var els = gsap.utils.toArray(target);
window.hwOnUpdate(tl, function () {
var step = Math.floor((tl.time() * fps) / drop);
for (var i = 0; i < els.length; i++) {
gsap.set(els[i], {
x: window.hwHash(step * 3 + i * 97, seed) * amp,
y: window.hwHash(step * 3 + 1 + i * 97, seed) * amp,
rotation: (opts.baseRot || 0) + window.hwHash(step * 3 + 2 + i * 97, seed) * rot,
});
}
});
};
window.hwWobbleEllipse = function (cx, cy, rx, ry, seed, wobblePct) {
wobblePct = wobblePct === undefined ? 3 : wobblePct;
var N = 14,
pts = [];
for (var i = 0; i < N; i++) {
var a = (i / N) * Math.PI * 2;
var wr = 1 + window.hwHash(i * 13 + 5, seed) * (wobblePct / 100);
pts.push([cx + Math.cos(a) * rx * wr, cy + Math.sin(a) * ry * wr]);
}
var mx = (pts[0][0] + pts[N - 1][0]) / 2,
my = (pts[0][1] + pts[N - 1][1]) / 2;
var d = "M" + mx.toFixed(1) + " " + my.toFixed(1);
for (var j = 0; j < N; j++) {
var p = pts[j],
q = pts[(j + 1) % N];
d += " Q" + p[0].toFixed(1) + " " + p[1].toFixed(1) + " " + ((p[0] + q[0]) / 2).toFixed(1) + " " + ((p[1] + q[1]) / 2).toFixed(1);
}
return d;
};
// ---- hw-callout helpers (copy these too) ----
// Builds outline + scribble + connector + label inside the wrapper.
// opts: scribble (bool), seed, label, labelAt ("right"|"left"|"below"),
// connector (bool)
window.hwCalloutBuild = function (target, opts) {
opts = opts || {};
var seed = opts.seed || 1;
var el = document.querySelector(target);
var svg = el.querySelector("svg");
var vb = svg.getAttribute("viewBox").split(" ");
var W = parseFloat(vb[2]),
H = parseFloat(vb[3]);
var cx = W / 2,
cy = H / 2,
rx = W * 0.42,
ry = H * 0.36;
el.querySelector(".hw-co-outline").setAttribute("d", window.hwWobbleEllipse(cx, cy, rx, ry, seed, 4));
var scr = el.querySelector(".hw-co-scribble");
if (opts.scribble === false) {
scr.style.display = "none";
} else {
var d = "",
rows = 6;
for (var i = 0; i < rows; i++) {
var t = (i + 0.5) / rows;
var yy = cy - ry + t * ry * 2;
var half = Math.sqrt(Math.max(0.05, 1 - Math.pow((yy - cy) / ry, 2)));
var xw = rx * half * 0.86;
var xa = cx - xw + window.hwHash(i * 7 + 2, seed) * 8;
var xb = cx + xw + window.hwHash(i * 7 + 3, seed) * 8;
d += (i === 0 ? "M " : " L ") + (i % 2 ? xa : xb).toFixed(1) + " " + yy.toFixed(1);
d += " L " + (i % 2 ? xb : xa).toFixed(1) + " " + (yy + window.hwHash(i * 7 + 4, seed) * 4).toFixed(1);
}
scr.setAttribute("d", d);
}
var conn = el.querySelector(".hw-co-connector");
var label = el.querySelector(".hw-co-label");
if (opts.connector === false) {
conn.style.display = "none";
} else {
var at = opts.labelAt || "right";
var sx = at === "left" ? cx - rx : at === "below" ? cx : cx + rx;
var sy = at === "below" ? cy + ry : cy + ry * 0.35;
var ex = at === "left" ? sx - 70 : at === "below" ? sx + 40 : sx + 70;
var ey = sy + 55;
conn.setAttribute(
"d",
"M " + sx.toFixed(1) + " " + sy.toFixed(1) + " Q " + ((sx + ex) / 2 + window.hwHash(21, seed) * 14).toFixed(1) + " " + (sy + 46).toFixed(1) + " " + ex.toFixed(1) + " " + ey.toFixed(1)
);
label.style.left = (at === "left" ? ex - 180 : ex + 12) + "px";
label.style.top = ey - 24 + "px";
}
label.textContent = opts.label || "";
};
// Draw sequence: outline -> scribble -> connector -> label pop.
window.hwCalloutOn = function (tl, target, at) {
var el = document.querySelector(target);
var t = at;
["outline", "scribble", "connector"].forEach(function (k, idx) {
var p = el.querySelector(".hw-co-" + k);
if (!p || p.style.display === "none") return;
var len = p.getTotalLength();
gsap.set(p, { strokeDasharray: len, strokeDashoffset: len });
var dur = k === "scribble" ? 0.55 : k === "connector" ? 0.35 : 0.7;
tl.to(p, { strokeDashoffset: 0, duration: dur, ease: "power2.inOut" }, t);
t += dur * 0.85;
});
var label = el.querySelector(".hw-co-label");
gsap.set(label, { scale: 0.8, transformOrigin: "0% 50%" });
tl.to(label, { opacity: 1, scale: 1, duration: 0.4, ease: "back.out(1.6)" }, t);
};
window.hwCalloutOff = function (tl, target, at) {
tl.to(target, { opacity: 0, duration: 0.35, ease: "power2.in" }, at);
};
// ---- self-preview ----
window.hwCalloutBuild("#hw-co-demo", { scribble: true, seed: 4, label: "look here!", labelAt: "right" });
var tl = gsap.timeline({ paused: true });
window.hwCalloutOn(tl, "#hw-co-demo", 0.4);
window.hwBoil(tl, "#hw-co-demo", { amp: 1.8, rot: 0.4, frameDrop: 3, seed: 4 });
window.hwCalloutOff(tl, "#hw-co-demo", 4.3);
tl.set("#hw-co-root", { visibility: "hidden" }, 4.98);
window.__timelines["hw-callout-circle"] = tl;
})();
</script>
</body>
</html>
+203
View File
@@ -0,0 +1,203 @@
<!doctype html>
<!-- Demo plate for the catalog preview. Identical to hw-underline.html except the
root background, which is opaque here so the transparent overlay reads.
Not installed by `hyperframes add` - registry-item.json lists hw-underline.html only. -->
<html lang="en">
<head>
<meta charset="utf-8" />
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<style>
@font-face {
font-family: "Caveat";
src: url("assets/fonts/Caveat-700-latin.woff2") format("woff2");
font-weight: 700;
font-display: block;
}
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #17161a;
overflow: hidden;
}
/* ---- hw-underline ------------------------------------------------
Squiggle underline / strikethrough / bracket marks that draw on.
Size the wrapper over the text they annotate. Requires the hw-boil
helpers in the host. */
.hw-mark {
position: absolute;
pointer-events: none;
}
.hw-mark svg {
width: 100%;
height: 100%;
overflow: visible;
}
.hw-mark path {
fill: none;
stroke: var(--hw-mark-color, var(--hw-accent-warm, #ffb020));
stroke-width: var(--hw-mark-w, 7);
stroke-linecap: round;
stroke-linejoin: round;
}
/* self-preview scene */
#hw-un-scene {
position: absolute;
inset: 0;
background: #17161a;
font-family: var(--hw-font-print, "Caveat", cursive);
font-weight: 700;
font-size: 84px;
color: #f4f2ec;
}
#hw-un-scene div {
position: absolute;
left: 480px;
white-space: nowrap;
}
</style>
</head>
<body>
<!-- Self-preview. WIRING: copy the .hw-mark CSS + markup pattern, the
hwMarkPath / hwMarkOn / hwMarkOff helpers, AND the hw-boil helpers.
Then:
document.querySelector("#hw-my-mark path")
.setAttribute("d", hwMarkPath(420, 34, "underline", 4));
hwMarkOn(tl, "#hw-my-mark", 2.0);
hwBoil(tl, "#hw-my-mark", { frameDrop: 3, seed: 4 });
-->
<div
id="hw-un-root"
data-composition-id="hw-underline"
data-start="0"
data-duration="4"
data-width="1920"
data-height="1080"
>
<div id="hw-un-scene">
<div style="top: 260px">underline the point</div>
<div style="top: 460px">cross this out</div>
<div style="top: 660px">bracket the aside</div>
</div>
<div class="hw-mark" id="hw-un-m1" style="left: 470px; top: 350px; width: 620px; height: 34px">
<svg viewBox="0 0 620 34"><path></path></svg>
</div>
<div class="hw-mark" id="hw-un-m2" style="left: 470px; top: 500px; width: 480px; height: 34px">
<svg viewBox="0 0 480 34"><path></path></svg>
</div>
<div class="hw-mark" id="hw-un-m3" style="left: 440px; top: 650px; width: 40px; height: 110px">
<svg viewBox="0 0 40 110"><path></path></svg>
</div>
<div
id="hw-un-drv"
class="clip"
data-start="0"
data-duration="4"
data-track-index="0"
style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
></div>
</div>
<script>
(function () {
window.__timelines = window.__timelines || {};
/* ---- hw-boil helpers (shared family runtime — copy once per host) ---- */
window.hwOnUpdate = function (tl, fn) {
if (!tl.__hwRenders) {
tl.__hwRenders = [];
tl.eventCallback("onUpdate", function () {
for (var i = 0; i < tl.__hwRenders.length; i++) tl.__hwRenders[i]();
});
}
tl.__hwRenders.push(fn);
fn();
};
window.hwHash = function (n, seed) {
var x = Math.sin(n * 127.1 + (seed || 1) * 311.7) * 43758.5453;
return (x - Math.floor(x)) * 2 - 1;
};
window.hwBoil = function (tl, target, opts) {
opts = opts || {};
var amp = opts.amp !== undefined ? opts.amp : 1.6;
var rot = opts.rot !== undefined ? opts.rot : 0.5;
var fps = opts.fps || 30;
var drop = opts.frameDrop || 3;
var seed = opts.seed || 1;
var els = gsap.utils.toArray(target);
window.hwOnUpdate(tl, function () {
var step = Math.floor((tl.time() * fps) / drop);
for (var i = 0; i < els.length; i++) {
gsap.set(els[i], {
x: window.hwHash(step * 3 + i * 97, seed) * amp,
y: window.hwHash(step * 3 + 1 + i * 97, seed) * amp,
rotation: (opts.baseRot || 0) + window.hwHash(step * 3 + 2 + i * 97, seed) * rot,
});
}
});
};
// ---- hw-mark helpers (copy these too) ----
// style: "underline" (squiggle) | "strike" (double pass) | "bracket"
window.hwMarkPath = function (w, h, style, seed) {
seed = seed || 1;
function wob(n, range) {
return window.hwHash(n, seed) * (range === undefined ? 5 : range);
}
var mid = h / 2;
if (style === "bracket") {
return (
"M " + (w * 0.9 + wob(1, 3)) + " " + (2 + wob(2, 3)) +
" Q " + wob(3, 4) + " " + (2 + wob(4)) + " " + (4 + wob(5, 3)) + " " + (h * 0.5 + wob(6)) +
" Q " + wob(7, 4) + " " + (h - 2 + wob(8)) + " " + (w * 0.9 + wob(9, 3)) + " " + (h - 2 + wob(10, 3))
);
}
if (style === "strike") {
return (
"M " + (2 + wob(1, 3)) + " " + (mid + wob(2)) +
" L " + (w - 4 + wob(3, 3)) + " " + (mid - 3 + wob(4)) +
" M " + (w - 10 + wob(5, 4)) + " " + (mid + 7 + wob(6)) +
" L " + (8 + wob(7, 4)) + " " + (mid + 3 + wob(8))
);
}
/* underline squiggle */
var segs = Math.max(5, Math.round(w / 95));
var d = "M 2 " + (mid + wob(0, 4));
for (var i = 1; i <= segs; i++) {
var x = (w / segs) * i;
d +=
" Q " + (x - w / segs / 2 + wob(i * 4 + 1, 9)) + " " + (mid + (i % 2 ? -1 : 1) * (9 + wob(i * 4 + 2, 4))) +
" " + x.toFixed(1) + " " + (mid + wob(i * 4 + 3, 4));
}
return d;
};
window.hwMarkOn = function (tl, target, at, dur) {
var p = document.querySelector(target + " path");
var len = p.getTotalLength();
gsap.set(p, { strokeDasharray: len, strokeDashoffset: len });
tl.to(p, { strokeDashoffset: 0, duration: dur === undefined ? 0.55 : dur, ease: "power2.inOut" }, at);
};
window.hwMarkOff = function (tl, target, at) {
tl.to(target, { opacity: 0, duration: 0.3, ease: "power2.in" }, at);
};
// ---- self-preview ----
document.querySelector("#hw-un-m1 path").setAttribute("d", window.hwMarkPath(620, 34, "underline", 4));
document.querySelector("#hw-un-m2 path").setAttribute("d", window.hwMarkPath(480, 34, "strike", 6));
document.querySelector("#hw-un-m3 path").setAttribute("d", window.hwMarkPath(40, 110, "bracket", 8));
var tl = gsap.timeline({ paused: true });
window.hwMarkOn(tl, "#hw-un-m1", 0.4);
window.hwMarkOn(tl, "#hw-un-m2", 1.0);
window.hwMarkOn(tl, "#hw-un-m3", 1.6);
window.hwBoil(tl, [".hw-mark"], { amp: 1.5, rot: 0.4, frameDrop: 3, seed: 5 });
tl.to([".hw-mark"], { opacity: 0, duration: 0.3, ease: "power2.in" }, 3.5);
tl.set("#hw-un-root", { visibility: "hidden" }, 3.98);
window.__timelines["hw-underline"] = tl;
})();
</script>
</body>
</html>
-135
View File
@@ -1,135 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<style>
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: transparent;
overflow: hidden;
}
/* ---- mk-cta-button ---------------------------------------------------
Pill CTA ("Buy", "Subscribe"). Set --mk-cta-bg/--mk-cta-fg to recolor
(any css color); add .mk-cta--framed for the outlined ring
with drop shadow, .mk-cta--frost for frosted glass over footage. */
.mk-cta {
--mk-font: "Inter", -apple-system, sans-serif;
--mk-ink: #1d1d1f;
--mk-accent: #0071e3;
--mk-cta-bg: var(--mk-accent);
--mk-cta-fg: #ffffff;
--mk-cta-frame: rgba(29, 29, 31, 0.22);
--mk-cta-arrow-w: 3.5;
--mk-shadow: 0 12px 36px rgba(0, 0, 0, 0.14);
position: absolute;
display: inline-flex;
align-items: center;
gap: 20px;
padding: 24px 48px;
border-radius: 999px;
font-family: var(--mk-font);
font-weight: 600;
font-size: 42px;
letter-spacing: -0.01em;
white-space: nowrap;
background: var(--mk-cta-bg);
color: var(--mk-cta-fg);
pointer-events: none;
}
.mk-cta--framed::before {
content: "";
position: absolute;
inset: -16px;
border: 2px solid var(--mk-cta-frame);
border-radius: 999px;
box-shadow: var(--mk-shadow);
}
.mk-cta--frost {
background: rgba(255, 255, 255, 0.42);
color: var(--mk-ink);
backdrop-filter: blur(24px) saturate(1.4);
-webkit-backdrop-filter: blur(24px) saturate(1.4);
box-shadow: var(--mk-shadow);
}
.mk-cta-arrow {
display: inline-flex;
width: 0.62em;
}
.mk-cta-arrow svg {
width: 100%;
stroke: currentColor;
stroke-width: var(--mk-cta-arrow-w);
stroke-linecap: round;
stroke-linejoin: round;
fill: none;
}
</style>
</head>
<body>
<!-- Self-preview wrapper. WIRING: copy the .mk-cta CSS above, the .mk-cta
markup below, and the helpers into your host composition. Position
via top/right/bottom/left; add class="clip" + data-start/duration/
track-index for host windowing. -->
<div
id="mk-cta-root"
data-composition-id="mk-cta-button"
data-start="0"
data-duration="4"
data-width="1920"
data-height="1080"
>
<div class="mk-cta mk-cta--framed" id="mk-cta-demo" style="top: 480px; left: 780px">
<span>Subscribe</span>
<span class="mk-cta-arrow"
><svg viewBox="0 0 24 24"><path d="M4 12h15M13 5l7 7-7 7" /></svg
></span>
</div>
<div
id="mk-cta-drv"
class="clip"
data-start="0"
data-duration="4"
data-track-index="0"
style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
></div>
</div>
<script>
(function () {
window.__timelines = window.__timelines || {};
// ---- copy these helpers into the host script, then call them on the
// paused host timeline:
// mkCtaIn(tl, "#mk-my-cta", 1.0);
// mkCtaNudge(tl, "#mk-my-cta", 2.0); // arrow beckons once
// mkCtaOut(tl, "#mk-my-cta", 8.5);
window.mkCtaIn = function (tl, target, at) {
gsap.set(target, { opacity: 0, y: 26, scale: 0.95 });
tl.to(target, { opacity: 1, y: 0, scale: 1, duration: 0.65, ease: "back.out(1.2)" }, at);
};
window.mkCtaNudge = function (tl, target, at) {
var arrow = document.querySelector(target + " .mk-cta-arrow");
if (arrow) tl.to(arrow, { x: 7, duration: 0.28, ease: "sine.inOut", yoyo: true, repeat: 3 }, at);
};
window.mkCtaOut = function (tl, target, at) {
tl.to(target, { opacity: 0, y: -16, duration: 0.35, ease: "power2.in" }, at);
};
// Self-preview timeline (not part of the snippet you copy).
var tl = gsap.timeline({ paused: true });
window.mkCtaIn(tl, "#mk-cta-demo", 0.4);
window.mkCtaNudge(tl, "#mk-cta-demo", 1.5);
window.mkCtaOut(tl, "#mk-cta-demo", 3.4);
tl.set("#mk-cta-root", { visibility: "hidden" }, 3.98);
window.__timelines["mk-cta-button"] = tl;
})();
</script>
</body>
</html>
@@ -1,15 +0,0 @@
{
"$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
"name": "mk-cta-button",
"type": "hyperframes:component",
"title": "Pill CTA Button",
"description": "Rounded call-to-action button with arrow, optional outline frame with shadow, frosted variant, and an arrow-nudge helper",
"tags": ["cta", "button", "minimal", "overlay"],
"files": [
{
"path": "mk-cta-button.html",
"target": "compositions/components/mk-cta-button.html",
"type": "hyperframes:snippet"
}
]
}
+100
View File
@@ -0,0 +1,100 @@
<!doctype html>
<!-- Demo plate for the catalog preview. Identical to mk-emphasis-type.html except the
root background, which is opaque here so the transparent overlay reads.
Not installed by `hyperframes add` - registry-item.json lists mk-emphasis-type.html only. -->
<html lang="en">
<head>
<meta charset="utf-8" />
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<style>
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #f2f4f7;
overflow: hidden;
}
/* ---- mk-emphasis-type ------------------------------------------------
Oversized low-contrast background word sliding slowly behind the
subject (the promo's "EMPHASIS" texture type). Decorative only —
keep it behind real content and mark it aria-hidden. */
.mk-emphasis {
--mk-font: "Inter", -apple-system, sans-serif;
--mk-emph-color: rgba(29, 29, 31, 0.07); /* dark scheme: rgba(245,245,247,0.08) */
position: absolute;
font-family: var(--mk-font);
font-weight: 600;
font-size: 260px;
letter-spacing: -0.02em;
line-height: 1;
white-space: nowrap;
text-transform: uppercase;
color: var(--mk-emph-color);
pointer-events: none;
}
</style>
</head>
<body>
<!-- Self-preview wrapper. WIRING: copy the .mk-emphasis CSS, the markup
below (keep aria-hidden + data-layout-allow-overflow — the word is
decorative and usually wider than the canvas), and the helpers. -->
<div
id="mk-emph-root"
data-composition-id="mk-emphasis-type"
data-start="0"
data-duration="4"
data-width="1920"
data-height="1080"
>
<div
class="mk-emphasis"
id="mk-emph-demo"
aria-hidden="true"
data-layout-allow-overflow
style="top: 400px; left: -40px"
>
Emphasis
</div>
<div
id="mk-emph-drv"
class="clip"
data-start="0"
data-duration="4"
data-track-index="0"
style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
></div>
</div>
<script>
(function () {
window.__timelines = window.__timelines || {};
// ---- copy these helpers into the host script, then:
// mkEmphasisIn(tl, "#mk-my-word", 1.0, { drift: -120, hold: 6 });
// mkEmphasisOut(tl, "#mk-my-word", 7.5);
window.mkEmphasisIn = function (tl, target, at, opts) {
opts = opts || {};
var drift = opts.drift === undefined ? -100 : opts.drift;
var hold = opts.hold === undefined ? 5 : opts.hold;
gsap.set(target, { opacity: 0, x: 40 });
tl.to(target, { opacity: 1, duration: 1.0, ease: "power2.out" }, at);
tl.to(target, { x: 40 + drift, duration: hold, ease: "sine.inOut" }, at);
};
window.mkEmphasisOut = function (tl, target, at) {
tl.to(target, { opacity: 0, duration: 0.5, ease: "power2.in" }, at);
};
// Self-preview timeline (not part of the snippet you copy).
var tl = gsap.timeline({ paused: true });
window.mkEmphasisIn(tl, "#mk-emph-demo", 0.3, { drift: -110, hold: 3.2 });
window.mkEmphasisOut(tl, "#mk-emph-demo", 3.4);
tl.set("#mk-emph-root", { visibility: "hidden" }, 3.98);
window.__timelines["mk-emphasis-type"] = tl;
})();
</script>
</body>
</html>
-123
View File
@@ -1,123 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<style>
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: transparent;
overflow: hidden;
}
/* ---- mk-pill-callout ----------------------------------------------
Capsule chip in a minimal presentation style: @handles, tags, URLs, CTAs.
Variants: --accent (iOS-blue fill), --paper (white card + shadow),
--frost (frosted glass over footage/gradient).
Tokens fall back to the mk family defaults; override in the host. */
.mk-pill {
--mk-font: "Inter", -apple-system, sans-serif;
--mk-ink: #1d1d1f;
--mk-paper: #ffffff;
--mk-accent: #0071e3;
--mk-radius-pill: 999px;
--mk-shadow: 0 12px 36px rgba(0, 0, 0, 0.14);
position: absolute;
display: inline-flex;
align-items: center;
gap: 16px;
padding: 22px 44px;
border-radius: var(--mk-radius-pill);
font-family: var(--mk-font);
font-weight: 600;
font-size: 40px;
letter-spacing: -0.01em;
white-space: nowrap;
pointer-events: none;
}
.mk-pill--accent {
background: var(--mk-accent);
color: #ffffff;
}
.mk-pill--paper {
background: var(--mk-paper);
color: var(--mk-ink);
box-shadow: var(--mk-shadow);
}
.mk-pill--frost {
background: rgba(255, 255, 255, 0.42);
color: var(--mk-ink);
backdrop-filter: blur(24px) saturate(1.4);
-webkit-backdrop-filter: blur(24px) saturate(1.4);
box-shadow: var(--mk-shadow);
}
.mk-pill-dot {
width: 14px;
height: 14px;
border-radius: 50%;
background: currentColor;
opacity: 0.9;
}
</style>
</head>
<body>
<!-- Self-preview wrapper. WIRING: copy only the .mk-pill CSS above, the
.mk-pill markup below, and the mkPillIn/mkPillOut helpers into your
host composition — position instances via top/right/bottom/left and
add class="clip" + data-start/data-duration/data-track-index when the
host should window their visibility. -->
<div
id="mk-pill-root"
data-composition-id="mk-pill-callout"
data-start="0"
data-duration="4"
data-width="1920"
data-height="1080"
>
<div class="mk-pill mk-pill--accent" id="mk-pill-demo" style="top: 120px; right: 140px">
<span class="mk-pill-dot"></span><span>@hyperframes</span>
</div>
<div
id="mk-pill-drv"
class="clip"
data-start="0"
data-duration="4"
data-track-index="0"
style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
></div>
</div>
<script>
(function () {
window.__timelines = window.__timelines || {};
// ---- copy these helpers into the host script (before your timeline
// code), then call them on your paused host timeline:
//
// mkPillIn(tl, "#mk-my-pill", 0.8); // slide+fade entrance
// mkPillOut(tl, "#mk-my-pill", 9.2); // fade exit
//
window.mkPillIn = function (tl, target, at) {
gsap.set(target, { opacity: 0, y: 24, scale: 0.96 });
tl.to(target, { opacity: 1, y: 0, scale: 1, duration: 0.6, ease: "power3.out" }, at);
};
window.mkPillOut = function (tl, target, at) {
tl.to(target, { opacity: 0, y: -16, duration: 0.35, ease: "power2.in" }, at);
};
// Self-preview timeline (not part of the snippet you copy).
var tl = gsap.timeline({ paused: true });
window.mkPillIn(tl, "#mk-pill-demo", 0.4);
window.mkPillOut(tl, "#mk-pill-demo", 3.4);
tl.set("#mk-pill-root", { visibility: "hidden" }, 3.98);
window.__timelines["mk-pill-callout"] = tl;
})();
</script>
</body>
</html>
@@ -1,15 +0,0 @@
{
"$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
"name": "mk-pill-callout",
"type": "hyperframes:component",
"title": "Capsule Callout Chip",
"description": "Rounded capsule chip for handles, tags, and URLs with solid, paper, and frosted-glass variants; slide in/out helpers for the host timeline",
"tags": ["callout", "social", "minimal", "overlay"],
"files": [
{
"path": "mk-pill-callout.html",
"target": "compositions/components/mk-pill-callout.html",
"type": "hyperframes:snippet"
}
]
}
+152
View File
@@ -0,0 +1,152 @@
<!doctype html>
<!-- Demo plate for the catalog preview. Identical to mk-usage-arc.html except the
root background, which is opaque here so the transparent overlay reads.
Not installed by `hyperframes add` - registry-item.json lists mk-usage-arc.html only. -->
<html lang="en">
<head>
<meta charset="utf-8" />
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<style>
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #f2f4f7;
overflow: hidden;
}
/* ---- mk-usage-arc ----------------------------------------------------
Hairline circular gauge: draw-on arc + centered value + tiny label.
The promo's "Usage" arc. Recolor via --mk-arc-color. */
.mk-arc {
--mk-font: "Inter", -apple-system, sans-serif;
--mk-ink: #1d1d1f;
--mk-ink-dim: #6e6e73;
--mk-accent: #0071e3;
--mk-arc-color: var(--mk-accent);
--mk-hairline: 3px;
position: absolute;
width: 220px;
height: 220px;
pointer-events: none;
}
.mk-arc svg {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
}
.mk-arc-track {
fill: none;
stroke: rgba(29, 29, 31, 0.1);
stroke-width: var(--mk-hairline);
}
.mk-arc-fill {
fill: none;
stroke: var(--mk-arc-color);
stroke-width: var(--mk-hairline);
stroke-linecap: round;
}
.mk-arc-center {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 4px;
font-family: var(--mk-font);
}
.mk-arc-num {
font-weight: 600;
font-size: 46px;
letter-spacing: -0.02em;
color: var(--mk-ink);
font-variant-numeric: tabular-nums;
}
.mk-arc-label {
font-weight: 500;
font-size: 20px;
color: var(--mk-ink);
}
</style>
</head>
<body>
<!-- Self-preview wrapper. WIRING: copy the .mk-arc CSS, the markup below,
and the helpers into your host. r=92 with 220px box; the helper reads
the circle's radius, so resizing the box + r just works. -->
<div
id="mk-arc-root"
data-composition-id="mk-usage-arc"
data-start="0"
data-duration="4"
data-width="1920"
data-height="1080"
>
<div class="mk-arc" id="mk-arc-demo" style="top: 430px; left: 850px">
<svg viewBox="0 0 220 220">
<circle class="mk-arc-track" cx="110" cy="110" r="92"></circle>
<circle class="mk-arc-fill" cx="110" cy="110" r="92" transform="rotate(-90 110 110)"></circle>
</svg>
<div class="mk-arc-center">
<span class="mk-arc-num">0%</span>
<span class="mk-arc-label">Usage</span>
</div>
</div>
<div
id="mk-arc-drv"
class="clip"
data-start="0"
data-duration="4"
data-track-index="0"
style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
></div>
</div>
<script>
(function () {
window.__timelines = window.__timelines || {};
// ---- copy these helpers into the host script, then:
// mkArcIn(tl, "#mk-my-arc", 1.0, 0.76); // draw to 76%, count the numeral
// mkArcOut(tl, "#mk-my-arc", 8.5);
window.mkArcIn = function (tl, target, at, value) {
var el = document.querySelector(target);
var fill = el.querySelector(".mk-arc-fill");
var num = el.querySelector(".mk-arc-num");
var C = 2 * Math.PI * parseFloat(fill.getAttribute("r"));
gsap.set(el, { opacity: 0, y: 16 });
gsap.set(fill, { strokeDasharray: C, strokeDashoffset: C });
tl.to(el, { opacity: 1, y: 0, duration: 0.6, ease: "power3.out" }, at);
tl.to(fill, { strokeDashoffset: C * (1 - value), duration: 1.1, ease: "power2.inOut" }, at + 0.15);
var st = { v: 0 };
tl.to(
st,
{
v: value,
duration: 1.1,
ease: "power2.inOut",
onUpdate: function () {
num.textContent = Math.round(st.v * 100) + "%";
},
},
at + 0.15
);
};
window.mkArcOut = function (tl, target, at) {
tl.to(target, { opacity: 0, y: -12, duration: 0.35, ease: "power2.in" }, at);
};
// Self-preview timeline (not part of the snippet you copy).
var tl = gsap.timeline({ paused: true });
window.mkArcIn(tl, "#mk-arc-demo", 0.4, 0.76);
window.mkArcOut(tl, "#mk-arc-demo", 3.4);
tl.set("#mk-arc-root", { visibility: "hidden" }, 3.98);
window.__timelines["mk-usage-arc"] = tl;
})();
</script>
</body>
</html>
@@ -1,15 +0,0 @@
{
"$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
"name": "yt-avatar-pip",
"type": "hyperframes:component",
"title": "Circle PiP Window",
"description": "Circular or rounded-rect masked picture-in-picture window with ring and shadow for a second video or image; pop in/out helpers",
"tags": ["pip", "overlay", "creator"],
"files": [
{
"path": "yt-avatar-pip.html",
"target": "compositions/components/yt-avatar-pip.html",
"type": "hyperframes:snippet"
}
]
}
-115
View File
@@ -1,115 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<style>
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: transparent;
overflow: hidden;
}
/* ---- yt-avatar-pip ---------------------------------------------------
The screen-recording PiP: a circular (or rounded-rect) masked window
for a second video/image — one drop replaces the manual
crop-mask-resize dance. Size via --yt-pip-size, framing via an
inline transform scale on the inner media (an "inside scale"). */
.yt-pip {
--yt-pip-size: 300px;
--yt-pip-ring: rgba(255, 255, 255, 0.92);
--yt-pip-ring-w: 4px;
position: absolute;
width: var(--yt-pip-size);
height: var(--yt-pip-size);
border-radius: 50%;
overflow: hidden;
border: var(--yt-pip-ring-w) solid var(--yt-pip-ring);
box-shadow: 0 18px 50px rgba(0, 0, 0, 0.4);
pointer-events: none;
}
.yt-pip--rect {
border-radius: 22px;
width: calc(var(--yt-pip-size) * 1.55);
}
.yt-pip > img,
.yt-pip > video,
.yt-pip > .yt-pip-fill {
width: 100%;
height: 100%;
object-fit: cover;
}
/* self-preview stand-in (not part of the snippet) */
.yt-pip-fill {
display: flex;
align-items: center;
justify-content: center;
font-family: "Inter", ui-sans-serif, sans-serif;
font-weight: 700;
font-size: 92px;
color: rgba(255, 255, 255, 0.95);
background: linear-gradient(135deg, #ff8fb8 0%, #7d5cff 100%);
}
</style>
</head>
<body>
<!-- Self-preview wrapper. WIRING: copy the .yt-pip CSS and the markup
pattern below into your host; put a <video data-start ...> or <img>
inside. The media element must live in the HOST root (this pattern
is copied in) — never inside a mounted sub-composition: sub-comps
are never seeked, so framework-owned media there renders black. Position via top/right/bottom/left; add class="clip" +
data-start/duration/track-index for host windowing. Framing: style
the inner media with transform: scale(1.15) etc. Then:
ytPipIn(tl, "#yt-my-pip", 2.0);
ytPipOut(tl, "#yt-my-pip", 12.0);
-->
<div
id="yt-pip-root"
data-composition-id="yt-avatar-pip"
data-start="0"
data-duration="4"
data-width="1920"
data-height="1080"
>
<div class="yt-pip" id="yt-pip-demo" style="left: 120px; bottom: 100px">
<div class="yt-pip-fill">AB</div>
</div>
<div
id="yt-pip-drv"
class="clip"
data-start="0"
data-duration="4"
data-track-index="0"
style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
></div>
</div>
<script>
(function () {
window.__timelines = window.__timelines || {};
// ---- copy these helpers into the host script ----
window.ytPipIn = function (tl, target, at) {
gsap.set(target, { opacity: 0, scale: 0.55, y: 26 });
tl.to(target, { opacity: 1, scale: 1, y: 0, duration: 0.55, ease: "back.out(1.5)" }, at);
};
window.ytPipOut = function (tl, target, at) {
tl.to(target, { opacity: 0, scale: 0.75, y: 18, duration: 0.35, ease: "power2.in" }, at);
};
// Self-preview timeline (not part of the snippet you copy).
var tl = gsap.timeline({ paused: true });
window.ytPipIn(tl, "#yt-pip-demo", 0.4);
window.ytPipOut(tl, "#yt-pip-demo", 3.4);
tl.set("#yt-pip-root", { visibility: "hidden" }, 3.98);
window.__timelines["yt-avatar-pip"] = tl;
})();
</script>
</body>
</html>
+173
View File
@@ -0,0 +1,173 @@
<!doctype html>
<!-- Demo plate for the catalog preview. Identical to yt-camera-move.html except the
root background, which is opaque here so the transparent overlay reads.
Not installed by `hyperframes add` - registry-item.json lists yt-camera-move.html only. -->
<html lang="en">
<head>
<meta charset="utf-8" />
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<style>
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #14161c;
overflow: hidden;
}
/* ---- yt-camera-move --------------------------------------------------
The creator-video punch-in as reusable GSAP helpers: dynamic
zoom + slide + 3D tilt/pan on any wrapper (a #video-wrap, a
sub-composition include, an image card). A talking-head recipe
is the default: cubic easing, ~11% zoom.
Optional: .yt-cm-defocus is an edge-blur overlay that pulses during
the move to sell the 3D feel — place it ABOVE the moving layer,
covering the same region. */
.yt-cm-defocus {
position: absolute;
inset: 0;
pointer-events: none;
opacity: 0;
backdrop-filter: blur(7px);
-webkit-backdrop-filter: blur(7px);
/* blur only the frame edges; center stays sharp */
-webkit-mask-image: radial-gradient(
ellipse 62% 58% at 50% 50%,
transparent 58%,
rgba(0, 0, 0, 0.85) 92%
);
mask-image: radial-gradient(
ellipse 62% 58% at 50% 50%,
transparent 58%,
rgba(0, 0, 0, 0.85) 92%
);
}
/* ---- self-preview stand-in "footage" (not part of the snippet) ---- */
#yt-cm-stage {
position: absolute;
inset: 0;
overflow: hidden;
background: linear-gradient(135deg, #35c8d9 0%, #2a7fff 55%, #7d5cff 100%);
}
#yt-cm-stage .yt-cm-grid {
position: absolute;
inset: -60px;
background:
repeating-linear-gradient(0deg, rgba(255, 255, 255, 0.14) 0 2px, transparent 2px 120px),
repeating-linear-gradient(90deg, rgba(255, 255, 255, 0.14) 0 2px, transparent 2px 120px);
}
#yt-cm-stage .yt-cm-label {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
font-family: "Inter", ui-sans-serif, sans-serif;
font-weight: 700;
font-size: 120px;
letter-spacing: -0.02em;
color: rgba(255, 255, 255, 0.92);
}
</style>
</head>
<body>
<!-- Self-preview wrapper. WIRING: copy the .yt-cm-defocus CSS (if you want
the edge blur), add <div class="yt-cm-defocus" id="yt-my-defocus"></div>
as a sibling ABOVE your moving layer, and copy the helpers into your
host script. Then drive any wrapper:
ytCameraMove(tl, "#video-wrap", 5.0, { zoom: 0.11, slideY: -24 });
ytDefocusPulse(tl, "#yt-my-defocus", 5.0, 1.2);
ytCameraReset(tl, "#video-wrap", 9.0);
-->
<div
id="yt-cm-root"
data-composition-id="yt-camera-move"
data-start="0"
data-duration="4"
data-width="1920"
data-height="1080"
>
<div id="yt-cm-stage">
<div class="yt-cm-grid" data-layout-allow-overflow></div>
<div class="yt-cm-label">FOOTAGE</div>
</div>
<div class="yt-cm-defocus" id="yt-cm-defocus"></div>
<div
id="yt-cm-drv"
class="clip"
data-start="0"
data-duration="4"
data-track-index="0"
style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
></div>
</div>
<script>
(function () {
window.__timelines = window.__timelines || {};
// ---- copy these helpers into the host script ----
// Punch-in / camera move on any wrapper. A talking-head base:
// zoom 0.11, cubic ease. tilt/pan are degrees (rotationX/rotationY,
// perspective is set for you); slideX/slideY in px; roll in degrees.
window.ytCameraMove = function (tl, target, at, opts) {
opts = opts || {};
var dur = opts.dur === undefined ? 1.2 : opts.dur;
gsap.set(target, { transformPerspective: 1200, transformOrigin: opts.origin || "50% 50%" });
tl.to(
target,
{
scale: 1 + (opts.zoom === undefined ? 0.11 : opts.zoom),
x: opts.slideX || 0,
y: opts.slideY || 0,
rotationX: opts.tilt || 0,
rotationY: opts.pan || 0,
rotation: opts.roll || 0,
duration: dur,
ease: opts.ease || "power2.inOut",
},
at
);
};
// Return to rest (same easing family).
window.ytCameraReset = function (tl, target, at, opts) {
opts = opts || {};
tl.to(
target,
{
scale: 1,
x: 0,
y: 0,
rotationX: 0,
rotationY: 0,
rotation: 0,
duration: opts.dur === undefined ? 1.2 : opts.dur,
ease: opts.ease || "power2.inOut",
},
at
);
};
// Edge-defocus pulse synced to a move (overlay fades in then out).
window.ytDefocusPulse = function (tl, target, at, dur) {
dur = dur === undefined ? 1.2 : dur;
tl.to(target, { opacity: 1, duration: dur * 0.45, ease: "power2.out" }, at);
tl.to(target, { opacity: 0, duration: dur * 0.55, ease: "power2.in" }, at + dur * 0.45);
};
// Self-preview timeline (not part of the snippet you copy):
// punch in with a slight tilt, hold, return.
var tl = gsap.timeline({ paused: true });
window.ytCameraMove(tl, "#yt-cm-stage", 0.4, { zoom: 0.11, slideY: -26, tilt: 3, pan: -2 });
window.ytDefocusPulse(tl, "#yt-cm-defocus", 0.4, 1.2);
window.ytCameraReset(tl, "#yt-cm-stage", 2.5);
window.ytDefocusPulse(tl, "#yt-cm-defocus", 2.5, 1.2);
tl.set("#yt-cm-root", { visibility: "hidden" }, 3.98);
window.__timelines["yt-camera-move"] = tl;
})();
</script>
</body>
</html>
+163
View File
@@ -0,0 +1,163 @@
<!doctype html>
<!-- Demo plate for the catalog preview. Identical to yt-circle-pointer.html except the
root background, which is opaque here so the transparent overlay reads.
Not installed by `hyperframes add` - registry-item.json lists yt-circle-pointer.html only. -->
<html lang="en">
<head>
<meta charset="utf-8" />
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<style>
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #14161c;
overflow: hidden;
}
/* ---- yt-circle-pointer -----------------------------------------------
Annotation tools: a hand-drawn-feel circle that draws on around a
target, and a countdown chip. Position instances via top/left with
explicit width/height. */
.yt-circle {
--yt-red: #e62e2e;
position: absolute;
pointer-events: none;
}
.yt-circle svg {
width: 100%;
height: 100%;
overflow: visible;
}
.yt-circle ellipse {
fill: none;
stroke: var(--yt-ptr-color, var(--yt-red));
stroke-width: 6;
stroke-linecap: round;
transform: rotate(-6deg);
transform-origin: 50% 50%;
}
.yt-count {
position: absolute;
display: inline-flex;
align-items: center;
gap: 14px;
padding: 16px 32px;
border-radius: 999px;
font-family: "Inter", ui-sans-serif, sans-serif;
font-weight: 700;
font-size: 40px;
background: var(--yt-count-bg, var(--yt-red));
color: #ffffff;
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.3);
pointer-events: none;
opacity: 0;
}
.yt-count .num {
font-variant-numeric: tabular-nums;
min-width: 1.2em;
text-align: center;
}
/* self-preview scene */
#yt-ptr-scene {
position: absolute;
inset: 0;
background: linear-gradient(135deg, #35c8d9 0%, #2a7fff 100%);
display: flex;
align-items: center;
justify-content: center;
font-family: "Inter", ui-sans-serif, sans-serif;
font-weight: 700;
font-size: 60px;
color: rgba(255, 255, 255, 0.92);
}
</style>
</head>
<body>
<!-- Self-preview wrapper. WIRING: copy the .yt-circle / .yt-count CSS and
markup patterns, position over your target, then:
ytCircleOn(tl, "#yt-my-circle", 3.0); // draws the ring
ytCircleOff(tl, "#yt-my-circle", 6.0);
ytCountIn(tl, "#yt-my-count", 2.0);
ytCountdown(tl, "#yt-my-count", 2.4, 5); // 5 -> 0, pulsing each tick
-->
<div
id="yt-ptr-root"
data-composition-id="yt-circle-pointer"
data-start="0"
data-duration="5"
data-width="1920"
data-height="1080"
>
<div id="yt-ptr-scene"><span>the thing</span></div>
<div class="yt-circle" id="yt-ptr-circle" style="left: 760px; top: 440px; width: 420px; height: 210px">
<svg viewBox="0 0 420 210"><ellipse cx="210" cy="105" rx="196" ry="88"></ellipse></svg>
</div>
<div class="yt-count" id="yt-ptr-count" style="right: 140px; top: 110px">
<span class="num">5</span><span>sec</span>
</div>
<div
id="yt-ptr-drv"
class="clip"
data-start="0"
data-duration="5"
data-track-index="0"
style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
></div>
</div>
<script>
(function () {
window.__timelines = window.__timelines || {};
// ---- copy these helpers into the host script ----
window.ytCircleOn = function (tl, target, at, dur) {
var el = document.querySelector(target + " ellipse");
var len = el.getTotalLength();
gsap.set(el, { strokeDasharray: len, strokeDashoffset: len });
tl.to(el, { strokeDashoffset: 0, duration: dur === undefined ? 0.7 : dur, ease: "power2.inOut" }, at);
};
window.ytCircleOff = function (tl, target, at) {
tl.to(target, { opacity: 0, duration: 0.35, ease: "power2.in" }, at);
};
window.ytCountIn = function (tl, target, at) {
gsap.set(target, { y: -18, scale: 0.9 });
tl.to(target, { opacity: 1, y: 0, scale: 1, duration: 0.45, ease: "back.out(1.5)" }, at);
};
window.ytCountdown = function (tl, target, at, from) {
var num = document.querySelector(target + " .num");
var st = { v: from };
tl.to(
st,
{
v: 0,
duration: from,
ease: "none",
onUpdate: function () {
num.textContent = String(Math.ceil(st.v));
},
},
at
);
for (var i = 1; i <= from; i++) {
tl.to(target, { scale: 1.07, duration: 0.09, ease: "sine.out" }, at + i - 0.09);
tl.to(target, { scale: 1, duration: 0.14, ease: "sine.in" }, at + i);
}
};
// Self-preview timeline.
var tl = gsap.timeline({ paused: true });
window.ytCountIn(tl, "#yt-ptr-count", 0.3);
window.ytCountdown(tl, "#yt-ptr-count", 0.7, 3);
window.ytCircleOn(tl, "#yt-ptr-circle", 1.0);
window.ytCircleOff(tl, "#yt-ptr-circle", 4.2);
tl.to("#yt-ptr-count", { opacity: 0, duration: 0.3, ease: "power2.in" }, 4.3);
tl.set("#yt-ptr-root", { visibility: "hidden" }, 4.98);
window.__timelines["yt-circle-pointer"] = tl;
})();
</script>
</body>
</html>
+148
View File
@@ -0,0 +1,148 @@
<!doctype html>
<!-- Demo plate for the catalog preview. Identical to yt-feather-highlight.html except the
root background, which is opaque here so the transparent overlay reads.
Not installed by `hyperframes add` - registry-item.json lists yt-feather-highlight.html only. -->
<html lang="en">
<head>
<meta charset="utf-8" />
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<style>
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #14161c;
overflow: hidden;
}
/* ---- yt-feather-highlight --------------------------------------------
Spotlight part of the frame: a dim layer with a feathered ellipse
hole. The hole's position/size are CSS variables, so the helpers
tween them directly (GSAP animates CSS vars). */
.yt-hl {
--yt-hl-x: 50%;
--yt-hl-y: 50%;
--yt-hl-rx: 22%;
--yt-hl-ry: 26%;
--yt-hl-dim: 0.55;
position: absolute;
inset: 0;
pointer-events: none;
opacity: 0;
background: rgba(6, 8, 12, var(--yt-hl-dim));
-webkit-mask-image: radial-gradient(
ellipse var(--yt-hl-rx) var(--yt-hl-ry) at var(--yt-hl-x) var(--yt-hl-y),
transparent 0%,
transparent 52%,
black 88%
);
mask-image: radial-gradient(
ellipse var(--yt-hl-rx) var(--yt-hl-ry) at var(--yt-hl-x) var(--yt-hl-y),
transparent 0%,
transparent 52%,
black 88%
);
}
/* self-preview stand-in scene (not part of the snippet) */
#yt-hl-scene {
position: absolute;
inset: 0;
background: linear-gradient(135deg, #2a7fff 0%, #7d5cff 100%);
display: flex;
align-items: center;
justify-content: space-around;
font-family: "Inter", ui-sans-serif, sans-serif;
font-weight: 700;
font-size: 64px;
color: rgba(255, 255, 255, 0.92);
}
#yt-hl-scene .box {
width: 420px;
height: 300px;
border-radius: 24px;
background: rgba(255, 255, 255, 0.16);
display: flex;
align-items: center;
justify-content: center;
}
</style>
</head>
<body>
<!-- Self-preview wrapper. WIRING: copy the .yt-hl CSS and drop
<div class="yt-hl" id="yt-my-hl"></div> ABOVE the content to spotlight
(same region). Then:
ytHighlightIn(tl, "#yt-my-hl", 3.0, { x: "28%", y: "50%", rx: "20%", ry: "30%" });
ytHighlightTo(tl, "#yt-my-hl", 5.0, { x: "72%" }, 0.8); // glide to the next target
ytHighlightOut(tl, "#yt-my-hl", 8.0);
-->
<div
id="yt-hl-root"
data-composition-id="yt-feather-highlight"
data-start="0"
data-duration="5"
data-width="1920"
data-height="1080"
>
<div id="yt-hl-scene">
<div class="box">A</div>
<div class="box">B</div>
</div>
<div class="yt-hl" id="yt-hl-demo"></div>
<div
id="yt-hl-drv"
class="clip"
data-start="0"
data-duration="5"
data-track-index="0"
style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
></div>
</div>
<script>
(function () {
window.__timelines = window.__timelines || {};
// ---- copy these helpers into the host script ----
window.ytHighlightIn = function (tl, target, at, opts) {
opts = opts || {};
var vars = { opacity: 1, duration: 0.5, ease: "power2.out" };
if (opts.x !== undefined) vars["--yt-hl-x"] = opts.x;
if (opts.y !== undefined) vars["--yt-hl-y"] = opts.y;
if (opts.rx !== undefined) vars["--yt-hl-rx"] = opts.rx;
if (opts.ry !== undefined) vars["--yt-hl-ry"] = opts.ry;
gsap.set(target, {
"--yt-hl-x": opts.x !== undefined ? opts.x : "50%",
"--yt-hl-y": opts.y !== undefined ? opts.y : "50%",
"--yt-hl-rx": opts.rx !== undefined ? opts.rx : "22%",
"--yt-hl-ry": opts.ry !== undefined ? opts.ry : "26%",
});
tl.to(target, { opacity: 1, duration: 0.5, ease: "power2.out" }, at);
};
window.ytHighlightTo = function (tl, target, at, opts, dur) {
opts = opts || {};
var vars = { duration: dur === undefined ? 0.8 : dur, ease: "power2.inOut" };
if (opts.x !== undefined) vars["--yt-hl-x"] = opts.x;
if (opts.y !== undefined) vars["--yt-hl-y"] = opts.y;
if (opts.rx !== undefined) vars["--yt-hl-rx"] = opts.rx;
if (opts.ry !== undefined) vars["--yt-hl-ry"] = opts.ry;
tl.to(target, vars, at);
};
window.ytHighlightOut = function (tl, target, at) {
tl.to(target, { opacity: 0, duration: 0.4, ease: "power2.in" }, at);
};
// Self-preview timeline: spotlight box A, glide to box B, release.
var tl = gsap.timeline({ paused: true });
window.ytHighlightIn(tl, "#yt-hl-demo", 0.5, { x: "26%", y: "50%", rx: "17%", ry: "24%" });
window.ytHighlightTo(tl, "#yt-hl-demo", 2.2, { x: "74%" }, 0.9);
window.ytHighlightOut(tl, "#yt-hl-demo", 4.2);
tl.set("#yt-hl-root", { visibility: "hidden" }, 4.98);
window.__timelines["yt-feather-highlight"] = tl;
})();
</script>
</body>
</html>
+121
View File
@@ -0,0 +1,121 @@
<!doctype html>
<!-- Demo plate for the catalog preview. Identical to yt-screen-warp.html except the
root background, which is opaque here so the transparent overlay reads.
Not installed by `hyperframes add` - registry-item.json lists yt-screen-warp.html only. -->
<html lang="en">
<head>
<meta charset="utf-8" />
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<style>
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #14161c;
overflow: hidden;
}
/* ---- yt-screen-warp ---------------------------------------------------
Make footage read as a physical display: add .yt-screen-warp3d to the
media WRAPPER (subtle perspective), and place .yt-screen-fx overlay
inside it (pixel grid + scanlines + vignette + sheen). */
.yt-screen-warp3d {
transform: perspective(1400px) rotateY(-3.5deg) rotateX(1.6deg);
}
.yt-screen-fx {
position: absolute;
inset: 0;
pointer-events: none;
opacity: 0;
background:
/* sheen */
linear-gradient(115deg, rgba(255, 255, 255, 0.09) 0%, transparent 30%),
/* scanlines */
repeating-linear-gradient(0deg, rgba(0, 0, 0, 0.1) 0 1px, transparent 1px 3px),
/* pixel grid */
repeating-linear-gradient(90deg, rgba(0, 0, 0, 0.06) 0 1px, transparent 1px 4px);
box-shadow:
inset 0 0 90px rgba(0, 0, 0, 0.4),
inset 0 0 8px rgba(0, 0, 0, 0.5);
}
/* self-preview stand-in */
#yt-sw-wrap {
position: absolute;
left: 360px;
top: 190px;
width: 1200px;
height: 700px;
border-radius: 18px;
overflow: hidden;
}
#yt-sw-fill {
position: absolute;
inset: 0;
background: linear-gradient(150deg, #ff8fb8 0%, #7d5cff 60%, #2a7fff 100%);
display: flex;
align-items: center;
justify-content: center;
font-family: "Inter", ui-sans-serif, sans-serif;
font-weight: 700;
font-size: 92px;
color: rgba(255, 255, 255, 0.93);
}
</style>
</head>
<body>
<!-- Self-preview wrapper. WIRING: copy the two CSS classes; add
class="yt-screen-warp3d" to your media wrapper and
<div class="yt-screen-fx" id="yt-my-fx"></div> as its LAST child. Then:
ytScreenOn(tl, "#yt-my-fx", 2.0);
ytScreenOff(tl, "#yt-my-fx", 8.0);
-->
<div
id="yt-sw-root"
data-composition-id="yt-screen-warp"
data-start="0"
data-duration="4"
data-width="1920"
data-height="1080"
>
<div id="yt-sw-wrap" class="yt-screen-warp3d">
<div id="yt-sw-fill">on screen</div>
<div class="yt-screen-fx" id="yt-sw-fx"></div>
</div>
<div
id="yt-sw-drv"
class="clip"
data-start="0"
data-duration="4"
data-track-index="0"
style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
></div>
</div>
<script>
(function () {
window.__timelines = window.__timelines || {};
// ---- copy these helpers into the host script ----
window.ytScreenOn = function (tl, target, at) {
tl.to(target, { opacity: 1, duration: 0.5, ease: "power2.out" }, at);
};
window.ytScreenOff = function (tl, target, at) {
tl.to(target, { opacity: 0, duration: 0.4, ease: "power2.in" }, at);
};
// Self-preview timeline.
var tl = gsap.timeline({ paused: true });
gsap.set("#yt-sw-wrap", { opacity: 0, scale: 0.96 });
tl.to("#yt-sw-wrap", { opacity: 1, scale: 1, duration: 0.5, ease: "power2.out" }, 0.2);
window.ytScreenOn(tl, "#yt-sw-fx", 0.8);
window.ytScreenOff(tl, "#yt-sw-fx", 3.1);
tl.to("#yt-sw-wrap", { opacity: 0, duration: 0.35, ease: "power2.in" }, 3.5);
tl.set("#yt-sw-root", { visibility: "hidden" }, 3.98);
window.__timelines["yt-screen-warp"] = tl;
})();
</script>
</body>
</html>
-20
View File
@@ -615,18 +615,6 @@
"name": "mk-line-graph", "name": "mk-line-graph",
"type": "hyperframes:block" "type": "hyperframes:block"
}, },
{
"name": "mk-logo-sting",
"type": "hyperframes:block"
},
{
"name": "mk-pill-callout",
"type": "hyperframes:component"
},
{
"name": "mk-cta-button",
"type": "hyperframes:component"
},
{ {
"name": "mk-usage-arc", "name": "mk-usage-arc",
"type": "hyperframes:component" "type": "hyperframes:component"
@@ -655,18 +643,10 @@
"name": "yt-vertical-fill", "name": "yt-vertical-fill",
"type": "hyperframes:block" "type": "hyperframes:block"
}, },
{
"name": "yt-doc-lower-third",
"type": "hyperframes:block"
},
{ {
"name": "yt-camera-move", "name": "yt-camera-move",
"type": "hyperframes:component" "type": "hyperframes:component"
}, },
{
"name": "yt-avatar-pip",
"type": "hyperframes:component"
},
{ {
"name": "yt-feather-highlight", "name": "yt-feather-highlight",
"type": "hyperframes:component" "type": "hyperframes:component"