Thank you for your interest in contributing to skillsAI! This guide will help you get set up and understand our development workflow.
- Getting Started
- Development Setup
- Project Structure
- Coding Standards
- Git Workflow
- Testing
- Accessibility
- Pull Request Process
- Reporting Issues
| Tool | Version | Install |
|---|---|---|
| Node.js | 25.3.0+ | nvm recommended |
| pnpm | 10+ | npm install -g pnpm |
| Git | 2.30+ | git-scm.com |
- Fork the repository on GitHub
- Clone your fork:
git clone https://github.com/<your-username>/skills-ai.git cd skills-ai
- Add the upstream remote:
git remote add upstream https://github.com/iblai/skills-ai.git
pnpm installcp .env.example .env.localEdit .env.local with valid IBL.ai platform URLs. See the README for all available variables.
pnpm devThe app starts at http://localhost:3000.
pnpm test # Single run├── app/ # Next.js App Router — pages and routes
├── components/ # React components
│ ├── ui/ # Base UI primitives (shadcn/ui)
│ └── ... # Feature components
├── features/ # Feature modules (state + business logic)
├── hooks/ # Custom React hooks
├── services/ # API service definitions (RTK Query)
├── types/ # TypeScript interfaces and type definitions
├── config/ # Runtime configuration and theme
├── lib/ # Utility functions
├── providers/ # React context providers
├── styles/ # Global CSS
├── public/ # Static assets
└── utils/ # Helper functions and localStorage utilities
- Pages go in
app/following Next.js App Router conventions - Components are colocated with their feature or placed in
components/ - Business logic lives in
features/with Redux slices and related hooks - API services are defined in
services/using RTK Query - Reusable hooks go in
hooks/, organized by domain (courses/,profile/,skills/) - UI primitives (buttons, dialogs, inputs) go in
components/ui/ - Tests are colocated next to the code they test in
__tests__/directories
- All code is written in TypeScript with strict mode enabled
- Use explicit types for function parameters and return values at module boundaries
- Prefer
interfacefor object shapes andtypefor unions/intersections - Avoid
any— useunknownand narrow the type when the shape is uncertain
- Use functional components with hooks
- Prefer named exports over default exports
- Keep components focused on a single responsibility
- Extract complex logic into custom hooks
- Use
'use client'directive only when the component genuinely needs client-side APIs
- Use Tailwind CSS utility classes for styling
- Use the
cn()utility (fromlib/utils) to merge class names conditionally - Follow the existing shadcn/ui patterns for new UI primitives
- Keep responsive design in mind — the app supports mobile, tablet, and desktop
- Redux Toolkit with RTK Query for server state
- Use RTK Query hooks (
useGetXQuery,useLazyGetXQuery,useXMutation) for API calls - Local UI state stays in React state (
useState,useReducer) - Shared UI state goes through Redux slices in
features/
- Components:
PascalCase.tsxorkebab-case.tsx(follow existing patterns in each directory) - Hooks:
use-kebab-case.ts(e.g.,use-course-detail.ts) - Utilities:
kebab-case.ts(e.g.,helpers.ts) - Tests:
*.test.tsor*.test.tsxin a__tests__/directory - Types:
kebab-case.ts(e.g.,courses.ts,skills.ts)
- Use the
@/path alias for absolute imports from the project root - Group imports: external packages first, then internal modules, then relative imports
- Use the
@iblai/iblai-jsSDK for all data layer, auth, and shared component imports:import { useLazyGetOverTimeActivityQuery, StorageService } from '@iblai/iblai-js/data-layer'; import { AuthProvider, TenantProvider, useTenantMetadata } from '@iblai/iblai-js/web-utils'; import { Loader, EducationDialog, ExperienceDialog } from '@iblai/iblai-js/web-containers'; import { SsoLogin, UserProfileModal } from '@iblai/iblai-js/web-containers/next';
Use the pattern: <type>/<scope>/<description>
Types:
feat— new featurefix— bug fixchore— maintenance, dependencies, configdocs— documentation changesrefactor— code restructuring without behavior changetest— adding or updating tests
Scope (optional but encouraged):
courses,discover,profile,skills,credentials,analytics,auth,ui,billing, etc.
Examples:
feat/discover/add-difficulty-filter
fix/courses/handle-enrollment-error
chore/deps/upgrade-next-to-15.4
refactor/profile/extract-skill-chart-hook
We use Conventional Commits:
type(scope): description
[optional body]
Examples:
feat(skills): add skill leaderboard component
fix(discover): correct pagination on faceted search
chore(deps): upgrade @iblai/iblai-js to 1.0.10
docs: update deployment guide for Docker
refactor(hooks): extract course metadata logic into dedicated hook
test(profile): add unit tests for education box
Rules:
- Use imperative mood: "add feature" not "added feature"
- Keep the first line under 72 characters
- Reference issue numbers in the body when applicable:
Closes #123
We use Vitest with Testing Library for unit and component tests.
pnpm test # Run all tests once
pnpm test -- path/to/file.test.ts # Run a single test file- Place tests in a
__tests__/directory next to the source file - Name test files
<source-file>.test.tsor<source-file>.test.tsx - Test behavior, not implementation details
- Use
@testing-library/reactfor component tests — query by role, label, or text, not by CSS class or test ID - Mock external dependencies (API calls, hooks) at the module level
Example:
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { describe, it, expect, vi } from 'vitest';
import { SkillCard } from '../skill-card';
describe('SkillCard', () => {
it('renders the skill name and level', () => {
render(<SkillCard name="Python" level={3} points={450} />);
expect(screen.getByText('Python')).toBeInTheDocument();
expect(screen.getByText('Level 3')).toBeInTheDocument();
});
it('calls onViewCourses when link is clicked', async () => {
const onViewCourses = vi.fn();
render(<SkillCard name="Python" level={3} points={450} onViewCourses={onViewCourses} />);
await userEvent.click(screen.getByRole('link', { name: /view courses/i }));
expect(onViewCourses).toHaveBeenCalled();
});
});skillsAI targets WCAG 2.1 AA compliance. All contributions should meet these standards:
- All interactive elements must be keyboard-accessible
- Use semantic HTML elements (
button,nav,main,section, etc.) - Provide
aria-labeloraria-labelledbyfor elements without visible text labels - Ensure color contrast ratios meet AA standards (4.5:1 for normal text, 3:1 for large text)
- Support screen readers — test with VoiceOver (macOS) or NVDA (Windows)
- Use Radix UI primitives for complex widgets (dialogs, dropdowns, tabs) — they handle ARIA automatically
- Never use
outline: nonewithout providing an alternative focus indicator
-
Sync with upstream:
git fetch upstream git rebase upstream/main
-
Run the full check suite:
pnpm lint pnpm typecheck pnpm test -
Verify your changes work end-to-end — start the dev server and manually test the affected feature
- Push your branch to your fork
- Open a pull request against
mainon the upstream repository - Fill in the PR template:
- Summary — what does this PR do and why?
- Test plan — how did you verify the changes?
- Screenshots — include before/after screenshots for UI changes
PRs will be reviewed for:
- Correctness — does it work as described?
- Code quality — does it follow our coding standards?
- Tests — are new features and bug fixes covered by tests?
- Accessibility — does it meet WCAG 2.1 AA requirements?
- Performance — does it introduce unnecessary re-renders, large bundles, or slow queries?
- Security — are there any injection vectors, exposed secrets, or insecure patterns?
- Address review feedback with new commits (don't force-push during review)
- Once approved, a maintainer will merge your PR
- Your contribution will be included in the next release
When filing a bug report, include:
- Description — what happened vs. what you expected
- Steps to reproduce — minimal steps to trigger the issue
- Environment — browser, OS, Node.js version
- Screenshots or logs — console errors, network failures, visual glitches
We welcome feature ideas! When proposing a feature:
- Describe the problem — what user need does this address?
- Propose a solution — how should it work from the user's perspective?
- Consider alternatives — are there existing workarounds?
We are committed to providing a welcoming and inclusive experience for everyone. Please be respectful, constructive, and collaborative in all interactions.
If you have questions about contributing, open a Discussion or reach out to the maintainers.
Thank you for helping make skillsAI better!