feat: full JS-less theme transition, better transition animation between themes, better light theme colors

This commit is contained in:
Oleksandr 2026-02-13 07:46:00 +02:00
parent da6dd0100b
commit c0e0266d45
Signed by: Xanazf
GPG key ID: 821EEC32761AC17C
46 changed files with 4031 additions and 2536 deletions

1280
.pnp.cjs generated

File diff suppressed because it is too large Load diff

View file

@ -11,7 +11,7 @@
}, },
"plugins": [], "plugins": [],
"linter": { "linter": {
"enabled": false, "enabled": true,
"rules": { "rules": {
"recommended": true, "recommended": true,
"style": { "style": {
@ -53,10 +53,7 @@
}, },
"overrides": [ "overrides": [
{ {
"includes": [ "includes": ["**/*.ts", "**/*.tsx"],
"**/*.ts",
"**/*.tsx"
],
"linter": { "linter": {
"rules": { "rules": {
"complexity": { "complexity": {
@ -80,11 +77,13 @@
} }
}, },
{ {
"includes": [ "includes": ["*.astro"],
"*.astro"
],
"linter": { "linter": {
"rules": { "rules": {
"correctness": {
"noUnusedImports": "off",
"noUnusedVariables": "off"
},
"style": { "style": {
"useConst": "off", "useConst": "off",
"useImportType": "off" "useImportType": "off"

2807
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -18,7 +18,7 @@
"@fontsource-variable/rubik": "^5.2.8", "@fontsource-variable/rubik": "^5.2.8",
"@hbsnow/rehype-sectionize": "^1.0.7", "@hbsnow/rehype-sectionize": "^1.0.7",
"@pagefind/default-ui": "^1.4.0", "@pagefind/default-ui": "^1.4.0",
"@shikijs/rehype": "^3.20.0", "@shikijs/rehype": "^3.22.0",
"astro": "5.17.2", "astro": "5.17.2",
"astro-breadcrumbs": "^3.3.3", "astro-breadcrumbs": "^3.3.3",
"astro-icon": "^1.1.5", "astro-icon": "^1.1.5",
@ -26,26 +26,27 @@
"hastscript": "^9.0.1", "hastscript": "^9.0.1",
"rehype": "^13.0.2", "rehype": "^13.0.2",
"remark-github-blockquote-alert": "^2.0.1", "remark-github-blockquote-alert": "^2.0.1",
"solid-js": "^1.9.10", "solid-js": "^1.9.11",
"unified": "^11.0.5", "unified": "^11.0.5",
"unist-util-visit": "^5.0.0" "unist-util-visit": "^5.1.0"
}, },
"devDependencies": { "devDependencies": {
"@astrojs/ts-plugin": "1.10.6", "@astrojs/ts-plugin": "1.10.6",
"@babel/core": "^7.28.5", "@babel/core": "^7.29.0",
"@babel/plugin-syntax-typescript": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.28.6",
"@biomejs/biome": "^2.3.10", "@biomejs/biome": "^2.3.15",
"@types/babel__core": "^7.20.5", "@types/babel__core": "^7.20.5",
"@types/hast": "^3.0.4", "@types/hast": "^3.0.4",
"@types/mdast": "^4.0.4", "@types/mdast": "^4.0.4",
"@types/node": "^25.0.3", "@types/node": "^25.2.3",
"@types/unist": "^3.0.3", "@types/unist": "^3.0.3",
"baseline-browser-mapping": "^2.9.19",
"jsonc-parser": "^3.3.1", "jsonc-parser": "^3.3.1",
"pagefind": "^1.4.0", "pagefind": "^1.4.0",
"shiki": "^3.20.0", "shiki": "^3.22.0",
"tsx": "^4.21.0", "tsx": "^4.21.0",
"typescript": "^5.9.3", "typescript": "^5.9.3",
"vite": "^7.3.0" "vite": "^7.3.1"
}, },
"packageManager": "yarn@4.12.0" "packageManager": "yarn@4.12.0"
} }

View file

@ -12,15 +12,11 @@ export default function pagefind(): AstroIntegration {
const cwd = dirname(fileURLToPath(import.meta.url)); const cwd = dirname(fileURLToPath(import.meta.url));
const relativeDir = relative(cwd, targetDir); const relativeDir = relative(cwd, targetDir);
return new Promise<void>(resolve => { return new Promise<void>(resolve => {
spawn( spawn("yarn", ["pagefind", "--site", relativeDir], {
"yarn", stdio: "inherit",
["pagefind", "--site", relativeDir], shell: true,
{ cwd,
stdio: "inherit", }).on("close", () => resolve());
shell: true,
cwd,
}
).on("close", () => resolve());
}); });
}, },
}, },

View file

@ -2,7 +2,6 @@
import matrixLogo from "@icons/matrix-logo.svg?raw"; import matrixLogo from "@icons/matrix-logo.svg?raw";
import discordLogo from "@icons/discord-logo.svg?raw"; import discordLogo from "@icons/discord-logo.svg?raw";
import gitLogo from "@icons/git-logo.svg?raw"; import gitLogo from "@icons/git-logo.svg?raw";
import { ThemeSelect } from "./hooks/ThemeSwitch";
import ThemeToggle from "./ThemeToggle.astro"; import ThemeToggle from "./ThemeToggle.astro";
interface Props { interface Props {

View file

@ -1,6 +1,6 @@
--- ---
import { ThemeSelect } from "@components/hooks/ThemeSwitch"; import ThemeToggle from "./ThemeToggle.astro";
import type { TypeData } from "@config/io/types"; import type { TypeData } from "@config/_types";
import Nav from "@components/navigation/sidebars/nav/index.astro"; import Nav from "@components/navigation/sidebars/nav/index.astro";
import TOC from "@components/navigation/sidebars/TOC.astro"; import TOC from "@components/navigation/sidebars/TOC.astro";
import type { ConfigHeading } from "@components/navigation/sidebars/types"; import type { ConfigHeading } from "@components/navigation/sidebars/types";
@ -23,7 +23,7 @@ const { title, headings, type } = Astro.props;
</div> </div>
<div class="header-item header-right"> <div class="header-item header-right">
<Search/> <Search/>
<ThemeSelect client:load /> <ThemeToggle />
<TOC title={title} headings={headings} type={type} mobile={true}/> <TOC title={title} headings={headings} type={type} mobile={true}/>
</div> </div>
</div> </div>

View file

@ -2,21 +2,19 @@
import { Icon } from "astro-icon/components"; import { Icon } from "astro-icon/components";
--- ---
<label class="theme-toggle icon-button standard" title="Toggle theme"> <label
<input class="theme-toggle icon-button standard"
type="checkbox" title="Toggle theme"
id="theme-manual-toggle" for="theme-manual-toggle"
class="theme-toggle-input" >
aria-label="Toggle theme (light/dark)"
/>
<Icon <Icon
name="sun" name="moon"
class="light-icon" class="light-icon"
style="width: 24px; height: 24px;" style="width: 24px; height: 24px;"
aria-hidden="true" aria-hidden="true"
/> />
<Icon <Icon
name="moon" name="sun"
class="dark-icon" class="dark-icon"
style="width: 24px; height: 24px;" style="width: 24px; height: 24px;"
aria-hidden="true" aria-hidden="true"
@ -30,13 +28,6 @@ import { Icon } from "astro-icon/components";
user-select: none; user-select: none;
} }
.theme-toggle-input {
position: absolute;
opacity: 0;
width: 0;
height: 0;
}
.light-icon { .light-icon {
display: block; display: block;
} }
@ -44,11 +35,11 @@ import { Icon } from "astro-icon/components";
display: none; display: none;
} }
.theme-toggle:has(.theme-toggle-input:checked) .light-icon { :global(html:has(input#theme-manual-toggle:checked)) .light-icon {
display: none; display: none;
} }
.theme-toggle:has(.theme-toggle-input:checked) .dark-icon { :global(html:has(input#theme-manual-toggle:checked)) .dark-icon {
display: block; display: block;
} }

View file

@ -1,30 +0,0 @@
---
// NOTE: to be migrated to @config/styling/animations_helper.ts
---
<script>
window.addEventListener('DOMContentLoaded', () => {
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
const heading = entry.target.querySelector('h1, h2, h3, h4, h5, h6')
if(heading) {
const id = heading.id
if (entry.intersectionRatio > 0) {
const desktopElement = document.querySelector(`.toc-wrapper li a[href="#${id}"]`);
const mobileElement = document.querySelector(`.toc-wrapper-mobile li a[href="#${id}"]`);
const element = mobileElement?.checkVisibility() ? mobileElement : desktopElement;
element?.parentElement?.classList.add('active')
} else {
const desktopElement = document.querySelector(`.toc-wrapper li a[href="#${id}"]`);
const mobileElement = document.querySelector(`.toc-wrapper-mobile li a[href="#${id}"]`);
const element = mobileElement?.checkVisibility() ? mobileElement : desktopElement;
element?.parentElement?.classList.remove('active')
}
}
});
});
document.querySelectorAll('section[data-heading-rank]').forEach((section) => {
observer.observe(section);
});
});
</script>

View file

@ -1,113 +0,0 @@
// NOTE: to be replaced by @components/ThemeToggle.astro
import {
createSignal,
createEffect,
onCleanup,
onMount,
type VoidComponent,
} from "solid-js";
import Sun from "@icons/sun.svg?raw";
import Moon from "@icons/moon.svg?raw";
export interface ThemeProps {
theme: "light" | "dark";
system: "light" | "dark";
}
export const getCurrentTheme = (): ThemeProps => {
if (
typeof localStorage !== "undefined" &&
(localStorage.theme === "dark" ||
(!("theme" in localStorage) &&
window.matchMedia("(prefers-color-scheme: dark)")
.matches))
) {
return {
theme: "dark",
system: window.matchMedia("(prefers-color-scheme: dark)")
.matches
? "dark"
: "light",
};
}
return {
theme: "light",
system: window.matchMedia("(prefers-color-scheme: dark)")
.matches
? "dark"
: "light",
};
};
const updateTheme = () => {
document.documentElement.classList.add("changing-theme");
if (
localStorage.theme === "dark" ||
(!("theme" in localStorage) &&
window.matchMedia("(prefers-color-scheme: dark)").matches)
) {
document.documentElement.classList.add("dark");
} else {
document.documentElement.classList.remove("dark");
}
requestAnimationFrame(() => {
requestAnimationFrame(() => {
document.documentElement.classList.remove("changing-theme");
});
});
};
export const ThemeSelect: VoidComponent = () => {
const [currentTheme, setCurrentTheme] =
createSignal<ThemeProps>({
theme: "dark",
system: "dark",
});
const [mounted, setMounted] = createSignal(false);
const toggleTheme = () => {
if (!mounted()) return;
setCurrentTheme(getCurrentTheme());
if (currentTheme()!.theme !== currentTheme()!.system) {
localStorage.removeItem("theme");
} else {
localStorage.theme =
currentTheme()!.theme === "dark" ? "light" : "dark";
}
updateTheme();
setCurrentTheme(getCurrentTheme());
};
onMount(() => {
setMounted(true);
setCurrentTheme(getCurrentTheme());
});
createEffect(() => {
const mediaQuery = window.matchMedia(
"(prefers-color-scheme: dark)"
);
mediaQuery.addEventListener("change", updateTheme);
window.addEventListener("storage", updateTheme);
onCleanup(() => {
mediaQuery.removeEventListener("change", updateTheme);
window.removeEventListener("storage", updateTheme);
setMounted(false);
});
});
return (
<div
onclick={toggleTheme}
class="theme-toggle"
innerHTML={
(mounted() && currentTheme().theme === "light") ||
currentTheme().system === "light"
? Sun
: Moon
}
/>
);
};

View file

@ -1,6 +1,6 @@
--- ---
import "@pagefind/default-ui/css/ui.css"; import "@pagefind/default-ui/css/ui.css";
import magnifierIcon from "@icons/magnifier.svg?raw" import magnifierIcon from "@icons/magnifier.svg?raw";
--- ---
<site-search class="search-wrapper"> <site-search class="search-wrapper">
<button <button

View file

@ -12,12 +12,14 @@ export interface Props {
const { title, headings, type, mobile } = Astro.props; const { title, headings, type, mobile } = Astro.props;
const types: TypeTOC | null = type ? { const types: TypeTOC | null = type
properties: Object.keys(type.properties ?? {}), ? {
functions: (type.functions ?? []).map(f => f.name), properties: Object.keys(type.properties ?? {}),
signals: Object.keys(type.signals ?? {}), functions: (type.functions ?? []).map(f => f.name),
variants: Object.keys(type.variants ?? {}), signals: Object.keys(type.signals ?? {}),
} : null; variants: Object.keys(type.variants ?? {}),
}
: null;
--- ---
{((headings?.length ?? 0) != 0 || types) && {((headings?.length ?? 0) != 0 || types) &&
<div id="toc" aria-mobile={mobile} class=`toc-wrapper${mobile ? "-mobile":""}`> <div id="toc" aria-mobile={mobile} class=`toc-wrapper${mobile ? "-mobile":""}`>

View file

@ -13,32 +13,46 @@ import Link from "./Link.astro";
const versions = await getVersionsData(); const versions = await getVersionsData();
const versionName = Astro.params.version; const versionName = Astro.params.version;
const modules = versions.versions.find(version => version.name === versionName)?.modules; const modules = versions.versions.find(
version => version.name === versionName
)?.modules;
const currentPath = Astro.url.pathname.split('/').filter(s => s !== ""); const currentPath = Astro.url.pathname
.split("/")
.filter(s => s !== "");
const guidePages = await getGuideCollection(versionName ?? ""); const guidePages = await getGuideCollection(versionName ?? "");
interface NavTree { interface NavTree {
title: string, title: string;
slug: string, slug: string;
entries?: NavTree[], entries?: NavTree[];
} }
function mkTree(mount: string, pathIdx: number, { title, slug, entries }: NavTree): TreeEntry { function mkTree(
mount: string,
pathIdx: number,
{ title, slug, entries }: NavTree
): TreeEntry {
const link = `${mount}/${slug}`; const link = `${mount}/${slug}`;
return { return {
title, title,
link, link,
current: currentPath[pathIdx] === slug, current: currentPath[pathIdx] === slug,
entries: entries?.map(entry => mkTree(link, pathIdx + 1, entry)), entries: entries?.map(entry =>
mkTree(link, pathIdx + 1, entry)
),
}; };
} }
function genGuideNav(base: string): NavTree[] | undefined { function genGuideNav(base: string): NavTree[] | undefined {
const pages = guidePages const pages = guidePages
.filter(page => page.id.match(`^${base}[^/]*$`) !== null && page.id !== "index") .filter(
page =>
page.id.match(`^${base}[^/]*$`) !== null &&
page.id !== "index"
)
.sort((a, b) => a.data.index - b.data.index) .sort((a, b) => a.data.index - b.data.index)
.map(page => ({ .map(page => ({
title: page.data.title, title: page.data.title,
@ -68,8 +82,8 @@ if (versionName) {
entries: module.types.map(type => ({ entries: module.types.map(type => ({
title: type.name, title: type.name,
slug: type.name, slug: type.name,
})) })),
})) })),
}), }),
}; };

View file

@ -20,10 +20,12 @@ export const Table: Component<{
if (configTOC) { if (configTOC) {
return ( return (
<div class="toc-content"> <div class="toc-content">
{title && <> {title && (
<p>{title}</p> <>
<hr/> <p>{title}</p>
</>} <hr />
</>
)}
<For each={configTOC}> <For each={configTOC}>
{heading => ( {heading => (
<Heading <Heading

View file

@ -6,7 +6,7 @@ import {
type Component, type Component,
} from "solid-js"; } from "solid-js";
import { Article } from "@icons"; import { Article, MenuToX } from "@icons";
import { Table } from "./Table"; import { Table } from "./Table";
import type { TOCProps } from "../types"; import type { TOCProps } from "../types";
import { buildHierarchy } from "@config/io/helpers"; import { buildHierarchy } from "@config/io/helpers";
@ -27,7 +27,10 @@ const TableOfContents: Component<TOCProps> = props => {
return type ? ( return type ? (
<Table typeTOC={type} /> <Table typeTOC={type} />
) : ( ) : (
<Table title={title} configTOC={buildHierarchy(config!)} /> <Table
title={title}
configTOC={buildHierarchy(config!)}
/>
); );
} }
@ -92,13 +95,17 @@ const TableOfContents: Component<TOCProps> = props => {
id="toc-toggle" id="toc-toggle"
> >
<div onclick={e => toggle(e)}> <div onclick={e => toggle(e)}>
<Article /> <Article class={`toc-icon ${!open() ? "active" : ""}`} />
<MenuToX class={`toc-icon ${open() ? "active" : ""}`} />
</div> </div>
<div class={`toc-mobile ${open() ? "shown" : ""}`}> <div class={`toc-mobile ${open() ? "shown" : ""}`}>
{type ? ( {type ? (
<Table typeTOC={type} /> <Table typeTOC={type} />
) : ( ) : (
<Table title={title} configTOC={buildHierarchy(config!)} /> <Table
title={title}
configTOC={buildHierarchy(config!)}
/>
)} )}
</div> </div>
</div> </div>

View file

@ -8,7 +8,9 @@ export interface Props {
const { markdown } = Astro.props; const { markdown } = Astro.props;
const { version } = Astro.params; const { version } = Astro.params;
const html = markdown ? await processMarkdown(version!, markdown) : null; const html = markdown
? await processMarkdown(version!, markdown)
: null;
--- ---
<section class="typedata-details"> <section class="typedata-details">
{html ? <div class="typedata-detailsdata" set:html={html} /> : <em>No details provided</em>} {html ? <div class="typedata-detailsdata" set:html={html} /> : <em>No details provided</em>}

View file

@ -1,23 +0,0 @@
---
---
<script is:inline>
function updateTheme() {
if (
localStorage.theme === "dark" ||
(!("theme" in localStorage) &&
window.matchMedia("(prefers-color-scheme: dark)").matches)
) {
document.documentElement.classList.add("dark");
} else {
document.documentElement.classList.remove("dark");
}
}
// Run on initial load
updateTheme();
// Run on view transitions
document.addEventListener("astro:after-swap", updateTheme);
</script>

View file

@ -27,118 +27,118 @@ let currentVersion = "NOVERSION";
const remarkParseAtTypes: RemarkPlugin<[]> = const remarkParseAtTypes: RemarkPlugin<[]> =
() => () =>
(root: Md.Root): Md.Root => { (root: Md.Root): Md.Root => {
visit(root as Unist.Parent, (rawNode: Unist.Node) => { visit(root as Unist.Parent, (rawNode: Unist.Node) => {
if ( if (
rawNode.type === "text" || rawNode.type === "text" ||
(rawNode.type === "code" && (rawNode.type === "code" &&
(rawNode as Md.Code).lang === "qml") (rawNode as Md.Code).lang === "qml")
) { ) {
const node = rawNode as Md.Literal; const node = rawNode as Md.Literal;
node.value = node.value.replace( node.value = node.value.replace(
/@@((?<module>([A-Z]\w*\.)*)(?<type>([A-Z]\w*))(\.(?!\s|$))?)?((?<member>[a-z]\w*)((?<function>\(\))|(?<signal>\(s\)))?)?(?=[$.,;:)\s]|$)/g, /@@((?<module>([A-Z]\w*\.)*)(?<type>([A-Z]\w*))(\.(?!\s|$))?)?((?<member>[a-z]\w*)((?<function>\(\))|(?<signal>\(s\)))?)?(?=[$.,;:)\s]|$)/g,
(_full, ...args) => { (_full, ...args) => {
type Capture = { type Capture = {
module: string | undefined; module: string | undefined;
type: string | undefined; type: string | undefined;
member: string | undefined; member: string | undefined;
function: string | undefined; function: string | undefined;
signal: string | undefined; signal: string | undefined;
}; };
const groups = args.pop() as Capture; const groups = args.pop() as Capture;
if (groups.module) { if (groups.module) {
groups.module = groups.module.substring( groups.module = groups.module.substring(
0, 0,
groups.module.length - 1 groups.module.length - 1
); );
const isQs = groups.module.startsWith("Quickshell"); const isQs = groups.module.startsWith("Quickshell");
groups.module = `99M${isQs ? "QS" : "QT_qml"}_${groups.module.replace(".", "_")}`; groups.module = `99M${isQs ? "QS" : "QT_qml"}_${groups.module.replace(".", "_")}`;
} else groups.module = ""; // WARNING: rehype parser can't currently handle intra-module links } else groups.module = ""; // WARNING: rehype parser can't currently handle intra-module links
groups.type = groups.type ? `99N${groups.type}` : ""; groups.type = groups.type ? `99N${groups.type}` : "";
groups.member = groups.member groups.member = groups.member
? `99V${groups.member}` ? `99V${groups.member}`
: ""; : "";
const type = groups.member const type = groups.member
? `99T${groups.function ? "func" : groups.signal ? "signal" : "prop"}` ? `99T${groups.function ? "func" : groups.signal ? "signal" : "prop"}`
: ""; : "";
return `TYPE${groups.module}${groups.type}${groups.member}${type}99TYPE`; return `TYPE${groups.module}${groups.type}${groups.member}${type}99TYPE`;
} }
); );
} }
}); });
return root; return root;
}; };
const rehypeRewriteTypelinks: RehypePlugin<[]> = const rehypeRewriteTypelinks: RehypePlugin<[]> =
() => () =>
(root: Html.Root): Html.Root => { (root: Html.Root): Html.Root => {
visit( visit(
root as Unist.Parent, root as Unist.Parent,
"text", "text",
(node: Html.Text, index: number, parent: Html.Parent) => { (node: Html.Text, index: number, parent: Html.Parent) => {
let changed = false; let changed = false;
node.value = node.value.replace( node.value = node.value.replace(
/TYPE99(\w+.)99TYPE/g, /TYPE99(\w+.)99TYPE/g,
(_full: string, match: string) => { (_full: string, match: string) => {
changed = true; changed = true;
const linkObject = getQMLTypeLinkObject(match); const linkObject = getQMLTypeLinkObject(match);
const link = getQMLTypeLink( const link = getQMLTypeLink(
currentVersion, currentVersion,
linkObject linkObject
); );
const icon = const icon =
linkObject.mtype && linkObject.mtype !== "func" linkObject.mtype && linkObject.mtype !== "func"
? getIconForLink(linkObject.mtype, false) ? getIconForLink(linkObject.mtype, false)
: null; : null;
const hasParens = const hasParens =
linkObject.mtype === "func" || linkObject.mtype === "func" ||
linkObject.mtype === "signal"; linkObject.mtype === "signal";
const hasDot = linkObject.name && linkObject.mname; const hasDot = linkObject.name && linkObject.mname;
return `<a class="type${linkObject.mtype}-link typedata-link" href=${link}>${icon ?? ""}${linkObject.name ?? ""}${hasDot ? "." : ""}${linkObject.mname ?? ""}${hasParens ? "()" : ""}</a>`; return `<a class="type${linkObject.mtype}-link typedata-link" href=${link}>${icon ?? ""}${linkObject.name ?? ""}${hasDot ? "." : ""}${linkObject.mname ?? ""}${hasParens ? "()" : ""}</a>`;
}
);
if (changed) {
const fragment = fromHtml(node.value, {
fragment: true,
});
parent.children.splice(index, 1, ...fragment.children);
return SKIP;
} }
);
return CONTINUE; if (changed) {
const fragment = fromHtml(node.value, {
fragment: true,
});
parent.children.splice(index, 1, ...fragment.children);
return SKIP;
} }
);
return root; return CONTINUE;
}; }
);
return root;
};
const rehypeRewriteVersionedDoclinks: RehypePlugin<[]> = const rehypeRewriteVersionedDoclinks: RehypePlugin<[]> =
() => () =>
(root: Html.Root): Html.Root => { (root: Html.Root): Html.Root => {
visit( visit(
root as Unist.Parent, root as Unist.Parent,
"element", "element",
({ tagName, properties }: Html.Element) => { ({ tagName, properties }: Html.Element) => {
if (tagName !== "a") return CONTINUE; if (tagName !== "a") return CONTINUE;
if ( if (
!((properties.href as string) ?? "").startsWith("@docs") !((properties.href as string) ?? "").startsWith("@docs")
) )
return CONTINUE;
properties.href = `/docs/${currentVersion}/${(properties.href as string).slice(6)}`;
return CONTINUE; return CONTINUE;
} properties.href = `/docs/${currentVersion}/${(properties.href as string).slice(6)}`;
); return CONTINUE;
}
);
return root; return root;
}; };
const shikiRewriteTypelinks: ShikiTransformer = { const shikiRewriteTypelinks: ShikiTransformer = {
name: "rewrite-typelinks", name: "rewrite-typelinks",
@ -170,7 +170,7 @@ const shikiCopyButton: ShikiTransformer = {
role: "button", role: "button",
"aria-label": "Copy to clipboard", "aria-label": "Copy to clipboard",
"alia-live": "polite", "alia-live": "polite",
// "data-code": removeCodeAnnotations(this.source), "data-code": this.source,
onclick: ` onclick: `
navigator.clipboard.writeText(this.dataset.code); navigator.clipboard.writeText(this.dataset.code);
this.classList.add('copied'); this.classList.add('copied');
@ -179,8 +179,6 @@ const shikiCopyButton: ShikiTransformer = {
`, `,
}, },
[ [
h("span", { class: "ready" }),
h("span", { class: "success" }),
h( h(
"svg", "svg",
{ {
@ -198,6 +196,23 @@ const shikiCopyButton: ShikiTransformer = {
}), }),
] ]
), ),
h(
"svg",
{
class: "check-icon",
role: "icon",
xmlns: "http://www.w3.org/2000/svg",
width: "1em",
height: "1em",
viewBox: "0 0 256 256",
},
[
h("path", {
fill: "currentColor",
d: "M229.66 77.66l-128 128a8 8 0 0 1-11.32 0l-56-56a8 8 0 0 1 11.32-11.32L96 188.69L218.34 66.34a8 8 0 0 1 11.32 11.32Z",
}),
]
),
] ]
); );
node.children.splice(0, 0, button); node.children.splice(0, 0, button);

View file

@ -23,7 +23,47 @@ export function initAnimations() {
animatedElements.forEach(el => observer.observe(el)); animatedElements.forEach(el => observer.observe(el));
} }
export function initTOCHighlighting() {
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
const heading = entry.target.querySelector(
"h1, h2, h3, h4, h5, h6"
);
if (heading) {
const id = heading.id;
const desktopElement = document.querySelector(
`.toc-wrapper li a[href="#${id}"]`
);
const mobileElement = document.querySelector(
`.toc-wrapper-mobile li a[href="#${id}"]`
);
if (entry.isIntersecting) {
desktopElement?.parentElement?.classList.add("active");
mobileElement?.parentElement?.classList.add("active");
} else {
desktopElement?.parentElement?.classList.remove(
"active"
);
mobileElement?.parentElement?.classList.remove(
"active"
);
}
}
});
});
document
.querySelectorAll("section[data-heading-rank]")
.forEach(section => {
observer.observe(section);
});
}
// auto-init on DOMContentLoaded // auto-init on DOMContentLoaded
if (typeof document !== "undefined") { if (typeof document !== "undefined") {
document.addEventListener("DOMContentLoaded", initAnimations); document.addEventListener("DOMContentLoaded", () => {
initAnimations();
initTOCHighlighting();
});
} }

View file

@ -1,8 +1,14 @@
document.addEventListener("DOMContentLoaded", () => { document.addEventListener("DOMContentLoaded", () => {
const container = document.querySelector(".marquee") as HTMLDivElement; const container = document.querySelector(
const scroller = document.querySelector(".marquee-content") as HTMLDivElement; ".marquee"
) as HTMLDivElement;
const scroller = document.querySelector(
".marquee-content"
) as HTMLDivElement;
const btnLeft = document.getElementById("marquee-scroll-left"); const btnLeft = document.getElementById("marquee-scroll-left");
const btnRight = document.getElementById("marquee-scroll-right"); const btnRight = document.getElementById(
"marquee-scroll-right"
);
if (!container || !scroller) return; if (!container || !scroller) return;
@ -28,13 +34,15 @@ document.addEventListener("DOMContentLoaded", () => {
// setup clones // setup clones
const setupClones = () => { const setupClones = () => {
// remove existing clones // remove existing clones
scroller.querySelectorAll(".clone").forEach((c) => c.remove()); scroller.querySelectorAll(".clone").forEach(c => c.remove());
const originals = Array.from(scroller.querySelectorAll(".marquee-item")) as HTMLDivElement[]; const originals = Array.from(
scroller.querySelectorAll(".marquee-item")
) as HTMLDivElement[];
// add clones after // add clones after
for (let i = 0; i < bufferSize; i++) { for (let i = 0; i < bufferSize; i++) {
originals.forEach((item) => { originals.forEach(item => {
const clone = item.cloneNode(true) as HTMLDivElement; const clone = item.cloneNode(true) as HTMLDivElement;
clone.classList.add("clone"); clone.classList.add("clone");
scroller.appendChild(clone); scroller.appendChild(clone);
@ -44,7 +52,7 @@ document.addEventListener("DOMContentLoaded", () => {
// add clones before // add clones before
const beforeContainer = document.createDocumentFragment(); const beforeContainer = document.createDocumentFragment();
for (let i = 0; i < bufferSize; i++) { for (let i = 0; i < bufferSize; i++) {
originals.forEach((item) => { originals.forEach(item => {
const clone = item.cloneNode(true) as HTMLDivElement; const clone = item.cloneNode(true) as HTMLDivElement;
clone.classList.add("clone"); clone.classList.add("clone");
beforeContainer.appendChild(clone); beforeContainer.appendChild(clone);
@ -52,7 +60,9 @@ document.addEventListener("DOMContentLoaded", () => {
} }
scroller.insertBefore(beforeContainer, scroller.firstChild); scroller.insertBefore(beforeContainer, scroller.firstChild);
items = Array.from(scroller.querySelectorAll(".marquee-item")) as HTMLDivElement[]; items = Array.from(
scroller.querySelectorAll(".marquee-item")
) as HTMLDivElement[];
}; };
const updateDimensions = () => { const updateDimensions = () => {
@ -63,13 +73,15 @@ document.addEventListener("DOMContentLoaded", () => {
// standardize width // standardize width
scroller.style.width = `${items.length * itemWidth}px`; scroller.style.width = `${items.length * itemWidth}px`;
items.forEach((item) => { items.forEach(item => {
item.style.width = `${itemWidth}px`; item.style.width = `${itemWidth}px`;
item.style.flex = `0 0 ${itemWidth}px`; item.style.flex = `0 0 ${itemWidth}px`;
item.style.maxWidth = `${itemWidth}px`; item.style.maxWidth = `${itemWidth}px`;
}); });
targetScrollX = bufferSize * sequenceWidth + (targetScrollX % sequenceWidth); targetScrollX =
bufferSize * sequenceWidth +
(targetScrollX % sequenceWidth);
currentScrollX = targetScrollX; currentScrollX = targetScrollX;
scroller.style.transform = `translateX(-${currentScrollX}px)`; scroller.style.transform = `translateX(-${currentScrollX}px)`;
}; };
@ -79,20 +91,35 @@ document.addEventListener("DOMContentLoaded", () => {
const animate = () => { const animate = () => {
if (!isDown && Math.abs(touchVelocity) < 0.1) { if (!isDown && Math.abs(touchVelocity) < 0.1) {
// snap to nearest item if not interacting and close to one // snap to nearest item if not interacting and close to one
const nearestItemScroll = Math.round(targetScrollX / itemWidth) * itemWidth; const nearestItemScroll =
if (Math.abs(targetScrollX - nearestItemScroll) < itemWidth * 0.5) { Math.round(targetScrollX / itemWidth) * itemWidth;
targetScrollX = lerp(targetScrollX, nearestItemScroll, 0.1); if (
} Math.abs(targetScrollX - nearestItemScroll) <
itemWidth * 0.5
) {
targetScrollX = lerp(
targetScrollX,
nearestItemScroll,
0.1
);
}
} }
currentScrollX = lerp(currentScrollX, targetScrollX, smoothFactor); currentScrollX = lerp(
currentScrollX,
targetScrollX,
smoothFactor
);
// boundary reset // boundary reset
if (currentScrollX > (bufferSize + 1) * sequenceWidth) { if (currentScrollX > (bufferSize + 1) * sequenceWidth) {
currentScrollX -= sequenceWidth; currentScrollX -= sequenceWidth;
targetScrollX -= sequenceWidth; targetScrollX -= sequenceWidth;
} else if (currentScrollX < (bufferSize - 1) * sequenceWidth) { } else if (
currentScrollX <
(bufferSize - 1) * sequenceWidth
) {
currentScrollX += sequenceWidth; currentScrollX += sequenceWidth;
targetScrollX += sequenceWidth; targetScrollX += sequenceWidth;
} }
@ -112,7 +139,9 @@ document.addEventListener("DOMContentLoaded", () => {
item.style.opacity = opacity.toString(); item.style.opacity = opacity.toString();
// NOTE: apply transform to the video container specifically // NOTE: apply transform to the video container specifically
// to keep layout stable // to keep layout stable
const content = item.querySelector(".marquee-item-content") as HTMLElement; const content = item.querySelector(
".marquee-item-content"
) as HTMLElement;
if (content) { if (content) {
content.style.transform = `scale(${scale}) translateY(${yOffset}px)`; content.style.transform = `scale(${scale}) translateY(${yOffset}px)`;
} }
@ -144,8 +173,8 @@ document.addEventListener("DOMContentLoaded", () => {
threshold: 0.5, threshold: 0.5,
}; };
const videoObserver = new IntersectionObserver((entries) => { const videoObserver = new IntersectionObserver(entries => {
entries.forEach((entry) => { entries.forEach(entry => {
const video = entry.target as HTMLVideoElement; const video = entry.target as HTMLVideoElement;
if (entry.isIntersecting) { if (entry.isIntersecting) {
video.play().catch(() => {}); // Handle potential autoplay blocks video.play().catch(() => {}); // Handle potential autoplay blocks
@ -155,7 +184,7 @@ document.addEventListener("DOMContentLoaded", () => {
}); });
}, observerOptions); }, observerOptions);
videos.forEach((v) => { videos.forEach(v => {
videoObserver.observe(v); videoObserver.observe(v);
v.addEventListener("ended", () => { v.addEventListener("ended", () => {
targetScrollX += itemWidth; targetScrollX += itemWidth;
@ -174,20 +203,24 @@ document.addEventListener("DOMContentLoaded", () => {
startAnimation(); startAnimation();
}); });
container.addEventListener("wheel", (e) => { container.addEventListener(
e.preventDefault(); "wheel",
targetScrollX += e.deltaY; e => {
startAnimation(); e.preventDefault();
}, { passive: false }); targetScrollX += e.deltaY;
startAnimation();
},
{ passive: false }
);
container.addEventListener("touchstart", (e) => { container.addEventListener("touchstart", e => {
isDown = true; isDown = true;
lastTouchX = e.touches[0].clientX; lastTouchX = e.touches[0].clientX;
lastTouchTime = Date.now(); lastTouchTime = Date.now();
touchVelocity = 0; touchVelocity = 0;
}); });
container.addEventListener("touchmove", (e) => { container.addEventListener("touchmove", e => {
if (!isDown) return; if (!isDown) return;
const currentTouchX = e.touches[0].clientX; const currentTouchX = e.touches[0].clientX;
const deltaX = lastTouchX - currentTouchX; const deltaX = lastTouchX - currentTouchX;
@ -221,6 +254,7 @@ document.addEventListener("DOMContentLoaded", () => {
setupClones(); setupClones();
setTimeout(() => { setTimeout(() => {
updateDimensions(); updateDimensions();
container.classList.add("initialized");
startAnimation(); startAnimation();
}, 50); }, 50);
}); });

View file

@ -1,52 +0,0 @@
document.addEventListener("DOMContentLoaded", () => {
let currentProgressScale = 0;
let targetProgressScale = 0;
let lastPercentage = 0;
const progressCounter = document.querySelector(
".progress-counter h1"
);
const progressBar = document.querySelector(
".progress-bar"
) as HTMLDivElement;
const updateProgress = (
sequenceWidth: number,
forceReset = false
) => {
const basePosition = sequenceWidth * bufferSize;
const currentPosition =
(currentScrollX - basePosition) % sequenceWidth;
let percentage = (currentPosition / sequenceWidth) * 100;
if (percentage < 0) {
percentage = 100 + percentage;
}
const isWrapping =
(lastPercentage > 80 && percentage < 20) ||
(lastPercentage < 20 && percentage > 80) ||
forceReset;
progressCounter.textContent = `${Math.round(percentage)}`;
targetProgressScale = percentage / 100;
if (isWrapping) {
currentProgressScale = targetProgressScale;
progressBar.style.transform = `scaleX(${currentProgressScale})`;
}
lastPercentage = percentage;
};
updateProgress(sequenceWidth, true);
progressBar.style.transform = `scaleX(${currentProgressScale})`;
updateProgress(sequenceWidth, forceProgressReset);
if (!forceProgressReset) {
currentProgressScale = lerp(
currentProgressScale,
targetProgressScale,
smoothFactor
);
}
progressBar.style.transform = `scaleX(${currentProgressScale})`;
});

View file

@ -0,0 +1,75 @@
interface ThemeProps {
theme: "light" | "dark";
system: "light" | "dark";
}
export const getCurrentTheme = (): ThemeProps => {
const isDarkSystem = window.matchMedia(
"(prefers-color-scheme: dark)"
).matches;
const systemTheme = isDarkSystem ? "dark" : "light";
if (typeof localStorage !== "undefined") {
if (localStorage.theme === "dark") {
return { theme: "dark", system: systemTheme };
}
if (localStorage.theme === "light") {
return { theme: "light", system: systemTheme };
}
}
return { theme: systemTheme, system: systemTheme };
};
export const updateTheme = () => {
const theme = getCurrentTheme();
const toggle = document.getElementById(
"theme-manual-toggle"
) as HTMLInputElement;
document.documentElement.classList.add("changing-theme");
if (theme.theme === "dark") {
document.documentElement.classList.add("dark");
if (toggle) toggle.checked = true;
} else {
document.documentElement.classList.remove("dark");
if (toggle) toggle.checked = false;
}
requestAnimationFrame(() => {
requestAnimationFrame(() => {
document.documentElement.classList.remove("changing-theme");
});
});
};
export const initTheme = () => {
const toggle = document.getElementById(
"theme-manual-toggle"
) as HTMLInputElement;
if (toggle) {
toggle.addEventListener("change", () => {
localStorage.theme = toggle.checked ? "dark" : "light";
updateTheme();
});
}
window
.matchMedia("(prefers-color-scheme: dark)")
.addEventListener("change", updateTheme);
window.addEventListener("storage", updateTheme);
// initial sync
updateTheme();
};
// auto-init on client
if (typeof document !== "undefined") {
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", initTheme);
} else {
initTheme();
}
}

View file

@ -2,11 +2,11 @@ import { defineCollection, z } from "astro:content";
import { glob } from "astro/loaders"; import { glob } from "astro/loaders";
const guide = defineCollection({ const guide = defineCollection({
loader: glob({ pattern: "**/*.md", base: "src/guide" }), loader: glob({ pattern: "**/*.md", base: "src/guide" }),
schema: z.object({ schema: z.object({
title: z.string(), title: z.string(),
index: z.number(), index: z.number(),
}), }),
}); });
export const collections = { guide }; export const collections = { guide };

View file

@ -1,7 +1,6 @@
--- ---
//import Header from "@components/Header.astro"; //import Header from "@components/Header.astro";
import Head from "@config/Head.astro"; import Head from "@config/Head.astro";
import PreTheme from "@config/PreTheme.astro";
interface Props { interface Props {
title: string; title: string;
@ -14,13 +13,20 @@ const { title, description } = Astro.props;
<html lang="en" class="dark"> <html lang="en" class="dark">
<head> <head>
<Head description={description} title={title} /> <Head description={description} title={title} />
<PreTheme />
</head> </head>
<body class="baselayout"> <body class="baselayout">
<input
type="checkbox"
id="theme-manual-toggle"
class="theme-toggle-input"
style="display: none;"
aria-label="Toggle theme (light/dark)"
/>
<!--<Header />--> <!--<Header />-->
<slot /> <slot />
<script> <script>
import "@config/styling/animations_helper.ts"; import "@config/styling/animations_helper.ts";
import "@config/styling/theme_persistence.ts";
</script> </script>
</body> </body>
</html> </html>

View file

@ -2,13 +2,12 @@
import { Breadcrumbs } from "astro-breadcrumbs"; import { Breadcrumbs } from "astro-breadcrumbs";
import "astro-breadcrumbs/breadcrumbs.css"; import "astro-breadcrumbs/breadcrumbs.css";
import PreTheme from "@config/PreTheme.astro";
import Header from "@components/Header.astro"; import Header from "@components/Header.astro";
import Head from "@config/Head.astro"; import Head from "@config/Head.astro";
import Nav from "@components/navigation/sidebars/nav/index.astro"; import Nav from "@components/navigation/sidebars/nav/index.astro";
import type { ConfigHeading } from "@src/components/navigation/sidebars/types"; import type { ConfigHeading } from "@src/components/navigation/sidebars/types";
import Footer from "@src/components/Footer.astro"; import Footer from "@src/components/Footer.astro";
import type { TypeData } from "@config/io/types"; import type { TypeData } from "@config/_types";
interface Props { interface Props {
title: string; title: string;
@ -50,9 +49,15 @@ for (const segment of url) {
<html lang="en" class="dark"> <html lang="en" class="dark">
<head> <head>
<Head description={description} title={title} /> <Head description={description} title={title} />
<PreTheme />
</head> </head>
<body class="docslayout"> <body class="docslayout">
<input
type="checkbox"
id="theme-manual-toggle"
class="theme-toggle-input"
aria-label="Toggle theme (light/dark)"
style="display: none;"
/>
<Header title={title} headings={headings} type={type}/> <Header title={title} headings={headings} type={type}/>
<div class="docslayout-root"> <div class="docslayout-root">
<Nav mobile={false}/> <Nav mobile={false}/>
@ -88,6 +93,10 @@ for (const segment of url) {
<slot name="alongside-content"/> <slot name="alongside-content"/>
</div> </div>
<Footer/> <Footer/>
<script>
import "@config/styling/animations_helper.ts";
import "@config/styling/theme_persistence.ts";
</script>
</body> </body>
</html> </html>
<script> <script>

View file

@ -1,7 +1,6 @@
--- ---
import DocsLayout from "@layouts/DocsLayout.astro"; import DocsLayout from "@layouts/DocsLayout.astro";
import TOC from "@components/navigation/sidebars/TOC.astro"; import TOC from "@components/navigation/sidebars/TOC.astro";
import TOCIntersectionObserver from "@src/components/hooks/TOCIntersectionObserver.astro";
import type { ConfigHeading } from "@src/components/navigation/sidebars/types"; import type { ConfigHeading } from "@src/components/navigation/sidebars/types";
export interface Props { export interface Props {
@ -22,5 +21,3 @@ const { title, description, headings } = Astro.props;
</div> </div>
<TOC slot="alongside-content" mobile={false} title={title} headings={headings} data-pagefind-ignore/> <TOC slot="alongside-content" mobile={false} title={title} headings={headings} data-pagefind-ignore/>
</DocsLayout> </DocsLayout>
<TOCIntersectionObserver/>

View file

@ -70,13 +70,13 @@ const details = type.details
<Properties props={type.properties!}/> <Properties props={type.properties!}/>
)} )}
{ (type.functions?.length ?? 0) != 0 && ( { (type.functions?.length ?? 0) != 0 && (
<h2>Functions <a href={`/docs/${version.name}guide/qml-language#functions`}>[?]</a></h2> <h2>Functions <a href={`/docs/${version.name}/guide/qml-language#functions`}>[?]</a></h2>
<Functions <Functions
funcData={type.functions!} funcData={type.functions!}
/> />
)} )}
{ Object.keys(type.signals ?? {}).length != 0 && ( { Object.keys(type.signals ?? {}).length != 0 && (
<h2>Signals <a href={`/docs/${version.name}guide/qml-language#signals`}>[?]</a></h2> <h2>Signals <a href={`/docs/${version.name}/guide/qml-language#signals`}>[?]</a></h2>
<Signals <Signals
signals={type.signals!} signals={type.signals!}
/> />

View file

@ -1,7 +1,7 @@
.accordion { .accordion {
& summary { & summary {
list-style: none; list-style: none;
transition: background-color 0.15s ease-out; transition: background-color var(--theme-transition);
} }
& .accordion-container { & .accordion-container {
@ -12,7 +12,9 @@
& .accordion-container.animate { & .accordion-container.animate {
/* this somehow breaks if both min AND max aren't animated */ /* this somehow breaks if both min AND max aren't animated */
transition: min-height 0.3s ease, max-height 0.3s ease; transition:
min-height 0.3s ease,
max-height 0.3s ease;
min-height: var(--height); min-height: var(--height);
max-height: var(--height); max-height: var(--height);
} }

View file

@ -20,25 +20,33 @@
background-color: hsl(var(--blue) 60% 98%); background-color: hsl(var(--blue) 60% 98%);
padding: 0.618rem; padding: 0.618rem;
border: 1px solid hsl(var(--blue) 9% 75%); border: 1px solid hsl(var(--blue) 9% 75%);
transition:
background-color var(--theme-transition),
border-color var(--theme-transition);
&::before { &::before {
content: ""; content: "";
position: absolute; position: absolute;
inset: var(--xs); inset: var(--xs);
background-image: radial-gradient(hsl(var(--blue) 9% 75%) 1px, background-image: radial-gradient(
transparent 1px); hsl(var(--blue) 9% 75%) 1px,
transparent 1px
);
background-position: 50% 50%; background-position: 50% 50%;
background-size: 1.1rem 1.1rem; background-size: 1.1rem 1.1rem;
} }
} }
html.dark .featurelist-item { html.dark .featurelist-item,
html:has(input#theme-manual-toggle:checked) .featurelist-item {
background-color: hsl(var(--blue) 100% 81% / 0.05); background-color: hsl(var(--blue) 100% 81% / 0.05);
border-color: hsl(0deg 0% 100% / 0.05); border-color: hsl(0deg 0% 100% / 0.05);
&::before { &::before {
background-image: radial-gradient(hsl(0deg 0% 100% / 0.1) 1px, background-image: radial-gradient(
transparent 1px); hsl(0deg 0% 100% / 0.1) 1px,
transparent 1px
);
} }
} }
@ -53,10 +61,12 @@ html.dark .featurelist-item {
& .feature-subtitle { & .feature-subtitle {
color: #303030; color: #303030;
transition: color var(--theme-transition);
} }
} }
html.dark .feature-text { html.dark .feature-text,
html:has(input#theme-manual-toggle:checked) .feature-text {
& .feature-subtitle { & .feature-subtitle {
color: #afafaf; color: #afafaf;
} }
@ -99,8 +109,13 @@ html.dark .feature-text {
} }
} }
html:not(.dark) .feature-showcase .shiki, html:not(.dark):not(:has(input#theme-manual-toggle:checked))
html:not(.dark) .feature-showcase .shiki span { .feature-showcase
.shiki,
html:not(.dark):not(:has(input#theme-manual-toggle:checked))
.feature-showcase
.shiki
span {
background-color: #ffffff; background-color: #ffffff;
} }
@ -139,7 +154,7 @@ html:not(.dark) .feature-showcase .shiki span {
transform-origin: center bottom; transform-origin: center bottom;
transform: translateX(-50%); transform: translateX(-50%);
&>div { & > div {
transform: rotate(0deg); transform: rotate(0deg);
animation: counter-spin 40s linear infinite; animation: counter-spin 40s linear infinite;

View file

@ -33,6 +33,13 @@
justify-content: flex-start; justify-content: flex-start;
align-items: flex-start; align-items: flex-start;
overflow: hidden; overflow: hidden;
opacity: 0;
transition: opacity 0.6s ease;
min-height: 200px; /* placeholder height */
&.initialized {
opacity: 1;
}
} }
.marquee-content { .marquee-content {
@ -44,6 +51,11 @@
padding: 0; padding: 0;
will-change: transform; will-change: transform;
transform: translateX(0); transform: translateX(0);
visibility: hidden;
.initialized & {
visibility: visible;
}
} }
.marquee-item { .marquee-item {
@ -56,7 +68,7 @@
box-sizing: border-box; box-sizing: border-box;
will-change: opacity; will-change: opacity;
&>* { & > * {
z-index: 11; z-index: 11;
} }
@ -89,8 +101,8 @@
justify-content: space-between; justify-content: space-between;
align-items: stretch; align-items: stretch;
transition: transition:
background-color 0.3s, background-color var(--theme-transition),
opacity 0.3s; opacity var(--theme-transition);
z-index: 20; z-index: 20;
user-select: none; user-select: none;
pointer-events: none; pointer-events: none;
@ -106,7 +118,7 @@
align-items: center; align-items: center;
height: 100%; height: 100%;
&>div { & > div {
width: 2.5rem; width: 2.5rem;
aspect-ratio: 1 / 1; aspect-ratio: 1 / 1;
display: flex; display: flex;
@ -120,7 +132,7 @@
&:hover { &:hover {
cursor: pointer; cursor: pointer;
&>div { & > div {
opacity: 0.9; opacity: 0.9;
} }
} }
@ -154,10 +166,9 @@
.marquee-scroll-arrow { .marquee-scroll-arrow {
height: unset; height: unset;
&>div { & > div {
background-color: #55555580; background-color: #55555580;
border-radius: var(--radius-xs); border-radius: var(--radius-xs);
} }
} }
} }

View file

@ -7,8 +7,8 @@ html {
font-weight: 400; font-weight: 400;
height: 100svh; height: 100svh;
width: 100svw; /* width: 100svw; causes horizontal overflow due to the scrollbar*/
max-width: 100svw; width: 100%;
font-synthesis: none; font-synthesis: none;
text-rendering: optimizeLegibility; text-rendering: optimizeLegibility;

View file

@ -16,7 +16,9 @@ pre.shiki {
} }
html.dark .shiki, html.dark .shiki,
html.dark .shiki span { html.dark .shiki span,
html:has(input#theme-manual-toggle:checked) .shiki,
html:has(input#theme-manual-toggle:checked) .shiki span {
color: var(--shiki-dark); color: var(--shiki-dark);
background-color: var(--shiki-dark-bg); background-color: var(--shiki-dark-bg);
} }
@ -27,8 +29,8 @@ pre {
overflow: hidden; overflow: hidden;
text-wrap: wrap; text-wrap: wrap;
transition: transition:
background-color 0.3s var(--ease-in-out), background-color var(--theme-transition),
color 0.3s var(--ease-in-out); color var(--theme-transition);
& .copy-button { & .copy-button {
all: unset; all: unset;
@ -48,17 +50,38 @@ pre {
background-color: hsla(var(--blue) 85 35 / 0.01); background-color: hsla(var(--blue) 85 35 / 0.01);
cursor: pointer; cursor: pointer;
transition: transition:
background-color 0.3s var(--ease-in-out), background-color var(--theme-transition),
color 0.3s var(--ease-in-out); color var(--theme-transition);
z-index: 10; z-index: 10;
& svg {
position: absolute;
transition:
transform 0.3s var(--ease-in-out),
opacity 0.3s var(--ease-in-out);
}
& .check-icon {
opacity: 0;
transform: scale(0.5);
color: hsl(var(--green) 100 69);
}
&:hover { &:hover {
color: hsla(var(--blue) 100 75 / 0.75); color: hsla(var(--blue) 100 75 / 0.75);
background-color: hsla(var(--blue) 85 35 / 0.1); background-color: hsla(var(--blue) 85 35 / 0.1);
} }
&.copied { &.copied {
animation: var(--animate-bounce); & .copy-icon {
opacity: 0;
transform: scale(0.5);
}
& .check-icon {
opacity: 1;
transform: scale(1);
}
} }
} }

View file

@ -1,11 +1,11 @@
:root { html:not(.dark):not(:has(input#theme-manual-toggle:checked)) {
color-scheme: light dark; color-scheme: light dark;
/* accent */ /* accent */
--green: 141deg; --green: 141deg;
--accent-400: var(--green) 90% 57%; --accent-400: var(--green) 90% 57%;
--accent-500: var(--green) 90% 47%; --accent-500: var(--green) 90% 47%;
--accent-600: var(--green) 88% 40%; --accent-600: var(--green) 88% 40%;
--accent-700: var(--green) 70% 40%; --accent-700: var(--green) 70% 35%;
/* secondary */ /* secondary */
--blue: 224deg; --blue: 224deg;
@ -18,44 +18,45 @@
/* primary */ /* primary */
--white: 194deg; --white: 194deg;
--bg-400: var(--white) 10% 95%; --bg-400: var(--white) 10% 98%;
--bg-500: var(--white) 5% 90%; --bg-500: var(--white) 10% 95%;
--bg-600: var(--white) 5% 76%; --bg-600: var(--white) 8% 88%;
--bg-700: var(--white) 5% 56%; --bg-700: var(--white) 8% 78%;
--bg-800: var(--white) 5% 36%; --bg-800: var(--white) 5% 56%;
--bg-900: var(--white) 5% 16%; --bg-900: var(--white) 5% 16%;
/* docs */ /* docs */
--background: var(--bg-500); --background: var(--bg-400);
--text: var(--white) 0% 0%; --text: var(--white) 0% 10%;
--text-dark: var(--white) 0% 18%; --text-dark: var(--white) 0% 25%;
--text-darker: var(--white) 0% 30%; --text-darker: var(--white) 0% 40%;
--link: var(--green) 48% 40%; --link: var(--green) 60% 35%;
--toc-link: var(--green) 74% 30%; --toc-link: var(--white) 0% 40%;
--toc-link-active: var(--green) 80% 38%; --toc-link-active: var(--green) 60% 35%;
--prop-color: 350deg 78% 70%; --prop-color: 350deg 78% 65%;
--prop-link-color: 350deg 78% 60%; --prop-link-color: 350deg 78% 45%;
--func-color: 50deg 68% 50%; --func-color: 50deg 78% 45%;
--func-link-color: 50deg 58% 55%; --func-link-color: 50deg 85% 30%;
--signal-color: 270deg 78% 70%; --signal-color: 270deg 60% 65%;
--signal-link-color: 270deg 85% 60%; --signal-link-color: 270deg 75% 45%;
--var-color: 190deg 78% 70%; --var-color: 190deg 78% 65%;
--var-link-color: 190deg 85% 60%; --var-link-color: 190deg 85% 40%;
--inner-param-color: 215deg 80% 27%; --inner-param-color: 215deg 80% 27%;
--inner-param-border-color: 215deg 50% 50%; --inner-param-border-color: 215deg 50% 50%;
--nav-hovered-bkg: var(--blue) 100% 87%; --nav-hovered-bkg: var(--blue) 100% 94%;
--nav-hovered-weak-bkg: var(--blue) 100% 91%; --nav-hovered-weak-bkg: var(--blue) 100% 96%;
--nav-selected-bkg: var(--blue) 100% 90%; --nav-selected-bkg: var(--blue) 100% 92%;
--nav-selected-hovered-bkg: var(--blue) 100% 85%; --nav-selected-hovered-bkg: var(--blue) 100% 88%;
--nav-selected-text: var(--blue) 60% 60%; --nav-selected-text: var(--blue) 70% 45%;
--nav-indicator-bkg: var(--blue) 45% 80%; --nav-indicator-bkg: var(--blue) 45% 80%;
--toc-hovered-bkg: 0deg 0% 0% / 0.1; --toc-hovered-bkg: 0deg 0% 0% / 0.05;
--overlay-bkg: var(--blue) 25% 93%; --overlay-bkg: var(--white) 10% 98%;
--overlay-bkg-border: var(--blue) 10% 75%; --overlay-bkg-border: var(--white) 10% 85%;
--footer-bkg: var(--blue) 8% 87%; --footer-bkg: var(--white) 10% 95%;
--footer-bkg-border: var(--blue) 32% 84%; --footer-bkg-border: var(--white) 10% 88%;
} }
html.dark,
html:has(input#theme-manual-toggle:checked) { html:has(input#theme-manual-toggle:checked) {
/* accent */ /* accent */
--green: 141deg; --green: 141deg;

View file

@ -32,15 +32,15 @@ body {
/* aside, */ /* aside, */
details, details,
/* figcaption, */ /* figcaption, */
/* figure, */ /* figure, */
/* footer, */ /* footer, */
/* header, */ /* header, */
/* hgroup, */ /* hgroup, */
/* main, */ /* main, */
/* menu, */ /* menu, */
/* nav, */ /* nav, */
/* section, */ /* section, */
summary { summary {
display: block; display: block;
} }

View file

@ -77,4 +77,6 @@ html {
--ease-in: cubic-bezier(0.4, 0, 1, 1); --ease-in: cubic-bezier(0.4, 0, 1, 1);
--ease-out: cubic-bezier(0, 0, 0.2, 1); --ease-out: cubic-bezier(0, 0, 0.2, 1);
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1); --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
--theme-transition: 0.3s var(--ease-in-out);
} }

View file

@ -7,7 +7,7 @@
[data-scope="collapsible"][data-part="content"] { [data-scope="collapsible"][data-part="content"] {
padding: 0; padding: 0;
margin: 0; margin: 0;
transition: all 300ms; transition: all var(--theme-transition);
} }
[data-scope="collapsible"][data-part="content"][data-state="open"] { [data-scope="collapsible"][data-part="content"][data-state="open"] {

View file

@ -68,7 +68,9 @@
margin-bottom: 0.618rem; margin-bottom: 0.618rem;
border-radius: 12px; border-radius: 12px;
padding: 0.8rem; padding: 0.8rem;
transition: border 0.3s; transition:
background-color var(--theme-transition),
border-color var(--theme-transition);
} }
& .typedata-details { & .typedata-details {
@ -164,6 +166,9 @@
.typeprops { .typeprops {
& .typeprop-root { & .typeprop-root {
border: 1px solid hsl(var(--prop-color) / 0.6); border: 1px solid hsl(var(--prop-color) / 0.6);
transition:
background-color var(--theme-transition),
border-color var(--theme-transition);
&:hover { &:hover {
border: 1px solid hsl(var(--prop-color)); border: 1px solid hsl(var(--prop-color));
@ -175,11 +180,13 @@
& .typeprop-name { & .typeprop-name {
color: hsl(var(--prop-link-color)); color: hsl(var(--prop-link-color));
transition: color var(--theme-transition);
} }
} }
} }
html.dark .typeprops { html.dark .typeprops,
html:has(input#theme-manual-toggle:checked) .typeprops {
& .typeprop-root { & .typeprop-root {
border: 1px solid hsl(var(--prop-color) / 0.3); border: 1px solid hsl(var(--prop-color) / 0.3);
@ -193,6 +200,9 @@ html.dark .typeprops {
.typefuncs { .typefuncs {
& .typefunc-root { & .typefunc-root {
border: 1px solid hsl(var(--func-color) / 0.6); border: 1px solid hsl(var(--func-color) / 0.6);
transition:
background-color var(--theme-transition),
border-color var(--theme-transition);
&:hover { &:hover {
border: 1px solid hsl(var(--func-color)); border: 1px solid hsl(var(--func-color));
@ -204,6 +214,7 @@ html.dark .typeprops {
& .typefunc-name { & .typefunc-name {
color: hsl(var(--func-link-color)); color: hsl(var(--func-link-color));
transition: color var(--theme-transition);
} }
& .typefunc-params { & .typefunc-params {
@ -222,7 +233,8 @@ html.dark .typeprops {
} }
} }
html.dark .typefuncs { html.dark .typefuncs,
html:has(input#theme-manual-toggle:checked) .typefuncs {
& .typefunc-root { & .typefunc-root {
border: 1px solid hsl(var(--func-color) / 0.3); border: 1px solid hsl(var(--func-color) / 0.3);
@ -236,6 +248,9 @@ html.dark .typefuncs {
.typesignals { .typesignals {
& .typesignal-root { & .typesignal-root {
border: 1px solid hsl(var(--signal-color) / 0.6); border: 1px solid hsl(var(--signal-color) / 0.6);
transition:
background-color var(--theme-transition),
border-color var(--theme-transition);
&:hover { &:hover {
border: 1px solid hsl(var(--signal-color)); border: 1px solid hsl(var(--signal-color));
@ -249,6 +264,7 @@ html.dark .typefuncs {
position: relative; position: relative;
width: max-content; width: max-content;
color: hsl(var(--signal-link-color)); color: hsl(var(--signal-link-color));
transition: color var(--theme-transition);
& .typesignal-doclink { & .typesignal-doclink {
top: -12px; top: -12px;
@ -274,7 +290,8 @@ html.dark .typefuncs {
} }
} }
html.dark .typesignals { html.dark .typesignals,
html:has(input#theme-manual-toggle:checked) .typesignals {
& .typesignal-root { & .typesignal-root {
border: 1px solid hsl(var(--signal-color) / 0.3); border: 1px solid hsl(var(--signal-color) / 0.3);
@ -288,6 +305,9 @@ html.dark .typesignals {
.typevariants { .typevariants {
& .typevariant-root { & .typevariant-root {
border: 1px solid hsl(var(--var-color) / 0.6); border: 1px solid hsl(var(--var-color) / 0.6);
transition:
background-color var(--theme-transition),
border-color var(--theme-transition);
&:hover { &:hover {
border: 1px solid hsl(var(--var-color)); border: 1px solid hsl(var(--var-color));
@ -301,6 +321,7 @@ html.dark .typesignals {
position: relative; position: relative;
width: max-content; width: max-content;
color: hsl(var(--var-link-color)); color: hsl(var(--var-link-color));
transition: color var(--theme-transition);
& .typevariant-doclink { & .typevariant-doclink {
position: absolute; position: absolute;
@ -313,7 +334,8 @@ html.dark .typesignals {
} }
} }
html.dark .typevariants { html.dark .typevariants,
html:has(input#theme-manual-toggle:checked) .typevariants {
& .typevariant-root { & .typevariant-root {
border: 1px solid hsl(var(--var-color) / 0.3); border: 1px solid hsl(var(--var-color) / 0.3);

View file

@ -2,7 +2,7 @@
@import "./docs-types.css"; @import "./docs-types.css";
.docslayout { .docslayout {
transition: background-color 0.3s; transition: background-color var(--theme-transition);
} }
.docslayout-root { .docslayout-root {
@ -12,7 +12,7 @@
justify-content: center; justify-content: center;
flex-direction: row; flex-direction: row;
flex-grow: 1; flex-grow: 1;
transition: filter 0.3s; transition: filter var(--theme-transition);
} }
.docslayout-inner { .docslayout-inner {
@ -31,9 +31,12 @@
pointer-events: none; pointer-events: none;
} }
:not(html.dark) > .dim-content-toc, html:not(.dark):not(:has(input#theme-manual-toggle:checked)) > .dim-content-toc,
:not(html.dark) > .dim-content-nav {
html:not(.dark):not(:has(input#theme-manual-toggle:checked)) > .dim-content-nav {
background-color: #909090; background-color: #909090;
} }
.docs-content { .docs-content {
@ -66,7 +69,7 @@
--color-link-breadcrumbs: hsl(var(--link)); --color-link-breadcrumbs: hsl(var(--link));
margin-top: 0.5rem; margin-top: 0.5rem;
margin-bottom: 0.318rem; margin-bottom: 0.318rem;
max-width: 100svw; max-width: 100%;
} }
.heading { .heading {

View file

@ -20,7 +20,9 @@
.nav-item { .nav-item {
display: block; display: block;
border-radius: 6px; border-radius: 6px;
transition: background-color 0.2s ease; transition:
background-color var(--theme-transition),
color var(--theme-transition);
padding: 0.4em; padding: 0.4em;
font-size: 1rem; font-size: 1rem;
@ -39,6 +41,7 @@
} }
.fade { .fade {
mask-image: linear-gradient(to right, #000 80%, transparent);
-webkit-mask-image: linear-gradient( -webkit-mask-image: linear-gradient(
to right, to right,
#000 80%, #000 80%,
@ -55,7 +58,9 @@
& > div { & > div {
& > .nav-collapse-marker, & > .nav-collapse-marker,
a { a {
transition: background-color 0.2s ease; transition:
background-color var(--theme-transition),
color var(--theme-transition);
} }
& > .nav-collapse-marker { & > .nav-collapse-marker {

View file

@ -7,12 +7,16 @@
.nav-icon { .nav-icon {
opacity: 0; opacity: 0;
transform: scale(0.5);
position: absolute; position: absolute;
transition: opacity 0.6s; transition:
opacity var(--theme-transition),
transform var(--theme-transition);
} }
.nav-icon.active { .nav-icon.active {
opacity: 1; opacity: 1;
transform: scale(1);
position: relative; position: relative;
} }
@ -50,7 +54,9 @@
display: none; display: none;
} }
transition: left 0.3s ease, padding 0.3s ease; transition:
left 0.3s ease,
padding 0.3s ease;
&.shown { &.shown {
display: flex; display: flex;

View file

@ -4,20 +4,48 @@
} }
#qs_search { #qs_search {
--search-result-spacing: calc(1.25rem * var(--pagefind-ui-scale)); --search-result-spacing: calc(
--search-result-pad-inline-start: calc(3.75rem * var(--pagefind-ui-scale)); 1.25rem *
--search-result-pad-inline-end: calc(1.25rem * var(--pagefind-ui-scale)); var(--pagefind-ui-scale)
--search-result-pad-block: calc(0.9375rem * var(--pagefind-ui-scale)); );
--search-result-nested-pad-block: calc(0.625rem * var(--pagefind-ui-scale)); --search-result-pad-inline-start: calc(
--search-corners: calc(0.3125rem * var(--pagefind-ui-scale)); 3.75rem *
--search-page-icon-size: calc(1.875rem * var(--pagefind-ui-scale)); var(--pagefind-ui-scale)
--search-page-icon-inline-start: calc( );
(var(--search-result-pad-inline-start) - var(--search-page-icon-size)) / 2 --search-result-pad-inline-end: calc(
1.25rem *
var(--pagefind-ui-scale)
);
--search-result-pad-block: calc(
0.9375rem *
var(--pagefind-ui-scale)
);
--search-result-nested-pad-block: calc(
0.625rem *
var(--pagefind-ui-scale)
);
--search-corners: calc(0.3125rem * var(--pagefind-ui-scale));
--search-page-icon-size: calc(
1.875rem *
var(--pagefind-ui-scale)
);
--search-page-icon-inline-start: calc(
(
var(--search-result-pad-inline-start) -
var(--search-page-icon-size)
) /
2
);
--search-tree-diagram-size: calc(
2.5rem *
var(--pagefind-ui-scale)
); );
--search-tree-diagram-size: calc(2.5rem * var(--pagefind-ui-scale));
--search-tree-diagram-inline-start: calc( --search-tree-diagram-inline-start: calc(
(var(--search-result-pad-inline-start) - var(--search-tree-diagram-size)) / (
2 var(--search-result-pad-inline-start) -
var(--search-tree-diagram-size)
) /
2
); );
} }
@ -29,7 +57,12 @@
#qs_search #qs_search
.pagefind-ui--reset .pagefind-ui--reset
*:where(:not(html, iframe, canvas, img, svg, video):not(svg *, symbol *)) { *:where(
:not(html, iframe, canvas, img, svg, video):not(
svg *,
symbol *
)
) {
outline: unset; outline: unset;
} }
@ -57,9 +90,11 @@
#qs_search .pagefind-ui__search-clear::before { #qs_search .pagefind-ui__search-clear::before {
content: ""; content: "";
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='m13.41 12 6.3-6.29a1 1 0 1 0-1.42-1.42L12 10.59l-6.29-6.3a1 1 0 0 0-1.42 1.42l6.3 6.29-6.3 6.29a1 1 0 0 0 .33 1.64 1 1 0 0 0 1.09-.22l6.29-6.3 6.29 6.3a1 1 0 0 0 1.64-.33 1 1 0 0 0-.22-1.09L13.41 12Z'/%3E%3C/svg%3E") -webkit-mask:
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='m13.41 12 6.3-6.29a1 1 0 1 0-1.42-1.42L12 10.59l-6.29-6.3a1 1 0 0 0-1.42 1.42l6.3 6.29-6.3 6.29a1 1 0 0 0 .33 1.64 1 1 0 0 0 1.09-.22l6.29-6.3 6.29 6.3a1 1 0 0 0 1.64-.33 1 1 0 0 0-.22-1.09L13.41 12Z'/%3E%3C/svg%3E")
center / 50% no-repeat; center / 50% no-repeat;
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='m13.41 12 6.3-6.29a1 1 0 1 0-1.42-1.42L12 10.59l-6.29-6.3a1 1 0 0 0-1.42 1.42l6.3 6.29-6.3 6.29a1 1 0 0 0 .33 1.64 1 1 0 0 0 1.09-.22l6.29-6.3 6.29 6.3a1 1 0 0 0 1.64-.33 1 1 0 0 0-.22-1.09L13.41 12Z'/%3E%3C/svg%3E") mask:
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='m13.41 12 6.3-6.29a1 1 0 1 0-1.42-1.42L12 10.59l-6.29-6.3a1 1 0 0 0-1.42 1.42l6.3 6.29-6.3 6.29a1 1 0 0 0 .33 1.64 1 1 0 0 0 1.09-.22l6.29-6.3 6.29 6.3a1 1 0 0 0 1.64-.33 1 1 0 0 0-.22-1.09L13.41 12Z'/%3E%3C/svg%3E")
center / 50% no-repeat; center / 50% no-repeat;
background-color: hsl(0deg 25% 45%); background-color: hsl(0deg 25% 45%);
display: block; display: block;
@ -84,14 +119,18 @@
} }
#qs_search #qs_search
.pagefind-ui__result-title:not(:where(.pagefind-ui__result-nested *)), .pagefind-ui__result-title:not(
:where(.pagefind-ui__result-nested *)
),
#qs_search .pagefind-ui__result-nested { #qs_search .pagefind-ui__result-nested {
position: relative; position: relative;
background-color: hsl(0deg 0% 10%); background-color: hsl(0deg 0% 10%);
} }
#qs_search #qs_search
.pagefind-ui__result-title:not(:where(.pagefind-ui__result-nested *)):hover, .pagefind-ui__result-title:not(
:where(.pagefind-ui__result-nested *)
):hover,
#qs_search #qs_search
.pagefind-ui__result-title:not( .pagefind-ui__result-title:not(
:where(.pagefind-ui__result-nested *) :where(.pagefind-ui__result-nested *)
@ -122,12 +161,17 @@
border-radius: 0 0 var(--search-corners) var(--search-corners); border-radius: 0 0 var(--search-corners) var(--search-corners);
} }
#qs_search .pagefind-ui__result-inner > .pagefind-ui__result-title { #qs_search
padding: var(--search-result-pad-block) var(--search-result-pad-inline-end); .pagefind-ui__result-inner
> .pagefind-ui__result-title {
padding: var(--search-result-pad-block)
var(--search-result-pad-inline-end);
padding-inline-start: var(--search-result-pad-inline-start); padding-inline-start: var(--search-result-pad-inline-start);
} }
#qs_search .pagefind-ui__result-inner > .pagefind-ui__result-title::before { #qs_search
.pagefind-ui__result-inner
> .pagefind-ui__result-title::before {
content: ""; content: "";
position: absolute; position: absolute;
inset-block: 0; inset-block: 0;
@ -155,7 +199,9 @@
text-decoration: none; text-decoration: none;
} }
#qs_search .pagefind-ui__result-nested .pagefind-ui__result-link::before { #qs_search
.pagefind-ui__result-nested
.pagefind-ui__result-link::before {
content: unset; content: unset;
} }
@ -166,9 +212,11 @@
inset-inline-start: var(--search-tree-diagram-inline-start); inset-inline-start: var(--search-tree-diagram-inline-start);
width: var(--search-tree-diagram-size); width: var(--search-tree-diagram-size);
background: hsl(var(--blue) 10% 30%); background: hsl(var(--blue) 10% 30%);
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='currentColor' stroke-linecap='round' viewBox='0 0 16 1000' preserveAspectRatio='xMinYMin slice'%3E%3Cpath d='M8 0v1000m6-988H8'/%3E%3C/svg%3E") -webkit-mask:
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='currentColor' stroke-linecap='round' viewBox='0 0 16 1000' preserveAspectRatio='xMinYMin slice'%3E%3Cpath d='M8 0v1000m6-988H8'/%3E%3C/svg%3E")
0% 0% / 100% no-repeat; 0% 0% / 100% no-repeat;
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='currentColor' stroke-linecap='round' viewBox='0 0 16 1000' preserveAspectRatio='xMinYMin slice'%3E%3Cpath d='M8 0v1000m6-988H8'/%3E%3C/svg%3E") mask:
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='currentColor' stroke-linecap='round' viewBox='0 0 16 1000' preserveAspectRatio='xMinYMin slice'%3E%3Cpath d='M8 0v1000m6-988H8'/%3E%3C/svg%3E")
0% 0% / 100% no-repeat; 0% 0% / 100% no-repeat;
} }
@ -188,7 +236,9 @@
overflow-wrap: anywhere; overflow-wrap: anywhere;
} }
#qs_search .pagefind-ui__result-inner > .pagefind-ui__result-excerpt { #qs_search
.pagefind-ui__result-inner
> .pagefind-ui__result-excerpt {
display: inline-block; display: inline-block;
position: relative; position: relative;
background: hsl(0deg 0% 10%); background: hsl(0deg 0% 10%);
@ -204,9 +254,11 @@
inset-inline-start: var(--search-tree-diagram-inline-start); inset-inline-start: var(--search-tree-diagram-inline-start);
width: var(--search-tree-diagram-size); width: var(--search-tree-diagram-size);
background: hsl(var(--blue) 10% 30%); background: hsl(var(--blue) 10% 30%);
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='currentColor' stroke-linecap='round' viewBox='0 0 16 1000' preserveAspectRatio='xMinYMin slice'%3E%3Cpath d='M8 0v1000m'/%3E%3C/svg%3E") -webkit-mask:
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='currentColor' stroke-linecap='round' viewBox='0 0 16 1000' preserveAspectRatio='xMinYMin slice'%3E%3Cpath d='M8 0v1000m'/%3E%3C/svg%3E")
0% 0% / 100% no-repeat; 0% 0% / 100% no-repeat;
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='currentColor' stroke-linecap='round' viewBox='0 0 16 1000' preserveAspectRatio='xMinYMin slice'%3E%3Cpath d='M8 0v1000m'/%3E%3C/svg%3E") mask:
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='currentColor' stroke-linecap='round' viewBox='0 0 16 1000' preserveAspectRatio='xMinYMin slice'%3E%3Cpath d='M8 0v1000m'/%3E%3C/svg%3E")
0% 0% / 100% no-repeat; 0% 0% / 100% no-repeat;
} }
} }
@ -219,9 +271,20 @@
/* default styles */ /* default styles */
site-search { site-search {
--shadow-lg: 0px 25px 7px hsl(0deg, 0%, 0%, 0.03), --shadow-lg:
0px 16px 6px hsl(0deg, 0%, 0%, 0.1), 0px 9px 5px hsl(223deg, 13%, 10%, 0.33), 0px 25px 7px hsl(0deg, 0%, 0%, 0.03),
0px 4px 4px hsl(0deg, 0%, 0%, 0.75), 0px 4px 2px hsl(0deg, 0%, 0%, 0.25); 0px 16px 6px hsl(0deg, 0%, 0%, 0.1), 0px 9px 5px hsl(
223deg,
13%,
10%,
0.33
),
0px 4px 4px hsl(0deg, 0%, 0%, 0.75), 0px 4px 2px hsl(
0deg,
0%,
0%,
0.25
);
display: contents; display: contents;
} }
@ -275,7 +338,8 @@ button > kbd {
background-color: hsl(var(--blue) 15% 80%); background-color: hsl(var(--blue) 15% 80%);
} }
html.dark button > kbd { html.dark button > kbd,
html:has(input#theme-manual-toggle:checked) button > kbd {
background-color: hsl(var(--blue) 5% 20% / 0.5); background-color: hsl(var(--blue) 5% 20% / 0.5);
} }
@ -368,7 +432,9 @@ button[data-close-modal] {
} }
} }
html.dark button[data-open-modal] { html.dark button[data-open-modal],
html:has(input#theme-manual-toggle:checked)
button[data-open-modal] {
background-color: hsla(var(--blue) 15% 15% / 0.5); background-color: hsla(var(--blue) 15% 15% / 0.5);
color: hsl(var(--blue) 40% 65%); color: hsl(var(--blue) 40% 65%);

View file

@ -30,6 +30,28 @@
font-size: 1.614rem; font-size: 1.614rem;
max-height: 500px; max-height: 500px;
& > div {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}
& .toc-icon {
opacity: 0;
transform: scale(0.5);
position: absolute;
transition:
opacity var(--theme-transition),
transform var(--theme-transition);
&.active {
opacity: 1;
transform: scale(1);
position: relative;
}
}
& > svg { & > svg {
height: 100%; height: 100%;
width: 24px; width: 24px;
@ -61,7 +83,9 @@
display: none; display: none;
} }
transition: width 0.3s ease, padding 0.3s ease; transition:
width 0.3s ease,
padding 0.3s ease;
&.shown { &.shown {
& .toc-content { & .toc-content {
@ -115,7 +139,7 @@
margin-right: 1.272rem; margin-right: 1.272rem;
& .toc_a { & .toc_a {
transition: color 0.33s; transition: color var(--theme-transition);
color: hsl(var(--toc-link)); color: hsl(var(--toc-link));
} }

View file

@ -12,10 +12,6 @@
@import "./components/featurelist.css"; @import "./components/featurelist.css";
@import "./components/marquee.css"; @import "./components/marquee.css";
.changing-theme * {
transition: none !important;
}
.theme-toggle { .theme-toggle {
height: 24px; height: 24px;
font-size: 1.614rem; font-size: 1.614rem;
@ -31,17 +27,25 @@
.header { .header {
background-color: hsl(var(--bg-400)); background-color: hsl(var(--bg-400));
box-shadow: 0 1px 1px 1px hsla(var(--white) 100 0 / 0.1); box-shadow: 0 1px 1px 1px hsla(var(--white) 100 0 / 0.1);
transition:
background-color var(--theme-transition),
color var(--theme-transition),
box-shadow var(--theme-transition);
} }
.baselayout, .baselayout,
.docslayout { .docslayout {
background-color: hsl(var(--background)); background-color: hsl(var(--background));
color: hsl(var(--secondary-900)); color: hsl(var(--secondary-900));
transition:
background-color var(--theme-transition),
color var(--theme-transition);
} }
a { a {
color: hsl(var(--link)); color: hsl(var(--link));
text-decoration: none; text-decoration: none;
transition: color var(--theme-transition);
&:hover { &:hover {
text-decoration: underline; text-decoration: underline;
@ -50,12 +54,15 @@ a {
} }
html.dark .baselayout, html.dark .baselayout,
html.dark .docslayout { html.dark .docslayout,
html:has(input#theme-manual-toggle:checked) .baselayout,
html:has(input#theme-manual-toggle:checked) .docslayout {
background-color: hsl(var(--bg-900)); background-color: hsl(var(--bg-900));
color: hsl(var(--secondary-400)); color: hsl(var(--secondary-400));
} }
html.dark { html.dark,
html:has(input#theme-manual-toggle:checked) {
& .header { & .header {
background-color: hsl(var(--secondary-900)); background-color: hsl(var(--secondary-900));
color: hsl(var(--secondary-500)); color: hsl(var(--secondary-500));
@ -153,6 +160,7 @@ body.overflow-toc {
footer { footer {
position: relative; position: relative;
margin-top: var(--2xl);
width: 100%; width: 100%;
font-size: 0.9rem; font-size: 0.9rem;
display: flex; display: flex;
@ -160,15 +168,17 @@ footer {
padding: 1rem 2rem; padding: 1rem 2rem;
flex-shrink: 0; flex-shrink: 0;
background: hsl(var(--footer-bkg)); background: hsl(var(--footer-bkg));
transition:
background-color var(--theme-transition),
color var(--theme-transition);
&::before { &::before {
content: ""; content: "";
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
right: 54%; right: calc(50% + 1.25rem);
height: 1px; height: 1px;
width: calc(48%);
background: linear-gradient( background: linear-gradient(
90deg, 90deg,
transparent 0%, transparent 0%,
@ -181,9 +191,8 @@ footer {
position: absolute; position: absolute;
top: 0; top: 0;
right: 0; right: 0;
left: 54%; left: calc(50% + 1.25rem);
height: 1px; height: 1px;
width: calc(48% - 1rem);
background: linear-gradient( background: linear-gradient(
90deg, 90deg,
hsl(var(--footer-bkg-border)) 0%, hsl(var(--footer-bkg-border)) 0%,
@ -195,16 +204,17 @@ footer {
position: absolute; position: absolute;
top: -12px; top: -12px;
left: 50%; left: 50%;
transform: translateX(-50%);
width: max-content; width: max-content;
height: max-content; height: max-content;
} }
& a { & a {
color: hsl(var(--text-dark)); color: hsl(var(--text-dark));
transition: color 0.3s ease; transition: color var(--theme-transition);
& .hint { & .hint {
transition: color 0.3s ease; transition: color var(--theme-transition);
} }
&:nth-child(2) .hint { &:nth-child(2) .hint {

View file

@ -40,15 +40,20 @@
} }
h1.gradient-text { h1.gradient-text {
background: linear-gradient(30deg, background: linear-gradient(
hsl(var(--green) 80% 42%), 30deg,
hsl(var(--blue) 80% 49%)); hsl(var(--green) 80% 42%),
hsl(var(--blue) 80% 49%)
);
background-clip: text;
-webkit-background-clip: text; -webkit-background-clip: text;
-webkit-text-fill-color: transparent; -webkit-text-fill-color: transparent;
} }
html.dark h1.gradient-text { html.dark h1.gradient-text,
html:has(input#theme-manual-toggle:checked) h1.gradient-text {
background: linear-gradient(30deg, #42b96b, #4281b9); background: linear-gradient(30deg, #42b96b, #4281b9);
background-clip: text;
-webkit-background-clip: text; -webkit-background-clip: text;
-webkit-text-fill-color: transparent; -webkit-text-fill-color: transparent;
} }
@ -70,7 +75,8 @@ html.dark h1.gradient-text {
} }
} }
html.dark .main-page_hero-text { html.dark .main-page_hero-text,
html:has(input#theme-manual-toggle:checked) .main-page_hero-text {
& h2 { & h2 {
color: hsl(var(--blue) 100% 83%); color: hsl(var(--blue) 100% 83%);
} }
@ -130,9 +136,9 @@ html.dark .main-page_hero-text {
overflow: hidden; overflow: hidden;
box-shadow: var(--shadow-md); box-shadow: var(--shadow-md);
transition: transition:
background-color 0.3s, background-color var(--theme-transition),
box-shadow 0.3s, box-shadow var(--theme-transition),
border-color 0.3s; border-color var(--theme-transition);
background-color: hsl(var(--green) 38% 30%); background-color: hsl(var(--green) 38% 30%);
color: hsl(194deg 0% 100%); color: hsl(194deg 0% 100%);
@ -164,7 +170,8 @@ html.dark .main-page_hero-text {
} }
} }
html.dark .main-page_link-card { html.dark .main-page_link-card,
html:has(input#theme-manual-toggle:checked) .main-page_link-card {
background-color: hsl(var(--green) 38% 25%); background-color: hsl(var(--green) 38% 25%);
color: hsl(194deg 0% 100%); color: hsl(194deg 0% 100%);

View file

@ -1,9 +1,7 @@
{ {
"extends": "astro/tsconfigs/strict", "extends": "astro/tsconfigs/strict",
"compilerOptions": { "compilerOptions": {
"lib": [ "lib": ["es2016"],
"es2016"
],
"plugins": [ "plugins": [
{ {
"name": "@astrojs/ts-plugin" "name": "@astrojs/ts-plugin"
@ -14,36 +12,16 @@
"verbatimModuleSyntax": true, "verbatimModuleSyntax": true,
"baseUrl": ".", "baseUrl": ".",
"paths": { "paths": {
"@*": [ "@*": ["./*"],
"./*" "@/*": ["./src/*"],
], "@config/*": ["./src/config/*"],
"@/*": [ "@icons": ["./src/components/icons.tsx"],
"./src/*" "@icons/*": ["./src/icons/*"],
], "@components/*": ["./src/components/*"],
"@config/*": [ "@layouts/*": ["./src/layouts/*"],
"./src/config/*" "@styles/*": ["./src/styles/*"],
], "@_types": ["./src/config/_types/index.ts"],
"@icons": [ "@_types/*": ["./src/config/_types/*"]
"./src/components/icons.tsx"
],
"@icons/*": [
"./src/icons/*"
],
"@components/*": [
"./src/components/*"
],
"@layouts/*": [
"./src/layouts/*"
],
"@styles/*": [
"./src/styles/*"
],
"@_types": [
"./src/config/_types/index.ts"
],
"@_types/*": [
"./src/config/_types/*"
]
} }
} }
} }

1134
yarn.lock

File diff suppressed because it is too large Load diff