Initialize css-refactor
This commit is contained in:
389
src/layouts/Layout.astro
Normal file
389
src/layouts/Layout.astro
Normal file
@@ -0,0 +1,389 @@
|
||||
---
|
||||
import '@fontsource/roboto/400.css'
|
||||
import '@fontsource/roboto/500.css'
|
||||
import '@fontsource/roboto/700.css'
|
||||
import ImageWrapper from '@components/misc/ImageWrapper.astro'
|
||||
|
||||
import { profileConfig, siteConfig } from '@/config'
|
||||
import ConfigCarrier from '@components/ConfigCarrier.astro'
|
||||
import {
|
||||
AUTO_MODE,
|
||||
DARK_MODE,
|
||||
DEFAULT_THEME,
|
||||
LIGHT_MODE,
|
||||
} from '../constants/constants'
|
||||
import { defaultFavicons } from '../constants/icon'
|
||||
import type { Favicon } from '../types/config'
|
||||
import { url, pathsEqual } from '../utils/url-utils'
|
||||
|
||||
import '../styles/main.css' // let Astro catch postcss errors
|
||||
import '../styles/variables.styl'
|
||||
|
||||
interface Props {
|
||||
title?: string
|
||||
banner?: string
|
||||
description?: string
|
||||
}
|
||||
|
||||
let { title, banner, description } = Astro.props
|
||||
|
||||
// apply a class to the body element to decide the height of the banner, only used for initial page load
|
||||
// Swup can update the body for each page visit, but it's after the page transition, causing a delay for banner height change
|
||||
// so use Swup hooks instead to change the height immediately when a link is clicked
|
||||
const isHomePage = pathsEqual(Astro.url.pathname, url('/'))
|
||||
|
||||
// defines global css variables
|
||||
// why doing this in Layout instead of GlobalStyles: https://github.com/withastro/astro/issues/6728#issuecomment-1502203757
|
||||
const configHue = siteConfig.themeColor.hue
|
||||
if (!banner || typeof banner !== 'string' || banner.trim() === '') {
|
||||
banner = siteConfig.banner.src
|
||||
}
|
||||
|
||||
// TODO don't use post cover as banner for now
|
||||
banner = siteConfig.banner.src
|
||||
|
||||
const enableBanner = siteConfig.banner.enable
|
||||
|
||||
let pageTitle: string
|
||||
if (title) {
|
||||
pageTitle = `${title} - ${siteConfig.title}`
|
||||
} else {
|
||||
pageTitle = `${siteConfig.title} - ${siteConfig.subtitle}`
|
||||
}
|
||||
|
||||
const favicons: Favicon[] =
|
||||
siteConfig.favicon.length > 0 ? siteConfig.favicon : defaultFavicons
|
||||
|
||||
const siteLang = siteConfig.lang.replace('_', '-')
|
||||
---
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang={siteLang} class="bg-[var(--page-bg)] transition text-[14px] md:text-[16px]">
|
||||
<head>
|
||||
|
||||
<title>{pageTitle}</title>
|
||||
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="description" content={description || pageTitle}>
|
||||
<meta name="author" content={profileConfig.name}>
|
||||
|
||||
<meta property="og:site_name" content={siteConfig.title}>
|
||||
<meta property="og:url" content={Astro.url}>
|
||||
<meta property="og:title" content={pageTitle}>
|
||||
<meta property="og:description" content={description || pageTitle}>
|
||||
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta property="twitter:url" content={Astro.url}>
|
||||
<meta name="twitter:title" content={pageTitle}>
|
||||
<meta name="twitter:description" content={description || pageTitle}>
|
||||
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
{favicons.map(favicon => (
|
||||
<link rel="icon"
|
||||
href={favicon.src.startsWith('/') ? url(favicon.src) : favicon.src}
|
||||
sizes={favicon.sizes}
|
||||
media={favicon.theme && `(prefers-color-scheme: ${favicon.theme})`}
|
||||
/>
|
||||
))}
|
||||
<!-- Set the theme before the page is rendered to avoid a flash -->
|
||||
<script is:inline define:vars={{DEFAULT_THEME: DEFAULT_THEME, LIGHT_MODE: LIGHT_MODE, DARK_MODE: DARK_MODE, AUTO_MODE: AUTO_MODE}}>
|
||||
const theme = localStorage.getItem('theme') || DEFAULT_THEME;
|
||||
switch (theme) {
|
||||
case LIGHT_MODE:
|
||||
document.documentElement.classList.remove('dark');
|
||||
break
|
||||
case DARK_MODE:
|
||||
document.documentElement.classList.add('dark');
|
||||
break
|
||||
case AUTO_MODE:
|
||||
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
||||
document.documentElement.classList.add('dark');
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<slot name="head"></slot>
|
||||
|
||||
<link rel="stylesheet" href="https://cdn.staticfile.net/KaTeX/0.16.9/katex.min.css" integrity="sha384-n8MVd4RsNIU0tAv4ct0nTaAbDJwPJzDEaqSD1odI+WdtXRGWt2kTvGFasHpSy3SV" crossorigin="anonymous">
|
||||
<link rel="alternate" type="application/rss+xml" title={profileConfig.name} href={`${Astro.site}rss.xml`}/>
|
||||
|
||||
<style define:vars={{ configHue }}></style> <!-- defines global css variables. This will be applied to <html> <body> and some other elements idk why -->
|
||||
|
||||
</head>
|
||||
<body class=" min-h-screen transition " class:list={[{"is-home": isHomePage, "enable-banner": enableBanner}]}>
|
||||
<ConfigCarrier></ConfigCarrier>
|
||||
<div id="banner-wrapper" class="absolute w-full transition-all duration-700">
|
||||
<ImageWrapper id="boxtest" alt="Banner image of the blog" class:list={["object-cover h-full", {"hidden": !siteConfig.banner.enable}]}
|
||||
src={siteConfig.banner.src} position={siteConfig.banner.position}
|
||||
>
|
||||
</ImageWrapper>
|
||||
</div>
|
||||
<slot />
|
||||
</body>
|
||||
</html>
|
||||
<style is:global>
|
||||
:root {
|
||||
--hue: var(--configHue);
|
||||
--page-width: 75rem;
|
||||
}
|
||||
</style>
|
||||
<style is:global>
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer components {
|
||||
.enable-banner.is-home #banner-wrapper {
|
||||
@apply h-[var(--banner-height)] md:h-[var(--banner-height-home)]
|
||||
}
|
||||
.enable-banner #banner-wrapper {
|
||||
@apply h-[var(--banner-height)]
|
||||
}
|
||||
|
||||
.enable-banner.is-home #top-row {
|
||||
@apply h-[calc(var(--banner-height)_-_4.5rem)] md:h-[calc(var(--banner-height-home)_-_4.5rem)]
|
||||
}
|
||||
.enable-banner #top-row {
|
||||
@apply h-[calc(var(--banner-height)_-_4.5rem)]
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
import 'overlayscrollbars/overlayscrollbars.css';
|
||||
import {
|
||||
OverlayScrollbars,
|
||||
// ScrollbarsHidingPlugin,
|
||||
// SizeObserverPlugin,
|
||||
// ClickScrollPlugin
|
||||
} from 'overlayscrollbars';
|
||||
import {getHue, getStoredTheme, setHue, setTheme} from "../utils/setting-utils";
|
||||
import {pathsEqual, url} from "../utils/url-utils";
|
||||
|
||||
/* Preload fonts */
|
||||
// (async function() {
|
||||
// try {
|
||||
// await Promise.all([
|
||||
// document.fonts.load("400 1em Roboto"),
|
||||
// document.fonts.load("700 1em Roboto"),
|
||||
// ]);
|
||||
// document.body.classList.remove("hidden");
|
||||
// } catch (error) {
|
||||
// console.log("Failed to load fonts:", error);
|
||||
// }
|
||||
// })();
|
||||
|
||||
/* TODO This is a temporary solution for style flicker issue when the transition is activated */
|
||||
/* issue link: https://github.com/withastro/astro/issues/8711, the solution get from here too */
|
||||
/* update: fixed in Astro 3.2.4 */
|
||||
/*
|
||||
function disableAnimation() {
|
||||
const css = document.createElement('style')
|
||||
css.appendChild(
|
||||
document.createTextNode(
|
||||
`*{
|
||||
-webkit-transition:none!important;
|
||||
-moz-transition:none!important;
|
||||
-o-transition:none!important;
|
||||
-ms-transition:none!important;
|
||||
transition:none!important
|
||||
}`
|
||||
)
|
||||
)
|
||||
document.head.appendChild(css)
|
||||
|
||||
return () => {
|
||||
// Force restyle
|
||||
;(() => window.getComputedStyle(document.body))()
|
||||
|
||||
// Wait for next tick before removing
|
||||
setTimeout(() => {
|
||||
document.head.removeChild(css)
|
||||
}, 1)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
function setClickOutsideToClose(panel: string, ignores: string[]) {
|
||||
document.addEventListener("click", event => {
|
||||
let panelDom = document.getElementById(panel);
|
||||
let tDom = event.target;
|
||||
if (!(tDom instanceof Node)) return; // Ensure the event target is an HTML Node
|
||||
for (let ig of ignores) {
|
||||
let ie = document.getElementById(ig)
|
||||
if (ie == tDom || (ie?.contains(tDom))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
panelDom!.classList.add("float-panel-closed");
|
||||
});
|
||||
}
|
||||
setClickOutsideToClose("display-setting", ["display-setting", "display-settings-switch"])
|
||||
setClickOutsideToClose("nav-menu-panel", ["nav-menu-panel", "nav-menu-switch"])
|
||||
setClickOutsideToClose("search-panel", ["search-panel", "search-bar", "search-switch"])
|
||||
|
||||
|
||||
function loadTheme() {
|
||||
const theme = getStoredTheme()
|
||||
setTheme(theme)
|
||||
}
|
||||
|
||||
function loadHue() {
|
||||
setHue(getHue())
|
||||
}
|
||||
|
||||
function initCustomScrollbar() {
|
||||
const bodyElement = document.querySelector('body');
|
||||
if (!bodyElement) return;
|
||||
OverlayScrollbars(
|
||||
// docs say that a initialization to the body element would affect native functionality like window.scrollTo
|
||||
// but just leave it here for now
|
||||
{
|
||||
target: bodyElement,
|
||||
cancel: {
|
||||
nativeScrollbarsOverlaid: true, // don't initialize the overlay scrollbar if there is a native one
|
||||
}
|
||||
}, {
|
||||
scrollbars: {
|
||||
theme: 'scrollbar-base scrollbar-auto py-1',
|
||||
autoHide: 'move',
|
||||
autoHideDelay: 500,
|
||||
autoHideSuspend: false,
|
||||
},
|
||||
});
|
||||
document.querySelectorAll('pre').forEach((ele) => {
|
||||
OverlayScrollbars(ele, {
|
||||
scrollbars: {
|
||||
theme: 'scrollbar-base scrollbar-dark px-2',
|
||||
autoHide: 'leave',
|
||||
autoHideDelay: 500,
|
||||
autoHideSuspend: false
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function init() {
|
||||
// disableAnimation()() // TODO
|
||||
loadTheme();
|
||||
loadHue();
|
||||
initCustomScrollbar();
|
||||
}
|
||||
|
||||
/* Load settings when entering the site */
|
||||
init();
|
||||
|
||||
/* Load settings before swapping */
|
||||
/* astro:after-swap event happened before swap animation */
|
||||
document.addEventListener('astro:after-swap', init);
|
||||
|
||||
const setup = () => {
|
||||
// TODO: temp solution to change the height of the banner
|
||||
/*
|
||||
window.swup.hooks.on('animation:out:start', () => {
|
||||
const path = window.location.pathname
|
||||
const body = document.querySelector('body')
|
||||
if (path[path.length - 1] === '/' && !body.classList.contains('is-home')) {
|
||||
body.classList.add('is-home')
|
||||
} else if (path[path.length - 1] !== '/' && body.classList.contains('is-home')) {
|
||||
body.classList.remove('is-home')
|
||||
}
|
||||
})
|
||||
*/
|
||||
// Remove the delay for the first time page load
|
||||
window.swup.hooks.on('link:click', () => {
|
||||
document.documentElement.style.setProperty('--content-delay', '0ms')
|
||||
})
|
||||
window.swup.hooks.on('content:replace', initCustomScrollbar)
|
||||
window.swup.hooks.on('visit:start', (visit: {to: {url: string}}) => {
|
||||
// change banner height immediately when a link is clicked
|
||||
const bodyElement = document.querySelector('body')
|
||||
if (pathsEqual(visit.to.url, url('/'))) {
|
||||
bodyElement!.classList.add('is-home');
|
||||
} else {
|
||||
bodyElement!.classList.remove('is-home');
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
if (window?.swup?.hooks) {
|
||||
setup()
|
||||
} else {
|
||||
document.addEventListener('swup:enable', setup)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<script>
|
||||
import PhotoSwipeLightbox from "photoswipe/lightbox"
|
||||
import "photoswipe/style.css"
|
||||
|
||||
let lightbox: PhotoSwipeLightbox
|
||||
let pswp = import("photoswipe")
|
||||
|
||||
function createPhotoSwipe() {
|
||||
lightbox = new PhotoSwipeLightbox({
|
||||
gallery: ".custom-md img, #post-cover img",
|
||||
pswpModule: () => pswp,
|
||||
closeSVG: '<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#ffffff"><path d="M480-424 284-228q-11 11-28 11t-28-11q-11-11-11-28t11-28l196-196-196-196q-11-11-11-28t11-28q11-11 28-11t28 11l196 196 196-196q11-11 28-11t28 11q11 11 11 28t-11 28L536-480l196 196q11 11 11 28t-11 28q-11 11-28 11t-28-11L480-424Z"/></svg>',
|
||||
zoomSVG: '<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#ffffff"><path d="M340-540h-40q-17 0-28.5-11.5T260-580q0-17 11.5-28.5T300-620h40v-40q0-17 11.5-28.5T380-700q17 0 28.5 11.5T420-660v40h40q17 0 28.5 11.5T500-580q0 17-11.5 28.5T460-540h-40v40q0 17-11.5 28.5T380-460q-17 0-28.5-11.5T340-500v-40Zm40 220q-109 0-184.5-75.5T120-580q0-109 75.5-184.5T380-840q109 0 184.5 75.5T640-580q0 44-14 83t-38 69l224 224q11 11 11 28t-11 28q-11 11-28 11t-28-11L532-372q-30 24-69 38t-83 14Zm0-80q75 0 127.5-52.5T560-580q0-75-52.5-127.5T380-760q-75 0-127.5 52.5T200-580q0 75 52.5 127.5T380-400Z"/></svg>',
|
||||
padding: { top: 20, bottom: 20, left: 20, right: 20 },
|
||||
wheelToZoom: true,
|
||||
arrowPrev: false,
|
||||
arrowNext: false,
|
||||
imageClickAction: 'close',
|
||||
tapAction: 'close',
|
||||
doubleTapAction: 'zoom',
|
||||
})
|
||||
|
||||
lightbox.addFilter("domItemData", (itemData, element) => {
|
||||
if (element instanceof HTMLImageElement) {
|
||||
itemData.src = element.src
|
||||
|
||||
itemData.w = Number(element.naturalWidth || window.innerWidth)
|
||||
itemData.h = Number(element.naturalHeight || window.innerHeight)
|
||||
|
||||
itemData.msrc = element.src
|
||||
}
|
||||
|
||||
return itemData
|
||||
})
|
||||
|
||||
lightbox.init()
|
||||
}
|
||||
|
||||
const setup = () => {
|
||||
if (!lightbox) {
|
||||
createPhotoSwipe()
|
||||
}
|
||||
window.swup.hooks.on("page:view", () => {
|
||||
createPhotoSwipe()
|
||||
})
|
||||
|
||||
window.swup.hooks.on(
|
||||
"content:replace",
|
||||
() => {
|
||||
lightbox?.destroy?.()
|
||||
},
|
||||
{ before: true },
|
||||
)
|
||||
}
|
||||
|
||||
if (window.swup) {
|
||||
setup()
|
||||
} else {
|
||||
document.addEventListener("swup:enable", setup)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style is:global lang="stylus">
|
||||
#banner-wrapper
|
||||
top: 0
|
||||
opacity: 1
|
||||
.banner-closed
|
||||
#banner-wrapper
|
||||
top: -120px
|
||||
opacity: 0
|
||||
</style>
|
||||
Reference in New Issue
Block a user