Improve everything
This commit is contained in:
parent
421dc1e614
commit
cd69584b66
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"editor.rulers": [100],
|
||||
"editor.tabSize": 2
|
||||
"editor.tabSize": 2,
|
||||
"typescript.preferences.importModuleSpecifier": "non-relative"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
- [ ] Check why I'm getting NS_BINDING_ABORTED on fonts when fetched in the network browser dev tool tab
|
||||
- [ ] Add a background when opening menus in mobile mode
|
|
@ -1,28 +0,0 @@
|
|||
/*
|
||||
original light mode
|
||||
color: #fff1e0;
|
||||
color: #ffedd8;
|
||||
color: #f0d1b3;
|
||||
color: #c0845e;
|
||||
color: #9c6644;
|
||||
color: #1b1811; */
|
||||
|
||||
/*
|
||||
original dark mode
|
||||
color: #191914;
|
||||
color: #26221e;
|
||||
color: #2c2803;
|
||||
color: #392d22;
|
||||
color: #c0845e;
|
||||
color: #ebeae7; */
|
||||
|
||||
/* new version using https://leonardocolor.io/theme.html */
|
||||
|
||||
:where(button) {
|
||||
background-color: inherit;
|
||||
color: inherit;
|
||||
border: initial;
|
||||
padding: initial;
|
||||
margin: initial;
|
||||
cursor: pointer;
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
.tippy-box[data-animation="fade"][data-state="hidden"] {
|
||||
opacity: 0;
|
||||
}
|
||||
[data-tippy-root] {
|
||||
max-width: calc(100vw - 10px);
|
||||
}
|
||||
.tippy-box {
|
||||
position: relative;
|
||||
background-color: var(--color-elevation-1);
|
||||
color: var(--color-base-1000);
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: 0 20px 25px -5px var(--color-shadow),
|
||||
0 0 10px -6px var(--color-shadow);
|
||||
transition-property: transform, visibility, opacity;
|
||||
}
|
||||
.tippy-box[data-placement^="top"] > .tippy-arrow {
|
||||
bottom: 0;
|
||||
}
|
||||
.tippy-box[data-placement^="top"] > .tippy-arrow:before {
|
||||
bottom: -7px;
|
||||
left: 0;
|
||||
border-width: 8px 8px 0;
|
||||
border-top-color: initial;
|
||||
transform-origin: center top;
|
||||
}
|
||||
.tippy-box[data-placement^="bottom"] > .tippy-arrow {
|
||||
top: 0;
|
||||
}
|
||||
.tippy-box[data-placement^="bottom"] > .tippy-arrow:before {
|
||||
top: -7px;
|
||||
left: 0;
|
||||
border-width: 0 8px 8px;
|
||||
border-bottom-color: initial;
|
||||
transform-origin: center bottom;
|
||||
}
|
||||
.tippy-box[data-placement^="left"] > .tippy-arrow {
|
||||
right: 0;
|
||||
}
|
||||
.tippy-box[data-placement^="left"] > .tippy-arrow:before {
|
||||
border-width: 8px 0 8px 8px;
|
||||
border-left-color: initial;
|
||||
right: -7px;
|
||||
transform-origin: center left;
|
||||
}
|
||||
.tippy-box[data-placement^="right"] > .tippy-arrow {
|
||||
left: 0;
|
||||
}
|
||||
.tippy-box[data-placement^="right"] > .tippy-arrow:before {
|
||||
left: -7px;
|
||||
border-width: 8px 8px 8px 0;
|
||||
border-right-color: initial;
|
||||
transform-origin: center right;
|
||||
}
|
||||
.tippy-box[data-inertia][data-state="visible"] {
|
||||
transition-timing-function: cubic-bezier(0.54, 1.5, 0.38, 1.11);
|
||||
}
|
||||
.tippy-arrow {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
color: var(--color-elevation-1);
|
||||
}
|
||||
.tippy-arrow:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
border-color: transparent;
|
||||
border-style: solid;
|
||||
}
|
||||
.tippy-content {
|
||||
position: relative;
|
||||
padding: 1rem 1.5rem;
|
||||
z-index: 1;
|
||||
}
|
||||
.tippy-box[data-placement^="top"] {
|
||||
transform-origin: bottom;
|
||||
}
|
||||
.tippy-box[data-placement^="bottom"] {
|
||||
transform-origin: top;
|
||||
}
|
||||
.tippy-box[data-placement^="left"] {
|
||||
transform-origin: right;
|
||||
}
|
||||
.tippy-box[data-placement^="right"] {
|
||||
transform-origin: left;
|
||||
}
|
||||
.tippy-box[data-state="hidden"] {
|
||||
transform: scale(0.8);
|
||||
opacity: 0;
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
User-agent: *
|
||||
Disallow: /
|
|
@ -1,20 +0,0 @@
|
|||
---
|
||||
import { Icon } from "astro-icon/components";
|
||||
import { getLocalizedUrl } from "utils/urls";
|
||||
interface Props {
|
||||
href: string;
|
||||
isActive?: boolean;
|
||||
text?: string;
|
||||
icon?: string;
|
||||
}
|
||||
|
||||
const { locale } = Astro.params;
|
||||
const { href, text, icon, isActive } = Astro.props;
|
||||
---
|
||||
|
||||
<a href={getLocalizedUrl(href, locale)} class:list={{ active: isActive }}>
|
||||
{text && text}
|
||||
{icon && <Icon name={icon} />}
|
||||
</a>
|
||||
|
||||
<style></style>
|
|
@ -1,262 +0,0 @@
|
|||
---
|
||||
import { Icon } from "astro-icon/components";
|
||||
import { getLocalizedUrl } from "utils/urls";
|
||||
import { CookieNames } from "utils/cookies";
|
||||
import NavOption from "./NavOption.astro";
|
||||
|
||||
const { locale = "en" } = Astro.params;
|
||||
const isReduced =
|
||||
Astro.cookies.get(CookieNames.MENU_PANEL_REDUCED)?.boolean() ?? false;
|
||||
const themeColors =
|
||||
Astro.cookies.get(CookieNames.THEME_COLOR)?.value ?? "theme-color-light";
|
||||
---
|
||||
|
||||
<div id="component" class:list={{ reduced: isReduced }}>
|
||||
<a
|
||||
id="accords-logo"
|
||||
data-turbo-confirm="Do you want to leave this page?"
|
||||
href={getLocalizedUrl("/", locale)}
|
||||
>
|
||||
<Icon name="accords" />
|
||||
</a>
|
||||
|
||||
<p id="title">Accord’s Library</p>
|
||||
|
||||
<button id="reduce-toggle">
|
||||
<Icon name="material-symbols:chevron-left-rounded" width={24} height={24} />
|
||||
</button>
|
||||
|
||||
<button
|
||||
class={themeColors === "theme-color-dark" ? "dark" : "light"}
|
||||
id="theme-toggle"
|
||||
>
|
||||
<Icon class="when-light" name="material-symbols:light-mode" />
|
||||
<Icon class="when-dark" name="material-symbols:dark-mode" />
|
||||
</button>
|
||||
|
||||
<NavOption
|
||||
href="/library"
|
||||
icon="material-symbols:auto-stories"
|
||||
title="Library"
|
||||
subtitle="Browse all physical and digital media"
|
||||
/>
|
||||
|
||||
<NavOption
|
||||
href="/contents"
|
||||
icon="material-symbols:workspaces"
|
||||
title="Content"
|
||||
subtitle="Explore all content and filter by type or category"
|
||||
/>
|
||||
|
||||
<NavOption
|
||||
href="/wiki"
|
||||
icon="material-symbols:travel-explore"
|
||||
title="Wiki"
|
||||
subtitle="An encyclopedia for everything related to DrakeNieR"
|
||||
/>
|
||||
|
||||
<NavOption
|
||||
href="/chronicles"
|
||||
icon="material-symbols:schedule"
|
||||
title="Chronicles"
|
||||
subtitle="Experience all events and content in chronological order"
|
||||
/>
|
||||
|
||||
<NavOption href="/news" icon="material-symbols:newspaper" title="News" />
|
||||
|
||||
<NavOption
|
||||
href="https://gallery.accords-library.com/posts/"
|
||||
icon="material-symbols:perm-media"
|
||||
title="Gallery"
|
||||
/>
|
||||
|
||||
<NavOption href="/archives" icon="material-symbols:save" title="Archives" />
|
||||
|
||||
<NavOption href="/about-us" icon="material-symbols:info" title="About us" />
|
||||
|
||||
<p>
|
||||
This website’s content is made available under <a
|
||||
href="https://creativecommons.org/licenses/by-sa/4.0/">CC-BY-SA</a
|
||||
> unless otherwise noted.
|
||||
</p>
|
||||
|
||||
<div id="common-creative">
|
||||
<Icon name="creative-commons-brands" />
|
||||
<Icon name="creative-commons-by-brands" />
|
||||
<Icon name="creative-commons-sa-brands" />
|
||||
</div>
|
||||
|
||||
<p>
|
||||
Accord’s Library is not affiliated with or endorsed by SQUARE ENIX CO. LTD.
|
||||
All game assets and promotional materials belongs to © SQUARE ENIX CO. LTD.
|
||||
</p>
|
||||
|
||||
<div id="social-links">
|
||||
<Icon name="github-brands" />
|
||||
<Icon name="x-brands" />
|
||||
<Icon name="discord-brands" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
import { CookieNames } from "utils/cookies";
|
||||
import { observableWithPersistence } from "utils/micro-observables";
|
||||
import { Elementos } from "utils/Elementos";
|
||||
import { onLoad } from "utils/turbo";
|
||||
|
||||
onLoad(() => {
|
||||
const component = new Elementos("#component");
|
||||
const reduceToggleButton = new Elementos("#reduce-toggle");
|
||||
|
||||
const isReduced = observableWithPersistence(
|
||||
CookieNames.MENU_PANEL_REDUCED,
|
||||
false
|
||||
);
|
||||
|
||||
reduceToggleButton.onClick(() => {
|
||||
isReduced.update((oldValue) => !oldValue);
|
||||
Turbo.cache.clear();
|
||||
});
|
||||
|
||||
component.setClass("reduced", isReduced);
|
||||
|
||||
// theme-toggle
|
||||
const body = new Elementos("body");
|
||||
const themeToggleButton = new Elementos("#theme-toggle");
|
||||
|
||||
const themeColor = observableWithPersistence(
|
||||
CookieNames.THEME_COLOR,
|
||||
"theme-color-light"
|
||||
);
|
||||
|
||||
const isDarkMode = themeColor.select(
|
||||
(value) => value === "theme-color-dark"
|
||||
);
|
||||
|
||||
themeToggleButton.onClick(() => {
|
||||
themeColor.update((oldValue) =>
|
||||
oldValue === "theme-color-light"
|
||||
? "theme-color-dark"
|
||||
: "theme-color-light"
|
||||
);
|
||||
Turbo.cache.clear();
|
||||
});
|
||||
|
||||
themeToggleButton.setClass(
|
||||
{ ifFalse: "light", ifTrue: "dark" },
|
||||
isDarkMode
|
||||
);
|
||||
|
||||
body.setClass(
|
||||
{ ifFalse: "theme-color-light", ifTrue: "theme-color-dark" },
|
||||
isDarkMode
|
||||
);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#component {
|
||||
--reduced-transition-duration: 5s;
|
||||
display: grid;
|
||||
padding: 2rem;
|
||||
gap: 1rem;
|
||||
|
||||
container-type: inline-size;
|
||||
|
||||
@media (width >= 60rem) {
|
||||
transition:
|
||||
var(--reduced-transition-duration) width,
|
||||
var(--reduced-transition-duration) padding;
|
||||
position: relative;
|
||||
|
||||
width: 20rem;
|
||||
flex-shrink: 0;
|
||||
border-right: var(--border-style);
|
||||
|
||||
& > #title {
|
||||
font-family: var(--font-headers);
|
||||
font-size: 1.875rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
& > p {
|
||||
transition:
|
||||
var(--reduced-transition-duration) font-size,
|
||||
var(--reduced-transition-duration) opacity;
|
||||
}
|
||||
|
||||
& > #common-creative,
|
||||
& > #social-links {
|
||||
transition:
|
||||
var(--reduced-transition-duration) transform,
|
||||
var(--reduced-transition-duration) opacity;
|
||||
}
|
||||
|
||||
&.reduced {
|
||||
width: 6rem;
|
||||
|
||||
padding: 2rem 1rem;
|
||||
|
||||
& > p,
|
||||
& > #title {
|
||||
font-size: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
& > #common-creative,
|
||||
& > #social-links {
|
||||
transform: scale(0);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& > p {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
& > #accords-logo {
|
||||
place-self: center;
|
||||
width: max(50%, 3rem);
|
||||
aspect-ratio: 1/1;
|
||||
color: var(--color-base-1000);
|
||||
transition: 0.1s color;
|
||||
&:hover {
|
||||
color: var(--color-base-600);
|
||||
}
|
||||
|
||||
& > svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
& > #theme-toggle.dark > .when-light,
|
||||
& > #theme-toggle.light > .when-dark {
|
||||
display: none;
|
||||
}
|
||||
|
||||
& > #common-creative {
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
place-content: center;
|
||||
gap: 0.25rem;
|
||||
|
||||
& > svg {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
& > #social-links {
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
place-items: center;
|
||||
|
||||
& > svg {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,75 +0,0 @@
|
|||
---
|
||||
import { Icon } from "astro-icon/components";
|
||||
interface Props {
|
||||
title: string;
|
||||
showSubPanel: boolean;
|
||||
}
|
||||
|
||||
const { title, showSubPanel } = Astro.props;
|
||||
---
|
||||
|
||||
<div id="component">
|
||||
<button
|
||||
id="toggle-menu-panel"
|
||||
_="on click
|
||||
toggle .opened on #menu-panel
|
||||
remove .opened from #sub-panel
|
||||
remove .on from #toggle-sub-panel
|
||||
toggle .on on me"
|
||||
>
|
||||
<Icon
|
||||
class="when-off"
|
||||
name="material-symbols:menu-rounded"
|
||||
width={24}
|
||||
height={24}
|
||||
/>
|
||||
<Icon
|
||||
class="when-on"
|
||||
name="material-symbols:close-rounded"
|
||||
width={24}
|
||||
height={24}
|
||||
/>
|
||||
</button>
|
||||
<p>{title}</p>
|
||||
<div>
|
||||
{
|
||||
showSubPanel && (
|
||||
<button
|
||||
id="toggle-sub-panel"
|
||||
_="on click
|
||||
toggle .opened on #sub-panel
|
||||
remove .opened from #menu-panel
|
||||
remove .on from #toggle-menu-panel
|
||||
toggle .on on me"
|
||||
>
|
||||
<Icon
|
||||
class="when-off"
|
||||
name="material-symbols:tune-rounded"
|
||||
width={24}
|
||||
height={24}
|
||||
/>
|
||||
<Icon
|
||||
class="when-on"
|
||||
name="material-symbols:close-rounded"
|
||||
width={24}
|
||||
height={24}
|
||||
/>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#component {
|
||||
border-top: var(--border-style);
|
||||
display: grid;
|
||||
grid-template-columns: 5rem 1fr 5rem;
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
.on > .when-off,
|
||||
:not(.on) > .when-on {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,2 @@
|
|||
/// <reference types="astro/client" />
|
||||
/// <reference path="../.astro/types.d.ts" />
|
|
@ -1,159 +0,0 @@
|
|||
---
|
||||
import { CookieNames } from "utils/cookies";
|
||||
interface Props {
|
||||
title: string;
|
||||
}
|
||||
|
||||
const { title } = Astro.props;
|
||||
const themeColors =
|
||||
Astro.cookies.get(CookieNames.THEME_COLOR)?.value ?? "theme-color-light";
|
||||
---
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<title>{title}</title>
|
||||
<script is:inline src="/js/turbo.es2017-umd.js" async defer></script>
|
||||
<link href="/css/sanitize.min.css" rel="stylesheet" />
|
||||
<link href="/css/global.css" rel="stylesheet" />
|
||||
</head>
|
||||
|
||||
<body class={themeColors} hx-ext="head-support" hx-boost="true">
|
||||
<slot />
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<script>
|
||||
import tippy from "tippy.js";
|
||||
import "tippy.js/dist/tippy.css";
|
||||
|
||||
tippy("[data-tippy-content]", { allowHTML: true });
|
||||
</script>
|
||||
|
||||
<style is:global>
|
||||
.theme-color-light {
|
||||
--color-base-0: #ffffff;
|
||||
--color-base-50: #fffaf3;
|
||||
--color-base-100: #fff4e6;
|
||||
--color-base-150: #fdebd4;
|
||||
--color-base-200: #f7ddc2;
|
||||
--color-base-250: #efcfb0;
|
||||
--color-base-300: #e5be9e;
|
||||
--color-base-350: #ddb08e;
|
||||
--color-base-400: #d3a07c;
|
||||
--color-base-450: #ca926c;
|
||||
--color-base-500: #c0835d;
|
||||
--color-base-550: #b3754f;
|
||||
--color-base-600: #a26a47;
|
||||
--color-base-650: #905e3f;
|
||||
--color-base-700: #805438;
|
||||
--color-base-750: #6e4a31;
|
||||
--color-base-800: #5e402b;
|
||||
--color-base-850: #4d3625;
|
||||
--color-base-900: #3c2d1e;
|
||||
--color-base-950: #2f2419;
|
||||
--color-base-1000: #1f1a13;
|
||||
|
||||
--color-shadow: var(--color-base-500);
|
||||
|
||||
--texture-dots: url(/images/paper-dots.webp);
|
||||
--texture-dots-blend: multiply;
|
||||
}
|
||||
|
||||
.theme-color-dark {
|
||||
--color-base-1000: #ebeae7;
|
||||
--color-base-950: #eae5e0;
|
||||
--color-base-900: #e8dfd8;
|
||||
--color-base-850: #e4d1c4;
|
||||
--color-base-800: #e0bfaa;
|
||||
--color-base-750: #dcb095;
|
||||
--color-base-700: #d4a07f;
|
||||
--color-base-650: #cb916c;
|
||||
--color-base-600: #bf835d;
|
||||
--color-base-550: #b07751;
|
||||
--color-base-500: #a06b48;
|
||||
--color-base-450: #8f5f40;
|
||||
--color-base-400: #7d5539;
|
||||
--color-base-350: #6b4a33;
|
||||
--color-base-300: #5c412e;
|
||||
--color-base-250: #4a3728;
|
||||
--color-base-200: #3a2d22;
|
||||
--color-base-150: #27231e;
|
||||
--color-base-100: #1c1b16;
|
||||
--color-base-50: #11110d;
|
||||
--color-base-0: #000000;
|
||||
|
||||
--color-shadow: var(--color-base-0);
|
||||
|
||||
--texture-dots: url(/images/paper-dots-dark.webp);
|
||||
--texture-dots-blend: overlay;
|
||||
}
|
||||
|
||||
.texture-dots {
|
||||
background-size: 10cm;
|
||||
background-attachment: local;
|
||||
background-image: var(--texture-dots);
|
||||
background-blend-mode: var(--texture-dots-blend);
|
||||
background-repeat: repeat;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--color-base-150);
|
||||
color: var(--color-base-1000);
|
||||
}
|
||||
|
||||
.turbo-progress-bar {
|
||||
height: 5px;
|
||||
background-color: #b07751;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Vollkorn";
|
||||
src: url("/fonts/Vollkorn-Bold.woff2") format("woff2");
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Noto Sans";
|
||||
src: url("/fonts/NotoSans-Medium.woff2") format("woff2");
|
||||
font-weight: medium;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Noto Sans";
|
||||
src: url("/fonts/NotoSans-Bold.woff2") format("woff2");
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Angelic Agrippa";
|
||||
src: url("/fonts/AngelicAgrippa-Regular.woff2") format("woff2");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
:root {
|
||||
--font-body: "Noto Sans", sans-serif;
|
||||
--font-headers: "Vollkorn", serif;
|
||||
--font-angelic: "Angelic Agrippa", serif;
|
||||
}
|
||||
|
||||
p {
|
||||
font-family: var(--font-body);
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-family: var(--font-headers);
|
||||
}
|
||||
</style>
|
|
@ -1,7 +1,11 @@
|
|||
---
|
||||
import AppLayout from "layouts/AppLayout.astro";
|
||||
import AppLayout from "pages/_components/AppLayout/AppLayout.astro";
|
||||
---
|
||||
|
||||
{
|
||||
/* ------------------------------------------- HTML ------------------------------------------- */
|
||||
}
|
||||
|
||||
<AppLayout title="Archive">
|
||||
<div slot="subPanel">
|
||||
Besides physical medias, we also archive digital contents such as websites,
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
---
|
||||
import AppLayout from "pages/_components/AppLayout/AppLayout.astro";
|
||||
---
|
||||
|
||||
{
|
||||
/* ------------------------------------------- HTML ------------------------------------------- */
|
||||
}
|
||||
|
||||
<AppLayout title="Library">
|
||||
<main slot="mainPanel"><h1>Contents</h1></main>
|
||||
<div slot="subPanel">
|
||||
All the contents (textual, audio, and video) from the Library or other
|
||||
online sources.
|
||||
</div>
|
||||
</AppLayout>
|
|
@ -1,8 +1,12 @@
|
|||
---
|
||||
import { Icon } from "astro-icon/components";
|
||||
import AppLayout from "layouts/AppLayout.astro";
|
||||
import AppLayout from "pages/_components/AppLayout/AppLayout.astro";
|
||||
---
|
||||
|
||||
{
|
||||
/* ------------------------------------------- HTML ------------------------------------------- */
|
||||
}
|
||||
|
||||
<AppLayout title="Accord’s Library">
|
||||
<main slot="mainPanel">
|
||||
<div id="header">
|
||||
|
@ -46,6 +50,10 @@ import AppLayout from "layouts/AppLayout.astro";
|
|||
</main>
|
||||
</AppLayout>
|
||||
|
||||
{
|
||||
/* ------------------------------------------- CSS -------------------------------------------- */
|
||||
}
|
||||
|
||||
<style>
|
||||
main {
|
||||
padding-left: 2.5rem;
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
---
|
||||
import AppLayout from "layouts/AppLayout.astro";
|
||||
import AppLayout from "pages/_components/AppLayout/AppLayout.astro";
|
||||
---
|
||||
|
||||
{
|
||||
/* ------------------------------------------- HTML ------------------------------------------- */
|
||||
}
|
||||
|
||||
<AppLayout title="Library">
|
||||
<main slot="mainPanel"><h1>Library</h1></main>
|
||||
<div slot="subPanel">
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
---
|
||||
import Html from "layouts/Html.astro";
|
||||
import Navbar from "components/Navbar.astro";
|
||||
import MenuPanel from "components/MenuPanel.astro";
|
||||
import Html from "pages/_components/Html.astro";
|
||||
import MenuPanel from "./components/MenuPanel.astro";
|
||||
import Navbar from "./components/Navbar.astro";
|
||||
import MenuPanelReduced from "./components/MenuPanelReduced.astro";
|
||||
import { parseCookie } from "utils/astro";
|
||||
import { CookieNames } from "utils/cookies";
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
|
@ -11,15 +14,32 @@ const { title } = Astro.props;
|
|||
|
||||
const turnSubPanelIntoMainPanel =
|
||||
Astro.slots.has("subPanel") && !Astro.slots.has("mainPanel");
|
||||
|
||||
const isMenuPanelReduced = parseCookie<boolean>(
|
||||
Astro.cookies.get(CookieNames.MENU_PANEL_REDUCED),
|
||||
false
|
||||
);
|
||||
---
|
||||
|
||||
{
|
||||
/* ------------------------------------------- HTML ------------------------------------------- */
|
||||
}
|
||||
|
||||
<Html title={title}>
|
||||
<div
|
||||
id="panels"
|
||||
class:list={{ "turn-sub-into-main": turnSubPanelIntoMainPanel }}
|
||||
>
|
||||
<div id="menu-panel" class="texture-dots">
|
||||
<MenuPanel />
|
||||
<div
|
||||
id="menu-panel"
|
||||
class:list={["texture-dots", { reduced: isMenuPanelReduced }]}
|
||||
>
|
||||
<div class="when-reduced">
|
||||
<MenuPanelReduced />
|
||||
</div>
|
||||
<div class="when-not-reduced">
|
||||
<MenuPanel />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="sub-panel" class="texture-dots">
|
||||
|
@ -39,6 +59,10 @@ const turnSubPanelIntoMainPanel =
|
|||
</div>
|
||||
</Html>
|
||||
|
||||
{
|
||||
/* ------------------------------------------- CSS -------------------------------------------- */
|
||||
}
|
||||
|
||||
<style>
|
||||
body {
|
||||
overflow: hidden;
|
||||
|
@ -51,39 +75,29 @@ const turnSubPanelIntoMainPanel =
|
|||
grid-template-rows: 1fr 5rem;
|
||||
}
|
||||
|
||||
& > #navbar {
|
||||
display: grid;
|
||||
@media (width >= 60rem) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
& > #panels {
|
||||
display: flex;
|
||||
position: relative;
|
||||
|
||||
@media (width >= 60rem) {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
}
|
||||
|
||||
@media (width < 60rem) {
|
||||
&.turn-sub-into-main > #sub-panel {
|
||||
inset: 0;
|
||||
z-index: 0;
|
||||
z-index: initial;
|
||||
transition: initial;
|
||||
border: initial;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (width >= 60rem) {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
& > #sub-panel {
|
||||
@media (width < 60rem) {
|
||||
right: -100%;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
transition: 0.2s right;
|
||||
z-index: 1;
|
||||
border-left: var(--border-style);
|
||||
|
||||
&.opened {
|
||||
|
@ -92,48 +106,99 @@ const turnSubPanelIntoMainPanel =
|
|||
}
|
||||
|
||||
@media (width >= 60rem) {
|
||||
width: 20rem;
|
||||
flex-shrink: 0;
|
||||
border-right: var(--border-style);
|
||||
}
|
||||
}
|
||||
|
||||
& > #menu-panel {
|
||||
border-right: var(--border-style);
|
||||
background-position: top right;
|
||||
flex-shrink: 0;
|
||||
overflow-y: auto;
|
||||
|
||||
@media (width < 60rem) {
|
||||
left: -100%;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
transition: 0.2s left;
|
||||
z-index: 1;
|
||||
border-right: var(--border-style);
|
||||
|
||||
&.opened {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
& > .when-reduced {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (width >=60rem) {
|
||||
transition: 0.2s width;
|
||||
|
||||
&.reduced {
|
||||
width: auto;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
&.reduced > .when-not-reduced,
|
||||
&:not(.reduced) > .when-reduced {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
& > div {
|
||||
display: grid;
|
||||
align-content: start;
|
||||
justify-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
& > #main-panel {
|
||||
width: 100%;
|
||||
|
||||
@media (width < 60rem) {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
}
|
||||
}
|
||||
|
||||
& > #menu-panel,
|
||||
& > #sub-panel {
|
||||
width: 20rem;
|
||||
flex-shrink: 0;
|
||||
overflow-x: hidden;
|
||||
|
||||
@media (width < 60rem) {
|
||||
z-index: 1;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: min(30rem, 90%);
|
||||
|
||||
@media (width < 20rem) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& > div {
|
||||
display: grid;
|
||||
background-color: var(--color-base-150);
|
||||
transition: 0.1s background-color;
|
||||
justify-content: center;
|
||||
overflow-y: auto;
|
||||
padding: 2rem;
|
||||
|
||||
&:empty {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (width < 60rem) {
|
||||
position: absolute;
|
||||
width: min(30rem, 90%);
|
||||
}
|
||||
& > #navbar {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
border-top: var(--border-style);
|
||||
grid-template-columns: 5rem 1fr 5rem;
|
||||
|
||||
@media (width >= 60rem) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,162 @@
|
|||
---
|
||||
import { Icon } from "astro-icon/components";
|
||||
import { getLocalizedUrl } from "utils/urls";
|
||||
import NavOption from "pages/_components/NavOption.astro";
|
||||
import HorizontalLine from "pages/_components/HorizontalLine.astro";
|
||||
import ReduceToggleButton from "./ReduceToggleButton.astro";
|
||||
import ThemeToggleButton from "./ThemeToggleButton.astro";
|
||||
|
||||
const { locale = "en" } = Astro.params;
|
||||
---
|
||||
|
||||
{
|
||||
/* ------------------------------------------- HTML ------------------------------------------- */
|
||||
}
|
||||
|
||||
<a
|
||||
id="accords-logo"
|
||||
data-turbo-confirm="Do you want to leave this page?"
|
||||
href={getLocalizedUrl("/", locale)}
|
||||
>
|
||||
<Icon name="accords" />
|
||||
</a>
|
||||
|
||||
<p id="title">Accord’s Library</p>
|
||||
|
||||
<div id="buttons">
|
||||
<ReduceToggleButton icon="material-symbols:chevron-left-rounded" />
|
||||
<ThemeToggleButton />
|
||||
</div>
|
||||
|
||||
<HorizontalLine />
|
||||
|
||||
<NavOption
|
||||
href="/library"
|
||||
icon="material-symbols:auto-stories"
|
||||
title="Library"
|
||||
subtitle="Browse all physical and digital media"
|
||||
/>
|
||||
|
||||
<NavOption
|
||||
href="/contents"
|
||||
icon="material-symbols:workspaces"
|
||||
title="Content"
|
||||
subtitle="Explore all content and filter by type or category"
|
||||
/>
|
||||
|
||||
<NavOption
|
||||
href="/wiki"
|
||||
icon="material-symbols:travel-explore"
|
||||
title="Wiki"
|
||||
subtitle="An encyclopedia for everything related to DrakeNieR"
|
||||
/>
|
||||
|
||||
<NavOption
|
||||
href="/chronicles"
|
||||
icon="material-symbols:schedule"
|
||||
title="Chronicles"
|
||||
subtitle="Experience all events and content in chronological order"
|
||||
/>
|
||||
|
||||
<HorizontalLine />
|
||||
|
||||
<NavOption href="/news" icon="material-symbols:newspaper" title="News" />
|
||||
|
||||
<NavOption
|
||||
href="https://gallery.accords-library.com/posts/"
|
||||
icon="material-symbols:perm-media"
|
||||
title="Gallery"
|
||||
/>
|
||||
|
||||
<NavOption href="/archives" icon="material-symbols:save" title="Archives" />
|
||||
|
||||
<NavOption href="/about-us" icon="material-symbols:info" title="About us" />
|
||||
|
||||
<HorizontalLine />
|
||||
|
||||
<p>
|
||||
This website’s content is made available under <a
|
||||
href="https://creativecommons.org/licenses/by-sa/4.0/">CC-BY-SA</a
|
||||
> unless otherwise noted.
|
||||
</p>
|
||||
|
||||
<div id="common-creative">
|
||||
<Icon name="creative-commons-brands" />
|
||||
<Icon name="creative-commons-by-brands" />
|
||||
<Icon name="creative-commons-sa-brands" />
|
||||
</div>
|
||||
|
||||
<p>
|
||||
Accord’s Library is not affiliated with or endorsed by SQUARE ENIX CO. LTD.
|
||||
All game assets and promotional materials belongs to © SQUARE ENIX CO. LTD.
|
||||
</p>
|
||||
|
||||
<div id="social-links">
|
||||
<Icon name="github-brands" />
|
||||
<Icon name="x-brands" />
|
||||
<Icon name="discord-brands" />
|
||||
</div>
|
||||
|
||||
{
|
||||
/* ------------------------------------------- CSS -------------------------------------------- */
|
||||
}
|
||||
|
||||
<style>
|
||||
#title {
|
||||
font-family: var(--font-headers);
|
||||
font-size: 1.875rem;
|
||||
}
|
||||
|
||||
p {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#accords-logo {
|
||||
width: max(50%, 3rem);
|
||||
aspect-ratio: 1/1;
|
||||
color: var(--color-base-1000);
|
||||
|
||||
&:hover {
|
||||
color: var(--color-base-600);
|
||||
}
|
||||
|
||||
& > svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
#buttons {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
#common-creative {
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
place-content: center;
|
||||
gap: 0.25rem;
|
||||
|
||||
& > svg {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#social-links {
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
place-items: center;
|
||||
gap: 2rem;
|
||||
|
||||
& > svg {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,159 @@
|
|||
---
|
||||
import { Icon } from "astro-icon/components";
|
||||
import { getLocalizedUrl } from "utils/urls";
|
||||
import { CookieNames } from "utils/cookies";
|
||||
import NavOptionIcon from "pages/_components/NavOptionIcon.astro";
|
||||
import { parseCookie } from "utils/astro";
|
||||
import HorizontalLine from "pages/_components/HorizontalLine.astro";
|
||||
import ReduceToggleButton from "./ReduceToggleButton.astro";
|
||||
|
||||
const { locale = "en" } = Astro.params;
|
||||
|
||||
const themeColors = parseCookie<string>(
|
||||
Astro.cookies.get(CookieNames.THEME_COLOR),
|
||||
"theme-color-light"
|
||||
);
|
||||
---
|
||||
|
||||
{
|
||||
/* ------------------------------------------- HTML ------------------------------------------- */
|
||||
}
|
||||
|
||||
<a
|
||||
id="accords-logo"
|
||||
data-turbo-confirm="Do you want to leave this page?"
|
||||
href={getLocalizedUrl("/", locale)}
|
||||
>
|
||||
<Icon name="accords" />
|
||||
</a>
|
||||
|
||||
<div id="buttons">
|
||||
<ReduceToggleButton icon="material-symbols:chevron-right-rounded" />
|
||||
|
||||
<button
|
||||
class={themeColors === "theme-color-dark" ? "dark" : "light"}
|
||||
id="theme-toggle"
|
||||
>
|
||||
<Icon
|
||||
class="when-light"
|
||||
name="material-symbols:light-mode"
|
||||
width={24}
|
||||
height={24}
|
||||
/>
|
||||
<Icon
|
||||
class="when-dark"
|
||||
name="material-symbols:dark-mode"
|
||||
width={24}
|
||||
height={24}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<HorizontalLine />
|
||||
|
||||
<NavOptionIcon
|
||||
href="/library"
|
||||
icon="material-symbols:auto-stories"
|
||||
title="Library"
|
||||
subtitle="Browse all physical and digital media"
|
||||
/>
|
||||
|
||||
<NavOptionIcon
|
||||
href="/contents"
|
||||
icon="material-symbols:workspaces"
|
||||
title="Content"
|
||||
subtitle="Explore all content and filter by type or category"
|
||||
/>
|
||||
|
||||
<NavOptionIcon
|
||||
href="/wiki"
|
||||
icon="material-symbols:travel-explore"
|
||||
title="Wiki"
|
||||
subtitle="An encyclopedia for everything related to DrakeNieR"
|
||||
/>
|
||||
|
||||
<NavOptionIcon
|
||||
href="/chronicles"
|
||||
icon="material-symbols:schedule"
|
||||
title="Chronicles"
|
||||
subtitle="Experience all events and content in chronological order"
|
||||
/>
|
||||
|
||||
<HorizontalLine />
|
||||
|
||||
<NavOptionIcon href="/news" icon="material-symbols:newspaper" title="News" />
|
||||
|
||||
<NavOptionIcon
|
||||
href="https://gallery.accords-library.com/posts/"
|
||||
icon="material-symbols:perm-media"
|
||||
title="Gallery"
|
||||
/>
|
||||
|
||||
<NavOptionIcon href="/archives" icon="material-symbols:save" title="Archives" />
|
||||
|
||||
<NavOptionIcon href="/about-us" icon="material-symbols:info" title="About us" />
|
||||
|
||||
{
|
||||
/* ------------------------------------------- CSS -------------------------------------------- */
|
||||
}
|
||||
|
||||
<style>
|
||||
#accords-logo {
|
||||
width: 3rem;
|
||||
aspect-ratio: 1/1;
|
||||
color: var(--color-base-1000);
|
||||
|
||||
&:hover {
|
||||
color: var(--color-base-600);
|
||||
}
|
||||
|
||||
& > svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
#buttons {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
flex-wrap: wrap;
|
||||
|
||||
& > #theme-toggle.dark > .when-light,
|
||||
& > #theme-toggle.light > .when-dark {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
{
|
||||
/* -------------------------------------------- JS -------------------------------------------- */
|
||||
}
|
||||
|
||||
<script>
|
||||
import tippy, { createSingleton } from "tippy.js";
|
||||
import { onLoad } from "utils/turbo";
|
||||
|
||||
onLoad(() => {
|
||||
createSingleton(
|
||||
tippy(".menu-panel-content > [data-tippy-template]", {
|
||||
content: (reference) => {
|
||||
const templateQuery = reference.getAttribute("data-tippy-template")!;
|
||||
const template = reference.querySelector(templateQuery)!;
|
||||
const container = document.createElement("div");
|
||||
container.append(template.cloneNode(true));
|
||||
container.style.fontSize = "90%";
|
||||
return container;
|
||||
},
|
||||
}),
|
||||
{
|
||||
allowHTML: true,
|
||||
placement: "right",
|
||||
delay: 250,
|
||||
moveTransition: "transform 0.2s ease",
|
||||
maxWidth: "18rem",
|
||||
inertia: true,
|
||||
}
|
||||
);
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,93 @@
|
|||
---
|
||||
import { Icon } from "astro-icon/components";
|
||||
interface Props {
|
||||
title: string;
|
||||
showSubPanel: boolean;
|
||||
}
|
||||
|
||||
const { title, showSubPanel } = Astro.props;
|
||||
---
|
||||
|
||||
{
|
||||
/* ------------------------------------------- HTML ------------------------------------------- */
|
||||
}
|
||||
|
||||
<button id="toggle-menu-panel">
|
||||
<Icon
|
||||
class="when-off"
|
||||
name="material-symbols:menu-rounded"
|
||||
width={24}
|
||||
height={24}
|
||||
/>
|
||||
<Icon
|
||||
class="when-on"
|
||||
name="material-symbols:close-rounded"
|
||||
width={24}
|
||||
height={24}
|
||||
/>
|
||||
</button>
|
||||
<p>{title}</p>
|
||||
<div>
|
||||
{
|
||||
showSubPanel && (
|
||||
<button id="toggle-sub-panel">
|
||||
<Icon
|
||||
class="when-off"
|
||||
name="material-symbols:tune-rounded"
|
||||
width={24}
|
||||
height={24}
|
||||
/>
|
||||
<Icon
|
||||
class="when-on"
|
||||
name="material-symbols:close-rounded"
|
||||
width={24}
|
||||
height={24}
|
||||
/>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
|
||||
{
|
||||
/* ------------------------------------------- CSS -------------------------------------------- */
|
||||
}
|
||||
|
||||
<style>
|
||||
.on > .when-off,
|
||||
:not(.on) > .when-on {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
{
|
||||
/* -------------------------------------------- JS -------------------------------------------- */
|
||||
}
|
||||
|
||||
<script>
|
||||
import { Elementos } from "utils/Elementos";
|
||||
import { observable } from "utils/micro-observables";
|
||||
import { onLoad } from "utils/turbo";
|
||||
|
||||
onLoad(() => {
|
||||
const isMenuPanelOpened = observable(false);
|
||||
const menuPanel = new Elementos("#menu-panel");
|
||||
const menuPanelToggle = new Elementos("#toggle-menu-panel");
|
||||
|
||||
menuPanelToggle.onClick(() => {
|
||||
isMenuPanelOpened.update((current) => !current);
|
||||
isSubPanelOpened.set(false);
|
||||
});
|
||||
menuPanel.setClass("opened", isMenuPanelOpened);
|
||||
menuPanelToggle.setClass("on", isMenuPanelOpened);
|
||||
|
||||
const isSubPanelOpened = observable(false);
|
||||
const subPanel = new Elementos("#sub-panel");
|
||||
const subPanelToggle = new Elementos("#toggle-sub-panel");
|
||||
subPanelToggle.onClick(() => {
|
||||
isSubPanelOpened.update((current) => !current);
|
||||
isMenuPanelOpened.set(false);
|
||||
});
|
||||
subPanel.setClass("opened", isSubPanelOpened);
|
||||
subPanelToggle.setClass("on", isSubPanelOpened);
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,45 @@
|
|||
---
|
||||
import { Icon } from "astro-icon/components";
|
||||
|
||||
interface Props {
|
||||
icon: string;
|
||||
}
|
||||
|
||||
const { icon } = Astro.props;
|
||||
---
|
||||
|
||||
{
|
||||
/* ------------------------------------------- HTML ------------------------------------------- */
|
||||
}
|
||||
|
||||
<button id="reduce-toggle">
|
||||
<Icon name={icon} width={24} height={24} />
|
||||
</button>
|
||||
|
||||
{
|
||||
/* -------------------------------------------- JS -------------------------------------------- */
|
||||
}
|
||||
|
||||
<script>
|
||||
import { Elementos } from "utils/Elementos";
|
||||
import { CookieNames } from "utils/cookies";
|
||||
import { observableWithPersistence } from "utils/micro-observables";
|
||||
import { onLoad } from "utils/turbo";
|
||||
|
||||
onLoad(() => {
|
||||
const toggleReducedButton = new Elementos("#reduce-toggle");
|
||||
const menuPanel = new Elementos("#menu-panel");
|
||||
|
||||
const isReduced = observableWithPersistence(
|
||||
CookieNames.MENU_PANEL_REDUCED,
|
||||
false
|
||||
);
|
||||
|
||||
toggleReducedButton.onClick(() => {
|
||||
isReduced.update((oldValue) => !oldValue);
|
||||
Turbo.cache.clear();
|
||||
});
|
||||
|
||||
menuPanel.setClass("reduced", isReduced);
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,87 @@
|
|||
---
|
||||
import { Icon } from "astro-icon/components";
|
||||
import { parseCookie } from "utils/astro";
|
||||
import { CookieNames } from "utils/cookies";
|
||||
|
||||
const themeColors = parseCookie<string>(
|
||||
Astro.cookies.get(CookieNames.THEME_COLOR),
|
||||
"theme-color-light"
|
||||
);
|
||||
---
|
||||
|
||||
{
|
||||
/* ------------------------------------------- HTML ------------------------------------------- */
|
||||
}
|
||||
|
||||
<button
|
||||
class={themeColors === "theme-color-dark" ? "dark" : "light"}
|
||||
id="theme-toggle"
|
||||
>
|
||||
<Icon
|
||||
class="when-light"
|
||||
name="material-symbols:light-mode"
|
||||
width={24}
|
||||
height={24}
|
||||
/>
|
||||
<Icon
|
||||
class="when-dark"
|
||||
name="material-symbols:dark-mode"
|
||||
width={24}
|
||||
height={24}
|
||||
/>
|
||||
</button>
|
||||
|
||||
{
|
||||
/* ------------------------------------------- CSS -------------------------------------------- */
|
||||
}
|
||||
|
||||
<style>
|
||||
#theme-toggle.dark > .when-light,
|
||||
#theme-toggle.light > .when-dark {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
{
|
||||
/* -------------------------------------------- JS -------------------------------------------- */
|
||||
}
|
||||
|
||||
<script>
|
||||
import { Elementos } from "utils/Elementos";
|
||||
import { CookieNames } from "utils/cookies";
|
||||
import { observableWithPersistence } from "utils/micro-observables";
|
||||
import { onLoad } from "utils/turbo";
|
||||
|
||||
onLoad(() => {
|
||||
const body = new Elementos("body");
|
||||
const themeToggleButton = new Elementos("#theme-toggle");
|
||||
|
||||
const themeColor = observableWithPersistence(
|
||||
CookieNames.THEME_COLOR,
|
||||
"theme-color-light"
|
||||
);
|
||||
|
||||
const isDarkMode = themeColor.select(
|
||||
(value) => value === "theme-color-dark"
|
||||
);
|
||||
|
||||
themeToggleButton.onClick(() => {
|
||||
themeColor.update((oldValue) =>
|
||||
oldValue === "theme-color-light"
|
||||
? "theme-color-dark"
|
||||
: "theme-color-light"
|
||||
);
|
||||
Turbo.cache.clear();
|
||||
});
|
||||
|
||||
themeToggleButton.setClass(
|
||||
{ ifFalse: "light", ifTrue: "dark" },
|
||||
isDarkMode
|
||||
);
|
||||
|
||||
body.setClass(
|
||||
{ ifFalse: "theme-color-light", ifTrue: "theme-color-dark" },
|
||||
isDarkMode
|
||||
);
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
/* ------------------------------------------- HTML ------------------------------------------- */
|
||||
}
|
||||
|
||||
<hr />
|
||||
|
||||
{
|
||||
/* ------------------------------------------- CSS -------------------------------------------- */
|
||||
}
|
||||
|
||||
<style>
|
||||
hr {
|
||||
width: 100%;
|
||||
border: none;
|
||||
border-top: 3px dotted var(--color-base-1000);
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,388 @@
|
|||
---
|
||||
import { parseCookie } from "utils/astro";
|
||||
import { CookieNames } from "utils/cookies";
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
}
|
||||
|
||||
const { title } = Astro.props;
|
||||
const themeColors = parseCookie(
|
||||
Astro.cookies.get(CookieNames.THEME_COLOR),
|
||||
"theme-color-light"
|
||||
);
|
||||
---
|
||||
|
||||
{
|
||||
/* ------------------------------------------- HTML ------------------------------------------- */
|
||||
}
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<title>{title}</title>
|
||||
<script is:inline src="/js/turbo.es2017-umd.js" async defer></script>
|
||||
<link href="/css/sanitize.min.css" rel="stylesheet" />
|
||||
<link href="/css/tippy.css" rel="stylesheet" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
</head>
|
||||
|
||||
<body class={themeColors}>
|
||||
<slot />
|
||||
</body>
|
||||
</html>
|
||||
|
||||
{
|
||||
/* ------------------------------------------- CSS -------------------------------------------- */
|
||||
}
|
||||
|
||||
<style is:global>
|
||||
/* RESET */
|
||||
|
||||
html,
|
||||
body,
|
||||
div,
|
||||
span,
|
||||
applet,
|
||||
object,
|
||||
iframe,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
p,
|
||||
blockquote,
|
||||
pre,
|
||||
a,
|
||||
abbr,
|
||||
acronym,
|
||||
address,
|
||||
big,
|
||||
cite,
|
||||
code,
|
||||
del,
|
||||
dfn,
|
||||
em,
|
||||
img,
|
||||
ins,
|
||||
kbd,
|
||||
q,
|
||||
s,
|
||||
samp,
|
||||
small,
|
||||
strike,
|
||||
strong,
|
||||
sub,
|
||||
sup,
|
||||
tt,
|
||||
var,
|
||||
b,
|
||||
u,
|
||||
i,
|
||||
center,
|
||||
dl,
|
||||
dt,
|
||||
dd,
|
||||
ol,
|
||||
ul,
|
||||
li,
|
||||
fieldset,
|
||||
form,
|
||||
label,
|
||||
legend,
|
||||
table,
|
||||
caption,
|
||||
tbody,
|
||||
tfoot,
|
||||
thead,
|
||||
tr,
|
||||
th,
|
||||
td,
|
||||
article,
|
||||
aside,
|
||||
canvas,
|
||||
details,
|
||||
embed,
|
||||
figure,
|
||||
figcaption,
|
||||
footer,
|
||||
header,
|
||||
hgroup,
|
||||
main,
|
||||
menu,
|
||||
nav,
|
||||
output,
|
||||
ruby,
|
||||
section,
|
||||
summary,
|
||||
time,
|
||||
mark,
|
||||
audio,
|
||||
video {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
font-size: 100%;
|
||||
font: inherit;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
body {
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
menu,
|
||||
ol,
|
||||
ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
blockquote,
|
||||
q {
|
||||
quotes: none;
|
||||
}
|
||||
|
||||
blockquote:before,
|
||||
blockquote:after,
|
||||
q:before,
|
||||
q:after {
|
||||
content: "";
|
||||
content: none;
|
||||
}
|
||||
|
||||
:where(button) {
|
||||
background-color: inherit;
|
||||
color: inherit;
|
||||
border: initial;
|
||||
padding: initial;
|
||||
margin: initial;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* THEMING */
|
||||
|
||||
.theme-color-light {
|
||||
--color-base-0: #ffffff;
|
||||
--color-base-50: #fffaf3;
|
||||
--color-base-100: #fff4e6;
|
||||
--color-base-150: #fdebd4;
|
||||
--color-base-200: #f7ddc2;
|
||||
--color-base-250: #efcfb0;
|
||||
--color-base-300: #e5be9e;
|
||||
--color-base-350: #ddb08e;
|
||||
--color-base-400: #d3a07c;
|
||||
--color-base-450: #ca926c;
|
||||
--color-base-500: #c0835d;
|
||||
--color-base-550: #b3754f;
|
||||
--color-base-600: #a26a47;
|
||||
--color-base-650: #905e3f;
|
||||
--color-base-700: #805438;
|
||||
--color-base-750: #6e4a31;
|
||||
--color-base-800: #5e402b;
|
||||
--color-base-850: #4d3625;
|
||||
--color-base-900: #3c2d1e;
|
||||
--color-base-950: #2f2419;
|
||||
--color-base-1000: #1f1a13;
|
||||
|
||||
--color-elevation-1: var(--color-base-100);
|
||||
--color-elevation-0: var(--color-base-150);
|
||||
|
||||
--color-shadow: var(--color-base-500);
|
||||
|
||||
--texture-dots: url(/images/paper-dots.webp);
|
||||
--texture-dots-blend: multiply;
|
||||
}
|
||||
|
||||
.theme-color-dark {
|
||||
--color-base-1000: #ebeae7;
|
||||
--color-base-950: #eae5e0;
|
||||
--color-base-900: #e8dfd8;
|
||||
--color-base-850: #e4d1c4;
|
||||
--color-base-800: #e0bfaa;
|
||||
--color-base-750: #dcb095;
|
||||
--color-base-700: #d4a07f;
|
||||
--color-base-650: #cb916c;
|
||||
--color-base-600: #bf835d;
|
||||
--color-base-550: #b07751;
|
||||
--color-base-500: #a06b48;
|
||||
--color-base-450: #8f5f40;
|
||||
--color-base-400: #7d5539;
|
||||
--color-base-350: #6b4a33;
|
||||
--color-base-300: #5c412e;
|
||||
--color-base-250: #4a3728;
|
||||
--color-base-200: #3a2d22;
|
||||
--color-base-150: #27231e;
|
||||
--color-base-100: #1c1b16;
|
||||
--color-base-50: #11110d;
|
||||
--color-base-0: #000000;
|
||||
|
||||
--color-elevation-1: var(--color-base-200);
|
||||
--color-elevation-0: var(--color-base-150);
|
||||
|
||||
--color-shadow: var(--color-base-50);
|
||||
|
||||
--texture-dots: url(/images/paper-dots-dark.webp);
|
||||
--texture-dots-blend: overlay;
|
||||
}
|
||||
|
||||
/* FONTS */
|
||||
|
||||
@font-face {
|
||||
font-family: "Vollkorn";
|
||||
src: url("/fonts/Vollkorn-Bold.woff2") format("woff2");
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Noto Sans";
|
||||
src: url("/fonts/NotoSans-Medium.woff2") format("woff2");
|
||||
font-weight: medium;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Noto Sans";
|
||||
src: url("/fonts/NotoSans-Bold.woff2") format("woff2");
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Angelic Agrippa";
|
||||
src: url("/fonts/AngelicAgrippa-Regular.woff2") format("woff2");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
p {
|
||||
font-family: var(--font-body);
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-family: var(--font-headers);
|
||||
}
|
||||
|
||||
.font-headers {
|
||||
font-family: var(--font-headers);
|
||||
}
|
||||
|
||||
.font-body {
|
||||
font-family: var(--font-body);
|
||||
}
|
||||
|
||||
.text-xs {
|
||||
font-size: 0.75rem /* 12px */;
|
||||
line-height: 1rem /* 16px */;
|
||||
}
|
||||
|
||||
.text-sm {
|
||||
font-size: 0.875rem /* 14px */;
|
||||
line-height: 1.25rem /* 20px */;
|
||||
}
|
||||
|
||||
.text-base {
|
||||
font-size: 1rem /* 16px */;
|
||||
line-height: 1.5rem /* 24px */;
|
||||
}
|
||||
|
||||
.text-lg {
|
||||
font-size: 1.125rem /* 18px */;
|
||||
line-height: 1.75rem /* 28px */;
|
||||
}
|
||||
|
||||
.text-xl {
|
||||
font-size: 1.25rem /* 20px */;
|
||||
line-height: 1.75rem /* 28px */;
|
||||
}
|
||||
|
||||
.text-2xl {
|
||||
font-size: 1.5rem /* 24px */;
|
||||
line-height: 2rem /* 32px */;
|
||||
}
|
||||
|
||||
.text-3xl {
|
||||
font-size: 1.875rem /* 30px */;
|
||||
line-height: 2.25rem /* 36px */;
|
||||
}
|
||||
|
||||
/* GLOBAL */
|
||||
|
||||
* {
|
||||
scrollbar-color: var(--color-base-600) transparent;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar {
|
||||
background-color: transparent;
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-thumb {
|
||||
background: var(--color-base-600);
|
||||
border-radius: 9999px;
|
||||
}
|
||||
|
||||
.texture-dots {
|
||||
background-size: 10cm;
|
||||
background-attachment: local;
|
||||
background-image: var(--texture-dots);
|
||||
background-blend-mode: var(--texture-dots-blend);
|
||||
background-repeat: repeat;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--color-base-150);
|
||||
color: var(--color-base-1000);
|
||||
font-size: 95%;
|
||||
}
|
||||
|
||||
button {
|
||||
color: var(--color-base-600);
|
||||
border: 1px solid var(--color-base-600);
|
||||
border-radius: 9999px;
|
||||
padding: 0 1rem;
|
||||
height: 2.5rem;
|
||||
transition-property: color, background-color, box-shadow, border-color;
|
||||
transition-duration: 0.1s;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--color-base-600);
|
||||
color: var(--color-base-150);
|
||||
box-shadow:
|
||||
0 10px 15px -3px var(--color-shadow),
|
||||
0 0 6px -4px var(--color-shadow);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: var(--color-base-1000);
|
||||
color: var(--color-base-0);
|
||||
border-color: var(--color-base-1000);
|
||||
box-shadow:
|
||||
0 10px 15px -3px var(--color-base-1000),
|
||||
0 0 6px -4px var(--color-base-1000);
|
||||
}
|
||||
}
|
||||
|
||||
.turbo-progress-bar {
|
||||
height: 5px;
|
||||
background-color: #b07751;
|
||||
}
|
||||
|
||||
:root {
|
||||
--font-body: "Noto Sans", sans-serif;
|
||||
--font-headers: "Vollkorn", serif;
|
||||
--font-angelic: "Angelic Agrippa", serif;
|
||||
}
|
||||
</style>
|
|
@ -18,35 +18,30 @@ const currentPath = new URL(Astro.request.url).pathname.slice(
|
|||
const isActive = currentPath.startsWith(href);
|
||||
---
|
||||
|
||||
{
|
||||
/* ------------------------------------------- HTML ------------------------------------------- */
|
||||
}
|
||||
|
||||
<a
|
||||
id="component"
|
||||
href={getLocalizedUrl(href, locale)}
|
||||
class:list={{ active: isActive }}
|
||||
>
|
||||
{icon && <Icon name={icon} />}
|
||||
<div>
|
||||
<p id="title">
|
||||
<div id="text-content">
|
||||
<p class="font-headers">
|
||||
{title}
|
||||
</p>
|
||||
<p>
|
||||
{subtitle}
|
||||
</p>
|
||||
{subtitle && <p>{subtitle}</p>}
|
||||
</div>
|
||||
</a>
|
||||
|
||||
{
|
||||
/* ------------------------------------------- CSS -------------------------------------------- */
|
||||
}
|
||||
|
||||
<style>
|
||||
#component {
|
||||
@container (max-width: 15rem) {
|
||||
width: 3.5rem;
|
||||
|
||||
& > div {
|
||||
& > #title,
|
||||
& > p {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
padding: 1rem;
|
||||
color: inherit;
|
||||
text-decoration: inherit;
|
||||
|
@ -76,19 +71,26 @@ const isActive = currentPath.startsWith(href);
|
|||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
& > div {
|
||||
display: grid;
|
||||
flex-grow: 1;
|
||||
@container (max-width: 15rem) {
|
||||
width: 3.5rem;
|
||||
|
||||
& > #title {
|
||||
line-height: 1.1;
|
||||
font-size: 1.5rem;
|
||||
font-family: var(--font-headers);
|
||||
}
|
||||
|
||||
& > p {
|
||||
margin: 0;
|
||||
& > #text-content {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#text-content {
|
||||
display: grid;
|
||||
flex-grow: 1;
|
||||
gap: 0.25rem;
|
||||
|
||||
& > .font-headers {
|
||||
font-size: 150%;
|
||||
}
|
||||
|
||||
& > :not(.font-headers) {
|
||||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,93 @@
|
|||
---
|
||||
import { Icon } from "astro-icon/components";
|
||||
import { getLocalizedUrl } from "utils/urls";
|
||||
interface Props {
|
||||
href: string;
|
||||
icon?: string;
|
||||
title: string | null | undefined;
|
||||
subtitle?: string | null | undefined;
|
||||
}
|
||||
|
||||
const { locale } = Astro.params;
|
||||
const { href, icon, title, subtitle } = Astro.props;
|
||||
|
||||
const currentPath = new URL(Astro.request.url).pathname.slice(
|
||||
`/${locale}`.length
|
||||
);
|
||||
|
||||
const isActive = currentPath.startsWith(href);
|
||||
---
|
||||
|
||||
{
|
||||
/* ------------------------------------------- HTML ------------------------------------------- */
|
||||
}
|
||||
|
||||
<a
|
||||
id="component"
|
||||
href={getLocalizedUrl(href, locale)}
|
||||
class:list={{ active: isActive }}
|
||||
data-tippy-template="#text-content"
|
||||
>
|
||||
{icon && <Icon name={icon} />}
|
||||
<div id="text-content">
|
||||
<p class="font-headers">
|
||||
{title}
|
||||
</p>
|
||||
{subtitle && <p>{subtitle}</p>}
|
||||
</div>
|
||||
</a>
|
||||
|
||||
{
|
||||
/* ------------------------------------------- CSS -------------------------------------------- */
|
||||
}
|
||||
|
||||
<style>
|
||||
#component {
|
||||
padding: 1rem;
|
||||
color: inherit;
|
||||
text-decoration: inherit;
|
||||
|
||||
display: flex;
|
||||
width: 100%;
|
||||
gap: 1.25rem;
|
||||
border-radius: 1rem;
|
||||
|
||||
transition:
|
||||
0.15s background-color,
|
||||
0.15s box-shadow;
|
||||
|
||||
&:hover,
|
||||
&.active {
|
||||
box-shadow: inset 0 1px 4px -2px var(--color-shadow);
|
||||
background-color: var(--color-base-250);
|
||||
|
||||
&:active {
|
||||
box-shadow: inset 0 2px 4px 0 var(--color-shadow);
|
||||
}
|
||||
}
|
||||
|
||||
& > svg {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
& > #text-content {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
#text-content {
|
||||
display: grid;
|
||||
flex-grow: 1;
|
||||
gap: 0.25rem;
|
||||
|
||||
& > .font-headers {
|
||||
font-size: 150%;
|
||||
}
|
||||
|
||||
& > :not(.font-headers) {
|
||||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,13 +1,13 @@
|
|||
import type { Observable } from "./micro-observables";
|
||||
|
||||
export class Elementos {
|
||||
readonly element: HTMLElement;
|
||||
readonly element: NodeListOf<Element>;
|
||||
constructor(readonly selector: string) {
|
||||
this.element = document.querySelector(selector)!;
|
||||
this.element = document.querySelectorAll(selector)!;
|
||||
}
|
||||
|
||||
onClick(listener: () => void) {
|
||||
this.element.addEventListener("click", listener);
|
||||
this.element.forEach((e) => e.addEventListener("click", listener));
|
||||
}
|
||||
|
||||
setClass(
|
||||
|
@ -15,12 +15,14 @@ export class Elementos {
|
|||
observable: Observable<boolean>
|
||||
) {
|
||||
observable.subscribe((val) => {
|
||||
if (typeof className === "string") {
|
||||
this.element.classList.toggle(className, val);
|
||||
} else {
|
||||
this.element.classList.toggle(className.ifFalse, val === false);
|
||||
this.element.classList.toggle(className.ifTrue, val === true);
|
||||
}
|
||||
this.element.forEach((e) => {
|
||||
if (typeof className === "string") {
|
||||
e.classList.toggle(className, val);
|
||||
} else {
|
||||
e.classList.toggle(className.ifFalse, val === false);
|
||||
e.classList.toggle(className.ifTrue, val === true);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
import { type AstroCookies } from "astro";
|
||||
|
||||
type AstroCookie = ReturnType<AstroCookies["get"]>;
|
||||
|
||||
export const parseCookie = <T>(cookies: AstroCookie, defaultValue: T): T => {
|
||||
const value: T | null = JSON.parse(cookies?.value ?? "null");
|
||||
return value ?? defaultValue;
|
||||
};
|
|
@ -0,0 +1,5 @@
|
|||
export const customEvents = {
|
||||
mainPanel: {
|
||||
reduce: { toggle: "mainPanel.reduce.toggle" },
|
||||
},
|
||||
};
|
|
@ -5,8 +5,10 @@ export const observableWithPersistence = <T>(
|
|||
cookieKey: string,
|
||||
defaultValue: T
|
||||
): WritableObservable<T> => {
|
||||
const valueFromCookie = Cookies.get(cookieKey) as T | undefined;
|
||||
const valueFromCookie: T | null = JSON.parse(
|
||||
Cookies.get(cookieKey) ?? "null"
|
||||
);
|
||||
const obs = observable(valueFromCookie ?? defaultValue);
|
||||
obs.subscribe((val) => Cookies.set(cookieKey, val as string));
|
||||
obs.subscribe((val) => Cookies.set(cookieKey, JSON.stringify(val)));
|
||||
return obs;
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue