npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@lukfel/scaffold

v20.0.10

Published

This Angular library provides a basic UI scaffold and services for modern web and mobile apps

Readme

Documentation

Introduction

This Angular library provides a foundational scaffold for modern web and mobile applications. It includes essential UI elements such as a header, sidebar, drawer, footer, floating button, and built-in services for theme switching, snackbar notifications, dialog management, and breakpoint detection. Simply wrap your router-outlet with the lf-scaffold element and configure the ScaffoldConfig within the ScaffoldService.

Installation

Install the package using npm:

npm install @lukfel/scaffold

Module

Import the ScaffoldModule into your app.module.ts file.

  • Note: (Optional) The library includes a built-in logging service called Logger, which logs library deugging events when a ScaffoldLibraryConfig is provided and debugging is set to true. Logging is automatically disabled in production mode when prodution is set to true.
import { ScaffoldModule } from '@lukfel/scaffold';
import { isDevMode } from '@angular/core';

@NgModule({
  ...
  imports: [
    ScaffoldModule.forRoot({ production: !isDevMode(), debugging: isDevMode() }),    // Omit .forRoot(...) if logging is not required
  ]
})
export class AppModule { }

Styling

Import the styles in your styles.scss and apply a default theme.

  • Note: The library’s styles include Material icons and Roboto font styles.
@use "@lukfel/scaffold/styles" as lf;
@include lf.scaffold-theme();           // include a default theme

Custom Themes (Optional)

To customize the default theme, define a new theme map specifying primary, accent, and warn colors using Material palettes. Enabling the dark option applies a dark theme. Pass your custom theme to lf.scaffold-theme($my-theme).

@use "@lukfel/scaffold/styles" as lf;
@use '@angular/material' as mat;

$my-theme: (
  primary: mat.define-palette(mat.$pink-palette),
  accent: mat.define-palette(mat.$blue-palette),
  warn: mat.define-palette(mat.$red-palette),
  dark: false
);

@include lf.scaffold-theme($my-theme);

Multiple Themes (Optional)

To switch between multiple themes dynamically, define additional themes using lf.scaffold-colors($theme, 'theme-class'), then apply the class to the <body class="theme-class"> tag.

  • Note: The ThemeService allows dynamic theme switching.
@use "@lukfel/scaffold/styles" as lf;
@use '@angular/material' as mat;

$my-theme: (
  primary: mat.define-palette(mat.$pink-palette),
  accent: mat.define-palette(mat.$blue-palette),
  warn: mat.define-palette(mat.$red-palette),
  dark: false
);

$my-theme2: (
  primary: mat.define-palette(mat.$purple-palette),
  accent: mat.define-palette(mat.$amber-palette),
  dark: false
);

@include lf.scaffold-theme($my-theme);                      // Set the primary theme with lf.scaffold-theme(...)
@include lf.scaffold-colors($my-theme2, 'my-theme2');       // Set additional themes with lf.scaffold-colors(...)

Custom Typography (Optional)

To change the default typography from Roboto, pass an additional parameter font-family in the theme map.

  • Note: Don't forget to also import and set the font-family in the styles.
@use "@lukfel/scaffold/styles" as lf;
@use '@angular/material' as mat;

$my-theme: (
  primary: mat.define-palette(mat.$pink-palette),
  accent: mat.define-palette(mat.$blue-palette),
  warn: mat.define-palette(mat.$red-palette),
  dark: false,
  font-family: 'Comic Sans'
);

@include lf.scaffold-theme($my-theme); 

body {
    font-family: "Comic Sans MS" !important;
}

Template

Wrap your application’s content inside the lf-scaffold component in app.component.html.

<lf-scaffold>
  <!-- (Optional) drawer content shows inside the left drawer if enabled -->
  <ng-container drawerContent></ng-container>
  <router-outlet></router-outlet>
</lf-scaffold>

Configuration

Import the ScaffoldService in app.component.ts to manage the ScaffoldConfig settings.

import { ScaffoldService } from '@lukfel/scaffold';

export class AppComponent {
  constructor(private scaffoldService: ScaffoldService) {}
}

Update Configuration

Define the ScaffoldConfig in app.component.ts and update the scaffoldConfig property in ScaffoldService.

  • Notes:
    • If a sub-configuration (e.g., headerConfig) is missing or does not have enable: true, the corresponding UI element will not be displayed.
    • Refer to the demo project for full configuration details.
import { ScaffoldService, ScaffoldConfig } from '@lukfel/scaffold';

export class AppComponent {

  public scaffoldConfig: ScaffoldConfig = {
    scrollPositionRestoration: true,
    headerConfig: { enable: true, title: 'Scaffold', subtitle: 'by Lukas Felbinger' },
    navbarConfig: { enable: true },
    footerConfig: { enable: true, copyright: '© Lukas Felbinger 2023' },
    floatingButtonConfig: { enable: true }
  };

  constructor(private scaffoldService: ScaffoldService) {
    this.scaffoldService.scaffoldConfig = this.scaffoldConfig;
  }
}

Events

There are two ways to listen to scaffold user events (button clicks, input changes, ...):

Option 1 (Recommended) – Subscribe to Observables

Subscribe to the event Observables and listen to changes

constructor(private scaffoldService: ScaffoldService, private router: Router) {
  // Listen to click events (header menu and navbar buttons - click)
  this.scaffoldService.buttonClickEventValue$.subscribe((id: string) => {
    this.router.navigate([id]);
  });

  // Listen to header input change events (header input field - change)
  this.scaffoldService.headerInputChangeValue$.subscribe((value: string) => {
    ...
  });
}

Option 2 – Use Output Events

Specify the needed output events and call custom methods

<lf-scaffold
  (headerButtonClickEvent)="headerButtonClickEvent($event)"
  (headerInputSubmitEvent)="headerInputSubmitEvent($event)"
  (headerInputChangeEvent)="headerInputChangeEvent($event)"
  (navbarButtonClickEvent)="navbarButtonClickEvent($event)">
  <router-outlet></router-outlet>
</lf-scaffold>
// Listen to header click events (header menu buttons - click)
public headerButtonClickEvent(id: string): void {
  this.router.navigate([id]);
}

// Listen to header input submit events (header input field - submit)
public headerInputSubmitEvent(value: string): void {
  ...
}

// Listen to header input change events (header input field - change)
public headerInputChangeEvent(value: string): void {
  ...
}

// Listen to navbar click events (navbar menu buttons - click)
public navbarButtonClickEvent(id: string): void {
  this.router.navigate([id]);
}

Additional Services

This library includes several utility services:

  • Logger – Development-only logging
  • SnackbarService – Display snackbar notifications
  • DialogService – Display custom dialogs
  • BreakpointService – Detect screen breakpoints
  • ThemeService – Manage themes dynamically
  • RouterService – Track route changes and retreive route history
  • SeoService – Manage meta tags
  • LocalStorageService – Handle local storage

Logger

Logs internal library information if debugging is true and hides application logs during production if production is true.

  • Note: ScaffoldLibraryConfig must be set during initialization ScaffoldModule.forRoot( { production: !isDevMode(), debugging: isDevMode() } )
import { Logger } from '@lukfel/scaffold';

export class AppComponent {

  constructor(private logger: Logger) {}
  
  // Generic api call with logging
  public apiCallWithLogging(): void {
    this.apiService.apiCall().then(result => {
      this.logger.log(result);
    }).catch(error => {
      this.logger.error(error);
    });
  }
}

SnackbarService

Provides basic methods to display simple snackbar notifications with or without actions.

import { SnackbarService } from '@lukfel/scaffold';

export class AppComponent {

  constructor(private snackbarService: SnackbarService) {}
  
  // Generic api call with snackbar response
  public apiCallWithSnackbarResponse(): void {
    this.apiService.apiCall().then(result => {
      this.snackbarService.openSnackbar('Call was successful');
    }).catch(error => {
      this.snackbarService.openSnackbar('Call was not successful');
    });
  }
}

DialogService

Includes a basic confirmation dialog thar returns a Promise. Use the method openCustomDialog to pass your own dialog template and config.

import { DialogService } from '@lukfel/scaffold';

export class AppComponent {

  constructor(private dialogService: DialogService) {}
  
  // Generic api call with a subsequent confirmation dialog
  public apiCallWithDialogConfirmation(): void {
    this.dialogService.openConfirmDialog('Do you really want to make this api call?').then(response => {
      // If the user confirmed the dialog, go through with the api call
      if(response === true) {
        this.apiService.apiCall().then(result => {
          ...
        }).catch(error => {
          ...
        }); 
      }
    });
  }
}

BreakpointService

Allows you to subscribe to breakpoint changes.

import { BreakpointService } from '@lukfel/scaffold';

export class AppComponent {

  constructor(private breakpointService: BreakpointService) {
    this.breakpointService.breakpoint$.subscribe((result: BreakpointState) => {
      // Check which breakpoint is active
      if (result.breakpoints[Breakpoints.XSmall]) {
        ...
      } else if (result.breakpoints[Breakpoints.Small]) {
        ...
      } else if (result.breakpoints[Breakpoints.Medium]) {
        ...
      } else if (result.breakpoints[Breakpoints.Large]) {
        ...
      }
    });
  }
}

ThemeService

Dynamically change between your defined themes.

import { ThemeService } from '@lukfel/scaffold';

export class AppComponent {

  constructor(private themeService: ThemeService) {
    this.themeService.setTheme('my-theme2', true);    // the second parameter allows to persists the theme in the LocalStorage (using the built in LocalStorageService)
  }
}

Interceptors

Intercept HTTP Calls and automatically show a loading spinner.

  • Note: The loading spinner can also be manually shown by udpating the value for scaffoldConfig.loading in the ScaffoldService
import { ScaffoldModule } from '@lukfel/scaffold';
import { isDevMode } from '@angular/core';

@NgModule({
  ...
  imports: [
    ScaffoldModule.forRoot({ production: !isDevMode(), debugging: isDevMode() }),    // Omit .forRoot(...) if logging is not required
  ],
  providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: LoadingInterceptor,
      multi: true
    }
  ]
})
export class AppModule { }