diff --git a/components/animate/AnimationEditor.tsx b/components/animate/AnimationEditor.tsx
index 74be420..86bf710 100644
--- a/components/animate/AnimationEditor.tsx
+++ b/components/animate/AnimationEditor.tsx
@@ -98,7 +98,7 @@ export function AnimationEditor() {
{/* Row 4: Preset Library */}
-
+
);
}
diff --git a/components/animate/PresetLibrary.tsx b/components/animate/PresetLibrary.tsx
index 046ff0f..bcba38d 100644
--- a/components/animate/PresetLibrary.tsx
+++ b/components/animate/PresetLibrary.tsx
@@ -3,39 +3,29 @@
import { useEffect, useRef } from 'react';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
-import { cn } from '@/lib/utils/cn';
import { PRESETS, PRESET_CATEGORIES } from '@/lib/animate/presets';
-import { buildCSS } from '@/lib/animate/cssBuilder';
+import { buildKeyframesOnly } from '@/lib/animate/cssBuilder';
import type { AnimationConfig, AnimationPreset } from '@/types/animate';
interface Props {
- currentName: string;
onSelect: (config: AnimationConfig) => void;
}
-function PresetCard({ preset, isActive, onSelect }: {
+function PresetCard({ preset, onSelect }: {
preset: AnimationPreset;
- isActive: boolean;
onSelect: () => void;
}) {
const styleRef = useRef(null);
- const key = `preset-${preset.id}`;
+ const animName = `preview-${preset.id}`;
- // Each card gets its own @keyframes injected with a unique name to avoid conflicts
+ // Inject only the @keyframes block under a unique name — no .animated class rule
useEffect(() => {
- const renamedConfig = {
- ...preset.config,
- name: key,
- keyframes: preset.config.keyframes,
- };
+ const renamedConfig = { ...preset.config, name: animName };
if (!styleRef.current) {
styleRef.current = document.createElement('style');
document.head.appendChild(styleRef.current);
}
- styleRef.current.textContent = buildCSS(renamedConfig).replace(
- /animation: \S+/,
- `animation: ${key}`
- );
+ styleRef.current.textContent = buildKeyframesOnly(renamedConfig);
return () => {
styleRef.current?.remove();
styleRef.current = null;
@@ -43,32 +33,36 @@ function PresetCard({ preset, isActive, onSelect }: {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
+ // Cap thumbnail duration so fast presets loop nicely; slow ones cap at 1.2s
+ const thumbDuration = Math.min(preset.config.duration, 1200);
+
return (
);
}
-export function PresetLibrary({ currentName, onSelect }: Props) {
+export function PresetLibrary({ onSelect }: Props) {
return (
@@ -90,7 +84,6 @@ export function PresetLibrary({ currentName, onSelect }: Props) {
onSelect(preset.config)}
/>
))}
diff --git a/lib/animate/cssBuilder.ts b/lib/animate/cssBuilder.ts
index cf7da5d..74d1321 100644
--- a/lib/animate/cssBuilder.ts
+++ b/lib/animate/cssBuilder.ts
@@ -61,6 +61,24 @@ export function buildAnimationShorthand(config: AnimationConfig): string {
return `${config.name} ${config.duration}ms ${config.easing}${delay} ${iter} ${config.direction} ${config.fillMode}`;
}
+export function buildKeyframesOnly(config: AnimationConfig): string {
+ const sorted = [...config.keyframes].sort((a, b) => a.offset - b.offset);
+ let out = `@keyframes ${config.name} {\n`;
+ for (const kf of sorted) {
+ const lines = buildProperties(kf.properties);
+ if (lines.length === 0) {
+ out += ` ${kf.offset}% { }\n`;
+ } else {
+ out += ` ${kf.offset}% {\n`;
+ for (const line of lines) out += ` ${line};\n`;
+ if (kf.easing) out += ` animation-timing-function: ${kf.easing};\n`;
+ out += ` }\n`;
+ }
+ }
+ out += `}\n`;
+ return out;
+}
+
export function buildCSS(config: AnimationConfig): string {
const sorted = [...config.keyframes].sort((a, b) => a.offset - b.offset);