Add New Sub Library

Documentation for Developers

How to Add a New Component (or Sub-Library) to the Monorepo

This guide documents the complete process of adding a new library to our NX monorepo, following the established patterns and architecture principles.

Prerequisites

Before creating a new component (or sub-library), ensure you have:

Component Helper Libraries for our Library or Sub-Library (component)

@widgets - Widget system

Dependencies: @core

@gograb-ai - AI integration

This library will help our component to be more intelligent

@storage-system - Storage system

This library will help our component to store data

@theme-system - Theme system

This library will help our component to be themed

Step 1: Generate the Library

Use the NX Angular library generator:

npx nx g @nx/angular:library my-library-name

Configuration Options

When prompted, use these settings:

Step 2: Configure Library Structure

Standard Library Structure

libs/my-library-name/
├── src/
│   ├── lib/
│   │   ├── components/      # Angular components
│   │   │   ├── index.ts       # Add barrel file for components
│   │   ├── services/        # Injectable services
│   │   │   ├── index.ts       # Add barrel file for services
│   │   ├── models/          # Type definitions
│   │   │   ├── index.ts       # Add barrel file for models
│   │   ├── directives/      # Custom directives
│   │   │   ├── index.ts       # Add barrel file for directives
│   │   ├── pipes/           # Custom pipes
│   │   │   ├── index.ts       # Add barrel file for pipes
│   │   ├── utils/           # Utility functions
│   │   └── index.ts         # Internal exports
│   └── index.ts             # Public API entry point
├── project.json             # NX project configuration
├── tsconfig.json            # TypeScript configuration
├── tsconfig.lib.json        # Library-specific TypeScript config
├── tsconfig.spec.json       # Test TypeScript config
├── jest.config.ts           # Jest configuration
├── README.md                # Library documentation
└── package.json             # Package configuration

Step 3: Configure TypeScript and Build Settings

Update tsconfig.base.json

Add your library to the path mapping:

{
  "compilerOptions": {
    "paths": {
      // ... existing paths ...
      "@my-library-name": ["libs/my-library-name/src/index.ts"],
      "@my-library-name/*": ["libs/my-library-name/src/*"]
    }
  }
}

Configure Library TypeScript (tsconfig.lib.json)

{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "outDir": "../../dist/out-tsc",
    "declaration": true,
    "declarationMap": true,
    "inlineSources": true,
    "types": ["node"]
  },
  "include": ["src/**/*.ts"],
  "exclude": [
    "src/**/*.spec.ts",
    "src/**/*.test.ts",
    "jest.config.ts",
    "src/**/*.stories.ts"
  ]
}

Configure Build Target (project.json)

{
  "name": "my-library-name",
  "sourceRoot": "libs/my-library-name/src",
  "prefix": "lib",
  "targets": {
    "build": {
      "executor": "@nx/angular:ng-packagr-lite",
      "outputs": ["dist/libs/my-library-name"],
      "options": {
        "project": "libs/my-library-name/ng-package.json"
      },
      "configurations": {
        "production": {
          "tsConfig": "libs/my-library-name/tsconfig.lib.prod.json"
        },
        "development": {
          "tsConfig": "libs/my-library-name/tsconfig.lib.json"
        }
      },
      "defaultConfiguration": "production"
    },
    "test": {
      "executor": "@nx/jest:jest",
      "outputs": ["coverage/libs/my-library-name"],
      "options": {
        "jestConfig": "libs/my-library-name/jest.config.ts"
      }
    },
    "lint": {
      "executor": "@nx/eslint:lint",
      "outputs": ["eslint-report.json"],
      "options": {
        "lintFilePatterns": [
          "libs/my-library-name/**/*.ts",
          "libs/my-library-name/**/*.html"
        ]
      }
    }
  },
  "tags": ["npm:public"]
}

Step 4: Configure Package for Publishing

Create ng-package.json

{
  "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
  "dest": "../../dist/libs/my-library-name",
  "lib": {
    "entryFile": "src/index.ts"
  }
}

Create package.json

