Back to Wekan

Design: Change color

docs/Design/Page/Theme.md

10.415.5 KB
Original Source

Design: Change color

One design, one implementation, for every place a theme colour is chosen: the swatches grouped by category, the "Default theme" row, the custom-colour wheels and the preview. One template renders all of them — the way Table is one table page for every table and Left Menu is one menu for every Admin Panel page.

A place that offers "Change color" does not write markup. It renders the shared template and says whose theme is being set:

jade
+themeColorPicker(scope="board")    //- Board Settings / Change Color
+themeColorPicker(scope="global")   //- Member Settings / Change color
+themeColorPicker(scope="admin")    //- Admin Panel / Settings / Visibility / Change color

scope is the only difference between them.

File PathFile TypeDescription
client/components/main/themeColorPicker.jade.jade templateThe picker: the "Default theme" row, the category groups of swatches, the custom-colour wheels and the preview.
client/components/main/themeColorPicker.js.js componentReads the current value for the scope, applies a click immediately, and writes it where that scope says.
models/lib/themeCategories.js.js module, pure helpersThe categories, their order, which allow a custom colour and how many.
models/metadata/colors.js.js moduleBOARD_COLORS — the theme names themselves.
client/components/main/globalThemeColor.js.js startupApplies the winning theme to <body> and the CSS variables — the layering below.
client/components/sidebar/sidebar.jade.jade templateBoard Settings / Change Color — scope="board".
client/components/users/userHeader.jade.jade templateMember Settings / Change color — scope="global".
client/components/settings/settingBody.jade.jade templateAdmin Panel / Settings / Visibility / Change color — scope="admin".
server/models/users.js.js methodssetGlobalThemeColor — the user's own override.
server/methods/tenant.js.js methodsgetAdminThemeColor / setAdminThemeColor — the site theme, and which document it lands in.
models/lib/tenantAdmin.js.js module, pure helpersthemeTarget() — the instance's theme, or one Organization's.
docs/Theme/Theme.md.md guideWhat the themes look like, and the custom-colour categories.

The order of themes

Weakest first. Each layer replaces the one under it, and only where it is set:

LayerSet inStored in
1Default themenothing; WeKan's own stylesheet
2Site themeAdmin Panel / Settings / Visibility / Change colorSettings.themeColor, or an Organization's orgThemeColor on its own hosts
3User's ownMember Settings / Change coloruser.profile.globalThemeColor

A board's own colour is not one of these layers: it is what a board looks like. It owns the board page, so the site theme is not applied there — but a user's own override is, because they asked for it everywhere.

Layer 2 is one setting with two possible homes, and the server picks the home (themeTarget() in models/lib/tenantAdmin.js):

  • the site admin sets the instance's theme — the one every Organization that has not chosen its own inherits, which is why their pane says so under the title;
  • an Organization's own admin sets that Organization's theme, which replaces the instance's on that Organization's hosts. It reaches nothing else.

The client never decides that. It calls getAdminThemeColor / setAdminThemeColor, and the server answers for whoever is asking — see Multitenancy (D.9) for how an Organization's value gets published in the instance's place.

Places that use this design

PlaceScopeWritesClearing it means
Board Settings / Change Colorboardboard.color + board.customThemeColors— (a board always has a colour, so there is no "Default theme" row)
Member Settings / Change colorglobalsetGlobalThemeColorfall back to the site theme, and to per-board colours
Admin Panel / Settings / Visibility / Change coloradminsetAdminThemeColorfall back to WeKan's default theme (or, for an Organization, to the instance's)

What the picker guarantees

  • No Save button anywhere. Clicking a swatch applies it immediately, and a custom-colour wheel applies on change (not on every intermediate value of a drag). The three places behave identically because it is the same code.
  • The same categories, in the same order, with the same labels (THEME_CATEGORY_ORDER), and the colour wheels appear only for the categories that take a custom colour — one for flat, two for clear (a gradient).
  • A "Default theme" row wherever there is something to fall back to, i.e. every scope except board.
  • The value is validated on the server. A theme name must be one of BOARD_COLORS and a custom colour must be a hex colour; anything else is refused rather than stored and rendered as a class.

Adding another place that chooses a theme

  1. Render +themeColorPicker(scope="…") — no markup of your own.
  2. Add the scope to SCOPES in themeColorPicker.js, with one branch in readCurrent() and one in applySelection().
  3. Say where it sits in the order of themes, in the table above.

If a new place needs a different-looking picker, change this template so every place gets it. That is the point of the design.