Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DevTools] Refactor imperative theme code #21900

Closed
bvaughn opened this issue Jul 16, 2021 · 5 comments
Closed

[DevTools] Refactor imperative theme code #21900

bvaughn opened this issue Jul 16, 2021 · 5 comments

Comments

@bvaughn
Copy link
Contributor

bvaughn commented Jul 16, 2021

DevTools defaults a "light" and a "dark" theme in a root-level CSS file:

: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-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-shadow: rgba(0, 0, 0, 0.25);
--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;
/* 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,.9);
--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-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-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.9);
--dark-color-tooltip-text: #000000;

The current theme value ("light", "dark", or "auto") is stored in the ThemeContext and persisted between sessions using the localStorage API:

const [theme, setTheme] = useLocalStorage<Theme>(
'React::DevTools::theme',
'auto',
);

When the theme changes, a layout effect updates root-level CSS variables:

useLayoutEffect(() => {
switch (theme) {
case 'light':
updateThemeVariables('light', documentElements);
break;
case 'dark':
updateThemeVariables('dark', documentElements);
break;
case 'auto':
updateThemeVariables(browserTheme, documentElements);
break;
default:
throw Error(`Unsupported theme value "${theme}"`);
}
}, [browserTheme, theme, documentElements]);

This has an unfortunate side effect of creating a race condition between any other imperative code (e.g. a Canvas rendering component) that may want to read the current CSS variables. This code cannot run before the code above or it will read previous/stale values.

We should refactor this code to declaratively share theme variables to its subtree, e.g.:

function ThemeProvider({ value, children }) {
  // ...

  return (
    <ThemeContext.Provider value={theme}>
      <div
        style={{
          '--color-attribute-name': '#ef6632',
          '--color-attribute-name-not-editable': '#23272f',
          // ...
        }}
      >
        {children}
      </div>
    </ThemeContext.Provider>
  );
}

We will also need to update any place that reads these computed styles, e.g.

// 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));
const fontSize = computedStyle.getPropertyValue(
`--${displayDensity}-root-font-size`,
);
const root = ((document.querySelector(':root'): any): HTMLElement);
root.style.fontSize = fontSize;

A similar approach is used for font size ("comfortable" or "compact"). We should do the same refactor for this.

@houssemchebeb
Copy link
Contributor

Hi @bvaughn, can i take this ?

@bvaughn
Copy link
Contributor Author

bvaughn commented Jul 17, 2021

This issue is all yours @houssemchebeb 😄

I've added the "good first issue (taken)" label so that others will know not to start work on the issue. If you change your mind about the issue, no worries! Just let me know so that I can remove the label and free it up for someone else to claim.

Cheers!

@houssemchebeb
Copy link
Contributor

Thanks :)

@NishantChandla
Copy link

NishantChandla commented Jul 23, 2021

@houssemchebeb Is this issue up for grabs? Do you mind if i take over

@houssemchebeb
Copy link
Contributor

@NishantChandla I am already working on this issue sorry

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants