Compare commits

...
3 changed files with 35 additions and 4 deletions
@@ -1,7 +1,11 @@
// scss-lint:disable PropertySortOrder
@layer base {
// 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-1: 252 252 253;
--slate-2: 249 249 251;
@@ -1,5 +1,5 @@
<script setup>
import { computed, onBeforeUnmount, ref, watch } from 'vue';
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { useStore } from 'vuex';
import { useCallSession } from 'dashboard/composables/useCallSession';
@@ -232,13 +232,37 @@ watch(
{ 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>
<template>
<div
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="inverseThemeClass"
>
<!-- Stacked incoming calls (shown above the primary card) -->
<CallCard
+4 -1
View File
@@ -20,7 +20,10 @@ const defaultSansFonts = [
];
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: [
'./enterprise/app/views/**/*.erb',
'./app/javascript/widget/**/*.vue',