saadeghi/daisyui · error · Error

Unsafe PR CSS in ${filePath}: ${reason}

Error message

Unsafe PR CSS in ${filePath}: ${reason}

What it means

Thrown by unsafePrCss (called from assertSafePrCss) when the PR's component CSS contains content the preview pipeline refuses to render in Tailwind Play: a forbidden at-rule (@config, @plugin, @source), an external @import URL, or an external url() reference. The check is a security/safety gate before the CSS is shipped to play.tailwindcss.com.

Source

Thrown at packages/tailwind-play-share/pr-preview.mjs:289

}

function isNonLocalCssUrl(value) {
  if (!value) return false
  const normalized = normalizeCssUrl(value)
  return normalized.startsWith("//") || /^[a-z][a-z0-9+.-]*:/i.test(normalized)
}

function normalizeCssUrl(value) {
  return [...value.trim()]
    .filter((character) => {
      const codePoint = character.codePointAt(0)
      return codePoint > 0x20 && codePoint !== 0x7f
    })
    .join("")
}

function unsafePrCss(filePath, reason) {
  throw new Error(`Unsafe PR CSS in ${filePath}: ${reason}`)
}

export function assertSafePrCss(css, filePath = "component stylesheet") {
  let index = 0

  while (index < css.length) {
    if (css.startsWith("/*", index)) {
      index = consumeCssComment(css, index)
      continue
    }
    if (css[index] === '"' || css[index] === "'") {
      index = consumeCssString(css, index).end
      continue
    }
    if (css[index] === "@") {
      const atRule = consumeCssIdentifier(css, index + 1)
      const name = atRule.value.toLowerCase()
      if (forbiddenPrCssAtRules.has(name)) {

View on GitHub (pinned to 42b09e637e)

Solutions

  1. Remove the forbidden @-rule or external URL from the component CSS in the PR.
  2. For @plugin/@config/@source, keep them in the root stylesheet, not in per-component CSS.
  3. Replace external url() references with relative or data: URLs if a URL is truly needed.
  4. Re-run; the error names the file and the reason so you can locate the offending line.

Example fix

/* before - packages/daisyui/src/components/btn.css */
@import url("https://cdn.example.com/btn.css");
.btn { background: url("https://cdn.example.com/bg.png"); }

/* after */
.btn { background: var(--btn-bg); }
Defensive patterns

Strategy: validation

Validate before calling

import { assertSafePrCss } from "./pr-preview.mjs"
// Run the safety check on the changed CSS BEFORE invoking the full preview pipeline.
assertSafePrCss(changedCss, changedFilePath)

Try / catch

try {
  await generatePrPreview({ baseSha, headSha, output })
} catch (error) {
  if (/^Unsafe PR CSS in /.test(error.message)) {
  // the message names the file + reason; edit the CSS and re-push
  }
  throw error
}

Prevention

When it happens

Trigger: assertSafePrCss walks the changed component CSS at the head SHA and, for any @-rule whose name is in forbiddenPrCssAtRules or any url()/@import resolving to a non-local scheme, calls unsafePrCss(filePath, reason).

Common situations: A PR adds `@plugin "..."`, `@config "..."`, `@source "..."`, `@import url("https://...")`, or `background: url("https://...")` inside packages/daisyui/src/components/*.css. These are blocked because Tailwind Play cannot safely load them.

Related errors


AI-assisted analysis of saadeghi/daisyui@42b09e637e (2026-08-13). Data as JSON: /api/errors/d2cc0d76c944cab0. Report an issue: GitHub.