DocFooterExtra.vue (9415B)
1 <template> 2 <footer class="doc-footer-extra" :class="{ 'is-home': isHome }"> 3 <!-- Decorative divider: rising entrance animation on scroll --> 4 <div 5 ref="dividerRef" 6 class="footer-divider" 7 :class="{ 'is-visible': isDividerVisible }" 8 aria-hidden="true" 9 > 10 <img 11 src="/skyline3.svg" 12 alt="" 13 class="footer-divider-img" 14 width="1000" 15 height="460" 16 loading="lazy" 17 decoding="async" 18 /> 19 </div> 20 21 <!-- Top Row --> 22 <div class="footer-top-row"> 23 <a 24 href="https://github.com/notamitgamer/bsc" 25 target="_blank" 26 rel="noopener noreferrer" 27 class="gh-mark" 28 aria-label="View BSc Repository on GitHub" 29 > 30 <svg viewBox="0 0 16 16" width="20" height="20" fill="currentColor"> 31 <path 32 d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27 c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8Z" 33 /> 34 </svg> 35 </a> 36 37 <button class="back-to-top" type="button" @click="scrollTop"> 38 Back to top 39 <svg 40 viewBox="0 0 16 16" 41 width="13" 42 height="13" 43 fill="none" 44 stroke="currentColor" 45 stroke-width="2" 46 > 47 <path 48 d="M8 13V3M3 8l5-5 5 5" 49 stroke-linecap="round" 50 stroke-linejoin="round" 51 /> 52 </svg> 53 </button> 54 </div> 55 56 <!-- Plain Corporate Columns --> 57 <div class="footer-columns"> 58 <div class="col"> 59 <h3 class="col-title">Help Improve</h3> 60 <p class="col-text"> 61 Spotted an error, outdated syllabus item, or missing code? All coursework documentation is open source. 62 </p> 63 <a 64 href="https://github.com/notamitgamer/bsc/issues/new/choose" 65 target="_blank" 66 rel="noopener noreferrer" 67 class="col-link" 68 > 69 Suggest an edit → 70 </a> 71 </div> 72 73 <div class="col"> 74 <h3 class="col-title">Automation & Scripts</h3> 75 <p class="col-text"> 76 Review, run, or patch the Markdown scrapers, syllabus crawlers, and generator pipelines. 77 </p> 78 <a 79 href="https://github.com/notamitgamer/bsc/tree/main/utils/bsc_md" 80 target="_blank" 81 rel="noopener noreferrer" 82 class="col-link" 83 > 84 View scripts repository → 85 </a> 86 </div> 87 88 <div class="col"> 89 <h3 class="col-title">Community & Support</h3> 90 <ul class="help-links"> 91 <li> 92 <a 93 href="https://github.com/notamitgamer/bsc/discussions" 94 target="_blank" 95 rel="noopener noreferrer" 96 > 97 Ask a question 98 </a> 99 </li> 100 <li> 101 <a 102 href="https://github.com/notamitgamer/bsc/issues" 103 target="_blank" 104 rel="noopener noreferrer" 105 > 106 Report an issue 107 </a> 108 </li> 109 <li> 110 <a href="/changelog">Changelog</a> 111 </li> 112 </ul> 113 </div> 114 </div> 115 116 <!-- Corporate Legal Bottom Bar --> 117 <div class="footer-bottom-bar"> 118 <div class="copyright-line"> 119 <span>© 2025–2026 </span> 120 <a 121 href="https://github.com/notamitgamer" 122 target="_blank" 123 rel="noopener noreferrer" 124 class="author-link" 125 > 126 notamitgamer 127 </a> 128 <span class="license-sep">•</span> 129 <span>All content is licensed under the </span> 130 <a 131 href="https://github.com/notamitgamer/bsc/blob/main/LICENSE" 132 target="_blank" 133 rel="noopener noreferrer" 134 class="license-link" 135 > 136 MIT License 137 </a> 138 </div> 139 140 <div class="bottom-links"> 141 <a href="/terms">Terms</a> 142 <a 143 href="https://github.com/notamitgamer/bsc/blob/main/LICENSE" 144 target="_blank" 145 rel="noopener noreferrer" 146 > 147 License 148 </a> 149 <a 150 href="https://github.com/notamitgamer/bsc" 151 target="_blank" 152 rel="noopener noreferrer" 153 > 154 GitHub 155 </a> 156 </div> 157 </div> 158 </footer> 159 </template> 160 161 <script setup> 162 import { ref, computed, onMounted, onUnmounted } from 'vue' 163 import { useData } from 'vitepress' 164 165 const { page } = useData() 166 const isHome = computed(() => page.value?.frontmatter?.layout === 'home') 167 168 const dividerRef = ref(null) 169 const isDividerVisible = ref(false) 170 let observer = null 171 172 function scrollTop() { 173 window.scrollTo({ top: 0, behavior: 'smooth' }) 174 } 175 176 onMounted(() => { 177 if (typeof window === 'undefined' || !('IntersectionObserver' in window)) { 178 isDividerVisible.value = true 179 return 180 } 181 182 observer = new IntersectionObserver( 183 (entries) => { 184 const [entry] = entries 185 if (entry.isIntersecting) { 186 isDividerVisible.value = true 187 if (dividerRef.value) observer.unobserve(dividerRef.value) 188 } 189 }, 190 { threshold: 0.15 } 191 ) 192 193 if (dividerRef.value) { 194 observer.observe(dividerRef.value) 195 } 196 }) 197 198 onUnmounted(() => { 199 if (observer) observer.disconnect() 200 }) 201 </script> 202 203 <style scoped> 204 .doc-footer-extra { 205 margin-top: 3.5rem; 206 border-top: 1px solid var(--vp-c-divider); 207 padding-top: 0; 208 font-family: var(--vp-font-family-base); 209 clear: both; 210 width: 100%; 211 max-width: 688px; 212 margin-left: auto; 213 margin-right: auto; 214 padding-left: 24px; 215 padding-right: 24px; 216 box-sizing: border-box; 217 } 218 219 .doc-footer-extra.is-home { 220 max-width: 1152px; 221 padding-left: 64px; 222 padding-right: 64px; 223 } 224 225 @media (max-width: 960px) { 226 .doc-footer-extra.is-home { 227 padding-left: 32px; 228 padding-right: 32px; 229 } 230 } 231 232 @media (max-width: 640px) { 233 .doc-footer-extra, 234 .doc-footer-extra.is-home { 235 padding-left: 16px; 236 padding-right: 16px; 237 } 238 } 239 240 .doc-footer-extra button, 241 .doc-footer-extra a { 242 font-family: inherit; 243 } 244 245 /* Decorative Divider with Smooth Scroll Elevation */ 246 .footer-divider { 247 width: 100%; 248 position: relative; 249 display: flex; 250 justify-content: center; 251 align-items: flex-end; 252 line-height: 0; 253 margin-bottom: 0.5rem; 254 opacity: 0; 255 transform: translateY(28px); 256 transition: opacity 0.7s cubic-bezier(0.16, 1, 0.3, 1), 257 transform 0.7s cubic-bezier(0.16, 1, 0.3, 1); 258 will-change: transform, opacity; 259 overflow: hidden; 260 } 261 262 .footer-divider.is-visible { 263 opacity: 0.95; 264 transform: translateY(0); 265 } 266 267 @media (prefers-reduced-motion: reduce) { 268 .footer-divider { 269 transition: none !important; 270 transform: none !important; 271 opacity: 0.95 !important; 272 } 273 } 274 275 .footer-divider-img { 276 width: 100%; 277 height: clamp(140px, 18vw, 240px); 278 object-fit: contain; 279 object-position: bottom center; 280 display: block; 281 } 282 283 /* Top Action Row */ 284 .footer-top-row { 285 display: flex; 286 align-items: center; 287 justify-content: space-between; 288 padding: 0.85rem 0; 289 border-bottom: 1px solid var(--vp-c-divider); 290 color: var(--vp-c-text-2); 291 } 292 293 .gh-mark { 294 color: var(--vp-c-text-2); 295 display: flex; 296 } 297 298 .back-to-top { 299 display: flex; 300 align-items: center; 301 gap: 6px; 302 background: none; 303 border: none; 304 color: var(--vp-c-text-2); 305 font-size: 13px; 306 cursor: pointer; 307 font-weight: 500; 308 } 309 310 /* Plain Corporate Columns */ 311 .footer-columns { 312 display: grid; 313 grid-template-columns: repeat(3, 1fr); 314 gap: 2rem; 315 padding: 1.75rem 0; 316 border-bottom: 1px solid var(--vp-c-divider); 317 } 318 319 @media (max-width: 768px) { 320 .footer-columns { 321 grid-template-columns: 1fr; 322 gap: 1.5rem; 323 } 324 } 325 326 .col { 327 display: flex; 328 flex-direction: column; 329 } 330 331 .col-title { 332 font-size: 13px; 333 font-weight: 600; 334 letter-spacing: 0.02em; 335 color: var(--vp-c-text-1); 336 margin: 0 0 0.5rem; 337 } 338 339 .col-text { 340 font-size: 13px; 341 color: var(--vp-c-text-2); 342 line-height: 1.55; 343 margin: 0 0 0.75rem; 344 } 345 346 .col-link { 347 font-size: 13px; 348 font-weight: 500; 349 color: var(--vp-c-brand-1); 350 text-decoration: none; 351 } 352 353 .help-links { 354 list-style: none; 355 margin: 0; 356 padding: 0; 357 display: flex; 358 flex-direction: column; 359 gap: 0.45rem; 360 } 361 362 .help-links a { 363 font-size: 13px; 364 color: var(--vp-c-text-2); 365 text-decoration: none; 366 } 367 368 /* Plain Bottom Legal Bar */ 369 .footer-bottom-bar { 370 display: flex; 371 justify-content: space-between; 372 align-items: center; 373 flex-wrap: wrap; 374 gap: 0.75rem; 375 padding: 1.25rem 0; 376 font-size: 12.5px; 377 color: var(--vp-c-text-3, var(--vp-c-text-2)); 378 } 379 380 .copyright-line { 381 display: inline-flex; 382 align-items: center; 383 flex-wrap: wrap; 384 gap: 4px; 385 } 386 387 .author-link { 388 font-weight: 500; 389 color: var(--vp-c-text-1); 390 text-decoration: none; 391 } 392 393 .license-sep { 394 margin: 0 4px; 395 opacity: 0.6; 396 } 397 398 .license-link { 399 color: var(--vp-c-text-2); 400 text-decoration: underline; 401 text-underline-offset: 2px; 402 } 403 404 .bottom-links { 405 display: flex; 406 gap: 1.25rem; 407 } 408 409 .bottom-links a { 410 color: inherit; 411 text-decoration: none; 412 } 413 </style>