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

@frontfactory/readmore

v1.3.1

Published

Zero-dependency plugin to truncate text to N lines or a fixed height, with an ellipsis and an accessible read more / read less toggle.

Readme

Readmore JS

Version Downloads License

Lightweight, framework-agnostic plugin to clamp text to N lines (or a fixed pixel height) with an ellipsis and a Read more / Read less toggle button.

  • Zero dependencies, ~1.3 KB gzipped
  • TypeScript types included
  • CSS-driven clamping via -webkit-line-clamp or max-height
  • The toggle button is only created when the text actually overflows
  • Accessible by default: aria-expanded and aria-controls on the toggle button

Installation

npm install @frontfactory/readmore

Requires Node.js >=20.19 to build from source. The published package runs in any modern browser supporting ResizeObserver.

Playground

Try it live on StackBlitz — no installation required:

Open in StackBlitz

Or run it locally:

npm run dev

Usage

import { ReadMore } from '@frontfactory/readmore';
import '@frontfactory/readmore/style.css';

// clamp to 3 lines
ReadMore.init('.excerpt', {
    lines: 3
});

// clamp to a fixed pixel height
ReadMore.init('.excerpt', {
    height: 80
});

ReadMore.init() also accepts a NodeList or an array of elements:

ReadMore.init(document.querySelectorAll<HTMLElement>('.excerpt'), {
    lines: 3
});

Or instantiate against a single element:

const el = document.querySelector<HTMLElement>('#bio');

if (el) new ReadMore(el, {
    lines: 4,
    onToggle: (expanded) => console.log('expanded:', expanded)
});

Options

| Option | Type | Default | Description | |-----------------|-------------------------------|------------------|---------------------------------------------------------------------| | lines | number | 3 | Number of lines to clamp to (min 1). Ignored if height is set. | | height | number | — | Max height in pixels (min 1). Takes precedence over lines. | | moreText | string | 'Read more' | Label of the button when the text is collapsed. | | lessText | string | 'Read less' | Label of the button when the text is expanded. | | buttonClass | string | 'readmore-btn' | Class applied to the toggle button. | | expandedClass | string | 'is-expanded' | Class applied to the target element while expanded. | | openingClass | string | 'is-opening' | Transient class applied while expanding, removed after transition. | | closingClass | string | 'is-closing' | Transient class applied while collapsing, removed after transition. | | onToggle | (expanded: boolean) => void | — | Callback fired after each toggle with the current expanded state. |

API

const instance = new ReadMore(element, options);

instance.el;            // the target element
instance.options;       // resolved options
instance.toggle();      // expand / collapse programmatically
instance.expanded;      // boolean getter
instance.destroy();     // remove button, classes, listeners and generated id

ReadMore.init(target, options);   // target: selector | NodeList | HTMLElement[] — returns ReadMore[]
ReadMore.getInstance(element);    // returns the ReadMore bound to an element, or undefined

Errors

The constructor throws instead of failing silently:

| Error | When | |--------------|-------------------------------------------------------------------------------| | TypeError | element is not an HTMLElement. | | Error | The element is already initialized — call destroy() before re-initializing. | | RangeError | lines is lower than 1, or height is lower than or equal to 0. |

Accessibility

When the button is mounted, it gets aria-expanded (kept in sync on every toggle) and aria-controls pointing to the target element. If the element has no id, a unique one (readmore-1, readmore-2, …) is generated and removed again on destroy(); an existing id is always preserved.

How it works

The plugin sets a CSS custom property on the target element and toggles .readmore-clamp. In lines mode (default), it sets --readmore-lines and the bundled stylesheet applies -webkit-line-clamp, producing an ellipsis at the end of the last visible line. In height mode (height option), it sets --readmore-height and applies max-height via the .readmore-clamp--height modifier class instead. The toggle button is mounted right after the element only when the content actually overflows; a ResizeObserver watches the element and unmounts the button again if the text ends up fitting (or mounts it later if it starts overflowing).

Transient classes for animations

In addition to expandedClass (which stays applied for the entire expanded state), the plugin applies short-lived openingClass and closingClass markers on the target element at the moment of expand and collapse respectively. Each is added when the toggle starts and removed automatically once the CSS transition on the element ends — or on the next tick if no transition is declared. Use them as hooks to drive enter/leave animations:

.is-opening { 
    /* expand animation */
}

.is-closing {
    /* collapse animation */ 
}