Responsive LettersInteractive text where each letter responds to cursor proximity, animating variable font properties (weight, stretch, italic, letter-spacing, skew) to create a deformation-under-pressure effect.
TypographyHero sectionEditorialType specimen
Switch to light
Refresh
Full screen
WHAT?!
Loading source…
Add to your project
One command adds this component to your project.
1
Run the following command. New project? Run npx shadcn@latest init first to set up Tailwind and path aliases.
With AI Canvas MCP, your AI knows every component we ship. Ask for one inside Claude Code, Codex, or Cursor and it installs the component you pick. Works with any AI Canvas account, free or premium.
Responsive Letters is interactive variable-font typography where each letter responds to cursor proximity by morphing its weight, width, italic angle, letter-spacing, and skew — together, a deformation-under-pressure effect that makes the headline feel like an elastic material. Motion drives the per-letter property animation, and the variable-font axes do the rendering, so the cost stays in CSS rather than canvas. It is built for hero headlines, type specimens, and editorial covers where the typeface itself is the protagonist.
Built with
MotionTailwind CSS
Frequently asked questions
Is Responsive Letters free to use?
Yes. Responsive Letters is part of the free AI Canvas library and is open source under the MIT license, so you can use it in personal and commercial projects.
How do I install Responsive Letters?
One command: npx shadcn@latest add https://aicanvas.me/r/responsive-letters.json, free with a free AI Canvas account. Or connect the AI Canvas MCP server and ask your AI editor to install Responsive Letters for you. You can also copy the source straight from the Code tab, no account needed. It works in any React project with Tailwind CSS.
What is Responsive Letters built with?
Responsive Letters is built with React and TypeScript, using Motion and Tailwind CSS. It ships with both light and dark styling.
Where would I use Responsive Letters?
Common uses include Hero section, Editorial, and Type specimen. Like every AI Canvas component, it is self-contained and drops into any React project.
Can I remix Responsive Letters with AI?
Yes. Responsive Letters ships with one comprehensive AI prompt written against the real source code. Open "Remix with AI" on this page to read and copy it into Claude, Cursor, ChatGPT, or any AI tool. Prompts are for remixing your own variation; for the exact component, install it with the one-command CLI.
One comprehensive prompt, written against the real source code. Works in Claude, Cursor, ChatGPT, or any AI tool you use.
This prompt is for remixing. Use it to build your own variation of Responsive Letters. Results depend on the model you use, and no prompt in the world is 100% exact.
Want the exact component?
One command installs it, pixel-perfect. Copy, paste into your project, done.
Create an interactive text component where each letter responds to cursor proximity with smooth variable font animations.
## Requirements
### Setup
- Use Next.js 16 with TypeScript
- Verify Tailwind CSS v4 is installed (configured via @theme inline in globals.css)
- Install: framer-motion, next/font
- The component should be a client-side React component ('use client')
### Typography & Text
- Display the text: "WHAT ?!"
- Use Science Gothic font from next/font/google with subsets: ['latin']
- Font size: clamp(5rem, 12vw, 9rem) to be responsive across all screen sizes
- Line height: 1
- Base letter spacing: 0.15em
- Word spacing: 0.8em
- Each letter is an inline-block element with select-none
### Theme Support (Dark & Light)
- Dark mode (default):
- Background: #0d001a (dark navy)
- Text color: #40FFA7 (neon cyan)
- Light mode:
- Background: #40FFA7 (neon cyan)
- Text color: #0d001a (dark navy)
- Import useTheme from ../../app/components/ThemeProvider to detect theme
- Use theme to set bgColor and textColor (as Tailwind className for text: text-[#40FFA7] or text-[#0d001a])
### Animation System
Each letter animates 5 properties based on cursor distance:
#### Influence Radius
- 300px from cursor center affects letters
- Distance-based influence: linear falloff from 1 to 0 across the radius
- Apply smoothstep easing: influence = influence² × (3 - 2 × influence)
#### Font Weight (CSS custom property --font-weight)
- Min: 100, Max: 900
- At cursor position (influence = 1): weight = 900
- Far from cursor (influence = 0): weight = 100
- Interpolate: targetWeight = 100 + (900 - 100) × influence
#### Font Stretch (CSS custom property --font-stretch)
- Min: 100%, Max: 200%
- At cursor: 200% (expanded)
- Far: 100% (normal)
- Interpolate: targetStretch = 100 + (200 - 100) × influence
#### Letter Spacing (CSS custom property --letter-spacing)
- Min: 0em, Max: 0.4em
- At cursor: 0.4em (expanded)
- Far: 0em (touching)
- Interpolate: targetLetterSpacing = 0 + (0.4 - 0) × influence
#### Italic (CSS custom property --italic as number 0-1)
- At cursor (influence = 1): italic = 0 (normal font-style)
- Far (influence = 0): italic = 1 (italic font-style)
- Inverse of influence: italicValue = 1 - influence
- In LetterSpan component: watch --italic CSS var and toggle fontStyle between 'italic' and 'normal' when value crosses 0.5
#### Skew Y (CSS custom property --skew in degrees)
- Min: 0°, Max: ±18°
- Direction depends on cursor angle relative to letter
- Calculate angle from cursor to letter: angle = atan2(dy, dx)
- Map to skew direction: skewDirection = sin(angle)
- Interpolate: targetSkew = 18 × influence × skewDirection
- Apply as transform: skewY(var(--skew, 0)deg)
### Animation & Physics
#### Hover State (cursor active)
- Spring: damping = 10, stiffness = 160
- Easing factor: 0.15 (smooth follow)
- Letters respond smoothly and quickly to cursor movement
#### Exit State (cursor leaves)
- Spring: damping = 6, stiffness = 35 (slower, more elastic)
- Easing factor: 0.05 (stretchy recovery)
- When cursor leaves (mouseRef.current = null), letters decay back to default state slowly
#### Animation Loop
- Use requestAnimationFrame to update all letters at 60fps
- For each letter:
1. Get its bounding rect (getBoundingClientRect)
2. Calculate distance to cursor center
3. Calculate influence using smoothstep falloff
4. Calculate target values for all 5 properties
5. Ease current state toward target using exponential interpolation
6. Update CSS custom properties on the element via setProperty()
- Maintain exit state object per letter to preserve animation during easing
### Component Structure
Create a LetterSpan component that:
1. Renders as a <span> with inline-block, select-none
2. Accepts props: letter (string), textColor (className), fontFamily (string from scienceGothic.style.fontFamily), forwardedRef
3. Uses useRef and useEffect to:
- Monitor the --italic CSS variable via getComputedStyle()
- Toggle fontStyle between 'italic' and 'normal' based on --italic > 0.5
- Expose span via forwardedRef so parent can collect all letter refs
4. Styles: fontSize, fontWeight, fontStretch, fontFamily, fontStyle, lineHeight, letterSpacing, wordSpacing, transform via CSS vars
Wrap with motion(LetterSpanComponent) to enable Framer Motion.
Main ResponsiveLetters component:
1. Manages containerRef, lettersRef array, mouseRef, animIdRef, aliveRef
2. Tracks cursor position via onMouseMove (set mouseRef.current = { x, y })
3. Clears cursor on onMouseLeave (set mouseRef.current = null)
4. Add onTouchStart handler for mobile (optional, touch doesn't affect animation but shows component is touch-aware)
5. Runs animation loop in useEffect that:
- Initializes exit state per letter (weight, stretch, letterSpacing, skew, italic all at default)
- Calls animate() via requestAnimationFrame
- Cleans up on unmount
### Responsive Design
- Container: flex, min-h-screen, w-full, items-center, justify-center
- Letter gaps: gap-0.5 (sm:gap-1) for mobile responsiveness
- Component must fill viewport and work on all screen sizes from 320px to 1200px
- No hardcoded widths/heights — use clamp() and Tailwind fluid utilities
### Code Structure
- Single file component exported as default function ResponsiveLetters
- All constants defined at module level (TEXT, INFLUENCE_RADIUS, MAX_WEIGHT, MIN_WEIGHT, etc.)
- Clean up all effects: cancel animIdRef on unmount, disconnect MutationObserver
- No TypeScript errors or use of 'any'
### Mobile Responsiveness
- Component supports touch (onTouchStart handler present)
- No hover-only interactions (cursor proximity is primary, but mobile still sees full animation)
- Text scales responsively via clamp()
- Gaps adjust via sm: breakpoint (gap-0.5 to gap-1)
Create this as a fully functional, copy-paste-ready component that showcases variable font animation with cursor-driven influence.