diff --git a/docs/catalog/components/morph-text.mdx b/docs/catalog/components/morph-text.mdx
new file mode 100644
index 0000000000..660a3bb89d
--- /dev/null
+++ b/docs/catalog/components/morph-text.mdx
@@ -0,0 +1,40 @@
+---
+title: "Morph Text"
+description: "Gooey text morph — cycles through an editable word list using SVG threshold + GSAP-driven blur for a fluid, satisfying transition effect"
+---
+
+# Morph Text
+
+Gooey text morph — cycles through an editable word list using SVG threshold + GSAP-driven blur for a fluid, satisfying transition effect
+
+`text` `text-effect` `typography` `morph` `gooey` `kinetic` `animation`
+
+
+
+## Install
+
+
+
+```bash Terminal
+npx hyperframes add morph-text
+```
+
+
+
+## Details
+
+| Property | Value |
+| --- | --- |
+| Type | Component |
+
+## Files
+
+| File | Target | Type |
+| --- | --- | --- |
+| `morph-text.html` | `compositions/components/morph-text.html` | hyperframes:snippet |
+
+## Usage
+
+Open `compositions/components/morph-text.html` and paste its contents into your composition.
+
+Edit the `
` list to change the statements that cycle through. Each `- ` accepts `data-font` (CSS font-family) and `data-color` (hex) attributes. Adjust `data-morph-speed` (seconds per transition) and `data-morph-pause` (hold time) on the root element.
diff --git a/docs/docs.json b/docs/docs.json
index 2a7c3353f4..0de7e282ec 100644
--- a/docs/docs.json
+++ b/docs/docs.json
@@ -223,16 +223,22 @@
{
"group": "Effects",
"pages": [
- "catalog/components/caption-blend-difference",
"catalog/components/grain-overlay",
"catalog/components/grid-pixelate-wipe",
"catalog/components/parallax-unzoom",
"catalog/components/parallax-zoom",
"catalog/components/shimmer-sweep",
- "catalog/components/texture-mask-text",
"catalog/components/vignette"
]
},
+ {
+ "group": "Text Effects",
+ "pages": [
+ "catalog/components/caption-blend-difference",
+ "catalog/components/morph-text",
+ "catalog/components/texture-mask-text"
+ ]
+ },
{
"group": "Blocks",
"pages": [
diff --git a/packages/core/src/registry/types.ts b/packages/core/src/registry/types.ts
index 32ded043f9..cd7ae29c68 100644
--- a/packages/core/src/registry/types.ts
+++ b/packages/core/src/registry/types.ts
@@ -181,7 +181,8 @@ export type BlockCategory =
| "data"
| "scenes"
| "captions"
- | "effects";
+ | "effects"
+ | "text-effects";
export interface BlockCategoryMeta {
id: BlockCategory;
@@ -194,6 +195,7 @@ export const BLOCK_CATEGORIES: BlockCategoryMeta[] = [
{ id: "vfx", label: "VFX", color: "purple" },
{ id: "transitions", label: "Transitions", color: "blue" },
{ id: "effects", label: "Effects", color: "rose" },
+ { id: "text-effects", label: "Text Effects", color: "violet" },
{ id: "social", label: "Social", color: "pink" },
{ id: "data", label: "Data", color: "green" },
{ id: "scenes", label: "Scenes", color: "amber" },
@@ -207,6 +209,7 @@ export function resolveBlockCategory(tags: string[] | undefined): BlockCategory
if (set.has("social") || set.has("overlay")) return "social";
if (set.has("data") || set.has("chart") || set.has("map")) return "data";
if (set.has("html-in-canvas") || set.has("webgl") || set.has("shader")) return "vfx";
+ if (set.has("text-effect")) return "text-effects";
if (set.has("effect") || set.has("grain") || set.has("vignette")) return "effects";
return "scenes";
}
diff --git a/packages/studio/src/hooks/useBlockCatalog.ts b/packages/studio/src/hooks/useBlockCatalog.ts
index 83ea930ed3..74a7eadee1 100644
--- a/packages/studio/src/hooks/useBlockCatalog.ts
+++ b/packages/studio/src/hooks/useBlockCatalog.ts
@@ -1,6 +1,10 @@
import { useState, useEffect, useMemo } from "react";
import type { RegistryItem } from "@hyperframes/core/registry";
-import { type BlockCategory, resolveBlockCategory } from "../utils/blockCategories";
+import {
+ BLOCK_CATEGORIES,
+ type BlockCategory,
+ resolveBlockCategory,
+} from "../utils/blockCategories";
export type CatalogItem = RegistryItem & {
category: BlockCategory;
@@ -15,16 +19,6 @@ export function useBlockCatalog() {
// fallow-ignore-next-line complexity
useEffect(() => {
- const CATEGORY_ORDER: Record = {
- captions: 0,
- vfx: 1,
- transitions: 2,
- effects: 3,
- social: 4,
- data: 5,
- scenes: 6,
- };
-
let cancelled = false;
(async () => {
try {
@@ -34,7 +28,11 @@ export function useBlockCatalog() {
if (cancelled) return;
const items = data
.map((b) => ({ ...b, category: resolveBlockCategory(b.tags) }))
- .sort((a, b) => (CATEGORY_ORDER[a.category] ?? 9) - (CATEGORY_ORDER[b.category] ?? 9));
+ .sort((a, b) => {
+ const ia = BLOCK_CATEGORIES.findIndex((c) => c.id === a.category);
+ const ib = BLOCK_CATEGORIES.findIndex((c) => c.id === b.category);
+ return (ia === -1 ? 99 : ia) - (ib === -1 ? 99 : ib);
+ });
setBlocks(items);
} catch (err) {
if (cancelled) return;
diff --git a/packages/studio/src/utils/blockCategories.ts b/packages/studio/src/utils/blockCategories.ts
index c43b142d89..6358ed6482 100644
--- a/packages/studio/src/utils/blockCategories.ts
+++ b/packages/studio/src/utils/blockCategories.ts
@@ -16,6 +16,7 @@ const COLOR_MAP: Record
+
+
+
+
+
+ Morph Text
+
+
+
+
+
+
+
+
+
+
+ - Do more with less.
+ - Built for what's next.
+ - Fast. Focused. Powerful.
+ - Your work, amplified.
+ - Start free today.
+
+
+
+
+
+
+
+
+
+
diff --git a/registry/components/morph-text/morph-text.html b/registry/components/morph-text/morph-text.html
new file mode 100644
index 0000000000..1b205bafd4
--- /dev/null
+++ b/registry/components/morph-text/morph-text.html
@@ -0,0 +1,234 @@
+
+
+
+
+
+ Morph Text
+
+
+
+
+
+
+
+
+
+
+ - Do more with less.
+ - Built for what's next.
+ - Fast. Focused. Powerful.
+ - Your work, amplified.
+ - Start free today.
+
+
+
+
+
+
+
+
+
+
diff --git a/registry/components/morph-text/registry-item.json b/registry/components/morph-text/registry-item.json
new file mode 100644
index 0000000000..1e2b641760
--- /dev/null
+++ b/registry/components/morph-text/registry-item.json
@@ -0,0 +1,18 @@
+{
+ "$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
+ "name": "morph-text",
+ "type": "hyperframes:component",
+ "title": "Morph Text",
+ "description": "Gooey text morph — cycles through an editable word list using SVG threshold + GSAP-driven blur for a fluid, satisfying transition effect",
+ "tags": ["text", "text-effect", "typography", "morph", "gooey", "kinetic", "animation"],
+ "files": [
+ {
+ "path": "morph-text.html",
+ "target": "compositions/components/morph-text.html",
+ "type": "hyperframes:snippet"
+ }
+ ],
+ "preview": {
+ "video": "https://static.heygen.ai/hyperframes-oss/docs/images/catalog/components/morph-text.mp4"
+ }
+}
diff --git a/registry/components/texture-mask-text/registry-item.json b/registry/components/texture-mask-text/registry-item.json
index 27e34957e1..532a2972c4 100644
--- a/registry/components/texture-mask-text/registry-item.json
+++ b/registry/components/texture-mask-text/registry-item.json
@@ -4,7 +4,7 @@
"type": "hyperframes:component",
"title": "Texture Mask Text",
"description": "CSS luminance masks that cut holes through letterforms - 66 pre-built texture masks from ambientCG PBR color maps",
- "tags": ["text", "texture", "mask", "effect"],
+ "tags": ["text", "text-effect", "effect", "texture", "mask"],
"textureGroups": [
{
"title": "Masonry",
diff --git a/registry/registry.json b/registry/registry.json
index 5e6ad4b93f..baed2b259e 100644
--- a/registry/registry.json
+++ b/registry/registry.json
@@ -155,6 +155,10 @@
"name": "caption-blend-difference",
"type": "hyperframes:component"
},
+ {
+ "name": "morph-text",
+ "type": "hyperframes:component"
+ },
{
"name": "instagram-follow",
"type": "hyperframes:block"