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

@pagix/series-nav

v0.1.0

Published

Anvilkit Puck-native PLP series navigation — 6-card horizontal grid with image + title + link, no carousel/popup.

Downloads

139

Readme

@pagix/series-nav

Puck-native PLP series navigation — a strip of six product-series cards (square image + series name), each one clickable. No carousel, no popup, no title: the whole card is the link.

Replica of the Huawei PLP top series nav (#mate / #pura / #pocket / #nova / #changxiang / accessories).

Install

pnpm add @pagix/series-nav

Peer deps: @anvilkit/ui, @puckeditor/core, lucide-react, react, react-dom.

Usage

import { createSeriesNavConfig } from "@pagix/series-nav";
import "@pagix/series-nav/styles.css";

const config = createSeriesNavConfig();
// config.render({ id: "node-1", ...config.defaultProps })

The static componentConfig export is the English default; createSeriesNavConfig({ locale, messages }) returns a localized copy for the Puck inspector.

Props

| Prop | Type | Default | Notes | | --- | --- | --- | --- | | cards | SeriesCard[] | six Huawei series | One card per series. | | navAriaLabel | string | "Series navigation" | Accessible name for the <nav>; localized by the factory. | | containerWidth | "design" \| "sm" \| "md" \| "lg" \| "xl" \| "full" | "design" | Overall width — see Layout. | | containerAlign | "left" \| "center" \| "right" | "left" | Horizontal placement of the width-capped box. | | gap | "design" \| "sm" \| "md" \| "lg" \| "xl" | "design" | Space between cards. | | classNames | Record<string, string> | — | Tailwind passthrough per style target. | | animation | AnimationProps | — | Entrance animation (preset / durationMs / delayMs / easing). |

animation: { preset: "none" } (and an unset value) emits no attributes at all. Keyframes live in this package's styles.css behind prefers-reduced-motion: no-preference.

SeriesCard

interface SeriesCard {
	image: { src: string; alt: string };
	title: string;
	link?: string;
	openInNewPage?: boolean;
}

image.src empty renders a muted placeholder square rather than a broken <img>. image.alt empty falls back to title.

Layout

All three layout props default to the source design, so an untouched component renders the SPEC's own ladder exactly. Each is an inspector control in the Properties panel.

containerWidth — "design" keeps the SPEC ladder (full-bleed with container padding, then max-width: 1760px centred from 1920 up). Every other tier pins an inline max-width at all breakpoints, which also replaces the 1920 rule:

| Value | max-width | | --- | --- | | design | the ladder above (no inline style) | | sm / md / lg / xl | 640 / 768 / 1024 / 1200px | | full | 100% |

The sm–xl tiers are the same px values as centered-hero's containerWidth, so the two components share one width vocabulary.

containerAlign — mr-auto / mx-auto / ml-auto. With the default design width the box is already full-bleed, so alignment only becomes visible once a width tier frees up space; at 1920+ the design ladder applies its own mx-auto, which stays in charge.

gap — "design" keeps the SPEC ladder (8 → 12 → 16 → 20 → 24px); the named tiers flatten it to one step at every breakpoint (1→2 / 3→4 / 5→6 / 8→10). The gap belongs to the row, so one value spaces all six cards equally.

Responsive breakpoints

Mobile-first; the six fixed-width cards cannot fit one viewport below md, so the track scrolls horizontally there and becomes six equal columns from md up.

| Breakpoint | Card width | Text | Gap | Container padding | | --- | --- | --- | --- | --- | | < 480 | 110px fixed | 12px | 8px | 16px | | 480–767 | 130px fixed | 14px | 12px | 16px | | 768–1079 | six equal columns | 14px | 16px | 40px | | 1080–1440 | six equal columns | 16px | 20px | 80px | | 1441+ | six equal columns | 16px | 24px | 80px | | 1920+ | six equal columns | 16px | 24px | 0 (centred, max-width 1760) |

The last two columns are what the design values of gap and containerWidth resolve to; see Layout for the overrides.

Links

  • #anchor → in-page jump. Never gets target="_blank", even if openInNewPage is left on — a new tab for an anchor is always a bug.
  • /path → page link. Opens in a new tab only when openInNewPage is explicitly true (then with rel="noopener noreferrer").
  • No link at all → the card renders without an href.

Image field

image is an object field containing a custom src control (URL input + device upload + thumbnail preview) and a text alt control. Uploads are stored as data URLs in the document payload — fine for authoring; the production upload pipeline is @anvilkit/plugin-asset-manager.

The custom sub-field renders its own title through the injected Puck Label when the host provides one, and the array-item panel wraps its own FieldLabel instead — so no duplicate title appears in either position.

Style targets

Five targets, all responsive, stamped in edit and production so the shared style compiler can materialize authored CSS:

root (the <nav>) · track (the flex container) · card (one stamp per card) · card-img (the square wrapper) · card-text (the series name).

Each target grants a slice of the authorable-style vocabulary. Two consumers read that one declaration: the style inspector builds its controls from the grant, and the compiler's allowlist filter decides what survives into CSS — so nothing granted can go unrendered.

In the studio app in this repo those controls are not reachable yet. The chrome mounts only the Properties panel (StudioLayout.tsx renders <Puck.Fields /> in #ak-inspector-panel, with no tabs); the tabbed StylePanel / StudioPuckLayout are exported from @anvilkit/core but no app here mounts them. The grants remain the contract for hosts that do, and the containerWidth / containerAlign / gap fields above cover the same ground from the Properties panel today.

| Target | Use it to set | | --- | --- | | root | The module's own box — position, inset, width/height (+ min/max), margin, padding, background, radius. | | track | The row: gap is the spacing between cards; rowGap/columnGap space a wrapped row's two axes. | | card | One card's box and spacing — margin is each card's outer spacing, gap is the image↔title spacing inside it. The stamp is identical on all six, so a change applies to every card at once. | | card-img | The square wrapper — size, radius, overflow. | | card-text | The series name — typography. |

Two rules worth knowing:

  • width/height on root also unlock the canvas resize handles. The handle layer answers its eligibility question from the root target's grant (CanvasHandles.tsx), so granting them is what puts a resize grip on the block. Drag-to-move is not part of it: the repo ships no move handle for position: absolute nodes yet, so placement is set from the position/inset controls in the Style tab.
  • root withholds zIndex. This target is the component root, so its stacking would escape into the page and change its relationship to elements the author never selected (ADR 0007 decision 5 — the same rule slider and tooltip follow).

Accessibility

  • The root is a <nav> landmark with an aria-label.
  • There is no heading — this module has no title, per the source design.
  • Cards are real <a> elements, so they are keyboard reachable and show a visible focus ring.

editMode

All interactions are disabled: each card renders as a <span> instead of an <a>, so no link is clickable while editing. Style-target stamps are still emitted, and an empty cards array shows an editor-only "add cards" hint (production renders nothing there).

i18n

Ships en / zh / ja / ko catalogs at i18n/messages/*.json with flat series-nav.* keys. Only editor chrome is translated (field labels, option labels, the aria name, the empty hint); defaultProps page content stays as authored.

createSeriesNavConfig({ locale: "zh" });
createSeriesNavConfig({ messages: { "series-nav.label": "OVERRIDE" } });

Unknown locales and missing keys fall back to English.

Independence

This package shares no field factories with its four sibling replicas (plp-product-gallery, focused-product, feature-image-text, feature-highlights-gallery). custom-fields.tsx and authoring.ts are private copies by design — see the family README §0.