CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-storybook--addon-measure

Visual measurement and box model inspection tool for Storybook development (deprecated in v9.0+)

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

Storybook Addon Measure

Storybook Addon Measure is a visual measurement and box model inspection tool for Storybook that enables developers to inspect layouts and visualize CSS box models by hovering over DOM elements and pressing keyboard shortcuts.

⚠️ DEPRECATION NOTICE: This package is deprecated in Storybook 9.0+. The functionality has been moved to the core Storybook package and is now available by default without requiring separate installation.

Package Information

  • Package Name: @storybook/addon-measure
  • Package Type: npm
  • Language: TypeScript
  • Installation: npm install @storybook/addon-measure (deprecated - throws error in v9.0+)
  • Migration: Remove from dependencies - functionality included in Storybook 9.0+ core

Core Imports

Deprecated package (v9.0+):

// These imports throw migration errors in v9.0+:
// Error: Your Storybook project is referring to package @storybook/addon-measure, 
// which no longer exists in Storybook 9.0 and above.
import { withMeasure } from "@storybook/addon-measure";

Current usage (Storybook 9.0+ core):

// Functionality is now available automatically in Storybook core
// No imports needed - the measure decorator is included automatically when FEATURES.measure is enabled
// Access through Storybook's internal APIs if needed for custom implementation:
import { useGlobals } from 'storybook/manager-api';
import type { MeasureParameters } from 'storybook/internal/types';

Basic Usage

Legacy usage (pre-v9.0):

// In .storybook/main.js - NO LONGER WORKS
export default {
  addons: ['@storybook/addon-measure'], // This will cause errors in v9.0+
};

Current usage (v9.0+):

// No configuration needed - measure is included in core automatically
// Usage:
// 1. Press 'M' key to enable measurement mode (or click the ruler icon in toolbar)
// 2. Hover over any DOM element in your story
// 3. View margin, padding, border, and content dimensions with color-coded overlays

// To disable measure for specific stories:
export default {
  parameters: {
    measure: {
      disable: true, // Disable measure for this component/story
    },
  },
};

Architecture

The measure system (now part of Storybook core) consists of several key components:

  • Feature Flag System: Controlled by globalThis.FEATURES?.measure flag in Storybook core
  • Decorator System: React decorator (withMeasure) automatically included when feature is enabled
  • Canvas Overlay: HTML5 canvas element overlaying the story viewport for drawing measurements
  • Event Handling: Mouse tracking (pointermove, pointerover) and keyboard shortcut integration
  • Measurement Engine: CSS computed style analysis and box model calculation using getComputedStyle
  • Visualization System: Color-coded overlay rendering with intelligent label positioning and collision detection
  • Shadow DOM Support: Deep element traversal including web components via crawlShadows
  • Manager Integration: Toolbar button and keyboard shortcut registration through Storybook's addon system

Capabilities

Measurement Activation

Control when measurement mode is active through global state and keyboard shortcuts.

/**
 * Global state key for controlling measurement mode
 */
const PARAM_KEY = 'measureEnabled';

/**
 * Addon and tool identifiers
 */
const ADDON_ID = 'storybook/measure-addon';
const TOOL_ID = 'storybook/measure-addon/tool';

Decorator Function

React decorator that adds measurement capability to Storybook stories (automatically included in core).

/**
 * React decorator for adding measurement functionality to stories
 * Automatically registered when globalThis.FEATURES?.measure is enabled
 * @param StoryFn - The story component function
 * @param context - Storybook story context with globals and viewMode
 * @returns Story component with measurement capabilities
 */
const withMeasure: DecoratorFunction<StoryContext> = (StoryFn, context) => React.ReactElement;

interface StoryContext {
  globals: {
    measureEnabled?: boolean;
  };
  viewMode: 'story' | 'canvas' | 'docs';
  parameters: {
    measure?: MeasureParameters['measure'];
  };
}

Canvas Management

HTML5 canvas overlay system for rendering measurements.

/**
 * Initialize measurement canvas overlay
 */
function init(): void;

/**
 * Clear all drawn measurements from canvas
 */
function clear(): void;

/**
 * Execute drawing operations with automatic clear
 * @param callback - Drawing function receiving canvas context
 */
function draw(callback: (context?: CanvasRenderingContext2D) => void): void;

/**
 * Rescale canvas for viewport changes
 */
function rescale(): void;

/**
 * Remove canvas overlay and cleanup resources
 */
function destroy(): void;

Element Measurement

Core measurement analysis and DOM element inspection.

/**
 * Get DOM element at coordinates with shadow DOM support
 * Crawls through shadow DOM boundaries to find the deepest element
 * @param x - X coordinate in pixels
 * @param y - Y coordinate in pixels
 * @returns HTML element at the specified coordinates
 */
function deepElementFromPoint(x: number, y: number): HTMLElement;

/**
 * Draw box model measurements for specified element
 * @param element - HTML element to measure and visualize
 */
function drawSelectedElement(element: HTMLElement): void;

/**
 * Measure element dimensions and calculate box model values
 * @param element - HTML element to measure
 * @returns Complete measurement data including box model and positioning
 */
function measureElement(element: HTMLElement): ElementMeasurements;

/**
 * Internal function to crawl shadow DOM boundaries
 * @param node - HTML element that may contain shadow DOM
 * @returns Deepest accessible element in shadow DOM tree
 */
function crawlShadows(node: HTMLElement): HTMLElement;

Manager UI Integration

Storybook manager panel integration for measurement controls.

/**
 * Register measurement tool in Storybook manager
 * Only registers if globalThis.FEATURES?.measure is enabled
 */
function default(): void;

/**
 * React component for measurement toggle button with ruler icon
 * Uses useGlobals hook to manage measureEnabled state
 */
const Tool: React.FC = () => React.ReactElement;

/**
 * Addon registration configuration
 */
interface AddonConfig {
  type: 'TOOL';
  title: 'Measure';
  match: (args: { viewMode: string; tabId?: string }) => boolean;
  render: () => React.ReactElement;
}

/**
 * Keyboard shortcut configuration object passed to setAddonShortcut
 */
interface ShortcutConfig {
  label: 'Toggle Measure [M]';
  defaultShortcut: ['M'];
  actionName: 'measure';
  showInMenu: false;
  action: () => void;
}

/**
 * Hook for accessing and updating global measure state
 * @returns Tuple of [globals, updateGlobals] for measure control
 */
function useGlobals(): [{ measureEnabled?: boolean }, (globals: any) => void];

Configuration Interface

Parameter interface for controlling measurement behavior.

/**
 * Configuration parameters for measurement addon
 */
interface MeasureParameters {
  measure: {
    /** Remove the addon panel and disable the addon's behavior */
    disable?: boolean;
  };
}

Types

Measurement Data Types

/**
 * Margin measurements in pixels
 */
interface Margin {
  top: number;
  bottom: number;
  left: number;
  right: number;
}

/**
 * Padding measurements in pixels
 */
interface Padding {
  top: number;
  bottom: number;
  left: number;
  right: number;
}

/**
 * Border measurements in pixels
 */
interface Border {
  top: number;
  bottom: number;
  left: number;
  right: number;
}

/**
 * Complete box model dimensions
 */
interface Dimensions {
  margin: Margin;
  padding: Padding;
  border: Border;
  width: number;
  height: number;
  top: number;
  left: number;
  bottom: number;
  right: number;
}

/**
 * Element boundary coordinates
 */
interface Extremities {
  top: number;
  bottom: number;
  left: number;
  right: number;
}

/**
 * Label positioning alignment
 */
interface FloatingAlignment {
  x: 'left' | 'right';
  y: 'top' | 'bottom';
}

/**
 * Complete element measurement data
 */
interface ElementMeasurements extends Dimensions {
  extremities: Extremities;
  floatingAlignment: FloatingAlignment;
}

Label System Types

/**
 * Label type categories
 */
type LabelType = 'margin' | 'padding' | 'border' | 'content';

/**
 * Label positioning options
 */
type LabelPosition = 'top' | 'right' | 'bottom' | 'left' | 'center';

/**
 * Direction for measurements
 */
type Direction = 'top' | 'right' | 'bottom' | 'left';

/**
 * Individual measurement label
 */
interface Label {
  type: LabelType;
  text: number | string;
  position: LabelPosition;
}

/**
 * Collection of labels for rendering
 */
