From 830f129e6a0122e8cb97a29e507e31831e538b4d Mon Sep 17 00:00:00 2001 From: Oliver Lazoroski Date: Wed, 11 Nov 2026 17:11:37 +0100 Subject: [PATCH] fix(styling): drop @charset from the published CSS (v14) Sass prepends `@charset "UTF-8"` to any stylesheet with a non-ASCII character. Imported into a cascade layer, it ends up nested inside `@layer`, where it is an unknown at-rule that minifiers such as Lightning CSS warn about on every build. Build with `--no-charset` (and `charset: false` in the watch script), and keep the AI UI glyphs as CSS escapes so ai-components.css decodes right under any encoding. Closes #3423 --- package.json | 2 +- scripts/watch-styling.mjs | 2 ++ src/plugins/AIComponents/styling/_ai-ui-icons.scss | 7 +++++-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 0cf10d819..31bbac005 100644 --- a/package.json +++ b/package.json @@ -244,7 +244,7 @@ "scripts": { "clean": "rm -rf dist", "build": "yarn clean && concurrently 'yarn build-translations' 'vite build' 'tsc --project tsconfig.lib.json' 'yarn build-styling'", - "build-styling": "sass src/styling/index.scss:dist/css/index.css src/styling/_emoji-replacement.scss:dist/css/emoji-replacement.css src/plugins/Emojis/styling/index.scss:dist/css/emoji-picker.css src/plugins/ChannelDetail/styling/index.scss:dist/css/channel-detail.css src/plugins/AIComponents/styling/index.scss:dist/css/ai-components.css; cp -r src/styling/assets dist/css/assets", + "build-styling": "sass --no-charset src/styling/index.scss:dist/css/index.css src/styling/_emoji-replacement.scss:dist/css/emoji-replacement.css src/plugins/Emojis/styling/index.scss:dist/css/emoji-picker.css src/plugins/ChannelDetail/styling/index.scss:dist/css/channel-detail.css src/plugins/AIComponents/styling/index.scss:dist/css/ai-components.css; cp -r src/styling/assets dist/css/assets", "build-translations": "i18next-cli extract", "coverage": "vitest run --coverage", "lint": "yarn prettier --list-different && yarn eslint && yarn validate-translations", diff --git a/scripts/watch-styling.mjs b/scripts/watch-styling.mjs index 20bd6d815..d58daa012 100644 --- a/scripts/watch-styling.mjs +++ b/scripts/watch-styling.mjs @@ -108,6 +108,8 @@ const flushQueuedBuild = () => { const buildStyleEntry = async ({ entryFile, outputFile }) => { const { css, sourceMap } = await compileAsync(entryFile, { + // Same as `--no-charset` in `build-styling`: `@charset` is invalid once imported into a layer. + charset: false, sourceMap: true, style: 'expanded', }); diff --git a/src/plugins/AIComponents/styling/_ai-ui-icons.scss b/src/plugins/AIComponents/styling/_ai-ui-icons.scss index 08665e101..54a360ca3 100644 --- a/src/plugins/AIComponents/styling/_ai-ui-icons.scss +++ b/src/plugins/AIComponents/styling/_ai-ui-icons.scss @@ -1,3 +1,4 @@ +@use 'sass:string'; @use './tokens' as *; // The marks a reply's generated UI draws, the same in A2UI and OpenUI: glyphs and shapes in CSS, @@ -21,7 +22,9 @@ content: $glyph; } +// Non-ASCII glyphs stay CSS escapes in the output: Sass would otherwise write the characters +// themselves, so the stylesheet would decode right only as UTF-8 (it carries no `@charset`). $ai-ui-glyph-info: 'i'; -$ai-ui-glyph-success: '\2713'; +$ai-ui-glyph-success: string.unquote('"\\2713"'); $ai-ui-glyph-warning: '!'; -$ai-ui-glyph-danger: '\00d7'; +$ai-ui-glyph-danger: string.unquote('"\\00d7"');