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

@psu-flex/chrome-elements

v1.0.4

Published

Vanilla HTML / CSS / JS for PSU chrome (brand bar, mega menu header, site footer, brand footer).

Readme

@psu-flex/chrome-elements

Vanilla HTML / CSS / JS for PSU chrome (brand bar, mega menu header, site footer, brand footer).

No React, no Contentful client, no fetch. Render needs a props object — not a live API call. You can:

  1. Build that object yourself (static JSON, CMS, your own backend), or
  2. Pass a raw fetchAllFederatedData payload and let map-props convert it.

Same render functions power both internal Next.js (document CSS, no shadow DOM) and external custom elements (shadow DOM via @psu-flex/core-ui-federated-wc).

Package split

| Package | Role | | ------- | ---- | | @psu-flex/chrome-elements | Render, map-props, behavior, types, CSS. Optional custom-element wrappers. | | @psu-flex/core-ui-federated-wc | Registers <psu-*> tags for Angular / Drupal / static HTML. | | @psu-flex/wc-styles | Document fonts for external hosts (@font-face cannot live in shadow DOM). |

Internal Flex Next.js apps should use BrandBar.server / BrandBar.ctf (and Footer / BrandFooter / MegaMenuHeader equivalents) in @psu-flex/core-ui-federated and @psu-flex/core-ui-layout-navigation, not the custom elements. *New.server / *New.ctf still work as aliases.


Hydrate without the API

Each component has a mapped props type. If you assign that shape, map-props returns it unchanged and render* paints the chrome.

How the mapper decides:

  • Payload has __typename or sys → treated as raw Contentful / fetchAllFederatedData.
  • Otherwise, if the mapped fields are present (see below) → used as-is.

Do not put __typename or sys on a hand-built object, or it will be treated as raw CMS data.

Next.js / React — custom data, no API

Flex Next.js apps should not use <psu-*> custom elements. Pass mapped props into the .server wrappers (BrandBar.server, MegaMenuHeader.server, Footer.server, BrandFooter.server). They SSR the HTML, attach behavior on the client, and import component CSS. Theme tokens still load on the document.

*New.server paths still work; they are aliases of the names below.

app/layout.tsx:

import '@psu-flex/platform-theme/global.css';
import '@psu-flex/platform-theme/theme-variables.css';
import '@psu-flex/platform-theme/theme-utilities.css';

import { BrandBarServer } from '@psu-flex/core-ui-federated/BrandBar.server';
import { BrandFooterServer } from '@psu-flex/core-ui-federated/BrandFooter.server';
import { MegaMenuHeaderServer } from '@psu-flex/core-ui-layout-navigation/MegaMenuHeader.server';
import { FooterServer } from '@psu-flex/core-ui-layout-navigation/Footer.server';
import type {
  BrandBarProps,
  MegaMenuHeaderProps,
  FooterProps,
  BrandFooterProps,
} from '@psu-flex/chrome-elements';

const brandBar: BrandBarProps = {
  isExternal: false,
  searchAction: '/search',
  brandBarHeading: 'Search this site',
  ctaItems: [{ label: 'Apply', url: '/admission' }],
  flyoutHelpfulLinksHeading: 'Helpful links',
  flyoutHelpfulLinksItems: [{ label: 'Academic calendar', url: '/calendar' }],
  flyoutInformationLinksHeading: 'Information for',
  flyoutInformationLinksItems: [{ label: 'Current students', url: '/students' }],
};

const header: MegaMenuHeaderProps = {
  tierTwoEntity: { label: 'College of Earth and Mineral Sciences', url: '/' },
  ctaButtonsCollection: [{ label: 'Apply', url: '/admission' }],
  navigationCollection: [
    {
      label: 'Academics',
      url: '/academics',
      referenceCollection: {
        items: [
          {
            linkContentCollection: {
              items: [
                { type: 'Heading Link', label: 'Undergraduate', url: '/academics/undergraduate' },
              ],
            },
          },
        ],
      },
    },
  ],
};

const footer: FooterProps = {
  institutionName: 'Penn State',
  departmentName: 'Department of Geosciences',
  streetAddress: '503 Deike Building',
  city: 'University Park',
  state: 'PA',
  zipCode: '16802',
  contactItems: [{ label: 'Phone', href: 'tel:8148656711', value: '814-865-6711' }],
  navItems: [
    {
      label: 'Explore',
      linkItemsCollection: { items: [{ title: 'About', to: '/about' }] },
    },
  ],
};

