Customize Business Pack theme

Custom CSS properties allow you to easily override the visual style of DotVVM Business Pack controls.

Naming conventions

The custom properties are divided into state, global and control-specific properties. All custom properties are constructed using the same convention.

For example, the properties might look like this: --bp-padding-horizontal_large. Each custom property name starts with the prefix --bp.

The second part of the custom property is the name of the property that is being modified with a suffix, such as padding-horizontal, text-color, or gap. The third part denotes the modifier, in our case large. The prefix and the name of the custom property are joined by a „-“ separator, and „_“ is used to append the modifier.

Override properties globally

All custom properties are set on the pseudo-element :root by default, which is at the same level as the html element in the page DOM.

In order to override the styles of DotVVM Business Pack easily, we recommend that you create a a custom CSS class (e.g. .bp-override), and apply it on the body element.

<body class="bp-override">
  <!-- Page content -->
</body>
.bp-override{
	--bp-padding-horizontal_large: 2em;
}

State properties

These custom properties configure the color appearance for various control states (e.g. success, danger, warning, hover, active, etc.). All custom properties configuring colors should use the HSL (hue, saturation, lightness) format.

There are also custom properties that represent multipliers (e.g. --bp-hover). Using these properties, you can easily manipulate with lightness of a specific color.

.bp-override {
  --bp-hover: .8;
  --bp-active: .6;
  --bp-accent: .7;
  --bp-lighten: 1.55;
  --bp-color_success: hsl(130, 40%, 54%);
  --bp-color_success-contrast: hsl(130, 40%, 100%);
  --bp-color_success-hover: hsl(130, 40%, calc(var(--bp-hover) * 54%));
  --bp-color_success-lighten: hsl(130, 40%, calc(var(--bp-lighten) * 54%));
  --bp-color_success-active: hsl(130, 40%, calc(var(--bp-active) * 54%));
  --bp-color_success-accent: hsl(130, 40%, calc(var(--bp-accent) * 54%));
  --bp-color_success-disabled: hsl(0, 0%, 80%);
}

Control-specific properties

Some controls, such as Alert or TabControl, use component-specific properties in addition to the global custom properties. You can recognize these properties easily - they start with the control name immediately after the –bp prefix (e.g. --bp-alert-background-color_warning).

.bp-override {
  --bp-popup-z-index: 100;
  --bp-modal-z-index: 1000;
  --bp-alert-border-left-width: .2em;
  --bp-alert-border-width: 0;
  --bp-alert-background-color_info: hsl(194, 66%, calc(var(--bp-lighten) * 61%));
  --bp-alert-color_info: hsl(194, 66%, calc(0.7 * 61%));
}

Customize styles on specific controls

If you want to keep the original style of DotVVM Business Pack but you need to customize the style for a particular control, you can simply override the custom property at the level of that control.

You can set property on the base CSS class generated by the control. You don't have to create your own CSS class like .my-button because the CSS class selector has higher specificity than the :root preudo-element.

.dotvvm-bp-button {
  --bp-padding-horizontal_large: 1rem;
  --bp-padding-vertical: .5rem;
  --bp-gap-m: 1em;
}

List of all custom CSS properties

:root {
  /* state properties */
  --bp-hover: .8;
  --bp-active: .6;
  --bp-accent: .7;
  --bp-lighten: 1.55;
  --bp-color_base: hsl(0, 0%, 100%);
  --bp-color_base-contrast: hsl(0, 0%, 10%);
  --bp-color_base-hover: hsl(0, 0%, calc(var(--bp-hover) * 100%));
  --bp-color_base-lighten: hsl(0, 0%, calc(var(--bp-lighten) * 100%));
  --bp-color_base-active: hsl(0, 0%, calc(var(--bp-active) * 100%));
  --bp-color_base-accent: hsl(0, 0%, calc(var(--bp-accent) * 100%));
  --bp-color_base-disabled: hsl(0, 0%, 80%);
  --bp-color_base-overlay: hsla(0, 0%, 65%, .8);
  --bp-color_primary: hsl(200, 99%, 51%);
  --bp-color_primary-contrast: hsl(200, 99%, 100%);
  --bp-color_primary-hover: hsl(200, 99%, calc(var(--bp-hover) * 51%));
  --bp-color_primary-lighten: hsl(200, 99%, calc(var(--bp-lighten) * 51%));
  --bp-color_primary-active: hsl(200, 99%, calc(var(--bp-active) * 51%));
  --bp-color_primary-accent: hsl(200, 99%, calc(var(--bp-accent) * 51%));
  --bp-color_primary-disabled: hsl(0, 0%, 80%);
  --bp-color_secondary: hsl(208, 7%, 46%);
  --bp-color_secondary-contrast: hsl(208, 7%, 100%);
  --bp-color_secondary-hover: hsl(208, 7%, calc(var(--bp-hover) * 46%));
  --bp-color_secondary-lighten: hsl(208, 7%, calc(var(--bp-lighten) * 46%));
  --bp-color_secondary-active: hsl(208, 7%, calc(var(--bp-active) * 46%));
  --bp-color_secondary-accent: hsl(208, 7%, calc(var(--bp-accent) * 46%));
  --bp-color_secondary-disabled: hsl(0, 0%, 80%);
  --bp-color_default: hsl(0, 0%, 86%);
  --bp-color_default-contrast: hsl(0, 0%, 0%);
  --bp-color_default-hover: hsl(0, 0%, calc(var(--bp-hover) * 86%));
  --bp-color_default-lighten: hsl(0, 0%, calc(var(--bp-lighten) * 86%));
  --bp-color_default-active: hsl(0, 0%, calc(var(--bp-active) * 86%));
  --bp-color_default-accent: hsl(0, 0%, calc(var(--bp-accent) * 86%));
  --bp-color_default-disabled: hsl(0, 0%, 80%);
  --bp-color_info: hsl(194, 66%, 61%);
  --bp-color_info-contrast: hsl(194, 66%, 100%);
  --bp-color_info-hover: hsl(194, 66%, calc(var(--bp-hover) * 61%));
  --bp-color_info-lighten: hsl(194, 66%, calc(var(--bp-lighten) * 61%));
  --bp-color_info-active: hsl(194, 66%, calc(var(--bp-active) * 61%));
  --bp-color_info-accent: hsl(194, 66%, calc(var(--bp-accent) * 61%));
  --bp-color_info-disabled: hsl(0, 0%, 80%);  
  --bp-color_warning: hsl(35, 84%, 62%);
  --bp-color_warning-contrast: hsl(35, 84%, 100%);
  --bp-color_warning-hover: hsl(35, 84%, calc(var(--bp-hover) * 62%));
  --bp-color_warning-lighten: hsl(35, 84%, calc(var(--bp-lighten) * 62%));
  --bp-color_warning-active: hsl(35, 84%, calc(var(--bp-active) * 62%));
  --bp-color_warning-accent: hsl(35, 84%, calc(var(--bp-accent) * 62%));
  --bp-color_warning-disabled: hsl(0, 0%, 80%);
  --bp-color_danger: hsl(2, 64%, 58%);
  --bp-color_danger-contrast: hsl(2, 64%, 100%);
  --bp-color_danger-hover: hsl(2, 64%, calc(var(--bp-hover) * 58%));
  --bp-color_danger-lighten: hsl(2, 64%, calc(var(--bp-lighten) * 58%));
  --bp-color_danger-active: hsl(2, 64%, calc(var(--bp-active) * 58%));
  --bp-color_danger-accent: hsl(2, 64%, calc(var(--bp-accent) * 58%));
  --bp-color_danger-disabled: hsl(0, 0%, 80%);
  --bp-color_success: hsl(130, 40%, 54%);
  --bp-color_success-contrast: hsl(130, 40%, 100%);
  --bp-color_success-hover: hsl(130, 40%, calc(var(--bp-hover) * 54%));
  --bp-color_success-lighten: hsl(130, 40%, calc(var(--bp-lighten) * 54%));
  --bp-color_success-active: hsl(130, 40%, calc(var(--bp-active) * 54%));
  --bp-color_success-accent: hsl(130, 40%, calc(var(--bp-accent) * 54%));
  --bp-color_success-disabled: hsl(0, 0%, 80%);
  --bp-color_transparent: hsla(0, 0%, 0%, 0);
  --bp-color_base-contrast: hsl(0, 0%, 0%);
  --bp-color_focus: hsla(41, 95%, 54%, 1);
  --bp-color_error: hsl(2, 80%, 58%);

  /* global properties */
  --bp-border-color: hsl(0, 0%, 64%);
  --bp-border-color_disabled: var(--bp-font-color_disabled);
  --bp-background-color: var(--bp-color_base);
  --bp-background-color_hover: var(--bp-color_base-hover);
  --bp-border-width: 1px;
  --bp-border-radius: 0;
  --bp-box-shadow_focus: 0 0 0 1px var(--bp-color_base), 0 0 0 3px var(--bp-color_focus);
  --bp-box-shadow_error: 0 0 0 1px var(--bp-color_base), 0 0 0 3px var(--bp-color_error);
  --bp-control-font-size: 1rem;
  --bp-font-color: var(--bp-color_base-contrast);
  --bp-font-color_active: var(--bp-color_base);
  --bp-font-color_disabled: hsl(0, 0%, calc(0.5 * 100%));
  --bp-font-color_disabled-active: hsl(0, 0%, calc(0.4 * 100%));
  --bp-font-color_placeholder: hsl(0, 0%, calc(0.6 * 100%));
  --bp-font-size: .8em;
  --bp-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
  --bp-gap-s: 0.2em;
  --bp-gap-m: 0.4em;
  --bp-gap-xl: 1em;
  --bp-line-height: 1.4;
  --bp-padding-horizontal: var(--bp-gap-m);
  --bp-padding-horizontal_large: var(--bp-gap-xl);
  --bp-padding-vertical: var(--bp-gap-s);
  --bp-padding: var(--bp-padding-vertical) var(--bp-padding-horizontal);
  --bp-text-decoration: none;

  /* control-specific properties */
  --bp-alert-background-color_info: hsl(194, 66%, calc(var(--bp-lighten) * 61%));
  --bp-alert-color_info: hsl(194, 66%, calc(0.7 * 61%));
  --bp-alert-background-color_warning: hsl(35, 84%, calc(var(--bp-lighten) * 62%));
  --bp-alert-color_warning: hsl(35, 84%, calc(0.7 * 62%));
  --bp-alert-background-color_danger: hsl(2, 64%, calc(var(--bp-lighten) * 58%));
  --bp-alert-color_danger: hsl(2, 64%, calc(0.7 * 58%));
  --bp-alert-background-color_success: hsl(130, 40%, calc(var(--bp-lighten) * 54%));
  --bp-alert-color_success: hsl(130, 40%, calc(0.7 * 54%));
  --bp-alert-border-left-width: .2em;
  --bp-alert-border-width: 0;
  --bp-calendar-item-size: calc((var(--bp-font-size) * 1.7) + (var(--bp-padding-horizontal) * 2));
  --bp-checkbox-size: 1.2em;
  --bp-data-pager_border-width: 0;
  --bp-form-item-border-color: var(--bp-border-color);
  --bp-form-item-width: 14em;
  --bp-footer_border-bottom-width: 0;
  --bp-grid-view_border-bottom-width: 0;
  --bp-header_border-bottom-width: 0;
  --bp-modal-z-index: 1000;
  --bp-picker-background-color: var(--bp-color_base);
  --bp-popup-max-height: 20em;
  --bp-popup-z-index: 100;
  --bp-slider-grip_width-multiplier: .4em;
  --bp-slider-grip_height-multiplier: 1em;
  --bp-tab-control-item_border-bottom-color: var(--bp-color_transparent);
  --bp-tab-control-item_border-width: 0;
  --bp-tab-control-nav_gap: var(--bp-gap-s);
  --bp-tab-control_border-bottom-multiplier: 2;
}

See also