Compare commits
7 Commits
9bf56d5748
...
6ace96f2cf
| Author | SHA1 | Date | |
|---|---|---|---|
| 6ace96f2cf | |||
| 2d71f2ce30 | |||
| 2c3dba55e6 | |||
| c7a04dcc70 | |||
| 4b14c15518 | |||
| c68ef346bf | |||
| c5d7fcc303 |
@@ -72,16 +72,6 @@ Implemented and gate-green; confirm each per `LOTUS_TESTING.md`, then delete the
|
|||||||
- **Architecture notes (low priority):** deep `features/` + `hooks/` nesting, many small coupled hooks, possible dead CSS/components, `SpacingVariant` / `DropTarget` recipe simplification.
|
- **Architecture notes (low priority):** deep `features/` + `hooks/` nesting, many small coupled hooks, possible dead CSS/components, `SpacingVariant` / `DropTarget` recipe simplification.
|
||||||
- **Git workflow (forward-looking):** keep commits scoped — past monolithic "fix all bugs" commits and inconsistent prefixes hurt `git bisect`.
|
- **Git workflow (forward-looking):** keep commits scoped — past monolithic "fix all bugs" commits and inconsistent prefixes hurt `git bisect`.
|
||||||
|
|
||||||
### Native-Cinny polish (remaining from the design-law audit)
|
|
||||||
|
|
||||||
The "renders-broken-on-stock-themes" cluster (ungated invented CSS vars across
|
|
||||||
~13 files + the toast rebuild) is fixed; Sentry was removed. Lower-priority
|
|
||||||
pattern items left:
|
|
||||||
|
|
||||||
- **Profile timezone `<select>`** (`settings/account/Profile.tsx`) — still a raw native select (`colorScheme:'dark'`); it's wired to native form submission + a disabled state, so converting to `SettingsSelect` needs care.
|
|
||||||
- **MediaGallery lightbox** (`room/MediaGallery.tsx`) — raw `<div role="dialog">` + `#fff`/rgba chrome over forced-black media. Should be folds `Overlay`/`Modal`; the over-media light-on-dark scheme is a borderline-justified scrim.
|
|
||||||
- **Nits:** scattered `opacity:` → `priority`, the poll `✓` Unicode glyph → folds `Icon`, a few `zIndex` magic numbers.
|
|
||||||
|
|
||||||
### Big Projects
|
### Big Projects
|
||||||
|
|
||||||
- **#5 — Seasonal themes & chat-background redesign.** Current backgrounds are basic CSS; goal is high-fidelity, research-backed, GPU-accelerated designs (layered `oklch`, `backdrop-filter`, `contain:paint`) with WCAG-AA overlay contrast. Treat each as its own design sprint.
|
- **#5 — Seasonal themes & chat-background redesign.** Current backgrounds are basic CSS; goal is high-fidelity, research-backed, GPU-accelerated designs (layered `oklch`, `backdrop-filter`, `contain:paint`) with WCAG-AA overlay contrast. Treat each as its own design sprint.
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ import { getRoomPermissionsAPI } from '../hooks/useRoomPermissions';
|
|||||||
import { useLivekitSupport } from '../hooks/useLivekitSupport';
|
import { useLivekitSupport } from '../hooks/useLivekitSupport';
|
||||||
import { CallAvatarAnimation } from '../styles/Animations.css';
|
import { CallAvatarAnimation } from '../styles/Animations.css';
|
||||||
import { webRTCSupported } from '../utils/rtc';
|
import { webRTCSupported } from '../utils/rtc';
|
||||||
|
import { zIndices } from '../styles/zIndex';
|
||||||
|
|
||||||
const PIP_MIN_W = 200;
|
const PIP_MIN_W = 200;
|
||||||
const PIP_MIN_H = 112;
|
const PIP_MIN_H = 112;
|
||||||
@@ -323,7 +324,7 @@ function IncomingCallBanner({ dm, info, onIgnore, onAnswer, onReject }: Incoming
|
|||||||
position: 'fixed',
|
position: 'fixed',
|
||||||
top: config.space.S400,
|
top: config.space.S400,
|
||||||
right: config.space.S400,
|
right: config.space.S400,
|
||||||
zIndex: 9990,
|
zIndex: zIndices.inCallBanner,
|
||||||
width: toRem(300),
|
width: toRem(300),
|
||||||
maxWidth: `calc(100vw - 2 * ${config.space.S400})`,
|
maxWidth: `calc(100vw - 2 * ${config.space.S400})`,
|
||||||
padding: config.space.S300,
|
padding: config.space.S300,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import React, { useCallback, useEffect, useState } from 'react';
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
import { Box, color, config, Text, toRem } from 'folds';
|
import { Box, color, config, Icon, Icons, Text, toRem } from 'folds';
|
||||||
import { RelationsEvent } from 'matrix-js-sdk/lib/models/relations';
|
import { RelationsEvent } from 'matrix-js-sdk/lib/models/relations';
|
||||||
import { RoomEvent } from 'matrix-js-sdk';
|
import { RoomEvent } from 'matrix-js-sdk';
|
||||||
import { useMatrixClient } from '../../../hooks/useMatrixClient';
|
import { useMatrixClient } from '../../../hooks/useMatrixClient';
|
||||||
@@ -339,11 +339,7 @@ export function PollContent({
|
|||||||
transition: 'all 0.15s',
|
transition: 'all 0.15s',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{selected && isMultiple ? (
|
{selected && isMultiple ? <Icon size="50" src={Icons.Check} /> : null}
|
||||||
<Text as="span" size="T200" style={{ lineHeight: 1 }}>
|
|
||||||
✓
|
|
||||||
</Text>
|
|
||||||
) : null}
|
|
||||||
</span>
|
</span>
|
||||||
<Text as="span" size="T300" style={{ flexGrow: 1 }}>
|
<Text as="span" size="T300" style={{ flexGrow: 1 }}>
|
||||||
{text}
|
{text}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { useAtomValue } from 'jotai';
|
import { useAtomValue } from 'jotai';
|
||||||
import { settingsAtom } from '../../state/settings';
|
import { settingsAtom } from '../../state/settings';
|
||||||
|
import { zIndices } from '../../styles/zIndex';
|
||||||
import {
|
import {
|
||||||
animSeasonFall,
|
animSeasonFall,
|
||||||
animLeafFall,
|
animLeafFall,
|
||||||
@@ -758,7 +759,7 @@ function SeasonalOverlay({ theme, reduced }: { theme: SeasonTheme; reduced: bool
|
|||||||
pointerEvents: 'none',
|
pointerEvents: 'none',
|
||||||
// Below the Night Light overlay (9998) so seasonal particles are tinted
|
// Below the Night Light overlay (9998) so seasonal particles are tinted
|
||||||
// by it, and below modals (9999) so dialogs are never obscured.
|
// by it, and below modals (9999) so dialogs are never obscured.
|
||||||
zIndex: 9997,
|
zIndex: zIndices.seasonalEffect,
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -18,11 +18,13 @@ export function SettingsSelect<T extends string>({
|
|||||||
value,
|
value,
|
||||||
options,
|
options,
|
||||||
onChange,
|
onChange,
|
||||||
|
disabled,
|
||||||
'aria-label': ariaLabel,
|
'aria-label': ariaLabel,
|
||||||
}: {
|
}: {
|
||||||
value: T;
|
value: T;
|
||||||
options: SettingsSelectOption<T>[];
|
options: SettingsSelectOption<T>[];
|
||||||
onChange: (v: T) => void;
|
onChange: (v: T) => void;
|
||||||
|
disabled?: boolean;
|
||||||
'aria-label'?: string;
|
'aria-label'?: string;
|
||||||
}) {
|
}) {
|
||||||
const [menuCords, setMenuCords] = useState<RectCords>();
|
const [menuCords, setMenuCords] = useState<RectCords>();
|
||||||
@@ -47,6 +49,7 @@ export function SettingsSelect<T extends string>({
|
|||||||
radii="300"
|
radii="300"
|
||||||
after={<Icon size="300" src={Icons.ChevronBottom} />}
|
after={<Icon size="300" src={Icons.ChevronBottom} />}
|
||||||
onClick={handleMenu}
|
onClick={handleMenu}
|
||||||
|
disabled={disabled}
|
||||||
aria-label={ariaLabel}
|
aria-label={ariaLabel}
|
||||||
aria-haspopup="menu"
|
aria-haspopup="menu"
|
||||||
aria-expanded={!!menuCords}
|
aria-expanded={!!menuCords}
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ function EncryptedRoomCachePanel({ roomIds, onLoaded }: EncryptedRoomCachePanelP
|
|||||||
<Text size="T300" truncate>
|
<Text size="T300" truncate>
|
||||||
{room.name}
|
{room.name}
|
||||||
</Text>
|
</Text>
|
||||||
<Text size="T200" style={{ opacity: 0.55 }}>
|
<Text size="T200" priority="300">
|
||||||
{msgEvents.length > 0
|
{msgEvents.length > 0
|
||||||
? `${msgEvents.length} messages cached · oldest: ${new Date(oldest!.getTs()).toLocaleDateString()}`
|
? `${msgEvents.length} messages cached · oldest: ${new Date(oldest!.getTs()).toLocaleDateString()}`
|
||||||
: 'No messages cached yet'}
|
: 'No messages cached yet'}
|
||||||
@@ -153,7 +153,7 @@ function EncryptedRoomCachePanel({ roomIds, onLoaded }: EncryptedRoomCachePanelP
|
|||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{!canLoadMore && events.length > 0 && (
|
{!canLoadMore && events.length > 0 && (
|
||||||
<Text size="T200" style={{ opacity: 0.5, flexShrink: 0 }}>
|
<Text size="T200" priority="300" style={{ flexShrink: 0 }}>
|
||||||
Fully cached
|
Fully cached
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
@@ -656,7 +656,7 @@ export function MessageSearch({
|
|||||||
<Icon size="200" src={senderOnlyMode ? Icons.User : Icons.Lock} />
|
<Icon size="200" src={senderOnlyMode ? Icons.User : Icons.Lock} />
|
||||||
<Text size="H5">{senderOnlyMode ? 'Messages from user' : 'Encrypted Rooms'}</Text>
|
<Text size="H5">{senderOnlyMode ? 'Messages from user' : 'Encrypted Rooms'}</Text>
|
||||||
{!senderOnlyMode && (
|
{!senderOnlyMode && (
|
||||||
<Text size="T200" style={{ opacity: 0.55 }}>
|
<Text size="T200" priority="300">
|
||||||
{`${localResult.searchedRoomsCount} / ${localResult.encryptedRoomsCount} cached`}
|
{`${localResult.searchedRoomsCount} / ${localResult.encryptedRoomsCount} cached`}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -280,7 +280,8 @@ export function SearchInput({
|
|||||||
<Text
|
<Text
|
||||||
size="T200"
|
size="T200"
|
||||||
truncate
|
truncate
|
||||||
style={{ opacity: 0.6, fontFamily: 'monospace', fontSize: '0.75em' }}
|
priority="300"
|
||||||
|
style={{ fontFamily: 'monospace', fontSize: '0.75em' }}
|
||||||
>
|
>
|
||||||
{user.userId}
|
{user.userId}
|
||||||
</Text>
|
</Text>
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import {
|
|||||||
Icon,
|
Icon,
|
||||||
IconButton,
|
IconButton,
|
||||||
Icons,
|
Icons,
|
||||||
|
Overlay,
|
||||||
|
OverlayBackdrop,
|
||||||
Scroll,
|
Scroll,
|
||||||
Spinner,
|
Spinner,
|
||||||
Text,
|
Text,
|
||||||
@@ -15,6 +17,7 @@ import {
|
|||||||
config,
|
config,
|
||||||
} from 'folds';
|
} from 'folds';
|
||||||
import { EventType, MatrixClient, MatrixEvent, MsgType, Room } from 'matrix-js-sdk';
|
import { EventType, MatrixClient, MatrixEvent, MsgType, Room } from 'matrix-js-sdk';
|
||||||
|
import FocusTrap from 'focus-trap-react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { useNearViewport } from '../../hooks/useNearViewport';
|
import { useNearViewport } from '../../hooks/useNearViewport';
|
||||||
import { IEncryptedFile, IImageInfo, IThumbnailContent } from '../../../types/matrix/common';
|
import { IEncryptedFile, IImageInfo, IThumbnailContent } from '../../../types/matrix/common';
|
||||||
@@ -250,7 +253,15 @@ function Lightbox({
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
|
<Overlay open backdrop={<OverlayBackdrop />}>
|
||||||
|
<FocusTrap
|
||||||
|
focusTrapOptions={{
|
||||||
|
initialFocus: false,
|
||||||
|
clickOutsideDeactivates: false,
|
||||||
|
escapeDeactivates: false,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}
|
||||||
<div
|
<div
|
||||||
role="dialog"
|
role="dialog"
|
||||||
aria-modal
|
aria-modal
|
||||||
@@ -346,6 +357,8 @@ function Lightbox({
|
|||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
</div>
|
</div>
|
||||||
|
</FocusTrap>
|
||||||
|
</Overlay>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -241,7 +241,7 @@ export function ScheduleMessageModal({
|
|||||||
<Text size="L400">Send at</Text>
|
<Text size="L400">Send at</Text>
|
||||||
<Box gap="200">
|
<Box gap="200">
|
||||||
<Box direction="Column" gap="100" style={{ flex: 1 }}>
|
<Box direction="Column" gap="100" style={{ flex: 1 }}>
|
||||||
<Text as="label" htmlFor="schedule-date" size="T200" style={{ opacity: 0.7 }}>
|
<Text as="label" htmlFor="schedule-date" size="T200" priority="400">
|
||||||
Date
|
Date
|
||||||
</Text>
|
</Text>
|
||||||
<input
|
<input
|
||||||
@@ -253,7 +253,7 @@ export function ScheduleMessageModal({
|
|||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<Box direction="Column" gap="100" style={{ flex: 1 }}>
|
<Box direction="Column" gap="100" style={{ flex: 1 }}>
|
||||||
<Text as="label" htmlFor="schedule-time" size="T200" style={{ opacity: 0.7 }}>
|
<Text as="label" htmlFor="schedule-time" size="T200" priority="400">
|
||||||
Time
|
Time
|
||||||
</Text>
|
</Text>
|
||||||
<input
|
<input
|
||||||
|
|||||||
@@ -140,17 +140,17 @@ export function ScheduledMessagesTray({ roomId }: ScheduledMessagesTrayProps) {
|
|||||||
>
|
>
|
||||||
<Text
|
<Text
|
||||||
size="T200"
|
size="T200"
|
||||||
|
priority="400"
|
||||||
style={{
|
style={{
|
||||||
flex: 1,
|
flex: 1,
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
textOverflow: 'ellipsis',
|
textOverflow: 'ellipsis',
|
||||||
whiteSpace: 'nowrap',
|
whiteSpace: 'nowrap',
|
||||||
opacity: 0.8,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{typeof msg.content.body === 'string' ? (msg.content.body as string) : '(message)'}
|
{typeof msg.content.body === 'string' ? (msg.content.body as string) : '(message)'}
|
||||||
</Text>
|
</Text>
|
||||||
<Text size="T200" style={{ opacity: 0.6, whiteSpace: 'nowrap', flexShrink: 0 }}>
|
<Text size="T200" priority="300" style={{ whiteSpace: 'nowrap', flexShrink: 0 }}>
|
||||||
{formatSendAt(msg.sendAt)}
|
{formatSendAt(msg.sendAt)}
|
||||||
</Text>
|
</Text>
|
||||||
<IconButton
|
<IconButton
|
||||||
|
|||||||
@@ -542,7 +542,7 @@ function ProfileStatus() {
|
|||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
<Box alignItems="Center" gap="200">
|
<Box alignItems="Center" gap="200">
|
||||||
<Text size="T200" style={{ opacity: 0.6, whiteSpace: 'nowrap', flexShrink: 0 }}>
|
<Text size="T200" priority="300" style={{ whiteSpace: 'nowrap', flexShrink: 0 }}>
|
||||||
Auto-clear after:
|
Auto-clear after:
|
||||||
</Text>
|
</Text>
|
||||||
<SettingsSelect
|
<SettingsSelect
|
||||||
@@ -759,10 +759,6 @@ function ProfileTimezone() {
|
|||||||
);
|
);
|
||||||
const saving = saveState.status === AsyncStatus.Loading;
|
const saving = saveState.status === AsyncStatus.Loading;
|
||||||
|
|
||||||
const handleSelectChange = (evt: React.ChangeEvent<HTMLSelectElement>) => {
|
|
||||||
setTimezone(evt.currentTarget.value);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleReset = () => {
|
const handleReset = () => {
|
||||||
setTimezone(savedTimezone);
|
setTimezone(savedTimezone);
|
||||||
};
|
};
|
||||||
@@ -791,39 +787,16 @@ function ProfileTimezone() {
|
|||||||
<Box direction="Column" grow="Yes" gap="100">
|
<Box direction="Column" grow="Yes" gap="100">
|
||||||
<Box as="form" onSubmit={handleSubmit} gap="200" alignItems="Center" aria-disabled={saving}>
|
<Box as="form" onSubmit={handleSubmit} gap="200" alignItems="Center" aria-disabled={saving}>
|
||||||
<Box grow="Yes" direction="Column">
|
<Box grow="Yes" direction="Column">
|
||||||
<select
|
<SettingsSelect
|
||||||
name="timezoneInput"
|
|
||||||
aria-label="Timezone"
|
|
||||||
value={timezone}
|
value={timezone}
|
||||||
onChange={handleSelectChange}
|
options={[
|
||||||
|
{ value: '', label: '— select timezone —' },
|
||||||
|
...COMMON_TIMEZONES.map((tz) => ({ value: tz, label: tz })),
|
||||||
|
]}
|
||||||
|
onChange={setTimezone}
|
||||||
disabled={saving}
|
disabled={saving}
|
||||||
style={{
|
aria-label="Timezone"
|
||||||
background: color.SurfaceVariant.Container,
|
/>
|
||||||
border: `1px solid ${color.SurfaceVariant.ContainerLine}`,
|
|
||||||
borderRadius: config.radii.R300,
|
|
||||||
color: color.SurfaceVariant.OnContainer,
|
|
||||||
colorScheme: 'dark',
|
|
||||||
fontSize: '0.875rem',
|
|
||||||
padding: `${config.space.S200} ${config.space.S300}`,
|
|
||||||
width: '100%',
|
|
||||||
cursor: 'pointer',
|
|
||||||
outline: 'none',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<option value="">— select timezone —</option>
|
|
||||||
{COMMON_TIMEZONES.map((tz) => (
|
|
||||||
<option
|
|
||||||
key={tz}
|
|
||||||
value={tz}
|
|
||||||
style={{
|
|
||||||
background: color.SurfaceVariant.Container,
|
|
||||||
color: color.SurfaceVariant.OnContainer,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{tz}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</Box>
|
</Box>
|
||||||
{hasChanges && !saving && (
|
{hasChanges && !saving && (
|
||||||
<IconButton
|
<IconButton
|
||||||
|
|||||||
@@ -218,7 +218,7 @@ export function ProfileDecoration() {
|
|||||||
>
|
>
|
||||||
{DECORATION_CATEGORIES.map((category) => (
|
{DECORATION_CATEGORIES.map((category) => (
|
||||||
<div key={category.id} style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
|
<div key={category.id} style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
|
||||||
<Text size="L400" style={{ opacity: 0.7 }}>
|
<Text size="L400" priority="400">
|
||||||
{category.label}
|
{category.label}
|
||||||
</Text>
|
</Text>
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { color, config, Icon, IconButton, Icons } from 'folds';
|
|||||||
import { toastQueueAtom, dismissToastAtom, ToastNotif } from '../../state/toast';
|
import { toastQueueAtom, dismissToastAtom, ToastNotif } from '../../state/toast';
|
||||||
import { useSetting } from '../../state/hooks/settings';
|
import { useSetting } from '../../state/hooks/settings';
|
||||||
import { settingsAtom } from '../../state/settings';
|
import { settingsAtom } from '../../state/settings';
|
||||||
|
import { zIndices } from '../../styles/zIndex';
|
||||||
|
|
||||||
// Inject the keyframe animation once
|
// Inject the keyframe animation once
|
||||||
const STYLE_ID = 'lotus-toast-keyframes';
|
const STYLE_ID = 'lotus-toast-keyframes';
|
||||||
@@ -214,7 +215,7 @@ export function LotusToastContainer() {
|
|||||||
position: 'fixed',
|
position: 'fixed',
|
||||||
bottom: '1.5rem',
|
bottom: '1.5rem',
|
||||||
right: '1.5rem',
|
right: '1.5rem',
|
||||||
zIndex: 10001,
|
zIndex: zIndices.toast,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
gap: config.space.S200,
|
gap: config.space.S200,
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import { LotusToastContainer } from '../features/toast/LotusToastContainer';
|
|||||||
import { useTauriNotificationBadge } from '../hooks/useTauriNotificationBadge';
|
import { useTauriNotificationBadge } from '../hooks/useTauriNotificationBadge';
|
||||||
import { SeasonalEffect } from '../components/seasonal/SeasonalEffect';
|
import { SeasonalEffect } from '../components/seasonal/SeasonalEffect';
|
||||||
import { applyCustomAccent, removeCustomAccent } from '../utils/accentColor';
|
import { applyCustomAccent, removeCustomAccent } from '../utils/accentColor';
|
||||||
|
import { zIndices } from '../styles/zIndex';
|
||||||
|
|
||||||
const FONT_MAP: Record<string, string> = {
|
const FONT_MAP: Record<string, string> = {
|
||||||
system: "system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
|
system: "system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
|
||||||
@@ -95,7 +96,7 @@ function NightLightOverlay() {
|
|||||||
position: 'fixed',
|
position: 'fixed',
|
||||||
inset: 0,
|
inset: 0,
|
||||||
pointerEvents: 'none',
|
pointerEvents: 'none',
|
||||||
zIndex: 9998,
|
zIndex: zIndices.nightLight,
|
||||||
backgroundColor: `rgba(255, 140, 0, ${(settings.nightLightOpacity ?? 30) / 100})`,
|
backgroundColor: `rgba(255, 140, 0, ${(settings.nightLightOpacity ?? 30) / 100})`,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Global overlay stacking layers, centralized so floating Lotus UI doesn't
|
||||||
|
* collide. (folds `Overlay`/`Dialog` modals resolve to 9999, which sits between
|
||||||
|
* `nightLight` and `toast`.) Component-internal stacking uses small local
|
||||||
|
* z-index values and is intentionally not listed here.
|
||||||
|
*/
|
||||||
|
export const zIndices = {
|
||||||
|
/** In-call incoming-call banner — below seasonal/night-light/modals. */
|
||||||
|
inCallBanner: 9990,
|
||||||
|
/** Seasonal particle effect — below the night-light tint so particles tint. */
|
||||||
|
seasonalEffect: 9997,
|
||||||
|
/** Night Light tint overlay — above effects, below modals. */
|
||||||
|
nightLight: 9998,
|
||||||
|
/** Toasts — above everything, including modals. */
|
||||||
|
toast: 10001,
|
||||||
|
} as const;
|
||||||
Reference in New Issue
Block a user