diff --git a/src/puter-js/src/ui/PuterWebComponent.js b/src/puter-js/src/ui/PuterWebComponent.js
index a369e2144..f7b2a7e1e 100644
--- a/src/puter-js/src/ui/PuterWebComponent.js
+++ b/src/puter-js/src/ui/PuterWebComponent.js
@@ -12,9 +12,64 @@ class PuterWebComponent extends (globalThis.HTMLElement || Object) {
}
connectedCallback () {
+ this._setupThemeWatchers();
+ this._applyTheme();
this._rerender();
}
+ disconnectedCallback () {
+ this._teardownThemeWatchers();
+ }
+
+ _setupThemeWatchers () {
+ if ( typeof globalThis.MutationObserver !== 'undefined' && ! this._themeObserver ) {
+ this._themeObserver = new globalThis.MutationObserver(() => this._applyTheme());
+ this._themeObserver.observe(this, { attributes: true, attributeFilter: ['theme'] });
+ }
+ if ( typeof globalThis.matchMedia === 'function' && ! this._themeMediaQuery ) {
+ this._themeMediaQuery = globalThis.matchMedia('(prefers-color-scheme: dark)');
+ this._themeMediaListener = () => this._applyTheme();
+ if ( this._themeMediaQuery.addEventListener ) {
+ this._themeMediaQuery.addEventListener('change', this._themeMediaListener);
+ }
+ }
+ }
+
+ _teardownThemeWatchers () {
+ if ( this._themeObserver ) {
+ this._themeObserver.disconnect();
+ this._themeObserver = null;
+ }
+ if ( this._themeMediaQuery && this._themeMediaListener && this._themeMediaQuery.removeEventListener ) {
+ this._themeMediaQuery.removeEventListener('change', this._themeMediaListener);
+ }
+ this._themeMediaQuery = null;
+ this._themeMediaListener = null;
+ }
+
+ /**
+ * Resolves the effective theme from the `theme` attribute and the system
+ * preference, then toggles a `.puter-theme-dark` class on the host so
+ * components can style with `:host(.puter-theme-dark) ...`.
+ * theme="dark" → always dark
+ * theme="light" → always light
+ * unset / other → follow system (prefers-color-scheme)
+ */
+ _applyTheme () {
+ if ( typeof this.getAttribute !== 'function' ) return;
+ const themeAttr = this.getAttribute('theme');
+ let isDark;
+ if ( themeAttr === 'dark' ) {
+ isDark = true;
+ } else if ( themeAttr === 'light' ) {
+ isDark = false;
+ } else {
+ isDark = typeof globalThis.matchMedia === 'function'
+ && globalThis.matchMedia('(prefers-color-scheme: dark)').matches;
+ }
+ this.classList.toggle('puter-theme-dark', isDark);
+ }
+
_rerender () {
if ( ! this.shadowRoot ) return;
this.shadowRoot.innerHTML = `${this.render()}`;
diff --git a/src/puter-js/src/ui/components.md b/src/puter-js/src/ui/components.md
index f41cfe792..555eafa6b 100644
--- a/src/puter-js/src/ui/components.md
+++ b/src/puter-js/src/ui/components.md
@@ -340,7 +340,25 @@ All components render inside Shadow DOM and match puter.com's native GUI appeara
## Dark mode
-Components with `@media (prefers-color-scheme: dark)` rules auto-adapt when the OS is in dark mode: ``, ``, ``, ``, ``.
+By default, components that ship dark-mode styles auto-adapt to the OS setting (`prefers-color-scheme: dark`): ``, ``, ``, ``, ``.
+
+To force a specific theme regardless of the OS preference, set the `theme` attribute on the component:
+
+| Value | Behavior |
+|-------|----------|
+| `dark` | Always render in dark theme |
+| `light` | Always render in light theme |
+| _unset_ | Follow the system `prefers-color-scheme` (default) |
+
+```html
+
+
+
+
+
+```
+
+The attribute is live — changing it at runtime re-paints the component immediately. For `` and ``, the `theme` is forwarded to any dropdowns and submenus they spawn, so the whole tree stays in sync.
## Responsive / mobile
diff --git a/src/puter-js/src/ui/components/PuterColorPicker.js b/src/puter-js/src/ui/components/PuterColorPicker.js
index 1f9b84f74..13024ea5c 100644
--- a/src/puter-js/src/ui/components/PuterColorPicker.js
+++ b/src/puter-js/src/ui/components/PuterColorPicker.js
@@ -176,47 +176,45 @@ class PuterColorPicker extends PuterWebComponent {
flex: 1;
}
}
- @media (prefers-color-scheme: dark) {
- .picker-body {
- background-color: rgba(40, 44, 52, .95);
- color: #e6e6e6;
- box-shadow: 0px 0px 15px #000000aa;
- }
- .preview {
- border-color: #555;
- }
- .header-label {
- color: #aaa;
- }
- .hex-input {
- background-color: #1f1f1f;
- border-color: #555;
- color: #e6e6e6;
- }
- .native-color-row {
- background: rgba(255, 255, 255, 0.05);
- border-color: #555;
- }
- .native-color-row label {
- color: #aaa;
- }
- input[type="color"] {
- border-color: #555;
- }
- .swatch {
- border-color: rgba(255, 255, 255, 0.1);
- }
- .btn {
- color: #e6e6e6;
- border-color: #555;
- background: linear-gradient(#4a4a4a, #3a3a3a);
- box-shadow: inset 0px 1px 0px rgb(255 255 255 / 8%), 0 1px 2px rgb(0 0 0 / 25%);
- }
- .btn:active {
- background-color: #333;
- border-color: #444;
- color: #999;
- }
+ :host(.puter-theme-dark) .picker-body {
+ background-color: rgba(40, 44, 52, .95);
+ color: #e6e6e6;
+ box-shadow: 0px 0px 15px #000000aa;
+ }
+ :host(.puter-theme-dark) .preview {
+ border-color: #555;
+ }
+ :host(.puter-theme-dark) .header-label {
+ color: #aaa;
+ }
+ :host(.puter-theme-dark) .hex-input {
+ background-color: #1f1f1f;
+ border-color: #555;
+ color: #e6e6e6;
+ }
+ :host(.puter-theme-dark) .native-color-row {
+ background: rgba(255, 255, 255, 0.05);
+ border-color: #555;
+ }
+ :host(.puter-theme-dark) .native-color-row label {
+ color: #aaa;
+ }
+ :host(.puter-theme-dark) input[type="color"] {
+ border-color: #555;
+ }
+ :host(.puter-theme-dark) .swatch {
+ border-color: rgba(255, 255, 255, 0.1);
+ }
+ :host(.puter-theme-dark) .btn {
+ color: #e6e6e6;
+ border-color: #555;
+ background: linear-gradient(#4a4a4a, #3a3a3a);
+ box-shadow: inset 0px 1px 0px rgb(255 255 255 / 8%), 0 1px 2px rgb(0 0 0 / 25%);
+ }
+ :host(.puter-theme-dark) .btn:active {
+ background-color: #333;
+ border-color: #444;
+ color: #999;
}
`;
}
diff --git a/src/puter-js/src/ui/components/PuterContextMenu.js b/src/puter-js/src/ui/components/PuterContextMenu.js
index 873aa0786..3e4446736 100644
--- a/src/puter-js/src/ui/components/PuterContextMenu.js
+++ b/src/puter-js/src/ui/components/PuterContextMenu.js
@@ -345,99 +345,100 @@ class PuterContextMenu extends PuterWebComponent {
height: 20px;
}
- @media (prefers-color-scheme: dark) {
- .context-menu {
- background: #2d2d2d;
- background-color: rgb(45 45 45 / 94%);
- color: #e6e6e6;
- border-color: #00000080;
- box-shadow: 0px 0px 15px #000000aa;
- }
- .menu-item {
- color: #e6e6e6;
- }
- /* Inactive items: icon/check/shortcut/arrow tones */
- .icon,
- .check {
- color: #e6e6e6;
- }
- .submenu-arrow {
- color: #b0b0b0;
- }
- .shortcut {
- color: #888;
- }
- .icon img {
- filter: drop-shadow(0px 0px 0.3px rgb(230, 230, 230));
- }
- /* Inactive icon SVGs use currentColor already; nothing to invert */
+ /* Dark theme — applied when system prefers dark and no light
+ override is set, or when theme="dark" is forced. The base
+ class toggles .puter-theme-dark on the host accordingly. */
+ :host(.puter-theme-dark) .context-menu {
+ background: #2d2d2d;
+ background-color: rgb(45 45 45 / 94%);
+ color: #e6e6e6;
+ border-color: #00000080;
+ box-shadow: 0px 0px 15px #000000aa;
+ }
+ :host(.puter-theme-dark) .menu-item {
+ color: #e6e6e6;
+ }
+ /* Inactive items: icon/check/shortcut/arrow tones */
+ :host(.puter-theme-dark) .icon,
+ :host(.puter-theme-dark) .check {
+ color: #e6e6e6;
+ }
+ :host(.puter-theme-dark) .submenu-arrow {
+ color: #b0b0b0;
+ }
+ :host(.puter-theme-dark) .shortcut {
+ color: #888;
+ }
+ :host(.puter-theme-dark) .icon img {
+ filter: drop-shadow(0px 0px 0.3px rgb(230, 230, 230));
+ }
+ /* Inactive icon SVGs use currentColor already; nothing to invert */
- /* Safe-triangle / keyboard-nav: non-active hover restored colors should match dark */
- .context-menu.safe-traverse .menu-item:hover:not(.has-open-submenu):not(.focused):not(.disabled):not(.divider),
- .context-menu.keyboard-nav .menu-item:hover:not(.has-open-submenu):not(.focused):not(.disabled):not(.divider) {
- color: #e6e6e6;
- }
- .context-menu.safe-traverse .menu-item:hover:not(.has-open-submenu):not(.focused):not(.disabled):not(.divider) .icon,
- .context-menu.safe-traverse .menu-item:hover:not(.has-open-submenu):not(.focused):not(.disabled):not(.divider) .check,
- .context-menu.safe-traverse .menu-item:hover:not(.has-open-submenu):not(.focused):not(.disabled):not(.divider) .submenu-arrow,
- .context-menu.safe-traverse .menu-item:hover:not(.has-open-submenu):not(.focused):not(.disabled):not(.divider) .label,
- .context-menu.keyboard-nav .menu-item:hover:not(.has-open-submenu):not(.focused):not(.disabled):not(.divider) .icon,
- .context-menu.keyboard-nav .menu-item:hover:not(.has-open-submenu):not(.focused):not(.disabled):not(.divider) .check,
- .context-menu.keyboard-nav .menu-item:hover:not(.has-open-submenu):not(.focused):not(.disabled):not(.divider) .submenu-arrow,
- .context-menu.keyboard-nav .menu-item:hover:not(.has-open-submenu):not(.focused):not(.disabled):not(.divider) .label {
- color: #e6e6e6;
- }
- .context-menu.safe-traverse .menu-item:hover:not(.has-open-submenu):not(.focused):not(.disabled):not(.divider) .shortcut,
- .context-menu.keyboard-nav .menu-item:hover:not(.has-open-submenu):not(.focused):not(.disabled):not(.divider) .shortcut {
- color: #888;
- }
- .context-menu.safe-traverse .menu-item:hover:not(.has-open-submenu):not(.focused):not(.disabled):not(.divider) .icon img,
- .context-menu.keyboard-nav .menu-item:hover:not(.has-open-submenu):not(.focused):not(.disabled):not(.divider) .icon img {
- filter: drop-shadow(0px 0px 0.3px rgb(230, 230, 230));
- }
+ /* Safe-triangle / keyboard-nav: non-active hover restored colors should match dark */
+ :host(.puter-theme-dark) .context-menu.safe-traverse .menu-item:hover:not(.has-open-submenu):not(.focused):not(.disabled):not(.divider),
+ :host(.puter-theme-dark) .context-menu.keyboard-nav .menu-item:hover:not(.has-open-submenu):not(.focused):not(.disabled):not(.divider) {
+ color: #e6e6e6;
+ }
+ :host(.puter-theme-dark) .context-menu.safe-traverse .menu-item:hover:not(.has-open-submenu):not(.focused):not(.disabled):not(.divider) .icon,
+ :host(.puter-theme-dark) .context-menu.safe-traverse .menu-item:hover:not(.has-open-submenu):not(.focused):not(.disabled):not(.divider) .check,
+ :host(.puter-theme-dark) .context-menu.safe-traverse .menu-item:hover:not(.has-open-submenu):not(.focused):not(.disabled):not(.divider) .submenu-arrow,
+ :host(.puter-theme-dark) .context-menu.safe-traverse .menu-item:hover:not(.has-open-submenu):not(.focused):not(.disabled):not(.divider) .label,
+ :host(.puter-theme-dark) .context-menu.keyboard-nav .menu-item:hover:not(.has-open-submenu):not(.focused):not(.disabled):not(.divider) .icon,
+ :host(.puter-theme-dark) .context-menu.keyboard-nav .menu-item:hover:not(.has-open-submenu):not(.focused):not(.disabled):not(.divider) .check,
+ :host(.puter-theme-dark) .context-menu.keyboard-nav .menu-item:hover:not(.has-open-submenu):not(.focused):not(.disabled):not(.divider) .submenu-arrow,
+ :host(.puter-theme-dark) .context-menu.keyboard-nav .menu-item:hover:not(.has-open-submenu):not(.focused):not(.disabled):not(.divider) .label {
+ color: #e6e6e6;
+ }
+ :host(.puter-theme-dark) .context-menu.safe-traverse .menu-item:hover:not(.has-open-submenu):not(.focused):not(.disabled):not(.divider) .shortcut,
+ :host(.puter-theme-dark) .context-menu.keyboard-nav .menu-item:hover:not(.has-open-submenu):not(.focused):not(.disabled):not(.divider) .shortcut {
+ color: #888;
+ }
+ :host(.puter-theme-dark) .context-menu.safe-traverse .menu-item:hover:not(.has-open-submenu):not(.focused):not(.disabled):not(.divider) .icon img,
+ :host(.puter-theme-dark) .context-menu.keyboard-nav .menu-item:hover:not(.has-open-submenu):not(.focused):not(.disabled):not(.divider) .icon img {
+ filter: drop-shadow(0px 0px 0.3px rgb(230, 230, 230));
+ }
- /* Submenu-open parent (no hover): subtle dark highlight */
- .menu-item.has-open-submenu:not(:hover) {
- background-color: #3f3f3f;
- color: #e6e6e6;
- }
- .menu-item.has-open-submenu:not(:hover) .icon,
- .menu-item.has-open-submenu:not(:hover) .icon svg,
- .menu-item.has-open-submenu:not(:hover) .icon img {
- color: #e6e6e6;
- }
+ /* Submenu-open parent (no hover): subtle dark highlight */
+ :host(.puter-theme-dark) .menu-item.has-open-submenu:not(:hover) {
+ background-color: #3f3f3f;
+ color: #e6e6e6;
+ }
+ :host(.puter-theme-dark) .menu-item.has-open-submenu:not(:hover) .icon,
+ :host(.puter-theme-dark) .menu-item.has-open-submenu:not(:hover) .icon svg,
+ :host(.puter-theme-dark) .menu-item.has-open-submenu:not(:hover) .icon img {
+ color: #e6e6e6;
+ }
- /* Danger items */
- .menu-item.danger,
- .menu-item.danger .icon {
- color: #ff7b72;
- }
+ /* Danger items */
+ :host(.puter-theme-dark) .menu-item.danger,
+ :host(.puter-theme-dark) .menu-item.danger .icon {
+ color: #ff7b72;
+ }
- /* Divider */
- .divider hr {
- background: #444;
- }
+ /* Divider */
+ :host(.puter-theme-dark) .divider hr {
+ background: #444;
+ }
- /* Sheet mode (mobile) */
- :host(.sheet-mode) .context-menu {
- background-color: rgb(40 40 40 / 96%);
- box-shadow: 0 -6px 24px rgba(0, 0, 0, 0.45);
- }
- :host(.sheet-mode) .menu-item:hover:not(.disabled):not(.divider) {
- background-color: rgba(0, 122, 255, 0.22);
- }
- :host(.sheet-mode) .menu-item:active:not(.disabled):not(.divider) {
- background-color: rgba(0, 122, 255, 0.35);
- }
- :host(.sheet-mode) .menu-item:hover:not(.disabled):not(.divider) .label,
- :host(.sheet-mode) .menu-item:hover:not(.disabled):not(.divider) .icon,
- :host(.sheet-mode) .menu-item:active:not(.disabled):not(.divider) .label,
- :host(.sheet-mode) .menu-item:active:not(.disabled):not(.divider) .icon {
- color: #e6e6e6;
- }
- :host(.sheet-mode) .divider hr {
- background: rgba(255, 255, 255, 0.15);
- }
+ /* Sheet mode (mobile) */
+ :host(.puter-theme-dark.sheet-mode) .context-menu {
+ background-color: rgb(40 40 40 / 96%);
+ box-shadow: 0 -6px 24px rgba(0, 0, 0, 0.45);
+ }
+ :host(.puter-theme-dark.sheet-mode) .menu-item:hover:not(.disabled):not(.divider) {
+ background-color: rgba(0, 122, 255, 0.22);
+ }
+ :host(.puter-theme-dark.sheet-mode) .menu-item:active:not(.disabled):not(.divider) {
+ background-color: rgba(0, 122, 255, 0.35);
+ }
+ :host(.puter-theme-dark.sheet-mode) .menu-item:hover:not(.disabled):not(.divider) .label,
+ :host(.puter-theme-dark.sheet-mode) .menu-item:hover:not(.disabled):not(.divider) .icon,
+ :host(.puter-theme-dark.sheet-mode) .menu-item:active:not(.disabled):not(.divider) .label,
+ :host(.puter-theme-dark.sheet-mode) .menu-item:active:not(.disabled):not(.divider) .icon {
+ color: #e6e6e6;
+ }
+ :host(.puter-theme-dark.sheet-mode) .divider hr {
+ background: rgba(255, 255, 255, 0.15);
}
`;
}
@@ -930,6 +931,9 @@ class PuterContextMenu extends PuterWebComponent {
const submenu = document.createElement('puter-context-menu');
submenu.setAttribute('data-submenu', '');
submenu.setAttribute('data-parent-managed', '');
+ // Forward any forced theme so the submenu paints in the same theme.
+ const themeAttr = this.getAttribute('theme');
+ if ( themeAttr ) submenu.setAttribute('theme', themeAttr);
submenu.items = items;
submenu._parentMenu = this;
submenu._parentItemEl = parentEl;
@@ -1184,6 +1188,7 @@ class PuterContextMenu extends PuterWebComponent {
}
disconnectedCallback () {
+ super.disconnectedCallback();
if ( this._outsideClickHandler ) {
document.removeEventListener('pointerdown', this._outsideClickHandler, true);
}
diff --git a/src/puter-js/src/ui/components/PuterFontPicker.js b/src/puter-js/src/ui/components/PuterFontPicker.js
index 438dd8cc5..dfecaa8f0 100644
--- a/src/puter-js/src/ui/components/PuterFontPicker.js
+++ b/src/puter-js/src/ui/components/PuterFontPicker.js
@@ -185,50 +185,48 @@ class PuterFontPicker extends PuterWebComponent {
flex: 1;
}
}
- @media (prefers-color-scheme: dark) {
- .picker-body {
- background-color: rgba(40, 44, 52, .95);
- color: #e6e6e6;
- box-shadow: 0px 0px 15px #000000aa;
- }
- .title {
- color: #e6e6e6;
- text-shadow: none;
- }
- .search {
- background-color: #1f1f1f;
- border-color: #555;
- color: #e6e6e6;
- }
- .font-list {
- background-color: #1f1f1f;
- border-color: #555;
- }
- .font-item {
- color: #e6e6e6;
- }
- .font-item:hover {
- background: rgba(255, 255, 255, 0.06);
- }
- .font-name-label {
- color: #888;
- }
- .preview {
- background: #1f1f1f;
- border-color: #555;
- color: #e6e6e6;
- }
- .btn {
- color: #e6e6e6;
- border-color: #555;
- background: linear-gradient(#4a4a4a, #3a3a3a);
- box-shadow: inset 0px 1px 0px rgb(255 255 255 / 8%), 0 1px 2px rgb(0 0 0 / 25%);
- }
- .btn:active {
- background-color: #333;
- border-color: #444;
- color: #999;
- }
+ :host(.puter-theme-dark) .picker-body {
+ background-color: rgba(40, 44, 52, .95);
+ color: #e6e6e6;
+ box-shadow: 0px 0px 15px #000000aa;
+ }
+ :host(.puter-theme-dark) .title {
+ color: #e6e6e6;
+ text-shadow: none;
+ }
+ :host(.puter-theme-dark) .search {
+ background-color: #1f1f1f;
+ border-color: #555;
+ color: #e6e6e6;
+ }
+ :host(.puter-theme-dark) .font-list {
+ background-color: #1f1f1f;
+ border-color: #555;
+ }
+ :host(.puter-theme-dark) .font-item {
+ color: #e6e6e6;
+ }
+ :host(.puter-theme-dark) .font-item:hover {
+ background: rgba(255, 255, 255, 0.06);
+ }
+ :host(.puter-theme-dark) .font-name-label {
+ color: #888;
+ }
+ :host(.puter-theme-dark) .preview {
+ background: #1f1f1f;
+ border-color: #555;
+ color: #e6e6e6;
+ }
+ :host(.puter-theme-dark) .btn {
+ color: #e6e6e6;
+ border-color: #555;
+ background: linear-gradient(#4a4a4a, #3a3a3a);
+ box-shadow: inset 0px 1px 0px rgb(255 255 255 / 8%), 0 1px 2px rgb(0 0 0 / 25%);
+ }
+ :host(.puter-theme-dark) .btn:active {
+ background-color: #333;
+ border-color: #444;
+ color: #999;
}
`;
}
diff --git a/src/puter-js/src/ui/components/PuterMenubar.js b/src/puter-js/src/ui/components/PuterMenubar.js
index 6997f10cc..0104d678a 100644
--- a/src/puter-js/src/ui/components/PuterMenubar.js
+++ b/src/puter-js/src/ui/components/PuterMenubar.js
@@ -91,22 +91,23 @@ class PuterMenubar extends PuterWebComponent {
flex-shrink: 0;
}
}
- @media (prefers-color-scheme: dark) {
- .menubar {
- background-color: #2a2a2a;
- border-bottom-color: #3a3a3a;
- }
- .menu-button {
- color: #e6e6e6;
- }
- .menu-button:hover,
- .menu-button.active,
- .menu-button.focused {
- background-color: #3a3a3a;
- }
- .menubar.keyboard-nav .menu-button:hover:not(.focused):not(.active) {
- background-color: transparent;
- }
+ /* Dark theme — applied when system prefers dark and no light
+ override is set, or when theme="dark" is forced. The base
+ class toggles .puter-theme-dark on the host accordingly. */
+ :host(.puter-theme-dark) .menubar {
+ background-color: #2a2a2a;
+ border-bottom-color: #3a3a3a;
+ }
+ :host(.puter-theme-dark) .menu-button {
+ color: #e6e6e6;
+ }
+ :host(.puter-theme-dark) .menu-button:hover,
+ :host(.puter-theme-dark) .menu-button.active,
+ :host(.puter-theme-dark) .menu-button.focused {
+ background-color: #3a3a3a;
+ }
+ :host(.puter-theme-dark) .menubar.keyboard-nav .menu-button:hover:not(.focused):not(.active) {
+ background-color: transparent;
}
`;
}
@@ -352,6 +353,9 @@ class PuterMenubar extends PuterWebComponent {
const rect = buttonEl.getBoundingClientRect();
const dropdown = document.createElement('puter-context-menu');
dropdown.setAttribute('data-submenu', ''); // skip mobile sheet behavior
+ // Forward any forced theme so the dropdown paints in the same theme.
+ const themeAttr = this.getAttribute('theme');
+ if ( themeAttr ) dropdown.setAttribute('theme', themeAttr);
dropdown.items = item.items;
dropdown.setAttribute('x', String(rect.left));
dropdown.setAttribute('y', String(rect.bottom));
@@ -410,6 +414,7 @@ class PuterMenubar extends PuterWebComponent {
}
disconnectedCallback () {
+ super.disconnectedCallback();
this._closeDropdown();
clearTimeout(this._suppressClickTimer);
this._suppressClickFor = null;
diff --git a/src/puter-js/src/ui/components/PuterNotification.js b/src/puter-js/src/ui/components/PuterNotification.js
index 869fa65a7..9fc72f5a3 100644
--- a/src/puter-js/src/ui/components/PuterNotification.js
+++ b/src/puter-js/src/ui/components/PuterNotification.js
@@ -175,30 +175,28 @@ class PuterNotification extends PuterWebComponent {
width: auto;
}
}
- @media (prefers-color-scheme: dark) {
- .notification {
- background: #2d2d2dcd;
- border-color: #3a3a3a;
- box-shadow: 0px 0px 17px -6px #000;
- }
- .close-btn {
- background: #3a3a3a;
- color: #ccc;
- filter: drop-shadow(0px 0px 0.5px rgb(230, 230, 230));
- }
- .close-btn:hover {
- background: #4a4a4a;
- color: #fff;
- }
- .icon-area {
- filter: drop-shadow(0px 0px 0.5px rgb(230, 230, 230));
- }
- .title {
- color: #e6e6e6;
- }
- .text {
- color: #b0b0b0;
- }
+ :host(.puter-theme-dark) .notification {
+ background: #2d2d2dcd;
+ border-color: #3a3a3a;
+ box-shadow: 0px 0px 17px -6px #000;
+ }
+ :host(.puter-theme-dark) .close-btn {
+ background: #3a3a3a;
+ color: #ccc;
+ filter: drop-shadow(0px 0px 0.5px rgb(230, 230, 230));
+ }
+ :host(.puter-theme-dark) .close-btn:hover {
+ background: #4a4a4a;
+ color: #fff;
+ }
+ :host(.puter-theme-dark) .icon-area {
+ filter: drop-shadow(0px 0px 0.5px rgb(230, 230, 230));
+ }
+ :host(.puter-theme-dark) .title {
+ color: #e6e6e6;
+ }
+ :host(.puter-theme-dark) .text {
+ color: #b0b0b0;
}
`;
}
@@ -280,6 +278,7 @@ class PuterNotification extends PuterWebComponent {
}
disconnectedCallback () {
+ super.disconnectedCallback();
if ( this._dismissTimer ) clearTimeout(this._dismissTimer);
const idx = activeNotifications.indexOf(this);
if ( idx !== -1 ) activeNotifications.splice(idx, 1);