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

@skagerakenergi/design

v0.39.0

Published

Skagerak Energi design system — M3 component library (CSS only)

Readme

@skagerakenergi/design

Material Design 3 (M3) component library for Skagerak Energi.

This package is CSS-first — most components are pure CSS + your own markup:

  • M3 token files
  • Per-component CSS files
  • One convenience bundle (index.css)

A few overlay components ship optional, tree-shakeable ESM helpers for behavior CSS cannot provide (each with a CSS-only fallback):

  • menu.js, dialog.js, tooltip.js — small JS enhancers (focus management, scroll lock, popover stacking)
  • bottom-sheet.js — the self-registering <se-bottom-sheet> web component (drag/snap/swipe)

Install

pnpm add @skagerakenergi/design

Upgrading

Unreleased (upgrading from 0.36.0 or earlier) — ⚠️ breaking typography change

The baseline Title Medium, Title Small, Label Large, Label Medium, and Label Small type styles were corrected from font-weight 700 to the M3-canonical font-weight 500. The previous 700 value was a bug — it is actually M3's emphasized weight for those roles, not the baseline.

This changes the rendered weight (bold → medium) of:

  • Button and Extended FAB labels (--md-sys-typescale-label-large-weight)
  • Chip labels, Menu items, List item titles (--md-sys-typescale-label-large-weight / -medium-weight)
  • Badge text (--md-sys-typescale-label-small-weight)
  • Navigation Bar / Navigation Rail labels (--md-sys-typescale-label-medium-weight)
  • Tab labels, Extended FAB label fallback, Toolbar/App Bar titles using Title Medium/Small (--md-sys-typescale-title-medium-weight / -small-weight)

No class names, markup, or CSS custom property names changed — only the computed weight value these tokens resolve to. If your product relied on the bold (700) appearance for these roles, override it explicitly:

:root {
  --md-sys-typescale-label-large-weight: 700; /* restore old behavior if needed */
}

See .specs/design-tokens.md for the full corrected type scale table and the planned emphasized type scale (a future opt-in 700-weight variant for these same roles).

Quick start (all styles)

Use index.css to load token files and all components.

@import '@skagerakenergi/design/index.css';
<button class="se-button se-button--filled">Save</button>

This is the fastest option when you want all components and automatic theme handling.

Recommended import (theme + selected components)

Load auto theme, system tokens, and only the components you use.

@import '@skagerakenergi/design/tokens/theme-auto.css';
@import '@skagerakenergi/design/tokens/system.css';
@import '@skagerakenergi/design/button.css';
@import '@skagerakenergi/design/icon-button.css';

Use this mode to keep bundle size smaller by importing only the components you use.

Import only a single component

@import '@skagerakenergi/design/tokens/theme-auto.css';
@import '@skagerakenergi/design/tokens/system.css';
@import '@skagerakenergi/design/chips.css';
<div class="se-chip-set" role="listbox" aria-label="Filter options">
  <button class="se-chip se-chip--filter" aria-pressed="false">Option</button>
</div>

Theming

The package supports six theme variants:

  • light
  • dark
  • light-medium-contrast
  • dark-medium-contrast
  • light-high-contrast
  • dark-high-contrast

Automatic theming (recommended)

theme-auto.css sets light tokens by default and automatically adapts to user OS settings for dark mode and high contrast.

@import '@skagerakenergi/design/tokens/theme-auto.css';
@import '@skagerakenergi/design/tokens/system.css';

theme-auto.css includes:

  • Light defaults on :root
  • Automatic dark mode via prefers-color-scheme: dark
  • Automatic high contrast via prefers-contrast: more
  • Manual override classes (.light, .dark, .light-medium-contrast, .dark-medium-contrast, .light-high-contrast, .dark-high-contrast)

Manual theme swapping

Import explicit scheme files and set a theme class yourself.

@import '@skagerakenergi/design/tokens/light.css';
@import '@skagerakenergi/design/tokens/dark.css';
@import '@skagerakenergi/design/tokens/light-mc.css';
@import '@skagerakenergi/design/tokens/dark-mc.css';
@import '@skagerakenergi/design/tokens/light-hc.css';
@import '@skagerakenergi/design/tokens/dark-hc.css';
@import '@skagerakenergi/design/tokens/system.css';
@import '@skagerakenergi/design/button.css';

Set a theme class on any ancestor element (html, body, or a section wrapper):

<body class="dark">
  <button class="se-button se-button--filled">Save</button>
</body>

Swap class at runtime in your app to change theme.

Bring your own tokens (custom theme)

Skip library color theme files and provide your own --md-sys-color-* values.

/* Your app token file */
:root {
  --md-sys-color-primary: #1976d2;
  --md-sys-color-on-primary: #ffffff;
  --md-sys-color-surface: #fdfcff;
  --md-sys-color-on-surface: #1a1c1e;
  /* Provide the full M3 system color set used by your components */
}

/* Library imports */
@import '@skagerakenergi/design/tokens/system.css';
@import '@skagerakenergi/design/button.css';

Use this when you want your own brand palette while keeping the same component CSS API.

Bring your own tokens + auto-engine (OS preferences without custom media queries)

Use this repository's auto-engine to generate a theme-auto.css from your own Material Theme Builder exports, so you keep automatic dark/high-contrast behavior.

Important:

  • @skagerakenergi/design/tokens/theme-auto.css always points to the package's built-in token set.
  • To use your own token set, import your own generated theme-auto.css from your codebase.

Supported input filenames:

  • light.css
  • dark.css
  • light-mc.css
  • dark-mc.css
  • light-hc.css
  • dark-hc.css

Minimal setup (directory input):

pnpm build:theme-auto -- --source-dir ./brand-tokens

This generates tokens/theme-auto.css using your files when present and falls back to the library defaults for missing variants.

Strict mode (require all 6 custom variants):

pnpm build:theme-auto -- --source-dir ./brand-tokens --strict

Per-file override example:

pnpm build:theme-auto -- --source-dir ./brand-tokens --dark ./brand/dark.css --out ./tokens/theme-auto.css

Then import as usual:

@import './tokens/theme-auto.css';
@import '@skagerakenergi/design/tokens/system.css';
@import '@skagerakenergi/design/button.css';

In short:

  • Import package theme-auto.css when you want the package defaults.
  • Import local theme-auto.css when you want your own upstream tokens.

Customizing components (shape, sizing, spacing)

Beyond color theming, every component exposes its own shape, sizing, and spacing tokens as CSS custom properties, named --se-{component}-{property} (e.g. --se-button-height, --se-card-radius, --se-divider-thickness). These are independent of the color-token system above — use them to tweak an individual component's dimensions or corners without forking its CSS or writing overly-specific selectors.

Each token is declared inline with a fallback to the exact Material Design 3 spec default, so leaving a token unset always renders the canonical M3 value:

/* Give every square button a custom corner radius */
.se-button--square {
  --se-button-square-radius: 4px;
}

/* Make navigation rail wider app-wide */
:root {
  --se-navigation-rail-width: 120px;
}

/* One-off override on a single element */
<div class="se-card" style="--se-card-radius: 24px; --se-card-padding: 24px">
  ...
</div>

Guidelines:

  • Tokens can be set on :root (applies everywhere), on a component's class (applies to every instance of that component), or inline via the style attribute (applies to a single instance) — normal CSS cascade rules apply.
  • Only shape (radius/corner), sizing (height/width/icon size), and spacing (padding/gap/inset) are covered by this tier. Colors, typography, and motion are controlled by the --md-sys-* system tokens described above.
  • Setting a token never changes any other property — overriding --se-button-height doesn't affect padding or icon size, so you can mix and match freely.
  • Every token is also exposed as a live control in each component's Storybook story — open the component's story and use the Controls panel to experiment with values before writing CSS.
  • The full, current token list for every component (names, defaults, descriptions) is documented in INSTRUCTIONS.md under each component's "Customization tokens" section, and the overall token architecture is described in .specs/design-tokens.md.