{
  "name": "@my-library-name",
  "version": "0.0.1",
  "description": "Description of my library",
  "keywords": ["angular", "library", "typescript"],
  "author": "GoGrab Space",
  "license": "MIT",
  "repository": {
    "type": "git",
    "url": "https://github.com/gograb-space/editor.git",
    "directory": "libs/my-library-name"
  },
  "peerDependencies": {
    "@angular/common": "^18.0.0",
    "@angular/core": "^18.0.0"
  },
  "dependencies": {
    "tslib": "^2.3.0"
  },
  "sideEffects": false,
  "main": "index.js",
  "module": "fesm2022/my-library-name.mjs",
  "typings": "index.d.ts"
}

Step 5: Integrate Core Systems (ESSENTIAL) ⭐

Before implementing any logic, configure integration with our three core systems:

🧠 Flash-Persistence Integration

Add state management with NGXS compatibility:

// libs/my-library-name/src/lib/providers/my-library.providers.ts
import { EnvironmentProviders } from '@angular/core';
import { provideFlashPersistenceWithNGXS } from '@flash-persistence/ngxs-compat';
import { MyLibraryState } from '../state/my-library.state';

export function provideMyLibrary(): EnvironmentProviders {
  return provideFlashPersistenceWithNGXS({
    states: [MyLibraryState],
    namespace: 'my-library',
    devMode: !environment.production
  });
}

🗄️ Storage-System Integration

Configure intelligent storage:

// libs/my-library-name/src/lib/services/my-library.service.ts
import { Injectable, inject } from '@angular/core';
import { StorageService } from '@storage-system';
import { signal, computed } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class MyLibraryService {
  private readonly storage = inject(StorageService);
  
  // Reactive storage-backed signals
  readonly config = this.storage.asSignal<MyLibraryConfig>('my-library:config', {
    theme: 'default',
    enabled: true
  }).value;
  
  // Computed properties based on storage
  readonly isReady = computed(() => this.config() !== null);
  
  async updateConfig(config: Partial<MyLibraryConfig>): Promise<void> {
    const current = this.config();
    await this.storage.put('my-library:config', { ...current, ...config });
  }
}

🎨 Theme-System Integration [[memory:6454111]]

Follow our established three-library architecture:

// libs/my-library-name/src/lib/providers/theme.providers.ts

// ✅ For production libraries (most common)
import { 
  ThemeApplicationService,
  ThemeManagerService,
  ThemeStorageService 
} from '@theme-runtime';

// ✅ OR for development/creation libraries
import { 
  ThemeEditorComponent,
  ThemeDebuggerComponent,
  ThemeManagerService  // Also available here
} from '@theme-dev-tools';

// ✅ OR for compatibility (always works)
import { ThemeManagerService } from '@theme-system';

// Add theme provider
import { provideUnifiedThemeSystemWithSync } from '@theme-runtime';

export function provideLibraryTheme() {
  return [
    provideUnifiedThemeSystemWithSync({
      enableSync: true,
      autoSync: true
    })
  ];
}

Library SCSS Integration:

// libs/my-library-name/src/lib/styles/index.scss
// ✅ CORRECT: Use direct path with proper module resolution
@use '@theme-styles/src/lib/styles/index' as *;

// Your component styles using theme variables
.my-component {
  // ✅ Use proper --ux- prefixed variables
  background: var(--ux-surface);
  color: var(--ux-on-surface);
  border: 1px solid var(--ux-outline);
  padding: var(--ux-spacing-md);
  border-radius: var(--ux-radius-md);
  
  // ✅ Use theme utility classes when possible
  @extend .ux-shadow;
  @extend .ux-transition;
}

Step 6: Implement Safe Typing with Models Directory

ALWAYS Create Models Directory First

# Create models directory structure
mkdir -p libs/my-library-name/src/lib/models
mkdir -p libs/my-library-name/src/lib/state
mkdir -p libs/my-library-name/src/lib/providers
touch libs/my-library-name/src/lib/models/index.ts
touch libs/my-library-name/src/lib/models/my-library.types.ts
touch libs/my-library-name/src/lib/state/my-library.state.ts

Define All Types in Models Directory

// libs/my-library-name/src/lib/models/my-library.types.ts
import type { SafeAny } from '@libs-foundational/core-models'; 

export interface MyLibraryConfig {
  name: string;
  theme: 'light' | 'dark' | 'auto';
  enabled: boolean;
  options: Record<string, SafeAny>;
  storage?: {
    namespace?: string;
    strategy?: 'speed' | 'balanced' | 'durable';
  };
}

export interface MyLibraryState {
  config: MyLibraryConfig;
  isInitialized: boolean;
  error?: string;
}

export interface MyComponentProps {
  data: MyLibraryConfig;
  disabled?: boolean;
  className?: string;
}

Create Flash-Persistence State

// libs/my-library-name/src/lib/state/my-library.state.ts
import { State, Action, Selector } from '@flash-persistence/ngxs-compat';
import { MyLibraryState, MyLibraryConfig } from '../models';

@State<MyLibraryState>({
  name: 'myLibrary',
  defaults: {
    config: {
      name: 'MyLibrary',
      theme: 'auto',
      enabled: true,
      options: {}
    },
    isInitialized: false
  }
})
export class MyLibraryState {
  @Selector()
  static config(state: MyLibraryState): MyLibraryConfig {
    return state.config;
  }

  @Selector()
  static isReady(state: MyLibraryState): boolean {
    return state.isInitialized && state.config.enabled;
  }

  @Action({ type: 'UpdateConfig' })
  updateConfig(ctx: any, config: Partial<MyLibraryConfig>) {
    const state = ctx.getState();
    ctx.setState({
      ...state,
      config: { ...state.config, ...config }
    });
  }

  @Action({ type: 'Initialize' })
  initialize(ctx: any) {
    const state = ctx.getState();
    ctx.setState({
      ...state,
      isInitialized: true
    });
  }
}

Export from Models Index

// libs/my-library-name/src/lib/models/index.ts
export * from './my-library.types';

Import from Models in Services/Components

// libs/my-library-name/src/lib/services/my-service.ts
import { MyLibraryConfig } from '../models';  // ← Use relative import from models

// libs/my-library-name/src/lib/components/my-component.ts
import { MyComponentProps } from '../models';  // ← Use relative import from models

Avoid Circular Dependencies

  1. Don't import from libraries at the same level
  2. Use core for shared interfaces
  3. Follow dependency hierarchy: Core → UI → Feature → App
// Good ✅
import type { SafeAny } from '@libs-foundational/core-models'; 

// Avoid ❌ - Don't import from peer libraries
// import { SomeType } from '@other-library';

Step 6: Create Public API

Define Public Exports (src/index.ts)

// Export all public models FIRST (other modules depend on these)
export * from './lib/models';

// Export all public components
export * from './lib/components';

// Export all public services
export * from './lib/services';

// Export all public directives
export * from './lib/directives';

// Export all public pipes
export * from './lib/pipes';

// Export utilities if public
export * from './lib/utils/public-utils';

// Re-export types from core for convenience
export type { SafeAny } from '@libs-foundational/core-models'; 

Component Export Example

// libs/my-library-name/src/lib/components/index.ts
export { MyComponent } from './my-component/my-component.component';
export { AnotherComponent } from './another-component/another-component.component';

// Export component types
export type { MyComponentProps } from '../models/my-component-props';

Step 7: Implement Modern Components with Full Integration

Modern Standalone Component with Full Theme Integration [[memory:6454111]]

// libs/my-library-name/src/lib/components/my-component/my-component.component.ts
import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, inject, signal, computed, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Store } from '@flash-persistence/ngxs-compat';
import { StorageService } from '@storage-system';
import { ThemeManagerService } from '@theme-system'; // ✅ Compatibility layer
import type { SafeAny } from '@libs-foundational/core-models'; 
import { MyComponentConfig, MyLibraryState } from '../../models';
import { MyLibraryService } from '../../services/my-library.service';

