Compare commits

...
3 changed files with 35 additions and 4 deletions
@@ -1,7 +1,11 @@
// scss-lint:disable PropertySortOrder // scss-lint:disable PropertySortOrder
@layer base { @layer base {
// NEXT COLORS START // NEXT COLORS START
:root { // `.light` mirrors `:root` so any subtree can be forced back to the light
// palette even when an ancestor (e.g. `body.dark`) sets the dark tokens —
// used to render the call window in the opposite theme of the product.
:root,
.light {
// slate // slate
--slate-1: 252 252 253; --slate-1: 252 252 253;
--slate-2: 249 249 251; --slate-2: 249 249 251;
@@ -1,5 +1,5 @@
<script setup> <script setup>
import { computed, onBeforeUnmount, ref, watch } from 'vue'; import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue';
import { useRoute, useRouter } from 'vue-router'; import { useRoute, useRouter } from 'vue-router';
import { useStore } from 'vuex'; import { useStore } from 'vuex';
import { useCallSession } from 'dashboard/composables/useCallSession'; import { useCallSession } from 'dashboard/composables/useCallSession';
@@ -232,13 +232,37 @@ watch(
{ immediate: true } { immediate: true }
); );
onBeforeUnmount(stopRingtone); // Render the call window in the opposite theme of the product (light UI while
// the app is dark and vice versa) so an incoming call stands out. The app
// toggles `.dark` on document.body; mirror that here and apply the inverse
// class to the widget root — both the n-* CSS tokens and Tailwind `dark:`
// variants pick up the `.light`/`.dark` boundary.
const isAppDark = ref(false);
const inverseThemeClass = computed(() => (isAppDark.value ? 'light' : 'dark'));
const syncAppTheme = () => {
isAppDark.value = document.body.classList.contains('dark');
};
const themeObserver = new MutationObserver(syncAppTheme);
onMounted(() => {
syncAppTheme();
themeObserver.observe(document.body, {
attributes: true,
attributeFilter: ['class'],
});
});
onBeforeUnmount(() => {
stopRingtone();
themeObserver.disconnect();
});
</script> </script>
<template> <template>
<div <div
v-if="incomingCalls.length || hasActiveCall" v-if="incomingCalls.length || hasActiveCall"
class="fixed ltr:right-4 rtl:left-4 bottom-4 z-50 flex flex-col gap-3 w-[400px]" class="fixed ltr:right-4 rtl:left-4 bottom-4 z-50 flex flex-col gap-3 w-[400px]"
:class="inverseThemeClass"
> >
<!-- Stacked incoming calls (shown above the primary card) --> <!-- Stacked incoming calls (shown above the primary card) -->
<CallCard <CallCard
+4 -1
View File
@@ -20,7 +20,10 @@ const defaultSansFonts = [
]; ];
const tailwindConfig = { const tailwindConfig = {
darkMode: 'class', // `dark:` applies under any `.dark` ancestor, but a nested `.light` boundary
// opts a subtree back out (so descendant `dark:` utilities don't leak into the
// call window when it renders in the inverse/light theme under `body.dark`).
darkMode: ['variant', '&:where(.dark, .dark *):not(:where(.light, .light *))'],
content: [ content: [
'./enterprise/app/views/**/*.erb', './enterprise/app/views/**/*.erb',
'./app/javascript/widget/**/*.vue', './app/javascript/widget/**/*.vue',