{
  "$schema": "https://ui.shadcn.com/schema/registry.json",
  "name": "Sora UI",
  "homepage": "https://ui.soralabs.io.vn",
  "items": [
    {
      "name": "index",
      "type": "registry:style",
      "dependencies": [
        "tw-animate-css",
        "class-variance-authority",
        "lucide-react"
      ],
      "registryDependencies": ["utils"],
      "cssVars": {},
      "files": []
    },
    {
      "name": "demo-accordion",
      "type": "registry:ui",
      "title": "Accordion Demo",
      "description": "FAQ accordion with animated panels.",
      "registryDependencies": ["@soralabs/accordion"],
      "files": [
        {
          "path": "registry/demo/primitives/disclosure/accordion/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/demo/disclosure/accordion.tsx"
        }
      ]
    },
    {
      "name": "demo-border-trail",
      "type": "registry:ui",
      "title": "Border Trail Demo",
      "description": "Card with animated border spotlight.",
      "registryDependencies": ["@soralabs/border-trail"],
      "files": [
        {
          "path": "registry/demo/primitives/effects/border-trail/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/demo/effects/border-trail.tsx"
        }
      ],
      "meta": {
        "demoProps": {
          "BorderTrailDemo": {
            "size": {
              "value": 100,
              "min": 40,
              "max": 140,
              "step": 4
            },
            "duration": {
              "value": 5,
              "min": 1,
              "max": 12,
              "step": 0.5
            }
          }
        }
      }
    },
    {
      "name": "demo-border-trail-loading",
      "type": "registry:ui",
      "title": "Border Trail Loading Card Demo",
      "description": "Submit-triggered border trail that loops twice then fades.",
      "registryDependencies": ["@soralabs/border-trail"],
      "files": [
        {
          "path": "registry/demo/primitives/effects/border-trail-loading/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/demo/effects/border-trail-loading.tsx"
        }
      ],
      "meta": {
        "demoProps": {
          "BorderTrailLoadingCard": {
            "size": {
              "value": 120,
              "min": 60,
              "max": 160,
              "step": 4
            }
          }
        }
      }
    },
    {
      "name": "demo-border-trail-textarea",
      "type": "registry:ui",
      "title": "Border Trail Textarea Demo",
      "description": "Textarea with a continuous gradient border trail.",
      "registryDependencies": ["@soralabs/border-trail"],
      "files": [
        {
          "path": "registry/demo/primitives/effects/border-trail-textarea/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/demo/effects/border-trail-textarea.tsx"
        }
      ],
      "meta": {
        "demoProps": {
          "BorderTrailTextarea": {
            "size": {
              "value": 120,
              "min": 60,
              "max": 160,
              "step": 4
            },
            "duration": {
              "value": 5,
              "min": 1,
              "max": 12,
              "step": 0.5
            }
          }
        }
      }
    },
    {
      "name": "demo-cursor-trail-reveal",
      "type": "registry:ui",
      "title": "Cursor Trail Reveal Demo",
      "description": "Full-page contact layout with cursor trail image reveals on desktop.",
      "registryDependencies": [
        "@soralabs/cursor-trail-reveal",
        "@soralabs/text-reveal-block",
        "@soralabs/text-scramble"
      ],
      "files": [
        {
          "path": "registry/demo/primitives/effects/cursor-trail-reveal/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/demo/effects/cursor-trail-reveal.tsx"
        }
      ]
    },
    {
      "name": "demo-custom-cursor",
      "type": "registry:ui",
      "title": "Custom Cursor Demo",
      "description": "Custom cursor demo with Sora icons and shape variants.",
      "registryDependencies": ["@soralabs/custom-cursor"],
      "files": [
        {
          "path": "registry/demo/primitives/effects/custom-cursor/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/demo/effects/custom-cursor.tsx"
        }
      ],
      "meta": {
        "demoProps": {
          "CustomCursorExample": {
            "color": {
              "value": "#ff4c24"
            },
            "followStiffness": {
              "value": 150,
              "min": 50,
              "max": 300,
              "step": 10
            },
            "followDamping": {
              "value": 22,
              "min": 10,
              "max": 40,
              "step": 1
            },
            "className": {
              "value": "w-full"
            }
          }
        }
      }
    },
    {
      "name": "demo-dock-nav",
      "type": "registry:ui",
      "title": "Dock Nav Demo",
      "description": "Apple-style dock navigation demo with app icons.",
      "registryDependencies": ["@soralabs/dock-nav"],
      "files": [
        {
          "path": "registry/demo/primitives/effects/dock-nav/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/demo/effects/dock-nav.tsx"
        }
      ]
    },
    {
      "name": "demo-highlight",
      "type": "registry:ui",
      "title": "Highlight Demo",
      "description": "Shared sliding highlight pill moving between nav items on hover.",
      "registryDependencies": ["@soralabs/highlight"],
      "files": [
        {
          "path": "registry/demo/primitives/effects/highlight/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/demo/effects/highlight.tsx"
        }
      ]
    },
    {
      "name": "demo-highlight-click",
      "type": "registry:ui",
      "title": "Highlight Click Demo",
      "description": "Per-item fade background activated on click — menu list style.",
      "registryDependencies": ["@soralabs/highlight"],
      "files": [
        {
          "path": "registry/demo/primitives/effects/highlight-click/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/demo/effects/highlight-click.tsx"
        }
      ]
    },
    {
      "name": "demo-logo-carousel-swapper",
      "type": "registry:ui",
      "title": "Logo Carousel Swapper Demo",
      "description": "Staggered four-column logo swapper with blur and vertical motion.",
      "registryDependencies": ["@soralabs/logo-carousel-swapper"],
      "files": [
        {
          "path": "registry/demo/primitives/effects/logo-carousel-swapper/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/demo/effects/logo-carousel-swapper.tsx"
        }
      ]
    },
    {
      "name": "demo-magnetic-cards",
      "type": "registry:ui",
      "title": "Magnetic Cards Demo",
      "description": "A fanned stack of cards reacting to cursor movement with GSAP-driven spring physics.",
      "registryDependencies": ["@soralabs/magnetic-cards"],
      "files": [
        {
          "path": "registry/demo/primitives/effects/magnetic-cards/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/demo/effects/magnetic-cards.tsx"
        }
      ]
    },
    {
      "name": "demo-progressive-blur",
      "type": "registry:ui",
      "title": "Progressive Blur Demo",
      "description": "A scrollable panel with progressive blur fading content under the top and bottom edges.",
      "registryDependencies": ["@soralabs/progressive-blur"],
      "files": [
        {
          "path": "registry/demo/primitives/effects/progressive-blur/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/demo/effects/progressive-blur.tsx"
        }
      ]
    },
    {
      "name": "demo-scroll-gallery",
      "type": "registry:ui",
      "title": "Scroll Gallery Demo",
      "description": "Sticky scroll slider with 4 slides, strip-mask transitions, and animated title swaps.",
      "registryDependencies": ["@soralabs/scroll-gallery"],
      "files": [
        {
          "path": "registry/demo/primitives/effects/scroll-gallery/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/demo/effects/scroll-gallery.tsx"
        }
      ]
    },
    {
      "name": "demo-skeleton",
      "type": "registry:ui",
      "title": "Skeleton Demo",
      "description": "A profile card loading, then crossfading into content once ready.",
      "registryDependencies": ["@soralabs/skeleton"],
      "files": [
        {
          "path": "registry/demo/primitives/effects/skeleton/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/demo/effects/skeleton.tsx"
        }
      ]
    },
    {
      "name": "demo-sticky-scroll-cards",
      "type": "registry:ui",
      "title": "Sticky Scroll Cards Demo",
      "description": "Front card flips to reveal 4 stacked back cards that dismiss one at a time as you scroll.",
      "registryDependencies": ["@soralabs/sticky-scroll-cards"],
      "files": [
        {
          "path": "registry/demo/primitives/effects/sticky-scroll-cards/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/demo/effects/sticky-scroll-cards.tsx"
        }
      ]
    },
    {
      "name": "demo-bottom-sheet",
      "type": "registry:ui",
      "title": "Bottom Sheet Demo",
      "description": "A settings panel reproduced with the Bottom Sheet primitive: a trigger, a hidden title, and a row list toggling a few mock settings.",
      "registryDependencies": ["@soralabs/bottom-sheet"],
      "files": [
        {
          "path": "registry/demo/primitives/radix/bottom-sheet/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/demo/radix/bottom-sheet.tsx"
        }
      ]
    },
    {
      "name": "demo-draw-underline-link",
      "type": "registry:ui",
      "title": "Draw Underline Link Demo",
      "description": "Scrollable catalog preview with two centered link instances.",
      "registryDependencies": ["@soralabs/draw-underline-link"],
      "files": [
        {
          "path": "registry/demo/primitives/texts/draw-underline-link/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/demo/texts/draw-underline-link.tsx"
        }
      ]
    },
    {
      "name": "demo-number-flow",
      "type": "registry:ui",
      "title": "Number Flow Demo",
      "description": "Number Flow demo with currency, input overlay, and countdown examples.",
      "registryDependencies": ["@soralabs/number-flow"],
      "files": [
        {
          "path": "registry/demo/primitives/texts/number-flow/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/demo/texts/number-flow.tsx"
        }
      ]
    },
    {
      "name": "demo-text-effect",
      "type": "registry:ui",
      "title": "Text Effect Demo",
      "description": "Scroll-triggered text reveal with blur, fade, slide, and scale presets.",
      "registryDependencies": ["@soralabs/text-effect"],
      "files": [
        {
          "path": "registry/demo/primitives/texts/text-effect/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/demo/texts/text-effect.tsx"
        }
      ]
    },
    {
      "name": "demo-text-morph",
      "type": "registry:ui",
      "title": "Text Morph Demo",
      "description": "Continue / Confirm pill button with per-character layout morph.",
      "registryDependencies": ["@soralabs/text-morph"],
      "files": [
        {
          "path": "registry/demo/primitives/texts/text-morph/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/demo/texts/text-morph.tsx"
        }
      ]
    },
    {
      "name": "demo-text-morph-input",
      "type": "registry:ui",
      "title": "Text Morph Input Demo",
      "description": "Live text morph driven by a controlled input.",
      "registryDependencies": ["@soralabs/text-morph"],
      "files": [
        {
          "path": "registry/demo/primitives/texts/text-morph-input/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/demo/texts/text-morph-input.tsx"
        }
      ]
    },
    {
      "name": "demo-text-reveal-box",
      "type": "registry:ui",
      "title": "Text Reveal Box Demo",
      "description": "Scroll-pinned manifesto copy with word-by-word highlight reveal.",
      "registryDependencies": ["@soralabs/text-reveal-box"],
      "files": [
        {
          "path": "registry/demo/primitives/texts/text-reveal-box/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/demo/texts/text-reveal-box.tsx"
        }
      ]
    },
    {
      "name": "demo-text-reveal-mask",
      "type": "registry:ui",
      "title": "Text Reveal (Mask) Demo",
      "description": "Scroll-triggered masked text reveal with rich text emphasis.",
      "registryDependencies": ["@soralabs/text-reveal-mask"],
      "files": [
        {
          "path": "registry/demo/primitives/texts/text-reveal-mask/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/demo/texts/text-reveal-mask.tsx"
        }
      ],
      "meta": {
        "demoProps": {
          "MaskedTextRevealExample": {
            "splitBy": {
              "value": "lines",
              "options": {
                "Lines": "lines",
                "Words": "words",
                "Characters": "chars"
              }
            },
            "stagger": {
              "value": 0.1,
              "min": 0.01,
              "max": 0.5,
              "step": 0.01
            },
            "duration": {
              "value": 0.8,
              "min": 0.1,
              "max": 2,
              "step": 0.1
            },
            "yPercent": {
              "value": 110,
              "min": 50,
              "max": 200,
              "step": 5
            },
            "delay": {
              "value": 0,
              "min": 0,
              "max": 2,
              "step": 0.1
            },
            "viewportMargin": {
              "value": "0px 0px -20% 0px"
            }
          }
        }
      }
    },
    {
      "name": "demo-text-roll",
      "type": "registry:ui",
      "title": "Text Roll Demo",
      "description": "Interactive copy button with chromatic text roll feedback.",
      "registryDependencies": ["@soralabs/text-roll"],
      "files": [
        {
          "path": "registry/demo/primitives/texts/text-roll/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/demo/texts/text-roll.tsx"
        }
      ],
      "meta": {
        "demoProps": {
          "TextRollPlayground": {
            "className": {
              "value": "font-medium text-lg tabular-nums"
            },
            "stagger": {
              "value": 45,
              "min": 0,
              "max": 120,
              "step": 5
            },
            "duration": {
              "value": 300,
              "min": 100,
              "max": 600,
              "step": 25
            },
            "bounce": {
              "value": 0.6,
              "min": 0,
              "max": 1,
              "step": 0.1
            },
            "chromatic": {
              "value": true
            }
          }
        }
      }
    },
    {
      "name": "hooks-use-auto-height",
      "type": "registry:hook",
      "title": "useAutoHeight",
      "description": "A hook that allows you to automatically adjust the height of an element based on its content.",
      "files": [
        {
          "path": "registry/hooks/use-auto-height/index.tsx",
          "type": "registry:hook",
          "target": "hooks/use-auto-height.tsx"
        }
      ]
    },
    {
      "name": "hooks-use-controlled-state",
      "type": "registry:hook",
      "title": "useControlledState",
      "description": "A hook that allows you to control a state.",
      "files": [
        {
          "path": "registry/hooks/use-controlled-state/index.tsx",
          "type": "registry:hook",
          "target": "hooks/use-controlled-state.tsx"
        }
      ]
    },
    {
      "name": "hooks-use-prefers-reduced-motion",
      "type": "registry:hook",
      "title": "usePrefersReducedMotion",
      "description": "A hook that tracks the OS-level Reduced Motion setting live, so GSAP-driven primitives can skip or shortcut their animations.",
      "files": [
        {
          "path": "registry/hooks/use-prefers-reduced-motion/index.tsx",
          "type": "registry:hook",
          "target": "hooks/use-prefers-reduced-motion.tsx"
        }
      ]
    },
    {
      "name": "lib-ease",
      "type": "registry:lib",
      "title": "Ease & Spring Tokens",
      "description": "Shared cubic-bezier eases and Motion spring presets (press, swap, panel, layout, mouse) used across primitives to keep motion consistent.",
      "files": [
        {
          "path": "registry/lib/ease/index.tsx",
          "type": "registry:lib",
          "target": "lib/ease.tsx"
        }
      ]
    },
    {
      "name": "lib-get-strict-context",
      "type": "registry:lib",
      "title": "getStrictContext",
      "description": "A hook that allows you to create a strict context.",
      "files": [
        {
          "path": "registry/lib/get-strict-context/index.tsx",
          "type": "registry:lib",
          "target": "lib/get-strict-context.tsx"
        }
      ]
    },
    {
      "name": "lib-scroll-trigger-utils",
      "type": "registry:lib",
      "title": "Scroll Trigger Utils",
      "description": "Shared GSAP ScrollTrigger helpers (window/scroller detection, ready/resize waiting) used by scroll-driven primitives.",
      "files": [
        {
          "path": "registry/lib/scroll-trigger-utils/index.tsx",
          "type": "registry:lib",
          "target": "lib/scroll-trigger-utils.tsx"
        }
      ]
    },
    {
      "name": "char-stagger-button",
      "type": "registry:ui",
      "title": "Char Stagger Button",
      "description": "A button with per-character stagger animation on hover.",
      "dependencies": ["class-variance-authority"],
      "registryDependencies": ["utils"],
      "files": [
        {
          "path": "registry/primitives/buttons/char-stagger-button/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/buttons/char-stagger-button.tsx"
        }
      ],
      "meta": {
        "demoProps": {
          "CharStaggerButton": {
            "label": {
              "value": "Staggering Button"
            },
            "variant": {
              "value": "default",
              "options": {
                "Default": "default",
                "Link": "link"
              }
            },
            "staggerDelay": {
              "value": 0.01,
              "min": 0,
              "max": 0.1,
              "step": 0.005
            }
          }
        }
      }
    },
    {
      "name": "section-cta",
      "type": "registry:ui",
      "title": "Section CTA",
      "description": "A chip-style call-to-action link with a Motion-powered sliding label and rotating icon chips on hover.",
      "dependencies": ["motion", "class-variance-authority"],
      "registryDependencies": ["utils"],
      "files": [
        {
          "path": "registry/primitives/buttons/section-cta/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/buttons/section-cta.tsx"
        }
      ],
      "meta": {
        "inspiration": {
          "type": "inspired",
          "label": "Annnimate",
          "url": "https://www.annnimate.com"
        },
        "demoProps": {
          "SectionCta": {
            "label": {
              "value": "Explore components"
            },
            "variant": {
              "value": "accent",
              "options": {
                "Accent": "accent",
                "Inverted": "inverted",
                "Outline": "outline",
                "Ghost": "ghost"
              }
            },
            "size": {
              "value": "default",
              "options": {
                "Small": "sm",
                "Default": "default",
                "Large": "lg"
              }
            },
            "duration": {
              "value": 0.7,
              "min": 0.2,
              "max": 2,
              "step": 0.1
            }
          }
        }
      }
    },
    {
      "name": "accordion",
      "type": "registry:ui",
      "title": "Accordion",
      "description": "An animated FAQ accordion with expandable panels, powered by Motion.",
      "dependencies": ["motion", "class-variance-authority"],
      "registryDependencies": ["utils", "@soralabs/hooks-use-auto-height"],
      "files": [
        {
          "path": "registry/primitives/disclosure/accordion/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/disclosure/accordion.tsx"
        }
      ],
      "meta": {
        "inspiration": {
          "type": "reimplemented",
          "label": "Annnimate",
          "url": "https://www.annnimate.com",
          "stack": "Motion and React"
        }
      }
    },
    {
      "name": "border-trail",
      "type": "registry:ui",
      "title": "Border Trail",
      "description": "Animated border spotlight that travels around a rounded container, powered by Motion.",
      "dependencies": ["motion"],
      "registryDependencies": ["utils"],
      "files": [
        {
          "path": "registry/primitives/effects/border-trail/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/effects/border-trail.tsx"
        }
      ],
      "meta": {
        "inspiration": {
          "type": "inspired",
          "label": "motion-primitives",
          "url": "https://motion-primitives.com/docs/border-trail"
        }
      }
    },
    {
      "name": "cursor-trail-reveal",
      "type": "registry:ui",
      "title": "Cursor Trail Reveal",
      "description": "Desktop cursor trail that reveals staggered image strips with clip-path wipes as the pointer moves.",
      "dependencies": ["class-variance-authority"],
      "registryDependencies": [
        "utils",
        "@soralabs/hooks-use-prefers-reduced-motion"
      ],
      "files": [
        {
          "path": "registry/primitives/effects/cursor-trail-reveal/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/effects/cursor-trail-reveal.tsx"
        }
      ]
    },
    {
      "name": "custom-cursor",
      "type": "registry:ui",
      "title": "Custom Cursor",
      "description": "A smooth custom cursor that morphs when hovering interactive targets, powered by Motion.",
      "dependencies": ["motion", "class-variance-authority"],
      "registryDependencies": ["utils"],
      "files": [
        {
          "path": "registry/primitives/effects/custom-cursor/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/effects/custom-cursor.tsx"
        }
      ]
    },
    {
      "name": "dock-nav",
      "type": "registry:ui",
      "title": "Dock Nav",
      "description": "An Apple-style dock navigation bar with Motion-powered expanding icons and tooltips on hover.",
      "dependencies": ["motion", "class-variance-authority"],
      "registryDependencies": ["utils"],
      "files": [
        {
          "path": "registry/primitives/effects/dock-nav/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/effects/dock-nav.tsx"
        }
      ]
    },
    {
      "name": "highlight",
      "type": "registry:ui",
      "title": "Highlight",
      "description": "A Motion-powered highlight effect that allows you to highlight elements on hover, click or with a controlled value.",
      "dependencies": ["motion"],
      "registryDependencies": ["utils"],
      "files": [
        {
          "path": "registry/primitives/effects/highlight/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/effects/highlight.tsx"
        }
      ]
    },
    {
      "name": "logo-carousel-swapper",
      "type": "registry:ui",
      "title": "Logo Carousel Swapper",
      "description": "Staggered logo column swapper with fade, vertical shift, and motion blur powered by Motion.",
      "dependencies": ["motion", "class-variance-authority"],
      "registryDependencies": ["utils"],
      "files": [
        {
          "path": "registry/primitives/effects/logo-carousel-swapper/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/effects/logo-carousel-swapper.tsx"
        }
      ],
      "meta": {
        "keywords": [
          "logo",
          "carousel",
          "stagger",
          "blur",
          "motion",
          "brands",
          "swap"
        ],
        "demoProps": {
          "LogoCarouselSwapper": {
            "monochrome": {
              "value": true
            }
          }
        }
      }
    },
    {
      "name": "magnetic-cards",
      "type": "registry:ui",
      "title": "Magnetic Cards",
      "description": "A fanned stack of cards that spring away from the cursor with GSAP-driven, velocity-based physics.",
      "dependencies": ["gsap", "@gsap/react"],
      "registryDependencies": ["@soralabs/hooks-use-prefers-reduced-motion"],
      "files": [
        {
          "path": "registry/primitives/effects/magnetic-cards/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/effects/magnetic-cards.tsx"
        }
      ],
      "meta": {
        "keywords": [
          "magnetic",
          "cards",
          "cursor",
          "physics",
          "spring",
          "gsap"
        ],
        "inspiration": {
          "type": "reimplemented",
          "label": "Spencer Gabor",
          "url": "https://spencergabor.work/",
          "stack": "GSAP and React"
        }
      }
    },
    {
      "name": "pixelated-image-reveal",
      "type": "registry:ui",
      "title": "Pixelated Image Reveal",
      "description": "An image card that reveals a second image through a randomized pixel grid on hover, powered by Motion.",
      "dependencies": ["motion", "class-variance-authority"],
      "registryDependencies": ["utils"],
      "files": [
        {
          "path": "registry/primitives/effects/pixelated-image-reveal/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/effects/pixelated-image-reveal.tsx"
        }
      ],
      "meta": {
        "demoProps": {
          "PixelatedImageReveal": {
            "alt": {
              "value": "Pixelated image reveal demo"
            },
            "pixelColor": {
              "value": "#ff4c24"
            },
            "gridSize": {
              "value": 7,
              "min": 3,
              "max": 12,
              "step": 1
            },
            "stepDuration": {
              "value": 0.3,
              "min": 0.1,
              "max": 1,
              "step": 0.05
            },
            "aspect": {
              "value": "square",
              "options": {
                "Square": "square",
                "Video": "video",
                "Auto": "auto"
              }
            },
            "className": {
              "value": "mx-auto w-72"
            }
          }
        }
      }
    },
    {
      "name": "progressive-blur",
      "type": "registry:ui",
      "title": "Progressive Blur",
      "description": "A stacked-layer blur that ramps smoothly from sharp to fully blurred toward an edge, for fading content under a fixed header or above a scroll boundary.",
      "registryDependencies": [
        "utils",
        "@soralabs/hooks-use-prefers-reduced-motion"
      ],
      "files": [
        {
          "path": "registry/primitives/effects/progressive-blur/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/effects/progressive-blur.tsx"
        }
      ],
      "meta": {
        "keywords": [
          "progressive-blur",
          "blur",
          "scroll",
          "fade",
          "backdrop-filter",
          "gradient-blur"
        ],
        "inspiration": {
          "type": "adapted",
          "label": "gxuri (Skiper UI)",
          "url": "https://gxuri.me"
        }
      }
    },
    {
      "name": "scroll-gallery",
      "type": "registry:ui",
      "title": "Scroll Gallery",
      "description": "A full-viewport sticky scroll slider with strip-mask image transitions and animated title swaps, driven by GSAP ScrollTrigger.",
      "dependencies": ["gsap", "@gsap/react"],
      "registryDependencies": [
        "utils",
        "@soralabs/hooks-use-prefers-reduced-motion",
        "@soralabs/lib-scroll-trigger-utils"
      ],
      "files": [
        {
          "path": "registry/primitives/effects/scroll-gallery/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/effects/scroll-gallery.tsx"
        }
      ],
      "meta": {
        "demoProps": {
          "ScrollGallery": {
            "prefixLabel": {
              "value": "Featured"
            },
            "linkLabel": {
              "value": "Explore"
            },
            "showPrefix": {
              "value": true
            },
            "showLink": {
              "value": true
            },
            "showInfoBand": {
              "value": true
            },
            "scrollPerTransition": {
              "value": 600,
              "min": 200,
              "max": 1500,
              "step": 50
            },
            "stripsCount": {
              "value": 20,
              "min": 4,
              "max": 40,
              "step": 2
            },
            "scrub": {
              "value": 1,
              "min": 0,
              "max": 3,
              "step": 0.1
            }
          }
        }
      }
    },
    {
      "name": "skeleton",
      "type": "registry:ui",
      "title": "Skeleton",
      "description": "A loading placeholder with shimmer or fade variants, plus a transition that crossfades into real content once loading finishes.",
      "registryDependencies": ["utils"],
      "files": [
        {
          "path": "registry/primitives/effects/skeleton/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/effects/skeleton.tsx"
        }
      ],
      "meta": {
        "keywords": [
          "skeleton",
          "loading",
          "placeholder",
          "shimmer",
          "shimmer-effect"
        ]
      }
    },
    {
      "name": "sticky-scroll-cards",
      "type": "registry:ui",
      "title": "Sticky Scroll Cards",
      "description": "Scroll-pinned hero where a front card flips to reveal a stack of back cards, dismissed one at a time as you keep scrolling.",
      "dependencies": ["gsap", "@gsap/react"],
      "registryDependencies": [
        "@soralabs/hooks-use-prefers-reduced-motion",
        "@soralabs/lib-scroll-trigger-utils"
      ],
      "files": [
        {
          "path": "registry/primitives/effects/sticky-scroll-cards/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/effects/sticky-scroll-cards.tsx"
        }
      ],
      "meta": {
        "keywords": [
          "sticky",
          "pin",
          "card",
          "flip",
          "stack",
          "scroll",
          "gsap",
          "scrolltrigger"
        ],
        "inspiration": {
          "type": "reimplemented",
          "label": "Codegrid",
          "stack": "GSAP and vanilla JS"
        }
      }
    },
    {
      "name": "bottom-sheet",
      "type": "registry:ui",
      "title": "Bottom Sheet",
      "description": "A draggable, snap-point bottom sheet built on Radix Dialog for accessibility, with Motion driving the drag gesture and glide.",
      "dependencies": ["motion", "radix-ui"],
      "registryDependencies": [
        "utils",
        "@soralabs/hooks-use-controlled-state",
        "@soralabs/hooks-use-prefers-reduced-motion",
        "@soralabs/lib-get-strict-context",
        "@soralabs/lib-ease"
      ],
      "files": [
        {
          "path": "registry/primitives/radix/bottom-sheet/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/radix/bottom-sheet.tsx"
        }
      ],
      "meta": {
        "inspiration": {
          "type": "inspired",
          "label": "Vaul",
          "url": "https://vaul.emilkowal.ski",
          "stack": "Radix Dialog and Motion"
        }
      }
    },
    {
      "name": "draw-underline-link",
      "type": "registry:ui",
      "title": "Draw Underline Link",
      "description": "A link with a hand-drawn SVG underline that animates on hover, powered by Motion.",
      "dependencies": ["motion", "class-variance-authority"],
      "registryDependencies": ["utils"],
      "files": [
        {
          "path": "registry/primitives/texts/draw-underline-link/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/texts/draw-underline-link.tsx"
        }
      ],
      "meta": {
        "inspiration": {
          "type": "reimplemented",
          "label": "Osmo",
          "url": "https://osmo.supply",
          "stack": "Motion and React"
        },
        "demoProps": {
          "DrawUnderlineLink": {
            "label": {
              "value": "Hover me"
            },
            "variant": {
              "value": "default",
              "options": {
                "Default": "default",
                "Small": "sm",
                "Large": "lg"
              }
            },
            "duration": {
              "value": 0.5,
              "min": 0.2,
              "max": 2,
              "step": 0.1
            },
            "underlineColor": {
              "value": "#ff4c24"
            }
          }
        }
      }
    },
    {
      "name": "number-flow",
      "type": "registry:ui",
      "title": "Number Flow",
      "description": "An animated, digit-by-digit number counter with a smooth rolling odometer effect, powered by Motion.",
      "dependencies": ["motion"],
      "registryDependencies": ["utils"],
      "files": [
        {
          "path": "registry/primitives/texts/number-flow/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/texts/number-flow.tsx"
        }
      ],
      "meta": {
        "inspiration": {
          "type": "reimplemented",
          "label": "Number Flow",
          "url": "https://number-flow.barvian.me/",
          "stack": "Motion and React"
        }
      }
    },
    {
      "name": "text-effect",
      "type": "registry:ui",
      "title": "Text Effect",
      "description": "Preset-driven staggered text reveals with blur, fade, slide, and scale animations, powered by Motion.",
      "dependencies": ["motion"],
      "registryDependencies": ["utils"],
      "files": [
        {
          "path": "registry/primitives/texts/text-effect/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/texts/text-effect.tsx"
        }
      ],
      "meta": {
        "inspiration": {
          "type": "inspired",
          "label": "motion-primitives",
          "url": "https://motion-primitives.com/docs/text-effect"
        },
        "demoProps": {
          "TextEffect": {
            "as": {
              "value": "h2",
              "options": {
                "h2": "h2",
                "p": "p",
                "span": "span"
              }
            },
            "children": {
              "value": "Motion-first text effects"
            },
            "className": {
              "value": "text-center font-bold text-3xl"
            },
            "preset": {
              "value": "fade-in-blur",
              "options": {
                "Fade in blur": "fade-in-blur",
                "Blur": "blur",
                "Fade": "fade",
                "Slide": "slide",
                "Scale": "scale"
              }
            },
            "per": {
              "value": "word",
              "options": {
                "Word": "word",
                "Char": "char",
                "Line": "line"
              }
            },
            "delay": {
              "value": 0,
              "min": 0,
              "max": 2,
              "step": 0.1
            },
            "speedReveal": {
              "value": 1,
              "min": 0.25,
              "max": 3,
              "step": 0.25
            },
            "speedSegment": {
              "value": 1,
              "min": 0.25,
              "max": 3,
              "step": 0.25
            },
            "scrollTrigger": {
              "value": true
            }
          }
        }
      }
    },
    {
      "name": "text-morph",
      "type": "registry:ui",
      "title": "Text Morph",
      "description": "Per-character layout morphing for labels, counters, and live text swaps, powered by Motion.",
      "dependencies": ["motion"],
      "registryDependencies": ["utils"],
      "files": [
        {
          "path": "registry/primitives/texts/text-morph/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/texts/text-morph.tsx"
        }
      ],
      "meta": {
        "inspiration": {
          "type": "inspired",
          "label": "motion-primitives",
          "url": "https://motion-primitives.com/docs/text-morph"
        }
      }
    },
    {
      "name": "text-reveal-block",
      "type": "registry:ui",
      "title": "Text Reveal (Block)",
      "description": "Line-by-line text reveal with a colored block wipe, powered by GSAP SplitText.",
      "dependencies": ["gsap", "@gsap/react"],
      "registryDependencies": [
        "@soralabs/hooks-use-prefers-reduced-motion",
        "@soralabs/lib-scroll-trigger-utils"
      ],
      "files": [
        {
          "path": "registry/primitives/texts/text-reveal-block/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/texts/text-reveal-block.tsx"
        }
      ],
      "meta": {
        "demoProps": {
          "TextRevealBlock": {
            "as": {
              "value": "h2",
              "options": {
                "h1": "h1",
                "h2": "h2",
                "p": "p"
              }
            },
            "text": {
              "value": "Framed in shadow and light, every line arrives with deliberate tension."
            },
            "className": {
              "value": "mx-auto max-w-lg text-center font-semibold text-2xl leading-tight tracking-tight"
            },
            "animateOnScroll": {
              "value": false
            },
            "blockColor": {
              "value": "#fe0100"
            },
            "stagger": {
              "value": 0.15,
              "min": 0,
              "max": 0.5,
              "step": 0.05
            },
            "duration": {
              "value": 0.75,
              "min": 0.2,
              "max": 2,
              "step": 0.05
            },
            "delay": {
              "value": 0,
              "min": 0,
              "max": 1,
              "step": 0.05
            },
            "direction": {
              "value": "left",
              "options": {
                "left": "left",
                "right": "right",
                "up": "up",
                "down": "down"
              }
            }
          }
        }
      }
    },
    {
      "name": "text-reveal-box",
      "type": "registry:ui",
      "title": "Text Reveal Box",
      "description": "Scroll-pinned word-by-word text reveal with a highlight flash, powered by GSAP ScrollTrigger.",
      "dependencies": ["gsap", "@gsap/react"],
      "registryDependencies": [
        "@soralabs/hooks-use-prefers-reduced-motion",
        "@soralabs/lib-scroll-trigger-utils"
      ],
      "files": [
        {
          "path": "registry/primitives/texts/text-reveal-box/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/texts/text-reveal-box.tsx"
        }
      ],
      "meta": {
        "keywords": [
          "scroll",
          "text",
          "reveal",
          "gsap",
          "scrolltrigger",
          "pin"
        ],
        "inspiration": {
          "type": "reimplemented",
          "label": "Deadlock Studios",
          "url": "https://github.com/Axyl1410",
          "stack": "GSAP and React"
        }
      }
    },
    {
      "name": "text-reveal-mask",
      "type": "registry:ui",
      "title": "Text Reveal (Mask)",
      "description": "A scroll-triggered masked slide-up text reveal animation, powered by Motion.",
      "dependencies": ["motion"],
      "registryDependencies": ["utils"],
      "files": [
        {
          "path": "registry/primitives/texts/text-reveal-mask/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/texts/text-reveal-mask.tsx"
        }
      ],
      "meta": {
        "inspiration": {
          "type": "reimplemented",
          "label": "Annnimate",
          "url": "https://www.annnimate.com",
          "stack": "Motion and React"
        },
        "demoProps": {
          "MaskedTextReveal": {
            "as": {
              "value": "h2",
              "options": {
                "h2": "h2",
                "p": "p",
                "span": "span"
              }
            },
            "className": {
              "value": "mx-auto max-w-3xl text-center text-3xl leading-tight font-normal tracking-tight text-foreground/75 [&_strong]:font-medium [&_strong]:text-foreground"
            },
            "text": {
              "value": "Every stride is a statement. Every finish line a new beginning. We build gear for athletes who refuse to settle for yesterday's pace. Push beyond limits. Outrun expectations. This is where champions are made."
            },
            "splitBy": {
              "value": "lines",
              "options": {
                "Lines": "lines",
                "Words": "words",
                "Characters": "chars"
              }
            },
            "stagger": {
              "value": 0.1,
              "min": 0.01,
              "max": 0.5,
              "step": 0.01
            },
            "duration": {
              "value": 0.8,
              "min": 0.1,
              "max": 2,
              "step": 0.1
            },
            "yPercent": {
              "value": 110,
              "min": 50,
              "max": 200,
              "step": 5
            },
            "delay": {
              "value": 0,
              "min": 0,
              "max": 2,
              "step": 0.1
            },
            "viewportMargin": {
              "value": "0px 0px -25% 0px"
            }
          }
        }
      }
    },
    {
      "name": "text-roll",
      "type": "registry:ui",
      "title": "Text Roll",
      "description": "A per-character vertical text roll animation for label swaps and micro-interactions, powered by Motion.",
      "dependencies": ["motion"],
      "registryDependencies": ["utils"],
      "files": [
        {
          "path": "registry/primitives/texts/text-roll/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/texts/text-roll.tsx"
        }
      ],
      "meta": {
        "inspiration": {
          "type": "inspired",
          "label": "slot-text",
          "url": "https://textmotion.dev/"
        }
      }
    },
    {
      "name": "text-scramble",
      "type": "registry:ui",
      "title": "Text Scramble",
      "description": "A text scramble reveal animation that decodes characters on mount or when re-triggered, powered by Motion.",
      "dependencies": ["motion"],
      "files": [
        {
          "path": "registry/primitives/texts/text-scramble/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/texts/text-scramble.tsx"
        }
      ],
      "meta": {
        "demoProps": {
          "TextScramble": {
            "children": {
              "value": "Sora UI"
            },
            "as": {
              "value": "p",
              "options": {
                "p": "p",
                "h2": "h2",
                "span": "span"
              }
            },
            "className": {
              "value": "font-mono text-2xl font-bold uppercase tracking-widest text-foreground"
            },
            "scrambleColor": {
              "value": ""
            },
            "holdDuration": {
              "value": 0,
              "min": 0,
              "max": 2,
              "step": 0.1
            },
            "duration": {
              "value": 0.5,
              "min": 0.2,
              "max": 3,
              "step": 0.1
            },
            "speed": {
              "value": 0.03,
              "min": 0.01,
              "max": 0.15,
              "step": 0.01
            },
            "trigger": {
              "value": true
            },
            "triggerOnHover": {
              "value": false
            }
          }
        }
      }
    },
    {
      "name": "text-underline",
      "type": "registry:ui",
      "title": "Text Underline",
      "description": "A CSS-only link underline that slides through on hover.",
      "dependencies": ["class-variance-authority"],
      "registryDependencies": ["utils"],
      "files": [
        {
          "path": "registry/primitives/texts/text-underline/index.tsx",
          "type": "registry:ui",
          "target": "components/sora-ui/texts/text-underline.tsx"
        }
      ],
      "meta": {
        "demoProps": {
          "TextUnderline": {
            "label": {
              "value": "Performance Collection"
            },
            "underlineColor": {
              "value": "brand",
              "options": {
                "Current": "currentColor",
                "Brand": "brand",
                "Brand Muted": "brand-muted",
                "Gray": "gray"
              }
            },
            "underlineHeight": {
              "value": "thin",
              "options": {
                "Thin": "thin",
                "Medium": "medium",
                "Thick": "thick"
              }
            },
            "duration": {
              "value": "normal",
              "options": {
                "Fast": "fast",
                "Normal": "normal",
                "Slow": "slow"
              }
            },
            "initialScale": {
              "value": "0",
              "options": {
                "0%": "0",
                "25%": "25",
                "50%": "50",
                "75%": "75"
              }
            },
            "anchor": {
              "value": "left",
              "options": {
                "Left (slide through)": "left",
                "Center (expand)": "center"
              }
            }
          }
        }
      }
    }
  ]
}