@Component({
  selector: 'lib-my-component',
  standalone: true,
  imports: [CommonModule],
  template: `
    <div 
      class="my-component ux-bg-surface ux-text-on-surface ux-p-4 ux-rounded-lg ux-shadow" 
      [class.ux-opacity-50]="disabled()"
      [class.ux-bg-primary]="isReady()"
    >
      @if (isReady()) {
        <div class="component-header ux-flex ux-items-center ux-space-x-3 ux-mb-4">
          @if (config().icon) {
            <i [class]="'fa fa-' + config().icon" class="ux-text-primary" aria-hidden="true"></i>
          }
          <h3 class="ux-text-lg ux-font-semibold ux-text-on-surface">{{ config().title }}</h3>
        </div>
        
        <div class="component-content ux-mb-4">
          <ng-content></ng-content>
        </div>
        
        <div class="component-actions ux-flex ux-space-x-2">
          @for (action of actions(); track action.id) {
            <button 
              type="button"
              class="ux-btn ux-btn-primary ux-px-4 ux-py-2 ux-rounded ux-transition"
              [class.ux-opacity-50]="disabled() || action.disabled"
              [disabled]="disabled() || action.disabled"
              (click)="handleAction(action)"
              [attr.aria-label]="action.label"
            >
              {{ action.label }}
            </button>
          }
        </div>
      } @else {
        <div class="component-loading ux-flex ux-items-center ux-justify-center ux-p-8">
          <i class="fa fa-spinner fa-spin ux-text-primary ux-mr-2" aria-hidden="true"></i>
          <span class="ux-text-on-surface">Loading...</span>
        </div>
      }
    </div>
  `,
  styleUrls: ['./my-component.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class MyComponent implements OnInit {
  private readonly store = inject(Store);
  private readonly storage = inject(StorageService);
  private readonly libraryService = inject(MyLibraryService);

  // Inputs with signals
  private _config = signal<MyComponentConfig>({
    title: 'Default Title',
    icon: 'cog',
    actions: []
  });
  
  @Input() set config(value: MyComponentConfig) {
    this._config.set(value);
  }
  
  private _disabled = signal(false);
  @Input() set disabled(value: boolean) {
    this._disabled.set(value);
  }

  // Outputs
  @Output() actionTriggered = new EventEmitter<SafeAny>();
  @Output() configChanged = new EventEmitter<MyComponentConfig>();

  // Computed properties
  config = computed(() => this._config());
  disabled = computed(() => this._disabled());
  
  // State selectors with signals
  readonly libraryState = this.store.select(MyLibraryState.config);
  readonly isReady = computed(() => this.libraryService.isReady());
  
  // Storage-backed configuration
  readonly userPreferences = this.storage.asSignal<SafeAny>('my-component:preferences', {}).value;
  
  // Theme integration
  readonly themeStyles = computed(() => {
    const theme = this.libraryState()?.theme || 'auto';
    const preferences = this.userPreferences();
    
    return {
      '--component-theme': theme,
      '--component-accent': preferences.accentColor || 'var(--ux-primary)'
    };
  });

  // Actions computed from config and state
  readonly actions = computed(() => {
    const baseActions = this.config().actions || [];
    const isEnabled = this.libraryState()?.enabled;
    
    return baseActions.map(action => ({
      ...action,
      disabled: !isEnabled || action.disabled
    }));
  });

  async ngOnInit(): Promise<void> {
    // Initialize component with state
    await this.libraryService.initialize();
  }

  async handleAction(action: SafeAny): Promise<void> {
    if (this.disabled() || action.disabled) return;

    // Store action in intelligent storage
    await this.storage.put(`my-component:last-action:${action.id}`, {
      action: action.id,
      timestamp: new Date().toISOString(),
      config: this.config()
    });

    // Emit action event
    this.actionTriggered.emit({
      action: action.id,
      data: action.data,
      timestamp: new Date()
    });

    // Dispatch to state if needed
    if (action.updateState) {
      this.store.dispatch({ type: 'UpdateConfig', payload: action.stateUpdate });
    }
  }
}

Models Example (Required)

// libs/my-library-name/src/lib/models/my-component.types.ts
import type { SafeAny } from '@libs-foundational/core-models'; 

export interface MyComponentConfig {
  title: string;
  data: SafeAny;
  options?: Record<string, SafeAny>;
}

Corresponding SCSS File:

// libs/my-library-name/src/lib/components/my-component/my-component.component.scss
// ✅ CORRECT: Use direct theme-system import
@use '@theme-styles/src/lib/styles/index' as *;

:host {
  display: block;
  width: 100%;
  
  .my-component {
    // ✅ Minimal custom styles - rely on theme utility classes in template
    position: relative;
    
    &.ux-bg-primary {
      // Enhanced styling when ready
      transform: translateY(-1px);
      box-shadow: var(--ux-shadow-lg);
    }
    
    // Only add styles not available in theme utilities
    .component-loading {
      min-height: 120px; // Custom min-height for loading state
    }
  }
}

Key Theme Integration Principles:

Service Example

// libs/my-library-name/src/lib/services/my-service.service.ts
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import type { SafeAny } from '@libs-foundational/core-models'; 
import { MyServiceConfig } from '../models';  // ← Import from models, NO local types!

@Injectable({
  providedIn: 'root'
})
export class MyService {
  private configSubject = new BehaviorSubject<MyServiceConfig | null>(null);
  private dataSubject = new BehaviorSubject<SafeAny[]>([]);
  
  config$ = this.configSubject.asObservable();
  data$ = this.dataSubject.asObservable();
  
  initialize(config: MyServiceConfig): void {
    this.configSubject.next(config);
  }
  
  async loadData(): Promise<SafeAny[]> {
    const config = this.configSubject.value;
    if (!config) {
      throw new Error('Service not initialized');
    }
    
    try {
      // Implementation logic here
      const data: SafeAny[] = [];
      this.dataSubject.next(data);
      return data;
    } catch (error) {
      console.error('Failed to load data:', error);
      throw error;
    }
  }
  
  updateData(newData: SafeAny[]): void {
    this.dataSubject.next(newData);
  }
}

Service Types (In Models Directory)

// libs/my-library-name/src/lib/models/my-service.types.ts
export interface MyServiceConfig {
  apiUrl: string;
  timeout?: number;
  retries?: number;
}

Step 8: Testing Setup

Configure Jest (jest.config.ts)

export default {
  displayName: 'my-library-name',
  preset: '../../jest.preset.js',
  setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
  coverageDirectory: '../../coverage/libs/my-library-name',
  transform: {
    '^.+\\.(ts|mjs|js|html)$': [
      'jest-preset-angular',
      {
        tsconfig: '<rootDir>/tsconfig.spec.json',
        stringifyContentPathRegex: '\\.(html|svg)$',
      },
    ],
  },
  transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
  snapshotSerializers: [
    'jest-preset-angular/build/serializers/no-ng-attributes',
    'jest-preset-angular/build/serializers/ng-snapshot',
    'jest-preset-angular/build/serializers/html-comment',
  ],
};

Component Test Example

// libs/my-library-name/src/lib/components/my-component/my-component.component.spec.ts
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MyComponent, MyComponentConfig } from './my-component.component';

describe('MyComponent', () => {
  let component: MyComponent;
  let fixture: ComponentFixture<MyComponent>;
  
  const mockConfig: MyComponentConfig = {
    title: 'Test Component',
    data: { test: true }
  };
  
  beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [MyComponent]
    }).compileComponents();
    
    fixture = TestBed.createComponent(MyComponent);
    component = fixture.componentInstance;
    component.config = mockConfig;
    fixture.detectChanges();
  });
  
  it('should create', () => {
    expect(component).toBeTruthy();
  });
  
  it('should display the title', () => {
    const compiled = fixture.nativeElement;
    expect(compiled.querySelector('h3').textContent).toContain('Test Component');
  });
  
  it('should emit actionTriggered when clicked', () => {
    spyOn(component.actionTriggered, 'emit');
    
    const button = fixture.nativeElement.querySelector('button');
    button.click();
    
    expect(component.actionTriggered.emit).toHaveBeenCalled();
  });
});

