Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Execute Framer secondary workflow: Core Workflow B. Use when implementing secondary use case, or complementing primary workflow. Trigger with phrases like "framer secondary workflow", "secondary task with framer".
.claude/skills/jeremylongshore-framer-core-workflow-b/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-03 | ✗→✓ | ▲ Improved | 21% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 19% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 19% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 42% | 0% |
| case-12 | ✗→✓ | ▲ Improved | 10% | 0% |
Build code components with property controls and code overrides for Framer sites. Components are custom React rendered on the canvas. Overrides modify existing layer behavior (animations, interactions) without changing the component.
tsximport { addPropertyControls, ControlType } from 'framer'; import { useRef, useEffect, useState } from 'react'; interface Props { target: number; duration: number; suffix: string; fontSize: number; color: string; } export default function AnimatedCounter({ target = 1000, duration = 2, suffix = '+', fontSize = 48, color = '#000' }: Props) { const ref = useRef(null); const [count, setCount] = useState(0); const [started, setStarted] = useState(false); useEffect(() => { const observer = new IntersectionObserver(([e]) => { if (e.isIntersecting) setStarted(true); }, { threshold: 0.5 }); if (ref.current) observer.observe(ref.current); return () => observer.disconnect(); }, []); useEffect(() => { if (!started) return; const inc = target / (duration * 60); let val = 0; const timer = setInterval(() => { val += inc; if (val >= target) { setCount(target); clearInterval(timer); } else setCount(Math.floor(val)); }, 1000 / 60); return () => clearInterval(timer); }, [started, target, duration]); return <div ref={ref} style={{ fontSize, fontWeight: 700, color, textAlign: 'center' }}>{count.toLocaleString()}{suffix}</div>; } addPropertyControls(AnimatedCounter, { target: { type: ControlType.Number, title: 'Target', defaultValue: 1000 }, duration: { type: ControlType.Number, title: 'Duration (s)', defaultValue: 2 }, suffix: { type: ControlType.String, title: 'Suffix', defaultValue: '+' }, fontSize: { type: ControlType.Number, title: 'Font Size', defaultValue: 48 }, color: { type: ControlType.Color, title: 'Color', defaultValue: '#000' }, });
tsximport { addPropertyControls, ControlType } from 'framer'; import { useEffect, useState } from 'react'; export default function DataList({ apiUrl = 'https://jsonplaceholder.typicode.com/posts', limit = 5 }) { const [items, setItems] = useState<any[]>([]); useEffect(() => { fetch(apiUrl).then(r => r.json()).then(d => setItems(d.slice(0, limit))).catch(() => {}); }, [apiUrl, limit]); return ( <ul style={{ listStyle: 'none', padding: 0 }}> {items.map((item, i) => <li key={i} style={{ padding: '8px 0', borderBottom: '1px solid #eee' }}>{item.title || item.name}</li>)} </ul> ); } addPropertyControls(DataList, { apiUrl: { type: ControlType.String, title: 'API URL', defaultValue: 'https://jsonplaceholder.typicode.com/posts' }, limit: { type: ControlType.Number, title: 'Limit', defaultValue: 5 }, });
tsx// overrides.tsx — apply to any layer via properties panel import { Override } from 'framer'; export function FadeInOnScroll(): Override { return { initial: { opacity: 0, y: 20 }, whileInView: { opacity: 1, y: 0 }, transition: { duration: 0.6 }, viewport: { once: true } }; } export function MagneticHover(): Override { return { whileHover: { scale: 1.05 }, whileTap: { scale: 0.95 }, transition: { type: 'spring', stiffness: 400, damping: 17 } }; } export function TypewriterText(): Override { return { initial: { width: 0 }, animate: { width: '100%' }, transition: { duration: 2, ease: 'linear' }, style: { overflow: 'hidden', whiteSpace: 'nowrap' }, }; }
tsximport { framer } from 'framer-plugin'; async function createOverrideFile() { const codeFile = await framer.createCodeFile({ name: 'Animations', language: 'tsx' }); await codeFile.setFileContent(` import { Override } from 'framer'; export function FadeIn(): Override { return { initial: { opacity: 0 }, animate: { opacity: 1 }, transition: { duration: 0.8 } }; }`.trim()); framer.notify('Override file created'); }
| Error | Cause | Solution | |-------|-------|----------| | Component blank | Runtime error | Check Framer browser console | | Controls not showing | Missing addPropertyControls | Call after component export | | Override not applying | Wrong export | Must return Override type | | Fetch fails | CORS | Use CORS-enabled API |
For common errors, see framer-common-errors.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 17,415 | 14,870 | -15% | 1 | 1 | 0% | 2,745 | 3,689 | +34% | 0 | 0 | — |
case-02 | fail→fail | 19,928 | 14,547 | -27% | 1 | 1 | 0% | 3,106 | 4,846 | +56% | 0 | 0 | — |
case-03 | fail→pass | 16,141 | 17,336 | +7% | 1 | 1 | 0% | 3,245 | 3,934 | +21% | 0 | 0 | — |
case-04 | pass→pass | 15,128 | 10,656 | -30% | 1 | 1 | 0% | 2,085 | 2,536 | +22% | 0 | 0 | — |
case-05 | fail→fail | 9,842 | 15,142 | +54% | 1 | 1 | 0% | 1,806 | 3,082 | +71% | 0 | 0 | — |
case-06 | pass→pass | 15,141 | 5,156 | -66% | 1 | 1 | 0% | 1,929 | 2,421 | +26% | 0 | 0 | — |
case-07 | fail→pass | 17,285 | 11,040 | -36% | 1 | 1 | 0% | 2,168 | 2,580 | +19% | 0 | 0 | — |
case-08 | fail→fail | 16,185 | 17,031 | +5% | 1 | 1 | 0% | 2,887 | 3,778 | +31% | 0 | 0 | — |
case-09 | fail→pass | 17,528 | 11,776 | -33% | 1 | 1 | 0% | 2,179 | 2,598 | +19% | 0 | 0 | — |
case-10 | fail→fail | 12,618 | 14,563 | +15% | 1 | 1 | 0% | 2,409 | 4,172 | +73% | 0 | 0 | — |
case-11 | fail→pass | 15,387 | 7,099 | -54% | 1 | 1 | 0% | 1,911 | 2,707 | +42% | 0 | 0 | — |
case-12 | fail→pass | 10,693 | 4,179 | -61% | 1 | 1 | 0% | 1,955 | 2,145 | +10% | 0 | 0 | — |
case-13 | pass→pass | 13,372 | 4,261 | -68% | 1 | 1 | 0% | 1,586 | 2,176 | +37% | 0 | 0 | — |
case-14 | fail→fail | 17,404 | 7,643 | -56% | 1 | 1 | 0% | 2,330 | 2,886 | +24% | 0 | 0 | — |
case-15 | fail→fail | 14,001 | 5,801 | -59% | 1 | 1 | 0% | 2,602 | 2,555 | -2% | 0 | 0 | — |
case-16 | pass→pass | 9,237 | 8,010 | -13% | 1 | 1 | 0% | 1,602 | 1,951 | +22% | 0 | 0 | — |
case-17 | fail→pass | 8,804 | 11,908 | +35% | 1 | 1 | 0% | 1,733 | 2,728 | +57% | 0 | 0 | — |
case-18 | pass→pass | 15,342 | 4,735 | -69% | 1 | 1 | 0% | 1,921 | 2,330 | +21% | 0 | 0 | — |
case-19 | pass→pass | 15,646 | 10,227 | -35% | 1 | 1 | 0% | 2,113 | 3,703 | +75% | 0 | 0 | — |
case-20 | pass→pass | 8,442 | 9,498 | +13% | 1 | 1 | 0% | 1,621 | 2,217 | +37% | 0 | 0 | — |
case-21 | fail→pass | 7,368 | 4,314 | -41% | 1 | 1 | 0% | 1,642 | 2,469 | +50% | 0 | 0 | — |
case-22 | pass→pass | 11,257 | 14,112 | +25% | 1 | 1 | 0% | 2,197 | 3,215 | +46% | 0 | 0 | — |
case-23 | pass→pass | 10,859 | 10,395 | -4% | 1 | 1 | 0% | 2,101 | 3,333 | +59% | 0 | 0 | — |
case-24 | pass→pass | 14,483 | 12,218 | -16% | 1 | 1 | 0% | 1,829 | 2,893 | +58% | 0 | 0 | — |
case-25 | pass→pass | 7,780 | 9,466 | +22% | 1 | 1 | 0% | 1,659 | 3,499 | +111% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 25 cases were attempted. The headline lift of +28 percentage points is the difference between those two pass rates over the 25 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.