actualbudget/actual · error
Theme CSS contains nested blocks or additional selectors. On
Error message
Theme CSS contains nested blocks or additional selectors. Only CSS variable declarations are allowed inside :root { ... }. What it means
validateRootContent rejects any '{' character inside the :root block, meaning nested blocks or additional selectors were found. Custom theme CSS must be a flat list of CSS variable declarations inside a single :root block; nested rules or sibling selectors are not supported.
Source
Thrown at packages/desktop-client/src/style/customThemes.ts:357
}
// ─── :root block validation ─────────────────────────────────────────────────
/**
* Validate the content inside a :root { ... } block.
* Only CSS custom properties (--*) with safe values are allowed.
*/
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.`,
);
}View on GitHub (pinned to d4334cb6e6)
Solutions
- Delete nested rules and additional selectors; keep only flat `--variable: value;` declarations in :root.
- Fix brace balance in actual.css so the :root block contains no '{'.
- Style component markup is not themeable via custom themes — restrict your theme to variable overrides only.
Example fix
// before
:root { --color-bg: #fff; .header { color: red; } }
// after
:root { --color-bg: #fff; } Defensive patterns
Strategy: validation
Validate before calling
function rootIsFlat(css) {
const root = css.match(/:root\s*{([\s\S]*?)}/)?.[1] ?? '';
return !root.includes('{');
}
if (!rootIsFlat(css)) throw new Error('nested block inside :root'); Try / catch
try {
await installTheme(css);
} catch (err) {
if ((err as Error).message.includes('nested blocks or additional selectors')) {
// ask the author to flatten :root to variable declarations only
} else throw err;
} Prevention
- Author themes as a flat :root block only; no nested rules or extra selectors.
- Validate brace balance of actual.css before publishing.
- Avoid preprocessor nesting when generating theme CSS.
When it happens
Trigger: A theme's actual.css contains nested braces inside :root (e.g. `:root { --x: 1; .btn { color: red; } }`) or unbalanced/extra braces that leave a '{' in the extracted root content.
Common situations: Theme authors pasting full stylesheets (component rules) into :root; malformed CSS with mismatched braces so extra blocks appear inside the root region; preprocessor output with nesting.
Related errors
- Invalid font-family value for "${property}": value must not
- Invalid font-family value for "${property}": empty font name
- Invalid value "${trimmedValue}" for property "${property}".
- Invalid CSS declaration: "${decl}"
- Invalid font-family value for "${property}": function calls
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/b4cd215b44e98c50.
Report an issue: GitHub.