Icons

Components that display icons expect Material Symbols Outlined to be loaded. The icon font is not bundled — load it yourself so you control which icons are included.

Google Fonts (recommended)

Add a <link> in your <head>. Use the &icon_names= parameter to include only the icons you need (keeps the download small):

<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:[email protected]&icon_names=home,search,menu,close&display=block" rel="stylesheet" />
  • [email protected] — enables the filled/outlined toggle used by navigation components
  • &display=block — prevents a flash of unstyled ligature text
  • &icon_names= — comma-separated list of the icons your app uses

Self-hosting

Download the WOFF2 from google/material-design-icons and add:

@font-face {
  font-family: 'Material Symbols Outlined';
  font-style: normal;
  src: url('./material-symbols-outlined.woff2') format('woff2');
}

.material-symbols-outlined {
  font-family: 'Material Symbols Outlined';
  font-weight: normal;
  font-style: normal;
  font-size: 24px;
  display: inline-block;
  line-height: 1;
  text-transform: none;
  letter-spacing: normal;
  word-wrap: normal;
  white-space: nowrap;
  direction: ltr;
  font-variation-settings: 'FILL' 0;
}

Using icons in markup

<span class="material-symbols-outlined" aria-hidden="true">home</span>

See .specs/icons.md for the full icon catalog and integration details.

Usage patterns

All components

@import '@skagerakenergi/design/index.css';

Selected components

@import '@skagerakenergi/design/tokens/theme-auto.css';
@import '@skagerakenergi/design/tokens/system.css';
@import '@skagerakenergi/design/button.css';
@import '@skagerakenergi/design/card.css';
@import '@skagerakenergi/design/text-field.css';

Manual theme + selected components

@import '@skagerakenergi/design/tokens/light.css';
@import '@skagerakenergi/design/tokens/dark.css';
@import '@skagerakenergi/design/tokens/system.css';
@import '@skagerakenergi/design/navigation-bar.css';
@import '@skagerakenergi/design/switch.css';

Custom tokens + selected components

@import './my-theme.css'; /* provides --md-sys-color-* */
@import '@skagerakenergi/design/tokens/system.css';
@import '@skagerakenergi/design/text-field.css';

Custom auto-theme + selected components (upstream token files)

@import './tokens/theme-auto.css'; /* generated from your own Theme Builder CSS files */
@import '@skagerakenergi/design/tokens/system.css';
@import '@skagerakenergi/design/text-field.css';

Available imports

Token files:

  • @skagerakenergi/design/tokens/theme-auto.css
  • @skagerakenergi/design/tokens/system.css
  • @skagerakenergi/design/tokens/light.css
  • @skagerakenergi/design/tokens/dark.css
  • @skagerakenergi/design/tokens/light-mc.css
  • @skagerakenergi/design/tokens/dark-mc.css
  • @skagerakenergi/design/tokens/light-hc.css
  • @skagerakenergi/design/tokens/dark-hc.css

Component files:

  • app-bar.css, badge.css, button.css, button-group.css, card.css
  • checkbox.css, chips.css, divider.css, extended-fab.css, fab.css
  • icon-button.css, list.css, loading-indicator.css, menu.css, navigation-bar.css
  • navigation-rail.css, progress-indicator.css, radio.css, snackbar.css
  • split-button.css, switch.css, tabs.css, text-field.css, toolbar.css
  • tooltip.css
  • bottom-sheet.css — CSS-only bottom sheet styles (standalone <dialog> usage without web component)
  • dialog.css — Dialog component styles (basic + full-screen variants)
  • layout.css — spacing utilities, breakpoints, canonical layouts (list-detail, supporting pane, feed, split-pane), column grids (.se-columns-2/3/4), auto-fill grid (.se-grid), flex containers (.se-row, .se-column), flex/alignment utilities

