Skip to content

feat: improve site ui and integrate with shadcn - #59

Merged
Dinil-Thilakarathne merged 61 commits into
masterfrom
dev
Feb 4, 2026
Merged

Dinil-Thilakarathne merged 61 commits into
masterfrom
dev

Conversation

@Dinil-Thilakarathne

@Dinil-Thilakarathne Dinil-Thilakarathne commented Feb 4, 2026 •

Copy link
Copy Markdown
Owner

This pull request introduces significant improvements to Sona UI by integrating it as a Shadcn-compatible registry, updating component registry files, enhancing the build process, and updating dependencies for better compatibility and developer experience.

Shadcn Registry Integration and Component Registry Updates:

  • Sona UI is now set up as a Shadcn-compatible registry, including a new registry configuration in components.json and a detailed feature request template describing the integration goals and benefits. [1] [2]
  • Added new registry JSON files for each Sona UI component (e.g., accordion, bubble-up-button, expandable-tabs, link-preview, magnetic-button, marquee) in the public/r/ directory, specifying component metadata, file paths, and dependencies for seamless use with the Shadcn CLI. [1] [2] [3] [4] [5] [6]

Build Process and Developer Tooling Improvements:

  • Updated build scripts in package.json to provide new commands for building the registry and syncing releases, replacing the old copy-sources.js approach.
  • Added .vscode/settings.json to improve Tailwind CSS IntelliSense for custom class functions (cva, cx).

Dependency Updates:

  • Upgraded several dependencies and devDependencies for improved stability, compatibility, and access to new features (e.g., next, lucide-react, motion, arktype, posthog-js, tailwindcss, and more).

These changes collectively enhance the developer experience, streamline component consumption via the Shadcn ecosystem, and modernize the build and dependency setup.

Summary by CodeRabbit

  • New Features

    • Added 10 new animated UI components (Accordion, Spinning Text, Bubble Up Button, Marquee, Ripple Button, Vertical Tab, Expandable Tabs, Link Preview, Stagger Text, Magnetic Button)
    • Implemented component registry system for easy installation and discovery
    • Added comprehensive code block component with syntax highlighting and line numbers
    • Added dark mode theme support with improved color system
  • Documentation

    • Added Installation, Theming, and Changelog guides
    • Updated component documentation with examples
  • Improvements

    • Enhanced copy-to-clipboard functionality
    • Improved scroll area with fade effects
    • Refined UI styling with updated color tokens

* fix: improve sidebar layout

* fix: update search bar component ui
* fix: improve sidebar layout

* fix: update sidebar ui
* fix: improve sidebar layout

* fix: update sidebar ui

* fix: update side bar ui
@vercel

vercel Bot commented Feb 4, 2026 •

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
sona-ui Ready Ready Preview, Comment Feb 4, 2026 10:32pm

@coderabbitai

coderabbitai Bot commented Feb 4, 2026 •

Copy link
Copy Markdown

Warning

Rate limit exceeded

@Dinil-Thilakarathne has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 0 minutes and 20 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📝 Walkthrough

Walkthrough

This PR implements a comprehensive registry system for Sona UI components with shadcn-compatible architecture. It restructures configuration to support dynamic component registration, adds build scripts for registry generation, introduces ten UI components with manifest entries, implements an advanced code block system with syntax highlighting, updates styling infrastructure, and reorganizes documentation with component-driven examples.

Changes

