feat. lnb컴포넌트

This commit is contained in:
clkim
2025-11-11 16:53:46 +09:00
parent 208a907e91
commit 4b034ec009
17 changed files with 486 additions and 122 deletions

View File

@@ -10,7 +10,7 @@ const handleScrollToTop = () => {
<template>
<Transition name="fade">
<button v-if="showBtn" class="btn-top" @click="handleScrollToTop">
<button v-show="showBtn" class="btn-top" @click="handleScrollToTop">
<span class="sr-only">top</span>
</button>
</Transition>

View File

@@ -0,0 +1,172 @@
<script setup lang="ts">
import type { PageDataLnbMenu } from '#layers/types/api/pageData'
const { y: windowY } = useWindowScroll({ behavior: 'smooth' })
const pageDataStore = usePageDataStore()
const breakpoints = useResponsiveBreakpoints()
const { pageData } = storeToRefs(pageDataStore)
const observerOptions = {
root: null,
rootMargin: '-20% 0px -60% 0px', // 상단 20%, 하단 60% 마진
threshold: 0,
}
const activeSection = ref<string>('')
const lnbList = computed<Record<string, PageDataLnbMenu>>(
() => pageData.value?.lnb_menus || {}
)
const isShowLnb = computed(() => {
return Boolean(
pageData.value?.use_lnb &&
breakpoints.value.isDesktop &&
Object.keys(lnbList.value).length > 0
)
})
const activeColor = computed(
() => pageData.value?.lnb_text_color_code_active || 'var(--text-primary)'
)
const disableColor = computed(
() => pageData.value?.lnb_text_color_code_deactive || 'var(--text-secondary)'
)
// 1depth가 활성화되었는지 확인 (자신 또는 자식 중 하나가 활성화된 경우)
const is1DepthActive = (lnbItem: PageDataLnbMenu): boolean => {
// 자신이 활성화된 경우
if (activeSection.value === lnbItem.page_ver_tmpl_name_en) {
return true
}
// children 중 하나가 활성화된 경우
if (lnbItem.children && Object.keys(lnbItem.children).length > 0) {
return Object.values(lnbItem.children).some(
child => activeSection.value === child.page_ver_tmpl_name_en
)
}
return false
}
const observer = new IntersectionObserver(entries => {
if (import.meta.server) return
// 교차하는 섹션들 중 가장 위에 있는 것을 활성화
const visibleEntries = entries
.filter(entry => entry.isIntersecting)
.sort((a, b) => a.boundingClientRect.top - b.boundingClientRect.top)
if (visibleEntries.length > 0) {
activeSection.value = visibleEntries[0].target.id
}
}, observerOptions)
const observeSections = () => {
if (import.meta.server) return
Object.values(lnbList.value).forEach(lnbItem => {
// 1depth 관찰
const el = document.getElementById(lnbItem.page_ver_tmpl_name_en)
if (el) {
observer.observe(el)
}
// 2depth 관찰
if (lnbItem.children && Object.keys(lnbItem.children).length > 0) {
Object.values(lnbItem.children).forEach(childItem => {
const childEl = document.getElementById(childItem.page_ver_tmpl_name_en)
if (childEl) {
observer.observe(childEl)
}
})
}
})
}
const handleLnbClick = (lnbItem: PageDataLnbMenu) => {
if (import.meta.server) return
const id = lnbItem.page_ver_tmpl_name_en
const el = document.getElementById(id)
if (!el) return
const elementTop = el.getBoundingClientRect().top
const currentScrollY = window.scrollY
const headerHeight = 64
const targetScrollY = currentScrollY + elementTop - headerHeight
windowY.value = targetScrollY
}
onMounted(() => {
observeSections()
})
onUnmounted(() => {
observer.disconnect()
})
</script>
<template>
<div
v-if="isShowLnb"
class="lnb-wrap"
:style="{
'--lnb-active-color': activeColor,
'--lnb-disable-color': disableColor,
}"
>
<ul class="main-list">
<li v-for="lnbItem in lnbList" :key="lnbItem.path_code">
<button
v-dompurify-html="lnbItem.menu_name"
type="button"
:class="['btn-1depth', { 'is-active': is1DepthActive(lnbItem) }]"
@click="handleLnbClick(lnbItem)"
></button>
<ul
v-if="Object.keys(lnbItem.children || {}).length > 0"
class="sub-list"
>
<li v-for="subItem in lnbItem.children" :key="subItem.path_code">
<button
v-dompurify-html="subItem.menu_name"
type="button"
:class="[
'btn-2depth',
{
'is-active': activeSection === subItem.page_ver_tmpl_name_en,
},
]"
@click="handleLnbClick(subItem)"
></button>
</li>
</ul>
</li>
</ul>
</div>
</template>
<style scoped>
.lnb-wrap {
@apply fixed top-1/2 right-0 py-12 pr-4 text-right -translate-y-1/2 bg-[radial-gradient(100%_50%_at_100%_50%,rgba(0,0,0,0.5)_25%,rgba(0,0,0,0)_100%)] z-50;
}
.main-list {
@apply flex flex-col gap-4 items-end;
}
.btn-1depth {
@apply text-[15px] leading-[26px] tracking-[-0.54px];
}
.sub-list {
@apply flex flex-col gap-2 items-end mt-4 mb-1 pr-[46px];
}
.btn-2depth {
@apply text-[14px] leading-[20px] tracking-[-0.42px];
}
button {
@apply font-[500] text-[var(--lnb-disable-color)] transition-all duration-300 ease-in-out;
}
button:hover,
button.is-active {
@apply text-[var(--lnb-active-color)];
}
</style>

View File

@@ -0,0 +1,211 @@
<script setup lang="ts">
import type { PageDataLnbMenu } from '#layers/types/api/pageData'
const HEADER_HEIGHT = 64
const { y: windowY } = useWindowScroll({ behavior: 'smooth' })
const pageDataStore = usePageDataStore()
const breakpoints = useResponsiveBreakpoints()
const { pageData } = storeToRefs(pageDataStore)
const activeSection = ref<string>('')
const observerRef = shallowRef<IntersectionObserver | null>(null)
/** 1뎁스 LNB 배열로 정규화 */
const lnbRoot = computed<PageDataLnbMenu[]>(() =>
Object.values(pageData.value?.lnb_menus || {}).filter(Boolean)
)
const isShowLnb = computed(() => {
const pageDataUseLnb = pageData.value?.use_lnb ?? false
const isDesktop = breakpoints.value.isDesktop
const lnbRootLength = lnbRoot.value.length
return Boolean(pageDataUseLnb && isDesktop && lnbRootLength)
})
const activeColor = computed(
() => pageData.value?.lnb_text_color_code_active || 'var(--text-primary)'
)
const disableColor = computed(
() => pageData.value?.lnb_text_color_code_deactive || 'var(--text-secondary)'
)
const getChildren = (item?: PageDataLnbMenu) =>
item?.children ? Object.values(item.children) : []
/** 1뎁스 활성 판정(자신 또는 자식) */
const is1DepthActive = (lnbItem: PageDataLnbMenu): boolean => {
if (!lnbItem) return false
if (activeSection.value === lnbItem.page_ver_tmpl_name_en) return true
return getChildren(lnbItem).some(
c => activeSection.value === c.page_ver_tmpl_name_en
)
}
/** 스크롤 이동 */
const scrollToLnb = async (id: string) => {
if (import.meta.server) return
await nextTick()
const el = document.getElementById(id)
if (!el) return
const rect = el.getBoundingClientRect()
const targetY = window.scrollY + rect.top - HEADER_HEIGHT
windowY.value = targetY
}
/** 관찰 대상 id 리스트(1뎁스 + 2뎁스) */
const sectionIds = computed<string[]>(() => {
const ids: string[] = []
for (const item of lnbRoot.value) {
if (item?.page_ver_tmpl_name_en) ids.push(item.page_ver_tmpl_name_en)
for (const c of getChildren(item)) {
if (c?.page_ver_tmpl_name_en) ids.push(c.page_ver_tmpl_name_en)
}
}
// 중복 제거
return Array.from(new Set(ids))
})
const createObserver = () => {
if (observerRef.value) observerRef.value.disconnect()
observerRef.value = new IntersectionObserver(
entries => {
// 보이는 섹션 중 화면 상단에 가장 가까운 요소를 활성화
const visibles = entries
.filter(e => e.isIntersecting)
.sort((a, b) => a.boundingClientRect.top - b.boundingClientRect.top)
if (visibles.length > 0) {
activeSection.value = (visibles[0].target as HTMLElement).id
return
}
// 가끔 빠르게 스크롤 시 entries가 비어있을 수 있으므로
// 현재 모든 섹션 중에서 '상단 기준으로 가장 가까운' 것을 폴백 계산
const candidates: Array<{ id: string; top: number }> = []
for (const id of sectionIds.value) {
const el = document.getElementById(id)
if (!el) continue
const top = el.getBoundingClientRect().top - HEADER_HEIGHT
candidates.push({ id, top })
}
// 0에 가장 가까운 양수/음수 모두 고려(위/아래 가장 가까운)
candidates.sort((a, b) => Math.abs(a.top) - Math.abs(b.top))
if (candidates.length) activeSection.value = candidates[0].id
},
{
root: null,
// 상단 20%, 하단 60% 마진은 유지하되 헤더 보정치 반영
rootMargin: `-${Math.max(HEADER_HEIGHT, 20)}px 0px -60% 0px`,
threshold: 0,
}
)
}
const handleLnbClick = (lnbItem: PageDataLnbMenu) => {
if (!lnbItem?.page_ver_tmpl_name_en) return
scrollToLnb(lnbItem.page_ver_tmpl_name_en)
}
const observeSections = () => {
if (import.meta.server) return
if (!observerRef.value) createObserver()
const obs = observerRef.value!
obs.disconnect() // 기존 관찰 해제
// DOM 렌더 후 관찰 등록
requestAnimationFrame(() => {
for (const id of sectionIds.value) {
const el = document.getElementById(id)
if (el) obs.observe(el)
}
})
}
onMounted(() => {
if (import.meta.server) return
createObserver()
observeSections()
})
// lnb 데이터/DOM이 바뀌면 재관찰
watchEffect(async () => {
if (!import.meta.client) return
if (isShowLnb.value && sectionIds.value.length) {
await nextTick()
observeSections()
}
})
onUnmounted(() => {
observerRef.value?.disconnect()
observerRef.value = null
})
</script>
<template>
<div
v-if="isShowLnb"
class="lnb-wrap"
:style="{
'--lnb-active-color': activeColor,
'--lnb-disable-color': disableColor,
}"
>
<ul class="main-list">
<li v-for="lnbItem in lnbRoot" :key="lnbItem.path_code">
<button
v-dompurify-html="lnbItem.menu_name"
type="button"
:class="['btn-1depth', { 'is-active': is1DepthActive(lnbItem) }]"
@click="handleLnbClick(lnbItem)"
></button>
<ul v-if="getChildren(lnbItem).length > 0" class="sub-list">
<li v-for="subItem in getChildren(lnbItem)" :key="subItem.path_code">
<button
v-dompurify-html="subItem.menu_name"
type="button"
:class="[
'btn-2depth',
{
'is-active': activeSection === subItem.page_ver_tmpl_name_en,
},
]"
@click="handleLnbClick(subItem)"
></button>
</li>
</ul>
</li>
</ul>
</div>
</template>
<style scoped>
.lnb-wrap {
@apply fixed top-1/2 right-0 py-12 pr-4 text-right -translate-y-1/2 bg-[radial-gradient(100%_50%_at_100%_50%,rgba(0,0,0,0.5)_25%,rgba(0,0,0,0)_100%)] z-50;
}
.main-list {
@apply flex flex-col gap-4 items-end;
}
.btn-1depth {
@apply text-[15px] leading-[26px] tracking-[-0.54px];
}
.sub-list {
@apply flex flex-col gap-2 items-end mt-4 mb-1 pr-[46px];
}
.btn-2depth {
@apply text-[14px] leading-[20px] tracking-[-0.42px];
}
button {
@apply font-[500] text-[var(--lnb-disable-color)] transition-all duration-300 ease-in-out;
}
button:hover,
button.is-active {
@apply text-[var(--lnb-active-color)];
}
</style>

View File

@@ -1,32 +0,0 @@
<script setup lang="ts">
interface Props {
isShowTopBtn: boolean
isShowSnsBtn: boolean
}
const props = withDefaults(defineProps<Props>(), {
isShowTopBtn: false,
isShowSnsBtn: false,
})
const pinToMain = inject('pinToMain')
</script>
<template>
<ClientOnly>
<div :class="['utile-container', { 'is-stop': pinToMain }]">
<AtomsButtonScrollTop v-if="props.isShowTopBtn" />
<AtomsButtonSns v-if="props.isShowSnsBtn" />
</div>
</ClientOnly>
</template>
<style scoped>
.utile-container {
@apply fixed flex flex-col z-[100]
bottom-[12px] right-[12px] gap-2 md:bottom-[40px] md:right-[40px] md:gap-3;
}
.utile-container.is-stop {
@apply absolute;
}
</style>

View File

@@ -6,6 +6,9 @@ import type {
const { locale } = useI18n()
const gameDomain = useGetGameDomain()
const scrollStore = useScrollStore()
const { isPassedStoveGnb } = storeToRefs(scrollStore)
const isEventNavigationOpen = ref(true)
const eventNavigationList = ref<Record<string, EventNavigation>>({})
@@ -51,6 +54,7 @@ onMounted(async () => {
class="event-navigation"
:class="{
'is-closed': !isEventNavigationOpen,
'is-fixed': isPassedStoveGnb,
}"
>
<div class="navigation-wrapper">
@@ -91,7 +95,10 @@ onMounted(async () => {
<style scoped>
.event-navigation {
@apply fixed top-0 left-0 bottom-0 mt-[var(--scroll-position,48px)] pt-[48px] md:pt-[64px] z-[100] transition-transform duration-300 ease-in-out;
@apply absolute top-0 left-0 bottom-0 pt-[48px] md:pt-[64px] z-[100] transition-transform duration-300 ease-in-out;
}
.event-navigation.is-fixed {
@apply fixed;
}
.navigation-wrapper {
@apply relative h-full p-3 sm:p-5 sm:pr-3

View File

@@ -2,7 +2,6 @@
import { onClickOutside, useWindowSize } from '@vueuse/core'
import { useGameDataStore } from '#layers/stores/useGameDataStore'
import type {
GameDataValue,
GameDataMenu,
GameDataMenuChildren,
GameDataResourceGroup,
@@ -12,12 +11,15 @@ import type {
const route = useRoute()
const { width } = useWindowSize()
const gameDataStore = useGameDataStore()
const scrollStore = useScrollStore()
const { gameData } = storeToRefs(gameDataStore)
const { isPassedStoveGnb } = storeToRefs(scrollStore)
const navAreaRef = ref<HTMLElement>()
const startRef = ref<HTMLElement>()
const gameData = gameDataStore.gameData as GameDataValue
const gnbData = gameData?.gnb
const gnbData = gameData.value?.gnb
const isMenuOpen = ref(false)
const navWidth = ref(0)
const startWidth = ref(0)
@@ -57,9 +59,6 @@ const isNavItemActive = (gnbItem: GameDataMenu): boolean => {
return selfActive || hasActiveChild(gnbItem.children)
}
const handleMenuOpen = () => (isMenuOpen.value = true)
const handleMenuClose = () => (isMenuOpen.value = false)
// navAreaRef의 넓이를 구하는 함수
const calculateNavWidth = () => {
if (!navAreaRef.value || !gnbData) return 0
@@ -132,15 +131,26 @@ const calculateOverflow = () => {
}
}
const stopClickOutside = onClickOutside(
navAreaRef,
() => (isMenuOpen.value = false)
)
const handleMenuOpen = () => {
isMenuOpen.value = true
scrollStore.controlScrollLock(true)
}
const handleMenuClose = () => {
isMenuOpen.value = false
scrollStore.controlScrollLock(false)
}
const isNotClickable = (gnbItem: GameDataMenu) => {
return gnbItem.click_action_type === 0
}
const has2depthButton = (gnbItem: GameDataMenu) => {
return gnbItem.children && Object.keys(gnbItem.children).length > 0
}
const stopClickOutside = onClickOutside(navAreaRef, () => handleMenuClose())
// 화면 크기 변경 시 오버플로우 재계산
watch(width, () => {
calculateOverflow()
@@ -168,7 +178,7 @@ onBeforeUnmount(() => {
<template>
<header class="header">
<BlocksStoveGnbNew class="h-[48px]" />
<div class="game-wrap">
<div :class="['game-wrap', { 'is-fixed': isPassedStoveGnb }]">
<AtomsLocaleLink to="/brand" class="mx-auto md:hidden">
<img
:src="getImageHost(gnbData?.bi_path)"
@@ -205,11 +215,11 @@ onBeforeUnmount(() => {
}"
>
<AtomsLocaleLink
:to="gnbItem.url_path"
:to="isNotClickable(gnbItem) ? '#' : gnbItem.url_path"
:target="gnbItem.link_target"
:class="[
'nav-1depth',
{ 'has-link': !!gnbItem.url_path },
{ 'has-link': !isNotClickable(gnbItem) },
{ active: isNavItemActive(gnbItem) },
]"
>
@@ -354,9 +364,13 @@ onBeforeUnmount(() => {
@apply bg-theme-foreground text-theme-foreground-reversal relative z-[110];
}
.game-wrap {
@apply fixed top-0 flex w-full h-[48px] items-center whitespace-nowrap mt-[var(--scroll-position,48px)] px-[52px] bg-theme-foreground sm:px-[72px] md:h-16 md:pl-0 md:pr-[40px]
@apply absolute flex w-full h-[48px] items-center whitespace-nowrap px-[52px] bg-theme-foreground sm:px-[72px] md:h-16 md:pl-0 md:pr-[40px]
before:content-[''] before:absolute before:top-0 before:left-0 before:right-0 before:h-px before:bg-theme-foreground-reversal-6;
}
.game-wrap.is-fixed {
@apply fixed top-0;
}
.game-logo {
@apply mx-auto shrink-0 md:mx-0;
}
@@ -426,7 +440,7 @@ onBeforeUnmount(() => {
@apply bg-theme-foreground-reversal-8 md:bg-transparent;
}
.nav-1depth.has-link {
@apply hover:bg-theme-foreground-reversal-4 active:bg-theme-foreground-reversal-10 md:hover:bg-transparent md:active:bg-transparent;
@apply cursor-pointer hover:bg-theme-foreground-reversal-4 active:bg-theme-foreground-reversal-10 md:hover:bg-transparent md:active:bg-transparent;
}
.nav-2depth {

View File

@@ -15,6 +15,8 @@ const props = defineProps<Props>()
const { locale } = useI18n()
const { getTemplateComponent } = useTemplateRegistry()
const pinToMain = inject('pinToMain')
// 개별 메타 태그 표시 여부 확인
const shouldShowMetaTag = computed(() => props.pageData?.meta_tag_type === 2)
@@ -30,6 +32,9 @@ const isTemplateVisible = (template: PageDataTemplate): boolean => {
const visibleTemplates = computed(() =>
Object.values(props.pageData?.templates).filter(isTemplateVisible)
)
const isShowTopBtn = computed(() => props.pageData?.use_top_btn ?? false)
const isShowSnsBtn = computed(() => props.pageData?.use_sns_btn ?? false)
const isShowLnb = computed(() => props.pageData?.use_lnb ?? false)
// SEO 메타 태그 설정
const setupSeoMeta = (metaTag: PageDataMetaTag) => {
@@ -66,14 +71,21 @@ watchEffect(() => {
>
<component
:is="getTemplateComponent(template.template_code)"
:id="template.page_ver_tmpl_name_en"
:components="template.page_ver_tmpl_json"
:page-ver-tmpl-seq="template.page_ver_tmpl_seq"
/>
</template>
<BlocksUtileContainer
:is-show-top-btn="pageData.use_top_btn ?? false"
:is-show-sns-btn="pageData.use_sns_btn ?? false"
/>
<ClientOnly>
<BlocksLnb v-if="isShowLnb" />
<div
v-if="isShowTopBtn || isShowSnsBtn"
:class="['utile-wrap', { 'is-stop': pinToMain }]"
>
<AtomsButtonScrollTop v-if="isShowTopBtn" />
<AtomsButtonSns v-if="isShowSnsBtn" />
</div>
</ClientOnly>
</div>
</template>
@@ -81,6 +93,13 @@ watchEffect(() => {
.main-content {
@apply relative min-h-[200px] pt-[48px] md:min-h-[800px] md:pt-[64px];
}
.utile-wrap {
@apply fixed flex flex-col z-[100]
bottom-[12px] right-[12px] gap-2 md:bottom-[40px] md:right-[40px] md:gap-3;
}
.utile-wrap.is-stop {
@apply absolute;
}
[data-theme='light'] {
.main-content {

View File

@@ -15,11 +15,11 @@ const { locale } = useI18n()
const { sendLog, useAnalyticsLogDataDirect } = useAnalytics()
const getBtnType = (item?: PageDataResourceGroupBtnInfo): ButtonType => {
const t = item?.detail?.btn_type
const type = item?.detail?.btn_type
const target = item?.detail?.action?.link_target
if (t === 'URL' && target)
if (type === 'URL' && target)
return target === '_blank' ? 'external' : 'internal'
if (t === 'DOWNLOAD') return 'download'
if (type === 'DOWNLOAD') return 'download'
return 'action'
}

View File

@@ -1,9 +1,9 @@
<script setup lang="ts">
const isLoading = ref(true)
const maintRef = ref<HTMLElement>()
const mainRef = ref<HTMLElement>()
const { height: viewportH } = useWindowSize()
const { bottom: mainBottom } = useElementBounding(maintRef)
const { bottom: mainBottom } = useElementBounding(mainRef)
const pinToMain = computed(() => {
if (!mainBottom.value) return false
@@ -20,7 +20,7 @@ onMounted(() => {
<template>
<LayoutsHeader />
<AtomsLoadingSimple :is-loading="isLoading" />
<main id="LayoutsMain" class="relative">
<main id="LayoutsMain" ref="mainRef" class="relative">
<slot />
</main>
<LayoutsFooter />

View File

@@ -1,8 +1,8 @@
<script setup lang="ts">
const maintRef = ref<HTMLElement>()
const mainRef = ref<HTMLElement>()
const { height: viewportH } = useWindowSize()
const { bottom: mainBottom } = useElementBounding(maintRef)
const { bottom: mainBottom } = useElementBounding(mainRef)
const pinToMain = computed(() => {
if (!mainBottom.value) return false
@@ -14,7 +14,7 @@ provide('pinToMain', pinToMain)
<template>
<LayoutsHeader />
<main id="LayoutsMain" ref="maintRef" class="relative">
<main id="LayoutsMain" ref="mainRef" class="relative">
<LayoutsEventNavigation />
<slot />
</main>

View File

@@ -35,7 +35,7 @@ export interface PageDataValue {
use_lnb: boolean
lnb_text_color_code_active: string
lnb_text_color_code_deactive: string
lnb_menus: PageDataLnbMenu[]
lnb_menus: Record<string, PageDataLnbMenu>
meta_tag_json: PageDataMetaTag
templates: Record<string, PageDataTemplate>
}
@@ -51,6 +51,7 @@ export interface PageDataLnbMenu {
target_type: number
page_ver_tmpl_name_en: string
tracking_json: Record<string, PageDataTracking>
children?: Record<string, PageDataLnbMenu>
}
// 메타 태그 타입

View File

@@ -1,20 +1,10 @@
<svg width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
<foreignObject x="-26" y="-28" width="108" height="108"><div xmlns="http://www.w3.org/1999/xhtml" style="backdrop-filter:blur(15px);clip-path:url(#bgblur_0_3300_15070_clip_path);height:100%;width:100%"></div></foreignObject><g filter="url(#filter0_d_3300_15070)" data-figma-bg-blur-radius="30">
<path d="M4 26C4 12.7452 14.7452 2 28 2C41.2548 2 52 12.7452 52 26C52 39.2548 41.2548 50 28 50C14.7452 50 4 39.2548 4 26Z" fill="white" fill-opacity="0.1" shape-rendering="crispEdges"/>
<path d="M28 2.5C40.9787 2.5 51.5 13.0213 51.5 26C51.5 38.9787 40.9787 49.5 28 49.5C15.0213 49.5 4.5 38.9787 4.5 26C4.5 13.0213 15.0213 2.5 28 2.5Z" stroke="white" stroke-opacity="0.06" shape-rendering="crispEdges"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M24.2929 18.2929C23.9024 18.6834 23.9024 19.3166 24.2929 19.7071L30.5858 26L24.2929 32.2929C23.9024 32.6834 23.9024 33.3166 24.2929 33.7071C24.6834 34.0976 25.3166 34.0976 25.7071 33.7071L32.7071 26.7071C33.0976 26.3166 33.0976 25.6834 32.7071 25.2929L25.7071 18.2929C25.3166 17.9024 24.6834 17.9024 24.2929 18.2929Z" fill="white"/>
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<foreignObject x="-30" y="-30" width="108" height="108"><div xmlns="http://www.w3.org/1999/xhtml" style="backdrop-filter:blur(15px);clip-path:url(#bgblur_0_3300_15070_clip_path);height:100%;width:100%"></div></foreignObject><g data-figma-bg-blur-radius="30">
<path d="M0 24C0 10.7452 10.7452 0 24 0C37.2548 0 48 10.7452 48 24C48 37.2548 37.2548 48 24 48C10.7452 48 0 37.2548 0 24Z" fill="white" fill-opacity="0.1"/>
<path d="M24 0.5C36.9787 0.5 47.5 11.0213 47.5 24C47.5 36.9787 36.9787 47.5 24 47.5C11.0213 47.5 0.5 36.9787 0.5 24C0.5 11.0213 11.0213 0.5 24 0.5Z" stroke="white" stroke-opacity="0.06"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.2929 16.2929C19.9024 16.6834 19.9024 17.3166 20.2929 17.7071L26.5858 24L20.2929 30.2929C19.9024 30.6834 19.9024 31.3166 20.2929 31.7071C20.6834 32.0976 21.3166 32.0976 21.7071 31.7071L28.7071 24.7071C29.0976 24.3166 29.0976 23.6834 28.7071 23.2929L21.7071 16.2929C21.3166 15.9024 20.6834 15.9024 20.2929 16.2929Z" fill="white"/>
</g>
<defs>
<filter id="filter0_d_3300_15070" x="-26" y="-28" width="108" height="108" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="2"/>
<feGaussianBlur stdDeviation="2"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.06 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_3300_15070"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_3300_15070" result="shape"/>
</filter>
<clipPath id="bgblur_0_3300_15070_clip_path" transform="translate(26 28)"><path d="M4 26C4 12.7452 14.7452 2 28 2C41.2548 2 52 12.7452 52 26C52 39.2548 41.2548 50 28 50C14.7452 50 4 39.2548 4 26Z"/>
<clipPath id="bgblur_0_3300_15070_clip_path" transform="translate(30 30)"><path d="M0 24C0 10.7452 10.7452 0 24 0C37.2548 0 48 10.7452 48 24C48 37.2548 37.2548 48 24 48C10.7452 48 0 37.2548 0 24Z"/>
</clipPath></defs>
</svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -1,20 +1,10 @@
<svg width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
<foreignObject x="-26" y="-28" width="108" height="108"><div xmlns="http://www.w3.org/1999/xhtml" style="backdrop-filter:blur(15px);clip-path:url(#bgblur_0_3300_15069_clip_path);height:100%;width:100%"></div></foreignObject><g filter="url(#filter0_d_3300_15069)" data-figma-bg-blur-radius="30">
<path d="M4 26C4 12.7452 14.7452 2 28 2C41.2548 2 52 12.7452 52 26C52 39.2548 41.2548 50 28 50C14.7452 50 4 39.2548 4 26Z" fill="white" fill-opacity="0.1" shape-rendering="crispEdges"/>
<path d="M28 2.5C40.9787 2.5 51.5 13.0213 51.5 26C51.5 38.9787 40.9787 49.5 28 49.5C15.0213 49.5 4.5 38.9787 4.5 26C4.5 13.0213 15.0213 2.5 28 2.5Z" stroke="white" stroke-opacity="0.06" shape-rendering="crispEdges"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M31.7071 18.2929C32.0976 18.6834 32.0976 19.3166 31.7071 19.7071L25.4142 26L31.7071 32.2929C32.0976 32.6834 32.0976 33.3166 31.7071 33.7071C31.3166 34.0976 30.6834 34.0976 30.2929 33.7071L23.2929 26.7071C22.9024 26.3166 22.9024 25.6834 23.2929 25.2929L30.2929 18.2929C30.6834 17.9024 31.3166 17.9024 31.7071 18.2929Z" fill="white"/>
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<foreignObject x="-30" y="-30" width="108" height="108"><div xmlns="http://www.w3.org/1999/xhtml" style="backdrop-filter:blur(15px);clip-path:url(#bgblur_0_3300_15069_clip_path);height:100%;width:100%"></div></foreignObject><g data-figma-bg-blur-radius="30">
<path d="M0 24C0 10.7452 10.7452 0 24 0C37.2548 0 48 10.7452 48 24C48 37.2548 37.2548 48 24 48C10.7452 48 0 37.2548 0 24Z" fill="white" fill-opacity="0.1"/>
<path d="M24 0.5C36.9787 0.5 47.5 11.0213 47.5 24C47.5 36.9787 36.9787 47.5 24 47.5C11.0213 47.5 0.5 36.9787 0.5 24C0.5 11.0213 11.0213 0.5 24 0.5Z" stroke="white" stroke-opacity="0.06"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M27.7071 16.2929C28.0976 16.6834 28.0976 17.3166 27.7071 17.7071L21.4142 24L27.7071 30.2929C28.0976 30.6834 28.0976 31.3166 27.7071 31.7071C27.3166 32.0976 26.6834 32.0976 26.2929 31.7071L19.2929 24.7071C18.9024 24.3166 18.9024 23.6834 19.2929 23.2929L26.2929 16.2929C26.6834 15.9024 27.3166 15.9024 27.7071 16.2929Z" fill="white"/>
</g>
<defs>
<filter id="filter0_d_3300_15069" x="-26" y="-28" width="108" height="108" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="2"/>
<feGaussianBlur stdDeviation="2"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.06 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_3300_15069"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_3300_15069" result="shape"/>
</filter>
<clipPath id="bgblur_0_3300_15069_clip_path" transform="translate(26 28)"><path d="M4 26C4 12.7452 14.7452 2 28 2C41.2548 2 52 12.7452 52 26C52 39.2548 41.2548 50 28 50C14.7452 50 4 39.2548 4 26Z"/>
<clipPath id="bgblur_0_3300_15069_clip_path" transform="translate(30 30)"><path d="M0 24C0 10.7452 10.7452 0 24 0C37.2548 0 48 10.7452 48 24C48 37.2548 37.2548 48 24 48C10.7452 48 0 37.2548 0 24Z"/>
</clipPath></defs>
</svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -1,10 +1,15 @@
<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<foreignObject x="0" y="0" width="100" height="100"><div xmlns="http://www.w3.org/1999/xhtml" style="backdrop-filter:blur(5px);clip-path:url(#bgblur_0_3299_15025_clip_path);height:100%;width:100%"></div></foreignObject><g data-figma-bg-blur-radius="10">
<circle cx="50" cy="50" r="40" fill="black" fill-opacity="0.5"/>
<circle cx="50" cy="50" r="39.5" stroke="white" stroke-opacity="0.5"/>
<svg width="80" height="80" viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_3299_15025)">
<foreignObject x="-10" y="-10" width="100" height="100"><div xmlns="http://www.w3.org/1999/xhtml" style="backdrop-filter:blur(5px);clip-path:url(#bgblur_1_3299_15025_clip_path);height:100%;width:100%"></div></foreignObject><g data-figma-bg-blur-radius="10">
<circle cx="40" cy="40" r="40" fill="black" fill-opacity="0.5"/>
<circle cx="40" cy="40" r="39.5" stroke="white" stroke-opacity="0.5"/>
</g>
<path fill-rule="evenodd" clip-rule="evenodd" d="M38.4839 49.0812C36.7639 50.0629 35.079 50.9852 33.4291 51.8405C32.4954 52.3239 31.4072 51.6545 31.337 50.5612C31.2106 48.62 31.1193 46.6119 31.0632 44.5443C31.0211 43.0568 31 41.5396 31 40C31 38.4604 31.0211 36.9506 31.0632 35.4557C31.1193 33.3881 31.2106 31.38 31.337 29.4388C31.4072 28.3455 32.4954 27.6761 33.4291 28.1595C35.079 29.0149 36.7709 29.9371 38.4839 30.9188C39.7196 31.6254 40.9692 32.3617 42.2259 33.1352C43.4826 33.9087 44.7112 34.6822 45.9047 35.4631C47.5686 36.549 49.1622 37.6349 50.6857 38.7208C51.5492 39.3306 51.5492 40.6694 50.6857 41.2793C49.1622 42.3651 47.5615 43.4584 45.9047 44.5369C44.7112 45.3178 43.4826 46.0987 42.2259 46.8648C40.9692 47.6383 39.7196 48.3672 38.4839 49.0812Z" fill="white" fill-opacity="0.5"/>
</g>
<path fill-rule="evenodd" clip-rule="evenodd" d="M48.4839 59.0812C46.7639 60.0629 45.079 60.9852 43.4291 61.8405C42.4954 62.3239 41.4072 61.6545 41.337 60.5612C41.2106 58.62 41.1193 56.6119 41.0632 54.5443C41.0211 53.0568 41 51.5396 41 50C41 48.4604 41.0211 46.9506 41.0632 45.4557C41.1193 43.3881 41.2106 41.38 41.337 39.4388C41.4072 38.3455 42.4954 37.6761 43.4291 38.1595C45.079 39.0149 46.7709 39.9371 48.4839 40.9188C49.7196 41.6254 50.9692 42.3617 52.2259 43.1352C53.4826 43.9087 54.7112 44.6822 55.9047 45.4631C57.5686 46.549 59.1622 47.6349 60.6857 48.7208C61.5492 49.3306 61.5492 50.6694 60.6857 51.2793C59.1622 52.3651 57.5615 53.4584 55.9047 54.5369C54.7112 55.3178 53.4826 56.0987 52.2259 56.8648C50.9692 57.6383 49.7196 58.3672 48.4839 59.0812Z" fill="white" fill-opacity="0.5"/>
<defs>
<clipPath id="bgblur_0_3299_15025_clip_path" transform="translate(0 0)"><circle cx="50" cy="50" r="40"/>
</clipPath></defs>
<clipPath id="bgblur_1_3299_15025_clip_path" transform="translate(10 10)"><circle cx="40" cy="40" r="40"/>
</clipPath><clipPath id="clip0_3299_15025">
<rect width="80" height="80" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -1,21 +1,11 @@
<svg width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
<foreignObject x="-26" y="-28" width="108" height="108"><div xmlns="http://www.w3.org/1999/xhtml" style="backdrop-filter:blur(15px);clip-path:url(#bgblur_0_3300_15073_clip_path);height:100%;width:100%"></div></foreignObject><g filter="url(#filter0_d_3300_15073)" data-figma-bg-blur-radius="30">
<path d="M4 26C4 12.7452 14.7452 2 28 2C41.2548 2 52 12.7452 52 26C52 39.2548 41.2548 50 28 50C14.7452 50 4 39.2548 4 26Z" fill="white" fill-opacity="0.1" shape-rendering="crispEdges"/>
<path d="M28 2.5C40.9787 2.5 51.5 13.0213 51.5 26C51.5 38.9787 40.9787 49.5 28 49.5C15.0213 49.5 4.5 38.9787 4.5 26C4.5 13.0213 15.0213 2.5 28 2.5Z" stroke="white" stroke-opacity="0.06" shape-rendering="crispEdges"/>
<path d="M29 23.4142L33.2929 27.7071C33.6834 28.0976 34.3166 28.0976 34.7071 27.7071C35.0976 27.3166 35.0976 26.6834 34.7071 26.2929L28.7078 20.2936C28.5289 20.1143 28.2822 20.0026 28.0094 20C28.0063 20 28.0032 20 28 20C27.9968 20 27.9937 20 27.9906 20C27.7269 20.0025 27.4877 20.1069 27.3104 20.2758C27.3045 20.2815 27.2987 20.2871 27.2929 20.2929L21.2929 26.2929C20.9024 26.6834 20.9024 27.3166 21.2929 27.7071C21.6834 28.0976 22.3166 28.0976 22.7071 27.7071L27 23.4142L27 34C27 34.5523 27.4477 35 28 35C28.5523 35 29 34.5523 29 34L29 23.4142Z" fill="white" fill-opacity="0.5"/>
<path d="M35.5 18C35.5 18.5523 35.0523 19 34.5 19L21.5 19C20.9477 19 20.5 18.5523 20.5 18C20.5 17.4477 20.9477 17 21.5 17L34.5 17C35.0523 17 35.5 17.4477 35.5 18Z" fill="white" fill-opacity="0.5"/>
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<foreignObject x="-30" y="-30" width="108" height="108"><div xmlns="http://www.w3.org/1999/xhtml" style="backdrop-filter:blur(15px);clip-path:url(#bgblur_0_3300_15073_clip_path);height:100%;width:100%"></div></foreignObject><g data-figma-bg-blur-radius="30">
<path d="M0 24C0 10.7452 10.7452 0 24 0C37.2548 0 48 10.7452 48 24C48 37.2548 37.2548 48 24 48C10.7452 48 0 37.2548 0 24Z" fill="white" fill-opacity="0.1"/>
<path d="M24 0.5C36.9787 0.5 47.5 11.0213 47.5 24C47.5 36.9787 36.9787 47.5 24 47.5C11.0213 47.5 0.5 36.9787 0.5 24C0.5 11.0213 11.0213 0.5 24 0.5Z" stroke="white" stroke-opacity="0.06"/>
<path d="M25 21.4142L29.2929 25.7071C29.6834 26.0976 30.3166 26.0976 30.7071 25.7071C31.0976 25.3166 31.0976 24.6834 30.7071 24.2929L24.7078 18.2936C24.5289 18.1143 24.2822 18.0026 24.0094 18C24.0063 18 24.0032 18 24 18C23.9968 18 23.9937 18 23.9906 18C23.7269 18.0025 23.4877 18.1069 23.3104 18.2758C23.3045 18.2815 23.2987 18.2871 23.2929 18.2929L17.2929 24.2929C16.9024 24.6834 16.9024 25.3166 17.2929 25.7071C17.6834 26.0976 18.3166 26.0976 18.7071 25.7071L23 21.4142L23 32C23 32.5523 23.4477 33 24 33C24.5523 33 25 32.5523 25 32L25 21.4142Z" fill="white" fill-opacity="0.5"/>
<path d="M31.5 16C31.5 16.5523 31.0523 17 30.5 17L17.5 17C16.9477 17 16.5 16.5523 16.5 16C16.5 15.4477 16.9477 15 17.5 15L30.5 15C31.0523 15 31.5 15.4477 31.5 16Z" fill="white" fill-opacity="0.5"/>
</g>
<defs>
<filter id="filter0_d_3300_15073" x="-26" y="-28" width="108" height="108" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="2"/>
<feGaussianBlur stdDeviation="2"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.06 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_3300_15073"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_3300_15073" result="shape"/>
</filter>
<clipPath id="bgblur_0_3300_15073_clip_path" transform="translate(26 28)"><path d="M4 26C4 12.7452 14.7452 2 28 2C41.2548 2 52 12.7452 52 26C52 39.2548 41.2548 50 28 50C14.7452 50 4 39.2548 4 26Z"/>
<clipPath id="bgblur_0_3300_15073_clip_path" transform="translate(30 30)"><path d="M0 24C0 10.7452 10.7452 0 24 0C37.2548 0 48 10.7452 48 24C48 37.2548 37.2548 48 24 48C10.7452 48 0 37.2548 0 24Z"/>
</clipPath></defs>
</svg>

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -1,3 +0,0 @@
<svg width="32" height="33" viewBox="0 0 32 33" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M26.2768 8.10947C26.7975 7.58877 26.7975 6.74455 26.2768 6.22385C25.7561 5.70315 24.9119 5.70315 24.3912 6.22385L16.0007 14.6144L7.61013 6.22385C7.08943 5.70315 6.24521 5.70315 5.72451 6.22385C5.20381 6.74455 5.20381 7.58877 5.72451 8.10947L14.115 16.5L5.72451 24.8905C5.20381 25.4112 5.20381 26.2554 5.72451 26.7761C6.24521 27.2968 7.08943 27.2968 7.61013 26.7761L16.0007 18.3856L24.3912 26.7761C24.9119 27.2968 25.7561 27.2968 26.2768 26.7761C26.7975 26.2554 26.7975 25.4112 26.2768 24.8905L17.8863 16.5L26.2768 8.10947Z" fill="#EBEBEB"/>
</svg>

Before

Width:  |  Height:  |  Size: 653 B