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

satteri-autolink-paragraphs

v0.3.0

Published

A Satteri plugin to add permalink anchors to top-level paragraphs.

Readme

satteri-autolink-paragraphs

Satteri plugin to add permalink anchors to top-level paragraphs. It is modeled on rehype-autolink-headings, but only targets p elements that are direct children of the document root.

What's this?

This package adds stable fragment IDs and accessible permalink anchors to top-level paragraphs:

<p id="paragraph-1">
  First paragraph. <a class="autolink-paragraph" aria-label="Link to this paragraph" href="#paragraph-1">¶</a>
</p>

Nested paragraphs in blockquotes, lists, footnotes, and other containers are left unchanged. Paragraphs can contain ordinary links: the permalink is inserted alongside their content instead of wrapping it, which avoids invalid nested anchors.

Install

npm install satteri-autolink-paragraphs
yarn add satteri-autolink-paragraphs
pnpm add satteri-autolink-paragraphs

Use

import { markdownToHtml } from "satteri";
import satteriAutolinkParagraphs from "satteri-autolink-paragraphs";

const { html } = markdownToHtml("First paragraph.", {
  hastPlugins: [satteriAutolinkParagraphs()],
});

console.log(html);

The default behavior appends a visible link, assigns the class autolink-paragraph and accessible label Link to this paragraph, and generates IDs such as paragraph-1.

[!IMPORTANT] When using satteri-figure, put satteriFigure() before this plugin in hastPlugins. This lets image-only paragraphs become figures before paragraph links are added.

API

The default export is satteriAutolinkParagraphs, a function returning a HAST plugin definition to pass to hastPlugins.

import satteriAutolinkParagraphs, {
  type SatteriAutolinkParagraphsOptions,
} from "satteri-autolink-paragraphs";

const options: SatteriAutolinkParagraphsOptions = {
  behavior: "prepend",
  prefix: "note-",
  paragraphProperties: { className: ["group"] },
};

const plugin = satteriAutolinkParagraphs(options);

Its main TypeScript API is:

type Behavior = "prepend" | "append" | "before" | "after";

interface ParagraphLinkInfo {
  id: string;
  index: number;
}

interface SatteriAutolinkParagraphsOptions {
  behavior?: Behavior;
  prefix?: string;
  content?: HastContent | HastContent[] | BuildContent;
  properties?: HastProperties | BuildProperties;
  paragraphProperties?: HastProperties | BuildProperties;
  test?: ParagraphTest;
}

All options are optional:

  • behavior: place the link inside the paragraph at the start (prepend) or end (append), or as a root-level sibling (before or after). Default: append.
  • prefix: prefix for generated IDs. Default: paragraph-.
  • content: static HAST link content or a builder receiving the readonly paragraph and its { id, index }. Default: the text .
  • properties: static HAST properties or a builder receiving the readonly paragraph and its { id, index }. Values override the default class and accessible label; the generated href always wins.
  • paragraphProperties: static HAST properties or a builder receiving the readonly paragraph and its { id, index }. Existing paragraph properties are preserved unless overridden; the authored or generated id always wins.
  • test: synchronous predicate evaluated against each top-level paragraph before numbering. Skipped paragraphs do not consume an index.

Non-empty authored paragraph IDs are preserved. Generated IDs are checked against every ID in the HAST; collisions gain a suffix such as paragraph-1-2. Duplicate authored IDs remain unchanged, but the plugin reports a warning because the fragment target is ambiguous.

There is deliberately no wrap behavior because paragraphs may already contain links, and wrapping them would create invalid nested anchors.

Show the permalink on paragraph hover with Tailwind CSS

Give each linked paragraph Tailwind's group class, then reveal the permalink when that group is hovered or contains keyboard focus:

satteriAutolinkParagraphs({
  behavior: "append",
  paragraphProperties: {
    className: ["group"],
  },
  properties: {
    className: [
      "opacity-0",
      "transition-opacity",
      "group-hover:opacity-100",
      "group-focus-within:opacity-100",
    ],
  },
});

Use append or prepend for this pattern. The before and after behaviors make the permalink a sibling of the paragraph, so group-hover cannot target it.

Development

From the workspace root:

  • Run pnpm build to compile the package to dist/
  • Run pnpm typecheck to type-check the implementation and tests
  • Run pnpm test to run the behavior tests
  • Run pnpm lint to run package lint scripts
  • Run pnpm format to run package formatting scripts

License

MIT