# Nebula Design System > Nebula is Kaluza's React component library and design system. > It provides accessible, themeable UI components for building energy retail products. > Built with TypeScript, React, and styled-components. Full documentation: https://nebula.kaluza.com Installation: https://nebula.kaluza.com/get-started/setup Each component page has these sections: - "## Properties": its props, with types, defaults and whether they are required. - "## Rules and guidance": when to use it and how, where written. - "## Accessibility": what Nebula provides, how it works with a keyboard, the design and development rules to follow, and what has been tested, where written. - "## Styles": the CSS its styled elements use. Reference only; you do not need it to use the component. --- ## Quick Reference All components are imported from `@kaluza-platform/nebula`: ```tsx import { Page, Card, PrimaryCTAButton } from '@kaluza-platform/nebula'; ``` Nebula needs three things set up at the app root (full guide: https://nebula.kaluza.com/get-started/setup): - the reset stylesheet, imported once - it is required, because it sets `1rem` to 10px and everything is sized in `rem` - a styled-components (v5) `ThemeProvider` with a Nebula theme - React Aria's `OverlayProvider`, which the Modal and the date and month pickers need ```tsx import '@kaluza-platform/nebula/dist/reset.css'; import { OverlayProvider } from '@react-aria/overlays'; import { ThemeProvider } from 'styled-components'; import { theme } from '@kaluza-platform/nebula'; ; ``` ### Page structure Every app page should use `Page` as the root layout: ```tsx Page title {/* Page content here */} ``` ### Form structure Forms use `Form`, `FormFields`, and `FormActions`: ```tsx
Submit Cancel
``` Fields inside `FormFields`. Buttons inside `FormActions`. Error handling is built in — pass an `errors` array to `Form` and field errors are set automatically via context using each field's `name` prop. ### When to use CTAButton vs CTALink - `CTAButton` (Primary/Secondary/Destructive) — Performs an action on the current page: submitting a form, opening a modal, deleting an item. - `CTALink` — Navigates the user to a different page. Renders as an anchor element. Use this even if the link is styled to look like a button. ### When to use DateField vs DatepickerField - `DateField` — For entering an exact known date by typing (e.g. date of birth). No calendar popup. - `DatepickerField` — For selecting a date from a calendar popup or by typing (e.g. appointment booking). ### Rules Rules use this vocabulary: **Always** (required), **Never** (prohibited), **Avoid** (strongly discouraged), **Ensure** (verify before shipping). Full rationale in the linked guides. #### Forms — https://nebula.kaluza.com/patterns/forms - **Always** ask for only the information you definitely need. - **Always** give every field a label — never use placeholder text as a label or hint. - **Always** validate on form submit, not as the user types. - **Always** display a list of errors at the top of the form using `ErrorSummaryNotification`. - **Always** use plain, concise language for error messages — avoid pleasantries like "please". - **Avoid** multi-column form layouts — error-prone and cause abandonment. - **Avoid** disabling buttons (except while submitting) — creates poor UX and excludes users with disabilities. - **Never** use HTML default validation — add `novalidate` to the `
`. - **Never** validate as the user types — it interrupts users mid-answer. - Nebula fields and buttons have **no disabled state** — use error handling or redesign the experience instead. #### Accessibility — https://nebula.kaluza.com/guides/accessibility - **Always** use plain English — no jargon or idioms; expand uncommon acronyms on first use. - **Always** accompany colour-only meaning with a symbol, emoji, or text — never rely on colour alone. - **Always** meet WCAG 2.2 Level AA contrast (1.4.3) for text against its background. - **Always** use semantic heading elements with correct nesting — one `

