@genesislcap/blank-app-seed 5.18.0 → 5.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/.genx/configure.js +4 -2
  2. package/.genx/package.json +2 -2
  3. package/.genx/utils/index.js +2 -0
  4. package/.genx/utils/toModalThemeFormat.js +74 -0
  5. package/.genx/versions.json +1 -1
  6. package/CHANGELOG.md +14 -0
  7. package/client-tmp/angular/README.md +9 -1
  8. package/client-tmp/angular/package.json +0 -1
  9. package/client-tmp/angular/src/app/layouts/blank/blank.layout.ts +12 -3
  10. package/client-tmp/angular/src/app/layouts/default/default.layout.html +1 -1
  11. package/client-tmp/angular/src/app/layouts/default/default.layout.ts +17 -13
  12. package/client-tmp/angular/src/jest.setup.ts +20 -0
  13. package/client-tmp/angular/src/styles/active-theme.ts +6 -0
  14. package/client-tmp/angular/src/styles/default.theme.json +37 -0
  15. package/client-tmp/react/README.md +9 -1
  16. package/client-tmp/react/package.json +0 -1
  17. package/client-tmp/react/src/layouts/blank/BlankLayout.tsx +9 -4
  18. package/client-tmp/react/src/layouts/default/DefaultLayout.tsx +15 -12
  19. package/client-tmp/react/src/styles/active-theme.ts +6 -0
  20. package/client-tmp/react/src/styles/default.theme.json +37 -0
  21. package/client-tmp/web-components/README.md +8 -7
  22. package/client-tmp/web-components/package.json +0 -1
  23. package/client-tmp/web-components/src/layouts/default.ts +2 -1
  24. package/client-tmp/web-components/src/main/main.ts +14 -11
  25. package/client-tmp/web-components/src/styles/active-theme.ts +6 -0
  26. package/client-tmp/web-components/src/styles/default.theme.json +37 -0
  27. package/package.json +1 -1
  28. package/client-tmp/angular/src/styles/design-tokens.json +0 -60
  29. package/client-tmp/react/src/styles/design-tokens.json +0 -60
  30. package/client-tmp/web-components/src/styles/design-tokens.json +0 -60
@@ -18,6 +18,7 @@ const {
18
18
  deleteGradleWrappers,
19
19
  generateStore,
20
20
  fontUtils,
21
+ toModalThemeFormat,
21
22
  } = require('./utils');
22
23
 
