(core) Improve dark mode

Summary:
Enhances dark mode support for the formula editor, and adds support to
the color select popup. Also fixes some bugs with dark mode.

Test Plan: Tested manually.

Reviewers: jarek

Reviewed By: jarek

Differential Revision: https://phab.getgrist.com/D3847
This commit is contained in:
George Gevoian
2023-04-11 01:00:28 -04:00
parent 9d0e6694fc
commit 8a0bb4d4fe
27 changed files with 537 additions and 107 deletions

View File

@@ -1,6 +1,6 @@
import {basicButton, primaryButton} from 'app/client/ui2018/buttons';
import {isLight, swatches} from 'app/client/ui2018/ColorPalette';
import {colors, testId, theme, vars} from 'app/client/ui2018/cssVars';
import {testId, theme, vars} from 'app/client/ui2018/cssVars';
import {textInput} from 'app/client/ui2018/editableLabel';
import {IconName} from 'app/client/ui2018/IconList';
import {icon} from 'app/client/ui2018/icons';
@@ -366,24 +366,24 @@ class FontComponent extends Disposable {
const cssFontOptions = styled('div', `
display: flex;
gap: 1px;
background: ${colors.darkGrey};
border: 1px solid ${colors.darkGrey};
background: ${theme.colorSelectFontOptionsBorder};
border: 1px solid ${theme.colorSelectFontOptionsBorder};
`);
const cssFontOption = styled('div', `
display: grid;
place-items: center;
flex-grow: 1;
background: ${colors.light};
--icon-color: ${colors.dark};
background: ${theme.colorSelectFontOptionBg};
--icon-color: ${theme.colorSelectFontOptionFg};
height: 24px;
cursor: pointer;
&:hover:not(&-selected) {
background: ${colors.lightGrey};
background: ${theme.colorSelectFontOptionBgHover};
}
&-selected {
background: ${colors.dark};
--icon-color: ${colors.light}
background: ${theme.colorSelectFontOptionBgSelected};
--icon-color: ${theme.colorSelectFontOptionFgSelected}
}
`);
@@ -409,6 +409,7 @@ const cssControlRow = styled('div', `
`);
const cssHeaderRow = styled('div', `
color: ${theme.colorSelectFg};
text-transform: uppercase;
font-size: ${vars.smallFontSize};
margin-bottom: 12px;
@@ -430,8 +431,8 @@ const cssVSpacer = styled('div', `
const cssContainer = styled('div', `
padding: 18px 16px;
background-color: white;
box-shadow: 0 2px 16px 0 rgba(38,38,51,0.6);
background-color: ${theme.colorSelectBg};
box-shadow: 0 2px 16px 0 ${theme.colorSelectShadow};
z-index: 20;
margin: 2px 0;
&:focus {
@@ -445,13 +446,13 @@ const cssContent = styled('div', `
`);
const cssHexBox = styled(textInput, `
border: 1px solid ${theme.inputBorder};
border: 1px solid ${theme.colorSelectInputBorder};
border-left: none;
font-size: ${vars.smallFontSize};
display: flex;
align-items: center;
color: ${theme.lightText};
background-color: ${theme.inputBg};
color: ${theme.colorSelectInputFg};
background-color: ${theme.colorSelectInputBg};
width: 56px;
outline: none;
padding: 0 3px;
@@ -460,7 +461,7 @@ const cssHexBox = styled(textInput, `
`);
const cssLightBorder = styled('div', `
border: 1px solid #D9D9D9;
border: 1px solid ${theme.colorSelectColorSquareBorder};
`);
const cssColorSquare = styled('div', `
@@ -471,16 +472,16 @@ const cssColorSquare = styled('div', `
align-items: center;
position: relative;
&-selected {
outline: 1px solid #D9D9D9;
outline: 1px solid ${theme.colorSelectColorSquareBorder};
outline-offset: 1px;
}
`);
const cssEmptyBox = styled(cssColorSquare, `
--icon-color: ${colors.error};
--icon-color: ${theme.iconError};
border: 1px solid #D9D9D9;
&-selected {
outline: 1px solid ${colors.dark};
outline: 1px solid ${theme.colorSelectColorSquareBorderEmpty};
outline-offset: 1px;
}
`);
@@ -493,7 +494,7 @@ const cssFontIcon = styled(icon, `
const cssNoneIcon = styled(icon, `
height: 100%;
width: 100%;
--icon-color: ${colors.error}
--icon-color: ${theme.iconError}
`);
const cssButtonIcon = styled(cssColorSquare, `

View File

@@ -6,6 +6,7 @@
* https://css-tricks.com/snippets/css/system-font-stack/
*
*/
import {createPausableObs, PausableObservable} from 'app/client/lib/pausableObs';
import {getStorage} from 'app/client/lib/storage';
import {urlState} from 'app/client/models/gristUrlState';
import {getTheme, ProductFlavor} from 'app/client/ui/CustomThemes';
@@ -257,6 +258,9 @@ export const theme = {
'rgba(76, 86, 103, 0.24)'),
popupCloseButtonFg: new CustomProp('theme-popup-close-button-fg', undefined, colors.slate),
/* Prompts */
promptFg: new CustomProp('theme-prompt-fg', undefined, '#606060'),
/* Progress Bars */
progressBarFg: new CustomProp('theme-progress-bar-fg', undefined, colors.lightGreen),
progressBarErrorFg: new CustomProp('theme-progress-bar-error-fg', undefined, colors.error),
@@ -382,6 +386,10 @@ export const theme = {
filterBarButtonSavedHoverBg: new CustomProp('theme-filter-bar-button-saved-hover-bg', undefined,
colors.darkGrey),
/* Icons */
iconDisabled: new CustomProp('theme-icon-disabled', undefined, colors.slate),
iconError: new CustomProp('theme-icon-error', undefined, colors.error),
/* Icon Buttons */
iconButtonFg: new CustomProp('theme-icon-button-fg', undefined, colors.light),
iconButtonPrimaryBg: new CustomProp('theme-icon-button-primary-bg', undefined,
@@ -589,6 +597,8 @@ export const theme = {
codeViewParams: new CustomProp('theme-code-view-params', undefined, '#444'),
codeViewString: new CustomProp('theme-code-view-string', undefined, '#880000'),
codeViewNumber: new CustomProp('theme-code-view-number', undefined, '#880000'),
codeViewBuiltin: new CustomProp('theme-code-view-builtin', undefined, '#397300'),
codeViewLiteral: new CustomProp('theme-code-view-literal', undefined, '#78A960'),
/* Importer */
importerTableInfoBorder: new CustomProp('theme-importer-table-info-border', undefined, colors.darkGrey),
@@ -646,6 +656,14 @@ export const theme = {
undefined, colors.light),
accessRulesColumnItemIconHoverBg: new CustomProp('theme-access-rules-column-item-icon-hover-bg',
undefined, colors.slate),
accessRulesFormulaEditorBg: new CustomProp('theme-access-rules-formula-editor-bg', undefined,
'white'),
accessRulesFormulaEditorBorderHover: new CustomProp(
'theme-access-rules-formula-editor-border-hover', undefined, colors.darkGrey),
accessRulesFormulaEditorBgDisabled: new CustomProp(
'theme-access-rules-formula-editor-bg-disabled', undefined, colors.mediumGreyOpaque),
accessRulesFormulaEditorFocus: new CustomProp('theme-access-rules-formula-editor-focus',
undefined, colors.cursor),
/* Cells */
cellFg: new CustomProp('theme-cell-fg', undefined, 'unset'),
@@ -701,6 +719,63 @@ export const theme = {
tutorialsPopupHeaderFg: new CustomProp('theme-tutorials-popup-header-fg', undefined,
colors.lightGreen),
tutorialsPopupBoxBg: new CustomProp('theme-tutorials-popup-box-bg', undefined, '#F5F5F5'),
/* Ace Autocomplete */
aceAutocompletePrimaryFg: new CustomProp('theme-ace-autocomplete-primary-fg', undefined, '#444'),
aceAutocompleteSecondaryFg: new CustomProp('theme-ace-autocomplete-secondary-fg', undefined,
'#8f8f8f'),
aceAutocompleteHighlightedFg: new CustomProp('theme-ace-autocomplete-highlighted-fg', undefined, '#000'),
aceAutocompleteBg: new CustomProp('theme-ace-autocomplete-bg', undefined, '#FBFBFB'),
aceAutocompleteBorder: new CustomProp('theme-ace-autocomplete-border', undefined, 'lightgray'),
aceAutocompleteLink: new CustomProp('theme-ace-autocomplete-link', undefined, colors.lightGreen),
aceAutocompleteLinkHighlighted: new CustomProp('theme-ace-autocomplete-link-highlighted',
undefined, colors.darkGreen),
aceAutocompleteActiveLineBg: new CustomProp('theme-ace-autocomplete-active-line-bg',
undefined, '#CAD6FA'),
aceAutocompleteLineBorderHover: new CustomProp('theme-ace-autocomplete-line-border-hover',
undefined, '#abbffe'),
aceAutocompleteLineBgHover: new CustomProp('theme-ace-autocomplete-line-bg-hover',
undefined, 'rgba(233,233,253,0.4)'),
/* Color Select */
colorSelectFg: new CustomProp('theme-color-select-fg', undefined, colors.dark),
colorSelectBg: new CustomProp('theme-color-select-bg', undefined, 'white'),
colorSelectShadow: new CustomProp('theme-color-select-shadow', undefined,
'rgba(38,38,51,0.6)'),
colorSelectFontOptionsBorder: new CustomProp('theme-color-select-font-options-border',
undefined, colors.darkGrey),
colorSelectFontOptionFg: new CustomProp('theme-color-select-font-option-fg',
undefined, colors.dark),
colorSelectFontOptionBg: new CustomProp('theme-color-select-font-option-bg',
undefined, colors.light),
colorSelectFontOptionBgHover: new CustomProp('theme-color-select-font-option-bg-hover',
undefined, colors.lightGrey),
colorSelectFontOptionFgSelected: new CustomProp('theme-color-select-font-option-selected-fg',
undefined, colors.light),
colorSelectFontOptionBgSelected: new CustomProp('theme-color-select-font-option-selected-bg',
undefined, colors.dark),
colorSelectColorSquareBorder: new CustomProp('theme-color-select-color-square-border',
undefined, '#D9D9D9'),
colorSelectColorSquareBorderEmpty: new CustomProp('theme-color-select-color-square-border-empty',
undefined, colors.dark),
colorSelectInputFg: new CustomProp('theme-color-select-input-fg',
undefined, colors.slate),
colorSelectInputBg: new CustomProp('theme-color-select-input-bg',
undefined, 'white'),
colorSelectInputBorder: new CustomProp('theme-color-select-input-border',
undefined, colors.darkGrey),
/* Highlighted Code */
highlightedCodeBlockBg: new CustomProp('theme-highlighted-code-block-bg', undefined,
colors.light),
highlightedCodeBlockBgDisabled: new CustomProp('theme-highlighted-code-block-bg-disabled',
undefined, colors.mediumGreyOpaque),
highlightedCodeFg: new CustomProp('theme-highlighted-code-fg',
undefined, colors.slate),
highlightedCodeBorder: new CustomProp('theme-highlighted-code-border',
undefined, colors.darkGrey),
highlightedCodeBgDisabled: new CustomProp('theme-highlighted-code-bg-disabled',
undefined, colors.mediumGreyOpaque),
};
const cssColors = values(colors).map(v => v.decl()).join('\n');
@@ -823,15 +898,19 @@ export function isScreenResizing(): Observable<boolean> {
return _isScreenResizingObs;
}
let _prefersDarkModeObs: Observable<boolean>|undefined;
export function prefersDarkMode(): boolean {
return window.matchMedia('(prefers-color-scheme: dark)').matches;
}
let _prefersDarkModeObs: PausableObservable<boolean>|undefined;
/**
* Returns a singleton observable for whether the user agent prefers dark mode.
*/
export function prefersDarkModeObs(): Observable<boolean> {
export function prefersDarkModeObs(): PausableObservable<boolean> {
if (!_prefersDarkModeObs) {
const query = window.matchMedia('(prefers-color-scheme: dark)');
const obs = Observable.create<boolean>(null, query.matches);
const obs = createPausableObs<boolean>(null, query.matches);
query.addEventListener('change', event => obs.set(event.matches));
_prefersDarkModeObs = obs;
}