Optional JavaScript modules (overlay components):

  • menu.js — keyboard navigation + focus management for Menu (ESM, TypeScript declarations at menu.d.ts)
  • tooltip.jsinitTooltips() enables popover="hint" stacking for fixed tooltips so they render above open popovers (ESM, TypeScript declarations at tooltip.d.ts)
  • dialog.js — open/close animation coordination, scroll lock, focus return for Dialog (ESM, TypeScript declarations at dialog.d.ts)

Web components (self-registering custom elements):

  • bottom-sheet.js<se-bottom-sheet> modal bottom sheet with drag/snap/swipe (ESM, TypeScript declarations at bottom-sheet.d.ts)

Loading indicator shape asset (avoids inlining the large SVG path data):

  • loading-indicator-shapes.svg — import with ?raw in Vite-based projects
  • loading-indicator-shapes.js — ESM export (loadingIndicatorShapesSvg: string) for non-Vite environments

Brand components (not part of M3 — use Skagerak brand palette, no M3 tokens required):

  • brand/logo.css — Skagerak group logo (sk-logo, sk-logo__mark, sk-logo__brand, sk-logo__subsidiary)
  • brand/skagerak.svg — globe emblem used as the sk-logo__mark image in the font-based logo variants (<img src="skagerak.svg">)
  • brand/skagerak_globe_white.svg — monochrome globe emblem (fill="currentColor") for inline embedding in the flat logo variant (sk-logo--flat)
  • brand/skagerak_flat_colorpatch.svg — complete pre-rendered SVG lockup, globe + "SKAGERAK" wordmark (fill="currentColor") for the sk-logo--svg variant
  • brand/skagerak_energitjenester_flat_colorpatch.svg — complete pre-rendered SVG lockup, globe + "Skagerak" + "Energitjenester" (fill="currentColor") for the sk-logo--svg variant
  • brand/lede-logo.css — Lede group logo, a single-colour wordmark (lede-logo, lede-logo__img)
  • brand/lede-wordmark.svg — monochrome Lede wordmark (fill="currentColor") for inline embedding + CSS colour control
  • brand/lede-green-rgb.svg — original Lede logo, dark green #234639 (unmodified, for <img> / download use)
  • brand/lede-white-rgb.svg — original Lede logo, white #ffffff (unmodified, for <img> / download use)
  • brand/lede-black-rgb.svg — original Lede logo, black #303030 (unmodified, for <img> / download use)

Figma Code Connect

This project uses Figma Code Connect to link Figma component instances to the corresponding HTML/CSS snippets so designers see real code in the Figma Dev Mode inspector.

Prerequisites

  • Figma access token — create one at Figma → Settings → Personal access tokens with the minimum scopes used by Code Connect publishing:

    • file_code_connect:write (Write and change component code)
    • file_dev_resources:write
    • file_content:read
    • file_metadata:read

    In the Figma token UI, these are shown under Development and Files with human-readable labels. Use the scope IDs above as the source of truth when documenting automation.

    Export the token as FIGMA_ACCESS_TOKEN:

    export FIGMA_ACCESS_TOKEN=figd_xxxxx
  • @figma/code-connect is already a dev dependency (see package.json).

Project config

figma.config.json at the repo root tells the CLI where to find Code Connect files:

{
  "codeConnect": {
    "include": ["src/components/**/*.figma.ts"],
    "exclude": ["node_modules/**", "dist/**", "storybook-static/**"],
    "label": "HTML"
  }
}
  • include — glob for Code Connect source files.
  • label — the language label shown in Figma Dev Mode (e.g. "HTML").

File naming convention

Each component that has a Code Connect mapping gets a *.figma.ts file alongside its CSS and Storybook files:

src/components/button/
  button.css
  button.stories.ts
  button.figma.ts       ← Code Connect file