23
24
  /**
@@ -52,8 +53,9 @@ module.exports = async (data, utils) => {
52
53
  if (data.designTokens && Object.keys(data.designTokens).length > 0) {
53
54
  try {
54
55
  const frameworkDir = FRAMEWORKS_DIR_MAP.get(data.framework);
55
- const templateFile = path.join(__dirname, '..', DIR_CLIENT_TEMP_ALIAS, frameworkDir, 'src/styles/design-tokens.json');
56
- const jsonContent = JSON.stringify(data.designTokens, null, 2);
56
+ const templateFile = path.join(__dirname, '..', DIR_CLIENT_TEMP_ALIAS, frameworkDir, 'src/styles/default.theme.json');
57
+ // Legacy designTokens payloads are converted to the modal theme format; modal payloads pass through verbatim
58
+ const jsonContent = JSON.stringify(toModalThemeFormat(data.designTokens), null, 2);
57
59
  const templateDir = path.dirname(templateFile);
58
60
  if (!fs.existsSync(templateDir)) {
59
61
  fs.mkdirSync(templateDir, { recursive: true });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@genesislcap/blank-app-seed-config",
3
3
  "description": "Genesis Blank App Seed Configuration",
4
- "version": "5.18.0",
4
+ "version": "5.19.0",
5
5
  "license": "Apache-2.0",
6
6
  "scripts": {
7
7
  "lint": "eslint .",
@@ -51,7 +51,7 @@
51
51
  ".iml"
52
52
  ],
53
53
  "exclude": [
54
- "design-tokens.json"
54
+ "default.theme.json"
55
55
  ]
56
56
  }
57
57
  }
@@ -9,6 +9,7 @@ const makeDirectory = require('./makeDirectory');
9
9
  const normalizeFrameworkAlias = require('./normalizeFrameworkAlias');
10
10
  const parseJSONArgument = require('./parseJSONArgument');
11
11
  const registerPartials = require('./registerPartials');
12
+ const toModalThemeFormat = require('./toModalThemeFormat');
12
13
  const validateRoute = require('./validateRoute');
13
14
  const validateFrameworkAlias = require('./validateFrameworkAlias');
14
15
  const fontUtils = require('./fontUtils');
@@ -25,6 +26,7 @@ module.exports = {
25
26
  normalizeFrameworkAlias,
26
27
  parseJSONArgument,
27
28
  registerPartials,
29
+ toModalThemeFormat,
28
30
  validateFrameworkAlias,
29
31
  validateRoute,
30
32
  fontUtils,
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Flattens legacy nested customTokens (`{ ag: { row: { height: { $value: '28px' } } } }`)
3
+ * to the modal theme's flat CSS custom property map (`{ '--ag-row-height': '28px' }`).
4
+ * A leaf is an object carrying `$value`, or a plain scalar.
5
+ */
6
+ const flattenCustomTokens = (customTokens, prefix = '') => {
7
+ const flat = {};
8
+ Object.entries(customTokens).forEach(([key, value]) => {
9
+ const name = prefix ? `${prefix}-${key}` : key;
10
+ if (value && typeof value === 'object') {
11
+ if ('$value' in value) {
12
+ flat[`--${name}`] = String(value.$value);
13
+ } else {
14
+ Object.assign(flat, flattenCustomTokens(value, name));
15
+ }
16
+ } else if (value !== undefined && value !== null) {
17
+ flat[`--${name}`] = String(value);
18
+ }
19
+ });
20
+ return flat;
21
+ };
22
+
23
+ /**
24
+ * Converts a designTokens payload to the modal theme FORMAT.
25
+ * - Already modal (has `modes`): returned verbatim.
26
+ * - Legacy (`design_tokens` at the top level): converted to an adaptive modal theme —
27
+ * everything except the `mode` group becomes shared, both light and dark modes are
28
+ * defined, nested customTokens are flattened, and `header_logo`/`custom_fonts` are
29
+ * preserved as top-level keys.
30
+ * - Anything else: returned verbatim.
31
+ *
32
+ * Not to be confused with `toModalTheme` from `@genesislcap/rapid-design-system`, which
33
+ * VALIDATES an already-modal theme at app startup (and throws on legacy shapes); this
34
+ * utility is the generation-time converter that produces that modal shape from legacy input.
35
+ */
36
+ const toModalThemeFormat = (designTokens) => {
37
+ if (designTokens.modes || !designTokens.design_tokens) {
38
+ return designTokens;
39
+ }
40
+
41
+ const { mode, ...sharedDesignTokens } = designTokens.design_tokens;
42
+ const theme = {
43
+ id: 'default',
44
+ colorStrategy: 'adaptive',
45
+ defaultMode: mode?.luminance?.$value < 0.5 ? 'dark' : 'light',
46
+ };
47
+ if (designTokens.header_logo !== undefined) {
48
+ theme.header_logo = designTokens.header_logo;
49
+ }
50
+ if (designTokens.custom_fonts !== undefined) {
51
+ theme.custom_fonts = designTokens.custom_fonts;
52
+ }
53
+ theme.shared = {
54
+ design_tokens: sharedDesignTokens,
55
+ };
56
+ if (designTokens.customTokens) {
57
+ theme.shared.customTokens = flattenCustomTokens(designTokens.customTokens);
58
+ }
59
+ theme.modes = {
60
+ light: {
61
+ design_tokens: {
62
+ mode: { luminance: { $value: 1, $type: 'number' } },
63
+ },
64
+ },
65
+ dark: {
66
+ design_tokens: {
67
+ mode: { luminance: { $value: 0.23, $type: 'number' } },
68
+ },
69
+ },
70
+ };
71
+ return theme;
72
+ };
73
+
74
+ module.exports = toModalThemeFormat;
@@ -1,5 +1,5 @@
1
1
  {
2
- "UI": "14.484.0",
2
+ "UI": "14.486.0",
3
3
  "GSF": "8.15.14",
4
4
  "Auth": "8.15.3"
5
5
  }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [5.19.0](https://github.com/genesiscommunitysuccess/blank-app-seed/compare/v5.18.0...v5.19.0) (2026-07-10)
4
+
5
+
6
+ ### Features
7
+
8
+ * adopt FUI modal theme API for theme selection GENC-0 (#602) 8ee122a
9
+ * apply UX default theme to the seed default GENC-0 e6e68de
10
+ * migrate theming to the FUI modal theme API ([FUI-2575](https://github.com/genesiscommunitysuccess/blank-app-seed/issues/2575)) GENC-0 60fd54a
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * align modal theming with FUI review-fixes PR 2384 GENC-0 f326bca
16
+
3
17
  ## [5.18.0](https://github.com/genesiscommunitysuccess/blank-app-seed/compare/v5.17.3...v5.18.0) (2026-07-08)
4
18
 
5
19
 
@@ -27,6 +27,15 @@ Run `npm run test:e2e` to execute the end-to-end tests via a platform of your ch
27
27
 
28
28
  Run `npm run lint` to lint the project.
29
29
 
30
+ ## Styling
31
+
32
+ The design system is driven by the theme file `src/styles/default.theme.json`: tokens under `shared` (colours,
33
+ typography, sizing) apply to every mode, while the `modes` section declares the available modes (`light` and `dark` by
34
+ default). `src/styles/active-theme.ts` loads the theme and is used by the layout components to inject the theme styles,
35
+ apply the initial mode, and decide whether the header shows the light/dark toggle. The toggle appears when the theme
36
+ declares more than one mode (set `showModeToggle` in the theme file to override this), cycles through the declared
37
+ modes, and the selected mode is remembered across reloads.
38
+
30
39
  ## NPM Scripts
31
40
 
32
41
  - `npm run baseline`: Cleans the project and installs dependencies.
@@ -41,7 +50,6 @@ Run `npm run lint` to lint the project.
41
50
  - `npm run dev:intellij`: Serves the project in development mode (specific for IntelliJ).
42
51
  - `npm run dev:https`: Serves the project in development mode with HTTPS.
43
52
  - `npm run dev:webpack`: Serves the project in development mode (specific for webpack configuration).
44
- - `npm run dsconfig`: Configures design system based on `src/styles/design-tokens.json`.
45
53
  - `npm run lint`: Lints the project using the specified profile.
46
54
  - `npm run lint:fix`: Fixes linting errors in the project.
47
55
  - `npm run lint:eslint`: Lints the project using ESLint and the specified profile.
@@ -22,7 +22,6 @@
22
22
  "dev:intellij": "npm run dev",
23
23
  "dev:https": "npm run dev -- --https",
24
24
  "dev:webpack": "npm run dev",
25
- "dsconfig": "dsconfig --path src/styles/design-tokens.json",
26
25
  "lint": "genx lint --profile",
27
26
  "lint:fix": "genx lint --fix",
28
27
  "lint:eslint": "genx lint -l eslint --profile",
@@ -1,6 +1,10 @@
1
1
  import { Component, ElementRef, ViewChild, AfterViewInit } from '@angular/core';
2
- import { configureDesignSystem } from '@genesislcap/foundation-ui';
3
- import * as designTokens from '../../../styles/design-tokens.json';
2
+ import {
3
+ applyMode,
4
+ injectThemeStyles,
5
+ resolveInitialMode,
6
+ } from '@genesislcap/rapid-design-system';
7
+ import { activeTheme } from '../../../styles/active-theme';
4
8
 
5
9
  @Component({
6
10
  selector: 'app-blank-layout',
@@ -11,6 +15,11 @@ export class BlankLayoutComponent implements AfterViewInit {
11
15
  @ViewChild('designSystemProvider') designSystemProviderElement!: ElementRef;
12
16
 
13
17
  ngAfterViewInit() {
14
- configureDesignSystem(this.designSystemProviderElement.nativeElement, designTokens);
18
+ injectThemeStyles(this.designSystemProviderElement.nativeElement, activeTheme);
19
+ applyMode(
20
+ this.designSystemProviderElement.nativeElement,
21
+ activeTheme,
22
+ resolveInitialMode(activeTheme),
23
+ );
15
24
  }
16
25
  }
@@ -5,7 +5,7 @@
5
5
  logo-src="assets/{{headerLogoSrc}}"
6
6
  {{/if}}
7
7
  #foundationHeader
8
- show-luminance-toggle-button
8
+ [attr.show-luminance-toggle-button]="modeToggleEnabled"
9
9
  show-misc-toggle-button
10
10
  (luminance-icon-clicked)="onLuminanceToogle()"
11
11
  (logout-clicked)="onLogout()"
@@ -1,8 +1,13 @@
1
1
  import { Component, ViewChild, ElementRef, AfterViewInit } from '@angular/core';
2
2
  import { Router } from '@angular/router';
3
- import { configureDesignSystem, FoundationRouteNavItem } from '@genesislcap/foundation-ui';
4
- import { baseLayerLuminance, StandardLuminance } from '@genesislcap/web-core';
5
- import * as designTokens from '../../../styles/design-tokens.json';
3
+ import { FoundationRouteNavItem } from '@genesislcap/foundation-ui';
4
+ import {
5
+ applyMode,
6
+ injectThemeStyles,
7
+ nextMode,
8
+ resolveInitialMode,
9
+ } from '@genesislcap/rapid-design-system';
10
+ import { activeTheme, modeToggleEnabled } from '../../../styles/active-theme';
6
11
  import { RouteService } from '../../services/route.service';
7
12
  import BaseLayout from '../base.layout';
8
13
  import { registerStylesTarget } from '../../../pbc/utils';
@@ -15,6 +20,8 @@ import { registerStylesTarget } from '../../../pbc/utils';
15
20
  export class DefaultLayoutComponent extends BaseLayout implements AfterViewInit {
16
21
  @ViewChild('designSystemProvider') designSystemProviderElement!: ElementRef;
17
22
  navItems: FoundationRouteNavItem[] = [];
23
+ modeToggleEnabled = modeToggleEnabled;
24
+ private themeMode = resolveInitialMode(activeTheme);
18
25
 
19
26
  constructor(
20
27
  private el: ElementRef,
@@ -26,10 +33,12 @@ export class DefaultLayoutComponent extends BaseLayout implements AfterViewInit
26
33
  }
27
34
 
28
35
  ngAfterViewInit() {
29
- configureDesignSystem(this.designSystemProviderElement.nativeElement, designTokens);
36
+ injectThemeStyles(this.designSystemProviderElement.nativeElement, activeTheme);
37
+ this.themeMode = resolveInitialMode(activeTheme);
38
+ applyMode(this.designSystemProviderElement.nativeElement, activeTheme, this.themeMode);
30
39
  registerStylesTarget(this.el.nativeElement, 'layout');
31
40
  }
32
-
41
+
33
42
  navigateAngular = (path: string) => {
34
43
  this.router.navigate([path]);
35
44
  };
@@ -37,14 +46,9 @@ export class DefaultLayoutComponent extends BaseLayout implements AfterViewInit
37
46
  onLogout = () => {
38
47
  this.router.navigate(['/login']);
39
48
  };
40
-
49
+
41
50
  onLuminanceToogle = (): void => {
42
- baseLayerLuminance.setValueFor(
43
- this.designSystemProviderElement.nativeElement,
44
- baseLayerLuminance.getValueFor(this.designSystemProviderElement.nativeElement) ===
45
- StandardLuminance.DarkMode
46
- ? StandardLuminance.LightMode
47
- : StandardLuminance.DarkMode,
48
- );
51
+ this.themeMode = nextMode(activeTheme, this.themeMode);
52
+ applyMode(this.designSystemProviderElement.nativeElement, activeTheme, this.themeMode);
49
53
  };
50
54
  }
@@ -44,3 +44,23 @@ Object.defineProperty(window, 'BroadcastChannel', {
44
44
  writable: true,
45
45
  value: MockBroadcastChannel,
46
46
  });
47
+
48
+ // jsdom lacks the constructable-stylesheet API used by the modal-theme runtime
49
+ // (CSSStyleSheet.replaceSync + Document.adoptedStyleSheets)
50
+ if (typeof CSSStyleSheet === 'undefined') {
51
+ Object.defineProperty(window, 'CSSStyleSheet', {
52
+ writable: true,
53
+ value: class {
54
+ replaceSync() {}
55
+ },
56
+ });
57
+ } else if (!CSSStyleSheet.prototype.replaceSync) {
58
+ CSSStyleSheet.prototype.replaceSync = () => {};
59
+ }
60
+
61
+ if (!('adoptedStyleSheets' in document)) {
62
+ Object.defineProperty(document, 'adoptedStyleSheets', {
63
+ writable: true,
64
+ value: [],
65
+ });
66
+ }
@@ -0,0 +1,6 @@
1
+ import { isModeToggleEnabled, toModalTheme } from '@genesislcap/rapid-design-system';
2
+ import themeJson from './default.theme.json';
3
+
4
+ export const activeTheme = toModalTheme(themeJson);
5
+
6
+ export const modeToggleEnabled = isModeToggleEnabled(activeTheme);
@@ -0,0 +1,37 @@
1
+ {
2
+ "id": "default",
3
+ "colorStrategy": "adaptive",
4
+ "defaultMode": "dark",
5
+ "shared": {
6
+ "design_tokens": {
7
+ "fontFamily": {
8
+ "bodyFont": {
9
+ "$value": "{{#if fontFamily}}{{fontFamily}}, {{/if}}Roboto, \"Segoe UI\", Arial, Helvetica, sans-serif",
10
+ "$type": "fontFamily"
11
+ }
12
+ }
13
+ }
14
+ },
15
+ "modes": {
16
+ "light": {
17
+ "design_tokens": {
18
+ "mode": {
19
+ "luminance": {
20
+ "$value": 1,
21
+ "$type": "number"
22
+ }
23
+ }
24
+ }
25
+ },
26
+ "dark": {
27
+ "design_tokens": {
28
+ "mode": {
29
+ "luminance": {
30
+ "$value": 0.23,
31
+ "$type": "number"
32
+ }
33
+ }
34
+ }
35
+ }
36
+ }
37
+ }
@@ -29,6 +29,15 @@ Run `npm run test:e2e` to execute the end-to-end tests.
29
29
 
30
30
  Run `npm run lint` to lint the project.
31
31
 
32
+ ## Styling
33
+
34
+ The design system is driven by the theme file `src/styles/default.theme.json`: tokens under `shared` (colours,
35
+ typography, sizing) apply to every mode, while the `modes` section declares the available modes (`light` and `dark` by
36
+ default). `src/styles/active-theme.ts` loads the theme and is used by the layout components to inject the theme styles,
37
+ apply the initial mode, and decide whether the header shows the light/dark toggle. The toggle appears when the theme
38
+ declares more than one mode (set `showModeToggle` in the theme file to override this), cycles through the declared
39
+ modes, and the selected mode is remembered across reloads.
40
+
32
41
  ## NPM Scripts
33
42
 
34
43
  - `npm run baseline`: Cleans the project and installs dependencies.
@@ -43,7 +52,6 @@ Run `npm run lint` to lint the project.
43
52
  - `npm run dev:no-open`: Serves the project in development mode without automatically opening the browser.
44
53
  - `npm run dev:intellij`: Serves the project in development mode (specific for IntelliJ).
45
54
  - `npm run dev:https`: Serves the project in development mode with HTTPS.
46
- - `npm run dsconfig`: Configures design system based on `src/styles/design-tokens.json`.
47
55
  - `npm run lint`: Lints the project using the specified profile.
48
56
  - `npm run lint:fix`: Fixes linting errors in the project.
49
57
  - `npm run lint:eslint`: Lints the project using ESLint and the specified profile.
@@ -20,7 +20,6 @@
20
20
  "dev:no-open": "NO_OPEN=true npm run dev",
21
21
  "dev:intellij": "npm run dev",
22
22
  "dev:https": "HTTPS=true npm run dev",
23
- "dsconfig": "dsconfig --path src/styles/design-tokens.json",
24
23
  "lint": "genx lint --profile",
25
24
  "lint:fix": "genx lint --fix",
26
25
  "lint:eslint": "genx lint -l eslint --profile",
@@ -1,7 +1,11 @@
1
1
  import React, { ReactNode, useEffect, useRef } from 'react';
2
- import { configureDesignSystem } from '@genesislcap/foundation-ui';
2
+ import {
3
+ applyMode,
4
+ injectThemeStyles,
5
+ resolveInitialMode,
6
+ } from '@genesislcap/rapid-design-system';
3
7
  import styles from './BlankLayout.module.css';
4
- import * as designTokens from '../../styles/design-tokens.json';
8
+ import { activeTheme } from '../../styles/active-theme';
5
9
 
6
10
  interface BlankLayoutProps {
7
11
  children: ReactNode;
@@ -12,12 +16,13 @@ const BlankLayout: React.FC<BlankLayoutProps> = ({ children }) =>{
12
16
 
13
17
  useEffect(() => {
14
18
  if (designSystemProviderRef.current) {
15
- configureDesignSystem(designSystemProviderRef.current, designTokens);
19
+ injectThemeStyles(designSystemProviderRef.current, activeTheme);
20
+ applyMode(designSystemProviderRef.current, activeTheme, resolveInitialMode(activeTheme));
16
21
  }
17
22
  }, []);
18
23
 
19
24
  return (
20
- <rapid-design-system-provider className={styles['blank-layout']}>
25
+ <rapid-design-system-provider ref={designSystemProviderRef} className={styles['blank-layout']}>
21
26
  <section className={styles.content}>{children}</section>
22
27
  </rapid-design-system-provider>
23
28
  );
@@ -1,11 +1,16 @@
1
1
  import React, { useEffect, useRef, useSyncExternalStore } from 'react';
2
2
  import { RouteObject, useNavigate, Outlet } from 'react-router-dom';
3
- import { configureDesignSystem, getNavItems } from '@genesislcap/foundation-ui';
4
- import { baseLayerLuminance, StandardLuminance } from '@microsoft/fast-components';
3
+ import { getNavItems } from '@genesislcap/foundation-ui';
4
+ import {
5
+ applyMode,
6
+ injectThemeStyles,
7
+ nextMode,
8
+ resolveInitialMode,
9
+ } from '@genesislcap/rapid-design-system';
5
10
  import styles from './DefaultLayout.module.css';
6
11
  import PBCElementsRenderer from '../../pbc/elementsRenderer';
7
12
  import { registerStylesTarget } from '../../pbc/utils';
8
- import * as designTokens from '../../styles/design-tokens.json';
13
+ import { activeTheme, modeToggleEnabled } from '../../styles/active-theme';
9
14
  import { useRoutesContext } from '../../store/RoutesContext';
10
15
  import { useDocumentTitle } from '../../utils/useDocumentTitle';
11
16
  import { LOGOUT_URL } from '@genesislcap/foundation-utils';
@@ -41,6 +46,7 @@ const getSnapshot = () => connect.isConnected;
41
46
  const DefaultLayout: React.FC<DefaultLayoutProps> = () => {
42
47
  const navigate = useNavigate();
43
48
  const designSystemProviderRef = useRef<HTMLElement>(null);
49
+ const themeModeRef = useRef<string>(resolveInitialMode(activeTheme));
44
50
  const routes = useRoutesContext() as ExtendedRouteObject[];
45
51
  const navItems = getNavItems(
46
52
  routes.flatMap((route) => ({
@@ -53,13 +59,8 @@ const DefaultLayout: React.FC<DefaultLayoutProps> = () => {
53
59
 
54
60
  const onLuminanceToggle = (): void => {
55
61
  if (designSystemProviderRef.current) {
56
- baseLayerLuminance.setValueFor(
57
- designSystemProviderRef.current,
58
- baseLayerLuminance.getValueFor(designSystemProviderRef.current) ===
59
- StandardLuminance.DarkMode
60
- ? StandardLuminance.LightMode
61
- : StandardLuminance.DarkMode,
62
- );
62
+ themeModeRef.current = nextMode(activeTheme, themeModeRef.current);
63
+ applyMode(designSystemProviderRef.current, activeTheme, themeModeRef.current);
63
64
  }
64
65
  };
65
66
 
@@ -67,7 +68,9 @@ const DefaultLayout: React.FC<DefaultLayoutProps> = () => {
67
68
 
68
69
  useEffect(() => {
69
70
  if (designSystemProviderRef.current) {
70
- configureDesignSystem(designSystemProviderRef.current, designTokens);
71
+ injectThemeStyles(designSystemProviderRef.current, activeTheme);
72
+ themeModeRef.current = resolveInitialMode(activeTheme);
73
+ applyMode(designSystemProviderRef.current, activeTheme, themeModeRef.current);
71
74
  registerStylesTarget(document.body, 'layout');
72
75
  registerStylesTarget(document.body, 'header');
73
76
  registerStylesTarget(document.body, 'content');
@@ -90,7 +93,7 @@ const DefaultLayout: React.FC<DefaultLayoutProps> = () => {
90
93
  await fetch(LOGOUT_URL);
91
94
  window.location.reload();
92
95
  }}
93
- show-luminance-toggle-button
96
+ show-luminance-toggle-button={modeToggleEnabled}
94
97
  show-misc-toggle-button
95
98
  routeNavItems={navItems}
96
99
  navigateTo={(path: string) => navigate(path)}
@@ -0,0 +1,6 @@
1
+ import { isModeToggleEnabled, toModalTheme } from '@genesislcap/rapid-design-system';
2
+ import themeJson from './default.theme.json';
3
+
4
+ export const activeTheme = toModalTheme(themeJson);
5
+
6
+ export const modeToggleEnabled = isModeToggleEnabled(activeTheme);
@@ -0,0 +1,37 @@
1
+ {
2
+ "id": "default",
3
+ "colorStrategy": "adaptive",
4
+ "defaultMode": "dark",
5
+ "shared": {
6
+ "design_tokens": {
7
+ "fontFamily": {
8
+ "bodyFont": {
9
+ "$value": "{{#if fontFamily}}{{fontFamily}}, {{/if}}Roboto, \"Segoe UI\", Arial, Helvetica, sans-serif",
10
+ "$type": "fontFamily"
11
+ }
12
+ }
13
+ }
14
+ },
15
+ "modes": {
16
+ "light": {
17
+ "design_tokens": {
18
+ "mode": {
19
+ "luminance": {
20
+ "$value": 1,
21
+ "$type": "number"
22
+ }
23
+ }
24
+ }
25
+ },
26
+ "dark": {
27
+ "design_tokens": {
28
+ "mode": {
29
+ "luminance": {
30
+ "$value": 0.23,
31
+ "$type": "number"
32
+ }
33
+ }
34
+ }
35
+ }
36
+ }
37
+ }
@@ -47,15 +47,16 @@ npm run baseline
47
47
  ## Styling
48
48
 
49
49
  Genesis components are registered with a Design System,
50
- and the default system is named `rapid`. Design systems are highly configurable. You can configure them by importing and
51
- modifying the tokens directly, or by using our [Design System Configuration](https://learn.genesis.global/docs/develop/client-capabilities/styling/direct-customization-genesis-components/styling-direct-dsc/)
52
- tool in the context of your application.
50
+ and the default system is named `rapid`. The design system is driven by the theme file `src/styles/default.theme.json`:
51
+ tokens under `shared` (colours, typography, sizing) apply to every mode, while the `modes` section declares the
52
+ available modes (`light` and `dark` by default) with their per-mode tokens.
53
53
 
54
- Simply run the application, configure the design system, select Save Changes, and hit Save on popup, then restart your application.
54
+ `src/styles/active-theme.ts` loads the theme file and exposes it as the app's active theme. It is the single source of
55
+ truth used to inject the theme styles and apply the initial mode at startup (`src/main/main.ts`), and to decide whether
56
+ the header shows the light/dark toggle (`src/layouts/default.ts`).
55
57
 
56
- ```shell
57
- npm run dsconfig
58
- ```
58
+ The toggle appears automatically when the theme declares more than one mode (set `showModeToggle` in the theme file to
59
+ override this). Clicking it cycles through the declared modes, and the selected mode is remembered across reloads.
59
60
 
60
61
  ## Custom Elements IntelliSense
61
62
 
@@ -35,7 +35,6 @@
35
35
  "dev:https": "npm run dev -- -- --https",
36
36
  "dev:vite": "genx dev -e API_HOST,ENABLE_SSO -b vite",
37
37
  "dev:webpack": "genx dev -e API_HOST,ENABLE_SSO -b webpack",
38
- "dsconfig": "dsconfig --path src/styles/design-tokens.json",
39
38
  "git:setup": "cd .. && npx --yes husky install",
40
39
  "lint": "genx lint --profile",
41
40
  "lint:fix": "genx lint --fix",
@@ -2,6 +2,7 @@ import { getApp } from '@genesislcap/foundation-shell/app';
2
2
  import type { FoundationRouter } from '@genesislcap/foundation-ui';
3
3
  import { css, GenesisElementLayout, html } from '@genesislcap/web-core';
4
4
  import type { Store } from '../store/foundation-store';
5
+ import { modeToggleEnabled } from '../styles/active-theme';
5
6
 
6
7
  type ClientAppRouter = FoundationRouter & { store: Store };
7
8
 
@@ -43,7 +44,7 @@ export const defaultLayout = new GenesisElementLayout(
43
44
  {{#if headerLogoSrc}}
44
45
  logo-src="{{headerLogoSrc}}"
45
46
  {{/if}}
46
- show-luminance-toggle-button
47
+ ?show-luminance-toggle-button=${() => modeToggleEnabled}
47
48
  show-misc-toggle-button
48
49
  :routeNavItems=${(x) => x.config.getNavItems()}
49
50
  ></foundation-header>
@@ -2,9 +2,13 @@ import { Connect, ConnectConfig, defaultConnectConfig } from '@genesislcap/found
2
2
  import { EventEmitter } from '@genesislcap/foundation-events';
3
3
  import { App } from '@genesislcap/foundation-shell/app';
4
4
  import { importPBCAssets } from '@genesislcap/foundation-shell/pbc';
5
- import { configureDesignSystem } from '@genesislcap/foundation-ui';
6
5
  import {
7
- baseLayerLuminance,
6
+ applyMode,
7
+ injectThemeStyles,
8
+ nextMode,
9
+ resolveInitialMode,
10
+ } from '@genesislcap/rapid-design-system';
11
+ import {
8
12
  customElement,
9
13
  Container,
10
14
  DefaultRouteRecognizer,
@@ -13,12 +17,11 @@ import {
13
17
  inject,
14
18
  observable,
15
19
  Registration,
16
- StandardLuminance,
17
20
  } from '@genesislcap/web-core';
18
21
  import * as Components from '../components';
19
22
  import { MainRouterConfig } from '../routes';
20
23
  import { Store, StoreEventDetailMap } from '../store/foundation-store';
21
- import designTokens from '../styles/design-tokens.json';
24
+ import { activeTheme } from '../styles/active-theme';
22
25
  {{#if FDC3.channels.length}}
23
26
  import { listenToChannel, onFDC3Ready } from '../utils';
24
27
  {{/if}}
@@ -48,6 +51,8 @@ export class MainApplication extends EventEmitter<StoreEventDetailMap>(GenesisEl
48
51
  @observable ready: boolean = false;
49
52
  @observable data: any = null;
50
53
 
54
+ private themeMode: string = resolveInitialMode(activeTheme);
55
+
51
56
  async connectedCallback() {
52
57
  this.registerDIDependencies();
53
58
  super.connectedCallback();
@@ -59,7 +64,9 @@ export class MainApplication extends EventEmitter<StoreEventDetailMap>(GenesisEl
59
64
  onFDC3Ready(this.FDC3ReadyHandler);
60
65
  {{/if}}
61
66
  DOM.queueUpdate(() => {
62
- configureDesignSystem(this.provider, designTokens);
67
+ injectThemeStyles(this.provider, activeTheme);
68
+ this.themeMode = resolveInitialMode(activeTheme);
69
+ applyMode(this.provider, activeTheme, this.themeMode);
63
70
  });
64
71
  }
65
72
 
@@ -70,12 +77,8 @@ export class MainApplication extends EventEmitter<StoreEventDetailMap>(GenesisEl
70
77
  }
71
78
 
72
79
  onDarkModeToggle() {
73
- baseLayerLuminance.setValueFor(
74
- this.provider,
75
- baseLayerLuminance.getValueFor(this.provider) === StandardLuminance.DarkMode
76
- ? StandardLuminance.LightMode
77
- : StandardLuminance.DarkMode,
78
- );
80
+ this.themeMode = nextMode(activeTheme, this.themeMode);
81
+ applyMode(this.provider, activeTheme, this.themeMode);
79
82
  }
80
83
 
81
84
  async loadPBCs() {
@@ -0,0 +1,6 @@
1
+ import { isModeToggleEnabled, toModalTheme } from '@genesislcap/rapid-design-system';
2
+ import themeJson from './default.theme.json';
3
+
4
+ export const activeTheme = toModalTheme(themeJson);
5
+
6
+ export const modeToggleEnabled = isModeToggleEnabled(activeTheme);
@@ -0,0 +1,37 @@
1
+ {
2
+ "id": "default",
3
+ "colorStrategy": "adaptive",
4
+ "defaultMode": "dark",
5
+ "shared": {
6
+ "design_tokens": {
7
+ "fontFamily": {
8
+ "bodyFont": {
9
+ "$value": "{{#if fontFamily}}{{fontFamily}}, {{/if}}Roboto, \"Segoe UI\", Arial, Helvetica, sans-serif",
10
+ "$type": "fontFamily"
11
+ }
12
+ }
13
+ }
14
+ },
15
+ "modes": {
16
+ "light": {
17
+ "design_tokens": {
18
+ "mode": {
19
+ "luminance": {
20
+ "$value": 1,
21
+ "$type": "number"
22
+ }
23
+ }
24
+ }
25
+ },
26
+ "dark": {
27
+ "design_tokens": {
28
+ "mode": {
29
+ "luminance": {
30
+ "$value": 0.23,
31
+ "$type": "number"
32
+ }
33
+ }
34
+ }
35
+ }
36
+ }
37
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@genesislcap/blank-app-seed",
3
3
  "description": "Genesis Blank App Seed",
4
- "version": "5.18.0",
4
+ "version": "5.19.0",
5
5
  "license": "Apache-2.0",
6
6
  "scripts": {
7
7
  "release": "semantic-release"
@@ -1,60 +0,0 @@
1
- {
2
- "design_tokens": {
3
- "color": {
4
- "accent": {
5
- "$value": "#0EAFE2",
6
- "$type": "color"
7
- },
8
- "neutral": {
9
- "$value": "#7C909B",
10
- "$type": "color"
11
- }
12
- },
13
- "fontFamily": {
14
- "bodyFont": {
15
- "$value": "{{#if fontFamily}}{{fontFamily}}, {{/if}}Roboto, \"Segoe UI\", Arial, Helvetica, sans-serif",
16
- "$type": "fontFamily"
17
- }
18
- },
19
- "typography": {
20
- "baseFontSize": {
21
- "$value": "14px",
22
- "$type": "dimension"
23
- },
24
- "baseLineHeight": {
25
- "$value": "20px",
26
- "$type": "dimension"
27
- }
28
- },
29
- "mode": {
30
- "luminance": {
31
- "$value": 0.23,
32
- "$type": "number"
33
- }
34
- },
35
- "style": {
36
- "density": {
37
- "$value": 0,
38
- "$type": "number"
39
- },
40
- "baseHeightMultiplier": {
41
- "$value": 10,
42
- "$type": "number"
43
- },
44
- "borderRadius": {
45
- "$value": 4,
46
- "$type": "number"
47
- },
48
- "strokeWidth": {
49
- "$value": 1,
50
- "$type": "number"
51
- }
52
- },
53
- "space": {
54
- "designUnit": {
55
- "$value": 4,
56
- "$type": "number"
57
- }
58
- }
59
- }
60
- }
@@ -1,60 +0,0 @@
1
- {
2
- "design_tokens": {
3
- "color": {
4
- "accent": {
5
- "$value": "#0EAFE2",
6
- "$type": "color"
7
- },
8
- "neutral": {
9
- "$value": "#7C909B",
10
- "$type": "color"
11
- }
12
- },
13
- "fontFamily": {
14
- "bodyFont": {
15
- "$value": "{{#if fontFamily}}{{fontFamily}}, {{/if}}Roboto, \"Segoe UI\", Arial, Helvetica, sans-serif",
16
- "$type": "fontFamily"
17
- }
18
- },
19
- "typography": {
20
- "baseFontSize": {
21
- "$value": "14px",
22
- "$type": "dimension"
23
- },
24
- "baseLineHeight": {
25
- "$value": "20px",
26
- "$type": "dimension"
27
- }
28
- },
29
- "mode": {
30
- "luminance": {
31
- "$value": 0.23,
32
- "$type": "number"
33
- }
34
- },
35
- "style": {
36
- "density": {
37
- "$value": 0,
38
- "$type": "number"
39
- },
40
- "baseHeightMultiplier": {
41
- "$value": 10,
42
- "$type": "number"
43
- },
44
- "borderRadius": {
45
- "$value": 4,
46
- "$type": "number"
47
- },
48
- "strokeWidth": {
49
- "$value": 1,
50
- "$type": "number"
51
- }
52
- },
53
- "space": {
54
- "designUnit": {
55
- "$value": 4,
56
- "$type": "number"
57
- }
58
- }
59
- }
60
- }
@@ -1,60 +0,0 @@
1
- {
2
- "design_tokens": {
3
- "color": {
4
- "accent": {
5
- "$value": "#0EAFE2",
6
- "$type": "color"
7
- },
8
- "neutral": {
9
- "$value": "#7C909B",
10
- "$type": "color"
11
- }
12
- },
13
- "fontFamily": {
14
- "bodyFont": {
15
- "$value": "{{#if fontFamily}}{{fontFamily}}, {{/if}}Roboto, \"Segoe UI\", Arial, Helvetica, sans-serif",
16
- "$type": "fontFamily"
17
- }
18
- },
19
- "typography": {
20
- "baseFontSize": {
21
- "$value": "14px",
22
- "$type": "dimension"
23
- },
24
- "baseLineHeight": {
25
- "$value": "20px",
26
- "$type": "dimension"
27
- }
28
- },
29
- "mode": {
30
- "luminance": {
31
- "$value": 0.23,
32
- "$type": "number"
33
- }
34
- },
35
- "style": {
36
- "density": {
37
- "$value": 0,
38
- "$type": "number"
39
- },
40
- "baseHeightMultiplier": {
41
- "$value": 10,
42
- "$type": "number"
43
- },
44
- "borderRadius": {
45
- "$value": 4,
46
- "$type": "number"
47
- },
48
- "strokeWidth": {
49
- "$value": 1,
50
- "$type": "number"
51
- }
52
- },
53
- "space": {
54
- "designUnit": {
55
- "$value": 4,
56
- "$type": "number"
57
- }
58
- }
59
- }
60
- }