0
# Storybook Addon Measure
1
2
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.
3
4
**⚠️ 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.
5
6
## Package Information
7
8
- **Package Name**: @storybook/addon-measure
9
- **Package Type**: npm
10
- **Language**: TypeScript
11
- **Installation**: `npm install @storybook/addon-measure` (deprecated - throws error in v9.0+)
12
- **Migration**: Remove from dependencies - functionality included in Storybook 9.0+ core
13
14
## Core Imports
15
16
**Deprecated package (v9.0+):**
17
18
```typescript
19
// These imports throw migration errors in v9.0+:
20
// Error: Your Storybook project is referring to package @storybook/addon-measure,
21
// which no longer exists in Storybook 9.0 and above.
22
import { withMeasure } from "@storybook/addon-measure";
23
```
24
25
**Current usage (Storybook 9.0+ core):**
26
27
```typescript
28
// Functionality is now available automatically in Storybook core
29
// No imports needed - the measure decorator is included automatically when FEATURES.measure is enabled
30
// Access through Storybook's internal APIs if needed for custom implementation:
31
import { useGlobals } from 'storybook/manager-api';
32
import type { MeasureParameters } from 'storybook/internal/types';
33
```
34
35
## Basic Usage
36
37
**Legacy usage (pre-v9.0):**
38
39
```typescript
40
// In .storybook/main.js - NO LONGER WORKS
41
export default {
42
addons: ['@storybook/addon-measure'], // This will cause errors in v9.0+
43
};
44
```
45
46
**Current usage (v9.0+):**
47
48
```typescript
49
// No configuration needed - measure is included in core automatically
50
// Usage:
51
// 1. Press 'M' key to enable measurement mode (or click the ruler icon in toolbar)
52
// 2. Hover over any DOM element in your story
53
// 3. View margin, padding, border, and content dimensions with color-coded overlays
54
55
// To disable measure for specific stories:
56
export default {
57
parameters: {
58
measure: {
59
disable: true, // Disable measure for this component/story
60
},
61
},
62
};
63
```
64
65
## Architecture
66
67
The measure system (now part of Storybook core) consists of several key components:
68
69
- **Feature Flag System**: Controlled by `globalThis.FEATURES?.measure` flag in Storybook core
70
- **Decorator System**: React decorator (`withMeasure`) automatically included when feature is enabled
71
- **Canvas Overlay**: HTML5 canvas element overlaying the story viewport for drawing measurements
72
- **Event Handling**: Mouse tracking (`pointermove`, `pointerover`) and keyboard shortcut integration
73
- **Measurement Engine**: CSS computed style analysis and box model calculation using `getComputedStyle`
74
- **Visualization System**: Color-coded overlay rendering with intelligent label positioning and collision detection
75
- **Shadow DOM Support**: Deep element traversal including web components via `crawlShadows`
76
- **Manager Integration**: Toolbar button and keyboard shortcut registration through Storybook's addon system
77
78
## Capabilities
79
80
### Measurement Activation
81
82
Control when measurement mode is active through global state and keyboard shortcuts.
83
84
```typescript { .api }
85
/**
86
* Global state key for controlling measurement mode
87
*/
88
const PARAM_KEY = 'measureEnabled';
89
90
/**
91
* Addon and tool identifiers
92
*/
93
const ADDON_ID = 'storybook/measure-addon';
94
const TOOL_ID = 'storybook/measure-addon/tool';
95
```
96
97
### Decorator Function
98
99
React decorator that adds measurement capability to Storybook stories (automatically included in core).
100
101
```typescript { .api }
102
/**
103
* React decorator for adding measurement functionality to stories
104
* Automatically registered when globalThis.FEATURES?.measure is enabled
105
* @param StoryFn - The story component function
106
* @param context - Storybook story context with globals and viewMode
107
* @returns Story component with measurement capabilities
108
*/
109
const withMeasure: DecoratorFunction<StoryContext> = (StoryFn, context) => React.ReactElement;
110
111
interface StoryContext {
112
globals: {
113
measureEnabled?: boolean;
114
};
115
viewMode: 'story' | 'canvas' | 'docs';
116
parameters: {
117
measure?: MeasureParameters['measure'];
118
};
119
}
120
```
121
122
### Canvas Management
123
124
HTML5 canvas overlay system for rendering measurements.
125
126
```typescript { .api }
127
/**
128
* Initialize measurement canvas overlay
129
*/
130
function init(): void;
131
132
/**
133
* Clear all drawn measurements from canvas
134
*/
135
function clear(): void;
136
137
/**
138
* Execute drawing operations with automatic clear
139
* @param callback - Drawing function receiving canvas context
140
*/
141
function draw(callback: (context?: CanvasRenderingContext2D) => void): void;
142
143
/**
144
* Rescale canvas for viewport changes
145
*/
146
function rescale(): void;
147
148
/**
149
* Remove canvas overlay and cleanup resources
150
*/
151
function destroy(): void;
152
```
153
154
### Element Measurement
155
156
Core measurement analysis and DOM element inspection.
157
158
```typescript { .api }
159
/**
160
* Get DOM element at coordinates with shadow DOM support
161
* Crawls through shadow DOM boundaries to find the deepest element
162
* @param x - X coordinate in pixels
163
* @param y - Y coordinate in pixels
164
* @returns HTML element at the specified coordinates
165
*/
166
function deepElementFromPoint(x: number, y: number): HTMLElement;
167
168
/**
169
* Draw box model measurements for specified element
170
* @param element - HTML element to measure and visualize
171
*/
172
function drawSelectedElement(element: HTMLElement): void;
173
174
/**
175
* Measure element dimensions and calculate box model values
176
* @param element - HTML element to measure
177
* @returns Complete measurement data including box model and positioning
178
*/
179
function measureElement(element: HTMLElement): ElementMeasurements;
180
181
/**
182
* Internal function to crawl shadow DOM boundaries
183
* @param node - HTML element that may contain shadow DOM
184
* @returns Deepest accessible element in shadow DOM tree
185
*/
186
function crawlShadows(node: HTMLElement): HTMLElement;
187
```
188
189
### Manager UI Integration
190
191
Storybook manager panel integration for measurement controls.
192
193
```typescript { .api }
194
/**
195
* Register measurement tool in Storybook manager
196
* Only registers if globalThis.FEATURES?.measure is enabled
197
*/
198
function default(): void;
199
200
/**
201
* React component for measurement toggle button with ruler icon
202
* Uses useGlobals hook to manage measureEnabled state
203
*/
204
const Tool: React.FC = () => React.ReactElement;
205
206
/**
207
* Addon registration configuration
208
*/
209
interface AddonConfig {
210
type: 'TOOL';
211
title: 'Measure';
212
match: (args: { viewMode: string; tabId?: string }) => boolean;
213
render: () => React.ReactElement;
214
}
215
216
/**
217
* Keyboard shortcut configuration object passed to setAddonShortcut
218
*/
219
interface ShortcutConfig {
220
label: 'Toggle Measure [M]';
221
defaultShortcut: ['M'];
222
actionName: 'measure';
223
showInMenu: false;
224
action: () => void;
225
}
226
227
/**
228
* Hook for accessing and updating global measure state
229
* @returns Tuple of [globals, updateGlobals] for measure control
230
*/
231
function useGlobals(): [{ measureEnabled?: boolean }, (globals: any) => void];
232
```
233
234
### Configuration Interface
235
236
Parameter interface for controlling measurement behavior.
237
238
```typescript { .api }
239
/**
240
* Configuration parameters for measurement addon
241
*/
242
interface MeasureParameters {
243
measure: {
244
/** Remove the addon panel and disable the addon's behavior */
245
disable?: boolean;
246
};
247
}
248
```
249
250
## Types
251
252
### Measurement Data Types
253
254
```typescript { .api }
255
/**
256
* Margin measurements in pixels
257
*/
258
interface Margin {
259
top: number;
260
bottom: number;
261
left: number;
262
right: number;
263
}
264
265
/**
266
* Padding measurements in pixels
267
*/
268
interface Padding {
269
top: number;
270
bottom: number;
271
left: number;
272
right: number;
273
}
274
275
/**
276
* Border measurements in pixels
277
*/
278
interface Border {
279
top: number;
280
bottom: number;
281
left: number;
282
right: number;
283
}
284
285
/**
286
* Complete box model dimensions
287
*/
288
interface Dimensions {
289
margin: Margin;
290
padding: Padding;
291
border: Border;
292
width: number;
293
height: number;
294
top: number;
295
left: number;
296
bottom: number;
297
right: number;
298
}
299
300
/**
301
* Element boundary coordinates
302
*/
303
interface Extremities {
304
top: number;
305
bottom: number;
306
left: number;
307
right: number;
308
}
309
310
/**
311
* Label positioning alignment
312
*/
313
interface FloatingAlignment {
314
x: 'left' | 'right';
315
y: 'top' | 'bottom';
316
}
317
318
/**
319
* Complete element measurement data
320
*/
321
interface ElementMeasurements extends Dimensions {
322
extremities: Extremities;
323
floatingAlignment: FloatingAlignment;
324
}
325
```
326
327
### Label System Types
328
329
```typescript { .api }
330
/**
331
* Label type categories
332
*/
333
type LabelType = 'margin' | 'padding' | 'border' | 'content';
334
335
/**
336
* Label positioning options
337
*/
338
type LabelPosition = 'top' | 'right' | 'bottom' | 'left' | 'center';
339
340
/**
341
* Direction for measurements
342
*/
343
type Direction = 'top' | 'right' | 'bottom' | 'left';
344
345
/**
346
* Individual measurement label
347
*/
348
interface Label {
349
type: LabelType;
350
text: number | string;
351
position: LabelPosition;
352
}
353
354
/**
355
* Collection of labels for rendering
356
*/
357
type LabelStack = Label[];
358
359
/**
360
* Canvas drawing utilities
361
*/
362
interface RectSize {
363
w: number;
364
h: number;
365
}
366
367
interface Coordinate {
368
x: number;
369
y: number;
370
}
371
372
interface Rect extends RectSize, Coordinate {}
373
374
interface RoundedRect extends Rect {
375
r: number;
376
}
377
```
378
379
### Event System
380
381
```typescript { .api }
382
/**
383
* Addon event identifiers
384
*/
385
const EVENTS = {
386
RESULT: 'storybook/measure-addon/result';
387
REQUEST: 'storybook/measure-addon/request';
388
CLEAR: 'storybook/measure-addon/clear';
389
};
390
391
/**
392
* Addon identifiers
393
*/
394
const ADDON_ID = 'storybook/measure-addon';
395
const TOOL_ID = 'storybook/measure-addon/tool';
396
```
397
398
## Visual Features
399
400
### Box Model Visualization
401
402
- **Margin**: Semi-transparent orange overlay (`#f6b26ba8`)
403
- **Border**: Semi-transparent yellow overlay (`#ffe599a8`)
404
- **Padding**: Semi-transparent green overlay (`#93c47d8c`)
405
- **Content**: Semi-transparent blue overlay (`#6fa8dca8`)
406
407
### Smart Labeling
408
409
- **Collision Detection**: Labels automatically reposition to avoid overlaps using `filterZeroValues`
410
- **External Positioning**: Small elements (< 30px) show labels outside the element bounds
411
- **Floating Labels**: Content dimensions displayed as floating labels with intelligent alignment
412
- **Color Coding**: Labels use matching colors - margin (`#f6b26b`), border (`#ffe599`), padding (`#93c47d`), content (`#6fa8dc`)
413
- **Rounded Label Backgrounds**: Labels rendered with rounded rectangles and 6px padding
414
415
### Responsive Behavior
416
417
- **Auto-scaling**: Canvas automatically rescales on window resize using `devicePixelRatio`
418
- **High DPI Support**: Proper scaling for high-density displays with context scaling
419
- **Pointer Tracking**: Real-time mouse position tracking with `requestAnimationFrame` optimization
420
- **Shadow DOM**: Deep traversal into Shadow DOM boundaries via recursive `crawlShadows`
421
- **Performance Optimization**: Uses `pointerover` events rather than continuous `mousemove` for better performance
422
- **Canvas Management**: Dynamic canvas creation with proper z-index (`2147483647`) and pointer-events disabled
423
424
### Utility Functions
425
426
Additional internal utility functions used in the measurement system.
427
428
```typescript { .api }
429
/**
430
* Convert CSS pixel string to number
431
* @param px - CSS pixel value (e.g., "16px")
432
* @returns Numeric pixel value
433
*/
434
function pxToNumber(px: string): number;
435
436
/**
437
* Round number to appropriate precision
438
* @param value - Number to round
439
* @returns Integer if whole number, otherwise 2 decimal places
440
*/
441
function round(value: number): number | string;
442
443
/**
444
* Remove zero-value labels from label stack
445
* @param labels - Array of labels to filter
446
* @returns Filtered labels without zero values
447
*/
448
function filterZeroValues(labels: LabelStack): LabelStack;
449
450
/**
451
* Calculate optimal floating label alignment
452
* @param extremities - Element boundary coordinates
453
* @returns Alignment configuration for label positioning
454
*/
455
function floatingAlignment(extremities: Extremities): FloatingAlignment;
456
457
/**
458
* Get document dimensions including scroll areas
459
* @returns Width and height of the full document
460
*/
461
function getDocumentWidthAndHeight(): { width: number; height: number };
462
```
463
464
## Migration Guide
465
466
For projects upgrading to Storybook 9.0+:
467
468
1. **Remove the addon**: Delete `@storybook/addon-measure` from `package.json` dependencies
469
2. **Update configuration**: Remove `@storybook/addon-measure` from `.storybook/main.js` addons array
470
3. **Handle errors**: The old package now throws descriptive migration errors when imported
471
4. **No code changes**: Measurement functionality works identically with same keyboard shortcuts
472
5. **Feature flag**: Ensure `FEATURES.measure` is enabled in your Storybook configuration (enabled by default)
473
6. **Keyboard shortcut**: Continue using 'M' key or ruler toolbar button to toggle measurements
474
475
**Migration example:**
476
477
```diff
478
// .storybook/main.js
479
export default {
480
addons: [
481
- '@storybook/addon-measure',
482
'@storybook/addon-essentials',
483
// measure is now included automatically in core
484
],
485
};
486
```
487
488
```diff
489
// package.json
490
{
491
"devDependencies": {
492
- "@storybook/addon-measure": "^8.x.x",
493
"@storybook/react": "^9.0.0"
494
}
495
}
496
```
497
498
The core functionality remains unchanged - only the installation and configuration requirements have been removed. All API functions are now internal to Storybook core.