File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -15,6 +15,7 @@ const commitUrl = computed(() =>
1515const { stats } = useSiteStats ()
1616const totalViews = computed (() => stats .value .totalViews ?.toLocaleString (' en-US' ) ?? ' —' )
1717const onlineVisitors = computed (() => stats .value .onlineVisitors ?.toLocaleString (' en-US' ) ?? ' —' )
18+ const isPresenceConnected = computed (() => stats .value .onlineVisitors !== null )
1819
1920const socialLinks = [
2021 { label: ' GitHub' , href: ' https://github.com/zyyv' , external: true },
@@ -44,7 +45,10 @@ function backToTop() {
4445 <dd >{{ totalViews }}</dd >
4546 </div >
4647 <div >
47- <dt ><span class =" site-footer__live-dot" aria-hidden =" true" /> Online</dt >
48+ <dt class =" site-footer__online-label" >
49+ <StatusDot :active =" isPresenceConnected " pulse />
50+ Online
51+ </dt >
4852 <dd >{{ onlineVisitors }}</dd >
4953 </div >
5054 </dl >
@@ -171,15 +175,10 @@ function backToTop() {
171175 color : color-mix(in srgb , currentColor 48% , transparent );
172176}
173177
174- .site-footer__live-dot {
175- display : inline-block ;
176- width : 0.38rem ;
177- height : 0.38rem ;
178- margin-right : 0.18rem ;
179- border-radius : 50% ;
180- background : #4f9a68 ;
181- box-shadow : 0 0 0 0.18rem color-mix(in srgb , #4f9a68 18% , transparent );
182- vertical-align : 0.04rem ;
178+ .site-footer__online-label {
179+ display : inline-flex ;
180+ align-items : center ;
181+ gap : 0.35rem ;
183182}
184183
185184.site-footer__links a {
Original file line number Diff line number Diff line change 1+ <script setup lang="ts">
2+ interface Props {
3+ active? : boolean
4+ color? : string
5+ pulse? : boolean
6+ }
7+
8+ const props = withDefaults (defineProps <Props >(), {
9+ active: true ,
10+ color: ' #4f9a68' ,
11+ pulse: false ,
12+ })
13+
14+ const dotStyle = computed (() => ({ ' --status-dot-color' : props .color }))
15+ </script >
16+
17+ <template >
18+ <span
19+ class =" status-dot"
20+ :class =" {
21+ 'status-dot--active': active,
22+ 'status-dot--pulse': active && pulse,
23+ }"
24+ :style =" dotStyle"
25+ aria-hidden =" true"
26+ />
27+ </template >
28+
29+ <style scoped>
30+ .status-dot {
31+ position : relative ;
32+ display : inline-block ;
33+ width : 0.4rem ;
34+ height : 0.4rem ;
35+ flex : 0 0 auto ;
36+ border-radius : 50% ;
37+ background : color-mix(in srgb , currentColor 28% , transparent );
38+ }
39+
40+ .status-dot--active {
41+ background : var (--status-dot-color );
42+ box-shadow : 0 0 0 0.15rem color-mix(in srgb , var (--status-dot-color ) 16% , transparent );
43+ }
44+
45+ .status-dot--pulse ::after {
46+ position : absolute ;
47+ inset : -0.16rem ;
48+ border : 1px solid color-mix(in srgb , var (--status-dot-color ) 58% , transparent );
49+ border-radius : inherit ;
50+ content : ' ' ;
51+ opacity : 0 ;
52+ transform : scale (0.72 );
53+ animation : status-dot-pulse 2.2s cubic-bezier (0.16 , 1 , 0.3 , 1 ) infinite ;
54+ pointer-events : none ;
55+ }
56+
57+ @keyframes status-dot-pulse {
58+ 0% {
59+ opacity : 0 ;
60+ transform : scale (0.72 );
61+ }
62+
63+ 14% {
64+ opacity : 0.62 ;
65+ }
66+
67+ 72% ,
68+ 100% {
69+ opacity : 0 ;
70+ transform : scale (1.55 );
71+ }
72+ }
73+
74+ @media (prefers-reduced-motion: reduce) {
75+ .status-dot--pulse ::after {
76+ opacity : 0.38 ;
77+ transform : scale (1 );
78+ animation : none ;
79+ }
80+ }
81+ </style >
You can’t perform that action at this time.
0 commit comments