From ce74927305e6fb10cbc29569df55f1e1434acc78 Mon Sep 17 00:00:00 2001
From: m7kvqbe1 <48086589+m7kvqbe1@users.noreply.github.com>
Date: Wed, 24 Jun 2026 20:43:27 +0100
Subject: [PATCH 01/11] feat(DesignTokens): Add dark colour set and CSS
variable engine
---
.../src/__tests__/selectors.test.tsx | 21 ++-
packages/design-tokens/src/getters.ts | 56 ++++++-
packages/design-tokens/src/index.ts | 4 +-
packages/design-tokens/src/selectors.ts | 2 +
.../design-tokens/src/tokens/dark/colors.json | 145 +++++++++++-------
5 files changed, 165 insertions(+), 63 deletions(-)
diff --git a/packages/design-tokens/src/__tests__/selectors.test.tsx b/packages/design-tokens/src/__tests__/selectors.test.tsx
index a065ec5476..473910e07c 100644
--- a/packages/design-tokens/src/__tests__/selectors.test.tsx
+++ b/packages/design-tokens/src/__tests__/selectors.test.tsx
@@ -25,6 +25,7 @@ const {
fontSize,
shadow,
zIndex,
+ colorValue,
} = selectors
let wrapper: RenderResult
@@ -211,15 +212,31 @@ describe('color', () => {
color: ${color('neutral', '100')};
`
- it('should set styles correctly', () => {
+ it('resolves to a themeable CSS variable with the light value as fallback', () => {
wrapper = render()
expect(wrapper.getByTestId('test-element')).toHaveStyleRule(
'color',
- '#e2e9ee'
+ 'var(--color-neutral-100, #e2e9ee)'
+ )
+ })
+ })
+})
+
+describe('colorValue', () => {
+ describe('when the group or shade is invalid', () => {
+ it('throws a friendly error', () => {
+ expect(() => colorValue('invalid' as ColorGroup, '400')).toThrow(
+ 'Invalid color token'
)
})
})
+
+ describe('when the group and shade are valid', () => {
+ it('returns the concrete light value with no theme', () => {
+ expect(colorValue('neutral', '100')).toBe('#e2e9ee')
+ })
+ })
})
describe('fontSize', () => {
diff --git a/packages/design-tokens/src/getters.ts b/packages/design-tokens/src/getters.ts
index 2765f37e13..91ed11b655 100644
--- a/packages/design-tokens/src/getters.ts
+++ b/packages/design-tokens/src/getters.ts
@@ -102,7 +102,19 @@ export function getAnimation(index: AnimationTiming, theme?: Theme): string {
throw new Error(`Invalid animation token for index: '${index}'`)
}
-export function getColor(
+export function colorVariableName(
+ group: ColorGroup,
+ weight: ColorShade
+): string {
+ return `--color-${group}-${weight}`
+}
+
+/**
+ * Resolve a colour token to its concrete hex value for the given theme
+ * (falling back to the default light theme). Use this where the value
+ * must be a parseable colour, e.g. when passed to `polished` helpers.
+ */
+export function getColorValue(
group: ColorGroup,
weight: ColorShade,
theme?: Theme
@@ -119,6 +131,48 @@ export function getColor(
)
}
+/**
+ * Resolve a colour token. When an explicit theme is provided the concrete
+ * hex value is returned (preserving threaded-theme behaviour). Otherwise a
+ * CSS custom property reference is returned so the colour reacts to the
+ * theme injected by `GlobalStyleProvider`, with the light value as a static
+ * fallback for unprovided/SSR contexts.
+ */
+export function getColor(
+ group: ColorGroup,
+ weight: ColorShade,
+ theme?: Theme
+): string {
+ if (theme?.colorsTokens) {
+ return getColorValue(group, weight, theme)
+ }
+
+ const fallback = getColorValue(group, weight)
+
+ return `var(${colorVariableName(group, weight)}, ${fallback})`
+}
+
+/**
+ * Build the `--color-*` custom property declarations for a theme, for
+ * injection by `GlobalStyleProvider`.
+ */
+export function getColorVariables(theme?: Theme): Record {
+ const { color } = getTheme(theme).colorsTokens as unknown as {
+ color: Record>
+ }
+
+ const entries = Object.entries(color).flatMap(([group, shades]) =>
+ Object.entries(shades)
+ .filter(([, token]) => isTokenValid(token?.value))
+ .map(
+ ([weight, token]) =>
+ [`--color-${group}-${weight}`, token.value as string] as const
+ )
+ )
+
+ return Object.fromEntries(entries)
+}
+
export function getTypography(size: TypographySize, theme?: Theme): string {
const value = get(
getTheme(theme).typographyTokens,
diff --git a/packages/design-tokens/src/index.ts b/packages/design-tokens/src/index.ts
index 97aaa5cf3e..22ba872e62 100644
--- a/packages/design-tokens/src/index.ts
+++ b/packages/design-tokens/src/index.ts
@@ -3,7 +3,7 @@ import selectors from './selectors'
export * from './types'
export * from './flat'
-export { getTheme } from './getters'
+export { getTheme, getColorVariables, colorVariableName } from './getters'
export { selectors }
@@ -14,6 +14,7 @@ const {
animation,
breakpoint,
color,
+ colorValue,
fontSize,
mediaQuery,
mq,
@@ -26,6 +27,7 @@ export {
animation,
breakpoint,
color,
+ colorValue,
fontSize,
mediaQuery,
mq,
diff --git a/packages/design-tokens/src/selectors.ts b/packages/design-tokens/src/selectors.ts
index 9ce602a37b..9852758dfd 100644
--- a/packages/design-tokens/src/selectors.ts
+++ b/packages/design-tokens/src/selectors.ts
@@ -3,6 +3,7 @@ import {
getBreakpoint,
getMediaQuery,
getColor,
+ getColorValue,
getShadow,
getSpacing,
getTypography,
@@ -15,6 +16,7 @@ export default {
breakpoint: getBreakpoint,
animation: getAnimation,
color: getColor,
+ colorValue: getColorValue,
fontSize: getTypography,
shadow: getShadow,
spacing: getSpacing,
diff --git a/packages/design-tokens/src/tokens/dark/colors.json b/packages/design-tokens/src/tokens/dark/colors.json
index 0bacd800a9..cd884fd879 100644
--- a/packages/design-tokens/src/tokens/dark/colors.json
+++ b/packages/design-tokens/src/tokens/dark/colors.json
@@ -2,51 +2,64 @@
"color": {
"neutral": {
"black": {
- "value": "#0a141b"
+ "value": "#f8fafc",
+ "description": "Foreground (inverted in dark mode)"
},
"white": {
- "value": "#ffffff"
+ "value": "#1c2d39",
+ "description": "Raised surfaces, cards, panels (inverted in dark mode)"
},
"000": {
- "value": "#f8fafc"
+ "value": "#0a141b",
+ "description": "Page background, Disabled button"
},
"100": {
- "value": "#000000"
+ "value": "#16242f",
+ "description": "Borders and dividers, Inactive tab"
},
"200": {
- "value": "#b8c7d2"
+ "value": "#2a3f4d",
+ "description": "Input borders, Disabled button border, Toggle off disabled"
},
"300": {
- "value": "#748999"
+ "value": "#3e5667",
+ "description": "Breadcrumb divider, Toggle off"
},
"400": {
- "value": "#3e5667"
+ "value": "#8da0ad",
+ "description": "Body text, Input labels, Disabled button label"
},
"500": {
- "value": "#233745"
+ "value": "#a7b6c1",
+ "description": "Sidebar user menu background"
},
"600": {
- "value": "#1c2d39"
+ "value": "#cdd8e0",
+ "description": "Headings, Input text"
},
"700": {
- "value": "#12202b"
+ "value": "#dde5ea",
+ "description": "Sidebar"
},
"800": {
- "value": "#0c1720"
+ "value": "#eef2f5"
},
"900": {
- "value": "#0a141b"
+ "value": "#f8fafc"
}
},
"action": {
"000": {
- "value": "#ecf8ff"
+ "value": "#102536",
+ "description": "Checked/selected radio box background"
},
"100": {
- "value": "#ddf4ff"
+ "value": "#133048",
+ "description": "Secondary button, Hovered primary button border, Phase banner"
},
"200": {
- "value": "#b7dff7"
+ "value": "#b7dff7",
+ "description": "Hovered secondary and tertiary button, Toggle on disabled"
},
"300": {
"value": "#85c6f2"
@@ -55,27 +68,32 @@
"value": "#58aae9"
},
"500": {
- "value": "#3a8fdd"
+ "value": "#3a8fdd",
+ "description": "Info alert elements, Focus state, Checked/selected radio, Toggle on"
},
"600": {
- "value": "#2a77c7"
+ "value": "#2a77c7",
+ "description": "Primary button, Secondary and tertiary button border"
},
"700": {
- "value": "#2661a7"
+ "value": "#2661a7",
+ "description": "Input interactions"
},
"800": {
- "value": "#274776"
+ "value": "#274776",
+ "description": "Primary button border, Hovered primary button"
},
"900": {
- "value": "#253b5b"
+ "value": "#b7dff7",
+ "description": "Secondary and tertiary button label"
}
},
"success": {
"000": {
- "value": "#f4ffef"
+ "value": "#102516"
},
"100": {
- "value": "#e5ffd9"
+ "value": "#15331f"
},
"200": {
"value": "#c6f3b5"
@@ -87,7 +105,8 @@
"value": "#8fd57f"
},
"500": {
- "value": "#76c767"
+ "value": "#76c767",
+ "description": "Success icon"
},
"600": {
"value": "#60b255"
@@ -99,15 +118,16 @@
"value": "#3b6f33"
},
"900": {
- "value": "#3b612c"
+ "value": "#abe39b",
+ "description": "Success text"
}
},
"warning": {
"000": {
- "value": "#ffffee"
+ "value": "#2a2208"
},
"100": {
- "value": "#fffddc"
+ "value": "#33290a"
},
"200": {
"value": "#fefbb8"
@@ -119,7 +139,8 @@
"value": "#f5db54"
},
"500": {
- "value": "#e8c242"
+ "value": "#e8c242",
+ "description": "Warning icon"
},
"600": {
"value": "#cf9328"
@@ -131,15 +152,17 @@
"value": "#8c4f17"
},
"900": {
- "value": "#693a12"
+ "value": "#faed7e",
+ "description": "Warning text"
}
},
"danger": {
"000": {
- "value": "#fff3f4"
+ "value": "#2a1011"
},
"100": {
- "value": "#feeaec"
+ "value": "#331417",
+ "description": "Hovered danger button border"
},
"200": {
"value": "#fed1d1"
@@ -151,27 +174,31 @@
"value": "#fc7c75"
},
"500": {
- "value": "#f45249"
+ "value": "#f45249",
+ "description": "Danger icon"
},
"600": {
"value": "#ec4138"
},
"700": {
- "value": "#d53229"
+ "value": "#d53229",
+ "description": "Danger button"
},
"800": {
- "value": "#B22820"
+ "value": "#B22820",
+ "description": "Hovered danger button, Input error border"
},
"900": {
- "value": "#841c1b"
+ "value": "#fea9a9",
+ "description": "Danger text"
}
},
"supa": {
"000": {
- "value": "#e8f2ff"
+ "value": "#14182e"
},
"100": {
- "value": "#deebff"
+ "value": "#181d3a"
},
"200": {
"value": "#bbd5fe"
@@ -195,15 +222,15 @@
"value": "#3b3985"
},
"900": {
- "value": "#343160"
+ "value": "#99b7f9"
}
},
"supb": {
"000": {
- "value": "#f9f3ff"
+ "value": "#1c142e"
},
"100": {
- "value": "#f2e9ff"
+ "value": "#22193a"
},
"200": {
"value": "#e5d3fd"
@@ -227,15 +254,15 @@
"value": "#4b358f"
},
"900": {
- "value": "#3b2d6e"
+ "value": "#d0b5f9"
}
},
"supc": {
"000": {
- "value": "#fff3ff"
+ "value": "#2a142b"
},
"100": {
- "value": "#fee9ff"
+ "value": "#331937"
},
"200": {
"value": "#fcd3fd"
@@ -259,47 +286,47 @@
"value": "#8c358f"
},
"900": {
- "value": "#6c2d6e"
+ "value": "#f7b5f9"
}
},
"supd": {
"000": {
- "value": "#fff3ff"
+ "value": "#2e141d"
},
"100": {
- "value": "#fee9ff"
+ "value": "#3a1926"
},
"200": {
- "value": "#fcd3fd"
+ "value": "#ffcce5"
},
"300": {
- "value": "#f7b5f9"
+ "value": "#ffa2b5"
},
"400": {
- "value": "#ee89f1"
+ "value": "#f77f97"
},
"500": {
- "value": "#e46fe8"
+ "value": "#f35d7b"
},
"600": {
- "value": "#cc4fd0"
+ "value": "#d84b67"
},
"700": {
- "value": "#b43fb8"
+ "value": "#c43854"
},
"800": {
- "value": "#8c358f"
+ "value": "#972b41"
},
"900": {
- "value": "#6c2d6e"
+ "value": "#ffa2b5"
}
},
"supe": {
"000": {
- "value": "#fff8f3"
+ "value": "#2e1c10"
},
"100": {
- "value": "#fef2ea"
+ "value": "#3a2415"
},
"200": {
"value": "#fee2d1"
@@ -323,15 +350,15 @@
"value": "#9d4712"
},
"900": {
- "value": "#853a0c"
+ "value": "#fecaa9"
}
},
"supf": {
"000": {
- "value": "#eefff2"
+ "value": "#0e2016"
},
"100": {
- "value": "#dfffe9"
+ "value": "#13301f"
},
"200": {
"value": "#bff4cf"
@@ -355,7 +382,7 @@
"value": "#245c40"
},
"900": {
- "value": "#1f4a35"
+ "value": "#8fe2ab"
}
}
}
From 80723b9895afa1d6192af5719be5ef5c3eb7ee6f Mon Sep 17 00:00:00 2001
From: m7kvqbe1 <48086589+m7kvqbe1@users.noreply.github.com>
Date: Wed, 24 Jun 2026 20:43:38 +0100
Subject: [PATCH 02/11] feat(GlobalStyle): Inject theme CSS variables and add
Storybook toggle
---
.../.storybook/preview.js | 46 ++++++++++++++++++-
.../src/styled-components/GlobalStyle.tsx | 32 ++++++++++++-
2 files changed, 75 insertions(+), 3 deletions(-)
diff --git a/packages/react-component-library/.storybook/preview.js b/packages/react-component-library/.storybook/preview.js
index a2ab7c2d86..a2655515d6 100644
--- a/packages/react-component-library/.storybook/preview.js
+++ b/packages/react-component-library/.storybook/preview.js
@@ -1,12 +1,53 @@
import { DocsPage } from '@storybook/addon-docs/blocks'
import isChromatic from 'chromatic'
import React from 'react'
+import { lightTheme, darkTheme, color } from '@royalnavy/design-tokens'
import '@royalnavy/fonts'
import 'iframe-resizer/js/iframeResizer.contentWindow'
import 'url-search-params-polyfill'
import { GlobalStyleProvider } from '../src/styled-components/GlobalStyle'
+const THEMES = {
+ light: lightTheme,
+ dark: darkTheme,
+}
+
+function getThemeFromContext(context) {
+ return THEMES[context.globals.theme] ?? lightTheme
+}
+
+const ThemedCanvas = ({ theme, children }) => (
+
+
+ {children}
+
+
+)
+
+export const globalTypes = {
+ theme: {
+ name: 'Theme',
+ description: 'Design System token set',
+ defaultValue: 'light',
+ toolbar: {
+ icon: 'paintbrush',
+ items: [
+ { value: 'light', title: 'Light' },
+ { value: 'dark', title: 'Dark' },
+ ],
+ dynamicTitle: true,
+ },
+ },
+}
+
export const parameters = {
jsx: {
// Filter out callback props injected by the Actions addon
@@ -54,14 +95,15 @@ export const parameters = {
export const decorators = [
// https://github.com/storybookjs/storybook/issues/15223#issuecomment-1092837912
- (Story) => {
+ (Story, context) => {
+ const theme = getThemeFromContext(context)
const queryParams = new URLSearchParams(window.location.search)
if (queryParams.get('viewMode') === 'docs') {
return Story()
}
- return {Story()}
+ return {Story()}
},
]
diff --git a/packages/react-component-library/src/styled-components/GlobalStyle.tsx b/packages/react-component-library/src/styled-components/GlobalStyle.tsx
index 53cfe14133..ce840ee70c 100644
--- a/packages/react-component-library/src/styled-components/GlobalStyle.tsx
+++ b/packages/react-component-library/src/styled-components/GlobalStyle.tsx
@@ -1,7 +1,13 @@
import React, { createContext, useMemo } from 'react'
import { createGlobalStyle, ThemeProvider } from 'styled-components'
import { Normalize } from 'styled-normalize'
-import { lightTheme, color, fontSize } from '@royalnavy/design-tokens'
+import {
+ lightTheme,
+ color,
+ fontSize,
+ getColorVariables,
+ Theme,
+} from '@royalnavy/design-tokens'
export interface GlobalStyleContextDefaults {
theme?: Record
@@ -12,6 +18,23 @@ export interface GlobalStyleProviderProps {
theme?: Record
}
+/**
+ * Expose the active theme's colour tokens as CSS custom properties on the
+ * root, so every `color(...)` reference (which resolves to a `var(--color-*)`)
+ * reacts to the theme — including content rendered through portals that sits
+ * outside the provider's DOM subtree.
+ */
+const ThemeVariables = createGlobalStyle<{
+ $variables: Record
+}>`
+ :root {
+ ${({ $variables }) =>
+ Object.entries($variables)
+ .map(([name, value]) => `${name}: ${value};`)
+ .join('\n')}
+ }
+`
+
/**
* Globally setting `border-box`
*/
@@ -50,6 +73,7 @@ const Fonts = createGlobalStyle`
html {
font-family: "lato", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
font-size: ${fontSize('m')};
+ color: ${color('neutral', '600')};
}
h1,
@@ -113,10 +137,16 @@ export const GlobalStyleProvider = ({
[theme]
)
+ const colorVariables = useMemo(
+ () => getColorVariables(theme as Theme),
+ [theme]
+ )
+
return (
+
{children}
From 7ff763cf0e9bd29edd5e06ee02f670ec1fc8249d Mon Sep 17 00:00:00 2001
From: m7kvqbe1 <48086589+m7kvqbe1@users.noreply.github.com>
Date: Wed, 24 Jun 2026 20:43:58 +0100
Subject: [PATCH 03/11] fix(Components): Theme dark-mode stragglers and adapt
colour tests
---
.../Autocomplete/Autocomplete.test.tsx | 9 +++---
.../src/components/Checkbox/Checkbox.test.tsx | 6 ++--
.../partials/StyledLoadingOverlay.tsx | 13 +++++++-
.../DatePicker/__tests__/DatePicker.test.tsx | 17 +++++-----
.../DatePickerWhenTodayPropIsSet.test.tsx | 17 +++++-----
.../DatePicker/partials/StyledDayPicker.tsx | 31 ++++++++++++++++---
.../components/Modal/partials/StyledModal.tsx | 5 ++-
.../src/components/Radio/Radio.test.tsx | 14 +++------
.../src/components/RangeSlider/constants.ts | 16 +++++++---
.../RangeSlider/partials/StyledHandle.tsx | 4 +--
.../src/components/Select/Select.test.tsx | 6 ++--
.../Masthead/partials/StyledBanner.tsx | 4 +--
.../Masthead/partials/StyledOption.tsx | 5 +--
.../SearchBar/partials/StyledButton.tsx | 4 +--
14 files changed, 95 insertions(+), 56 deletions(-)
diff --git a/packages/react-component-library/src/components/Autocomplete/Autocomplete.test.tsx b/packages/react-component-library/src/components/Autocomplete/Autocomplete.test.tsx
index 213def3acf..a18ba03656 100644
--- a/packages/react-component-library/src/components/Autocomplete/Autocomplete.test.tsx
+++ b/packages/react-component-library/src/components/Autocomplete/Autocomplete.test.tsx
@@ -1,4 +1,4 @@
-import { ColorDanger800, TypographyS } from '@royalnavy/design-tokens'
+import { color, TypographyS } from '@royalnavy/design-tokens'
import React from 'react'
import { render, RenderResult, waitFor } from '@testing-library/react'
@@ -8,9 +8,10 @@ import { COMPONENT_SIZE } from '../Forms'
import { BORDER_WIDTH } from '../../styled-components'
import { Autocomplete, AutocompleteOption } from '.'
-const ERROR_BOX_SHADOW = `0 0 0 ${
- BORDER_WIDTH[COMPONENT_SIZE.FORMS]
-} ${ColorDanger800.toUpperCase()}`
+const ERROR_BOX_SHADOW = `0 0 0 ${BORDER_WIDTH[COMPONENT_SIZE.FORMS]} ${color(
+ 'danger',
+ '800'
+)}`
describe('Autocomplete', () => {
let onBlurSpy: jest.Mock
diff --git a/packages/react-component-library/src/components/Checkbox/Checkbox.test.tsx b/packages/react-component-library/src/components/Checkbox/Checkbox.test.tsx
index c7d328f00a..35050fd1ec 100644
--- a/packages/react-component-library/src/components/Checkbox/Checkbox.test.tsx
+++ b/packages/react-component-library/src/components/Checkbox/Checkbox.test.tsx
@@ -1,7 +1,7 @@
import React, { useState } from 'react'
import 'jest-styled-components'
-import { ColorNeutral200 } from '@royalnavy/design-tokens'
+import { color } from '@royalnavy/design-tokens'
import { render, RenderResult } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
@@ -106,7 +106,7 @@ describe('Checkbox', () => {
it('should render a container', () => {
expect(checkbox.getByTestId('checkbox')).toHaveStyleRule(
'border',
- `1px solid ${ColorNeutral200}`
+ `1px solid ${color('neutral', '200')}`
)
expect(checkbox.getByTestId('checkbox')).toHaveStyleRule(
'border-radius',
@@ -292,7 +292,7 @@ describe('Checkbox', () => {
it('should not render a container', () => {
expect(checkbox.getByTestId('checkbox')).not.toHaveStyleRule(
'border',
- `1px solid ${ColorNeutral200}`
+ `1px solid ${color('neutral', '200')}`
)
expect(checkbox.getByTestId('checkbox')).not.toHaveStyleRule(
'border-radius',
diff --git a/packages/react-component-library/src/components/DataGrid/partials/StyledLoadingOverlay.tsx b/packages/react-component-library/src/components/DataGrid/partials/StyledLoadingOverlay.tsx
index cf9e560f7c..5cb132d7dd 100644
--- a/packages/react-component-library/src/components/DataGrid/partials/StyledLoadingOverlay.tsx
+++ b/packages/react-component-library/src/components/DataGrid/partials/StyledLoadingOverlay.tsx
@@ -1,4 +1,5 @@
import styled from 'styled-components'
+import { color } from '@royalnavy/design-tokens'
export const StyledLoadingOverlay = styled.div`
position: absolute;
@@ -6,7 +7,6 @@ export const StyledLoadingOverlay = styled.div`
left: 0;
right: 0;
bottom: 0;
- background-color: rgba(255, 255, 255, 0.5);
display: flex;
justify-content: center;
align-items: center;
@@ -14,6 +14,17 @@ export const StyledLoadingOverlay = styled.div`
border-radius: 4px;
overflow: hidden;
+ &::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: ${color('neutral', 'white')};
+ opacity: 0.5;
+ }
+
> div {
position: absolute;
top: 50%;
diff --git a/packages/react-component-library/src/components/DatePicker/__tests__/DatePicker.test.tsx b/packages/react-component-library/src/components/DatePicker/__tests__/DatePicker.test.tsx
index bda8000930..e7520ebde1 100644
--- a/packages/react-component-library/src/components/DatePicker/__tests__/DatePicker.test.tsx
+++ b/packages/react-component-library/src/components/DatePicker/__tests__/DatePicker.test.tsx
@@ -1,6 +1,6 @@
import { format, isValid } from 'date-fns'
import React, { useState } from 'react'
-import { ColorDanger800, ColorWarning800 } from '@royalnavy/design-tokens'
+import { color } from '@royalnavy/design-tokens'
import {
act,
fireEvent,
@@ -17,9 +17,10 @@ import { DATE_VALIDITY } from '../constants'
const NOW = '2019-12-05T11:00:00.000Z'
-const ERROR_BOX_SHADOW = `0 0 0 ${
- BORDER_WIDTH[COMPONENT_SIZE.FORMS]
-} ${ColorDanger800.toUpperCase()}`
+const ERROR_BOX_SHADOW = `0 0 0 ${BORDER_WIDTH[COMPONENT_SIZE.FORMS]} ${color(
+ 'danger',
+ '800'
+)}`
const PREVIOUS_MONTH_BUTTON_LABEL = 'Go to previous month'
@@ -162,11 +163,11 @@ describe('DatePicker', () => {
})
})
- it('colours the current date', () => {
+ it('marks the current date as today', () => {
return waitFor(() => {
- expect(wrapper.getByRole('gridcell', { name: '5' })).toHaveStyle({
- color: ColorWarning800,
- })
+ expect(wrapper.getByRole('gridcell', { name: '5' })).toHaveClass(
+ 'rdp-day_today'
+ )
})
})
diff --git a/packages/react-component-library/src/components/DatePicker/__tests__/DatePickerWhenTodayPropIsSet.test.tsx b/packages/react-component-library/src/components/DatePicker/__tests__/DatePickerWhenTodayPropIsSet.test.tsx
index 6af7a2c303..3772b71ad7 100644
--- a/packages/react-component-library/src/components/DatePicker/__tests__/DatePickerWhenTodayPropIsSet.test.tsx
+++ b/packages/react-component-library/src/components/DatePicker/__tests__/DatePickerWhenTodayPropIsSet.test.tsx
@@ -1,5 +1,4 @@
import React from 'react'
-import { ColorWarning800 } from '@royalnavy/design-tokens'
import { act, render, RenderResult } from '@testing-library/react'
import { DatePicker, DatePickerOnChangeData } from '../index'
@@ -26,15 +25,15 @@ describe('when today prop is set', () => {
onChange.mockReset()
})
- it('colours the override date', () => {
- expect(wrapper.getByRole('gridcell', { name: '15' })).toHaveStyle({
- color: ColorWarning800,
- })
+ it('marks the override date as today', () => {
+ expect(wrapper.getByRole('gridcell', { name: '15' })).toHaveClass(
+ 'rdp-day_today'
+ )
})
- it('does not colour the actual current date', () => {
- expect(wrapper.getByRole('gridcell', { name: '5' })).not.toHaveStyle({
- color: ColorWarning800,
- })
+ it('does not mark the actual current date as today', () => {
+ expect(wrapper.getByRole('gridcell', { name: '5' })).not.toHaveClass(
+ 'rdp-day_today'
+ )
})
})
diff --git a/packages/react-component-library/src/components/DatePicker/partials/StyledDayPicker.tsx b/packages/react-component-library/src/components/DatePicker/partials/StyledDayPicker.tsx
index e3970c4178..3342edd9f5 100644
--- a/packages/react-component-library/src/components/DatePicker/partials/StyledDayPicker.tsx
+++ b/packages/react-component-library/src/components/DatePicker/partials/StyledDayPicker.tsx
@@ -48,23 +48,44 @@ export const StyledDayPicker = styled(DayPicker)`
position: absolute;
top: ${spacing('6')};
width: 38px;
- background-position: center;
- background-size: 18px;
- background-repeat: no-repeat;
svg {
display: none;
}
+
+ &::after {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: ${color('neutral', '500')};
+ -webkit-mask-position: center;
+ mask-position: center;
+ -webkit-mask-size: 18px;
+ mask-size: 18px;
+ -webkit-mask-repeat: no-repeat;
+ mask-repeat: no-repeat;
+ }
}
.rdp-nav_button_previous {
left: ${spacing('6')};
- background-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg width='16px' height='16px' viewBox='0 0 16 16' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3Ctitle%3EIcons/Navigation/chevron-left%3C/title%3E%3Cdefs%3E%3Cpolygon id='path-1' points='10.2755556 4.9422222 9.33333334 3.99999998 5.33333332 8 9.33333334 12 10.2755556 11.0577778 7.21777777 8'%3E%3C/polygon%3E%3C/defs%3E%3Cg id='Icons/Navigation/chevron-left' stroke='none' stroke-width='1' fill='none' fill-rule='evenodd'%3E%3Cmask id='mask-2' fill='white'%3E%3Cuse xlink:href='%23path-1'%3E%3C/use%3E%3C/mask%3E%3Cuse id='Icons/Navigation/ic_chevron_left_18px' fill='%23233745' fill-rule='nonzero' xlink:href='%23path-1'%3E%3C/use%3E%3C/g%3E%3C/svg%3E");
+
+ &::after {
+ -webkit-mask-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg width='16px' height='16px' viewBox='0 0 16 16' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3Ctitle%3EIcons/Navigation/chevron-left%3C/title%3E%3Cdefs%3E%3Cpolygon id='path-1' points='10.2755556 4.9422222 9.33333334 3.99999998 5.33333332 8 9.33333334 12 10.2755556 11.0577778 7.21777777 8'%3E%3C/polygon%3E%3C/defs%3E%3Cg id='Icons/Navigation/chevron-left' stroke='none' stroke-width='1' fill='none' fill-rule='evenodd'%3E%3Cmask id='mask-2' fill='white'%3E%3Cuse xlink:href='%23path-1'%3E%3C/use%3E%3C/mask%3E%3Cuse id='Icons/Navigation/ic_chevron_left_18px' fill='%23233745' fill-rule='nonzero' xlink:href='%23path-1'%3E%3C/use%3E%3C/g%3E%3C/svg%3E");
+ mask-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg width='16px' height='16px' viewBox='0 0 16 16' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3Ctitle%3EIcons/Navigation/chevron-left%3C/title%3E%3Cdefs%3E%3Cpolygon id='path-1' points='10.2755556 4.9422222 9.33333334 3.99999998 5.33333332 8 9.33333334 12 10.2755556 11.0577778 7.21777777 8'%3E%3C/polygon%3E%3C/defs%3E%3Cg id='Icons/Navigation/chevron-left' stroke='none' stroke-width='1' fill='none' fill-rule='evenodd'%3E%3Cmask id='mask-2' fill='white'%3E%3Cuse xlink:href='%23path-1'%3E%3C/use%3E%3C/mask%3E%3Cuse id='Icons/Navigation/ic_chevron_left_18px' fill='%23233745' fill-rule='nonzero' xlink:href='%23path-1'%3E%3C/use%3E%3C/g%3E%3C/svg%3E");
+ }
}
.rdp-nav_button_next {
right: ${spacing('6')};
- background-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg width='16px' height='16px' viewBox='0 0 16 16' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3Ctitle%3EIcons/Navigation/chevron-right%3C/title%3E%3Cdefs%3E%3Cpolygon id='path-1' points='6.66666666 3.99999998 5.72444443 4.9422222 8.78222223 8 5.72444443 11.0577778 6.66666666 12 10.6666667 8'%3E%3C/polygon%3E%3C/defs%3E%3Cg id='Icons/Navigation/chevron-right' stroke='none' stroke-width='1' fill='none' fill-rule='evenodd'%3E%3Cmask id='mask-2' fill='white'%3E%3Cuse xlink:href='%23path-1'%3E%3C/use%3E%3C/mask%3E%3Cuse id='Icons/Navigation/ic_chevron_right_18px' fill='%23233745' fill-rule='nonzero' xlink:href='%23path-1'%3E%3C/use%3E%3C/g%3E%3C/svg%3E");
+
+ &::after {
+ -webkit-mask-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg width='16px' height='16px' viewBox='0 0 16 16' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3Ctitle%3EIcons/Navigation/chevron-right%3C/title%3E%3Cdefs%3E%3Cpolygon id='path-1' points='6.66666666 3.99999998 5.72444443 4.9422222 8.78222223 8 5.72444443 11.0577778 6.66666666 12 10.6666667 8'%3E%3C/polygon%3E%3C/defs%3E%3Cg id='Icons/Navigation/chevron-right' stroke='none' stroke-width='1' fill='none' fill-rule='evenodd'%3E%3Cmask id='mask-2' fill='white'%3E%3Cuse xlink:href='%23path-1'%3E%3C/use%3E%3C/mask%3E%3Cuse id='Icons/Navigation/ic_chevron_right_18px' fill='%23233745' fill-rule='nonzero' xlink:href='%23path-1'%3E%3C/use%3E%3C/g%3E%3C/svg%3E");
+ mask-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg width='16px' height='16px' viewBox='0 0 16 16' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3Ctitle%3EIcons/Navigation/chevron-right%3C/title%3E%3Cdefs%3E%3Cpolygon id='path-1' points='6.66666666 3.99999998 5.72444443 4.9422222 8.78222223 8 5.72444443 11.0577778 6.66666666 12 10.6666667 8'%3E%3C/polygon%3E%3C/defs%3E%3Cg id='Icons/Navigation/chevron-right' stroke='none' stroke-width='1' fill='none' fill-rule='evenodd'%3E%3Cmask id='mask-2' fill='white'%3E%3Cuse xlink:href='%23path-1'%3E%3C/use%3E%3C/mask%3E%3Cuse id='Icons/Navigation/ic_chevron_right_18px' fill='%23233745' fill-rule='nonzero' xlink:href='%23path-1'%3E%3C/use%3E%3C/g%3E%3C/svg%3E");
+ }
}
.rdp-caption_label {
diff --git a/packages/react-component-library/src/components/Modal/partials/StyledModal.tsx b/packages/react-component-library/src/components/Modal/partials/StyledModal.tsx
index 6e44b3071d..365e8f0d49 100644
--- a/packages/react-component-library/src/components/Modal/partials/StyledModal.tsx
+++ b/packages/react-component-library/src/components/Modal/partials/StyledModal.tsx
@@ -1,4 +1,4 @@
-import { zIndex } from '@royalnavy/design-tokens'
+import { color, zIndex } from '@royalnavy/design-tokens'
import styled from 'styled-components'
export const StyledModal = styled.dialog`
@@ -8,5 +8,8 @@ export const StyledModal = styled.dialog`
bottom: 0;
left: 0;
background: rgba(0, 0, 0, 0.2);
+ /* Native