Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
71afa3a
fix(sidebar): show icon-sized skeletons in collapsed settings sidebar
waleedlatif1 Mar 15, 2026
af872df
fix(sidebar): hide expanded skeletons during pre-hydration in collaps…
waleedlatif1 Mar 15, 2026
a58de5a
fix(sidebar): align collapsed settings skeletons with actual icon pos…
waleedlatif1 Mar 15, 2026
03739d0
fix(sidebar): match collapsed skeleton section grouping with loaded l…
waleedlatif1 Mar 15, 2026
c191f15
fix(sidebar): hoist skeleton section counts to module constant
waleedlatif1 Mar 15, 2026
0725c98
improvement(ui): align all public pages with dark landing theme and i…
waleedlatif1 Mar 15, 2026
4ede522
fix(ui): address PR review comments - restore StatusPageLayout wrappe…
waleedlatif1 Mar 15, 2026
84b8604
fix(ui): add missing dark class to 404 page, add relative positioning…
waleedlatif1 Mar 15, 2026
a814db7
fix(ui): fallback to white button when whitelabeled without primary c…
waleedlatif1 Mar 15, 2026
7a6d689
improvement(seo): add x-default hreflang, speakable schema to landing…
waleedlatif1 Mar 15, 2026
fe92bf4
fix(ui): fix OTP/input light-mode styles and add missing header class…
waleedlatif1 Mar 15, 2026
b487eee
undo hardcoded ff config
waleedlatif1 Mar 15, 2026
8255763
update old components/ui usage to emcn
waleedlatif1 Mar 15, 2026
d904f81
fix(ui): add SupportFooter to InviteLayout, remove duplicates from un…
waleedlatif1 Mar 15, 2026
3cb33e5
fix(ui): fix invite layout flex centering, use BrandedButton on 404 page
waleedlatif1 Mar 16, 2026
f337b66
fix(ui): fix OTP group styling, props spread order in BrandedButton, …
waleedlatif1 Mar 16, 2026
88ac9d3
fix: enterprise hydration error
emir-karabeg Mar 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 10 additions & 20 deletions apps/sim/app/(auth)/auth-layout-client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,22 @@

import { useEffect } from 'react'
import AuthBackground from '@/app/(auth)/components/auth-background'
import Nav from '@/app/(landing)/components/nav/nav'

function isColorDark(hexColor: string): boolean {
const hex = hexColor.replace('#', '')
const r = Number.parseInt(hex.substr(0, 2), 16)
const g = Number.parseInt(hex.substr(2, 2), 16)
const b = Number.parseInt(hex.substr(4, 2), 16)
const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255
return luminance < 0.5
}
import Navbar from '@/app/(home)/components/navbar/navbar'

export default function AuthLayoutClient({ children }: { children: React.ReactNode }) {
useEffect(() => {
const rootStyle = getComputedStyle(document.documentElement)
const brandBackground = rootStyle.getPropertyValue('--brand-background-hex').trim()

if (brandBackground && isColorDark(brandBackground)) {
document.body.classList.add('auth-dark-bg')
} else {
document.body.classList.remove('auth-dark-bg')
document.documentElement.classList.add('dark')
return () => {
document.documentElement.classList.remove('dark')
}
}, [])

return (
<AuthBackground>
<main className='relative flex min-h-screen flex-col text-foreground'>
<Nav hideAuthButtons={true} variant='auth' />
<AuthBackground className='dark font-[430] font-season'>
<main className='relative flex min-h-full flex-col text-[#ECECEC]'>
<header className='shrink-0 bg-[#1C1C1C]'>
<Navbar logoOnly />
</header>
<div className='relative z-30 flex flex-1 items-center justify-center px-4 pb-24'>
<div className='w-full max-w-lg px-4'>{children}</div>
</div>
Expand Down
127 changes: 88 additions & 39 deletions apps/sim/app/(auth)/components/auth-background-svg.tsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,93 @@
export default function AuthBackgroundSVG() {
return (
<svg
aria-hidden='true'
className='pointer-events-none fixed inset-0 h-full w-full'
style={{ zIndex: 5 }}
viewBox='0 0 1880 960'
fill='none'
xmlns='http://www.w3.org/2000/svg'
preserveAspectRatio='xMidYMid slice'
>
{/* Right side paths - extended to connect */}
<path
d='M1393.53 42.8889C1545.99 173.087 1688.28 339.75 1878.44 817.6'
stroke='#E7E4EF'
strokeWidth='2'
/>
<path d='M1624.21 960L1625.78 0' stroke='#E7E4EF' strokeWidth='2' />
<path d='M1832.67 715.81L1880 716.031' stroke='#E7E4EF' strokeWidth='2' />
<path d='M1393.4 40V0' stroke='#E7E4EF' strokeWidth='2' />
<circle cx='1393.03' cy='40.0186' r='8.07846' fill='white' stroke='#E7E4EF' strokeWidth='2' />
<circle cx='1625.28' cy='303.147' r='8.07846' fill='white' stroke='#E7E4EF' strokeWidth='2' />
<circle cx='1837.37' cy='715.81' r='8.07846' fill='white' stroke='#E7E4EF' strokeWidth='2' />
<>
{/* Top-left card outline */}
<div
aria-hidden='true'
className='pointer-events-none absolute top-[-3vw] left-[-10vw] z-[5] aspect-[344/328] w-[38vw]'
>
<svg
viewBox='0 0 344 328'
fill='none'
xmlns='http://www.w3.org/2000/svg'
preserveAspectRatio='xMidYMid meet'
className='h-full w-full'
>
<path
d='M322.641 326.586L335.508 326.586C339.926 326.586 343.508 323.004 343.508 318.586V153.613C343.508 149.195 339.926 145.613 335.508 145.613H228.282C223.864 145.613 220.282 142.031 220.282 137.613V-50H190.282V137.613C190.282 142.031 186.7 145.613 182.282 145.613H-157V318.586C-157 323.004 -153.418 326.586 -149 326.586H322.641Z'
fill='#1C1C1C'
stroke='#323232'
strokeOpacity='0.4'
strokeWidth='1'
/>
</svg>
</div>

{/* Left side paths - extended to connect */}
<path
d='M160 157.764C319.811 136.451 417.278 102.619 552.39 0'
stroke='#E7E4EF'
strokeWidth='2'
/>
<path d='M310.22 803.025V0' stroke='#E7E4EF' strokeWidth='2' />
<path
d='M160 530.184C256.142 655.353 308.338 749.141 348.382 960'
stroke='#E7E4EF'
strokeWidth='2'
/>
<path d='M160 157.764V960' stroke='#E7E4EF' strokeWidth='2' />
<path d='M-50 157.764L160 157.764' stroke='#E7E4EF' strokeWidth='2' />
<circle cx='160' cy='157.764' r='8.07846' fill='white' stroke='#E7E4EF' strokeWidth='2' />
<circle cx='310.22' cy='803.025' r='8.07846' fill='white' stroke='#E7E4EF' strokeWidth='2' />
<circle cx='160' cy='530.184' r='8.07846' fill='white' stroke='#E7E4EF' strokeWidth='2' />
</svg>
{/* Top-right card outline */}
<div
aria-hidden='true'
className='pointer-events-none absolute top-[-4vw] right-[-14vw] z-[5] aspect-[471/470] w-[42vw]'
>
<svg
viewBox='0 0 471 470'
fill='none'
xmlns='http://www.w3.org/2000/svg'
preserveAspectRatio='xMidYMid meet'
className='h-full w-full'
>
<path
d='M471 94.274L471 124.274L365.88 124.274C361.462 124.274 357.88 127.856 357.88 132.274L357.88 225.495C357.88 229.913 354.298 233.495 349.88 233.495L219.5 233.495C215.082 233.495 211.5 237.077 211.5 241.495L211.5 461.5C211.5 465.918 207.918 469.5 203.5 469.5L8.5 469.5C4.082 469.5 0.5 465.918 0.5 461.5L0.5 157.274C0.5 152.856 4.082 149.274 8.5 149.274L184 149.274C188.418 149.274 192 145.692 192 141.274L192 102.274C192 97.856 195.582 94.274 200 94.274L471 94.274Z'
fill='#1C1C1C'
stroke='#323232'
strokeOpacity='0.4'
strokeWidth='1'
/>
</svg>
</div>

{/* Bottom-left card outline (mirrored) */}
<div
aria-hidden='true'
className='pointer-events-none absolute bottom-[-6vw] left-[-12vw] z-[5] aspect-[471/470] w-[36vw] rotate-180'
>
<svg
viewBox='0 0 471 470'
fill='none'
xmlns='http://www.w3.org/2000/svg'
preserveAspectRatio='xMidYMid meet'
className='h-full w-full'
>
<path
d='M471 94.274L471 124.274L365.88 124.274C361.462 124.274 357.88 127.856 357.88 132.274L357.88 225.495C357.88 229.913 354.298 233.495 349.88 233.495L219.5 233.495C215.082 233.495 211.5 237.077 211.5 241.495L211.5 461.5C211.5 465.918 207.918 469.5 203.5 469.5L8.5 469.5C4.082 469.5 0.5 465.918 0.5 461.5L0.5 157.274C0.5 152.856 4.082 149.274 8.5 149.274L184 149.274C188.418 149.274 192 145.692 192 141.274L192 102.274C192 97.856 195.582 94.274 200 94.274L471 94.274Z'
fill='#1C1C1C'
stroke='#323232'
strokeOpacity='0.4'
strokeWidth='1'
/>
</svg>
</div>

{/* Bottom-right card outline (mirrored) */}
<div
aria-hidden='true'
className='pointer-events-none absolute right-[-12vw] bottom-[-5vw] z-[5] aspect-[344/328] w-[34vw] rotate-180'
>
<svg
viewBox='0 0 344 328'
fill='none'
xmlns='http://www.w3.org/2000/svg'
preserveAspectRatio='xMidYMid meet'
className='h-full w-full'
>
<path
d='M322.641 326.586L335.508 326.586C339.926 326.586 343.508 323.004 343.508 318.586V153.613C343.508 149.195 339.926 145.613 335.508 145.613H228.282C223.864 145.613 220.282 142.031 220.282 137.613V-50H190.282V137.613C190.282 142.031 186.7 145.613 182.282 145.613H-157V318.586C-157 323.004 -153.418 326.586 -149 326.586H322.641Z'
fill='#1C1C1C'
stroke='#323232'
strokeOpacity='0.4'
strokeWidth='1'
/>
</svg>
</div>
</>
)
}
6 changes: 3 additions & 3 deletions apps/sim/app/(auth)/components/auth-background.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ type AuthBackgroundProps = {

export default function AuthBackground({ className, children }: AuthBackgroundProps) {
return (
<div className={cn('relative min-h-screen w-full overflow-hidden', className)}>
<div className='-z-50 pointer-events-none fixed inset-0 bg-white' />
<div className={cn('fixed inset-0 overflow-hidden', className)}>
<div className='-z-50 pointer-events-none absolute inset-0 bg-[#1C1C1C]' />
<AuthBackgroundSVG />
<div className='relative z-20'>{children}</div>
<div className='relative z-20 h-full overflow-auto'>{children}</div>
</div>
)
}
56 changes: 29 additions & 27 deletions apps/sim/app/(auth)/components/branded-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,20 @@

import { forwardRef, useState } from 'react'
import { ArrowRight, ChevronRight, Loader2 } from 'lucide-react'
import { Button, type ButtonProps as EmcnButtonProps } from '@/components/emcn'
import { cn } from '@/lib/core/utils/cn'
import { useBrandedButtonClass } from '@/hooks/use-branded-button-class'
import { useBrandConfig } from '@/ee/whitelabeling'

export interface BrandedButtonProps extends Omit<EmcnButtonProps, 'variant' | 'size'> {
/** Shows loading spinner and disables button */
export interface BrandedButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
loading?: boolean
/** Text to show when loading (appends "..." automatically) */
loadingText?: string
/** Show arrow animation on hover (default: true) */
showArrow?: boolean
/** Make button full width (default: true) */
fullWidth?: boolean
}

/**
* Branded button for auth and status pages.
* Automatically detects whitelabel customization and applies appropriate styling.
*
* @example
* ```tsx
* // Primary branded button with arrow
* <BrandedButton onClick={handleSubmit}>Sign In</BrandedButton>
*
* // Loading state
* <BrandedButton loading loadingText="Signing in">Sign In</BrandedButton>
*
* // Without arrow animation
* <BrandedButton showArrow={false}>Continue</BrandedButton>
* ```
* Default: white button matching the landing page "Get started" style.
* Whitelabel: uses the brand's primary color as background with white text.
*/
export const BrandedButton = forwardRef<HTMLButtonElement, BrandedButtonProps>(
(
Expand All @@ -49,7 +33,8 @@ export const BrandedButton = forwardRef<HTMLButtonElement, BrandedButtonProps>(
},
ref
) => {
const buttonClass = useBrandedButtonClass()
const brand = useBrandConfig()
const hasCustomColor = brand.isWhitelabeled && Boolean(brand.theme?.primaryColor)
const [isHovered, setIsHovered] = useState(false)

const handleMouseEnter = (e: React.MouseEvent<HTMLButtonElement>) => {
Expand All @@ -63,15 +48,32 @@ export const BrandedButton = forwardRef<HTMLButtonElement, BrandedButtonProps>(
}

return (
<Button
<button
ref={ref}
variant='branded'
size='branded'
{...props}
disabled={disabled || loading}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
className={cn(buttonClass, 'group', fullWidth && 'w-full', className)}
{...props}
className={cn(
'group inline-flex h-[30px] items-center justify-center gap-[7px] rounded-[5px] border px-[9px] text-[13.5px] transition-colors disabled:cursor-not-allowed disabled:opacity-50',
!hasCustomColor &&
'border-[#FFFFFF] bg-[#FFFFFF] text-black hover:border-[#E0E0E0] hover:bg-[#E0E0E0]',
fullWidth && 'w-full',
className
)}
style={
hasCustomColor
? {
backgroundColor: isHovered
? (brand.theme?.primaryHoverColor ?? brand.theme?.primaryColor)
: brand.theme?.primaryColor,
borderColor: isHovered
? (brand.theme?.primaryHoverColor ?? brand.theme?.primaryColor)
: brand.theme?.primaryColor,
color: '#FFFFFF',
}
: undefined
}
>
{loading ? (
<span className='flex items-center gap-2'>
Expand All @@ -92,7 +94,7 @@ export const BrandedButton = forwardRef<HTMLButtonElement, BrandedButtonProps>(
) : (
children
)}
</Button>
</button>
)
}
)
Expand Down
9 changes: 4 additions & 5 deletions apps/sim/app/(auth)/components/social-login-buttons.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
'use client'

import { type ReactNode, useEffect, useState } from 'react'
import { Button } from '@/components/emcn'
import { GithubIcon, GoogleIcon } from '@/components/icons'
import { Button } from '@/components/ui/button'
import { client } from '@/lib/auth/auth-client'
import { inter } from '@/app/_styles/fonts/inter/inter'

interface SocialLoginButtonsProps {
githubAvailable: boolean
Expand Down Expand Up @@ -82,7 +81,7 @@ export function SocialLoginButtons({
const githubButton = (
<Button
variant='outline'
className='w-full rounded-[10px] shadow-sm hover:bg-gray-50'
className='w-full rounded-[10px]'
disabled={!githubAvailable || isGithubLoading}
onClick={signInWithGithub}
>
Expand All @@ -94,7 +93,7 @@ export function SocialLoginButtons({
const googleButton = (
<Button
variant='outline'
className='w-full rounded-[10px] shadow-sm hover:bg-gray-50'
className='w-full rounded-[10px]'
disabled={!googleAvailable || isGoogleLoading}
onClick={signInWithGoogle}
>
Expand All @@ -110,7 +109,7 @@ export function SocialLoginButtons({
}

return (
<div className={`${inter.className} grid gap-3 font-light`}>
<div className='grid gap-3 font-light'>
{googleAvailable && googleButton}
{githubAvailable && githubButton}
{children}
Expand Down
4 changes: 2 additions & 2 deletions apps/sim/app/(auth)/components/sso-login-button.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client'

import { useRouter } from 'next/navigation'
import { Button } from '@/components/ui/button'
import { Button } from '@/components/emcn'
import { getEnv, isTruthy } from '@/lib/core/config/env'
import { cn } from '@/lib/core/utils/cn'

Expand Down Expand Up @@ -38,7 +38,7 @@ export function SSOLoginButton({
'flex w-full items-center justify-center gap-2 rounded-[10px] border font-medium text-[15px] text-white transition-all duration-200'
)

const outlineBtnClasses = cn('w-full rounded-[10px] shadow-sm hover:bg-gray-50')
const outlineBtnClasses = cn('w-full rounded-[10px]')

return (
<Button
Expand Down
Loading
Loading