Cohort / File(s) Summary
Registry Configuration
components.json, registry.json, .vscode/settings.json
Restructured components.json to global UI schema with registries section; added root registry.json with 10 component entries; configured Tailwind CSS class function helpers.
Registry Build Scripts
scripts/build-registry.ts, scripts/build-registry-json.ts, scripts/update-registry-json.ts, scripts/sync-releases.ts, scripts/generate-usage-code.ts
New TypeScript build infrastructure for generating registry manifests, syncing GitHub releases, and generating usage code from component files.
Component Registry Manifests
public/r/*.json
Added 10 individual component manifest files (accordion, bubble-up-button, expandable-tabs, link-preview, magnetic-button, marquee, ripple-button, spinning-text, stagger-text, vertical-tab) defining component metadata, files, and dependencies.
UI Components - Accordion
src/registry/sonaui/accordion/accordion.tsx, src/registry/sonaui/accordion/animated-plus-minus-button.tsx, src/content/docs/accordion.mdx
New accordion component with CVA-based variants ("default", "outlined", "splitted"), updated prop interface, changed doc slug to kebab-case.
UI Components - Interactive Motion
src/registry/sonaui/bubble-up-button/bubble-up-button.tsx, src/registry/sonaui/magnetic-button/magnetic-button.tsx, src/registry/sonaui/ripple-button/ripple-button.tsx, src/registry/sonaui/expandable-tabs/expandable-tabs.tsx
New animated button/tab components with motion controls (hover effects, magnetic fields, ripple animations) and corresponding documentation updates.
UI Components - Text Effects
src/registry/sonaui/spinning-text/spinning-text.tsx, src/registry/sonaui/stagger-text/stagger-text.tsx, src/content/docs/spinning-text.mdx, src/content/docs/stagger-text.mdx
New text animation components with character-level staggering and circular layouts; updated docs with component-driven examples.
UI Components - Utility
src/registry/sonaui/link-preview/link-preview.tsx, src/registry/sonaui/marquee/marquee.tsx, src/registry/sonaui/vertical-tab/vertical-tab.tsx
New link preview with hover popups, marquee scrolling, and vertical tab navigation; updated component slugs and documentation.
Code Block System
src/components/code-block/code-block.tsx, src/components/code-block/code-block-source.tsx, src/components/code-block/internal-code-block.tsx, src/components/code-block/lib/...
Comprehensive code block component with syntax highlighting via Shiki, optional line numbers/diffs/focus modes, floating copy button, tabs support, and transformer utilities (highlight-lines, diff, focus).
Scroll Area & Tabs
src/components/scroll-area/scroll-area.tsx, src/components/scroll-area/scroll-area.css, src/components/tabs/tabs.tsx, src/components/common/tabs.tsx
New ScrollArea with CSS-driven fade edges, dual render modes (native/custom); new Tabs components with CVA variants and animated indicators.
Copy Button
src/components/copy-button/copy-button.tsx, src/components/copy-button/lib/copy-to-clipboard.ts
New CopyButton with clipboard interaction hook, copy-to-clipboard utility with Clipboard API fallback, and icon animations.
Component Installation & Preview
src/components/common/component-installation.tsx, src/components/common/component-installation-server.tsx, src/components/common/component-preview.tsx, src/components/common/component-preview-server.tsx
New server/client components for rendering component installation instructions, usage examples, and tabbed previews with package manager selection.
Documentation & Examples
src/content/docs/installation.mdx, src/content/docs/theming.mdx, src/content/docs/changelog.mdx, src/registry/examples/*
New Getting Started docs (installation, theming, changelog); reorganized examples from example/ to examples/ with new demo components; updated all component docs to use component-driven layouts.
Styling & Theme
src/app/globals.css, src/styles/theme-data.css, src/lib/types.ts
Extended CSS with fumadocs-ui imports, unified root color tokens, new animations (fade-in-up, float), easing functions, and comprehensive color-scheme/theme support; added "Getting Started" to typeVariants.
Layout & Common Components
src/app/layout.tsx, src/app/docs/layout.tsx, src/app/page.tsx, src/components/header/..., src/components/common/...
Updated import paths to lowercase conventions; simplified ThemeProvider props; added ModeToggle to header; updated component card, sidebar, and profile popover paths; minor UI tweaks (border radius, height adjustments).
Registry Index
src/registry/index.ts, src/registry/registry.json
Generated centralized registry exporting RegistryEntry type, exampleRegistry (component examples), Registry type, and registry object with aggregated component metadata and code content.
Package Configuration
package.json
Added fumadocs-ui, class-variance-authority, caniuse-lite dependencies; added build:registry and sync:releases scripts; upgraded multiple packages (next, react, @base-ui/react, etc.); removed yarn packageManager field.
Removed Components
src/components/Common/ComponentPreview.tsx, src/components/Common/ModeToggle.tsx, src/components/Common/Tabs.tsx, src/registry/example/*_ex.tsx
Deprecated old dynamic-import ComponentPreview, ModeToggle (replaced with theme-toggle), and custom Tabs in favor of new base-ui versions; removed old example files from example/ directory.
Feature Request Documentation
FEATURE_REQUEST_SHADCN_REGISTRY.md
New proposal document outlining Sona UI shadcn registry integration with registry.json structure, installation flows, and alignment with shadcn specs.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

Possibly related PRs

Suggested labels

enhancement, documentation

Poem

🐰 A registry, oh what a sight!
Ten components, bundled tight,
Scripts that build, docs that guide,
With shadcn tools side by side!
From text that spins to buttons that glow—
Hop along, let Sona UI flow! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 24.49% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: improve site ui and integrate with shadcn' directly aligns with the PR's main objectives: site UI improvements and shadcn registry integration.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dev

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Note

Due to the large number of review comments, Critical severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (6)
src/content/docs/magnetic-button.mdx (1)

11-56: ⚠️ Potential issue | 🟡 Minor

Remove "global" from the Features section.

The interactionArea prop only supports "self" and "parent". The Features section incorrectly lists "global" as an option—remove it to match the actual component API shown in the Props table.

src/content/docs/accordion.mdx (1)

69-73: ⚠️ Potential issue | 🟡 Minor

Props table variant type is inconsistent with the features list.

Line 17 states the variants are default, outlined, and splitted, but the PropTable on line 70 still shows 'default' | 'bordered' | 'splitted'. The AI summary confirms the variant was renamed from "bordered" to "outlined".

📝 Proposed fix
     {
       name: "variant",
-      type: "'default' | 'bordered' | 'splitted'",
+      type: "'default' | 'outlined' | 'splitted'",
       default: "default",
       description: "The visual style of the accordion.",
     },
src/content/docs/expandable-tabs.mdx (1)

52-57: ⚠️ Potential issue | 🟡 Minor

Props table shows outdated prop name transitionConfig.

The component interface uses motionConfig (visible in relevant code snippets at src/registry/sonaui/expandableTabs/ExpandableTabs.tsx:15), but the documentation still references transitionConfig. The changelog also confirms this rename occurred in v2.5.1.

📝 Proposed fix
     {
-      name: "transitionConfig",
-      type: "Transition",
+      name: "motionConfig",
+      type: "MotionConfigProps",
       default: '{ duration: 0.2, ease: "easeInOut" }',
       description: "Configuration for the transition animations.",
     },
src/content/docs/bubble-up-button.mdx (1)

17-17: ⚠️ Potential issue | 🟡 Minor

Update library reference from "Framer Motion" to "Motion".

The PR migrated the dependency from framer-motion to motion (as noted in the commit messages and library context showing motion version 12.29.2). The documentation should reflect this change.

📝 Proposed fix
-- Built with Framer Motion for smooth animations.
+- Built with Motion for smooth animations.
src/content/docs/spinning-text.mdx (1)

68-72: ⚠️ Potential issue | 🟡 Minor

Remove fontSize from the props documentation or add it as a prop to the component.

The fontSize prop documented in the props table (lines 68-72) does not exist in the SpinningTextProps interface (lines 8-20 of src/registry/sonaui/spinning-text/spinning-text.tsx). The component accepts a style prop for custom styling but does not expose a dedicated fontSize prop. Either add fontSize as a dedicated prop to the component or update the documentation to direct users to the style prop instead.

src/registry/sonaui/accordion/accordion.tsx (1)

163-182: ⚠️ Potential issue | 🟠 Major

Accessibility: Missing keyboard support on accordion trigger.

The AccordionItemTrigger uses a div with onClick but lacks keyboard accessibility. Users should be able to toggle with Enter/Space keys.

🐛 Add keyboard support
 const AccordionItemTrigger = ({
   value,
   children,
 }: AccordionItemTriggerProps) => {
   const context = useContext(AccordionContext);
   if (!context)
     throw new Error("AccordionTrigger must be used within AccordionRoot");

   const { openItems, toggleItem } = context;
   const isOpen = openItems.has(value);

+  const handleKeyDown = (e: React.KeyboardEvent) => {
+    if (e.key === "Enter" || e.key === " ") {
+      e.preventDefault();
+      toggleItem(value);
+    }
+  };

   return (
     <div
+      role="button"
+      tabIndex={0}
       aria-expanded={isOpen}
       onClick={() => toggleItem(value)}
+      onKeyDown={handleKeyDown}
       className="cursor-pointer"
     >
       {children}
     </div>
   );
 };
🤖 Fix all issues with AI agents
In `@src/app/globals.css`:
- Around line 76-80: The CSS defines circular font variables (--font-sans,
--font-mono, --font-rubik) that reference themselves; update the assignments
inside the `@theme` block to reference the actual Next.js-injected variables
(--font-geist-sans, --font-geist-mono, --font-clash-display) used in
src/app/layout.tsx (or replace them with real font stacks), e.g. set
--font-sans: var(--font-geist-sans), --font-mono: var(--font-geist-mono), and
--font-rubik: var(--font-clash-display) so the variables resolve correctly.

In `@src/components/common/component-installation.tsx`:
- Line 78: The code uses the experimental ViewTransition component
(ViewTransition) which is not available in stable react@19.2 and is applied only
to the manual tab content (ManualInstallTab) while the CLI tab (CliInstallTab)
remains unwrapped, causing both compatibility and inconsistent animation
behavior; either remove the ViewTransition import and wrapper entirely from the
component (ComponentInstallation) to remain compatible with stable React, or
switch the project to react@canary/react@experimental and wrap both tab contents
(the ManualInstallTab and CliInstallTab render branches) with ViewTransition so
the behavior is consistent across tabs.

In `@src/registry/registry.json`:
- Around line 16-136: Registry entries use camelCase "name" values causing
lookup mismatches; update each component "name" to kebab-case to match how MDX
passes slugs and the generator's expectation. Edit src/registry/registry.json
and replace names like verticalTab, rippleButton, spinningText, staggerText,
linkPreview, bubbleUpButton, expandableTabs with vertical-tab, ripple-button,
spinning-text, stagger-text, link-preview, bubble-up-button, expandable-tabs
respectively (leave other fields unchanged). After change, run the registry
generator (the script that documents "kebab-case directory name") or validate
that MDX ComponentInstallation lookups succeed.

In `@src/styles/theme-data.css`:
- Around line 6-9: The CSS custom properties --font-sans, --font-mono, and
--font-rubik currently alias themselves (e.g., --font-sans: var(--font-sans))
causing circular, unresolved values; replace each of those declarations with
real font-family fallbacks (for example set --font-sans to a stack like "Inter,
system-ui, -apple-system, sans-serif") or remove the unused variables entirely,
and apply the same change to any duplicate declarations elsewhere in the
codebase to ensure fonts actually resolve.
🟠 Major comments (20)
src/components/common/tabs.tsx-24-44 (1)

24-44: ⚠️ Potential issue | 🟠 Major

Add tab semantics and keyboard navigation.

This is a tabs UI but lacks role="tablist"/"tab"/"tabpanel", aria-selected, aria-controls, and keyboard navigation, which makes it inaccessible to screen readers and keyboard users.

Minimal a11y wiring (example)
-      <div className="bg-secondary max-w-[calc(100vw-16px) relative flex w-fit overflow-auto rounded-xl p-1 md:max-w-[auto] md:space-x-2">
+      <div
+        role="tablist"
+        aria-orientation="horizontal"
+        className="bg-secondary max-w-[calc(100vw-16px)] relative flex w-fit overflow-auto rounded-xl p-1 md:max-w-[auto] md:space-x-2"
+      >
         {tabs.map((tab, index) => (
           <Button
             key={index}
             className={cn("relative cursor-pointer rounded-lg")}
             onClick={() => setActiveTab(index)}
             data-active={activeTab === index}
+            role="tab"
+            id={`${tabId}-tab-${index}`}
+            aria-selected={activeTab === index}
+            aria-controls={`${tabId}-panel-${index}`}
+            tabIndex={activeTab === index ? 0 : -1}
           >
             {activeTab === index && (
               <motion.div
                 layoutId={`active-tab-${tabId}`}
                 className="bg-background/90 absolute inset-0 h-full w-full rounded-lg border-2"
                 transition={{ type: "spring", bounce: 0.2, duration: 0.6 }}
               />
             )}
             <span className="relative z-10 block px-4 py-2">{tab.title}</span>
           </Button>
         ))}
       </div>
-      <div className="tabs-content">{tabs[activeTab]?.content}</div>
+      <div
+        role="tabpanel"
+        id={`${tabId}-panel-${activeTab}`}
+        aria-labelledby={`${tabId}-tab-${activeTab}`}
+        className="tabs-content"
+      >
+        {tabs[activeTab]?.content}
+      </div>

You’ll also want ArrowLeft/ArrowRight/Home/End key handling to complete keyboard support.

registry.json-72-102 (1)

72-102: ⚠️ Potential issue | 🟠 Major

Add missing motion dependency to Marquee and Ripple Button registry entries.

Both components import from motion/react but their dependencies arrays don't include it:

  • Marquee declares only ["react-use-measure"], missing "motion"
  • Ripple Button declares [], missing "motion"

This will break downstream installs via the registry. Update both entries to include "motion" in their dependencies arrays.

public/r/spinning-text.json-8-15 (1)

8-15: ⚠️ Potential issue | 🟠 Major

Add missing motion dependency to manifest.

The embedded code imports motion/react, but the registry entry lists no dependencies. This will break consumers pulling from the public registry.

Suggested JSON update
-  "dependencies": [],
+  "dependencies": [
+    "motion"
+  ],
public/r/vertical-tab.json-8-15 (1)

8-15: ⚠️ Potential issue | 🟠 Major

Add missing dependencies for imported packages to the registry manifest.

The component code imports motion/react and react-use-measure, but both packages are missing from the dependencies array. This will cause installation failures for consumers. Add them to match the pattern used in other registry components like accordion and marquee.

Suggested JSON fix
-  "dependencies": [],
+  "dependencies": [
+    "motion",
+    "react-use-measure"
+  ],
src/registry/registry.json-16-89 (1)

16-89: ⚠️ Potential issue | 🟠 Major

Add missing external dependency motion to components that import it.

verticalTab, spinningText, and rippleButton all import motion/react, but dependencies don't include motion. This will cause broken installs when users consume registry entries.

  • verticalTab: Add "motion" to dependencies
  • spinningText: Add "motion" to dependencies
  • rippleButton: Add "motion" to dependencies

(Note: accordion already has motion correctly listed.)

🛠️ Suggested JSON updates
   "dependencies": [
-    "react-use-measure"
+    "react-use-measure",
+    "motion"
   ]
-  "dependencies": []
+  "dependencies": [
+    "motion"
+  ]
-  "dependencies": []
+  "dependencies": [
+    "motion"
+  ]
public/r/stagger-text.json-14-15 (1)

14-15: ⚠️ Potential issue | 🟠 Major

Registry entry is missing the motion dependency.

The embedded component imports motion/react (line 9 of the file content), but dependencies is empty. Registry consumers will miss-install the package and hit module resolution errors.

Suggested fix
-  "dependencies": [],
+  "dependencies": ["motion"],
public/r/bubble-up-button.json-14-15 (1)

14-15: ⚠️ Potential issue | 🟠 Major

Add the missing motion dependency.
The component imports motion/react, but the dependency is not declared. Consumers using the CLI will not automatically install it.

Suggested fix
-  "dependencies": [],
+  "dependencies": ["motion"],
src/components/code-block/code-block-source.tsx-9-18 (1)

9-18: ⚠️ Potential issue | 🟠 Major

Constrain filePath in readFileContent to trusted locations.

The readFileContent function in src/lib/file-utils.ts accepts an arbitrary filePath and joins it with process.cwd() without validating against path traversal attacks. A malicious or accidental path like ../../../../etc/passwd could read arbitrary server files. Add a normalization check to ensure paths stay within the intended directory.

🛡️ Example fix
+import path from "node:path";
...
export async function readFileContent(filePath: string): Promise<string> {
  try {
-   const fullPath = path.join(process.cwd(), filePath);
+   const root = path.join(process.cwd(), "src");
+   const resolved = path.resolve(root, filePath);
+   if (!resolved.startsWith(root + path.sep)) {
+     throw new Error("Invalid filePath");
+   }
+   const fullPath = resolved;
    const content = await fs.readFile(fullPath, "utf-8");
    return content;
src/components/code-block/internal-code-block.tsx-15-23 (1)

15-23: ⚠️ Potential issue | 🟠 Major

className from props is overwritten.
Because className is set after {...props}, any caller‑provided className is discarded. Merge instead.

🔧 Suggested fix
-import * as React from "react";
+import * as React from "react";
+import { cn } from "@/lib/utils";
...
-export function InternalCodeBlock({
-  code,
-  language,
-  filename,
-  ...props
-}: InternalCodeBlockProps) {
+export function InternalCodeBlock({
+  code,
+  language,
+  filename,
+  className,
+  ...props
+}: InternalCodeBlockProps) {
   return (
-    <CodeBlock code={code} language={language} {...props} className=" my-4">
+    <CodeBlock
+      code={code}
+      language={language}
+      {...props}
+      className={cn("my-4", className)}
+    >
public/r/link-preview.json-9-9 (1)

9-9: ⚠️ Potential issue | 🟠 Major

Positioning logic may cause incorrect popup placement.

The embedded LinkPreview component calculates popup position using containerBounds.left and containerBounds.top (viewport-relative coordinates from getBoundingClientRect), but applies them directly to an absolutely positioned element rendered as a sibling fragment. This will only work correctly if the popup's positioned ancestor is at (0,0) of the viewport. In most layouts with scrolling or nested positioned containers, the popup will appear misaligned.

Consider either:

  1. Using position: fixed for the popup (with viewport coordinates)
  2. Calculating the offset relative to the positioned ancestor
  3. Using a portal to render at the document body level
public/r/link-preview.json-14-15 (1)

14-15: ⚠️ Potential issue | 🟠 Major

Missing runtime dependencies in the registry manifest.

The component imports motion/react, react-icons/fa6, and react-use-measure, but the dependencies array is empty. Consumers installing this component via the shadcn CLI won't have these packages installed automatically.

Suggested fix
-  "dependencies": [],
+  "dependencies": ["motion", "react-icons", "react-use-measure"],
public/r/ripple-button.json-14-15 (1)

14-15: ⚠️ Potential issue | 🟠 Major

Missing motion dependency.

The component imports from motion/react but the dependencies array is empty.

Suggested fix
-  "dependencies": [],
+  "dependencies": ["motion"],
public/r/expandable-tabs.json-14-15 (1)

14-15: ⚠️ Potential issue | 🟠 Major

Missing runtime dependencies.

The component imports from motion/react and react-icons, but these are not listed in the dependencies array. This will cause installation failures for consumers using the shadcn CLI.

Suggested fix
-  "dependencies": [],
+  "dependencies": ["motion", "react-icons"],
public/r/magnetic-button.json-14-15 (1)

14-15: ⚠️ Potential issue | 🟠 Major

Missing motion dependency.

The component imports from motion/react but the dependencies array is empty. This will cause installation issues for consumers.

Suggested fix
-  "dependencies": [],
+  "dependencies": ["motion"],
scripts/build-registry-json.ts-86-95 (1)

86-95: ⚠️ Potential issue | 🟠 Major

Spread operator may unintentionally override computed fields.

The ...metadata spread at the end will override any previously set fields (name, type, title, description, files, dependencies, registryDependencies) if they exist in the metadata object. This is likely unintended since you're computing these values explicitly.

Suggested fix - spread metadata first
       const entry = {
+        ...metadata, // Apply metadata first so explicit values take precedence
         name: componentName,
         type: "registry:ui",
         title: metadata.title || toTitleCase(componentName),
         description: metadata.description || `Component for ${componentName}`,
         files: componentFiles,
         dependencies: metadata.dependencies || [],
         registryDependencies: metadata.registryDependencies || [],
-        ...metadata, // overwrite with specific metadata if exists
       };
scripts/update-registry-json.ts-9-23 (1)

9-23: ⚠️ Potential issue | 🟠 Major

Complete the migration from framer-motion to motion or remove unused package from KNOWN_DEPENDENCIES.

The codebase still has 3 active imports from framer-motion (in src/registry/index.ts, src/registry/sonaui/accordion/animated-plus-minus-button.tsx, and src/components/common/scroll-up-button.tsx), but the motion package is listed in KNOWN_DEPENDENCIES and installed as a dependency without being used. Either finish migrating all components to use the motion package and remove "framer-motion" from KNOWN_DEPENDENCIES, or remove "motion" if it's no longer needed.

scripts/build-registry.ts-246-257 (1)

246-257: ⚠️ Potential issue | 🟠 Major

Normalize componentMetadata keys to kebab-case to avoid lookup misses.

The generated registry uses kebab-case component IDs (from folder names), but componentMetadata keys are taken verbatim from registry.json (camelCase in the current output). ComponentInstallationServer indexes by the kebab-case component string, so metadata will resolve to undefined for names like vertical-tab vs verticalTab. Normalize keys when ingesting registry.json.

✅ Suggested fix
-      componentMetadata = registryData.reduce((acc: any, item: any) => {
+      componentMetadata = registryData.reduce((acc: any, item: any) => {
         // Normalize usage fields if they are arrays
         if (item.usage) {
           if (Array.isArray(item.usage.imports)) {
             item.usage.imports = item.usage.imports.join("\n");
           }
           if (Array.isArray(item.usage.code)) {
             item.usage.code = item.usage.code.join("\n");
           }
         }
-        acc[item.name] = item;
+        const key = toKebabCase(item.name);
+        acc[key] = { ...item, name: key };
         return acc;
       }, {});
public/r/registry.json-58-162 (1)

58-162: ⚠️ Potential issue | 🟠 Major

Fill in missing dependency lists for motion/react, react-use-measure, react-icons.

Several components use external packages but list incomplete or empty dependencies, which will cause shadcn CLI installs to fail:

  • bubble-up-button, ripple-button, stagger-text import motion/react but have no dependencies listed
  • vertical-tab imports both motion/react and react-use-measure but has no dependencies listed
  • expandable-tabs imports motion/react and react-icons but has no dependencies listed
  • link-preview imports motion/react, react-icons, and react-use-measure but has no dependencies listed
  • marquee imports motion/react but only lists react-use-measure
  • accordion's animated-plus-minus-button.tsx imports framer-motion while the manifest declares "motion" as a dependency; align this discrepancy
Example dependency fixes (apply similarly)
     {
       "name": "bubble-up-button",
       "type": "registry:ui",
       "title": "Bubble Up Button",
       "description": "Component for bubble-up-button",
       "files": [
         {
           "path": "registry/sonaui/bubble-up-button/bubble-up-button.tsx",
           "type": "registry:ui",
           "target": "components/sona-ui/bubble-up-button/bubble-up-button.tsx"
         }
       ],
-      "dependencies": [],
+      "dependencies": ["motion"],
       "registryDependencies": []
     },
     {
       "name": "vertical-tab",
       "type": "registry:ui",
       "title": "Vertical Tab",
       "description": "Component for vertical-tab",
       "files": [
         {
           "path": "registry/sonaui/vertical-tab/vertical-tab.tsx",
           "type": "registry:ui",
           "target": "components/sona-ui/vertical-tab/vertical-tab.tsx"
         }
       ],
-      "dependencies": [],
+      "dependencies": ["motion", "react-use-measure"],
       "registryDependencies": []
     },
     {
       "name": "expandable-tabs",
       "type": "registry:ui",
       "title": "Expandable Tabs",
       "description": "Component for expandable-tabs",
       "files": [
         {
           "path": "registry/sonaui/expandable-tabs/expandable-tabs.tsx",
           "type": "registry:ui",
           "target": "components/sona-ui/expandable-tabs/expandable-tabs.tsx"
         }
       ],
-      "dependencies": [],
+      "dependencies": ["motion", "react-icons"],
       "registryDependencies": []
     },
     {
       "name": "link-preview",
       "type": "registry:ui",
       "title": "Link Preview",
       "description": "Component for link-preview",
       "files": [
         {
           "path": "registry/sonaui/link-preview/link-preview.tsx",
           "type": "registry:ui",
           "target": "components/sona-ui/link-preview/link-preview.tsx"
         }
       ],
-      "dependencies": [],
+      "dependencies": ["motion", "react-icons", "react-use-measure"],
       "registryDependencies": []
     }
src/components/common/mdx-components.tsx-5-25 (1)

5-25: ⚠️ Potential issue | 🟠 Major

Add "use client" directive and resolve client/server component mixing in the MDX components map.

This module uses the useMDXComponent React hook (line 146) but lacks a "use client" directive, which will cause Next.js to throw an error since hooks can only run in client components. Additionally, the components object (lines 46–138) mixes client-only components (Tabs, CodeBlock variants, CodeSyntaxHighlighter, ComponentWrapper) with server-only components (ComponentInstallationServer, ComponentUsageServer, ComponentPreviewServer), creating a client/server boundary violation.

Add "use client" at the top of the file, then either split the component map into separate server/client modules or use dynamic imports to wrap server-only components so they're not directly imported into a client module.

src/components/code-block/code-block.tsx-130-140 (1)

130-140: ⚠️ Potential issue | 🟠 Major

Prevent stale async highlight results from overwriting newer code. When code/language changes quickly, earlier promises can resolve after newer ones and call setNodes() with stale results. Add a cancellation flag in the effect cleanup to guard against this race condition.

✅ Suggested fix (cancel stale async results)
   useLayoutEffect(() => {
-    // Only run async highlighting when no pre-rendered content
-    if (!initial) {
-      const normalizedLanguage = (language as BundledLanguage) || "javascript";
-      void highlight(code, normalizedLanguage, {
-        highlightLines,
-        showDiff,
-        focusLines,
-      }).then(setNodes);
-    }
+    if (initial) return;
+    let cancelled = false;
+    const normalizedLanguage = (language as BundledLanguage) || "javascript";
+    void highlight(code, normalizedLanguage, {
+      highlightLines,
+      showDiff,
+      focusLines,
+    }).then((result) => {
+      if (!cancelled) setNodes(result);
+    });
+    return () => {
+      cancelled = true;
+    };
   }, [code, language, initial, highlightLines, showDiff, focusLines]);
🟡 Minor comments (22)
src/content/docs/ripple-button.mdx-3-3 (1)

3-3: ⚠️ Potential issue | 🟡 Minor

Update sitemap.ts to use the new slug format.

The documentation slug was changed to ripple-button, but src/app/sitemap.ts still references the old camelCase format rippleButton. Update the sitemap entry to match the new slug to ensure proper navigation and SEO consistency.

src/components/common/start-count.tsx-21-26 (1)

21-26: ⚠️ Potential issue | 🟡 Minor

Keep a screen-reader label on small screens.

The label is now fully hidden on small screens, which can remove the accessible name. Consider keeping it sr-only for small viewports and visible on large.

Suggested adjustment
-      <span className="hidden leading-3.5 lg:block">Star on GitHub</span>
+      <span className="sr-only lg:not-sr-only">Star on GitHub</span>
FEATURE_REQUEST_SHADCN_REGISTRY.md-28-28 (1)

28-28: ⚠️ Potential issue | 🟡 Minor

Typo: "strucute" should be "structure".

📝 Proposed fix
-# page strucute
+# page structure
src/content/docs/accordion.mdx-41-41 (1)

41-41: ⚠️ Potential issue | 🟡 Minor

Typo: "Splited" should be "Splitted".

The section header has a spelling error. Note that line 43 correctly uses "splitted".

📝 Proposed fix
-### Splited
+### Splitted
src/components/common/prop-table.tsx-21-23 (1)

21-23: ⚠️ Potential issue | 🟡 Minor

rounded-lg! is invalid Tailwind syntax.

If you need the important modifier, the ! must prefix the class.

Proposed fix
-          className="props-table overflow-clip rounded-lg! border border-border text-wrap shadow-sm"
+          className="props-table overflow-clip !rounded-lg border border-border text-wrap shadow-sm"
src/components/common/tabs.tsx-24-26 (1)

24-26: ⚠️ Potential issue | 🟡 Minor

Fix malformed Tailwind class token.

max-w-[calc(100vw-16px) is missing the closing ], so the max-width rule won’t apply.

Proposed fix
-      <div className="bg-secondary max-w-[calc(100vw-16px) relative flex w-fit overflow-auto rounded-xl p-1 md:max-w-[auto] md:space-x-2">
+      <div className="bg-secondary max-w-[calc(100vw-16px)] relative flex w-fit overflow-auto rounded-xl p-1 md:max-w-[auto] md:space-x-2">
src/components/copy-button/lib/copy-to-clipboard.ts-1-22 (1)

1-22: ⚠️ Potential issue | 🟡 Minor

Guard for non-browser usage and ensure cleanup in finally.

If this helper is ever invoked outside the browser, it will throw. Also, if execCommand fails mid-way, the textarea may not be removed.

Proposed fix
 export async function copyToClipboard(text: string): Promise<boolean> {
+  if (typeof navigator === "undefined" || typeof document === "undefined") {
+    return false;
+  }
   try {
     // Try modern Clipboard API first
     await navigator.clipboard.writeText(text);
     return true;
   } catch {
     // Fallback to legacy execCommand method
     try {
       const textarea = document.createElement("textarea");
       textarea.value = text;
       textarea.style.position = "fixed";
       textarea.style.opacity = "0";
       document.body.appendChild(textarea);
       textarea.select();
-
-      const success = document.execCommand("copy");
-      document.body.removeChild(textarea);
-
-      return success;
+      try {
+        return document.execCommand("copy");
+      } finally {
+        document.body.removeChild(textarea);
+      }
     } catch {
       return false;
     }
   }
 }
src/registry/examples/ripple-button/ripple-button-demo.tsx-22-31 (1)

22-31: ⚠️ Potential issue | 🟡 Minor

Forward className and remaining props to RippleButton.

className and ...props are accepted but never applied, so styling/ARIA/disabled props are silently dropped. Consider forwarding and merging.

Proposed fix
 export default function RippleButtonExample({
   textProps,
   buttonProps,
   className,
   ...props
 }: RippleButtonEx1Props) {
+  const mergedClassName = [className, buttonProps?.className]
+    .filter(Boolean)
+    .join(" ");
   return (
-    <RippleButton {...buttonProps}>
+    <RippleButton {...props} {...buttonProps} className={mergedClassName}>
       <RippleButtonText {...textProps} text="Hover me!" />
     </RippleButton>
   );
 };
src/app/globals.css-197-199 (1)

197-199: ⚠️ Potential issue | 🟡 Minor

Undefined CSS variable --color-codeWrapper.

The rule pre > code span references var(--color-codeWrapper), but this variable is not defined in :root or the @theme inline blocks. This will result in no background color being applied.

🐛 Define the missing variable

Add to the :root block:

   --scrollbar: light-dark(oklch(0.87 0 0), oklch(0.7 0 0));
+  --codeWrapper: transparent; /* or your desired color */

