Skip to content

Token editor colours do not apply for users who chose the dark theme #698

Description

@rubenvdlinde

A colour an admin sets in the token editor shows for users on "System default", but not for a user who picked the dark theme in their personal settings. Two dark users then see different colours.

Where, at development fd992ea:

  • lib/Service/CustomOverridesService.php:232-237 writes every override into one :root {} block.
  • lib/Service/CustomOverridesService.php:249-278 adds !important to each declaration to win the cascade.
  • Nextcloud server apps/theming/lib/Service/ThemeInjectionService.php:52-59 loads each chosen theme as a non-plain stylesheet.
  • Nextcloud server apps/theming/lib/Controller/ThemingController.php:433 scopes that stylesheet to [data-theme-<id>] { ... }.
  • Nextcloud server core/templates/layout.user.php:54-56 puts data-theme-dark on <body>.
    return $header . ':root {}' . PHP_EOL;
    }
    $lines = $this->buildDeclarationLines(tokens: $tokens);
    return $header . ':root {' . PHP_EOL . implode(PHP_EOL, $lines) . PHP_EOL . '}' . PHP_EOL;
    }//end buildCss()
    /**
    * Build individual CSS declaration lines from a token map.
    *
    * @param array<string, string> $tokens Token name => value pairs.
    *
    * @return array<string> List of CSS declaration lines.
    *
    * @spec openspec/changes/retrofit-2026-05-24-annotate-nldesign/tasks.md#task-33
    */
    private function buildDeclarationLines(array $tokens): array {
    $lines = [];
    foreach ($tokens as $name => $value) {
    // Reject any value containing CSS injection characters.
    if (preg_match('/[{};]|\/\*|\*\//', $value) === 1) {
    continue;
    }
    $safeValue = str_replace(["\n", "\r", ';', '{', '}', '/*', '*/'], '', $value);
    $safeName = preg_replace('/[^a-zA-Z0-9\-]/', '', $name);
    // Strip any pre-existing !important the caller may have included; it is
    // re-applied uniformly below so the round-trip stays canonical.
    $safeValue = trim(preg_replace('/\s*!\s*important\s*$/i', '', $safeValue));
    // Emit each user override with !important so it wins the cascade.
    // The nldesign design-system stylesheets (theme/overrides/element-overrides.css)
    // re-declare every editable token with !important, and Nextcloud core theming
    // also overrides tokens like --color-primary. custom-overrides.css loads last,
    // but without !important it loses the cascade and the saved theme has no visible
    // effect. !important here is scoped to user-set overrides only (this file only
    // ever contains tokens the admin explicitly set in the editor), so it does not
    // blanket-!important the full token registry.
    $lines[] = ' ' . $safeName . ': ' . $safeValue . ' !important;';
    }//end foreach
    return $lines;
    }//end buildDeclarationLines()
    /**

!important only wins between declarations on the same element. Core declares the dark colours on body, so body and everything inside it use core's value and never inherit the one on :root. For "System default", core serves the dark values on :root inside a media query, where the override does win. An explicitly chosen light theme is served the same non-plain way, so it can lose the override too.

Specified fix: openspec/changes/authoring-token-value-types (requirement "Editor overrides apply the same way for every dark user", task 2.2).

Found by the OpenSpec pass on 27 Sep 2026 and re-read at fd992ea on 28 Sep.

Live check: set "Primary color" in the editor, then compare Files for a user on "System default" with a dark OS and a user who chose "Dark theme".

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingtriageAwaiting triage

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions