Skip to content

feat(widgets): ThemeWidget#9471

Merged
ibgreen merged 14 commits intomasterfrom
ib/theme-widget
Mar 28, 2025
Merged

feat(widgets): ThemeWidget#9471
ibgreen merged 14 commits intomasterfrom
ib/theme-widget

Conversation

@ibgreen
Copy link
Copy Markdown
Collaborator

@ibgreen ibgreen commented Feb 27, 2025

Closes #

Background

  • A new ThemeWidget that switches between light and dark mode.
    image
    image

Change List

Note: Setting the themes using Deck level styles has no effect. My hypothesis is that the theme variables are too nested under specific classes?

@ibgreen ibgreen requested a review from chrisgervang February 27, 2025 17:57
@coveralls
Copy link
Copy Markdown

coveralls commented Feb 27, 2025

Coverage Status

coverage: 91.631% (+0.003%) from 91.628%
when pulling bef6261 on ib/theme-widget
into ce6e844 on master.

@ibgreen ibgreen mentioned this pull request Mar 4, 2025
62 tasks
Copy link
Copy Markdown
Collaborator

@chrisgervang chrisgervang left a comment

Choose a reason for hiding this comment

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

Deck's DOM structure can vary depending on the environment, but I'm assuming this is much is always true:

<deck-root <!-- deck's style prop -->>
  <widget-container>
    <view-container <!-- class='dark-theme' is applied here -->>
      <widget <!-- widget's style prop -->/>
    </view-container>
    <!-- other views -->
  </widget-container>
</deck-root>

ThemeWidget is close but is ambiguous about an important behavior: Does the widget's theme apply to all widgets or just the widget's neighboring it within the same view? dark-theme's position in the DOM implies the latter, but querySelectorAll implies the former.

I discuss how either could be implemented in my comments, but I'm in favor of the the simpler direction: apply the theme to all widgets (<widget-container>).

I think it's important that this widget follows CSS cascading norms.. Namely,

  • deck.setProps({ style: ...}) cannot overwrite what this widget sets, because this widgets are lower in the hierarchy then the deck root.
  • but a widget's style prop should overwrite what this widget sets. (e.g. new FullscreenWidget({ style: ... }) would overwrite the ThemeWidget's effect on itself.

/**
* A Deck Theme is a set of CSS variables that control CSS styling of the official widgets.
*/
export type DeckWidgetTheme = {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Nice!

Comment thread modules/widgets/src/theme-widget.tsx Outdated
Comment thread modules/widgets/src/theme-widget.tsx Outdated
Comment thread modules/widgets/src/theme-widget.tsx Outdated
Comment on lines +124 to +126
this.deck?.setProps({
style: this.darkMode ? this.props.darkModeTheme : this.props.lightModeTheme
});
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
this.deck?.setProps({
style: this.darkMode ? this.props.darkModeTheme : this.props.lightModeTheme
});

We shouldn't set deck props in widgets (user's will overwrite it).

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

We shouldn't set deck props in widgets (user's will overwrite it).

Let's sort that out in a second PR

Comment thread modules/widgets/src/theme-widget.tsx Outdated

The `id` must be unique among all your widgets at a given time. It's recommended to set `id` explicitly if you have multipe widgets of a given thpe.

#### `placement` (string, optional) {#placement}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Just a note of context: Similar to FullscreenWidget, if we're deciding themes apply to all widgets then we'd omit the viewId prop (leave this as-is).

viewId is on compass and zoom widgets to scope the widget to a specific view, useful to get unique controls when you have a MapView side-by-side with a GlobeView, for example.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Just a note of context: Similar to FullscreenWidget, if we're deciding themes apply to all widgets then we'd omit the viewId prop (leave this as-is).

Yes, I am going with the global theme approach.

(For what its worth. I was initially incorrectly assuming the viewId allowed the user to position the widget in a certain view. Perhaps a potential point of confusion for users.)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

viewId does position it, and that could be all it does.

So far though I haven't seen feedback to indicate the need for position global widgets (Fullscreen, Screenshot, and Theme)