And expose it in @theme inline:

   --color-scrollbar: var(--scrollbar);
+  --color-codeWrapper: var(--codeWrapper);
scripts/sync-releases.ts-64-74 (1)

64-74: ⚠️ Potential issue | 🟡 Minor

Raw release.body injection may produce malformed MDX.

The release body is inserted directly without sanitization. If any release body contains MDX frontmatter delimiters (---) or invalid JSX, the generated file may fail to parse. Additionally, the <Divider/> component is used but never imported in the generated MDX.

🛡️ Suggested improvements
   const content = releases
     .map((release) => {
+      // Escape potential frontmatter delimiters in body
+      const safeBody = release.body?.replace(/^---$/gm, '\\---') ?? '';
       return `
 
-${release.body}
+${safeBody}
 
 [View on GitHub](${release.html_url})
-<Divider/>
 `;
     })
-    .join("\n---\n\n");
+    .join("\n\n---\n\n");

Also consider adding an import statement for <Divider/> in the frontmatter section if you keep using it, or replace it with a standard Markdown horizontal rule (---).

src/app/globals.css-143-145 (1)

143-145: ⚠️ Potential issue | 🟡 Minor

Missing float keyframes definition.

Line 144 references --animate-float: float 4s ease-in-out infinite but the float keyframes are not defined. Only fade-in-up keyframes are present (lines 146-155).

