Last Updated: 2025-12-30 Version: 0.1.0 (Kit-Agnostic Refactor) Applies To: claudekit-docs codebase (20 components, 8 content categories)
Coding standards for Astro v5 documentation site. Covers file organization, naming conventions, component patterns, content structure, and quality guidelines.
Layout Components:
Header.astro- Top navigation with logo and language switcherSidebar.astro- Left sidebar container (responsive mobile/desktop)SidebarNav.astro- Navigation tree with kit-switching and section detection
React Islands (Interactive):
AIChat.tsx- Chat interface with OpenRouter integration (UI complete)TableOfContents.tsx- Dynamic heading extraction and navigationCopyForLLMs.tsx- Export content for LLM consumptionLanguageSwitcher.tsx- EN/VI language switcher with i18nKitSwitcher.tsx- Switch between Engineer/Marketing/CLI kitsKitContext.tsx- React context for kit state management
Navigation Components (Kit-specific):
DocsNav.astro- General docs navigationGettingStartedNav.astro- Getting started section navCLINav.astro- CLI kit section navEngineerNav.astro- Engineer kit section navMarketingNav.astro- Marketing kit section navWorkflowsNav.astro- Workflows section navToolsNav.astro- Tools section navChangelogNav.astro- Changelog section navSupportNav.astro- Support section nav
UI Components:
Search.astro- Pagefind search interface (placeholder)AIPanel.astro- AI chat panel wrapper (disabled)
8 Main Categories:
getting-started/- 8+ pages (installation, quick-start, onboarding)cli/- CLI kit documentationengineer/- 138 pages (agents: 18, commands: 66, skills: 49, config: 4, index: 1)marketing/- 88 pages (agents, commands, skills, workflows, features)workflows/- 20+ cross-kit workflow guidestools/- Tools directory and referencessupport/- FAQ, troubleshooting, community resourceschangelog/- Version history and release notes
Vietnamese Coverage: 176/275 pages (64% - targeting 100%)
- Configuration:
astro.config.mjs,tailwind.config.mjs,tsconfig.json - Content Schema:
src/content/config.ts(Zod validation with 8 categories) - i18n:
src/i18n/locales.ts,ui.ts(18 keys × 2 locales),utils.ts - Deployment:
Dockerfile(multi-stage bun→node),k8s/manifests
- Build features when needed, not speculatively
- Avoid premature optimization
- Keep components simple and focused
- Prefer simple solutions over complex
- Clear code over clever code
- Minimal dependencies
- Extract reusable components
- Use CSS variables for design tokens
- Centralize i18n strings
src/
├── components/ # UI components (Astro + React)
├── content/ # Markdown content (Zod validated)
│ ├── docs/ # English
│ └── docs-vi/ # Vietnamese
├── i18n/ # Internationalization
├── layouts/ # Page layouts
├── lib/ # Utilities
├── pages/ # File-based routing
└── styles/ # Global CSS
Components:
- Astro: PascalCase (e.g.,
Header.astro,SidebarNav.astro) - React: PascalCase (e.g.,
AIChat.tsx,LanguageSwitcher.tsx) - Use descriptive names indicating purpose
Content:
- Markdown: kebab-case (e.g.,
quick-start.md,installation-issues.md) - Match URL structure
- Descriptive, lowercase, hyphen-separated
Utilities:
- TypeScript: kebab-case (e.g.,
openrouter.ts) - Config: kebab-case (e.g.,
astro.config.mjs)
Directories:
- kebab-case for all directories
- Singular for utilities (e.g.,
lib/,i18n/) - Plural for collections (e.g.,
components/,layouts/)
Hard Limits:
- Astro components: < 300 lines
- React components: < 250 lines
- TypeScript files: < 200 lines
- Markdown docs: < 500 lines (content only, code examples excluded)
Refactoring Strategy:
- Extract reusable logic to
lib/ - Split large components into sub-components
- Create shared hooks for React components
- Use Astro slots for composition
TypeScript/JavaScript:
// Variables: camelCase
const pageTitle = 'Introduction';
const isPublished = true;
// Functions: camelCase
function generateSlug(title: string) { }
const getLocalizedPath = (path, locale) => { };
// Constants: UPPER_SNAKE_CASE
const MAX_SIDEBAR_DEPTH = 3;
const DEFAULT_LOCALE = 'en';
// Types/Interfaces: PascalCase
interface DocsFrontmatter {
title: string;
category: string;
}
type LocaleCode = 'en' | 'vi';Astro Components:
---
// Props: camelCase
interface Props {
pageTitle: string;
currentLocale: string;
}
const { pageTitle, currentLocale } = Astro.props;
---Class Names:
- Use Tailwind utility classes primarily
- Custom classes: kebab-case (e.g.,
.sidebar-nav,.nav-item) - BEM-style for complex components (e.g.,
.nav-section__title)
CSS Variables:
/* Design tokens: kebab-case with semantic names */
--color-bg-primary
--color-text-secondary
--space-4
--text-lg
--radius-md---
title: "Page Title" # Human-readable
description: "SEO description" # 150-160 chars
category: "getting-started" # kebab-case enum
order: 1 # Number for sorting
published: true # Boolean
---Valid Categories (from src/content/config.ts):
Main Kit-Agnostic Categories:
getting-started- Installation, quick-start, onboardingcli- CLI-specific documentationengineer- Engineer kit docs (agents, commands, skills, config)marketing- Marketing kit docs (agents, commands, skills, workflows, features)workflows- Cross-kit workflow guidestools- Tools directory and referencessupport- FAQ, troubleshooting, community resourceschangelog- Version history and release notes
Legacy Categories (kept for backward compatibility):
core-concepts- Architecture and conceptscomponents- UI component reference (placeholder)
Structure:
---
// 1. Imports
import Layout from '../layouts/BaseLayout.astro';
import { getCollection } from 'astro:content';
// 2. Props interface
interface Props {
title: string;
}
// 3. Props extraction
const { title } = Astro.props;
// 4. Data fetching
const docs = await getCollection('docs');
// 5. Logic
const sortedDocs = docs.sort((a, b) => a.data.order - b.data.order);
---
<!-- 6. Template -->
<Layout title={title}>
<div class="container">
<!-- Content -->
</div>
</Layout>
<!-- 7. Scoped styles (if needed) -->
<style>
.container {
/* Prefer Tailwind, use scoped CSS for complex cases */
}
</style>Best Practices:
- Use Astro for static content and layout
- Frontmatter for data fetching and processing
- Minimal client-side JavaScript
- Prefer CSS variables over hardcoded values
Structure:
import { useState } from 'react';
interface AIChat Props {
initialMessages?: Message[];
}
export default function AIChat({ initialMessages = [] }: AIChatProps) {
const [messages, setMessages] = useState(initialMessages);
// Logic
return (
<div className="ai-chat">
{/* JSX */}
</div>
);
}Client Directives:
<!-- Load immediately -->
<AIChat client:load />
<!-- Load when visible -->
<AIChat client:visible />
<!-- Load when idle -->
<AIChat client:idle />
<!-- Only hydrate on interaction -->
<AIChat client:only="react" />Best Practices:
- Use islands for interactive components only
- Keep state management simple (useState, useReducer)
- Prefer
client:visibleorclient:idlefor non-critical components - TypeScript strict mode
Cross-Component Communication (Phase 1 - KitSwitcher):
- Use custom DOM events for component-to-component messaging
- Dispatch from React islands, listen in Astro/vanilla JS
- localStorage for persistent state shared across tabs
- Example:
window.dispatchEvent(new CustomEvent('kit-changed', { detail: { kit } }))
localStorage Conventions:
- Use kebab-case keys:
claudekit-selected-kit,sidebar-section-${name} - Store simple values: strings, booleans, JSON-serialized objects
- Document key format and default values in component comments
BaseLayout.astro:
- HTML document structure
- Meta tags, fonts, global scripts
- Theme initialization
- No layout-specific styles
DocsLayout.astro:
- Uses BaseLayout
- Defines page structure (sidebar, content, panel)
- Responsive breakpoints
- Grid/Flexbox layout
Nested Layouts:
---
import BaseLayout from './BaseLayout.astro';
---
<BaseLayout {...props}>
<div class="docs-wrapper">
<slot />
</div>
</BaseLayout>---
title: "Clear, Descriptive Title"
description: "SEO-optimized description (150-160 chars)"
category: "appropriate-category"
order: 10
published: true
---
# Page Title (H1 - only one per page)
Brief introduction paragraph (1-3 sentences).
## First Section (H2)
Content with [links](https://example.com) and **formatting**.
### Subsection (H3)
More detailed content.
#### Detail Level (H4)
Avoid H5 and H6.
## Code Examples
\```typescript
// Always specify language
const example: string = 'Hello';
\```
## Lists
- Unordered lists for related items
- Start with dash, space, lowercase
- Parallel structure
1. Ordered lists for sequences
2. Start with number, period, space
3. Complete sentences or fragments (consistent)
## Tables
| Column 1 | Column 2 | Column 3 |
|----------|----------|----------|
| Value A | Value B | Value C |
## Links
Internal: [Getting Started](./getting-started/introduction)
External: [Astro Docs](https://docs.astro.build)
## Notes
> **Note**: Use blockquotes for notes, warnings, tips.
## See Also
- [Related Doc 1](./related-1)
- [Related Doc 2](./related-2)Tone:
- Professional but friendly
- Active voice preferred
- Second person ("you") for instructions
- Present tense
Formatting:
- One sentence per line in source (easier diffs)
- Max 80-100 characters per line
- Two spaces after H1, one space after H2-H4
- Consistent list formatting
Code Blocks:
- Always specify language
- Include comments for clarity
- Show complete, runnable examples
- Syntax: ```language
Links:
- Descriptive anchor text (not "click here")
- Internal links relative (e.g.,
./introduction) - External links absolute with https://
- Open external links in same tab (users decide)
Images:
- Store in
public/directory - Reference with
/image-name.png - Alt text required
- Optimize for web (<200KB)
Utility-First Approach:
<div class="flex items-center gap-4 p-6 bg-[var(--color-bg-secondary)]">
<!-- Content -->
</div>Responsive Design:
<div class="w-full md:w-1/2 lg:w-1/3">
<!-- Mobile-first, breakpoints: md (768px), lg (1024px) -->
</div>Dark Mode:
/* Use CSS variables, not Tailwind dark: variant */
.element {
color: var(--color-text-primary);
background: var(--color-bg-secondary);
}Usage:
/* Defined in src/styles/global.css */
.custom-component {
/* Colors */
color: var(--color-text-primary);
background: var(--color-bg-secondary);
border-color: var(--color-border);
/* Spacing */
padding: var(--space-4);
margin-bottom: var(--space-6);
gap: var(--space-2);
/* Typography */
font-size: var(--text-base);
line-height: var(--leading-normal);
font-weight: var(--font-medium);
/* Effects */
border-radius: var(--radius-md);
transition: all var(--duration-normal) var(--ease-out);
}Never Hardcode:
/* ❌ Bad */
.element {
color: #abb2bf;
padding: 16px;
font-size: 14px;
}
/* ✅ Good */
.element {
color: var(--color-text-primary);
padding: var(--space-4);
font-size: var(--text-sm);
}Naming (src/i18n/ui.ts):
export const ui = {
en: {
'nav.getting-started': 'Getting Started', // Dot notation
'search.placeholder': 'Search docs...',
'button.submit': 'Submit',
},
vi: {
'nav.getting-started': 'Bắt Đầu',
'search.placeholder': 'Tìm kiếm...',
'button.submit': 'Gửi',
}
};Usage in Components:
---
import { useTranslations } from '../i18n/utils';
const t = useTranslations(currentLocale);
---
<button>{t('button.submit')}</button>File Structure:
src/content/
├── docs/ # English
│ └── category/
│ └── page.md
└── docs-vi/ # Vietnamese (mirror structure)
└── category/
└── page.md
Translation Workflow:
- Create English content first
- Mirror file structure in
docs-vi/ - Translate all frontmatter and content
- Ensure code examples remain consistent
- Test both locales
Strict Mode (tsconfig.json):
{
"extends": "astro/tsconfigs/strict",
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true
}
}Interface Definitions:
// Prefer interfaces for objects
interface DocsFrontmatter {
title: string;
description: string;
category?: Category;
order?: number;
published: boolean;
}
// Use type for unions, primitives
type Category =
| 'getting-started'
| 'core-concepts'
| 'agents';
type LocaleCode = 'en' | 'vi';Type Annotations:
// Explicit return types
function getSlug(title: string): string {
return title.toLowerCase().replace(/\s+/g, '-');
}
// Inferred types (simple cases)
const locale = 'en'; // Type: string
// Explicit when needed
const locale: LocaleCode = 'en'; // Type: LocaleCodeFunctionality:
- Features work as specified
- Edge cases handled
- Error states covered
- Responsive on mobile/desktop
Code Quality:
- Follows naming conventions
- Under file size limits
- No code duplication
- TypeScript strict mode passes
- No unused imports/variables
Performance:
- Minimal client-side JavaScript
- Images optimized
- No unnecessary re-renders
- Efficient data fetching
Accessibility:
- Semantic HTML
- Alt text for images
- Keyboard navigation works
- Color contrast sufficient (WCAG AA)
i18n:
- All UI strings in i18n files
- Vietnamese translation provided (or issue created)
- Locale-specific formatting correct
Documentation:
- Complex logic commented
- Props documented (JSDoc)
- README updated if needed
Manual Testing:
- Dev server:
npm run dev - Build test:
npm run build && npm run preview - Test both locales:
/docs/...and/vi/docs/... - Test responsive breakpoints
- Check navigation, links, search
Content Validation:
- Frontmatter validates against schema
- No broken internal links
- Images load correctly
- Code blocks have language specified
Format: Conventional Commits
type(scope): description
[optional body]
[optional footer]
Types:
feat: New featurefix: Bug fixdocs: Documentation onlystyle: Formatting, white-spacerefactor: Code restructuringperf: Performance improvementtest: Add/update testschore: Build, configs
Examples:
feat(content): add troubleshooting guide for installation issues
docs(readme): update quick start instructions
fix(sidebar): add missing troubleshooting category
style(global): adjust heading spacing for better readability
refactor(i18n): extract translation utilities to separate file
Rules:
- Subject: imperative mood, lowercase, no period
- Max 72 characters
- Body: explain WHY, not WHAT
- Footer: reference issues (
Closes #123)
Format: type/description
Examples:
feat/add-search-functionality
fix/sidebar-collapse-bug
docs/update-installation-guide
refactor/simplify-i18n-utils
- No secrets, API keys, credentials
- No console.log or debug code
- TypeScript compiles without errors
- Build succeeds (
npm run build) - No linting errors
- Files under size limits
- Conventional commit message
Never Commit:
- API keys
- Credentials
- .env files (except .env.example)
- Personal access tokens
Environment Variables:
# .env (gitignored)
OPENROUTER_API_KEY=sk-...
# .env.example (committed)
OPENROUTER_API_KEY=your_api_key_hereUsage:
// Server-side only
const apiKey = import.meta.env.OPENROUTER_API_KEY;
// Never expose in client-side codeUser Input: No user-generated content (static site) External Links: Open in same tab (let users decide) Images: Only from trusted sources Scripts: Minimal client-side JS, no eval()
Static Generation:
- All pages pre-rendered at build time
- No server-side rendering (SSR)
- Minimal JavaScript hydration
Asset Optimization:
- Images: WebP format, < 200KB
- Fonts: Variable fonts, preload
- CSS: Purged unused Tailwind classes
- JS: Tree-shaking, code splitting
Target Metrics:
- First Contentful Paint (FCP): < 1.5s
- Largest Contentful Paint (LCP): < 2.5s
- Time to Interactive (TTI): < 3.0s
- Total Blocking Time (TBT): < 200ms
Optimization Strategies:
- Islands architecture (partial hydration)
- Lazy load images
- Defer non-critical JavaScript
- Use
client:visiblefor below-fold components
Astro Components:
---
/**
* SidebarNav - Collapsible navigation tree
*
* Groups documentation by category from frontmatter.
* Sorts by order field, persists collapse state in localStorage.
*
* @component
*/
interface Props {
/** Current page path for active highlighting */
currentPath: string;
}
---React Components:
/**
* AI chat interface with message history
*
* Renders chat messages with markdown support and syntax highlighting.
* Backend integration pending (OpenRouter API).
*
* @param props - Component props
* @param props.initialMessages - Pre-populated messages
*/
export default function AIChat({ initialMessages = [] }: AIChatProps) {
// ...
}When to Update:
- New feature added
- Breaking changes
- Installation process changes
- New scripts added to package.json
- Architecture changes
Keep Under 300 Lines: Focus on essentials, link to detailed docs
When to Deviate:
- Performance-critical code (document reason)
- Third-party library constraints
- Generated code (mark clearly)
- Temporary workarounds (add TODO with date)
Documentation Required:
/**
* EXCEPTION: File exceeds 300 lines
* REASON: Complex navigation logic requires unified state management
* TODO: Refactor into smaller components when time permits
* DATE: 2025-11-25
*/None. All standards are clear and documented.