` per page/screen. - **Always** make link text meaningful out of context — avoid "click here" or "link". - **Always** add descriptive alt text to informative images; decorative images use empty alt (`alt=""`). - **Never** use images of text (logos excepted). #### Theming & configuration — https://nebula.kaluza.com/guides/theme - **Always** migrate `markRequiredFormFields` to `true` — the legacy default (`false`) will be removed once all teams have migrated. - When `markRequiredFormFields: true`: required fields show a red asterisk; mark genuinely optional fields with the `optional` prop; `Form` auto-renders the "Fields marked \* are required" key. - Applies to every Nebula field component (including `RadioField`, `CheckboxField`, `DateField`), not just `Field`-based components. --- # Dev Setup Get set up using Nebula in your app. Section: get-started Docs: https://nebula.kaluza.com/get-started/setup --- Nebula is a React component library built on [styled-components](https://styled-components.com). This page takes you from an empty app to a rendered component, then covers each piece of setup in detail. ## Requirements - **React** 16.8, 17 or 18. Nebula's declared peer range stops at 18, so React 19 isn't supported yet. - **styled-components** v5 (`^5.2.0`). v6 isn't supported, and a plain `yarn add styled-components` installs v6, so ask for v5 explicitly (see [peer dependencies](#install-the-peer-dependencies)). - A bundler that can import a CSS file, for the [CSS reset](#css-reset). - A personal npm account in the private `kaluza-platform` organisation. See [Get access](#get-access). TypeScript types are included in the package, so there is nothing extra to install for them. ## Installation ### Get access `@kaluza-platform/nebula` is a private package on npm. You need a personal npm account in the `kaluza-platform` organisation (you join it with the `/npm` Slack command) and a read-only token available as `NPM_TOKEN`. Kaluza's guide, [npm publish / install private kaluza-platform npm registry](https://kaluza.atlassian.net/wiki/spaces/QSRE/pages/92738053/Guide+npm+publish+install+private+kaluza-platform+npm+registry), covers all of it: onboarding, configuring npm or Yarn, and setting up CI. Follow it before installing, and check with `npm whoami` (or `yarn npm whoami`) that you're logged in. ### Install the package ```sh yarn add @kaluza-platform/nebula ``` Or if you prefer: ```sh npm install --save @kaluza-platform/nebula ``` ### Install the peer dependencies Nebula expects your app to provide these. npm 7 and later installs them for you; Yarn doesn't, so add them yourself. `react` and `react-dom` are your app's own. ```json "peerDependencies": { "@internationalized/date": "^3.8.1", "@react-aria/button": "^3.13.1", "@react-aria/calendar": "^3.8.1", "@react-aria/datepicker": "^3.14.3", "@react-aria/dialog": "^3.5.25", "@react-aria/focus": "^3.20.3", "@react-aria/i18n": "^3.12.9", "@react-aria/overlays": "^3.27.1", "@react-aria/tooltip": "^3.8.7", "@react-aria/utils": "^3.29.0", "react": "^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", "react-stately": "^3.38.0", "styled-components": "^5.2.0" } ``` ```sh yarn add @internationalized/date @react-aria/button @react-aria/calendar @react-aria/datepicker @react-aria/dialog @react-aria/focus @react-aria/i18n @react-aria/overlays @react-aria/tooltip @react-aria/utils react-stately styled-components@^5.2.0 ``` `styled-components` is pinned to v5 there because a plain `yarn add styled-components` installs v6, which Nebula doesn't support. ## Set up your app Nebula needs three things around your app: the reset stylesheet, a theme, and an overlay provider. Import the stylesheet once, in your app's entry file: ```tsx import '@kaluza-platform/nebula/dist/reset.css'; ``` Then wrap your app in the two providers: ```tsx import { OverlayProvider } from '@react-aria/overlays'; import { ThemeProvider } from 'styled-components'; import { createTheme, PrimaryCTAButton } from '@kaluza-platform/nebula'; const theme = createTheme({}); export const App = () => ( alert('Yippee!')}> I ❤ Design Systems ); ``` That is a working setup. The sections below explain each part, and the [troubleshooting](#troubleshooting) list at the end covers what it looks like when one is missing. ## CSS reset The reset is **required**, not optional polish. Nebula sizes everything in `rem` on the assumption that `1rem` is 10px, and the reset is what makes that true: it sets `html { font-size: 62.5% }`. Without it `1rem` is the browser's 16px, and every component renders about 60% larger than designed. It also applies a [CSS reset](https://meyerweb.com/eric/tools/css/reset/) so elements start from the same defaults on every browser. ```tsx import '@kaluza-platform/nebula/dist/reset.css'; ``` How you import it depends on your bundler. Webpack needs a `css-loader` to import CSS. Vite handles CSS imports with no setup. In Next.js, import it in your root layout (`app/layout.tsx`) with the App Router, or in `pages/_app.tsx` with the Pages Router, which is the only place that router allows global CSS. The reset gives `body` a default font of Open Sans or Arial. Nebula's components set their own fonts from the theme, so set the font for the rest of your page in your own CSS (usually Inter, see [Fonts](#fonts)). ## Fonts Nebula's theme uses two font families: - [Aspekta](https://github.com/ivodolenc/aspekta), an open source font, for display text such as headings (`theme.fonts.display`, falling back to Inter and then `sans-serif`). - [Inter](https://fonts.google.com/specimen/Inter), available as a Google font, for body text (`theme.fonts.body`, falling back to `sans-serif`). Nebula uses four weights: normal (400), medium (500), semi bold (600) and bold (700). Both fonts support them, so load all four. Nebula doesn't load the fonts for you. Add them to your app from the links above; until you do, text falls back to the system sans-serif font. ## Theming Nebula uses styled-components, so your app needs a `ThemeProvider` at its root, with a theme from `createTheme`. Called with an empty object it gives you the default theme. The package also exports that as `theme`, ready to use. ```tsx import { ThemeProvider } from 'styled-components'; import { createTheme } from '@kaluza-platform/nebula'; const theme = createTheme({}); ; ``` To change colours, spacing and so on, and to turn on the transitional configuration flags (`markRequiredFormFields`, `accessibleBadge`, `disableAutocomplete`), see [Theming](/guides/theme). ## OverlayProvider Components that render overlays, such as the Modal and the popovers of the date and month pickers, use [React Aria](https://react-spectrum.adobe.com/react-aria)'s overlay hooks, which expect an `OverlayProvider` above them. Without one, some of them throw `Modal is not contained within a provider`. Add one at the root rather than working out which components need it. ```tsx import { OverlayProvider } from '@react-aria/overlays'; const MyApp = () => Welcome to my app!; ``` ## Icons Icons work with no setup: Nebula ships a default set, and components that take an icon (`Icon`, the CTA buttons and links) look names up in it. To add your own, wrap your app in an `IconsProvider`. Your icons are merged with the defaults, and each one is a component that receives the props for an ``: ```tsx import { Icon, IconsProvider } from '@kaluza-platform/nebula'; const icons = { rocket: (props: JSX.IntrinsicElements['svg']) => ( ), }; ; ``` ## Using the components Everything is a named export of `@kaluza-platform/nebula`: ```tsx import { Page, PrimaryCTAButton } from '@kaluza-platform/nebula'; const Example = () => ( alert('Yippee!')}> I ❤ Design Systems ); ``` Browse the [components](/category/components) for what each one does, how to use it, and its props on the Spec tab. ## Testing your app Components need the same providers in tests as in your app. With React Testing Library, create a wrapper once and use it for every render: ```tsx import { render } from '@testing-library/react'; import { OverlayProvider } from '@react-aria/overlays'; import type { ReactElement, ReactNode } from 'react'; import { ThemeProvider } from 'styled-components'; import { theme } from '@kaluza-platform/nebula'; const Providers = ({ children }: { children: ReactNode }) => ( {children} ); export const renderWithProviders = (ui: ReactElement) => render(ui, { wrapper: Providers }); ``` ## Using Nebula with AI tools See [Using Nebula with AI](/get-started/using-nebula-with-ai). ## Troubleshooting - **Everything looks too big.** The reset stylesheet isn't loaded, so `1rem` is 16px instead of 10px. See [CSS reset](#css-reset). - **A `TypeError` about reading a property of `undefined`, from inside a Nebula component.** There is no `ThemeProvider` above it. See [Theming](#theming). - **`Modal is not contained within a provider`.** Add an `OverlayProvider` at the root. See [OverlayProvider](#overlayprovider). - **`404` when installing `@kaluza-platform/nebula`.** npm answers `404` for a private package you can't read: you aren't in the organisation yet, or your token isn't being picked up. Run `npm whoami` and follow the guide in [Get access](#get-access). - **Peer dependency warnings.** Install the packages listed under [peer dependencies](#install-the-peer-dependencies). - **Warnings about several versions of styled-components.** Your app and Nebula have ended up with separate copies. Make sure there is one copy, at v5. - **Text is in the wrong font.** The fonts aren't loaded. See [Fonts](#fonts). --- # Figma Setup Design with Nebula in Figma. Section: get-started Docs: https://nebula.kaluza.com/get-started/figma --- [Nebula Enterprise](https://www.figma.com/design/BWszvKOkRmWLhYCSvduoqW/Nebula-Enterprise?m=auto&node-id=3744-0&t=JBxu9h2rkl6sPW2Q-1) is Nebula as a Figma library, describing the same components as the React library. ## Access You need a Figma seat to design with the library. Request one from inside Figma, which sends it to an admin to approve. The library itself is added to every Kaluza Figma file by default, so its components, styles and variables are in your assets panel already. If it isn't there, open the Assets tab, add more libraries, and search for Nebula Enterprise. ## Using components Drag the component you want from the assets panel onto the canvas. Use the component rather than drawing your own. A redrawn component will look right in the file and then behave differently once it's built, because the thing that ships is the React component and not your drawing of it. Change an instance through the properties panel rather than by showing and hiding layers in the layer tree. The panel lists what the component is built to vary. Avoid detaching an instance. Where the properties don't cover what you need, that's worth raising rather than working around: a detached instance will stop receiving library updates, and nothing will flag it later. For anything beyond placing one, go to the library file. Each component has its own page in it, named after the component, holding its variants and states as labelled sections. The quickest route is the Figma button next to the title on any component page here. ## Colour, spacing and type Use the library's variables rather than raw values. See [Colours](/foundations/colour), [Spacing](/foundations/spacing) and [Typography](/foundations/typography) for what each one is for. A hard-coded hex is the same problem as a redrawn component: it won't follow a theme change, and nobody downstream can tell whether you chose it deliberately. ## Where Figma and the code differ We aim for the Figma library and the code to match one to one, and we don't guarantee it. Two kinds of difference are deliberate. Some interactions can't be reproduced usefully in Figma, and modelling them costs more than it's worth. Where a behaviour only exists in code, you'll find it on the [component's own page](/category/components) rather than in the library. Some are grouped differently on purpose, to keep the Figma library easier to work in. `CTAButton` and `CTALink` are a single 'Button' component in Figma, so both link to the same Figma page. Anything else is drift. If you find a variant, a property name or a token that disagrees between the two, flag it rather than designing around it, because the next person will hit the same thing and won't know which side is right. ## Claude and the Figma canvas Claude can read a Figma file and make changes to it through the Figma MCP server, which makes it useful for the mechanical parts of a design: laying out a screen from components that already exist, or applying variables across a file. Point it at your own file, not at the Nebula Enterprise library. Changes to the library are a design system decision and they reach everyone, so they go through the design system team rather than Claude acting on a prompt. For what else Claude can do with Nebula, and how to set it up, see [Using Nebula with AI](/get-started/using-nebula-with-ai). ## Support For anything about Nebula in Figma, including a mismatch between the library and the code, contact the design system team on Slack at `#ug-design-system`. For Figma itself, `#ug-figma`. --- # Using Nebula with AI Work with Nebula through Claude and other AI tools. Section: get-started Docs: https://nebula.kaluza.com/get-started/using-nebula-with-ai --- Nebula publishes itself in a form AI tools can read, so Claude can work from what the components actually do rather than from what it remembers about them. ## The plain-text docs Every page on this site has a plain-text version, linked from the `LLMs` button under its title. Paste that URL into any AI tool and it reads the page as the tool sees it, with nothing to install. For a component, the plain-text version carries more than the page shows at a glance: its props with types and defaults, its rules with their strength, its accessibility notes and its styles, all in one file. Two entry points cover the whole site: | URL | What it is | | --- | --- | | [`llms.txt`](pathname:///llms.txt) | The index. A quick reference, then every page grouped by section, each linking to its plain-text version. | | [`llms-full.txt`](pathname:///llms-full.txt) | Every page in one file, around 100,000 tokens. Only worth fetching when you genuinely need all of it. | Start an AI tool at the index rather than at a single page. It tells the tool how the docs are organised and what each section holds, so it can fetch the component it needs instead of guessing from one page it was handed. ## Skills A skill teaches Claude how to use the docs for a particular job, so you don't have to point it at the right pages yourself. | What you're doing | Skill | | --- | --- | | Writing or reviewing UI copy: labels, errors, notifications, empty states | `nebula-content` | | Choosing a component, or building with one | `nebula` | | Designing in Figma | See [Figma Setup](/get-started/figma) | Both skills live in the Kaluza skills registry and neither is installed by default, so you add the one you need. Ask in `#ask-ai` if you're not sure how to add a skill on your setup, since it differs between Claude Code, the desktop app and claude.ai. Neither skill replaces reading the docs yourself. They make Claude read them, which is a different thing. ## Checking the answer Ask for the rule behind each point, and you can check it. Component rules have ids like `notification.title.no-terminal-punctuation`, printed next to each rule on the plain-text version of that component's page. A point with an id can be looked up. A point without one is Claude's opinion, which may still be right, but you should know which you're reading. Rule strength matters as much as the rule. `MUST NOT` is a prohibition and `SHOULD NOT` isn't, and the two are easy to flatten into each other. If you're writing code, check anything surprising against the version you have installed. Your app may be on an older `@kaluza-platform/nebula` than the one documented here, in which case a prop described on a component page won't exist in your build. The JSDoc comments shipped with the package's type definitions are what your app actually runs, so they settle it. See [Dev Setup](/get-started/setup). ## Contributing to Nebula Two more skills live in the Nebula repository rather than the registry. They're for people changing Nebula, not for building with it: - `nebula-docs` writes and reviews pages for this site. - `nebula-a11y-docs` writes the Accessibility tab. Nebula is moving to a federated model, where product teams contribute components rather than only requesting them. Fuller guidance on contributing will follow. ## Support For the skills, or anything about using Nebula with AI, contact the design system team on Slack at `#ug-design-system`. --- # Borders Border width, styles and radii for bounding elements. Section: foundations Docs: https://nebula.kaluza.com/foundations/borders --- ## Border Width Borders should use the standard width and this should be the default option when in doubt. Interactive elements, such as form inputs, may use the `heavy` option to visually set it apart, e.g. input errors. Heavier is reserved for active context indicators, such as disclosure and tabs. _Theme tokens for this component are configurable via `createTheme`. See the theme guide: https://nebula.kaluza.com/guides/theme_ ## Radii Radii should be used on all corners of bounding elements. **Brands can set this to 0 to remove all border radius**. _Theme tokens for this component are configurable via `createTheme`. See the theme guide: https://nebula.kaluza.com/guides/theme_ --- # Colours Colour palettes, semantic usage and accessibility requirements. Section: foundations Docs: https://nebula.kaluza.com/foundations/colour --- import { ColorPanel } from '../../src/components/docs-helpers/ColorPanel'; ## Palettes Colours that are grouped into semantic groups are called **palettes**. Context determines which palette colour should be used and the contrast requirements that need to be met. ### Palette colours Each palette has six colours, except the Primary palette, which has four. `{base}` represents the palette name. For instance, in the success palette, the `{base}` token is success and the `on{Base}` token is onSuccess. - `{base}` - saturated fill, or for use on canvas - `on{Base}` - text and icon colour on `{base}` or `alt` surfaces (meets 4.5:1 contrast against both) - `alt` - saturated fill, slightly darker than `{base}`; used for hover state when a component using `{base}` is interactive; never used as a static colour - `subtle` - very light background, suitable for larger fill areas - `muted` - light background, suitable for smaller fill areas - `onMuted` - text and icon colour on `subtle` or `muted` surfaces (meets 4.5:1 contrast against both) Note: `infoContrast`, `successContrast`, `warningContrast`, and `errorContrast` are deprecated aliases for their respective `on{Base}` tokens. `primaryContrast` is not deprecated. ### Primary Establishes visual hierarchy and draws attention to the most important action on a page. The main signal of interactivity in the system. May be the brand colour, but not necessarily. _Theme tokens for this component are configurable via `createTheme`. See the theme guide: https://nebula.kaluza.com/guides/theme_ ### Error Signals that something has gone wrong or will cause a problem if the user proceeds. _Theme tokens for this component are configurable via `createTheme`. See the theme guide: https://nebula.kaluza.com/guides/theme_ ### Warning Signals that something needs attention, but is not necessarily a problem yet. _Theme tokens for this component are configurable via `createTheme`. See the theme guide: https://nebula.kaluza.com/guides/theme_ ### Info Provides supporting context that is useful but not urgent. _Theme tokens for this component are configurable via `createTheme`. See the theme guide: https://nebula.kaluza.com/guides/theme_ ### Success Confirms that an action completed as expected or that a state is healthy. _Theme tokens for this component are configurable via `createTheme`. See the theme guide: https://nebula.kaluza.com/guides/theme_ ## Canvas Canvas is main colour used for backgrounds and impacts the contrast requirements of the other colours - **Primary**, **Body** and **Heading** for example need to be visible when appearing with this colour. _Theme tokens for this component are configurable via `createTheme`. See the theme guide: https://nebula.kaluza.com/guides/theme_ ## Canvas Muted Canvas Muted is similar to **Canvas**, but allows for an additinal background colour to distunguish section, such as alternating rows on a table. _Theme tokens for this component are configurable via `createTheme`. See the theme guide: https://nebula.kaluza.com/guides/theme_ ## Body Body is used for body text. It needs to be accessible when used with **Canvas** and **Canvas Muted**. _Theme tokens for this component are configurable via `createTheme`. See the theme guide: https://nebula.kaluza.com/guides/theme_ ## Heading Heading is used for heading text. It needs to be accessible when used with **Canvas** and **Canvas Muted**. _Theme tokens for this component are configurable via `createTheme`. See the theme guide: https://nebula.kaluza.com/guides/theme_ ## Border Border is used the borders of important UI elements, such as text inputs. It needs to be accessible when used with **Canvas** and **Canvas Muted**. _Theme tokens for this component are configurable via `createTheme`. See the theme guide: https://nebula.kaluza.com/guides/theme_ ## Border Muted Border Muted is used the borders of decorative UI elements, such as cards. As it is used for decorative elements only it has no specific requirement to be accessible. _Theme tokens for this component are configurable via `createTheme`. See the theme guide: https://nebula.kaluza.com/guides/theme_ ## Focus Focus is used to highlight the currently selected item when using tab navigation. It needs to be accessible when used with **Canvas** and **Canvas Muted**. _Theme tokens for this component are configurable via `createTheme`. See the theme guide: https://nebula.kaluza.com/guides/theme_ ## Accessibility We take web accessibility standards seriously. As a result, we require all designs to meet the minimum contrast ratios specified by **WCAG 2.1 Level AA** which requires a contrast ratio of at least **4.5:1 for normal text** and **3:1 for large text**. It also requires a contrast ratio of at least **3:1 for graphics and user interface components** (such as form input borders). You can check contrast at [https://webaim.org/resources/contrastchecker/](https://webaim.org/resources/contrastchecker/) ### What is large text? Large text is defined as typically **bold text of 18.66px and larger, or regular text of 24px and larger**. WCAG 2.1 Level AA requres a contrast ratio of 3:1 for Large Text. ### What counts as decorative use? Just because a colour doesn’t have sufficient contrast, doesn’t mean we can’t use it for **purely decorative** means. When using colours in this way it’s important not to relay any important information, as visually impared users may struggle to see it at all. The element should have no impact on the meaning of the page - **if it were removed, it wouldn’t affect the user’s experience**. --- # Elevation Shadows, layers and z-indices for raised canvas elements. Section: foundations Docs: https://nebula.kaluza.com/foundations/elevation --- ## Overview Elevation describes how elements are raised from a canvas in order to communicate priority, importance or position. Raising elements can be applied in both a visual form, e.g. a [Card](/components/card/) shadow or a functional way, e.g. a z-index on a modal. ## Shadows _Theme tokens for this component are configurable via `createTheme`. See the theme guide: https://nebula.kaluza.com/guides/theme_ --- # Interaction Active, hover and focus states for interactive elements. Section: foundations Docs: https://nebula.kaluza.com/foundations/interaction --- ## Overview Interactive elements show their state through focus, hover and active styles, so people can tell what they can use and where they are on the page. ## Focus Ring Focus rings should be used for all interactive elements, such as a [CTA Button](/components/cta-button/) or [Text Link](/components/text-link/). This will clearly identify elements as interactive to keyboard users. ```tsx Plant a Tree ``` --- # Layout Responsive layout, breakpoints and grids. Section: foundations Docs: https://nebula.kaluza.com/foundations/layout --- ## Overview Layouts in Nebula respond to screen size through shared breakpoints and media queries, so every product changes layout at the same points. ## Breakpoints Responsive tokens define breakpoints and media queries. They are based on screen size, not device type. Therefore, a mobile device or tablet will change layout based on breakpoints. _Theme tokens for this component are configurable via `createTheme`. See the theme guide: https://nebula.kaluza.com/guides/theme_ ## Media Queries _Theme tokens for this component are configurable via `createTheme`. See the theme guide: https://nebula.kaluza.com/guides/theme_ --- # Motion Speed, distance, direction and duration for animated elements. Section: foundations Docs: https://nebula.kaluza.com/foundations/motion --- ## Overview Motion describes how elements move across a canvas, including the **speed**, **distance**, **direction** and **duration**. Micro-interactions such as buttons and dropdowns will favour quicker motion, where as slide in menus and modals will be more suited to a slower interaction. ## Transitions _Theme tokens for this component are configurable via `createTheme`. See the theme guide: https://nebula.kaluza.com/guides/theme_ --- # Spacing Padding, margins and white-space between elements. Section: foundations Docs: https://nebula.kaluza.com/foundations/spacing --- ## Overview Spacing is commonly referred to as **white space**. It is the space between elements and defines the value for common properties such as **padding** and **margin**. ## Scale Nebula spacing is based on a `4px` scale. This means each token is a multiple of 4. A pixel value can easily be converted to its token form by dividing by 4. For example, a 32px space is `space.8`. Additionally we also include some "semantic" values for specific cases - between form fields for example. _Theme tokens for this component are configurable via `createTheme`. See the theme guide: https://nebula.kaluza.com/guides/theme_ --- # Typography Fonts, scales and styles for typographic elements. Section: foundations Docs: https://nebula.kaluza.com/foundations/typography --- ## Overview Typography groups together **font size**, **weight**, **family** and **line heights**. Responsive font sizes are array values that correspond to the responsive breakpoints. The first value will be used below the smallAndUp breakpoint, the second value will be used for all other screen sizes. ## Line Height Line height is a ratio, set per type style so headings, body text and small text can each be tuned independently. Text that isn't one of these named styles uses `standard`. _Theme tokens for this component are configurable via `createTheme`. See the theme guide: https://nebula.kaluza.com/guides/theme_ ## Weight Aa Aa Aa _Theme tokens for this component are configurable via `createTheme`. See the theme guide: https://nebula.kaluza.com/guides/theme_ ## Fonts Nebula uses the open source [Aspekta](https://github.com/ivodolenc/aspekta) font family for headings and display text, and the [Inter](https://fonts.google.com/specimen/Inter) font family, available from Google Fonts, for body text. If Aspekta is not present, Inter is used as the fallback. Display Body Monospace _Theme tokens for this component are configurable via `createTheme`. See the theme guide: https://nebula.kaluza.com/guides/theme_ ## Font Sizes Font sizes are paired for responsive scaling. Each token has an array of corresponding values. As a general rule, the small screen size is usually one step down the scale. e.g. `body` on small screen is equivalent to `small` on screen sizes above `small`. Level 1 Level 2 Level 3 Level 4 Body Small Level 1 Level 2 Level 3 Level 4 Body Small The scale is also available under `fontSizes`, but the recommendation is to use `responsiveFontSizes` to get the responsive pairings. _Theme tokens for this component are configurable via `createTheme`. See the theme guide: https://nebula.kaluza.com/guides/theme_ --- # Accordion Accordions let users show and hide sections of related content on a page. Section: components Docs: https://nebula.kaluza.com/components/accordion --- ## When to use - To show an overview of multiple related sections of content, where users can selectively reveal what is relevant to them - To let users compare details across sections without having to navigate away - For frequently-asked-question lists where most users will not read every answer - When page length would otherwise be very long and most content is optional for any given user ## When not to use - **When the content is essential and all users must see it** - place the content directly on the page as body text instead. Hiding critical information risks users missing it. - **For a sequence of questions** - use a separate page per question in a multi-step form, following the one-thing-per-page pattern. - **For content that belongs in a navigation structure** - use [Tabs](/components/tabs) instead. - **For a single show/hide toggle** - use [Disclosure](/components/disclosure) instead, which is lighter and does not carry the visual weight of a full accordion group. - **When you need to nest accordions** - do not nest accordions inside other accordions. Flatten the content hierarchy instead. ## Anatomy 1. **Container** (the Card wrapper that groups all accordion items) 2. **Header** (the clickable area containing the button and title) 3. **Toggle button** (the button that expands or collapses the panel; contains the icon and title text) 4. **Icon** (plus when collapsed, minus when expanded) 5. **Title** (the label for the section) 6. **Content panel** (the region revealed when the accordion is expanded; hidden from all users when collapsed) ## Keyboard support ### Keyboard functions - **Enter / Space:** toggle the focused accordion open or closed - **Tab:** move focus to the next focusable element inside the content panel (when expanded) or to the next accordion header ## Basic usage ```tsx

Good question. As you might have guessed from the examples above, your carbon footprint comes from many areas. Like driving your car. How you power your home. Which foods you decide to eat.

To calculate it, you have to add up the carbon emitted by absolutely everything you do, over time. We take you through the things you'd need to look at below.

The average carbon footprint of a person in the UK weighs in at 6.6 tonnes. Sounds like quite a lot, doesn't it. Yikes.

``` ## Variants and features ### Default expanded Accordions are collapsed by default. Pass `expanded` to open a panel on initial render. This is useful when you want to draw attention to a specific section on page load. ```tsx

Good question. As you might have guessed from the examples above, your carbon footprint comes from many areas. Like driving your car. How you power your home. Which foods you decide to eat.

To calculate it, you have to add up the carbon emitted by absolutely everything you do, over time. We take you through the things you'd need to look at below.

The average carbon footprint of a person in the UK weighs in at 6.6 tonnes. Sounds like quite a lot, doesn't it. Yikes.

``` ### Custom heading level The accordion title renders as an `h3` by default. You must use `headingComponent` to match the heading level to the document outline of the page it sits on. ```tsx

Good question. As you might have guessed from the examples above, your carbon footprint comes from many areas. Like driving your car. How you power your home. Which foods you decide to eat.

To calculate it, you have to add up the carbon emitted by absolutely everything you do, over time. We take you through the things you'd need to look at below.

The average carbon footprint of a person in the UK weighs in at 6.6 tonnes. Sounds like quite a lot, doesn't it. Yikes.

``` ### Controlled toggle Use `onToggle` to respond to open/close events, for example to track analytics or to synchronise state elsewhere in the page. `onToggle` does not control the panel: the accordion still manages its own open state. ```tsx const Example = () => { const [lastToggled, setLastToggled] = useState('nothing yet'); return ( setLastToggled('Where does my carbon footprint come from?')} >

Good question. As you might have guessed from the examples above, your carbon footprint comes from many areas. Like driving your car. How you power your home. Which foods you decide to eat.

setLastToggled('How do I calculate my carbon footprint?')} >

To calculate it, you have to add up the carbon emitted by absolutely everything you do, over time. We take you through the things you'd need to look at below.

Last toggled: {lastToggled}

); } ``` ## Grouping Wrap related accordion items in `AccordionGroup`. The group provides the Card container and the dividers between items. Do not place a single accordion item outside an `AccordionGroup` unless you have a strong reason. A lone accordion without its group container loses the visual structure that helps users understand the pattern. ## Rules and guidance ### Usage - **Accordion** MUST NOT be used to hide content that all users must see to complete their task. Rationale: Hidden content is easy to overlook. If the content is critical, place it directly on the page. [id: accordion.non-essential-content; testability: manual] - **Accordion** MUST NOT be used as a container for a sequence of questions in a multi-step flow. Rationale: Multi-step question sequences belong on separate pages, following the one-thing-per-page pattern. [id: accordion.no-question-sequences; testability: context] - **Accordion headingComponent** MUST match the heading level required by the document outline of the page it sits on. Rationale: Incorrect heading levels break the page's document structure and confuse screen reader users navigating by headings. [id: accordion.heading-level; testability: manual] ### Content - **Accordion title** MUST be unique within its AccordionGroup. Rationale: Duplicate titles make it impossible for users navigating by heading or button list to distinguish between items. [id: accordion.title.unique; testability: auto] - **Accordion title** SHOULD be short and scannable so users can decide whether to expand without reading the panel content. Rationale: Accordion titles act as navigation labels. Long or vague titles undermine the component's purpose. [id: accordion.title.concise; testability: manual] - **Accordion title** SHOULD NOT end with a full stop or colon. [id: accordion.title.no-terminal-punctuation; testability: auto] ### Interaction - **Accordion toggle button** WILL be rendered as a native HTML button element. The component outputs a styled