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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@paulelyson/elyui

v0.1.3

Published

A developer-friendly Angular Material. Minimalist components, themeable with plain CSS variables.

Readme

elyui

A developer-friendly Angular Material.

Angular Material under the hood, dressed in a clean minimalist skin.

An npm package nobody asked for. Built anyway.

→ Live demo — every component, every variant, every size, on one page.


Why though

Angular Material is genuinely excellent at the parts that are actually hard. Accessibility. Keyboard navigation. Overlay positioning. Focus traps. ControlValueAccessor plumbing. You should not be rewriting any of that, and elyui doesn't — it's Material underneath.

The problem is everything else.

You wanted a small green button. You got two module imports, a nested <mat-icon>, and a stylesheet where you're overriding --mdc- custom properties because Material's palette has primary and error but no idea what success means to your app.

Your template has become a scroll simulator and you still haven't written any actual features.

elyui keeps Material's brain and throws out its wardrobe.


The pitch

Every elyui snippet below is real, working API — the same inputs the demo page in this repo exercises. Nothing here is aspirational marketing.

Buttons

Angular Material. Two module imports, one size, and whatever colors your theme palette happens to define:

imports: [MatButtonModule, MatIconModule]
<button matButton="filled">
  <mat-icon>star</mat-icon>
  favorite
</button>

Want it small? Material has no size scale — that's your CSS to write. Want it green because it's a success action? Material gives you your theme's primary / secondary / tertiary / error. There's no success, no warning. So you're overriding --mdc-* custom properties or reaching into .mat-mdc-button-base internals, per state, forever.

elyui. One import, one line, and every axis is just an attribute:

<ely-button appearance="filled" variant="success" size="md" icon="star">favorite</ely-button>

| Input | Values | | --- | --- | | appearance | filled · tonal · outlined · ghost · link · icon | | variant | neutral · primary · accent · success · warning · danger | | size | xs · sm · md · lg | | icon | any Material Icons ligature — no <mat-icon> nesting | | hasBorder / hasRadius | false for a seamless or square look | | width | width-auto · width-filled |

That's 144 combinations of appearance × variant × size out of a single tag, and not one line of CSS on your side:

<ely-button appearance="tonal" variant="danger" size="xs">delete</ely-button>
<ely-button appearance="outlined" variant="warning" icon="star">favorite</ely-button>
<ely-button appearance="icon" variant="accent" icon="bookmark" />
<ely-button appearance="filled" variant="primary" [hasRadius]="false">square</ely-button>

Badges

Careful with names here: Material's matBadge is the little notification dot that floats over another element — not this. The closest Material analogue to a status label is a chip, which was designed for input and filtering, not for labelling state:

<mat-chip-set>
  <mat-chip>Active</mat-chip>
</mat-chip-set>

A chip is meant to live inside a <mat-chip-set> (or mat-chip-listbox / mat-chip-grid), so a single status label drags a wrapper element along with it. And there is no variant, no size, no semantic color — every chip looks identical. One green "Active" and one red "Overdue" means hand-writing CSS against .mat-mdc-chip internals for each state your app has.

elyui:

<ely-badge variant="success" size="md">Active</ely-badge>
<ely-badge variant="danger" [hasCloseIcon]="true">Overdue</ely-badge>
<ely-badge variant="primary" [hasBadgeIcon]="true">Info</ely-badge>
<ely-badge variant="neutral" [hasBorder]="false">Seamless</ely-badge>

Same six variants, same four sizes, no wrapper. hasBadgeIcon prepends an info icon, hasCloseIcon appends a dismiss icon that emits (closed).

Icons

This one's closer to a fair fight — <mat-icon>star</mat-icon> is already short. The friction is everything around it.

mat-icon is locked to 24px, and resizing it is the classic Material gotcha: setting font-size alone leaves the element's width and height at 24px, so your layout quietly breaks until you remember to write all three. Color inherits currentColor, so semantic coloring is on you. A tooltip is another module import.

<mat-icon class="icon-lg-success" matTooltip="done">star</mat-icon>
.icon-lg-success {
  font-size: 32px;
  width: 32px;   /* don't forget */
  height: 32px;  /* seriously, don't forget */
  color: #539364;
}

elyui:

<ely-icon name="star" size="lg" variant="success" tooltip="done" />

Sizes and variants come from the same scale every other component uses, so an md icon lines up with an md button without you measuring anything. [clickable]="false" drops the pointer cursor when it's decorative.

Snackbars

The one that hurts most. In Material you have to author an entire component before you can show a single toast:

@Component({
  selector: 'app-my-snackbar',
  imports: [MatSnackBarModule, MatIconModule],
  template: `
    <div class="wrapper">
      <mat-icon>{{ data.icon }}</mat-icon>
      <span>{{ data.message }}</span>
      <button matIconButton (click)="ref.dismiss()">
        <mat-icon>close</mat-icon>
      </button>
    </div>
  `,
})
export class MySnackbar {
  constructor(
    @Inject(MAT_SNACK_BAR_DATA) public data: any,
    public ref: MatSnackBarRef<MySnackbar>,
  ) {}
}

