Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
88b2940
added `Hds::ThemeSwitcher` component
didoo Sep 30, 2025
3d408da
added `Hds::Theming` service
didoo Sep 30, 2025
fbb16f8
added theming to the Showcase itself (and replaced hardcoded values w…
didoo Sep 30, 2025
d7e3e92
added `Shw::ThemeSwitcher` component for showcase
didoo Sep 30, 2025
de0984e
updated `Mock::App` and added new yielded sub-components
didoo Sep 30, 2025
6a31cff
added `Shw:: ThemeSwitcher` to the Showcase page header
didoo Sep 30, 2025
57799ef
added `foundations/theming` showcase page (and a frameless demo)
didoo Sep 30, 2025
bed93e9
refactored `hds-theming` service to align with the new themes/modes a…
didoo Oct 1, 2025
cc65171
added `hdsTheming` initialization to main showcase app
didoo Oct 1, 2025
359e934
removed compilation of components Scss and replaced it with static in…
didoo Oct 3, 2025
2788299
added theming options via popover - part 1
didoo Oct 3, 2025
5d04eba
added theming options via popover - part 2
didoo Oct 3, 2025
719411a
added theming options via popover - part 3
didoo Oct 3, 2025
b0a2218
added theming options via popover - part 4
didoo Oct 4, 2025
fb62738
added theming options via popover - part 5
didoo Oct 6, 2025
98ac8d4
big code refactoring for the theme selector, to streamline user selec…
didoo Oct 6, 2025
55c4841
updated logic that sets the theming for the showcase itself (without …
didoo Oct 7, 2025
90ec51f
small fixes here and there for cleanup and linting
didoo Oct 10, 2025
f5e040a
fixed issue with `pnpm lint:format` (missing newline at the end of `p…
didoo Oct 10, 2025
86b998b
fixed accessibility issue in `advanced-table` page, due to changes to…
didoo Oct 10, 2025
8b9d6ff
fixed typescript error due to new mock page being added
didoo Oct 10, 2025
c9dfdf9
added fix for tests failing
didoo Oct 13, 2025
fd21465
started large refactoring/rewrite of the theming switcher and page in…
didoo Oct 14, 2025
40563cf
updated logic by creating a `shwTheming` service that extends `hdsThe…
didoo Oct 15, 2025
78e96b0
moved theming logic from `ShwThemeSwitcher` component/subcomponents t…
didoo Oct 15, 2025
d12eb32
updated reference CSS files to follow new theming approach/logic
didoo Oct 16, 2025
2766694
further refactoring/rewriting of theming logic
didoo Oct 16, 2025
040810b
updated approach to `light/dark` styles in showcase by using the HDS …
didoo Oct 17, 2025
deaa58d
migrated back the `Contextual` demo content to the index page
didoo Oct 17, 2025
b6e5be6
added a `DebuggingPanel` to the `ShwThemeSwitcher` controls
didoo Oct 17, 2025
dc64ab6
refactored/improved `DebuggingPanel` and added new preferences to adv…
didoo Oct 17, 2025
254d913
small cleanups and refactorings
didoo Oct 17, 2025
7575e9a
fixed small issue with `ShwThemeSwitcher` selector
didoo Oct 20, 2025
65cb852
removed some outdated comments
didoo Oct 20, 2025
7affe21
small refactorings
didoo Oct 20, 2025
e26eb15
added local storage support for theming options
didoo Oct 20, 2025
8f39d2b
big refactoring of the `hdsTheming` service to simplify logic and red…
didoo Oct 20, 2025
14e8fa5
cleanup of debugging comments and other stuff
didoo Oct 20, 2025
b4f25ae
refactor and cleanup in preparation for PR review
didoo Oct 21, 2025
11475f2
refactored code to fix logic flow for theming initialization
didoo Oct 21, 2025
8ae3979
fixed how the theming options were saved in local storage
didoo Oct 21, 2025
fe848d5
Apply suggestions from Copilot's code review
didoo Oct 21, 2025
633627a
fixed issue with `setTheme` not being passed `options` by the `ShwThe…
didoo Oct 22, 2025
b94de3a
fix issue with the popover of the ShwThemeSwitcher component, where t…
didoo Oct 22, 2025
d1d4c5d
Update showcase/app/services/shw-theming.ts
didoo Oct 23, 2025
b89cab8
small tweak to the typing of `HdsModes` per code review suggestion
didoo Oct 23, 2025
6078feb
updated how the CSS files (tokens and components, with/without themin…
didoo Oct 24, 2025
e271652
updated logic for styleshet switching using the `disabled` attribute
didoo Oct 24, 2025
852bf16
removed unused import
didoo Oct 24, 2025
0526e49
removed `isInitialized` logic from the `hdeTheming` service to avoid bug
didoo Oct 24, 2025
f6d2ca9
TEMP - added temporary `hds-theme-light/dark` selectors
didoo Oct 24, 2025
ebe0f95
TEMP - Added back custom token values for testing purpose
didoo Oct 24, 2025
f680549
fixed small issue with the `HdsThemeSwitcher` components used in the …
didoo Oct 29, 2025
a012d1d
added small comment
didoo Oct 29, 2025
a0fc423
updated `hdsTheming` service to support `default` theme / mode
didoo Nov 7, 2025
edd9898
added support for `default` option to HDS ThemeSwitcher component
didoo Nov 7, 2025
e614486
Updated β€œfoundations/theming” showcase page to include variants of HD…
didoo Nov 7, 2025
451b508
updated Showcase ThemeSwitcher to use ``default` as theme option
didoo Nov 7, 2025
54cd4f4
updated logic for how CSS selectors are applied to the `html:root` el…
didoo Nov 7, 2025
623fcde
updated CSS selectors for local `public/assets` themed tokens CSS fil…
didoo Nov 7, 2025
91b0e34
added fake theming for display/text typography to `public/assets` the…
didoo Nov 7, 2025
a470cd8
updated β€œfoundations/theming” showcase page to include `default` and …
didoo Nov 7, 2025
46391f1
implemented `ThemeContex` component
didoo Nov 13, 2025
8bee0d9
added demos of `ThemeContext` component to showcase β€œfoundations/them…
didoo Nov 13, 2025
166ac3d
improved type definitions for ThemeContext and showcase β€œfoundations/…
didoo Nov 13, 2025
a3e69bc
refactored how CSS selectors are applied to enable mode-specific over…
didoo Nov 14, 2025
47859c6
updated CSS selectors for local `public/assets` themed tokens CSS fil…
didoo Nov 14, 2025
c9fd7ec
updated `ThemeContext` to use a more generic `context` argument
didoo Nov 14, 2025
fea2372
updated showcase β€œtheming” page to use the new `@context` argument fo…
didoo Nov 14, 2025
5450cb7
finally defined proper typing for `ThemeContext` component
didoo Nov 14, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,8 @@
"./components/hds/text/code.js": "./dist/_app_/components/hds/text/code.js",
"./components/hds/text/display.js": "./dist/_app_/components/hds/text/display.js",
"./components/hds/text.js": "./dist/_app_/components/hds/text.js",
"./components/hds/theme-context.js": "./dist/_app_/components/hds/theme-context.js",
"./components/hds/theme-switcher.js": "./dist/_app_/components/hds/theme-switcher.js",
"./components/hds/time.js": "./dist/_app_/components/hds/time.js",
"./components/hds/time/range.js": "./dist/_app_/components/hds/time/range.js",
"./components/hds/time/single.js": "./dist/_app_/components/hds/time/single.js",
Expand Down Expand Up @@ -397,6 +399,7 @@
"./modifiers/hds-register-event.js": "./dist/_app_/modifiers/hds-register-event.js",
"./modifiers/hds-tooltip.js": "./dist/_app_/modifiers/hds-tooltip.js",
"./services/hds-intl.js": "./dist/_app_/services/hds-intl.js",
"./services/hds-theming.js": "./dist/_app_/services/hds-theming.js",
"./services/hds-time.js": "./dist/_app_/services/hds-time.js"
}
},
Expand Down
6 changes: 6 additions & 0 deletions packages/components/src/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,12 @@ export { default as HdsTextCode } from './components/hds/text/code.ts';
export { default as HdsTextDisplay } from './components/hds/text/display.ts';
export * from './components/hds/text/types.ts';

// Theme Context
export { default as HdsThemeContext } from './components/hds/theme-context/index.ts';

// Theme Switcher
export { default as HdsThemeSwitcher } from './components/hds/theme-switcher/index.ts';

// Time
export { default as HdsTime } from './components/hds/time/index.ts';
export { default as HdsTimeSingle } from './components/hds/time/single.ts';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{{!
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: MPL-2.0
}}
<div class={{this.classNames}} ...attributes>{{yield}}</div>
67 changes: 67 additions & 0 deletions packages/components/src/components/hds/theme-context/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: MPL-2.0
*/

import Component from '@glimmer/component';
import { assert } from '@ember/debug';
import type Owner from '@ember/owner';

import {
HdsThemeContextThemesValues,
HdsThemeContextModesValues,
} from './types.ts';
import type { HdsThemeContexts } from './types.ts';

export interface HdsThemeContextSignature {
Args: {
// it can be an `HdsTheme` or an `HdsMode`
context: HdsThemeContexts;
};
Blocks: {
default: [];
};
Element: HTMLElement;
}

export const CONTEXTUAL_THEMES: string[] = Object.values(
HdsThemeContextThemesValues
);
export const CONTEXTUAL_MODES: string[] = Object.values(
HdsThemeContextModesValues
);
export const CONTEXTUAL_VALUES: string[] = [
...CONTEXTUAL_THEMES,
...CONTEXTUAL_MODES,
];

export default class HdsThemeContext extends Component<HdsThemeContextSignature> {
constructor(owner: Owner, args: HdsThemeContextSignature['Args']) {
super(owner, args);

const { context } = args;

assert(
`@context for "Hds::ThemeContext" must be one of the following: ${CONTEXTUAL_VALUES.join(
', '
)}; received: ${context}`,
CONTEXTUAL_VALUES.includes(context)
);
}

// Get the class names to apply to the component.
get classNames(): string {
const classes = ['hds-theme-context'];

const { context } = this.args;

// add "theme" or "mode" classes based on the @context arguments
if (CONTEXTUAL_THEMES.includes(context)) {
classes.push(`hds-theme-${context}`);
} else if (CONTEXTUAL_MODES.includes(context)) {
classes.push(`hds-mode-${context}`);
}

return classes.join(' ');
}
}
35 changes: 35 additions & 0 deletions packages/components/src/components/hds/theme-context/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: MPL-2.0
*/

import {
HdsThemeValues,
HdsModesLightValues,
HdsModesDarkValues,
} from '../../../services/hds-theming.ts';

import type {
HdsThemes,
HdsModes,
} from '../../../services/hds-theming.ts';

// re-export the enum values for the `HdsThemes` to use in the component
// note: using `as const` ensures Object.values() returns only the values (not keys _and_ values)
export const HdsThemeContextThemesValues = {
Default: HdsThemeValues.Default,
System: HdsThemeValues.System,
Light: HdsThemeValues.Light,
Dark: HdsThemeValues.Dark,
} as const;

// re-export the enum values for the `HdsModes` to use in the component
// note: using `as const` ensures Object.values() returns only the values (not keys _and_ values)
export const HdsThemeContextModesValues = {
CdsG0: HdsModesLightValues.CdsG0,
CdsG10: HdsModesLightValues.CdsG10,
CdsG90: HdsModesDarkValues.CdsG90,
CdsG100: HdsModesDarkValues.CdsG100,
} as const;

export type HdsThemeContexts = HdsThemes | HdsModes;
29 changes: 29 additions & 0 deletions packages/components/src/components/hds/theme-switcher/index.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{{!
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: MPL-2.0
}}

{{!
------------------------------------------------------------------------------------------
IMPORTANT: this is a temporary implementation, while we wait for the design specifications
------------------------------------------------------------------------------------------
}}

<Hds::Dropdown
@enableCollisionDetection={{true}}
@matchToggleWidth={{@toggleIsFullWidth}}
class="hds-theme-switcher-control"
...attributes
as |D|
>
<D.ToggleButton
@color="secondary"
@size={{this.toggleSize}}
@isFullWidth={{this.toggleIsFullWidth}}
@text={{this.toggleContent.label}}
@icon={{this.toggleContent.icon}}
/>
{{#each-in this._options as |key data|}}
<D.Interactive @icon={{data.icon}} {{on "click" (fn this.onSelectTheme data.theme)}}>{{data.label}}</D.Interactive>
{{/each-in}}
</Hds::Dropdown>
108 changes: 108 additions & 0 deletions packages/components/src/components/hds/theme-switcher/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: MPL-2.0
*/

// ------------------------------------------------------------------------------------------
// IMPORTANT: this is a temporary implementation, while we wait for the design specifications
// ------------------------------------------------------------------------------------------

import Component from '@glimmer/component';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';

import type { HdsDropdownSignature } from '../dropdown/index.ts';
import type { HdsDropdownToggleButtonSignature } from '../dropdown/toggle/button.ts';
import type { HdsIconSignature } from '../icon/index.ts';
import type HdsThemingService from '../../../services/hds-theming.ts';
import type {
HdsThemes,
OnSetThemeCallback,
} from '../../../services/hds-theming.ts';

interface ThemeOption {
theme: HdsThemes | undefined;
icon: HdsIconSignature['Args']['name'];
label: string;
}

const OPTIONS: Record<HdsThemes, ThemeOption> = {
default: { theme: 'default', icon: 'hashicorp', label: 'Default' },
system: { theme: 'system', icon: 'monitor', label: 'System' },
light: { theme: 'light', icon: 'sun', label: 'Light' },
dark: { theme: 'dark', icon: 'moon', label: 'Dark' },
};

interface HdsThemeSwitcherSignature {
Args: {
toggleSize?: HdsDropdownToggleButtonSignature['Args']['size'];
toggleIsFullWidth?: HdsDropdownToggleButtonSignature['Args']['isFullWidth'];
hasDefaultOption?: boolean;
hasSystemOption?: boolean;
onSetTheme?: OnSetThemeCallback;
};
Element: HdsDropdownSignature['Element'];
}

export default class HdsThemeSwitcher extends Component<HdsThemeSwitcherSignature> {
@service declare readonly hdsTheming: HdsThemingService;

get toggleSize() {
return this.args.toggleSize ?? 'small';
}

get toggleIsFullWidth() {
return this.args.toggleIsFullWidth ?? false;
}

get toggleContent() {
if (
(this.currentTheme === 'default' && this.hasDefaultOption) ||
(this.currentTheme === 'system' && this.hasSystemOption) ||
this.currentTheme === 'light' ||
this.currentTheme === 'dark'
) {
return {
label: OPTIONS[this.currentTheme].label,
icon: OPTIONS[this.currentTheme].icon,
};
} else {
return { label: 'Theme', icon: undefined };
}
}

// note: we will use the `default` option in development, while migrating to the `cds` theming
// during this process, consumers will enable/disable this option via code logic or feature flag
get hasDefaultOption() {
return this.args.hasDefaultOption ?? false;
}

get hasSystemOption() {
return this.args.hasSystemOption ?? true;
}

get _options() {
const options: Partial<typeof OPTIONS> = { ...OPTIONS };

if (!this.hasDefaultOption) {
delete options.default;
}

if (!this.hasSystemOption) {
delete options.system;
}

return options;
}

get currentTheme() {
// we get the theme from the global service
return this.hdsTheming.currentTheme;
}

@action
onSelectTheme(theme: HdsThemes | undefined): void {
// we set the theme in the global service (and provide an optional user-defined callback)
this.hdsTheming.setTheme({ theme, onSetTheme: this.args.onSetTheme });
}
}
2 changes: 2 additions & 0 deletions packages/components/src/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@
*/

// This file is used to expose public services

export * from './services/hds-theming.ts';
Loading
Loading