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;