🐛 Add the missing keyframes
   `@keyframes` fade-in-up {
     from {
       opacity: 0;
       transform: translateY(20px);
     }
     to {
       opacity: 1;
       transform: translateY(0);
     }
   }
+
+  `@keyframes` float {
+    0%, 100% {
+      transform: translateY(0);
+    }
+    50% {
+      transform: translateY(-10px);
+    }
+  }
 }
src/content/docs/installation.mdx-26-35 (1)

26-35: ⚠️ Potential issue | 🟡 Minor

CLI command format inconsistency.

The documentation shows npx shadcn@latest add @sona-ui/magnetic-button``, but component-installation.tsx (line 42) uses `npx sona-ui@latest add ${component}`. Please align these command formats across the documentation and code to avoid user confusion.

src/content/docs/installation.mdx-43-46 (1)

43-46: ⚠️ Potential issue | 🟡 Minor

Filename casing inconsistency in manual instructions.

Step 3 suggests saving as components/ui/Accordion.tsx (PascalCase), but the PR normalizes filenames to kebab-case (e.g., accordion.tsx). Consider updating to match the actual file naming convention used in the registry.

Proposed fix
-3.  Copy the code into a file in your project (e.g., `components/ui/Accordion.tsx`).
+3.  Copy the code into a file in your project (e.g., `components/ui/accordion.tsx`).
src/styles/theme-data.css-73-75 (1)

