From 1ddbcdfe355a398743c2c70dac8fbc990e9f298f Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Wed, 26 Aug 2026 17:11:04 -0700 Subject: [PATCH 1/3] fix(landing): harden cross-browser compatibility --- .../components/auth-modal/auth-modal.tsx | 9 +- .../components/hero-visual/hero-visual.tsx | 4 +- .../navbar/components/logo-mark/logo-mark.tsx | 5 +- .../components/mobile-nav/mobile-nav.tsx | 6 +- .../nav-menu-item/nav-menu-item.tsx | 2 +- .../components/navbar-shell/navbar-shell.tsx | 4 +- .../code-window-graphic.tsx | 3 +- .../color-mix-fallbacks.module.css | 115 ++++++++++++++++++ .../components/demo-booking/demo-booking.tsx | 23 +++- .../components/legacy-inert-fallback.test.ts | 36 ++++++ .../demo/components/legacy-inert-fallback.ts | 43 +++++++ .../access-control-graphic.tsx | 10 +- .../feature-graphics/deploy-graphic.tsx | 22 +++- .../feature-graphics/lifecycle-graphic.tsx | 12 +- .../operations-teams-graphic.tsx | 8 +- .../feature-graphics/rollback-graphic.tsx | 24 +++- .../feature-graphics/standards-graphic.tsx | 39 ++++-- .../files/components/files-hero-loop.tsx | 4 +- .../components/knowledge-hero-loop.tsx | 2 +- .../failure-alert-graphic.tsx | 5 +- .../feature-graphics/run-trace-graphic.tsx | 3 +- .../workflow-canvas-graphic.tsx | 8 +- 22 files changed, 339 insertions(+), 48 deletions(-) create mode 100644 apps/sim/app/(landing)/components/shared/color-mix-fallbacks/color-mix-fallbacks.module.css create mode 100644 apps/sim/app/(landing)/demo/components/legacy-inert-fallback.test.ts create mode 100644 apps/sim/app/(landing)/demo/components/legacy-inert-fallback.ts diff --git a/apps/sim/app/(landing)/components/auth-modal/auth-modal.tsx b/apps/sim/app/(landing)/components/auth-modal/auth-modal.tsx index da7f1e76324..6e182a8ef48 100644 --- a/apps/sim/app/(landing)/components/auth-modal/auth-modal.tsx +++ b/apps/sim/app/(landing)/components/auth-modal/auth-modal.tsx @@ -2,6 +2,7 @@ import { useEffect, useRef, useState } from 'react' import { + cn, Loader, Modal, ModalClose, @@ -22,6 +23,7 @@ import { getEnv, isFalsy } from '@/lib/core/config/env' import { isSsoEnabled } from '@/lib/core/config/env-flags' import { captureClientEvent } from '@/lib/posthog/client' import type { PostHogEventMap } from '@/lib/posthog/events' +import colorMixFallbacks from '@/app/(landing)/components/shared/color-mix-fallbacks/color-mix-fallbacks.module.css' import { getBrandConfig } from '@/ee/whitelabeling' const logger = createLogger('AuthModal') @@ -196,7 +198,12 @@ export function AuthModal({ children, defaultView = 'login', source }: AuthModal className='h-[22px] w-auto shrink-0 object-contain' />
-

+

Start building.

diff --git a/apps/sim/app/(landing)/components/hero/components/hero-visual/hero-visual.tsx b/apps/sim/app/(landing)/components/hero/components/hero-visual/hero-visual.tsx index e3f46d6790a..2fe719fdd0a 100644 --- a/apps/sim/app/(landing)/components/hero/components/hero-visual/hero-visual.tsx +++ b/apps/sim/app/(landing)/components/hero/components/hero-visual/hero-visual.tsx @@ -38,6 +38,7 @@ import { TYPE_MS_PER_ATOM, WORKFLOW_FOCUS_SCALE, } from '@/app/(landing)/components/hero/components/hero-visual/workflow-data' +import colorMixFallbacks from '@/app/(landing)/components/shared/color-mix-fallbacks/color-mix-fallbacks.module.css' /** * Animated hero visual - the only client island in the hero, decorative and @@ -299,7 +300,7 @@ const SEND_BUTTON_INK = { */ const LANDING_LOADER_INK = { '--tl-grad-inner': 'var(--text-body)', - '--tl-grad-outer': 'color-mix(in srgb, var(--text-body) 76%, #fff)', + '--tl-grad-outer': 'var(--landing-loader-outer)', '--tl-glow': 'transparent', } as CSSProperties @@ -1144,6 +1145,7 @@ export function HeroVisual() { // layer, so the slide + dock read as smooth sub-pixel motion instead of // jittering as the position pixel-snaps each frame. 'pointer-events-none absolute top-0 left-0 z-20 transform-gpu transition-opacity duration-300 will-change-transform [transition-timing-function:cubic-bezier(0.23,1,0.32,1)]', + colorMixFallbacks.loaderOuter, loaderFading ? 'opacity-0' : 'opacity-100' )} style={{ transformOrigin: '0 0' }} diff --git a/apps/sim/app/(landing)/components/navbar/components/logo-mark/logo-mark.tsx b/apps/sim/app/(landing)/components/navbar/components/logo-mark/logo-mark.tsx index 1d26c75dd5c..d76ba068b69 100644 --- a/apps/sim/app/(landing)/components/navbar/components/logo-mark/logo-mark.tsx +++ b/apps/sim/app/(landing)/components/navbar/components/logo-mark/logo-mark.tsx @@ -3,6 +3,7 @@ import { type CSSProperties, type ReactNode, useState } from 'react' import { cn } from '@sim/emcn' import { ThinkingLoader } from '@/components/ui' +import colorMixFallbacks from '@/app/(landing)/components/shared/color-mix-fallbacks/color-mix-fallbacks.module.css' interface LogoMarkProps { /** Server-rendered Sim wordmark, shown by default. */ @@ -18,7 +19,7 @@ interface LogoMarkProps { */ const LOADER_INK = { '--tl-grad-inner': 'var(--text-body)', - '--tl-grad-outer': 'color-mix(in srgb, var(--text-body) 76%, #fff)', + '--tl-grad-outer': 'var(--landing-loader-outer)', '--tl-glow': 'transparent', } as CSSProperties @@ -35,7 +36,7 @@ export function LogoMark({ children }: LogoMarkProps) { return (

-
+
{lead ? : null}
diff --git a/apps/sim/app/(landing)/demo/components/legacy-inert-fallback.test.ts b/apps/sim/app/(landing)/demo/components/legacy-inert-fallback.test.ts new file mode 100644 index 00000000000..c1ff2600343 --- /dev/null +++ b/apps/sim/app/(landing)/demo/components/legacy-inert-fallback.test.ts @@ -0,0 +1,36 @@ +/** + * @vitest-environment jsdom + */ +import { describe, expect, it } from 'vitest' +import { applyLegacyInertFallback } from '@/app/(landing)/demo/components/legacy-inert-fallback' + +describe('applyLegacyInertFallback', () => { + it('removes descendants from interaction and restores their exact prior state', () => { + const panel = document.createElement('div') + panel.setAttribute('aria-hidden', 'false') + panel.style.pointerEvents = 'auto' + panel.innerHTML = ` + + Demo + + ` + + const button = panel.querySelector('button') + const link = panel.querySelector('a') + const disabledInput = panel.querySelector('input') + const restore = applyLegacyInertFallback(panel) + + expect(panel.getAttribute('aria-hidden')).toBe('true') + expect(panel.style.pointerEvents).toBe('none') + expect(button?.getAttribute('tabindex')).toBe('-1') + expect(link?.getAttribute('tabindex')).toBe('-1') + expect(disabledInput?.getAttribute('tabindex')).toBeNull() + + restore() + + expect(panel.getAttribute('aria-hidden')).toBe('false') + expect(panel.style.pointerEvents).toBe('auto') + expect(button?.getAttribute('tabindex')).toBeNull() + expect(link?.getAttribute('tabindex')).toBe('2') + }) +}) diff --git a/apps/sim/app/(landing)/demo/components/legacy-inert-fallback.ts b/apps/sim/app/(landing)/demo/components/legacy-inert-fallback.ts new file mode 100644 index 00000000000..f68fa195479 --- /dev/null +++ b/apps/sim/app/(landing)/demo/components/legacy-inert-fallback.ts @@ -0,0 +1,43 @@ +const FOCUSABLE_SELECTOR = [ + 'a[href]', + 'area[href]', + 'button:not([disabled])', + 'input:not([disabled])', + 'select:not([disabled])', + 'textarea:not([disabled])', + 'iframe', + 'object', + 'embed', + '[contenteditable="true"]', + '[tabindex]', +].join(',') + +/** + * Mirrors the interaction-blocking parts of `inert` for Firefox 111, the only + * browser in Next's supported range without native support. Modern browsers + * never call this fallback. + */ +export function applyLegacyInertFallback(node: HTMLElement): () => void { + const previousAriaHidden = node.getAttribute('aria-hidden') + const previousPointerEvents = node.style.pointerEvents + const previousTabIndexes = new Map() + + node.setAttribute('aria-hidden', 'true') + node.style.pointerEvents = 'none' + + for (const element of node.querySelectorAll(FOCUSABLE_SELECTOR)) { + previousTabIndexes.set(element, element.getAttribute('tabindex')) + element.setAttribute('tabindex', '-1') + } + + return () => { + if (previousAriaHidden === null) node.removeAttribute('aria-hidden') + else node.setAttribute('aria-hidden', previousAriaHidden) + node.style.pointerEvents = previousPointerEvents + + for (const [element, tabIndex] of previousTabIndexes) { + if (tabIndex === null) element.removeAttribute('tabindex') + else element.setAttribute('tabindex', tabIndex) + } + } +} diff --git a/apps/sim/app/(landing)/enterprise/components/feature-graphics/access-control-graphic.tsx b/apps/sim/app/(landing)/enterprise/components/feature-graphics/access-control-graphic.tsx index a24147065c1..1e22d3e5ec8 100644 --- a/apps/sim/app/(landing)/enterprise/components/feature-graphics/access-control-graphic.tsx +++ b/apps/sim/app/(landing)/enterprise/components/feature-graphics/access-control-graphic.tsx @@ -1,5 +1,6 @@ import { ChipTag, cn } from '@sim/emcn' import Image from 'next/image' +import colorMixFallbacks from '@/app/(landing)/components/shared/color-mix-fallbacks/color-mix-fallbacks.module.css' import styles from '@/app/(landing)/enterprise/components/feature-graphics/access-control-graphic.module.css' import { FeatureGraphicShell } from '@/app/(landing)/enterprise/components/feature-graphics/feature-graphic-shell' @@ -123,13 +124,10 @@ export function AccessControlGraphic() { pathLength={1} className={cn( styles.edgeDraw, - edge.emphasized ? styles.edgeDrawEmphasized : EDGE_DRAW_CLASSES[index] + edge.emphasized ? styles.edgeDrawEmphasized : EDGE_DRAW_CLASSES[index], + !edge.emphasized && colorMixFallbacks.mutedStroke35 )} - stroke={ - edge.emphasized - ? 'var(--text-secondary)' - : 'color-mix(in srgb, var(--text-muted) 35%, transparent)' - } + stroke={edge.emphasized ? 'var(--text-secondary)' : undefined} strokeWidth='1' /> ))} diff --git a/apps/sim/app/(landing)/enterprise/components/feature-graphics/deploy-graphic.tsx b/apps/sim/app/(landing)/enterprise/components/feature-graphics/deploy-graphic.tsx index 69a26ab089a..3e8056e6e0f 100644 --- a/apps/sim/app/(landing)/enterprise/components/feature-graphics/deploy-graphic.tsx +++ b/apps/sim/app/(landing)/enterprise/components/feature-graphics/deploy-graphic.tsx @@ -2,6 +2,7 @@ import type { CSSProperties } from 'react' import { ChipTag, chipContentLabelClass, chipGeometryClass, cn } from '@sim/emcn' import { CircleCheck, Lock } from '@sim/emcn/icons' import { ThinkingLoader } from '@/components/ui' +import colorMixFallbacks from '@/app/(landing)/components/shared/color-mix-fallbacks/color-mix-fallbacks.module.css' import styles from '@/app/(landing)/enterprise/components/feature-graphics/deploy-graphic.module.css' import { FeatureGraphicShell } from '@/app/(landing)/enterprise/components/feature-graphics/feature-graphic-shell' @@ -100,7 +101,12 @@ export function DeployGraphic({ {versionTag} - + @@ -116,11 +122,21 @@ export function DeployGraphic({ - + -
+
- +
@@ -92,12 +93,17 @@ export function LifecycleGraphic() {
- +
- + {version.label} diff --git a/apps/sim/app/(landing)/enterprise/components/feature-graphics/operations-teams-graphic.tsx b/apps/sim/app/(landing)/enterprise/components/feature-graphics/operations-teams-graphic.tsx index 977bc56e017..b5bf65212ce 100644 --- a/apps/sim/app/(landing)/enterprise/components/feature-graphics/operations-teams-graphic.tsx +++ b/apps/sim/app/(landing)/enterprise/components/feature-graphics/operations-teams-graphic.tsx @@ -1,6 +1,7 @@ import type { CSSProperties } from 'react' import { cn } from '@sim/emcn' import { ThinkingLoader } from '@/components/ui' +import colorMixFallbacks from '@/app/(landing)/components/shared/color-mix-fallbacks/color-mix-fallbacks.module.css' import { FeatureGraphicShell } from '@/app/(landing)/enterprise/components/feature-graphics/feature-graphic-shell' import styles from '@/app/(landing)/enterprise/components/feature-graphics/operations-teams-graphic.module.css' @@ -118,14 +119,11 @@ const OUT_PATHS = { jira: 'M 140 155 C 140 184 224 178 224 206', } as const -/** Faint-ink stroke for the resting wires (the deploy tile's guide-line grey, quieter). */ -const QUIET_STROKE = 'color-mix(in srgb, var(--text-muted-inverse) 28%, transparent)' - /** Shared 1px outline ink for the tags, port dots, and router hub ring. */ -const OUTLINE_INK = 'border-[color:color-mix(in_srgb,var(--text-muted-inverse)_45%,transparent)]' +const OUTLINE_INK = colorMixFallbacks.inverseBorder45 /** Shared SVG props for a resting wire. */ -const WIRE_PROPS = { stroke: QUIET_STROKE, strokeWidth: '1' } as const +const WIRE_PROPS = { className: colorMixFallbacks.inverseStroke28, strokeWidth: '1' } as const /** Shared SVG props for a traveling white request-pulse overlay on a wire. */ const PULSE_PROPS = { diff --git a/apps/sim/app/(landing)/enterprise/components/feature-graphics/rollback-graphic.tsx b/apps/sim/app/(landing)/enterprise/components/feature-graphics/rollback-graphic.tsx index 52276001e1b..78d0bf143d6 100644 --- a/apps/sim/app/(landing)/enterprise/components/feature-graphics/rollback-graphic.tsx +++ b/apps/sim/app/(landing)/enterprise/components/feature-graphics/rollback-graphic.tsx @@ -1,5 +1,6 @@ -import { Button, ChipTag } from '@sim/emcn' +import { Button, ChipTag, cn } from '@sim/emcn' import { Undo } from '@sim/emcn/icons' +import colorMixFallbacks from '@/app/(landing)/components/shared/color-mix-fallbacks/color-mix-fallbacks.module.css' import { FeatureGraphicShell } from '@/app/(landing)/enterprise/components/feature-graphics/feature-graphic-shell' /** @@ -71,7 +72,12 @@ export function RollbackGraphic() {
- +
@@ -103,13 +109,23 @@ export function RollbackGraphic() {
- +
- + v2 diff --git a/apps/sim/app/(landing)/enterprise/components/feature-graphics/standards-graphic.tsx b/apps/sim/app/(landing)/enterprise/components/feature-graphics/standards-graphic.tsx index 627d2e2c7dd..bdf82aa8140 100644 --- a/apps/sim/app/(landing)/enterprise/components/feature-graphics/standards-graphic.tsx +++ b/apps/sim/app/(landing)/enterprise/components/feature-graphics/standards-graphic.tsx @@ -1,5 +1,6 @@ import { ChipTag, cn } from '@sim/emcn' import { ShieldCheck } from '@sim/emcn/icons' +import colorMixFallbacks from '@/app/(landing)/components/shared/color-mix-fallbacks/color-mix-fallbacks.module.css' import { FeatureGraphicShell } from '@/app/(landing)/enterprise/components/feature-graphics/feature-graphic-shell' import styles from '@/app/(landing)/enterprise/components/feature-graphics/standards-graphic.module.css' @@ -86,8 +87,18 @@ export function StandardsGraphic({