diff --git a/src/breadcrumb-group/__tests__/widgetized-breadcrumbs.test.tsx b/src/breadcrumb-group/__tests__/widgetized-breadcrumbs.test.tsx
index 7a105d12cd..5c472771fd 100644
--- a/src/breadcrumb-group/__tests__/widgetized-breadcrumbs.test.tsx
+++ b/src/breadcrumb-group/__tests__/widgetized-breadcrumbs.test.tsx
@@ -47,6 +47,7 @@ function getResourceTypeElements(container: HTMLElement) {
jest.mock('../../../lib/components/internal/hooks/use-visual-mode', () => ({
useVisualRefresh: jest.fn().mockReturnValue(false),
+ useOneTheme: jest.fn().mockReturnValue(false),
}));
describe('Classic design', () => {
diff --git a/src/container/__tests__/sticky-header.test.tsx b/src/container/__tests__/sticky-header.test.tsx
index 593d6eea61..c0fb8618b9 100644
--- a/src/container/__tests__/sticky-header.test.tsx
+++ b/src/container/__tests__/sticky-header.test.tsx
@@ -12,6 +12,7 @@ jest.mock('../../../lib/components/internal/hooks/use-mobile', () => ({
}));
jest.mock('../../internal/hooks/use-visual-mode', () => ({
useVisualRefresh: jest.fn().mockReturnValue(true),
+ useOneTheme: jest.fn().mockReturnValue(false),
}));
jest.mock('@cloudscape-design/component-toolkit/dom', () => ({
diff --git a/src/content-layout/__tests__/content-layout.test.tsx b/src/content-layout/__tests__/content-layout.test.tsx
index 892ccd2626..60a4fed801 100644
--- a/src/content-layout/__tests__/content-layout.test.tsx
+++ b/src/content-layout/__tests__/content-layout.test.tsx
@@ -13,6 +13,7 @@ import styles from '../../../lib/components/content-layout/styles.selectors.js';
jest.mock('../../../lib/components/internal/hooks/use-visual-mode', () => ({
useVisualRefresh: jest.fn().mockReturnValue(false),
+ useOneTheme: jest.fn().mockReturnValue(false),
}));
function renderContentLayout(props: ContentLayoutProps = {}) {
diff --git a/src/header/__tests__/sticky-responsiveness.test.tsx b/src/header/__tests__/sticky-responsiveness.test.tsx
index 8bb4dbbc9d..89a120af6b 100644
--- a/src/header/__tests__/sticky-responsiveness.test.tsx
+++ b/src/header/__tests__/sticky-responsiveness.test.tsx
@@ -12,6 +12,7 @@ import styles from '../../../lib/components/header/styles.css.js';
jest.mock('../../../lib/components/internal/hooks/use-visual-mode', () => ({
useVisualRefresh: jest.fn().mockReturnValue(false),
+ useOneTheme: jest.fn().mockReturnValue(false),
}));
function renderHeader(jsx: React.ReactElement) {
diff --git a/src/internal/hooks/use-portal-mode-classes/__tests__/use-portal-mode-classes.test.tsx b/src/internal/hooks/use-portal-mode-classes/__tests__/use-portal-mode-classes.test.tsx
index 0412a88741..216fc23072 100644
--- a/src/internal/hooks/use-portal-mode-classes/__tests__/use-portal-mode-classes.test.tsx
+++ b/src/internal/hooks/use-portal-mode-classes/__tests__/use-portal-mode-classes.test.tsx
@@ -42,6 +42,24 @@ describe('usePortalModeClasses', () => {
expect(screen.getByTestId('subject')).toHaveClass('awsui-visual-refresh', { exact: true });
});
+ test('should detect one theme mode', () => {
+ document.body.classList.add('awsui-one-theme');
+
+ render();
+ expect(screen.getByTestId('subject')).toHaveClass('awsui-one-theme', { exact: true });
+ // Must not stamp the VR class, which would override One Theme tokens on portaled content.
+ expect(screen.getByTestId('subject')).not.toHaveClass('awsui-visual-refresh');
+ });
+
+ test('should let one theme win when visual refresh is also active', () => {
+ globalWithFlags[Symbol.for('awsui-visual-refresh-flag')] = () => true;
+ document.body.classList.add('awsui-one-theme');
+
+ render();
+ expect(screen.getByTestId('subject')).toHaveClass('awsui-one-theme', { exact: true });
+ expect(screen.getByTestId('subject')).not.toHaveClass('awsui-visual-refresh');
+ });
+
test('should detect contexts', () => {
render(
diff --git a/src/internal/hooks/use-portal-mode-classes/index.ts b/src/internal/hooks/use-portal-mode-classes/index.ts
index 606d36237f..682aa6b8a3 100644
--- a/src/internal/hooks/use-portal-mode-classes/index.ts
+++ b/src/internal/hooks/use-portal-mode-classes/index.ts
@@ -7,18 +7,20 @@ import { useCurrentMode, useDensityMode } from '@cloudscape-design/component-too
import { useVisualContext } from '../../components/visual-context';
import { ALWAYS_VISUAL_REFRESH } from '../../environment';
-import { useVisualRefresh } from '../use-visual-mode';
+import { useOneTheme, useVisualRefresh } from '../use-visual-mode';
export function usePortalModeClasses(ref: React.RefObject, options?: { resetVisualContext?: boolean }) {
const colorMode = useCurrentMode(ref);
const densityMode = useDensityMode(ref);
const context = useVisualContext(ref);
- const visualRefreshWithClass = useVisualRefresh() && !ALWAYS_VISUAL_REFRESH;
+ const oneTheme = useOneTheme();
+ const visualRefreshWithClass = useVisualRefresh() && !ALWAYS_VISUAL_REFRESH && !oneTheme;
return clsx({
'awsui-polaris-dark-mode awsui-dark-mode': colorMode === 'dark',
'awsui-polaris-compact-mode awsui-compact-mode': densityMode === 'compact',
'awsui-visual-refresh': visualRefreshWithClass,
+ 'awsui-one-theme': oneTheme,
[`awsui-context-${context}`]: context && !options?.resetVisualContext,
});
}
diff --git a/src/internal/hooks/use-visual-mode/index.ts b/src/internal/hooks/use-visual-mode/index.ts
index 6bba0d58b1..24c1965904 100644
--- a/src/internal/hooks/use-visual-mode/index.ts
+++ b/src/internal/hooks/use-visual-mode/index.ts
@@ -1,8 +1,10 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
-import { useRuntimeVisualRefresh } from '@cloudscape-design/component-toolkit/internal';
+import { isThemeActive, Theme, useRuntimeVisualRefresh } from '@cloudscape-design/component-toolkit/internal';
import { ALWAYS_VISUAL_REFRESH } from '../../environment';
export const useVisualRefresh = ALWAYS_VISUAL_REFRESH ? () => true : useRuntimeVisualRefresh;
+
+export const useOneTheme = () => isThemeActive(Theme.OneTheme);
diff --git a/src/internal/widgets/__tests__/widgets.test.tsx b/src/internal/widgets/__tests__/widgets.test.tsx
index d3c58e0917..4a0634c599 100644
--- a/src/internal/widgets/__tests__/widgets.test.tsx
+++ b/src/internal/widgets/__tests__/widgets.test.tsx
@@ -20,6 +20,7 @@ function findContent(container: HTMLElement) {
jest.mock('../../../../lib/components/internal/hooks/use-visual-mode', () => ({
useVisualRefresh: jest.fn().mockReturnValue(false),
+ useOneTheme: jest.fn().mockReturnValue(false),
}));
describe('Classic design', () => {
diff --git a/src/split-panel/__tests__/widgetized-panel.test.tsx b/src/split-panel/__tests__/widgetized-panel.test.tsx
index 9c49dfad95..be1a0be113 100644
--- a/src/split-panel/__tests__/widgetized-panel.test.tsx
+++ b/src/split-panel/__tests__/widgetized-panel.test.tsx
@@ -43,6 +43,7 @@ const defaultProps: SplitPanelImplementationProps = {
jest.mock('../../../lib/components/internal/hooks/use-visual-mode', () => ({
useVisualRefresh: jest.fn().mockReturnValue(false),
+ useOneTheme: jest.fn().mockReturnValue(false),
}));
describe('Classic design', () => {
diff --git a/src/wizard/__tests__/analytics-metadata.test.tsx b/src/wizard/__tests__/analytics-metadata.test.tsx
index b64a571b53..1bb2fb080e 100644
--- a/src/wizard/__tests__/analytics-metadata.test.tsx
+++ b/src/wizard/__tests__/analytics-metadata.test.tsx
@@ -26,6 +26,7 @@ import labels from '../../../lib/components/wizard/analytics-metadata/styles.css
jest.mock('../../../lib/components/internal/hooks/use-visual-mode', () => ({
useVisualRefresh: jest.fn().mockReturnValue(false),
+ useOneTheme: jest.fn().mockReturnValue(false),
}));
const steps: WizardProps['steps'] = [