actualbudget/actual · error
Invalid CSS declaration: "${decl}"
Error message
Invalid CSS declaration: "${decl}" What it means
validateThemeCss parses the :root block into declarations (split on semicolons, quote/url aware) and requires each declaration to contain a colon separating property from value. This error is thrown when a semicolon-separated chunk has no ':' at all, so it cannot be a property:value declaration. It exists because arbitrary non-declaration content inside :root could be an injection vector or simply broken CSS.
Source
Thrown at packages/desktop-client/src/style/customThemes.ts:365
function validateRootContent(rootContent: string): void {
// Check for forbidden at-rules inside :root
if (/@[a-z-]+/i.test(rootContent)) {
throw new Error(
'Theme CSS contains forbidden at-rules (@import, @media, @keyframes, etc.). Only CSS variable declarations are allowed inside :root { ... }.',
);
}
// Check for nested blocks
if (/\{/.test(rootContent)) {
throw new Error(
'Theme CSS contains nested blocks or additional selectors. Only CSS variable declarations are allowed inside :root { ... }.',
);
}
for (const decl of splitDeclarations(rootContent)) {
const colonIndex = decl.indexOf(':');
if (colonIndex === -1) {
throw new Error(`Invalid CSS declaration: "${decl}"`);
}
const property = decl.substring(0, colonIndex).trim();
// Property must start with --
if (!property.startsWith('--')) {
throw new Error(
`Invalid property "${property}". Only CSS custom properties (starting with --) are allowed.`,
);
}
// Validate property name format
// CSS custom property names must:
// - Start with --
// - Not be empty (not just --)
// - Not end with a dash
// - Contain only valid characters (letters, digits, underscore, dash, but not at start/end positions)
if (property === '--' || property === '-') {View on GitHub (pinned to d4334cb6e6)
Solutions
- Find the quoted declaration in the message and add the missing colon and value (e.g. --color-bg; -> --color-bg: #ffffff;)
- Remove any stray text or incomplete tokens from inside the :root block
- Keep only '--property: value;' declarations inside :root; move other CSS out (it is not allowed anyway)
- Validate the CSS with a quick regex per line before submitting: /^--[A-Za-z0-9_-]+\s*:\s*[^;]+;$/
Example fix
// before
:root {
--color-bg;
}
// after
:root {
--color-bg: #ffffff;
} Defensive patterns
Strategy: validation
Validate before calling
function hasValidDeclarations(css: string): boolean {
const root = css.match(/:root\s*\{([\s\S]*?)\}/);
if (!root) return false;
return root[1]
.split(';')
.map((d) => d.trim())
.filter(Boolean)
.every((d) => d.includes(':'));
} Type guard
const isDeclaration = (decl: string): boolean => decl.includes(':'); Try / catch
try {
const validated = validateThemeCss(userCss);
} catch (e) {
if (e instanceof Error && e.message.startsWith('Invalid CSS declaration')) {
showError('A declaration inside :root is missing its colon and value.');
}
} Prevention
- Write every :root entry as complete '--name: value;' pairs
- Lint theme CSS with a per-line declaration regex before saving
- Never build declarations by string concatenation without a colon check
- Strip stray text and empty fragments from the :root block
When it happens
Trigger: Calling validateThemeCss with a :root block containing a stray token without a colon, e.g. ':root { --color-bg; }' (missing value), leftover text like ':root { foo bar; --a: b; }', or a truncated/corrupted paste where the colon was dropped.
Common situations: Hand-edited custom theme CSS with a typo, a theme copied from docs where a value was accidentally deleted, or template interpolation that produced an empty value leaving '--x: ;' fragments or bare property names.
Related errors
- Theme CSS contains nested blocks or additional selectors. On
- Invalid property "${property}". Only CSS custom properties (
- Invalid property "${property}". Property name cannot be empt
- Invalid property "${property}". Property name cannot be empt
- Invalid property "${property}". Property name contains inval
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/71d78a001553f513.
Report an issue: GitHub.