Skip to content

Latest commit

 

History

History
191 lines (146 loc) · 7.62 KB

File metadata and controls

191 lines (146 loc) · 7.62 KB

Tailwind Safelist Artifact

@viscalyx/developer-mode-react publishes its overlay's Tailwind v4 class list as a first-class artifact, so consumers do not have to hand-curate a safelist or @source node_modules directly.

This document explains how the artifact is produced and the three ways downstream applications can consume it. If you only want the short version, jump to Option A — recommended.

Contents

How it works

The overlay code lives in packages/developer-mode-react/src/index.tsx. Every Tailwind utility string the overlay renders is exported from packages/developer-mode-react/src/safelist.ts:

  • one named constant per overlay region — OVERLAY_ROOT_CLASS, OVERLAY_HOVER_OUTLINE_CLASS, OVERLAY_BADGE_CLASS, OVERLAY_CHIP_CLASS, TOAST_CONTAINER_CLASS, TOAST_SUCCESS_TONE_CLASS, TOAST_ERROR_TONE_CLASS
  • a DEVELOPER_MODE_OVERLAY_CLASSES array that aggregates all of them

The overlay JSX in src/index.tsx references those constants directly via className={OVERLAY_BADGE_CLASS} etc., so the published JS bundle and the safelist module always agree.

tsdown then emits three artifacts:

Artifact Source Subpath export
dist/safelist.js src/safelist.ts @viscalyx/developer-mode-react/safelist
dist/safelist.d.ts src/safelist.ts (types for the above)
dist/safelist.css generated post-build @viscalyx/developer-mode-react/safelist.css

dist/safelist.css is generated by the onSuccess hook in packages/developer-mode-react/tsdown.config.ts. It imports the just-built dist/safelist.js and writes one Tailwind v4 @source inline("…") declaration per entry. Because the CSS file is derived from the same JS module the overlay imports from, the three artifacts cannot drift.

Two safety nets back this up:

  • A drift-guard test in packages/developer-mode-react/tests/safelist.test.ts scans src/index.tsx for any Tailwind class string literal and fails if a class is inlined instead of being imported from the safelist module.
  • The CI safelist-check job in .github/workflows/ci.yml builds the React package and re-runs the drift-guard test in isolation, so failures surface as their own signal on every PR.

The .github/instructions/overlay-safelist.instructions.md instruction file makes the workflow explicit for AI agents working in the repo.

Option A — import safelist.css (recommended)

Add a single line to your Tailwind v4 entry CSS:

/* src/styles/globals.css */
@import "tailwindcss";
@import "@viscalyx/developer-mode-react/safelist.css";

That is the entire integration. Tailwind v4 reads the @source inline(...) declarations during CSS compilation and emits the overlay's utility classes into your CSS bundle.

Properties:

  • Build-time only. safelist.css is consumed by the Tailwind CSS compiler. There is no JavaScript runtime surface and nothing for the application bundler to ship to the client.
  • Compatible with the ./noop entry. You can alias @viscalyx/developer-mode-react to ./noop (or to a first-party stub) without touching this @import. The CSS file simply emits unused selectors that compress away.
  • Survives npm prune --omit=dev as long as the CSS build runs before the prune. Most CI pipelines do exactly that.
  • Versioned with the package. Upgrading @viscalyx/developer-mode-react automatically updates the safelist.

Option B — @source the JS module (advanced)

If you maintain a Tailwind config or CSS-in-JS layer that prefers a JS source-of-truth, the same constants are exported from a TypeScript subpath:

import {
  DEVELOPER_MODE_OVERLAY_CLASSES,
  OVERLAY_BADGE_CLASS,
} from '@viscalyx/developer-mode-react/safelist'

For a Tailwind v4 setup that prefers @source over @import, point at the published JS file:

/* src/styles/globals.css */
@import "tailwindcss";
@source "../../node_modules/@viscalyx/developer-mode-react/dist/safelist.js";

This option is identical in coverage to Option A. Choose it when you need programmatic access to the individual class strings (e.g. to re-export them, to feed a Tailwind config generator, or to compose them with consumer-side classes). It does require @viscalyx/developer-mode-react to remain in node_modules at CSS build time.

Option C — first-party safelist (fallback)

For air-gapped builds, monorepos that cannot @import from node_modules, or older versions of @viscalyx/developer-mode-react that do not yet ship the artifact, copy the class strings into a local file in your own source tree and @source it from your Tailwind entry CSS.

The full snippet, the local file template, and the drift-guard test that catches upstream changes live in docs/production-noop-guide.md.

When using Option C, treat any package upgrade of @viscalyx/developer-mode-react as a checkpoint to re-sync the local file against the published dist/safelist.js.

Verification checklist for downstreams

After wiring up Option A or B:

  1. Build the consumer application with developer mode enabled.

    ENABLE_DEVELOPER_MODE=true npm run build
  2. Optionally simulate a production-style install to confirm the CSS build is independent of node_modules post-prune:

    npm ci --omit=dev
    ENABLE_DEVELOPER_MODE=false NODE_ENV=production npm run build

    (Option A's CSS compilation must happen before the prune; the resulting CSS file is self-contained.)

  3. Render a page that mounts DeveloperModeProvider, press Mod+Alt+Shift+H, and confirm:

    • the badge renders top-right with rounded border, white background, and uppercase letter spacing;
    • hovering an element renders a chip with a coloured border and shadow;
    • copying a target produces a styled toast in the bottom-right.
  4. Inspect the generated CSS bundle for one of the overlay's arbitrary-value classes (e.g. bg-white\/92 or tracking-\[0\.18em\]). If it is absent, the safelist is not being read by Tailwind — re-check the @import / @source path.

Versioning expectations

The safelist is part of the package's public API.

  • Adding a new constant or extending an existing class string = minor bump.
  • Removing or renaming a constant = major bump.
  • The shape of dist/safelist.css (one @source inline("…"); per entry, in the order published by DEVELOPER_MODE_OVERLAY_CLASSES) is stable; layout changes inside the file will be released as major.

Consumers who pin a major version of @viscalyx/developer-mode-react can rely on every documented constant remaining available, even if new ones are added.