type LabelStack = Label[];

/**
 * Canvas drawing utilities
 */
interface RectSize {
  w: number;
  h: number;
}

interface Coordinate {
  x: number;
  y: number;
}

interface Rect extends RectSize, Coordinate {}

interface RoundedRect extends Rect {
  r: number;
}

Event System

/**
 * Addon event identifiers
 */
const EVENTS = {
  RESULT: 'storybook/measure-addon/result';
  REQUEST: 'storybook/measure-addon/request';
  CLEAR: 'storybook/measure-addon/clear';
};

/**
 * Addon identifiers
 */
const ADDON_ID = 'storybook/measure-addon';
const TOOL_ID = 'storybook/measure-addon/tool';

Visual Features

Box Model Visualization

  • Margin: Semi-transparent orange overlay (#f6b26ba8)
  • Border: Semi-transparent yellow overlay (#ffe599a8)
  • Padding: Semi-transparent green overlay (#93c47d8c)
  • Content: Semi-transparent blue overlay (#6fa8dca8)

Smart Labeling

  • Collision Detection: Labels automatically reposition to avoid overlaps using filterZeroValues
  • External Positioning: Small elements (< 30px) show labels outside the element bounds
  • Floating Labels: Content dimensions displayed as floating labels with intelligent alignment
  • Color Coding: Labels use matching colors - margin (#f6b26b), border (#ffe599), padding (#93c47d), content (#6fa8dc)
  • Rounded Label Backgrounds: Labels rendered with rounded rectangles and 6px padding

Responsive Behavior

  • Auto-scaling: Canvas automatically rescales on window resize using devicePixelRatio
  • High DPI Support: Proper scaling for high-density displays with context scaling
  • Pointer Tracking: Real-time mouse position tracking with requestAnimationFrame optimization
  • Shadow DOM: Deep traversal into Shadow DOM boundaries via recursive crawlShadows
  • Performance Optimization: Uses pointerover events rather than continuous mousemove for better performance
  • Canvas Management: Dynamic canvas creation with proper z-index (2147483647) and pointer-events disabled

Utility Functions

Additional internal utility functions used in the measurement system.

/**
 * Convert CSS pixel string to number
 * @param px - CSS pixel value (e.g., "16px")
 * @returns Numeric pixel value
 */
function pxToNumber(px: string): number;

/**
 * Round number to appropriate precision
 * @param value - Number to round
 * @returns Integer if whole number, otherwise 2 decimal places
 */
function round(value: number): number | string;

/**
 * Remove zero-value labels from label stack
 * @param labels - Array of labels to filter
 * @returns Filtered labels without zero values
 */
function filterZeroValues(labels: LabelStack): LabelStack;

/**
 * Calculate optimal floating label alignment
 * @param extremities - Element boundary coordinates
 * @returns Alignment configuration for label positioning
 */
function floatingAlignment(extremities: Extremities): FloatingAlignment;

/**
 * Get document dimensions including scroll areas
 * @returns Width and height of the full document
 */
function getDocumentWidthAndHeight(): { width: number; height: number };

Migration Guide

For projects upgrading to Storybook 9.0+:

  1. Remove the addon: Delete @storybook/addon-measure from package.json dependencies
  2. Update configuration: Remove @storybook/addon-measure from .storybook/main.js addons array
  3. Handle errors: The old package now throws descriptive migration errors when imported
  4. No code changes: Measurement functionality works identically with same keyboard shortcuts
  5. Feature flag: Ensure FEATURES.measure is enabled in your Storybook configuration (enabled by default)
  6. Keyboard shortcut: Continue using 'M' key or ruler toolbar button to toggle measurements

Migration example:

// .storybook/main.js
export default {
  addons: [
-   '@storybook/addon-measure',
    '@storybook/addon-essentials',
    // measure is now included automatically in core
  ],
};
// package.json
{
  "devDependencies": {
-   "@storybook/addon-measure": "^8.x.x",
    "@storybook/react": "^9.0.0"
  }
}

The core functionality remains unchanged - only the installation and configuration requirements have been removed. All API functions are now internal to Storybook core.

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/@storybook/addon-measure@9.0.x
Publish Source
CLI
Badge
tessl/npm-storybook--addon-measure badge