Theme Configuration and custom branding
Overview
This guide provides instructions for modifying and creating themes, focusing on style for application and visual identity.
Contents
- Basic Theme and Functionality Overview
- Brand Assets Overview
- Client Theme Customization
- Theme Key Descriptions
- Custom Styles
- Credits
- Badges
Basic Theme and Functionality Overview
To automate the creation of CSS variables from a structured map of values, we use SASS mixins in our approach. Here's a detailed explanation of how it works:
-
Defining Variables: Begin by creating a map of variables in SASS. This map contains all the CSS properties and values that you want to convert into CSS variables.
-
Generating CSS Variables: The
generate-css-varsmixin processes this map and generates the corresponding CSS variables with the specified prefix. This mixin ensures that all variables are properly formatted and nested. -
Setting Variables: The
set-css-varmixin is responsible for setting the actual CSS variables in the generated CSS code. It ensures each variable is formatted correctly. -
Creating Prefixes: The
build-prefixfunction helps create appropriate prefixes for the variables, ensuring they are unique and correctly placed.
With this foundation, we can now delve into the specifics of how to implement and configure this feature in your project.
Code Structure
-
generate-css-vars($vars, $config): -
This mixin is the entry point that takes a variable map
$varsand a configuration object$config. -
It creates a prefix from the configuration object and calls the
parse-varsmixin. -
parse-vars($map, $prefix, $is-first-iteration: true): -
Recursively traverses the variable map.
- If a value in the map is another map, it calls itself recursively with an updated prefix.
-
If a value is a simple type (e.g., string, number, boolean), it calls the
set-css-varmixin. -
set-css-var($value, $prefix): -
Sets the CSS variable in the format
--prefix: value;. -
build-prefix($prefix, $name, $is-first-iteration): -
A function to create the variable prefix.
- Uses
__as a separator for the first iteration. - For subsequent iterations, it uses
-or no separator if the name indicates a subcomponent.
Brand Assets Overview
Default brand files are stored in app/frontend/assets/images/brand/.
CSS variables that reference brand files are defined in app/frontend/src/shared/stylesheets/easy/themes/_brand-assets.scss. The file uses Vite alias (@assets) so assets are resolved and hashed by Vite.
| CSS variable | Default file | Usage |
|---|---|---|
--logo-url |
logo.svg |
Main color logo |
--logo-url--black |
logo--bw.svg |
Black/white logo |
--logo-url--inverted |
logo--inverted.svg |
Inverted/light logo |
--logo-url--mark |
logo--mark.svg |
Symbol-only mark |
--logo-url--mark-black |
logo--mark-bw.svg |
Symbol black/white |
--logo-url--mark-inverted |
logo--mark-inverted.svg |
Symbol inverted |
--logo-url--mutation |
logo--mutation.svg |
Alternate logo |
--login-bg-url |
login-bg--default.svg |
Login background |
In addition to CSS-driven assets, the backend also uses:
favicon.icofor browser tab iconlogo.pngfor mailer inline logo attachment
Client Theme Customization
Use this section to override brand assets for a client deployment without changing the default files.
Full rebrand checklist
A complete rebrand requires all of the following. Missing any item leaves that part of the UI using the default Easy brand.
- [ ]
app/frontend/assets/custom_brand/favicon.ico— browser tab icon (16, 32, 48 px sizes) - [ ]
app/frontend/assets/custom_brand/logo.png— mailer inline logo (320 × 89 px recommended) - [ ]
app/frontend/assets/custom_brand/logo.svg— main color logo - [ ]
app/frontend/assets/custom_brand/logo--bw.svg— black/white logo - [ ]
app/frontend/assets/custom_brand/logo--inverted.svg— inverted/light logo - [ ]
app/frontend/assets/custom_brand/logo--mark.svg— symbol mark - [ ]
app/frontend/assets/custom_brand/logo--mark-bw.svg— symbol mark black/white - [ ]
app/frontend/assets/custom_brand/logo--mark-inverted.svg— symbol mark inverted - [ ]
app/frontend/assets/custom_brand/logo--mutation.svg— alternate logo - [ ]
app/frontend/assets/custom_brand/login-bg--default.svg— login background - [ ]
brand-enabled: trueset in_theme--brand.scss— required to output any brand CSS variables - [ ] CSS asset variables overridden in
_theme--brand.scss(SVG files only; raster files above are picked up automatically) - [ ]
app/frontend/assets/custom_brand/<FontName>.ttf— brand font file (required by fallback source) - [ ]
app/frontend/assets/custom_brand/<FontName>.woff2— brand font file (recommended primary source) - [ ] Brand fonts loaded via
typo.loadFontFace(..., true)in_theme--brand.scss - [ ]
--easy__font-family--baseand--easy__font-family--headingset in_theme--brand.scss - [ ] Brand color keys (
-main,-front,-inverse) set in$theme__map--brand
Override brand files
Upload custom brand files to:
app/frontend/assets/custom_brand/
Automatically reused files
If the following files exist in app/frontend/assets/custom_brand/, they are used automatically:
favicon.ico— browser tab iconlogo.png— mailer inline logo attachment
These filenames must remain unchanged. If either file is not present, the application uses the default version from:
app/frontend/assets/images/brand/
Frontend brand assets (SVG / login background)
Other frontend brand assets, such as SVG logos or the login background, must be mapped manually through CSS variables in:
app/frontend/src/shared/stylesheets/easy/themes/_theme--brand.scss
Note: The CSS variable overrides only take effect when
brand-enabled: trueis set in$theme__map--brand. Without it the:rootblock insidegenerate-brand-themes()is never emitted and all logo/background variables remain at their defaults.
Process:
- Upload the new files to
app/frontend/assets/custom_brand/ - Set
brand-enabled: truein$theme__map--brand - Override the asset variables in
_theme--brand.scss
Example:
// app/frontend/src/shared/stylesheets/easy/themes/_theme--brand.scss
:root {
--logo-url: url('@assets/custom_brand/logo.svg');
--logo-url--black: url('@assets/custom_brand/logo--bw.svg');
--logo-url--inverted: url('@assets/custom_brand/logo--inverted.svg');
--logo-url--mark: url('@assets/custom_brand/logo--mark.svg');
--logo-url--mark-black: url('@assets/custom_brand/logo--mark-bw.svg');
--logo-url--mark-inverted: url('@assets/custom_brand/logo--mark-inverted.svg');
--logo-url--mutation: url('@assets/custom_brand/logo--mutation.svg');
--login-bg-url: url('@assets/custom_brand/login-bg--default.svg');
}
Override brand fonts
To use a client-specific font family, place font files in:
app/frontend/assets/custom_brand/
Then wire them in:
app/frontend/src/shared/stylesheets/easy/themes/_theme--brand.scss
Process:
- Upload font files to
app/frontend/assets/custom_brand/(keep filenames aligned with the mixin calls, for exampleComicRelief-Regular.ttf). - Import typography mixins:
@use "../../../../design_system/shared/stylesheets/foundations/typography/_abstracts/_typography.mixins" as typo;
- Inside
@if map.get($theme__map--brand, brand-enabled), load all required font faces usingtypo.loadFontFace(..., true). - Apply the font family globally and set font CSS variables used by the theme.
Example:
@mixin generate-brand-themes() {
@if map.get($theme__map--brand, brand-enabled) {
@include typo.loadFontFace('Comic Relief', 'ComicRelief-Regular', 400, normal, true);
@include typo.loadFontFace('Comic Relief', 'ComicRelief-Bold', 700, normal, true);
html {
font-family: 'Comic Relief', sans-serif !important;
}
:root {
--easy__font-family--base: 'Comic Relief', sans-serif;
--easy__font-family--heading: 'Comic Relief', sans-serif;
}
}
}
Note:
loadFontFacepoints to bothwoff2andttfsources under@assets/custom_brand. Keep both formats when possible for best compatibility and loading performance.
Override theme color variables
Modify app/frontend/src/shared/stylesheets/easy/themes/_theme--brand.scss:
- Keep map name
$theme__map--brand(required by theme generation). - Set
brand-enabled: trueto output brand CSS variables. - Override
colorkeys (-main,-front,-inverse) as needed.
// app/frontend/src/shared/stylesheets/easy/themes/_theme--brand.scss
@use '../utils/helpers/functions' as *;
@use '../utils/helpers/variables/config' as *;
@use '../utils/abstracts/css-variables/create' as *;
@use 'sass:map';
// This one needs to exist as it is used in the mailer theme
$theme__map--brand: (
brand-enabled: true,
color: (
-main: (
'': prep-color-tints(#75b849),
),
-front: (
'': prep-color-tints(#ffffff),
),
-inverse: (
'': prep-color-tints(#1f2937),
),
),
);
$theme__map--brand-dark: (
color: (
-main: (
'': prep-color-tints(#5f38c3),
),
),
);
@mixin generate-brand-themes() {
@if map.get($theme__map--brand, brand-enabled) {
:root {
@include generate-css-vars($theme__map--brand, map.get($config, global));
}
}
:root[data-theme*='theme-dark'] {
@include generate-css-vars($theme__map--brand-dark, map.get($config, global));
}
}
Dark Theme
To specify settings for the Dark theme, create a map named $theme__map--brand-dark in _theme--brand.scss and place
it under the default brand map.
Note: Changes will not be reflected in the contrast theme _theme--contrast.scss, which is specially designed for
visually impaired users. This theme is optimized for maximum contrast and readability.
Customizing Account Submenu (Optional)
If you need to adjust account submenu, add or remove buttons, you can do so in menus.rb
```ruby menus.rb
plugins/easyproject/easy_plugins/modification_client/lib/modification_client/menus.rb`
Redmine::MenuManager.map :account_menu do |menu| menu.delete :theme_dark_toggle menu.delete :theme_contrast_toggle end
Then require the `menus.rb` in initializers.
```ruby 02_menu_manager.rb
# plugins/easyproject/easy_plugins/modification_client/config/initializers/02_menu_manager.rb`
Rails.application.config.after_initialize do
require "modification_client/menus"
end
Theme Key Descriptions
Here are detailed descriptions of the keys in the $theme__map--default map, which influence various aspects of the
application's appearance:
Key Elements:
Logo
The paths to logos are already defined in the default theme, so there is no need to redefine them.
Font
The font style is defined in the map. Changing the base font size (key font-size) can affect various dimensions across the application, such as spacing, heights, and widths of individual elements.
Colors
Includes basic colors and their shades for text, background, and other elements.
-
Key
-main: The main color affects the color scheme of buttons, action elements, hover effects, links, menus, notifications, active elements, the main top bar, and borders of various elements, such as secondary buttons. -
Key
-front: The color of white backgrounds, module backgrounds, and form elements' backgrounds. This color often mixes with other colors, such as background colors for menus. -
Key
-inverse: Used for reverse color logic. It is light for the default theme and dark for the Dark theme. It is used in elements where the color needs to be adjusted based on the background color. -
Key
-mixed: Mixes the -front and -background keys, used in specific application parts such as mind maps, sidebars, and tables. -
Key
-text: Influences text throughout the application and contains text categories such as:
muted: Affects the color of text in disabled form elements, various inactive elements, separators, and some links.\ inverted: Applied in reverse color logic, similar to inverse, but for text.\ light: Uses the color from inverted.\ dark: Basic text color.
Color Palette
Key palette: Contains the base palette -base and other color palettes. The values inside influence multicolored components and are interconnected. They can affect elements like trend cards, charts, tables, mind maps, and WBS.
Other Elements
All components already have all parameters set, such as colors, etc. If you want to further customize the components, you can use additional keys from the default map and adjust them as needed.
When adjusting various sizes, spacings, and areas around elements, it's important to focus on functions that convert units from px to rem, so components adapt correctly based on font size. These functions include:
ms(): This function defines basic spacing sizes. The most commonly used values are ms(0) for basic spacing, ms(2)
for
medium spacing, and ms(-2) for small spacing. The basic scale ranges from -6 to 12.
px(): This function converts the specified number of pixels to rem and can include negative numbers.
Custom Styles
If you want to add your own styles, always place them in the same file where you define the maps and place them at the end of page.