This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Angular 21 standalone template project using zoneless change detection (default), NgRx SignalStore for state management, TailwindCSS v4 with DaisyUI, internationalization (i18n), theming capabilities, and ultra-secure authentication via HttpOnly cookies.
This is a production-ready template designed for building scalable Angular applications with modern best practices, security-first authentication, and optimal performance.
pnpm start- Start development server on http://localhost:4200pnpm build- Production build (outputs todist/angular-template-project)pnpm watch- Development build with watch mode
pnpm test- Run Vitest testspnpm test:ui- Run Vitest with UIpnpm test:coverage- Generate test coverage report
pnpm lint- Run Biome linter with auto-fix (includes unsafe fixes)pnpm format- Run Biome formatter with auto-fixpnpm precommit- Run both lint and format (used by husky pre-commit hook)
ng generate component component-name- Generate new component (SCSS styles by default)ng generate directive|pipe|service|class|guard|interface|enum- Generate other Angular artifacts
The application follows a feature-first architecture with a central core module for shared functionality:
src/app/
├── core/ # Global shared functionality
│ ├── guards/ # Route guards
│ ├── interceptors/ # HTTP interceptors
│ ├── pipes/ # Shared pipes
│ ├── services/ # Utility services (NOT for state)
│ ├── stores/ # NgRx SignalStores (global state)
│ ├── types/ # TypeScript types and enums
│ └── utils/ # Utility functions
└── features/ # Feature modules (domain-specific)
└── [feature-name]/ # Each feature is self-contained
src/app/core/
guards/- Route guards (auth.guard.ts,anonymous.guard.ts)interceptors/- HTTP interceptors (auth.interceptor.tswith CSRF support)pipes/- Shared pipes (safe-html.pipe.ts,safe-url.pipe.ts)services/- Utility services (NOT for state management)seo.service.ts- SEO meta tags management with i18n supportrouter-state.service.ts- Router state management with signalsnotify.service.ts- Notifications (using Notiflix)
stores/- NgRx SignalStores for global stateauth.store.ts- Authentication with HttpOnly cookies + CSRFlanguage.store.ts- i18n management with signalstheme.store.ts- Theme switching (light/dark) with signals
types/- TypeScript types and enumsenums/- Languages, Themesinterfaces/- Shared interfaces (Seo)
utils/- Utility functionscookie.utils.ts- Cookie management utilitiesrouter-seo.init.ts- Router SEO initialization with translation support
Each feature should be organized as a self-contained module with structure similar to core/:
features/[feature-name]/
├── components/ # Feature-specific components
├── pages/ # Feature pages/views (*.page.ts)
├── services/ # Feature-specific services
├── stores/ # Feature-specific stores (if needed)
├── types/ # Feature-specific types
│ ├── models/ # Data models
│ └── enums/ # Feature enums
├── guards/ # Feature-specific guards
├── utils/ # Feature-specific utilities
└── [feature-name].routes.ts # Feature routes
Feature Guidelines:
- Self-contained: Each feature should be independent
- No barrel files: Use direct imports for better tree-shaking
- Lazy-loaded: Features should be lazy-loaded via routes
- Minimal dependencies: Avoid cross-feature dependencies (use
core/instead) - Naming conventions: Use
*.page.tsfor routable components
Example lazy-loaded feature:
// app.routes.ts
{
path: 'products',
loadChildren: () => import('./features/products/products.routes')
}IMPORTANT: This project uses NgRx SignalStore for all global state management. Do NOT create services for state storage.
Ultra-secure authentication using HttpOnly + Secure + SameSite cookies:
import { AuthStore } from '@core/stores/auth.store';
// Inject store
const authStore = inject(AuthStore);
// Read state
authStore.isAuthenticated(); // signal
authStore.user(); // signal
authStore.isAnonymous(); // computed
authStore.userName(); // computed
// Actions
authStore.login({ email, password, rememberMe });
authStore.logout();
authStore.refreshUser();
authStore.fetchCsrfToken();
authStore.checkAuth();Key features:
- Zero tokens in localStorage (immune to XSS)
- HttpOnly cookies for JWT storage
- CSRF protection with token rotation
- Automatic session validation on init
- Refresh token rotation ready
import { LanguageStore } from '@core/stores/language.store';
const languageStore = inject(LanguageStore);
// Read state
languageStore.current(); // signal: Languages
languageStore.isEnglish(); // computed
languageStore.isSpanish(); // computed
// Actions
languageStore.setLanguage(Languages.English);
languageStore.toggleLanguage();import { ThemeStore } from '@core/stores/theme.store';
const themeStore = inject(ThemeStore);
// Read state
themeStore.current(); // signal: Themes
themeStore.isDarkMode(); // computed
themeStore.isLightMode(); // computed
// Actions
themeStore.setTheme(Themes.Dark);
themeStore.toggleTheme();The template includes reactive SEO management that waits for translations to load:
src/app/core/utils/router-seo.init.ts:
- Automatically updates page title and meta description on route changes
- Waits for translations to load before applying SEO tags
- Uses
TranslateService.onLangChangefor reactivity - Initialized in
app.config.tsviaprovideAppInitializer
Route configuration with SEO:
{
path: 'home',
component: HomeComponent,
data: {
title: 'routes.home.title', // Translation key
description: 'routes.home.description' // Translation key
}
}- TailwindCSS v4 with DaisyUI v5 component library
- Dark mode: Class-based with
[data-theme="dark"]attribute - SCSS: Default style language for components
- Tailwind plugins: typography, forms, aspect-ratio, container-queries
CRITICAL: This app uses HttpOnly cookies for authentication, NOT localStorage.
- Cookies sent automatically with
withCredentials: true - No manual token handling needed
- CSRF token included in POST/PUT/DELETE/PATCH requests
See docs/BACKEND_AUTH_REQUIREMENTS.md for complete implementation guide.
Quick overview:
POST /api/auth/login- Returns Set-Cookie headers (HttpOnly)GET /api/auth/csrf- Returns CSRF tokenGET /api/auth/me- Validates sessionPOST /api/auth/logout- Clears cookies- All endpoints must validate CSRF on mutations
Auth Interceptor (src/app/core/interceptors/auth.interceptor.ts):
- Adds
withCredentials: trueto all requests - Adds
X-CSRF-Tokenheader to POST/PUT/DELETE/PATCH - Handles 401 (logout) and 403 (CSRF invalid)
- ngx-translate: Translation management
- Translation files:
src/assets/i18n/*.json - Supported languages defined in
Languagesenum - Managed by
LanguageStorewith cookie persistence - SEO integration waits for translations before updating meta tags
IMPORTANT: This project does NOT use barrel files (index.ts) for better tree-shaking.
TypeScript path aliases configured in tsconfig.json:
@app/*→src/app/*@core/*→src/app/core/*@environments/*→src/environments/*
Always use direct imports:
// ✅ DO THIS
import { AuthStore } from '@core/stores/auth.store';
import { Languages } from '@core/types/enums/languages';
import { environment } from '@environments/environment';
// ❌ DON'T DO THIS (no barrel files)
import { AuthStore } from '@core/stores';
import { Languages } from '@core/types';
import { environment } from '@core/environments';Environment files:
src/environments/environment.ts- Development environmentsrc/environments/environment.production.ts- Production environment
Environment variables include:
- API URL configuration
- Default language and theme
- Cookie keys (only for non-sensitive data like theme/language)
- Timezone settings
NOTE: No auth token keys in environment (cookies handle auth).
Zoneless change detection enabled by default in Angular 21.
Router features enabled:
- Component input binding
- View transitions
- In-memory scrolling with anchor scrolling
- Same URL navigation reload
HTTP client configured with:
- Fetch API
- Auth interceptor for CSRF + cookies
withCredentials: trueglobally
App initialization (app.config.ts):
provideAppInitializer(() => {
inject(LanguageStore); // Init language from cookie
inject(ThemeStore); // Init theme from cookie
initRouterSeoUpdates(); // Init SEO with translation support
})Biome is used for linting and formatting:
- Tab indentation (width: 2)
- Single quotes
- Semicolons required
- ES5 trailing commas
- Arrow function parentheses: as needed
- Vitest with
@analogjs/vitest-angular - Test setup:
src/setup-vitest.ts - Run tests with
pnpm test - UI mode available with
pnpm test:ui
Husky pre-commit hook runs:
pnpm lint- Biome linterpnpm format- Biome formatter
DO inject stores directly in components:
export class MyComponent {
authStore = inject(AuthStore);
themeStore = inject(ThemeStore);
languageStore = inject(LanguageStore);
// Use signals in template
isAuth = this.authStore.isAuthenticated;
isDark = this.themeStore.isDarkMode;
}DON'T create services for global state:
// ❌ DON'T DO THIS
@Injectable()
export class MyStateService {
private state = signal({...});
}
// ✅ DO THIS INSTEAD
// Create a SignalStore in src/app/core/stores/When creating a new feature:
-
Create feature folder:
features/my-feature/ ├── pages/ ├── components/ ├── services/ └── my-feature.routes.ts -
Define routes:
// features/my-feature/my-feature.routes.ts import { Routes } from '@angular/router'; export default [ { path: '', component: MyFeaturePage } ] as Routes;
-
Lazy load in main routes:
// app.routes.ts { path: 'my-feature', loadChildren: () => import('./features/my-feature/my-feature.routes') }
Router state is managed by RouterStateService using signals (no NgRx needed):
import { RouterStateService } from '@core/services/router-state.service';
const routerState = inject(RouterStateService);
// Access route data
routerState.url(); // current URL
routerState.params(); // route params
routerState.queryParams(); // query params
routerState.data(); // route dataauthGuard- Protects authenticated routes, checksAuthStore.isAuthenticated()anonymousGuard- Protects routes for non-authenticated users only
Both guards use direct imports:
import { authGuard } from '@core/guards/auth.guard';
import { anonymousGuard } from '@core/guards/anonymous.guard';authInterceptor automatically:
- Includes
withCredentials: truefor cookies - Attaches CSRF tokens to mutating requests
- Handles 401 (expired session) and 403 (invalid CSRF)
- ✅ Tokens in HttpOnly cookies only
- ✅ CSRF protection on all mutations
- ✅ Automatic logout on 401
- ✅ Refresh token rotation ready
- ❌ Never store auth tokens in localStorage
- ❌ Never send tokens in response bodies
Frontend expects backend to have:
credentials: true // Allow cookies
origin: 'http://localhost:4200' // Frontend URL- Only store non-sensitive config (theme, language keys)
- Never store API keys or secrets in frontend
This project uses pnpm. Always use pnpm commands, not npm or yarn.
- ✅ Feature-first structure
- ✅ No barrel files (direct imports)
- ✅ Lazy-loaded features
- ✅ SignalStore for state
- ✅ Services only for utilities (not state)
- ✅ HttpOnly cookies for auth
- ✅ CSRF protection
- ✅ XSS-safe pipes
- ✅ No sensitive data in localStorage
- ✅ Zoneless change detection
- ✅ Signal-based reactivity
- ✅ Better tree-shaking (no barrels)
- ✅ Lazy loading
- ✅ Fetch API
- ✅ Strict TypeScript
- ✅ Biome linting/formatting
- ✅ Pre-commit hooks
- ✅ Direct imports
- ✅ Consistent naming
- ✅ Fast testing (Vitest)
- ✅ Type-safe routing
- ✅ Path aliases
- ✅ Clear documentation
- ✅ Modern patterns
This project represents the modern Angular 21 architecture:
- Zoneless change detection - Default in Angular 21
- SignalStore over services - Modern state management
- HttpOnly cookies - XSS protection for auth
- No barrel files - Better tree-shaking
- Feature-first - Scalable architecture
- Vitest over Jest - Faster testing
- No NgModules (standalone components only)
- No
index.tsbarrel files - No localStorage for auth tokens
- CSRF token required for mutations
- Router state via signals (not NgRx router store)
For complete backend auth implementation, see docs/BACKEND_AUTH_REQUIREMENTS.md.