๐Ÿงฑ Chapter 81: Web Components Architecture

Publishing Web Components to NPM

Distributing production-grade Web Components: Custom Elements Manifest (`custom-elements.json`), modern ESM packaging, TypeScript definitions, and IDE autocomplete.

LEARNING OBJECTIVES โŒต
  • Structure a production-grade Web Component package configured for NPM distribution.
  • Generate and validate the standardized Custom Elements Manifest (custom-elements.json) using @custom-elements-manifest/analyzer.
  • Configure package.json with standard exports maps, "customElements", and TypeScript types fields.
  • Enable automated IDE autocomplete (VS Code / WebStorm) and Storybook documentation directly from source code annotations.
๐ŸŽฌ INTERACTIVE VISUAL PIPELINE Core Architecture Simulation
๐ŸŒ
1. Input
Directives & Tags
โš™๏ธ
2. Parse
Tokenizer & AST
๐ŸŒณ
3. Layout
Box Model & Flow
๐ŸŽจ
4. Render
GPU Paint & Composite
PHASE 1: INPUT & DIRECTIVES
Browser receives declarative markup stream, parsing tag tokens and initializing component state.

๐Ÿ“– The Mental Model & Story (Intuitive Foundation)

Imagine a specialized semiconductor manufacturer manufacturing high-precision microcontrollers.

When they ship thousands of microcontrollers to circuit board designers, they don't just dump a bag of raw black silicon chips on the loading dock. They include an authoritative Standard Datasheet (Pinout Diagram) detailing:

  • Exact pin voltages (Properties & Attributes)
  • Output signal frequencies (Custom Events)
  • Mounting dimensions and socket connectors (Slots & Parts)
+-------------------------------------------------------------------------------+
|                       THE UNIVERSAL COMPONENT DATASHEET                       |
+-------------------------------------------------------------------------------+
| SOURCE CODE WITH JSDOC ANNOTATIONS:                                           |
|   /**                                                                         |
|    * @tag ui-button                                                           |
|    * @attr {string} variant - Visual style variant                            |
|    * @fires {CustomEvent} ui-click - Dispatched when button activates        |
|    * @cssprop --btn-bg - Background color token                               |
|    */                                                                         |
|                                       |                                       |
|                                       v                                       |
|                      [@custom-elements-manifest/analyzer]                     |
|                                       |                                       |
|                                       v                                       |
|               STANDARDIZED `custom-elements.json` MANIFEST                    |
|                                       |                                       |
|         +-----------------------------+-----------------------------+         |
|         |                             |                             |         |
|         v                             v                             v         |
|   VS Code IntelliSense     Storybook Automated Docs      React / Angular Wrappers
+-------------------------------------------------------------------------------+

The Custom Elements Manifest (CEM) is the official open W3C Community Group standard datasheet for Web Components. When you publish a component accompanied by custom-elements.json, IDEs, documentation engines, and framework compilers instantly understand how to provide autocomplete, type checking, and visual playgrounds.


Technical Deep Dive & Specifications

The Custom Elements Manifest (custom-elements.json)

The Custom Elements Manifest is a standardized JSON format describing all custom elements in a package:

{
  "schemaVersion": "1.0.0",
  "readme": "",
  "modules": [
    {
      "kind": "javascript-module",
      "path": "src/components/metric-card.js",
      "declarations": [
        {
          "kind": "class",
          "name": "MetricCard",
          "tagName": "metric-card",
          "description": "Displays an enterprise analytical metric card with trend indicators.",
          "attributes": [
            {
              "name": "value",
              "type": { "text": "string" },
              "description": "The numeric metric value to display."
            },
            {
              "name": "trend",
              "type": { "text": "'up' | 'down' | 'neutral'" },
              "default": "'neutral'",
              "description": "The directional trajectory of the metric."
            }
          ],
          "events": [
            {
              "name": "metric-refresh",
              "type": { "text": "CustomEvent<void>" },
              "description": "Fires when user clicks the refresh icon."
            }
          ],
          "slots": [
            {
              "name": "",
              "description": "Default slot for sub-metric descriptive text."
            }
          ],
          "cssParts": [
            {
              "name": "container",
              "description": "The main card wrapper div."
            }
          ],
          "cssProperties": [
            {
              "name": "--metric-accent",
              "description": "Accent border and highlight color.",
              "default": "#3b82f6"
            }
          ]
        }
      ]
    }
  ]
}

Modern package.json Distribution Architecture

A modern Web Component library should deliver pure ES Modules (ESM) and register its manifest so the ecosystem can discover it:

{
  "name": "@acme/design-system",
  "version": "1.0.0",
  "type": "module",
  "main": "./dist/index.js",
  "module": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "customElements": "custom-elements.json",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.js"
    },
    "./button": {
      "types": "./dist/components/button/button.d.ts",
      "import": "./dist/components/button/button.js"
    },
    "./card": {
      "types": "./dist/components/card/card.d.ts",
      "import": "./dist/components/card/card.js"
    },
    "./custom-elements.json": "./custom-elements.json"
  },
  "files": [
    "dist",
    "custom-elements.json"
  ],
  "scripts": {
    "build": "tsc && cem analyze --litelement",
    "analyze": "cem analyze --litelement"
  },
  "peerDependencies": {
    "lit": "^3.0.0"
  },
  "devDependencies": {
    "@custom-elements-manifest/analyzer": "^0.9.0",
    "typescript": "^5.3.0"
  }
}

