Skip to content

Conversation

@P-Sanjana
Copy link

@P-Sanjana P-Sanjana commented Jan 17, 2026

Summary

This PR introduces a reusable CircularText React component that renders text laid out along a circular path and smoothly rotates it using Framer Motion. The component is designed for interactive UI elements such as hero sections, loaders, badges, or call-to-action highlights.


Key Features

  • 🔄 Infinite smooth rotation using Framer Motion and CSS
  • 🧮 Even character distribution around a circular path
  • ⚙️ Configurable spin duration via spinDuration prop and circle radius via radius prop
  • 🎨 Customizable styling through className
  • ⚡ Optimized rendering with useMemo

Implementation Details

  • Uses CSS for smooth, continuous rotation
  • Characters are positioned using CSS transforms for accurate circular layout
  • Animation restarts seamlessly when props change (text, duration or radius)
  • Linear, GPU-friendly animation for consistent performance

Props

interface CircularTextProps {
  text: string;          // Text rendered around the circle
  spinDuration?: number; // Time (in seconds) for one full rotation
  radius?: number, // Radius (in px) for positioning characters around the circle
  className?: string;    // Optional style overrides
}

Example Usage

<CircularText
  text="SCROLL • EXPLORE • DISCOVER •"
  spinDuration={20}
  radius={8}
  className="text-sm uppercase"
/>

Demo

Screen.Recording.2026-01-23.113217.mp4

Checklist

  • Component is reusable and configurable
  • Animation is smooth and performs well
  • No breaking changes introduced
  • Follows existing codebase conventions
  • Props are clearly defined and documented
  • Tested with different text lengths
  • Works across modern browsers

Summary by CodeRabbit

  • New Features

    • Circular Text component: displays text arranged around a circle with continuous rotation; supports configurable spin speed, radius, and styling.
  • Storybook

    • Added a Storybook demo to preview and interact with the Circular Text component.
  • Documentation

    • New docs with installation guidance, usage example, and component preview.

✏️ Tip: You can customize this high-level summary in your review settings.

@P-Sanjana P-Sanjana temporarily deployed to preview-deployment January 17, 2026 04:59 — with GitHub Actions Inactive
@coderabbitai
Copy link

coderabbitai bot commented Jan 17, 2026

📝 Walkthrough

Walkthrough

Adds a new CircularText React component (rotating characters arranged on a circle), a Storybook story for it, and an MDX documentation page. Files added: animata/text/circular-text.tsx, animata/text/circular-text.stories.tsx, content/docs/text/circular-text.mdx.

Changes

Cohort / File(s) Summary
Component
animata/text/circular-text.tsx
New default-exported CircularText component. Props: text, spinDuration (default 30), radius (default 5), className. Computes per-character angles, positions chars around a circle with transforms, and uses Framer Motion motion.div to animate continuous rotation. Note: component remounts on spinDuration via dynamic key.
Storybook
animata/text/circular-text.stories.tsx
New Storybook meta (title: "Text/Circular Text", component: CircularText, layout: "centered", tags: ["autodocs"]) and Primary story with text: "CIRCULAR•TEXT•COMPONENT•".
Documentation
content/docs/text/circular-text.mdx
New MDX doc with frontmatter (title, description, author), installation note referencing framer-motion, example/creation guidance, and credits.

Sequence Diagram(s)

(omitted)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 I curled my letters into a round,
Hopped in circles without a sound,
Spins and sparkles, gentle trance,
Characters waltz in midnight dance,
Little rabbit, joyful bound ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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
Title check ✅ Passed The title 'feat: add circular text' directly and clearly describes the main change: introducing a new CircularText component that renders text along a circular path with rotation.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing touches
  • 📝 Generate docstrings

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.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In `@animata/text/circular-text.tsx`:
- Around line 39-42: The Tailwind class in the className string of the
CircularText component is misspelled as "origins-center" so the transform-origin
isn't applied; update the className value (where cn(...) is called in
circular-text.tsx) to use "origin-center" instead of "origins-center" so the
element correctly uses center transform-origin while keeping the surrounding
classes and the cn(...) composition unchanged.
- Around line 47-61: The character positioning uses a tiny numeric offset;
replace the current offset logic in the characters.map block with a proper
radius-based translation: introduce a radius constant (e.g., radius) and build
transform as rotate(angle) translateX(radius) rotate(-angle) (so each character
is rotated into place around the circle but stays upright) by updating the
transform variable used in the span returned from characters.map; also correct
the Tailwind easing class name in className from ease-[cubic-bezier(0_0_0_1)] to
ease-[cubic-bezier(0,0,0,1)] so commas are used instead of underscores.
- Around line 26-36: The rotation MotionValue created in useMotionValue is never
updated by controls.start so rotation.get() always returns 0; fix by driving the
rotation value directly instead of only animating controls: replace the
controls.start call inside the useEffect with an animation that targets the
MotionValue (e.g. using animate(rotation, rotation.get() + 360, {
...createRotation(spinDuration, rotation.get()) })) or call controls.start and
also subscribe to the element's rotate updates (via onUpdate or controls.set) to
keep rotation in sync; update references to rotation, controls.start,
createRotation, spinDuration and characters accordingly so the "continue from
current position" logic uses the actual MotionValue value.
- Around line 6-15: The createRotation helper currently places invalid
transition keys `from` and `to` inside the transition object; update
createRotation so it returns an object separating the rotation animation target
and its transition (i.e., provide the rotate target values as animation props
like initial/animate or a rotate key with numeric start and end values, and put
only timing keys — duration, ease, repeat, type — inside a nested transition
object). Locate createRotation in circular-text.tsx and refactor its returned
structure so the rotation target values are not inside transition but are
applied via the component's initial/animate or a rotate animation target while
keeping duration/ease/repeat/type in the transition field.
🧹 Nitpick comments (1)
animata/text/circular-text.stories.tsx (1)

4-21: Consider adding argTypes and additional story variants.

The story is functional but could be enhanced for better documentation and testing:

  1. Add argTypes to provide better control descriptions in Storybook
  2. Consider adding additional stories to showcase different spinDuration values
✨ Optional enhancement
 const meta = {
   title: "Text/Circular Text",
   component: CircularText,
   parameters: {
     layout: "centered",
   },
   tags: ["autodocs"],
-  argTypes: {},
+  argTypes: {
+    text: { description: "Text to display around the circle" },
+    spinDuration: { 
+      description: "Duration in seconds for one full rotation",
+      control: { type: "number" },
+    },
+    className: { description: "Additional CSS classes" },
+  },
 } satisfies Meta<typeof CircularText>;
 
 export default meta;
 type Story = StoryObj<typeof meta>;
 
 export const Primary: Story = {
   args: {
     text: "CIRCULAR•TEXT•COMPONENT•",
   },
 };
+
+export const SlowSpin: Story = {
+  args: {
+    text: "SCROLL • EXPLORE • DISCOVER •",
+    spinDuration: 60,
+  },
+};
+
+export const FastSpin: Story = {
+  args: {
+    text: "FAST • ROTATION •",
+    spinDuration: 5,
+  },
+};

@github-actions
Copy link

🚀 Preview deployed to: https://e6cdb9e1.animata.pages.dev

@hari
Copy link
Contributor

hari commented Jan 21, 2026

@P-Sanjana please review the comments

@P-Sanjana P-Sanjana temporarily deployed to preview-deployment January 23, 2026 05:18 — with GitHub Actions Inactive
@github-actions
Copy link

🚀 Preview deployed to: https://c3881158.animata.pages.dev

@P-Sanjana P-Sanjana deployed to preview-deployment January 23, 2026 16:18 — with GitHub Actions Active
@github-actions
Copy link

🚀 Preview deployed to: https://38d5526a.animata.pages.dev

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants