Observed since 20 Oct, 21:55 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
React and framework-independent live layout guide overlays, plus captured layout-map SVG rendering
@01.works/layout-overlayA development overlay that draws live layout regions and guides over a browser
application. The root entry is an SSR-safe React component; the dom subpath
is a React-free imperative API for any modern ESM browser application. Neither
requires Next.js, Tailwind CSS, or a specific design-token system.
pnpm add @01.works/layout-overlay
React 18 or newer is required only when importing the root React component.
Both @01.works/layout-overlay/dom and ./layout-map are
framework-independent and do not import React. The package is ESM-only and does
not provide a CommonJS require() entry.
Version 0.3.0 adds the ./dom public subpath. It is a backward-compatible
minor release: existing root React and ./layout-map imports retain their
public API. Install @01.works/layout-overlay@^0.3.0 (or a later compatible
minor) when using ./dom.
import {
LayoutOverlay,
type LayoutOverlayRegion,
} from "@01.works/layout-overlay";
const regions = [
{ key: "logo", start: 0, end: "var(--logo-end)" },
{
key: "navigation",
start: "var(--nav-start)",
end: "var(--nav-end)",
minWidth: "48rem",
},
] satisfies readonly LayoutOverlayRegion[];
export function DevelopmentTools() {
return (
<LayoutOverlay
enabled={process.env.NODE_ENV === "development"}
defaultVisible={process.env.NODE_ENV === "development"}
insetInline="var(--page-margin)"
regions={regions}
horizontalGuides={[
{ key: "header", at: "var(--header-height)" },
]}
/>
);
}
Import the dom subpath in vanilla JS, Web Components, Svelte, Vue, or any
other modern ESM browser application. It does not pull React into the runtime.
import {
mountLayoutOverlay,
type LayoutOverlayRegion,
} from "@01.works/layout-overlay/dom";
const regions = [
{ key: "content", start: "var(--page-margin)", end: "calc(100% - var(--page-margin))" },
{ key: "navigation", start: 320, end: 640, minWidth: "48rem" },
] satisfies readonly LayoutOverlayRegion[];
const overlay = mountLayoutOverlay({
// Use the build tool's development flag. The overlay is inert in production.
enabled: import.meta.env.DEV,
insetInline: "var(--page-margin)",
regions,
horizontalGuides: [{ key: "header", at: "var(--header-height)" }],
});
// For a settings panel, live preview, or application teardown:
overlay.setVisible(true);
overlay.update({ verticalGuides: [{ key: "center", at: "50%" }] });
overlay.unmount();
mountLayoutOverlay() is browser-only and performs no import-time DOM work.
It returns an idempotent handle. update() shallow-merges options (provided
arrays replace prior arrays), setVisible() sets current visibility, and
toggle() flips it. element is the current root or null while hidden,
disabled, or after unmount(). In this imperative API, visible sets current
visibility on mount or update(); it is not a React-style controlled prop.
Keep enabled false in production as a runtime exclusion mechanism. An
enabled: false mount creates no overlay DOM, generated style element, or
keyboard listener. Bundlers may still retain the imported module; this package
does not promise a zero-byte production bundle. For complete bundle exclusion,
conditionally import @01.works/layout-overlay/dom from your own
development-only integration point.
The package does nothing unless enabled is explicitly set to true. Press the
physical G key to toggle the overlay. The shortcut works across keyboard layouts
and is ignored while typing, during IME composition, on repeated key events, or
when Meta, Control, or Alt is held.
The component ships with a "use client" directive, so it can be imported from
Next.js App Router Server Components without an extra client wrapper. It has no
import-time side effects and does not read from window during server rendering.
Use the framework-independent subpath to turn a captured layout analysis map into a transparent SVG. The output uses document CSS-pixel coordinates, so it can be placed directly over a matching full-page screenshot without rasterizing the guides or labels.
import {
renderLayoutMapSvg,
type LayoutAnalysisMap,
} from "@01.works/layout-overlay/layout-map";
const map: LayoutAnalysisMap = await fetch("/layout-map.json").then(response =>
response.json(),
);
const svg = renderLayoutMapSvg(map, {
labels: "all",
layers: { columns: true, rows: true, regions: true },
maxDepth: 3,
});
labels accepts "all", "guides", or "none". Layers can independently
enable columns, rows, regions, layout containers, and floating regions. Invalid
document dimensions throw; individual geometry is clamped to the document and
identity-bearing text is XML-escaped before rendering. maxGuides, maxRegions,
and maxDepth bound work when rendering maps from an external archive.
All guide lines remain visible; labels that would collide are suppressed while
the first and last boundary labels are retained.
LAYOUT_MAP_RENDERER_VERSION changes when serialized output changes. Include it
in cache keys or asset URLs when SVG responses are cached by a browser or CDN.
Only layout-map schema version 1 is rendered. The public version field stays
numeric for 0.3.x source compatibility, while unsupported values fail closed
at runtime instead of being interpreted as version 1.
Every region and guide accepts minWidth and maxWidth:
<LayoutOverlay
enabled
defaultVisible
verticalGuides={[
{ key: "mobile-center", at: "50%", maxWidth: 767 },
{ key: "desktop-left", at: 320, minWidth: 768 },
{ key: "wide-center", at: "50%", minWidth: "64rem" },
]}
/>
Numeric coordinates and widths are converted to pixels. Strings are emitted as
CSS values, so rem, %, calc(), and custom properties all work. CSS control
tokens are rejected; arbitrary raw media queries are not part of the API.
Region and vertical-guide coordinates follow the logical inline axis; horizontal guides follow the logical block axis. In the usual horizontal writing mode these render as vertical and horizontal guides respectively. Logical coordinates also keep the overlay meaningful in RTL and vertical writing modes.
Use visible and onVisibleChange when another UI owns the state:
const [visible, setVisible] = useState(false);
<LayoutOverlay
enabled
visible={visible}
onVisibleChange={(next) => setVisible(next)}
/>
When visible is omitted, defaultVisible initializes internal state and the
keyboard shortcut updates it automatically.
No stylesheet import is required because every visual value has an inline fallback. Import the optional token entry when the same values should be shared with application CSS:
@import "@01.works/layout-overlay/tokens.css";
:root {
--layout-overlay-line-color: rgb(0 140 255 / 55%);
--layout-overlay-band-color: rgb(0 140 255 / 8%);
--layout-overlay-line-width: 1px;
}
For Tailwind CSS v4, import the Tailwind entry instead. Do not import both:
@import "tailwindcss";
@import "@01.works/layout-overlay/tailwind.css";
This registers bg-layout-overlay-line, bg-layout-overlay-band,
w-layout-overlay-line, and h-layout-overlay-line utilities.
LayoutOverlaytype CssLength = number | string;
type LayoutOverlayResponsiveRule = {
minWidth?: CssLength;
maxWidth?: CssLength;
};
type LayoutOverlayRegion = LayoutOverlayResponsiveRule & {
key: string;
start: CssLength;
end: CssLength;
};
type LayoutOverlayGuide = LayoutOverlayResponsiveRule & {
key: string;
at: CssLength;
};
| Prop | Type | Default | Purpose |
|---|---|---|---|
enabled | boolean | false | Opt in to rendering and keyboard handling. |
defaultVisible | boolean | false | Initial visibility for uncontrolled usage. |
visible | boolean | — | Controlled visibility. |
onVisibleChange | (visible, detail) => void | — | Receives shortcut visibility requests. detail.reason is "shortcut". |
regions | readonly LayoutOverlayRegion[] | [] | Filled inline-axis regions with automatic start/end guides. |
verticalGuides | readonly LayoutOverlayGuide[] | [] | Extra inline-axis guide coordinates. |
horizontalGuides | readonly LayoutOverlayGuide[] | [] | Extra block-axis guide coordinates. |
insetInline | CssLength | 0 | Insets the logical inline coordinate space. |
insetBlock | CssLength | 0 | Insets the logical block coordinate space. |
drawEdges | boolean | true | Draws inline-start and inline-end boundary guides. |
lineColor | string | CSS token | Overrides guide colors. |
bandColor | string | CSS token | Overrides region fill colors. |
lineWidth | CssLength | CSS token | Overrides guide thickness. |
zIndex | number | 100 | Sets the root stacking order; must be a safe integer whose +1 is representable for View Transitions. |
toggleCode | string | null | "KeyG" | Matches a physical KeyboardEvent.code. |
toggleKeys | readonly string[] | ['g', 'G', 'ㅎ'] | Additional KeyboardEvent.key matches. |
position | "fixed" | "absolute" | "fixed" | Binds the overlay to the viewport or a positioned ancestor. |
className | string | — | Customizes the root class. |
style | CSSProperties | — | Adds or overrides root inline styles. |
viewTransitionName | string | null | null | Enables View Transition persistence; use a stable, unique name. |
nonce | string | — | Applies a CSP nonce to the generated <style> element. |
Set toggleCode={null} and toggleKeys={[]} to disable keyboard toggling.
mountLayoutOverlayThe @01.works/layout-overlay/dom entry exports the same CssLength,
LayoutOverlayResponsiveRule, LayoutOverlayRegion, and
LayoutOverlayGuide geometry types as independent DOM types. Its options share
the visual and keyboard options above, except that target?: HTMLElement
replaces React placement and style?: LayoutOverlayDomStyle is a plain record
of CSS property names to string values. Use explicit units such as "12px";
numeric custom style values are rejected rather than assigned guessed units.
const handle = mountLayoutOverlay(options);
handle.update({ enabled: true, visible: true });
handle.setVisible(false);
handle.toggle();
handle.unmount();
enabled defaults to false, and defaultVisible defaults to false.
target defaults to document.body; use position: "absolute" with a
positioned target when the overlay should follow an ancestor rather than the
viewport. The default shortcut and editable-target guard are identical to the
React component. onVisibleChange is called only for keyboard shortcut
changes, with detail.reason === "shortcut".
The rendered DOM is decorative (aria-hidden) and does not receive pointer
events. The following attribute names and documented values are public for
debugging and consumer styling:
data-layout-overlay identifies the root and data-layout-overlay-canvas
identifies its coordinate space.data-layout-overlay-key contains the corresponding region or guide key.data-layout-overlay-kind is one of region, region-start, region-end,
vertical-guide, horizontal-guide, inline-start-edge, or
inline-end-edge.data-layout-overlay-query is an opaque implementation token. Its presence is
stable for responsive items, but consumers must not depend on its value.The component renders geometry with inline styles. A nonce can authorize the
generated responsive and View Transition rules, but it does not make the package
compatible with style-src-attr 'none'. Use the overlay in development or
preview environments, or allow inline style attributes in the relevant policy.
pnpm install
pnpm test
pnpm run test:package
pnpm run test:all
pnpm run typecheck
pnpm run lint:package
pnpm pack --dry-run
pnpm test runs the fast, local server and DOM suite without downloading
packages. pnpm run test:package installs the generated tarball and React 18
into an isolated consumer project, so it requires package-registry access.
pnpm run test:all runs both suites and is used before publishing. The package
smoke test verifies JavaScript, declarations, source maps, the "use client"
directive, and CSS subpath exports. pnpm run lint:package checks the packed
metadata and exports with Publint.
Download counts and popularity will appear here as the collector receives them.
This describes our collected data; it is not a security assessment.
Observed since 20 Oct, 21:55 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package:117db4a7a2d2a118d6c0a9505862acdd898716e07a4c4bdd60b4a764b1772fea