refactor(web): simplify Dify logo theming (#39580)

This commit is contained in:
yyh 2026-07-27 11:01:24 +08:00 committed by GitHub
parent 7e6ba05464
commit 34e6e5a049
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 75 additions and 86 deletions

View File

@ -1691,11 +1691,6 @@
"count": 1
}
},
"web/app/components/base/logo/dify-logo.tsx": {
"react/only-export-components": {
"count": 2
}
},
"web/app/components/base/markdown-blocks/__tests__/paragraph.spec.tsx": {
"jsx_a11y/anchor-is-valid": {
"count": 1

View File

@ -120,7 +120,7 @@ vi.mock('@/app/components/base/loading', () => ({
vi.mock('@/app/components/base/logo/dify-logo', () => ({
__esModule: true,
default: () => <div>dify-logo</div>,
DifyLogo: () => <div>dify-logo</div>,
}))
vi.mock('@/app/components/base/app-icon', () => ({

View File

@ -1,5 +1,5 @@
import { useTranslation } from 'react-i18next'
import DifyLogo from '@/app/components/base/logo/dify-logo'
import { DifyLogo } from '@/app/components/base/logo/dify-logo'
type BrandingFooterProps = {
removeWebappBrand?: boolean
@ -20,7 +20,7 @@ const BrandingFooter = ({ removeWebappBrand, replaceWebappLogo }: BrandingFooter
{replaceWebappLogo ? (
<img src={replaceWebappLogo} alt="logo" className="block h-5 w-auto" />
) : (
<DifyLogo size="small" />
<DifyLogo alt="Dify" size="small" />
)}
</div>
</div>

View File

@ -3,7 +3,7 @@ import { Button } from '@langgenius/dify-ui/button'
import { useSuspenseQuery } from '@tanstack/react-query'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import DifyLogo from '@/app/components/base/logo/dify-logo'
import { DifyLogo } from '@/app/components/base/logo/dify-logo'
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
import Link from '@/next/link'
import { useRouter } from '@/next/navigation'

View File

@ -17,7 +17,7 @@ import ActionButton from '@/app/components/base/action-button'
import AppIcon from '@/app/components/base/app-icon'
import List from '@/app/components/base/chat/chat-with-history/sidebar/list'
import RenameModal from '@/app/components/base/chat/chat-with-history/sidebar/rename-modal'
import DifyLogo from '@/app/components/base/logo/dify-logo'
import { DifyLogo } from '@/app/components/base/logo/dify-logo'
import MenuDropdown from '@/app/components/share/text-generation/menu-dropdown'
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
import { useChatWithHistoryContext } from '../context'
@ -177,7 +177,7 @@ const Sidebar = ({ isPanel }: Props) => {
className="block h-5 w-auto"
/>
) : (
<DifyLogo size="small" />
<DifyLogo alt="Dify" size="small" />
)}
</div>
)}

View File

@ -9,7 +9,7 @@ import { useTranslation } from 'react-i18next'
import ActionButton from '@/app/components/base/action-button'
import ViewFormDropdown from '@/app/components/base/chat/embedded-chatbot/inputs-form/view-form-dropdown'
import Divider from '@/app/components/base/divider'
import DifyLogo from '@/app/components/base/logo/dify-logo'
import { DifyLogo } from '@/app/components/base/logo/dify-logo'
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
import { isClient } from '@/utils/client'
import { useEmbeddedChatbotContext } from '../context'
@ -105,7 +105,7 @@ const Header: FC<IHeaderProps> = ({
className="block h-5 w-auto"
/>
) : (
<DifyLogo size="small" />
<DifyLogo alt="Dify" size="small" />
)}
</div>
)}

View File

@ -7,7 +7,7 @@ import { useTranslation } from 'react-i18next'
import ChatWrapper from '@/app/components/base/chat/embedded-chatbot/chat-wrapper'
import Header from '@/app/components/base/chat/embedded-chatbot/header'
import Loading from '@/app/components/base/loading'
import DifyLogo from '@/app/components/base/logo/dify-logo'
import { DifyLogo } from '@/app/components/base/logo/dify-logo'
import LogoHeader from '@/app/components/base/logo/logo-embedded-chat-header'
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
@ -95,7 +95,7 @@ const Chatbot = () => {
className="block h-5 w-auto"
/>
) : (
<DifyLogo size="small" />
<DifyLogo alt="Dify" size="small" />
)}
</div>
)}

View File

@ -0,0 +1,16 @@
import { render, screen } from '@testing-library/react'
import { DifyLogo } from '../dify-logo'
describe('DifyLogo', () => {
it('uses the provided alternative text as its accessible name', () => {
const { container, rerender } = render(<DifyLogo alt="Dify" />)
expect(screen.getByRole('img', { name: 'Dify' })).toHaveAttribute('src', '/logo/logo.svg')
rerender(<DifyLogo alt="" />)
const decorativeLogo = container.querySelector('img')
expect(decorativeLogo).toHaveAttribute('alt', '')
expect(screen.queryByRole('img')).not.toBeInTheDocument()
})
})

View File

@ -1,47 +1,44 @@
'use client'
import type { FC } from 'react'
import type { VariantProps } from 'class-variance-authority'
import type { ComponentProps } from 'react'
import { cn } from '@langgenius/dify-ui/cn'
import useTheme from '@/hooks/use-theme'
import { cva } from 'class-variance-authority'
import { basePath } from '@/utils/var'
export type LogoStyle = 'default' | 'monochromeWhite'
const difyLogoVariants = cva(
'block object-contain [html[data-theme=dark]_&]:brightness-0 [html[data-theme=dark]_&]:invert',
{
variants: {
size: {
small: 'h-4 w-9',
medium: 'h-[22px] w-12',
large: 'h-7 w-16',
},
},
defaultVariants: {
size: 'medium',
},
},
)
export const logoPathMap: Record<LogoStyle, string> = {
default: '/logo/logo.svg',
monochromeWhite: '/logo/logo-monochrome-white.svg',
}
export type DifyLogoProps = Omit<
ComponentProps<'img'>,
'alt' | 'height' | 'size' | 'src' | 'width'
> &
VariantProps<typeof difyLogoVariants> & {
alt: string
}
export type LogoSize = 'large' | 'medium' | 'small'
export const logoSizeMap: Record<LogoSize, string> = {
large: 'w-16 h-7',
medium: 'w-12 h-[22px]',
small: 'w-9 h-4',
}
type DifyLogoProps = {
style?: LogoStyle
size?: LogoSize
className?: string
alt?: string
}
const DifyLogo: FC<DifyLogoProps> = ({
style = 'default',
size = 'medium',
className,
alt = 'Dify',
}) => {
const { theme } = useTheme()
const themedStyle = theme === 'dark' && style === 'default' ? 'monochromeWhite' : style
export function DifyLogo({ alt, className, size, ...props }: DifyLogoProps) {
const classes = cn(difyLogoVariants({ size, className }))
return (
<img
src={`${basePath}${logoPathMap[themedStyle]}`}
className={cn('block object-contain', logoSizeMap[size], className)}
{...props}
src={`${basePath}/logo/logo.svg`}
width={48}
height={22}
className={classes}
alt={alt}
/>
)
}
export default DifyLogo

View File

@ -3,7 +3,7 @@ import { cn } from '@langgenius/dify-ui/cn'
import { DialogDescription, DialogTitle } from '@langgenius/dify-ui/dialog'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import DifyLogo from '../../base/logo/dify-logo'
import { DifyLogo } from '../../base/logo/dify-logo'
import styles from './header.module.css'
type HeaderProps = {
@ -18,7 +18,7 @@ const Header = ({ onClose }: HeaderProps) => {
<div className="relative flex max-w-[1680px] grow flex-col justify-end gap-y-1 border-x border-divider-accent p-6 pt-8">
<div className="flex items-end">
<div aria-hidden="true" className="py-[5px]">
<DifyLogo className="h-[27px] w-[60px]" />
<DifyLogo alt="" className="h-[27px] w-[60px]" />
</div>
<DialogTitle
className={cn(

View File

@ -1,4 +1,4 @@
import DifyLogo from '@/app/components/base/logo/dify-logo'
import { DifyLogo } from '@/app/components/base/logo/dify-logo'
type PoweredByBrandProps = {
webappBrandRemoved?: boolean
@ -23,7 +23,7 @@ const PoweredByBrand = ({
{previewLogo ? (
<img src={previewLogo} alt="logo" className="block h-5 w-auto" />
) : (
<DifyLogo size="small" />
<DifyLogo alt="Dify" size="small" />
)}
</>
)

View File

@ -6,7 +6,7 @@ import { RiCloseLine } from '@remixicon/react'
import { useSuspenseQuery } from '@tanstack/react-query'
import dayjs from 'dayjs'
import { useTranslation } from 'react-i18next'
import DifyLogo from '@/app/components/base/logo/dify-logo'
import { DifyLogo } from '@/app/components/base/logo/dify-logo'
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
import Link from '@/next/link'
@ -48,7 +48,7 @@ export default function AccountAbout({ langGeniusVersionInfo, onCancel }: IAccou
alt="logo"
/>
) : (
<DifyLogo size="large" className="mx-auto" />
<DifyLogo alt="Dify" size="large" className="mx-auto" />
)}
<div className="text-center text-xs font-normal text-text-tertiary">

View File

@ -7,7 +7,7 @@ import { useAtomValue } from 'jotai'
import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import Badge from '@/app/components/base/badge'
import DifyLogo from '@/app/components/base/logo/dify-logo'
import { DifyLogo } from '@/app/components/base/logo/dify-logo'
import EnvNav from '@/app/components/header/env-nav'
import StepByStepTourMount from '@/app/components/step-by-step-tour/mount'
import { langGeniusVersionInfoAtom } from '@/context/version-state'

View File

@ -5,7 +5,7 @@ import { motion, useMotionValue, useSpring, useTransform } from 'motion/react'
import { useEffect, useLayoutEffect, useRef } from 'react'
import { useLocale, useTranslation } from '#i18n'
import Divider from '@/app/components/base/divider'
import DifyLogo from '@/app/components/base/logo/dify-logo'
import { DifyLogo } from '@/app/components/base/logo/dify-logo'
import { SubmitRequestDropdown } from '@/app/components/plugins/plugin-page/nav-operations'
import PluginTypeSwitch from '../plugin-type-switch'
import SearchBoxWrapper from '../search-box/search-box-wrapper'
@ -231,7 +231,7 @@ const Description = ({
<div className="flex h-[60px] w-full items-center rounded-lg border-[0.5px] border-white/50 bg-components-panel-bg-blur backdrop-blur-[6px]">
<div className="flex h-full min-w-0 flex-1 items-center px-5 py-2">
<div className="flex shrink-0 items-center gap-1.5">
<DifyLogo alt="" className="h-6 w-[52px]" />
<DifyLogo alt="Dify" className="h-6 w-[52px]" />
<span className="max-w-0 overflow-hidden title-3xl-semi-bold whitespace-nowrap text-text-primary opacity-0 transition-all duration-200 md:max-w-[150px] md:opacity-100">
{tCommon(($) => $['mainNav.marketplace'])}
</span>

View File

@ -12,7 +12,7 @@ import { useTranslation } from 'react-i18next'
import SavedItems from '@/app/components/app/text-generate/saved-items'
import AppIcon from '@/app/components/base/app-icon'
import Badge from '@/app/components/base/badge'
import DifyLogo from '@/app/components/base/logo/dify-logo'
import { DifyLogo } from '@/app/components/base/logo/dify-logo'
import { appDefaultIconBackground } from '@/config'
import { AccessMode } from '@/models/access-control'
import MenuDropdown from './menu-dropdown'
@ -223,7 +223,7 @@ const TextGenerationSidebar: FC<TextGenerationSidebarProps> = ({
) : customConfig?.replace_webapp_logo ? (
<img src={customConfig.replace_webapp_logo} alt="logo" className="block h-5 w-auto" />
) : (
<DifyLogo size="small" />
<DifyLogo alt="Dify" size="small" />
)}
</div>
)}

View File

@ -1,6 +1,7 @@
'use client'
import { useSuspenseQuery } from '@tanstack/react-query'
import Divider from '@/app/components/base/divider'
import { DifyLogo } from '@/app/components/base/logo/dify-logo'
import LocaleMenu from '@/app/signin/_locale-menu'
import { useLocale } from '@/context/i18n'
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
@ -8,10 +9,6 @@ import { setLocaleOnClient } from '@/i18n-config'
import { languages } from '@/i18n-config/language'
import dynamic from '@/next/dynamic'
const DifyLogo = dynamic(() => import('@/app/components/base/logo/dify-logo'), {
ssr: false,
loading: () => <div className="h-7 w-16 bg-transparent" />,
})
const ThemeSelector = dynamic(() => import('@/app/components/base/theme-selector'), {
ssr: false,
loading: () => <div className="size-8 bg-transparent" />,
@ -30,7 +27,7 @@ const Header = () => {
alt="logo"
/>
) : (
<DifyLogo size="large" />
<DifyLogo alt="Dify" size="large" />
)}
<div className="flex items-center gap-1">
<LocaleMenu

View File

@ -21,7 +21,7 @@ import { useAsyncWindowOpen } from '@/hooks/use-async-window-open'
import { useRouter, useSearchParams } from '@/next/navigation'
import { consoleClient, consoleQuery } from '@/service/client'
import { useEducationAdd, useInvalidateEducationStatus } from '@/service/use-education'
import DifyLogo from '../components/base/logo/dify-logo'
import { DifyLogo } from '../components/base/logo/dify-logo'
import AppliedEducationContent from './applied-education-content'
import RoleSelector from './role-selector'
import SearchInput from './search-input'
@ -179,7 +179,7 @@ const EducationApplyAgeContent = () => {
}}
></div>
<div className="mt-[-349px] box-content flex h-7 items-center justify-between p-6">
<DifyLogo size="large" style="monochromeWhite" />
<DifyLogo alt="Dify" size="large" className="brightness-0 invert" />
</div>
<div className="mx-auto max-w-[720px] px-8 pb-[180px]">
<div className="mb-2 flex h-[192px] flex-col justify-end pt-3 pb-4 text-text-primary-on-surface">

View File

@ -1,6 +1,7 @@
'use client'
import { useSuspenseQuery } from '@tanstack/react-query'
import Divider from '@/app/components/base/divider'
import { DifyLogo } from '@/app/components/base/logo/dify-logo'
import { useLocale } from '@/context/i18n'
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
import { setLocaleOnClient } from '@/i18n-config'
@ -8,11 +9,6 @@ import { languages } from '@/i18n-config/language'
import dynamic from '@/next/dynamic'
import LocaleMenu from './_locale-menu'
// Avoid rendering the logo and theme selector on the server
const DifyLogo = dynamic(() => import('@/app/components/base/logo/dify-logo'), {
ssr: false,
loading: () => <div className="h-7 w-16 bg-transparent" />,
})
const ThemeSelector = dynamic(() => import('@/app/components/base/theme-selector'), {
ssr: false,
loading: () => <div className="size-8 bg-transparent" />,
@ -31,7 +27,7 @@ const Header = () => {
alt="logo"
/>
) : (
<DifyLogo size="large" />
<DifyLogo alt="Dify" size="large" />
)}
<div className="flex items-center gap-1">
<LocaleMenu

View File

@ -1,12 +0,0 @@
<svg width="48" height="22" viewBox="0 0 48 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="White=True">
<g id="if">
<path d="M21.2002 4.0695C22.5633 4.0695 23.0666 3.23413 23.0666 2.20309C23.0666 1.17204 22.5623 0.33667 21.2002 0.33667C19.838 0.33667 19.3337 1.17204 19.3337 2.20309C19.3337 3.23413 19.838 4.0695 21.2002 4.0695Z" fill="white"/>
<path d="M27.7336 4.46931V5.66969H24.6668V8.33667H27.7336V15.0037H22.6668V5.67063H15.9998V8.33761H19.7336V15.0046H15.3337V17.6716H35.3337V15.0046H30.6668V8.33761H35.3337V5.67063H30.6668V3.00365H35.3337V0.33667H31.8671C29.5877 0.33667 27.7336 2.19086 27.7336 4.47025V4.46931Z" fill="white"/>
</g>
<g id="Dy">
<path d="M5.66698 0.335902H0V17.6689H5.66698C12.667 17.6689 14.667 13.6689 14.667 9.00194C14.667 4.33496 12.667 0.334961 5.66698 0.334961V0.335902ZM5.73377 15.0029H3.20038V3.00288H5.73377C9.75823 3.00288 11.4666 4.97842 11.4666 9.00288C11.4666 13.0273 9.75823 15.0029 5.73377 15.0029Z" fill="white"/>
<path d="M44.8335 5.66986L42.1665 14.3368L39.4995 5.66986H36.333L40.2013 16.8815C40.604 18.049 39.9229 19.0029 38.6886 19.0029H37.333V21.6699H39.3255C41.063 21.6699 42.6265 20.5711 43.2145 18.9361L48 5.66986H44.8335Z" fill="white"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB