Custom CSS without breaking production
My styles work locally but vanish when deployed.
The strict CSP silently drops inline styles — the page deploys unstyled with no error. Here is the bundled-stylesheet way that always survives.
Typical effort: 10 minutes
The recipe — paste this to your AI
# Recipe: custom CSS the CSP-safe way (avoid the "unstyled in production" trap)
**Goal:** add custom styling to a page or component that actually renders in production.
**The trap (this is real — it bit a launch):** this site ships a strict Content-Security-Policy
(`style-src 'self' 'nonce-…'`). That means **inline styles are silently dropped in production** — no
build error, no console warning, the page just deploys as a wall of **unstyled text**. And it looks
perfectly fine on your local dev server, so it's easy to ship. The banned patterns:
```tsx
// ❌ inline <style> block — dropped by CSP in production
<style>{`.hero { color: red }`}</style>
// ❌ inline style attribute — dropped by CSP in production
<div style={{ color: 'red' }}>…</div>
<div style="color:red">…</div>
// ❌ un-encoded inline-SVG data: URI in CSS — img-src blocks data:, parser can choke
background: url('data:image/svg+xml,<svg>…</svg>');
```
## The right way — a bundled stylesheet served from `'self'`
1. **Put your CSS in a real `.css` file** and `import` it into your component. Next.js bundles it and
serves it from your own origin (`'self'`), which the CSP allows:
```
app/(home)/home.css ← your styles
app/(home)/page.tsx ← import './home.css'
```
```tsx
import './home.css';
export default function Home() {
return <div className="hero">…</div>; // class names, not inline styles
}
```
2. **Prefer the design system first.** Most styling should be Tailwind utility classes + the brand
tokens in `app/globals.css` (`:root`). Reach for a custom `.css` file only for things the tokens +
utilities can't express.
3. **Need an image/SVG?** Put it in `public/` and reference it by URL (`/my-art.svg`), or import it as
a module. Don't inline it as a `data:` URI in CSS.
4. **Do NOT loosen the CSP** to make inline styles work. The CSP is a shipped framework security
control (`middleware.ts` / config) — editing it drifts you off the upgrade path and weakens the
site. Author the CSS the bundled way instead.
## Verify
- **Build and check production, not just dev:** `pnpm build` then serve the production build (or deploy
a preview). If it looks right in the production build, the CSP is happy.
- Symptom to remember: **styled locally, unstyled in production = inline styles + CSP.** Move them to a
bundled `.css` file.
## Paste-ready prompt
> Style this component with custom CSS the CSP-safe way: no inline `<style>` blocks and no `style="…"`
> attributes (the site's CSP silently drops them in production). Put the CSS in a `.css` file imported
> into the component, use class names, and prefer Tailwind + the `app/globals.css` brand tokens where
> possible. Don't inline SVGs as `data:` URIs and don't touch the CSP. Verify with a production build.
Source of truth
This recipe ships inside the template as docs/prompts/custom-css-the-csp-safe-way.md — this page renders that exact file. The repository is always the newest version: https://github.com/srisenmay/bringthefront-template. Clone the template and you get every recipe locally, versioned with the release you are on.