Skip to content

Customization

GolemUI gives you three levels of customization — pick the one that fits your needs:

  1. CSS variable overrides — change design tokens for quick, broad adjustments.
  2. Class-based overrides — target specific widgets with standard CSS selectors.
  3. Icon customization — render the icons your design language calls for.

For full theme replacement, see Going Headless.

The fastest way to customize GolemUI. Override any design token on :root or a scoped selector, and every widget that references it updates automatically.

Set variables on :root to apply changes across your entire application:

:root {
--gui-intent-primary: #8b5cf6;
--gui-intent-primary-hover: #7c3aed;
--gui-intent-primary-active: #6d28d9;
--gui-radius-md: 8px;
--gui-font-family: 'Inter', sans-serif;
}

Target a specific container to limit changes to one section of your page:

.my-sidebar-form {
--gui-intent-primary: #10b981;
--gui-bg-surface: #f0fdf4;
--gui-border-default: #a7f3d0;
}
<div class="my-sidebar-form">
<gui-form ...></gui-form>
</div>

When CSS variables alone aren’t enough, target specific BEM classes with standard CSS selectors.

GolemUI follows the BEM naming convention consistently:

PatternExampleDescription
Block.gui-button, .gui-form, .gui-widgetTop-level widget class
Element.gui-repeater__card, .gui-repeater__add-btnChild element within a block
Modifier.gui-widget--horizontal, .gui-checkbox--leftVariant of a block or element
Custom elementgui-textinput, gui-select, gui-toggleThe web component tag itself

Target all inputs within a form:

.gui-form .gui-widget input {
border-radius: 8px;
border-width: 2px;
}

Style buttons with uppercase text:

.gui-form button.gui-button {
text-transform: uppercase;
letter-spacing: 0.05em;
}

Customize the toggle slider shape:

.gui-form gui-toggle .gui-toggle--slider {
border-radius: 4px;
}

Add a custom background to alert notifications:

.gui-alert-notification--success {
background: linear-gradient(135deg, #d1fae5, #a7f3d0);
}

GolemUI defines its entire visual language through CSS custom properties on :root. Override any variable to customize the look of all widgets at once — no need to hunt through widget-specific styles.

These are the base color palettes that power the entire design system. Override them to transform the overall look of every widget at once.

TokenDefaultPreview
--gui-color-neutral-50#f8fafc
--gui-color-neutral-100#f1f5f9
--gui-color-neutral-200#e2e8f0
--gui-color-neutral-300#cbd5e1
--gui-color-neutral-400#94a3b8
--gui-color-neutral-500#64748b
--gui-color-neutral-600#475569
--gui-color-neutral-700#334155
--gui-color-neutral-800#1e293b
--gui-color-neutral-900#0f172a
--gui-color-neutral-950#020617
TokenDefaultPreview
--gui-color-primary-50#eff6ff
--gui-color-primary-100#dbeafe
--gui-color-primary-200#bfdbfe
--gui-color-primary-300#93c5fd
--gui-color-primary-400#60a5fa
--gui-color-primary-500#3b82f6
--gui-color-primary-600#2563eb
--gui-color-primary-700#1d4ed8
--gui-color-primary-800#1e40af
--gui-color-primary-900#1e3a8a
--gui-color-primary-950#172554

GolemUI ships full 50–900 scales for each status palette. Override individual stops as you need them — for example, --gui-color-success-500 and --gui-color-success-600 are the most commonly used. The semantic intent tokens below typically pull from these palettes.

TokenDefault
--gui-color-white#ffffff
--gui-color-black#000000
--gui-color-transparenttransparent

Semantic tokens control how widgets actually look — backgrounds, text colors, borders, and status indicators. Each token has a light and a dark value; the dark column applies when dark mode is active via data-theme="dark" or data-theme="auto", and the light column applies otherwise.

TokenLightDarkUsage
--gui-bg-defaultwhiteneutral-950Page / form background
--gui-bg-surfacewhiteneutral-900Card and input surfaces
--gui-bg-surface-hoverneutral-50neutral-800Hovered surfaces
--gui-bg-surface-activeneutral-100neutral-700Active / pressed surfaces
--gui-bg-disabledneutral-100neutral-800Disabled elements
--gui-bg-inverseneutral-900whiteInverted backgrounds
TokenLightDarkUsage
--gui-text-defaultneutral-900neutral-50Primary text
--gui-text-secondaryneutral-500neutral-400Secondary text
--gui-text-mutedneutral-400neutral-500Muted / placeholder text
--gui-text-inversewhiteneutral-900Text on inverse backgrounds
--gui-text-disabledneutral-400neutral-600Disabled text
TokenLightDarkUsage
--gui-border-defaultneutral-300neutral-700Standard borders
--gui-border-subtleneutral-200neutral-800Subtle / divider borders
--gui-border-strongneutral-400neutral-600Emphasized borders
--gui-border-disabledneutral-200neutral-700Disabled borders
--gui-border-focusprimary-500primary-500Focus ring color

Each status intent provides a base color, hover variant, background tint, and text color. The five intents are primary, success, info, warning, error, each with --gui-intent-<name>, --gui-intent-<name>-hover, --gui-intent-<name>-bg, and --gui-intent-<name>-text tokens (and --gui-intent-primary-active on primary):

:root {
--gui-intent-primary: var(--gui-color-primary-600);
--gui-intent-primary-hover: var(--gui-color-primary-700);
--gui-intent-success: var(--gui-color-success-600);
--gui-intent-warning: var(--gui-color-warning-500);
--gui-intent-error: var(--gui-color-error-600);
/* …and so on for every intent. */
}

In dark mode, intent backgrounds use a subtle tinted overlay that works well on dark surfaces.

A rem-based spacing scale used for padding, margins, and gaps throughout all widgets.

TokenValuePixels
--gui-space-00px0
--gui-space-10.25rem4
--gui-space-20.5rem8
--gui-space-30.75rem12
--gui-space-41rem16
--gui-space-51.25rem20
--gui-space-61.5rem24
--gui-space-82rem32
--gui-space-102.5rem40
TokenValuePixels
--gui-font-familySystem sans-serif stack
--gui-font-xs0.75rem12
--gui-font-sm0.875rem14
--gui-font-base1rem16
--gui-font-lg1.125rem18
--gui-font-xl1.25rem20
--gui-font-2xl1.5rem24
--gui-font-3xl2rem32

The default font stack is:

--gui-font-family:
ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
Roboto, 'Helvetica Neue', Arial, sans-serif;
TokenValuePixels
--gui-radius-none0px0
--gui-radius-sm0.125rem2
--gui-radius-md0.25rem4
--gui-radius-lg0.5rem8
--gui-radius-xl0.75rem12
--gui-radius-full9999pxpill shape
TokenUsage
--gui-shadow-smSubtle elevation (0 1px 2px 0 rgb(0 0 0 / 0.05))
--gui-shadow-mdMedium elevation
--gui-shadow-lgLarge elevation
--gui-shadow-focusFocus indicator — double-ring effect using color-mix()

The focus shadow creates a double-ring effect — a solid inner ring matching the background and a semi-transparent outer ring in the focus color:

--gui-shadow-focus:
0 0 0 2px var(--gui-bg-default),
0 0 0 4px color-mix(in srgb, var(--gui-border-focus) 50%, transparent);

Some widgets expose dedicated tokens for fine-grained control.

Toggle

TokenDefaultDescription
--gui-toggle-width38pxTrack width
--gui-toggle-height20pxTrack height
--gui-toggle-slider-width16pxThumb width
--gui-toggle-slider-height16pxThumb height
--gui-toggle-slider-transform16pxTranslation distance when checked

Calendar

TokenDefaultDescription
--gui-calendar-width300pxCalendar container width
--gui-calendar-change-month-button-width36pxMonth navigation button width
--gui-calendar-change-month-button-height36pxMonth navigation button height

Markdown Text

These tokens control the typography and visual style of rendered markdown content inside the Markdown Text widget.

TokenDefaultDescription
--gui-md-text-color--gui-text-defaultBase text color
--gui-md-line-height1.6Base line height
--gui-md-heading-color--gui-text-defaultColor for h1–h6 headings
--gui-md-heading-weight600Font weight for headings
--gui-md-link-color--gui-intent-primaryLink text color
--gui-md-link-hover-color--gui-intent-primary-hoverLink color on hover
--gui-md-blockquote-bg--gui-bg-surface-hoverBlockquote background
--gui-md-blockquote-border-color--gui-border-strongBlockquote left border color
--gui-md-blockquote-color--gui-text-secondaryBlockquote text color
--gui-md-code-bg--gui-bg-surface-activeBackground for inline code and code blocks
--gui-md-code-color--gui-text-defaultText color for inline code and code blocks
--gui-md-code-radius--gui-radius-smBorder radius for inline code
--gui-md-hr-color--gui-border-defaultColor of horizontal rules (<hr>)

GolemUI widgets accept an icon prop and render it as a <span> element with predictable CSS hooks. GolemUI does not prescribe any specific icon library — it provides the HTML structure, and you bring the CSS to render whatever icon you want.

When you set "icon": "save" in your form configuration, GolemUI renders:

<span class="gui-widget-icon save" data-icon="save"></span>

This gives you three CSS selectors to work with:

SelectorDescription
.gui-widget-iconAlways present on every icon element
.{icon-name} (e.g. .save)A class matching the exact icon string you passed
[data-icon="{icon-name}"]A data attribute with the icon string value

GolemUI provides base positioning styles for the icon element (absolute positioning, flexbox centering, 40×40 dimensions), but the actual icon rendering is intentionally left to you. This means you can use any icon system you prefer.

WidgetIcon props
Textinputicon
Passwordicon, showPasswordIcon, hidePasswordIcon
Textareaicon
Selecticon
Currencyicon
Date Inputicon
Range Date Inputicon
Calendaricon, prevMonthIcon, nextMonthIcon
Buttonicon, iconPosition ('left' | 'right')
RepeateraddButtonIcon, removeButtonIcon

The simplest approach. A single CSS rule targets [data-icon]::before and uses content: attr(data-icon) to render the ligature text from a webfont.

First, load the webfont in your project:

@import url('https://fonts.googleapis.com/icon?family=Material+Icons');

Then add the icon rendering rule:

.gui-widget-icon[data-icon]::before {
content: attr(data-icon);
font-family: 'Material Icons';
font-size: var(--gui-font-lg);
font-style: normal;
font-weight: normal;
line-height: 1;
-webkit-font-smoothing: antialiased;
}

With this in place, pass any Material Icons ligature name as the icon value:

icons-webfont.json
{
"form": [
{
"uid": "",
"kind": "input",
"type": "textinput",
"path": "phone",
"label": "Phone",
"props": {
"icon": "phone_callback"
}
},
{
"uid": "",
"kind": "input",
"type": "textinput",
"path": "email",
"label": "Email",
"props": {
"icon": "alternate_email"
}
},
{
"uid": "",
"kind": "action",
"type": "button",
"label": "Save",
"props": {
"icon": "save"
}
}
]
}

You can render SVG icons by targeting the icon-specific class on the <span>. Since the icon string becomes a CSS class, you can use any class-based icon system.

Pass a custom class name as the icon value:

{
"props": {
"icon": "my-custom-icon"
}
}

Then define the icon with CSS background-image:

/* Clear the default ligature text */
.gui-widget-icon.my-custom-icon::before {
content: '';
}
/* Render an SVG icon */
.gui-widget-icon.my-custom-icon {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23FFFFFF' viewBox='0 0 24 24'%3E%3Cpath d='M15.5 14h-.79l-.28-.27A6.47 6.47 0 0 0 16 9.5 6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z'/%3E%3C/svg%3E");
background-size: 20px 20px;
background-repeat: no-repeat;
background-position: center;
}
icons-svg.json
{
"form": [
{
"uid": "",
"kind": "input",
"type": "textinput",
"path": "search",
"label": "Search",
"props": {
"icon": "my-custom-icon"
}
},
{
"uid": "",
"kind": "action",
"type": "button",
"label": "Submit"
}
]
}

You can also use libraries like FontAwesome by passing their class names directly:

{
"props": {
"icon": "fas fa-globe"
}
}

Since the icon string is added as a CSS class, FontAwesome (or similar libraries) will pick it up automatically — as long as you’ve loaded the library’s CSS in your project.

Use background-image with image URLs (PNG, SVG files, etc.) — the same CSS technique as inline SVGs, but referencing external files:

.gui-widget-icon.my-logo-icon::before {
content: '';
}
.gui-widget-icon.my-logo-icon {
background-image: url('/assets/icons/github.png');
background-size: 20px 20px;
background-repeat: no-repeat;
background-position: center;
}

Then use it in your form config:

icons-image.json
{
"form": [
{
"uid": "",
"kind": "input",
"type": "textinput",
"path": "search",
"label": "Search"
},
{
"uid": "",
"kind": "action",
"type": "button",
"label": "Submit",
"props": {
"icon": "my-logo-icon"
}
}
]
}

Since each icon is independently styled via its unique CSS class, you can freely mix approaches in the same form. One field can use Material Icons ligatures, another can use an SVG, and a third can use an image.

Icon size and color can be controlled through standard CSS:

/* Change icon color globally */
.gui-widget-icon {
color: var(--gui-intent-primary);
}
/* Change icon size for webfont ligatures */
.gui-widget-icon[data-icon]::before {
font-size: var(--gui-font-xl);
}
/* Change icon size for background-image icons */
.gui-widget-icon.my-custom-icon {
background-size: 24px 24px;
}
  • Theming — built-in themes (Clay), creating new themes, scoped themes.
  • Going Headless — drop the courtesy layer and build a theme from scratch.
  • Styling Overview — responsive scaling and RTL support.