73-75: ⚠️ Potential issue | 🟡 Minor

Missing float keyframes definition.

--animate-float references float 4s ease-in-out infinite but only the fade-in-up keyframes are defined (lines 76-85). The float keyframes need to be added for this animation to work.

Proposed fix - add float keyframes
   `@keyframes` fade-in-up {
     from {
       opacity: 0;
       transform: translateY(20px);
     }
     to {
       opacity: 1;
       transform: translateY(0);
     }
   }
+
+  `@keyframes` float {
+    0%, 100% {
+      transform: translateY(0);
+    }
+    50% {
+      transform: translateY(-10px);
+    }
+  }
 }
src/content/docs/theming.mdx-53-180 (1)

53-180: ⚠️ Potential issue | 🟡 Minor

Update CSS filename references to match the actual repo file.

The documentation references global.css, but the actual file is globals.css. Update both the text and the code block filename attribute:

Changes required
-You can customize the color palette by overriding the Tailwind CSS configuration in your `global.css` file.
+You can customize the color palette by overriding the Tailwind CSS configuration in your `globals.css` file.
-`} language="" filename="global.css" />
+`} language="" filename="globals.css" />
src/components/common/theme-toggle.tsx-10-55 (1)

10-55: ⚠️ Potential issue | 🟡 Minor

Use resolvedTheme to show correct active state when theme is set to "system".

Currently, when theme is "system", neither button appears active because the checks only look for explicit "dark" or "light" values. Using resolvedTheme ensures the button matching the actual OS-resolved theme displays as active.

Note: This changes the default behavior when the theme is undefined—it will no longer default to showing dark mode as active.

Suggested adjustment
-  const { setTheme, theme } = useTheme();
+  const { setTheme, resolvedTheme } = useTheme();
...
-            data-active={theme === "dark" || theme === undefined}
+            data-active={resolvedTheme === "dark"}
...
-                  theme: theme === "dark" ? "opacity-100" : "opacity-75",
+                  theme: resolvedTheme === "dark" ? "opacity-100" : "opacity-75",
...
-            data-active={theme === "light" || theme === undefined}
+            data-active={resolvedTheme === "light"}
src/components/common/component-preview-server.tsx-1-27 (1)

1-27: ⚠️ Potential issue | 🟡 Minor

Fix RSC serialization pattern: pass component function instead of JSX element.

The props signature is correct (component: React.ReactNode; code: string), but passing <example.component /> (a JSX element) from the server component to the client component violates React Server Component best practices. Instead, pass the component function itself and instantiate it inside the client component. Change <ComponentPreview component={<example.component />} code={example.code} /> to <ComponentPreview component={example.component} code={example.code} /> and update ComponentPreview to handle React.ComponentType rather than React.ReactNode.

src/registry/examples/link-preview/link-preview-demo.tsx-6-6 (1)

6-6: ⚠️ Potential issue | 🟡 Minor

Fix heading grammar.
“Projects that I works on!!!” → “Projects that I work on”.

✍️ Suggested fix
-      <h3>Projects that I works on!!!</h3>
+      <h3>Projects that I work on</h3>
src/components/code-block/lib/transformers/utils.ts-11-16 (1)

11-16: ⚠️ Potential issue | 🟡 Minor

Make array input normalization consistent with string input in parseLineRange.

Array input is returned as-is while string input is deduplicated and sorted, creating behavioral inconsistency. The callers (highlight-lines.ts, focus.ts) both defensively apply new Set() afterwards, suggesting this normalization should happen within the function.

♻️ Suggested fix
-  if (Array.isArray(input)) return input;
+  if (Array.isArray(input)) {
+    return Array.from(new Set(input)).sort((a, b) => a - b);
+  }
public/r/expandable-tabs.json-9-9 (1)

9-9: ⚠️ Potential issue | 🟡 Minor

Keyboard activation missing for interactive tab elements.

The embedded component renders tabs with role="button" and tabIndex={0}, making them focusable and announcing them as buttons to assistive technology. However, there's no onKeyDown handler to activate them via Enter or Space keys, which is expected for elements with role="button".

Suggested keyboard handler addition
onKeyDown={(e) => {
  if (e.key === 'Enter' || e.key === ' ') {
    e.preventDefault();
    setIsActive(index);
  }
}}
public/r/ripple-button.json-9-9 (1)

9-9: ⚠️ Potential issue | 🟡 Minor

Contradictory accessibility attributes on RippleButtonText.

In the embedded code, RippleButtonText renders a <span> with both role="presentation" and aria-label="ripple-button-text". The role="presentation" removes the element from the accessibility tree, making the aria-label ineffective. Choose one approach:

  • Remove role="presentation" if the label should be announced
  • Remove aria-label if the element is truly presentational
scripts/build-registry-json.ts-139-142 (1)

139-142: ⚠️ Potential issue | 🟡 Minor

Log message inconsistent with actual output path.

The log message states the public index is written to public/r/index.json, but line 135 writes to public/r/registry.json.

Suggested fix
   console.log(`Registry generated:
   - Root: registry.json
-  - Public Index: public/r/index.json
+  - Public Index: public/r/registry.json
   - Individual Items: public/r/*.json`);
🧹 Nitpick comments (22)
src/components/common/component-wrapper.tsx (1)

18-18: UI styling improvements look good.

The addition of min-h-[350px] ensures consistent height for component previews, and rounded-xl provides a more pronounced border radius.

Minor nitpick: There's a double space between bg-secondary and min-h-[350px] in the class string. Not a functional issue, but could be cleaned up for consistency.

🧹 Optional: Remove extra space
-        "xs:w-full bg-secondary  min-h-[350px] flex w-[calc(100vw-16px)] items-center justify-center overflow-clip rounded-xl border p-4 shadow-lg sm:w-full md:p-6",
+        "xs:w-full bg-secondary min-h-[350px] flex w-[calc(100vw-16px)] items-center justify-center overflow-clip rounded-xl border p-4 shadow-lg sm:w-full md:p-6",
src/components/common/Fade.tsx (1)

1-71: Consider renaming file to kebab-case for consistency.

The PR is normalizing filenames to kebab-case (e.g., animated-link.tsx, component-wrapper.tsx), but this file remains Fade.tsx (PascalCase). Consider renaming to fade.tsx for consistency across the codebase.

src/components/button/index.tsx (1)

8-13: Unused href prop in interface.

The href prop is defined in ButtonProps but never used in the component implementation. If this is intended for link-like button behavior, consider implementing it (e.g., rendering an anchor when href is provided) or remove it to avoid confusion.

♻️ Option: Remove unused prop or implement link behavior

If not needed:

 interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
   children: ReactNode;
   className?: string;
   variant?: "default" | "outline" | "filled";
-  href?: string;
 }

Or implement as link button:

+import Link from "next/link";
+
 export default function Button({
   children,
   className,
   variant = "default",
+  href,
   ...props
 }: ButtonProps) {
   // ...
+  if (href) {
+    return (
+      <Link href={href} className={cn(baseClasses, variantClasses[variant], className)}>
+        {children}
+      </Link>
+    );
+  }
   return (
     <BaseButton
scripts/generate-usage-code.ts (1)

18-21: Normalize CRLF and trim trailing empty line to avoid noisy entries.

Proposed tweak
-  const content = fs.readFileSync(absolutePath, "utf-8");
-  const lines = content.split("\n");
+  const content = fs.readFileSync(absolutePath, "utf-8");
+  const normalized = content.replace(/\r\n/g, "\n");
+  const lines = normalized.split("\n");
+  if (lines.length > 0 && lines[lines.length - 1] === "") {
+    lines.pop();
+  }
FEATURE_REQUEST_SHADCN_REGISTRY.md (1)

28-42: Consider expanding the page structure outline.

The outline section (lines 28-42) appears to be rough notes. Consider either removing it from the feature request or expanding it with more detailed descriptions for each section to make it actionable.

src/lib/file-utils.ts (1)

14-16: Consider throwing instead of returning an error string.

Returning // Error reading file: ${filePath} silently masks failures—callers may unknowingly render this as valid code content. If this is intentional (graceful degradation in code blocks), consider logging at a higher severity or adding a distinct return type to differentiate success from failure.

♻️ Alternative: use a Result-style return or re-throw
-  } catch (error) {
-    console.error(`Error reading file at ${filePath}:`, error);
-    return `// Error reading file: ${filePath}`;
-  }
+  } catch (error) {
+    console.error(`Error reading file at ${filePath}:`, error);
+    throw new Error(`Failed to read file: ${filePath}`);
+  }

