Skip to content

refactor(clerk-js): Expand ApplicationLogo usage for oAuth logo #6518

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Aug 11, 2025
5 changes: 5 additions & 0 deletions .changeset/chilly-ravens-cover.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clerk/clerk-js': patch
---

Refactor ApplicationLogo rendering logic to account for oAuth logos within OAuthConsent component.
2 changes: 1 addition & 1 deletion packages/clerk-js/bundlewatch.config.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"files": [
{ "path": "./dist/clerk.js", "maxSize": "621KB" },
{ "path": "./dist/clerk.js", "maxSize": "621.1KB" },
{ "path": "./dist/clerk.browser.js", "maxSize": "75KB" },
{ "path": "./dist/clerk.legacy.browser.js", "maxSize": "117KB" },
{ "path": "./dist/clerk.headless*.js", "maxSize": "57KB" },
Expand Down
75 changes: 42 additions & 33 deletions packages/clerk-js/src/ui/components/OAuthConsent/OAuthConsent.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { useUser } from '@clerk/shared/react';
import type { ComponentProps } from 'react';
import { useState } from 'react';

import { useEnvironment, useOAuthConsentContext } from '@/ui/contexts';
import { Box, Button, Flex, Flow, Grid, Icon, Text } from '@/ui/customizables';
import { ApplicationLogo } from '@/ui/elements/ApplicationLogo';
import { Avatar } from '@/ui/elements/Avatar';
import { Card } from '@/ui/elements/Card';
import { withCardStateProvider } from '@/ui/elements/contexts';
import { Header } from '@/ui/elements/Header';
Expand All @@ -17,8 +17,14 @@ import { common } from '@/ui/styledSystem';
import { colors } from '@/ui/utils/colors';

export function OAuthConsentInternal() {
const { scopes, oAuthApplicationName, oAuthApplicationLogoUrl, redirectUrl, onDeny, onAllow } =
useOAuthConsentContext();
const {
scopes,
oAuthApplicationName,
oAuthApplicationLogoUrl = 'https://img.clerk.com/eyJ0eXBlIjoicHJveHkiLCJzcmMiOiJodHRwczovL2ltYWdlcy5jbGVyay5kZXYvdXBsb2FkZWQvaW1nXzMxOHVMSERjc0hTNXNaWlF0MlFEeWxpMER0RyJ9?width=400',
redirectUrl,
onDeny,
onAllow,
} = useOAuthConsentContext();
const { user } = useUser();
const { applicationName, logoImageUrl } = useEnvironment().displayConfig;
const [isUriModalOpen, setIsUriModalOpen] = useState(false);
Expand All @@ -42,13 +48,16 @@ export function OAuthConsentInternal() {
{/* both have avatars */}
{oAuthApplicationLogoUrl && logoImageUrl && (
<ConnectionHeader>
<Avatar
imageUrl={oAuthApplicationLogoUrl}
size={t => t.space.$12}
rounded={false}
/>
<ConnectionItem justify='end'>
<ApplicationLogo
src={oAuthApplicationLogoUrl}
alt={oAuthApplicationName}
/>
</ConnectionItem>
<ConnectionSeparator />
<ApplicationLogo />
<ConnectionItem justify='start'>
<ApplicationLogo />
</ConnectionItem>
</ConnectionHeader>
)}
{/* only OAuth app has an avatar */}
Expand All @@ -59,10 +68,9 @@ export function OAuthConsentInternal() {
position: 'relative',
}}
>
<Avatar
imageUrl={oAuthApplicationLogoUrl}
size={t => t.space.$12}
rounded={false}
<ApplicationLogo
src={oAuthApplicationLogoUrl}
alt={oAuthApplicationName}
/>
<ConnectionIcon
size='sm'
Expand All @@ -77,31 +85,21 @@ export function OAuthConsentInternal() {
)}
{/* only Clerk application has an avatar */}
{!oAuthApplicationLogoUrl && logoImageUrl && (
<Flex
justify='center'
align='center'
gap={4}
sx={t => ({
marginBlockEnd: t.space.$6,
})}
>
<ConnectionIcon />
<ConnectionHeader>
<ConnectionItem justify='end'>
<ConnectionIcon />
</ConnectionItem>
<ConnectionSeparator />
<ApplicationLogo />
</Flex>
<ConnectionItem justify='start'>
<ApplicationLogo />
</ConnectionItem>
</ConnectionHeader>
)}
{/* no avatars */}
{!oAuthApplicationLogoUrl && !logoImageUrl && (
<Flex
justify='center'
align='center'
gap={4}
sx={t => ({
marginBlockEnd: t.space.$6,
})}
>
<ConnectionHeader>
<ConnectionIcon />
</Flex>
</ConnectionHeader>
)}
<Header.Title localizationKey={oAuthApplicationName} />
<Header.Subtitle
Expand Down Expand Up @@ -320,6 +318,17 @@ function ConnectionHeader({ children }: { children: React.ReactNode }) {
);
}

function ConnectionItem({ children, ...props }: ComponentProps<typeof Flex>) {
return (
<Flex
{...props}
sx={[{ flex: 1 }, props.sx]}
>
{children}
</Flex>
);
}

function ConnectionIcon({ size = 'md', sx }: { size?: 'sm' | 'md'; sx?: ThemableCssProp }) {
const scale: ThemableCssProp = t => {
const value = size === 'sm' ? t.space.$6 : t.space.$12;
Expand Down
33 changes: 23 additions & 10 deletions packages/clerk-js/src/ui/elements/ApplicationLogo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,30 @@ const getContainerHeightForImageRatio = (imageRef: React.RefObject<HTMLImageElem
return width;
};

type ApplicationLogoProps = PropsOfComponent<typeof Flex>;
export type ApplicationLogoProps = PropsOfComponent<typeof Flex> & {
/**
* The URL of the image to display.
*/
src?: string;
/**
* The alt text for the image.
*/
alt?: string;
/**
* The URL to navigate to when the logo is clicked.
*/
href?: string;
};

export const ApplicationLogo = (props: ApplicationLogoProps) => {
export const ApplicationLogo: React.FC<ApplicationLogoProps> = (props: ApplicationLogoProps): JSX.Element | null => {
const { src, alt, href, sx, ...rest } = props;
const imageRef = React.useRef<HTMLImageElement>(null);
const [loaded, setLoaded] = React.useState(false);
const { logoImageUrl, applicationName, homeUrl } = useEnvironment().displayConfig;
const { parsedLayout } = useAppearance();
const imageSrc = parsedLayout.logoImageUrl || logoImageUrl;
const logoUrl = parsedLayout.logoLinkUrl || homeUrl;
const imageSrc = src || parsedLayout.logoImageUrl || logoImageUrl;
const imageAlt = alt || applicationName;
const logoUrl = href || parsedLayout.logoLinkUrl || homeUrl;

if (!imageSrc) {
return null;
Expand All @@ -39,7 +54,7 @@ export const ApplicationLogo = (props: ApplicationLogoProps) => {
<Image
ref={imageRef}
elementDescriptor={descriptors.logoImage}
alt={applicationName}
alt={imageAlt}
src={imageSrc}
size={200}
onLoad={() => setLoaded(true)}
Expand All @@ -55,20 +70,18 @@ export const ApplicationLogo = (props: ApplicationLogoProps) => {
return (
<Flex
elementDescriptor={descriptors.logoBox}
{...props}
{...rest}
sx={[
theme => ({
height: getContainerHeightForImageRatio(imageRef, theme.sizes.$6),
justifyContent: 'center',
}),
props.sx,
sx,
]}
>
{logoUrl ? (
<RouterLink
sx={{
justifyContent: 'center',
}}
focusRing
to={logoUrl}
>
{image}
Expand Down
9 changes: 9 additions & 0 deletions packages/clerk-js/src/ui/primitives/Link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,19 @@ const { applyVariants, filterProps } = createVariants(theme => ({
},
inherit: { color: 'inherit' },
},
focusRing: {
true: {
'&:focus': {
outline: 'none',
...common.focusRing(theme),
},
},
},
},
defaultVariants: {
colorScheme: 'primary',
variant: 'body',
focusRing: false,
},
}));

Expand Down
Loading