Skip to main content

Custom Themes

note

This feature is enabled for all new sitekeys on Pro and Enterprise accounts. If your sitekey does not accept custom themes, please contact support for assistance.

This document describes the custom theming feature, how to enable it, and the accepted schema that controls theme options.

Built-in Themes vs. Custom Themes

hCaptcha offers several pre-defined color schemes ("themes"), which you can choose by simply setting the theme property to one of those strings.

Custom Themes instead let you customize any color in the hCaptcha UI to match your brand requirements by passing a JS object at render time, as described below.

How to enable Custom Themes

First, the query parameter custom=true must be added to the https://js.hcaptcha.com/1/api.js script tag to let the hCaptcha client know it should load themes. Note that this will be ignored if the feature has not been enabled for your account. When using custom themes, the current theme configuration method takes a JSON string or Javascript object matching the theme schema outlined below.

Example Setup

  <script src="https://js.hcaptcha.com/1/api.js?render=explicit&onload=onLoad&custom=true" async defer></script>

<script type="text/javascript">

var onLoad = function() {
hcaptcha.render("h-captcha", {
"sitekey": "<sitekey>",
"theme" : {
palette: {
primary: {
main: "#00FF00"
},
text: {
heading: "#454545",
body : "#8C8C8C"
}
}
}
});
};

</script>

Schema

We've divided our theming into two pieces: palette and component. Palette is our overall style guide and color system to use when applying styles to our components internally. Component provide customization at a granular level, in the event you want to style parts of the UI beyond simply updating greys or our primary color.

Finally we have a mode attached to palette that generally dictates how we look up a color. For example if the mode is set to light then text will be a dark grey variant, specifically Grey 700. However when dark is set, the text would use Grey 100 as the base color. In short, the grey values looked up are flipped based on mode set.

Below is an outline of the entire schema that can be provided. A subset or even a single property change can be provided and the rest will be merged based on the default styles set by either mode.

var theme = {
palette: {
mode: "light",
grey: {
100 : "#FAFAFA",
200 : "#F5F5F5",
300 : "#E0E0E0",
400 : "#D7D7D7",
500 : "#BFBFBF",
600 : "#919191",
700 : "#555555",
800 : "#333333",
900 : "#222222",
1000: "#14191F"
},
primary: {
main: "#00838F"
},
warn: {
main: "#EB5757"
},
text: {
heading: "#555555",
body : "#555555"
}
},
component: {
checkbox: {
main: {
fill : "#FAFAFA",
border: "#E0E0E0"
},
hover: {
fill: "#F5F5F5"
}
},
challenge: {
main: {
fill : "#FAFAFA",
border: "#E0E0E0"
},
hover: {
fill: "#FAFAFA"
}
},
modal: {
main: {
fill : "#FFFFFF"
},
hover: {
fill: "#F5F5F5"
},
focus: {
outline: "#0074BF"
}
},
breadcrumb: {
main: {
fill: "#F5F5F5"
},
active: {
fill: "#00838F"
}
},
button: {
main: {
fill: "#FFFFFF",
icon: "#555555",
text: "#555555"
},
hover: {
fill: "#F5F5F5"
},
focus: {
icon : "#00838F",
text : "#00838F",
outline: "#0074BF"
},
active: {
fill: "#F5F5F5",
icon: "#555555",
text: "#555555"
}
},
link: {
focus: {
outline: "#0074BF"
}
},
list: {
main: {
fill : "#FFFFFF",
border: "#D7D7D7"
}
},
listItem: {
main: {
fill: "#FFFFFF",
line: "#F5F5F5",
text: "#555555"
},
hover: {
fill: "#F5F5F5"
},
selected: {
fill: "#E0E0E0"
},
focus: {
outline: "#0074BF"
}
},
input: {
main: {
fill : "#FAFAFA",
border: "#919191"
},
focus: {
fill : "#F5F5F5",
border : "#333333",
outline: "#0074BF"
}
},
radio: {
main: {
file : "#F5F5F5",
border: "#919191",
check : "#F5F5F5"
},
selected: {
check: "#00838F"
},
focus: {
outline: "#0074BF"
}
},
task: {
// Image shown in challenge to be answered
main: {
fill: "#F5F5F5"
},
selected: {
badge: "#00838F",
outline: "#00838F"
},
report: {
badge: "#EB5757",
outline: "#EB5757"
},
focus: {
badge: "#00838F",
outline: "#00838F"
}
},
prompt: {
main: {
fill : "#00838F",
border: "#00838F",
text : "#FFFFFF"
},
report: {
fill : "#EB5757",
border: "#EB5757",
text : "#FFFFFF"
}
},
skipButton: {
main: {
fill : "#919191",
border: "#919191",
text : "#FFFFFF"
},
hover: {
fill : "#555555",
border: "#919191",
text : "#FFFFFF"
},
focus: {
outline: "#0074BF"
}
},
verifyButton: {
main: {
fill : "#00838F",
border: "#00838F",
text : "#FFFFFF"
},
hover: {
fill : "#00838F",
border: "#00838F",
text : "#FFFFFF"
},
focus: {
outline: "#0074BF"
}
},
slider: {
main: {
bar : "#C4C4C4",
handle: "#0F8390"
},
focus: {
handle: "#0F8390"
}
},
textarea: {
main: {
fill : "#C4C4C4",
border: "#919191"
},
focus: {
fill : "#C4C4C4",
outline: "#0074BF"
},
disabled: {
fill: "#919191"
}
}
}
};

Custom Theme Configurator

info

The Custom Theme Configurator serves as an example of how to build your own Custom Themes and is a resource for building your own schema, but it does not represent all the customizable values between the palette and component properties.

Color Configuration

Palette Customization

Our overall style guide and color system used to style internal components.
Grey:
Primary:
Warn:
Text:

Component Customization

Style individual UI elements beyond the general palette.
Checkbox:
Main
Hover
Modal:
Main
Hover
Focus
Challenge:
Main
Hover
Breadcrumb:
Main
Active
Button:
Main
Hover
Focus
Active
Link:
Focus
List:
Main
ListItem:
Main
Hover
Selected
Focus
Input:
Main
Focus
Radio:
Main
Selected
Focus
Task:
Main
Selected
Report
Focus
Prompt:
Main
Report
SkipButton:
Main
Hover
Focus
VerifyButton:
Main
Hover
Focus
Slider:
Main
Focus
Textarea:
Main
Focus
Disabled

Live Preview

I am human

Configurator Schema Output

var theme = {
"palette": {
"mode": "light",
"grey": {
"100": "#FAFAFA",
"200": "#F5F5F5",
"300": "#E0E0E0",
"400": "#D7D7D7",
"500": "#BFBFBF",
"600": "#919191",
"700": "#555555",
"800": "#333333",
"900": "#222222",
"1000": "#14191F"
},
"primary": {
"main": "#00838F"
},
"warn": {
"main": "#EB5757"
},
"text": {
"heading": "#555555",
"body": "#555555"
}
},
"component": {
"checkbox": {
"main": {
"fill": "#FAFAFA",
"border": "#E0E0E0"
},
"hover": {
"fill": "#F5F5F5"
}
},
"modal": {
"main": {
"fill": "#FFFFFF"
},
"hover": {
"fill": "#F5F5F5"
},
"focus": {
"outline": "#0074BF"
}
},
"challenge": {
"main": {
"fill": "#FAFAFA",
"border": "#E0E0E0"
},
"hover": {
"fill": "#FAFAFA"
}
},
"breadcrumb": {
"main": {
"fill": "#F5F5F5"
},
"active": {
"fill": "#00838F"
}
},
"button": {
"main": {
"fill": "#FFFFFF",
"icon": "#555555",
"text": "#555555"
},
"hover": {
"fill": "#F5F5F5"
},
"focus": {
"icon": "#00838F",
"text": "#00838F",
"outline": "#0074BF"
},
"active": {
"fill": "#F5F5F5",
"icon": "#555555",
"text": "#555555"
}
},
"link": {
"focus": {
"outline": "#0074BF"
}
},
"list": {
"main": {
"fill": "#FFFFFF",
"border": "#D7D7D7"
}
},
"listItem": {
"main": {
"fill": "#FFFFFF",
"line": "#F5F5F5",
"text": "#555555"
},
"hover": {
"fill": "#F5F5F5"
},
"selected": {
"fill": "#E0E0E0"
},
"focus": {
"outline": "#0074BF"
}
},
"input": {
"main": {
"fill": "#FAFAFA",
"border": "#919191"
},
"focus": {
"fill": "#F5F5F5",
"border": "#333333",
"outline": "#0074BF"
}
},
"radio": {
"main": {
"fill": "#F5F5F5",
"border": "#919191",
"check": "#F5F5F5"
},
"selected": {
"check": "#00838F"
},
"focus": {
"outline": "#0074BF"
}
},
"task": {
"main": {
"fill": "#F5F5F5"
},
"selected": {
"badge": "#00838F",
"outline": "#00838F"
},
"report": {
"badge": "#EB5757",
"outline": "#EB5757"
},
"focus": {
"badge": "#00838F",
"outline": "#00838F"
}
},
"prompt": {
"main": {
"fill": "#00838F",
"border": "#00838F",
"text": "#FFFFFF"
},
"report": {
"fill": "#EB5757",
"border": "#EB5757",
"text": "#FFFFFF"
}
},
"skipButton": {
"main": {
"fill": "#919191",
"border": "#919191",
"text": "#FFFFFF"
},
"hover": {
"fill": "#555555",
"border": "#919191",
"text": "#FFFFFF"
},
"focus": {
"outline": "#0074BF"
}
},
"verifyButton": {
"main": {
"fill": "#00838F",
"border": "#00838F",
"text": "#FFFFFF"
},
"hover": {
"fill": "#00838F",
"border": "#00838F",
"text": "#FFFFFF"
},
"focus": {
"outline": "#0074BF"
}
},
"slider": {
"main": {
"bar": "#C4C4C4",
"handle": "#0F8390"
},
"focus": {
"handle": "#0F8390"
}
},
"textarea": {
"main": {
"fill": "#C4C4C4",
"border": "#919191"
},
"focus": {
"fill": "#C4C4C4",
"outline": "#0074BF"
},
"disabled": {
"fill": "#919191"
}
}
}
};