...then wire it up at every call site:

this.snackBar.openFromComponent(MySnackbar, {
  data: { message: 'Saved.', icon: 'check' },
  panelClass: ['my-snackbar-success'],
  verticalPosition: 'top',
  duration: 10000,
});

...then write global CSS to undo Material's default padding, because the snackbar renders in a CDK overlay attached to document.body, outside your component's styles. (Ask how we know.)

elyui:

this.snackbar.openSnackbar({ type: 'success', message: ['Saved.'], icon: 'check' });

That's it. That's the whole thing.

Theming

Angular Material — Sass, a theming API, and a rebuild:

@use '@angular/material' as mat;

html {
  @include mat.theme((
    color: (primary: mat.$violet-palette),
    typography: Roboto,
    density: 0,
  ));
}

elyui — it's just CSS variables. Change a value, save, done:

:root {
  --color-bg-primary-idle: #d1fae5;
  --color-text-primary-default: #047857;
  --border-radius-md: 6px;
}

No Sass. No rebuild. No theming API to learn. If you can write a hex code, you can retheme the entire library.


Install

npm install @paulelyson/elyui @angular/material @angular/cdk

@angular/core, @angular/common, @angular/forms, and rxjs are also peer dependencies, but you already have those in any Angular app.

Requires Angular 21+.

Setup

1. Import the theme. Add it to your angular.json styles array (or @import it from your own global stylesheet). This defines every CSS variable the components render with — without it, components render unstyled:

"styles": [
  "node_modules/@paulelyson/elyui/styles/elyui.css",
  "src/styles.css"
]

2. Load the Material Icons font. Icon — and anything that renders one internally, which is most things — uses ligature icon names and expects this font in your index.html:

<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />

That's it — there's no third step. Angular Material 21 animates via CSS, so you do not need provideAnimationsAsync() or the @angular/animations package.

Usage

Components are standalone — import the ones you use, no NgModule:

import { Component } from '@angular/core';
import { Button, Badge, Icon } from '@paulelyson/elyui';

@Component({
  selector: 'app-example',
  imports: [Button, Badge, Icon],
  template: `
    <ely-button appearance="filled" variant="primary" icon="save">Save</ely-button>
    <ely-badge variant="success">Active</ely-badge>
    <ely-icon name="star" size="lg" variant="warning" tooltip="favorite" />
  `,
})
export class Example {}

Snackbar is opened imperatively through its service — no component to import, no template markup:

import { SnackbarService } from '@paulelyson/elyui';

export class Example {
  constructor(private snackbar: SnackbarService) {}

  save(): void {
    this.snackbar.openSnackbar({
      type: 'success',
      message: ['Saved successfully.'],
      icon: 'check',
    });
  }
}

Theming

Every size- and color-dependent value is a CSS variable. Override any of them after importing elyui.css:

:root {
  --color-bg-primary-idle: #d1fae5;
  --color-text-primary-default: #047857;
  --border-radius-md: 6px;
}

The token families:

| Family | Pattern | Example | | --- | --- | --- | | Font size | --font-size-{xs,sm,md,lg} | --font-size-md: 14px | | Padding | --padding-{xs,sm,md,lg} | --padding-md: 6px 10px | | Border radius | --border-radius-{xs,sm,md,lg} | --border-radius-md: 12px | | Gap | --gap-{xs,sm,md,lg} | --gap-md: 6px | | Text color | --color-text-{variant}-default | --color-text-success-default | | Border color | --color-border-{variant}-idle | --color-border-danger-idle | | Background | --color-bg-{variant}-idle | --color-bg-accent-idle |

{variant} is primary, accent, success, warning, or danger — plus gray (the neutral) for border and background only.

Text colors additionally expose three standalone tokens instead of a gray variant: --color-text (the default body color), --color-text-white, and --color-text-gray-light. There's also --color-bg-primary-active for the pressed state, and --color-toggle-track-off / --color-toggle-thumb for the Toggle switch.

The full token list is short and readable — worth skimming styles/elyui.css directly.

Components

Icon · Badge · Button · Toggle · Snackbar (+ SnackbarService) · SegmentedControl

Most accept:

  • sizexs | sm | md | lg
  • variantneutral | primary | accent | success | warning | danger
  • hasBorderboolean, for a seamless borderless look
  • hasRadiusboolean on Button and SegmentedControl, for square corners

Nothing is forced on you. Don't want the focus glow? [hasFocusGlow]="false". Don't want the border? [hasBorder]="false". The library has opinions, not rules.

Roadmap

Form controls are next, and they're where this gets properly satisfying. The autocomplete in the app this library was extracted from is 23 lines of template plus ~150 lines of FormControl / valueChanges / displayWith / CVA wiring. The goal:

<ely-autocomplete label="Equipment" [options]="options" (optionselected)="pick($event)" />

Not shipped yet — coming in a future release, alongside input, textarea, dropdown, datepicker, and tab.

Status

Pre-1.0. The API may still shift as more components land. Pin the version if that bothers you.

License

MIT.