| name | portfolio-standards |
| description | Shared development standards for all Portfolio agents. Covers project conventions, stack rules, TypeScript strictness, code quality gates, testing standards, naming conventions, and Git workflow. Always apply this skill when working on any part of this project. |
| compatibility | opencode |
Language and environment
- Language: TypeScript (strict mode —
"strict": true in tsconfig, no any ever)
- Frontend: Next.js 15 with App Router — never Pages Router
- Package manager: pnpm — never npm or yarn
- Node version: 20 LTS (minimum)
Project structure
/ # Monorepo root
├── apps/
│ └── web/ # Next.js 15 portfolio app
│ ├── public/ # Static assets
│ └── src/
│ ├── app/ # Routes, layouts, pages
│ │ ├── globals.css # Global styles & design tokens
│ │ ├── layout.tsx # Root HTML layout (fonts)
│ │ └── [locale]/ # Bilingual pages (EN/ES)
│ ├── components/ # Reusable UI components
│ │ ├── layout/ # Header, Footer, etc.
│ │ ├── sections/ # Hero, About, Projects, Experience, Contact
│ │ └── shared/ # Common primitive sub-components
│ ├── i18n/ # next-intl configuration
│ ├── lib/ # helper constants and utilities
│ └── messages/ # Translation JSON files (en.json, es.json)
│
└── packages/
└── shared/ # Internal shared package (@portfolio/shared)
└── src/
└── index.ts # Shared types & utilities
Naming conventions
PascalCase → React components, TypeScript interfaces/types
camelCase → functions, variables, props, object keys
kebab-case → file names, folder names, CSS class names
SCREAMING_SNAKE → environment variables, global constants
use + PascalCase → custom React hooks (e.g. useProjects, useAuth)
File naming examples
✓ ProjectCard.tsx ← React component
✓ useProjects.ts ← custom hook
✗ projectcard.tsx ← wrong case
TypeScript rules
"strict": true is mandatory in every tsconfig.json — no exceptions
- Never use
any — use unknown and narrow, or define a proper type/interface
- Never use type assertions (
as SomeType) without a comment explaining why it is safe
- Prefer
interface over type for object shapes that may be extended
- Use
type for unions, intersections, and utility types
- All function parameters and return types must be explicitly typed — no implicit returns
- Shared types between workspaces live in
packages/shared and are imported from @portfolio/shared
const data = response as any;
interface ApiResponse<T> {
data: T;
message: string;
}
const data: ApiResponse<Project> = response;
Frontend rules (Next.js 15 + React)
Component structure
interface ProjectCardProps {
project: Project;
isLoading?: boolean;
}
export function ProjectCard({ project, isLoading = false }: ProjectCardProps) {
}
export default ProjectCard;
Server vs Client Components
- Server Components by default — add
'use client' ONLY when required
'use client' is required when: using useState, useEffect, useRef, event handlers, browser APIs
- Never add
'use client' to a component just because a child needs it — extract the interactive part instead
- Data fetching happens in Server Components — never
useEffect for initial data fetch
Data fetching
async function ProjectsPage() {
const projects = await getProjects();
return <ProjectList projects={projects} />;
}
'use client';
function ProjectsPage() {
const [projects, setProjects] = useState([]);
useEffect(() => { fetchProjects().then(setProjects); }, []);
}
Styling
- Tailwind CSS for all styling — no inline styles, no CSS modules unless absolutely necessary
- shadcn/ui components are in
components/ui/ — never modify them directly; wrap them instead
- Use
cn() utility (from lib/utils.ts) for conditional class merging
Accessibility
- All interactive elements must have
aria-label or visible text
- Use semantic HTML:
<nav>, <main>, <section>, <article>, <header>, <footer>
- Images must have meaningful
alt text — never empty or alt="image"
- Focus management on modals and dialogs must be handled correctly
Code quality gates
All code produced or modified must pass these checks before being considered complete:
pnpm --filter web type-check
pnpm --filter web lint
pnpm --filter web build
pnpm --filter shared type-check
No // eslint-disable comments without an inline explanation of why the rule is a false positive in that specific case.
Testing rules
Frontend (React Testing Library + Jest / Vitest)
- Test user interactions, not implementation details
- Never test internal state — test what the user sees
- Use
screen.getByRole and screen.getByLabelText over getByTestId
- Mock API calls at the network level with
msw
it('shows project title after loading', async () => {
render(<ProjectCard project={mockProject} />);
expect(await screen.findByText(mockProject.title)).toBeInTheDocument();
});
it('sets isLoading to false after fetch', () => {
});
Environment and configuration
- Never hardcode secrets — all secrets live in
.env files (gitignored)
.env.example documents every variable with a description — keep it up to date
- Frontend env vars exposed to the browser must be prefixed with
NEXT_PUBLIC_
- Never commit
.env, .env.local, or any file with real credentials
Git and versioning
- Commit messages follow Conventional Commits:
feat:, fix:, refactor:, docs:, test:, chore:
- Every feature branch targets
main
- Branch naming:
feat/feature-name, fix/bug-name, chore/task-name
- Pull requests require: passing CI, no TypeScript errors, no lint errors
Import organization
Imports must be ordered in this sequence (ESLint enforces this):
import { join } from 'path';
import { useState } from 'react';
import { motion } from 'framer-motion';
import type { Project } from '@portfolio/shared';
import { cn } from '@/lib/utils';
import { ProjectCard } from './ProjectCard';
What all agents must respect
- Planner defines feature architecture before any code is written — Developer does not deviate without flagging the change
- Developer produces code that passes all quality gates before handoff to the Reviewer
- Reviewer blocks any code with CRITICAL or HIGH issues — no exceptions
- All agents share this skill file as the single source of truth for project standards
- When this skill conflicts with an agent's own instructions, this skill takes precedence