const brandFooter: BrandFooterProps = {
  linkItemsCollection: [{ label: 'Privacy', url: 'https://www.psu.edu/privacy' }],
};

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <BrandBarServer props={brandBar} />
        <MegaMenuHeaderServer props={header} />
        <main>
          <a id="main-content" tabIndex={-1} />
          {children}
        </main>
        <FooterServer props={footer} />
        <BrandFooterServer props={brandFooter} />
      </body>
    </html>
  );
}

That is the same markup Flex apps use with Contentful (BrandBar.ctf, etc.), except you skip *.ctf and pass your own objects. Field lists: Mapped props contracts.

If you have raw federated JSON instead of mapped props, map once then pass the result:

import { mapBrandBarProps } from '@psu-flex/chrome-elements/BrandBar.map-props';

<BrandBarServer props={mapBrandBarProps(rawBrandBar)} />

1. Assign props on a custom element

Works after definePsuBrandBar() (or defineAllPsuElements() from federated-wc):

import { definePsuBrandBar } from '@psu-flex/chrome-elements/BrandBar';
import type { BrandBarProps } from '@psu-flex/chrome-elements';

definePsuBrandBar();

const el = document.querySelector('psu-brand-bar') as HTMLElement & {
  props: BrandBarProps;
};

el.props = {
  isExternal: true,
  brandBarHeading: 'Search Penn State',
  ctaItems: [{ label: 'Apply', url: 'https://www.psu.edu/admission' }],
  flyoutHelpfulLinksHeading: 'Helpful links',
  flyoutHelpfulLinksItems: [
    { label: 'Academic calendar', url: 'https://www.psu.edu/academic-calendar' },
  ],
  flyoutInformationLinksHeading: 'Information for',
  flyoutInformationLinksItems: [
    { label: 'Current students', url: 'https://www.psu.edu/current-students' },
  ],
  extraSearchOptions: [
    {
      label: 'All Penn State',
      value: 'All Penn State',
      slug: 'https://www.psu.edu/search',
    },
  ],
};

The same pattern applies to header, footer, and brand footer (el.props = mapped).

2. Call render + behavior directly (no custom element)

Use this in a host that already owns a container (Next.js .server wrappers, tests, static HTML):

import { renderBrandBar } from '@psu-flex/chrome-elements/BrandBar.render';
import { initBrandBar } from '@psu-flex/chrome-elements/BrandBar.behavior';
import type { BrandBarProps } from '@psu-flex/chrome-elements';

const props: BrandBarProps = {
  isExternal: true,
  ctaItems: [{ label: 'Apply', url: 'https://www.psu.edu/admission' }],
};

const root = document.querySelector('#chrome-brand-bar') as HTMLElement;
renderBrandBar(root, props);
initBrandBar(root);

Load component CSS on the document for this path:

import '@psu-flex/chrome-elements/BrandBar.css';
// plus theme tokens in the host app:
import '@psu-flex/platform-theme/theme-variables.css';

3. data attribute / hydrateChromeElementData

Serialized JSON of either mapped props or a raw federated payload:

import { hydrateChromeElementData } from '@psu-flex/chrome-elements/hydrate';

hydrateChromeElementData(document.querySelector('psu-brand-bar'), mappedBrandBarProps);

Mapped props contracts

Import types from @psu-flex/chrome-elements. Collections on mapped objects are arrays, not Contentful { items: [] } wrappers — except where the type still nests *.items (header columns, footer nav groups).

Brand bar — BrandBarProps

Detected as mapped when ctaItems is an array.

| Field | Type | Notes | | ----- | ---- | ----- | | ctaItems | { label, url }[] | Top-bar links | | brandBarHeading | string | Flyout heading | | flyoutHelpfulLinksHeading / flyoutHelpfulLinksItems | string / { label, url }[] | | | flyoutInformationLinksHeading / flyoutInformationLinksItems | string / { label, url }[] | | | isExternal | boolean | Custom elements default to true (search → psu.edu). Internal Next sets false. | | searchAction | string | Override form action. Omit on external hosts. | | currentSiteSearchOption | boolean | Show “Current Site” radio (default true) | | extraSearchOptions | { label, value, slug?, queryParamName?, urlToInclude? }[] | Extra radios |

Mega menu header — MegaMenuHeaderProps

Detected as mapped when navigationCollection is an array.

import type { MegaMenuHeaderProps } from '@psu-flex/chrome-elements';

const header: MegaMenuHeaderProps = {
  tierOneMark: { url: '/wordmark.svg' },
  tierTwoEntity: { label: 'College of Earth and Mineral Sciences', url: '/' },
  tierThreeDepartment: { label: 'Geosciences', url: '/geosciences' },
  ctaButtonsCollection: [{ label: 'Apply', url: 'https://www.psu.edu/admission' }],
  navigationCollection: [
    {
      label: 'Academics',
      url: '/academics',
      referenceCollection: {
        items: [
          {
            linkContentCollection: {
              items: [
                { type: 'Heading Link', label: 'Undergraduate', url: '/academics/undergraduate' },
                { type: 'External URL', label: 'Bulletin', url: 'https://bulletins.psu.edu' },
              ],
            },
          },
        ],
      },
    },
  ],
};

Site footer — FooterProps

Detected as mapped when navItems or contactItems is an array.

import type { FooterProps } from '@psu-flex/chrome-elements';

const footer: FooterProps = {
  tierOneMark: { url: '/wordmark.svg' },
  tierTwoEntity: { title: 'College of Earth and Mineral Sciences', href: '/' },
  departmentName: 'Department of Geosciences',
  institutionName: 'Penn State',
  streetAddress: '503 Deike Building',
  city: 'University Park',
  state: 'PA',
  zipCode: '16802',
  contactItems: [
    { label: 'Phone', href: 'tel:8148656711', value: '814-865-6711' },
    { label: 'Email', href: 'mailto:[email protected]', value: '[email protected]' },
  ],
  socialIcons: [{ label: 'Facebook', href: 'https://facebook.com/pennstate' }],
  navItems: [
    {
      label: 'Explore',
      linkItemsCollection: {
        items: [
          { title: 'About', to: '/about' },
          { title: 'News', to: '/news' },
        ],
      },
    },
  ],
};

Brand footer — BrandFooterProps

Detected as mapped when linkItemsCollection is an array.

import type { BrandFooterProps } from '@psu-flex/chrome-elements';

const brandFooter: BrandFooterProps = {
  linkItemsCollection: [
    { label: 'Privacy', url: 'https://www.psu.edu/privacy' },
    { label: 'Non-discrimination', url: 'https://www.psu.edu/non-discrimination' },
  ],
};

Optional: map a federated API payload

If you already have fetchAllFederatedData JSON, pass it through the mapper (or assign it as props / data and let the custom element map it):

import { mapBrandBarProps } from '@psu-flex/chrome-elements/BrandBar.map-props';
import { mapMegaMenuHeaderProps } from '@psu-flex/chrome-elements/MegaMenuHeader.map-props';
import { mapFooterProps } from '@psu-flex/chrome-elements/Footer.map-props';
import { mapBrandFooterProps } from '@psu-flex/chrome-elements/BrandFooter.map-props';

const brandBar = mapBrandBarProps(apiJson.brandBar);
const header = mapMegaMenuHeaderProps(apiJson.header);
const footer = mapFooterProps(apiJson.footer);
const brandFooter = mapBrandFooterProps(apiJson.brandFooter);

Endpoints (optional convenience, not required):


Exports (per component)

Canonical names (no New). The *New subpaths still resolve to the same files.

| Subpath | Purpose | | ------- | ------- | | ./BrandBar.render (and Header / Footer / BrandFooter) | render*(el, mappedProps) | | ./BrandBar.map-props | Raw CMS/API → mapped props (mapBrandBarProps) | | ./BrandBar.behavior | Click / keyboard / search after render | | ./BrandBar.element | Shadow-DOM custom element class | | ./BrandBar | definePsuBrandBar() registrar | | ./BrandBar.css | Document CSS (Next.js / non-shadow) | | ./BrandBarTypes | Prop types (BrandBarProps) | | ./hydrate | hydrateChromeElementData | | ./fonts | injectPsuFonts() | | ./document.css | All four component stylesheets |

@psu-flex/core-ui-federated/BrandBar and /BrandFooter are still the legacy Theme UI components. Use BrandBar.server / BrandBar.ctf (and the Footer / BrandFooter equivalents) for this chrome.


Related