Anatomy of a Code Connect file

A Code Connect file imports the HTML helpers and calls figma.connect() once per Figma component set (node). Here is a minimal example:

import figma, { html } from '@figma/code-connect/html'

figma.connect(
  // 1. Figma node URL — right-click a component set in Figma → "Copy link to selection"
  'https://www.figma.com/design/<FILE_KEY>/<FILE_NAME>?node-id=<NODE_ID>',
  {
    // 2. Props — map Figma variant properties to code values
    props: {
      className: figma.className([
        'se-button',
        'se-button--filled',
        figma.enum('Size', {
          XSmall: 'se-button--xs',
          Medium: 'se-button--md',
          Large:  'se-button--lg',
          XLarge: 'se-button--xl'
        })
      ]),
      label: figma.string('Label text'),
      icon: figma.boolean('Show icon', {
        true: html`<span class="se-button__icon material-symbols-outlined">add</span>`,
        false: undefined
      })
    },

    // 3. Example — the HTML snippet shown in Figma Dev Mode
    example: ({ className, label, icon }) => html`\
<button class="${className}">
  ${icon}
  ${label}
</button>`,

    // 4. Imports — CSS import hint displayed alongside the snippet
    imports: ["@import '@skagerakenergi/design/button.css';"]
  }
)

Key prop helpers

| Helper | Purpose | Example | |---|---|---| | figma.className([...]) | Build a class string from static values and variant mappings | figma.className(['se-button', figma.enum('Style', { Filled: 'se-button--filled' })]) | | figma.enum('Prop', map) | Map a Figma enum property to code values | figma.enum('Size', { Small: 'sm', Large: 'lg' }) | | figma.string('Prop') | Pull a text/string property value straight through | figma.string('Label text') | | figma.boolean('Prop', { true, false }) | Map a boolean toggle to different code outputs | figma.boolean('Show icon', { true: html\...`, false: undefined })| |figma.instance('Prop')| Render a nested Figma instance with its own Code Connect |figma.instance('Leading icon')` |

Writing a new Code Connect file step by step

  1. Identify the Figma component set. Open the Figma file, navigate to the component, right-click the top-level component set, and choose Copy link to selection. The URL contains the node-id you need.

  2. Create the file. Add src/components/<name>/<name>.figma.ts.

  3. Map props. Open the component's property panel in Figma to see every variant property (e.g. Size, Type, Style, Show icon). Map each one using the helpers above.

  4. Write the example function. Return an html tagged template that produces the same markup the component's CSS expects. Reference the mapped props by name.

  5. Add imports. List the CSS @import statement(s) a consumer needs.

  6. Handle multiple variants. If the Figma file splits a component into separate component sets (e.g. filled button vs outlined button), add a separate figma.connect() call for each and hardcode the modifier class. See button.figma.ts for a real example.

  7. Validate locally.

    # Parse all Code Connect files and report errors without publishing
    pnpm figma:publish --dry-run
  8. Publish to Figma.

    pnpm figma:publish

    This pushes the snippets to Figma so they appear in Dev Mode for the linked components.

  9. Unpublish (remove all snippets).

    pnpm figma:unpublish

Tips

  • One figma.connect() per component set node. If a component has five style variants as separate nodes in Figma, write five calls.
  • Prop names are case-sensitive and must match the Figma property name exactly (e.g. 'Label text', not 'label text').
  • Use html tagged templates (from @figma/code-connect/html) so Figma renders the snippet with proper syntax highlighting.
  • Backslash after the opening backtick (html\\`) prevents a leading newline in the rendered snippet.
  • imports is an array of strings — each string is shown as-is in the Figma UI. Use the package-level import path (@skagerakenergi/design/...) so consumers see the right import.
  • Keep snippets minimal — show the most common usage; don't try to cover every edge case.
  • Refer to existing files for patterns: button.figma.ts, icon-button.figma.ts.