Skip to main content

Theming and Theme Utils Documentation

Prerequisites

The hosting app uses styled-components and provides theme through ThemeProvider. The current theme is available with the useTheme React hook:

import { useTheme } from "styled-components";
...
// Must be called inside a react component
const theme = useTheme();

The type definition for the theme object can be imported from:

import type { IThemeProps } from "@invent/shared-types";

Theme Example

Here's an example theme structure:

const ThemeExample = {
name: "core-light",
label: "Light",
fonts: {
primary: {
family: "DM Sans",
sources: [
{
link: "/templates/assets/fonts/DMSans-Regular.ttf",
weight: 400,
format: "truetype",
},
{
link: "/templates/assets/fonts/DMSans-Bold.ttf",
weight: 700,
format: "truetype",
},
],
},
},
colors: {
primary: "rgb(19, 183, 209)",
secondary: "rgb(32, 78, 110)",
tertiary: "rgb(237, 250, 252)",
quaternary: "rgb(45, 56, 66)",
background: "rgb(247, 247, 250)",
surface: "rgb(255, 255, 255)",
danger: "rgb(243, 51, 9)",
warning: "rgb(231, 115, 8)",
success: "rgb(12, 166, 96)",
onPrimary: "rgb(255, 255, 255)",
onSecondary: "rgb(255, 255, 255)",
onBackground: "rgb(219, 223, 228)",
onSurface: "rgb(45, 56, 66)",
onDanger: "rgb(255, 255, 255)",
link: "rgb(0, 159, 219)",
linkHover: "rgb(18, 135, 176)",
linkCurrent: "rgb(18, 135, 176)",
linkVisited: "rgb(228, 159, 255)",
shadows: "rgb(24, 31, 36)",
},
rgl: {
cols: {
lg: 36,
md: 24,
sm: 12,
xs: 6,
},
},
sizeUnit: 4,
widgetsBorderRadius: 6,
borderRadius: 3,
typography: {
Heading1: {
fontSize: "26px",
fontWeight: 700,
lineHeight: "32px",
},
Heading2: {
fontSize: "22px",
fontWeight: 700,
lineHeight: "24px",
},
// ... more typography definitions
},
chartColors: [
"rgb(118, 203, 222)",
"rgb(0, 57, 117)",
// ... more chart colors
],
type: "Light",
};

Theme Utils

The following theme utils can be imported from @invent/wl-ui-kit:

Color Utils

getColor(colorName, shade?)

Gets a color from the theme, with optional shade mixing using color-mix CSS function.

// Basic color
color: ${getColor('primary')}; // Returns: rgb(19, 183, 209)

// Color with shade
color: ${getColor('primary', 'mediumEmphasis')}; // Returns: color-mix(in srgb, rgb(19, 183, 209) 72%, rgb(255, 255, 255))

getColorShade(color, shade)

Gets a color with specified shade using RGB with alpha transparency.

// Color with shade
color: ${getColorShade('primary', 'mediumEmphasis')}; // Returns: rgb(19 183 209 / 0.72)

getChartColor(index)

Gets a chart color by index from the theme's chartColors array.

background-color: ${getChartColor(0)}; // Returns first chart color

getGradient(color, gradientMap)

Generates gradient color stops for CSS gradients.

background: linear-gradient(${getGradient('primary', [
{ opacity: 0, gradientSlicePercent: 0 },
{ opacity: 1, gradientSlicePercent: 100 }
])}); // Returns: rgb(19 183 209 / 0) 0%,rgb(19 183 209 / 1) 100%

Size Utils

getSizeUnit()

Gets the base size unit from theme (default: 4px).

line-height: ${getSizeUnit}; // Returns: 4

getSizeBy(multiplier, componentSize?)

Calculates size based on multiplier and optional component size modifier.

// Basic size
padding: ${getSizeBy(2)}; // Returns: 8px (4 * 2)

// With size modifier
margin: ${getSizeBy(2, 'large')}; // Returns: 11px (4 * 2 * 1.33)

getBorderThickness()

Gets border thickness from theme's widgetBorderThickness property.

border-width: ${getBorderThickness()}; // Returns thickness based on theme

Typography and Font Utils

getFontFamily(fontKey)

Gets font family from theme fonts, with sans-serif fallback.

font-family: ${getFontFamily('primary')}; // Returns: 'DM Sans, sans-serif'

getTypography(name)

Gets typography object from theme's typography definitions.

${getTypography('Heading1')} // Returns typography CSS properties

injectFontFaces(baseUrl?)

Injects @font-face declarations for theme fonts.

${injectFontFaces('/assets')} // Generates @font-face CSS

Elevation and Shadow Utils

getElevation(elevation)

Gets box-shadow based on elevation level (0, 1, 2, 4, 8, 16).

box-shadow: ${getElevation(1)}; // Returns: 0px 1px 1px rgb(24 31 36 / 0.06)

getWidgetShadowOptions()

Gets custom widget shadow options from theme.

box-shadow: ${getWidgetShadowOptions}; // Returns theme-defined shadow or 'none'

Generic Property Utils

getThemeProp(themeProp)

Gets any top-level theme property.

border-radius: ${getThemeProp('borderRadius')}px; // Returns: 3px

getThemePropOrElse(themeProps, defaultValue)

Gets theme property with fallback default value.

${getThemePropOrElse(['customProp'], 'defaultValue')}

getPropOrElse(pathName, defaultValue)

Gets component prop with fallback default value.

letter-spacing: ${getPropOrElse(['letterSpacing'], 0.08)}px;

ifPropExist(pathName, returnValue)

Returns value only if prop exists.

margin-bottom: ${ifPropExist(['disabled'], '6px')};

Utility Functions

getHintColor(type?)

Gets appropriate color for message types (success, warning, error, info).

color: ${getHintColor('success')}; // Returns success color with high emphasis

getMixinFromTheme(name)

Gets CSS mixin from theme's themeMixins property.

${getMixinFromTheme('customMixin')} // Returns theme-defined CSS properties

Styled-Components Example

import React from "react";
import styled from "styled-components";
import {
getColor,
getColorShade,
getSizeBy,
getSizeUnit,
getThemeProp,
getPropOrElse,
ifPropExist,
getElevation,
getFontFamily,
getGradient,
getTypography,
getChartColor,
getBorderThickness,
getHintColor,
} from "@invent/wl-ui-kit";

interface ThemedButtonProps {
variant?: "primary" | "secondary";
size?: "small" | "medium" | "large";
disabled?: boolean;
elevation?: 0 | 1 | 2 | 4 | 8 | 16;
letterSpacing?: number;
}

const ThemedButton = styled.button<ThemedButtonProps>`
/* Color utils */
color: ${({ variant }) =>
getColor(variant === "secondary" ? "secondary" : "primary")};
background-color: ${getColorShade("primary", "lowEmphasis")};
border-color: ${getChartColor(0)};

/* Size utils */
padding: ${getSizeBy(2)};
margin: ${getSizeBy(1, "medium")};
line-height: ${getSizeUnit};
border-width: ${getBorderThickness()};

/* Typography */
font-family: ${getFontFamily("primary")};
${getTypography("ButtonText")};
letter-spacing: ${getPropOrElse(["letterSpacing"], 0.08)}px;

/* Theme properties */
border-radius: ${getThemeProp("borderRadius")}px;

/* Conditional styles */
margin-bottom: ${ifPropExist(["disabled"], "4px")};

/* Elevation */
box-shadow: ${({ elevation = 0 }) => getElevation(elevation)};

/* Gradient background on hover */
&:hover {
background: linear-gradient(
135deg,
${getGradient("primary", [
{ opacity: 0.1, gradientSlicePercent: 0 },
{ opacity: 0.3, gradientSlicePercent: 100 },
])}
);
}

/* Status colors */
&.success {
border-color: ${getHintColor("success")};
}
`;

export default ThemedButton;

Tailwind CSS Example

import React from "react";
import { useTheme } from "styled-components";
import {
getColor,
getColorShade,
getSizeBy,
getSizeUnit,
getThemeProp,
getPropOrElse,
ifPropExist,
getElevation,
getFontFamily,
getGradient,
getTypography,
getChartColor,
getBorderThickness,
getHintColor,
} from "@invent/wl-ui-kit";

interface TailwindThemedButtonProps {
variant?: "primary" | "secondary";
size?: "small" | "medium" | "large";
disabled?: boolean;
elevation?: 0 | 1 | 2 | 4 | 8 | 16;
letterSpacing?: number;
type?: "success" | "warning" | "error" | "info";
}

const TailwindThemedButton: React.FC<TailwindThemedButtonProps> = ({
variant = "primary",
size = "medium",
disabled = false,
elevation = 0,
letterSpacing,
type,
children,
...props
}) => {
const theme = useTheme();

// Generate CSS custom properties using theme utils
const styles = {
"--primary-color": getColor(
variant === "secondary" ? "secondary" : "primary"
)({ theme }),
"--bg-color": getColorShade("primary", "lowEmphasis")({ theme }),
"--border-color": getChartColor(0)({ theme }),
"--padding": getSizeBy(2)({ theme }),
"--margin": getSizeBy(1, size)({ theme }),
"--line-height": getSizeUnit({ theme }),
"--border-width": getBorderThickness()({ theme }),
"--font-family": getFontFamily("primary")({ theme }),
"--border-radius": `${getThemeProp("borderRadius")({ theme })}px`,
"--letter-spacing": `${getPropOrElse(
["letterSpacing"],
letterSpacing || 0.08
)({ letterSpacing, theme })}px`,
"--box-shadow": elevation > 0 ? getElevation(elevation)({ theme }) : "none",
"--gradient": getGradient("primary", [
{ opacity: 0.1, gradientSlicePercent: 0 },
{ opacity: 0.3, gradientSlicePercent: 100 },
])({ theme }),
"--status-color": type ? getHintColor(type)({ theme }) : undefined,
...getTypography("ButtonText")({ theme }),
} as React.CSSProperties;

// Conditional margin for disabled state
const conditionalMargin = disabled
? ifPropExist(["disabled"], "4px")({ disabled })
: null;
if (conditionalMargin) {
styles["--margin-bottom"] = conditionalMargin;
}

const className = [
// Base styles
"inline-flex items-center justify-center",
"transition-all duration-200",
"border border-solid",
"hover:opacity-80",

// Disabled state
disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer",

// Status variant
type === "success" ? "border-green-500" : "",
]
.filter(Boolean)
.join(" ");

return (
<button
className={className}
style={{
...styles,
color: "var(--primary-color)",
backgroundColor: "var(--bg-color)",
borderColor: type ? "var(--status-color)" : "var(--border-color)",
padding: "var(--padding)",
margin: "var(--margin)",
lineHeight: "var(--line-height)",
borderWidth: "var(--border-width)",
fontFamily: "var(--font-family)",
borderRadius: "var(--border-radius)",
letterSpacing: "var(--letter-spacing)",
boxShadow: "var(--box-shadow)",
marginBottom: conditionalMargin || undefined,
fontSize: styles.fontSize,
fontWeight: styles.fontWeight,
}}
disabled={disabled}
{...props}
>
{children}
</button>
);
};

export default TailwindThemedButton;