Schema Walkthrough

Palette

Palette.grey values are used if no component colors are used directly. If you create a new theme with purely the palette property, these values will be used throughout the checkbox widget and challenge.

Palette.primary is the color of the underline beneath the heading of the menu modal.

challenge menu modal

Palette.warn is the "Please Try Again" text if a challenge isn't passed.

hCaptcha challenge warning message

Palette.text includes .heading, the heading of the menu's modal pop-ups, and .body, the "I am human" text beside the widget's checkbox.

challenge menu modal text headinghCaptcha widget text

Component

The checkbox values style the checkbox widget.

  • .main.fill is the background color, and .main.border is the border color of the hCaptcha widget.

  • .hover.fill is the background color of the hCaptcha widget when hovering.

hCaptcha Checkbox Widget

The modal values style the modal pop-ups when clicking a ListItem within the challenge's menu (vertical ellipsis).

  • .main.fill is the background color of the modal.

  • .hover.fill is the modal's background color when hovering.

  • .focus.outline is the modal's outline color when focused.

hCaptcha Information Modal

The challenge values style the challenge pop-up users must solve to complete the hCaptcha.

  • .main.fill is the background color, and .main.border is the border color of the challenge.

  • .hover.fill is the background color of the challenge when hovering.

hCaptcha challenge component

The breadcrumb values style the horizontal ellipsis beneath the challenge images.

  • .main.fill are the inactive dots shown.

  • .main.active is the active dot shown.

challenge breadcrumb component

The button values style the interface icon buttons in the bottom-left of the challenge.

  • .main.fill is the button's background color, .main.icon is the icon's color, and .main.text is the color of the abbreviated language text.

  • .hover.fill is the button's background color when hovering.

  • .focus.icon is the color of the icon when hovering, .focus.text is not live, and focus.outline is the button's outline color when focused.

  • .active properties are not live.

challenge menu buttons

The link.focus.outline value is the outline color surrounding the "Privacy" and "Terms" links when focused.

checkbox widget links

The list values style the modals with list items that open when clicking the icon buttons in the bottom-left of the challenge.

  • .main.fill is the background color (behind ListItem), and .main.border is the modal's border color.
challenge menu pop-up

The listItem values style the clickable rows within the language list and menu modals.

  • .main.fill is the background color, main.line is the horizontal line separating rows, and main.text is the text color of a ListItem.

  • .hover.fill is the background color of the ListItem when hovering.

  • .selected.fill is the background color of the ListItem selected from the list.

  • .focus.outline is not live.

challenge menu and language list items

The input values style the clickable checkbox within the widget that renders the check upon completion of the hCaptcha.

  • .main.fill is the background color, and main.border is the border color of the square checkbox.

  • focus.border is the border color when hovering and focus.outline is the outline color when focused. .focus.fill is not live.

