Back to all fixes
CuratedExecution-verifiedTypeScriptVite 8local

Vite 8 build emits CSS with non-ASCII codepoints and no @charset, breaking icon fonts (mojibake glyphs)

Problem

Icon fonts using PUA (Private Use Area) codepoints in CSS `content:` rules (e.g. video-react, older FontAwesome) render fallback/missing glyphs after upgrading to Vite 8. The built CSS bundle contains raw UTF-8 bytes for those codepoints but no `@charset` rule, no BOM, and the server's `Content-Type: text/css` response has no `charset=utf-8` parameter, so the browser falls back to a non-UTF-8 encoding (e.g. Windows-1252) and mis-decodes the multi-byte codepoint into garbage characters. Caused by the switch from esbuild to lightningcss for CSS minification - lightningcss does not add an ASCII-safe escape or a charset marker the way esbuild's minifier used to.

Solution

typescript
No core Vite/Rolldown fix has landed for this; the reporter (alphathekiwi) confirmed adding a custom Rollup/Vite plugin as a working fix, applied at the `generateBundle` stage to prepend a charset declaration to every emitted CSS file:
```ts
import type { Plugin } from 'vite'
export function vitePluginCssCharset(): Plugin {
  return {
    name: 'vite-plugin-css-charset',
    enforce: 'post',
    generateBundle(_, bundle) {
      for (const chunk of Object.values(bundle)) {
        if (chunk.type === 'asset' && chunk.fileName.endsWith('.css')) {
          const source = chunk.source.toString()
          if (!source.startsWith('@charset')) {
            chunk.source = '@charset "UTF-8";\n' + source
          }
        }
      }
    }
  }
}
```
Alternatively, ensure your server sends `Content-Type: text/css; charset=utf-8` explicitly, which also resolves the mis-decoding without touching the build output.