๐Ÿ’ป Interactive Code Playground

Here is a fully documented, production-grade custom element (<metric-card>) annotated with standard JSDoc tags ready for @custom-elements-manifest/analyzer extraction.

Starter Code

Line-by-Line Code Breakdown

  • Lines 44โ€“58: Standard JSDoc annotations. The CEM analyzer parses @tag, @attr, @slot, @csspart, @cssprop, and @fires to build the machine-readable custom-elements.json.
  • Line 60: export class MetricCard extends HTMLElement: Standard ES module class export for npm bundling.
  • Line 72: refresh-requested custom event is documented in the JSDoc header and dispatched with { bubbles: true, composed: true }.
  • Line 99: border-left: 4px solid var(--metric-accent, #3b82f6): Provides token customizability, documented in @cssprop.

Expected Browser Render Output

Two polished analytical metric cards display:

  1. Monthly Recurring Revenue: Green up arrow โ–ฒ +14.2% and Blue accent bar.
  2. API Error Rate: Red down arrow โ–ผ -0.02% with customized Emerald accent bar via --metric-accent: #10b981.

SYS: ACTIVE
HULL: 98%
CORE: STABLE
NET: ONLINE
HTML STARSHIP CODE TERMINAL playground.html
LIVE RENDER & DIAGNOSTICS CORE TEMP: 45ยฐC
INSPECTING DOM: VALID
TAGS: SCANNING...

๐Ÿ‹๏ธ Hands-On Exercise

๐ŸŽฏ The Challenge: Package & Document <color-swatch>

Build a documented <color-swatch> component with complete JSDoc annotations and write the corresponding custom-elements.json JSON block.

Instructions:

  1. Implement class ColorSwatch with attributes: hex (color string) and name (label).
  2. Document @tag, @attr, @fires color-selected, and @csspart swatch-box.
  3. When clicked, copy the hex code to clipboard (navigator.clipboard.writeText) and dispatch CustomEvent('color-selected', { detail: { hex, name } }).
  4. Provide the exact JSON declaration node that @custom-elements-manifest/analyzer will generate.

๐Ÿ Starter Code Sandbox

SYS: ACTIVE
HULL: 98%
CORE: STABLE
NET: ONLINE
STARTER CODE SANDBOX exercise.html
LIVE RENDER & DIAGNOSTICS CORE TEMP: 45ยฐC
INSPECTING DOM: VALID
TAGS: SCANNING...

โš ๏ธ Common Pitfalls

  1. Bundling Peer Dependencies into the NPM Bundle: If your component uses Lit and you bundle Lit inside dist/index.js, downstream applications that also use Lit will load two separate copies of the Lit runtime, bloating bundles and breaking instanceof checks. Always declare "lit" in "peerDependencies" or "dependencies", never bundle it as an inlined dependency.
  2. Missing "customElements" in package.json: Forgetting to add "customElements": "custom-elements.json" in your package.json root prevents tools like Storybook, VS Code Custom Data, and WebStorm from auto-discovering your component manifest.

๐Ÿ’ก Pro Tips

  1. Subpath Exports for Granular Tree-Shaking: Configure exports in package.json so consumers can import individual elements without loading the whole suite:
    import '@acme/ui/button.js'; // Imports only 2 KB button code
    
  2. VS Code HTML Custom Data Generation: Use @custom-elements-manifest/to-vscode to generate a vscode-html-custom-data.json file. Adding this to your repository gives all VS Code users rich attribute suggestions and documentation tooltips when authoring HTML.

๐Ÿ“Œ Key Takeaways

  • The Custom Elements Manifest (custom-elements.json) is the standardized W3C JSON schema describing custom elements, properties, slots, events, and CSS tokens.
  • Use @custom-elements-manifest/analyzer to extract documentation automatically from JSDoc and TypeScript source code.
  • Always declare "customElements": "custom-elements.json" in package.json.
  • Configure fine-grained subpath exports in package.json to enable optimal consumer tree-shaking.
  • Declare shared libraries (e.g. Lit) as peerDependencies or external modules to avoid duplicate runtime bundling.
  • --
โญ LEARN: HTML ๐ŸŒŸ โš”๏ธ QUIZ BATTLE ARENA // ACTIVE
3x
STREAK!
BONUS ACTIVE
COMBO
? Question 1 / 3

What is the purpose of the custom-elements.json manifest file?

Question 1 / 3 Topic: HTML Fundamentals
00:45 REMAINING
XP REWARD
+250 XP
3x
STREAK!
BONUS ACTIVE
COMBO
? Question 2 / 3

Why is it an anti-pattern to bundle and inline lit inside your published NPM package distribution files?

Question 2 / 3 Topic: HTML Fundamentals
00:45 REMAINING
XP REWARD
+250 XP
3x
STREAK!
BONUS ACTIVE
COMBO
? Question 3 / 3

Which package.json property allows modern bundlers to discover the Custom Elements Manifest automatically?

Question 3 / 3 Topic: HTML Fundamentals
00:45 REMAINING
XP REWARD
+250 XP