Comment thread docs/api-reference/widgets/theme-widget.md
this.element = el;
this.update();
return el;
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Insert initialTheme's effect in onAdd?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Insert initialTheme's effect in onAdd?

Not sure I understand.

render(ui, element);
}

setProps(props: Partial<ThemeWidgetProps>) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

If we decide to scope the theme, ThemeWidget#setProps would need to account for the widget moving views. We'd need to unset all of the theme variables on the old view, and apply them in the new one.

Needless to say, it's simpler to just apply themes equally to all widgets.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Needless to say, it's simpler to just apply themes equally to all widgets.

We are going with global themes.

ibgreen and others added 4 commits March 28, 2025 11:33
Co-authored-by: Chris Gervang <chrisgervang@users.noreply.github.com>
Co-authored-by: Chris Gervang <chrisgervang@users.noreply.github.com>
Copy link
Copy Markdown
Collaborator Author

@ibgreen ibgreen left a comment

Choose a reason for hiding this comment

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

Addressed the comments that do not relate directly to the mechanics of updating the CSS variables.

One change I made is that I moved the documentation of widget specific CSS variables (currently just icons) into each widget doc.

I feel that the variables associated with the theme should just be the general variables.

We can still have CSS variables for each widget to allow some extra control but they should specific to the widget (I would prefer if they were also scoped to the widgets).


The `id` must be unique among all your widgets at a given time. It's recommended to set `id` explicitly if you have multipe widgets of a given thpe.

#### `placement` (string, optional) {#placement}
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Just a note of context: Similar to FullscreenWidget, if we're deciding themes apply to all widgets then we'd omit the viewId prop (leave this as-is).

Yes, I am going with the global theme approach.

(For what its worth. I was initially incorrectly assuming the viewId allowed the user to position the widget in a certain view. Perhaps a potential point of confusion for users.)

viewContainer.style.pointerEvents = 'none';
viewContainer.style.position = 'absolute';
viewContainer.style.overflow = 'hidden';
viewContainer.classList.add('deck-theme');
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

But if we instead are deciding we want themes to apply to all widgets, we'd instead add

I will leave this for a second PR as I have additional thoughts on it and focus on getting the rest of this PR clean

this.element = el;
this.update();
return el;
}
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Insert initialTheme's effect in onAdd?

Not sure I understand.

render(ui, element);
}

setProps(props: Partial<ThemeWidgetProps>) {
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Needless to say, it's simpler to just apply themes equally to all widgets.

We are going with global themes.

Comment on lines +124 to +126
this.deck?.setProps({
style: this.darkMode ? this.props.darkModeTheme : this.props.lightModeTheme
});
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

We shouldn't set deck props in widgets (user's will overwrite it).

Let's sort that out in a second PR

Comment on lines +136 to +140
// Note: deck does not support updating the style property
// this.deck?.setProps({
// style: themeStyle
// });
const elements = document.querySelectorAll('.deck-theme');
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I think closest is what we want to use from within a widget.

Agreed but will leave this for second PR to avoid bike shedding here.

Copy link
Copy Markdown
Collaborator

@chrisgervang chrisgervang left a comment

Choose a reason for hiding this comment

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

Stamping so we can experimentally merge this in. To graduate I'll take a pass on CSS mechanics.

Comment thread modules/widgets/src/stylesheet.css
Comment on lines +1 to +12
.deck-theme {
--widget-margin: 12px;
--button-stroke: rgba(255, 255, 255, 0.3);
--button-corner-radius: 8px;
--button-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.25);
--button-size: 28px;
--button-background: #616166;
--button-backdrop-filter: unset;
--button-inner-stroke: unset;
--button-icon-hover: rgb(24, 24, 26);
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I don't want to maintain this approach. It's another source of truth that can get out of sync, and is redundant to the JS definitions

Comment thread modules/widgets/src/stylesheet.css
@ibgreen ibgreen merged commit 66bedf6 into master Mar 28, 2025
4 checks passed
@ibgreen ibgreen deleted the ib/theme-widget branch March 29, 2025 14:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants