一键导入
spline-3d-embed
Embed Spline 3D scenes safely: lazy-load, perf-gated, mobile-aware, a11y-conscious. Never ships without LCP measurement.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Embed Spline 3D scenes safely: lazy-load, perf-gated, mobile-aware, a11y-conscious. Never ships without LCP measurement.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Review existing Architecture Decision Records. Flag ADRs whose aging signals may have triggered given current state. Categorizes each as valid / aged / superseded and suggests follow-up actions.
Author an Architecture Decision Record (ADR). Captures decisions with implications beyond the current change — lasts months, affects multiple files, hard to reverse. Records context, alternatives considered, consequences, and aging signals so the decision can be revisited when conditions change.
4-phase loop-detection and recovery protocol. Fires when an agent detects it is stuck or repeating the same tool call with no progress — before tool-loop-detection circuit-breaks at 30 calls.
Socratic exploration of requirements before implementation. Refines spec through targeted questions. Use when user asks to 'explore options', 'refine requirements', or mentions 'unclear requirements'.
Generate structured changelog from git history. Groups commits by type, filters noise, produces Keep a Changelog format. Single source of truth — no /commands wrapper.
Generate pipe-delimited commits: Tipo|IdTarea|YYYYMMDD|Descripción. Single source of truth — no /commands wrapper.
| name | spline-3d-embed |
| description | Embed Spline 3D scenes safely: lazy-load, perf-gated, mobile-aware, a11y-conscious. Never ships without LCP measurement. |
| version | 1.0.0 |
| when | {"keywords":["spline","3d","three","hero animation","interactive 3d","splinetool"]} |
| allowed-tools | ["Bash(npm install *)","Bash(pnpm add *)","Read","Write","Edit","WebFetch"] |
When the user wants to embed a Spline scene, you DO NOT just drop <Spline url="..."/>. You measure LCP first, lazy-load by default, gate on Save-Data and prefers-reduced-motion, and add a11y. A 5MB Spline scene on a hero kills mobile Core Web Vitals — refuse to ship without measurement.
If user wants Spline in SSR, push back: scenes must run client-only.
https://prod.spline.design/.../scene.splinecode).Content-Length. If > 3MB → mandatory lazy load + intersection observer. If > 8MB → push back, ask if they can simplify the scene.npm install @splinetool/react-spline @splinetool/runtimenpm install @splinetool/runtimeprefers-reduced-motion guard, saveData guard, a11y wrapper.dynamic(() => ..., { ssr: false }) in Next.js.prefers-reduced-motion: reduce — show static fallback instead.navigator.connection.saveData === true — skip the scene.aria-label to the wrapper and tabindex={-1} (3D scenes are not keyboard-tabbable).<Spline url={...} /> rendered eagerly above the fold on mobile → tanks LCP.window access.prefers-reduced-motion → accessibility violation, user discomfort."use client";
import dynamic from "next/dynamic";
import { Suspense, useEffect, useRef, useState } from "react";
const Spline = dynamic(() => import("@splinetool/react-spline"), {
ssr: false,
loading: () => <SceneFallback />,
});
const SCENE_URL = "https://prod.spline.design/your-id/scene.splinecode";
const ASPECT = "aspect-[16/9]"; // match scene to avoid CLS
export function SplineScene({ alt }: { alt: string }): JSX.Element {
const [render, setRender] = useState(false);
const [skip, setSkip] = useState<"motion" | "data" | null>(null);
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
if (typeof window === "undefined") return;
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
setSkip("motion"); return;
}
const conn = (navigator as Navigator & { connection?: { saveData?: boolean } }).connection;
if (conn?.saveData === true) { setSkip("data"); return; }
if (!ref.current) return;
const obs = new IntersectionObserver((entries) => {
if (entries.some((e) => e.isIntersecting)) { setRender(true); obs.disconnect(); }
}, { rootMargin: "200px" });
obs.observe(ref.current);
return () => obs.disconnect();
}, []);
if (skip !== null) return <SceneFallback />;
return (
<div ref={ref} role="img" aria-label={alt} tabIndex={-1}
className={`relative w-full ${ASPECT}`}>
{render ? (
<Suspense fallback={<SceneFallback />}>
<Spline scene={SCENE_URL} />
</Suspense>
) : <SceneFallback />}
</div>
);
}
function SceneFallback(): JSX.Element {
return <div aria-hidden className={`w-full ${ASPECT} bg-gradient-to-br
from-slate-100 to-slate-200 animate-pulse rounded-lg`} />;
}
Before shipping, the user must confirm:
prefers-reduced-motion: reduce enabled in DevTools, the static fallback renders instead of the scene.If the user pushes back ("just embed it"), respond:
A Spline scene without these gates breaks mobile users. The minimum is lazy load + reduced-motion fallback. I will not skip those.
<div id="spline-canvas" role="img" aria-label="Hero scene" tabindex="-1"
style="aspect-ratio: 16/9; width: 100%;"></div>
<script type="module">
if (!window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
const { Application } = await import("@splinetool/runtime");
const canvas = document.createElement("canvas");
document.getElementById("spline-canvas").appendChild(canvas);
const app = new Application(canvas);
await app.load("https://prod.spline.design/your-id/scene.splinecode");
}
</script>
Before returning generated code, confirm:
dynamic(..., { ssr: false }) used (Next.js)prefers-reduced-motion guardednavigator.connection.saveData guardedrole="img" + aria-label + tabIndex={-1} on wrapper