(core) Add dark mode to user preferences

Summary:
Adds initial implementation of dark mode. Preferences for dark mode are
available on the account settings page. Dark mode is currently a beta feature
as there are still some small bugs to squash and a few remaining UI elements
to style.

Test Plan: Browser tests.

Reviewers: jarek

Reviewed By: jarek

Subscribers: paulfitz, jarek

Differential Revision: https://phab.getgrist.com/D3587
pull/223/head
George Gevoian 2 years ago
parent d7b3fb972c
commit ec157dc469

@ -5,7 +5,7 @@ import {createUserImage} from 'app/client/ui/UserImage';
import {cssMemberImage, cssMemberListItem, cssMemberPrimary, import {cssMemberImage, cssMemberListItem, cssMemberPrimary,
cssMemberSecondary, cssMemberText} from 'app/client/ui/UserItem'; cssMemberSecondary, cssMemberText} from 'app/client/ui/UserItem';
import {basicButton, basicButtonLink} from 'app/client/ui2018/buttons'; import {basicButton, basicButtonLink} from 'app/client/ui2018/buttons';
import {colors, testId} from 'app/client/ui2018/cssVars'; import {testId, theme} from 'app/client/ui2018/cssVars';
import {icon} from 'app/client/ui2018/icons'; import {icon} from 'app/client/ui2018/icons';
import {menuCssClass, menuDivider} from 'app/client/ui2018/menus'; import {menuCssClass, menuDivider} from 'app/client/ui2018/menus';
import {PermissionDataWithExtraUsers} from 'app/common/ActiveDocAPI'; import {PermissionDataWithExtraUsers} from 'app/common/ActiveDocAPI';
@ -130,7 +130,7 @@ const cssUserItem = styled(cssMemberListItem, `
padding: 8px 16px; padding: 8px 16px;
align-items: center; align-items: center;
&:hover { &:hover {
background-color: ${colors.lightGrey}; background-color: ${theme.lightHover};
} }
`); `);
@ -146,7 +146,9 @@ const cssUserButton = styled('div', `
white-space: nowrap; white-space: nowrap;
gap: 4px; gap: 4px;
&:hover { &:hover {
background-color: ${colors.darkGrey}; --icon-color: ${theme.controlFg};
color: ${theme.controlFg};
background-color: ${theme.hover};
} }
&-disabled { &-disabled {
visibility: hidden; visibility: hidden;

@ -12,7 +12,7 @@ import {TableData} from 'app/client/models/TableData';
import {shadowScroll} from 'app/client/ui/shadowScroll'; import {shadowScroll} from 'app/client/ui/shadowScroll';
import {bigBasicButton, bigPrimaryButton} from 'app/client/ui2018/buttons'; import {bigBasicButton, bigPrimaryButton} from 'app/client/ui2018/buttons';
import {squareCheckbox} from 'app/client/ui2018/checkbox'; import {squareCheckbox} from 'app/client/ui2018/checkbox';
import {colors, testId} from 'app/client/ui2018/cssVars'; import {testId, theme} from 'app/client/ui2018/cssVars';
import {textInput} from 'app/client/ui2018/editableLabel'; import {textInput} from 'app/client/ui2018/editableLabel';
import {cssIconButton, icon} from 'app/client/ui2018/icons'; import {cssIconButton, icon} from 'app/client/ui2018/icons';
import {menu, menuItemAsync} from 'app/client/ui2018/menus'; import {menu, menuItemAsync} from 'app/client/ui2018/menus';
@ -1445,45 +1445,50 @@ const cssSectionHeading = styled('div', `
align-items: center; align-items: center;
margin-bottom: 8px; margin-bottom: 8px;
font-weight: bold; font-weight: bold;
color: ${colors.slate}; color: ${theme.lightText};
`); `);
const cssTableName = styled('span', ` const cssTableName = styled('span', `
color: ${colors.dark}; color: ${theme.text};
`); `);
const cssInput = styled(textInput, ` const cssInput = styled(textInput, `
color: ${theme.inputFg};
background-color: ${theme.inputBg};
width: 100%; width: 100%;
border: 1px solid transparent; border: 1px solid transparent;
cursor: pointer; cursor: pointer;
&:hover { &:hover {
border: 1px solid ${colors.darkGrey}; border: 1px solid ${theme.inputBorder};
} }
&:focus { &:focus {
box-shadow: inset 0 0 0 1px ${colors.cursor}; box-shadow: inset 0 0 0 1px ${theme.controlFg};
border-color: ${colors.cursor}; border-color: ${theme.controlFg};
cursor: unset; cursor: unset;
} }
&[disabled] { &[disabled] {
color: ${colors.dark}; color: ${theme.inputDisabledFg};
background-color: ${colors.mediumGreyOpaque}; background-color: ${theme.inputDisabledBg};
box-shadow: unset; box-shadow: unset;
border-color: transparent; border-color: transparent;
} }
&::placeholder {
color: ${theme.inputPlaceholderFg};
}
`); `);
const cssConditionError = styled('div', ` const cssConditionError = styled('div', `
margin-top: 4px; margin-top: 4px;
width: 100%; width: 100%;
color: ${colors.error}; color: ${theme.errorText};
`); `);
/** /**
* Fairly general table styles. * Fairly general table styles.
*/ */
const cssTableRounded = styled('div', ` const cssTableRounded = styled('div', `
border: 1px solid ${colors.slate}; border: 1px solid ${theme.accessRulesTableBorder};
border-radius: 8px; border-radius: 8px;
overflow: hidden; overflow: hidden;
`); `);
@ -1491,7 +1496,7 @@ const cssTableRounded = styled('div', `
// Row with a border // Row with a border
const cssTableRow = styled('div', ` const cssTableRow = styled('div', `
display: flex; display: flex;
border-bottom: 1px solid ${colors.slate}; border-bottom: 1px solid ${theme.accessRulesTableBorder};
&:last-child { &:last-child {
border-bottom: none; border-bottom: none;
} }
@ -1499,8 +1504,8 @@ const cssTableRow = styled('div', `
// Darker table header // Darker table header
const cssTableHeaderRow = styled(cssTableRow, ` const cssTableHeaderRow = styled(cssTableRow, `
background-color: ${colors.mediumGrey}; background-color: ${theme.accessRulesTableHeaderBg};
color: ${colors.dark}; color: ${theme.accessRulesTableHeaderFg};
`); `);
// Cell for table column header. // Cell for table column header.
@ -1517,7 +1522,7 @@ const cssCell = styled('div', `
overflow: hidden; overflow: hidden;
&-rborder { &-rborder {
border-right: 1px solid ${colors.slate}; border-right: 1px solid ${theme.accessRulesTableBorder};
} }
&-center { &-center {
text-align: center; text-align: center;
@ -1551,6 +1556,7 @@ const cssRuleBody = styled('div', `
`); `);
const cssRuleDescription = styled('div', ` const cssRuleDescription = styled('div', `
color: ${theme.text};
display: flex; display: flex;
align-items: center; align-items: center;
margin: 16px 0 8px 0; margin: 16px 0 8px 0;
@ -1568,6 +1574,6 @@ const cssCenterContent = styled('div', `
`); `);
const cssDefaultLabel = styled('div', ` const cssDefaultLabel = styled('div', `
color: ${colors.slate}; color: ${theme.accessRulesTableBodyFg};
font-weight: bold; font-weight: bold;
`); `);

@ -44,7 +44,6 @@
--color-hint-text: #888; --color-hint-text: #888;
--scroll-bar-width: 12px; --scroll-bar-width: 12px;
--scroll-bar-bg: #f0f0f0;
/* fonts */ /* fonts */
--font-navbar-title: "Helvetica", "Arial", sans-serif; --font-navbar-title: "Helvetica", "Arial", sans-serif;
@ -96,12 +95,12 @@ body {
.show_scrollbar::-webkit-scrollbar { .show_scrollbar::-webkit-scrollbar {
width: var(--scroll-bar-width); width: var(--scroll-bar-width);
height: var(--scroll-bar-width); height: var(--scroll-bar-width);
background-color: var(--scroll-bar-bg); background-color: var(--scroll-bar-bg, #f0f0f0);
} }
.show_scrollbar::-webkit-scrollbar-thumb { .show_scrollbar::-webkit-scrollbar-thumb {
background-color: rgba(0,0,0,0.3); background-color: var(--scroll-bar-fg, #a8a8a8);
-webkit-border-radius: 100px; -webkit-border-radius: 100px;
border: 2px solid var(--scroll-bar-bg); border: 2px solid var(--scroll-bar-bg, #f0f0f0);
} }
.show_scrollbar::-webkit-scrollbar-thumb:vertical { .show_scrollbar::-webkit-scrollbar-thumb:vertical {
min-height: 4rem; min-height: 4rem;
@ -110,13 +109,16 @@ body {
min-width: 4rem; min-width: 4rem;
} }
.show_scrollbar::-webkit-scrollbar-thumb:hover { .show_scrollbar::-webkit-scrollbar-thumb:hover {
background-color: rgba(0,0,0,0.4); /* Some darker color when you click it */ background-color: var(--scroll-bar-hover-fg, #8f8f8f);
-webkit-border-radius: 100px; -webkit-border-radius: 100px;
} }
.show_scrollbar::-webkit-scrollbar-thumb:active { .show_scrollbar::-webkit-scrollbar-thumb:active {
background-color: rgba(0,0,0,0.5); /* Some darker color when you click it */ background-color: var(--scroll-bar-active-fg, #7c7c7c);
-webkit-border-radius: 100px; -webkit-border-radius: 100px;
} }
.show_scrollbar::-webkit-scrollbar-corner {
background-color: var(--scroll-bar-bg, #f0f0f0);
}
div.dev_warning { div.dev_warning {
position: absolute; position: absolute;
z-index: 10; z-index: 10;

@ -1,10 +1,12 @@
var ace = require('brace'); var ace = require('brace');
var _ = require('underscore'); var _ = require('underscore');
// Used to load python language settings and 'chrome' ace style // Used to load python language settings and color themes
require('brace/mode/python'); require('brace/mode/python');
require('brace/theme/chrome'); require('brace/theme/chrome');
require('brace/theme/dracula');
require('brace/ext/language_tools'); require('brace/ext/language_tools');
var {setupAceEditorCompletions} = require('./AceEditorCompletions'); var {setupAceEditorCompletions} = require('./AceEditorCompletions');
var {getGristConfig} = require('../../common/urlUtils');
var dom = require('../lib/dom'); var dom = require('../lib/dom');
var dispose = require('../lib/dispose'); var dispose = require('../lib/dispose');
var modelUtil = require('../models/modelUtil'); var modelUtil = require('../models/modelUtil');
@ -198,7 +200,14 @@ AceEditor.prototype._setup = function() {
}); });
this.session = this.editor.getSession(); this.session = this.editor.getSession();
this.session.setMode('ace/mode/python'); this.session.setMode('ace/mode/python');
this.editor.setTheme('ace/theme/chrome');
const gristTheme = this.gristDoc?.docPageModel.appModel.currentTheme;
this._setAceTheme(gristTheme?.get());
if (!getGristConfig().enableCustomCss && gristTheme) {
this.autoDispose(gristTheme.addListener((theme) => {
this._setAceTheme(theme);
}));
}
// Default line numbers to hidden // Default line numbers to hidden
this.editor.renderer.setShowGutter(false); this.editor.renderer.setShowGutter(false);
@ -270,6 +279,12 @@ AceEditor.prototype._getContentHeight = function() {
return Math.max(1, this.session.getScreenLength()) * this.editor.renderer.lineHeight; return Math.max(1, this.session.getScreenLength()) * this.editor.renderer.lineHeight;
}; };
AceEditor.prototype._setAceTheme = function(gristTheme) {
const {enableCustomCss} = getGristConfig();
const gristAppearance = gristTheme?.appearance;
const aceTheme = gristAppearance === 'dark' && !enableCustomCss ? 'dracula' : 'chrome';
this.editor.setTheme(`ace/theme/${aceTheme}`);
};
let _RangeConstructor = null; //singleton, load it lazily let _RangeConstructor = null; //singleton, load it lazily
AceEditor.makeRange = function(a, b, c, d) { AceEditor.makeRange = function(a, b, c, d) {

@ -27,7 +27,11 @@
} }
.action_info_from_self { .action_info_from_self {
color: #333333; color: var(--grist-theme-document-history-activity-text-light, #333333);
}
.action_desc {
color: var(--grist-theme-document-history-activity-text-light, unset);
} }
.action_log_item.undone > .action_info, .action_log_item.undone > .action_info,
@ -46,10 +50,12 @@
} }
.action_log_rename_pre { .action_log_rename_pre {
color: #333333;
background: #faa; background: #faa;
} }
.action_log_rename_post { .action_log_rename_post {
color: #333333;
background: #afa; background: #afa;
} }
@ -62,7 +68,7 @@
text-align: center; text-align: center;
margin-top: 0; margin-top: 0;
padding-top: 0; padding-top: 0;
color: #000; color: var(--grist-theme-document-history-activity-text, #000);
} }
.action_log_table td { .action_log_table td {
@ -100,6 +106,7 @@
} }
.action_log_cell_remove { .action_log_cell_remove {
color: #333333;
background: #faa; background: #faa;
text-decoration: line-through; text-decoration: line-through;
padding-left: 2px; padding-left: 2px;
@ -111,6 +118,7 @@
} }
.action_log_cell_add { .action_log_cell_add {
color: #333333;
background: #afa; background: #afa;
padding-left: 2px; padding-left: 2px;
padding-right: 2px; padding-right: 2px;

@ -16,7 +16,7 @@ import {cssLabel, cssRow, cssSeparator} from 'app/client/ui/RightPanelStyles';
import {cssFieldEntry, cssFieldLabel, IField, VisibleFieldsConfig } from 'app/client/ui/VisibleFieldsConfig'; import {cssFieldEntry, cssFieldLabel, IField, VisibleFieldsConfig } from 'app/client/ui/VisibleFieldsConfig';
import {IconName} from 'app/client/ui2018/IconList'; import {IconName} from 'app/client/ui2018/IconList';
import {squareCheckbox} from 'app/client/ui2018/checkbox'; import {squareCheckbox} from 'app/client/ui2018/checkbox';
import {colors, vars} from 'app/client/ui2018/cssVars'; import {theme, vars} from 'app/client/ui2018/cssVars';
import {cssDragger} from 'app/client/ui2018/draggableList'; import {cssDragger} from 'app/client/ui2018/draggableList';
import {icon} from 'app/client/ui2018/icons'; import {icon} from 'app/client/ui2018/icons';
import {IOptionFull, linkSelect, menu, menuItem, menuText, select} from 'app/client/ui2018/menus'; import {IOptionFull, linkSelect, menu, menuItem, menuText, select} from 'app/client/ui2018/menus';
@ -33,6 +33,7 @@ import debounce = require('lodash/debounce');
import defaults = require('lodash/defaults'); import defaults = require('lodash/defaults');
import defaultsDeep = require('lodash/defaultsDeep'); import defaultsDeep = require('lodash/defaultsDeep');
import isNumber = require('lodash/isNumber'); import isNumber = require('lodash/isNumber');
import merge = require('lodash/merge');
import sum = require('lodash/sum'); import sum = require('lodash/sum');
import union = require('lodash/union'); import union = require('lodash/union');
import type {Annotations, Config, Datum, ErrorBar, Layout, LayoutAxis, Margin, import type {Annotations, Config, Datum, ErrorBar, Layout, LayoutAxis, Margin,
@ -221,6 +222,8 @@ export class ChartView extends Disposable {
this.listenTo(this.sortedRows, 'rowNotify', this._update); this.listenTo(this.sortedRows, 'rowNotify', this._update);
this.autoDispose(this.sortedRows.getKoArray().subscribe(this._update)); this.autoDispose(this.sortedRows.getKoArray().subscribe(this._update));
this.autoDispose(this._formatterComp.subscribe(this._update)); this.autoDispose(this._formatterComp.subscribe(this._update));
this.autoDispose(this.gristDoc.docPageModel.appModel.currentTheme.addListener(() =>
this._update()));
} }
public prepareToPrint(onOff: boolean) { public prepareToPrint(onOff: boolean) {
@ -333,7 +336,7 @@ export class ChartView extends Disposable {
// meantime and cause error later. So let's check again. // meantime and cause error later. So let's check again.
if (this.isDisposed()) { return; } if (this.isDisposed()) { return; }
const layout: Partial<Layout> = defaultsDeep(plotData.layout, getPlotlyLayout(options)); const layout: Partial<Layout> = defaultsDeep(plotData.layout, this._getPlotlyLayout(options));
const config: Partial<Config> = {...plotData.config, displayModeBar: false}; const config: Partial<Config> = {...plotData.config, displayModeBar: false};
// react() can be used in place of newPlot(), and is faster when updating an existing plot. // react() can be used in place of newPlot(), and is faster when updating an existing plot.
await Plotly.react(this._chartDom, plotData.data, layout, config); await Plotly.react(this._chartDom, plotData.data, layout, config);
@ -348,6 +351,50 @@ export class ChartView extends Disposable {
private _isCompatibleSeries(col: ColumnRec) { private _isCompatibleSeries(col: ColumnRec) {
return isNumericOnly(this._chartType.peek()) ? isNumericLike(col) : true; return isNumericOnly(this._chartType.peek()) ? isNumericLike(col) : true;
} }
private _getPlotlyLayout(options: ChartOptions): Partial<Layout> {
// Note that each call to getPlotlyLayout() creates a new layout object. We are intentionally
// avoiding reuse because Plotly caches too many layout calculations when the object is reused.
const yaxis: Partial<LayoutAxis> = {automargin: true, title: {standoff: 0}};
const xaxis: Partial<LayoutAxis> = {automargin: true, title: {standoff: 0}};
if (options.logYAxis) { yaxis.type = 'log'; }
if (options.invertYAxis) { yaxis.autorange = 'reversed'; }
const layout = {
// Margins include labels, titles, legend, and may get auto-expanded beyond this.
margin: {
l: 50,
r: 50,
b: 40, // Space below chart which includes x-axis labels
t: 30, // Space above the chart (doesn't include any text)
pad: 4
} as Margin,
yaxis,
xaxis,
...(options.stacked ? {barmode: 'relative'} : {}),
};
return merge(layout, this._getPlotlyTheme());
}
private _getPlotlyTheme(): Partial<Layout> {
const appModel = this.gristDoc.docPageModel.appModel;
const {colors} = appModel.currentTheme.get();
return {
paper_bgcolor: colors['chart-bg'],
plot_bgcolor: colors['chart-bg'],
xaxis: {
color: colors['chart-x-axis'],
},
yaxis: {
color: colors['chart-y-axis'],
},
font: {
color: colors['chart-fg'],
},
legend: {
bgcolor: colors['chart-legend-bg'],
},
};
}
} }
/** /**
@ -420,32 +467,6 @@ function extractErrorBars(series: Series[], options: ChartOptions): Map<Series,
defaults(ChartView.prototype, BaseView.prototype); defaults(ChartView.prototype, BaseView.prototype);
Object.assign(ChartView.prototype, BackboneEvents); Object.assign(ChartView.prototype, BackboneEvents);
function getPlotlyLayout(options: ChartOptions): Partial<Layout> {
// Note that each call to getPlotlyLayout() creates a new layout object. We are intentionally
// avoiding reuse because Plotly caches too many layout calculations when the object is reused.
const yaxis: Partial<LayoutAxis> = {automargin: true, title: {standoff: 0}};
const xaxis: Partial<LayoutAxis> = {automargin: true, title: {standoff: 0}};
if (options.logYAxis) { yaxis.type = 'log'; }
if (options.invertYAxis) { yaxis.autorange = 'reversed'; }
return {
// Margins include labels, titles, legend, and may get auto-expanded beyond this.
margin: {
l: 50,
r: 50,
b: 40, // Space below chart which includes x-axis labels
t: 30, // Space above the chart (doesn't include any text)
pad: 4
} as Margin,
legend: {
// Translucent background, so chart data is still visible if legend overlaps it.
bgcolor: "#FFFFFF80",
},
yaxis,
xaxis,
...(options.stacked ? {barmode: 'relative'} : {}),
};
}
/** /**
* The grainjs component for side-pane configuration options for a Chart section. * The grainjs component for side-pane configuration options for a Chart section.
*/ */
@ -1257,7 +1278,7 @@ const cssRowLabel = styled('div', `
margin-right: 8px; margin-right: 8px;
font-weight: initial; /* negate bootstrap */ font-weight: initial; /* negate bootstrap */
color: ${colors.dark}; color: ${theme.text};
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
user-select: none; user-select: none;
@ -1265,7 +1286,7 @@ const cssRowLabel = styled('div', `
const cssRowHelp = styled(cssRow, ` const cssRowHelp = styled(cssRow, `
font-size: ${vars.smallFontSize}; font-size: ${vars.smallFontSize};
color: ${colors.slate}; color: ${theme.lightText};
`); `);
const cssAddIcon = styled(icon, ` const cssAddIcon = styled(icon, `
@ -1275,15 +1296,15 @@ const cssAddIcon = styled(icon, `
const cssAddYAxis = styled('div', ` const cssAddYAxis = styled('div', `
display: flex; display: flex;
cursor: pointer; cursor: pointer;
color: ${colors.lightGreen}; color: ${theme.controlFg};
--icon-color: ${colors.lightGreen}; --icon-color: ${theme.controlFg};
&:not(:first-child) { &:not(:first-child) {
margin-top: 8px; margin-top: 8px;
} }
&:hover, &:focus, &:active { &:hover, &:focus, &:active {
color: ${colors.darkGreen}; color: ${theme.controlHoverFg};
--icon-color: ${colors.darkGreen}; --icon-color: ${theme.controlHoverFg};
} }
`); `);
@ -1299,7 +1320,7 @@ const cssRemoveIcon = styled(icon, `
const cssHintRow = styled('div', ` const cssHintRow = styled('div', `
margin: -4px 16px 8px 16px; margin: -4px 16px 8px 16px;
color: ${colors.slate}; color: ${theme.lightText};
`); `);
const cssRangeInput = styled('input', ` const cssRangeInput = styled('input', `

@ -15,9 +15,38 @@
} }
.g-code-viewer.hljs { .g-code-viewer.hljs {
color: var(--grist-theme-code-view-text, #444);
background-color: inherit; background-color: inherit;
} }
.g-code-panel-denied { .g-code-panel-denied {
text-align: center; text-align: center;
} }
.g-code-viewer .hljs-keyword {
color: var(--grist-theme-code-view-keyword, #444);
}
.g-code-viewer .hljs-comment {
color: var(--grist-theme-code-view-comment, #888888);
}
.g-code-viewer .hljs-meta {
color: var(--grist-theme-code-view-meta, #1F7199);
}
.g-code-viewer .hljs-title {
color: var(--grist-theme-code-view-title, #880000);
}
.g-code-viewer .hljs-params {
color: var(--grist-theme-code-view-params, #444);
}
.g-code-viewer .hljs-string {
color: var(--grist-theme-code-view-string, #880000);
}
.g-code-viewer .hljs-number {
color: var(--grist-theme-code-view-number, #880000);
}

@ -47,7 +47,7 @@ export class DataTables extends Disposable {
cssItem( cssItem(
testId('table'), testId('table'),
cssLeft( cssLeft(
dom.domComputed((use) => cssGreenIcon( dom.domComputed((use) => cssTableTypeIcon(
use(tableRec.summarySourceTable) !== 0 ? 'PivotLight' : 'TypeTable', use(tableRec.summarySourceTable) !== 0 ? 'PivotLight' : 'TypeTable',
testId(`table-id-${use(tableRec.tableId)}`) testId(`table-id-${use(tableRec.tableId)}`)
)), )),
@ -107,7 +107,7 @@ export class DataTables extends Disposable {
const tableName = [ const tableName = [
use(table.tableNameDef), isSummaryTable ? use(table.groupDesc) : '' use(table.tableNameDef), isSummaryTable ? use(table.groupDesc) : ''
].filter(p => Boolean(p?.trim())).join(' '); ].filter(p => Boolean(p?.trim())).join(' ');
return dom('span', tableName); return cssTableName(tableName);
} else { } else {
return dom('div', // to disable flex grow in the widget return dom('div', // to disable flex grow in the widget
dom.domComputed(fromKo(table.rawViewSection), vs => dom.domComputed(fromKo(table.rawViewSection), vs =>
@ -178,9 +178,9 @@ const cssItem = styled('div', `
width: 100%; width: 100%;
height: calc(1em * 56/13); /* 56px for 13px font */ height: calc(1em * 56/13); /* 56px for 13px font */
max-width: 750px; max-width: 750px;
border: 1px solid ${css.colors.mediumGrey}; border: 1px solid ${css.theme.rawDataTableBorder};
&:hover { &:hover {
border-color: ${css.colors.slate}; border-color: ${css.theme.rawDataTableBorderHover};
} }
`); `);
@ -224,8 +224,8 @@ const cssRight = styled('div', `
flex: none; flex: none;
`); `);
const cssGreenIcon = styled(icon, ` const cssTableTypeIcon = styled(icon, `
--icon-color: ${css.colors.lightGreen}; --icon-color: ${css.theme.accentIcon};
`); `);
const cssLine = styled('span', ` const cssLine = styled('span', `
@ -246,7 +246,7 @@ const cssTableRowsWrapper = styled('div', `
width: 80px; width: 80px;
overflow: hidden; overflow: hidden;
align-items: baseline; align-items: baseline;
color: ${css.colors.slate}; color: ${css.theme.lightText};
line-height: 18px; line-height: 18px;
padding: 0px 2px; padding: 0px 2px;
`); `);
@ -256,12 +256,12 @@ const cssHoverWrapper = styled('div', `
overflow: hidden; overflow: hidden;
cursor: default; cursor: default;
align-items: baseline; align-items: baseline;
color: ${css.colors.slate}; color: ${css.theme.lightText};
transition: background 0.05s; transition: background 0.05s;
padding: 0px 2px; padding: 0px 2px;
line-height: 18px; line-height: 18px;
&:hover { &:hover {
background: ${css.colors.lightGrey}; background: ${css.theme.lightHover};
} }
`); `);
@ -272,6 +272,7 @@ const cssTableId = styled(cssLine, `
const cssTableRows = cssTableId; const cssTableRows = cssTableId;
const cssTableTitle = styled('div', ` const cssTableTitle = styled('div', `
color: ${css.theme.text};
white-space: nowrap; white-space: nowrap;
`); `);
@ -294,3 +295,7 @@ const cssTableList = styled('div', `
const cssLoadingDots = styled(loadingDots, ` const cssLoadingDots = styled(loadingDots, `
--dot-size: 6px; --dot-size: 6px;
`); `);
const cssTableName = styled('span', `
color: ${css.theme.text};
`);

@ -9,7 +9,7 @@
left: 0; left: 0;
width: 100%; width: 100%;
background: white; background: var(--grist-theme-page-panels-main-panel-bg, white);
z-index: 1; z-index: 1;
margin-top: -3px; margin-top: -3px;
} }
@ -37,15 +37,15 @@
min-height: 16px; min-height: 16px;
white-space: pre; white-space: pre;
word-wrap: break-word; word-wrap: break-word;
color: black; color: var(--grist-theme-cell-fg, black);
} }
.g_record_detail_value.record-add { .g_record_detail_value.record-add {
background-color: #f6f6ff; background-color: var(--grist-theme-table-add-new-bg, #f6f6ff);
} }
.g_record_detail_value.scissors { .g_record_detail_value.scissors {
outline: 2px dashed var(--grist-color-cursor); outline: 2px dashed var(--grist-theme-cursor, var(--grist-color-cursor));
} }
.g_record_detail_value.draft { .g_record_detail_value.draft {
@ -55,7 +55,7 @@
.detail_row_num { .detail_row_num {
font-size: var(--grist-x-small-font-size); font-size: var(--grist-x-small-font-size);
font-weight: normal; font-weight: normal;
color: var(--grist-color-slate); color: var(--grist-theme-text-light, var(--grist-color-slate));
padding: 8px; padding: 8px;
display: flex; display: flex;
align-items: center; align-items: center;
@ -163,7 +163,7 @@
.detail_theme_record_compact { .detail_theme_record_compact {
/* 12px is enough margin on the right to include most of the floating scrollbar on MacOS */ /* 12px is enough margin on the right to include most of the floating scrollbar on MacOS */
padding: 4px 16px 0px 16px; padding: 4px 16px 0px 16px;
background-color: var(--grist-color-medium-grey); background-color: var(--grist-theme-card-compact-widget-bg, var(--grist-color-medium-grey));
} }
.detail_theme_record_compact.detailview_record_single { .detail_theme_record_compact.detailview_record_single {
@ -175,12 +175,12 @@
} }
.detail_theme_record_compact > .g_record_detail_inner { .detail_theme_record_compact > .g_record_detail_inner {
background-color: white; background-color: var(--grist-theme-card-compact-record-bg, white);
position: relative; position: relative;
} }
.detail_theme_record_compact > .g_record_detail_inner > .layout_root { .detail_theme_record_compact > .g_record_detail_inner > .layout_root {
border: 1px solid var(--grist-color-dark-grey); border: 1px solid var(--grist-theme-card-compact-border, var(--grist-color-dark-grey));
border-right: none; border-right: none;
border-bottom: none; border-bottom: none;
} }
@ -196,8 +196,8 @@
.detail_theme_field_compact { .detail_theme_field_compact {
border-top: none; border-top: none;
border-left: none; border-left: none;
border-right: 1px solid var(--grist-color-dark-grey); border-right: 1px solid var(--grist-theme-card-compact-border, var(--grist-color-dark-grey));
border-bottom: 1px solid var(--grist-color-dark-grey); border-bottom: 1px solid var(--grist-theme-card-compact-border, var(--grist-color-dark-grey));
padding: 1px 1px 1px 5px; padding: 1px 1px 1px 5px;
margin: 0; margin: 0;
line-height: 1.2; line-height: 1.2;
@ -206,7 +206,7 @@
.detail_theme_field_compact > .g_record_detail_label { .detail_theme_field_compact > .g_record_detail_label {
font-weight: normal; font-weight: normal;
font-size: var(--grist-small-font-size); font-size: var(--grist-small-font-size);
color: var(--grist-color-slate); color: var(--grist-theme-card-compact-label, var(--grist-color-slate));
min-height: 0px; min-height: 0px;
white-space: nowrap; white-space: nowrap;
@ -227,7 +227,7 @@
.detail_theme_field_form > .g_record_detail_label { .detail_theme_field_form > .g_record_detail_label {
font-size: var(--grist-small-font-size); font-size: var(--grist-small-font-size);
color: var(--grist-color-slate); color: var(--grist-theme-card-form-label, var(--grist-color-slate));
font-weight: bold; font-weight: bold;
min-height: 0px; min-height: 0px;
white-space: nowrap; white-space: nowrap;
@ -241,7 +241,7 @@
* needs to learn to match the value box's style. Right now, the cell editor style is hard-coded. * needs to learn to match the value box's style. Right now, the cell editor style is hard-coded.
*/ */
.detail_theme_field_form > .g_record_detail_value { .detail_theme_field_form > .g_record_detail_value {
border: 1px solid lightgrey; border: 1px solid var(--grist-theme-card-form-border, lightgrey);
} }
.detail_theme_record_form { .detail_theme_record_form {
@ -253,7 +253,7 @@
} }
.detail_theme_record_form.detailview_record_detail { .detail_theme_record_form.detailview_record_detail {
border-bottom: 1px solid var(--grist-color-dark-grey); border-bottom: 1px solid var(--grist-theme-card-list-form-border, var(--grist-color-dark-grey));
padding-bottom: 12px; padding-bottom: 12px;
} }
@ -272,20 +272,20 @@
} }
.detail_theme_record_blocks.detailview_record_detail { .detail_theme_record_blocks.detailview_record_detail {
border-bottom: 1px solid var(--grist-color-dark-grey); border-bottom: 1px solid var(--grist-theme-card-list-blocks-border, var(--grist-color-dark-grey));
padding-bottom: 8px; padding-bottom: 8px;
} }
.detail_theme_field_blocks { .detail_theme_field_blocks {
padding: 6px; padding: 6px;
margin: 8px; margin: 8px;
background-color: var(--grist-color-medium-grey); background-color: var(--grist-theme-card-blocks-bg, var(--grist-color-medium-grey));
border-radius: 2px; border-radius: 2px;
} }
.detail_theme_field_blocks > .g_record_detail_label { .detail_theme_field_blocks > .g_record_detail_label {
font-size: var(--grist-small-font-size); font-size: var(--grist-small-font-size);
color: var(--grist-color-slate); color: var(--grist-theme-card-blocks-label, var(--grist-color-slate));
font-weight: normal; font-weight: normal;
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;

@ -2,7 +2,7 @@ import {DocPageModel} from 'app/client/models/DocPageModel';
import {urlState} from 'app/client/models/gristUrlState'; import {urlState} from 'app/client/models/gristUrlState';
import {docListHeader} from 'app/client/ui/DocMenuCss'; import {docListHeader} from 'app/client/ui/DocMenuCss';
import {infoTooltip} from 'app/client/ui/tooltips'; import {infoTooltip} from 'app/client/ui/tooltips';
import {colors, mediaXSmall} from 'app/client/ui2018/cssVars'; import {mediaXSmall, theme} from 'app/client/ui2018/cssVars';
import {icon} from 'app/client/ui2018/icons'; import {icon} from 'app/client/ui2018/icons';
import {loadingDots, loadingSpinner} from 'app/client/ui2018/loaders'; import {loadingDots, loadingSpinner} from 'app/client/ui2018/loaders';
import {APPROACHING_LIMIT_RATIO, DataLimitStatus} from 'app/common/DocUsage'; import {APPROACHING_LIMIT_RATIO, DataLimitStatus} from 'app/common/DocUsage';
@ -341,12 +341,11 @@ const cssLightlyBoldedText = styled('div', `
font-weight: 500; font-weight: 500;
`); `);
const cssIconAndText = styled('div', ` const cssWarningMessage = styled('div', `
color: ${theme.text};
--icon-color: ${theme.text};
display: flex; display: flex;
gap: 16px; gap: 16px;
`);
const cssWarningMessage = styled(cssIconAndText, `
margin-top: 16px; margin-top: 16px;
`); `);
@ -392,6 +391,7 @@ const cssUsageMetrics = styled('div', `
`); `);
const cssUsageMetric = styled('div', ` const cssUsageMetric = styled('div', `
color: ${theme.text};
display: flex; display: flex;
flex-direction: column; flex-direction: column;
width: 180px; width: 180px;
@ -408,14 +408,14 @@ const cssProgressBarContainer = styled('div', `
width: 100%; width: 100%;
height: 4px; height: 4px;
border-radius: 5px; border-radius: 5px;
background: ${colors.darkGrey}; background: ${theme.progressBarBg};
`); `);
const cssProgressBarFill = styled(cssProgressBarContainer, ` const cssProgressBarFill = styled(cssProgressBarContainer, `
background: ${colors.lightGreen}; background: ${theme.progressBarFg};
&-approaching-limit { &-approaching-limit {
background: ${colors.error}; background: ${theme.progressBarErrorFg};
} }
`); `);

@ -6,7 +6,7 @@ import {
import { GristDoc } from "app/client/components/GristDoc"; import { GristDoc } from "app/client/components/GristDoc";
import { ITooltipControl, showTooltip, tooltipCloseButton } from "app/client/ui/tooltips"; import { ITooltipControl, showTooltip, tooltipCloseButton } from "app/client/ui/tooltips";
import { FieldEditorStateEvent } from "app/client/widgets/FieldEditor"; import { FieldEditorStateEvent } from "app/client/widgets/FieldEditor";
import { colors, testId } from "app/client/ui2018/cssVars"; import { testId, theme } from "app/client/ui2018/cssVars";
import { cssLink } from "app/client/ui2018/links"; import { cssLink } from "app/client/ui2018/links";
/** /**
@ -408,7 +408,7 @@ class EditorAdapter extends Disposable implements Editor {
const styledTooltip = styled('div', ` const styledTooltip = styled('div', `
display: flex; display: flex;
align-items: center; align-items: center;
--icon-color: ${colors.lightGreen}; --icon-color: ${theme.controlFg};
& > .${cssLink.className} { & > .${cssLink.className} {
margin-left: 8px; margin-left: 8px;
@ -430,7 +430,7 @@ function cellTooltip(clb: () => any) {
// Discard notification dom // Discard notification dom
const styledNotification = styled('div', ` const styledNotification = styled('div', `
cursor: pointer; cursor: pointer;
color: ${colors.lightGreen}; color: ${theme.controlFg};
&:hover { &:hover {
text-decoration: underline; text-decoration: underline;
} }

@ -26,7 +26,10 @@ export class FormulaTransform extends ColumnTransform {
* Build the transform menu for a formula transform * Build the transform menu for a formula transform
*/ */
public buildDom() { public buildDom() {
this.editor = this.autoDispose(AceEditor.create({ observable: this.transformColumn.formula })); this.editor = this.autoDispose(AceEditor.create({
gristDoc: this.gristDoc,
observable: this.transformColumn.formula,
}));
return [ return [
dom('div.transform_menu', dom('div.transform_menu',
dom('div.transform_editor', dom('div.transform_editor',

@ -1,4 +1,5 @@
.gridview_data_pane { .gridview_data_pane {
background-color: var(--grist-theme-table-body-bg, unset);
position: relative; position: relative;
width: 100%; width: 100%;
overflow: hidden; overflow: hidden;
@ -22,7 +23,7 @@
overscroll-behavior: none; overscroll-behavior: none;
z-index: 2; /* scrollbar should be over the overlay background */ z-index: 2; /* scrollbar should be over the overlay background */
border-top: 1px solid lightgrey; border-top: 1px solid var(--grist-theme-table-header-border, lightgrey);
} }
.gridview_data_pane > .gridview_data_scroll { .gridview_data_pane > .gridview_data_scroll {
@ -48,7 +49,7 @@
} }
.field.column_name { .field.column_name {
border-bottom: 1px solid lightgray; border-bottom: 1px solid var(--grist-theme-table-header-border, lightgray);
line-height: var(--gridview-header-height); line-height: var(--gridview-header-height);
height: calc(var(--gridview-header-height) + 1px); /* Also should match height for overlay elements */ height: calc(var(--gridview-header-height) + 1px); /* Also should match height for overlay elements */
} }
@ -70,8 +71,9 @@
width: 4rem; /* Also should match width for .gridview_header_corner, and the overlay elements */ width: 4rem; /* Also should match width for .gridview_header_corner, and the overlay elements */
flex: none; flex: none;
border-bottom: 1px solid var(--grist-color-dark-grey); border-bottom: 1px solid var(--grist-theme-table-header-border-dark, var(--grist-color-dark-grey));
background-color: var(--grist-color-light-grey); color: var(--grist-theme-table-header-fg, unset);
background-color: var(--grist-theme-table-header-bg, var(--grist-color-light-grey));
z-index: 2; /* goes over data cells */ z-index: 2; /* goes over data cells */
padding-top: 2px; padding-top: 2px;
@ -100,7 +102,7 @@
* do not want !important, as it interferes with row selection. * do not want !important, as it interferes with row selection.
*/ */
.gridview_data_row_num { .gridview_data_row_num {
background-color: var(--grist-color-light-grey) !important; background-color: var(--grist-theme-table-header-bg , var(--grist-color-light-grey)) !important;
} }
.gridview_header_backdrop_top { .gridview_header_backdrop_top {
display: none; display: none;
@ -109,7 +111,7 @@
display: none; display: none;
} }
.gridview_data_header { .gridview_data_header {
background-color: var(--grist-color-light-grey) !important; background-color: var(--grist-theme-table-header-bg, var(--grist-color-light-grey)) !important;
} }
.print-widget .gridview_header_backdrop_left, .print-widget .gridview_data_corner_overlay { .print-widget .gridview_header_backdrop_left, .print-widget .gridview_data_corner_overlay {
display: none; display: none;
@ -125,15 +127,15 @@
display: table-header-group; display: table-header-group;
break-inside: avoid; break-inside: avoid;
position: static; position: static;
border-top: 1px solid var(--grist-color-dark-grey); border-top: 1px solid var(--grist-theme-table-header-border-dark, var(--grist-color-dark-grey));
border-left: 1px solid var(--grist-color-dark-grey); border-left: 1px solid var(--grist-theme-table-header-border-dark, var(--grist-color-dark-grey));
} }
.print-widget .gridview_data_header { .print-widget .gridview_data_header {
padding-left: 4rem !important; padding-left: 4rem !important;
} }
.print-widget .gridview_data_pane .print-all-rows { .print-widget .gridview_data_pane .print-all-rows {
display: table-row-group; display: table-row-group;
border-left: 1px solid var(--grist-color-dark-grey); border-left: 1px solid var(--grist-theme-table-body-border, var(--grist-color-dark-grey));
} }
.print-widget .gridview_data_pane .print-row { .print-widget .gridview_data_pane .print-row {
display: table-row; display: table-row;
@ -149,14 +151,14 @@
.scroll_shadow_top, .scroll_shadow_top,
.scroll_shadow_left { .scroll_shadow_left {
position:absolute; position:absolute;
background-color: var(--grist-color-light-grey) !important; background-color: var(--grist-theme-table-header-bg, var(--grist-color-light-grey)) !important;
} }
.gridview_data_corner_overlay { .gridview_data_corner_overlay {
width: 4rem; width: 4rem;
height: calc(var(--gridview-header-height) + 1px); /* matches gridview_data_header height (+border) */ height: calc(var(--gridview-header-height) + 1px); /* matches gridview_data_header height (+border) */
top: 1px; /* go under 1px border on scrollpane */ top: 1px; /* go under 1px border on scrollpane */
border-bottom: 1px solid lightgray; border-bottom: 1px solid var(--grist-theme-table-header-border, lightgray);
z-index: 3; z-index: 3;
cursor: pointer; cursor: pointer;
} }
@ -176,7 +178,7 @@
this value is the position where this movement should stop. this value is the position where this movement should stop.
*/ */
left: calc(4em + (var(--frozen-width, 0) - min(var(--frozen-scroll-offset, 0), var(--frozen-offset, 0))) * 1px); left: calc(4em + (var(--frozen-width, 0) - min(var(--frozen-scroll-offset, 0), var(--frozen-offset, 0))) * 1px);
box-shadow: -6px 0 6px 6px #444; box-shadow: -6px 0 6px 6px var(--grist-theme-table-scroll-shadow, #444);
/* shadow should only show to the right of it (10px should be enough) */ /* shadow should only show to the right of it (10px should be enough) */
-webkit-clip-path: polygon(0 0, 10px 0, 10px 100%, 0 100%); -webkit-clip-path: polygon(0 0, 10px 0, 10px 100%, 0 100%);
clip-path: polygon(0 0, 10px 0, 10px 100%, 0 100%); clip-path: polygon(0 0, 10px 0, 10px 100%, 0 100%);
@ -188,7 +190,7 @@
height: 100%; height: 100%;
width: 0px; width: 0px;
left: 4em; left: 4em;
box-shadow: -8px 0 14px 4px #444; box-shadow: -8px 0 14px 4px var(--grist-theme-table-scroll-shadow, #444);
-webkit-clip-path: polygon(0 0, 10px 0, 10px 100%, 0 100%); -webkit-clip-path: polygon(0 0, 10px 0, 10px 100%, 0 100%);
clip-path: polygon(0 0, 28px 0, 24px 100%, 0 100%); clip-path: polygon(0 0, 28px 0, 24px 100%, 0 100%);
z-index: 3; z-index: 3;
@ -204,7 +206,7 @@
as this component will be hidden when the scroll starts as this component will be hidden when the scroll starts
*/ */
left: calc(4em + var(--frozen-width, 0) * 1px); left: calc(4em + var(--frozen-width, 0) * 1px);
background-color: #999999; background-color: var(--grist-theme-table-frozen-columns-border, #999999);
z-index: 3; z-index: 3;
user-select: none; user-select: none;
pointer-events: none pointer-events: none
@ -215,7 +217,7 @@
height: 0; height: 0;
width: 100%; /* needs to be wide enough to flow off the side*/ width: 100%; /* needs to be wide enough to flow off the side*/
top: calc(var(--gridview-header-height) + 1px); /* matches gridview_data_header height (+border) */ top: calc(var(--gridview-header-height) + 1px); /* matches gridview_data_header height (+border) */
box-shadow: 0 -6px 6px 6px #444; box-shadow: 0 -6px 6px 6px var(--grist-theme-table-scroll-shadow, #444);
/* should only show below it (10px should be enough) */ /* should only show below it (10px should be enough) */
-webkit-clip-path: polygon(0 0, 0 10px, 100% 10px, 100% 0); -webkit-clip-path: polygon(0 0, 0 10px, 100% 10px, 100% 0);
@ -228,7 +230,7 @@
height:100%; height:100%;
top: 1px; /* go under 1px border on scrollpane */ top: 1px; /* go under 1px border on scrollpane */
z-index: 1; z-index: 1;
border-right: 1px solid lightgray; border-right: 1px solid var(--grist-theme-table-header-border, lightgray);
} }
.gridview_left_border { .gridview_left_border {
@ -236,7 +238,7 @@
width: 0px; /* Matches rowid width (+border) */ width: 0px; /* Matches rowid width (+border) */
height: 100%; height: 100%;
z-index: 3; z-index: 3;
border-right: 1px solid var(--grist-color-dark-grey) !important; border-right: 1px solid var(--grist-theme-table-body-border, var(--grist-color-dark-grey)) !important;
user-select: none; user-select: none;
pointer-events: none pointer-events: none
} }
@ -245,7 +247,7 @@
width: 100%; width: 100%;
height: calc(var(--gridview-header-height) + 1px); /* matches gridview_data_header height (+border) */ height: calc(var(--gridview-header-height) + 1px); /* matches gridview_data_header height (+border) */
top: 1px; /* go under 1px border on scrollpane */ top: 1px; /* go under 1px border on scrollpane */
border-bottom: 1px solid lightgray; border-bottom: 1px solid var(--grist-theme-table-header-border, lightgray);
z-index: 1; z-index: 1;
} }
@ -266,7 +268,7 @@
width: 0px; width: 0px;
height: 100%; height: 100%;
position: absolute; position: absolute;
border: 2px solid gray; border: 2px solid var(--grist-theme-table-drag-drop-indicator, gray);
z-index: 20; z-index: 20;
top: 0px; top: 0px;
} }
@ -275,10 +277,10 @@
width: 0px; width: 0px;
height: 100%; height: 100%;
position: absolute; position: absolute;
border: 1px solid gray; border: 1px solid var(--grist-theme-table-drag-drop-indicator, gray);
z-index: 15; z-index: 15;
top: 0px; top: 0px;
background-color: #F0F0F0; background-color: var(--grist-theme-table-drag-drop-shadow, #F0F0F0);
opacity: 0.5; opacity: 0.5;
} }
@ -286,7 +288,7 @@
width: 100%; width: 100%;
height: 0px; height: 0px;
position: absolute; position: absolute;
border: 2px solid gray; border: 2px solid var(--grist-theme-table-drag-drop-indicator, gray);
z-index: 20; z-index: 20;
left: 0px; left: 0px;
} }
@ -295,10 +297,10 @@
width: 100%; width: 100%;
height: 0px; height: 0px;
position: absolute; position: absolute;
border: 1px solid gray; border: 1px solid var(--grist-theme-table-drag-drop-indicator, gray);
z-index: 15; z-index: 15;
left: 0px; left: 0px;
background-color: #F0F0F0; background-color: var(--grist-theme-table-drag-drop-shadow, #F0F0F0);
opacity: 0.5; opacity: 0.5;
pointer-events: none; /* prevents row drag shadow from stealing row headers clicks */ pointer-events: none; /* prevents row drag shadow from stealing row headers clicks */
} }
@ -323,12 +325,12 @@
} }
.gridview_row .record.record-hlines .field.frozen { .gridview_row .record.record-hlines .field.frozen {
box-shadow: 0px 1px 0px var(--grist-color-dark-grey); box-shadow: 0px 1px 0px var(--grist-theme-table-body-border, var(--grist-color-dark-grey));
} }
/* selected field has a transparent color - with frozen fields we can't do it */ /* selected field has a transparent color - with frozen fields we can't do it */
.gridview_row .field.frozen.selected { .gridview_row .field.frozen.selected {
background-color: var(--grist-color-selection-opaque); background-color: var(--grist-theme-selection-opaque-bg, var(--grist-color-selection-opaque));
} }
/* make room for a frozen line by adding margin to first not frozen field - in header and in data */ /* make room for a frozen line by adding margin to first not frozen field - in header and in data */
@ -342,22 +344,25 @@
background: white !important; background: white !important;
} }
.column_names .column_name.frozen { .column_names .column_name.frozen {
background: var(--grist-color-light-grey) !important; background: var(--grist-theme-table-header-bg, var(--grist-color-light-grey)) !important;
} }
} }
/* Column hover effect */ /* Column hover effect */
.gridview_row .field.hover-column, /* normal field in a row */ .gridview_row .field.hover-column, /* normal field in a row */
.gridview_row .field.frozen.hover-column, /* frozen field in a row */ .gridview_row .field.hover-column .field_clip,
.column_name.hover-column, /* column name */ .column_name.hover-column, /* column name */
.column_name.hover-column.selected /* selected column name */ { .column_name.hover-column.selected, /* selected column name */
.gridview_row .field.frozen.hover-column /* frozen field in a row */ {
/* for frozen fields can't use alpha channel */ /* for frozen fields can't use alpha channel */
background-color: var(--grist-color-selection-opaque); background-color: var(--grist-theme-selection-opaque-bg, var(--grist-color-selection-opaque));
color: var(--grist-theme-selection-opaque-fg, unset);
} }
/* For zebra stripes, make the selection little darker */ /* For zebra stripes, make the selection little darker */
.record-zebra.record-even .field.hover-column { .record-zebra.record-even .field.hover-column {
background-color: var(--grist-color-selection-darker-opaque); background-color: var(--grist-theme-selection-opaque-dark-bg, var(--grist-color-selection-darker-opaque));
color: var(--grist-theme-selection-opaque-fg, unset);
} }
/* When column has a hover, remove menu button. */ /* When column has a hover, remove menu button. */
.column_name.hover-column .menu_toggle { .column_name.hover-column .menu_toggle {

@ -16,7 +16,7 @@ import {SortedRowSet} from 'app/client/models/rowset';
import {buildHighlightedCode} from 'app/client/ui/CodeHighlight'; import {buildHighlightedCode} from 'app/client/ui/CodeHighlight';
import {openFilePicker} from 'app/client/ui/FileDialog'; import {openFilePicker} from 'app/client/ui/FileDialog';
import {bigBasicButton, bigPrimaryButton} from 'app/client/ui2018/buttons'; import {bigBasicButton, bigPrimaryButton} from 'app/client/ui2018/buttons';
import {colors, testId, vars} from 'app/client/ui2018/cssVars'; import {testId, theme, vars} from 'app/client/ui2018/cssVars';
import {icon} from 'app/client/ui2018/icons'; import {icon} from 'app/client/ui2018/icons';
import {IOptionFull, linkSelect, menu, menuDivider, menuItem, multiSelect} from 'app/client/ui2018/menus'; import {IOptionFull, linkSelect, menu, menuDivider, menuItem, multiSelect} from 'app/client/ui2018/menus';
import {cssModalButtons, cssModalTitle} from 'app/client/ui2018/modals'; import {cssModalButtons, cssModalTitle} from 'app/client/ui2018/modals';
@ -663,7 +663,7 @@ export class Importer extends DisposableWithEvents {
fields && fields.length > 0 ? fields && fields.length > 0 ?
cssUnmatchedFields( cssUnmatchedFields(
dom('div', dom('div',
cssGreenText( cssAccentText(
`${fields.length} unmatched ${fields.length > 1 ? 'fields' : 'field'}` `${fields.length} unmatched ${fields.length > 1 ? 'fields' : 'field'}`
), ),
' in import:' ' in import:'
@ -939,11 +939,11 @@ const cssActionLink = styled('div', `
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
cursor: pointer; cursor: pointer;
color: ${colors.lightGreen}; color: ${theme.controlFg};
--icon-color: ${colors.lightGreen}; --icon-color: ${theme.controlFg};
&:hover { &:hover {
color: ${colors.darkGreen}; color: ${theme.controlHoverFg};
--icon-color: ${colors.darkGreen}; --icon-color: ${theme.controlHoverFg};
} }
`); `);
@ -972,7 +972,7 @@ const cssPreviewWrapper = styled('div', `
// This partly duplicates cssSectionHeader from HomeLeftPane.ts // This partly duplicates cssSectionHeader from HomeLeftPane.ts
const cssSectionHeader = styled('div', ` const cssSectionHeader = styled('div', `
margin-bottom: 8px; margin-bottom: 8px;
color: ${colors.slate}; color: ${theme.lightText};
text-transform: uppercase; text-transform: uppercase;
font-weight: 500; font-weight: 500;
font-size: ${vars.xsmallFontSize}; font-size: ${vars.xsmallFontSize};
@ -994,9 +994,9 @@ const cssTableInfo = styled('div', `
margin: 4px 0px; margin: 4px 0px;
width: 300px; width: 300px;
border-radius: 3px; border-radius: 3px;
border: 1px solid ${colors.darkGrey}; border: 1px solid ${theme.importerTableInfoBorder};
&:hover, &-selected { &:hover, &-selected {
background-color: ${colors.mediumGrey}; background-color: ${theme.hover};
} }
`); `);
@ -1009,7 +1009,7 @@ const cssTableLine = styled('div', `
const cssToFrom = styled('span', ` const cssToFrom = styled('span', `
flex: none; flex: none;
margin-right: 8px; margin-right: 8px;
color: ${colors.slate}; color: ${theme.lightText};
text-transform: uppercase; text-transform: uppercase;
font-weight: 500; font-weight: 500;
font-size: ${vars.xsmallFontSize}; font-size: ${vars.xsmallFontSize};
@ -1062,11 +1062,11 @@ const cssOverlay = styled('div', `
height: 100%; height: 100%;
width: 100%; width: 100%;
z-index: 10; z-index: 10;
background: ${colors.mediumGrey}; background: ${theme.importerSkippedTableOverlay};
`); `);
const cssPreviewGrid = styled(cssPreview, ` const cssPreviewGrid = styled(cssPreview, `
border: 1px solid ${colors.darkGrey}; border: 1px solid ${theme.importerPreviewBorder};
position: relative; position: relative;
`); `);
@ -1079,7 +1079,7 @@ const cssMergeOptionsToggle = styled('div', `
`); `);
const cssMergeOptionsMessage = styled('div', ` const cssMergeOptionsMessage = styled('div', `
color: ${colors.slate}; color: ${theme.lightText};
margin-bottom: 8px; margin-bottom: 8px;
`); `);
@ -1099,14 +1099,14 @@ const cssFieldFormula = styled(buildHighlightedCode, `
cursor: pointer; cursor: pointer;
margin-top: 1px; margin-top: 1px;
padding-left: 4px; padding-left: 4px;
--icon-color: ${colors.lightGreen}; --icon-color: ${theme.accentIcon};
`); `);
const cssColumnMatchIcon = styled(icon, ` const cssColumnMatchIcon = styled(icon, `
flex-shrink: 0; flex-shrink: 0;
width: 20px; width: 20px;
height: 32px; height: 32px;
background-color: ${colors.darkGrey}; background-color: ${theme.importerMatchIcon};
margin-right: 4px; margin-right: 4px;
`); `);
@ -1138,10 +1138,10 @@ const cssDestinationFieldSettings = styled('div', `
line-height: 0px; line-height: 0px;
border-radius: 3px; border-radius: 3px;
cursor: pointer; cursor: pointer;
--icon-color: ${colors.slate}; --icon-color: ${theme.lightText};
&:hover, &.weasel-popup-open { &:hover, &.weasel-popup-open {
background-color: ${colors.mediumGrey}; background-color: ${theme.hover};
} }
`); `);
@ -1154,9 +1154,9 @@ const cssUnmatchedFieldsList = styled('div', `
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden; overflow: hidden;
padding-right: 16px; padding-right: 16px;
color: ${colors.slate}; color: ${theme.lightText};
`); `);
const cssGreenText = styled('span', ` const cssAccentText = styled('span', `
color: ${colors.lightGreen}; color: ${theme.accentText};
`); `);

@ -1,6 +1,6 @@
import {bigBasicButton, bigPrimaryButton} from 'app/client/ui2018/buttons'; import {bigBasicButton, bigPrimaryButton} from 'app/client/ui2018/buttons';
import {squareCheckbox} from 'app/client/ui2018/checkbox'; import {squareCheckbox} from 'app/client/ui2018/checkbox';
import {colors, testId} from 'app/client/ui2018/cssVars'; import {testId, theme} from 'app/client/ui2018/cssVars';
import {cssModalButtons} from 'app/client/ui2018/modals'; import {cssModalButtons} from 'app/client/ui2018/modals';
import {ParseOptionSchema} from 'app/plugin/FileParserAPI'; import {ParseOptionSchema} from 'app/plugin/FileParserAPI';
import {Computed, dom, DomContents, IDisposableOwner, input, Observable, styled} from 'grainjs'; import {Computed, dom, DomContents, IDisposableOwner, input, Observable, styled} from 'grainjs';
@ -107,12 +107,18 @@ const cssParseOptionName = styled('div', `
margin-bottom: 8px; margin-bottom: 8px;
`); `);
const cssInputText = styled(input, ` const cssInputText = styled(input, `
color: ${theme.inputFg};
background-color: ${theme.inputBg};
position: relative; position: relative;
display: inline-block; display: inline-block;
outline: none; outline: none;
height: 28px; height: 28px;
border: 1px solid ${colors.darkGrey}; border: 1px solid ${theme.inputBorder};
border-radius: 3px; border-radius: 3px;
padding: 0 6px; padding: 0 6px;
width: 100%; width: 100%;
&::placeholder {
color: ${theme.inputPlaceholderFg};
}
`); `);

@ -33,6 +33,7 @@
} }
.preference_desc { .preference_desc {
color: var(--grist-theme-document-history-activity-text, unset);
margin-left: 5px; margin-left: 5px;
cursor: pointer; cursor: pointer;
font-weight: normal; font-weight: normal;

@ -4,7 +4,7 @@ import {DocumentUsage} from 'app/client/components/DocumentUsage';
import {GristDoc} from 'app/client/components/GristDoc'; import {GristDoc} from 'app/client/components/GristDoc';
import {printViewSection} from 'app/client/components/Printing'; import {printViewSection} from 'app/client/components/Printing';
import {buildViewSectionDom, ViewSectionHelper} from 'app/client/components/ViewLayout'; import {buildViewSectionDom, ViewSectionHelper} from 'app/client/components/ViewLayout';
import {colors, mediaSmall, vars} from 'app/client/ui2018/cssVars'; import {mediaSmall, theme} from 'app/client/ui2018/cssVars';
import {icon} from 'app/client/ui2018/icons'; import {icon} from 'app/client/ui2018/icons';
import {Computed, Disposable, dom, fromKo, makeTestId, Observable, styled} from 'grainjs'; import {Computed, Disposable, dom, fromKo, makeTestId, Observable, styled} from 'grainjs';
import {reportError} from 'app/client/models/errors'; import {reportError} from 'app/client/models/errors';
@ -112,7 +112,7 @@ const cssContainer = styled('div', `
`); `);
const cssOverlay = styled('div', ` const cssOverlay = styled('div', `
background-color: ${colors.backdrop}; background-color: ${theme.modalBackdrop};
inset: 0px; inset: 0px;
height: 100%; height: 100%;
width: 100%; width: 100%;
@ -127,7 +127,7 @@ const cssOverlay = styled('div', `
`); `);
const cssSectionWrapper = styled('div', ` const cssSectionWrapper = styled('div', `
background: white; background: ${theme.mainPanelBg};
height: 100%; height: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -154,9 +154,9 @@ const cssCloseButton = styled(icon, `
height: 24px; height: 24px;
width: 24px; width: 24px;
cursor: pointer; cursor: pointer;
--icon-color: ${vars.primaryBg}; --icon-color: ${theme.modalBackdropCloseButtonFg};
&:hover { &:hover {
--icon-color: ${colors.lighterGreen}; --icon-color: ${theme.modalBackdropCloseButtonHoverFg};
} }
@media ${mediaSmall} { @media ${mediaSmall} {
& { & {

@ -11,11 +11,11 @@
cursor: move; cursor: move;
z-index: 5; z-index: 5;
background-color: rgba(192, 192, 192, 0.2); background-color: var(--grist-theme-card-editing-layout-bg, rgba(192, 192, 192, 0.2));
border-left: 1px solid white; border-left: 1px solid var(--grist-theme-card-editing-layout-border, white);
border-top: 1px solid white; border-top: 1px solid var(--grist-theme-card-editing-layout-border, white);
border-right: 1px solid var(--grist-color-dark-grey); border-right: 1px solid var(--grist-theme-card-editing-layout-border, var(--grist-color-dark-grey));
border-bottom: 1px solid var(--grist-color-dark-grey); border-bottom: 1px solid var(--grist-theme-card-editing-layout-border, var(--grist-color-dark-grey));
} }
.dropdown-menu .g_record_layout_newfield { .dropdown-menu .g_record_layout_newfield {

@ -47,7 +47,10 @@ export class TypeTransform extends ColumnTransform {
const disableButtons = Observable.create(null, false); const disableButtons = Observable.create(null, false);
this._reviseTypeChange.set(false); this._reviseTypeChange.set(false);
this.editor = this.autoDispose(AceEditor.create({ observable: this.transformColumn.formula })); this.editor = this.autoDispose(AceEditor.create({
gristDoc: this.gristDoc,
observable: this.transformColumn.formula,
}));
return dom('div', return dom('div',
testId('type-transform-top'), testId('type-transform-top'),
dom.maybe(this._transformWidget, transformWidget => transformWidget.buildTransformConfigDom()), dom.maybe(this._transformWidget, transformWidget => transformWidget.buildTransformConfigDom()),

@ -17,7 +17,7 @@ const {addFilterMenu} = require('app/client/ui/FilterBar');
const {cssIcon, cssRow} = require('app/client/ui/RightPanelStyles'); const {cssIcon, cssRow} = require('app/client/ui/RightPanelStyles');
const {basicButton, primaryButton} = require('app/client/ui2018/buttons'); const {basicButton, primaryButton} = require('app/client/ui2018/buttons');
const {labeledLeftSquareCheckbox} = require("app/client/ui2018/checkbox"); const {labeledLeftSquareCheckbox} = require("app/client/ui2018/checkbox");
const {colors} = require('app/client/ui2018/cssVars'); const {theme} = require('app/client/ui2018/cssVars');
const {cssDragger} = require('app/client/ui2018/draggableList'); const {cssDragger} = require('app/client/ui2018/draggableList');
const {menu, menuItem, select} = require('app/client/ui2018/menus'); const {menu, menuItem, select} = require('app/client/ui2018/menus');
const {confirmModal} = require('app/client/ui2018/modals'); const {confirmModal} = require('app/client/ui2018/modals');
@ -236,7 +236,7 @@ ViewConfigTab.prototype._buildSortRow = function(colRef, sortSpec, columns) {
), ),
cssMenu( cssMenu(
cssBigIconWrapper( cssBigIconWrapper(
cssIcon('Dots', grainjsDom.cls(cssBgLightGreen.className, hasSpecs)), cssIcon('Dots', grainjsDom.cls(cssBgAccent.className, hasSpecs)),
testId('sort-options-icon'), testId('sort-options-icon'),
), ),
menu(_ctl => flags.map(({computed, allowedTypes, flag, label}) => { menu(_ctl => flags.map(({computed, allowedTypes, flag, label}) => {
@ -571,7 +571,7 @@ const cssMenuIcon = styled(cssIcon, `
margin: 0 8px 0 0; margin: 0 8px 0 0;
.${cssMenuItem.className}-sel > & { .${cssMenuItem.className}-sel > & {
background-color: ${colors.light}; background-color: ${theme.iconButtonFg};
} }
`); `);
@ -586,37 +586,37 @@ const cssSortIconBtn = styled(cssIcon, `
flex: none; flex: none;
margin: 0 6px; margin: 0 6px;
cursor: pointer; cursor: pointer;
background-color: ${colors.slate}; background-color: ${theme.controlSecondaryFg};
&:hover { &:hover {
background-color: ${colors.dark}; background-color: ${theme.controlSecondaryHoverFg};
} }
`); `);
const cssSortIconPrimaryBtn = styled(cssSortIconBtn, ` const cssSortIconPrimaryBtn = styled(cssSortIconBtn, `
background-color: ${colors.lightGreen}; background-color: ${theme.controlFg};
&:hover { &:hover {
background-color: ${colors.darkGreen}; background-color: ${theme.controlHoverFg};
} }
`); `);
const cssTextBtn = styled('div', ` const cssTextBtn = styled('div', `
color: ${colors.lightGreen}; color: ${theme.controlFg};
cursor: pointer; cursor: pointer;
&:hover { &:hover {
color: ${colors.darkGreen}; color: ${theme.controlHoverFg};
} }
`); `);
const cssPlusIcon = styled(cssIcon, ` const cssPlusIcon = styled(cssIcon, `
background-color: ${colors.lightGreen}; background-color: ${theme.controlFg};
cursor: pointer; cursor: pointer;
margin: 0px 4px 3px 0; margin: 0px 4px 3px 0;
.${cssTextBtn.className}:hover > & { .${cssTextBtn.className}:hover > & {
background-color: ${colors.darkGreen}; background-color: ${theme.controlHoverFg};
} }
`); `);
@ -673,24 +673,24 @@ const cssMenu = styled('div', `
border-radius: 3px; border-radius: 3px;
border: 1px solid transparent; border: 1px solid transparent;
&:hover, &.weasel-popup-open { &:hover, &.weasel-popup-open {
background-color: ${colors.mediumGrey}; background-color: ${theme.hover};
} }
`); `);
const cssBgLightGreen = styled(`div`, ` const cssBgAccent = styled(`div`, `
background: ${colors.lightGreen} background: ${theme.accentIcon}
`) `)
const cssOptionMenuItem = styled('div', ` const cssOptionMenuItem = styled('div', `
&:hover { &:hover {
background-color: ${colors.mediumGrey}; background-color: ${theme.hover};
} }
& label { & label {
flex: 1; flex: 1;
cursor: pointer; cursor: pointer;
} }
&.disabled * { &.disabled * {
color: ${colors.darkGrey} important; color: ${theme.menuItemDisabledFg} important;
cursor: not-allowed; cursor: not-allowed;
} }
`) `)

@ -12,14 +12,13 @@
cursor: default; cursor: default;
height: 24px; height: 24px;
margin-left: -16px; /* to include drag handle that shows up on hover */ margin-left: -16px; /* to include drag handle that shows up on hover */
color: var(--grist-color-slate); color: var(--grist-theme-text-light, var(--grist-color-slate));
font-size: var(--grist-small-font-size); font-size: var(--grist-small-font-size);
font-weight: 500; font-weight: 500;
white-space: nowrap; white-space: nowrap;
} }
.viewsection_content { .viewsection_content {
background-color: #ffffff;
overflow: visible; overflow: visible;
margin: 12px; margin: 12px;
} }
@ -91,19 +90,19 @@
.view_data_pane_container { .view_data_pane_container {
position: relative; position: relative;
flex: auto; flex: auto;
border: 1px solid var(--grist-color-dark-grey); border: 1px solid var(--grist-theme-widget-border, var(--grist-color-dark-grey));
} }
@media not print { @media not print {
.active_section > .view_data_pane_container { .active_section > .view_data_pane_container {
box-shadow: -2px 0 0 0px var(--grist-color-light-green); box-shadow: -2px 0 0 0px var(--grist-theme-widget-active-border, var(--grist-color-light-green));
border-left: 1px solid var(--grist-color-light-green); border-left: 1px solid var(--grist-theme-widget-active-border, var(--grist-color-light-green));
} }
.active_section > .view_data_pane_container.viewsection_type_detail { .active_section > .view_data_pane_container.viewsection_type_detail {
/* this color is a translucent version of grist-color-light-green */ /* this color is a translucent version of grist-color-light-green */
box-shadow: -2px 0 0 0px var(--grist-color-inactive-cursor); box-shadow: -2px 0 0 0px var(--grist-theme-cursor-inactive, var(--grist-color-inactive-cursor));
border-left: 1px solid var(--grist-color-inactive-cursor); border-left: 1px solid var(--grist-theme-cursor-inactive, var(--grist-color-inactive-cursor));
} }
} }
@ -111,7 +110,7 @@
.active_section--no-indicator > .view_data_pane_container, .active_section--no-indicator > .view_data_pane_container,
.active_section--no-indicator > .view_data_pane_container.viewsection_type_detail { .active_section--no-indicator > .view_data_pane_container.viewsection_type_detail {
box-shadow: none; box-shadow: none;
border-left: 1px solid var(--grist-color-dark-grey); border-left: 1px solid var(--grist-theme-widget-border, var(--grist-color-dark-grey));
} }
.disable_viewpane { .disable_viewpane {

@ -15,7 +15,7 @@ import {reportError} from 'app/client/models/errors';
import {filterBar} from 'app/client/ui/FilterBar'; import {filterBar} from 'app/client/ui/FilterBar';
import {viewSectionMenu} from 'app/client/ui/ViewSectionMenu'; import {viewSectionMenu} from 'app/client/ui/ViewSectionMenu';
import {buildWidgetTitle} from 'app/client/ui/WidgetTitle'; import {buildWidgetTitle} from 'app/client/ui/WidgetTitle';
import {colors, mediaSmall, testId} from 'app/client/ui2018/cssVars'; import {mediaSmall, testId, theme} from 'app/client/ui2018/cssVars';
import {icon} from 'app/client/ui2018/icons'; import {icon} from 'app/client/ui2018/icons';
import {DisposableWithEvents} from 'app/common/DisposableWithEvents'; import {DisposableWithEvents} from 'app/common/DisposableWithEvents';
import {mod} from 'app/common/gutil'; import {mod} from 'app/common/gutil';
@ -335,7 +335,7 @@ const cssTestClick = styled(`div`, `
const cssSigmaIcon = styled(icon, ` const cssSigmaIcon = styled(icon, `
bottom: 1px; bottom: 1px;
margin-right: 5px; margin-right: 5px;
background-color: ${colors.slate} background-color: ${theme.lightText}
`); `);
const cssViewLeaf = styled('div', ` const cssViewLeaf = styled('div', `
@ -352,12 +352,12 @@ const cssViewLeafInactive = styled('div', `
overflow: hidden; overflow: hidden;
background: repeating-linear-gradient( background: repeating-linear-gradient(
-45deg, -45deg,
${colors.mediumGreyOpaque}, ${theme.widgetInactiveStripesDark},
${colors.mediumGreyOpaque} 10px, ${theme.widgetInactiveStripesDark} 10px,
${colors.lightGrey} 10px, ${theme.widgetInactiveStripesLight} 10px,
${colors.lightGrey} 20px ${theme.widgetInactiveStripesLight} 20px
); );
border: 1px solid ${colors.darkGrey}; border: 1px solid ${theme.widgetBorder};
border-radius: 4px; border-radius: 4px;
padding: 0 2px; padding: 0 2px;
} }

@ -10,7 +10,7 @@
border-width: 0px; border-width: 0px;
border-style: none; border-style: none;
border-color: var(--grist-color-dark-grey); border-color: var(--grist-theme-table-body-border, var(--grist-color-dark-grey));
border-left-style: solid; /* left border, against rownumbers div, always on */ border-left-style: solid; /* left border, against rownumbers div, always on */
border-bottom-width: 1px; /* style: none, set by record-hlines*/ border-bottom-width: 1px; /* style: none, set by record-hlines*/
/* Record background is white by default. /* Record background is white by default.
@ -19,8 +19,8 @@
selected fields - this still remains white. selected fields - this still remains white.
TODO: consider making this color the single source TODO: consider making this color the single source
*/ */
background: var(--grist-row-background-color, white); background: var(--grist-row-background-color, var(--grist-theme-cell-bg, white));
color: var(--grist-row-color, black); color: var(--grist-row-color, var(--grist-theme-cell-fg, black));
} }
.record.record-hlines { /* Overwrites style, width set on element */ .record.record-hlines { /* Overwrites style, width set on element */
@ -28,11 +28,11 @@
} }
.record.record-zebra.record-even { .record.record-zebra.record-even {
background-color: var(--grist-row-background-color-zebra, #f8f8f8); background-color: var(--grist-row-background-color-zebra, var(--grist-theme-cell-zebra-bg, #f8f8f8));
} }
.record.record-add { .record.record-add {
background-color: #f6f6ff !important; /* important to win over zebra stripes */ background-color: var(--grist-theme-table-add-new-bg, #f6f6ff) !important; /* important to win over zebra stripes */
} }
.field { .field {
@ -47,15 +47,16 @@
} }
.record-vlines > .field { .record-vlines > .field {
border-right-color: var(--grist-color-dark-grey); /* set border visibility */ /* set border visibility */
border-right-color: var(--grist-theme-table-body-border, var(--grist-color-dark-grey));
} }
.field.scissors { .field.scissors {
outline: 2px dashed var(--grist-color-cursor); outline: 2px dashed var(--grist-theme-cursor, var(--grist-color-cursor));
} }
.field.selected { .field.selected {
background-color: var(--grist-color-selection); background-color: var(--grist-theme-selection, var(--grist-color-selection));
} }
.field.draft { .field.draft {
@ -136,58 +137,38 @@
width: 100%; width: 100%;
height: 100%; height: 100%;
/* one pixel outline around the cell, and one inside the cell */ /* one pixel outline around the cell, and one inside the cell */
outline: 1px solid var(--grist-color-inactive-cursor); outline: 1px solid var(--grist-theme-cursor-inactive, var(--grist-color-inactive-cursor));
box-shadow: inset 0 0 0 1px var(--grist-color-inactive-cursor); box-shadow: inset 0 0 0 1px var(--grist-theme-cursor-inactive, var(--grist-color-inactive-cursor));
pointer-events: none; pointer-events: none;
} }
.active_cursor { .active_cursor {
outline: 1px solid var(--grist-color-cursor); outline: 1px solid var(--grist-theme-cursor, var(--grist-color-cursor));
box-shadow: inset 0 0 0 1px var(--grist-color-cursor); box-shadow: inset 0 0 0 1px var(--grist-theme-cursor, var(--grist-color-cursor));
} }
} }
/* These classes are used to flash the cursor to indicate that editing in a cell is disabled. */
.cursor_read_only {
outline: 1px solid #ff9a00;
box-shadow: inset 0 0 0 1px #ff9a00;
}
.cursor_read_only_fade {
outline-color: var(--grist-color-cursor);
box-shadow: inset 0 0 0 1px var(--grist-color-cursor);
transition: outline-color 0.5s ease-in, box-shadow 0.5s ease-in;
}
.cursor_read_only_lock {
top: 0px;
height: 100%;
padding: 0 4px;
line-height: inherit;
background-color: #ff9a00;
color: white;
opacity: 1;
}
.cursor_read_only_fade > .cursor_read_only_lock {
opacity: 0;
transition: opacity 0.5s ease-in;
}
.column_name { .column_name {
background-color: var(--grist-color-light-grey); color: var(--grist-theme-table-header-fg, unset);
background-color: var(--grist-theme-table-header-bg, var(--grist-color-light-grey));
text-align: center; text-align: center;
cursor: pointer; cursor: pointer;
/* Column headers always show vertical gridlines, to make it clear how to resize them */ /* Column headers always show vertical gridlines, to make it clear how to resize them */
border-right-color: var(--grist-color-dark-grey); border-right-color: var(--grist-theme-table-header-border-dark, var(--grist-color-dark-grey));
}
.column_names.record {
border-left-color: var(--grist-theme-table-header-border, var(--grist-color-dark-grey));
} }
.column_name.selected { .column_name.selected {
background-color: var(--grist-color-medium-grey-opaque); color: var(--grist-theme-table-header-selected-fg, unset);
background-color: var(--grist-theme-table-header-selected-bg, var(--grist-color-medium-grey-opaque));
} }
.gridview_data_row_num.selected { .gridview_data_row_num.selected {
background-color: var(--grist-color-medium-grey-opaque); color: var(--grist-theme-table-header-selected-fg, unset);
background-color: var(--grist-theme-table-header-selected-bg, var(--grist-color-medium-grey-opaque));
} }
.gridview_data_row_info.linked_dst::before { .gridview_data_row_info.linked_dst::before {

@ -1,6 +1,6 @@
import {ACIndex, ACItem, buildHighlightedDom} from 'app/client/lib/ACIndex'; import {ACIndex, ACItem, buildHighlightedDom} from 'app/client/lib/ACIndex';
import {Autocomplete, IAutocompleteOptions} from 'app/client/lib/autocomplete'; import {Autocomplete, IAutocompleteOptions} from 'app/client/lib/autocomplete';
import {colors} from "app/client/ui2018/cssVars"; import {theme} from "app/client/ui2018/cssVars";
import {icon} from "app/client/ui2018/icons"; import {icon} from "app/client/ui2018/icons";
import {menuCssClass} from 'app/client/ui2018/menus'; import {menuCssClass} from 'app/client/ui2018/menus';
import {dom, DomElementArg, Holder, IDisposableOwner, Observable, styled} from 'grainjs'; import {dom, DomElementArg, Holder, IDisposableOwner, Observable, styled} from 'grainjs';
@ -96,11 +96,12 @@ const cssSelectBtn = styled('div', `
position: relative; position: relative;
width: 100%; width: 100%;
height: 30px; height: 30px;
color: ${colors.dark}; color: ${theme.selectButtonFg};
--icon-color: ${colors.dark}; --icon-color: ${theme.selectButtonFg};
`); `);
const cssSelectItem = styled('li', ` const cssSelectItem = styled('li', `
color: ${theme.menuItemFg};
display: block; display: block;
white-space: pre; white-space: pre;
overflow: hidden; overflow: hidden;
@ -110,12 +111,14 @@ const cssSelectItem = styled('li', `
cursor: pointer; cursor: pointer;
&.selected { &.selected {
background-color: var(--weaseljs-selected-background-color, #5AC09C); background-color: ${theme.menuItemSelectedBg};
color: var(--weaseljs-selected-color, white); color: ${theme.menuItemSelectedFg};
} }
`); `);
const cssInput = styled('input', ` const cssInput = styled('input', `
color: ${theme.inputFg};
background-color: ${theme.inputBg};
appearance: none; appearance: none;
-webkit-appearance: none; -webkit-appearance: none;
-moz-appearance: none; -moz-appearance: none;
@ -123,20 +126,23 @@ const cssInput = styled('input', `
width: 100%; width: 100%;
padding: 0 6px; padding: 0 6px;
outline: none; outline: none;
border: 1px solid ${colors.darkGrey}; border: 1px solid ${theme.inputBorder};
border-radius: 3px; border-radius: 3px;
cursor: pointer; cursor: pointer;
line-height: 16px; line-height: 16px;
cursor: pointer; cursor: pointer;
&:disabled { &:disabled {
color: grey; color: ${theme.inputDisabledFg};
background-color: initial; background-color: ${theme.inputDisabledBg};
} }
&:focus { &:focus {
cursor: initial; cursor: initial;
outline: none; outline: none;
box-shadow: 0px 0px 2px 2px #5E9ED6; box-shadow: 0px 0px 2px 2px ${theme.inputFocus};
}
&::placeholder {
color: ${theme.inputPlaceholderFg};
} }
`); `);
@ -147,8 +153,8 @@ const cssIcon = styled(icon, `
`); `);
const cssMatchText = styled('span', ` const cssMatchText = styled('span', `
color: ${colors.lightGreen}; color: ${theme.autocompleteMatchText};
.selected > & { .selected > & {
color: ${colors.lighterGreen}; color: ${theme.autocompleteSelectedMatchText};
} }
`); `);

@ -18,7 +18,7 @@
import { ACItem } from 'app/client/lib/ACIndex'; import { ACItem } from 'app/client/lib/ACIndex';
import { modKeyProp } from 'app/client/lib/browserInfo'; import { modKeyProp } from 'app/client/lib/browserInfo';
import { Autocomplete, IAutocompleteOptions } from 'app/client/lib/autocomplete'; import { Autocomplete, IAutocompleteOptions } from 'app/client/lib/autocomplete';
import { colors, testId } from 'app/client/ui2018/cssVars'; import { colors, testId, theme } from 'app/client/ui2018/cssVars';
import { icon } from 'app/client/ui2018/icons'; import { icon } from 'app/client/ui2018/icons';
import { csvDecodeRow, csvEncodeRow } from 'app/common/csvFormat'; import { csvDecodeRow, csvEncodeRow } from 'app/common/csvFormat';
import { computedArray, IDisposableCtor, IObsArraySplice, ObsArray, obsArray, Observable } from 'grainjs'; import { computedArray, IDisposableCtor, IObsArraySplice, ObsArray, obsArray, Observable } from 'grainjs';
@ -679,6 +679,8 @@ const cssInputWrapper = styled('div', `
`); `);
const cssTokenInput = styled('input', ` const cssTokenInput = styled('input', `
color: ${theme.cellEditorFg};
background-color: ${theme.cellEditorBg};
flex: auto; flex: auto;
-webkit-appearance: none; -webkit-appearance: none;
-moz-appearance: none; -moz-appearance: none;

@ -1,5 +1,5 @@
import { theme } from 'app/client/ui2018/cssVars';
import { Disposable, dom, Observable, styled } from 'grainjs'; import { Disposable, dom, Observable, styled } from 'grainjs';
import { colors } from 'app/client/ui2018/cssVars';
/** /**
* Simple validation controls. Renders as a red text with a validation message. * Simple validation controls. Renders as a red text with a validation message.
@ -117,5 +117,5 @@ export class Validator extends Disposable {
} }
const cssError = styled('div.validator', ` const cssError = styled('div.validator', `
color: ${colors.error}; color: ${theme.errorText};
`); `);

@ -103,7 +103,7 @@ div:hover > .kf_tooltip {
min-width: 16px; min-width: 16px;
min-height: 16px; min-height: 16px;
padding: 4px; padding: 4px;
background-color: white; background-color: var(--grist-theme-popup-bg, white);
border-radius: 2px; border-radius: 2px;
box-shadow: 0 1px 1px 1px rgba(0,0,0,0.15); box-shadow: 0 1px 1px 1px rgba(0,0,0,0.15);
line-height: 1.1rem; line-height: 1.1rem;
@ -129,7 +129,7 @@ div:hover > .kf_tooltip {
right: 20px; right: 20px;
width: 10px; width: 10px;
height: 10px; height: 10px;
background-color: white; background-color: var(--grist-theme-popup-bg, white);
transform: rotate(45deg); transform: rotate(45deg);
z-index: 11; z-index: 11;
} }
@ -393,6 +393,7 @@ div:hover > .kf_tooltip {
} }
.kf_label { .kf_label {
color: var(--grist-theme-text, unset);
white-space: nowrap; white-space: nowrap;
font-size: 1.1rem; font-size: 1.1rem;
cursor: default; cursor: default;
@ -436,7 +437,7 @@ div:hover > .kf_tooltip {
top: 0; top: 0;
left: 0; left: 0;
padding: 0; padding: 0;
color: #333; color: var(--grist-theme-input-fg, #333);
} }
.elabel_content_measure { .elabel_content_measure {

@ -5,6 +5,7 @@ import {urlState} from 'app/client/models/gristUrlState';
import {Notifier} from 'app/client/models/NotifyModel'; import {Notifier} from 'app/client/models/NotifyModel';
import {getFlavor, ProductFlavor} from 'app/client/ui/CustomThemes'; import {getFlavor, ProductFlavor} from 'app/client/ui/CustomThemes';
import {buildNewSiteModal, buildUpgradeModal} from 'app/client/ui/ProductUpgrades'; import {buildNewSiteModal, buildUpgradeModal} from 'app/client/ui/ProductUpgrades';
import {attachCssThemeVars, prefersDarkModeObs} from 'app/client/ui2018/cssVars';
import {OrgUsageSummary} from 'app/common/DocUsage'; import {OrgUsageSummary} from 'app/common/DocUsage';
import {Features, isLegacyPlan, Product} from 'app/common/Features'; import {Features, isLegacyPlan, Product} from 'app/common/Features';
import {GristLoadConfig} from 'app/common/gristUrls'; import {GristLoadConfig} from 'app/common/gristUrls';
@ -13,6 +14,9 @@ import {LocalPlugin} from 'app/common/plugin';
import {UserPrefs} from 'app/common/Prefs'; import {UserPrefs} from 'app/common/Prefs';
import {isOwner} from 'app/common/roles'; import {isOwner} from 'app/common/roles';
import {getTagManagerScript} from 'app/common/tagManager'; import {getTagManagerScript} from 'app/common/tagManager';
import {getDefaultThemePrefs, Theme, ThemeAppearance, ThemeColors, ThemePrefs,
ThemePrefsChecker} from 'app/common/ThemePrefs';
import {getThemeColors} from 'app/common/Themes';
import {getGristConfig} from 'app/common/urlUtils'; import {getGristConfig} from 'app/common/urlUtils';
import {getOrgName, Organization, OrgError, SUPPORT_EMAIL, UserAPI, UserAPIImpl} from 'app/common/UserAPI'; import {getOrgName, Organization, OrgError, SUPPORT_EMAIL, UserAPI, UserAPIImpl} from 'app/common/UserAPI';
import {getUserPrefObs, getUserPrefsObs} from 'app/client/models/UserPrefs'; import {getUserPrefObs, getUserPrefsObs} from 'app/client/models/UserPrefs';
@ -75,7 +79,10 @@ export interface AppModel {
currentProduct: Product|null; // The current org's product. currentProduct: Product|null; // The current org's product.
currentFeatures: Features; // Features of the current org's product. currentFeatures: Features; // Features of the current org's product.
userPrefsObs: Observable<UserPrefs>; userPrefsObs: Observable<UserPrefs>;
themePrefs: Observable<ThemePrefs>;
currentTheme: Computed<Theme>;
pageType: Observable<PageType>; pageType: Observable<PageType>;
@ -209,6 +216,11 @@ export class AppModelImpl extends Disposable implements AppModel {
public readonly isLegacySite = Boolean(this.currentProduct && isLegacyPlan(this.currentProduct.name)); public readonly isLegacySite = Boolean(this.currentProduct && isLegacyPlan(this.currentProduct.name));
public readonly userPrefsObs = getUserPrefsObs(this); public readonly userPrefsObs = getUserPrefsObs(this);
public readonly themePrefs = getUserPrefObs(this.userPrefsObs, 'theme', {
defaultValue: getDefaultThemePrefs(),
checker: ThemePrefsChecker,
}) as Observable<ThemePrefs>;
public readonly currentTheme = this._getCurrentThemeObs();
// Get the current PageType from the URL. // Get the current PageType from the URL.
public readonly pageType: Observable<PageType> = Computed.create(this, urlState().state, public readonly pageType: Observable<PageType> = Computed.create(this, urlState().state,
@ -223,6 +235,10 @@ export class AppModelImpl extends Disposable implements AppModel {
public readonly orgError?: OrgError, public readonly orgError?: OrgError,
) { ) {
super(); super();
this._applyTheme();
this.autoDispose(this.currentTheme.addListener(() => this._applyTheme()));
this._recordSignUpIfIsNewUser(); this._recordSignUpIfIsNewUser();
const state = urlState().state.get(); const state = urlState().state.get();
@ -311,6 +327,39 @@ export class AppModelImpl extends Disposable implements AppModel {
dataLayer.push({event: 'new-sign-up'}); dataLayer.push({event: 'new-sign-up'});
getUserPrefObs(this.userPrefsObs, 'recordSignUpEvent').set(undefined); getUserPrefObs(this.userPrefsObs, 'recordSignUpEvent').set(undefined);
} }
private _getCurrentThemeObs() {
return Computed.create(this, this.themePrefs, prefersDarkModeObs(),
(_use, themePrefs, prefersDarkMode) => {
let appearance: ThemeAppearance;
if (!themePrefs.syncWithOS) {
appearance = themePrefs.appearance;
} else {
appearance = prefersDarkMode ? 'dark' : 'light';
}
const nameOrColors = themePrefs.colors[appearance];
let colors: ThemeColors;
if (typeof nameOrColors === 'string') {
colors = getThemeColors(nameOrColors);
} else {
colors = nameOrColors;
}
return {appearance, colors};
},
);
}
/**
* Applies a theme based on the user's current theme preferences.
*/
private _applyTheme() {
// Custom CSS is incompatible with custom themes.
if (getGristConfig().enableCustomCss) { return; }
attachCssThemeVars(this.currentTheme.get());
}
} }
export function getHomeUrl(): string { export function getHomeUrl(): string {

@ -2,6 +2,7 @@ import {localStorageObs} from 'app/client/lib/localStorageObs';
import {AppModel} from 'app/client/models/AppModel'; import {AppModel} from 'app/client/models/AppModel';
import {UserOrgPrefs, UserPrefs} from 'app/common/Prefs'; import {UserOrgPrefs, UserPrefs} from 'app/common/Prefs';
import {Computed, Observable} from 'grainjs'; import {Computed, Observable} from 'grainjs';
import {CheckerT} from 'ts-interface-checker';
interface PrefsTypes { interface PrefsTypes {
userOrgPrefs: UserOrgPrefs; userOrgPrefs: UserOrgPrefs;
@ -12,15 +13,14 @@ function makePrefFunctions<P extends keyof PrefsTypes>(prefsTypeName: P) {
type PrefsType = PrefsTypes[P]; type PrefsType = PrefsTypes[P];
/** /**
* Creates an observable that returns UserOrgPrefs, and which stores them when set. * Creates an observable that returns a PrefsType, and which stores changes when set.
* *
* For anon user, the prefs live in localStorage. Note that the observable isn't actually watching * For anon user, the prefs live in localStorage. Note that the observable isn't actually watching
* for changes on the server, it will only change when set. * for changes on the server, it will only change when set.
*/ */
function getPrefsObs(appModel: AppModel): Observable<PrefsType> { function getPrefsObs(appModel: AppModel): Observable<PrefsType> {
const savedPrefs = appModel.currentValidUser ? appModel.currentOrg?.[prefsTypeName] : undefined; if (appModel.currentValidUser) {
if (savedPrefs) { const prefsObs = Observable.create<PrefsType>(null, appModel.currentOrg?.[prefsTypeName] ?? {});
const prefsObs = Observable.create<PrefsType>(null, savedPrefs!);
return Computed.create(null, (use) => use(prefsObs)) return Computed.create(null, (use) => use(prefsObs))
.onWrite(prefs => { .onWrite(prefs => {
prefsObs.set(prefs); prefsObs.set(prefs);
@ -41,10 +41,30 @@ function makePrefFunctions<P extends keyof PrefsTypes>(prefsTypeName: P) {
* stores it when set. * stores it when set.
*/ */
function getPrefObs<Name extends keyof PrefsType>( function getPrefObs<Name extends keyof PrefsType>(
prefsObs: Observable<PrefsType>, prefName: Name prefsObs: Observable<PrefsType>,
): Observable<PrefsType[Name]> { prefName: Name,
return Computed.create(null, (use) => use(prefsObs)[prefName]) options: {
.onWrite(value => prefsObs.set({...prefsObs.get(), [prefName]: value})); defaultValue?: Exclude<PrefsType[Name], undefined>;
checker?: CheckerT<PrefsType[Name]>;
} = {}
): Observable<PrefsType[Name] | undefined> {
const {defaultValue, checker} = options;
return Computed.create(null, (use) => {
const prefs = use(prefsObs);
if (!(prefName in prefs)) { return defaultValue; }
const value = prefs[prefName];
if (checker) {
try {
checker.check(value);
} catch (e) {
console.error(`getPrefObs: preference ${prefName.toString()} has value of invalid type`, e);
return defaultValue;
}
}
return value;
}).onWrite(value => prefsObs.set({...prefsObs.get(), [prefName]: value}));
} }
return {getPrefsObs, getPrefObs}; return {getPrefsObs, getPrefObs};

@ -1,17 +1,19 @@
import {AppModel, reportError} from 'app/client/models/AppModel'; import {AppModel, reportError} from 'app/client/models/AppModel';
import {urlState} from 'app/client/models/gristUrlState'; import {urlState} from 'app/client/models/gristUrlState';
import * as css from 'app/client/ui/AccountPageCss';
import {ApiKey} from 'app/client/ui/ApiKey'; import {ApiKey} from 'app/client/ui/ApiKey';
import {AppHeader} from 'app/client/ui/AppHeader'; import {AppHeader} from 'app/client/ui/AppHeader';
import {buildChangePasswordDialog} from 'app/client/ui/ChangePasswordDialog'; import {buildChangePasswordDialog} from 'app/client/ui/ChangePasswordDialog';
import {leftPanelBasic} from 'app/client/ui/LeftPanelCommon'; import {leftPanelBasic} from 'app/client/ui/LeftPanelCommon';
import {MFAConfig} from 'app/client/ui/MFAConfig'; import {MFAConfig} from 'app/client/ui/MFAConfig';
import {pagePanels} from 'app/client/ui/PagePanels'; import {pagePanels} from 'app/client/ui/PagePanels';
import {ThemeConfig} from 'app/client/ui/ThemeConfig';
import {createTopBarHome} from 'app/client/ui/TopBar'; import {createTopBarHome} from 'app/client/ui/TopBar';
import {transientInput} from 'app/client/ui/transientInput'; import {transientInput} from 'app/client/ui/transientInput';
import {cssBreadcrumbs, cssBreadcrumbsLink, separator} from 'app/client/ui2018/breadcrumbs'; import {cssBreadcrumbs, separator} from 'app/client/ui2018/breadcrumbs';
import {labeledSquareCheckbox} from 'app/client/ui2018/checkbox'; import {labeledSquareCheckbox} from 'app/client/ui2018/checkbox';
import {icon} from 'app/client/ui2018/icons'; import {cssLink} from 'app/client/ui2018/links';
import {colors, vars} from 'app/client/ui2018/cssVars'; import {getGristConfig} from 'app/common/urlUtils';
import {FullUser} from 'app/common/UserAPI'; import {FullUser} from 'app/common/UserAPI';
import {Computed, Disposable, dom, domComputed, makeTestId, Observable, styled} from 'grainjs'; import {Computed, Disposable, dom, domComputed, makeTestId, Observable, styled} from 'grainjs';
@ -46,19 +48,21 @@ export class AccountPage extends Disposable {
}, },
headerMain: this._buildHeaderMain(), headerMain: this._buildHeaderMain(),
contentMain: this._buildContentMain(), contentMain: this._buildContentMain(),
testId,
}); });
} }
private _buildContentMain() { private _buildContentMain() {
const {enableCustomCss} = getGristConfig();
return domComputed(this._userObs, (user) => user && ( return domComputed(this._userObs, (user) => user && (
cssContainer(cssAccountPage( css.container(css.accountPage(
cssHeader('Account settings'), css.header('Account settings'),
cssDataRow( css.dataRow(
cssSubHeader('Email'), css.inlineSubHeader('Email'),
cssEmail(user.email), css.email(user.email),
), ),
cssDataRow( css.dataRow(
cssSubHeader('Name'), css.inlineSubHeader('Name'),
domComputed(this._isEditingName, (isEditing) => ( domComputed(this._isEditingName, (isEditing) => (
isEditing ? [ isEditing ? [
transientInput( transientInput(
@ -69,16 +73,16 @@ export class AccountPage extends Disposable {
}, },
{ size: '5' }, // Lower size so that input can shrink below ~152px. { size: '5' }, // Lower size so that input can shrink below ~152px.
dom.on('input', (_ev, el) => this._nameEdit.set(el.value)), dom.on('input', (_ev, el) => this._nameEdit.set(el.value)),
cssFlexGrow.cls(''), css.flexGrow.cls(''),
), ),
cssTextBtn( css.textBtn(
cssIcon('Settings'), 'Save', css.icon('Settings'), 'Save',
// No need to save on 'click'. The transient input already does it on close. // No need to save on 'click'. The transient input already does it on close.
), ),
] : [ ] : [
cssName(user.name), css.name(user.name),
cssTextBtn( css.textBtn(
cssIcon('Settings'), 'Edit', css.icon('Settings'), 'Edit',
dom.on('click', () => this._isEditingName.set(true)), dom.on('click', () => this._isEditingName.set(true)),
), ),
] ]
@ -87,17 +91,17 @@ export class AccountPage extends Disposable {
), ),
// show warning for invalid name but not for the empty string // show warning for invalid name but not for the empty string
dom.maybe(use => use(this._nameEdit) && !use(this._isNameValid), cssWarnings), dom.maybe(use => use(this._nameEdit) && !use(this._isNameValid), cssWarnings),
cssHeader('Password & Security'), css.header('Password & Security'),
cssDataRow( css.dataRow(
cssSubHeader('Login Method'), css.inlineSubHeader('Login Method'),
cssLoginMethod(user.loginMethod), css.loginMethod(user.loginMethod),
user.loginMethod === 'Email + Password' ? cssTextBtn('Change Password', user.loginMethod === 'Email + Password' ? css.textBtn('Change Password',
dom.on('click', () => this._showChangePasswordDialog()), dom.on('click', () => this._showChangePasswordDialog()),
) : null, ) : null,
testId('login-method'), testId('login-method'),
), ),
user.loginMethod !== 'Email + Password' ? null : dom.frag( user.loginMethod !== 'Email + Password' ? null : dom.frag(
cssDataRow( css.dataRow(
labeledSquareCheckbox( labeledSquareCheckbox(
this._allowGoogleLogin, this._allowGoogleLogin,
'Allow signing in to this account with Google', 'Allow signing in to this account with Google',
@ -105,16 +109,21 @@ export class AccountPage extends Disposable {
), ),
testId('allow-google-login'), testId('allow-google-login'),
), ),
cssSubHeaderFullWidth('Two-factor authentication'), css.subHeader('Two-factor authentication'),
cssDescription( css.description(
"Two-factor authentication is an extra layer of security for your Grist account designed " + "Two-factor authentication is an extra layer of security for your Grist account designed " +
"to ensure that you're the only person who can access your account, even if someone " + "to ensure that you're the only person who can access your account, even if someone " +
"knows your password." "knows your password."
), ),
dom.create(MFAConfig, user), dom.create(MFAConfig, user),
), ),
cssHeader('API'), // Custom CSS is incompatible with custom themes.
cssDataRow(cssSubHeader('API Key'), cssContent( enableCustomCss ? null : [
css.header('Theme'),
dom.create(ThemeConfig, this._appModel),
],
css.header('API'),
css.dataRow(css.inlineSubHeader('API Key'), css.content(
dom.create(ApiKey, { dom.create(ApiKey, {
apiKey: this._apiKey, apiKey: this._apiKey,
onCreate: () => this._createApiKey(), onCreate: () => this._createApiKey(),
@ -131,7 +140,7 @@ export class AccountPage extends Disposable {
private _buildHeaderMain() { private _buildHeaderMain() {
return dom.frag( return dom.frag(
cssBreadcrumbs({ style: 'margin-left: 16px;' }, cssBreadcrumbs({ style: 'margin-left: 16px;' },
cssBreadcrumbsLink( cssLink(
urlState().setLinkUrl({}), urlState().setLinkUrl({}),
'Home', 'Home',
testId('home'), testId('home'),
@ -204,103 +213,12 @@ export function checkName(name: string): boolean {
* Builds dom to show marning messages to the user. * Builds dom to show marning messages to the user.
*/ */
function buildNameWarningsDom() { function buildNameWarningsDom() {
return cssWarning( return css.warning(
"Names only allow letters, numbers and certain special characters", "Names only allow letters, numbers and certain special characters",
testId('username-warning'), testId('username-warning'),
); );
} }
const cssContainer = styled('div', `
display: flex;
justify-content: center;
overflow: auto;
`);
const cssHeader = styled('div', `
height: 32px;
line-height: 32px;
margin: 28px 0 16px 0;
color: ${colors.dark};
font-size: ${vars.xxxlargeFontSize};
font-weight: ${vars.headerControlTextWeight};
`);
const cssAccountPage = styled('div', `
max-width: 600px;
padding: 16px;
`);
const cssDataRow = styled('div', `
margin: 8px 0px;
display: flex;
align-items: baseline;
`);
const cssSubHeaderFullWidth = styled('div', `
padding: 8px 0;
display: inline-block;
vertical-align: top;
font-weight: bold;
`);
const cssSubHeader = styled(cssSubHeaderFullWidth, `
min-width: 110px;
`);
const cssContent = styled('div', `
flex: 1 1 300px;
`);
const cssTextBtn = styled('button', `
font-size: ${vars.mediumFontSize};
color: ${colors.lightGreen};
cursor: pointer;
margin-left: 16px;
background-color: transparent;
border: none;
padding: 0;
text-align: left;
min-width: 110px;
&:hover {
color: ${colors.darkGreen};
}
`);
const cssIcon = styled(icon, `
background-color: ${colors.lightGreen};
margin: 0 4px 2px 0;
.${cssTextBtn.className}:hover > & {
background-color: ${colors.darkGreen};
}
`);
const cssWarnings = styled(buildNameWarningsDom, ` const cssWarnings = styled(buildNameWarningsDom, `
margin: -8px 0 0 110px; margin: -8px 0 0 110px;
`); `);
const cssDescription = styled('div', `
color: #8a8a8a;
font-size: 13px;
`);
const cssFlexGrow = styled('div', `
flex-grow: 1;
`);
const cssName = styled(cssFlexGrow, `
word-break: break-word;
`);
const cssEmail = styled('div', `
word-break: break-word;
`);
const cssLoginMethod = styled(cssFlexGrow, `
word-break: break-word;
`);
const cssWarning = styled('div', `
color: red;
`);

@ -0,0 +1,108 @@
import {theme, vars} from 'app/client/ui2018/cssVars';
import {icon as gristIcon} from 'app/client/ui2018/icons';
import {styled} from 'grainjs';
export const container = styled('div', `
display: flex;
justify-content: center;
overflow: auto;
`);
export const accountPage = styled('div', `
max-width: 600px;
margin-top: auto;
margin-bottom: auto;
padding: 16px;
`);
export const content = styled('div', `
flex: 1 1 300px;
`);
export const textBtn = styled('button', `
font-size: ${vars.mediumFontSize};
color: ${theme.controlFg};
cursor: pointer;
margin-left: 16px;
background-color: transparent;
border: none;
padding: 0;
text-align: left;
min-width: 110px;
&:hover {
color: ${theme.controlHoverFg};
}
`);
export const icon = styled(gristIcon, `
background-color: ${theme.controlFg};
margin: 0 4px 2px 0;
.${textBtn.className}:hover > & {
background-color: ${theme.controlHoverFg};
}
`);
export const description = styled('div', `
color: ${theme.lightText};
font-size: 13px;
`);
export const flexGrow = styled('div', `
flex-grow: 1;
`);
export const name = styled(flexGrow, `
color: ${theme.text};
word-break: break-word;
`);
export const email = styled('div', `
color: ${theme.text};
word-break: break-word;
`);
export const loginMethod = styled(flexGrow, `
color: ${theme.text};
word-break: break-word;
`);
export const warning = styled('div', `
color: ${theme.errorText};
`);
export const header = styled('div', `
height: 32px;
line-height: 32px;
margin: 28px 0 16px 0;
color: ${theme.text};
font-size: ${vars.xxxlargeFontSize};
font-weight: ${vars.headerControlTextWeight};
`);
export const subHeader = styled('div', `
color: ${theme.text};
padding: 8px 0;
vertical-align: top;
font-weight: bold;
display: block;
`);
export const inlineSubHeader = styled(subHeader, `
display: inline-block;
min-width: 110px;
`);
export const dataRow = styled('div', `
margin: 8px 0px;
display: flex;
align-items: baseline;
`);
export const betaTag = styled('span', `
text-transform: uppercase;
vertical-align: super;
font-size: ${vars.xsmallFontSize};
color: ${theme.accentText};
`);

@ -7,7 +7,7 @@ import {manageTeamUsers} from 'app/client/ui/OpenUserManager';
import {createUserImage} from 'app/client/ui/UserImage'; import {createUserImage} from 'app/client/ui/UserImage';
import * as viewport from 'app/client/ui/viewport'; import * as viewport from 'app/client/ui/viewport';
import {primaryButton} from 'app/client/ui2018/buttons'; import {primaryButton} from 'app/client/ui2018/buttons';
import {colors, mediaDeviceNotSmall, testId, vars} from 'app/client/ui2018/cssVars'; import {mediaDeviceNotSmall, testId, theme, vars} from 'app/client/ui2018/cssVars';
import {icon} from 'app/client/ui2018/icons'; import {icon} from 'app/client/ui2018/icons';
import {menu, menuDivider, menuItem, menuItemLink, menuSubHeader} from 'app/client/ui2018/menus'; import {menu, menuDivider, menuItem, menuItemLink, menuSubHeader} from 'app/client/ui2018/menus';
import {commonUrls, shouldHideUiElement} from 'app/common/gristUrls'; import {commonUrls, shouldHideUiElement} from 'app/common/gristUrls';
@ -165,14 +165,14 @@ const cssUserName = styled('div', `
margin-left: 8px; margin-left: 8px;
font-size: ${vars.mediumFontSize}; font-size: ${vars.mediumFontSize};
font-weight: ${vars.headerControlTextWeight}; font-weight: ${vars.headerControlTextWeight};
color: ${colors.dark}; color: ${theme.text};
`); `);
const cssEmail = styled('div', ` const cssEmail = styled('div', `
margin-top: 4px; margin-top: 4px;
font-size: ${vars.smallFontSize}; font-size: ${vars.smallFontSize};
font-weight: initial; font-weight: initial;
color: ${colors.slate}; color: ${theme.lightText};
`); `);
const cssSmallIconWrap = styled('div', ` const cssSmallIconWrap = styled('div', `
@ -181,16 +181,16 @@ const cssSmallIconWrap = styled('div', `
`); `);
const cssOtherEmail = styled('div', ` const cssOtherEmail = styled('div', `
color: ${colors.slate}; color: ${theme.lightText};
.${cssMenuItem.className}-sel & { .${cssMenuItem.className}-sel & {
color: ${colors.light}; color: ${theme.menuItemSelectedFg};
} }
`); `);
const cssCheckmark = styled(icon, ` const cssCheckmark = styled(icon, `
flex: none; flex: none;
margin-left: 16px; margin-left: 16px;
--icon-color: ${colors.lightGreen}; --icon-color: ${theme.accentIcon};
`); `);
// Note that this css class hides the item when the device width is small (not based on viewport // Note that this css class hides the item when the device width is small (not based on viewport

@ -1,4 +1,4 @@
import {colors, vars} from 'app/client/ui2018/cssVars'; import {theme, vars} from 'app/client/ui2018/cssVars';
import {icon} from 'app/client/ui2018/icons'; import {icon} from 'app/client/ui2018/icons';
import {dom, DomElementArg, Observable, styled} from "grainjs"; import {dom, DomElementArg, Observable, styled} from "grainjs";
@ -20,7 +20,7 @@ export const cssAddNewButton = styled('div', `
align-items: center; align-items: center;
margin: 22px 0px 22px 0px; margin: 22px 0px 22px 0px;
height: 40px; height: 40px;
color: ${colors.light}; color: ${theme.controlPrimaryFg};
border: none; border: none;
border-radius: 4px; border-radius: 4px;
@ -30,19 +30,19 @@ export const cssAddNewButton = styled('div', `
font-weight: bold; font-weight: bold;
overflow: hidden; overflow: hidden;
--circle-color: ${colors.lightGreen}; --circle-color: ${theme.addNewCircleSmallBg};
&:hover, &.weasel-popup-open { &:hover, &.weasel-popup-open {
--circle-color: ${colors.darkGreen}; --circle-color: ${theme.addNewCircleSmallHoverBg};
} }
&-open { &-open {
margin: 22px 16px 22px 16px; margin: 22px 16px 22px 16px;
background-color: ${colors.lightGreen}; background-color: ${theme.controlPrimaryBg};
--circle-color: ${colors.darkGreen}; --circle-color: ${theme.addNewCircleBg};
} }
&-open:hover, &-open.weasel-popup-open { &-open:hover, &-open.weasel-popup-open {
background-color: ${colors.darkGreen}; background-color: ${theme.controlPrimaryHoverBg};
--circle-color: ${colors.darkerGreen}; --circle-color: ${theme.addNewCircleHoverBg};
} }
`); `);
const cssLeftMargin = styled('div', ` const cssLeftMargin = styled('div', `
@ -53,6 +53,7 @@ const cssLeftMargin = styled('div', `
} }
`); `);
const cssAddText = styled('div', ` const cssAddText = styled('div', `
color: ${theme.controlPrimaryFg};
flex: 0 0.5 content; flex: 0 0.5 content;
white-space: nowrap; white-space: nowrap;
min-width: 0px; min-width: 0px;
@ -70,6 +71,6 @@ const cssPlusButton = styled('div', `
text-align: center; text-align: center;
`); `);
const cssPlusIcon = styled(icon, ` const cssPlusIcon = styled(icon, `
background-color: ${colors.light}; background-color: ${theme.addNewCircleFg};
margin-top: 6px; margin-top: 6px;
`); `);

@ -1,4 +1,5 @@
import { basicButton, textButton } from 'app/client/ui2018/buttons'; import { basicButton, textButton } from 'app/client/ui2018/buttons';
import { theme, vars } from 'app/client/ui2018/cssVars';
import { icon } from 'app/client/ui2018/icons'; import { icon } from 'app/client/ui2018/icons';
import { confirmModal } from 'app/client/ui2018/modals'; import { confirmModal } from 'app/client/ui2018/modals';
import { Disposable, dom, IDomArgs, makeTestId, Observable, observable, styled } from 'grainjs'; import { Disposable, dom, IDomArgs, makeTestId, Observable, observable, styled } from 'grainjs';
@ -119,11 +120,17 @@ export class ApiKey extends Disposable {
} }
const description = styled('div', ` const description = styled('div', `
color: #8a8a8a; margin-top: 8px;
font-size: 13px; color: ${theme.lightText};
font-size: ${vars.mediumFontSize};
`); `);
const cssInput = styled('input', ` const cssInput = styled('input', `
background-color: transparent;
color: ${theme.inputFg};
border: 1px solid ${theme.inputBorder};
padding: 4px;
border-radius: 3px;
outline: none; outline: none;
flex: 1 0 0; flex: 1 0 0;
`); `);

@ -9,7 +9,8 @@ body {
font-size: 1.2rem; font-size: 1.2rem;
margin: 0; margin: 0;
padding: 0; padding: 0;
background: url('img/gplaypattern.png'); background: var(--grist-theme-bg, url('img/gplaypattern.png'));
background-color: var(--grist-theme-bg-color, unset);
} }
.g-help { .g-help {

@ -2,9 +2,8 @@ import {urlState} from 'app/client/models/gristUrlState';
import {buildAppMenuBillingItem} from 'app/client/ui/BillingButtons'; import {buildAppMenuBillingItem} from 'app/client/ui/BillingButtons';
import {getTheme} from 'app/client/ui/CustomThemes'; import {getTheme} from 'app/client/ui/CustomThemes';
import {cssLeftPane} from 'app/client/ui/PagePanels'; import {cssLeftPane} from 'app/client/ui/PagePanels';
import {colors, testId, vars} from 'app/client/ui2018/cssVars'; import {colors, testId, theme, vars} from 'app/client/ui2018/cssVars';
import * as version from 'app/common/version'; import * as version from 'app/common/version';
import {BindableValue, Disposable, dom, styled} from "grainjs";
import {menu, menuItem, menuItemLink, menuSubHeader} from 'app/client/ui2018/menus'; import {menu, menuItem, menuItemLink, menuSubHeader} from 'app/client/ui2018/menus';
import {isTemplatesOrg, Organization} from 'app/common/UserAPI'; import {isTemplatesOrg, Organization} from 'app/common/UserAPI';
import {AppModel} from 'app/client/models/AppModel'; import {AppModel} from 'app/client/models/AppModel';
@ -13,7 +12,7 @@ import {DocPageModel} from 'app/client/models/DocPageModel';
import * as roles from 'app/common/roles'; import * as roles from 'app/common/roles';
import {manageTeamUsersApp} from 'app/client/ui/OpenUserManager'; import {manageTeamUsersApp} from 'app/client/ui/OpenUserManager';
import {maybeAddSiteSwitcherSection} from 'app/client/ui/SiteSwitcher'; import {maybeAddSiteSwitcherSection} from 'app/client/ui/SiteSwitcher';
import {DomContents} from 'grainjs'; import {BindableValue, Disposable, dom, DomContents, styled} from 'grainjs';
// Maps a name of a Product (from app/gen-server/entity/Product.ts) to a tag (pill) to show next // Maps a name of a Product (from app/gen-server/entity/Product.ts) to a tag (pill) to show next
// to the org name. // to the org name.
@ -33,12 +32,12 @@ export class AppHeader extends Disposable {
} }
public buildDom() { public buildDom() {
const theme = getTheme(this._appModel.topAppModel.productFlavor); const productFlavor = getTheme(this._appModel.topAppModel.productFlavor);
const currentOrg = this._appModel.currentOrg; const currentOrg = this._appModel.currentOrg;
return cssAppHeader( return cssAppHeader(
cssAppHeader.cls('-widelogo', theme.wideLogo || false), cssAppHeader.cls('-widelogo', productFlavor.wideLogo || false),
// Show version when hovering over the application icon. // Show version when hovering over the application icon.
cssAppLogo( cssAppLogo(
{title: `Ver ${version.version} (${version.gitcommit})`}, {title: `Ver ${version.version} (${version.gitcommit})`},
@ -96,10 +95,11 @@ const cssAppHeader = styled('div', `
width: 100%; width: 100%;
height: 100%; height: 100%;
align-items: center; align-items: center;
background-color: ${theme.leftPanelBg};
&, &:hover, &:focus { &, &:hover, &:focus {
text-decoration: none; text-decoration: none;
outline: none; outline: none;
color: ${colors.dark}; color: ${theme.text};
} }
`); `);
@ -124,6 +124,7 @@ const cssAppLogo = styled('a', `
`); `);
const cssDropdownIcon = styled(icon, ` const cssDropdownIcon = styled(icon, `
--icon-color: ${theme.text};
flex-shrink: 0; flex-shrink: 0;
margin-right: 8px; margin-right: 8px;
`); `);
@ -138,7 +139,7 @@ const cssOrg = styled('div', `
font-weight: 500; font-weight: 500;
&:hover { &:hover {
background-color: ${colors.mediumGrey}; background-color: ${theme.hover};
} }
.${cssLeftPane.className}-open & { .${cssLeftPane.className}-open & {

@ -105,6 +105,7 @@ function pagePanelsHome(owner: IDisposableOwner, appModel: AppModel, app: App) {
headerMain: createTopBarHome(appModel), headerMain: createTopBarHome(appModel),
contentMain: createDocMenu(pageModel), contentMain: createDocMenu(pageModel),
contentTop: buildHomeBanners(appModel), contentTop: buildHomeBanners(appModel),
testId,
}); });
} }

@ -11,10 +11,11 @@ import {FilterInfo} from 'app/client/models/entities/ViewSectionRec';
import {RowId, RowSource} from 'app/client/models/rowset'; import {RowId, RowSource} from 'app/client/models/rowset';
import {ColumnFilterFunc, SectionFilter} from 'app/client/models/SectionFilter'; import {ColumnFilterFunc, SectionFilter} from 'app/client/models/SectionFilter';
import {TableData} from 'app/client/models/TableData'; import {TableData} from 'app/client/models/TableData';
import {cssInput} from 'app/client/ui/cssInput';
import {basicButton, primaryButton} from 'app/client/ui2018/buttons'; import {basicButton, primaryButton} from 'app/client/ui2018/buttons';
import {cssLabel as cssCheckboxLabel, cssCheckboxSquare, cssLabelText, Indeterminate, labeledTriStateSquareCheckbox import {cssLabel as cssCheckboxLabel, cssCheckboxSquare, cssLabelText, Indeterminate, labeledTriStateSquareCheckbox
} from 'app/client/ui2018/checkbox'; } from 'app/client/ui2018/checkbox';
import {colors, vars} from 'app/client/ui2018/cssVars'; import {theme, vars} from 'app/client/ui2018/cssVars';
import {icon} from 'app/client/ui2018/icons'; import {icon} from 'app/client/ui2018/icons';
import {menuCssClass, menuDivider} from 'app/client/ui2018/menus'; import {menuCssClass, menuDivider} from 'app/client/ui2018/menus';
import {CellValue} from 'app/common/DocActions'; import {CellValue} from 'app/common/DocActions';
@ -593,7 +594,7 @@ const cssMenu = styled('div', `
max-width: 400px; max-width: 400px;
max-height: 90vh; max-height: 90vh;
outline: none; outline: none;
background-color: white; background-color: ${theme.menuBg};
padding-top: 0; padding-top: 0;
padding-bottom: 12px; padding-bottom: 12px;
`); `);
@ -608,15 +609,15 @@ const cssMenuHeader = styled('div', `
`); `);
const cssSelectAll = styled('div', ` const cssSelectAll = styled('div', `
display: flex; display: flex;
color: ${colors.lightGreen}; color: ${theme.controlFg};
cursor: default; cursor: default;
user-select: none; user-select: none;
&-disabled { &-disabled {
color: ${colors.slate}; color: ${theme.controlSecondaryFg};
} }
`); `);
const cssDotSeparator = styled('span', ` const cssDotSeparator = styled('span', `
color: ${colors.lightGreen}; color: ${theme.controlFg};
margin: 0 4px; margin: 0 4px;
user-select: none; user-select: none;
`); `);
@ -637,14 +638,13 @@ const cssMenuItem = styled('div', `
`); `);
export const cssItemValue = styled(cssLabelText, ` export const cssItemValue = styled(cssLabelText, `
margin-right: 12px; margin-right: 12px;
color: ${colors.dark};
white-space: pre; white-space: pre;
`); `);
const cssItemCount = styled('div', ` const cssItemCount = styled('div', `
flex-grow: 1; flex-grow: 1;
align-self: normal; align-self: normal;
text-align: right; text-align: right;
color: ${colors.slate}; color: ${theme.lightText};
`); `);
const cssMenuFooter = styled('div', ` const cssMenuFooter = styled('div', `
display: flex; display: flex;
@ -656,6 +656,8 @@ const cssApplyButton = styled(primaryButton, `
margin-right: 4px; margin-right: 4px;
`); `);
const cssSearch = styled(input, ` const cssSearch = styled(input, `
color: ${theme.inputFg};
background-color: ${theme.inputBg};
flex-grow: 1; flex-grow: 1;
min-width: 1px; min-width: 1px;
-webkit-appearance: none; -webkit-appearance: none;
@ -668,22 +670,26 @@ const cssSearch = styled(input, `
border: none; border: none;
outline: none; outline: none;
&::placeholder {
color: ${theme.inputPlaceholderFg};
}
`); `);
const cssSearchIcon = styled(icon, ` const cssSearchIcon = styled(icon, `
--icon-color: ${theme.lightText};
flex-shrink: 0; flex-shrink: 0;
margin-left: auto; margin-left: auto;
margin-right: 4px; margin-right: 4px;
`); `);
const cssNoResults = styled(cssMenuItem, ` const cssNoResults = styled(cssMenuItem, `
font-style: italic; font-style: italic;
color: ${colors.slate}; color: ${theme.lightText};
justify-content: center; justify-content: center;
`); `);
const cssSortIcon = styled(icon, ` const cssSortIcon = styled(icon, `
--icon-color: ${colors.slate}; --icon-color: ${theme.controlSecondaryFg};
margin-left: auto; margin-left: auto;
&-active { &-active {
--icon-color: ${colors.lightGreen} --icon-color: ${theme.controlFg}
} }
`); `);
const cssLabel = styled(cssCheckboxLabel, ` const cssLabel = styled(cssCheckboxLabel, `
@ -695,6 +701,7 @@ const cssToken = styled('div', `
margin-right: 12px; margin-right: 12px;
`); `);
const cssRangeHeader = styled(cssMenuItem, ` const cssRangeHeader = styled(cssMenuItem, `
color: ${theme.text};
padding: unset; padding: unset;
border-radius: 0 0 3px 0; border-radius: 0 0 3px 0;
text-transform: uppercase; text-transform: uppercase;
@ -704,14 +711,14 @@ const cssRangeHeader = styled(cssMenuItem, `
const cssRangeContainer = styled(cssMenuItem, ` const cssRangeContainer = styled(cssMenuItem, `
display: flex; display: flex;
justify-content: left; justify-content: left;
align-items: center;
column-gap: 10px; column-gap: 10px;
`); `);
const cssRangeInputSeparator = styled('span', ` const cssRangeInputSeparator = styled('span', `
font-weight: 600; font-weight: 600;
position: relative; color: ${theme.lightText};
top: 3px;
color: var(--grist-color-slate);
`); `);
const cssRangeInput = styled('input', ` const cssRangeInput = styled(cssInput, `
height: unset;
width: 120px; width: 120px;
`); `);

@ -5,7 +5,7 @@ import {urlState} from 'app/client/models/gristUrlState';
import {getTimeFromNow} from 'app/client/models/HomeModel'; import {getTimeFromNow} from 'app/client/models/HomeModel';
import {buildConfigContainer} from 'app/client/ui/RightPanel'; import {buildConfigContainer} from 'app/client/ui/RightPanel';
import {buttonSelect} from 'app/client/ui2018/buttonSelect'; import {buttonSelect} from 'app/client/ui2018/buttonSelect';
import {colors, testId, vars} from 'app/client/ui2018/cssVars'; import {testId, theme, vars} from 'app/client/ui2018/cssVars';
import {icon} from 'app/client/ui2018/icons'; import {icon} from 'app/client/ui2018/icons';
import {menu, menuAnnotate, menuItemLink} from 'app/client/ui2018/menus'; import {menu, menuAnnotate, menuItemLink} from 'app/client/ui2018/menus';
import {buildUrlId, parseUrlId} from 'app/common/gristUrls'; import {buildUrlId, parseUrlId} from 'app/common/gristUrls';
@ -108,7 +108,7 @@ export class DocHistory extends Disposable implements IDomComponent {
const cssSubTabs = styled('div', ` const cssSubTabs = styled('div', `
padding: 16px; padding: 16px;
border-bottom: 1px solid ${colors.mediumGrey}; border-bottom: 1px solid ${theme.pagePanelsBorder};
`); `);
const cssSnapshot = styled('div', ` const cssSnapshot = styled('div', `
@ -117,24 +117,25 @@ const cssSnapshot = styled('div', `
const cssSnapshotTime = styled('div', ` const cssSnapshotTime = styled('div', `
text-align: right; text-align: right;
color: ${colors.slate}; color: ${theme.lightText};
font-size: ${vars.smallFontSize}; font-size: ${vars.smallFontSize};
`); `);
const cssSnapshotCard = styled('div', ` const cssSnapshotCard = styled('div', `
border: 1px solid ${colors.mediumGrey}; border: 1px solid ${theme.documentHistorySnapshotBorder};
padding: 8px; padding: 8px;
background: white; color: ${theme.documentHistorySnapshotFg};
background: ${theme.documentHistorySnapshotBg};
border-radius: 8px; border-radius: 8px;
overflow: hidden; overflow: hidden;
display: flex; display: flex;
align-items: center; align-items: center;
--icon-color: ${colors.slate}; --icon-color: ${theme.controlSecondaryFg};
&-current { &-current {
background-color: ${colors.dark}; background-color: ${theme.documentHistorySnapshotSelectedBg};
color: ${colors.light}; color: ${theme.documentHistorySnapshotSelectedFg};
--icon-color: ${colors.light}; --icon-color: ${theme.documentHistorySnapshotSelectedFg};
} }
`); `);
@ -152,6 +153,6 @@ const cssMenuDots = styled('div', `
border-radius: 3px; border-radius: 3px;
cursor: default; cursor: default;
&:hover, &.weasel-popup-open { &:hover, &.weasel-popup-open {
background-color: ${colors.mediumGrey}; background-color: ${theme.hover};
} }
`); `);

@ -17,7 +17,7 @@ import {transition} from 'app/client/ui/transitions';
import {showWelcomeQuestions} from 'app/client/ui/WelcomeQuestions'; import {showWelcomeQuestions} from 'app/client/ui/WelcomeQuestions';
import {createVideoTourTextButton} from 'app/client/ui/OpenVideoTour'; import {createVideoTourTextButton} from 'app/client/ui/OpenVideoTour';
import {buttonSelect, cssButtonSelect} from 'app/client/ui2018/buttonSelect'; import {buttonSelect, cssButtonSelect} from 'app/client/ui2018/buttonSelect';
import {colors, isNarrowScreenObs} from 'app/client/ui2018/cssVars'; import {isNarrowScreenObs, theme} from 'app/client/ui2018/cssVars';
import {icon} from 'app/client/ui2018/icons'; import {icon} from 'app/client/ui2018/icons';
import {loadingSpinner} from 'app/client/ui2018/loaders'; import {loadingSpinner} from 'app/client/ui2018/loaders';
import {menu, menuItem, menuText, select} from 'app/client/ui2018/menus'; import {menu, menuItem, menuText, select} from 'app/client/ui2018/menus';
@ -84,7 +84,7 @@ function createLoadedDocMenu(owner: IDisposableOwner, home: HomeModel) {
// TODO: this is shown on all pages, but there is a hack in currentWSPinnedDocs that // TODO: this is shown on all pages, but there is a hack in currentWSPinnedDocs that
// removes all pinned docs when on trash page. // removes all pinned docs when on trash page.
dom.maybe((use) => use(home.currentWSPinnedDocs).length > 0, () => [ dom.maybe((use) => use(home.currentWSPinnedDocs).length > 0, () => [
css.docListHeader(css.docHeaderIconDark('PinBig'), 'Pinned Documents'), css.docListHeader(css.pinnedDocsIcon('PinBig'), 'Pinned Documents'),
createPinnedDocs(home, home.currentWSPinnedDocs), createPinnedDocs(home, home.currentWSPinnedDocs),
]), ]),
@ -393,7 +393,7 @@ function buildWorkspaceDocBlock(home: HomeModel, workspace: Workspace, flashDocI
// The flash value may change to true, and then immediately to false. We highlight it // The flash value may change to true, and then immediately to false. We highlight it
// using a transition, and scroll into view, when it turns back to false. // using a transition, and scroll into view, when it turns back to false.
transition(flash, { transition(flash, {
prepare(elem, val) { if (!val) { elem.style.backgroundColor = colors.slate.toString(); } }, prepare(elem, val) { if (!val) { elem.style.backgroundColor = theme.lightText.toString(); } },
run(elem, val) { if (!val) { elem.style.backgroundColor = ''; scrollIntoViewIfNeeded(elem); } }, run(elem, val) { if (!val) { elem.style.backgroundColor = ''; scrollIntoViewIfNeeded(elem); } },
}) })
), ),

@ -1,5 +1,5 @@
import {transientInput} from 'app/client/ui/transientInput'; import {transientInput} from 'app/client/ui/transientInput';
import {colors, mediaSmall, vars} from 'app/client/ui2018/cssVars'; import {mediaSmall, theme, vars} from 'app/client/ui2018/cssVars';
import {icon} from 'app/client/ui2018/icons'; import {icon} from 'app/client/ui2018/icons';
import {styled} from 'grainjs'; import {styled} from 'grainjs';
import {bigBasicButton} from 'app/client/ui2018/buttons'; import {bigBasicButton} from 'app/client/ui2018/buttons';
@ -41,7 +41,7 @@ export const docList = styled('div', `
const listHeader = styled('div', ` const listHeader = styled('div', `
min-height: 32px; min-height: 32px;
line-height: 32px; line-height: 32px;
color: ${colors.dark}; color: ${theme.text};
font-size: ${vars.xxxlargeFontSize}; font-size: ${vars.xxxlargeFontSize};
font-weight: ${vars.headerControlTextWeight}; font-weight: ${vars.headerControlTextWeight};
`); `);
@ -81,6 +81,7 @@ export const allDocsTemplates = styled('div', `
`); `);
export const docBlock = styled('div', ` export const docBlock = styled('div', `
color: ${theme.text};
max-width: 550px; max-width: 550px;
min-width: 300px; min-width: 300px;
margin-bottom: 28px; margin-bottom: 28px;
@ -96,6 +97,7 @@ export const templatesDocBlock = styled(docBlock, `
`); `);
export const otherSitesBlock = styled('div', ` export const otherSitesBlock = styled('div', `
color: ${theme.text};
margin-bottom: 32px; margin-bottom: 32px;
`); `);
@ -112,16 +114,18 @@ export const siteButton = styled(bigBasicButton, `
flex: 0 0 auto; flex: 0 0 auto;
`); `);
export const docHeaderIconDark = styled(icon, ` export const docHeaderIcon = styled(icon, `
margin-right: 8px; margin-right: 8px;
margin-top: -3px; margin-top: -3px;
--icon-color: ${theme.lightText};
`); `);
export const docHeaderIcon = styled(docHeaderIconDark, ` export const pinnedDocsIcon = styled(docHeaderIcon, `
--icon-color: ${colors.slate}; --icon-color: ${theme.text};
`); `);
export const featuredTemplatesIcon = styled(icon, ` export const featuredTemplatesIcon = styled(icon, `
--icon-color: ${theme.text};
margin-right: 8px; margin-right: 8px;
width: 20px; width: 20px;
height: 20px; height: 20px;
@ -141,7 +145,7 @@ const docBlockHeader = `
line-height: 40px; line-height: 40px;
margin-bottom: 8px; margin-bottom: 8px;
margin-right: -16px; margin-right: -16px;
color: ${colors.dark}; color: ${theme.text};
font-size: ${vars.mediumFontSize}; font-size: ${vars.mediumFontSize};
font-weight: bold; font-weight: bold;
&, &:hover, &:focus { &, &:hover, &:focus {
@ -156,6 +160,7 @@ export const docBlockHeaderLink = styled('a', docBlockHeader);
export const templateBlockHeader = styled('div', docBlockHeader); export const templateBlockHeader = styled('div', docBlockHeader);
export const wsLeft = styled('div', ` export const wsLeft = styled('div', `
color: ${theme.text};
flex: 1 0 50%; flex: 1 0 50%;
min-width: 0px; min-width: 0px;
margin-right: 24px; margin-right: 24px;
@ -166,11 +171,11 @@ export const docRowWrapper = styled('div', `
margin: 0px -16px 8px -16px; margin: 0px -16px 8px -16px;
border-radius: 3px; border-radius: 3px;
font-size: ${vars.mediumFontSize}; font-size: ${vars.mediumFontSize};
color: ${colors.dark}; color: ${theme.text};
--icon-color: ${colors.slate}; --icon-color: ${theme.lightText};
&:hover, &.weasel-popup-open, &-renaming { &:hover, &.weasel-popup-open, &-renaming {
background-color: ${colors.mediumGrey}; background-color: ${theme.hover};
} }
`); `);
@ -188,7 +193,7 @@ export const docRowLink = styled('a', `
color: inherit; color: inherit;
} }
&-no-access, &-no-access:hover, &-no-access:focus { &-no-access, &-no-access:hover, &-no-access:focus {
color: ${colors.slate}; color: ${theme.disabledText};
cursor: not-allowed; cursor: not-allowed;
} }
`); `);
@ -211,13 +216,13 @@ export const docName = styled('div', `
export const docPinIcon = styled(icon, ` export const docPinIcon = styled(icon, `
flex: none; flex: none;
margin-left: 4px; margin-left: 4px;
--icon-color: ${colors.lightGreen}; --icon-color: ${theme.accentIcon};
`); `);
export const docPublicIcon = styled(icon, ` export const docPublicIcon = styled(icon, `
flex: none; flex: none;
margin-left: auto; margin-left: auto;
--icon-color: ${colors.lightGreen}; --icon-color: ${theme.accentIcon};
`); `);
export const docEditorInput = styled(transientInput, ` export const docEditorInput = styled(transientInput, `
@ -231,7 +236,7 @@ export const docEditorInput = styled(transientInput, `
export const docRowUpdatedAt = styled('div', ` export const docRowUpdatedAt = styled('div', `
flex: 1 1 50%; flex: 1 1 50%;
color: ${colors.slate}; color: ${theme.lightText};
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
@ -247,20 +252,20 @@ export const docMenuTrigger = styled('div', `
line-height: 0px; line-height: 0px;
border-radius: 3px; border-radius: 3px;
cursor: default; cursor: default;
--icon-color: ${colors.darkGrey}; --icon-color: ${theme.docMenuDocOptionsFg};
.${docRowLink.className}:hover > & { .${docRowLink.className}:hover > & {
--icon-color: ${colors.slate}; --icon-color: ${theme.docMenuDocOptionsHoverFg};
} }
&:hover, &.weasel-popup-open { &:hover, &.weasel-popup-open {
background-color: ${colors.darkGrey}; background-color: ${theme.docMenuDocOptionsHoverBg};
--icon-color: ${colors.slate}; --icon-color: ${theme.docMenuDocOptionsHoverFg};
} }
`); `);
export const moveDocModalBody = styled('div', ` export const moveDocModalBody = styled('div', `
display: flex; display: flex;
flex-direction: column; flex-direction: column;
border-bottom: 1px solid ${colors.darkGrey}; border-bottom: 1px solid ${theme.modalBorderDark};
margin: 0 -64px; margin: 0 -64px;
height: 200px; height: 200px;
`); `);
@ -275,11 +280,11 @@ export const moveDocListItem = styled('div', `
font-size: ${vars.mediumFontSize}; font-size: ${vars.mediumFontSize};
&-selected { &-selected {
background-color: ${colors.lightGreen}; background-color: ${theme.moveDocsSelectedBg};
color: white; color: ${theme.moveDocsSelectedFg};
} }
&-disabled { &-disabled {
color: ${colors.darkGrey}; color: ${theme.moveDocsDisabledFg};
cursor: default; cursor: default;
} }
`); `);
@ -319,13 +324,14 @@ export const sortSelector = styled('div', `
line-height: unset; line-height: unset;
align-items: center; align-items: center;
border-radius: ${vars.controlBorderRadius}; border-radius: ${vars.controlBorderRadius};
color: ${colors.lightGreen}; color: ${theme.controlFg};
--icon-color: ${colors.lightGreen}; --icon-color: ${theme.controlFg};
background-color: unset;
&:focus, &:hover { &:focus, &:hover {
outline: none; outline: none;
box-shadow: none; box-shadow: none;
background-color: ${colors.mediumGrey}; background-color: ${theme.hover};
} }
@media ${mediaSmall} { @media ${mediaSmall} {
& { & {

@ -1,6 +1,6 @@
import {IExampleInfo} from 'app/client/ui/ExampleInfo'; import {IExampleInfo} from 'app/client/ui/ExampleInfo';
import {prepareForTransition, TransitionWatcher} from 'app/client/ui/transitions'; import {prepareForTransition, TransitionWatcher} from 'app/client/ui/transitions';
import {colors, mediaXSmall, testId, vars} from 'app/client/ui2018/cssVars'; import {mediaXSmall, testId, theme, vars} from 'app/client/ui2018/cssVars';
import {icon} from 'app/client/ui2018/icons'; import {icon} from 'app/client/ui2018/icons';
import {cssLink} from 'app/client/ui2018/links'; import {cssLink} from 'app/client/ui2018/links';
import {dom, styled} from 'grainjs'; import {dom, styled} from 'grainjs';
@ -98,8 +98,8 @@ const cssCard = styled('div', `
margin-right: 24px; margin-right: 24px;
max-width: 624px; max-width: 624px;
padding: 32px 56px 32px 32px; padding: 32px 56px 32px 32px;
background-color: white; background-color: ${theme.popupBg};
box-shadow: 0 2px 18px 0 rgba(31,37,50,0.31), 0 0 1px 0 rgba(76,86,103,0.24); box-shadow: 0 2px 18px 0 ${theme.popupInnerShadow}, 0 0 1px 0 ${theme.popupOuterShadow};
display: flex; display: flex;
overflow: hidden; overflow: hidden;
transition-property: opacity, transform; transition-property: opacity, transform;
@ -129,10 +129,12 @@ const cssImage = styled('img', `
`); `);
const cssBody = styled('div', ` const cssBody = styled('div', `
color: ${theme.text};
min-width: 0px; min-width: 0px;
`); `);
const cssTitle = styled('div', ` const cssTitle = styled('div', `
color: ${theme.text};
font-size: var(--title-font-size); font-size: var(--title-font-size);
font-weight: ${vars.headerControlTextWeight}; font-weight: ${vars.headerControlTextWeight};
margin-bottom: 16px; margin-bottom: 16px;
@ -165,10 +167,10 @@ export const cssCloseButton = styled('div', `
padding: 4px; padding: 4px;
border-radius: 4px; border-radius: 4px;
cursor: pointer; cursor: pointer;
--icon-color: ${colors.slate}; --icon-color: ${theme.popupCloseButtonFg};
&:hover { &:hover {
background-color: ${colors.mediumGreyOpaque}; background-color: ${theme.hover};
} }
`); `);

@ -5,7 +5,7 @@ import {buildHighlightedCode, cssCodeBlock} from 'app/client/ui/CodeHighlight';
import {cssBlockedCursor, cssLabel, cssRow} from 'app/client/ui/RightPanelStyles'; import {cssBlockedCursor, cssLabel, cssRow} from 'app/client/ui/RightPanelStyles';
import {buildFormulaTriggers} from 'app/client/ui/TriggerFormulas'; import {buildFormulaTriggers} from 'app/client/ui/TriggerFormulas';
import {textButton} from 'app/client/ui2018/buttons'; import {textButton} from 'app/client/ui2018/buttons';
import {colors, testId} from 'app/client/ui2018/cssVars'; import {testId, theme} from 'app/client/ui2018/cssVars';
import {textInput} from 'app/client/ui2018/editableLabel'; import {textInput} from 'app/client/ui2018/editableLabel';
import {cssIconButton, icon} from 'app/client/ui2018/icons'; import {cssIconButton, icon} from 'app/client/ui2018/icons';
import {IconName} from 'app/client/ui2018/IconList'; import {IconName} from 'app/client/ui2018/IconList';
@ -42,13 +42,13 @@ export function buildNameConfig(owner: MultiHolder, origColumn: ColumnRec, curso
cssRow( cssRow(
dom.cls(cssBlockedCursor.className, origColumn.disableModify), dom.cls(cssBlockedCursor.className, origColumn.disableModify),
cssColLabelBlock( cssColLabelBlock(
editor = textInput(fromKo(origColumn.label), editor = cssInput(fromKo(origColumn.label),
async val => { await origColumn.label.saveOnly(val); editedLabel.set(''); }, async val => { await origColumn.label.saveOnly(val); editedLabel.set(''); },
dom.on('input', (ev, elem) => { if (!untieColId.peek()) { editedLabel.set(elem.value); } }), dom.on('input', (ev, elem) => { if (!untieColId.peek()) { editedLabel.set(elem.value); } }),
dom.boolAttr('disabled', origColumn.disableModify), dom.boolAttr('disabled', origColumn.disableModify),
testId('field-label'), testId('field-label'),
), ),
textInput(editableColId, cssInput(editableColId,
saveColId, saveColId,
dom.boolAttr('disabled', use => use(origColumn.disableModify) || !use(origColumn.untieColIdFromLabel)), dom.boolAttr('disabled', use => use(origColumn.disableModify) || !use(origColumn.untieColIdFromLabel)),
cssCodeBlock.cls(''), cssCodeBlock.cls(''),
@ -332,10 +332,10 @@ export const cssFieldFormula = styled(buildHighlightedCode, `
cursor: pointer; cursor: pointer;
margin-top: 4px; margin-top: 4px;
padding-left: 24px; padding-left: 24px;
--icon-color: ${colors.lightGreen}; --icon-color: ${theme.accentIcon};
&-disabled-icon.formula_field_sidepane::before { &-disabled-icon.formula_field_sidepane::before {
--icon-color: ${colors.slate}; --icon-color: ${theme.lightText};
} }
&-disabled { &-disabled {
pointer-events: none; pointer-events: none;
@ -344,20 +344,20 @@ export const cssFieldFormula = styled(buildHighlightedCode, `
const cssToggleButton = styled(cssIconButton, ` const cssToggleButton = styled(cssIconButton, `
margin-left: 8px; margin-left: 8px;
background-color: var(--grist-color-medium-grey-opaque); background-color: ${theme.rightPanelToggleButtonDisabledBg};
box-shadow: inset 0 0 0 1px ${colors.darkGrey}; box-shadow: inset 0 0 0 1px ${theme.inputBorder};
&-selected, &-selected:hover { &-selected, &-selected:hover {
box-shadow: none; box-shadow: none;
background-color: ${colors.dark}; background-color: ${theme.rightPanelToggleButtonEnabledBg};
--icon-color: ${colors.light}; --icon-color: ${theme.rightPanelToggleButtonEnabledFg};
} }
&-selected:hover { &-selected:hover {
--icon-color: ${colors.darkGrey}; --icon-color: ${theme.rightPanelToggleButtonEnabledHoverFg};
} }
&-disabled, &-disabled:hover { &-disabled, &-disabled:hover {
--icon-color: ${colors.light}; --icon-color: ${theme.rightPanelToggleButtonDisabledFg};
background-color: var(--grist-color-medium-grey-opaque); background-color: ${theme.rightPanelToggleButtonDisabledBg};
} }
`); `);
@ -374,7 +374,7 @@ const cssColTieBlock = styled('div', `
const cssColTieConnectors = styled('div', ` const cssColTieConnectors = styled('div', `
position: absolute; position: absolute;
border: 2px solid var(--grist-color-dark-grey); border: 2px solid ${theme.inputBorder};
top: -9px; top: -9px;
bottom: -9px; bottom: -9px;
right: 11px; right: 11px;
@ -386,3 +386,18 @@ const cssColTieConnectors = styled('div', `
const cssEmptySeparator = styled('div', ` const cssEmptySeparator = styled('div', `
margin-top: 16px; margin-top: 16px;
`); `);
const cssInput = styled(textInput, `
color: ${theme.inputFg};
background-color: ${theme.mainPanelBg};
border: 1px solid ${theme.inputBorder};
&::placeholder {
color: ${theme.inputPlaceholderFg};
}
&:disabled {
background-color: ${theme.inputDisabledBg};
color: ${theme.inputDisabledFg};
}
`);

@ -3,7 +3,7 @@ import { ColumnRec, ViewFieldRec, ViewSectionRec } from "app/client/models/DocMo
import { FilterInfo } from "app/client/models/entities/ViewSectionRec"; import { FilterInfo } from "app/client/models/entities/ViewSectionRec";
import { attachColumnFilterMenu } from "app/client/ui/ColumnFilterMenu"; import { attachColumnFilterMenu } from "app/client/ui/ColumnFilterMenu";
import { cssButton, cssButtonGroup } from "app/client/ui2018/buttons"; import { cssButton, cssButtonGroup } from "app/client/ui2018/buttons";
import { colors, testId } from "app/client/ui2018/cssVars"; import { testId, theme } from "app/client/ui2018/cssVars";
import { icon } from "app/client/ui2018/icons"; import { icon } from "app/client/ui2018/icons";
import { menu, menuItemAsync } from "app/client/ui2018/menus"; import { menu, menuItemAsync } from "app/client/ui2018/menus";
import { dom, IDisposableOwner, IDomArgs, styled } from "grainjs"; import { dom, IDisposableOwner, IDomArgs, styled } from "grainjs";
@ -117,14 +117,14 @@ const cssBtn = styled('div', `
margin: 0 4px; margin: 0 4px;
} }
&-grayed { &-grayed {
color: ${colors.light}; color: ${theme.filterBarButtonSavedFg};
--icon-color: ${colors.light}; --icon-color: ${theme.filterBarButtonSavedFg};
background-color: ${colors.slate}; background-color: ${theme.filterBarButtonSavedBg};
border-color: ${colors.slate}; border-color: ${theme.filterBarButtonSavedBg};
} }
&-grayed:hover { &-grayed:hover {
background-color: ${colors.darkGrey}; background-color: ${theme.filterBarButtonSavedHoverBg};
border-color: ${colors.darkGrey}; border-color: ${theme.filterBarButtonSavedHoverBg};
} }
`); `);
const primaryButton = (...args: IDomArgs<HTMLDivElement>) => ( const primaryButton = (...args: IDomArgs<HTMLDivElement>) => (

@ -1,6 +1,6 @@
import { allCommands } from 'app/client/components/commands'; import { allCommands } from 'app/client/components/commands';
import { ViewFieldRec } from 'app/client/models/entities/ViewFieldRec'; import { ViewFieldRec } from 'app/client/models/entities/ViewFieldRec';
import { testId, vars } from 'app/client/ui2018/cssVars'; import { testId, theme } from 'app/client/ui2018/cssVars';
import { icon } from 'app/client/ui2018/icons'; import { icon } from 'app/client/ui2018/icons';
import { menuDivider, menuItem, menuItemCmd } from 'app/client/ui2018/menus'; import { menuDivider, menuItem, menuItemCmd } from 'app/client/ui2018/menus';
import { Sort } from 'app/common/SortSpec'; import { Sort } from 'app/common/SortSpec';
@ -297,9 +297,9 @@ const cssCustomMenuItem = styled('div', `
padding: 8px 8px; padding: 8px 8px;
display: flex; display: flex;
&:not(:hover) { &:not(:hover) {
background-color: white; background-color: ${theme.menuBg};
color: black; color: ${theme.menuItemFg};
--icon-color: black; --icon-color: ${theme.menuItemFg};
} }
&:last-of-type { &:last-of-type {
padding-right: 24px; padding-right: 24px;
@ -310,9 +310,9 @@ const cssCustomMenuItem = styled('div', `
flex: 1 0 auto; flex: 1 0 auto;
} }
&-selected, &-selected:not(:hover) { &-selected, &-selected:not(:hover) {
background-color: ${vars.primaryBg}; background-color: ${theme.menuItemSelectedBg};
color: white; color: ${theme.menuItemSelectedFg};
--icon-color: white; --icon-color: ${theme.menuItemSelectedFg};
} }
`); `);

@ -5,7 +5,7 @@ import * as css from 'app/client/ui/DocMenuCss';
import {createDocAndOpen, importDocAndOpen} from 'app/client/ui/HomeLeftPane'; import {createDocAndOpen, importDocAndOpen} from 'app/client/ui/HomeLeftPane';
import {manageTeamUsersApp} from 'app/client/ui/OpenUserManager'; import {manageTeamUsersApp} from 'app/client/ui/OpenUserManager';
import {bigBasicButton, cssButton} from 'app/client/ui2018/buttons'; import {bigBasicButton, cssButton} from 'app/client/ui2018/buttons';
import {testId, vars} from 'app/client/ui2018/cssVars'; import {testId, theme, vars} from 'app/client/ui2018/cssVars';
import {icon} from 'app/client/ui2018/icons'; import {icon} from 'app/client/ui2018/icons';
import {cssLink} from 'app/client/ui2018/links'; import {cssLink} from 'app/client/ui2018/links';
import {commonUrls, shouldHideUiElement} from 'app/common/gristUrls'; import {commonUrls, shouldHideUiElement} from 'app/common/gristUrls';
@ -131,6 +131,7 @@ function makeCreateButtons(homeModel: HomeModel) {
} }
const cssParagraph = styled(css.docBlock, ` const cssParagraph = styled(css.docBlock, `
color: ${theme.text};
line-height: 1.6; line-height: 1.6;
`); `);

@ -9,7 +9,7 @@ import {docImport, importFromPlugin} from 'app/client/ui/HomeImports';
import {cssLinkText, cssPageEntry, cssPageIcon, cssPageLink, cssSpacer} from 'app/client/ui/LeftPanelCommon'; import {cssLinkText, cssPageEntry, cssPageIcon, cssPageLink, cssSpacer} from 'app/client/ui/LeftPanelCommon';
import {createVideoTourToolsButton} from 'app/client/ui/OpenVideoTour'; import {createVideoTourToolsButton} from 'app/client/ui/OpenVideoTour';
import {transientInput} from 'app/client/ui/transientInput'; import {transientInput} from 'app/client/ui/transientInput';
import {colors, testId} from 'app/client/ui2018/cssVars'; import {testId, theme} from 'app/client/ui2018/cssVars';
import {icon} from 'app/client/ui2018/icons'; import {icon} from 'app/client/ui2018/icons';
import {menu, menuIcon, menuItem, upgradableMenuItem, upgradeText} from 'app/client/ui2018/menus'; import {menu, menuIcon, menuItem, upgradableMenuItem, upgradeText} from 'app/client/ui2018/menus';
import {confirmModal} from 'app/client/ui2018/modals'; import {confirmModal} from 'app/client/ui2018/modals';
@ -248,6 +248,7 @@ export const cssEditorInput = styled(transientInput, `
flex: 1 1 0px; flex: 1 1 0px;
min-width: 0px; min-width: 0px;
color: initial; color: initial;
background-color: ${theme.inputBg};
margin-right: 16px; margin-right: 16px;
font-size: inherit; font-size: inherit;
`); `);
@ -265,9 +266,9 @@ const cssMenuTrigger = styled('div', `
display: block; display: block;
} }
&:hover, &.weasel-popup-open { &:hover, &.weasel-popup-open {
background-color: ${colors.darkGrey}; background-color: ${theme.pageOptionsHoverBg};
} }
.${cssPageEntry.className}-selected &:hover, .${cssPageEntry.className}-selected &.weasel-popup-open { .${cssPageEntry.className}-selected &:hover, .${cssPageEntry.className}-selected &.weasel-popup-open {
background-color: ${colors.slate}; background-color: ${theme.pageOptionsSelectedHoverBg};
} }
`); `);

@ -15,7 +15,7 @@
*/ */
import {beaconOpenMessage} from 'app/client/lib/helpScout'; import {beaconOpenMessage} from 'app/client/lib/helpScout';
import {AppModel} from 'app/client/models/AppModel'; import {AppModel} from 'app/client/models/AppModel';
import {colors, testId, vars} from 'app/client/ui2018/cssVars'; import {testId, theme, vars} from 'app/client/ui2018/cssVars';
import {icon} from 'app/client/ui2018/icons'; import {icon} from 'app/client/ui2018/icons';
import {commonUrls, shouldHideUiElement} from 'app/common/gristUrls'; import {commonUrls, shouldHideUiElement} from 'app/common/gristUrls';
import {dom, DomContents, Observable, styled} from 'grainjs'; import {dom, DomContents, Observable, styled} from 'grainjs';
@ -83,7 +83,7 @@ export const cssTools = styled('div', `
export const cssSectionHeader = styled('div', ` export const cssSectionHeader = styled('div', `
margin: 24px 0 8px 24px; margin: 24px 0 8px 24px;
color: ${colors.slate}; color: ${theme.lightText};
text-transform: uppercase; text-transform: uppercase;
font-weight: 500; font-weight: 500;
font-size: ${vars.xsmallFontSize}; font-size: ${vars.xsmallFontSize};
@ -96,22 +96,22 @@ export const cssSectionHeader = styled('div', `
export const cssPageEntry = styled('div', ` export const cssPageEntry = styled('div', `
margin: 0px 16px 0px 0px; margin: 0px 16px 0px 0px;
border-radius: 0 3px 3px 0; border-radius: 0 3px 3px 0;
color: ${colors.dark}; color: ${theme.text};
--icon-color: ${colors.slate}; --icon-color: ${theme.lightText};
cursor: default; cursor: default;
&:hover, &.weasel-popup-open, &-renaming { &:hover, &.weasel-popup-open, &-renaming {
background-color: ${colors.mediumGrey}; background-color: ${theme.pageHoverBg};
} }
&-selected, &-selected:hover, &-selected.weasel-popup-open { &-selected, &-selected:hover, &-selected.weasel-popup-open {
background-color: ${colors.darkBg}; background-color: ${theme.activePageBg};
color: ${colors.light}; color: ${theme.activePageFg};
--icon-color: ${colors.light}; --icon-color: ${theme.activePageFg};
} }
&-disabled, &-disabled:hover, &-disabled.weasel-popup-open { &-disabled, &-disabled:hover, &-disabled.weasel-popup-open {
background-color: initial; background-color: initial;
color: ${colors.darkGrey}; color: ${theme.disabledPageFg};
--icon-color: ${colors.darkGrey}; --icon-color: ${theme.disabledPageFg};
} }
.${cssTools.className}-collapsed > & { .${cssTools.className}-collapsed > & {
margin-right: 0; margin-right: 0;
@ -171,12 +171,12 @@ export const cssPageEntryMain = styled(cssPageEntry, `
export const cssPageEntrySmall = styled(cssPageEntry, ` export const cssPageEntrySmall = styled(cssPageEntry, `
flex: none; flex: none;
border-radius: 3px; border-radius: 3px;
--icon-color: ${colors.lightGreen}; --icon-color: ${theme.controlFg};
& > .${cssPageLink.className} { & > .${cssPageLink.className} {
padding: 0 8px 0 16px; padding: 0 8px 0 16px;
} }
&:hover { &:hover {
--icon-color: ${colors.darkGreen}; --icon-color: ${theme.controlHoverFg};
} }
.${cssTools.className}-collapsed & { .${cssTools.className}-collapsed & {
display: none; display: none;

@ -9,7 +9,7 @@ import {getWorkspaceInfo, ownerName, workspaceName} from 'app/client/models/Work
import {cssInput} from 'app/client/ui/cssInput'; import {cssInput} from 'app/client/ui/cssInput';
import {bigBasicButton, bigPrimaryButtonLink} from 'app/client/ui2018/buttons'; import {bigBasicButton, bigPrimaryButtonLink} from 'app/client/ui2018/buttons';
import {labeledSquareCheckbox} from 'app/client/ui2018/checkbox'; import {labeledSquareCheckbox} from 'app/client/ui2018/checkbox';
import {colors, testId, vars} from 'app/client/ui2018/cssVars'; import {testId, theme, vars} from 'app/client/ui2018/cssVars';
import {loadingSpinner} from 'app/client/ui2018/loaders'; import {loadingSpinner} from 'app/client/ui2018/loaders';
import {select} from 'app/client/ui2018/menus'; import {select} from 'app/client/ui2018/menus';
import {confirmModal, cssModalBody, cssModalButtons, cssModalWidth, modal, saveModal} from 'app/client/ui2018/modals'; import {confirmModal, cssModalBody, cssModalButtons, cssModalWidth, modal, saveModal} from 'app/client/ui2018/modals';
@ -284,7 +284,7 @@ export const cssField = styled('div', `
export const cssLabel = styled('label', ` export const cssLabel = styled('label', `
font-weight: normal; font-weight: normal;
font-size: ${vars.mediumFontSize}; font-size: ${vars.mediumFontSize};
color: ${colors.dark}; color: ${theme.text};
margin: 8px 16px 0 0; margin: 8px 16px 0 0;
white-space: nowrap; white-space: nowrap;
width: 80px; width: 80px;
@ -292,7 +292,7 @@ export const cssLabel = styled('label', `
`); `);
const cssWarningText = styled('div', ` const cssWarningText = styled('div', `
color: red; color: ${theme.errorText};
margin-top: 8px; margin-top: 8px;
`); `);

@ -1,6 +1,6 @@
import { dom, DomArg, IDisposableOwner, styled } from "grainjs"; import { theme } from "app/client/ui2018/cssVars";
import { icon } from "app/client/ui2018/icons"; import { icon } from "app/client/ui2018/icons";
import { colors } from "app/client/ui2018/cssVars"; import { dom, DomArg, IDisposableOwner, styled } from "grainjs";
/** /**
* Creates a toggle button - little square button with a dropdown icon inside, used * Creates a toggle button - little square button with a dropdown icon inside, used
@ -15,18 +15,18 @@ export function menuToggle(obs: IDisposableOwner, ...args: DomArg[]) {
} }
const cssMenuToggle = styled('div.menu_toggle', ` const cssMenuToggle = styled('div.menu_toggle', `
background: white; background: ${theme.menuToggleBg};
cursor: pointer; cursor: pointer;
--icon-color: ${colors.slate}; --icon-color: ${theme.menuToggleFg};
border: 1px solid ${colors.slate}; border: 1px solid ${theme.menuToggleBorder};
border-radius: 4px; border-radius: 4px;
&:hover { &:hover {
--icon-color: ${colors.darkGreen}; --icon-color: ${theme.menuToggleHoverFg};
border-color: ${colors.darkGreen}; border-color: ${theme.menuToggleHoverFg};
} }
&:active { &:active {
--icon-color: ${colors.darkerGreen}; --icon-color: ${theme.menuToggleActiveFg};
border-color: ${colors.darkerGreen}; border-color: ${theme.menuToggleActiveFg};
} }
& > .menu_toggle_icon { & > .menu_toggle_icon {
display: block; /* don't create a line */ display: block; /* don't create a line */

@ -4,7 +4,7 @@ import {ConnectState} from 'app/client/models/ConnectState';
import {urlState} from 'app/client/models/gristUrlState'; import {urlState} from 'app/client/models/gristUrlState';
import {Expirable, IAppError, Notification, Notifier, NotifyAction, Progress} from 'app/client/models/NotifyModel'; import {Expirable, IAppError, Notification, Notifier, NotifyAction, Progress} from 'app/client/models/NotifyModel';
import {cssHoverCircle, cssTopBarBtn} from 'app/client/ui/TopBarCss'; import {cssHoverCircle, cssTopBarBtn} from 'app/client/ui/TopBarCss';
import {colors, vars} from 'app/client/ui2018/cssVars'; import {theme, vars} from 'app/client/ui2018/cssVars';
import {icon} from 'app/client/ui2018/icons'; import {icon} from 'app/client/ui2018/icons';
import {IconName} from "app/client/ui2018/IconList"; import {IconName} from "app/client/ui2018/IconList";
import {menuCssClass} from 'app/client/ui2018/menus'; import {menuCssClass} from 'app/client/ui2018/menus';
@ -193,8 +193,8 @@ function buildConnectStateButton(state: ConnectState): Element {
const cssDropdownWrapper = styled('div', ` const cssDropdownWrapper = styled('div', `
background-color: white; background-color: ${theme.notificationsPanelBodyBg};
border: 1px solid ${colors.darkGrey}; border: 1px solid ${theme.notificationsPanelBorder};
padding: 0px; padding: 0px;
`); `);
@ -208,17 +208,18 @@ const cssDropdownHeader = styled('div', `
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 24px; padding: 24px;
background-color: ${colors.lightGrey}; background-color: ${theme.notificationsPanelHeaderBg};
outline: 1px solid ${colors.darkGrey}; outline: 1px solid ${theme.notificationsPanelBorder};
`); `);
const cssDropdownHeaderTitle = styled('span', ` const cssDropdownHeaderTitle = styled('span', `
color: ${theme.text};
font-weight: bold; font-weight: bold;
`); `);
const cssDropdownFeedbackLink = styled('div', ` const cssDropdownFeedbackLink = styled('div', `
display: flex; display: flex;
color: ${colors.lightGreen}; color: ${theme.controlFg};
cursor: pointer; cursor: pointer;
user-select: none; user-select: none;
&:hover { &:hover {
@ -227,21 +228,21 @@ const cssDropdownFeedbackLink = styled('div', `
`); `);
const cssDropdownFeedbackIcon = styled(icon, ` const cssDropdownFeedbackIcon = styled(icon, `
background-color: ${colors.lightGreen}; background-color: ${theme.controlFg};
margin-right: 4px; margin-right: 4px;
`); `);
const cssDropdownStatus = styled('div', ` const cssDropdownStatus = styled('div', `
padding: 16px 48px 24px 48px; padding: 16px 48px 24px 48px;
text-align: center; text-align: center;
border-top: 1px solid ${colors.darkGrey}; border-top: 1px solid ${theme.notificationsPanelBorder};
`); `);
const cssDropdownStatusText = styled('div', ` const cssDropdownStatusText = styled('div', `
display: inline-block; display: inline-block;
margin: 8px 0 0 0; margin: 8px 0 0 0;
text-align: left; text-align: left;
color: ${colors.slate}; color: ${theme.lightText};
`); `);
// z-index below is set above other assorted children of <body> which include z-index such as 999 // z-index below is set above other assorted children of <body> which include z-index such as 999
@ -279,7 +280,7 @@ const cssToastActions = styled('div', `
display: flex; display: flex;
align-items: flex-end; align-items: flex-end;
margin-top: 16px; margin-top: 16px;
color: ${colors.lightGreen}; color: ${theme.toastControlFg};
`); `);
const cssToastWrapper = styled('div', ` const cssToastWrapper = styled('div', `
@ -292,8 +293,8 @@ const cssToastWrapper = styled('div', `
padding: 12px; padding: 12px;
border-radius: 3px; border-radius: 3px;
color: ${colors.light}; color: ${theme.toastText};
background-color: ${vars.toastBg}; background-color: ${theme.toastBg};
pointer-events: auto; pointer-events: auto;
@ -301,28 +302,28 @@ const cssToastWrapper = styled('div', `
transition: opacity ${Expirable.fadeDelay}ms; transition: opacity ${Expirable.fadeDelay}ms;
&-error { &-error {
border-left: 6px solid ${colors.error}; border-left: 6px solid ${theme.toastErrorBg};
padding-left: 6px; padding-left: 6px;
--icon-color: ${colors.error}; --icon-color: ${theme.toastErrorIcon};
} }
&-success { &-success {
border-left: 6px solid ${colors.darkGreen}; border-left: 6px solid ${theme.toastSuccessBg};
padding-left: 6px; padding-left: 6px;
--icon-color: ${colors.darkGreen}; --icon-color: ${theme.toastSuccessIcon};
} }
&-warning { &-warning {
border-left: 6px solid ${colors.warningBg}; border-left: 6px solid ${theme.toastWarningBg};
padding-left: 6px; padding-left: 6px;
--icon-color: ${colors.warning}; --icon-color: ${theme.toastWarningIcon};
} }
&-info { &-info {
border-left: 6px solid ${colors.lightBlue}; border-left: 6px solid ${theme.toastInfoBg};
padding-left: 6px; padding-left: 6px;
--icon-color: ${colors.lightBlue}; --icon-color: ${theme.toastInfoIcon};
} }
&-info .${cssToastActions.className} { &-info .${cssToastActions.className} {
color: ${colors.lighterBlue}; color: ${theme.toastInfoControlFg};
} }
&-left-icon { &-left-icon {
@ -340,9 +341,9 @@ const cssToastWrapper = styled('div', `
} }
.${cssDropdownContent.className} > & { .${cssDropdownContent.className} > & {
background-color: unset; background-color: unset;
color: unset; color: ${theme.text};
border-radius: 0px; border-radius: 0px;
border-top: 1px solid ${colors.darkGrey}; border-top: 1px solid ${theme.notificationsPanelBorder};
margin: 0px; margin: 0px;
padding: 16px 20px; padding: 16px 20px;
} }
@ -389,8 +390,8 @@ const cssToastMemos = styled('div', `
const cssToastMemo = styled('div', ` const cssToastMemo = styled('div', `
margin: 3px; margin: 3px;
color: ${colors.dark}; color: ${theme.text};
background: ${colors.light}; background: ${theme.notificationsPanelBodyBg};
padding: 3px; padding: 3px;
`); `);
@ -399,16 +400,16 @@ const cssProgressBarWrapper = styled('div', `
margin-bottom: 11px; margin-bottom: 11px;
height: 3px; height: 3px;
border-radius: 3px; border-radius: 3px;
background-color: ${colors.light}; background-color: ${theme.progressBarBg};
`); `);
const cssProgressBarSize = styled('span', ` const cssProgressBarSize = styled('span', `
color: ${colors.slate}; color: ${theme.toastLightText};
`); `);
const cssProgressBarStatus = styled('div', ` const cssProgressBarStatus = styled('div', `
height: 3px; height: 3px;
min-width: 3px; min-width: 3px;
border-radius: 3px; border-radius: 3px;
background-color: ${colors.lightGreen}; background-color: ${theme.progressBarFg};
`); `);

@ -27,7 +27,7 @@ import { createPopper, Placement } from '@popperjs/core';
import { FocusLayer } from 'app/client/lib/FocusLayer'; import { FocusLayer } from 'app/client/lib/FocusLayer';
import * as Mousetrap from 'app/client/lib/Mousetrap'; import * as Mousetrap from 'app/client/lib/Mousetrap';
import { bigBasicButton, bigPrimaryButton } from "app/client/ui2018/buttons"; import { bigBasicButton, bigPrimaryButton } from "app/client/ui2018/buttons";
import { colors, vars } from "app/client/ui2018/cssVars"; import { theme, vars } from "app/client/ui2018/cssVars";
import range = require("lodash/range"); import range = require("lodash/range");
import {IGristUrlState} from "app/common/gristUrls"; import {IGristUrlState} from "app/common/gristUrls";
import {urlState} from "app/client/models/gristUrlState"; import {urlState} from "app/client/models/gristUrlState";
@ -321,13 +321,13 @@ function buildArrow() {
const Container = styled('div', ` const Container = styled('div', `
align-self: center; align-self: center;
border: 2px solid ${colors.lightGreen}; border: 2px solid ${theme.accentBorder};
border-radius: 3px; border-radius: 3px;
z-index: 1000; z-index: 1000;
max-width: 490px; max-width: 490px;
position: relative; position: relative;
background-color: white; background-color: ${theme.popupBg};
box-shadow: 0 2px 18px 0 rgba(31,37,50,0.31), 0 0 1px 0 rgba(76,86,103,0.24); box-shadow: 0 2px 18px 0 ${theme.popupInnerShadow}, 0 0 1px 0 ${theme.popupOuterShadow};
outline: unset; outline: unset;
`); `);
@ -339,9 +339,9 @@ const ArrowContainer = styled('div', `
position: absolute; position: absolute;
& path { & path {
stroke: ${colors.lightGreen}; stroke: ${theme.accentBorder};
stroke-width: 2px; stroke-width: 2px;
fill: white; fill: ${theme.popupBg};
} }
${sideSelectorChunk('top')} > & { ${sideSelectorChunk('top')} > & {
@ -376,7 +376,7 @@ const ArrowContainer = styled('div', `
const ContentWrapper = styled('div', ` const ContentWrapper = styled('div', `
position: relative; position: relative;
padding: 32px; padding: 32px;
background-color: white; background-color: ${theme.popupBg};
`); `);
const Footer = styled('div', ` const Footer = styled('div', `
@ -402,9 +402,9 @@ const Dot = styled('div', `
border-radius: 3px; border-radius: 3px;
margin-right: 12px; margin-right: 12px;
align-self: center; align-self: center;
background-color: ${colors.lightGreen}; background-color: ${theme.progressBarFg};
&-done { &-done {
background-color: ${colors.darkGrey}; background-color: ${theme.progressBarBg};
} }
`); `);
@ -424,10 +424,11 @@ const Overlay = styled('div', `
const cssTitle = styled('div', ` const cssTitle = styled('div', `
font-size: ${vars.xxxlargeFontSize}; font-size: ${vars.xxxlargeFontSize};
font-weight: ${vars.headerControlTextWeight}; font-weight: ${vars.headerControlTextWeight};
color: ${colors.dark}; color: ${theme.text};
margin: 0 0 16px 0; margin: 0 0 16px 0;
line-height: 32px; line-height: 32px;
`); `);
const cssBody = styled('div', ` const cssBody = styled('div', `
color: ${theme.text};
`); `);

@ -1,6 +1,6 @@
import * as commands from 'app/client/components/commands'; import * as commands from 'app/client/components/commands';
import {cssLinkText, cssPageEntryMain, cssPageIcon, cssPageLink} from 'app/client/ui/LeftPanelCommon'; import {cssLinkText, cssPageEntryMain, cssPageIcon, cssPageLink} from 'app/client/ui/LeftPanelCommon';
import {colors} from 'app/client/ui2018/cssVars'; import {theme} from 'app/client/ui2018/cssVars';
import {icon} from 'app/client/ui2018/icons'; import {icon} from 'app/client/ui2018/icons';
import {modal} from 'app/client/ui2018/modals'; import {modal} from 'app/client/ui2018/modals';
import {commonUrls, shouldHideUiElement} from 'app/common/gristUrls'; import {commonUrls, shouldHideUiElement} from 'app/common/gristUrls';
@ -106,21 +106,21 @@ const cssVideo = styled('iframe', `
`); `);
const cssVideoTourTextButton = styled('div', ` const cssVideoTourTextButton = styled('div', `
color: ${colors.lightGreen}; color: ${theme.controlFg};
cursor: pointer; cursor: pointer;
&:hover { &:hover {
color: ${colors.darkGreen}; color: ${theme.controlHoverFg};
} }
`); `);
const cssVideoIcon = styled(icon, ` const cssVideoIcon = styled(icon, `
background-color: ${colors.lightGreen}; background-color: ${theme.controlFg};
cursor: pointer; cursor: pointer;
margin: 0px 4px 3px 0; margin: 0px 4px 3px 0;
.${cssVideoTourTextButton.className}:hover > & { .${cssVideoTourTextButton.className}:hover > & {
background-color: ${colors.darkGreen}; background-color: ${theme.controlHoverFg};
} }
`); `);
@ -130,10 +130,10 @@ const cssCloseButton = styled('div', `
padding: 4px; padding: 4px;
border-radius: 4px; border-radius: 4px;
cursor: pointer; cursor: pointer;
--icon-color: ${colors.slate}; --icon-color: ${theme.modalCloseButtonFg};
&:hover { &:hover {
background-color: ${colors.mediumGreyOpaque}; background-color: ${theme.hover};
} }
`); `);

@ -6,7 +6,7 @@ import {watchElementForBlur} from 'app/client/lib/FocusLayer';
import {urlState} from "app/client/models/gristUrlState"; import {urlState} from "app/client/models/gristUrlState";
import {resizeFlexVHandle} from 'app/client/ui/resizeHandle'; import {resizeFlexVHandle} from 'app/client/ui/resizeHandle';
import {transition, TransitionWatcher} from 'app/client/ui/transitions'; import {transition, TransitionWatcher} from 'app/client/ui/transitions';
import {colors, cssHideForNarrowScreen, isScreenResizing, mediaNotSmall, mediaSmall} from 'app/client/ui2018/cssVars'; import {cssHideForNarrowScreen, isScreenResizing, mediaNotSmall, mediaSmall, theme} from 'app/client/ui2018/cssVars';
import {isNarrowScreenObs} from 'app/client/ui2018/cssVars'; import {isNarrowScreenObs} from 'app/client/ui2018/cssVars';
import {icon} from 'app/client/ui2018/icons'; import {icon} from 'app/client/ui2018/icons';
import {dom, DomArg, MultiHolder, noTestId, Observable, styled, subscribe, TestId} from "grainjs"; import {dom, DomArg, MultiHolder, noTestId, Observable, styled, subscribe, TestId} from "grainjs";
@ -102,7 +102,7 @@ export function pagePanels(page: PageContents) {
testId('left-panel'), testId('left-panel'),
cssOverflowContainer( cssOverflowContainer(
contentWrapper = cssLeftPanelContainer( contentWrapper = cssLeftPanelContainer(
cssTopHeader(left.header), cssLeftPaneHeader(left.header),
left.content, left.content,
), ),
), ),
@ -264,7 +264,7 @@ export function pagePanels(page: PageContents) {
cssRightPane( cssRightPane(
testId('right-panel'), testId('right-panel'),
cssTopHeader(right.header), cssRightPaneHeader(right.header),
right.content, right.content,
dom.style('width', (use) => use(right.panelOpen) ? use(right.panelWidth) + 'px' : ''), dom.style('width', (use) => use(right.panelOpen) ? use(right.panelWidth) + 'px' : ''),
@ -341,7 +341,7 @@ const cssPageContainer = styled(cssVBox, `
right: 0; right: 0;
bottom: 0; bottom: 0;
min-width: 600px; min-width: 600px;
background-color: ${colors.lightGrey}; background-color: ${theme.pageBg};
@media ${mediaSmall} { @media ${mediaSmall} {
& { & {
@ -359,7 +359,7 @@ const cssContentMain = styled(cssHBox, `
`); `);
export const cssLeftPane = styled(cssVBox, ` export const cssLeftPane = styled(cssVBox, `
position: relative; position: relative;
background-color: ${colors.lightGrey}; background-color: ${theme.leftPanelBg};
width: 48px; width: 48px;
margin-right: 0px; margin-right: 0px;
transition: width 0.4s; transition: width 0.4s;
@ -415,7 +415,7 @@ const cssMainPane = styled(cssVBox, `
position: relative; position: relative;
flex: 1 1 0px; flex: 1 1 0px;
min-width: 0px; min-width: 0px;
background-color: white; background-color: ${theme.mainPanelBg};
z-index: 1; z-index: 1;
&-left-overlap { &-left-overlap {
margin-left: 48px; margin-left: 48px;
@ -423,7 +423,7 @@ const cssMainPane = styled(cssVBox, `
`); `);
const cssRightPane = styled(cssVBox, ` const cssRightPane = styled(cssVBox, `
position: relative; position: relative;
background-color: ${colors.lightGrey}; background-color: ${theme.rightPanelBg};
width: 0px; width: 0px;
margin-left: 0px; margin-left: 0px;
overflow: hidden; overflow: hidden;
@ -461,13 +461,13 @@ const cssRightPane = styled(cssVBox, `
display: none; display: none;
} }
`); `);
const cssTopHeader = styled('div', ` const cssHeader = styled('div', `
height: 48px; height: 49px;
flex: none; flex: none;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
border-bottom: 1px solid ${colors.mediumGrey}; border-bottom: 1px solid ${theme.pagePanelsBorder};
@media print { @media print {
& { & {
@ -479,9 +479,18 @@ const cssTopHeader = styled('div', `
display: none; display: none;
} }
`); `);
const cssTopHeader = styled(cssHeader, `
background-color: ${theme.topHeaderBg};
`);
const cssLeftPaneHeader = styled(cssHeader, `
background-color: ${theme.leftPanelBg};
`);
const cssRightPaneHeader = styled(cssHeader, `
background-color: ${theme.rightPanelBg};
`);
const cssBottomFooter = styled ('div', ` const cssBottomFooter = styled ('div', `
height: ${bottomFooterHeightPx}px; height: ${bottomFooterHeightPx}px;
background-color: white; background-color: ${theme.bottomFooterBg};
z-index: 20; z-index: 20;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
@ -492,7 +501,7 @@ const cssBottomFooter = styled ('div', `
bottom: 0; bottom: 0;
left: 0; left: 0;
right: 0; right: 0;
border-top: 1px solid ${colors.mediumGrey}; border-top: 1px solid ${theme.pagePanelsBorder};
@media ${mediaNotSmall} { @media ${mediaNotSmall} {
& { & {
display: none; display: none;
@ -508,8 +517,8 @@ const cssBottomFooter = styled ('div', `
} }
`); `);
const cssResizeFlexVHandle = styled(resizeFlexVHandle, ` const cssResizeFlexVHandle = styled(resizeFlexVHandle, `
--resize-handle-color: ${colors.mediumGrey}; --resize-handle-color: ${theme.pagePanelsBorder};
--resize-handle-highlight: ${colors.lightGreen}; --resize-handle-highlight: ${theme.pagePanelsBorderResizing};
@media print { @media print {
& { & {
@ -521,7 +530,7 @@ const cssResizeDisabledBorder = styled('div', `
flex: none; flex: none;
width: 1px; width: 1px;
height: 100%; height: 100%;
background-color: ${colors.mediumGrey}; background-color: ${theme.pagePanelsBorder};
position: absolute; position: absolute;
top: 0; top: 0;
bottom: 0; bottom: 0;
@ -535,20 +544,20 @@ const cssPanelOpener = styled(icon, `
padding: 8px 8px; padding: 8px 8px;
cursor: pointer; cursor: pointer;
-webkit-mask-size: 16px 16px; -webkit-mask-size: 16px 16px;
background-color: ${colors.lightGreen}; background-color: ${theme.controlFg};
transition: transform 0.4s; transition: transform 0.4s;
&:hover { background-color: ${colors.darkGreen}; } &:hover { background-color: ${theme.controlHoverFg}; }
&-open { transform: rotateY(180deg); } &-open { transform: rotateY(180deg); }
`); `);
const cssPanelOpenerNarrowScreenBtn = styled('div', ` const cssPanelOpenerNarrowScreenBtn = styled('div', `
width: 32px; width: 32px;
height: 32px; height: 32px;
--icon-color: ${colors.slate}; --icon-color: ${theme.sidePanelOpenerFg};
cursor: pointer; cursor: pointer;
border-radius: 4px; border-radius: 4px;
&-open { &-open {
background-color: ${colors.lightGreen}; background-color: ${theme.sidePanelOpenerActiveBg};
--icon-color: white; --icon-color: ${theme.sidePanelOpenerActiveFg};
} }
`); `);
const cssPanelOpenerNarrowScreen = styled(icon, ` const cssPanelOpenerNarrowScreen = styled(icon, `
@ -562,7 +571,7 @@ const cssContentOverlay = styled('div', `
left: 0; left: 0;
bottom: 0; bottom: 0;
right: 0; right: 0;
background-color: grey; background-color: ${theme.pageBackdrop};
opacity: 0.5; opacity: 0.5;
display: none; display: none;
z-index: 9; z-index: 9;

@ -3,7 +3,7 @@ import { ColumnRec, DocModel, TableRec, ViewSectionRec } from 'app/client/models
import { linkId, NoLink } from 'app/client/ui/selectBy'; import { linkId, NoLink } from 'app/client/ui/selectBy';
import { getWidgetTypes, IWidgetType } from 'app/client/ui/widgetTypes'; import { getWidgetTypes, IWidgetType } from 'app/client/ui/widgetTypes';
import { bigPrimaryButton } from "app/client/ui2018/buttons"; import { bigPrimaryButton } from "app/client/ui2018/buttons";
import { colors, vars } from "app/client/ui2018/cssVars"; import { theme, vars } from "app/client/ui2018/cssVars";
import { icon } from "app/client/ui2018/icons"; import { icon } from "app/client/ui2018/icons";
import { spinnerModal } from 'app/client/ui2018/modals'; import { spinnerModal } from 'app/client/ui2018/modals';
import { isLongerThan, nativeCompare } from "app/common/gutil"; import { isLongerThan, nativeCompare } from "app/common/gutil";
@ -421,15 +421,15 @@ function header(label: string) {
} }
const cssContainer = styled('div', ` const cssContainer = styled('div', `
--outline: 1px solid rgba(217,217,217,0.60); --outline: 1px solid ${theme.widgetPickerBorder};
max-height: 386px; max-height: 386px;
box-shadow: 0 2px 20px 0 rgba(38,38,51,0.20); box-shadow: 0 2px 20px 0 ${theme.widgetPickerShadow};
border-radius: 2px; border-radius: 2px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
user-select: none; user-select: none;
background-color: white; background-color: ${theme.widgetPickerPrimaryBg};
`); `);
const cssPopupWrapper = styled('div', ` const cssPopupWrapper = styled('div', `
@ -450,17 +450,19 @@ const cssPanel = styled('div', `
overflow: auto; overflow: auto;
padding-bottom: 18px; padding-bottom: 18px;
&:nth-of-type(2n) { &:nth-of-type(2n) {
background-color: ${colors.lightGrey}; background-color: ${theme.widgetPickerSecondaryBg};
outline: var(--outline); outline: var(--outline);
} }
`); `);
const cssHeader = styled('div', ` const cssHeader = styled('div', `
color: ${theme.text};
margin: 24px 0 24px 24px; margin: 24px 0 24px 24px;
font-size: ${vars.mediumFontSize}; font-size: ${vars.mediumFontSize};
`); `);
const cssEntry = styled('div', ` const cssEntry = styled('div', `
color: ${theme.widgetPickerItemFg};
padding: 0 0 0 24px; padding: 0 0 0 24px;
height: 32px; height: 32px;
display: flex; display: flex;
@ -471,10 +473,10 @@ const cssEntry = styled('div', `
overflow: hidden; overflow: hidden;
cursor: pointer; cursor: pointer;
&-selected { &-selected {
background-color: ${colors.mediumGrey}; background-color: ${theme.widgetPickerItemSelectedBg};
} }
&-disabled { &-disabled {
color: ${colors.mediumGrey}; color: ${theme.widgetPickerItemDisabledBg};
cursor: default; cursor: default;
} }
&-disabled&-selected { &-disabled&-selected {
@ -485,14 +487,14 @@ const cssEntry = styled('div', `
const cssIcon = styled(icon, ` const cssIcon = styled(icon, `
margin-right: 8px; margin-right: 8px;
flex-shrink: 0; flex-shrink: 0;
--icon-color: ${colors.slate}; --icon-color: ${theme.widgetPickerIcon};
.${cssEntry.className}-disabled > & { .${cssEntry.className}-disabled > & {
opacity: 0.2; opacity: 0.25;
} }
`); `);
const cssTypeIcon = styled(cssIcon, ` const cssTypeIcon = styled(cssIcon, `
--icon-color: ${colors.lightGreen}; --icon-color: ${theme.widgetPickerPrimaryIcon};
`); `);
const cssLabel = styled('span', ` const cssLabel = styled('span', `
@ -518,7 +520,7 @@ const cssPivot = styled(cssEntry, `
const cssBigIcon = styled(icon, ` const cssBigIcon = styled(icon, `
width: 24px; width: 24px;
height: 24px; height: 24px;
background-color: ${colors.darkGreen}; background-color: ${theme.widgetPickerSummaryIcon};
`); `);
const cssFooter = styled('div', ` const cssFooter = styled('div', `
@ -536,11 +538,14 @@ const cssFooterContent = styled('div', `
`); `);
const cssSmallLabel = styled('span', ` const cssSmallLabel = styled('span', `
color: ${theme.text};
font-size: ${vars.xsmallFontSize}; font-size: ${vars.xsmallFontSize};
margin-right: 8px; margin-right: 8px;
`); `);
const cssSelect = styled(select, ` const cssSelect = styled(select, `
color: ${theme.selectButtonFg};
background-color: ${theme.selectButtonBg};
flex: 1 0 160px; flex: 1 0 160px;
width: 160px; width: 160px;
`); `);

@ -8,7 +8,7 @@ import {find as findInTree, fromTableData, TreeItemRecord, TreeRecord,
TreeTableData} from 'app/client/models/TreeModel'; TreeTableData} from 'app/client/models/TreeModel';
import {TreeViewComponent} from 'app/client/ui/TreeViewComponent'; import {TreeViewComponent} from 'app/client/ui/TreeViewComponent';
import {labeledCircleCheckbox} from 'app/client/ui2018/checkbox'; import {labeledCircleCheckbox} from 'app/client/ui2018/checkbox';
import {colors} from 'app/client/ui2018/cssVars'; import {theme} from 'app/client/ui2018/cssVars';
import {cssLink} from 'app/client/ui2018/links'; import {cssLink} from 'app/client/ui2018/links';
import {ISaveModalOptions, saveModal} from 'app/client/ui2018/modals'; import {ISaveModalOptions, saveModal} from 'app/client/ui2018/modals';
import {buildPageDom, PageActions} from 'app/client/ui2018/pages'; import {buildPageDom, PageActions} from 'app/client/ui2018/pages';
@ -182,7 +182,7 @@ const cssOptions = styled('div', `
const cssBlockCheckbox = styled('div', ` const cssBlockCheckbox = styled('div', `
display: flex; display: flex;
padding: 10px 8px; padding: 10px 8px;
border: 1px solid ${colors.mediumGrey}; border: 1px solid ${theme.modalBorder};
border-radius: 3px; border-radius: 3px;
cursor: pointer; cursor: pointer;
& input::before, & input::after { & input::before, & input::after {
@ -190,7 +190,7 @@ const cssBlockCheckbox = styled('div', `
left: unset; left: unset;
} }
&:hover { &:hover {
border-color: ${colors.lightGreen}; border-color: ${theme.accentBorder};
} }
&-block { &-block {
pointer-events: none; pointer-events: none;
@ -208,7 +208,8 @@ const cssWarning = styled('div', `
`); `);
const cssTableName = styled('div', ` const cssTableName = styled('div', `
background: #eee; color: black;
background-color: #eee;
padding: 3px 6px; padding: 3px 6px;
border-radius: 4px; border-radius: 4px;
`); `);

@ -2,7 +2,7 @@ import {docUrl, urlState} from 'app/client/models/gristUrlState';
import {getTimeFromNow, HomeModel} from 'app/client/models/HomeModel'; import {getTimeFromNow, HomeModel} from 'app/client/models/HomeModel';
import {makeDocOptionsMenu, makeRemovedDocOptionsMenu} from 'app/client/ui/DocMenu'; import {makeDocOptionsMenu, makeRemovedDocOptionsMenu} from 'app/client/ui/DocMenu';
import {transientInput} from 'app/client/ui/transientInput'; import {transientInput} from 'app/client/ui/transientInput';
import {colors, vars} from 'app/client/ui2018/cssVars'; import {colors, theme, vars} from 'app/client/ui2018/cssVars';
import {icon} from 'app/client/ui2018/icons'; import {icon} from 'app/client/ui2018/icons';
import {menu} from 'app/client/ui2018/menus'; import {menu} from 'app/client/ui2018/menus';
import * as roles from 'app/common/roles'; import * as roles from 'app/common/roles';
@ -115,11 +115,11 @@ const pinnedDocWrapper = styled('div', `
position: relative; position: relative;
width: 210px; width: 210px;
margin: 16px 24px 16px 0; margin: 16px 24px 16px 0;
border: 1px solid ${colors.mediumGrey}; border: 1px solid ${theme.pinnedDocBorder};
border-radius: 1px; border-radius: 1px;
vertical-align: top; vertical-align: top;
&:hover { &:hover {
border: 1px solid ${colors.slate}; border: 1px solid ${theme.pinnedDocBorderHover};
} }
/* TODO: Specify a gap on flexbox parents of pinnedDocWrapper instead. */ /* TODO: Specify a gap on flexbox parents of pinnedDocWrapper instead. */
@ -132,16 +132,16 @@ const pinnedDoc = styled('a', `
display: flex; display: flex;
flex-direction: column; flex-direction: column;
width: 100%; width: 100%;
color: black; color: ${theme.text};
text-decoration: none; text-decoration: none;
cursor: pointer; cursor: pointer;
&:hover { &:hover {
color: black; color: ${theme.text};
text-decoration: none; text-decoration: none;
} }
&-no-access, &-no-access:hover { &-no-access, &-no-access:hover {
color: ${colors.slate}; color: ${theme.disabledText};
cursor: not-allowed; cursor: not-allowed;
} }
`); `);
@ -216,6 +216,7 @@ const pinnedDocOptions = styled('div', `
const pinnedDocFooter = styled('div', ` const pinnedDocFooter = styled('div', `
width: 100%; width: 100%;
font-size: ${vars.mediumFontSize}; font-size: ${vars.mediumFontSize};
background-color: ${theme.pinnedDocFooterBg};
`); `);
const pinnedDocTitle = styled('div', ` const pinnedDocTitle = styled('div', `
@ -238,17 +239,18 @@ const pinnedDocEditorInput = styled(transientInput, `
padding: 0; padding: 0;
border: none; border: none;
outline: none; outline: none;
background-color: ${colors.mediumGrey}; color: ${theme.text};
background-color: ${theme.pinnedDocEditorBg};
`); `);
const cssPinnedDocTimestamp = styled('div', ` const cssPinnedDocTimestamp = styled('div', `
margin: 8px 16px 16px 16px; margin: 8px 16px 16px 16px;
color: ${colors.slate}; color: ${theme.lightText};
`); `);
const cssPinnedDocDesc = styled(cssPinnedDocTimestamp, ` const cssPinnedDocDesc = styled(cssPinnedDocTimestamp, `
margin: 8px 16px 16px 16px; margin: 8px 16px 16px 16px;
color: ${colors.slate}; color: ${theme.lightText};
height: 48px; height: 48px;
line-height: 16px; line-height: 16px;
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
@ -271,5 +273,5 @@ const cssPublicIcon = styled(icon, `
position: absolute; position: absolute;
top: 16px; top: 16px;
left: 16px; left: 16px;
--icon-color: ${colors.lightGreen}; --icon-color: ${theme.accentIcon};
`); `);

@ -30,7 +30,7 @@ import {CustomSectionConfig} from 'app/client/ui/CustomSectionConfig';
import {VisibleFieldsConfig} from 'app/client/ui/VisibleFieldsConfig'; import {VisibleFieldsConfig} from 'app/client/ui/VisibleFieldsConfig';
import {IWidgetType, widgetTypes} from 'app/client/ui/widgetTypes'; import {IWidgetType, widgetTypes} from 'app/client/ui/widgetTypes';
import {basicButton, primaryButton} from 'app/client/ui2018/buttons'; import {basicButton, primaryButton} from 'app/client/ui2018/buttons';
import {colors, testId, vars} from 'app/client/ui2018/cssVars'; import {testId, theme, vars} from 'app/client/ui2018/cssVars';
import {textInput} from 'app/client/ui2018/editableLabel'; import {textInput} from 'app/client/ui2018/editableLabel';
import {IconName} from 'app/client/ui2018/IconList'; import {IconName} from 'app/client/ui2018/IconList';
import {icon} from 'app/client/ui2018/icons'; import {icon} from 'app/client/ui2018/icons';
@ -567,7 +567,7 @@ function tabContentToDom(content: Observable<TabContent[]>|TabContent[]|IDomComp
} }
const cssOverlay = styled('div', ` const cssOverlay = styled('div', `
background-color: white; background-color: ${theme.rightPanelDisabledOverlay};
opacity: 0.8; opacity: 0.8;
position: absolute; position: absolute;
top: 0; top: 0;
@ -578,19 +578,21 @@ const cssOverlay = styled('div', `
`); `);
const cssBottomText = styled('span', ` const cssBottomText = styled('span', `
color: ${theme.text};
position: absolute; position: absolute;
bottom: -40px; bottom: -40px;
padding: 4px 16px; padding: 4px 16px;
`); `);
const cssLabel = styled('div', ` const cssLabel = styled('div', `
color: ${theme.text};
text-transform: uppercase; text-transform: uppercase;
margin: 16px 16px 12px 16px; margin: 16px 16px 12px 16px;
font-size: ${vars.xsmallFontSize}; font-size: ${vars.xsmallFontSize};
`); `);
const cssRow = styled('div', ` const cssRow = styled('div', `
color: ${theme.text};
display: flex; display: flex;
margin: 8px 16px; margin: 8px 16px;
align-items: center; align-items: center;
@ -598,7 +600,7 @@ const cssRow = styled('div', `
margin-top: 24px; margin-top: 24px;
} }
&-disabled { &-disabled {
color: ${colors.slate}; color: ${theme.disabledText};
} }
`); `);
@ -613,29 +615,29 @@ const cssButtonRow = styled(cssRow, `
const cssIcon = styled(icon, ` const cssIcon = styled(icon, `
flex: 0 0 auto; flex: 0 0 auto;
--icon-color: ${colors.slate}; --icon-color: ${theme.lightText};
`); `);
const cssTopBarItem = styled('div', ` const cssTopBarItem = styled('div', `
flex: 1 1 0px; flex: 1 1 0px;
height: 100%; height: 100%;
background-color: ${colors.lightGrey}; background-color: ${theme.rightPanelTabBg};
font-weight: ${vars.headerControlTextWeight}; font-weight: ${vars.headerControlTextWeight};
color: ${colors.dark}; color: ${theme.rightPanelTabFg};
--icon-color: ${colors.slate}; --icon-color: ${theme.rightPanelTabIcon};
display: flex; display: flex;
align-items: center; align-items: center;
cursor: default; cursor: default;
&-selected { &-selected {
background-color: ${colors.lightGreen}; background-color: ${theme.rightPanelTabSelectedBg};
font-weight: initial; font-weight: initial;
color: ${colors.light}; color: ${theme.rightPanelTabSelectedFg};
--icon-color: ${colors.light}; --icon-color: ${theme.rightPanelTabSelectedFg};
} }
&:not(&-selected):hover { &:not(&-selected):hover {
background-color: ${colors.mediumGrey}; background-color: ${theme.rightPanelTabHoverBg};
--icon-color: ${colors.lightGreen}; --icon-color: ${theme.rightPanelTabIconHover};
} }
`); `);
@ -659,7 +661,7 @@ const cssHoverCircle = styled('div', `
justify-content: center; justify-content: center;
&:hover { &:hover {
background-color: ${colors.darkGreen}; background-color: ${theme.rightPanelTabCloseButtonHoverBg};
} }
`); `);
@ -678,7 +680,7 @@ const cssSubTabContainer = styled('div', `
`); `);
const cssSubTab = styled('div', ` const cssSubTab = styled('div', `
color: ${colors.lightGreen}; color: ${theme.rightPanelSubtabFg};
flex: auto; flex: auto;
height: 100%; height: 100%;
display: flex; display: flex;
@ -686,21 +688,21 @@ const cssSubTab = styled('div', `
justify-content: flex-end; justify-content: flex-end;
text-align: center; text-align: center;
padding-bottom: 8px; padding-bottom: 8px;
border-bottom: 1px solid ${colors.mediumGrey}; border-bottom: 1px solid ${theme.pagePanelsBorder};
cursor: default; cursor: default;
&-selected { &-selected {
color: ${colors.dark}; color: ${theme.rightPanelSubtabSelectedFg};
border-bottom: 1px solid ${colors.lightGreen}; border-bottom: 1px solid ${theme.rightPanelSubtabSelectedUnderline};
} }
&:not(&-selected):hover { &:not(&-selected):hover {
color: ${colors.darkGreen}; color: ${theme.rightPanelSubtabHoverFg};
} }
&:hover { &:hover {
border-bottom: 1px solid ${colors.lightGreen}; border-bottom: 1px solid ${theme.rightPanelSubtabHoverUnderline};
} }
.${cssSubTabContainer.className}:hover > &-selected:not(:hover) { .${cssSubTabContainer.className}:hover > &-selected:not(:hover) {
border-bottom: 1px solid ${colors.mediumGrey}; border-bottom: 1px solid ${theme.pagePanelsBorder};
} }
`); `);
@ -710,7 +712,7 @@ const cssTabContents = styled('div', `
`); `);
const cssSeparator = styled('div', ` const cssSeparator = styled('div', `
border-bottom: 1px solid ${colors.mediumGrey}; border-bottom: 1px solid ${theme.pagePanelsBorder};
margin-top: 16px; margin-top: 16px;
`); `);
@ -731,7 +733,7 @@ const cssConfigContainer = styled('div', `
const cssDataLabel = styled('div', ` const cssDataLabel = styled('div', `
flex: 0 0 81px; flex: 0 0 81px;
color: ${colors.slate}; color: ${theme.lightText};
font-size: ${vars.xsmallFontSize}; font-size: ${vars.xsmallFontSize};
margin-left: 4px; margin-left: 4px;
margin-top: 2px; margin-top: 2px;
@ -751,7 +753,7 @@ const cssList = styled('div', `
const cssListItem = styled('li', ` const cssListItem = styled('li', `
background-color: ${colors.mediumGrey}; background-color: ${theme.hover};
border-radius: 2px; border-radius: 2px;
margin-bottom: 4px; margin-bottom: 4px;
white-space: nowrap; white-space: nowrap;
@ -763,10 +765,12 @@ const cssListItem = styled('li', `
const cssTextInput = styled(textInput, ` const cssTextInput = styled(textInput, `
flex: 1 0 auto; flex: 1 0 auto;
color: ${theme.inputFg};
background-color: ${theme.inputBg};
&:disabled { &:disabled {
color: ${colors.slate}; color: ${theme.inputDisabledFg};
background-color: ${colors.lightGrey}; background-color: ${theme.inputDisabledBg};
pointer-events: none; pointer-events: none;
} }
`); `);

@ -1,13 +1,14 @@
import {colors, vars} from 'app/client/ui2018/cssVars'; import {theme, vars} from 'app/client/ui2018/cssVars';
import {icon} from 'app/client/ui2018/icons'; import {icon} from 'app/client/ui2018/icons';
import {styled} from 'grainjs'; import {styled} from 'grainjs';
export const cssIcon = styled(icon, ` export const cssIcon = styled(icon, `
flex: 0 0 auto; flex: 0 0 auto;
--icon-color: ${colors.slate}; --icon-color: ${theme.lightText};
`); `);
export const cssLabel = styled('div', ` export const cssLabel = styled('div', `
color: ${theme.text};
text-transform: uppercase; text-transform: uppercase;
margin: 16px 16px 12px 16px; margin: 16px 16px 12px 16px;
font-size: ${vars.xsmallFontSize}; font-size: ${vars.xsmallFontSize};
@ -23,11 +24,12 @@ export const cssRow = styled('div', `
display: flex; display: flex;
margin: 8px 16px; margin: 8px 16px;
align-items: center; align-items: center;
color: ${theme.text};
&-top-space { &-top-space {
margin-top: 24px; margin-top: 24px;
} }
&-disabled { &-disabled {
color: ${colors.slate}; color: ${theme.disabledText};
} }
`); `);
@ -46,6 +48,6 @@ export const cssButtonRow = styled(cssRow, `
`); `);
export const cssSeparator = styled('div', ` export const cssSeparator = styled('div', `
border-bottom: 1px solid ${colors.mediumGrey}; border-bottom: 1px solid ${theme.pagePanelsBorder};
margin-top: 16px; margin-top: 16px;
`); `);

@ -6,7 +6,7 @@ import {makeCopy, replaceTrunkWithFork} from 'app/client/ui/MakeCopyMenu';
import {sendToDrive} from 'app/client/ui/sendToDrive'; import {sendToDrive} from 'app/client/ui/sendToDrive';
import {cssHoverCircle, cssTopBarBtn} from 'app/client/ui/TopBarCss'; import {cssHoverCircle, cssTopBarBtn} from 'app/client/ui/TopBarCss';
import {primaryButton} from 'app/client/ui2018/buttons'; import {primaryButton} from 'app/client/ui2018/buttons';
import {colors, mediaXSmall, testId} from 'app/client/ui2018/cssVars'; import {mediaXSmall, testId, theme} from 'app/client/ui2018/cssVars';
import {icon} from 'app/client/ui2018/icons'; import {icon} from 'app/client/ui2018/icons';
import {menu, menuAnnotate, menuDivider, menuIcon, menuItem, menuItemLink, menuText} from 'app/client/ui2018/menus'; import {menu, menuAnnotate, menuDivider, menuIcon, menuItem, menuItemLink, menuText} from 'app/client/ui2018/menus';
import {buildUrlId, parseUrlId} from 'app/common/gristUrls'; import {buildUrlId, parseUrlId} from 'app/common/gristUrls';
@ -265,9 +265,9 @@ const cssShareButton = styled('div', `
margin: 5px; margin: 5px;
white-space: nowrap; white-space: nowrap;
--share-btn-bg: ${colors.lightGreen}; --share-btn-bg: ${theme.controlPrimaryBg};
&-combined:hover, &-combined.weasel-popup-open { &-combined:hover, &-combined.weasel-popup-open {
--share-btn-bg: ${colors.darkGreen}; --share-btn-bg: ${theme.controlPrimaryHoverBg};
} }
`); `);
@ -289,14 +289,14 @@ const cssShareAction = styled(primaryButton, `
const cssShareCircle = styled(cssHoverCircle, ` const cssShareCircle = styled(cssHoverCircle, `
z-index: 1; z-index: 1;
background-color: var(--share-btn-bg); background-color: var(--share-btn-bg);
border: 1px solid white; border: 1px solid ${theme.topHeaderBg};
&:hover, &.weasel-popup-open { &:hover, &.weasel-popup-open {
background-color: ${colors.darkGreen}; background-color: ${theme.controlPrimaryHoverBg};
} }
`); `);
const cssShareIcon = styled(cssTopBarBtn, ` const cssShareIcon = styled(cssTopBarBtn, `
background-color: white; background-color: ${theme.controlPrimaryFg};
height: 30px; height: 30px;
width: 30px; width: 30px;
`); `);
@ -310,8 +310,8 @@ const cssMenuSplitLinkText = styled('div', `
flex: auto; flex: auto;
padding: var(--weaseljs-menu-item-padding, 8px 24px); padding: var(--weaseljs-menu-item-padding, 8px 24px);
&:not(:hover) { &:not(:hover) {
background-color: white; background-color: ${theme.menuBg};
color: black; color: ${theme.menuItemFg};
} }
`); `);
@ -320,11 +320,11 @@ const cssMenuIconLink = styled('a', `
flex: none; flex: none;
padding: 8px 24px; padding: 8px 24px;
background-color: white; background-color: ${theme.menuBg};
--icon-color: ${colors.lightGreen}; --icon-color: ${theme.menuItemLinkFg};
&:hover { &:hover {
background-color: ${colors.mediumGreyOpaque}; background-color: ${theme.menuItemLinkselectedBg};
--icon-color: ${colors.darkGreen}; --icon-color: ${theme.menuItemLinkSelectedFg};
} }
`); `);

@ -3,9 +3,9 @@ import {getOrgName} from 'app/common/UserAPI';
import {dom, makeTestId, styled} from 'grainjs'; import {dom, makeTestId, styled} from 'grainjs';
import {AppModel} from 'app/client/models/AppModel'; import {AppModel} from 'app/client/models/AppModel';
import {urlState} from 'app/client/models/gristUrlState'; import {urlState} from 'app/client/models/gristUrlState';
import {theme} from 'app/client/ui2018/cssVars';
import {menuDivider, menuIcon, menuItem, menuItemLink, menuSubHeader} from 'app/client/ui2018/menus'; import {menuDivider, menuIcon, menuItem, menuItemLink, menuSubHeader} from 'app/client/ui2018/menus';
import {icon} from 'app/client/ui2018/icons'; import {icon} from 'app/client/ui2018/icons';
import {colors} from 'app/client/ui2018/cssVars';
const testId = makeTestId('test-site-switcher-'); const testId = makeTestId('test-site-switcher-');
@ -49,14 +49,14 @@ export function buildSiteSwitcher(appModel: AppModel) {
} }
const cssOrgSelected = styled('div', ` const cssOrgSelected = styled('div', `
background-color: ${colors.dark}; background-color: ${theme.siteSwitcherActiveBg};
color: ${colors.light}; color: ${theme.siteSwitcherActiveFg};
`); `);
const cssOrgCheckmark = styled(icon, ` const cssOrgCheckmark = styled(icon, `
flex: none; flex: none;
margin-left: 16px; margin-left: 16px;
--icon-color: ${colors.light}; --icon-color: ${theme.siteSwitcherActiveFg};
display: none; display: none;
.${cssOrgSelected.className} > & { .${cssOrgSelected.className} > & {
display: block; display: block;

@ -0,0 +1,62 @@
import {AppModel} from 'app/client/models/AppModel';
import * as css from 'app/client/ui/AccountPageCss';
import {labeledSquareCheckbox} from 'app/client/ui2018/checkbox';
import {select} from 'app/client/ui2018/menus';
import {ThemeAppearance} from 'app/common/ThemePrefs';
import {Computed, Disposable, dom, makeTestId, styled} from 'grainjs';
const testId = makeTestId('test-theme-config-');
export class ThemeConfig extends Disposable {
private _themePrefs = this._appModel.themePrefs;
private _appearance = Computed.create(this, this._themePrefs, (_use, prefs) => {
return prefs.appearance;
}).onWrite((value) => this._updateAppearance(value));
private _syncWithOS = Computed.create(this, this._themePrefs, (_use, prefs) => {
return prefs.syncWithOS;
}).onWrite((value) => this._updateSyncWithOS(value));
constructor(private _appModel: AppModel) {
super();
}
public buildDom() {
return dom('div',
css.subHeader('Appearance ', css.betaTag('Beta')),
css.dataRow(
cssAppearanceSelect(
select(
this._appearance,
[
{value: 'light', label: 'Light'},
{value: 'dark', label: 'Dark'},
],
),
testId('appearance'),
),
),
css.dataRow(
labeledSquareCheckbox(
this._syncWithOS,
'Switch appearance automatically to match system',
testId('sync-with-os'),
),
),
testId('container'),
);
}
private _updateAppearance(appearance: ThemeAppearance) {
this._themePrefs.set({...this._themePrefs.get(), appearance});
}
private _updateSyncWithOS(syncWithOS: boolean) {
this._themePrefs.set({...this._themePrefs.get(), syncWithOS});
}
}
const cssAppearanceSelect = styled('div', `
width: 120px;
`);

@ -7,7 +7,7 @@ import {createHelpTools, cssLinkText, cssPageEntry, cssPageEntryMain, cssPageEnt
cssPageIcon, cssPageLink, cssSectionHeader, cssSpacer, cssSplitPageEntry, cssPageIcon, cssPageLink, cssSectionHeader, cssSpacer, cssSplitPageEntry,
cssTools} from 'app/client/ui/LeftPanelCommon'; cssTools} from 'app/client/ui/LeftPanelCommon';
import {hoverTooltip, tooltipCloseButton} from 'app/client/ui/tooltips'; import {hoverTooltip, tooltipCloseButton} from 'app/client/ui/tooltips';
import {colors} from 'app/client/ui2018/cssVars'; import {theme} from 'app/client/ui2018/cssVars';
import {icon} from 'app/client/ui2018/icons'; import {icon} from 'app/client/ui2018/icons';
import {cssLink} from 'app/client/ui2018/links'; import {cssLink} from 'app/client/ui2018/links';
import {menuAnnotate} from 'app/client/ui2018/menus'; import {menuAnnotate} from 'app/client/ui2018/menus';
@ -206,7 +206,7 @@ function addRevertViewAsUI() {
const cssConvertTooltip = styled('div', ` const cssConvertTooltip = styled('div', `
display: flex; display: flex;
align-items: center; align-items: center;
--icon-color: ${colors.lightGreen}; --icon-color: ${theme.controlFg};
& > .${cssLink.className} { & > .${cssLink.className} {
margin-left: 8px; margin-left: 8px;
@ -223,10 +223,10 @@ const cssExampleCardOpener = styled('div', `
width: 24px; width: 24px;
padding: 4px; padding: 4px;
line-height: 0px; line-height: 0px;
--icon-color: ${colors.light}; --icon-color: ${theme.iconButtonFg};
background-color: ${colors.lightGreen}; background-color: ${theme.iconButtonPrimaryBg};
&:hover { &:hover {
background-color: ${colors.darkGreen}; background-color: ${theme.iconButtonPrimaryHoverBg};
} }
.${cssTools.className}-collapsed & { .${cssTools.className}-collapsed & {
display: none; display: none;
@ -234,9 +234,9 @@ const cssExampleCardOpener = styled('div', `
`); `);
const cssRevertViewAsButton = styled(cssExampleCardOpener, ` const cssRevertViewAsButton = styled(cssExampleCardOpener, `
background-color: ${colors.darkGrey}; background-color: ${theme.iconButtonSecondaryBg};
&:hover { &:hover {
background-color: ${colors.slate}; background-color: ${theme.iconButtonSecondaryHoverBg};
} }
`); `);

@ -10,7 +10,7 @@ import {buildShareMenuButton} from 'app/client/ui/ShareMenu';
import {cssHoverCircle, cssTopBarBtn} from 'app/client/ui/TopBarCss'; import {cssHoverCircle, cssTopBarBtn} from 'app/client/ui/TopBarCss';
import {docBreadcrumbs} from 'app/client/ui2018/breadcrumbs'; import {docBreadcrumbs} from 'app/client/ui2018/breadcrumbs';
import {basicButton} from 'app/client/ui2018/buttons'; import {basicButton} from 'app/client/ui2018/buttons';
import {colors, cssHideForNarrowScreen, testId} from 'app/client/ui2018/cssVars'; import {cssHideForNarrowScreen, testId, theme} from 'app/client/ui2018/cssVars';
import {IconName} from 'app/client/ui2018/IconList'; import {IconName} from 'app/client/ui2018/IconList';
import {waitGrainObs} from 'app/common/gutil'; import {waitGrainObs} from 'app/common/gutil';
import * as roles from 'app/common/roles'; import * as roles from 'app/common/roles';
@ -129,14 +129,14 @@ function topBarUndoBtn(iconName: IconName, ...domArgs: DomElementArg[]): Element
} }
const cssTopBarUndoBtn = styled(cssTopBarBtn, ` const cssTopBarUndoBtn = styled(cssTopBarBtn, `
background-color: ${colors.slate}; background-color: ${theme.topBarButtonSecondaryFg};
.${cssHoverCircle.className}:hover & { .${cssHoverCircle.className}:hover & {
background-color: ${colors.lightGreen}; background-color: ${theme.topBarButtonPrimaryFg};
} }
.${cssHoverCircle.className}-disabled:hover & { .${cssHoverCircle.className}-disabled:hover & {
background-color: ${colors.darkGrey}; background-color: ${theme.topBarButtonDisabledFg};
cursor: default; cursor: default;
} }
`); `);

@ -1,4 +1,4 @@
import {colors} from 'app/client/ui2018/cssVars'; import {theme} from 'app/client/ui2018/cssVars';
import {icon} from 'app/client/ui2018/icons'; import {icon} from 'app/client/ui2018/icons';
import {styled} from 'grainjs'; import {styled} from 'grainjs';
@ -9,7 +9,7 @@ export const cssHoverCircle = styled('div', `
border-radius: 16px; border-radius: 16px;
&:hover, &.weasel-popup-open { &:hover, &.weasel-popup-open {
background-color: ${colors.mediumGrey}; background-color: ${theme.hover};
} }
&-disabled:hover { &-disabled:hover {
@ -23,12 +23,12 @@ export const cssTopBarBtn = styled(icon, `
padding: 8px 8px; padding: 8px 8px;
cursor: pointer; cursor: pointer;
-webkit-mask-size: 16px 16px; -webkit-mask-size: 16px 16px;
background-color: ${colors.lightGreen}; background-color: ${theme.topBarButtonPrimaryFg};
.${cssHoverCircle.className}-disabled & { .${cssHoverCircle.className}-disabled & {
background-color: ${colors.darkGrey}; background-color: ${theme.topBarButtonDisabledFg};
cursor: default; cursor: default;
} }
&-slate { background-color: ${colors.slate}; } &-slate { background-color: ${theme.topBarButtonSecondaryFg}; }
&-error { background-color: ${colors.error}; } &-error { background-color: ${theme.topBarButtonErrorFg}; }
`); `);

@ -1,4 +1,4 @@
import { colors, vars } from "app/client/ui2018/cssVars"; import { theme } from "app/client/ui2018/cssVars";
import { icon } from "app/client/ui2018/icons"; import { icon } from "app/client/ui2018/icons";
import { styled } from "grainjs"; import { styled } from "grainjs";
@ -42,27 +42,28 @@ export const itemHeader = styled('div', `
min-width: 0; min-width: 0;
border-radius: 0 2px 2px 0; border-radius: 0 2px 2px 0;
border: solid 1px transparent; border: solid 1px transparent;
color: ${theme.text};
.${itemHeaderWrapper.className}-not-dragging:hover > & { .${itemHeaderWrapper.className}-not-dragging:hover > & {
background-color: ${colors.mediumGrey}; background-color: ${theme.pageHoverBg};
} }
.${itemHeaderWrapper.className}-not-dragging > &.selected { .${itemHeaderWrapper.className}-not-dragging > &.selected {
background-color: ${colors.darkBg}; background-color: ${theme.activePageBg};
color: white; color: ${theme.activePageFg};
} }
&.highlight { &.highlight {
border-color: ${vars.controlFg}; border-color: ${theme.controlFg};
} }
`); `);
export const dropdown = styled(icon, ` export const dropdown = styled(icon, `
background-color: ${colors.slate}; background-color: ${theme.controlSecondaryFg};
.${itemHeaderWrapper.className}-not-dragging > .${itemHeader.className}.selected & { .${itemHeaderWrapper.className}-not-dragging > .${itemHeader.className}.selected & {
background-color: white; background-color: ${theme.activePageFg};
} }
`); `);
export const itemLabelRight = styled('div', ` export const itemLabelRight = styled('div', `
--icon-color: ${colors.slate}; --icon-color: ${theme.controlSecondaryFg};
width: 16px; width: 16px;
.${treeViewContainer.className}-close & { .${treeViewContainer.className}-close & {
display: none; display: none;
@ -114,6 +115,6 @@ export const offset = styled('div', `
export const target = styled('div', ` export const target = styled('div', `
position: absolute; position: absolute;
height: 2px; height: 2px;
background: ${vars.controlFg}; background: ${theme.controlFg};
pointer-events: none; pointer-events: none;
`); `);

@ -5,7 +5,7 @@ import {cssRow} from 'app/client/ui/RightPanelStyles';
import {shadowScroll} from 'app/client/ui/shadowScroll'; import {shadowScroll} from 'app/client/ui/shadowScroll';
import {basicButton, primaryButton} from "app/client/ui2018/buttons"; import {basicButton, primaryButton} from "app/client/ui2018/buttons";
import {labeledSquareCheckbox} from "app/client/ui2018/checkbox"; import {labeledSquareCheckbox} from "app/client/ui2018/checkbox";
import {colors, testId} from 'app/client/ui2018/cssVars'; import {testId, theme} from 'app/client/ui2018/cssVars';
import {icon} from "app/client/ui2018/icons"; import {icon} from "app/client/ui2018/icons";
import {menuCssClass, menuDivider} from 'app/client/ui2018/menus'; import {menuCssClass, menuDivider} from 'app/client/ui2018/menus';
import {cssSelectBtn} from 'app/client/ui2018/select'; import {cssSelectBtn} from 'app/client/ui2018/select';
@ -228,7 +228,7 @@ const cssSelectSummary = styled('div', `
&:empty::before { &:empty::before {
content: "Select fields"; content: "Select fields";
color: ${colors.slate}; color: ${theme.selectButtonPlaceholderFg};
} }
`); `);
@ -244,8 +244,8 @@ const cssSelectorMenu = styled(cssMenu, `
const cssItemsList = styled(shadowScroll, ` const cssItemsList = styled(shadowScroll, `
flex: auto; flex: auto;
min-height: 80px; min-height: 80px;
border-top: 1px solid ${colors.darkGrey}; border-top: 1px solid ${theme.menuBorder};
border-bottom: 1px solid ${colors.darkGrey}; border-bottom: 1px solid ${theme.menuBorder};
margin-top: 8px; margin-top: 8px;
padding: 8px 0; padding: 8px 0;
`); `);
@ -263,7 +263,7 @@ const cssSelectorItem = styled(cssMenuItem, `
`); `);
const cssSelectorNote = styled('span', ` const cssSelectorNote = styled('span', `
color: ${colors.slate}; color: ${theme.lightText};
`); `);
const cssSelectorFooter = styled(cssSelectorItem, ` const cssSelectorFooter = styled(cssSelectorItem, `

@ -1,4 +1,4 @@
import {colors} from 'app/client/ui2018/cssVars'; import {colors, theme} from 'app/client/ui2018/cssVars';
import {FullUser} from 'app/common/LoginSessionAPI'; import {FullUser} from 'app/common/LoginSessionAPI';
import {dom, DomElementArg, styled} from 'grainjs'; import {dom, DomElementArg, styled} from 'grainjs';
@ -120,8 +120,7 @@ const cssUserPicture = styled('img', `
width: 100%; width: 100%;
height: 100%; height: 100%;
object-fit: cover; object-fit: cover;
background-color: white; background-color: ${theme.menuBg};
border-radius: 100px; border-radius: 100px;
border: 1px solid white; /* make sure edge of circle with initials is not visible */
box-sizing: content-box; /* keep the border outside of the size of the image */ box-sizing: content-box; /* keep the border outside of the size of the image */
`); `);

@ -1,4 +1,4 @@
import {colors, vars} from 'app/client/ui2018/cssVars'; import {colors, theme, vars} from 'app/client/ui2018/cssVars';
import {icon} from 'app/client/ui2018/icons'; import {icon} from 'app/client/ui2018/icons';
import {input, styled} from 'grainjs'; import {input, styled} from 'grainjs';
import {cssMenuItem} from 'popweasel'; import {cssMenuItem} from 'popweasel';
@ -54,16 +54,16 @@ export const cssMemberText = styled('div', `
export const cssMemberPrimary = styled('span', ` export const cssMemberPrimary = styled('span', `
font-weight: bold; font-weight: bold;
color: ${colors.dark}; color: ${theme.text};
padding: 2px 0; padding: 2px 0;
.${cssMenuItem.className}-sel & { .${cssMenuItem.className}-sel & {
color: white; color: ${theme.menuItemSelectedFg};
} }
`); `);
export const cssMemberSecondary = styled('span', ` export const cssMemberSecondary = styled('span', `
color: ${colors.slate}; color: ${theme.lightText};
/* the following just undo annoying bootstrap styles that apply to all labels */ /* the following just undo annoying bootstrap styles that apply to all labels */
margin: 0px; margin: 0px;
font-weight: normal; font-weight: normal;
@ -71,12 +71,12 @@ export const cssMemberSecondary = styled('span', `
white-space: nowrap; white-space: nowrap;
.${cssMenuItem.className}-sel & { .${cssMenuItem.className}-sel & {
color: white; color: ${theme.menuItemSelectedFg};
} }
`); `);
export const cssMemberType = styled('span', ` export const cssMemberType = styled('span', `
color: ${colors.slate}; color: ${theme.lightText};
/* the following just undo annoying bootstrap styles that apply to all labels */ /* the following just undo annoying bootstrap styles that apply to all labels */
margin: 0px; margin: 0px;
font-weight: normal; font-weight: normal;
@ -84,12 +84,12 @@ export const cssMemberType = styled('span', `
white-space: nowrap; white-space: nowrap;
.${cssMenuItem.className}-sel & { .${cssMenuItem.className}-sel & {
color: white; color: ${theme.menuItemSelectedFg};
} }
`); `);
export const cssMemberTypeProblem = styled('span', ` export const cssMemberTypeProblem = styled('span', `
color: ${colors.error}; color: ${theme.errorText};
/* the following just undo annoying bootstrap styles that apply to all labels */ /* the following just undo annoying bootstrap styles that apply to all labels */
margin: 0px; margin: 0px;
font-weight: normal; font-weight: normal;
@ -97,7 +97,7 @@ export const cssMemberTypeProblem = styled('span', `
white-space: nowrap; white-space: nowrap;
.${cssMenuItem.className}-sel & { .${cssMenuItem.className}-sel & {
color: white; color: ${theme.menuItemSelectedFg};
} }
`); `);
@ -113,6 +113,7 @@ export const cssMemberBtn = styled('div', `
`); `);
export const cssRemoveIcon = styled(icon, ` export const cssRemoveIcon = styled(icon, `
background-color: ${theme.lightText};
margin: 12px 0; margin: 12px 0;
`); `);
@ -122,26 +123,32 @@ export const cssEmailInputContainer = styled('div', `
height: 42px; height: 42px;
padding: 0 3px; padding: 0 3px;
margin: 16px 63px; margin: 16px 63px;
border: 1px solid ${colors.darkGrey}; border: 1px solid ${theme.inputBorder};
border-radius: 3px; border-radius: 3px;
font-size: ${vars.mediumFontSize}; font-size: ${vars.mediumFontSize};
outline: none; outline: none;
&-green { &-green {
border: 1px solid ${colors.lightGreen}; border: 1px solid ${theme.inputValid};
} }
`); `);
export const cssEmailInput = styled(input, ` export const cssEmailInput = styled(input, `
color: ${theme.inputFg};
background-color: ${theme.inputBg};
flex: 1 1 0; flex: 1 1 0;
line-height: 42px; line-height: 42px;
font-size: ${vars.mediumFontSize}; font-size: ${vars.mediumFontSize};
font-family: ${vars.fontFamily}; font-family: ${vars.fontFamily};
outline: none; outline: none;
border: none; border: none;
&::placeholder {
color: ${theme.inputPlaceholderFg};
}
`); `);
export const cssMailIcon = styled(icon, ` export const cssMailIcon = styled(icon, `
margin: 12px 8px 12px 13px; margin: 12px 8px 12px 13px;
background-color: ${colors.slate}; background-color: ${theme.lightText};
`); `);

@ -32,7 +32,7 @@ import {cssEmailInput, cssEmailInputContainer, cssMailIcon, cssMemberBtn, cssMem
cssMemberPrimary, cssMemberSecondary, cssMemberText, cssMemberType, cssMemberTypeProblem, cssMemberPrimary, cssMemberSecondary, cssMemberText, cssMemberType, cssMemberTypeProblem,
cssRemoveIcon} from 'app/client/ui/UserItem'; cssRemoveIcon} from 'app/client/ui/UserItem';
import {basicButton, bigBasicButton, bigPrimaryButton} from 'app/client/ui2018/buttons'; import {basicButton, bigBasicButton, bigPrimaryButton} from 'app/client/ui2018/buttons';
import {colors, mediaXSmall, testId, vars} from 'app/client/ui2018/cssVars'; import {colors, mediaXSmall, testId, theme, vars} from 'app/client/ui2018/cssVars';
import {icon} from 'app/client/ui2018/icons'; import {icon} from 'app/client/ui2018/icons';
import {cssLink} from 'app/client/ui2018/links'; import {cssLink} from 'app/client/ui2018/links';
import {loadingSpinner} from 'app/client/ui2018/loaders'; import {loadingSpinner} from 'app/client/ui2018/loaders';
@ -662,7 +662,7 @@ const cssAccessDetailsBody = styled('div', `
const cssUserManagerBody = styled(cssAccessDetailsBody, ` const cssUserManagerBody = styled(cssAccessDetailsBody, `
height: 374px; height: 374px;
border-bottom: 1px solid ${colors.darkGrey}; border-bottom: 1px solid ${theme.modalBorderDark};
`); `);
const cssSpinner = styled('div', ` const cssSpinner = styled('div', `
@ -695,7 +695,7 @@ const cssOptionRow = styled('div', `
const cssOptionBtn = styled('span', ` const cssOptionBtn = styled('span', `
display: inline-flex; display: inline-flex;
font-size: ${vars.mediumFontSize}; font-size: ${vars.mediumFontSize};
color: ${colors.lightGreen}; color: ${theme.controlFg};
cursor: pointer; cursor: pointer;
`); `);
@ -703,14 +703,15 @@ const cssPublicMemberIcon = styled(icon, `
width: 40px; width: 40px;
height: 40px; height: 40px;
margin: 0 4px; margin: 0 4px;
--icon-color: ${colors.lightGreen}; --icon-color: ${theme.accentIcon};
`); `);
const cssPublicAccessIcon = styled(icon, ` const cssPublicAccessIcon = styled(icon, `
--icon-color: ${colors.lightGreen}; --icon-color: ${theme.accentIcon};
`); `);
const cssUndoIcon = styled(icon, ` const cssUndoIcon = styled(icon, `
--icon-color: ${theme.controlSecondaryFg};
margin: 12px 0; margin: 12px 0;
`); `);
@ -718,7 +719,7 @@ const cssRoleBtn = styled('div', `
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
font-size: ${vars.mediumFontSize}; font-size: ${vars.mediumFontSize};
color: ${colors.lightGreen}; color: ${theme.controlFg};
margin: 12px 24px; margin: 12px 24px;
cursor: pointer; cursor: pointer;
@ -730,7 +731,7 @@ const cssRoleBtn = styled('div', `
const cssCollapseIcon = styled(icon, ` const cssCollapseIcon = styled(icon, `
margin-top: 1px; margin-top: 1px;
background-color: var(--grist-color-light-green); background-color: ${theme.controlFg};
`); `);
const cssInputMenuItem = styled(menuItem, ` const cssInputMenuItem = styled(menuItem, `
@ -747,8 +748,8 @@ const cssUserImagePlus = styled(cssUserImage, `
} }
.${cssMenuItem.className}-sel & { .${cssMenuItem.className}-sel & {
background-color: white; background-color: ${theme.menuItemIconSelectedFg};
color: ${colors.lightGreen}; color: ${theme.menuItemSelectedBg};
} }
`); `);
@ -762,7 +763,7 @@ const cssOrgName = styled('div', `
`); `);
const cssOrgDomain = styled('span', ` const cssOrgDomain = styled('span', `
color: ${colors.lightGreen}; color: ${theme.accentText};
`); `);
const cssFadeInFromTop = keyframes(` const cssFadeInFromTop = keyframes(`

@ -7,7 +7,7 @@ import {addFilterMenu} from 'app/client/ui/FilterBar';
import {hoverTooltip} from 'app/client/ui/tooltips'; import {hoverTooltip} from 'app/client/ui/tooltips';
import {makeViewLayoutMenu} from 'app/client/ui/ViewLayoutMenu'; import {makeViewLayoutMenu} from 'app/client/ui/ViewLayoutMenu';
import {basicButton, primaryButton} from 'app/client/ui2018/buttons'; import {basicButton, primaryButton} from 'app/client/ui2018/buttons';
import {colors, vars} from 'app/client/ui2018/cssVars'; import {theme, vars} from 'app/client/ui2018/cssVars';
import {icon} from 'app/client/ui2018/icons'; import {icon} from 'app/client/ui2018/icons';
import {menu} from 'app/client/ui2018/menus'; import {menu} from 'app/client/ui2018/menus';
import {Sort} from 'app/common/SortSpec'; import {Sort} from 'app/common/SortSpec';
@ -292,7 +292,7 @@ const cssMenu = styled('div', `
} }
&:hover, &.weasel-popup-open { &:hover, &.weasel-popup-open {
background-color: ${colors.mediumGrey}; background-color: ${theme.hover};
} }
`); `);
@ -310,13 +310,13 @@ const cssMenuIconWrapper = styled(cssIconWrapper, `
height: 22px; height: 22px;
&:hover, &.weasel-popup-open { &:hover, &.weasel-popup-open {
background-color: ${colors.mediumGrey}; background-color: ${theme.hover};
} }
&-changed { &-changed {
background-color: ${colors.lightGreen}; background-color: ${theme.accentIcon};
} }
&-changed:hover, &-changed:hover.weasel-popup-open { &-changed:hover, &-changed:hover.weasel-popup-open {
background-color: ${colors.darkGreen}; background-color: ${theme.controlHoverFg};
} }
`); `);
@ -326,7 +326,7 @@ const cssFilterMenuWrapper = styled('div', `
border-radius: 3px; border-radius: 3px;
align-items: center; align-items: center;
&-unsaved { &-unsaved {
border: 1px solid ${colors.lightGreen}; border: 1px solid ${theme.accentBorder};
} }
& .${cssMenu.className} { & .${cssMenu.className} {
border: none; border: none;
@ -337,18 +337,18 @@ const cssFilterMenuWrapper = styled('div', `
const cssIcon = styled(icon, ` const cssIcon = styled(icon, `
flex: none; flex: none;
cursor: pointer; cursor: pointer;
background-color: ${colors.slate}; background-color: ${theme.lightText};
.${cssMenuIconWrapper.className}-changed & { .${cssMenuIconWrapper.className}-changed & {
background-color: white; background-color: ${theme.controlPrimaryFg};
} }
.${clsOldUI.className} & { .${clsOldUI.className} & {
background-color: white; background-color: ${theme.controlPrimaryFg};
} }
&-green { &-green {
background-color: ${colors.lightGreen}; background-color: ${theme.accentIcon};
} }
`); `);
@ -363,20 +363,21 @@ const cssDotsIconWrapper = styled(cssIconWrapper, `
const cssFilterIconWrapper = styled(cssIconWrapper, ` const cssFilterIconWrapper = styled(cssIconWrapper, `
border-radius: 2px 0px 0px 2px; border-radius: 2px 0px 0px 2px;
.${cssFilterMenuWrapper.className}-unsaved & { .${cssFilterMenuWrapper.className}-unsaved & {
background-color: ${colors.lightGreen}; background-color: ${theme.accentIcon};
} }
`); `);
const cssFilterIcon = styled(cssIcon, ` const cssFilterIcon = styled(cssIcon, `
.${cssFilterIconWrapper.className}-any & { .${cssFilterIconWrapper.className}-any & {
background-color: ${colors.lightGreen}; background-color: ${theme.accentIcon};
} }
.${cssFilterMenuWrapper.className}-unsaved & { .${cssFilterMenuWrapper.className}-unsaved & {
background-color: white; background-color: ${theme.controlPrimaryFg};
} }
`); `);
const cssMenuInfoHeader = styled('div', ` const cssMenuInfoHeader = styled('div', `
color: ${theme.menuSubheaderFg};
font-weight: ${vars.bigControlTextWeight}; font-weight: ${vars.bigControlTextWeight};
padding: 8px 24px 8px 24px; padding: 8px 24px 8px 24px;
cursor: default; cursor: default;
@ -389,18 +390,19 @@ const cssMenuText = styled('div', `
cursor: default; cursor: default;
white-space: nowrap; white-space: nowrap;
&-green { &-green {
color: ${colors.lightGreen}; color: ${theme.accentText};
} }
&-gray { &-gray {
color: ${colors.slate}; color: ${theme.lightText};
} }
`); `);
const cssGrayedMenuText = styled(cssMenuText, ` const cssGrayedMenuText = styled(cssMenuText, `
color: ${colors.slate}; color: ${theme.lightText};
`); `);
const cssMenuTextLabel = styled('span', ` const cssMenuTextLabel = styled('span', `
color: ${theme.menuItemFg};
flex-grow: 1; flex-grow: 1;
padding: 0 4px; padding: 0 4px;
overflow: hidden; overflow: hidden;
@ -418,13 +420,13 @@ const cssSmallIconWrapper = styled('div', `
margin: 0 5px 0 5px; margin: 0 5px 0 5px;
&-green { &-green {
background-color: ${colors.lightGreen}; background-color: ${theme.accentIcon};
} }
&-gray { &-gray {
background-color: ${colors.slate}; background-color: ${theme.lightText};
} }
& > .${cssIcon.className} { & > .${cssIcon.className} {
background-color: white; background-color: ${theme.controlPrimaryFg};
} }
`); `);

@ -7,7 +7,7 @@ import { getFieldType } from "app/client/ui/RightPanel";
import { IWidgetType } from "app/client/ui/widgetTypes"; import { IWidgetType } from "app/client/ui/widgetTypes";
import { basicButton, cssButton, primaryButton } from 'app/client/ui2018/buttons'; import { basicButton, cssButton, primaryButton } from 'app/client/ui2018/buttons';
import * as checkbox from "app/client/ui2018/checkbox"; import * as checkbox from "app/client/ui2018/checkbox";
import { colors, vars } from "app/client/ui2018/cssVars"; import { theme, vars } from "app/client/ui2018/cssVars";
import { cssDragger } from "app/client/ui2018/draggableList"; import { cssDragger } from "app/client/ui2018/draggableList";
import { icon } from "app/client/ui2018/icons"; import { icon } from "app/client/ui2018/icons";
import * as gutil from 'app/common/gutil'; import * as gutil from 'app/common/gutil';
@ -200,7 +200,7 @@ export class VisibleFieldsConfig extends Disposable {
dom.maybe( dom.maybe(
(use) => Boolean(use(use(this._section.viewFields).getObservable()).length), (use) => Boolean(use(use(this._section.viewFields).getObservable()).length),
() => ( () => (
cssGreenLabel( cssControlLabel(
icon('Tick'), icon('Tick'),
'Select All', 'Select All',
dom.on('click', () => this._setVisibleCheckboxes(fieldsDraggable, true)), dom.on('click', () => this._setVisibleCheckboxes(fieldsDraggable, true)),
@ -236,7 +236,7 @@ export class VisibleFieldsConfig extends Disposable {
dom.maybe( dom.maybe(
(use) => Boolean(use(this._hiddenFields.getObservable()).length && !use(this._collapseHiddenFields)), (use) => Boolean(use(this._hiddenFields.getObservable()).length && !use(this._collapseHiddenFields)),
() => ( () => (
cssGreenLabel( cssControlLabel(
icon('Tick'), icon('Tick'),
'Select All', 'Select All',
dom.on('click', () => this._setHiddenCheckboxes(hiddenFieldsDraggable, true)), dom.on('click', () => this._setHiddenCheckboxes(hiddenFieldsDraggable, true)),
@ -453,7 +453,7 @@ export const cssDragRow = styled('div', `
export const cssFieldEntry = styled('div', ` export const cssFieldEntry = styled('div', `
display: flex; display: flex;
background-color: ${colors.mediumGrey}; background-color: ${theme.hover};
width: 100%; width: 100%;
border-radius: 2px; border-radius: 2px;
margin: 0 8px 0 0; margin: 0 8px 0 0;
@ -463,10 +463,11 @@ export const cssFieldEntry = styled('div', `
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
--icon-color: ${colors.slate}; --icon-color: ${theme.lightText};
`); `);
const cssHideIcon = styled(icon, ` const cssHideIcon = styled(icon, `
--icon-color: ${theme.lightText};
display: none; display: none;
cursor: pointer; cursor: pointer;
flex: none; flex: none;
@ -477,12 +478,14 @@ const cssHideIcon = styled(icon, `
`); `);
export const cssFieldLabel = styled('span', ` export const cssFieldLabel = styled('span', `
color: ${theme.text};
flex: 1 1 auto; flex: 1 1 auto;
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden; overflow: hidden;
`); `);
const cssFieldListHeader = styled('span', ` const cssFieldListHeader = styled('span', `
color: ${theme.text};
flex: 1 1 0px; flex: 1 1 0px;
font-size: ${vars.xsmallFontSize}; font-size: ${vars.xsmallFontSize};
text-transform: uppercase; text-transform: uppercase;
@ -492,15 +495,15 @@ const cssRow = styled('div', `
display: flex; display: flex;
margin: 16px; margin: 16px;
overflow: hidden; overflow: hidden;
--icon-color: ${colors.slate}; --icon-color: ${theme.lightText};
& > .${cssButton.className} { & > .${cssButton.className} {
margin-right: 8px; margin-right: 8px;
} }
`); `);
const cssGreenLabel = styled('div', ` const cssControlLabel = styled('div', `
--icon-color: ${colors.lightGreen}; --icon-color: ${theme.controlFg};
color: ${colors.lightGreen}; color: ${theme.controlFg};
cursor: pointer; cursor: pointer;
`); `);
@ -511,6 +514,7 @@ const cssHeader = styled(cssRow, `
`); `);
const cssHeaderIcon = styled(icon, ` const cssHeaderIcon = styled(icon, `
--icon-color: ${theme.lightText};
flex: none; flex: none;
margin-right: 4px; margin-right: 4px;
`); `);

@ -1,7 +1,7 @@
import {FocusLayer} from 'app/client/lib/FocusLayer'; import {FocusLayer} from 'app/client/lib/FocusLayer';
import {ViewSectionRec} from 'app/client/models/entities/ViewSectionRec'; import {ViewSectionRec} from 'app/client/models/entities/ViewSectionRec';
import {basicButton, cssButton, primaryButton} from 'app/client/ui2018/buttons'; import {basicButton, cssButton, primaryButton} from 'app/client/ui2018/buttons';
import {colors, vars} from 'app/client/ui2018/cssVars'; import {theme, vars} from 'app/client/ui2018/cssVars';
import {cssTextInput} from 'app/client/ui2018/editableLabel'; import {cssTextInput} from 'app/client/ui2018/editableLabel';
import {menuCssClass} from 'app/client/ui2018/menus'; import {menuCssClass} from 'app/client/ui2018/menus';
import {ModalControl} from 'app/client/ui2018/modals'; import {ModalControl} from 'app/client/ui2018/modals';
@ -189,7 +189,7 @@ const cssTitle = styled('div', `
text-overflow: ellipsis; text-overflow: ellipsis;
align-self: start; align-self: start;
&:hover { &:hover {
background-color: ${colors.mediumGrey}; background-color: ${theme.hover};
} }
&-empty { &-empty {
min-width: 48px; min-width: 48px;
@ -202,12 +202,13 @@ const cssRenamePopup = styled('div', `
flex-direction: column; flex-direction: column;
min-width: 280px; min-width: 280px;
padding: 16px; padding: 16px;
background-color: white; background-color: ${theme.popupBg};
border-radius: 2px; border-radius: 2px;
outline: none; outline: none;
`); `);
const cssLabel = styled('label', ` const cssLabel = styled('label', `
color: ${theme.text};
font-size: ${vars.xsmallFontSize}; font-size: ${vars.xsmallFontSize};
font-weight: ${vars.bigControlTextWeight}; font-weight: ${vars.bigControlTextWeight};
margin: 0 0 8px 0; margin: 0 0 8px 0;
@ -235,11 +236,16 @@ const cssInput = styled((
opts: IInputOptions, opts: IInputOptions,
...args) => input(obs, opts, cssTextInput.cls(''), ...args), ` ...args) => input(obs, opts, cssTextInput.cls(''), ...args), `
text-overflow: ellipsis; text-overflow: ellipsis;
color: ${theme.inputFg};
background-color: transparent;
&:disabled { &:disabled {
color: ${colors.slate}; color: ${theme.inputDisabledFg};
background-color: ${colors.lightGrey}; background-color: ${theme.inputDisabledBg};
pointer-events: none; pointer-events: none;
} }
&::placeholder {
color: ${theme.inputPlaceholderFg};
}
.${cssInputWithIcon.className} > &:disabled { .${cssInputWithIcon.className} > &:disabled {
padding-right: 28px; padding-right: 28px;
} }

@ -1,12 +1,18 @@
import {colors, vars} from 'app/client/ui2018/cssVars'; import {theme, vars} from 'app/client/ui2018/cssVars';
import {styled} from 'grainjs'; import {styled} from 'grainjs';
export const cssInput = styled('input', ` export const cssInput = styled('input', `
color: ${theme.inputFg};
background-color: ${theme.inputBg};
height: 30px; height: 30px;
width: 100%; width: 100%;
font-size: ${vars.mediumFontSize}; font-size: ${vars.mediumFontSize};
border-radius: 3px; border-radius: 3px;
padding: 5px; padding: 5px;
border: 1px solid ${colors.darkGrey}; border: 1px solid ${theme.inputBorder};
outline: none; outline: none;
&::placeholder {
color: ${theme.inputPlaceholderFg};
}
`); `);

@ -5,7 +5,7 @@ import {leftPanelBasic} from 'app/client/ui/LeftPanelCommon';
import {pagePanels} from 'app/client/ui/PagePanels'; import {pagePanels} from 'app/client/ui/PagePanels';
import {createTopBarHome} from 'app/client/ui/TopBar'; import {createTopBarHome} from 'app/client/ui/TopBar';
import {bigBasicButtonLink, bigPrimaryButtonLink} from 'app/client/ui2018/buttons'; import {bigBasicButtonLink, bigPrimaryButtonLink} from 'app/client/ui2018/buttons';
import {colors, vars} from 'app/client/ui2018/cssVars'; import {theme, vars} from 'app/client/ui2018/cssVars';
import {getPageTitleSuffix, GristLoadConfig} from 'app/common/gristUrls'; import {getPageTitleSuffix, GristLoadConfig} from 'app/common/gristUrls';
import {getGristConfig} from 'app/common/urlUtils'; import {getGristConfig} from 'app/common/urlUtils';
import {dom, DomElementArg, makeTestId, observable, styled} from 'grainjs'; import {dom, DomElementArg, makeTestId, observable, styled} from 'grainjs';
@ -144,12 +144,12 @@ const cssErrorHeader = styled('div', `
font-size: ${vars.xxxlargeFontSize}; font-size: ${vars.xxxlargeFontSize};
margin: 24px; margin: 24px;
text-align: center; text-align: center;
color: ${colors.dark}; color: ${theme.text};
`); `);
const cssErrorText = styled('div', ` const cssErrorText = styled('div', `
font-size: ${vars.mediumFontSize}; font-size: ${vars.mediumFontSize};
color: ${colors.dark}; color: ${theme.text};
margin: 0 auto 24px auto; margin: 0 auto 24px auto;
max-width: 400px; max-width: 400px;
text-align: center; text-align: center;

@ -1,4 +1,4 @@
import {colors, vars} from 'app/client/ui2018/cssVars'; import {theme, vars} from 'app/client/ui2018/cssVars';
import {dom, DomElementArg, Observable, styled} from 'grainjs'; import {dom, DomElementArg, Observable, styled} from 'grainjs';
export const cssInput = styled('input', ` export const cssInput = styled('input', `
@ -7,10 +7,16 @@ export const cssInput = styled('input', `
line-height: 20px; line-height: 20px;
width: 100%; width: 100%;
padding: 14px; padding: 14px;
border: 1px solid #D9D9D9; border: 1px solid ${theme.inputBorder};
border-radius: 4px; border-radius: 4px;
outline: none; outline: none;
display: block; display: block;
color: ${theme.inputFg};
background-color: ${theme.inputBg};
&::placeholder {
color: ${theme.inputPlaceholderFg};
}
&[type=number] { &[type=number] {
-moz-appearance: textfield; -moz-appearance: textfield;
@ -22,11 +28,11 @@ export const cssInput = styled('input', `
} }
&-invalid { &-invalid {
border: 1px solid ${colors.error}; border: 1px solid ${theme.inputInvalid};
} }
&-valid { &-valid {
border: 1px solid ${colors.lightGreen}; border: 1px solid ${theme.inputValid};
} }
`); `);

@ -1,71 +0,0 @@
import {Disposable} from 'app/client/lib/dispose';
import {dom, styled} from 'grainjs';
const modalBacker = styled('div', `
position: fixed;
display: flex;
flex-direction: column;
justify-content: center;
width: 100%;
height: 100%;
top: 0;
left: 0;
z-index: 100;
background-color: rgba(0, 0, 0, 0.5);
`);
const modal = styled('div', `
background-color: white;
color: black;
margin: 0 auto;
border-radius: 4px;
box-shadow: 0px 0px 10px 0px rgba(0,0,0,0.2);
border: 1px solid #aaa;
padding: 10px;
`);
export const modalHeader = styled('div', `
font-size: 12pt;
color: #859394;
padding: 5px;
`);
export const modalButtonRow = styled('div', `
width: 70%;
margin: 0 auto;
text-align: center;
& > button {
width: 80px;
}
`);
/**
* A simple modal. Shows up in the middle of the screen with a tinted backdrop.
* Created with the given body content and width.
*
* Closed and disposed via clicking anywhere outside the modal. May also be closed by
* calling the `dispose()` function.
*/
export class Modal1 extends Disposable {
private _dom: Element;
public create(
body: Element,
width: number = 300
) {
this._dom = modalBacker(
modal({style: `width: ${width}px;`, tabindex: "-1"},
dom.cls('clipboard_focus'),
body,
dom.on('click', (e) => e.stopPropagation())
),
dom.on('click', () => this.dispose())
);
document.body.appendChild(this._dom);
this.autoDisposeCallback(() => {
document.body.removeChild(this._dom);
});
}
}

@ -6,7 +6,7 @@
*/ */
import {prepareForTransition} from 'app/client/ui/transitions'; import {prepareForTransition} from 'app/client/ui/transitions';
import {colors, testId} from 'app/client/ui2018/cssVars'; import {testId, theme} from 'app/client/ui2018/cssVars';
import {IconName} from 'app/client/ui2018/IconList'; import {IconName} from 'app/client/ui2018/IconList';
import {icon} from 'app/client/ui2018/icons'; import {icon} from 'app/client/ui2018/icons';
import {dom, DomContents, DomElementArg, DomElementMethod, styled} from 'grainjs'; import {dom, DomContents, DomElementArg, DomElementMethod, styled} from 'grainjs';
@ -225,11 +225,11 @@ export function infoTooltip(tipContent: DomContents, ...domArgs: DomElementArg[]
const cssTooltip = styled('div', ` const cssTooltip = styled('div', `
position: absolute; position: absolute;
z-index: 5000; /* should be higher than a modal */ z-index: 5000; /* should be higher than a modal */
background-color: rgba(0, 0, 0, 0.75); background-color: ${theme.tooltipBg};
border-radius: 3px; border-radius: 3px;
box-shadow: 0 0 2px rgba(0,0,0,0.5); box-shadow: 0 0 2px rgba(0,0,0,0.5);
text-align: center; text-align: center;
color: white; color: ${theme.tooltipFg};
width: auto; width: auto;
font-family: sans-serif; font-family: sans-serif;
font-size: 10pt; font-size: 10pt;
@ -246,19 +246,19 @@ const cssTooltipCloseButton = styled('div', `
line-height: 16px; line-height: 16px;
text-align: center; text-align: center;
margin: -4px -4px -4px 8px; margin: -4px -4px -4px 8px;
--icon-color: white; --icon-color: ${theme.tooltipCloseButtonFg};
border-radius: 16px; border-radius: 16px;
&:hover { &:hover {
background-color: white; background-color: ${theme.tooltipCloseButtonHoverBg};
--icon-color: black; --icon-color: ${theme.tooltipCloseButtonHoverFg};
} }
`); `);
const cssIconTooltip = styled(icon, ` const cssIconTooltip = styled(icon, `
height: 12px; height: 12px;
width: 12px; width: 12px;
background-color: ${colors.slate}; background-color: ${theme.tooltipIcon};
flex-shrink: 0; flex-shrink: 0;
`); `);

@ -7,7 +7,8 @@
*/ */
import {reportError} from 'app/client/models/AppModel'; import {reportError} from 'app/client/models/AppModel';
import {dom, DomArg} from 'grainjs'; import {theme} from 'app/client/ui2018/cssVars';
import {dom, DomArg, styled} from 'grainjs';
export interface ITransientInputOptions { export interface ITransientInputOptions {
initialValue: string; initialValue: string;
@ -36,7 +37,7 @@ export function transientInput({initialValue, save, close}: ITransientInputOptio
setTimeout(() => { input.focus(); input.select(); }, 10); setTimeout(() => { input.focus(); input.select(); }, 10);
} }
const input = dom('input', {type: 'text', placeholder: 'Enter name'}, const input = cssInput({type: 'text', placeholder: 'Enter name'},
dom.prop('value', initialValue), dom.prop('value', initialValue),
dom.on('blur', () => onSave(false)), dom.on('blur', () => onSave(false)),
dom.onKeyDown({ dom.onKeyDown({
@ -48,3 +49,12 @@ export function transientInput({initialValue, save, close}: ITransientInputOptio
delayedFocus(); delayedFocus();
return input; return input;
} }
const cssInput = styled('input', `
background-color: transparent;
color: ${theme.inputFg};
&::placeholder {
color: ${theme.inputPlaceholderFg};
}
`);

@ -1,7 +1,7 @@
import * as commands from 'app/client/components/commands'; import * as commands from 'app/client/components/commands';
import { urlState } from 'app/client/models/gristUrlState'; import { urlState } from 'app/client/models/gristUrlState';
import { IOnBoardingMsg, startOnBoarding } from "app/client/ui/OnBoardingPopups"; import { IOnBoardingMsg, startOnBoarding } from "app/client/ui/OnBoardingPopups";
import { colors } from 'app/client/ui2018/cssVars'; import { theme } from 'app/client/ui2018/cssVars';
import { icon } from "app/client/ui2018/icons"; import { icon } from "app/client/ui2018/icons";
import { cssLink } from "app/client/ui2018/links"; import { cssLink } from "app/client/ui2018/links";
import { dom, styled } from "grainjs"; import { dom, styled } from "grainjs";
@ -54,7 +54,7 @@ export const welcomeTour: IOnBoardingMsg[] = [
selector: '.tour-share-icon', selector: '.tour-share-icon',
title: 'Sharing', title: 'Sharing',
body: () => [ body: () => [
dom('p', 'Use the Share button (', Icon('Share'), ') to share the document or export data.') dom('p', 'Use the Share button (', TopBarButtonIcon('Share'), ') to share the document or export data.')
], ],
placement: 'bottom', placement: 'bottom',
cropPadding: true, cropPadding: true,
@ -90,7 +90,7 @@ export function startWelcomeTour(onFinishCB: () => void) {
const KeyContent = styled('span', ` const KeyContent = styled('span', `
font-style: normal; font-style: normal;
font-family: inherit; font-family: inherit;
color: ${colors.darkGreen}; color: ${theme.shortcutKeyPrimaryFg};
`); `);
const KeyStrong = styled(KeyContent, ` const KeyStrong = styled(KeyContent, `
@ -102,20 +102,20 @@ const Key = styled('div', `
padding: 2px 5px; padding: 2px 5px;
border-radius: 4px; border-radius: 4px;
margin: 0px 2px; margin: 0px 2px;
border: 1px solid ${colors.slate}; border: 1px solid ${theme.shortcutKeyBorder};
color: black; color: ${theme.shortcutKeyFg};
background-color: white; background-color: ${theme.shortcutKeyBg};
font-family: inherit; font-family: inherit;
font-style: normal; font-style: normal;
white-space: nowrap; white-space: nowrap;
`); `);
const Icon = styled(icon, ` const TopBarButtonIcon = styled(icon, `
--icon-color: ${colors.lightGreen}; --icon-color: ${theme.topBarButtonPrimaryFg};
`); `);
const GreyIcon = styled(icon, ` const GreyIcon = styled(icon, `
--icon-color: ${colors.slate}; --icon-color: ${theme.shortcutKeySecondaryFg};
margin-right: 8px; margin-right: 8px;
`); `);

@ -1,6 +1,6 @@
import {basicButton, primaryButton} from 'app/client/ui2018/buttons'; import {basicButton, primaryButton} from 'app/client/ui2018/buttons';
import {isLight, swatches} from 'app/client/ui2018/ColorPalette'; import {isLight, swatches} from 'app/client/ui2018/ColorPalette';
import {colors, testId, vars} from 'app/client/ui2018/cssVars'; import {colors, testId, theme, vars} from 'app/client/ui2018/cssVars';
import {textInput} from 'app/client/ui2018/editableLabel'; import {textInput} from 'app/client/ui2018/editableLabel';
import {IconName} from 'app/client/ui2018/IconList'; import {IconName} from 'app/client/ui2018/IconList';
import {icon} from 'app/client/ui2018/icons'; import {icon} from 'app/client/ui2018/icons';
@ -348,7 +348,8 @@ const cssFontOption = styled('div', `
display: grid; display: grid;
place-items: center; place-items: center;
flex-grow: 1; flex-grow: 1;
background: white; background: ${colors.light};
--icon-color: ${colors.dark};
height: 24px; height: 24px;
cursor: pointer; cursor: pointer;
&:hover:not(&-selected) { &:hover:not(&-selected) {
@ -418,12 +419,13 @@ const cssContent = styled('div', `
`); `);
const cssHexBox = styled(textInput, ` const cssHexBox = styled(textInput, `
border: 1px solid ${colors.darkGrey}; border: 1px solid ${theme.inputBorder};
border-left: none; border-left: none;
font-size: ${vars.smallFontSize}; font-size: ${vars.smallFontSize};
display: flex; display: flex;
align-items: center; align-items: center;
color: ${colors.slate}; color: ${theme.lightText};
background-color: ${theme.inputBg};
width: 56px; width: 56px;
outline: none; outline: none;
padding: 0 3px; padding: 0 3px;

@ -6,35 +6,27 @@
* Workspace is a clickable link and document and page names are editable labels. * Workspace is a clickable link and document and page names are editable labels.
*/ */
import { urlState } from 'app/client/models/gristUrlState'; import { urlState } from 'app/client/models/gristUrlState';
import { colors, cssHideForNarrowScreen, mediaNotSmall, testId } from 'app/client/ui2018/cssVars'; import { cssHideForNarrowScreen, mediaNotSmall, testId, theme } from 'app/client/ui2018/cssVars';
import { editableLabel } from 'app/client/ui2018/editableLabel'; import { editableLabel } from 'app/client/ui2018/editableLabel';
import { icon } from 'app/client/ui2018/icons'; import { icon } from 'app/client/ui2018/icons';
import { cssLink } from 'app/client/ui2018/links';
import { UserOverride } from 'app/common/DocListAPI'; import { UserOverride } from 'app/common/DocListAPI';
import { userOverrideParams } from 'app/common/gristUrls'; import { userOverrideParams } from 'app/common/gristUrls';
import { BindableValue, dom, Observable, styled } from 'grainjs'; import { BindableValue, dom, Observable, styled } from 'grainjs';
import { tooltip } from 'popweasel'; import { tooltip } from 'popweasel';
export const cssBreadcrumbs = styled('div', ` export const cssBreadcrumbs = styled('div', `
color: ${colors.slate}; color: ${theme.lightText};
white-space: nowrap; white-space: nowrap;
cursor: default; cursor: default;
`); `);
export const cssBreadcrumbsLink = styled('a', `
color: ${colors.lightGreen};
text-decoration: none;
&:hover {
text-decoration: underline;
}
`);
export const separator = styled('span', ` export const separator = styled('span', `
padding: 0 2px; padding: 0 2px;
`); `);
const cssIcon = styled(icon, ` const cssIcon = styled(icon, `
background-color: ${colors.lightGreen}; background-color: ${theme.accentIcon};
margin-top: -2px; margin-top: -2px;
`); `);
@ -43,7 +35,7 @@ const cssPublicIcon = styled(cssIcon, `
margin-top: -4px; margin-top: -4px;
`); `);
const cssWorkspaceName = styled(cssBreadcrumbsLink, ` const cssWorkspaceName = styled(cssLink, `
margin-left: 8px; margin-left: 8px;
`); `);
@ -54,7 +46,7 @@ const cssWorkspaceNarrowScreen = styled(icon, `
margin-bottom: 4px; margin-bottom: 4px;
margin-left: -7px; margin-left: -7px;
margin-right: 8px; margin-right: 8px;
background-color: ${colors.slate}; background-color: ${theme.lightText};
cursor: pointer; cursor: pointer;
@media ${mediaNotSmall} { @media ${mediaNotSmall} {
& { & {
@ -65,21 +57,21 @@ const cssWorkspaceNarrowScreen = styled(icon, `
const cssEditableName = styled('input', ` const cssEditableName = styled('input', `
&:hover, &:focus { &:hover, &:focus {
color: ${colors.dark}; color: ${theme.text};
} }
`); `);
const cssTag = styled('span', ` const cssTag = styled('span', `
background-color: ${colors.slate}; background-color: ${theme.breadcrumbsTagBg};
color: white; color: ${theme.breadcrumbsTagFg};
border-radius: 3px; border-radius: 3px;
padding: 0 4px; padding: 0 4px;
margin-left: 4px; margin-left: 4px;
`); `);
const cssAlertTag = styled(cssTag, ` const cssAlertTag = styled(cssTag, `
background-color: ${colors.error}; background-color: ${theme.breadcrumbsTagAlertBg};
--icon-color: white; --icon-color: ${theme.breadcrumbsTagFg};
a { a {
cursor: pointer; cursor: pointer;
} }

@ -1,4 +1,4 @@
import {colors, testId, vars} from 'app/client/ui2018/cssVars'; import {colors, testId, theme, vars} from 'app/client/ui2018/cssVars';
import {IconName} from 'app/client/ui2018/IconList'; import {IconName} from 'app/client/ui2018/IconList';
import {icon} from 'app/client/ui2018/icons'; import {icon} from 'app/client/ui2018/icons';
import {isColorDark} from 'app/common/gutil'; import {isColorDark} from 'app/common/gutil';
@ -142,7 +142,7 @@ export const cssButtonSelect = styled('div', `
display: flex; display: flex;
/* Vars */ /* Vars */
color: ${colors.dark}; color: ${theme.text};
flex: 1 1 0; flex: 1 1 0;
`); `);
@ -165,8 +165,9 @@ const cssSelectorBtn = styled('div', `
white-space: nowrap; white-space: nowrap;
padding: 4px 10px; padding: 4px 10px;
border: 1px solid ${colors.darkGrey}; background-color: ${theme.buttonGroupBg};
--icon-color: ${colors.slate}; border: 1px solid ${theme.buttonGroupBorder};
--icon-color: ${theme.buttonGroupIcon};
margin-left: -1px; margin-left: -1px;
@ -184,15 +185,15 @@ const cssSelectorBtn = styled('div', `
} }
&:hover:not(&-selected) { &:hover:not(&-selected) {
border: 1px solid ${colors.hover}; border: 1px solid ${theme.buttonGroupBorderHover};
z-index: 5; /* Update z-index so selected borders take precedent */ z-index: 5; /* Update z-index so selected borders take precedent */
} }
&-selected { &-selected {
color: ${colors.light}; color: ${theme.buttonGroupSelectedFg};
--icon-color: ${colors.light}; --icon-color: ${theme.buttonGroupSelectedFg};
border: 1px solid ${colors.dark}; border: 1px solid ${theme.buttonGroupSelectedBorder};
background-color: ${colors.dark}; background-color: ${theme.buttonGroupSelectedBg};
z-index: 10; /* Update z-index so selected borders take precedent */ z-index: 10; /* Update z-index so selected borders take precedent */
} }
@ -202,18 +203,18 @@ const cssSelectorBtn = styled('div', `
border-radius: ${vars.controlBorderRadius}; border-radius: ${vars.controlBorderRadius};
margin-left: 0px; margin-left: 0px;
padding: 8px; padding: 8px;
color: ${colors.slate}; color: ${theme.buttonGroupLightFg};
--icon-color: ${colors.slate}; --icon-color: ${theme.buttonGroupLightFg};
} }
.${cssButtonSelect.className}-light > &-selected { .${cssButtonSelect.className}-light > &-selected {
border: none; border: none;
color: ${colors.lightGreen}; color: ${theme.buttonGroupLightSelectedFg};
--icon-color: ${colors.lightGreen}; --icon-color: ${theme.buttonGroupLightSelectedFg};
background-color: initial; background-color: initial;
} }
.${cssButtonSelect.className}-light > &:hover { .${cssButtonSelect.className}-light > &:hover {
border: none; border: none;
background-color: ${colors.mediumGrey}; background-color: ${theme.hover};
} }
`); `);

@ -12,7 +12,7 @@
* `primaryButton('Primary button', dom.prop('disabled', true))` * `primaryButton('Primary button', dom.prop('disabled', true))`
*/ */
import { colors, vars } from 'app/client/ui2018/cssVars'; import { theme, vars } from 'app/client/ui2018/cssVars';
import { tbind } from 'app/common/tbind'; import { tbind } from 'app/common/tbind';
import { dom, DomElementArg, styled } from 'grainjs'; import { dom, DomElementArg, styled } from 'grainjs';
@ -29,11 +29,12 @@ export const cssButton = styled('button', `
padding: 4px 8px; padding: 4px 8px;
background-color: transparent; background-color: transparent;
color: ${vars.controlFg}; color: ${theme.controlFg};
--icon-color: ${vars.controlFg}; --icon-color: ${theme.controlFg};
border: ${vars.controlBorder}; border: ${vars.controlBorder};
border-radius: ${vars.controlBorderRadius}; border-radius: ${vars.controlBorderRadius};
border-color: ${theme.controlBorder};
cursor: pointer; cursor: pointer;
@ -44,29 +45,29 @@ export const cssButton = styled('button', `
} }
&-primary { &-primary {
background-color: ${vars.primaryBg}; background-color: ${theme.controlPrimaryBg};
color: ${vars.primaryFg}; color: ${theme.controlPrimaryFg};
--icon-color: ${vars.primaryFg}; --icon-color: ${theme.controlPrimaryFg};
border-color: ${vars.primaryBg}; border-color: ${theme.controlPrimaryBg};
} }
&:hover { &:hover {
color: ${vars.controlFgHover}; color: ${theme.controlHoverFg};
--icon-color: ${vars.controlFgHover}; --icon-color: ${theme.controlHoverFg};
border-color: ${vars.controlFgHover}; border-color: ${theme.controlHoverFg};
} }
&-primary:hover { &-primary:hover {
color: ${vars.primaryFg}; color: ${theme.controlPrimaryFg};
--icon-color: ${vars.primaryFg}; --icon-color: ${theme.controlPrimaryFg};
background-color: ${vars.primaryBgHover}; background-color: ${theme.controlPrimaryHoverBg};
border-color: ${vars.primaryBgHover}; border-color: ${theme.controlPrimaryHoverBg};
} }
&:disabled { &:disabled {
cursor: not-allowed; cursor: not-allowed;
color: ${colors.light}; color: ${theme.controlDisabledFg};
--icon-color: ${colors.light}; --icon-color: ${theme.controlDisabledFg};
background-color: ${colors.slate}; background-color: ${theme.controlDisabledBg};
border-color: ${colors.slate}; border-color: ${theme.controlDisabledBg};
} }
`); `);
@ -107,7 +108,7 @@ export const textButton = styled(cssButton, `
padding: 0px; padding: 0px;
background-color: inherit !important; background-color: inherit !important;
&:disabled { &:disabled {
color: ${colors.inactiveCursor}; color: ${theme.controlPrimaryDisabled};
} }
`); `);

@ -15,7 +15,7 @@
* labeledSquareCheckbox(observable(false), 'Include other values', dom.prop('disabled', true)), * labeledSquareCheckbox(observable(false), 'Include other values', dom.prop('disabled', true)),
*/ */
import { colors } from 'app/client/ui2018/cssVars'; import { theme } from 'app/client/ui2018/cssVars';
import { Computed, dom, DomArg, styled } from 'grainjs'; import { Computed, dom, DomArg, styled } from 'grainjs';
import { Observable } from 'grainjs'; import { Observable } from 'grainjs';
@ -28,9 +28,9 @@ export const cssLabel = styled('label', `
outline: none; outline: none;
user-select: none; user-select: none;
--color: ${colors.darkGrey}; --color: ${theme.checkboxBorder};
&:hover { &:hover {
--color: ${colors.hover}; --color: ${theme.checkboxBorderHover};
} }
`); `);
@ -53,20 +53,14 @@ export const cssCheckboxSquare = styled('input', `
--radius: 3px; --radius: 3px;
&:checked:enabled, &:indeterminate:enabled { &:checked:enabled, &:indeterminate:enabled {
--color: ${colors.lightGreen}; --color: ${theme.controlPrimaryBg};
} }
&:disabled { &:disabled {
--color: ${colors.darkGrey}; --color: ${theme.checkboxDisabledBg};
cursor: not-allowed; cursor: not-allowed;
} }
.${cssLabel.className}:hover > &:checked:enabled,
.${cssLabel.className}:hover > &:indeterminate:enabled, {
--color: ${colors.darkGreen};
}
&::before, &::after { &::before, &::after {
content: ''; content: '';
@ -86,6 +80,14 @@ export const cssCheckboxSquare = styled('input', `
background-color: var(--color); background-color: var(--color);
} }
&:not(:checked):indeterminate::after {
-webkit-mask-image: var(--icon-Minus);
}
&:not(:disabled)::after {
background-color: ${theme.checkboxBg};
}
&:checked::after, &:indeterminate::after { &:checked::after, &:indeterminate::after {
content: ''; content: '';
position: absolute; position: absolute;
@ -95,15 +97,7 @@ export const cssCheckboxSquare = styled('input', `
-webkit-mask-size: contain; -webkit-mask-size: contain;
-webkit-mask-position: center; -webkit-mask-position: center;
-webkit-mask-repeat: no-repeat; -webkit-mask-repeat: no-repeat;
background-color: ${colors.light}; background-color: ${theme.controlPrimaryFg};
}
&:not(:checked):indeterminate::after {
-webkit-mask-image: var(--icon-Minus);
}
&:not(:disabled)::after {
background-color: ${colors.light};
} }
`); `);
@ -113,7 +107,7 @@ export const cssCheckboxCircle = styled(cssCheckboxSquare, `
export const cssLabelText = styled('span', ` export const cssLabelText = styled('span', `
margin-left: 8px; margin-left: 8px;
color: ${colors.dark}; color: ${theme.text};
font-weight: initial; /* negate bootstrap */ font-weight: initial; /* negate bootstrap */
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;

@ -8,6 +8,7 @@
*/ */
import {urlState} from 'app/client/models/gristUrlState'; import {urlState} from 'app/client/models/gristUrlState';
import {getTheme, ProductFlavor} from 'app/client/ui/CustomThemes'; import {getTheme, ProductFlavor} from 'app/client/ui/CustomThemes';
import {Theme, ThemeAppearance} from 'app/common/ThemePrefs';
import {dom, makeTestId, Observable, styled, TestId} from 'grainjs'; import {dom, makeTestId, Observable, styled, TestId} from 'grainjs';
import debounce = require('lodash/debounce'); import debounce = require('lodash/debounce');
import values = require('lodash/values'); import values = require('lodash/values');
@ -15,17 +16,32 @@ import values = require('lodash/values');
const VAR_PREFIX = 'grist'; const VAR_PREFIX = 'grist';
class CustomProp { class CustomProp {
constructor(public name: string, public value: string) { } constructor(public name: string, public value?: string, public fallback?: string | CustomProp) {
}
public decl(): string | undefined {
if (this.value === undefined) { return undefined; }
public decl() {
return `--${VAR_PREFIX}-${this.name}: ${this.value};`; return `--${VAR_PREFIX}-${this.name}: ${this.value};`;
} }
public toString() { public toString(): string {
return `var(--${VAR_PREFIX}-${this.name})`; let value = `--${VAR_PREFIX}-${this.name}`;
if (this.fallback) {
value += `, ${this.fallback}`;
}
return `var(${value})`;
} }
} }
/**
* Theme-agnostic color properties.
*
* These are appropriate for UI elements whose color should not change based on the active
* theme. Generally, you should instead use the properties defined in `theme`, which will change
* based on the active theme.
*/
export const colors = { export const colors = {
lightGrey: new CustomProp('color-light-grey', '#F7F7F7'), lightGrey: new CustomProp('color-light-grey', '#F7F7F7'),
mediumGrey: new CustomProp('color-medium-grey', 'rgba(217,217,217,0.6)'), mediumGrey: new CustomProp('color-medium-grey', 'rgba(217,217,217,0.6)'),
@ -46,7 +62,7 @@ export const colors = {
lightBlue: new CustomProp('color-light-blue', '#3B82F6'), lightBlue: new CustomProp('color-light-blue', '#3B82F6'),
orange: new CustomProp('color-orange', '#F9AE41'), orange: new CustomProp('color-orange', '#F9AE41'),
cursor: new CustomProp('color-cursor', '#16B378'), // cursor is lightGreen cursor: new CustomProp('color-cursor', '#16B378'),
selection: new CustomProp('color-selection', 'rgba(22,179,120,0.15)'), selection: new CustomProp('color-selection', 'rgba(22,179,120,0.15)'),
selectionOpaque: new CustomProp('color-selection-opaque', '#DCF4EB'), selectionOpaque: new CustomProp('color-selection-opaque', '#DCF4EB'),
selectionDarkerOpaque: new CustomProp('color-selection-darker-opaque', '#d6eee5'), selectionDarkerOpaque: new CustomProp('color-selection-darker-opaque', '#d6eee5'),
@ -117,6 +133,512 @@ export const vars = {
toastBg: new CustomProp('toast-bg', '#040404'), toastBg: new CustomProp('toast-bg', '#040404'),
}; };
/**
* Theme-related color properties.
*
* Unlike `colors`, these properties don't define any values as they aren't known ahead of time.
* Instead, when the application loads, CSS variables mapped to these properties are attached to
* the document based on the user's theme preferences.
*
* In the case that CSS variables aren't attached to the document, their fallback values will be
* used. This ensures that styles are still applied even when there's trouble fetching preferences,
* and also serves as a method of maintaining backwards compatibility with custom CSS rules that
* use legacy variable names (prefixed with `grist-color-`).
*/
export const theme = {
/* Text */
text: new CustomProp('theme-text', undefined, colors.dark),
lightText: new CustomProp('theme-text-light', undefined, colors.slate),
darkText: new CustomProp('theme-text-dark', undefined, 'black'),
errorText: new CustomProp('theme-text-error', undefined, colors.error),
dangerText: new CustomProp('theme-text-danger', undefined, '#FFA500'),
disabledText: new CustomProp('theme-text-disabled', undefined, colors.slate),
/* Page */
pageBg: new CustomProp('theme-page-bg', undefined, colors.lightGrey),
pageBackdrop: new CustomProp('theme-page-backdrop', undefined, 'grey'),
/* Page Panels */
mainPanelBg: new CustomProp('theme-page-panels-main-panel-bg', undefined, 'white'),
leftPanelBg: new CustomProp('theme-page-panels-left-panel-bg', undefined, colors.lightGrey),
rightPanelBg: new CustomProp('theme-page-panels-right-panel-bg', undefined, colors.lightGrey),
topHeaderBg: new CustomProp('theme-page-panels-top-header-bg', undefined, 'white'),
bottomFooterBg: new CustomProp('theme-page-panels-bottom-footer-bg', undefined, 'white'),
pagePanelsBorder: new CustomProp('theme-page-panels-border', undefined, colors.mediumGrey),
pagePanelsBorderResizing: new CustomProp('theme-page-panels-border-resizing', undefined,
colors.lightGreen),
sidePanelOpenerFg: new CustomProp('theme-page-panels-side-panel-opener-fg', undefined,
colors.slate),
sidePanelOpenerActiveFg: new CustomProp('theme-page-panels-side-panel-opener-active-fg',
undefined, 'white'),
sidePanelOpenerActiveBg: new CustomProp('theme-page-panels-side-panel-opener-active-bg',
undefined, colors.lightGreen),
/* Add New */
addNewCircleFg: new CustomProp('theme-add-new-circle-fg', undefined, colors.light),
addNewCircleBg: new CustomProp('theme-add-new-circle-bg', undefined, colors.darkGreen),
addNewCircleHoverBg: new CustomProp('theme-add-new-circle-hover-bg', undefined,
colors.darkerGreen),
addNewCircleSmallFg: new CustomProp('theme-add-new-circle-small-fg', undefined, colors.light),
addNewCircleSmallBg: new CustomProp('theme-add-new-circle-small-bg', undefined,
colors.lightGreen),
addNewCircleSmallHoverBg: new CustomProp('theme-add-new-circle-small-hover-bg', undefined,
colors.darkGreen),
/* Top Bar */
topBarButtonPrimaryFg: new CustomProp('theme-top-bar-button-primary-fg', undefined,
colors.lightGreen),
topBarButtonSecondaryFg: new CustomProp('theme-top-bar-button-secondary-fg', undefined,
colors.slate),
topBarButtonDisabledFg: new CustomProp('theme-top-bar-button-disabled-fg', undefined,
colors.darkGrey),
topBarButtonErrorFg: new CustomProp('theme-top-bar-button-error-fg', undefined, colors.error),
/* Notifications */
notificationsPanelHeaderBg: new CustomProp('theme-notifications-panel-header-bg', undefined,
colors.lightGrey),
notificationsPanelBodyBg: new CustomProp('theme-notifications-panel-body-bg', undefined,
'white'),
notificationsPanelBorder: new CustomProp('theme-notifications-panel-border', undefined,
colors.darkGrey),
/* Toasts */
toastText: new CustomProp('theme-toast-text', undefined, colors.light),
toastLightText: new CustomProp('theme-toast-text-light', undefined, colors.slate),
toastBg: new CustomProp('theme-toast-bg', undefined, vars.toastBg),
toastErrorIcon: new CustomProp('theme-toast-error-icon', undefined, colors.error),
toastErrorBg: new CustomProp('theme-toast-error-bg', undefined, colors.error),
toastSuccessIcon: new CustomProp('theme-toast-success-icon', undefined, colors.darkGreen),
toastSuccessBg: new CustomProp('theme-toast-success-bg', undefined, colors.darkGreen),
toastWarningIcon: new CustomProp('theme-toast-warning-icon', undefined, colors.warning),
toastWarningBg: new CustomProp('theme-toast-warning-bg', undefined, colors.warningBg),
toastInfoIcon: new CustomProp('theme-toast-info-icon', undefined, colors.lightBlue),
toastInfoBg: new CustomProp('theme-toast-info-bg', undefined, colors.lightBlue),
toastControlFg: new CustomProp('theme-toast-control-fg', undefined, colors.lightGreen),
toastInfoControlFg: new CustomProp('theme-toast-control-info-fg', undefined, colors.lighterBlue),
/* Tooltips */
tooltipFg: new CustomProp('theme-tooltip-fg', undefined, 'white'),
tooltipBg: new CustomProp('theme-tooltip-bg', undefined, 'rgba(0, 0, 0, 0.75)'),
tooltipIcon: new CustomProp('theme-tooltip-icon', undefined, colors.slate),
tooltipCloseButtonFg: new CustomProp('theme-tooltip-close-button-fg', undefined, 'white'),
tooltipCloseButtonHoverFg: new CustomProp('theme-tooltip-close-button-hover-fg', undefined,
'black'),
tooltipCloseButtonHoverBg: new CustomProp('theme-tooltip-close-button-hover-bg', undefined,
'white'),
/* Modals */
modalBg: new CustomProp('theme-modal-bg', undefined, 'white'),
modalBackdrop: new CustomProp('theme-modal-backdrop', undefined, colors.backdrop),
modalBorder: new CustomProp('theme-modal-border', undefined, colors.mediumGreyOpaque),
modalBorderDark: new CustomProp('theme-modal-border-dark', undefined, colors.darkGrey),
modalBorderHover: new CustomProp('theme-modal-border-hover', undefined, colors.slate),
modalInnerShadow: new CustomProp('theme-modal-shadow-inner', undefined,
'rgba(31, 37, 50, 0.31)'),
modalOuterShadow: new CustomProp('theme-modal-shadow-outer', undefined,
'rgba(76, 86, 103, 0.24)'),
modalCloseButtonFg: new CustomProp('theme-modal-close-button-fg', undefined, colors.slate),
modalBackdropCloseButtonFg: new CustomProp('theme-modal-backdrop-close-button-fg', undefined,
vars.primaryBg),
modalBackdropCloseButtonHoverFg: new CustomProp('theme-modal-backdrop-close-button-hover-fg',
undefined, colors.lighterGreen),
/* Popups */
popupBg: new CustomProp('theme-popup-bg', undefined, 'white'),
popupInnerShadow: new CustomProp('theme-popup-shadow-inner', undefined,
'rgba(31, 37, 50, 0.31)'),
popupOuterShadow: new CustomProp('theme-popup-shadow-outer', undefined,
'rgba(76, 86, 103, 0.24)'),
popupCloseButtonFg: new CustomProp('theme-popup-close-button-fg', undefined, colors.slate),
/* Progress Bars */
progressBarFg: new CustomProp('theme-progress-bar-fg', undefined, colors.lightGreen),
progressBarErrorFg: new CustomProp('theme-progress-bar-error-fg', undefined, colors.error),
progressBarBg: new CustomProp('theme-progress-bar-bg', undefined, colors.darkGrey),
/* Links */
link: new CustomProp('theme-link', undefined, colors.lightGreen),
linkHover: new CustomProp('theme-link-hover', undefined, colors.lightGreen),
/* Hover */
hover: new CustomProp('theme-hover', undefined, colors.mediumGrey),
lightHover: new CustomProp('theme-hover-light', undefined, colors.lightGrey),
/* Cell Editor */
cellEditorFg: new CustomProp('theme-cell-editor-fg', undefined, colors.dark),
cellEditorBg: new CustomProp('theme-cell-editor-bg', undefined, colors.light),
/* Cursor */
cursor: new CustomProp('theme-cursor', undefined, colors.cursor),
cursorInactive: new CustomProp('theme-cursor-inactive', undefined, colors.inactiveCursor),
cursorReadonly: new CustomProp('theme-cursor-readonly', undefined, colors.slate),
/* Tables */
tableHeaderFg: new CustomProp('theme-table-header-fg', undefined, 'unset'),
tableHeaderSelectedFg: new CustomProp('theme-table-header-selected-fg', undefined, 'unset'),
tableHeaderBg: new CustomProp('theme-table-header-bg', undefined, colors.lightGrey),
tableHeaderSelectedBg: new CustomProp('theme-table-header-selected-bg', undefined,
colors.mediumGreyOpaque),
tableHeaderBorder: new CustomProp('theme-table-header-border', undefined, 'lightgray'),
tableHeaderBorderDark: new CustomProp('theme-table-header-border-dark', undefined,
colors.darkGrey),
tableBodyBg: new CustomProp('theme-table-body-bg', undefined, 'unset'),
tableBodyBorder: new CustomProp('theme-table-body-border', undefined, colors.darkGrey),
tableAddNewBg: new CustomProp('theme-table-add-new-bg', undefined, 'inherit'),
tableScrollShadow: new CustomProp('theme-table-scroll-shadow', undefined, '#444444'),
tableFrozenColumnsBorder: new CustomProp('theme-table-frozen-columns-border', undefined,
'#999999'),
tableDragDropIndicator: new CustomProp('theme-table-drag-drop-indicator', undefined, 'gray'),
tableDragDropShadow: new CustomProp('theme-table-drag-drop-shadow', undefined, '#F0F0F0'),
/* Cards */
cardCompactWidgetBg: new CustomProp('theme-card-compact-widget-bg', undefined,
colors.mediumGrey),
cardCompactRecordBg: new CustomProp('theme-card-compact-record-bg', undefined, 'white'),
cardBlocksBg: new CustomProp('theme-card-blocks-bg', undefined, colors.mediumGrey),
cardFormLabel: new CustomProp('theme-card-form-label', undefined, colors.slate),
cardCompactLabel: new CustomProp('theme-card-compact-label', undefined, colors.slate),
cardBlocksLabel: new CustomProp('theme-card-blocks-label', undefined, colors.slate),
cardFormBorder: new CustomProp('theme-card-form-border', undefined, 'lightgrey'),
cardCompactBorder: new CustomProp('theme-card-compact-border', undefined, colors.darkGrey),
cardEditingLayoutBg: new CustomProp('theme-card-editing-layout-bg', undefined,
'rgba(192, 192, 192, 0.2)'),
cardEditingLayoutBorder: new CustomProp('theme-card-editing-layout-border', undefined,
colors.darkGrey),
/* Card Lists */
cardListFormBorder: new CustomProp('theme-card-list-form-border', undefined, colors.darkGrey),
cardListBlocksBorder: new CustomProp('theme-card-list-blocks-border', undefined,
colors.darkGrey),
/* Selection */
selection: new CustomProp('theme-selection', undefined, colors.selection),
selectionOpaqueFg: new CustomProp('theme-selection-opaque-fg', undefined, 'unset'),
selectionOpaqueBg: new CustomProp('theme-selection-opaque-bg', undefined,
colors.selectionOpaque),
selectionOpaqueDarkBg: new CustomProp('theme-selection-opaque-dark-bg', undefined,
colors.selectionDarkerOpaque),
/* Widgets */
widgetBorder: new CustomProp('theme-widget-border', undefined, colors.darkGrey),
widgetActiveBorder: new CustomProp('theme-widget-active-border', undefined, colors.lightGreen),
widgetInactiveStripesLight: new CustomProp('theme-widget-inactive-stripes-light', undefined,
colors.lightGrey),
widgetInactiveStripesDark: new CustomProp('theme-widget-inactive-stripes-dark', undefined,
colors.mediumGreyOpaque),
/* Pinned Docs */
pinnedDocFooterBg: new CustomProp('theme-pinned-doc-footer-bg', undefined, colors.light),
pinnedDocBorder: new CustomProp('theme-pinned-doc-border', undefined, colors.mediumGrey),
pinnedDocBorderHover: new CustomProp('theme-pinned-doc-border-hover', undefined, colors.slate),
pinnedDocEditorBg: new CustomProp('theme-pinned-doc-editor-bg', undefined, colors.mediumGrey),
/* Raw Data */
rawDataTableBorder: new CustomProp('theme-raw-data-table-border', undefined, colors.mediumGrey),
rawDataTableBorderHover: new CustomProp('theme-raw-data-table-border-hover',
undefined, colors.slate),
/* Controls */
controlFg: new CustomProp('theme-control-fg', undefined, vars.controlFg),
controlPrimaryFg: new CustomProp('theme-control-primary-fg', undefined, vars.primaryFg),
controlPrimaryBg: new CustomProp('theme-control-primary-bg', undefined, vars.primaryBg),
controlSecondaryFg: new CustomProp('theme-control-secondary-fg', undefined, colors.slate),
controlHoverFg: new CustomProp('theme-control-hover-fg', undefined, vars.controlFgHover),
controlPrimaryHoverBg: new CustomProp('theme-control-primary-hover-bg', undefined,
vars.primaryBgHover),
controlSecondaryHoverFg: new CustomProp('theme-control-secondary-hover-fg', undefined,
colors.dark),
controlSecondaryHoverBg: new CustomProp('theme-control-secondary-hover-bg', undefined,
colors.darkGrey),
controlDisabledFg: new CustomProp('theme-control-disabled-fg', undefined, colors.light),
controlDisabledBg: new CustomProp('theme-control-disabled-bg', undefined, colors.slate),
controlPrimaryDisabled: new CustomProp('theme-control-primary-disabled', undefined,
colors.inactiveCursor),
controlBorder: new CustomProp('theme-control-border', undefined, '#11B683'),
/* Checkboxes */
checkboxBg: new CustomProp('theme-checkbox-bg', undefined, colors.light),
checkboxDisabledBg: new CustomProp('theme-checkbox-disabled-bg', undefined, colors.darkGrey),
checkboxBorder: new CustomProp('theme-checkbox-border', undefined, colors.darkGrey),
checkboxBorderHover: new CustomProp('theme-checkbox-border-hover', undefined, colors.hover),
/* Move Docs */
moveDocsSelectedFg: new CustomProp('theme-move-docs-selected-fg', undefined, 'white'),
moveDocsSelectedBg: new CustomProp('theme-move-docs-selected-bg', undefined, colors.lightGreen),
moveDocsDisabledFg: new CustomProp('theme-move-docs-disabled-bg', undefined, colors.darkGrey),
/* Filter Bar */
filterBarButtonSavedFg: new CustomProp('theme-filter-bar-button-saved-fg', undefined,
colors.light),
filterBarButtonSavedBg: new CustomProp('theme-filter-bar-button-saved-bg', undefined,
colors.slate),
filterBarButtonSavedHoverBg: new CustomProp('theme-filter-bar-button-saved-hover-bg', undefined,
colors.darkGrey),
/* Icon Buttons */
iconButtonFg: new CustomProp('theme-icon-button-fg', undefined, colors.light),
iconButtonPrimaryBg: new CustomProp('theme-icon-button-primary-bg', undefined,
colors.lightGreen),
iconButtonPrimaryHoverBg: new CustomProp('theme-icon-button-primary-hover-bg',
undefined, colors.darkGreen),
iconButtonSecondaryBg: new CustomProp('theme-icon-button-secondary-bg', undefined,
colors.darkGrey),
iconButtonSecondaryHoverBg: new CustomProp('theme-icon-button-secondary-hover-bg',
undefined, colors.slate),
/* Left Panel */
pageHoverBg: new CustomProp('theme-left-panel-page-hover-bg', undefined, colors.mediumGrey),
activePageFg: new CustomProp('theme-left-panel-active-page-fg', undefined, 'white'),
activePageBg: new CustomProp('theme-left-panel-active-page-bg', undefined, colors.darkBg),
disabledPageFg: new CustomProp('theme-left-panel-disabled-page-fg', undefined, colors.darkGrey),
pageOptionsFg: new CustomProp('theme-left-panel-page-options-bg', undefined, colors.slate),
pageOptionsHoverFg: new CustomProp('theme-left-panel-page-options-hover-fg', undefined, 'white'),
pageOptionsHoverBg: new CustomProp('theme-left-panel-page-options-hover-bg', undefined,
colors.darkGrey),
pageOptionsSelectedHoverBg: new CustomProp('theme-left-panel-page-options-selected-hover-bg',
undefined, colors.slate),
pageInitialsFg: new CustomProp('theme-left-panel-page-initials-fg', undefined, 'white'),
pageInitialsBg: new CustomProp('theme-left-panel-page-initials-bg', undefined, colors.slate),
/* Right Panel */
rightPanelTabFg: new CustomProp('theme-right-panel-tab-fg', undefined, colors.dark),
rightPanelTabBg: new CustomProp('theme-right-panel-tab-bg', undefined, colors.lightGrey),
rightPanelTabIcon: new CustomProp('theme-right-panel-tab-icon', undefined, colors.slate),
rightPanelTabIconHover: new CustomProp('theme-right-panel-tab-icon-hover', undefined,
colors.lightGreen),
rightPanelTabHoverBg: new CustomProp('theme-right-panel-tab-hover-bg', undefined,
colors.mediumGrey),
rightPanelTabSelectedFg: new CustomProp('theme-right-panel-tab-selected-fg', undefined,
colors.light),
rightPanelTabSelectedBg: new CustomProp('theme-right-panel-tab-selected-bg', undefined,
colors.lightGreen),
rightPanelTabCloseButtonHoverBg: new CustomProp('theme-right-panel-tab-close-button-hover-bg',
undefined, colors.darkGreen),
rightPanelSubtabFg: new CustomProp('theme-right-panel-subtab-fg', undefined, colors.lightGreen),
rightPanelSubtabSelectedFg: new CustomProp('theme-right-panel-subtab-selected-fg', undefined,
colors.dark),
rightPanelSubtabSelectedUnderline: new CustomProp('theme-right-panel-subtab-selected-underline',
undefined, colors.lightGreen),
rightPanelSubtabHoverFg: new CustomProp('theme-right-panel-subtab-hover-fg', undefined,
colors.darkGreen),
rightPanelSubtabHoverUnderline: new CustomProp('theme-right-panel-subtab-hover-underline',
undefined, colors.lightGreen),
rightPanelDisabledOverlay: new CustomProp('theme-right-panel-disabled-overlay', undefined,
'white'),
rightPanelToggleButtonEnabledFg: new CustomProp('theme-right-panel-toggle-button-enabled-fg',
undefined, colors.light),
rightPanelToggleButtonEnabledBg: new CustomProp('theme-right-panel-toggle-button-enabled-bg',
undefined, colors.dark),
rightPanelToggleButtonEnabledHoverFg: new CustomProp(
'theme-right-panel-toggle-button-enabled-hover-fg', undefined, colors.darkGrey),
rightPanelToggleButtonDisabledFg: new CustomProp('theme-right-panel-toggle-button-disabled-fg',
undefined, colors.light),
rightPanelToggleButtonDisabledBg: new CustomProp('theme-right-panel-toggle-button-disabled-bg',
undefined, colors.mediumGreyOpaque),
rightPanelFieldSettingsBg: new CustomProp('theme-right-panel-field-settings-bg',
undefined, colors.mediumGreyOpaque),
rightPanelFieldSettingsButtonBg: new CustomProp('theme-right-panel-field-settings-button-bg',
undefined, 'lightgrey'),
/* Document History */
documentHistorySnapshotFg: new CustomProp('theme-document-history-snapshot-fg', undefined,
colors.dark),
documentHistorySnapshotSelectedFg: new CustomProp('theme-document-history-snapshot-selected-fg',
undefined, colors.light),
documentHistorySnapshotBg: new CustomProp('theme-document-history-snapshot-bg', undefined,
'white'),
documentHistorySnapshotSelectedBg: new CustomProp('theme-document-history-snapshot-selected-bg',
undefined, colors.dark),
documentHistorySnapshotBorder: new CustomProp('theme-document-history-snapshot-border',
undefined, colors.mediumGrey),
documentHistoryActivityText: new CustomProp('theme-document-history-activity-text', undefined,
'unset'),
documentHistoryActivityLightText: new CustomProp('theme-document-history-activity-text-light',
undefined, '#333333'),
/* Accents */
accentIcon: new CustomProp('theme-accent-icon', undefined, colors.lightGreen),
accentBorder: new CustomProp('theme-accent-border', undefined, colors.lightGreen),
accentText: new CustomProp('theme-accent-text', undefined, colors.lightGreen),
/* Inputs */
inputFg: new CustomProp('theme-input-fg', undefined, 'black'),
inputBg: new CustomProp('theme-input-bg', undefined, 'white'),
inputDisabledFg: new CustomProp('theme-input-disabled-fg', undefined, colors.slate),
inputDisabledBg: new CustomProp('theme-input-disabled-bg', undefined, colors.lightGrey),
inputPlaceholderFg: new CustomProp('theme-input-placeholder-fg', undefined, '#757575'),
inputBorder: new CustomProp('theme-input-border', undefined, colors.darkGrey),
inputValid: new CustomProp('theme-input-valid', undefined, colors.lightGreen),
inputInvalid: new CustomProp('theme-input-invalid', undefined, colors.error),
inputFocus: new CustomProp('theme-input-focus', undefined, '#5E9ED6'),
inputReadonlyBg: new CustomProp('theme-input-readonly-bg', undefined, colors.lightGrey),
inputReadonlyBorder: new CustomProp('theme-input-readonly-border', undefined, colors.mediumGreyOpaque),
/* Choice Entry */
choiceEntryBg: new CustomProp('theme-choice-entry-bg', undefined, 'white'),
choiceEntryBorder: new CustomProp('theme-choice-entry-border', undefined, colors.darkGrey),
choiceEntryBorderHover: new CustomProp('theme-choice-entry-border-hover', undefined,
colors.hover),
/* Select Buttons */
selectButtonFg: new CustomProp('theme-select-button-fg', undefined, colors.dark),
selectButtonPlaceholderFg: new CustomProp('theme-select-button-placeholder-fg', undefined,
colors.slate),
selectButtonDisabledFg: new CustomProp('theme-select-button-disabled-fg', undefined, 'grey'),
selectButtonBg: new CustomProp('theme-select-button-bg', undefined, 'white'),
selectButtonBorder: new CustomProp('theme-select-button-border', undefined, colors.darkGrey),
selectButtonBorderInvalid: new CustomProp('theme-select-button-border-invalid', undefined,
colors.error),
/* Menus */
menuText: new CustomProp('theme-menu-text', undefined, colors.slate),
menuLightText: new CustomProp('theme-menu-light-text', undefined, colors.slate),
menuBg: new CustomProp('theme-menu-bg', undefined, 'white'),
menuSubheaderFg: new CustomProp('theme-menu-subheader-fg', undefined, 'unset'),
menuBorder: new CustomProp('theme-menu-border', undefined, colors.mediumGreyOpaque),
menuShadow: new CustomProp('theme-menu-shadow', undefined, 'rgba(38, 38, 51, 0.6)'),
/* Menu Items */
menuItemFg: new CustomProp('theme-menu-item-fg', undefined, 'unset'),
menuItemSelectedFg: new CustomProp('theme-menu-item-selected-fg', undefined, colors.light),
menuItemSelectedBg: new CustomProp('theme-menu-item-selected-bg', undefined, vars.primaryBg),
menuItemDisabledFg: new CustomProp('theme-menu-item-disabled-fg', undefined, '#D9D9D9'),
menuItemIconFg: new CustomProp('theme-menu-item-icon-fg', undefined, colors.slate),
menuItemIconSelectedFg: new CustomProp('theme-menu-item-icon-selected-fg', undefined, 'white'),
menuItemLinkFg: new CustomProp('theme-menu-item-link-fg', undefined, colors.lightGreen),
menuItemLinkSelectedFg: new CustomProp('theme-menu-item-link-selected-fg', undefined,
colors.darkGreen),
menuItemLinkselectedBg: new CustomProp('theme-menu-item-link-selected-bg', undefined,
colors.mediumGreyOpaque),
/* Autocomplete */
autocompleteMatchText: new CustomProp('theme-autocomplete-match-text', undefined,
colors.lightGreen),
autocompleteSelectedMatchText: new CustomProp('theme-autocomplete-selected-match-text',
undefined, colors.lighterGreen),
autocompleteChoiceSelectedBg: new CustomProp('theme-autocomplete-item-selected-bg', undefined,
colors.mediumGreyOpaque),
/* Search */
searchBorder: new CustomProp('theme-search-border', undefined, 'grey'),
searchPrevNextButtonFg: new CustomProp('theme-search-prev-next-button-fg', undefined,
colors.slate),
searchPrevNextButtonBg: new CustomProp('theme-search-prev-next-button-bg', undefined,
colors.mediumGrey),
/* Loaders */
loaderFg: new CustomProp('theme-loader-fg', undefined, colors.lightGreen),
loaderBg: new CustomProp('theme-loader-bg', undefined, colors.darkGrey),
/* Site Switcher */
siteSwitcherActiveFg: new CustomProp('theme-site-switcher-active-fg', undefined, colors.light),
siteSwitcherActiveBg: new CustomProp('theme-site-switcher-active-bg', undefined, colors.dark),
/* Doc Menu */
docMenuDocOptionsFg: new CustomProp('theme-doc-menu-doc-options-fg', undefined, colors.darkGrey),
docMenuDocOptionsHoverFg: new CustomProp('theme-doc-menu-doc-options-hover-fg', undefined,
colors.slate),
docMenuDocOptionsHoverBg: new CustomProp('theme-doc-menu-doc-options-hover-bg', undefined,
colors.darkGrey),
/* Shortcut Keys */
shortcutKeyFg: new CustomProp('theme-shortcut-key-fg', undefined, 'black'),
shortcutKeyPrimaryFg: new CustomProp('theme-shortcut-key-primary-fg', undefined,
colors.darkGreen),
shortcutKeySecondaryFg: new CustomProp('theme-shortcut-key-secondary-fg', undefined,
colors.slate),
shortcutKeyBg: new CustomProp('theme-shortcut-key-bg', undefined, 'white'),
shortcutKeyBorder: new CustomProp('theme-shortcut-key-border', undefined, colors.slate),
/* Breadcrumbs */
breadcrumbsTagFg: new CustomProp('theme-breadcrumbs-tag-fg', undefined, 'white'),
breadcrumbsTagBg: new CustomProp('theme-breadcrumbs-tag-bg', undefined, colors.slate),
breadcrumbsTagAlertBg: new CustomProp('theme-breadcrumbs-tag-alert-fg', undefined, colors.error),
/* Page Widget Picker */
widgetPickerPrimaryBg: new CustomProp('theme-widget-picker-primary-bg', undefined, 'white'),
widgetPickerSecondaryBg: new CustomProp('theme-widget-picker-secondary-bg', undefined,
colors.lightGrey),
widgetPickerItemFg: new CustomProp('theme-widget-picker-item-fg', undefined, colors.lightGrey),
widgetPickerItemSelectedBg: new CustomProp('theme-widget-picker-item-selected-bg', undefined,
colors.lightGrey),
widgetPickerItemDisabledBg: new CustomProp('theme-widget-picker-item-disabled-bg', undefined,
colors.lightGrey),
widgetPickerIcon: new CustomProp('theme-widget-picker-icon', undefined, colors.slate),
widgetPickerPrimaryIcon: new CustomProp('theme-widget-picker-primary-icon', undefined,
colors.lightGreen),
widgetPickerSummaryIcon: new CustomProp('theme-widget-picker-summary-icon', undefined,
colors.darkGreen),
widgetPickerBorder: new CustomProp('theme-widget-picker-border', undefined, colors.mediumGrey),
widgetPickerShadow: new CustomProp('theme-widget-picker-shadow', undefined,
'rgba(38,38,51,0.20)'),
/* Code View */
codeViewText: new CustomProp('theme-code-view-text', undefined, '#444'),
codeViewKeyword: new CustomProp('theme-code-view-keyword', undefined, '#444'),
codeViewComment: new CustomProp('theme-code-view-comment', undefined, '#888888'),
codeViewMeta: new CustomProp('theme-code-view-meta', undefined, '#1F7199'),
codeViewTitle: new CustomProp('theme-code-view-title', undefined, '#880000'),
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'),
/* Importer */
importerTableInfoBorder: new CustomProp('theme-importer-table-info-border', undefined, colors.darkGrey),
importerPreviewBorder: new CustomProp('theme-importer-preview-border', undefined,
colors.darkGrey),
importerSkippedTableOverlay: new CustomProp('theme-importer-skipped-table-overlay', undefined,
colors.mediumGrey),
importerMatchIcon: new CustomProp('theme-importer-match-icon', undefined, colors.darkGrey),
/* Menu Toggles */
menuToggleFg: new CustomProp('theme-menu-toggle-fg', undefined, colors.slate),
menuToggleHoverFg: new CustomProp('theme-menu-toggle-hover-fg', undefined, colors.darkGreen),
menuToggleActiveFg: new CustomProp('theme-menu-toggle-active-fg', undefined, colors.darkerGreen),
menuToggleBg: new CustomProp('theme-menu-toggle-bg', undefined, 'white'),
menuToggleBorder: new CustomProp('theme-menu-toggle-border', undefined, colors.slate),
/* Button Groups */
buttonGroupFg: new CustomProp('theme-button-group-fg', undefined, colors.dark),
buttonGroupLightFg: new CustomProp('theme-button-group-light-fg', undefined, colors.slate),
buttonGroupBg: new CustomProp('theme-button-group-bg', undefined, 'unset'),
buttonGroupIcon: new CustomProp('theme-button-group-icon', undefined, colors.slate),
buttonGroupBorder: new CustomProp('theme-button-group-border', undefined, colors.darkGrey),
buttonGroupBorderHover: new CustomProp('theme-button-group-border-hover', undefined,
colors.hover),
buttonGroupSelectedFg: new CustomProp('theme-button-group-selected-fg', undefined, colors.light),
buttonGroupLightSelectedFg: new CustomProp('theme-button-group-light-selected-fg', undefined,
colors.lightGreen),
buttonGroupSelectedBg: new CustomProp('theme-button-group-selected-bg', undefined, colors.dark),
buttonGroupSelectedBorder: new CustomProp('theme-button-group-selected-border', undefined,
colors.dark),
/* Access Rules */
accessRulesTableHeaderFg: new CustomProp('theme-access-rules-table-header-fg', undefined,
colors.dark),
accessRulesTableHeaderBg: new CustomProp('theme-access-rules-table-header-bg', undefined,
colors.mediumGrey),
accessRulesTableBodyFg: new CustomProp('theme-access-rules-table-body-fg', undefined,
colors.dark),
accessRulesTableBorder: new CustomProp('theme-access-rules-table-border', undefined,
colors.slate),
/* Cells */
cellFg: new CustomProp('theme-cell-fg', undefined, 'unset'),
cellBg: new CustomProp('theme-cell-bg', undefined, '#FFFFFF00'),
cellZebraBg: new CustomProp('theme-cell-zebra-bg', undefined, '#F8F8F8'),
/* Formula Editor */
formulaEditorBg: new CustomProp('theme-formula-editor-bg', undefined, 'white'),
/* Charts */
chartFg: new CustomProp('theme-chart-fg', undefined, '#444'),
chartBg: new CustomProp('theme-chart-bg', undefined, '#fff'),
chartLegendBg: new CustomProp('theme-chart-legend-bg', undefined, '#FFFFFF80'),
chartXAxis: new CustomProp('theme-chart-x-axis', undefined, '#444'),
chartYAxis: new CustomProp('theme-chart-y-axis', undefined, '#444'),
};
const cssColors = values(colors).map(v => v.decl()).join('\n'); const cssColors = values(colors).map(v => v.decl()).join('\n');
const cssVars = values(vars).map(v => v.decl()).join('\n'); const cssVars = values(vars).map(v => v.decl()).join('\n');
const cssFontParams = ` const cssFontParams = `
@ -237,17 +759,107 @@ export function isScreenResizing(): Observable<boolean> {
return _isScreenResizingObs; return _isScreenResizingObs;
} }
let _prefersDarkModeObs: Observable<boolean>|undefined;
/** /**
* Attaches the global css properties to the document's root to them available in the page. * Returns a singleton observable for whether the user agent prefers dark mode.
*/
export function prefersDarkModeObs(): Observable<boolean> {
if (!_prefersDarkModeObs) {
const query = window.matchMedia('(prefers-color-scheme: dark)');
const obs = Observable.create<boolean>(null, query.matches);
query.addEventListener('change', event => obs.set(event.matches));
_prefersDarkModeObs = obs;
}
return _prefersDarkModeObs;
}
/**
* Attaches the global css properties to the document's root to make them available in the page.
*/ */
export function attachCssRootVars(productFlavor: ProductFlavor, varsOnly: boolean = false) { export function attachCssRootVars(productFlavor: ProductFlavor, varsOnly: boolean = false) {
dom.update(document.documentElement, varsOnly ? dom.cls(cssVarsOnly.className) : dom.cls(cssRootVars)); dom.update(document.documentElement, varsOnly ? dom.cls(cssVarsOnly.className) : dom.cls(cssRootVars));
document.documentElement.classList.add(cssRoot.className); document.documentElement.classList.add(cssRoot.className);
document.body.classList.add(cssBody.className); document.body.classList.add(cssBody.className);
const theme = getTheme(productFlavor); const customTheme = getTheme(productFlavor);
if (theme.bodyClassName) { if (customTheme.bodyClassName) {
document.body.classList.add(theme.bodyClassName); document.body.classList.add(customTheme.bodyClassName);
} }
const interfaceStyle = urlState().state.get().params?.style || 'full'; const interfaceStyle = urlState().state.get().params?.style || 'full';
document.body.classList.add(`interface-${interfaceStyle}`); document.body.classList.add(`interface-${interfaceStyle}`);
} }
/**
* Attaches theme-related css properties to the theme style element.
*/
export function attachCssThemeVars({appearance, colors: themeColors}: Theme) {
// Prepare the custom properties needed for applying the theme.
const properties = Object.entries(themeColors)
.map(([name, value]) => `--grist-theme-${name}: ${value};`);
// Include properties for styling the scrollbar.
properties.push(...getCssScrollbarProperties(appearance));
// Include properties for picking an appropriate background image.
properties.push(...getCssThemeBackgroundProperties(appearance));
// Apply the properties to the theme style element.
getOrCreateStyleElement('grist-theme').textContent = `:root {
${properties.join('\n')}
}`;
// Make the browser aware of the color scheme.
document.documentElement.style.setProperty(`color-scheme`, appearance);
// Cache the appearance in local storage; this is currently used to apply a suitable
// background image that's shown while the application is loading.
localStorage.setItem('appearance', appearance);
}
/**
* Gets scrollbar-related css properties that are appropriate for the given `appearance`.
*
* Note: Browser support for customizing scrollbars is still a mixed bag; the bulk of customization
* is non-standard and unsupported by Firefox. If support matures, we could expose some of these in
* custom themes, but for now we'll just go with reasonable presets.
*/
function getCssScrollbarProperties(appearance: ThemeAppearance) {
return [
'--scroll-bar-fg: ' +
(appearance === 'dark' ? '#6B6B6B;' : '#A8A8A8;'),
'--scroll-bar-hover-fg: ' +
(appearance === 'dark' ? '#7B7B7B;' : '#8F8F8F;'),
'--scroll-bar-active-fg: ' +
(appearance === 'dark' ? '#8B8B8B;' : '#7C7C7C;'),
'--scroll-bar-bg: ' +
(appearance === 'dark' ? '#2B2B2B;' : '#F0F0F0;'),
];
}
/**
* Gets background-related css properties that are appropriate for the given `appearance`.
*
* Currently, this sets a property for showing a background image that's visible while a page
* is loading.
*/
function getCssThemeBackgroundProperties(appearance: ThemeAppearance) {
const value = appearance === 'dark'
? 'url("img/prismpattern.png")'
: 'url("img/gplaypattern.png")';
return [`--grist-theme-bg: ${value};`];
}
/**
* Gets or creates a style element in the head of the document with the given `id`.
*
* Useful for grouping CSS values such as theme custom properties without needing to
* pollute the document with in-line styles.
*/
function getOrCreateStyleElement(id: string) {
let style = document.head.querySelector(id);
if (style) { return style; }
style = document.createElement('style');
style.setAttribute('id', id);
document.head.append(style);
return style;
}

@ -1,4 +1,4 @@
import {testId} from 'app/client/ui2018/cssVars'; import {testId, theme} from 'app/client/ui2018/cssVars';
import {icon} from 'app/client/ui2018/icons'; import {icon} from 'app/client/ui2018/icons';
import {styled} from 'grainjs'; import {styled} from 'grainjs';
@ -6,6 +6,7 @@ import {styled} from 'grainjs';
// Drag icon for use in koForm draggableList. // Drag icon for use in koForm draggableList.
export const cssDragger = styled((...args: any[]) => icon('DragDrop', testId('dragger'), ...args), ` export const cssDragger = styled((...args: any[]) => icon('DragDrop', testId('dragger'), ...args), `
--icon-color: ${theme.controlSecondaryFg};
visibility: hidden; visibility: hidden;
align-self: center; align-self: center;
flex-shrink: 0; flex-shrink: 0;

@ -9,7 +9,7 @@
* *
* TODO: Consider merging this into grainjs's input widget. * TODO: Consider merging this into grainjs's input widget.
*/ */
import { colors } from 'app/client/ui2018/cssVars'; import { theme } from 'app/client/ui2018/cssVars';
import { dom, DomArg, styled } from 'grainjs'; import { dom, DomArg, styled } from 'grainjs';
import { Observable } from 'grainjs'; import { Observable } from 'grainjs';
import noop = require('lodash/noop'); import noop = require('lodash/noop');
@ -45,7 +45,7 @@ export const cssLabelText = styled(rawTextInput, `
export const cssTextInput = styled('input', ` export const cssTextInput = styled('input', `
outline: none; outline: none;
height: 28px; height: 28px;
border: 1px solid ${colors.darkGrey}; border: 1px solid ${theme.inputBorder};
border-radius: 3px; border-radius: 3px;
padding: 0 6px; padding: 0 6px;
`); `);
@ -97,7 +97,7 @@ export function editableLabel(label: Observable<string>, options: EditableLabelO
/** /**
* Provides a text input element that pretty much behaves like the editableLabel only it shows as a * Provides a text input element that pretty much behaves like the editableLabel only it shows as a
* regular input within a rigid static frame. It takes in an observable that is setf on Enter or loss * regular input within a rigid static frame. It takes in an observable that is set on Enter or loss
* of focus. Escape cancels editing. Validation logic (if any) should happen in the save function, * of focus. Escape cancels editing. Validation logic (if any) should happen in the save function,
* to reject a value simply throw an error, this will revert to the the saved one. * to reject a value simply throw an error, this will revert to the the saved one.
*/ */

@ -49,7 +49,7 @@
* `); * `);
*/ */
import { colors } from 'app/client/ui2018/cssVars'; import { theme } from 'app/client/ui2018/cssVars';
import { dom, DomElementArg, styled } from 'grainjs'; import { dom, DomElementArg, styled } from 'grainjs';
import { IconName } from './IconList'; import { IconName } from './IconList';
@ -65,7 +65,7 @@ const iconDiv = styled('div', `
-webkit-mask-size: contain; -webkit-mask-size: contain;
width: 16px; width: 16px;
height: 16px; height: 16px;
background-color: var(--icon-color, black); background-color: var(--icon-color, var(--grist-theme-text, black));
`); `);
export const cssIconBackground = styled(iconDiv, ` export const cssIconBackground = styled(iconDiv, `
@ -85,7 +85,7 @@ export function icon(name: IconName, ...domArgs: DomElementArg[]): HTMLElement {
} }
/** /**
* Container box for an slate-colored icon to serve as a button, with a grey background on hover. * Container box for an icon to serve as a button..
*/ */
export const cssIconButton = styled('div', ` export const cssIconButton = styled('div', `
flex: none; flex: none;
@ -95,9 +95,9 @@ export const cssIconButton = styled('div', `
border-radius: 3px; border-radius: 3px;
line-height: 0px; line-height: 0px;
cursor: default; cursor: default;
--icon-color: ${colors.slate}; --icon-color: ${theme.controlSecondaryFg};
&:hover, &.weasel-popup-open { &:hover, &.weasel-popup-open {
background-color: ${colors.darkGrey}; background-color: ${theme.controlSecondaryHoverBg};
--icon-color: ${colors.slate}; --icon-color: ${theme.controlSecondaryFg};
} }
`); `);

@ -1,19 +1,19 @@
import { sameDocumentUrlState, urlState } from 'app/client/models/gristUrlState'; import { sameDocumentUrlState, urlState } from 'app/client/models/gristUrlState';
import { colors } from 'app/client/ui2018/cssVars'; import { theme } from 'app/client/ui2018/cssVars';
import { CellValue } from 'app/plugin/GristData'; import { CellValue } from 'app/plugin/GristData';
import { dom, IDomArgs, Observable, styled } from 'grainjs'; import { dom, IDomArgs, Observable, styled } from 'grainjs';
/** /**
* Styling for a simple green <A HREF> link. * Styling for a simple <A HREF> link.
*/ */
export const cssLink = styled('a', ` export const cssLink = styled('a', `
color: ${colors.lightGreen}; color: ${theme.link};
--icon-color: ${colors.lightGreen}; --icon-color: ${theme.link};
text-decoration: none; text-decoration: none;
&:hover, &:focus { &:hover, &:focus {
color: ${colors.lightGreen}; color: ${theme.linkHover};
--icon-color: ${colors.lightGreen}; --icon-color: ${theme.linkHover};
text-decoration: underline; text-decoration: underline;
} }
`); `);

@ -1,4 +1,4 @@
import {colors} from 'app/client/ui2018/cssVars'; import {theme} from 'app/client/ui2018/cssVars';
import {DomArg, keyframes, styled} from 'grainjs'; import {DomArg, keyframes, styled} from 'grainjs';
const rotate360 = keyframes(` const rotate360 = keyframes(`
@ -9,10 +9,10 @@ const rotate360 = keyframes(`
const flash = keyframes(` const flash = keyframes(`
0% { 0% {
background-color: ${colors.lightGreen}; background-color: ${theme.loaderFg};
} }
50%, 100% { 50%, 100% {
background-color: ${colors.darkGrey}; background-color: ${theme.loaderBg};
} }
`); `);
@ -25,8 +25,8 @@ export const loadingSpinner = styled('div', `
width: 32px; width: 32px;
height: 32px; height: 32px;
border-radius: 32px; border-radius: 32px;
border: 4px solid ${colors.darkGrey}; border: 4px solid ${theme.loaderBg};
border-top-color: ${colors.lightGreen}; border-top-color: ${theme.loaderFg};
animation: ${rotate360} 1s ease-out infinite; animation: ${rotate360} 1s ease-out infinite;
`); `);
@ -52,8 +52,8 @@ const cssLoadingDot = styled('div', `
border-radius: 50%; border-radius: 50%;
width: var(--dot-size); width: var(--dot-size);
height: var(--dot-size); height: var(--dot-size);
background-color: ${colors.lightGreen}; background-color: ${theme.loaderFg};
color: ${colors.lightGreen}; color: ${theme.loaderFg};
animation: ${flash} 1s alternate infinite; animation: ${flash} 1s alternate infinite;
&-left { &-left {

@ -2,7 +2,7 @@ import { Command } from 'app/client/components/commands';
import { NeedUpgradeError, reportError } from 'app/client/models/errors'; import { NeedUpgradeError, reportError } from 'app/client/models/errors';
import { textButton } from 'app/client/ui2018/buttons'; import { textButton } from 'app/client/ui2018/buttons';
import { cssCheckboxSquare, cssLabel, cssLabelText } from 'app/client/ui2018/checkbox'; import { cssCheckboxSquare, cssLabel, cssLabelText } from 'app/client/ui2018/checkbox';
import { colors, testId, vars } from 'app/client/ui2018/cssVars'; import { testId, theme, vars } from 'app/client/ui2018/cssVars';
import { IconName } from 'app/client/ui2018/IconList'; import { IconName } from 'app/client/ui2018/IconList';
import { icon } from 'app/client/ui2018/icons'; import { icon } from 'app/client/ui2018/icons';
import { cssSelectBtn } from 'app/client/ui2018/select'; import { cssSelectBtn } from 'app/client/ui2018/select';
@ -60,11 +60,12 @@ export const cssMenuElem = styled('div', `
line-height: initial; line-height: initial;
max-width: 400px; max-width: 400px;
padding: 8px 0px 16px 0px; padding: 8px 0px 16px 0px;
box-shadow: 0 2px 20px 0 rgba(38,38,51,0.6); box-shadow: 0 2px 20px 0 ${theme.menuShadow};
min-width: 160px; min-width: 160px;
z-index: 999; z-index: 999;
--weaseljs-selected-background-color: ${vars.primaryBg}; --weaseljs-selected-background-color: ${theme.menuItemSelectedBg};
--weaseljs-menu-item-padding: 8px 24px; --weaseljs-menu-item-padding: 8px 24px;
background-color: ${theme.menuBg};
@media print { @media print {
& { & {
@ -76,13 +77,16 @@ export const cssMenuElem = styled('div', `
const menuItemStyle = ` const menuItemStyle = `
justify-content: flex-start; justify-content: flex-start;
align-items: center; align-items: center;
--icon-color: ${colors.lightGreen}; color: ${theme.menuItemFg};
--icon-color: ${theme.accentIcon};
.${weasel.cssMenuItem.className}-sel { .${weasel.cssMenuItem.className}-sel {
--icon-color: ${colors.light}; color: ${theme.menuItemSelectedFg};
--icon-color: ${theme.menuItemSelectedFg};
} }
&.disabled { &.disabled {
cursor: default; cursor: default;
opacity: 0.2; color: ${theme.menuItemDisabledFg};
--icon-color: ${theme.menuItemDisabledFg};
} }
`; `;
@ -237,7 +241,9 @@ export function multiSelect<T>(selectedOptions: MutableObsArray<T>,
weasel.setPopupToCreateDom(elem, ctl => buildMultiSelectMenu(ctl), weasel.defaultMenuOptions); weasel.setPopupToCreateDom(elem, ctl => buildMultiSelectMenu(ctl), weasel.defaultMenuOptions);
}, },
dom.style('border', use => { dom.style('border', use => {
return options.error && use(options.error) ? '1px solid red' : `1px solid ${colors.darkGrey}`; return options.error && use(options.error)
? `1px solid ${theme.selectButtonBorderInvalid}`
: `1px solid ${theme.selectButtonBorder}`;
}), }),
...domArgs ...domArgs
); );
@ -394,6 +400,7 @@ export function selectOption(
} }
export const menuSubHeader = styled('div', ` export const menuSubHeader = styled('div', `
color: ${theme.menuSubheaderFg};
font-size: ${vars.xsmallFontSize}; font-size: ${vars.xsmallFontSize};
text-transform: uppercase; text-transform: uppercase;
font-weight: ${vars.bigControlTextWeight}; font-weight: ${vars.bigControlTextWeight};
@ -405,7 +412,7 @@ export const menuText = styled('div', `
display: flex; display: flex;
align-items: center; align-items: center;
font-size: ${vars.smallFontSize}; font-size: ${vars.smallFontSize};
color: ${colors.slate}; color: ${theme.menuText};
padding: 8px 24px 4px 24px; padding: 8px 24px 4px 24px;
max-width: 250px; max-width: 250px;
cursor: default; cursor: default;
@ -440,6 +447,7 @@ export function menuAnnotate(text: string, ...args: DomElementArg[]) {
} }
export const menuDivider = styled(weasel.cssMenuDivider, ` export const menuDivider = styled(weasel.cssMenuDivider, `
background-color: ${theme.menuBorder};
margin: 8px 0; margin: 8px 0;
`); `);
@ -464,8 +472,8 @@ const cssSelectBtnLink = styled('div', `
display: flex; display: flex;
align-items: center; align-items: center;
font-size: ${vars.mediumFontSize}; font-size: ${vars.mediumFontSize};
color: ${colors.lightGreen}; color: ${theme.controlFg};
--icon-color: ${colors.lightGreen}; --icon-color: ${theme.controlFg};
width: initial; width: initial;
height: initial; height: initial;
line-height: inherit; line-height: inherit;
@ -480,8 +488,8 @@ const cssSelectBtnLink = styled('div', `
-moz-appearance: none; -moz-appearance: none;
&:hover, &:focus, &:active { &:hover, &:focus, &:active {
color: ${colors.darkGreen}; color: ${theme.controlHoverFg};
--icon-color: ${colors.darkGreen}; --icon-color: ${theme.controlHoverFg};
box-shadow: initial; box-shadow: initial;
} }
`); `);
@ -489,7 +497,7 @@ const cssSelectBtnLink = styled('div', `
const cssOptionIcon = styled(icon, ` const cssOptionIcon = styled(icon, `
height: 16px; height: 16px;
width: 16px; width: 16px;
background-color: ${colors.slate}; background-color: ${theme.menuItemIconFg};
margin: -3px 8px 0 2px; margin: -3px 8px 0 2px;
`); `);
@ -504,7 +512,11 @@ export const cssOptionRowIcon = styled(cssOptionIcon, `
flex: none; flex: none;
.${weasel.cssMenuItem.className}-sel & { .${weasel.cssMenuItem.className}-sel & {
background-color: white; background-color: ${theme.menuItemSelectedFg};
}
.${weasel.cssMenuItem.className}.disabled & {
background-color: ${theme.menuItemDisabledFg};
} }
`); `);
@ -512,6 +524,19 @@ const cssOptionLabel = styled('div', `
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
.${weasel.cssMenuItem.className} & {
color: ${theme.menuItemFg};
}
.${weasel.cssMenuItem.className}-sel & {
color: ${theme.menuItemSelectedFg};
background-color: ${theme.menuItemSelectedBg};
}
.${weasel.cssMenuItem.className}.disabled & {
color: ${theme.menuItemDisabledFg};
}
`); `);
const cssInlineCollapseIcon = styled(icon, ` const cssInlineCollapseIcon = styled(icon, `
@ -524,7 +549,7 @@ const cssCollapseIcon = styled(icon, `
right: 12px; right: 12px;
top: calc(50% - 8px); top: calc(50% - 8px);
pointer-events: none; pointer-events: none;
background-color: ${colors.dark}; background-color: ${theme.selectButtonFg};
`); `);
const cssInputButtonMenuElem = styled(cssMenuElem, ` const cssInputButtonMenuElem = styled(cssMenuElem, `
@ -537,16 +562,20 @@ const cssMenuItemCmd = styled('div', `
const cssCmdKey = styled('span', ` const cssCmdKey = styled('span', `
margin-left: 16px; margin-left: 16px;
color: ${colors.slate}; color: ${theme.menuItemIconFg};
margin-right: -12px; margin-right: -12px;
.${weasel.cssMenuItem.className}-sel > & { .${weasel.cssMenuItem.className}-sel > & {
color: ${colors.lightGrey}; color: ${theme.menuItemIconSelectedFg};
}
.${weasel.cssMenuItem.className}.disabled > & {
color: ${theme.menuItemDisabledFg};
} }
`); `);
const cssAnnotateMenuItem = styled('span', ` const cssAnnotateMenuItem = styled('span', `
color: ${colors.lightGreen}; color: ${theme.accentText};
text-transform: uppercase; text-transform: uppercase;
font-size: 8px; font-size: 8px;
vertical-align: super; vertical-align: super;
@ -555,7 +584,7 @@ const cssAnnotateMenuItem = styled('span', `
font-weight: bold; font-weight: bold;
.${weasel.cssMenuItem.className}-sel > & { .${weasel.cssMenuItem.className}-sel > & {
color: white; color: ${theme.menuItemIconSelectedFg};
} }
`); `);
@ -563,9 +592,10 @@ const cssMultiSelectSummary = styled('div', `
flex: 1 1 0px; flex: 1 1 0px;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
color: ${theme.selectButtonFg};
&-placeholder { &-placeholder {
color: ${colors.slate} color: ${theme.selectButtonPlaceholderFg};
} }
`); `);
@ -575,6 +605,7 @@ const cssMultiSelectMenu = styled(weasel.cssMenu, `
max-height: calc(max(300px, 95vh - 300px)); max-height: calc(max(300px, 95vh - 300px));
max-width: 400px; max-width: 400px;
padding-bottom: 0px; padding-bottom: 0px;
background-color: ${theme.menuBg};
`); `);
const cssCheckboxLabel = styled(cssLabel, ` const cssCheckboxLabel = styled(cssLabel, `
@ -583,7 +614,7 @@ const cssCheckboxLabel = styled(cssLabel, `
const cssCheckboxText = styled(cssLabelText, ` const cssCheckboxText = styled(cssLabelText, `
margin-right: 12px; margin-right: 12px;
color: ${colors.dark}; color: ${theme.text};
white-space: pre; white-space: pre;
`); `);

@ -3,7 +3,7 @@ import {reportError} from 'app/client/models/errors';
import {cssInput} from 'app/client/ui/cssInput'; import {cssInput} from 'app/client/ui/cssInput';
import {prepareForTransition, TransitionWatcher} from 'app/client/ui/transitions'; import {prepareForTransition, TransitionWatcher} from 'app/client/ui/transitions';
import {bigBasicButton, bigPrimaryButton, cssButton} from 'app/client/ui2018/buttons'; import {bigBasicButton, bigPrimaryButton, cssButton} from 'app/client/ui2018/buttons';
import {colors, mediaSmall, testId, vars} from 'app/client/ui2018/cssVars'; import {mediaSmall, testId, theme, vars} from 'app/client/ui2018/cssVars';
import {loadingSpinner} from 'app/client/ui2018/loaders'; import {loadingSpinner} from 'app/client/ui2018/loaders';
import {waitGrainObs} from 'app/common/gutil'; import {waitGrainObs} from 'app/common/gutil';
import {Computed, Disposable, dom, DomContents, DomElementArg, input, keyframes, import {Computed, Disposable, dom, DomContents, DomElementArg, input, keyframes,
@ -474,12 +474,12 @@ export function cssModalWidth(style: ModalWidth) {
// the flex container, to ensure the full item can be scrolled in case of overflow. // the flex container, to ensure the full item can be scrolled in case of overflow.
// See https://stackoverflow.com/a/33455342/328565 // See https://stackoverflow.com/a/33455342/328565
const cssModalDialog = styled('div', ` const cssModalDialog = styled('div', `
background-color: white; background-color: ${theme.modalBg};
min-width: 428px; min-width: 428px;
color: black; color: ${theme.darkText};
margin: auto; margin: auto;
border-radius: 3px; border-radius: 3px;
box-shadow: 0 2px 18px 0 rgba(31,37,50,0.31), 0 0 1px 0 rgba(76,86,103,0.24); box-shadow: 0 2px 18px 0 ${theme.modalInnerShadow}, 0 0 1px 0 ${theme.modalOuterShadow};
padding: 40px 64px; padding: 40px 64px;
outline: none; outline: none;
@ -506,13 +506,14 @@ const cssModalDialog = styled('div', `
export const cssModalTitle = styled('div', ` export const cssModalTitle = styled('div', `
font-size: ${vars.xxxlargeFontSize}; font-size: ${vars.xxxlargeFontSize};
font-weight: ${vars.headerControlTextWeight}; font-weight: ${vars.headerControlTextWeight};
color: ${colors.dark}; color: ${theme.text};
margin: 0 0 16px 0; margin: 0 0 16px 0;
line-height: 32px; line-height: 32px;
overflow-wrap: break-word; overflow-wrap: break-word;
`); `);
export const cssModalBody = styled('div', ` export const cssModalBody = styled('div', `
color: ${theme.text};
margin: 16px 0; margin: 16px 0;
`); `);
@ -530,7 +531,7 @@ const cssFadeIn = keyframes(`
`); `);
const cssFadeOut = keyframes(` const cssFadeOut = keyframes(`
from {background-color: ${colors.backdrop}} from {background-color: ${theme.modalBackdrop}}
`); `);
const cssModalBacker = styled('div', ` const cssModalBacker = styled('div', `
@ -543,7 +544,7 @@ const cssModalBacker = styled('div', `
left: 0; left: 0;
padding: 16px; padding: 16px;
z-index: 999; z-index: 999;
background-color: ${colors.backdrop}; background-color: ${theme.modalBackdrop};
overflow-y: auto; overflow-y: auto;
animation-name: ${cssFadeIn}; animation-name: ${cssFadeIn};
animation-duration: 0.4s; animation-duration: 0.4s;

@ -1,7 +1,7 @@
import { isDesktop } from 'app/client/lib/browserInfo'; import { isDesktop } from 'app/client/lib/browserInfo';
import { cssEditorInput } from "app/client/ui/HomeLeftPane"; import { cssEditorInput } from "app/client/ui/HomeLeftPane";
import { itemHeader, itemHeaderWrapper, treeViewContainer } from "app/client/ui/TreeViewComponentCss"; import { itemHeader, itemHeaderWrapper, treeViewContainer } from "app/client/ui/TreeViewComponentCss";
import { colors } from "app/client/ui2018/cssVars"; import { theme } from "app/client/ui2018/cssVars";
import { icon } from "app/client/ui2018/icons"; import { icon } from "app/client/ui2018/icons";
import { menu, menuItem, menuText } from "app/client/ui2018/menus"; import { menu, menuItem, menuText } from "app/client/ui2018/menus";
import { dom, domComputed, DomElementArg, makeTestId, observable, Observable, styled } from "grainjs"; import { dom, domComputed, DomElementArg, makeTestId, observable, Observable, styled } from "grainjs";
@ -86,7 +86,7 @@ export function buildPageDom(name: Observable<string>, actions: PageActions, ...
dom.on('click', (ev) => isTargetSelected(ev.target as HTMLElement) && isRenaming.set(true)), dom.on('click', (ev) => isTargetSelected(ev.target as HTMLElement) && isRenaming.set(true)),
), ),
cssPageMenuTrigger( cssPageMenuTrigger(
cssPageIcon('Dots'), cssPageMenuIcon('Dots'),
menu(pageMenu, {placement: 'bottom-start', parentSelectorToMark: '.' + itemHeader.className}), menu(pageMenu, {placement: 'bottom-start', parentSelectorToMark: '.' + itemHeader.className}),
dom.on('click', (ev) => { ev.stopPropagation(); ev.preventDefault(); }), dom.on('click', (ev) => { ev.stopPropagation(); ev.preventDefault(); }),
@ -104,7 +104,6 @@ export function buildPageDom(name: Observable<string>, actions: PageActions, ...
} }
const cssPageItem = styled('a', ` const cssPageItem = styled('a', `
--icon-color: ${colors.slate};
display: flex; display: flex;
flex-direction: row; flex-direction: row;
height: 28px; height: 28px;
@ -122,9 +121,9 @@ const cssPageItem = styled('a', `
const cssPageInitial = styled('div', ` const cssPageInitial = styled('div', `
flex-shrink: 0; flex-shrink: 0;
color: white; color: ${theme.pageInitialsFg};
border-radius: 3px; border-radius: 3px;
background-color: ${colors.slate}; background-color: ${theme.pageInitialsBg};
width: 16px; width: 16px;
height: 16px; height: 16px;
text-align: center; text-align: center;
@ -187,20 +186,21 @@ const cssPageMenuTrigger = styled('div', `
} }
} }
.${itemHeaderWrapper.className}-not-dragging &:hover, &.weasel-popup-open { .${itemHeaderWrapper.className}-not-dragging &:hover, &.weasel-popup-open {
background-color: ${colors.darkGrey}; background-color: ${theme.pageOptionsHoverBg};
} }
.${itemHeaderWrapper.className}-not-dragging > .${itemHeader.className}.selected &:hover, .${itemHeaderWrapper.className}-not-dragging > .${itemHeader.className}.selected &:hover,
.${itemHeaderWrapper.className}-not-dragging > .${itemHeader.className}.selected &.weasel-popup-open { .${itemHeaderWrapper.className}-not-dragging > .${itemHeader.className}.selected &.weasel-popup-open {
background-color: ${colors.slate}; background-color: ${theme.pageOptionsSelectedHoverBg};
} }
.${itemHeader.className}.weasel-popup-open, .${itemHeader.className}-renaming { .${itemHeader.className}.weasel-popup-open, .${itemHeader.className}-renaming {
background-color: ${colors.mediumGrey}; background-color: ${theme.pageHoverBg};
} }
`); `);
const cssPageIcon = styled(icon, ` const cssPageMenuIcon = styled(icon, `
background-color: ${theme.pageOptionsFg};
.${itemHeader.className}.selected & { .${itemHeader.className}.selected & {
background-color: white; background-color: ${theme.pageOptionsHoverFg};
} }
`); `);

@ -8,7 +8,7 @@ import { SearchModel } from 'app/client/models/SearchModel';
import { hoverTooltip, IHoverTipOptions } from 'app/client/ui/tooltips'; import { hoverTooltip, IHoverTipOptions } from 'app/client/ui/tooltips';
import { cssHoverCircle, cssTopBarBtn } from 'app/client/ui/TopBarCss'; import { cssHoverCircle, cssTopBarBtn } from 'app/client/ui/TopBarCss';
import { labeledSquareCheckbox } from 'app/client/ui2018/checkbox'; import { labeledSquareCheckbox } from 'app/client/ui2018/checkbox';
import { colors, mediaSmall, vars } from 'app/client/ui2018/cssVars'; import { mediaSmall, theme, vars } from 'app/client/ui2018/cssVars';
import { icon } from 'app/client/ui2018/icons'; import { icon } from 'app/client/ui2018/icons';
import { dom, input, styled } from 'grainjs'; import { dom, input, styled } from 'grainjs';
import { noTestId, TestId } from 'grainjs'; import { noTestId, TestId } from 'grainjs';
@ -32,7 +32,7 @@ const searchWrapper = styled('div', `
position: relative; position: relative;
&-expand { &-expand {
width: 100% !important; width: 100% !important;
border: 1px solid grey; border: 1px solid ${theme.searchBorder};
} }
@media ${mediaSmall} { @media ${mediaSmall} {
& { & {
@ -58,6 +58,8 @@ const expandedSearch = styled('div', `
`); `);
const searchInput = styled(input, ` const searchInput = styled(input, `
background-color: ${theme.topHeaderBg};
color: ${theme.inputFg};
outline: none; outline: none;
border: none; border: none;
margin: 0; margin: 0;
@ -70,6 +72,9 @@ const searchInput = styled(input, `
.${searchWrapper.className}-expand & { .${searchWrapper.className}-expand & {
width: 100%; width: 100%;
} }
&::placeholder {
color: ${theme.inputPlaceholderFg};
}
`); `);
const cssArrowBtn = styled('div', ` const cssArrowBtn = styled('div', `
@ -80,8 +85,8 @@ const cssArrowBtn = styled('div', `
visibility: hidden; visibility: hidden;
width: 24px; width: 24px;
height: 24px; height: 24px;
background-color: ${colors.mediumGrey}; background-color: ${theme.searchPrevNextButtonBg};
--icon-color: ${colors.slate}; --icon-color: ${theme.searchPrevNextButtonFg};
border-radius: 3px; border-radius: 3px;
text-align: center; text-align: center;
display: flex; display: flex;
@ -94,31 +99,31 @@ const cssArrowBtn = styled('div', `
const cssCloseBtn = styled(icon, ` const cssCloseBtn = styled(icon, `
cursor: pointer; cursor: pointer;
background-color: ${colors.lightGreen}; background-color: ${theme.controlFg};
margin-left: 4px; margin-left: 4px;
flex-shrink: 0; flex-shrink: 0;
`); `);
const cssLabel = styled('span', ` const cssLabel = styled('span', `
font-size: ${vars.smallFontSize}; font-size: ${vars.smallFontSize};
color: ${colors.slate}; color: ${theme.lightText};
white-space: nowrap; white-space: nowrap;
margin-right: 12px; margin-right: 12px;
`); `);
const cssOptions = styled('div', ` const cssOptions = styled('div', `
background: ${theme.topHeaderBg};
position: absolute; position: absolute;
right: 0; right: 0;
top: 46px; top: 48px;
z-index: 1; z-index: 1;
background: white;
padding: 2px 4px; padding: 2px 4px;
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
`); `);
const cssShortcut = styled('span', ` const cssShortcut = styled('span', `
color: ${colors.slate}; color: ${theme.lightText};
`); `);
const searchArrowBtnTooltipOptions: IHoverTipOptions = { const searchArrowBtnTooltipOptions: IHoverTipOptions = {

@ -1,4 +1,4 @@
import {colors, vars} from 'app/client/ui2018/cssVars'; import {theme, vars} from 'app/client/ui2018/cssVars';
import {styled} from 'grainjs'; import {styled} from 'grainjs';
// Import popweasel so that the styles we define here are included later in CSS, and take priority // Import popweasel so that the styles we define here are included later in CSS, and take priority
@ -19,12 +19,12 @@ export const cssSelectBtn = styled('div', `
width: 100%; width: 100%;
height: 30px; height: 30px;
line-height: 16px; line-height: 16px;
background-color: white; background-color: ${theme.selectButtonBg};
color: ${colors.dark}; color: ${theme.selectButtonFg};
--icon-color: ${colors.dark}; --icon-color: ${theme.selectButtonFg};
font-size: ${vars.mediumFontSize}; font-size: ${vars.mediumFontSize};
padding: 5px; padding: 5px;
border: 1px solid ${colors.darkGrey}; border: 1px solid ${theme.selectButtonBorder};
border-radius: 3px; border-radius: 3px;
cursor: pointer; cursor: pointer;
overflow: hidden; overflow: hidden;
@ -42,7 +42,8 @@ export const cssSelectBtn = styled('div', `
} }
&.disabled { &.disabled {
color: grey; --icon-color: ${theme.selectButtonDisabledFg};
color: ${theme.selectButtonDisabledFg};
cursor: pointer; cursor: pointer;
} }
`); `);

@ -3,7 +3,7 @@ import {GristDoc} from 'app/client/components/GristDoc';
import {ViewFieldRec} from 'app/client/models/entities/ViewFieldRec'; import {ViewFieldRec} from 'app/client/models/entities/ViewFieldRec';
import {textButton} from 'app/client/ui2018/buttons'; import {textButton} from 'app/client/ui2018/buttons';
import {ColorOption, colorSelect} from 'app/client/ui2018/ColorSelect'; import {ColorOption, colorSelect} from 'app/client/ui2018/ColorSelect';
import {colors, vars} from 'app/client/ui2018/cssVars'; import {theme, vars} from 'app/client/ui2018/cssVars';
import {ConditionalStyle} from 'app/client/widgets/ConditionalStyle'; import {ConditionalStyle} from 'app/client/widgets/ConditionalStyle';
import {Disposable, dom, DomContents, fromKo, MultiHolder, Observable, styled} from 'grainjs'; import {Disposable, dom, DomContents, fromKo, MultiHolder, Observable, styled} from 'grainjs';
@ -67,6 +67,7 @@ const cssLine = styled('div', `
`); `);
const cssLabel = styled('div', ` const cssLabel = styled('div', `
color: ${theme.text};
text-transform: uppercase; text-transform: uppercase;
font-size: ${vars.xsmallFontSize}; font-size: ${vars.xsmallFontSize};
`); `);
@ -83,6 +84,6 @@ const cssRow = styled('div', `
margin-top: 24px; margin-top: 24px;
} }
&-disabled { &-disabled {
color: ${colors.slate}; color: ${theme.disabledText};
} }
`); `);

@ -2,7 +2,7 @@ import {createGroup} from 'app/client/components/commands';
import {ACIndexImpl, ACItem, ACResults, buildHighlightedDom, normalizeText, HighlightFunc} from 'app/client/lib/ACIndex'; import {ACIndexImpl, ACItem, ACResults, buildHighlightedDom, normalizeText, HighlightFunc} from 'app/client/lib/ACIndex';
import {IAutocompleteOptions} from 'app/client/lib/autocomplete'; import {IAutocompleteOptions} from 'app/client/lib/autocomplete';
import {IToken, TokenField, tokenFieldStyles} from 'app/client/lib/TokenField'; import {IToken, TokenField, tokenFieldStyles} from 'app/client/lib/TokenField';
import {colors, testId} from 'app/client/ui2018/cssVars'; import {colors, testId, theme} from 'app/client/ui2018/cssVars';
import {menuCssClass} from 'app/client/ui2018/menus'; import {menuCssClass} from 'app/client/ui2018/menus';
import {createMobileButtons, getButtonMargins} from 'app/client/widgets/EditorButtons'; import {createMobileButtons, getButtonMargins} from 'app/client/widgets/EditorButtons';
import {EditorPlacement} from 'app/client/widgets/EditorPlacement'; import {EditorPlacement} from 'app/client/widgets/EditorPlacement';
@ -253,7 +253,7 @@ export class ChoiceListEditor extends NewBaseEditor {
} }
const cssCellEditor = styled('div', ` const cssCellEditor = styled('div', `
background-color: white; background-color: ${theme.cellEditorBg};
font-family: var(--grist-font-family-data); font-family: var(--grist-font-family-data);
font-size: var(--grist-medium-font-size); font-size: var(--grist-medium-font-size);
`); `);
@ -332,7 +332,7 @@ const cssInputSizer = styled('div', `
// Set z-index to be higher than the 1000 set for .cell_editor. // Set z-index to be higher than the 1000 set for .cell_editor.
export const cssChoiceList = styled('div', ` export const cssChoiceList = styled('div', `
z-index: 1001; z-index: 1001;
box-shadow: 0 0px 8px 0 rgba(38,38,51,0.6); box-shadow: 0 0px 8px 0 ${theme.menuShadow};
overflow-y: auto; overflow-y: auto;
padding: 8px 0 0 0; padding: 8px 0 0 0;
--weaseljs-menu-item-padding: 8px 16px; --weaseljs-menu-item-padding: 8px 16px;

@ -2,7 +2,7 @@ import {IToken, TokenField} from 'app/client/lib/TokenField';
import {cssBlockedCursor} from 'app/client/ui/RightPanelStyles'; import {cssBlockedCursor} from 'app/client/ui/RightPanelStyles';
import {basicButton, primaryButton} from 'app/client/ui2018/buttons'; import {basicButton, primaryButton} from 'app/client/ui2018/buttons';
import {colorButton, ColorOption} from 'app/client/ui2018/ColorSelect'; import {colorButton, ColorOption} from 'app/client/ui2018/ColorSelect';
import {colors, testId} from 'app/client/ui2018/cssVars'; import {colors, testId, theme} from 'app/client/ui2018/cssVars';
import {editableLabel} from 'app/client/ui2018/editableLabel'; import {editableLabel} from 'app/client/ui2018/editableLabel';
import {icon} from 'app/client/ui2018/icons'; import {icon} from 'app/client/ui2018/icons';
import {ChoiceOptionsByName, IChoiceOptions} from 'app/client/widgets/ChoiceTextBox'; import {ChoiceOptionsByName, IChoiceOptions} from 'app/client/widgets/ChoiceTextBox';
@ -422,17 +422,17 @@ const cssListBox = styled('div', `
line-height: 1.5; line-height: 1.5;
padding-left: 4px; padding-left: 4px;
padding-right: 4px; padding-right: 4px;
border: 1px solid ${colors.hover}; border: 1px solid ${theme.choiceEntryBorderHover};
border-radius: 4px; border-radius: 4px;
background-color: white; background-color: ${theme.choiceEntryBg};
`); `);
const cssListBoxInactive = styled(cssListBox, ` const cssListBoxInactive = styled(cssListBox, `
cursor: pointer; cursor: pointer;
border: 1px solid ${colors.darkGrey}; border: 1px solid ${theme.choiceEntryBorder};
&:hover:not(&-disabled) { &:hover:not(&-disabled) {
border: 1px solid ${colors.hover}; border: 1px solid ${theme.choiceEntryBorderHover};
} }
&-disabled { &-disabled {
opacity: 0.6; opacity: 0.6;
@ -445,7 +445,7 @@ const cssListRow = styled('div', `
margin-bottom: 4px; margin-bottom: 4px;
padding: 4px 8px; padding: 4px 8px;
color: ${colors.dark}; color: ${colors.dark};
background-color: ${colors.mediumGrey}; background-color: ${colors.mediumGreyOpaque};
border-radius: 3px; border-radius: 3px;
text-overflow: ellipsis; text-overflow: ellipsis;
`); `);
@ -510,6 +510,7 @@ const cssEditableLabel = styled('div', `
`); `);
const cssTokenInput = styled('input', ` const cssTokenInput = styled('input', `
background-color: ${theme.choiceEntryBg};
padding-top: 4px; padding-top: 4px;
padding-bottom: 4px; padding-bottom: 4px;
overflow: hidden; overflow: hidden;

@ -1,8 +1,8 @@
import {dom, DomContents, DomElementArg, styled} from "grainjs";
import {colors, vars} from "app/client/ui2018/cssVars";
import {Style} from 'app/client/models/Styles'; import {Style} from 'app/client/models/Styles';
import {colors, theme, vars} from 'app/client/ui2018/cssVars';
import {dom, DomContents, DomElementArg, styled} from 'grainjs';
export const DEFAULT_FILL_COLOR = colors.mediumGreyOpaque.value; export const DEFAULT_FILL_COLOR = colors.mediumGreyOpaque.value!;
export const DEFAULT_TEXT_COLOR = '#000000'; export const DEFAULT_TEXT_COLOR = '#000000';
export interface IChoiceTokenOptions extends Style { export interface IChoiceTokenOptions extends Style {
@ -70,8 +70,7 @@ export const cssChoiceACItem = styled('li', `
cursor: pointer; cursor: pointer;
&.selected { &.selected {
background-color: ${colors.mediumGreyOpaque}; background-color: ${theme.autocompleteChoiceSelectedBg};
color: ${colors.dark};
} }
&-with-new { &-with-new {
scroll-margin-bottom: ${ADD_NEW_HEIGHT}; scroll-margin-bottom: ${ADD_NEW_HEIGHT};
@ -79,15 +78,11 @@ export const cssChoiceACItem = styled('li', `
&-new { &-new {
display: flex; display: flex;
align-items: center; align-items: center;
color: ${colors.slate};
position: sticky; position: sticky;
bottom: 0px; bottom: 0px;
height: ${ADD_NEW_HEIGHT}; height: ${ADD_NEW_HEIGHT};
background-color: white; background-color: ${theme.menuBg};
border-top: 1px solid ${colors.mediumGreyOpaque}; border-top: 1px solid ${theme.menuBorder};
scroll-margin-bottom: initial; scroll-margin-bottom: initial;
} }
&-new.selected {
color: ${colors.lightGrey};
}
`); `);

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save