Step 9: Documentation

🚨 CRITICAL: No Local Types Policy

NEVER define types/interfaces locally in service or component files!

✅ Correct Pattern: Use Models Directory

// ✅ GOOD: In libs/my-library/src/lib/models/my-types.ts
export interface MyComponentConfig {
  title: string;
  data: SafeAny;
}

// ✅ GOOD: In libs/my-library/src/lib/services/my-service.ts  
import { MyComponentConfig } from '../models/my-types';

❌ Wrong Pattern: Local Type Definitions

// ❌ BAD: Local interface in service file
interface MyComponentConfig {  // <- Never do this!
  title: string;
  data: SafeAny;
}

Required Structure: Models Directory

Every library MUST have a models/ directory:

libs/my-library-name/
├── src/
│   ├── lib/
│   │   ├── components/
│   │   ├── services/  
│   │   ├── models/              # ← REQUIRED
│   │   │   ├── my-types.ts      # ← All types go here
│   │   │   └── index.ts         # ← Export all types
│   │   └── index.ts
│   └── index.ts                 # ← Export models (library entry point)

Create README.md

# @my-library-name

Brief description of what this library does.

## Installation

```bash
yarn add @my-library-name

Usage

Basic Usage

import { MyComponent, MyService } from '@my-library-name';

// Component usage
@Component({
  imports: [MyComponent],
  template: `
    <lib-my-component 
      [config]="myConfig"
      (actionTriggered)="onAction($event)">
    </lib-my-component>
  `
})
export class MyAppComponent {
  myConfig = {
    title: 'Hello World',
    data: { example: true }
  };
  
  onAction(event: any): void {
    console.log('Action triggered:', event);
  }
}

API Reference

Components

MyComponent

Property Type Description
config MyComponentConfig Configuration object
disabled boolean Whether component is disabled

Services

MyService

Methods for managing data and configuration.

Development

See the monorepo documentation for development guidelines.


## Step 10: Build and Testing

### Build the Library

```bash
# Build the library
npx nx build my-library-name

