Proposal builder
Persona-based AI skill ecosystem — 7 personas, 14 orchestration skills for outbound campaigns, publishing, security, macOS automation, and full-stack dev. Works with Claude Code, Claude Chat, and Agent SDK. AGPL v3 + commercial license.
npx -y skills add 0xjitsu/jitsu-skills --skill proposal-builderAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Scaffolds production-ready R&D proposal and research pitch deck apps using Next.js App Router, Tailwind CSS, and Geist fonts. Generates the full project structure including dark-themed glass morphism UI, citation system, scroll-spy navigation, animated counters, and Lighthouse-ready metadata. Triggered when the user asks to build a research proposal, R&D app, pitch deck, or single-page research site.
The file declares its own license as AGPL-3.0-or-later. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
12.0 KB, as published. Nobody here has run it
Proposal Builder Skill
R&D proposal app scaffolder with dark theme, citations, and Lighthouse compliance.
When to Trigger
Activate this skill when the user:
- Asks to build a research proposal or R&D pitch
- Wants to create a single-page research site or pitch deck app
- Requests a scaffold for a data-driven narrative page
- Needs a presentation-quality web app for a grant, investor deck, or technical report
Stack
| Layer | Technology |
|---|---|
| Framework | Next.js App Router (latest stable) |
| Styling | Tailwind CSS v4+ |
| Fonts | Geist Sans + Geist Mono |
| Deployment | Vercel |
| Package manager | Bun (preferred) or npm |
Project Structure
src/
app/
page.tsx # Main page — metadata, JSON-LD, section composition
layout.tsx # Root layout — metadataBase, viewport, themeColor, fonts
globals.css # Design tokens, glass utilities, scrollbar, reduced-motion
robots.ts # Allow all, link to sitemap
sitemap.ts # Root URL with changeFrequency: "daily"
error.tsx # Error boundary with themed retry UI
components/
sections/ # One component per content section
Hero.tsx # Title, subtitle, key stats, scroll CTA
Problem.tsx # Problem statement with data points
Solution.tsx # Proposed solution with architecture
Methodology.tsx # Research methodology / approach
Timeline.tsx # Project timeline / milestones
Team.tsx # Team credentials
Budget.tsx # Budget breakdown (table or chart)
References.tsx # Full reference list with DOI links
ui/
Cite.tsx # Citation component (superscript + inline)
ScrollProgress.tsx # Fixed top gradient progress bar
ScrollSpy.tsx # Sidebar nav with active section highlight
AnimatedCounter.tsx # Number counter with requestAnimationFrame
SkipNav.tsx # Skip navigation link (accessibility)
SectionWrapper.tsx # IntersectionObserver fade-in wrapper
data/
data.js # ALL text, numbers, citations — single source of truth
Architecture Rules
Content Separation
ALL text, numbers, and citations live in src/data/data.js. Section components are pure render logic.
// src/data/data.js
export const hero = {
title: "Project Title",
subtitle: "A one-line elevator pitch",
stats: [
{ value: 2.4, suffix: "B", label: "Market size (USD)" },
{ value: 79, suffix: "%", label: "Efficiency improvement" },
],
};
export const references = [
{
id: 1,
authors: "Smith, J., & Doe, A.",
year: 2024,
title: "Research paper title",
journal: "Nature Biotechnology",
doi: "10.1038/s41587-024-00000-0",
},
// ...
];
Critical: Never use \uXXXX escape sequences in data files. Use actual Unicode characters: –, —, ², °, ±, >=, etc.
Single-Page Scroll Architecture
The page is a single vertical scroll with scroll-spy navigation:
// src/app/page.tsx
import Hero from '@/components/sections/Hero';
import Problem from '@/components/sections/Problem';
// ... static imports for ALL sections (LCP requirement)
export default function Page() {
return (
<main>
<Hero />
<Problem />
<Solution />
<Methodology />
<Timeline />
<Team />
<Budget />
<References />
</main>
);
}
LCP rule: Every section is a static import. Never use dynamic() for above-the-fold content.
Design System
Dark Theme Foundation
/* globals.css */
:root {
color-scheme: dark;
/* Opacity scale */
--white-05: rgba(255,255,255,0.05);
--white-10: rgba(255,255,255,0.10);
--white-20: rgba(255,255,255,0.20);
--white-40: rgba(255,255,255,0.40);
--white-60: rgba(255,255,255,0.60);
--white-80: rgba(255,255,255,0.80);
--white-90: rgba(255,255,255,0.90);
}
html {
background: #09090B; /* zinc-950 */
color: #F4F4F5; /* zinc-100 */
}
Glass Morphism Utilities
.glass {
background: var(--white-05);
backdrop-filter: blur(16px);
border: 1px solid var(--white-10);
border-radius: 1rem;
}
.glass-hover {
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.glass-hover:hover {
transform: translateY(-2px);
box-shadow: 0 8px 32px rgba(0,0,0,0.3);
}
.glass-static {
/* Full-width containers that should NOT lift on hover */
transform: none !important;
box-shadow: none !important;
}
@media (hover: none) {
.glass-hover:hover {
transform: none;
box-shadow: none;
}
}
Scrollbar Styling
::-webkit-scrollbar { width: 8px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--white-20); border-radius: 4px; }
/* Firefox */
html {
scrollbar-width: thin;
scrollbar-color: rgba(255,255,255,0.2) transparent;
}
Reduced Motion
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
Citation System
Cite Component
// src/components/ui/Cite.tsx
interface CiteProps {
n: number;
inline?: boolean;
}
export function Cite({ n, inline }: CiteProps) {
const ref = references.find(r => r.id === n);
if (!ref) return null;
if (inline) {
return (
<a href={`#ref-${n}`} className="text-blue-400 hover:text-blue-300 transition-colors">
{ref.authors.split(',')[0]} et al. ({ref.year})
</a>
);
}
return (
<sup>
<a href={`#ref-${n}`} className="text-blue-400 hover:text-blue-300 text-xs ml-0.5">
[{n}]
</a>
</sup>
);
}
Reference List with Target Highlight
/* Highlight scrolled-to reference */
[id^="ref-"]:target {
background: rgba(59, 130, 246, 0.1);
border-left: 3px solid #3B82F6;
padding-left: 0.75rem;
transition: background 0.3s ease;
}
Reference Rules
- Every citation must have a corresponding entry in
references[] - Every reference must include a DOI or institutional URL — no dead-end citations
- Format: Author(s), Year, Title, Journal/Source, DOI link
- References section renders at the bottom with
id="ref-{n}"anchors
Scroll Animations
IntersectionObserver Wrapper
// src/components/ui/SectionWrapper.tsx
'use client';
import { useEffect, useRef, useState } from 'react';
export function SectionWrapper({ children, id }: { children: React.ReactNode; id: string }) {
const ref = useRef<HTMLElement>(null);
const [isVisible, setIsVisible] = useState(false);
useEffect(() => {
// Check if already in viewport on mount (SSR safety)
const el = ref.current;
if (!el) return;
const rect = el.getBoundingClientRect();
if (rect.top < window.innerHeight && rect.bottom > 0) {
setIsVisible(true);
return;
}
const observer = new IntersectionObserver(
([entry]) => { if (entry.isIntersecting) setIsVisible(true); },
{ threshold: 0.1 }
);
observer.observe(el);
return () => observer.disconnect();
}, []);
return (
<section
ref={ref}
id={id}
className={`scroll-mt-20 transition-all duration-700 ${
isVisible ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-4'
}`}
>
{children}
</section>
);
}
Critical: Always check viewport on mount. Never default to opacity: 0 without an immediate viewport check — this breaks SSR and causes content to be invisible if IntersectionObserver fires late.
Animated Number Counter
// src/components/ui/AnimatedCounter.tsx
'use client';
import { useEffect, useRef, useState } from 'react';
export function AnimatedCounter({ target, duration = 2000 }: { target: number; duration?: number }) {
const [count, setCount] = useState(0);
const ref = useRef<HTMLSpanElement>(null);
useEffect(() => {
const el = ref.current;
if (!el) return;
const observer = new IntersectionObserver(([entry]) => {
if (!entry.isIntersecting) return;
observer.disconnect();
const start = performance.now();
const step = (now: number) => {
const progress = Math.min((now - start) / duration, 1);
// Ease-out cubic
const eased = 1 - Math.pow(1 - progress, 3);
setCount(Math.floor(eased * target));
if (progress < 1) requestAnimationFrame(step);
};
requestAnimationFrame(step);
}, { threshold: 0.5 });
observer.observe(el);
return () => observer.disconnect();
}, [target, duration]);
return <span ref={ref}>{count.toLocaleString()}</span>;
}
Scroll Progress Bar
// src/components/ui/ScrollProgress.tsx
'use client';
import { useEffect, useState } from 'react';
export function ScrollProgress() {
const [progress, setProgress] = useState(0);
useEffect(() => {
const onScroll = () => {
const { scrollTop, scrollHeight, clientHeight } = document.documentElement;
setProgress(scrollTop / (scrollHeight - clientHeight));
};
window.addEventListener('scroll', onScroll, { passive: true });
return () => window.removeEventListener('scroll', onScroll);
}, []);
return (
<div
className="fixed top-0 left-0 h-1 z-50 bg-gradient-to-r from-blue-500 via-purple-500 to-emerald-500"
style={{ width: `${progress * 100}%` }}
/>
);
}
Lighthouse Compliance Checklist
SEO (Target: 100)
-
metadataBaseset inlayout.tsx(resolves canonical URLs + OG image paths) - Separate
export const viewport: Viewport(not inside metadata object) -
themeColormatching primary background (#09090B) - Meta description: 150-160 characters
-
robots.ts— allow all, link to sitemap -
sitemap.ts— root URL withchangeFrequency: "daily" - JSON-LD structured data (
WebPageorReporttype) inpage.tsx - Preconnect/dns-prefetch for external domains (fonts, images, APIs)
Accessibility (Target: 90+)
- Skip navigation link as first focusable element
- Global
focus-visiblering:outline: 2px solid #38BDF8; outline-offset: 2px -
prefers-reduced-motiondisables ALL animations -
color-scheme: darkon<html> - WCAG AA color contrast (4.5:1 minimum)
-
aria-hidden="true"on decorative SVGs/icons -
aria-labelon<nav>, chart wrappers - Touch targets:
min-h-[44px]on buttons, nav links -
::selectionstyling for dark theme -
scope="col"on all<th>elements
Performance (Target: 90+)
- LCP section (Hero): static import, never
dynamic() - Loading skeletons on all
dynamic()imports withanimate-pulseand fixedmin-h -
optimizePackageImportsinnext.config.tsfor heavy libraries - Error boundary (
error.tsx) with themed retry UI - All design token colors defined upfront — no undefined Tailwind tokens
Scaffold Command
When triggered, generate the full project:
- Initialize with
bunx create-next-app@latest --app --tailwind --ts --src-dir - Install Geist fonts:
bun add geist - Create the directory structure above
- Generate
data.jswith placeholder content matching the user's topic - Generate all section components with glass morphism styling
- Generate all UI components (Cite, ScrollProgress, ScrollSpy, AnimatedCounter, SkipNav)
- Set up
globals.csswith full design system - Configure
layout.tsxwith metadata, viewport, fonts - Configure
page.tsxwith JSON-LD and section composition - Add
robots.tsandsitemap.ts - Verify build passes:
bun run build