homedesign bitsanimating ai input

Animating AI Input Interaction

Published Nov 18, 2024

After some back and forth with our design team, I refined the AI input field to be more fluid and responsive. Rather than static transitions, I added subtle animations that make the interaction feel more natural - the input grows with content, the mic button slides, and the submit button fades in when you start typing.

We kept the implementation relatively straightforward, using React's built-in state management and refs to handle the auto-expanding textarea. The mic and submit buttons position themselves dynamically based on the input state, with smooth transitions handling their movement. Dark mode support comes built-in through Tailwind's dark variants, making it easy to maintain across themes.

"use client";
 
import { CornerRightUp, Mic } from "lucide-react";
import { useState, useRef } from "react";
import { cn } from "@/lib/cn";
import { Textarea } from "../ui/textarea";
 
export default function AnimatedAiInput() {
    const [inputValue, setInputValue] = useState("");
    const textareaRef = useRef<HTMLTextAreaElement | null>(null);
 
    return (
        <div className="w-full py-4">
            <div className="relative max-w-xl w-full mx-auto">
                <Textarea
                    id="input-01"
                    placeholder="Type your message..."
                    className="max-w-xl bg-black/5 dark:bg-white/5 w-full rounded-3xl pl-6 pr-16 placeholder:text-black/50 dark:placeholder:text-white/50 border-none ring-black/20 dark:ring-white/20 text-black dark:text-white text-wrap py-4 max-h-[200px] overflow-y-auto resize-none min-h-[40px]"
                    rows={1}
                    ref={textareaRef}
                    value={inputValue}
                    onChange={(e) => {
                        setInputValue(e.target.value);
                        if (textareaRef.current) {
                            textareaRef.current.style.height = "auto";
                            textareaRef.current.style.height = `${textareaRef.current.scrollHeight}px`;
                        }
                    }}
                />
 
                <div
                    className={cn(
                        "absolute top-1/2 -translate-y-1/2 rounded-xl bg-black/5 dark:bg-white/5 py-1 px-1 transition-all duration-200",
                        inputValue ? "right-10" : "right-3"
                    )}
                >
                    <Mic className="w-4 h-4 text-black/70 dark:text-white/70" />
                </div>
                <button
                    type="button"
                    className={cn(
                        "absolute top-1/2 -translate-y-1/2 rounded-xl bg-black/5 dark:bg-white/5 py-1 px-1 transition-all duration-700",
                        inputValue
                            ? "block right-3 animate-slide-in cursor-pointer"
                            : "hidden"
                    )}
                >
                    <CornerRightUp className="w-4 h-4 text-black/70 dark:text-white/70 transition-opacity duration-700" />
                </button>
            </div>
        </div>
    );
}

09:00:03 PM

24th of January, 2025