Skip to main content
On this page

Theming

Learn how to create and customise a theme.

Create a theme​

Creating your first theme is as simple as calling the createTheme function. It's ready to go out-of-the-box with the default theme. The next few sections will show you how to customise the theme to match your brand.

import { createTheme } from '@kaluza-platform/nebula';
import { ThemeProvider } from 'styled-components';

const theme = createTheme({});

const MyApp = () => (
<ThemeProvider theme={theme}>Welcome to my app!</ThemeProvider>
);

Customise a theme​

Themes are built on a set of core tokens or Foundations. These tokens correspond to raw values, such as a font size or hex value.

const myTheme = createTheme<{}>({
colors: {
primary: 'green',
primaryAlt: 'darkgreen',
},
fonts: {
body: 'Tahoma'
},
borderWidths: {
heavy: '2rem'
}
});

Our approach to theming is to set default values and then allow you to override the theme.

Themes are combined with a deep merge. This will update any dynamic tokens. When you add custom theme values you should also provide the typings.

const myTheme = createTheme<{
fonts: {
superDisplay: string
},
borderRadius: {
full: string
}
}>({
fonts: {
superDisplay: 'Verdana'
},
borderRadius: {
full: '50%'
},
colors: {
primary: 'tomato'
}
});

It is also possible to extend the theme, adding your own variables.

type AdditionalValues = {
somethingCustom: {
color: string;
},
}
const myTheme = createTheme<AdditionalValues>({
colors: {
primary: 'green',
primaryAlt: 'darkgreen',
},
somethingCustom: {
color: 'tomato',
},
});

CSS Reset​

You will need to import the css reset in order for the theme to display components correctly. It will adjust the font size of the page to make it easier to work with rems. Rem Units in CSS

Transitional configuration​

The config block of the theme object contains properties used to manage the rollout of breaking changes. These are transitional mechanisms only — they exist to let apps adopt new behaviour on their own schedule and are not intended as permanent configuration options. All apps are expected to migrate to the default (enabled) state.

markRequiredFormFields​

Controls whether the new required field marking behaviour is active.

  • false (default during rollout) - Existing behaviour: optional prop appends "(optional)" text; no asterisk is shown
  • true - New behaviour: required fields display a red asterisk; optional suppresses it; Form renders the "Fields marked * are required" key

This applies consistently across every Nebula field component, including RadioField, CheckboxField, and DateField, not just Field-based components like TextField and SelectField.

const theme = createTheme({
config: {
markRequiredFormFields: true,
},
});

When adopting this change:

  1. Set markRequiredFormFields: true in your theme.
  2. Audit your forms for fields that are genuinely optional and add optional to them.
  3. Check forms that use field components outside the Nebula Form component - you will need to add the key text manually.

Once all teams have migrated, this property will be removed and the new behaviour will become the unconditional default.

accessibleBadge​

Controls whether the accessible badge styling is active.

  • false (default during rollout) - Existing badge styles displayed
  • true - New badge styles displayed (all existing badge styles map to one of the new styles
const theme = createTheme({
config: {
accessibleBadge: true,
},
});

When adopting this change:

  1. Set accessibleBadge: true in your theme.
  2. Check badges render as expected - note that there is now only a single style per variant, and no outlined/filled distinction

Once all teams have migrated, this property will be removed and the new behaviour will become the unconditional default.

disableAutocomplete​

Controls whether browser autofill is disabled by default on form fields.

  • false (default during rollout) - Existing behaviour: fields do not set autocomplete/aria-autocomplete, so browsers offer autofill suggestions as normal
  • true - New behaviour: form fields default autoComplete to "off" to suppress browser autofill; a field's own autoComplete prop still overrides this
const theme = createTheme({
config: {
disableAutocomplete: true,
},
});

When adopting this change:

  1. Set disableAutocomplete: true in your theme.
  2. Audit any screens where a field relies on browser autofill and add an explicit autoComplete prop to those fields.
  3. Note that Chrome and Firefox deliberately ignore autocomplete="off" on password fields to preserve password-manager suggestions, so PasswordInput/PasswordField will still show that browser UI regardless of this setting.

Once all teams have migrated, this property will be removed and the new behaviour will become the unconditional default.