checkbox of the hCaptcha widget

The radio values style the checkboxes when reporting an issue within the menu modal.

  • .main.fill is the checkbox's background color and main.border is the border color. main.check is not live.

  • .selected.check is the colored area within the checkbox indicating the selected item.

  • .focus.outline is the outline color of the checkbox when focused.

bug report radio elements

The task values style the images and badge used in the hCaptcha challenge.

  • .main.fill is the background color behind the challenge image.

  • .selected.badge is the color of the checkmark circle, and .selected.outline is the color of the outline indicating a selected image within the challenge.

  • .report.badge is the color of the checkmark circle and .selected.outline is the color of the outline when selecting an image to report within the challenge.

  • .focus.badge and .focus.outline are not live.

hCaptcha challenge images

The prompt values style the header area that provides instructions for the challenge.

  • .main.fill is the background color, .main.border is the border color, and main.text is the text color of the header area of the challenge.

  • .report properties are not live.

hCaptcha challenge header with prompt

The skipButton values style the "Skip" button in the bottom-right of the challenge.

  • .main.fill is the background color, main.border is the border color, and main.text is the text color of the Skip button.

  • .hover.fill is the background color, hover.border is the border color, and hover.text is the text color of the Skip button when hovering.

  • .focus.outline is the outline color of the Skip button when focused.

challenge skip button

The verifyButton values style the "Verify" button in the bottom-right of the challenge.

  • .main.fill is the background color, main.border is the border color, and main.text is the text color of the Verify button.

  • .hover.fill is the background color, hover.border is the border color, and hover.text is the text color of the Verify button when hovering.

  • .focus.outline is the outline color of the Verify button when focused.

challenge verify button

The slider values style the slider used in text free entry challenges.

  • .main.bar is the slider's bar color and .main.handle is the controller color for the slider.

  • .focus.handle is the slider's controller color when focused.

Text Free Entry Challenge component

The textarea values style the text area when reporting a bug and the text area in the text free entry challenge.

  • .main.fill is the background color, and .main.border is the border color of the "Other" text area when reporting a bug.
other bug report text area
  • .focus.fill is the text area's background color and .focus.outline is the outline color in the text free entry challenge when focused.

  • .disabled.fill is the text area's background color in the text free entry challenge when disabled.

Text Free Entry Challenge component

Try It Live

Test custom themes on a live hCaptcha widget:

Examples

Example using just palette customization:

var vibrantTheme = {
palette: {
mode: "light",
grey: { 100: "#FAFAFA", 200: "#F5F5F5", 300: "#E0E0E0", 400: "#D7D7D7", 500: "#BFBFBF", 600: "#919191", 700: "#555555", 800: "#333333", 900: "#222222", 1000: "#14191F" },
primary: { main: "#FF5722" },
warn: { main: "#F44336" },
text: { heading: "#212121", body: "#212121" }
},
};

Example using both palette and component customization:

var subtleDarkTheme = {
palette: {
mode: "dark",
grey: {
100: "#FAFAFA",
200: "#F5F5F5",
300: "#E0E0E0",
400: "#D7D7D7",
500: "#BFBFBF",
600: "#919191",
700: "#787878",
800: "#5E5E5E",
900: "#444444",
1000: "#2A2A2A"
},
primary: {
main: "#607D8B"
},
warn: {
main: "#FF5722"
},
text: {
heading: "#F5F5F5",
body: "#E0E0E0"
}
},
component: {
checkbox: {
main: {
fill: "#424242",
border: "#616161"
},
},
input: {
main: {
fill: "#424242",
border: "#616161"
},
focus: {
fill: "#555555",
border: "#6A6A6A",
outline: "#607D8B"
}
},
challenge: {
main: {
fill: "#303030",
border: "#424242"
},
hover: {
fill: "#383838"
}
},
button: {
main: {
fill: "#424242",
icon: "#F5F5F5",
text: "#F5F5F5"
},
hover: {
fill: "#555555"
},
focus: {
icon: "#607D8B",
text: "#607D8B",
outline: "#607D8B"
},
active: {
fill: "#555555",
icon: "#F5F5F5",
text: "#F5F5F5"
}
},
modal: {
main: {
fill: "#222222"
},
hover: {
fill: "#303030"
},
focus: {
outline: "#607D8B"
}
}
}
};