Devtools: Refactor imperative theme code (#21950)

Co-authored-by: Brian Vaughn <bvaughn@fb.com>
This commit is contained in:
houssemchebeb 2021-08-02 16:20:04 +01:00 committed by GitHub
parent d3f8747c8d
commit e3b76a85c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 375 additions and 635 deletions

View File

@ -15,6 +15,7 @@ import {
useContext,
useDeferredValue,
useLayoutEffect,
useRef,
useState,
} from 'react';
import {SettingsContext} from 'react-devtools-shared/src/devtools/views/Settings/SettingsContext';
@ -30,6 +31,8 @@ export function SchedulingProfiler(_: {||}) {
SchedulingProfilerContext,
);
const ref = useRef(null);
// HACK: Canvas rendering uses an imperative API,
// but DevTools colors are stored in CSS variables (see root.css and SettingsContext).
// When the theme changes, we need to trigger update the imperative colors and re-draw the Canvas.
@ -42,7 +45,7 @@ export function SchedulingProfiler(_: {||}) {
const [key, setKey] = useState<string>(theme);
useLayoutEffect(() => {
const pollForTheme = () => {
if (updateColorsToMatchTheme()) {
if (updateColorsToMatchTheme(((ref.current: any): HTMLDivElement))) {
clearInterval(intervalID);
setKey(deferredTheme);
}
@ -56,7 +59,7 @@ export function SchedulingProfiler(_: {||}) {
}, [deferredTheme]);
return (
<div className={styles.Content}>
<div className={styles.Content} ref={ref}>
{schedulingProfilerData ? (
<Suspense fallback={<ProcessingData />}>
<DataResourceComponent

View File

@ -82,8 +82,8 @@ export let COLORS = {
WARNING_TEXT_INVERED: '',
};
export function updateColorsToMatchTheme(): boolean {
const computedStyle = getComputedStyle((document.body: any));
export function updateColorsToMatchTheme(element: Element): boolean {
const computedStyle = getComputedStyle(element);
// Check to see if styles have been initialized...
if (computedStyle.getPropertyValue('--color-background') == null) {

View File

@ -55,33 +55,296 @@ export const UNSUPPORTED_VERSION_URL =
export const REACT_DEVTOOLS_WORKPLACE_URL =
'https://fburl.com/react-devtools-workplace-group';
import type {
Theme,
DisplayDensity,
} from './devtools/views/Settings/SettingsContext';
export const THEME_STYLES: {[style: Theme | DisplayDensity]: any} = {
light: {
'--color-attribute-name': '#ef6632',
'--color-attribute-name-not-editable': '#23272f',
'--color-attribute-name-inverted': 'rgba(255, 255, 255, 0.7)',
'--color-attribute-value': '#1a1aa6',
'--color-attribute-value-inverted': '#ffffff',
'--color-attribute-editable-value': '#1a1aa6',
'--color-background': '#ffffff',
'--color-background-hover': 'rgba(0, 136, 250, 0.1)',
'--color-background-inactive': '#e5e5e5',
'--color-background-invalid': '#fff0f0',
'--color-background-selected': '#0088fa',
'--color-button-background': '#ffffff',
'--color-button-background-focus': '#ededed',
'--color-button': '#5f6673',
'--color-button-disabled': '#cfd1d5',
'--color-button-active': '#0088fa',
'--color-button-focus': '#23272f',
'--color-button-hover': '#23272f',
'--color-border': '#eeeeee',
'--color-commit-did-not-render-fill': '#cfd1d5',
'--color-commit-did-not-render-fill-text': '#000000',
'--color-commit-did-not-render-pattern': '#cfd1d5',
'--color-commit-did-not-render-pattern-text': '#333333',
'--color-commit-gradient-0': '#37afa9',
'--color-commit-gradient-1': '#63b19e',
'--color-commit-gradient-2': '#80b393',
'--color-commit-gradient-3': '#97b488',
'--color-commit-gradient-4': '#abb67d',
'--color-commit-gradient-5': '#beb771',
'--color-commit-gradient-6': '#cfb965',
'--color-commit-gradient-7': '#dfba57',
'--color-commit-gradient-8': '#efbb49',
'--color-commit-gradient-9': '#febc38',
'--color-commit-gradient-text': '#000000',
'--color-component-name': '#6a51b2',
'--color-component-name-inverted': '#ffffff',
'--color-component-badge-background': 'rgba(0, 0, 0, 0.1)',
'--color-component-badge-background-inverted': 'rgba(255, 255, 255, 0.25)',
'--color-component-badge-count': '#777d88',
'--color-component-badge-count-inverted': 'rgba(255, 255, 255, 0.7)',
'--color-console-error-badge-text': '#ffffff',
'--color-console-error-background': '#fff0f0',
'--color-console-error-border': '#ffd6d6',
'--color-console-error-icon': '#eb3941',
'--color-console-error-text': '#fe2e31',
'--color-console-warning-badge-text': '#000000',
'--color-console-warning-background': '#fffbe5',
'--color-console-warning-border': '#fff5c1',
'--color-console-warning-icon': '#f4bd00',
'--color-console-warning-text': '#64460c',
'--color-context-background': 'rgba(0,0,0,.9)',
'--color-context-background-hover': 'rgba(255, 255, 255, 0.1)',
'--color-context-background-selected': '#178fb9',
'--color-context-border': '#3d424a',
'--color-context-text': '#ffffff',
'--color-context-text-selected': '#ffffff',
'--color-dim': '#777d88',
'--color-dimmer': '#cfd1d5',
'--color-dimmest': '#eff0f1',
'--color-error-background': 'hsl(0, 100%, 97%)',
'--color-error-border': 'hsl(0, 100%, 92%)',
'--color-error-text': '#ff0000',
'--color-expand-collapse-toggle': '#777d88',
'--color-link': '#0000ff',
'--color-modal-background': 'rgba(255, 255, 255, 0.75)',
'--color-bridge-version-npm-background': '#eff0f1',
'--color-bridge-version-npm-text': '#000000',
'--color-bridge-version-number': '#0088fa',
'--color-primitive-hook-badge-background': '#e5e5e5',
'--color-primitive-hook-badge-text': '#5f6673',
'--color-record-active': '#fc3a4b',
'--color-record-hover': '#3578e5',
'--color-record-inactive': '#0088fa',
'--color-resize-bar': '#eeeeee',
'--color-resize-bar-active': '#dcdcdc',
'--color-resize-bar-border': '#d1d1d1',
'--color-resize-bar-dot': '#333333',
'--color-scheduling-profiler-native-event': '#ccc',
'--color-scheduling-profiler-native-event-hover': '#aaa',
'--color-scheduling-profiler-priority-background': '#f6f6f6',
'--color-scheduling-profiler-priority-border': '#eeeeee',
'--color-scheduling-profiler-user-timing': '#c9cacd',
'--color-scheduling-profiler-user-timing-hover': '#93959a',
'--color-scheduling-profiler-react-idle': '#d3e5f6',
'--color-scheduling-profiler-react-idle-hover': '#c3d9ef',
'--color-scheduling-profiler-react-render': '#9fc3f3',
'--color-scheduling-profiler-react-render-hover': '#83afe9',
'--color-scheduling-profiler-react-commit': '#c88ff0',
'--color-scheduling-profiler-react-commit-hover': '#b281d6',
'--color-scheduling-profiler-react-layout-effects': '#b281d6',
'--color-scheduling-profiler-react-layout-effects-hover': '#9d71bd',
'--color-scheduling-profiler-react-passive-effects': '#b281d6',
'--color-scheduling-profiler-react-passive-effects-hover': '#9d71bd',
'--color-scheduling-profiler-react-schedule': '#9fc3f3',
'--color-scheduling-profiler-react-schedule-hover': '#2683E2',
'--color-scheduling-profiler-react-suspense-rejected': '#f1cc14',
'--color-scheduling-profiler-react-suspense-rejected-hover': '#ffdf37',
'--color-scheduling-profiler-react-suspense-resolved': '#a6e59f',
'--color-scheduling-profiler-react-suspense-resolved-hover': '#89d281',
'--color-scheduling-profiler-react-suspense-unresolved': '#c9cacd',
'--color-scheduling-profiler-react-suspense-unresolved-hover': '#93959a',
'--color-scheduling-profiler-text-color': '#000000',
'--color-scheduling-profiler-react-work-border': '#ffffff',
'--color-scroll-thumb': '#c2c2c2',
'--color-scroll-track': '#fafafa',
'--color-search-match': 'yellow',
'--color-search-match-current': '#f7923b',
'--color-selected-tree-highlight-active': 'rgba(0, 136, 250, 0.1)',
'--color-selected-tree-highlight-inactive': 'rgba(0, 0, 0, 0.05)',
'--color-scroll-caret': 'rgba(150, 150, 150, 0.5)',
'--color-tab-selected-border': '#0088fa',
'--color-text': '#000000',
'--color-text-invalid': '#ff0000',
'--color-text-selected': '#ffffff',
'--color-toggle-background-invalid': '#fc3a4b',
'--color-toggle-background-on': '#0088fa',
'--color-toggle-background-off': '#cfd1d5',
'--color-toggle-text': '#ffffff',
'--color-tooltip-background': 'rgba(0, 0, 0, 0.9)',
'--color-tooltip-text': '#ffffff',
'--color-warning-background': '#fb3655',
'--color-warning-background-hover': '#f82042',
'--color-warning-text-color': '#ffffff',
'--color-warning-text-color-inverted': '#fd4d69',
},
dark: {
'--color-attribute-name': '#9d87d2',
'--color-attribute-name-not-editable': '#ededed',
'--color-attribute-name-inverted': '#282828',
'--color-attribute-value': '#cedae0',
'--color-attribute-value-inverted': '#ffffff',
'--color-attribute-editable-value': 'yellow',
'--color-background': '#282c34',
'--color-background-hover': 'rgba(255, 255, 255, 0.1)',
'--color-background-inactive': '#3d424a',
'--color-background-invalid': '#5c0000',
'--color-background-selected': '#178fb9',
'--color-button-background': '#282c34',
'--color-button-background-focus': '#3d424a',
'--color-button': '#afb3b9',
'--color-button-active': '#61dafb',
'--color-button-disabled': '#4f5766',
'--color-button-focus': '#a2e9fc',
'--color-button-hover': '#ededed',
'--color-border': '#3d424a',
'--color-commit-did-not-render-fill': '#777d88',
'--color-commit-did-not-render-fill-text': '#000000',
'--color-commit-did-not-render-pattern': '#666c77',
'--color-commit-did-not-render-pattern-text': '#ffffff',
'--color-commit-gradient-0': '#37afa9',
'--color-commit-gradient-1': '#63b19e',
'--color-commit-gradient-2': '#80b393',
'--color-commit-gradient-3': '#97b488',
'--color-commit-gradient-4': '#abb67d',
'--color-commit-gradient-5': '#beb771',
'--color-commit-gradient-6': '#cfb965',
'--color-commit-gradient-7': '#dfba57',
'--color-commit-gradient-8': '#efbb49',
'--color-commit-gradient-9': '#febc38',
'--color-commit-gradient-text': '#000000',
'--color-component-name': '#61dafb',
'--color-component-name-inverted': '#282828',
'--color-component-badge-background': 'rgba(255, 255, 255, 0.25)',
'--color-component-badge-background-inverted': 'rgba(0, 0, 0, 0.25)',
'--color-component-badge-count': '#8f949d',
'--color-component-badge-count-inverted': 'rgba(255, 255, 255, 0.7)',
'--color-console-error-badge-text': '#000000',
'--color-console-error-background': '#290000',
'--color-console-error-border': '#5c0000',
'--color-console-error-icon': '#eb3941',
'--color-console-error-text': '#fc7f7f',
'--color-console-warning-badge-text': '#000000',
'--color-console-warning-background': '#332b00',
'--color-console-warning-border': '#665500',
'--color-console-warning-icon': '#f4bd00',
'--color-console-warning-text': '#f5f2ed',
'--color-context-background': 'rgba(255,255,255,.95)',
'--color-context-background-hover': 'rgba(0, 136, 250, 0.1)',
'--color-context-background-selected': '#0088fa',
'--color-context-border': '#eeeeee',
'--color-context-text': '#000000',
'--color-context-text-selected': '#ffffff',
'--color-dim': '#8f949d',
'--color-dimmer': '#777d88',
'--color-dimmest': '#4f5766',
'--color-error-background': '#200',
'--color-error-border': '#900',
'--color-error-text': '#f55',
'--color-expand-collapse-toggle': '#8f949d',
'--color-link': '#61dafb',
'--color-modal-background': 'rgba(0, 0, 0, 0.75)',
'--color-bridge-version-npm-background': 'rgba(0, 0, 0, 0.25)',
'--color-bridge-version-npm-text': '#ffffff',
'--color-bridge-version-number': 'yellow',
'--color-primitive-hook-badge-background': 'rgba(0, 0, 0, 0.25)',
'--color-primitive-hook-badge-text': 'rgba(255, 255, 255, 0.7)',
'--color-record-active': '#fc3a4b',
'--color-record-hover': '#a2e9fc',
'--color-record-inactive': '#61dafb',
'--color-resize-bar': '#282c34',
'--color-resize-bar-active': '#31363f',
'--color-resize-bar-border': '#3d424a',
'--color-resize-bar-dot': '#cfd1d5',
'--color-scheduling-profiler-native-event': '#b2b2b2',
'--color-scheduling-profiler-native-event-hover': '#949494',
'--color-scheduling-profiler-priority-background': '#1d2129',
'--color-scheduling-profiler-priority-border': '#282c34',
'--color-scheduling-profiler-user-timing': '#c9cacd',
'--color-scheduling-profiler-user-timing-hover': '#93959a',
'--color-scheduling-profiler-react-idle': '#3d485b',
'--color-scheduling-profiler-react-idle-hover': '#465269',
'--color-scheduling-profiler-react-render': '#2683E2',
'--color-scheduling-profiler-react-render-hover': '#1a76d4',
'--color-scheduling-profiler-react-commit': '#731fad',
'--color-scheduling-profiler-react-commit-hover': '#611b94',
'--color-scheduling-profiler-react-layout-effects': '#611b94',
'--color-scheduling-profiler-react-layout-effects-hover': '#51167a',
'--color-scheduling-profiler-react-passive-effects': '#611b94',
'--color-scheduling-profiler-react-passive-effects-hover': '#51167a',
'--color-scheduling-profiler-react-schedule': '#2683E2',
'--color-scheduling-profiler-react-schedule-hover': '#1a76d4',
'--color-scheduling-profiler-react-suspense-rejected': '#f1cc14',
'--color-scheduling-profiler-react-suspense-rejected-hover': '#e4c00f',
'--color-scheduling-profiler-react-suspense-resolved': '#a6e59f',
'--color-scheduling-profiler-react-suspense-resolved-hover': '#89d281',
'--color-scheduling-profiler-react-suspense-unresolved': '#c9cacd',
'--color-scheduling-profiler-react-suspense-unresolved-hover': '#93959a',
'--color-scheduling-profiler-text-color': '#000000',
'--color-scheduling-profiler-react-work-border': '#ffffff',
'--color-scroll-thumb': '#afb3b9',
'--color-scroll-track': '#313640',
'--color-search-match': 'yellow',
'--color-search-match-current': '#f7923b',
'--color-selected-tree-highlight-active': 'rgba(23, 143, 185, 0.15)',
'--color-selected-tree-highlight-inactive': 'rgba(255, 255, 255, 0.05)',
'--color-scroll-caret': '#4f5766',
'--color-shadow': 'rgba(0, 0, 0, 0.5)',
'--color-tab-selected-border': '#178fb9',
'--color-text': '#ffffff',
'--color-text-invalid': '#ff8080',
'--color-text-selected': '#ffffff',
'--color-toggle-background-invalid': '#fc3a4b',
'--color-toggle-background-on': '#178fb9',
'--color-toggle-background-off': '#777d88',
'--color-toggle-text': '#ffffff',
'--color-tooltip-background': 'rgba(255, 255, 255, 0.95)',
'--color-tooltip-text': '#000000',
'--color-warning-background': '#ee1638',
'--color-warning-background-hover': '#da1030',
'--color-warning-text-color': '#ffffff',
'--color-warning-text-color-inverted': '#ee1638',
},
compact: {
'--font-size-monospace-small': '9px',
'--font-size-monospace-normal': '11px',
'--font-size-monospace-large': '15px',
'--font-size-sans-small': '10px',
'--font-size-sans-normal': '12px',
'--font-size-sans-large': '14px',
'--line-height-data': '18px',
},
comfortable: {
'--font-size-monospace-small': '10px',
'--font-size-monospace-normal': '13px',
'--font-size-monospace-large': '17px',
'--font-size-sans-small': '12px',
'--font-size-sans-normal': '14px',
'--font-size-sans-large': '16px',
'--line-height-data': '22px',
},
};
// HACK
//
// Extracting during build time avoids a temporarily invalid state for the inline target.
// Sometimes the inline target is rendered before root styles are applied,
// which would result in e.g. NaN itemSize being passed to react-window list.
//
let COMFORTABLE_LINE_HEIGHT;
let COMPACT_LINE_HEIGHT;
try {
// $FlowFixMe
const rawStyleString = require('!!raw-loader!react-devtools-shared/src/devtools/views/root.css')
.default;
const extractVar = varName => {
const regExp = new RegExp(`${varName}: ([0-9]+)`);
const match = rawStyleString.match(regExp);
return parseInt(match[1], 10);
};
COMFORTABLE_LINE_HEIGHT = extractVar('comfortable-line-height-data');
COMPACT_LINE_HEIGHT = extractVar('compact-line-height-data');
} catch (error) {
// We can't use the Webpack loader syntax in the context of Jest,
// so tests need some reasonably meaningful fallback value.
COMFORTABLE_LINE_HEIGHT = 15;
COMPACT_LINE_HEIGHT = 10;
}
const COMFORTABLE_LINE_HEIGHT = parseInt(
THEME_STYLES.comfortable['--line-height-data'],
10,
);
const COMPACT_LINE_HEIGHT = parseInt(
THEME_STYLES.compact['--line-height-data'],
10,
);
export {COMFORTABLE_LINE_HEIGHT, COMPACT_LINE_HEIGHT};

View File

@ -31,6 +31,7 @@ import UnsupportedBridgeProtocolDialog from './UnsupportedBridgeProtocolDialog';
import UnsupportedVersionDialog from './UnsupportedVersionDialog';
import WarnIfLegacyBackendDetected from './WarnIfLegacyBackendDetected';
import {useLocalStorage} from './hooks';
import ThemeProvider from './ThemeProvider';
import styles from './DevTools.css';
@ -220,38 +221,40 @@ export default function DevTools({
<TreeContextController>
<ProfilerContextController>
<SchedulingProfilerContextController>
<div className={styles.DevTools} ref={devToolsRef}>
{showTabBar && (
<div className={styles.TabBar}>
<ReactLogo />
<span className={styles.DevToolsVersion}>
{process.env.DEVTOOLS_VERSION}
</span>
<div className={styles.Spacer} />
<TabBar
currentTab={tab}
id="DevTools"
selectTab={setTab}
tabs={tabs}
type="navigation"
<ThemeProvider>
<div className={styles.DevTools} ref={devToolsRef}>
{showTabBar && (
<div className={styles.TabBar}>
<ReactLogo />
<span className={styles.DevToolsVersion}>
{process.env.DEVTOOLS_VERSION}
</span>
<div className={styles.Spacer} />
<TabBar
currentTab={tab}
id="DevTools"
selectTab={setTab}
tabs={tabs}
type="navigation"
/>
</div>
)}
<div
className={styles.TabContent}
hidden={tab !== 'components'}>
<Components
portalContainer={componentsPortalContainer}
/>
</div>
<div
className={styles.TabContent}
hidden={tab !== 'profiler'}>
<Profiler
portalContainer={profilerPortalContainer}
/>
</div>
)}
<div
className={styles.TabContent}
hidden={tab !== 'components'}>
<Components
portalContainer={componentsPortalContainer}
/>
</div>
<div
className={styles.TabContent}
hidden={tab !== 'profiler'}>
<Profiler
portalContainer={profilerPortalContainer}
/>
</div>
</div>
</ThemeProvider>
</SchedulingProfilerContextController>
</ProfilerContextController>
</TreeContextController>

View File

@ -55,6 +55,8 @@ type Context = {|
theme: Theme,
setTheme(value: Theme): void,
browserTheme: Theme,
traceUpdatesEnabled: boolean,
setTraceUpdatesEnabled: (value: boolean) => void,
|};
@ -198,6 +200,7 @@ function SettingsContextController({
setShowInlineWarningsAndErrors,
showInlineWarningsAndErrors,
theme,
browserTheme,
traceUpdatesEnabled,
}),
[
@ -214,6 +217,7 @@ function SettingsContextController({
setShowInlineWarningsAndErrors,
showInlineWarningsAndErrors,
theme,
browserTheme,
traceUpdatesEnabled,
],
);
@ -251,26 +255,6 @@ export function updateDisplayDensity(
displayDensity: DisplayDensity,
documentElements: DocumentElements,
): void {
updateStyleHelper(
displayDensity,
'font-size-monospace-normal',
documentElements,
);
updateStyleHelper(
displayDensity,
'font-size-monospace-large',
documentElements,
);
updateStyleHelper(
displayDensity,
'font-size-monospace-small',
documentElements,
);
updateStyleHelper(displayDensity, 'font-size-sans-normal', documentElements);
updateStyleHelper(displayDensity, 'font-size-sans-large', documentElements);
updateStyleHelper(displayDensity, 'font-size-sans-small', documentElements);
updateStyleHelper(displayDensity, 'line-height-data', documentElements);
// Sizes and paddings/margins are all rem-based,
// so update the root font-size as well when the display preference changes.
const computedStyle = getComputedStyle((document.body: any));
@ -285,304 +269,6 @@ export function updateThemeVariables(
theme: Theme,
documentElements: DocumentElements,
): void {
updateStyleHelper(theme, 'color-attribute-name', documentElements);
updateStyleHelper(
theme,
'color-attribute-name-not-editable',
documentElements,
);
updateStyleHelper(theme, 'color-attribute-name-inverted', documentElements);
updateStyleHelper(theme, 'color-attribute-value', documentElements);
updateStyleHelper(theme, 'color-attribute-value-inverted', documentElements);
updateStyleHelper(theme, 'color-attribute-editable-value', documentElements);
updateStyleHelper(theme, 'color-background', documentElements);
updateStyleHelper(theme, 'color-background-hover', documentElements);
updateStyleHelper(theme, 'color-background-inactive', documentElements);
updateStyleHelper(theme, 'color-background-invalid', documentElements);
updateStyleHelper(theme, 'color-background-selected', documentElements);
updateStyleHelper(theme, 'color-border', documentElements);
updateStyleHelper(theme, 'color-button-background', documentElements);
updateStyleHelper(theme, 'color-button-background-focus', documentElements);
updateStyleHelper(theme, 'color-button', documentElements);
updateStyleHelper(theme, 'color-button-active', documentElements);
updateStyleHelper(theme, 'color-button-disabled', documentElements);
updateStyleHelper(theme, 'color-button-focus', documentElements);
updateStyleHelper(theme, 'color-button-hover', documentElements);
updateStyleHelper(
theme,
'color-commit-did-not-render-fill',
documentElements,
);
updateStyleHelper(
theme,
'color-commit-did-not-render-fill-text',
documentElements,
);
updateStyleHelper(
theme,
'color-commit-did-not-render-pattern',
documentElements,
);
updateStyleHelper(
theme,
'color-commit-did-not-render-pattern-text',
documentElements,
);
updateStyleHelper(theme, 'color-commit-gradient-0', documentElements);
updateStyleHelper(theme, 'color-commit-gradient-1', documentElements);
updateStyleHelper(theme, 'color-commit-gradient-2', documentElements);
updateStyleHelper(theme, 'color-commit-gradient-3', documentElements);
updateStyleHelper(theme, 'color-commit-gradient-4', documentElements);
updateStyleHelper(theme, 'color-commit-gradient-5', documentElements);
updateStyleHelper(theme, 'color-commit-gradient-6', documentElements);
updateStyleHelper(theme, 'color-commit-gradient-7', documentElements);
updateStyleHelper(theme, 'color-commit-gradient-8', documentElements);
updateStyleHelper(theme, 'color-commit-gradient-9', documentElements);
updateStyleHelper(theme, 'color-commit-gradient-text', documentElements);
updateStyleHelper(theme, 'color-component-name', documentElements);
updateStyleHelper(theme, 'color-component-name-inverted', documentElements);
updateStyleHelper(
theme,
'color-component-badge-background',
documentElements,
);
updateStyleHelper(
theme,
'color-component-badge-background-inverted',
documentElements,
);
updateStyleHelper(theme, 'color-component-badge-count', documentElements);
updateStyleHelper(
theme,
'color-component-badge-count-inverted',
documentElements,
);
updateStyleHelper(theme, 'color-console-error-badge-text', documentElements);
updateStyleHelper(theme, 'color-console-error-background', documentElements);
updateStyleHelper(theme, 'color-console-error-border', documentElements);
updateStyleHelper(theme, 'color-console-error-icon', documentElements);
updateStyleHelper(theme, 'color-console-error-text', documentElements);
updateStyleHelper(
theme,
'color-console-warning-badge-text',
documentElements,
);
updateStyleHelper(
theme,
'color-console-warning-background',
documentElements,
);
updateStyleHelper(theme, 'color-console-warning-border', documentElements);
updateStyleHelper(theme, 'color-console-warning-icon', documentElements);
updateStyleHelper(theme, 'color-console-warning-text', documentElements);
updateStyleHelper(theme, 'color-context-border', documentElements);
updateStyleHelper(theme, 'color-context-background', documentElements);
updateStyleHelper(theme, 'color-context-background-hover', documentElements);
updateStyleHelper(
theme,
'color-context-background-selected',
documentElements,
);
updateStyleHelper(theme, 'color-context-border', documentElements);
updateStyleHelper(theme, 'color-context-text', documentElements);
updateStyleHelper(theme, 'color-context-text-selected', documentElements);
updateStyleHelper(theme, 'color-dim', documentElements);
updateStyleHelper(theme, 'color-dimmer', documentElements);
updateStyleHelper(theme, 'color-dimmest', documentElements);
updateStyleHelper(theme, 'color-error-background', documentElements);
updateStyleHelper(theme, 'color-error-border', documentElements);
updateStyleHelper(theme, 'color-error-text', documentElements);
updateStyleHelper(theme, 'color-expand-collapse-toggle', documentElements);
updateStyleHelper(theme, 'color-link', documentElements);
updateStyleHelper(theme, 'color-modal-background', documentElements);
updateStyleHelper(
theme,
'color-bridge-version-npm-background',
documentElements,
);
updateStyleHelper(theme, 'color-bridge-version-npm-text', documentElements);
updateStyleHelper(theme, 'color-bridge-version-number', documentElements);
updateStyleHelper(
theme,
'color-primitive-hook-badge-background',
documentElements,
);
updateStyleHelper(theme, 'color-primitive-hook-badge-text', documentElements);
updateStyleHelper(theme, 'color-record-active', documentElements);
updateStyleHelper(theme, 'color-record-hover', documentElements);
updateStyleHelper(theme, 'color-record-inactive', documentElements);
updateStyleHelper(theme, 'color-resize-bar', documentElements);
updateStyleHelper(theme, 'color-resize-bar-active', documentElements);
updateStyleHelper(theme, 'color-resize-bar-border', documentElements);
updateStyleHelper(theme, 'color-resize-bar-dot', documentElements);
updateStyleHelper(theme, 'color-color-scroll-thumb', documentElements);
updateStyleHelper(theme, 'color-color-scroll-track', documentElements);
updateStyleHelper(theme, 'color-search-match', documentElements);
updateStyleHelper(theme, 'color-search-match-current', documentElements);
updateStyleHelper(
theme,
'color-scheduling-profiler-text-color',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-native-event',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-native-event-hover',
documentElements,
);
updateStyleHelper(
theme,
'color-selected-tree-highlight-active',
documentElements,
);
updateStyleHelper(
theme,
'color-selected-tree-highlight-inactive',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-priority-background',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-priority-border',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-user-timing',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-user-timing-hover',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-react-idle',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-react-idle-hover',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-react-render',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-react-render-hover',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-react-commit',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-react-commit-hover',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-react-layout-effects',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-react-layout-effects-hover',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-react-passive-effects',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-react-passive-effects-hover',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-react-schedule',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-react-schedule-hover',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-react-suspense-rejected-event',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-react-suspense-rejected-hover',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-react-suspense-resolved',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-react-suspense-resolved-hover',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-react-suspense-unresolved',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-react-suspense-unresolved-hover',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-react-work-border',
documentElements,
);
updateStyleHelper(theme, 'color-scroll-caret', documentElements);
updateStyleHelper(theme, 'color-shadow', documentElements);
updateStyleHelper(theme, 'color-tab-selected-border', documentElements);
updateStyleHelper(theme, 'color-text', documentElements);
updateStyleHelper(theme, 'color-text-invalid', documentElements);
updateStyleHelper(theme, 'color-text-selected', documentElements);
updateStyleHelper(theme, 'color-toggle-background-invalid', documentElements);
updateStyleHelper(theme, 'color-toggle-background-on', documentElements);
updateStyleHelper(theme, 'color-toggle-background-off', documentElements);
updateStyleHelper(theme, 'color-toggle-text', documentElements);
updateStyleHelper(theme, 'color-tooltip-background', documentElements);
updateStyleHelper(theme, 'color-tooltip-text', documentElements);
updateStyleHelper(theme, 'color-warning-background', documentElements);
updateStyleHelper(theme, 'color-warning-background-hover', documentElements);
updateStyleHelper(theme, 'color-warning-text-color', documentElements);
updateStyleHelper(
theme,
'color-warning-text-color-inverted',
documentElements,
);
// Font smoothing varies based on the theme.
updateStyleHelper(theme, 'font-smoothing', documentElements);
// Update scrollbar color to match theme.
// this CSS property is currently only supported in Firefox,
// but it makes a significant UI improvement in dark mode.
@ -591,6 +277,12 @@ export function updateThemeVariables(
// $FlowFixMe scrollbarColor is missing in CSSStyleDeclaration
documentElement.style.scrollbarColor = `var(${`--${theme}-color-scroll-thumb`}) var(${`--${theme}-color-scroll-track`})`;
});
// The ThemeProvider works by writing DOM style variables to an HTMLDivElement.
// Because Portals render in a different DOM subtree, these variables don't propagate.
// So we need to also set @reach/tooltip specific styles on the root.
updateStyleHelper(theme, 'color-tooltip-background', documentElements);
updateStyleHelper(theme, 'color-tooltip-text', documentElements);
}
export {SettingsContext, SettingsContextController};

View File

@ -0,0 +1,29 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import * as React from 'react';
import {useContext, useMemo} from 'react';
import {SettingsContext} from './Settings/SettingsContext';
import {THEME_STYLES} from '../../constants';
export default function ThemeProvider({children}: {|children: React$Node|}) {
const {theme, displayDensity, browserTheme} = useContext(SettingsContext);
const style = useMemo(
() => ({
width: '100%',
height: '100%',
...THEME_STYLES[displayDensity],
...THEME_STYLES[theme === 'auto' ? browserTheme : theme],
}),
[theme, browserTheme, displayDensity],
);
return <div style={style}>{children}</div>;
}

View File

@ -12,6 +12,7 @@ import {useContext} from 'react';
import {createPortal} from 'react-dom';
import ErrorBoundary from './ErrorBoundary';
import {StoreContext} from './context';
import ThemeProvider from './ThemeProvider';
export type Props = {portalContainer?: Element, ...};
@ -21,12 +22,19 @@ export default function portaledContent(
return function PortaledContent({portalContainer, ...rest}: Props) {
const store = useContext(StoreContext);
const children = (
let children = (
<ErrorBoundary store={store}>
<Component {...rest} />
</ErrorBoundary>
);
if (portalContainer != null) {
// The ThemeProvider works by writing DOM style variables to an HTMLDivElement.
// Because Portals render in a different DOM subtree, these variables don't propagate.
// So in this case, we need to re-wrap portaled content in a second ThemeProvider.
children = <ThemeProvider>{children}</ThemeProvider>;
}
return portalContainer != null
? createPortal(children, portalContainer)
: children;

View File

@ -1,283 +1,25 @@
:root {
/**
* IMPORTANT: When new theme variables are added below also add them to SettingsContext updateThemeVariables()
*/
/* Light theme */
--light-color-attribute-name: #ef6632;
--light-color-attribute-name-not-editable: #23272f;
--light-color-attribute-name-inverted: rgba(255, 255, 255, 0.7);
--light-color-attribute-value: #1a1aa6;
--light-color-attribute-value-inverted: #ffffff;
--light-color-attribute-editable-value: #1a1aa6;
--light-color-background: #ffffff;
--light-color-background-hover: rgba(0, 136, 250, 0.1);
--light-color-background-inactive: #e5e5e5;
--light-color-background-invalid: #fff0f0;
--light-color-background-selected: #0088fa;
--light-color-button-background: #ffffff;
--light-color-button-background-focus: #ededed;
--light-color-button: #5f6673;
--light-color-button-disabled: #cfd1d5;
--light-color-button-active: #0088fa;
--light-color-button-focus: #23272f;
--light-color-button-hover: #23272f;
--light-color-border: #eeeeee;
--light-color-commit-did-not-render-fill: #cfd1d5;
--light-color-commit-did-not-render-fill-text: #000000;
--light-color-commit-did-not-render-pattern: #cfd1d5;
--light-color-commit-did-not-render-pattern-text: #333333;
--light-color-commit-gradient-0: #37afa9;
--light-color-commit-gradient-1: #63b19e;
--light-color-commit-gradient-2: #80b393;
--light-color-commit-gradient-3: #97b488;
--light-color-commit-gradient-4: #abb67d;
--light-color-commit-gradient-5: #beb771;
--light-color-commit-gradient-6: #cfb965;
--light-color-commit-gradient-7: #dfba57;
--light-color-commit-gradient-8: #efbb49;
--light-color-commit-gradient-9: #febc38;
--light-color-commit-gradient-text: #000000;
--light-color-component-name: #6a51b2;
--light-color-component-name-inverted: #ffffff;
--light-color-component-badge-background: rgba(0, 0, 0, 0.1);
--light-color-component-badge-background-inverted: rgba(255, 255, 255, 0.25);
--light-color-component-badge-count: #777d88;
--light-color-component-badge-count-inverted: rgba(255, 255, 255, 0.7);
--light-color-console-error-badge-text: #ffffff;
--light-color-console-error-background: #fff0f0;
--light-color-console-error-border: #ffd6d6;
--light-color-console-error-icon: #eb3941;
--light-color-console-error-text: #fe2e31;
--light-color-console-warning-badge-text: #000000;
--light-color-console-warning-background: #fffbe5;
--light-color-console-warning-border: #fff5c1;
--light-color-console-warning-icon: #f4bd00;
--light-color-console-warning-text: #64460c;
--light-color-context-background: rgba(0,0,0,.9);
--light-color-context-background-hover: rgba(255, 255, 255, 0.1);
--light-color-context-background-selected: #178fb9;
--light-color-context-border: #3d424a;
--light-color-context-text: #ffffff;
--light-color-context-text-selected: #ffffff;
--light-color-dim: #777d88;
--light-color-dimmer: #cfd1d5;
--light-color-dimmest: #eff0f1;
--light-color-error-background: hsl(0, 100%, 97%);
--light-color-error-border: hsl(0, 100%, 92%);
--light-color-error-text: #ff0000;
--light-color-expand-collapse-toggle: #777d88;
--light-color-link: #0000ff;
--light-color-modal-background: rgba(255, 255, 255, 0.75);
--light-color-bridge-version-npm-background: #eff0f1;
--light-color-bridge-version-npm-text: #000000;
--light-color-bridge-version-number: #0088fa;
--light-color-primitive-hook-badge-background: #e5e5e5;
--light-color-primitive-hook-badge-text: #5f6673;
--light-color-record-active: #fc3a4b;
--light-color-record-hover: #3578e5;
--light-color-record-inactive: #0088fa;
--light-color-resize-bar: #eeeeee;
--light-color-resize-bar-active: #dcdcdc;
--light-color-resize-bar-border: #d1d1d1;
--light-color-resize-bar-dot: #333333;
--light-color-scheduling-profiler-native-event: #ccc;
--light-color-scheduling-profiler-native-event-hover: #aaa;
--light-color-scheduling-profiler-priority-background: #f6f6f6;
--light-color-scheduling-profiler-priority-border: #eeeeee;
--light-color-scheduling-profiler-user-timing: #c9cacd;
--light-color-scheduling-profiler-user-timing-hover: #93959a;
--light-color-scheduling-profiler-react-idle: #d3e5f6;
--light-color-scheduling-profiler-react-idle-hover: #c3d9ef;
--light-color-scheduling-profiler-react-render: #9fc3f3;
--light-color-scheduling-profiler-react-render-hover: #83afe9;
--light-color-scheduling-profiler-react-commit: #c88ff0;
--light-color-scheduling-profiler-react-commit-hover: #b281d6;
--light-color-scheduling-profiler-react-layout-effects: #b281d6;
--light-color-scheduling-profiler-react-layout-effects-hover: #9d71bd;
--light-color-scheduling-profiler-react-passive-effects: #b281d6;
--light-color-scheduling-profiler-react-passive-effects-hover: #9d71bd;
--light-color-scheduling-profiler-react-schedule: #9fc3f3;
--light-color-scheduling-profiler-react-schedule-hover: #2683E2;
--light-color-scheduling-profiler-react-suspense-rejected: #f1cc14;
--light-color-scheduling-profiler-react-suspense-rejected-hover: #ffdf37;
--light-color-scheduling-profiler-react-suspense-resolved: #a6e59f;
--light-color-scheduling-profiler-react-suspense-resolved-hover: #89d281;
--light-color-scheduling-profiler-react-suspense-unresolved: #c9cacd;
--light-color-scheduling-profiler-react-suspense-unresolved-hover: #93959a;
--light-color-scheduling-profiler-text-color: #000000;
--light-color-scheduling-profiler-react-work-border: #ffffff;
--light-color-scroll-thumb: #c2c2c2;
--light-color-scroll-track: #fafafa;
--light-color-search-match: yellow;
--light-color-search-match-current: #f7923b;
--light-color-selected-tree-highlight-active: rgba(0, 136, 250, 0.1);
--light-color-selected-tree-highlight-inactive: rgba(0, 0, 0, 0.05);
--light-color-scroll-caret: rgba(150, 150, 150, 0.5);
--light-color-tab-selected-border: #0088fa;
--light-color-text: #000000;
--light-color-text-invalid: #ff0000;
--light-color-text-selected: #ffffff;
--light-color-toggle-background-invalid: #fc3a4b;
--light-color-toggle-background-on: #0088fa;
--light-color-toggle-background-off: #cfd1d5;
--light-color-toggle-text: #ffffff;
--light-color-tooltip-background: rgba(0, 0, 0, 0.9);
--light-color-tooltip-text: #ffffff;
--light-color-warning-background: #fb3655;
--light-color-warning-background-hover: #f82042;
--light-color-warning-text-color: #ffffff;
--light-color-warning-text-color-inverted: #fd4d69;
/* Dark theme */
--dark-color-attribute-name: #9d87d2;
--dark-color-attribute-name-not-editable: #ededed;
--dark-color-attribute-name-inverted: #282828;
--dark-color-attribute-value: #cedae0;
--dark-color-attribute-value-inverted: #ffffff;
--dark-color-attribute-editable-value: yellow;
--dark-color-background: #282c34;
--dark-color-background-hover: rgba(255, 255, 255, 0.1);
--dark-color-background-inactive: #3d424a;
--dark-color-background-invalid: #5c0000;
--dark-color-background-selected: #178fb9;
--dark-color-button-background: #282c34;
--dark-color-button-background-focus: #3d424a;
--dark-color-button: #afb3b9;
--dark-color-button-active: #61dafb;
--dark-color-button-disabled: #4f5766;
--dark-color-button-focus: #a2e9fc;
--dark-color-button-hover: #ededed;
--dark-color-border: #3d424a;
--dark-color-commit-did-not-render-fill: #777d88;
--dark-color-commit-did-not-render-fill-text: #000000;
--dark-color-commit-did-not-render-pattern: #666c77;
--dark-color-commit-did-not-render-pattern-text: #ffffff;
--dark-color-commit-gradient-0: #37afa9;
--dark-color-commit-gradient-1: #63b19e;
--dark-color-commit-gradient-2: #80b393;
--dark-color-commit-gradient-3: #97b488;
--dark-color-commit-gradient-4: #abb67d;
--dark-color-commit-gradient-5: #beb771;
--dark-color-commit-gradient-6: #cfb965;
--dark-color-commit-gradient-7: #dfba57;
--dark-color-commit-gradient-8: #efbb49;
--dark-color-commit-gradient-9: #febc38;
--dark-color-commit-gradient-text: #000000;
--dark-color-component-name: #61dafb;
--dark-color-component-name-inverted: #282828;
--dark-color-component-badge-background: rgba(255, 255, 255, 0.25);
--dark-color-component-badge-background-inverted: rgba(0, 0, 0, 0.25);
--dark-color-component-badge-count: #8f949d;
--dark-color-component-badge-count-inverted: rgba(255, 255, 255, 0.7);
--dark-color-console-error-badge-text: #000000;
--dark-color-console-error-background: #290000;
--dark-color-console-error-border: #5c0000;
--dark-color-console-error-icon: #eb3941;
--dark-color-console-error-text: #fc7f7f;
--dark-color-console-warning-badge-text: #000000;
--dark-color-console-warning-background: #332b00;
--dark-color-console-warning-border: #665500;
--dark-color-console-warning-icon: #f4bd00;
--dark-color-console-warning-text: #f5f2ed;
--dark-color-context-background: rgba(255,255,255,.95);
--dark-color-context-background-hover: rgba(0, 136, 250, 0.1);
--dark-color-context-background-selected: #0088fa;
--dark-color-context-border: #eeeeee;
--dark-color-context-text: #000000;
--dark-color-context-text-selected: #ffffff;
--dark-color-dim: #8f949d;
--dark-color-dimmer: #777d88;
--dark-color-dimmest: #4f5766;
--dark-color-error-background: #200;
--dark-color-error-border: #900;
--dark-color-error-text: #f55;
--dark-color-expand-collapse-toggle: #8f949d;
--dark-color-link: #61dafb;
--dark-color-modal-background: rgba(0, 0, 0, 0.75);
--dark-color-bridge-version-npm-background: rgba(0, 0, 0, 0.25);
--dark-color-bridge-version-npm-text: #ffffff;
--dark-color-bridge-version-number: yellow;
--dark-color-primitive-hook-badge-background: rgba(0, 0, 0, 0.25);
--dark-color-primitive-hook-badge-text: rgba(255, 255, 255, 0.7);
--dark-color-record-active: #fc3a4b;
--dark-color-record-hover: #a2e9fc;
--dark-color-record-inactive: #61dafb;
--dark-color-resize-bar: #282c34;
--dark-color-resize-bar-active: #31363f;
--dark-color-resize-bar-border: #3d424a;
--dark-color-resize-bar-dot: #cfd1d5;
--dark-color-scheduling-profiler-native-event: #b2b2b2;
--dark-color-scheduling-profiler-native-event-hover: #949494;
--dark-color-scheduling-profiler-priority-background: #1d2129;
--dark-color-scheduling-profiler-priority-border: #282c34;
--dark-color-scheduling-profiler-user-timing: #c9cacd;
--dark-color-scheduling-profiler-user-timing-hover: #93959a;
--dark-color-scheduling-profiler-react-idle: #3d485b;
--dark-color-scheduling-profiler-react-idle-hover: #465269;
--dark-color-scheduling-profiler-react-render: #2683E2;
--dark-color-scheduling-profiler-react-render-hover: #1a76d4;
--dark-color-scheduling-profiler-react-commit: #731fad;
--dark-color-scheduling-profiler-react-commit-hover: #611b94;
--dark-color-scheduling-profiler-react-layout-effects: #611b94;
--dark-color-scheduling-profiler-react-layout-effects-hover: #51167a;
--dark-color-scheduling-profiler-react-passive-effects: #611b94;
--dark-color-scheduling-profiler-react-passive-effects-hover: #51167a;
--dark-color-scheduling-profiler-react-schedule: #2683E2;
--dark-color-scheduling-profiler-react-schedule-hover: #1a76d4;
--dark-color-scheduling-profiler-react-suspense-rejected: #f1cc14;
--dark-color-scheduling-profiler-react-suspense-rejected-hover: #e4c00f;
--dark-color-scheduling-profiler-react-suspense-resolved: #a6e59f;
--dark-color-scheduling-profiler-react-suspense-resolved-hover: #89d281;
--dark-color-scheduling-profiler-react-suspense-unresolved: #c9cacd;
--dark-color-scheduling-profiler-react-suspense-unresolved-hover: #93959a;
--dark-color-scheduling-profiler-text-color: #000000;
--dark-color-scheduling-profiler-react-work-border: #ffffff;
--dark-color-scroll-thumb: #afb3b9;
--dark-color-scroll-track: #313640;
--dark-color-search-match: yellow;
--dark-color-search-match-current: #f7923b;
--dark-color-selected-tree-highlight-active: rgba(23, 143, 185, 0.15);
--dark-color-selected-tree-highlight-inactive: rgba(255, 255, 255, 0.05);
--dark-color-scroll-caret: #4f5766;
--dark-color-shadow: rgba(0, 0, 0, 0.5);
--dark-color-tab-selected-border: #178fb9;
--dark-color-text: #ffffff;
--dark-color-text-invalid: #ff8080;
--dark-color-text-selected: #ffffff;
--dark-color-toggle-background-invalid: #fc3a4b;
--dark-color-toggle-background-on: #178fb9;
--dark-color-toggle-background-off: #777d88;
--dark-color-toggle-text: #ffffff;
--dark-color-tooltip-background: rgba(255, 255, 255, 0.95);
--dark-color-tooltip-text: #000000;
--dark-color-warning-background: #ee1638;
--dark-color-warning-background-hover: #da1030;
--dark-color-warning-text-color: #ffffff;
--dark-color-warning-text-color-inverted: #ee1638;
/* Font smoothing */
--light-font-smoothing: auto;
--dark-font-smoothing: antialiased;
--font-smoothing: auto;
/* Compact density */
--compact-font-size-monospace-small: 9px;
--compact-font-size-monospace-normal: 11px;
--compact-font-size-monospace-large: 15px;
--compact-font-size-sans-small: 10px;
--compact-font-size-sans-normal: 12px;
--compact-font-size-sans-large: 14px;
--compact-line-height-data: 18px;
--compact-root-font-size: 16px;
/* Comfortable density */
--comfortable-font-size-monospace-small: 10px;
--comfortable-font-size-monospace-normal: 13px;
--comfortable-font-size-monospace-large: 17px;
--comfortable-font-size-sans-small: 12px;
--comfortable-font-size-sans-normal: 14px;
--comfortable-font-size-sans-large: 16px;
--comfortable-line-height-data: 22px;
--comfortable-root-font-size: 20px;
@ -286,4 +28,4 @@
Courier, monospace;
--font-family-sans: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica,
Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol;
}
}