# Build with dependencies
npx nx build my-library-name --with-deps

Test the Library

# Run tests
npx nx test my-library-name

# Run tests with coverage
npx nx test my-library-name --coverage

# Run linting
npx nx lint my-library-name

Verify Library Integration

Create a test component in an app to verify the library works:

// In any app component
import { MyComponent } from '@my-library-name';

@Component({
  imports: [MyComponent],
  template: `<lib-my-component [config]="testConfig"></lib-my-component>`
})
export class TestComponent {
  testConfig = {
    title: 'Library Test',
    data: { working: true }
  };
}

Step 11: Dependency Management

Understanding Library Dependencies

  1. core (Foundation)

    • No dependencies
    • Contains all shared type definitions
    • Used by all other libraries
  2. Core Libraries

    • Can depend on core
    • Should not depend on other core libraries
  3. UI Libraries

    • Can depend on core and core libraries
    • Should not depend on feature libraries

Dependency Declaration

In your library's project.json, declare dependencies:

{
  "implicitDependencies": ["core-models"],
  "tags": ["npm:public", "scope:ui", "type:library"]
}

Common Patterns and Best Practices

1. Consistent Naming

2. Type Safety

3. Component Design

4. Service Design

5. Testing

Examples in Our Monorepo

core Library

Prompt-System Library (new)

Troubleshooting

Build Issues

  1. Circular Dependency Errors

    • Move shared types to @core
    • Avoid peer library imports
  2. Type Resolution Issues

    • Verify path mappings in tsconfig.base.json
    • Ensure proper export declarations
  3. Build Order Issues

    • Build dependencies first
    • Use --with-deps flag when needed

Testing Issues

  1. Import Errors in Tests

    • Verify test setup configuration
    • Check jest configuration
  2. Mock Issues

    • Use proper Angular testing utilities
    • Mock external dependencies

