@alma-oss/spirit-web-react
v4.5.0
Published
React implementation of Spirit Design System components
Readme
@alma-oss/spirit-web-react
React implementation of Spirit Design System components.
- Install
- Usage
- Additional Attributes
- Testing
- Styling
- Types Generated From Design Tokens
- Controlled vs Uncontrolled Components
- Deprecations
- Examples
- Implementing from Figma
- License
Install
Expecting you have react and react-dom installed in your app, run:
yarn add @alma-oss/spirit-web @alma-oss/spirit-web-reactor
npm install --save @alma-oss/spirit-web @alma-oss/spirit-web-reactUsage
Link Spirit CSS (see spirit-web docs for more options):
<link rel="stylesheet" href="node_modules/@alma-oss/spirit-web/css/foundation.min.css" />
<link rel="stylesheet" href="node_modules/@alma-oss/spirit-web/css/components.min.css" />
<link rel="stylesheet" href="node_modules/@alma-oss/spirit-web/css/helpers.min.css" />
<link rel="stylesheet" href="node_modules/@alma-oss/spirit-web/css/utilities.min.css" />⚠️ Make sure to load all CSS files above and in the same order.
Import React components in your app:
import { Button } from '@alma-oss/spirit-web-react';Prefixing CSS Classes in Components
If you want to prefix the component classes with your own namespace, you can use the ClassNamePrefixProvider context to provide a prefix to all components in your app.
Check spirit-web docs to learn how to prefix CSS class names.
import { ClassNamePrefixProvider } from '@alma-oss/spirit-web-react/context/ClassNamePrefixContext';
export const Example = () => {
return (
<ClassNamePrefixProvider value="jobs">
<Button>Button</Button>
</ClassNamePrefixProvider>
);
};Client Side Routing
If you want to use client-side routing with router-integrated link-like components (for example Link, ButtonLink, PaginationLink, HeaderLink, or TabLink), you can use the RouterProvider context to provide a router navigation function. When a router is provided, internal links will use client-side navigation instead of full page reloads.
The RouterProvider accepts the following props:
children- React node subtree that should receive the router integration (required)navigate- A function that accepts a path string and optional router options
External links (absolute http:// / https:// URLs, protocol-relative URLs like //example.com, or URLs with other schemes such as mailto: and tel:), links with non-_self target, links with download, and disabled links will always use standard anchor behavior.
Router Options
All link components accept a routerOptions prop, which is an object that is passed through to the client side router's navigate function as the second argument. This can be used to control any router-specific behaviors, such as scrolling, replacing instead of pushing to the history, etc.
<Link href="/login" routerOptions={{ replace: true }}>
Login
</Link>Next.js App Router
'use client';
import { Link, RouterProvider } from '@alma-oss/spirit-web-react';
import { useRouter } from 'next/navigation';
const MyComponent = () => {
const router = useRouter();
return (
<RouterProvider navigate={router.push}>
<Link href="/about">About</Link>
<Link href="/contact">Contact</Link>
</RouterProvider>
);
};If you use the Next.js basePath setting, include it directly in Spirit href values so rendered anchor URLs stay correct:
'use client';
import { Link, RouterProvider } from '@alma-oss/spirit-web-react';
import { useRouter } from 'next/navigation';
// Same string as `basePath` in `next.config.js` (leading slash, no trailing slash)
const basePath = '/docs';
const MyComponent = () => {
const router = useRouter();
return (
<RouterProvider navigate={router.push}>
<Link href={`${basePath}/about`}>About</Link>
</RouterProvider>
);
};Next.js Pages Router
In the Pages Router, router.push is (url, as?, options?): the second argument is the optional as path, and transition options (e.g. shallow) belong in the third argument. Spirit passes routerOptions from link components as the second argument to navigate, so you should not pass router.push directly—use a thin wrapper that forwards options as the third parameter:
import { Link, RouterProvider } from '@alma-oss/spirit-web-react';
import { useRouter } from 'next/router';
const MyComponent = () => {
const router = useRouter();
const navigate = (path, opts) => router.push(path, undefined, opts);
return (
<RouterProvider navigate={navigate}>
<Link href="/about">About</Link>
<Link href="/contact" routerOptions={{ shallow: true }}>
Contact
</Link>
</RouterProvider>
);
};React Router
Use navigate from your router. Keep href values in the format expected by your app/router configuration.
import { Link, RouterProvider } from '@alma-oss/spirit-web-react';
import { useNavigate } from 'react-router-dom';
const MyComponent = () => {
const navigate = useNavigate();
return (
<RouterProvider navigate={navigate}>
<Link href="/about">About</Link>
</RouterProvider>
);
};Remix
Remix re-exports compatible navigation helpers; use navigate in the same way:
import { Link, RouterProvider } from '@alma-oss/spirit-web-react';
import { useNavigate } from '@remix-run/react';
const MyComponent = () => {
const navigate = useNavigate();
return (
<RouterProvider navigate={navigate}>
<Link href="/about">About</Link>
</RouterProvider>
);
};Additional Attributes
All components accept additional attributes that are passed down to the root element of the component. This is useful for adding custom event handlers, accessibility attributes, or other attributes that are not supported by the component API.
ℹ️ If you need to pass down event handlers to the native form elements in our form components,
you can use the inputProps prop.
Supported attributes are:
on*(eg.onClick)data-*aria-*id
If the component sets a value for any of these attributes, the value passed in will be overwritten.
Most components also accept native HTML attributes based on the component's element type.
Testing
End-to-End Testing
For detailed information about E2E tests including visual regression and accessibility testing, please refer to docs/contribution/e2e-testing.md and docs/contribution/accessibility-testing.md.
Styling
Spirit components are designed to be consistent across all Alma Career applications. They include built-in styling that has been considered carefully, and extensively tested. In general, customizing Spirit design is discouraged, but most components do offer control over layout and other aspects. In addition, you can use Spirit defined design tokens to ensure your application conforms to design requirements, and is adaptive across platform scales and color schemes.
Style Props
All Spirit components accept a set of props that can be used to control their outer spacing and display. The props are:
Theme Prop
theme
The theme prop applies a theme utility class generated from the design tokens.
Use kebab-case values that mirror the token keys (eg. theme-light-default, theme-light-on-brand).
You can apply the theme prop at any nest level where the component consumes style props and children components inherit the theme.
Spacing Props
marginmarginTopmarginRightmarginBottommarginLeftmarginXmarginY
These props accept a spacing token (eg. space-100), auto or an object with breakpoint keys and spacing token
values or auto. We use these props to set global CSS utility classes on the root element of the component.
Display Props
hideOn- Hide the component only at specific breakpoint(s)hideFrom- Hide the component from a specific breakpoint and up
The hideOn prop accepts either a single breakpoint token (e.g. mobile, tablet, desktop), or an array of breakpoint tokens.
The hideFrom prop accepts a single breakpoint token.
The component will be hidden from the specified breakpoints up using CSS display utilities.
Examples
// Spacing examples
<Alert marginBottom="space-100" />
<Button marginX={{ mobile: 'space-100', tablet: 'space-200' }} />
// Hide examples
<Alert hideOn="mobile" /> // Hidden only on mobile
<Button hideOn={['mobile', 'tablet']} /> // Hidden only on mobile and tablet
<Alert hideFrom="tablet" /> // Hidden from tablet breakpoint and up
// Theme examples
<Header theme="theme-light-default" />
<Header theme="theme-light-on-brand" />If you need even more control over the component styling, use escape hatches.
Types Generated From Design Tokens
Some props (e.g. color, radii, container sizes, …) use types generated from design tokens. They restrict values to those defined in the design system and stay up to date with every token change.
If you need additional values, you’ll need to coordinate with a designer so they can be added to the Figma and become available through tokens.
Escape Hatches
While we encourage teams to utilize Spirit design as it is, we do realize that sometimes product specific customizations may be needed. In these cases, we encourage you or your designers to talk to us. We may be able to suggest an alternative implementation strategy, or perhaps your design can help propose future Spirit additions.
While the traditional className and style props are not supported in Spirit Web React components, there are two escape hatches that you can use at your own risk. These are UNSAFE_className and UNSAFE_style. Use of these props should be considered a last resort. They can be used to work around bugs or limitations in Spirit Web React, but should not be used in the long term.
The reasoning behind this is that future updates to Spirit design may cause unintended breaking changes in products. If the internal DOM structure or CSS properties of a Spirit Web React component change, this may lead to conflicts with CSS overrides in products. For this reason, className and style are unsafe, and if you use them know that you are doing so at your own risk.
Please consult additional styling with web package documentation.
Controlled vs Uncontrolled Components
A Controlled Component is one that takes its current value through props and notifies changes through callbacks like onChange. A parent component "controls" it by handling the callback and managing its own state and passing the new values as props to the controlled component. You could also call this a "dumb component".
An Uncontrolled Component is one that stores its own state internally, and you query the DOM using a ref to find its current value when you need it. This is a bit more like traditional HTML.
All components are by default provided as controlled components so you must provide your own controlling or toggle functionality to make them work as you want.
For a better developer experience there is also an uncontrolled variant of the component provided.
You can use the Uncontrolled variant for faster development.
Deprecations
This package uses the deprecation warnings for props, functions and components that will be removed or replaced in the next major release. Check your browser console to see if you are using any of the deprecated functionality.

👉 See the DEPRECATIONS file for the list of all deprecations.
Warnings in Environments
Production
The warning utility which is used for deprecation warnings checks the process.env.NODE_ENV variable to determine if the warnings should be shown.
If the environment variable is set to the production the warnings will not be shown.
Testing
While running tests, you likely will see the deprecation warnings.
You can suppress the warnings by simply mocking the implementation of the warning utility or console.warn function.
But we strongly discourage you from doing so, as the deprecation warnings are there to help you to prepare for the next major release.
beforeEach(() => {
jest.spyOn(console, 'warn').mockImplementation(() => {});
});
afterEach(() => {
console.warn.mockRestore();
});Examples
👀 See examples for a live demo.
Implementing From Figma
If you are implementing Spirit components from Figma designs — with or without AI assistance — we provide and recommend the following tools to help you.
Figma Code Connect
Figma Code Connect links Figma components to code snippets. Use it to see how each design component
is written in code and which props to use (e.g. size, iconName, textColor) when implementing. With Figma Dev Mode
you can read the snippets directly in Figma in the "Code" tab.

AI-Assisted Implementation
If you use AI-powered tools to implement UI from Figma, the following help the agent produce accurate Spirit code.
Figma MCP — Figma MCP (Model Context Protocol) lets the AI read your Figma files: design context,
screenshots, and metadata for selected nodes. With the Figma desktop app open and the Figma MCP server configured
in your tool, the agent can use tools such as get_design_context and get_screenshot.
See Figma MCP documentation for more information.
Figma-to-Spirit skill — We also provide an agent skill Figma to Spirit that teaches your AI-powered tool how to convert Figma designs into React using Spirit Web React components (layout, typography, cards, and rules for reading props from Figma and Code Connect). Install it with:
npx skills add https://github.com/alma-oss/spirit-design-system --skill figma-to-spiritTo learn more about skills, see Skills documentation.
Summary
- In Figma, use Figma Code Connect to see and copy the code for the component you are implementing.
- If you use AI, install the Figma MCP and Figma-to-Spirit skill and then prompt the agent to implement the UI from Figma.
License
See the LICENSE file for information.
