diff --git a/src/components/gui/gui.jsx b/src/components/gui/gui.jsx
index a44604898..53330d3b7 100644
--- a/src/components/gui/gui.jsx
+++ b/src/components/gui/gui.jsx
@@ -117,6 +117,7 @@ const GUIComponent = props => {
stageSizeMode,
targetIsStage,
telemetryModalVisible,
+ theme,
tipsLibraryVisible,
vm,
...componentProps
@@ -303,10 +304,12 @@ const GUIComponent = props => {
canUseCloud={canUseCloud}
grow={1}
isVisible={blocksTabVisible}
+ key={theme}
options={{
media: `${basePath}static/blocks-media/`
}}
stageSize={stageSize}
+ theme={theme}
vm={vm}
/>
@@ -422,6 +425,7 @@ GUIComponent.propTypes = {
stageSizeMode: PropTypes.oneOf(Object.keys(STAGE_SIZE_MODES)),
targetIsStage: PropTypes.bool,
telemetryModalVisible: PropTypes.bool,
+ theme: PropTypes.string,
tipsLibraryVisible: PropTypes.bool,
vm: PropTypes.instanceOf(VM).isRequired
};
@@ -448,7 +452,8 @@ GUIComponent.defaultProps = {
const mapStateToProps = state => ({
// This is the button's mode, as opposed to the actual current state
- stageSizeMode: state.scratchGui.stageSize.stageSize
+ stageSizeMode: state.scratchGui.stageSize.stageSize,
+ theme: state.scratchGui.theme.theme
});
export default injectIntl(connect(
diff --git a/src/containers/blocks.jsx b/src/containers/blocks.jsx
index 5756249f7..cb998b929 100644
--- a/src/containers/blocks.jsx
+++ b/src/containers/blocks.jsx
@@ -18,6 +18,7 @@ import {BLOCKS_DEFAULT_SCALE, STAGE_DISPLAY_SIZES} from '../lib/layout-constants
import DropAreaHOC from '../lib/drop-area-hoc.jsx';
import DragConstants from '../lib/drag-constants';
import defineDynamicBlock from '../lib/define-dynamic-block';
+import {getColorsForTheme, themes} from '../lib/themes';
import {connect} from 'react-redux';
import {updateToolbox} from '../reducers/toolbox';
@@ -94,7 +95,7 @@ class Blocks extends React.Component {
const workspaceConfig = defaultsDeep({},
Blocks.defaultOptions,
this.props.options,
- {rtl: this.props.isRtl, toolbox: this.props.toolboxXML}
+ {rtl: this.props.isRtl, toolbox: this.props.toolboxXML, colours: getColorsForTheme(this.props.theme)}
);
this.workspace = this.ScratchBlocks.inject(this.blocks, workspaceConfig);
@@ -351,7 +352,8 @@ class Blocks extends React.Component {
return makeToolboxXML(false, target.isStage, target.id, dynamicBlocksXML,
targetCostumes[targetCostumes.length - 1].name,
stageCostumes[stageCostumes.length - 1].name,
- targetSounds.length > 0 ? targetSounds[targetSounds.length - 1].name : ''
+ targetSounds.length > 0 ? targetSounds[targetSounds.length - 1].name : '',
+ getColorsForTheme(this.props.theme)
);
} catch {
return null;
@@ -620,22 +622,11 @@ Blocks.propTypes = {
wheel: PropTypes.bool,
startScale: PropTypes.number
}),
- colours: PropTypes.shape({
- workspace: PropTypes.string,
- flyout: PropTypes.string,
- toolbox: PropTypes.string,
- toolboxSelected: PropTypes.string,
- scrollbar: PropTypes.string,
- scrollbarHover: PropTypes.string,
- insertionMarker: PropTypes.string,
- insertionMarkerOpacity: PropTypes.number,
- fieldShadow: PropTypes.string,
- dragShadowOpacity: PropTypes.number
- }),
comments: PropTypes.bool,
collapse: PropTypes.bool
}),
stageSize: PropTypes.oneOf(Object.keys(STAGE_DISPLAY_SIZES)).isRequired,
+ theme: PropTypes.oneOf(themes),
toolboxXML: PropTypes.string,
updateMetrics: PropTypes.func,
updateToolboxState: PropTypes.func,
@@ -656,18 +647,6 @@ Blocks.defaultOptions = {
length: 2,
colour: '#ddd'
},
- colours: {
- workspace: '#F9F9F9',
- flyout: '#F9F9F9',
- toolbox: '#FFFFFF',
- toolboxSelected: '#E9EEF2',
- scrollbar: '#CECDCE',
- scrollbarHover: '#CECDCE',
- insertionMarker: '#000000',
- insertionMarkerOpacity: 0.2,
- fieldShadow: 'rgba(255, 255, 255, 0.3)',
- dragShadowOpacity: 0.6
- },
comments: true,
collapse: false,
sounds: false
@@ -675,7 +654,8 @@ Blocks.defaultOptions = {
Blocks.defaultProps = {
isVisible: true,
- options: Blocks.defaultOptions
+ options: Blocks.defaultOptions,
+ theme: 'standard'
};
const mapStateToProps = state => ({
diff --git a/src/lib/make-toolbox-xml.js b/src/lib/make-toolbox-xml.js
index 0b618979d..8d356e442 100644
--- a/src/lib/make-toolbox-xml.js
+++ b/src/lib/make-toolbox-xml.js
@@ -1,17 +1,19 @@
import ScratchBlocks from 'scratch-blocks';
+import {defaultColors} from './themes';
const categorySeparator = '';
const blockSeparator = ''; // At default scale, about 28px
/* eslint-disable no-unused-vars */
-const motion = function (isInitialSetup, isStage, targetId) {
+const motion = function (isInitialSetup, isStage, targetId, colors) {
const stageSelected = ScratchBlocks.ScratchMsgs.translate(
'MOTION_STAGE_SELECTED',
'Stage selected: no motion blocks'
);
+ // Note: the category's secondaryColour matches up with the blocks' tertiary color, both used for border color.
return `
-
+
${isStage ? `
` : `
@@ -151,11 +153,12 @@ const xmlEscape = function (unsafe) {
});
};
-const looks = function (isInitialSetup, isStage, targetId, costumeName, backdropName) {
+const looks = function (isInitialSetup, isStage, targetId, costumeName, backdropName, colors) {
const hello = ScratchBlocks.ScratchMsgs.translate('LOOKS_HELLO', 'Hello!');
const hmm = ScratchBlocks.ScratchMsgs.translate('LOOKS_HMM', 'Hmm...');
+ // Note: the category's secondaryColour matches up with the blocks' tertiary color, both used for border color.
return `
-
+
${isStage ? '' : `
@@ -288,9 +291,10 @@ const looks = function (isInitialSetup, isStage, targetId, costumeName, backdrop
`;
};
-const sound = function (isInitialSetup, isStage, targetId, soundName) {
+const sound = function (isInitialSetup, isStage, targetId, soundName, colors) {
+ // Note: the category's secondaryColour matches up with the blocks' tertiary color, both used for border color.
return `
-
+
@@ -343,9 +347,10 @@ const sound = function (isInitialSetup, isStage, targetId, soundName) {
`;
};
-const events = function (isInitialSetup, isStage) {
+const events = function (isInitialSetup, isStage, targetId, colors) {
+ // Note: the category's secondaryColour matches up with the blocks' tertiary color, both used for border color.
return `
-
+
@@ -382,9 +387,14 @@ const events = function (isInitialSetup, isStage) {
`;
};
-const control = function (isInitialSetup, isStage) {
+const control = function (isInitialSetup, isStage, targetId, colors) {
+ // Note: the category's secondaryColour matches up with the blocks' tertiary color, both used for border color.
return `
-
+
@@ -429,10 +439,15 @@ const control = function (isInitialSetup, isStage) {
`;
};
-const sensing = function (isInitialSetup, isStage) {
+const sensing = function (isInitialSetup, isStage, targetId, colors) {
const name = ScratchBlocks.ScratchMsgs.translate('SENSING_ASK_TEXT', 'What\'s your name?');
+ // Note: the category's secondaryColour matches up with the blocks' tertiary color, both used for border color.
return `
-
+
${isStage ? '' : `
@@ -504,12 +519,17 @@ const sensing = function (isInitialSetup, isStage) {
`;
};
-const operators = function (isInitialSetup) {
+const operators = function (isInitialSetup, isStage, targetId, colors) {
const apple = ScratchBlocks.ScratchMsgs.translate('OPERATORS_JOIN_APPLE', 'apple');
const banana = ScratchBlocks.ScratchMsgs.translate('OPERATORS_JOIN_BANANA', 'banana');
const letter = ScratchBlocks.ScratchMsgs.translate('OPERATORS_LETTEROF_APPLE', 'a');
+ // Note: the category's secondaryColour matches up with the blocks' tertiary color, both used for border color.
return `
-
+
@@ -691,25 +711,27 @@ const operators = function (isInitialSetup) {
`;
};
-const variables = function () {
+const variables = function (isInitialSetup, isStage, targetId, colors) {
+ // Note: the category's secondaryColour matches up with the blocks' tertiary color, both used for border color.
return `
`;
};
-const myBlocks = function () {
+const myBlocks = function (isInitialSetup, isStage, targetId, colors) {
+ // Note: the category's secondaryColour matches up with the blocks' tertiary color, both used for border color.
return `
`;
@@ -732,10 +754,11 @@ const xmlClose = '';
* @param {?string} costumeName - The name of the default selected costume dropdown.
* @param {?string} backdropName - The name of the default selected backdrop dropdown.
* @param {?string} soundName - The name of the default selected sound dropdown.
+ * @param {?object} colors - The colors for the theme.
* @returns {string} - a ScratchBlocks-style XML document for the contents of the toolbox.
*/
const makeToolboxXML = function (isInitialSetup, isStage = true, targetId, categoriesXML = [],
- costumeName = '', backdropName = '', soundName = '') {
+ costumeName = '', backdropName = '', soundName = '', colors = defaultColors) {
isStage = isInitialSetup || isStage;
const gap = [categorySeparator];
@@ -753,15 +776,16 @@ const makeToolboxXML = function (isInitialSetup, isStage = true, targetId, categ
}
// return `undefined`
};
- const motionXML = moveCategory('motion') || motion(isInitialSetup, isStage, targetId);
- const looksXML = moveCategory('looks') || looks(isInitialSetup, isStage, targetId, costumeName, backdropName);
- const soundXML = moveCategory('sound') || sound(isInitialSetup, isStage, targetId, soundName);
- const eventsXML = moveCategory('event') || events(isInitialSetup, isStage, targetId);
- const controlXML = moveCategory('control') || control(isInitialSetup, isStage, targetId);
- const sensingXML = moveCategory('sensing') || sensing(isInitialSetup, isStage, targetId);
- const operatorsXML = moveCategory('operators') || operators(isInitialSetup, isStage, targetId);
- const variablesXML = moveCategory('data') || variables(isInitialSetup, isStage, targetId);
- const myBlocksXML = moveCategory('procedures') || myBlocks(isInitialSetup, isStage, targetId);
+ const motionXML = moveCategory('motion') || motion(isInitialSetup, isStage, targetId, colors.motion);
+ const looksXML = moveCategory('looks') ||
+ looks(isInitialSetup, isStage, targetId, costumeName, backdropName, colors.looks);
+ const soundXML = moveCategory('sound') || sound(isInitialSetup, isStage, targetId, soundName, colors.sounds);
+ const eventsXML = moveCategory('event') || events(isInitialSetup, isStage, targetId, colors.event);
+ const controlXML = moveCategory('control') || control(isInitialSetup, isStage, targetId, colors.control);
+ const sensingXML = moveCategory('sensing') || sensing(isInitialSetup, isStage, targetId, colors.sensing);
+ const operatorsXML = moveCategory('operators') || operators(isInitialSetup, isStage, targetId, colors.operators);
+ const variablesXML = moveCategory('data') || variables(isInitialSetup, isStage, targetId, colors.data);
+ const myBlocksXML = moveCategory('procedures') || myBlocks(isInitialSetup, isStage, targetId, colors.more);
const everything = [
xmlOpen,
diff --git a/src/lib/themes/__mocks__/dark-mode.js b/src/lib/themes/__mocks__/dark-mode.js
new file mode 100644
index 000000000..c3faf8d54
--- /dev/null
+++ b/src/lib/themes/__mocks__/dark-mode.js
@@ -0,0 +1,8 @@
+const blockColors = {
+ motion: {
+ primary: '#AAAAAA'
+ },
+ text: '#BBBBBB'
+};
+
+export default blockColors;
diff --git a/src/lib/themes/__mocks__/default-colors.js b/src/lib/themes/__mocks__/default-colors.js
new file mode 100644
index 000000000..f382f38aa
--- /dev/null
+++ b/src/lib/themes/__mocks__/default-colors.js
@@ -0,0 +1,11 @@
+const blockColors = {
+ motion: {
+ primary: '#111111',
+ secondary: '#222222',
+ tertiary: '#333333'
+ },
+ text: '#444444',
+ workspace: '#555555'
+};
+
+export default blockColors;
diff --git a/src/lib/themes/dark-mode.js b/src/lib/themes/dark-mode.js
new file mode 100644
index 000000000..d9fc37a57
--- /dev/null
+++ b/src/lib/themes/dark-mode.js
@@ -0,0 +1,66 @@
+const blockColors = {
+ motion: {
+ primary: '#0F1E33',
+ secondary: '#4C4C4C',
+ tertiary: '#4C97FF'
+ },
+ looks: {
+ primary: '#1E1433',
+ secondary: '#4C4C4C',
+ tertiary: '#9966FF'
+ },
+ sounds: {
+ primary: '#291329',
+ secondary: '#4C4C4C',
+ tertiary: '#CF63CF'
+ },
+ control: {
+ primary: '#332205',
+ secondary: '#4C4C4C',
+ tertiary: '#FFAB19'
+ },
+ event: {
+ primary: '#332600',
+ secondary: '#4C4C4C',
+ tertiary: '#FFBF00'
+ },
+ sensing: {
+ primary: '#12232A',
+ secondary: '#4C4C4C',
+ tertiary: '#5CB1D6'
+ },
+ pen: {
+ primary: '#03251C',
+ secondary: '#4C4C4C',
+ tertiary: '#0fBD8C'
+ },
+ operators: {
+ primary: '#112611',
+ secondary: '#4C4C4C',
+ tertiary: '#59C059'
+ },
+ data: {
+ primary: '#331C05',
+ secondary: '#4C4C4C',
+ tertiary: '#FF8C1A'
+ },
+ data_lists: {
+ primary: '#331405',
+ secondary: '#4C4C4C',
+ tertiary: '#FF661A'
+ },
+ more: {
+ primary: '#331419',
+ secondary: '#4C4C4C',
+ tertiary: '#FF6680'
+ },
+ text: '#E5E5E5',
+ workspace: '#121212',
+ toolboxSelected: '#4C4C4C',
+ toolboxText: '#E5E5E5',
+ toolbox: '#121212',
+ flyout: '#121212',
+ textField: '#4C4C4C'
+};
+
+export default blockColors;
diff --git a/src/lib/themes/default-colors.js b/src/lib/themes/default-colors.js
new file mode 100644
index 000000000..e9f9cde3c
--- /dev/null
+++ b/src/lib/themes/default-colors.js
@@ -0,0 +1,90 @@
+const blockColors = {
+ motion: {
+ primary: '#4C97FF',
+ secondary: '#4280D7',
+ tertiary: '#3373CC'
+ },
+ looks: {
+ primary: '#9966FF',
+ secondary: '#855CD6',
+ tertiary: '#774DCB'
+ },
+ sounds: {
+ primary: '#CF63CF',
+ secondary: '#C94FC9',
+ tertiary: '#BD42BD'
+ },
+ control: {
+ primary: '#FFAB19',
+ secondary: '#EC9C13',
+ tertiary: '#CF8B17'
+ },
+ event: {
+ primary: '#FFBF00',
+ secondary: '#E6AC00',
+ tertiary: '#CC9900'
+ },
+ sensing: {
+ primary: '#5CB1D6',
+ secondary: '#47A8D1',
+ tertiary: '#2E8EB8'
+ },
+ pen: {
+ primary: '#0fBD8C',
+ secondary: '#0DA57A',
+ tertiary: '#0B8E69'
+ },
+ operators: {
+ primary: '#59C059',
+ secondary: '#46B946',
+ tertiary: '#389438'
+ },
+ data: {
+ primary: '#FF8C1A',
+ secondary: '#FF8000',
+ tertiary: '#DB6E00'
+ },
+ // This is not a new category, but rather for differentiation
+ // between lists and scalar variables.
+ data_lists: {
+ primary: '#FF661A',
+ secondary: '#FF5500',
+ tertiary: '#E64D00'
+ },
+ more: {
+ primary: '#FF6680',
+ secondary: '#FF4D6A',
+ tertiary: '#FF3355'
+ },
+ text: '#575E75',
+ workspace: '#F9F9F9',
+ toolboxHover: '#4C97FF',
+ toolboxSelected: '#E9EEF2',
+ toolboxText: '#575E75',
+ toolbox: '#FFFFFF',
+ flyout: '#F9F9F9',
+ scrollbar: '#CECDCE',
+ scrollbarHover: '#CECDCE',
+ textField: '#FFFFFF',
+ insertionMarker: '#000000',
+ insertionMarkerOpacity: 0.2,
+ dragShadowOpacity: 0.6,
+ stackGlow: '#FFF200',
+ stackGlowSize: 4,
+ stackGlowOpacity: 1,
+ replacementGlow: '#FFFFFF',
+ replacementGlowSize: 2,
+ replacementGlowOpacity: 1,
+ colourPickerStroke: '#FFFFFF',
+ // CSS colours: support RGBA
+ fieldShadow: 'rgba(255, 255, 255, 0.3)',
+ dropDownShadow: 'rgba(0, 0, 0, .3)',
+ numPadBackground: '#547AB2',
+ numPadBorder: '#435F91',
+ numPadActiveBackground: '#435F91',
+ numPadText: 'white', // Do not use hex here, it cannot be inlined with data-uri SVG
+ valueReportBackground: '#FFFFFF',
+ valueReportBorder: '#AAAAAA'
+};
+
+export default blockColors;
diff --git a/src/lib/themes/high-contrast.js b/src/lib/themes/high-contrast.js
new file mode 100644
index 000000000..1c185556f
--- /dev/null
+++ b/src/lib/themes/high-contrast.js
@@ -0,0 +1,87 @@
+const blockColors = {
+ motion: {
+ primary: '#80B5FF',
+ secondary: '#B3D2FF',
+ tertiary: '#3373CC',
+ // this field is not used in the "classic" Scratch 3 color scheme
+ // If we add "modes" we'd have to handle what happens when this field is not defined.
+ quaternary: '#CCE1FF'
+ },
+ looks: {
+ primary: '#CCB3FF',
+ secondary: '#DDCCFF',
+ tertiary: '#774DCB',
+ quaternary: '#EEE5FF'
+ },
+ sounds: {
+ primary: '#E19DE1',
+ secondary: '#FFB3FF',
+ tertiary: '#BD42BD',
+ quaternary: '#FFCCFF'
+
+ },
+ control: {
+ primary: '#FFBE4C',
+ secondary: '#FFDA99',
+ tertiary: '#CF8B17',
+ quaternary: '#FFE3B3'
+ },
+ event: {
+ primary: '#FFD966',
+ secondary: '#FFECB3',
+ tertiary: '#CC9900',
+ quaternary: '#FFF2CC'
+ },
+ sensing: {
+ primary: '#85C4E0',
+ secondary: '#AED8EA',
+ tertiary: '#2E8EB8',
+ quaternary: '#C2E2F0'
+ },
+ pen: {
+ primary: '#13ECAF',
+ secondary: '#75F0CD',
+ tertiary: '#0B8E69',
+ quaternary: '#A3F5DE'
+ },
+ operators: {
+ primary: '#7ECE7E',
+ secondary: '#B5E3B5',
+ tertiary: '#389438',
+ quaternary: '#DAF1DA'
+ },
+ data: {
+ primary: '#FFA54C',
+ secondary: '#FFCC99',
+ tertiary: '#DB6E00',
+ quaternary: '#FFE5CC'
+ },
+ // This is not a new category, but rather for differentiation
+ // between lists and scalar variables.
+ data_lists: {
+ primary: '#FF9966',
+ secondary: '#FFCAB0', // I don't think this is used, b/c we don't have any droppable fields in list blocks
+ tertiary: '#E64D00',
+ quaternary: '#FFDDCC'
+ },
+ more: {
+ primary: '#FF99AA',
+ secondary: '#FFCCD5',
+ tertiary: '#FF3355',
+ quaternary: '#FFE5EA'
+ },
+ // For productionizing: we should look into making this field be the color of the text on the blocks
+ // e.g. move and steps in move (10) steps, and rename this property below for controlling the text inside
+ // of inputs e.g. 10
+ text: '#000000', // Text inside of inputs e.g. 90 in [point in direction (90)]
+
+ toolboxText: '#000000', // Toolbox text, color picker text (used to be #575E75)
+ // The color that the category menu label (e.g. 'motion', 'looks', etc.) changes to on hover
+ toolboxHover: '#3373CC',
+ insertionMarker: '#000000',
+ insertionMarkerOpacity: 0.2,
+ fieldShadow: 'rgba(255, 255, 255, 0.3)',
+ dragShadowOpacity: 0.6
+};
+
+export default blockColors;
diff --git a/src/lib/themes/index.js b/src/lib/themes/index.js
new file mode 100644
index 000000000..e1fa87f71
--- /dev/null
+++ b/src/lib/themes/index.js
@@ -0,0 +1,31 @@
+import defaultsDeep from 'lodash.defaultsdeep';
+
+import darkMode from './dark-mode';
+import highContrast from './high-contrast';
+import defaultColors from './default-colors';
+
+const mergeWithDefaults = colors => defaultsDeep({}, colors, defaultColors);
+
+const themeMap = {
+ 'dark-mode': mergeWithDefaults(darkMode),
+ 'high-contrast': mergeWithDefaults(highContrast),
+ 'standard': defaultColors
+};
+
+const getColorsForTheme = theme => {
+ const colors = themeMap[theme];
+
+ if (!colors) {
+ throw new Error(`Undefined theme ${theme}`);
+ }
+
+ return colors;
+};
+
+const themes = Object.keys(themeMap);
+
+export {
+ defaultColors,
+ getColorsForTheme,
+ themes
+};
diff --git a/src/reducers/gui.js b/src/reducers/gui.js
index 378a3203c..2e70c40e1 100644
--- a/src/reducers/gui.js
+++ b/src/reducers/gui.js
@@ -21,6 +21,7 @@ import fontsLoadedReducer, {fontsLoadedInitialState} from './fonts-loaded';
import restoreDeletionReducer, {restoreDeletionInitialState} from './restore-deletion';
import stageSizeReducer, {stageSizeInitialState} from './stage-size';
import targetReducer, {targetsInitialState} from './targets';
+import themeReducer, {themeInitialState} from './theme';
import timeoutReducer, {timeoutInitialState} from './timeout';
import toolboxReducer, {toolboxInitialState} from './toolbox';
import vmReducer, {vmInitialState} from './vm';
@@ -55,6 +56,7 @@ const guiInitialState = {
fontsLoaded: fontsLoadedInitialState,
restoreDeletion: restoreDeletionInitialState,
targets: targetsInitialState,
+ theme: themeInitialState,
timeout: timeoutInitialState,
toolbox: toolboxInitialState,
vm: vmInitialState,
@@ -154,6 +156,7 @@ const guiReducer = combineReducers({
fontsLoaded: fontsLoadedReducer,
restoreDeletion: restoreDeletionReducer,
targets: targetReducer,
+ theme: themeReducer,
timeout: timeoutReducer,
toolbox: toolboxReducer,
vm: vmReducer,
diff --git a/src/reducers/theme.js b/src/reducers/theme.js
new file mode 100644
index 000000000..5d05eff00
--- /dev/null
+++ b/src/reducers/theme.js
@@ -0,0 +1,25 @@
+const SET_THEME = 'scratch-gui/theme/SET_THEME';
+
+const initialState = {
+ theme: 'standard'
+};
+
+const reducer = (state = initialState, action) => {
+ switch (action.type) {
+ case SET_THEME:
+ return {...state, theme: action.theme};
+ default:
+ return state;
+ }
+};
+
+const setTheme = theme => ({
+ type: SET_THEME,
+ theme
+});
+
+export {
+ reducer as default,
+ initialState as themeInitialState,
+ setTheme
+};
diff --git a/test/unit/util/themes.test.js b/test/unit/util/themes.test.js
new file mode 100644
index 000000000..d281add0c
--- /dev/null
+++ b/test/unit/util/themes.test.js
@@ -0,0 +1,22 @@
+import {defaultColors, getColorsForTheme} from '../../../src/lib/themes';
+
+jest.mock('../../../src/lib/themes/default-colors');
+jest.mock('../../../src/lib/themes/dark-mode');
+
+describe('themes', () => {
+ test('provides the default theme colors', () => {
+ expect(defaultColors.motion.primary).toEqual('#111111');
+ });
+
+ test('returns the dark mode', () => {
+ const colors = getColorsForTheme('dark-mode');
+
+ expect(colors.motion.primary).toEqual('#AAAAAA');
+ });
+
+ test('uses default theme colors when not specified', () => {
+ const colors = getColorsForTheme('dark-mode');
+
+ expect(colors.motion.secondary).toEqual('#222222');
+ });
+});