🌟 INTEGRATION SUMMARY ⭐

Every new library you create now automatically benefits from our three core systems:

🧠 Flash-Persistence Integration

// Automatic state persistence and reactivity
readonly libraryState = this.store.select(MyLibraryState.config);
this.store.dispatch({ type: 'UpdateConfig', payload: newConfig });

🗄️ Storage-System Integration

// Reactive storage with intelligent caching
readonly userPreferences = this.storage.asSignal<Config>('my-lib:config', defaults).value;
await this.storage.put('my-lib:config', newConfig); // Signal updates automatically!

🎨 Theme-System Integration

// Automatic theme integration
@use 'index' as theme;

.my-component {
  @include theme.ux-card(true);
  @include theme.ux-typography('heading', 'lg');
  color: var(--ux-on-surface);
}

🚀 Modern Angular 20+ Patterns

💡 Benefits You Get Immediately

  1. 🔥 Performance: Sub-10ms operations with intelligent caching
  2. 🧠 Intelligence: AI-powered storage decisions
  3. 🎨 Consistency: Automatic theme integration
  4. ⚡ Reactivity: Real-time updates across components
  5. 💾 Persistence: Zero-config state preservation
  6. 🌐 Sync: Cross-tab synchronization
  7. 📱 Responsive: Mobile-first design system
  8. ♿ Accessible: WCAG-compliant components

This comprehensive guide ensures that new libraries follow our established patterns and integrate seamlessly with our monorepo architecture.

🚨 Quick Troubleshooting Reference

If you encounter import/binding issues after creating a new library:

  1. "Can't bind to directive" errors → See Angular Import Issues
  2. Module resolution failures → Use Direct Library Paths
  3. Event handler type errors → Use $any($event) in templates

For detailed troubleshooting, see: Imports & Exports Guide

Integration in Applications

// In your application's global styles
@import '@theme-system/styles/themes';
@import '@theme-system/styles/utilities';

// Use theme mixins
.my-component {
  @include ux-card(true);
  
  .my-button {
    @include ux-button('primary');
  }
  
  @include ux-responsive(md) {
    padding: ux-spacing('lg');
  }
}

Step 8: Configure SASS Imports for Theme System (Optional)

If your library needs to use theme-system styles, configure SASS import paths:

Update Library project.json

Add stylePreprocessorOptions to your library's build configuration in libs/my-library-name/project.json:

{
  "targets": {
    "build": {
      "executor": "@nx/angular:ng-packagr-lite",
      "options": {
        "stylePreprocessorOptions": {
          "includePaths": ["libs/theme-system/src/lib/styles"]
        },
        "project": "libs/my-library-name/ng-package.json"
      }
    }
  }
}

Using Theme Styles in Library Components

Once configured, you can import theme-system styles in your library component SCSS files:

// libs/my-library-name/src/lib/components/my-component/my-component.component.scss
@use 'index' as theme;

.my-library-component {
  // Use theme variables
  background: var(--ux-surface);
  color: var(--ux-on-surface);
  padding: var(--ux-spacing-md);
  border-radius: var(--ux-border-radius);
  
  // Use theme mixins
  @include theme.ux-card(true);
  
  .component-button {
    @include theme.ux-button('primary');
  }
  
  // Responsive design
  @include theme.ux-responsive(md) {
    padding: var(--ux-spacing-lg);
  }
}

Alternative Approach for Libraries

If you prefer not to configure stylePreprocessorOptions in the library, you can use relative paths:

// Using relative path (less maintainable but works without configuration)
@use '../../../../../theme-system/src/lib/styles/index' as theme;

.my-component {
  @include theme.ux-card();
}

Important Notes for Library Development

  1. Theme System Dependency: If using theme-system styles, add @theme-system as a dependency
  2. Consumer Configuration: Applications using your library will need their own stylePreprocessorOptions configuration
  3. Documentation: Document theme-system usage requirements for library consumers
  4. Fallback Styles: Consider providing fallback styles for when theme-system is not available

Step 9: Testing

// ... existing code ...