Or, if graceful degradation is intended, return a structured result:

type ReadResult = { success: true; content: string } | { success: false; error: string };
src/components/hero/index-old.tsx (1)

1-89: Consider removing or deprecating this old file.

The filename index-old.tsx suggests this is a legacy version. If it's no longer used, consider removing it to avoid maintenance overhead. If kept for reference, add a comment explaining its purpose.

package.json (1)

11-15: Confirm Bun is required and documented for dev/build.

predev and prebuild now call bun scripts. Please ensure CI and contributors have Bun available (or add an engines/packageManager hint + docs).

scripts/sync-releases.ts (1)

39-41: Unused formatDate function.

The formatDate function is defined but never called anywhere in the script. Consider removing it or using it in the generateMarkdown function (e.g., to display published_at for each release).

♻️ Either remove the function or use it

If you want to display dates:

       return `
 
+## ${release.name || release.tag_name} - ${formatDate(release.published_at)}
+
 ${release.body}
 
 [View on GitHub](${release.html_url})

Or remove the unused function:

-function formatDate(dateString: string) {
-  return new Date(dateString).toISOString().split("T")[0];
-}
-
src/components/copy-button/copy-button.tsx (1)

21-26: Silent failure on copy operation.

The copy function silently ignores failures. Users won't know if the copy failed. Consider adding optional error feedback.

♻️ Optional: Add failure state handling
+export function useCopyToClipboard(timeout: number = 2000) {
+  const [copied, setCopied] = useState(false);
+  const [error, setError] = useState(false);

   // Clean up timeout on unmount or when copied changes
   useEffect(() => {
     if (copied) {
       const timer = setTimeout(() => setCopied(false), timeout);
       return () => clearTimeout(timer);
     }
   }, [copied, timeout]);

   const copy = async (text: string) => {
     const success = await copyToClipboard(text);
     if (success) {
       setCopied(true);
+      setError(false);
+    } else {
+      setError(true);
+      setTimeout(() => setError(false), timeout);
     }
   };

-  return { copied, copy };
+  return { copied, copy, error };
 }
src/registry/sonaui/accordion/accordion.tsx (1)

27-41: Typo in variable name and redundant variant definitions.

  1. accordionWrapperVarinats has a typo - should be accordionWrapperVariants
  2. All three variants have identical classes ("overflow-clip rounded-2xl"), making the variant system ineffective for the wrapper
♻️ Fix typo and differentiate variants
-const accordionWrapperVarinats = cva(
+const accordionWrapperVariants = cva(
   "flex flex-col overflow-clip rounded-2xl",
   {
     variants: {
       variant: {
-        default: "overflow-clip rounded-2xl",
-        outlined: "overflow-clip rounded-2xl",
-        splitted: "overflow-clip rounded-2xl",
+        default: "",
+        outlined: "border border-border",
+        splitted: "overflow-visible rounded-none",
       },
     },
     defaultVariants: {
       variant: "default",
     },
   },
 );

Also update line 114:

-          accordionWrapperVarinats({ variant }),
+          accordionWrapperVariants({ variant }),
src/registry/examples/accordion/accordion-splitted.tsx (1)

1-65: LGTM!

Clean example demonstrating the splitted accordion variant. Structure aligns with existing accordion examples.

Minor nit: Line 60 has an empty className="" on the <p> element which can be removed.

src/registry/examples/accordion/accordion-outlined.tsx (1)

10-45: Consider extracting shared accordion data.

The accordionData array is duplicated across accordion-demo.tsx, accordion-outlined.tsx, accordion-multistep.tsx, and accordion-splitted.tsx. Consider extracting this to a shared constant to reduce duplication and ease maintenance.

src/registry/examples/accordion/accordion-multistep.tsx (1)

9-9: Consider renaming for clarity.

The name AccordionMultiStepExample might be misleading as "multi-step" typically refers to wizard/stepper patterns. Since this example demonstrates allowMultiple={true} behavior, a name like AccordionMultiOpenExample or AccordionMultipleExample would better convey the intent.

src/components/common/component-installation.tsx (3)

4-4: Remove unused import.

startTransition is imported but never used in this component.

Proposed fix
-import { startTransition, useState, ViewTransition } from "react";
+import { useState, ViewTransition } from "react";

26-29: registryDependencies is defined but unused.

The registryDependencies field in the metadata interface is never referenced in the component logic. Either remove it or implement the intended functionality (perhaps to show peer registry component dependencies).


125-151: Consider extracting IIFE to a named component.

The immediately-invoked function expression (IIFE) for rendering multiple files could be cleaner as a separate component or by lifting the activeFile calculation outside the JSX.

Proposed refactor
-                  ) : (
-                    // Multiple files: use tabs
-                    (() => {
-                      const activeFile =
-                        componentFiles.find((f) => f.path === activeFileTab) ||
-                        componentFiles[0];
-
-                      return (
-                        <CodeBlock
-                          code={activeFile.content}
-                          language="tsx"
-                        >
+                  ) : (() => {
+                    const activeFile =
+                      componentFiles.find((f) => f.path === activeFileTab) ||
+                      componentFiles[0];
+                    return (
+                      <CodeBlock code={activeFile.content} language="tsx">

Or extract activeFile before the return statement for cleaner JSX.

public/r/stagger-text.json (1)

9-9: Use grapheme‑safe splitting for user text.
text.split("") breaks surrogate pairs and combining marks (e.g., emoji), producing visual glitches. Consider Array.from(text) (basic) or Intl.Segmenter (full grapheme support).

♻️ Suggested change
-        {text.split("").map((char, i) => {
+        {Array.from(text).map((char, i) => {
scripts/build-registry-json.ts (1)

23-23: Consider using async fs operations for consistency.

The function is declared async but uses synchronous fs operations (existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync). While functional for a build script, using fs/promises would be more consistent with the async declaration and avoid blocking the event loop.

scripts/update-registry-json.ts (2)

7-7: Unused constant EXAMPLE_DIR.

EXAMPLE_DIR is defined but never referenced in the script.

Suggested fix
-const EXAMPLE_DIR = path.join(process.cwd(), "src/registry/example");

164-171: Missing registryDependencies field in registry entries.

The registry entries pushed here don't include a registryDependencies field, but scripts/build-registry-json.ts expects it (line 93). This could cause inconsistencies between the two scripts' outputs.

Suggested fix
     registry.push({
       name: component, // kebab-case directory name
       type: "registry:ui",
       title: title,
       description: description,
       files: files,
       dependencies: dependencies,
+      registryDependencies: [], // Add for consistency with build-registry-json.ts
     });
public/r/magnetic-button.json (1)

9-9: Document-level event listener always active.

The embedded component adds a mousemove listener to document regardless of hover state. For pages with many Magnetic components, this could impact performance. Consider adding the listener only when isMouseHovered is true, or using passive event listeners.

This is a minor optimization consideration and may not be necessary depending on typical usage patterns.

Comment thread src/app/globals.css
Comment on lines +76 to +80
@theme inline {
/* Fonts */
--font-sans: var(--font-sans);
--font-mono: var(--font-mono);
--font-rubik: var(--font-rubik);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, let's look at the globals.css file to see the full context
echo "=== Content of src/app/globals.css (lines 70-85) ==="
if [ -f "src/app/globals.css" ]; then
    sed -n '70,85p' src/app/globals.css
else
    echo "File not found, searching for globals.css..."
    find . -name "globals.css" -type f 2>/dev/null
fi

Repository: Dinil-Thilakarathne/sona-ui

Length of output: 678


🏁 Script executed:

#!/bin/bash
# Search for font variable definitions in layout files and imports
echo "=== Layout files that might define fonts ==="
find . -path ./node_modules -prune -o -name "layout.*" -type f -print | head -10

echo -e "\n=== Searching for --font variable definitions ==="
rg -n '\-\-font-' --type=css --type=ts --type=tsx -g '!node_modules' | head -30

Repository: Dinil-Thilakarathne/sona-ui

Length of output: 258


🏁 Script executed:

#!/bin/bash
# Search for Next.js font setup
echo "=== Searching for font imports and setup ==="
rg -n 'from.*font|import.*font|fontFamily|localFont|Inter|Rubik' --type=ts --type=tsx -g '!node_modules' -C1 | head -50

Repository: Dinil-Thilakarathne/sona-ui

Length of output: 160


🏁 Script executed:

#!/bin/bash
# Search for font variable definitions without type flags
echo "=== Searching for --font- definitions across project ==="
rg '\-\-font-' -g '!node_modules' -C2 | head -40

echo -e "\n=== Checking root CSS for font definitions ==="
find . -path ./node_modules -prune -o -name "*.css" -type f -print | xargs grep -l "font" 2>/dev/null

Repository: Dinil-Thilakarathne/sona-ui

Length of output: 1739


🏁 Script executed:

#!/bin/bash
# Read layout.tsx to see how fonts are set up
echo "=== Content of src/app/layout.tsx ==="
head -100 src/app/layout.tsx

echo -e "\n=== Searching for font imports and setup in layout ==="
cat src/app/layout.tsx | grep -i "font" -C3

Repository: Dinil-Thilakarathne/sona-ui

Length of output: 1950


🏁 Script executed:

#!/bin/bash
# Search more broadly for font setup
echo "=== All mentions of font setup ==="
rg -i 'localFont|font.*loader|@import.*font' -g '!node_modules' -C2

Repository: Dinil-Thilakarathne/sona-ui

Length of output: 362


Fix font variable name mismatch: CSS references undefined variables.

Lines 78-80 define circular references (--font-sans: var(--font-sans), etc.) that can never resolve. The actual fonts injected by Next.js in src/app/layout.tsx use different variable names: --font-geist-sans, --font-geist-mono, and --font-clash-display. The CSS is attempting to reference --font-sans, --font-mono, and --font-rubik, which are never defined anywhere in the project. These should either:

  • Match the injected variable names (--font-geist-sans, --font-geist-mono, --font-clash-display), or
  • Have actual font stack definitions instead of self-references
🧰 Tools
🪛 Biome (2.3.13)

[error] 76-156: Tailwind-specific syntax is disabled.

Enable tailwindDirectives in the css parser options, or remove this if you are not using Tailwind CSS.

(parse)

🤖 Prompt for AI Agents
In `@src/app/globals.css` around lines 76 - 80, The CSS defines circular font
variables (--font-sans, --font-mono, --font-rubik) that reference themselves;
update the assignments inside the `@theme` block to reference the actual
Next.js-injected variables (--font-geist-sans, --font-geist-mono,
--font-clash-display) used in src/app/layout.tsx (or replace them with real font
stacks), e.g. set --font-sans: var(--font-geist-sans), --font-mono:
var(--font-geist-mono), and --font-rubik: var(--font-clash-display) so the
variables resolve correctly.

</CodeBlockPre>
</CodeBlock>
</TabsContent>
<ViewTransition>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

cat -n src/components/common/component-installation.tsx | head -100

Repository: Dinil-Thilakarathne/sona-ui

Length of output: 3613


🏁 Script executed:

rg "ViewTransition" src/components/common/component-installation.tsx -B 5 -A 5

Repository: Dinil-Thilakarathne/sona-ui

Length of output: 777


🌐 Web query:

ViewTransition React 19.2 stability

💡 Result:

In React 19.2 (stable channel), <ViewTransition /> is not stable/GA—it’s only available in React’s Canary and Experimental channels, not the regular react@19.2 / react-dom@19.2 packages. (react.dev)

React’s own React Labs update (Apr 23, 2025) describes View Transitions as “new experimental features” that are “ready for testing in react@experimental” and notes they’ve been tested in production and are stable, but the final API may still change. It also explicitly says <ViewTransition /> and addTransitionType are available in react@canary (while a different feature, <Activity />, shipped in react@19.2). (uk.react.dev)

Related API docs also label unstable_addTransitionType as an experimental feature not available in a stable version of React yet, recommending react@experimental. (es.react.dev)

Citations:


ViewTransition is not available in stable React 19.2—use only react@canary or react@experimental.

ViewTransition is imported and used here, but it's an experimental feature only available in the canary and experimental channels, not in the stable react@19.2 package. Additionally, ViewTransition wraps only the manual tab content (line 78) while the CLI tab content (line 70) is unwrapped, creating inconsistent view transition behavior.

Either ensure the project uses react@canary or react@experimental, or remove ViewTransition if stable React 19.2 compatibility is required. If keeping ViewTransition, apply it consistently to both tab contents for uniform animation behavior.

🤖 Prompt for AI Agents
In `@src/components/common/component-installation.tsx` at line 78, The code uses
the experimental ViewTransition component (ViewTransition) which is not
available in stable react@19.2 and is applied only to the manual tab content
(ManualInstallTab) while the CLI tab (CliInstallTab) remains unwrapped, causing
both compatibility and inconsistent animation behavior; either remove the
ViewTransition import and wrapper entirely from the component
(ComponentInstallation) to remain compatible with stable React, or switch the
project to react@canary/react@experimental and wrap both tab contents (the
ManualInstallTab and CliInstallTab render branches) with ViewTransition so the
behavior is consistent across tabs.

Comment thread src/registry/registry.json Outdated
Comment thread src/styles/theme-data.css
Comment on lines +6 to +9
/* Fonts */
--font-sans: var(--font-sans);
--font-mono: var(--font-mono);
--font-rubik: var(--font-rubik);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Check where base font variables are defined
rg -n "font-sans:|font-mono:|font-rubik:" --type=css src/

Repository: Dinil-Thilakarathne/sona-ui

Length of output: 425


🏁 Script executed:

# Also check the actual theme-data.css file to see the context
cat -n src/styles/theme-data.css

Repository: Dinil-Thilakarathne/sona-ui

Length of output: 6954


🏁 Script executed:

# Check if globals.css or other CSS files exist and contain font definitions
fd "\.(css|scss)$" src/styles/ | head -20

Repository: Dinil-Thilakarathne/sona-ui

Length of output: 96


🏁 Script executed:

cat -n src/app/globals.css | head -100

Repository: Dinil-Thilakarathne/sona-ui

Length of output: 5013


Remove or define the circular font variable references.

The font variables in lines 7-9 reference themselves (--font-sans: var(--font-sans), etc.), creating unresolvable circular references. The same pattern appears identically in src/app/globals.css at lines 78-80. No base definitions for these variables exist in the codebase—neither in globals.css nor elsewhere. This will result in fonts not being applied. Either define actual font values (e.g., --font-sans: "Arial", sans-serif;) or remove these unused variable declarations.

🤖 Prompt for AI Agents
In `@src/styles/theme-data.css` around lines 6 - 9, The CSS custom properties
--font-sans, --font-mono, and --font-rubik currently alias themselves (e.g.,
--font-sans: var(--font-sans)) causing circular, unresolved values; replace each
of those declarations with real font-family fallbacks (for example set
--font-sans to a stack like "Inter, system-ui, -apple-system, sans-serif") or
remove the unused variables entirely, and apply the same change to any duplicate
declarations elsewhere in the codebase to ensure fonts actually resolve.

…ton to the Hero section with updated text animations, and remove the "Documentation" navigation link.
… enhanced descriptions, and externalized code.
@Dinil-Thilakarathne
Dinil-Thilakarathne merged commit 6a031ac into master Feb 4, 2026
3 of 4 checks passed
@github-actions

github-actions Bot commented Feb 4, 2026

Copy link
Copy Markdown

🎉 This PR is included in version 2.10.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

This branch was successfully deployed

1 active deployment
Preview — 65c92c2b Deployed Feb 4, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant