{"record":{"id":"5d545a4f1a45e253","repo":"actualbudget/actual","slug":"invalid-property-property-only-css-custom-pr","errorCode":null,"errorMessage":"Invalid property \"${property}\". Only CSS custom properties (starting with --) are allowed.","messagePattern":"Invalid property \"(.+?)\"\\. Only CSS custom properties \\(starting with --\\) are allowed\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/desktop-client/src/style/customThemes.ts","lineNumber":372,"sourceCode":"\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    }\n\n    // Validate property name format\n    // CSS custom property names must:\n    // - Start with --\n    // - Not be empty (not just --)\n    // - Not end with a dash\n    // - Contain only valid characters (letters, digits, underscore, dash, but not at start/end positions)\n    if (property === '--' || property === '-') {\n      throw new Error(\n        `Invalid property \"${property}\". Property name cannot be empty or contain only dashes.`,\n      );\n    }\n\n    // Check for invalid characters in property name (no brackets, spaces, special chars except dash/underscore)\n    // Property name after -- should only contain: letters, digits, underscore, and dashes (not consecutive dashes at start/end)","sourceCodeStart":354,"sourceCodeEnd":390,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/desktop-client/src/style/customThemes.ts#L354-L390","documentation":"Only CSS custom properties (variables) are permitted inside the :root block of a custom theme. validateRootContent splits declarations and rejects any whose property name does not begin with the '--' prefix. This restricts themes to variable overrides so arbitrary CSS rules cannot be injected.","triggerScenarios":"Calling validateThemeCss with a :root block containing a regular CSS property such as 'color: red;', 'margin: 0;', or a selector-like token before the colon, e.g. ':root { color: red; --a: b; }'.","commonSituations":"Pasting a full CSS stylesheet into the custom theme editor instead of just variable overrides, copying rules from a theme that supported full CSS, or misunderstanding that the theme field takes variables only.","solutions":["Prefix the property with '--' if it was intended as a variable, or rename it to a custom property","Remove all regular CSS properties from :root; only --variable declarations are allowed","Move global styling needs out of the theme CSS field — it is not supported by design","Pre-validate: split on ';' and assert every chunk starts with '--'"],"exampleFix":"// before\n:root {\n  color: red;\n}\n// after\n:root {\n  --color-text: red;\n}","handlingStrategy":"validation","validationCode":"function onlyCustomProperties(css: string): boolean {\n  const root = css.match(/:root\\s*\\{([\\s\\S]*?)\\}/);\n  if (!root) return false;\n  return root[1]\n    .split(';')\n    .map((d) => d.trim())\n    .filter(Boolean)\n    .every((d) => d.split(':')[0].trim().startsWith('--'));\n}","typeGuard":"const isCustomPropertyDecl = (decl: string): boolean =>\n  decl.includes(':') && decl.split(':')[0].trim().startsWith('--');","tryCatchPattern":"try {\n  const validated = validateThemeCss(userCss);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('Only CSS custom properties')) {\n    showError('Theme CSS may only contain --variable declarations inside :root.');\n  }\n}","preventionTips":["Never paste regular CSS rules into the theme field","Educate users that themes are variable overrides only","Pre-process pasted CSS to extract only --variable lines","Add editor hints/placeholders showing only --variable syntax"],"tags":["css","validation","custom-themes"],"backgroundTag":"invalid-css-custom-property","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}