Skip to content
This repository was archived by the owner on Jan 6, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
d32ce59
Adjust applyTheme util function logic to fix button theming bug when …
hawkticehurst Nov 2, 2021
0ff00c2
Merge branch 'main' into high-contrast-theme
hawkticehurst Nov 2, 2021
6b924ae
Merge branch 'main' into high-contrast-theme
hawkticehurst Jan 19, 2022
34aeedc
Add prettier-ignore command which fixes a bug where base button focus…
hawkticehurst Jan 20, 2022
ddd0665
Fix comment typo
hawkticehurst Jan 20, 2022
7c541ef
Format file
hawkticehurst Jan 20, 2022
86aea49
Clean up setup webview test env script a bit
hawkticehurst Jan 20, 2022
46f0888
Revamp apply theme utilities to handle high contrast theme styling ed…
hawkticehurst Jan 21, 2022
15194d9
Switch out design tokens to address a themeing bug in the data grid r…
hawkticehurst Jan 21, 2022
a3894c4
Add todo comment
hawkticehurst Jan 25, 2022
324cba8
Merge changes from main
hawkticehurst Feb 1, 2022
636fc1f
Update data grid styles to include a hover outline in high contrast mode
hawkticehurst Feb 2, 2022
9380aec
Update icon button styles to fix a high contrast theme bug
hawkticehurst Feb 2, 2022
d88ada0
Create a new design token to help with fixing the icon button high co…
hawkticehurst Feb 2, 2022
7aa21ca
Update applyTheme and create token logic in order fix high contrast t…
hawkticehurst Feb 2, 2022
7a6025f
Update dev comment in design tokens file
hawkticehurst Feb 2, 2022
c5d7e23
Update code comments
hawkticehurst Feb 2, 2022
3cf3364
Update code comments
hawkticehurst Feb 2, 2022
d439a98
Fix test script sample extension install path
hawkticehurst Feb 3, 2022
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
2 changes: 1 addition & 1 deletion scripts/setup-webview-test-env.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ async function main() {
if (!fs.existsSync('./test-webview')) {
try {
console.log(color(['dim'], 'Copying webview test environment locally...'));
await execShellCommand('npx degit microsoft/vscode-webview-ui-toolkit-samples/all-components test-webview');
await execShellCommand('npx degit microsoft/vscode-webview-ui-toolkit-samples/default/all-components test-webview');
} catch (err) {
console.log(`${color(['red'], 'Error: Could not copy webview test environment locally')}\n ${err}`);
process.exit();
Expand Down
22 changes: 21 additions & 1 deletion src/button/button.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
buttonSecondaryBackground,
buttonSecondaryForeground,
buttonSecondaryHoverBackground,
contrastActiveBorder,
cornerRadius,
designUnit,
disabledOpacity,
Expand All @@ -36,8 +37,24 @@ import {
} from '../design-tokens';

/**
* Developer note:
*
* The prettier-ignore command is used on this block of code because when removed the
* '.control:${focusVisible}' CSS selector will be automatically reformatted to
* '.control: ${focusVisible}' (note the space between the colon and dollar sign).
*
* This results in non-valid CSS that will not render a focus outline on base buttons.
*
* Additionally, this prettier command must be declared on the entire code block and not
* directly above the CSS selector line because the below code block is a template literal
* string which will end up being used directly in the final component CSS.
*
* Thus having '// prettier-ignore' directly in the final CSS will also break the component
* styling.
*
* @internal
*/
// prettier-ignore
const BaseButtonStyles = css`
${display('inline-flex')} :host {
outline: none;
Expand Down Expand Up @@ -76,7 +93,7 @@ const BaseButtonStyles = css`
:host(:active) {
background: ${buttonPrimaryBackground};
}
.control: ${focusVisible} {
.control:${focusVisible} {
outline: calc(${borderWidth} * 1px) solid ${focusBorder};
outline-offset: calc(${borderWidth} * 2px);
}
Expand Down Expand Up @@ -161,9 +178,12 @@ const IconButtonStyles = css`
}
:host([appearance='icon']:hover) {
background: ${buttonIconHoverBackground};
outline: 1px dotted ${contrastActiveBorder};
outline-offset: -1px;
}
:host([appearance='icon']) .control {
padding: ${buttonIconPadding};
border: calc(${borderWidth} * 1px) solid transparent;
}
:host([appearance='icon']:active) .control:active {
background: ${buttonIconHoverBackground};
Expand Down
7 changes: 5 additions & 2 deletions src/data-grid/data-grid-row.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ import {
FoundationElementDefinition,
} from '@microsoft/fast-foundation';
import {
background,
contrastActiveBorder,
designUnit,
listHoverBackground,
quickInputBackground,
} from '../design-tokens';

export const dataGridRowStyles = (
Expand All @@ -26,11 +27,13 @@ export const dataGridRowStyles = (
:host(.header) {
}
:host(.sticky-header) {
background: ${quickInputBackground};
background: ${background};
position: sticky;
top: 0;
}
:host(:hover) {
background: ${listHoverBackground};
outline: 1px dotted ${contrastActiveBorder};
outline-offset: -1px;
}
`;
22 changes: 20 additions & 2 deletions src/design-tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,28 @@

import {create} from './utilities/design-tokens/create';

/**
* Developer note:
*
* There are some tokens defined in this file that make use of `--fake-vscode-token`. This is
* done when a toolkit token should be added to the tokenMappings map (and subsequently altered
* in the applyTheme function) but does not have a corresponding VS Code token that can be used.
*
* An example is buttonIconHoverBackground token which does not have a corresponding VS Code token
* at this time (it's a hardcoded value in VS Code), but needs to be adjusted to be transparent when a
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be an issue filed on /vscode? If it's a simple enough change I can add it to my list

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't be against that! Would certainly simplify the toolkit theming code

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The big request is that this token should either be set to transparent or null (not exist) in high contrast themes

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Otherwise, its value seems to be rgba(90, 93, 94, 0.31) across all themes?

Copy link
Member Author

@hawkticehurst hawkticehurst Feb 3, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I think about it, this could actually be a really nice opportunity to customize things a bit because I think this value ends up resulting in a slightly less than ideal contrast ratio in light themes

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Screen Shot 2022-02-03 at 2 50 17 PM

* high contrast theme is applied.
*
* As a rule of thumb, if there are special cases where a token needs to be adjusted based on the
* VS Code theme and does not have a corresponding VS Code token, `--fake-vscode-token` can be used
* to indicate that it should be added to the tokenMappings map and thus make it accessible to the
* applyTheme function where it can be dynamically adjusted.
*/

/**
* Global design tokens.
*/

export const background = create<string>('background', '--vscode-editor-background').withDefault('#1e1e1e');
export const borderWidth = create<number>('border-width').withDefault(1);
export const contrastActiveBorder = create<string>('contrast-active-border', '--vscode-contrastActiveBorder').withDefault('#f38518');
export const contrastBorder = create<string>('contrast-border', '--vscode-contrastBorder').withDefault('#6fc3df');
Expand Down Expand Up @@ -46,7 +64,8 @@ export const buttonBorder = create<string>('button-border', '--vscode-button-bor
export const buttonIconBackground = create<string>('button-icon-background').withDefault('transparent');
export const buttonIconCornerRadius = create<string>('button-icon-corner-radius').withDefault('5px');
export const buttonIconFocusBorderOffset = create<number>('button-icon-outline-offset').withDefault(0);
export const buttonIconHoverBackground = create<string>('button-icon-hover-background').withDefault('rgba(90, 93, 94, 0.31)');
// Note usage of `--fake-vscode-token` (refer to doc comment at top of file for explanation).
export const buttonIconHoverBackground = create<string>('button-icon-hover-background', '--fake-vscode-token').withDefault('rgba(90, 93, 94, 0.31)');
export const buttonIconPadding = create<string>('button-icon-padding').withDefault('3px');
export const buttonPrimaryBackground = create<string>('button-primary-background', '--vscode-button-background').withDefault('#0e639c');
export const buttonPrimaryForeground = create<string>('button-primary-foreground', '--vscode-button-foreground').withDefault('#ffffff');
Expand All @@ -73,7 +92,6 @@ export const checkboxForeground = create<string>('checkbox-foreground', '--vscod
export const listActiveSelectionBackground = create<string>('list-active-selection-background', '--vscode-list-activeSelectionBackground').withDefault('#094771');
export const listActiveSelectionForeground = create<string>('list-active-selection-foreground', '--vscode-list-activeSelectionForeground').withDefault('#ffffff');
export const listHoverBackground = create<string>('list-hover-background', '--vscode-list-hoverBackground').withDefault('#2a2d2e');
export const quickInputBackground = create<string>('quick-input-background', '--vscode-quickInput-background').withDefault('#252526');

/**
* Divider design tokens.
Expand Down
10 changes: 8 additions & 2 deletions src/utilities/design-tokens/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export type T =
* A mapping of all the Visual Studio Code theme CSS variables mapped to the
* toolkit design tokens.
*/
export const tokenMappings: {[index: string]: CSSDesignToken<T>} = {};
export const tokenMappings: Map<string, CSSDesignToken<T>> = new Map();

/**
* Boolean flag that ensures the VS Code theme listener is initialized once.
Expand All @@ -43,7 +43,13 @@ export function create<T>(name: string, vscodeThemeVar?: string) {
const designToken = DesignToken.create<T>(name);

if (vscodeThemeVar) {
tokenMappings[vscodeThemeVar] = designToken;
// If the fake vscode token is passed in, attach a unique ID to it so that it can
// be added to the tokenMappings map without overriding a previous fake token value
if (vscodeThemeVar.includes('--fake-vscode-token')) {
const uniqueId = 'id' + Math.random().toString(16).slice(2);
vscodeThemeVar = `${vscodeThemeVar}-${uniqueId}`;
}
tokenMappings.set(vscodeThemeVar, designToken);
}
if (!isThemeListenerInitialized) {
initThemeChangeListener(tokenMappings);
Expand Down
54 changes: 42 additions & 12 deletions src/utilities/theme/applyTheme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import type {T} from '../design-tokens/create';
* Configures a MutationObserver to watch for Visual Studio Code theme changes and
* applies the current Visual Studio Code theme to the toolkit components.
*/
export function initThemeChangeListener(tokenMappings: {
[index: string]: CSSDesignToken<T>;
}) {
export function initThemeChangeListener(
tokenMappings: Map<string, CSSDesignToken<T>>
) {
window.addEventListener('load', () => {
const observer = new MutationObserver(() => {
applyCurrentTheme(tokenMappings);
Expand All @@ -27,21 +27,51 @@ export function initThemeChangeListener(tokenMappings: {
/**
* Applies the current Visual Studio Code theme to the toolkit components.
*/
function applyCurrentTheme(tokenMappings: {
[index: string]: CSSDesignToken<T>;
}) {
function applyCurrentTheme(tokenMappings: Map<string, CSSDesignToken<T>>) {
// Get all the styles applied to the <body> tag in the webview HTML
// Importantly this includes all the CSS variables associated with the
// current Visual Studio Code theme
const styles = getComputedStyle(document.body);
const body = document.querySelector('body');

for (const vscodeTokenName in tokenMappings) {
const toolkitTokenName = tokenMappings[vscodeTokenName];
const body = document.querySelector('body');
const value = styles.getPropertyValue(vscodeTokenName).toString();
if (body) {
const themeKind = body.getAttribute('data-vscode-theme-kind');
for (const [vscodeTokenName, toolkitToken] of tokenMappings) {
let value = styles.getPropertyValue(vscodeTokenName).toString();

if (body) {
toolkitTokenName.setValueFor(body, value);
// Handle a couple of styling edge cases when a high contrast theme is applied
if (themeKind === 'vscode-high-contrast') {
// Developer note:
//
// There are a handful of VS Code theme tokens that have no value when a high
// contrast theme is applied.
//
// This is an issue because when no value is set the toolkit tokens will fall
// back to their default color values (aka the VS Code dark theme color palette).
// This results in the backgrounds of a couple of components having default dark
// theme colors––thus breaking the high contrast theme.
//
// The below code, catches these tokens which have no value and are also background
// tokens, then overrides their value to be transparent.
if (
value.length === 0 &&
toolkitToken.name.includes('background')
) {
value = 'transparent';
}

// Set icon button hover to be transparent in high contrast themes
if (toolkitToken.name === 'button-icon-hover-background') {
value = 'transparent';
}
} else {
// Set contrast-active-border token to be transparent in non-high-contrast themes
if (toolkitToken.name === 'contrast-active-border') {
value = 'transparent';
}
}

toolkitToken.setValueFor(body, value);
}
}
}