# Theme Logos
-
-
-
+
+
+
Logos can be added to new themes by adding to the `createTheme` call:
@@ -29,7 +29,7 @@ import {ImageColourSchemeSwitch} from "../../components/controls/ImageColourSche
```
diff --git a/src/themes/DiamondDSTheme.ts b/src/themes/DiamondDSTheme.ts
index 7e633b57..18973acf 100644
--- a/src/themes/DiamondDSTheme.ts
+++ b/src/themes/DiamondDSTheme.ts
@@ -30,8 +30,8 @@ import type { OutlinedInputProps } from "@mui/material/OutlinedInput";
import type { RadioProps } from "@mui/material/Radio";
import type { TabProps } from "@mui/material/Tab";
-import logoImageLight from "../public/diamond/logo-light.svg";
-import logoImageDark from "../public/diamond/logo-dark.svg";
+import logoImageLightSurface from "../public/diamond/logo-light-surface.svg";
+import logoImageDarkSurface from "../public/diamond/logo-dark-surface.svg";
import logoShort from "../public/diamond/logo-short.svg";
import type { ImageColourSchemeSwitchType } from "components/controls/ImageColourSchemeSwitch";
@@ -599,8 +599,8 @@ const DiamondDSTheme = extendTheme({
logos: {
normal: {
- src: logoImageLight,
- srcDark: logoImageDark ?? logoImageLight,
+ src: logoImageLightSurface,
+ srcDark: logoImageDarkSurface ?? logoImageLightSurface,
alt: "Diamond Light Source Logo",
width: "100",
},
diff --git a/src/themes/DiamondOldTheme.ts b/src/themes/DiamondOldTheme.ts
index 5cfbf385..b736c7a6 100644
--- a/src/themes/DiamondOldTheme.ts
+++ b/src/themes/DiamondOldTheme.ts
@@ -3,8 +3,8 @@ import { createTheme, Theme } from "@mui/material/styles";
import { mergeThemeOptions } from "./ThemeManager";
import { DiamondThemeOptions } from "./DiamondTheme";
-import logoImageDark from "../public/diamond/logo-dark.svg";
-import logoImageLight from "../public/diamond/logo-light.svg";
+import logoImageDark from "../public/diamond/logo-dark-surface.svg";
+import logoImageLight from "../public/diamond/logo-light-surface.svg";
const DiamondOldThemeOptions = mergeThemeOptions(
{
diff --git a/src/themes/DiamondTheme.ts b/src/themes/DiamondTheme.ts
index 17500862..7dad5178 100644
--- a/src/themes/DiamondTheme.ts
+++ b/src/themes/DiamondTheme.ts
@@ -3,7 +3,7 @@ import { createTheme, Theme } from "@mui/material/styles";
import { mergeThemeOptions } from "./ThemeManager";
-import logoImageLight from "../public/diamond/logo-light.svg";
+import logoImageLight from "../public/diamond/logo-light-surface.svg";
import logoShort from "../public/diamond/logo-short.svg";
const dlsLogoBlue = "#202740";
diff --git a/src/themes/GenericTheme.ts b/src/themes/GenericTheme.ts
index 6fb29d9e..76baaba0 100644
--- a/src/themes/GenericTheme.ts
+++ b/src/themes/GenericTheme.ts
@@ -2,8 +2,8 @@ import { createTheme, Theme } from "@mui/material/styles";
import { mergeThemeOptions } from "./ThemeManager";
-import logoImageDark from "../public/generic/logo-dark.svg";
-import logoImageLight from "../public/generic/logo-light.svg";
+import logoImageDark from "../public/generic/logo-dark-surface.svg";
+import logoImageLight from "../public/generic/logo-light-surface.svg";
const GenericThemeOptions = mergeThemeOptions({
logos: {
From 1723de483e6ffdb7ec654fc876b838426b81e39f Mon Sep 17 00:00:00 2001
From: VictoriaBeilstenEdmands
<45741274+VictoriaBeilsten-Edmands@users.noreply.github.com>
Date: Mon, 8 Jun 2026 11:32:31 +0100
Subject: [PATCH 02/13] Remove multi-theme support from Storybook
---
.storybook/preview.tsx | 27 ++++-----------------------
1 file changed, 4 insertions(+), 23 deletions(-)
diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx
index a03750d5..ef1f6e2c 100644
--- a/.storybook/preview.tsx
+++ b/.storybook/preview.tsx
@@ -10,15 +10,6 @@ import "../src/styles/diamondDS/diamond-ds-roles.css";
const TextThemeDiamondDS = "Theme: DiamondDS";
-// Left in for now even though only a single theme
-function resolveTheme(selectedTheme: string) {
- switch (selectedTheme) {
- case TextThemeDiamondDS:
- default:
- return DiamondDSTheme;
- }
-}
-
function resolveDefaultMode(selectedThemeMode: string) {
if (selectedThemeMode === TextLight) return "light";
if (selectedThemeMode === TextDark) return "dark";
@@ -36,12 +27,11 @@ export const decorators = [
},
(Story, context) => {
- const selectedTheme = context.globals.theme || TextThemeDiamondDS;
const selectedThemeMode = context.globals.themeMode || TextSystem;
return (
@@ -56,28 +46,19 @@ export const decorators = [
const preview: Preview = {
globalTypes: {
- theme: {
- description: "Global theme for components",
- toolbar: {
- title: "Theme",
- icon: "cog",
- items: [TextThemeDiamondDS],
- dynamicTitle: true,
- },
- },
themeMode: {
description: "Global theme mode for components",
toolbar: {
title: "Theme Mode",
icon: "mirror",
- items: [TextLight, TextDark, TextSystem],
+ items: [TextLight, TextDark],
dynamicTitle: true,
},
},
},
initialGlobals: {
- theme: TextThemeDiamond,
- themeMode: TextSystem,
+ theme: TextThemeDiamondDS,
+ themeMode: TextLight,
},
parameters: {
controls: {
From 0a33352a2d47c5d022af6126220dec0aaf83d9c2 Mon Sep 17 00:00:00 2001
From: VictoriaBeilstenEdmands
<45741274+VictoriaBeilsten-Edmands@users.noreply.github.com>
Date: Mon, 8 Jun 2026 11:44:46 +0100
Subject: [PATCH 03/13] Update changelog
---
changelog.md | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/changelog.md b/changelog.md
index 3797b903..c1a917d0 100644
--- a/changelog.md
+++ b/changelog.md
@@ -2,6 +2,14 @@
## [Unreleased]
+### Changed
+- **Breaking** Introduced new design system theme based on semantic surface tokens.
+- **Breaking** Removed Diamond and Generic themes which are no longer supported.
+- **Breaking** Updated components to new theme and ensure compatability in light/dark modes.
+- *Logo* uses tone (default/inverse) to adapt to surface colour. Deprecated use of interchange prop.
+
+## [v0.5.0] - 2026-06-03
+
### Changed
- **Breaking** `keycloak-js` has been moved from a direct dependency to a peer and optional dependency, so must now be installed by the consuming application.
From 4f1dee5fd3894516a21dbb4a110d257c28e23f2e Mon Sep 17 00:00:00 2001
From: VictoriaBeilstenEdmands
<45741274+VictoriaBeilsten-Edmands@users.noreply.github.com>
Date: Mon, 8 Jun 2026 14:16:01 +0100
Subject: [PATCH 04/13] Remove old theme and update docs
---
.storybook/preview.tsx | 1 -
src/components/controls/ScrollableImages.tsx | 2 +-
src/components/navigation/Navbar.test.tsx | 3 -
src/storybook/Installation.mdx | 68 +++++++-----------
src/storybook/theme/Colours.mdx | 72 --------------------
src/themes.ts | 2 +-
src/themes/DiamondOldTheme.ts | 37 ----------
src/themes/ThemeProvider.tsx | 4 +-
8 files changed, 28 insertions(+), 161 deletions(-)
delete mode 100644 src/storybook/theme/Colours.mdx
delete mode 100644 src/themes/DiamondOldTheme.ts
diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx
index ef1f6e2c..e1611868 100644
--- a/.storybook/preview.tsx
+++ b/.storybook/preview.tsx
@@ -79,7 +79,6 @@ const preview: Preview = {
"Helpers",
"Theme",
"Theme/Logos",
- "Theme/Colours",
"MUI",
"Components",
],
diff --git a/src/components/controls/ScrollableImages.tsx b/src/components/controls/ScrollableImages.tsx
index 28da374a..037bb2a0 100644
--- a/src/components/controls/ScrollableImages.tsx
+++ b/src/components/controls/ScrollableImages.tsx
@@ -43,7 +43,7 @@ const ScrollableImages = ({
wrapAround = true,
slider = true,
numeration = true,
- backgroundColor = "#eee",
+ backgroundColor,
scrollStep = 320,
}: ScrollableImagesProps) => {
const [imageList, setImageList] = useState([]);
diff --git a/src/components/navigation/Navbar.test.tsx b/src/components/navigation/Navbar.test.tsx
index 7674f77d..03239fc9 100644
--- a/src/components/navigation/Navbar.test.tsx
+++ b/src/components/navigation/Navbar.test.tsx
@@ -22,9 +22,6 @@ describe("Navbar", () => {
// check new style is set
expect(headerComputedStyle.border).toBe(borderStyle);
-
- // Check default values are still set
- expect(headerComputedStyle.height).not.toBe("0px");
});
});
diff --git a/src/storybook/Installation.mdx b/src/storybook/Installation.mdx
index f322bbbf..5529e767 100644
--- a/src/storybook/Installation.mdx
+++ b/src/storybook/Installation.mdx
@@ -32,24 +32,26 @@ import { Meta } from "@storybook/blocks";
### Next, Add a ThemeProvider
- Select theme `DiamondDSTheme` (or `GenericTheme`), and add it to the App via the ThemeProvider.
-
- Alternatively, you can fork an existing theme or create your own (see below).
+ Use the provided `DiamondDSTheme` with the ThemeProvider or create your own (see below). The theme controls colour via semantic surface tokens.
```tsx filename="main.tsx"
import {
ThemeProvider,
- GenericTheme
+ DiamondDSTheme
} from "@diamondlightsource/sci-react-ui";
root.render(
-
+
)
```
- ### Finally, Add the Components
+ The design sytem theme consists of two parts:
+ - Theme configuration (DiamondDSTheme.ts)
+ - Semantic role tokens (diamond-ds-roles.css)
+
+ ### Finally, add the Components
Then create something, for instance, you may want a Navigation Bar at the top of your website:
@@ -118,42 +120,6 @@ import { Meta } from "@storybook/blocks";
-
- ## Create new Theme
-
- The 'GenericTheme.ts' shows you a simple example of creating a new theme.
- Use `mergeThemeOptions` to create a new one from an existing one.
-
- To inherit from the DiamondTheme by overriding the light palette, use something like this:
-
- ```tsx
- import { createTheme, Theme } from "@mui/material/styles";
- import { DiamondThemeOptions, mergeThemeOptions } from '@diamondlightsource/sci-react-ui'
-
- const MyThemeOptions = mergeThemeOptions({
- colorSchemes: {
- light: {
- palette: {
- primary: {
- main: "#f00",
- light: "#f55",
- dark: "#a00",
- contrastText: "#fff",
- },
- },
- },
- }
- }, DiamondThemeOptions);
-
- const MyTheme: Theme = createTheme(MyThemeOptions);
- export { MyTheme };
- ```
-
- The theme options are documented in MUI. For the examples used in DiamondTheme
- see [DiamondTheme.tsx](https://github.com/DiamondLightSource/sci-react-ui/blob/main/src/themes/DiamondTheme.ts)
- on GitHub.
-
-
## Adapting the Diamond DS Theme
@@ -162,9 +128,23 @@ import { Meta } from "@storybook/blocks";
The important part is the role tokens. If your CSS defines the same role variables, the components will continue to work with your new colours.
- To customise the theme, copy `DiamondDSTheme` and `diamond-ds-roles.css`, then rename both for your project. In your copied CSS file, keep the same token names and replace the colour values with your own palette.
+ To create your own theme:
+
+ 1. Copy the base files and rename them for your own project:
+ - DiamondDSTheme.ts -> MyDSTheme.ts
+ - diamond-ds-roles.css -> my-ds-roles.css
+
+ 2. Update the CSS tokens
+
+ The CSS file defines semantic roles such as surfaces, text, and borders.
+ Keep the variable names the same, but update the values:
- For example, within the CSS file you can change `--ds-primary: #2a4db8;` to your desired primary colour.
+ ```
+ :root {
+ --ds-surface: #ffffff;
+ --ds-on-surface: #1a1c23;
+ }
+ ```
diff --git a/src/storybook/theme/Colours.mdx b/src/storybook/theme/Colours.mdx
deleted file mode 100644
index 828ac585..00000000
--- a/src/storybook/theme/Colours.mdx
+++ /dev/null
@@ -1,72 +0,0 @@
-import { Meta, ColorPalette, ColorItem } from '@storybook/blocks';
-
-import {GenericTheme} from "../../../src";
-import {DiamondTheme} from "../../../src";
-
-
-export function ThemeColorItem({title, theme, colourSet, mode}) {
- return
-}
-
-
-
-
-
- # Theme Colours
-
- Theme colours available:
-
- * [Generic Theme](#generic)
- * [Diamond Theme](#diamond)
-
-
-
-
-
- ## Generic Theme
-
-
- ### Light Mode
-
-
-
-
-
- ### Dark Mode
-
-
-
-
-
-
-
-
-
-
-
- ## Diamond Theme
-
-
- ### Light Mode
-
-
-
-
-
- ### Dark Mode
-
-
-
-
-
-
-
diff --git a/src/themes.ts b/src/themes.ts
index f55fdf99..60615048 100644
--- a/src/themes.ts
+++ b/src/themes.ts
@@ -1,6 +1,6 @@
export * from "./themes/BaseTheme";
export * from "./themes/DiamondTheme";
-export * from "./themes/DiamondOldTheme";
+export * from "./themes/DiamondDSTheme";
export * from "./themes/GenericTheme";
export * from "./themes/ThemeProvider";
export * from "./themes/ThemeManager";
diff --git a/src/themes/DiamondOldTheme.ts b/src/themes/DiamondOldTheme.ts
deleted file mode 100644
index b736c7a6..00000000
--- a/src/themes/DiamondOldTheme.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import { createTheme, Theme } from "@mui/material/styles";
-
-import { mergeThemeOptions } from "./ThemeManager";
-import { DiamondThemeOptions } from "./DiamondTheme";
-
-import logoImageDark from "../public/diamond/logo-dark-surface.svg";
-import logoImageLight from "../public/diamond/logo-light-surface.svg";
-
-const DiamondOldThemeOptions = mergeThemeOptions(
- {
- logos: {
- normal: {
- src: logoImageDark, // Use the dark image for light backgrounds
- srcDark: logoImageLight, // Use the light image for dark backgrounds
- alt: "Diamond Light Source Logo",
- width: "100",
- },
- },
- colorSchemes: {
- dark: {
- palette: {
- primary: {
- main: "#6175bd", //lightened version of {dlsLogoBlue}
- light: "#8090CA", // lighter blue
- dark: "#435184", // mid blue
- contrastText: "#ffffff", // white
- },
- },
- },
- },
- },
- DiamondThemeOptions,
-);
-
-const DiamondOldTheme: Theme = createTheme(DiamondOldThemeOptions);
-
-export { DiamondOldTheme, DiamondOldThemeOptions };
diff --git a/src/themes/ThemeProvider.tsx b/src/themes/ThemeProvider.tsx
index a4badc84..56fd254f 100644
--- a/src/themes/ThemeProvider.tsx
+++ b/src/themes/ThemeProvider.tsx
@@ -1,7 +1,7 @@
import { ThemeProvider as MuiThemeProvider } from "@mui/material/styles";
import { CssBaseline } from "@mui/material";
-import { GenericTheme } from "./GenericTheme";
import { ThemeProviderProps as MuiThemeProviderProps } from "@mui/material/styles";
+import { DiamondDSTheme } from "./DiamondDSTheme";
interface ThemeProviderProps extends Partial
{
baseline?: boolean;
@@ -9,7 +9,7 @@ interface ThemeProviderProps extends Partial {
const ThemeProvider = function ({
children,
- theme = GenericTheme,
+ theme = DiamondDSTheme,
baseline = true,
defaultMode = "system",
...props
From 0423b2980a380c1cadd7a7ba3385b3b46d689444 Mon Sep 17 00:00:00 2001
From: VictoriaBeilsten-Edmands
<45741274+VictoriaBeilsten-Edmands@users.noreply.github.com>
Date: Mon, 8 Jun 2026 14:57:28 +0100
Subject: [PATCH 05/13] Add slot text to Bar stories
Co-authored-by: Matthew Wilcoxson
---
src/components/controls/Bar.stories.tsx | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/components/controls/Bar.stories.tsx b/src/components/controls/Bar.stories.tsx
index e58c5270..58993bf9 100644
--- a/src/components/controls/Bar.stories.tsx
+++ b/src/components/controls/Bar.stories.tsx
@@ -13,9 +13,9 @@ type Story = StoryObj;
export const Default: Story = {
args: {
- leftSlot: Left,
- centreSlot: Centre,
- rightSlot: Right,
+ leftSlot: Left & Children Slot,
+ centreSlot: Centre Slot,
+ rightSlot: Right Slot,
},
};
From 5b01c3730f83462906feea19cfc0c9cd1a7c57a7 Mon Sep 17 00:00:00 2001
From: VictoriaBeilstenEdmands
<45741274+VictoriaBeilsten-Edmands@users.noreply.github.com>
Date: Tue, 9 Jun 2026 13:02:56 +0100
Subject: [PATCH 06/13] Update Bar and child components
---
.../controls/AppTitlebar.stories.tsx | 64 ++++-
src/components/controls/AppTitlebar.tsx | 16 +-
src/components/controls/Bar.stories.tsx | 261 ++++++++++++++----
src/components/controls/Bar.tsx | 163 +++++++----
.../navigation/Breadcrumbs.stories.tsx | 19 ++
src/components/navigation/Breadcrumbs.tsx | 17 +-
src/components/navigation/Footer.stories.tsx | 74 ++++-
src/components/navigation/Footer.tsx | 19 +-
src/components/navigation/Navbar.stories.tsx | 33 ++-
src/components/navigation/Navbar.tsx | 21 +-
src/styles/diamondDS/diamond-ds-roles.css | 57 ++++
src/themes/DiamondDSTheme.ts | 3 +
12 files changed, 595 insertions(+), 152 deletions(-)
diff --git a/src/components/controls/AppTitlebar.stories.tsx b/src/components/controls/AppTitlebar.stories.tsx
index 7a696998..531acffd 100644
--- a/src/components/controls/AppTitlebar.stories.tsx
+++ b/src/components/controls/AppTitlebar.stories.tsx
@@ -1,7 +1,7 @@
import { Meta, StoryObj } from "@storybook/react";
-import { AppTitle, AppTitlebar } from "./AppTitlebar";
+import { AppTitle, AppTitlebar, AppTitlebarProps } from "./AppTitlebar";
-const meta: Meta = {
+const meta: Meta = {
title: "Components/Controls/AppTitlebar",
component: AppTitlebar,
tags: ["autodocs"],
@@ -15,10 +15,25 @@ const meta: Meta = {
},
},
},
+
+ argTypes: {
+ surface: { table: { disable: true } },
+ variant: { table: { disable: true } },
+ elevation: { table: { disable: true } },
+
+ leftSlot: { control: false },
+ centreSlot: { control: false },
+ rightSlot: { control: false },
+ children: { control: false },
+ },
+
+ args: {
+ title: "Your App Name",
+ },
};
export default meta;
-type Story = StoryObj;
+type Story = StoryObj;
export const Simple: Story = {
args: {
@@ -47,17 +62,15 @@ export const InCentreSlot: Story = {
},
};
-export const DifferentBackground: Story = {
+export const WithActions: Story = {
args: {
- title: "My Great App",
- sx: {
- backgroundColor: "surface.subtle",
- },
+ title: "My App",
+ rightSlot: ,
},
parameters: {
docs: {
description: {
- story: "You can pass styles to the bar.",
+ story: "Use slots to add actions or controls to the title bar.",
},
},
},
@@ -67,4 +80,37 @@ export const CustomTypography: Story = {
args: {
children: ,
},
+ parameters: {
+ docs: {
+ description: {
+ story:
+ "You can override the title content if you need custom typography.",
+ },
+ },
+ },
+};
+export const AppTitlebarVariants: Story = {
+ render: (_args) => (
+ <>
+
+
+
+
+
+
+
+ >
+ ),
+ parameters: {
+ docs: {
+ description: {
+ story:
+ "AppTitlebar defaults to surface container but can be adapted for different emphasis and context.",
+ },
+ },
+ },
};
diff --git a/src/components/controls/AppTitlebar.tsx b/src/components/controls/AppTitlebar.tsx
index a85105d8..e2fdf786 100644
--- a/src/components/controls/AppTitlebar.tsx
+++ b/src/components/controls/AppTitlebar.tsx
@@ -11,16 +11,23 @@ const AppTitle = ({ title, ...props }: AppTitleProps) => (
);
-interface AppTitlebarProps extends BarSlotsProps {
+type AppTitlebarProps = BarSlotsProps & {
title?: string;
-}
+};
/**
A Title bar for your App.
*/
-const AppTitlebar = ({ title, children, ...props }: AppTitlebarProps) => {
+const AppTitlebar = ({
+ surface = "surface",
+ variant = "container",
+ elevation,
+ title,
+ children,
+ ...props
+}: AppTitlebarProps) => {
return (
-
+
{title && }
{children}
@@ -28,3 +35,4 @@ const AppTitlebar = ({ title, children, ...props }: AppTitlebarProps) => {
};
export { AppTitlebar, AppTitle };
+export type { AppTitlebarProps };
diff --git a/src/components/controls/Bar.stories.tsx b/src/components/controls/Bar.stories.tsx
index 58993bf9..f0026aba 100644
--- a/src/components/controls/Bar.stories.tsx
+++ b/src/components/controls/Bar.stories.tsx
@@ -1,98 +1,255 @@
import { Meta, StoryObj } from "@storybook/react";
-import { Typography } from "@mui/material";
-import { Bar } from "./Bar";
+import { BarSlotsProps, Bar } from "./Bar";
+import { Typography } from "../../components/MUI/MuiWrapped";
-const meta: Meta = {
+const meta: Meta = {
title: "Components/Controls/Bar",
component: Bar,
tags: ["autodocs"],
-};
-
-export default meta;
-type Story = StoryObj;
-export const Default: Story = {
- args: {
- leftSlot: Left & Children Slot,
- centreSlot: Centre Slot,
- rightSlot: Right Slot,
+ argTypes: {
+ surface: {
+ control: "select",
+ options: [
+ "surface",
+ "paper",
+ "background",
+ "primary",
+ "secondary",
+ "brand",
+ "brand-fixed",
+ "brand-fixedDim",
+ ],
+ table: { category: "Appearance" },
+ },
+ variant: {
+ control: "select",
+ options: ["base", "container", "solid"],
+ if: { arg: "surface", neq: ["background"] },
+ description:
+ "Use 'base' only with surface/paper. Use 'container' or 'solid' for primary, secondary, and brand.",
+ table: { category: "Appearance" },
+ },
+ elevation: {
+ control: { type: "number", min: 0, max: 24 },
+ if: { arg: "variant", eq: "base" },
+ description:
+ "Only applies to surface/paper with variant='base'. Ignored otherwise.",
+ table: { category: "Appearance" },
+ },
+ containerWidth: {
+ control: "select",
+ options: [false, "xs", "sm", "md", "lg", "xl"],
+ table: { category: "Layout" },
+ },
+ leftSlot: { control: false },
+ centreSlot: { control: false },
+ rightSlot: { control: false },
+ children: { control: false },
},
-};
-export const Primary: Story = {
args: {
- color: "primary",
- leftSlot: Primary Bar,
- rightSlot: Actions,
+ surface: "surface",
+ variant: "base",
+ elevation: 0,
+ leftSlot: Bar,
},
};
-export const Secondary: Story = {
+export default meta;
+type Story = StoryObj;
+
+export const Default: Story = {
args: {
- color: "secondary",
- leftSlot: Secondary Bar,
+ leftSlot: Default (surface),
},
};
-export const Subtle: Story = {
- args: {
- color: "primary",
- variant: "subtle",
- leftSlot: Subtle Primary,
+export const VariantsOnSurface: Story = {
+ render: (_args) => (
+ <>
+ Base}
+ />
+ Container}
+ />
+ Solid}
+ />
+ >
+ ),
+ parameters: {
+ docs: {
+ description: {
+ story:
+ "Variants control emphasis on neutral surfaces. Base relies on elevation, container is subtle, and solid is strong.",
+ },
+ },
},
};
-export const WithTitle: Story = {
- args: {
- color: "primary",
- leftSlot: My App,
- rightSlot: Controls,
+export const ElevationScale: Story = {
+ render: (_args) => (
+ <>
+ Elevation 0}
+ />
+ Elevation 1}
+ />
+ Elevation 3}
+ />
+ Elevation 6}
+ />
+ Elevation 12}
+ />
+ Elevation 24}
+ />
+ >
+ ),
+ parameters: {
+ docs: {
+ description: {
+ story:
+ "Elevation controls hierarchy on neutral surfaces with base variant. Higher values appear more raised.",
+ },
+ },
},
};
-export const FullWidth: Story = {
- args: {
- containerWidth: false,
- leftSlot: Full width content,
- rightSlot: Right,
+export const PrimaryVsSurface: Story = {
+ render: (_args) => (
+ <>
+ Primary (action)}
+ />
+ Surface (layout)}
+ />
+ Surface Elevated}
+ />
+ >
+ ),
+ parameters: {
+ docs: {
+ description: {
+ story:
+ "Semantic surfaces (e.g. primary, secondary) express intent, while neutral surfaces define structure and hierarchy.",
+ },
+ },
},
};
-export const AllSlots: Story = {
- args: {
- leftSlot: Left,
- centreSlot: Centre,
- rightSlot: Right,
- },
+export const ActionVariants: Story = {
+ render: (_args) => (
+ <>
+ Primary Solid}
+ />
+ Primary Container}
+ />
+
+ Secondary Solid}
+ />
+ Secondary Container}
+ />
+ >
+ ),
parameters: {
docs: {
description: {
- story: "Three slots are available, left, centre and right.",
+ story: "Variants adjust emphasis.",
},
},
},
};
-export const WithChildren: Story = {
- args: {
- leftSlot: Left,
- children: Children,
- },
+export const BrandOptions: Story = {
+ render: (_args) => (
+ <>
+ Brand Solid}
+ />
+ Brand Container}
+ />
+ Brand Fixed}
+ />
+ Brand Fixed Dim}
+ />
+ >
+ ),
parameters: {
docs: {
description: {
story:
- 'Children appear on the left, proceeding anything added directly to the "leftSlot".',
+ "Brand surfaces are used for identity. brand-fixed and brand-fixedDim ignore variant prop and remain consistent across dark/light modes.",
},
},
},
};
-export const WithContent: Story = {
+export const AllSlots: Story = {
+ args: {
+ leftSlot: Left,
+ centreSlot: Centre,
+ rightSlot: Right,
+ },
+};
+
+export const WithChildren: Story = {
args: {
- leftSlot: Text content,
- centreSlot: ,
- rightSlot: ,
+ leftSlot: Left,
+ children: Children,
},
};
diff --git a/src/components/controls/Bar.tsx b/src/components/controls/Bar.tsx
index db615fcb..aa047b19 100644
--- a/src/components/controls/Bar.tsx
+++ b/src/components/controls/Bar.tsx
@@ -4,82 +4,135 @@ import {
BoxProps,
Breakpoint,
Container,
- LinkProps,
Stack,
styled,
} from "@mui/material";
import { Theme } from "@mui/material/styles";
-type IntentColour =
- | "primary"
- | "secondary"
- | "error"
- | "warning"
- | "info"
- | "success";
+type BarProps = BoxProps & {
+ containerWidth?: false | Breakpoint;
+ surface?:
+ | "primary"
+ | "secondary"
+ | "brand"
+ | "brand-fixed"
+ | "brand-fixedDim"
+ | "surface"
+ | "paper"
+ | "background";
-interface SlotProps extends BoxProps, React.PropsWithChildren {
- className: string;
-}
+ variant?: "solid" | "container" | "base";
+ elevation?: number;
+};
-const Slot = ({ className, children }: SlotProps) => (
+type BarSlotsProps = BarProps & {
+ centreSlot?: React.ReactNode;
+ rightSlot?: React.ReactNode;
+ leftSlot?: React.ReactNode;
+};
+
+const Slot = ({
+ className,
+ children,
+}: {
+ className: string;
+ children?: React.ReactNode;
+}) => (
{children}
);
-interface BarProps extends BoxProps {
- containerWidth?: false | Breakpoint;
- color?: IntentColour;
- variant?: "default" | "subtle";
-}
-
-interface BarSlotsProps extends BarProps {
- centreSlot?: React.ReactElement;
- rightSlot?: React.ReactElement;
- leftSlot?: React.ReactElement;
-}
-
-const BoxStyled = styled(Box)(({
- theme,
- color,
- variant = "default",
-}: {
- theme: Theme;
- color?: IntentColour;
- variant?: "default" | "subtle";
-}) => {
- let styles;
-
- if (!color) {
- styles = {
- backgroundColor:
- variant === "subtle"
- ? (theme.palette.surface?.subtle ?? theme.palette.background.paper)
- : theme.palette.background.paper,
+const resolveBarSurface = (
+ theme: Theme,
+ surface: string,
+ variant: "solid" | "container" | "base",
+ elevation: number,
+) => {
+ const baseBg =
+ elevation > 0
+ ? theme.palette.surface.elevated(elevation)
+ : theme.palette.background.paper;
+
+ const semantic = ["primary", "secondary", "brand"] as const;
+
+ if (semantic.includes(surface as "primary" | "secondary" | "brand")) {
+ const p = (
+ surface === "brand"
+ ? theme.palette.brand
+ : theme.palette[surface as "primary" | "secondary"]
+ )!;
+
+ if (variant === "solid") {
+ return { backgroundColor: p.solid, color: p.onSolid };
+ }
+
+ if (variant === "container") {
+ return { backgroundColor: p.container, color: p.onContainer };
+ }
+
+ return { backgroundColor: baseBg, color: theme.palette.text.primary };
+ }
+
+ if (surface === "brand-fixed" || surface === "brand-fixedDim") {
+ const p = theme.palette.brand!;
+ return {
+ backgroundColor: surface === "brand-fixed" ? p.fixed : p.fixedDim,
+ color: p.onFixed,
+ };
+ }
+
+ if (surface === "background") {
+ return {
+ backgroundColor: theme.palette.background.default,
+ color: theme.palette.text.primary,
+ };
+ }
+
+ if (surface === "surface" || surface === "paper") {
+ if (variant === "container") {
+ return {
+ backgroundColor: theme.palette.surface.subtle,
+ color: theme.palette.text.primary,
+ };
+ }
+
+ if (variant === "solid") {
+ return {
+ backgroundColor: theme.palette.surface.strong,
+ color: theme.palette.text.primary,
+ };
+ }
+
+ return {
+ backgroundColor: baseBg,
color: theme.palette.text.primary,
};
- } else {
- const p = theme.palette[color];
-
- styles =
- variant === "subtle"
- ? {
- backgroundColor: p.container,
- color: p.onContainer,
- }
- : {
- backgroundColor: p.solid,
- color: p.onSolid,
- };
}
+ return {
+ backgroundColor: baseBg,
+ color: theme.palette.text.primary,
+ };
+};
+
+const BoxStyled = styled(Box)(({ theme, ...ownerState }) => {
+ const { surface = "surface", variant = "base", elevation = 0 } = ownerState;
+
+ const { backgroundColor, color } = resolveBarSurface(
+ theme,
+ surface,
+ variant,
+ elevation,
+ );
+
return {
width: "100%",
minHeight: 50,
display: "flex",
alignItems: "center",
- ...styles,
+ backgroundColor,
+ color,
};
});
diff --git a/src/components/navigation/Breadcrumbs.stories.tsx b/src/components/navigation/Breadcrumbs.stories.tsx
index 32181179..992c9e77 100644
--- a/src/components/navigation/Breadcrumbs.stories.tsx
+++ b/src/components/navigation/Breadcrumbs.stories.tsx
@@ -74,3 +74,22 @@ export const NoLinkComponentWithCustomPath: Story = {
],
},
};
+
+export const BreadcrumbsVariants: Story = {
+ render: (_args) => (
+ <>
+
+
+
+
+ >
+ ),
+ parameters: {
+ docs: {
+ description: {
+ story:
+ "Breadcrumbs are subtle by default but can adapt to different surfaces when needed.",
+ },
+ },
+ },
+};
diff --git a/src/components/navigation/Breadcrumbs.tsx b/src/components/navigation/Breadcrumbs.tsx
index 9e1b078a..3c454daf 100644
--- a/src/components/navigation/Breadcrumbs.tsx
+++ b/src/components/navigation/Breadcrumbs.tsx
@@ -11,11 +11,11 @@ import { CustomLink } from "types/links";
import { Bar, BarProps } from "../controls/Bar";
-interface BreadcrumbsProps extends BarProps {
+type BreadcrumbsProps = BarProps & {
path: string | string[] | CustomLink[];
linkComponent?: React.ElementType;
muiBreadcrumbsProps?: MuiBreadcrumbsProps;
-}
+};
/**
* Create CrumbData from crumb parts with links
@@ -53,6 +53,9 @@ export function getCrumbs(
}
const Breadcrumbs = ({
+ surface = "surface",
+ variant = "container",
+ elevation,
path,
linkComponent,
muiBreadcrumbsProps,
@@ -61,17 +64,22 @@ const Breadcrumbs = ({
const crumbs: CustomLink[] = getCrumbs(path);
return (
-
+
}
+ sx={{
+ color: "inherit",
+ "&, & *": {
+ color: "inherit !important", // required to use Bar colour for adequate text contrast
+ },
+ }}
{...muiBreadcrumbsProps}
>
= {
title: "Components/Navigation/Footer",
@@ -56,6 +57,17 @@ const staticFooterLinks = (
);
+const rightSlotLinks = (
+
+
+ The Moon
+
+
+ Phobos
+
+
+);
+
export const All: Story = {
args: {
logo: "theme",
@@ -103,16 +115,58 @@ export const RightSlot: Story = {
args: {
logo: "theme",
copyright: "Company",
- rightSlot: (
-
-
- The Moon
-
-
- Phobos
-
-
- ),
+ rightSlot: rightSlotLinks,
+ },
+};
+
+export const FooterVariants: Story = {
+ render: (_args) => (
+ <>
+