{"record":{"id":"b4cd215b44e98c50","repo":"actualbudget/actual","slug":"theme-css-contains-nested-blocks-or-additional-sel","errorCode":null,"errorMessage":"Theme CSS contains nested blocks or additional selectors. Only CSS variable declarations are allowed inside :root { ... }.","messagePattern":"Theme CSS contains nested blocks or additional selectors\\. Only CSS variable declarations are allowed inside :root (.+?)\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/desktop-client/src/style/customThemes.ts","lineNumber":357,"sourceCode":"}\n\n// ─── :root block validation ─────────────────────────────────────────────────\n\n/**\n * Validate the content inside a :root { ... } block.\n * Only CSS custom properties (--*) with safe values are allowed.\n */\nfunction validateRootContent(rootContent: string): void {\n  // Check for forbidden at-rules inside :root\n  if (/@[a-z-]+/i.test(rootContent)) {\n    throw new Error(\n      'Theme CSS contains forbidden at-rules (@import, @media, @keyframes, etc.). Only CSS variable declarations are allowed inside :root { ... }.',\n    );\n  }\n\n  // Check for nested blocks\n  if (/\\{/.test(rootContent)) {\n    throw new Error(\n      'Theme CSS contains nested blocks or additional selectors. Only CSS variable declarations are allowed inside :root { ... }.',\n    );\n  }\n\n  for (const decl of splitDeclarations(rootContent)) {\n    const colonIndex = decl.indexOf(':');\n    if (colonIndex === -1) {\n      throw new Error(`Invalid CSS declaration: \"${decl}\"`);\n    }\n\n    const property = decl.substring(0, colonIndex).trim();\n\n    // Property must start with --\n    if (!property.startsWith('--')) {\n      throw new Error(\n        `Invalid property \"${property}\". Only CSS custom properties (starting with --) are allowed.`,\n      );\n    }","sourceCodeStart":339,"sourceCodeEnd":375,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/desktop-client/src/style/customThemes.ts#L339-L375","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\n:root { --color-bg: #fff; .header { color: red; } }\n// after\n:root { --color-bg: #fff; }","handlingStrategy":"validation","validationCode":"function rootIsFlat(css) {\n  const root = css.match(/:root\\s*{([\\s\\S]*?)}/)?.[1] ?? '';\n  return !root.includes('{');\n}\nif (!rootIsFlat(css)) throw new Error('nested block inside :root');","typeGuard":null,"tryCatchPattern":"try {\n  await installTheme(css);\n} catch (err) {\n  if ((err as Error).message.includes('nested blocks or additional selectors')) {\n    // ask the author to flatten :root to variable declarations only\n  } else throw err;\n}","preventionTips":["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."],"tags":["css","validation","theme","syntax"],"backgroundTag":"css-value-validation-failed","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}