This commit is contained in:
Aron Petau 2025-08-27 16:56:14 +02:00
commit d013ece0f3
363 changed files with 20823 additions and 0 deletions

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "themes/duckquill"]
path = themes/duckquill
url = https://codeberg.org/daudix/duckquill.git

94
config.toml Normal file
View file

@ -0,0 +1,94 @@
base_url = "https://studio-umzu.de/"
title = "Studio UMZU"
compile_sass = true
minify_html = true
generate_feeds = true
feed_filenames = ["rss.xml", "atom.xml"]
build_search_index = true
author = "Aron Petau"
hard_link_static = false
taxonomies = [{ name = "tags", feed = true, paginate_by = 10 }]
theme = "duckquill"
default_language = "en"
[languages.de]
title = "Studio UMZU"
description = "Spielerisches Lernen mit Technikdidaktik"
generate_feeds = true
build_search_index = true
taxonomies = [{ name = "tags", feed = true, paginate_by = 10 }]
[slugify]
paths = "off"
taxonomies = "on"
anchors = "off"
paths_keep_dates = false
[search]
index_format = "fuse_json"
[extra]
styles = [
"/css/timeline.css",
"/css/mermaid.css",
"/css/skills.css",
"/css/gallery.css",
]
bundled_fonts = false
issues_url = "https://forgejo.petau.net/aron/awebsite/issues"
source_url = "https://forgejo.petau.net/aron/awebsite"
default_theme = "light"
accent_color = "#b12633"
accent_color_dark = "#c54854"
toc = true
toc_sidebar = true
toc_inline = true
card = true
favicon = "/logo.png"
apple_touch_icon = "/logo.png"
go_to_top = true
show_copy_button = true
show_reading_time = true
show_share_button = true
show_backlinks = true
[extra.nav]
auto_hide = false
show_feed = true
show_theme_switcher = true
show_repo = true
links = [
{ url = "@/_index.md", name = "Projects" },
]
[extra.footer]
links = [
{ url = "@/_index.md", name = "Projects" },
]
socials = [
{ url = "https://github.com/arontaupe", name = "GitHub", icon = "%3Csvg role='img' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Ctitle%3EGitHub%3C/title%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E" },
{ url = "https://www.printables.com/@arontaupe", name = "Printables", icon = "%3Csvg%20role%3D%22img%22%20viewBox%3D%220%200%2024%2024%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Ctitle%3EPrintables%3C%2Ftitle%3E%3Cpath%20d%3D%22M3.678%204.8%2012%209.6v9.6l8.322-4.8V4.8L12%200ZM12%2019.2l-8.322-4.8V24Z%22%2F%3E%3C%2Fsvg%3E" },
{ url = "https://www.etsy.com/de-en/shop/reprintedservices", name = "Etsy", icon = "%3Csvg%20role%3D%22img%22%20viewBox%3D%220%200%2024%2024%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Ctitle%3EEtsy%3C%2Ftitle%3E%3Cpath%20d%3D%22M8.559%202.445c0-.325.033-.52.59-.52h7.465c1.3%200%202.02%201.11%202.54%203.193l.42%201.666h1.27c.23-4.728.43-6.784.43-6.784s-3.196.36-5.09.36H6.635L1.521.196v1.37l1.725.326c1.21.24%201.5.496%201.6%201.606%200%200%20.11%203.27.11%208.64%200%205.385-.09%208.61-.09%208.61%200%20.973-.39%201.333-1.59%201.573l-1.722.33V24l5.13-.165h8.55c1.935%200%206.39.165%206.39.165.105-1.17.75-6.48.855-7.064h-1.2l-1.284%202.91c-1.005%202.28-2.476%202.445-4.11%202.445h-4.906c-1.63%200-2.415-.64-2.415-2.05V12.8s3.62%200%204.79.096c.912.064%201.463.325%201.76%201.598l.39%201.695h1.41l-.09-4.278.192-4.305h-1.391l-.45%201.89c-.283%201.244-.48%201.47-1.754%201.6-1.666.17-4.815.14-4.815.14V2.45h-.05z%22%2F%3E%3C%2Fsvg%3E" },
{ url = "https://mastodon.online/@reprintedAron", name = "Mastodon", icon = "%3Csvg role='img' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Ctitle%3EMastodon%3C/title%3E%3Cpath d='M23.268 5.313c-.35-2.578-2.617-4.61-5.304-5.004C17.51.242 15.792 0 11.813 0h-.03c-3.98 0-4.835.242-5.288.309C3.882.692 1.496 2.518.917 5.127.64 6.412.61 7.837.661 9.143c.074 1.874.088 3.745.26 5.611.118 1.24.325 2.47.62 3.68.55 2.237 2.777 4.098 4.96 4.857 2.336.792 4.849.923 7.256.38.265-.061.527-.132.786-.213.585-.184 1.27-.39 1.774-.753a.057.057 0 0 0 .023-.043v-1.809a.052.052 0 0 0-.02-.041.053.053 0 0 0-.046-.01 20.282 20.282 0 0 1-4.709.545c-2.73 0-3.463-1.284-3.674-1.818a5.593 5.593 0 0 1-.319-1.433.053.053 0 0 1 .066-.054c1.517.363 3.072.546 4.632.546.376 0 .75 0 1.125-.01 1.57-.044 3.224-.124 4.768-.422.038-.008.077-.015.11-.024 2.435-.464 4.753-1.92 4.989-5.604.008-.145.03-1.52.03-1.67.002-.512.167-3.63-.024-5.545zm-3.748 9.195h-2.561V8.29c0-1.309-.55-1.976-1.67-1.976-1.23 0-1.846.79-1.846 2.35v3.403h-2.546V8.663c0-1.56-.617-2.35-1.848-2.35-1.112 0-1.668.668-1.67 1.977v6.218H4.822V8.102c0-1.31.337-2.35 1.011-3.12.696-.77 1.608-1.164 2.74-1.164 1.311 0 2.302.5 2.962 1.498l.638 1.06.638-1.06c.66-.999 1.65-1.498 2.96-1.498 1.13 0 2.043.395 2.74 1.164.675.77 1.012 1.81 1.012 3.12z'/%3E%3C/svg%3E" },
]
show_copyright = true
show_powered_by = false
show_source = true
[extra.comments]
host = "mastodon.online"
user = "reprintedAron"
show_qr = true
[extra.goatcounter]
#host = ""
user = "awebsite"
[extra.debug]
layout = false
no_styles = false

167
content/_index.de.md Normal file
View file

@ -0,0 +1,167 @@
+++
title = "Werkraum - Making Workshops"
description = "Die Zukunft gestalten — mit Händen, Köpfen und Code. Buchen Sie uns für Ihren nächsten Digital- oder Handwerksworkshop."
draft=false
[extra]
show_copyright = false
show_shares = true
show_source = false
show_toc = true
inline_toc = true
[extra.footer]
copyright = "© Test"
show_copyright = false
+++
{% alert(note=true) %}
Dies ist ein Work in Progress.
Die hier bereitgestellten Informationen können sich noch ändern.
{% end %}
## Euer Kreativ-Workshop-Team
Wir sind Aron Petau und Friedrich Weber Goizel — Maker, Pädagogen und leidenschaftliche Tüftler. Wir entwickeln und veranstalten Workshops in Berlin.
Wir bringen die Welt des Machens in Bibliotheken, Schulen und Jugendzentren. Unsere Workshops sind praxisnah und darauf ausgelegt, Kreativität, Neugier und technische Fähigkeiten zu fördern.
Egal, ob ihr schon einen Makerspace mit 3D-Druckern, Lasercuttern oder Plottern habt oder ganz am Anfang steht — wir passen uns an.
Wir bieten sowohl mobile Vorführungen und Einführungsveranstaltungen als auch vertiefende Workshops, die auf eurer bestehenden Ausstattung aufbauen.
Jeder Workshop wird individuell auf eure Bedürfnisse, euer Publikum und euren Raum zugeschnitten.
- Workshops zu 3D-Druck, Lasercutting, Plotten, Robotik und 3D-Design
- Flexible Durchführung: auf eurem Equipment oder mit unseren mobilen Demo-Setups
- Erfahrung mit Schulklassen, Bibliotheken und öffentlichem Publikum
- Maßgeschneiderte Workshop-Konzepte passend zu euren Zielen und Teilnehmenden
- Wir helfen euch sogar bei der Planung, Gestaltung und dem Aufbau neuer Makerspaces!
Lasst uns gemeinsam einen Raum schaffen, in dem Handwerk auf Code, Analoges auf Digitales und Ideen auf Leben treffen.
Ihr könnt uns für einmalige Veranstaltungen oder laufende Programme kontaktieren.
---
## Kontakt
Wir freuen uns, von euch zu hören!
Für Anfragen, Buchungen oder um über einen individuellen Workshop zu sprechen, schreibt uns:
[aron@petau.net + f.goizel@yahoo.com](mailto:aron@petau.net,f.goizel@yahoo.com?subject=[Workshop])
---
### Referenzen & Erfahrung
Wir haben Workshops durchgeführt und zusammengearbeitet mit:
- [Junge Tüftler*innen, Berlin](https://junge-tueftler.de)
- [Futurium, Berlin](https://futurium.de)
- [Gabriele von Bülow Gymnasium, Berlin](https://www.gvb-berlin.de)
- [studio einszwovier](https://www.gvb-berlin.de/unterricht-plus/arbeitsgemeinschaften/maker-space-studio-einszwovier/)
- [inküle](https://inkuele.de), Innovationen für die künstlerische Lehre an der UdK
- Unterstützt vom Masterprogramm [*New Practice in Design and Technology*](https://www.newpractice.net)
- [Friedrich Weber @ New Practice](https://www.newpractice.net/author/friedrich-weber-goizel)
- [Aron Petau @ New Practice](https://www.newpractice.net/author/aron-petau)
- [Stadtteilbibliothek Karow](https://www.berlin.de/stadtbibliothek-pankow/bibliotheken/stadtteilbibliothek-karow/)
## Unsere Profile
### Aron
<aside>
![Aron](/images/colleagues/aron_petau.jpeg "Aron Petau")
</aside>
Aron hat einen Hintergrund in Kognitionswissenschaften, KI und Mediendidaktik.
Er liebt knifflige Software-Probleme und denkt gerne über Plastik als Werkstoff jenseits des Druckers nach.
Hier gibts eine ausführliche [Bio](/pages/cv):
### Friedrich
<aside>
![Friedrich](/images/colleagues/friedrich_weber.jpg "Friedrich Weber Goizel")
</aside>
#### Ausbildung:
20142015 Studium der Freien Kunst, Hochschule der Künste Braunschweig, Skulptur
20152019 Studium der Freien Kunst, Universität der Künste Berlin, Konzept und Neue Medien
2019 Master of Fine Arts
20182021 Studium humanoide Robotik, Berliner Hochschule für Technik
2022 Studium Design & Computation, Technische Universität Berlin & Universität der Künste Berlin
#### Beruf:
20192021 Besucherservice im Futurium Berlin
20212022 Besucherservice und Ausstellungsaufsicht im GID Berlin (Futurium)
2022 Künstlerisches Forschungsstipendium „KuRoBi4all“ (Kunst, Robotik, Bibliothek für alle); Hochschule für Wirtschaft und Recht Berlin, Stadtbibliotheken Pankow Berlin, Artspring Berlin
---
## Unsere kombinierten Workshop-Skills
{% skills() %}
[
{
"name": "3D-Design & Slicing",
"skills": [
{ "name": "<abbr title=\"Eine intuitive 3D-Modellierungs-App für druckbare Formen.\">Shapr3D</abbr>", "icon": "fas fa-drafting-compass", "link": "https://www.shapr3d.com" },
{ "name": "<abbr title=\"Ein professionelles CAD-Tool zur Konstruktion mechanischer Teile und Produkte.\">Fusion 360</abbr>", "icon": "fas fa-project-diagram", "link": "https://www.autodesk.com/products/fusion-360" },
{ "name": "<abbr title=\"Eine einsteigerfreundliche Web-App für einfache 3D-Modelle.\">Tinkercad</abbr>", "icon": "fas fa-cubes", "link": "https://www.tinkercad.com" },
{ "name": "<abbr title=\"Ein Programm, das 3D-Modelle für den Druck auf Prusa-Maschinen vorbereitet.\">PrusaSlicer</abbr>", "icon": "fas fa-cut", "link": "https://www.prusa3d.com/page/prusaslicer_424" },
{ "name": "<abbr title=\"Eine beliebte Slicer-Software zur Vorbereitung von 3D-Druckaufträgen.\">Cura</abbr>", "icon": "fas fa-layer-group", "link": "https://ultimaker.com/software/ultimaker-cura/" },
{ "name": "<abbr title=\"Eine Slicer-Software, optimiert für viele moderne 3D-Drucker.\">OrcaSlicer</abbr>", "icon": "fas fa-fish", "link": "https://orcaslicer.com" },
{ "name": "<abbr title=\"Ein webbasierter Slicer und CAM-Tool für 3D-Druck und CNC.\">Kiri:Moto</abbr>", "icon": "fas fa-network-wired", "link": "https://grid.space/kiri/" }
]
},
{
"name": "Grafikdesign",
"skills": [
{ "name": "<abbr title=\"Ein Vektor-Design-Tool für Grafiken und Illustrationen.\">Affinity Designer</abbr>", "icon": "fas fa-pencil-ruler", "link": "https://affinity.serif.com/designer/" },
{ "name": "<abbr title=\"Ein kostenloses Open-Source-Tool für Vektorgrafiken.\">Inkscape</abbr>", "icon": "fas fa-palette", "link": "https://inkscape.org" },
{ "name": "<abbr title=\"Eine Inkscape-Erweiterung für Stickmuster.\">Ink/Stitch</abbr>", "icon": "fas fa-needle", "link": "https://inkstitch.org" },
{ "name": "<abbr title=\"Eine Online-Plattform für einfaches Grafikdesign, Poster und mehr.\">Canva</abbr>", "icon": "fas fa-paint-brush", "link": "https://www.canva.com" },
{ "name": "<abbr title=\"Eine 3D-Umgebungsplattform, in der Kinder interaktive virtuelle Welten bauen.\">CoSpaces Edu</abbr>", "icon": "fas fa-vr-cardboard", "link": "https://cospaces.io/edu/" }
]
},
{
"name": "Bildungstechnologien",
"skills": [
{ "name": "<abbr title=\"Winzige Roboter, die Kindern spielerisch Programmieren beibringen.\">Ozobot</abbr>", "icon": "fas fa-robot", "link": "https://ozobot.com" },
{ "name": "<abbr title=\"Ein einfacher Bodenroboter, der jungen Lernenden grundlegendes Programmieren vermittelt.\">Blue-Bot</abbr>", "icon": "fas fa-car-side", "link": "https://www.tts-group.co.uk/blue-bot-bluetooth-programmable-floor-robot/1015267.html" },
{ "name": "<abbr title=\"Ein Mikrocontroller-Board zum Lehren von Programmierung und Elektronik.\">Calliope mini</abbr>", "icon": "fas fa-microchip", "link": "https://calliope.cc" },
{ "name": "<abbr title=\"Ein LEGO-Roboterbausatz für praxisnahes STEM-Lernen.\">LEGO Spike</abbr>", "icon": "fas fa-cog", "link": "https://education.lego.com/en-us/products/lego-education-spike-prime-set-/45678" },
{ "name": "<abbr title=\"Ein LEGO-Set, das Kindern Robotik und Technik näherbringt.\">LEGO WeDo</abbr>", "icon": "fas fa-cogs", "link": "https://education.lego.com/en-us/products/lego-education-wedo-2-0-core-set-/45300" },
{ "name": "<abbr title=\"Kleine Drohnen für Luftaufnahmen und praktische Flugdemos.\">DJI Drones</abbr>", "icon": "fas fa-drone", "link": "https://www.dji.com" },
{ "name": "<abbr title=\"Programmierbare Roboterarme wie in der Industrie.\">Industrieroboterarme</abbr>", "icon": "fas fa-hand-paper", "link": "#" }
]
},
{
"name": "3D-Druck-Hardware",
"skills": [
{ "name": "<abbr title=\"Drucker, die mit Flüssigharz und UV-Licht hochdetaillierte Drucke erstellen.\">Resindrucker</abbr>", "icon": "fas fa-vial", "link": "#" },
{ "name": "<abbr title=\"Drucker, die mit geschmolzenem Kunststofffilament 3D-Objekte erzeugen.\">Filamentdrucker</abbr>", "icon": "fas fa-cube", "link": "#" }
]
},
{
"name": "Programmierung",
"skills": [
{ "name": "<abbr title=\"Eine visuelle, blockbasierte Programmierplattform für Kinder und Einsteiger.\">Scratch</abbr>", "icon": "fas fa-cat", "link": "https://scratch.mit.edu" },
{ "name": "<abbr title=\"Ein Erfinder-Set, das Alltagsobjekte mit Computerprogrammen verbindet.\">Makey Makey</abbr>", "icon": "fas fa-plug", "link": "https://makeymakey.com" },
{ "name": "<abbr title=\"Eine beliebte, einsteigerfreundliche Programmiersprache.\">Python</abbr>", "icon": "fab fa-python", "link": "https://www.python.org" },
{ "name": "<abbr title=\"Das Erstellen und Gestalten von Webseiten und Webanwendungen.\">Webentwicklung</abbr>", "icon": "fas fa-code", "link": "#" }
]
}
]
{% end %}

174
content/_index.md Normal file
View file

@ -0,0 +1,174 @@
+++
title = "Werkraum - Making Workshops"
description = "Crafting the future — hands, minds, and code. Book us for your next digital / crafts workshop"
draft=false
[extra]
show_copyright = false
show_shares = true
show_source = false
show_toc = true
inline_toc = true
[extra.footer]
copyright = "© Test"
show_copyright = false
+++
{% alert(note=true) %}
This is a work in Progress.
Informations on here are subject to change.
{% end %}
## Your Creative Workshop Team
We are Aron Petau and Friedrich Weber Goizel — makers, educators, and passionate tinkerers conceptualizing and making workshops for a living in Berlin.
We bring the world of making to libraries, schools, and youth centers, offering hands-on workshops designed to spark creativity, curiosity, and technical skill.
Whether you already have a makerspace with 3D printers, laser cutters, or plotters, or youre just starting out — we are adapting.
We can offer both portable showcases and introductory sessions as well as deep-dive workshops that build on your existing equipment.
We tailor every workshop to your needs, audience, and space.
- Workshops in 3D printing, laser cutting, plotting, robotics, and 3D design
- Flexible delivery: on your equipment or with our portable demo setups
- Experience with school groups, libraries, and public audiences
- Custom workshop concepts to match your goals and participants
- We even help design, curate, and build out new maker spaces!
Lets co-create a space where craft meets code, analog meets digital, and ideas come alive.
Wed love to help turn your place into a hub of creativity and learning.
You can contact us for one-time events or ongoing programs.
---
## Contact
Wed love to hear from you!
For inquiries, bookings, or to discuss a custom workshop, reach out to us via email at:
[aron@petau.net + f.goizel@yahoo.com](mailto:aron@petau.net,f.goizel@yahoo.com?subject=[Workshop])
---
### References & Experience
Weve run workshops and collaborated with:
- [Junge Tüftler*innen, Berlin](https://junge-tueftler.de)
- [Futurium, Berlin](https://futurium.de)
- [Gabriele von Bülow Gymnasium, Berlin](https://www.gvb-berlin.de)
- [studio einszwovier](https://www.gvb-berlin.de/unterricht-plus/arbeitsgemeinschaften/maker-space-studio-einszwovier/)
- [inküle](https://inkuele.de), Innovationen für die künstlerische Lehre an der UdK
- Backed by the [*New Practice in Design and Technology*](https://www.newpractice.net) masters program
- [Friedrich Weber @ New Practice](https://www.newpractice.net/author/friedrich-weber-goizel)
- [Aron Petau @ New Practice](https://www.newpractice.net/author/aron-petau)
- [Stadtteilbibliothek Karow](https://www.berlin.de/stadtbibliothek-pankow/bibliotheken/stadtteilbibliothek-karow/)
## Our Profiles
### Aron
<aside>
![Aron](/images/colleagues/aron_petau.jpeg "Aron Petau")
</aside>
Aron has a background in Cognitive sciences, AI and Media Didactics.
He loves tricky software problems and likes to think of plastics as a material for making beyond the printer.
Read up a full [bio](/pages/cv) here:
### Friedrich
<aside>
![Friedrich](/images/colleagues/friedrich_weber.jpg "Friedrich Weber Goizel")
</aside>
#### Education:
2014-2015 Studies of Fine Arts, Hochschule der Künste Braunschweig, sculpture
2015-2019 Studies of Fine Arts, Universität der Künste Berlin, Concept and New Media
2019 Master of Fine Arts
2018-2021 Studies of humanoid Robotics, Berliner Hochschule für Technik
2022 Studies of Design & Computation, Technische Universität Berlin & Universität der Künste Berlin
#### Work:
2019-2021 Visitor service at Futurium Berlin
2021-2022 Visitor service and exhibition supervisor at GID Berlin (Futurium)
2022 Artistic Research Scholarship "KuRoBi4all" (Art, Robotics, Library for all); Hochschule für Wirtschaft und Recht Berlin, Stadtbibliotheken Pankow Berlin, Artspring Berlin
---
## Our combined workshop skills
{% skills() %}
[
{
"name": "3D Design & Slicing",
"skills": [
{ "name": "<abbr title=\"An intuitive 3D modeling app for creating printable shapes.\">Shapr3D</abbr>", "icon": "fas fa-drafting-compass", "link": "https://www.shapr3d.com" },
{ "name": "<abbr title=\"A professional CAD tool used for designing mechanical parts and products.\">Fusion 360</abbr>", "icon": "fas fa-project-diagram", "link": "https://www.autodesk.com/products/fusion-360" },
{ "name": "<abbr title=\"A beginner-friendly web app for building simple 3D models.\">Tinkercad</abbr>", "icon": "fas fa-cubes", "link": "https://www.tinkercad.com" },
{ "name": "<abbr title=\"A program that prepares 3D models for printing on Prusa machines.\">PrusaSlicer</abbr>", "icon": "fas fa-cut", "link": "https://www.prusa3d.com/page/prusaslicer_424" },
{ "name": "<abbr title=\"A popular slicer software for preparing 3D print jobs.\">Cura</abbr>", "icon": "fas fa-layer-group", "link": "https://ultimaker.com/software/ultimaker-cura/" },
{ "name": "<abbr title=\"A slicer program optimized for many modern 3D printers.\">OrcaSlicer</abbr>", "icon": "fas fa-fish", "link": "https://orcaslicer.com" },
{ "name": "<abbr title=\"A web-based slicer and CAM tool for 3D printing and CNC.\">Kiri:Moto</abbr>", "icon": "fas fa-network-wired", "link": "https://grid.space/kiri/" }
]
},
{
"name": "Graphic Design",
"skills": [
{ "name": "<abbr title=\"A vector design tool for creating graphics and illustrations.\">Affinity Designer</abbr>", "icon": "fas fa-pencil-ruler", "link": "https://affinity.serif.com/designer/" },
{ "name": "<abbr title=\"A free, open-source tool for creating vector drawings.\">Inkscape</abbr>", "icon": "fas fa-palette", "link": "https://inkscape.org" },
{ "name": "<abbr title=\"An embroidery design extension for Inkscape, used for preparing stitching patterns.\">Ink/Stitch</abbr>", "icon": "fas fa-needle", "link": "https://inkstitch.org" },
{ "name": "<abbr title=\"An online design platform for easy graphics, posters, and more.\">Canva</abbr>", "icon": "fas fa-paint-brush", "link": "https://www.canva.com" },
{ "name": "<abbr title=\"A 3D creation platform where kids can build interactive virtual worlds.\">CoSpaces Edu</abbr>", "icon": "fas fa-vr-cardboard", "link": "https://cospaces.io/edu/" }
]
},
{
"name": "Educational Technology",
"skills": [
{ "name": "<abbr title=\"Tiny robots that help kids learn coding through play.\">Ozobot</abbr>", "icon": "fas fa-robot", "link": "https://ozobot.com" },
{ "name": "<abbr title=\"A simple floor robot that teaches young learners basic programming.\">Blue-Bot</abbr>", "icon": "fas fa-car-side", "link": "https://www.tts-group.co.uk/blue-bot-bluetooth-programmable-floor-robot/1015267.html" },
{ "name": "<abbr title=\"A microcontroller board used for teaching coding and electronics.\">Calliope mini</abbr>", "icon": "fas fa-microchip", "link": "https://calliope.cc" },
{ "name": "<abbr title=\"A LEGO robotics kit for hands-on STEM learning.\">LEGO Spike</abbr>", "icon": "fas fa-cog", "link": "https://education.lego.com/en-us/products/lego-education-spike-prime-set-/45678" },
{ "name": "<abbr title=\"A LEGO kit designed to teach kids about robotics and engineering.\">LEGO WeDo</abbr>", "icon": "fas fa-cogs", "link": "https://education.lego.com/en-us/products/lego-education-wedo-2-0-core-set-/45300" },
{ "name": "<abbr title=\"Small drones used for aerial photography and hands-on flying demos.\">DJI Drones</abbr>", "icon": "fas fa-drone", "link": "https://www.dji.com" },
{ "name": "<abbr title=\"Programmable robot arms like those used in factories.\">Industrial Robot Arms</abbr>", "icon": "fas fa-hand-paper", "link": "#" }
]
},
{
"name": "3D Printing Hardware",
"skills": [
{ "name": "<abbr title=\"Printers that use liquid resin and UV light to make detailed prints.\">Resin Printers</abbr>", "icon": "fas fa-vial", "link": "#" },
{ "name": "<abbr title=\"Printers that use melted plastic filament to create 3D objects.\">Filament Printers</abbr>", "icon": "fas fa-cube", "link": "#" }
]
},
{
"name": "Programming",
"skills": [
{ "name": "<abbr title=\"A visual block-based coding platform for kids and beginners.\">Scratch</abbr>", "icon": "fas fa-cat", "link": "https://scratch.mit.edu" },
{ "name": "<abbr title=\"An invention kit that connects everyday objects to computer programs.\">Makey Makey</abbr>", "icon": "fas fa-plug", "link": "https://makeymakey.com" },
{ "name": "<abbr title=\"A popular, beginner-friendly programming language.\">Python</abbr>", "icon": "fab fa-python", "link": "https://www.python.org" },
{ "name": "<abbr title=\"The practice of building and designing websites and web applications.\">Web Development</abbr>", "icon": "fas fa-code", "link": "#" }
]
}
]
{% end %}

76
i18n/de.toml Normal file
View file

@ -0,0 +1,76 @@
language_name = "Deutsch"
date_format = "%d. %m. %Y"
date_locale = "de_DE"
Projects = "Projekte"
Contact = "Kontakt"
CV = "Vita"
About = "Über mich"
all_tags = "Zeige alle Schlagwörter"
archived = "Archiviert"
author = "Autor"
author_conjunction = " und "
author_separator = ", "
backlinks = "Rücklink"
blog_post_author = "Autor des Blogbeitrags"
boosts_from = "Boosts von $INSTANCE"
by_author = "Von $AUTHOR"
caution = "Vorsicht"
comments = "Kommentar"
comments_description = "Du kannst diesen Blog-Beitrag kommentieren, indem du mit einem Mastodon- oder einem anderen ActivityPub/Fediverse-Konto öffentlich auf diesen Beitrag antwortest. Bekannte nicht-private Antworten werden unten angezeigt."
comments_noscript = "Das Laden von Kommentaren basiert auf JavaScript. Versuche, JavaScript zu aktivieren und neu zu laden, oder besuchen den Originalbeitrag auf Mastodon"
comments_qr = "QR code zu einem Mastodon-Beitrag"
copy_code = "Code kopieren"
disclaimer = "Haftungsausschluss"
drafted = "Entworfen"
faves_from = "Favoriten von $INSTANCE"
featured = "Herausgestellt"
feed = "Feed"
file_an_issue = "Ein Issue erstellen"
filter_by_tag = "Nach Tag filtern"
first = "Zuerst"
go_to_top = "Nach Oben gehen"
hot = "Heiß"
important = "Wichtig"
language = "Sprache"
last = "Zuletzt"
load_comments = "Kommentare laden"
loading = "Lädt"
many_minutes_read = "$NUMBER Minuten gelesen"
many_posts = "$NUMBER Beiträge insgesamt"
many_tags = "$NUMBER Schlagwörter insgesamt"
minutes_read = "$NUMBER gelesene Minuten"
more_matches = "$MATCHES mehr Treffer"
next = "Nächstes"
no_comments = "Noch keine Kommentare :/"
note = "Anmerkung"
one_posts = "$NUMBER Beitrag insgesamt"
one_tags = "$NUMBER Schlagwort insgesamt"
open_post = "Beitrag öffnen"
poor = "Schlecht"
posts = "$NUMBER Beiträge insgesamt"
posts_with_tag = "Beiträge mit Tag $TAG"
powered_by = "Gemacht mit $ZOLA und $DUCKQUILL"
previous = "Vorherige"
published = "Veröffentlicht am"
reload = "Neu laden"
repo = "Repository"
search = "Suche"
search_for = "Suche nach"
sensitive = "Heikler Inhalt"
share = "Teilen"
skip_to_content = "Zum Hauptinhalt springen"
source = "Website-Quelle"
table_of_contents = "Inhaltsverzeichnis"
tags = "Schlagwörter"
tags_title = "Schlagwörter"
theme = "Thema"
theme_dark = "Zum dunklen Thema wechseln"
theme_light = "Zum hellen Thema wechseln"
theme_system = "Systemthema nutzen"
tip = "Tipp"
trigger_warning = "Inhaltswarnung"
updated = "Aktualisiert am"
view_comment = "Kommentar anzeigen auf"
view_profile = "Profil anzeigen auf"
warning = "Warnung"

16
old_config.toml Normal file
View file

@ -0,0 +1,16 @@
# The URL the site will be built for
base_url = "https://studio-umzu.de"
# Whether to automatically compile all Sass files in the sass directory
compile_sass = true
# Whether to build a search index to be used later on by a JavaScript library
build_search_index = true
[markdown]
# Whether to do syntax highlighting
# Theme can be customised by setting the `highlight_theme` variable to a theme supported by Zola
highlight_code = true
[extra]
# Put all your custom variables here

BIN
public/404.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

2
public/404.html Normal file

File diff suppressed because one or more lines are too long

BIN
public/404.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 B

BIN
public/apple-touch-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

9
public/atom.xml Normal file
View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
<title>Studio UMZU</title>
<link rel="self" type="application/atom+xml" href="https://studio-umzu.de/atom.xml"/>
<link rel="alternate" type="text/html" href="https://studio-umzu.de/"/>
<generator uri="https://www.getzola.org/">Zola</generator>
<id>https://studio-umzu.de/atom.xml</id>
</feed>

1
public/auto-render.min.js vendored Normal file
View file

@ -0,0 +1 @@
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("katex")):"function"==typeof define&&define.amd?define(["katex"],t):"object"==typeof exports?exports.renderMathInElement=t(require("katex")):e.renderMathInElement=t(e.katex)}("undefined"!=typeof self?self:this,(function(e){return function(){"use strict";var t={771:function(t){t.exports=e}},n={};function r(e){var o=n[e];if(void 0!==o)return o.exports;var i=n[e]={exports:{}};return t[e](i,i.exports,r),i.exports}r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,{a:t}),t},r.d=function(e,t){for(var n in t)r.o(t,n)&&!r.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)};var o={};return function(){r.d(o,{default:function(){return d}});var e=r(771),t=r.n(e);const n=function(e,t,n){let r=n,o=0;const i=e.length;for(;r<t.length;){const n=t[r];if(o<=0&&t.slice(r,r+i)===e)return r;"\\"===n?r++:"{"===n?o++:"}"===n&&o--,r++}return-1},i=/^\\begin{/;var a=function(e,t){let r;const o=[],a=new RegExp("("+t.map((e=>e.left.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"))).join("|")+")");for(;r=e.search(a),-1!==r;){r>0&&(o.push({type:"text",data:e.slice(0,r)}),e=e.slice(r));const a=t.findIndex((t=>e.startsWith(t.left)));if(r=n(t[a].right,e,t[a].left.length),-1===r)break;const l=e.slice(0,r+t[a].right.length),s=i.test(l)?l:e.slice(t[a].left.length,r);o.push({type:"math",data:s,rawData:l,display:t[a].display}),e=e.slice(r+t[a].right.length)}return""!==e&&o.push({type:"text",data:e}),o};const l=function(e,n){const r=a(e,n.delimiters);if(1===r.length&&"text"===r[0].type)return null;const o=document.createDocumentFragment();for(let e=0;e<r.length;e++)if("text"===r[e].type)o.appendChild(document.createTextNode(r[e].data));else{const i=document.createElement("span");let a=r[e].data;n.displayMode=r[e].display;try{n.preProcess&&(a=n.preProcess(a)),t().render(a,i,n)}catch(i){if(!(i instanceof t().ParseError))throw i;n.errorCallback("KaTeX auto-render: Failed to parse `"+r[e].data+"` with ",i),o.appendChild(document.createTextNode(r[e].rawData));continue}o.appendChild(i)}return o},s=function(e,t){for(let n=0;n<e.childNodes.length;n++){const r=e.childNodes[n];if(3===r.nodeType){let o=r.textContent,i=r.nextSibling,a=0;for(;i&&i.nodeType===Node.TEXT_NODE;)o+=i.textContent,i=i.nextSibling,a++;const s=l(o,t);if(s){for(let e=0;e<a;e++)r.nextSibling.remove();n+=s.childNodes.length-1,e.replaceChild(s,r)}else n+=a}else if(1===r.nodeType){const e=" "+r.className+" ";-1===t.ignoredTags.indexOf(r.nodeName.toLowerCase())&&t.ignoredClasses.every((t=>-1===e.indexOf(" "+t+" ")))&&s(r,t)}}};var d=function(e,t){if(!e)throw new Error("No element provided to render");const n={};for(const e in t)t.hasOwnProperty(e)&&(n[e]=t[e]);n.delimiters=n.delimiters||[{left:"$$",right:"$$",display:!0},{left:"\\(",right:"\\)",display:!1},{left:"\\begin{equation}",right:"\\end{equation}",display:!0},{left:"\\begin{align}",right:"\\end{align}",display:!0},{left:"\\begin{alignat}",right:"\\end{alignat}",display:!0},{left:"\\begin{gather}",right:"\\end{gather}",display:!0},{left:"\\begin{CD}",right:"\\end{CD}",display:!0},{left:"\\[",right:"\\]",display:!0}],n.ignoredTags=n.ignoredTags||["script","noscript","style","textarea","pre","code","option"],n.ignoredClasses=n.ignoredClasses||[],n.errorCallback=n.errorCallback||console.error,n.macros=n.macros||{},s(e,n)}}(),o=o.default}()}));

BIN
public/card.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

27
public/closable.js Normal file
View file

@ -0,0 +1,27 @@
const closable = document.querySelectorAll("details.closable");
closable.forEach((detail) => {
detail.addEventListener("toggle", () => {
if (detail.open) setTargetDetail(detail);
});
});
function setTargetDetail(targetDetail) {
closable.forEach((detail) => {
if (detail !== targetDetail) {
detail.open = false;
}
});
}
document.addEventListener("click", function (event) {
const isClickInsideDetail = [...closable].some((detail) =>
detail.contains(event.target)
);
if (!isClickInsideDetail) {
closable.forEach((detail) => {
detail.open = false;
});
}
});

406
public/comments.js Normal file
View file

@ -0,0 +1,406 @@
// Taken from https://carlschwan.eu/2020/12/29/adding-comments-to-your-static-blog-with-mastodon/
// Attachment, card, and spoiler code taken from https://github.com/cassidyjames/cassidyjames.github.io/blob/99782788a7e3ba3cc52d6803010873abd1b02b9e/_includes/comments.html#L251-L296
let blogPostAuthorText = document.getElementById("blog-post-author-text").textContent;
let boostsFromText = document.getElementById("boosts-from-text").textContent;
let dateLocale = document.getElementById("date-locale").textContent;
let favesFromText = document.getElementById("faves-from-text").textContent;
let host = document.getElementById("host").textContent;
let id = document.getElementById("id").textContent;
let lazyAsyncImage = document.getElementById("lazy-async-image").textContent;
let loadingText = document.getElementById("loading-text").textContent;
let noCommentsText = document.getElementById("no-comments-text").textContent;
let relAttributes = document.getElementById("rel-attributes").textContent;
let reloadText = document.getElementById("reload-text").textContent;
let sensitiveText = document.getElementById("sensitive-text").textContent;
let user = document.getElementById("user").textContent;
let viewCommentText = document.getElementById("view-comment-text").textContent;
let viewProfileText = document.getElementById("view-profile-text").textContent;
document.getElementById("load-comments").addEventListener("click", loadComments);
function escapeHtml(unsafe) {
return unsafe
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}
function emojify(input, emojis) {
let output = input;
emojis.forEach((emoji) => {
let picture = document.createElement("picture");
let source = document.createElement("source");
source.setAttribute("srcset", escapeHtml(emoji.url));
source.setAttribute("media", "(prefers-reduced-motion: no-preference)");
let img = document.createElement("img");
img.className = "emoji";
img.setAttribute("src", escapeHtml(emoji.static_url));
img.setAttribute("alt", `:${emoji.shortcode}:`);
img.setAttribute("title", `:${emoji.shortcode}:`);
if (lazyAsyncImage == "true") {
img.setAttribute("decoding", "async");
img.setAttribute("loading", "lazy");
}
picture.appendChild(source);
picture.appendChild(img);
output = output.replace(`:${emoji.shortcode}:`, picture.outerHTML);
});
return output;
}
function loadComments() {
let commentsWrapper = document.getElementById("comments-wrapper");
commentsWrapper.innerHTML = "";
let loadCommentsButton = document.getElementById("load-comments");
loadCommentsButton.innerHTML = loadingText;
loadCommentsButton.disabled = true;
fetch(`https://${host}/api/v1/statuses/${id}/context`)
.then(function (response) {
return response.json();
})
.then(function (data) {
let descendants = data["descendants"];
if (
descendants &&
Array.isArray(descendants) &&
descendants.length > 0
) {
commentsWrapper.innerHTML = "";
descendants.forEach(function (status) {
console.log(descendants);
if (status.account.display_name.length > 0) {
status.account.display_name = escapeHtml(
status.account.display_name
);
status.account.display_name = emojify(
status.account.display_name,
status.account.emojis
);
} else {
status.account.display_name = status.account.username;
}
let instance = "";
if (status.account.acct.includes("@")) {
instance = status.account.acct.split("@")[1];
} else {
instance = host;
}
const isReply = status.in_reply_to_id !== id;
let op = false;
if (status.account.acct == user) {
op = true;
}
status.content = emojify(status.content, status.emojis);
let comment = document.createElement("article");
comment.id = `comment-${status.id}`;
comment.className = isReply ? "comment comment-reply" : "comment";
comment.setAttribute("itemprop", "comment");
comment.setAttribute("itemtype", "http://schema.org/Comment");
let avatarSource = document.createElement("source");
avatarSource.setAttribute(
"srcset",
escapeHtml(status.account.avatar)
);
avatarSource.setAttribute(
"media",
"(prefers-reduced-motion: no-preference)"
);
let avatarImg = document.createElement("img");
avatarImg.className = "avatar";
avatarImg.setAttribute(
"src",
escapeHtml(status.account.avatar_static)
);
avatarImg.setAttribute(
"alt",
`@${status.account.username}@${instance} avatar`
);
if (lazyAsyncImage == "true") {
avatarImg.setAttribute("decoding", "async");
avatarImg.setAttribute("loading", "lazy");
}
let avatarPicture = document.createElement("picture");
avatarPicture.appendChild(avatarSource);
avatarPicture.appendChild(avatarImg);
let avatar = document.createElement("a");
avatar.className = "avatar-link";
avatar.setAttribute("href", status.account.url);
avatar.setAttribute("rel", relAttributes);
avatar.setAttribute(
"title",
`${viewProfileText} @${status.account.username}@${instance}`
);
avatar.appendChild(avatarPicture);
comment.appendChild(avatar);
let instanceBadge = document.createElement("a");
instanceBadge.className = "instance";
instanceBadge.setAttribute("href", status.account.url);
instanceBadge.setAttribute(
"title",
`@${status.account.username}@${instance}`
);
instanceBadge.setAttribute("rel", relAttributes);
instanceBadge.textContent = instance;
let display = document.createElement("span");
display.className = "display";
display.setAttribute("itemprop", "author");
display.setAttribute("itemtype", "http://schema.org/Person");
display.innerHTML = status.account.display_name;
let header = document.createElement("header");
header.className = "author";
header.appendChild(display);
header.appendChild(instanceBadge);
comment.appendChild(header);
let permalink = document.createElement("a");
permalink.setAttribute("href", status.url);
permalink.setAttribute("itemprop", "url");
permalink.setAttribute("title", `${viewCommentText} ${instance}`);
permalink.setAttribute("rel", relAttributes);
permalink.textContent = new Date(
status.created_at
).toLocaleString(dateLocale, {
dateStyle: "long",
timeStyle: "short",
});
let timestamp = document.createElement("time");
timestamp.setAttribute("datetime", status.created_at);
timestamp.appendChild(permalink);
permalink.classList.add("external");
comment.appendChild(timestamp);
let main = document.createElement("main");
main.setAttribute("itemprop", "text");
if (status.sensitive == true || status.spoiler_text != "") {
let summary = document.createElement("summary");
if (status.spoiler_text == "") {
status.spoiler_text == sensitiveText;
}
summary.innerHTML = status.spoiler_text;
let spoiler = document.createElement("details");
spoiler.appendChild(summary);
spoiler.innerHTML += status.content;
main.appendChild(spoiler);
} else {
main.innerHTML = status.content;
}
comment.appendChild(main);
let attachments = status.media_attachments;
let SUPPORTED_MEDIA = ["image", "video", "gifv", "audio"];
let media = document.createElement("div");
media.className = "attachments";
if (
attachments &&
Array.isArray(attachments) &&
attachments.length > 0
) {
attachments.forEach((attachment) => {
if (SUPPORTED_MEDIA.includes(attachment.type)) {
let mediaElement;
switch (attachment.type) {
case "image":
mediaElement = document.createElement("img");
mediaElement.setAttribute("src", attachment.preview_url);
if (attachment.description != null) {
mediaElement.setAttribute("alt", attachment.description);
mediaElement.setAttribute("title", attachment.description);
}
if (lazyAsyncImage == "true") {
mediaElement.setAttribute("decoding", "async");
mediaElement.setAttribute("loading", "lazy");
}
if (status.sensitive == true) {
mediaElement.classList.add("spoiler");
}
media.appendChild(mediaElement);
break;
case "video":
mediaElement = document.createElement("video");
mediaElement.setAttribute("src", attachment.url);
mediaElement.setAttribute("controls", "");
if (attachment.description != null) {
mediaElement.setAttribute("aria-title", attachment.description);
mediaElement.setAttribute("title", attachment.description);
}
if (status.sensitive == true) {
mediaElement.classList.add("spoiler");
}
media.appendChild(mediaElement);
break;
case "gifv":
mediaElement = document.createElement("video");
mediaElement.setAttribute("src", attachment.url);
mediaElement.setAttribute("autoplay", "");
mediaElement.setAttribute("playsinline", "");
mediaElement.setAttribute("loop", "");
if (attachment.description != null) {
mediaElement.setAttribute("aria-title", attachment.description);
mediaElement.setAttribute("title", attachment.description);
}
if (status.sensitive == true) {
mediaElement.classList.add("spoiler");
}
media.appendChild(mediaElement);
break;
case "audio":
mediaElement = document.createElement("audio");
mediaElement.setAttribute("src", attachment.url);
mediaElement.setAttribute("controls", "");
if (attachment.description != null) {
mediaElement.setAttribute("aria-title", attachment.description);
mediaElement.setAttribute("title", attachment.description);
}
media.appendChild(mediaElement);
break;
}
let mediaLink = document.createElement("a");
mediaLink.setAttribute("href", attachment.url);
mediaLink.setAttribute("rel", relAttributes);
mediaLink.appendChild(mediaElement);
media.appendChild(mediaLink);
}
});
comment.appendChild(media);
}
let interactions = document.createElement("footer");
let boosts = document.createElement("a");
boosts.className = "boosts";
boosts.setAttribute("href", `${status.url}/reblogs`);
boosts.setAttribute("title", `${boostsFromText}`.replace("$INSTANCE", instance));
let boostsIcon = document.createElement("i");
boostsIcon.className = "icon";
boosts.appendChild(boostsIcon);
boosts.insertAdjacentHTML('beforeend', ` ${status.reblogs_count}`);
interactions.appendChild(boosts);
let faves = document.createElement("a");
faves.className = "faves";
faves.setAttribute("href", `${status.url}/favourites`);
faves.setAttribute("title", `${favesFromText}`.replace("$INSTANCE", instance));
let favesIcon = document.createElement("i");
favesIcon.className = "icon";
faves.appendChild(favesIcon);
faves.insertAdjacentHTML('beforeend', ` ${status.favourites_count}`);
interactions.appendChild(faves);
comment.appendChild(interactions);
if (status.card != null) {
let cardFigure = document.createElement("figure");
if (status.card.image != null) {
let cardImg = document.createElement("img");
cardImg.setAttribute("src", status.card.image);
cardImg.classList.add("no-hover");
cardFigure.appendChild(cardImg);
}
let cardCaption = document.createElement("figcaption");
let cardTitle = document.createElement("strong");
cardTitle.innerHTML = status.card.title;
cardCaption.appendChild(cardTitle);
if (status.card.description != null && status.card.description.length > 0) {
let cardDescription = document.createElement("p");
cardDescription.innerHTML = status.card.description;
cardCaption.appendChild(cardDescription);
}
cardFigure.appendChild(cardCaption);
let card = document.createElement("a");
card.className = "card";
card.setAttribute("href", status.card.url);
card.setAttribute("rel", relAttributes);
card.appendChild(cardFigure);
comment.appendChild(card);
}
if (op === true) {
comment.classList.add("op");
avatar.classList.add("op");
avatar.setAttribute(
"title",
`${blogPostAuthorText}: ` + avatar.getAttribute("title")
);
instanceBadge.classList.add("op");
instanceBadge.setAttribute(
"title",
`${blogPostAuthorText}: ` + instanceBadge.getAttribute("title")
);
}
commentsWrapper.innerHTML += comment.outerHTML;
});
}
else {
var statusText = document.createElement("p");
statusText.innerHTML = noCommentsText;
statusText.setAttribute("id", "comments-status");
commentsWrapper.appendChild(statusText);
}
loadCommentsButton.innerHTML = reloadText;
})
.catch(function (error) {
console.error('Error loading comments:', error);
})
.finally(function () {
loadCommentsButton.disabled = false;
});
}

57
public/copy-button.js Normal file
View file

@ -0,0 +1,57 @@
// Based on https://www.roboleary.net/2022/01/13/copy-code-to-clipboard-blog.html
document.addEventListener("DOMContentLoaded", function () {
let blocks = document.querySelectorAll("pre[class^='language-']");
blocks.forEach((block) => {
if (navigator.clipboard) {
// Code block header title
let title = document.createElement("span");
let lang = block.getAttribute("data-lang");
title.innerHTML = lang;
// Copy button icon
let icon = document.createElement("i");
icon.classList.add("icon");
// Copy button
let button = document.createElement("button");
let copyCodeText = document.getElementById("copy-code-text").textContent;
button.setAttribute("title", copyCodeText)
button.appendChild(icon);
// Code block header
let header = document.createElement("div");
header.classList.add("header");
header.appendChild(title);
header.appendChild(button);
// Container that holds header and the code block itself
let container = document.createElement("div");
container.classList.add("pre-container");
container.appendChild(header);
// Move code block into the container
block.parentNode.insertBefore(container, block);
container.appendChild(block);
button.addEventListener("click", async () => {
await copyCode(block, header, button); // Pass the button here
});
}
});
async function copyCode(block, header, button) {
let code = block.querySelector("code");
let text = code.innerText;
await navigator.clipboard.writeText(text);
header.classList.add("active");
button.setAttribute("disabled", true);
header.addEventListener("animationend", () => {
header.classList.remove("active");
button.removeAttribute("disabled");
}, { once: true });
}
});

271
public/count.js Normal file
View file

@ -0,0 +1,271 @@
// GoatCounter: https://www.goatcounter.com
// This file is released under the ISC license: https://opensource.org/licenses/ISC
;(function() {
'use strict';
if (window.goatcounter && window.goatcounter.vars) // Compatibility with very old version; do not use.
window.goatcounter = window.goatcounter.vars
else
window.goatcounter = window.goatcounter || {}
// Load settings from data-goatcounter-settings.
var s = document.querySelector('script[data-goatcounter]')
if (s && s.dataset.goatcounterSettings) {
try { var set = JSON.parse(s.dataset.goatcounterSettings) }
catch (err) { console.error('invalid JSON in data-goatcounter-settings: ' + err) }
for (var k in set)
if (['no_onload', 'no_events', 'allow_local', 'allow_frame', 'path', 'title', 'referrer', 'event'].indexOf(k) > -1)
window.goatcounter[k] = set[k]
}
var enc = encodeURIComponent
// Get all data we're going to send off to the counter endpoint.
var get_data = function(vars) {
var data = {
p: (vars.path === undefined ? goatcounter.path : vars.path),
r: (vars.referrer === undefined ? goatcounter.referrer : vars.referrer),
t: (vars.title === undefined ? goatcounter.title : vars.title),
e: !!(vars.event || goatcounter.event),
s: [window.screen.width, window.screen.height, (window.devicePixelRatio || 1)],
b: is_bot(),
q: location.search,
}
var rcb, pcb, tcb // Save callbacks to apply later.
if (typeof(data.r) === 'function') rcb = data.r
if (typeof(data.t) === 'function') tcb = data.t
if (typeof(data.p) === 'function') pcb = data.p
if (is_empty(data.r)) data.r = document.referrer
if (is_empty(data.t)) data.t = document.title
if (is_empty(data.p)) data.p = get_path()
if (rcb) data.r = rcb(data.r)
if (tcb) data.t = tcb(data.t)
if (pcb) data.p = pcb(data.p)
return data
}
// Check if a value is "empty" for the purpose of get_data().
var is_empty = function(v) { return v === null || v === undefined || typeof(v) === 'function' }
// See if this looks like a bot; there is some additional filtering on the
// backend, but these properties can't be fetched from there.
var is_bot = function() {
// Headless browsers are probably a bot.
var w = window, d = document
if (w.callPhantom || w._phantom || w.phantom)
return 150
if (w.__nightmare)
return 151
if (d.__selenium_unwrapped || d.__webdriver_evaluate || d.__driver_evaluate)
return 152
if (navigator.webdriver)
return 153
return 0
}
// Object to urlencoded string, starting with a ?.
var urlencode = function(obj) {
var p = []
for (var k in obj)
if (obj[k] !== '' && obj[k] !== null && obj[k] !== undefined && obj[k] !== false)
p.push(enc(k) + '=' + enc(obj[k]))
return '?' + p.join('&')
}
// Show a warning in the console.
var warn = function(msg) {
if (console && 'warn' in console)
console.warn('goatcounter: ' + msg)
}
// Get the endpoint to send requests to.
var get_endpoint = function() {
var s = document.querySelector('script[data-goatcounter]')
if (s && s.dataset.goatcounter)
return s.dataset.goatcounter
return (goatcounter.endpoint || window.counter) // counter is for compat; don't use.
}
// Get current path.
var get_path = function() {
var loc = location,
c = document.querySelector('link[rel="canonical"][href]')
if (c) { // May be relative or point to different domain.
var a = document.createElement('a')
a.href = c.href
if (a.hostname.replace(/^www\./, '') === location.hostname.replace(/^www\./, ''))
loc = a
}
return (loc.pathname + loc.search) || '/'
}
// Run function after DOM is loaded.
var on_load = function(f) {
if (document.body === null)
document.addEventListener('DOMContentLoaded', function() { f() }, false)
else
f()
}
// Filter some requests that we (probably) don't want to count.
goatcounter.filter = function() {
if ('visibilityState' in document && document.visibilityState === 'prerender')
return 'visibilityState'
if (!goatcounter.allow_frame && location !== parent.location)
return 'frame'
if (!goatcounter.allow_local && location.hostname.match(/(localhost$|^127\.|^10\.|^172\.(1[6-9]|2[0-9]|3[0-1])\.|^192\.168\.|^0\.0\.0\.0$)/))
return 'localhost'
if (!goatcounter.allow_local && location.protocol === 'file:')
return 'localfile'
if (localStorage && localStorage.getItem('skipgc') === 't')
return 'disabled with #toggle-goatcounter'
return false
}
// Get URL to send to GoatCounter.
window.goatcounter.url = function(vars) {
var data = get_data(vars || {})
if (data.p === null) // null from user callback.
return
data.rnd = Math.random().toString(36).substr(2, 5) // Browsers don't always listen to Cache-Control.
var endpoint = get_endpoint()
if (!endpoint)
return warn('no endpoint found')
return endpoint + urlencode(data)
}
// Count a hit.
window.goatcounter.count = function(vars) {
var f = goatcounter.filter()
if (f)
return warn('not counting because of: ' + f)
var url = goatcounter.url(vars)
if (!url)
return warn('not counting because path callback returned null')
if (!navigator.sendBeacon(url)) {
// This mostly fails due to being blocked by CSP; try again with an
// image-based fallback.
var img = document.createElement('img')
img.src = url
img.style.position = 'absolute' // Affect layout less.
img.style.bottom = '0px'
img.style.width = '1px'
img.style.height = '1px'
img.loading = 'eager'
img.setAttribute('alt', '')
img.setAttribute('aria-hidden', 'true')
var rm = function() { if (img && img.parentNode) img.parentNode.removeChild(img) }
img.addEventListener('load', rm, false)
document.body.appendChild(img)
}
}
// Get a query parameter.
window.goatcounter.get_query = function(name) {
var s = location.search.substr(1).split('&')
for (var i = 0; i < s.length; i++)
if (s[i].toLowerCase().indexOf(name.toLowerCase() + '=') === 0)
return s[i].substr(name.length + 1)
}
// Track click events.
window.goatcounter.bind_events = function() {
if (!document.querySelectorAll) // Just in case someone uses an ancient browser.
return
var send = function(elem) {
return function() {
goatcounter.count({
event: true,
path: (elem.dataset.goatcounterClick || elem.name || elem.id || ''),
title: (elem.dataset.goatcounterTitle || elem.title || (elem.innerHTML || '').substr(0, 200) || ''),
referrer: (elem.dataset.goatcounterReferrer || elem.dataset.goatcounterReferral || ''),
})
}
}
Array.prototype.slice.call(document.querySelectorAll("*[data-goatcounter-click]")).forEach(function(elem) {
if (elem.dataset.goatcounterBound)
return
var f = send(elem)
elem.addEventListener('click', f, false)
elem.addEventListener('auxclick', f, false) // Middle click.
elem.dataset.goatcounterBound = 'true'
})
}
// Add a "visitor counter" frame or image.
window.goatcounter.visit_count = function(opt) {
on_load(function() {
opt = opt || {}
opt.type = opt.type || 'html'
opt.append = opt.append || 'body'
opt.path = opt.path || get_path()
opt.attr = opt.attr || {width: '200', height: (opt.no_branding ? '60' : '80')}
opt.attr['src'] = get_endpoint() + 'er/' + enc(opt.path) + '.' + enc(opt.type) + '?'
if (opt.no_branding) opt.attr['src'] += '&no_branding=1'
if (opt.style) opt.attr['src'] += '&style=' + enc(opt.style)
if (opt.start) opt.attr['src'] += '&start=' + enc(opt.start)
if (opt.end) opt.attr['src'] += '&end=' + enc(opt.end)
var tag = {png: 'img', svg: 'img', html: 'iframe'}[opt.type]
if (!tag)
return warn('visit_count: unknown type: ' + opt.type)
if (opt.type === 'html') {
opt.attr['frameborder'] = '0'
opt.attr['scrolling'] = 'no'
}
var d = document.createElement(tag)
for (var k in opt.attr)
d.setAttribute(k, opt.attr[k])
var p = document.querySelector(opt.append)
if (!p)
return warn('visit_count: append not found: ' + opt.append)
p.appendChild(d)
})
}
// Make it easy to skip your own views.
if (location.hash === '#toggle-goatcounter') {
if (localStorage.getItem('skipgc') === 't') {
localStorage.removeItem('skipgc', 't')
alert('GoatCounter tracking is now ENABLED in this browser.')
}
else {
localStorage.setItem('skipgc', 't')
alert('GoatCounter tracking is now DISABLED in this browser until ' + location + ' is loaded again.')
}
}
if (!goatcounter.no_onload)
on_load(function() {
// 1. Page is visible, count request.
// 2. Page is not yet visible; wait until it switches to 'visible' and count.
// See #487
if (!('visibilityState' in document) || document.visibilityState === 'visible')
goatcounter.count()
else {
var f = function(e) {
if (document.visibilityState !== 'visible')
return
document.removeEventListener('visibilitychange', f)
goatcounter.count()
}
document.addEventListener('visibilitychange', f)
}
if (!goatcounter.no_events)
goatcounter.bind_events()
})
})();

48
public/css/gallery.css Normal file
View file

@ -0,0 +1,48 @@
#image-gallery {
margin: 2rem 0;
}
.gallery {
column-count: 3;
column-gap: 1rem;
}
.gallery-item {
break-inside: avoid;
margin-bottom: 1rem;
text-align: center;
list-style: none; /* ← important! */
}
.gallery-item img {
width: 100%;
height: auto;
border-radius: 12px;
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
transition: transform 0.2s ease;
}
.gallery-item img:hover {
transform: scale(1.6);
}
.caption {
font-size: 0.9rem;
margin-top: 0.5rem;
margin-bottom: 0; /* ← just in case */
color: var(--fg-color);
list-style: none; /* ← extra-safe */
}
@media (max-width: 768px) {
.gallery {
column-count: 2;
}
}
@media (max-width: 480px) {
.gallery {
column-count: 1;
}
}

28
public/css/mermaid.css Normal file
View file

@ -0,0 +1,28 @@
.mermaid {
text-align: center;
margin-top: 1.5em;
margin-bottom: 1.5em;
padding: 1em;
border-radius: 0.5em;
background-color: var(--code-bg);
font-family: var(--code-font, monospace);
font-size: 0.9rem;
overflow-x: auto;
max-width: 100%;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
}
.mermaid strong {
font-weight: bold;
}
.mermaid svg {
max-width: 100%;
height: auto;
}
@media (prefers-color-scheme: dark) {
.mermaid {
background-color: var(--code-bg-dark, #2d2d2d);
}
}

72
public/css/skills.css Normal file
View file

@ -0,0 +1,72 @@
/* Basic Layout for Skills List */
#skills-content {
margin: 2rem 0;
}
#skills-content .category {
margin-bottom: 3rem;
}
#skills-content .category h3 {
font-size: 1.5rem;
font-weight: regular;
margin-bottom: 1rem;
}
/* Skills container for single-line display */
#skills-content .skills-list {
list-style: none;
padding: 0;
margin: 0;
display: flex; /* Use flexbox for single-line display */
flex-wrap: wrap; /* Allow skills to wrap if needed */
gap: 1rem; /* Spacing between skills */
}
/* Skill List Item */
#skills-content .skills-list .skill {
display: inline-flex; /* Ensures each skill is aligned inline */
align-items: center;
position: relative;
cursor: pointer; /* Show pointer cursor on hover */
text-decoration: none; /* Remove underline from links */
color: inherit; /* Inherit color from parent */
transition: background-color 0.3s, color 0.3s;
}
/* Skill Name */
#skills-content .skills-list .skill span {
display: inline-block;
font-size: 1.2rem;
margin-left: 0.5rem;
line-height: 1.4;
}
/* Skill Icon */
#skills-content .skills-list .skill .skill-icon {
margin-right: 0.5rem;
color: var(--accent-color);
font-size: 1.4rem;
vertical-align: middle;
}
/* Hover Effects for Skills */
#skills-content .skills-list .skill:hover::before {
background: var(--accent-color-dark);
}
#skills-content .skills-list .skill:hover {
background-color: var(--accent-color-alpha);
color: var(--fg-color); /* Change text color on hover */
}
/* Skill Category Hover Effect */
#skills-content .skills-list .skill:hover span {
color: var(--fg-color);
}
/* Optional: Customize Icon Color on Hover */
#skills-content .skills-list .skill:hover .skill-icon {
color: var(--fg-color);
}

91
public/css/timeline.css Normal file
View file

@ -0,0 +1,91 @@
/* Basic Layout */
#timeline-content {
position: relative;
margin: 2rem 0;
padding-left: 120px;
}
#timeline-content ul.timeline {
list-style: none;
padding: 0;
margin: 0;
position: relative;
}
#timeline-content ul.timeline::before {
content: '';
position: absolute;
left: -30px;
top: 0;
bottom: 0;
width: 2px;
background: var(--accent-color);
}
/* Event List Item */
#timeline-content li.event {
position: relative;
margin-bottom: 3rem;
}
/* Event Circle */
#timeline-content li.event::before {
content: '';
position: absolute;
left: -39px;
top: 5px;
width: 16px;
height: 16px;
border-radius: 50%;
background: var(--bg-color);
border: 2px solid white;
z-index: 1;
}
/* From + To Label — styled exactly like old date label */
#timeline-content li.event::after {
content: attr(data-from) "\A" attr(data-to);
white-space: pre; /* ensures newline works */
position: absolute;
left: -160px;
width: 100px;
text-align: right;
color: var(--fg-color);
font-weight: bold;
font-size: 0.9rem;
line-height: 1.3;
top: 0;
}
/* Event Heading */
#timeline-content li.event h3 {
margin: 0 0 0.5rem 0;
display: inline-block;
}
/* Event Description */
#timeline-content li.event p {
margin: 0;
}
/* Timeline Icon */
#timeline-content .timeline-icon {
margin-right: 0.5rem;
color: var(--accent-color);
font-size: 1.2rem;
vertical-align: middle;
}
/* Hover Effects */
#timeline-content li.event:hover::before {
background: var(--accent-color);
}
#timeline-content li.event:hover {
background-color: var(--accent-color-alpha);
}
/* From + To Label Hover Effect */
#timeline-content li.event:hover::after {
color: var(--accent-color);
}

10
public/de/atom.xml Normal file
View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="de">
<title>Studio UMZU</title>
<subtitle>Spielerisches Lernen mit Technikdidaktik</subtitle>
<link rel="self" type="application/atom+xml" href="https://studio-umzu.de/de/atom.xml"/>
<link rel="alternate" type="text/html" href="https://studio-umzu.de/"/>
<generator uri="https://www.getzola.org/">Zola</generator>
<id>https://studio-umzu.de/de/atom.xml</id>
</feed>

2
public/de/index.html Normal file

File diff suppressed because one or more lines are too long

BIN
public/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 548 B

1
public/fonts.css Normal file
View file

@ -0,0 +1 @@
@font-face{font-style:normal;font-weight:100 900;src:url("fonts/InterVariable.woff2") format("woff2");font-family:"Inter Variable";font-display:swap}@font-face{font-style:italic;font-weight:100 900;src:url("fonts/InterVariable-Italic.woff2") format("woff2");font-family:"Inter Variable";font-display:swap}@font-face{font-style:normal;font-weight:100 900;src:url("fonts/JetBrainsMono.woff2") format("woff2");font-family:"JetBrains Mono";font-display:swap}@font-face{font-style:italic;font-weight:100 900;src:url("fonts/JetBrainsMono-Italic.woff2") format("woff2");font-family:"JetBrains Mono";font-display:swap}body{font-family:"Inter Variable",var(--font-system-ui),var(--font-emoji)}h1,h2,h3,h4,h5,h6{font-weight:bold;font-family:"Inter Variable",var(--font-system-ui),var(--font-emoji)}h1{font-weight:900}pre,code,kbd,samp{font-family:"JetBrains Mono",var(--font-monospace-code)}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

9
public/fuse.js Normal file

File diff suppressed because one or more lines are too long

2
public/index.html Normal file

File diff suppressed because one or more lines are too long

8
public/katex-init.js Normal file
View file

@ -0,0 +1,8 @@
document.addEventListener("DOMContentLoaded", function () {
renderMathInElement(document.body, {
delimiters: [
{ left: "$$", right: "$$", display: true },
{ left: "$", right: "$", display: false },
],
});
});

1209
public/katex.css Normal file

File diff suppressed because it is too large Load diff

1
public/katex.min.js vendored Normal file

File diff suppressed because one or more lines are too long

BIN
public/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

4
public/robots.txt Normal file
View file

@ -0,0 +1,4 @@
User-agent: *
Disallow:
Allow: /
Sitemap: https://studio-umzu.de/sitemap.xml

12
public/rss.xml Normal file
View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
<channel>
<title>Studio UMZU</title>
<link>https://studio-umzu.de/</link>
<description></description>
<generator>Zola</generator>
<language>en</language>
<atom:link href="https://studio-umzu.de/rss.xml" rel="self" type="application/rss+xml"/>
</channel>
</rss>

View file

@ -0,0 +1,209 @@
// Based on https://github.com/getzola/zola/blob/1ac1231de1e342bbaf4d7a51a8a9a40ea152e246/docs/static/search.js
function debounce(func, wait) {
var timeout;
return function () {
var context = this;
var args = arguments;
clearTimeout(timeout);
timeout = setTimeout(function () {
timeout = null;
func.apply(context, args);
}, wait);
};
}
// Taken from mdbook
// The strategy is as follows:
// First, assign a value to each word in the document:
// Words that correspond to search terms (stemmer aware): 40
// Normal words: 2
// First word in a sentence: 8
// Then use a sliding window with a constant number of words and count the
// sum of the values of the words within the window. Then use the window that got the
// maximum sum. If there are multiple maximas, then get the last one.
// Enclose the terms in <b>.
function makeTeaser(body, terms) {
var TERM_WEIGHT = 40;
var NORMAL_WORD_WEIGHT = 2;
var FIRST_WORD_WEIGHT = 8;
var TEASER_MAX_WORDS = 30;
var stemmedTerms = terms.map(function (w) {
return elasticlunr.stemmer(w.toLowerCase());
});
var termFound = false;
var index = 0;
var weighted = []; // contains elements of ["word", weight, index_in_document]
// split in sentences, then words
var sentences = body.toLowerCase().split(". ");
for (var i in sentences) {
var words = sentences[i].split(" ");
var value = FIRST_WORD_WEIGHT;
for (var j in words) {
var word = words[j];
if (word.length > 0) {
for (var k in stemmedTerms) {
if (elasticlunr.stemmer(word).startsWith(stemmedTerms[k])) {
value = TERM_WEIGHT;
termFound = true;
}
}
weighted.push([word, value, index]);
value = NORMAL_WORD_WEIGHT;
}
index += word.length;
index += 1; // ' ' or '.' if last word in sentence
}
index += 1; // because we split at a two-char boundary '. '
}
if (weighted.length === 0) {
return body;
}
var windowWeights = [];
var windowSize = Math.min(weighted.length, TEASER_MAX_WORDS);
// We add a window with all the weights first
var curSum = 0;
for (var i = 0; i < windowSize; i++) {
curSum += weighted[i][1];
}
windowWeights.push(curSum);
for (var i = 0; i < weighted.length - windowSize; i++) {
curSum -= weighted[i][1];
curSum += weighted[i + windowSize][1];
windowWeights.push(curSum);
}
// If we didn't find the term, just pick the first window
var maxSumIndex = 0;
if (termFound) {
var maxFound = 0;
// backwards
for (var i = windowWeights.length - 1; i >= 0; i--) {
if (windowWeights[i] > maxFound) {
maxFound = windowWeights[i];
maxSumIndex = i;
}
}
}
var teaser = [];
var startIndex = weighted[maxSumIndex][2];
for (var i = maxSumIndex; i < maxSumIndex + windowSize; i++) {
var word = weighted[i];
if (startIndex < word[2]) {
// missing text from index to start of `word`
teaser.push(body.substring(startIndex, word[2]));
startIndex = word[2];
}
// add <strong> around search terms
if (word[1] === TERM_WEIGHT) {
teaser.push("<strong>");
}
startIndex = word[2] + word[0].length;
teaser.push(body.substring(word[2], startIndex));
if (word[1] === TERM_WEIGHT) {
teaser.push("</strong>");
}
}
teaser.push("…");
return teaser.join("");
}
function formatSearchResultItem(item, terms) {
return '<div class="item">'
+ `<a href="${item.ref}">${item.doc.title}</a>`
+ `<span>${makeTeaser(item.doc.body, terms)}</span>`
+ '</div>';
}
function initSearch() {
var searchBar = document.getElementById("search-bar");
var searchContainer = document.getElementById("search-container");
var searchResults = document.getElementById("search-results");
var MAX_ITEMS = 10;
var options = {
bool: "AND",
fields: {
title: { boost: 2 },
body: { boost: 1 },
}
};
var currentTerm = "";
var index;
var initIndex = async function () {
if (index === undefined) {
let searchIndex = document.getElementById("search-index").textContent;
index = fetch(searchIndex)
.then(
async function (response) {
return await elasticlunr.Index.load(await response.json());
}
);
}
let res = await index;
return res;
}
searchBar.addEventListener("keyup", debounce(async function () {
var term = searchBar.value.trim();
if (term === currentTerm) {
return;
}
searchResults.style.display = term === "" ? "none" : "flex";
searchResults.innerHTML = "";
currentTerm = term;
if (term === "") {
return;
}
var results = (await initIndex()).search(term, options);
if (results.length === 0) {
searchResults.style.display = "none";
return;
}
for (var i = 0; i < Math.min(results.length, MAX_ITEMS); i++) {
searchResults.innerHTML += formatSearchResultItem(results[i], term.split(" "));
}
}, 150));
document.addEventListener("keydown", function (event) {
if (event.key === "/") {
event.preventDefault();
toggleSearch();
}
});
document.getElementById("search-toggle").addEventListener("click", toggleSearch);
}
function toggleSearch() {
var searchContainer = document.getElementById("search-container");
var searchBar = document.getElementById("search-bar");
searchContainer.classList.toggle("active");
searchBar.toggleAttribute("disabled");
searchBar.focus();
}
if (document.readyState === "complete" ||
(document.readyState !== "loading" && !document.documentElement.doScroll)
) {
initSearch();
} else {
document.addEventListener("DOMContentLoaded", initSearch);
}

127
public/search-fuse.js Normal file
View file

@ -0,0 +1,127 @@
// Based on https://codeberg.org/daudix/duckquill/issues/101#issuecomment-2377169
let searchSetup = false;
let fuse;
async function initIndex() {
if (searchSetup) return;
const url = document.getElementById("search-index").textContent;
const response = await fetch(url);
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
const options = {
includeScore: false,
includeMatches: true,
ignoreLocation: true,
threshold: 0.15,
keys: [
{ name: "title", weight: 3 },
{ name: "description", weight: 2 },
{ name: "body", weight: 1 }
]
};
fuse = new Fuse(await response.json(), options);
searchSetup = true;
console.log("Search index initialized successfully");
}
function toggleSearch() {
initIndex();
const searchBar = document.getElementById("search-bar");
const searchContainer = document.getElementById("search-container");
const searchResults = document.getElementById("search-results");
searchContainer.classList.toggle("active");
searchBar.toggleAttribute("disabled");
searchBar.focus();
}
function debounce(actual_fn, wait) {
let timeoutId;
return (...args) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
actual_fn(...args);
}, wait);
};
};
function initSearch() {
const searchBar = document.getElementById("search-bar");
const searchResults = document.getElementById("search-results");
const searchContainer = document.getElementById("search-container");
const MAX_ITEMS = 10;
const MAX_RESULTS = 4;
let currentTerm = "";
searchBar.addEventListener("keyup", (e) => {
const searchVal = searchBar.value.trim();
const results = fuse.search(searchVal, { limit: MAX_ITEMS });
let html = "";
for (const result of results) {
html += makeTeaser(result, searchVal);
}
searchResults.innerHTML = html;
if (html) {
searchResults.style.display = "flex";
} else {
searchResults.style.display = "none";
}
});
function makeTeaser(result, searchVal) {
const TEASER_SIZE = 20;
let output = `<div class="search-result item"><a class="result-title" href=${result.item.url}>${result.item.title}</a>`;
for (const match of result.matches) {
if (match.key === "title") continue;
const indices = match.indices.sort((a, b) => Math.abs(a[1] - a[0] - searchVal.length) - Math.abs(b[1] - b[0] - searchVal.length)).slice(0, MAX_RESULTS);
const value = match.value;
for (const ind of indices) {
const start = Math.max(0, ind[0] - TEASER_SIZE);
const end = Math.min(value.length - 1, ind[1] + TEASER_SIZE);
output += "<span>"
+ value.substring(start, ind[0])
+ `<strong>${value.substring(ind[0], ind[1] + 1)}</strong>`
+ value.substring(ind[1] + 1, end)
+ "</span>";
}
if (match.indices.length > 4) {
const moreMatchesText = document.getElementById("more-matches-text").textContent;
output += `<span class="more-matches">${moreMatchesText}</span>`.replace("$MATCHES", `+${match.indices.length - MAX_RESULTS}`);
}
}
return output + "</div>";
}
/*window.addEventListener("click", function (event) {
if (searchSetup && searchBar.getAttribute("disabled") === null && !searchContainer.contains(event.target)) {
toggleSearch();
}
}, { passive: true });*/
document.addEventListener("keydown", function(event) {
if (event.key === "/") {
event.preventDefault();
toggleSearch();
}
});
document.getElementById("search-toggle").addEventListener("click", toggleSearch);
}
if (document.readyState === "complete" ||
(document.readyState !== "loading" && !document.documentElement.doScroll))
initSearch();
else
document.addEventListener("DOMContentLoaded", initSearch);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

15
public/sitemap.xml Normal file
View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://studio-umzu.de/</loc>
</url>
<url>
<loc>https://studio-umzu.de/de/</loc>
</url>
<url>
<loc>https://studio-umzu.de/de/tags/</loc>
</url>
<url>
<loc>https://studio-umzu.de/tags/</loc>
</url>
</urlset>

5
public/style.css Normal file

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,283 @@
/*
* theme "Solarized (dark)" generated by syntect
*/
.z-code {
color: #839496;
background-color: #002b36;
}
.z-comment, .z-meta.z-documentation {
color: #586e75;
}
.z-string {
color: #2aa198;
}
.z-string.z-regexp {
color: #2aa198;
}
.z-constant.z-character.z-escape {
color: #dc322f;
}
.z-constant.z-numeric {
color: #6c71c4;
}
.z-variable {
color: #268bd2;
}
.z-variable.z-function {
color: #b58900;
}
.z-variable.z-language {
color: #d33682;
}
.z-keyword {
color: #859900;
}
.z-meta.z-import .z-keyword, .z-keyword.z-control.z-import, .z-keyword.z-control.z-import.z-from, .z-keyword.z-other.z-import, .z-keyword.z-control.z-at-rule.z-include, .z-keyword.z-control.z-at-rule.z-import {
color: #cb4b16;
}
.z-keyword.z-operator.z-comparison, .z-keyword.z-operator.z-assignment, .z-keyword.z-operator.z-arithmetic {
color: #657b83;
}
.z-storage {
color: #859900;
}
.z-storage.z-modifier {
color: #93a1a1;
}
.z-keyword.z-control.z-class, .z-entity.z-name, .z-entity.z-name.z-class, .z-entity.z-name.z-type.z-class {
color: #b58900;
}
.z-entity.z-other.z-inherited-class {
color: #268bd2;
}
.z-entity.z-other.z-attribute-name {
color: #b58900;
}
.z-support, .z-support.z-type, .z-support.z-class {
color: #859900;
}
.z-entity.z-name.z-function {
color: #b58900;
}
.z-punctuation.z-definition.z-variable {
color: #859900;
}
.z-constant, .z-constant.z-language, .z-meta.z-preprocessor {
color: #b58900;
}
.z-entity.z-name.z-section {
color: #cb4b16;
}
.z-support.z-function.z-construct, .z-keyword.z-other.z-new {
color: #dc322f;
}
.z-constant.z-character, .z-constant.z-other {
color: #cb4b16;
}
.z-entity.z-name.z-tag {
color: #268bd2;
}
.z-punctuation.z-definition.z-tag.z-html, .z-punctuation.z-definition.z-tag.z-begin, .z-punctuation.z-definition.z-tag.z-end {
color: #586e75;
}
.z-support.z-function {
color: #859900;
}
.z-punctuation.z-separator.z-continuation {
color: #dc322f;
}
.z-storage.z-type {
color: #268bd2;
}
.z-support.z-type.z-exception {
color: #cb4b16;
}
.z-keyword.z-other.z-special-method {
color: #cb4b16;
}
.z-invalid {
background-color: #6e2e32;
}
.z-string.z-quoted.z-double, .z-string.z-quoted.z-single {
color: #2aa198;
}
.z-punctuation.z-definition.z-string {
color: #839496;
}
.z-meta.z-brace.z-square, .z-punctuation.z-section.z-brackets {
color: #268bd2;
}
.z-meta.z-brace.z-round, .z-meta.z-brace.z-curly, .z-punctuation.z-section, .z-punctuation.z-section.z-block, .z-punctuation.z-definition.z-parameters, .z-punctuation.z-section.z-group {
color: #657b83;
}
.z-support.z-constant.z-color, .z-invalid.z-deprecated.z-color.z-w3c-non-standard-color-name.z-scss {
color: #b58900;
}
.z-meta.z-selector.z-css {
color: #657b83;
}
.z-entity.z-name.z-tag.z-css, .z-entity.z-name.z-tag.z-scss, .z-source.z-less .z-keyword.z-control.z-html.z-elements, .z-source.z-sass .z-keyword.z-control.z-untitled {
color: #b58900;
}
.z-entity.z-other.z-attribute-name.z-class {
color: #b58900;
}
.z-entity.z-other.z-attribute-name.z-id {
color: #b58900;
}
.z-entity.z-other.z-attribute-name.z-pseudo-element, .z-entity.z-other.z-attribute-name.z-tag.z-pseudo-element, .z-entity.z-other.z-attribute-name.z-pseudo-class, .z-entity.z-other.z-attribute-name.z-tag.z-pseudo-class {
color: #268bd2;
}
.z-text.z-html.z-basic .z-meta.z-tag.z-other.z-html, .z-text.z-html.z-basic .z-meta.z-tag.z-any.z-html, .z-text.z-html.z-basic .z-meta.z-tag.z-block.z-any, .z-text.z-html.z-basic .z-meta.z-tag.z-inline.z-any, .z-text.z-html.z-basic .z-meta.z-tag.z-structure.z-any.z-html, .z-text.z-html.z-basic .z-source.z-js.z-embedded.z-html, .z-punctuation.z-separator.z-key-value.z-html {
color: #657b83;
}
.z-text.z-html.z-basic .z-entity.z-other.z-attribute-name.z-html, .z-meta.z-tag.z-xml .z-entity.z-other.z-attribute-name {
color: #b58900;
}
.z-keyword.z-other.z-special-method.z-ruby {
color: #859900;
}
.z-variable.z-other.z-constant.z-ruby {
color: #b58900;
}
.z-constant.z-other.z-symbol.z-ruby {
color: #2aa198;
}
.z-keyword.z-other.z-special-method.z-ruby {
color: #cb4b16;
}
.z-meta.z-array .z-support.z-function.z-construct.z-php {
color: #b58900;
}
.z-entity.z-name.z-function.z-preprocessor.z-c, .z-meta.z-preprocessor.z-c.z-include, .z-meta.z-preprocessor.z-macro.z-c {
color: #cb4b16;
}
.z-meta.z-preprocessor.z-c.z-include .z-string.z-quoted.z-other.z-lt-gt.z-include.z-c, .z-meta.z-preprocessor.z-c.z-include .z-punctuation.z-definition.z-string.z-begin.z-c, .z-meta.z-preprocessor.z-c.z-include .z-punctuation.z-definition.z-string.z-end.z-c {
color: #2aa198;
}
.z-other.z-package.z-exclude, .z-other.z-remove {
color: #dc322f;
}
.z-other.z-add {
color: #2aa198;
}
.z-punctuation.z-section.z-group.z-tex, .z-punctuation.z-definition.z-arguments.z-begin.z-latex, .z-punctuation.z-definition.z-arguments.z-end.z-latex, .z-punctuation.z-definition.z-arguments.z-latex {
color: #dc322f;
}
.z-meta.z-group.z-braces.z-tex {
color: #b58900;
}
.z-string.z-other.z-math.z-tex {
color: #b58900;
}
.z-variable.z-parameter.z-function.z-latex {
color: #cb4b16;
}
.z-punctuation.z-definition.z-constant.z-math.z-tex {
color: #dc322f;
}
.z-text.z-tex.z-latex .z-constant.z-other.z-math.z-tex, .z-constant.z-other.z-general.z-math.z-tex, .z-constant.z-other.z-general.z-math.z-tex, .z-constant.z-character.z-math.z-tex {
color: #2aa198;
}
.z-string.z-other.z-math.z-tex {
color: #b58900;
}
.z-punctuation.z-definition.z-string.z-begin.z-tex, .z-punctuation.z-definition.z-string.z-end.z-tex {
color: #dc322f;
}
.z-keyword.z-control.z-label.z-latex, .z-text.z-tex.z-latex .z-constant.z-other.z-general.z-math.z-tex {
color: #2aa198;
}
.z-variable.z-parameter.z-definition.z-label.z-latex {
color: #dc322f;
}
.z-support.z-function.z-be.z-latex {
color: #859900;
}
.z-support.z-function.z-section.z-latex {
color: #cb4b16;
}
.z-support.z-function.z-general.z-tex {
color: #2aa198;
}
.z-keyword.z-control.z-ref.z-latex {
color: #2aa198;
}
.z-storage.z-type.z-class.z-python, .z-storage.z-type.z-function.z-python, .z-storage.z-modifier.z-global.z-python {
color: #859900;
}
.z-support.z-type.z-exception.z-python {
color: #b58900;
}
.z-meta.z-scope.z-for-in-loop.z-shell, .z-variable.z-other.z-loop.z-shell {
color: #93a1a1;
}
.z-meta.z-scope.z-case-block.z-shell, .z-meta.z-scope.z-case-body.z-shell {
color: #93a1a1;
}
.z-punctuation.z-definition.z-logical-expression.z-shell {
color: #dc322f;
}
.z-storage.z-modifier.z-c++ {
color: #859900;
}
.z-support.z-function.z-perl {
color: #268bd2;
}
.z-meta.z-diff, .z-meta.z-diff.z-header {
color: #586e75;
}
.z-meta.z-diff.z-range {
color: #268bd2;
}
.z-markup.z-deleted {
color: #dc322f;
}
.z-markup.z-changed {
color: #b58900;
}
.z-markup.z-inserted {
color: #859900;
}
.z-markup.z-warning {
color: #b58900;
}
.z-markup.z-error {
color: #dc322f;
}
.z-markup.z-heading, .z-punctuation.z-definition.z-heading.z-markdown {
color: #b58900;
font-weight: bold;
}
.z-markup.z-quote {
color: #859900;
}
.z-markup.z-italic {
font-style: italic;
}
.z-markup.z-bold {
font-weight: bold;
}
.z-markup.z-underline.z-link.z-markdown, .z-meta.z-link.z-reference .z-constant.z-other.z-reference.z-link.z-markdown {
color: #2aa198;
}
.z-constant.z-other.z-reference.z-link.z-markdown {
color: #6c71c4;
}
.z-meta.z-paragraph.z-markdown .z-meta.z-dummy.z-line-break {
background-color: #586e75;
}
.z-brackethighlighter.z-all {
color: #586e75;
}
.z-entity.z-name.z-filename.z-find-in-files {
color: #2aa198;
}
.z-constant.z-numeric.z-line-number.z-find-in-files {
color: #586e75;
}
.z-variable.z-other.z-readwrite.z-js, .z-variable.z-other.z-object.z-js, .z-variable.z-other.z-constant.z-js {
color: #839496;
}

View file

@ -0,0 +1,283 @@
/*
* theme "Solarized (light)" generated by syntect
*/
.z-code {
color: #657b83;
background-color: #fdf6e3;
}
.z-comment, .z-meta.z-documentation {
color: #93a1a1;
}
.z-string {
color: #2aa198;
}
.z-string.z-regexp {
color: #2aa198;
}
.z-constant.z-character.z-escape {
color: #dc322f;
}
.z-constant.z-numeric {
color: #6c71c4;
}
.z-variable {
color: #268bd2;
}
.z-variable.z-function {
color: #b58900;
}
.z-variable.z-language {
color: #d33682;
}
.z-keyword {
color: #859900;
}
.z-meta.z-import .z-keyword, .z-keyword.z-control.z-import, .z-keyword.z-control.z-import.z-from, .z-keyword.z-other.z-import, .z-keyword.z-control.z-at-rule.z-include, .z-keyword.z-control.z-at-rule.z-import {
color: #cb4b16;
}
.z-keyword.z-operator.z-comparison, .z-keyword.z-operator.z-assignment, .z-keyword.z-operator.z-arithmetic {
color: #657b83;
}
.z-storage {
color: #859900;
}
.z-storage.z-modifier {
color: #586e75;
}
.z-keyword.z-control.z-class, .z-entity.z-name, .z-entity.z-name.z-class, .z-entity.z-name.z-type.z-class {
color: #b58900;
}
.z-entity.z-other.z-inherited-class {
color: #268bd2;
}
.z-entity.z-other.z-attribute-name {
color: #b58900;
}
.z-support, .z-support.z-type, .z-support.z-class {
color: #859900;
}
.z-entity.z-name.z-function {
color: #b58900;
}
.z-punctuation.z-definition.z-variable {
color: #859900;
}
.z-constant, .z-constant.z-language, .z-meta.z-preprocessor {
color: #b58900;
}
.z-entity.z-name.z-section {
color: #cb4b16;
}
.z-support.z-function.z-construct, .z-keyword.z-other.z-new {
color: #dc322f;
}
.z-constant.z-character, .z-constant.z-other {
color: #cb4b16;
}
.z-entity.z-name.z-tag {
color: #268bd2;
}
.z-punctuation.z-definition.z-tag.z-html, .z-punctuation.z-definition.z-tag.z-begin, .z-punctuation.z-definition.z-tag.z-end {
color: #93a1a1;
}
.z-support.z-function {
color: #859900;
}
.z-punctuation.z-separator.z-continuation {
color: #dc322f;
}
.z-storage.z-type {
color: #268bd2;
}
.z-support.z-type.z-exception {
color: #cb4b16;
}
.z-keyword.z-other.z-special-method {
color: #cb4b16;
}
.z-invalid {
background-color: #ec9489;
}
.z-string.z-quoted.z-double, .z-string.z-quoted.z-single {
color: #2aa198;
}
.z-punctuation.z-definition.z-string {
color: #839496;
}
.z-meta.z-brace.z-square, .z-punctuation.z-section.z-brackets {
color: #268bd2;
}
.z-meta.z-brace.z-round, .z-meta.z-brace.z-curly, .z-punctuation.z-section, .z-punctuation.z-section.z-block, .z-punctuation.z-definition.z-parameters, .z-punctuation.z-section.z-group {
color: #657b83;
}
.z-support.z-constant.z-color, .z-invalid.z-deprecated.z-color.z-w3c-non-standard-color-name.z-scss {
color: #b58900;
}
.z-meta.z-selector.z-css {
color: #657b83;
}
.z-entity.z-name.z-tag.z-css, .z-entity.z-name.z-tag.z-scss, .z-source.z-less .z-keyword.z-control.z-html.z-elements, .z-source.z-sass .z-keyword.z-control.z-untitled {
color: #b58900;
}
.z-entity.z-other.z-attribute-name.z-class {
color: #b58900;
}
.z-entity.z-other.z-attribute-name.z-id {
color: #b58900;
}
.z-entity.z-other.z-attribute-name.z-pseudo-element, .z-entity.z-other.z-attribute-name.z-tag.z-pseudo-element, .z-entity.z-other.z-attribute-name.z-pseudo-class, .z-entity.z-other.z-attribute-name.z-tag.z-pseudo-class {
color: #268bd2;
}
.z-text.z-html.z-basic .z-meta.z-tag.z-other.z-html, .z-text.z-html.z-basic .z-meta.z-tag.z-any.z-html, .z-text.z-html.z-basic .z-meta.z-tag.z-block.z-any, .z-text.z-html.z-basic .z-meta.z-tag.z-inline.z-any, .z-text.z-html.z-basic .z-meta.z-tag.z-structure.z-any.z-html, .z-text.z-html.z-basic .z-source.z-js.z-embedded.z-html, .z-punctuation.z-separator.z-key-value.z-html {
color: #657b83;
}
.z-text.z-html.z-basic .z-entity.z-other.z-attribute-name.z-html, .z-meta.z-tag.z-xml .z-entity.z-other.z-attribute-name {
color: #b58900;
}
.z-keyword.z-other.z-special-method.z-ruby {
color: #859900;
}
.z-variable.z-other.z-constant.z-ruby {
color: #b58900;
}
.z-constant.z-other.z-symbol.z-ruby {
color: #2aa198;
}
.z-keyword.z-other.z-special-method.z-ruby {
color: #cb4b16;
}
.z-meta.z-array .z-support.z-function.z-construct.z-php {
color: #b58900;
}
.z-entity.z-name.z-function.z-preprocessor.z-c, .z-meta.z-preprocessor.z-c.z-include, .z-meta.z-preprocessor.z-macro.z-c {
color: #cb4b16;
}
.z-meta.z-preprocessor.z-c.z-include .z-string.z-quoted.z-other.z-lt-gt.z-include.z-c, .z-meta.z-preprocessor.z-c.z-include .z-punctuation.z-definition.z-string.z-begin.z-c, .z-meta.z-preprocessor.z-c.z-include .z-punctuation.z-definition.z-string.z-end.z-c {
color: #2aa198;
}
.z-other.z-package.z-exclude, .z-other.z-remove {
color: #dc322f;
}
.z-other.z-add {
color: #2aa198;
}
.z-punctuation.z-section.z-group.z-tex, .z-punctuation.z-definition.z-arguments.z-begin.z-latex, .z-punctuation.z-definition.z-arguments.z-end.z-latex, .z-punctuation.z-definition.z-arguments.z-latex {
color: #dc322f;
}
.z-meta.z-group.z-braces.z-tex {
color: #b58900;
}
.z-string.z-other.z-math.z-tex {
color: #b58900;
}
.z-variable.z-parameter.z-function.z-latex {
color: #cb4b16;
}
.z-punctuation.z-definition.z-constant.z-math.z-tex {
color: #dc322f;
}
.z-text.z-tex.z-latex .z-constant.z-other.z-math.z-tex, .z-constant.z-other.z-general.z-math.z-tex, .z-constant.z-other.z-general.z-math.z-tex, .z-constant.z-character.z-math.z-tex {
color: #2aa198;
}
.z-string.z-other.z-math.z-tex {
color: #b58900;
}
.z-punctuation.z-definition.z-string.z-begin.z-tex, .z-punctuation.z-definition.z-string.z-end.z-tex {
color: #dc322f;
}
.z-keyword.z-control.z-label.z-latex, .z-text.z-tex.z-latex .z-constant.z-other.z-general.z-math.z-tex {
color: #2aa198;
}
.z-variable.z-parameter.z-definition.z-label.z-latex {
color: #dc322f;
}
.z-support.z-function.z-be.z-latex {
color: #859900;
}
.z-support.z-function.z-section.z-latex {
color: #cb4b16;
}
.z-support.z-function.z-general.z-tex {
color: #2aa198;
}
.z-keyword.z-control.z-ref.z-latex {
color: #2aa198;
}
.z-storage.z-type.z-class.z-python, .z-storage.z-type.z-function.z-python, .z-storage.z-modifier.z-global.z-python {
color: #859900;
}
.z-support.z-type.z-exception.z-python {
color: #b58900;
}
.z-meta.z-scope.z-for-in-loop.z-shell, .z-variable.z-other.z-loop.z-shell {
color: #586e75;
}
.z-meta.z-scope.z-case-block.z-shell, .z-meta.z-scope.z-case-body.z-shell {
color: #586e75;
}
.z-punctuation.z-definition.z-logical-expression.z-shell {
color: #dc322f;
}
.z-storage.z-modifier.z-c++ {
color: #859900;
}
.z-support.z-function.z-perl {
color: #268bd2;
}
.z-meta.z-diff, .z-meta.z-diff.z-header {
color: #93a1a1;
}
.z-meta.z-diff.z-range {
color: #268bd2;
}
.z-markup.z-deleted {
color: #dc322f;
}
.z-markup.z-changed {
color: #b58900;
}
.z-markup.z-inserted {
color: #859900;
}
.z-markup.z-warning {
color: #b58900;
}
.z-markup.z-error {
color: #dc322f;
}
.z-markup.z-heading, .z-punctuation.z-definition.z-heading.z-markdown {
color: #b58900;
font-weight: bold;
}
.z-markup.z-quote {
color: #859900;
}
.z-markup.z-italic {
font-style: italic;
}
.z-markup.z-bold {
font-weight: bold;
}
.z-markup.z-underline.z-link.z-markdown, .z-meta.z-link.z-reference .z-constant.z-other.z-reference.z-link.z-markdown {
color: #2aa198;
}
.z-constant.z-other.z-reference.z-link.z-markdown {
color: #6c71c4;
}
.z-meta.z-paragraph.z-markdown .z-meta.z-dummy.z-line-break {
background-color: #eee8d5;
}
.z-brackethighlighter.z-all {
color: #93a1a1;
}
.z-entity.z-name.z-filename.z-find-in-files {
color: #2aa198;
}
.z-constant.z-numeric.z-line-number.z-find-in-files {
color: #93a1a1;
}
.z-variable.z-other.z-readwrite.z-js, .z-variable.z-other.z-object.z-js, .z-variable.z-other.z-constant.z-js {
color: #657b83;
}

104
public/theme-switcher.js Normal file
View file

@ -0,0 +1,104 @@
// Theme Initialization
(function () {
// Get the default theme from the HTML data-theme attribute.
const defaultTheme = document.documentElement.getAttribute("data-theme");
// Set the data-default-theme attribute only if defaultTheme is not null.
if (defaultTheme) {
document.documentElement.setAttribute("data-default-theme", defaultTheme);
}
// Attempt to retrieve the current theme from the browser's local storage.
const storedTheme = localStorage.getItem("theme");
if (storedTheme && storedTheme !== "system") {
document.documentElement.setAttribute("data-theme", storedTheme);
} else if (defaultTheme && storedTheme !== "system") {
document.documentElement.setAttribute("data-theme", defaultTheme);
} else {
// If no theme is found in local storage and no default theme is set, hand over control to the CSS.
document.documentElement.removeAttribute("data-theme");
}
// Expose defaultTheme to the outer scope.
window.defaultTheme = defaultTheme;
})();
// Icon Update and Theme Switching
function setTheme(theme, saveToLocalStorage = false) {
if (theme === "system") {
document.documentElement.removeAttribute("data-theme");
} else {
document.documentElement.setAttribute("data-theme", theme);
}
if (saveToLocalStorage) {
localStorage.setItem("theme", theme);
} else {
localStorage.removeItem("theme");
}
// Update icon class based on the selected theme.
updateIconClass(theme);
// Update the active button based on the selected theme.
updateActiveButton(theme);
}
function resetTheme() {
// Reset the theme to the default or system preference if no default is set.
setTheme(window.defaultTheme || "system");
}
function switchTheme(theme) {
if (theme === "system") {
resetTheme();
} else {
setTheme(theme, true);
}
}
function updateIconClass(theme) {
const iconElement = document.querySelector("#theme-switcher summary .icon");
// Remove any existing theme classes
iconElement.classList.remove("light", "dark");
// Add the appropriate class based on the selected theme
if (theme === "light") {
iconElement.classList.add("light");
} else if (theme === "dark") {
iconElement.classList.add("dark");
}
}
function updateActiveButton(theme) {
// Remove .active class from all buttons
document.querySelectorAll('#theme-switcher button').forEach(button => {
button.classList.remove('active');
});
// Add .active class to the button corresponding to the current theme
const activeButton = document.querySelector(`#theme-${theme}`);
if (activeButton) {
activeButton.classList.add('active');
}
}
document.getElementById("theme-light").addEventListener("click", function () {
switchTheme("light");
});
document.getElementById("theme-dark").addEventListener("click", function () {
switchTheme("dark");
});
document.getElementById("theme-system").addEventListener("click", function () {
switchTheme("system");
});
// Update icon class on page load based on current theme
const currentTheme = localStorage.getItem("theme") || window.defaultTheme || "system";
updateIconClass(currentTheme);
updateActiveButton(currentTheme);
// Make the switchTheme function accessible globally
window.switchTheme = switchTheme;

81
sass/_alerts.scss Normal file
View file

@ -0,0 +1,81 @@
blockquote {
&.note {
border-inline-start: 0.25rem solid var(--blue-fg);
.alert-title,
li::marker {
color: var(--blue-fg);
}
.alert-title .icon {
-webkit-mask-image: var(--icon-info);
mask-image: var(--icon-info);
}
}
&.tip {
border-inline-start: 0.25rem solid var(--green-fg);
.alert-title,
li::marker {
color: var(--green-fg);
}
.alert-title .icon {
-webkit-mask-image: var(--icon-lightbulb);
mask-image: var(--icon-lightbulb);
}
}
&.important {
border-inline-start: 0.25rem solid var(--purple-fg);
.alert-title,
li::marker {
color: var(--purple-fg);
}
.alert-title .icon {
-webkit-mask-image: var(--icon-important);
mask-image: var(--icon-important);
}
}
&.warning {
border-inline-start: 0.25rem solid var(--yellow-fg);
.alert-title,
li::marker {
color: var(--yellow-fg);
}
.alert-title .icon {
-webkit-mask-image: var(--icon-warning);
mask-image: var(--icon-warning);
}
}
&.caution {
border-inline-start: 0.25rem solid var(--red-fg);
.alert-title,
li::marker {
color: var(--red-fg);
}
.alert-title .icon {
-webkit-mask-image: var(--icon-caution);
mask-image: var(--icon-caution);
}
}
.alert-title {
margin-block-end: -0.75rem;
font-weight: bold;
.icon {
vertical-align: -0.125em;
margin-inline-end: 0.25rem;
}
}
}

445
sass/_article-list.scss Normal file
View file

@ -0,0 +1,445 @@
#article-list {
display: flex;
flex-direction: column;
gap: 1rem;
margin-block-start: 2rem;
article {
--bg-overlay: var(--accent-color-alpha);
position: relative;
transition: var(--transition);
box-shadow: var(--edge-highlight), var(--shadow-glass);
border-radius: var(--rounded-corner);
border-start-end-radius: 2.125rem;
border-end-end-radius: 1.8125rem;
background-image: linear-gradient(var(--bg-overlay), var(--bg-overlay)),
linear-gradient(var(--glass-bg), var(--glass-bg)), var(--blurnail);
background-position: center;
background-size: cover;
background-color: var(--bg-overlay);
padding: 1rem;
overflow: hidden;
&:hover {
h3::after {
transform: none;
opacity: 1;
:root[dir*="rtl"] & {
transform: scaleX(-1);
}
}
}
&:active:not(:has(.tag:active)) {
transform: var(--active);
}
&:has(> a:focus-visible) {
animation: focus-in var(--transition);
outline: 0.125rem solid var(--accent-color);
outline-offset: 0.125rem;
}
@supports not selector(:focus-visible) {
&:has(> a:focus) {
animation: focus-in var(--transition);
outline: 0.125rem solid var(--accent-color);
outline-offset: 0.125rem;
}
}
& > a {
position: absolute;
inset: 0;
border-radius: var(--rounded-corner);
border-start-end-radius: 2.125rem;
border-end-end-radius: 1.8125rem;
}
h3 {
margin: 0;
color: var(--accent-color);
font-weight: bold;
line-height: 1;
font-family: var(--font-system-ui);
&::after {
-webkit-mask-image: var(--icon-right);
display: inline-block;
position: relative;
transform: translateX(-0.25rem);
opacity: 0;
mask-image: var(--icon-right);
transition: var(--transition);
margin-inline-start: 0.25rem;
background-color: currentColor;
width: 1rem;
height: 1rem;
pointer-events: none;
content: "";
:root[dir*="rtl"] & {
transform: scaleX(-1) translateX(-0.25rem);
}
}
}
.tags {
position: relative;
justify-content: flex-end;
z-index: 1;
margin: 0;
a {
background-color: var(--accent-color-alpha);
color: var(--accent-color);
&:hover {
background-color: var(--accent-color);
color: var(--contrast-color);
}
}
}
&.draft,
&.archive,
&.featured,
&.hot,
&.poor {
&::before {
position: absolute;
transform: translateY(-50%);
opacity: var(--disabled-opacity);
mask-size: cover;
transition: var(--transition-longer);
inset-block-start: 50%;
inset-inline-end: -3rem;
width: 12rem;
height: 12rem;
content: "";
}
&:hover::before {
transform: translateY(-50%) rotate(-10deg) scale(1.5);
}
:root[dir*="rtl"] &:hover::before {
transform: translateY(-50%) rotate(10deg) scale(1.5);
}
}
&.draft {
--bg-overlay: var(--fg-muted-1);
&::before {
-webkit-mask-image: var(--icon-pencil);
mask-image: var(--icon-pencil);
background-color: var(--fg-muted-1);
}
h3 {
color: var(--fg-muted-4);
}
.badge {
background-color: var(--fg-muted-1);
color: var(--fg-muted-5);
.icon {
-webkit-mask-image: var(--icon-pencil);
mask-image: var(--icon-pencil);
}
}
.tags a {
background-color: var(--fg-muted-1);
color: var(--fg-muted-5);
&:hover {
background-color: var(--fg-muted-5);
color: var(--fg-contrast);
}
}
}
&.archive {
--bg-overlay: var(--purple-bg);
&::before {
-webkit-mask-image: var(--icon-archive);
mask-image: var(--icon-archive);
background-color: var(--purple-bg);
}
h3 {
color: var(--purple-fg);
}
.badge {
background-color: var(--purple-bg);
color: var(--purple-fg);
.icon {
-webkit-mask-image: var(--icon-archive);
mask-image: var(--icon-archive);
}
}
.tags a {
background-color: var(--purple-bg);
color: var(--purple-fg);
&:hover {
background-color: var(--purple-fg);
color: var(--fg-contrast);
}
}
}
&.featured {
--bg-overlay: var(--yellow-bg);
&::before {
-webkit-mask-image: var(--icon-star);
mask-image: var(--icon-star);
background-color: var(--yellow-bg);
}
&:hover {
&::before {
transform: translateY(-50%) rotate(62deg) scale(1.5);
}
:root[dir*="rtl"] &::before {
transform: translateY(-50%) rotate(-62deg) scale(1.5);
}
}
h3 {
color: var(--yellow-fg);
}
.badge {
background-color: var(--yellow-bg);
color: var(--yellow-fg);
.icon {
-webkit-mask-image: var(--icon-star);
mask-image: var(--icon-star);
}
}
.tags a {
background-color: var(--yellow-bg);
color: var(--yellow-fg);
&:hover {
background-color: var(--yellow-fg);
color: var(--fg-contrast);
}
}
}
&.hot {
--bg-overlay: var(--red-bg);
&::before {
-webkit-mask-image: var(--icon-fire);
mask-image: var(--icon-fire);
background-color: var(--red-bg);
}
h3 {
color: var(--red-fg);
}
.badge {
background-color: var(--red-bg);
color: var(--red-fg);
.icon {
-webkit-mask-image: var(--icon-fire);
mask-image: var(--icon-fire);
}
}
.tags a {
background-color: var(--red-bg);
color: var(--red-fg);
&:hover {
background-color: var(--red-fg);
color: var(--fg-contrast);
}
}
}
&.poor {
--bg-overlay: var(--brown-bg);
&::before {
-webkit-mask-image: var(--icon-poop);
mask-image: var(--icon-poop);
background-color: var(--brown-bg);
}
h3 {
color: var(--brown-fg);
}
.badge {
background-color: var(--brown-bg);
color: var(--brown-fg);
.icon {
-webkit-mask-image: var(--icon-poop);
mask-image: var(--icon-poop);
}
}
.tags a {
background-color: var(--brown-bg);
color: var(--brown-fg);
&:hover {
background-color: var(--brown-fg);
color: var(--fg-contrast);
}
}
}
.badge {
float: inline-end;
box-shadow: var(--edge-highlight);
border-radius: 999px;
padding: 0.375rem 0.75rem;
height: fit-content;
font-weight: bold;
white-space: nowrap;
.icon {
vertical-align: -0.125em;
margin-inline-end: 0.25rem;
}
}
.details {
display: flex;
flex-wrap: nowrap;
justify-content: space-between;
align-items: center;
gap: 0.25rem;
margin-block-start: 1rem;
border-block-start: max(1px, 0.0625rem) solid var(--fg-muted-2);
padding-block-start: 0.5rem;
}
}
}
#paginator {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
gap: 0.25rem;
margin-block-start: 4rem;
#paginator-first,
#paginator-previous,
#paginator-next,
#paginator-last {
display: inline-block;
transition: var(--transition);
box-shadow: var(--edge-highlight);
border-radius: 1rem;
background-color: var(--fg-muted-1);
padding: 0.5rem;
color: var(--fg-muted-4);
line-height: 0;
.icon {
transition: var(--transition);
:root[dir*="rtl"] & {
transform: scaleX(-1);
}
}
}
a#paginator-first,
a#paginator-previous,
a#paginator-next,
a#paginator-last {
&:hover {
background-color: var(--fg-muted-2);
color: var(--fg-muted-5);
}
&:active {
transform: var(--active);
border-radius: 1rem;
}
}
span#paginator-first,
span#paginator-previous,
span#paginator-next,
span#paginator-last {
opacity: var(--disabled-opacity);
cursor: not-allowed;
}
#paginator-previous {
border-start-end-radius: var(--rounded-corner-small);
border-end-end-radius: var(--rounded-corner-small);
}
#paginator-next {
border-start-start-radius: var(--rounded-corner-small);
border-end-start-radius: var(--rounded-corner-small);
}
#paginator-first .icon {
-webkit-mask-image: var(--icon-first);
mask-image: var(--icon-first);
}
#paginator-previous .icon {
-webkit-mask-image: var(--icon-previous);
mask-image: var(--icon-previous);
}
#paginator-next .icon {
-webkit-mask-image: var(--icon-next);
mask-image: var(--icon-next);
}
#paginator-last .icon {
-webkit-mask-image: var(--icon-last);
mask-image: var(--icon-last);
}
#paginator-counter {
display: inline-block;
transition: var(--transition);
box-shadow: var(--edge-highlight);
border-radius: var(--rounded-corner-small);
background-color: var(--accent-color-alpha);
padding: 0.5rem 0.625rem;
color: var(--accent-color);
font-weight: bold;
line-height: 1;
font-variant-numeric: tabular-nums;
}
&:has(a#paginator-previous:active) #paginator-counter {
border-start-start-radius: 1rem;
border-end-start-radius: 1rem;
}
&:has(a#paginator-next:active) #paginator-counter {
border-start-end-radius: 1rem;
border-end-end-radius: 1rem;
}
}

248
sass/_article.scss Normal file
View file

@ -0,0 +1,248 @@
#banner-container {
--mask: linear-gradient(black, transparent);
-webkit-user-select: none;
-webkit-mask-image: var(--mask);
position: absolute;
z-index: -1;
mask-image: var(--mask);
inset-block-start: 0;
inset-inline-start: 0;
aspect-ratio: 2 / 1;
width: 100%;
user-select: none;
#banner {
position: fixed;
transition: none;
margin: 0;
inset-block-start: 0;
inset-inline-start: 0;
}
& + #heading {
margin-block-start: calc(50vw - 7rem);
}
@media only screen and (max-width: 480px) {
body:has(&) #site-nav:not(#handle + #site-nav) {
margin-block-start: calc(50vw + 1rem);
}
}
}
#heading {
margin: 2rem 0 1rem;
text-align: center;
h1 {
-webkit-background-clip: text;
margin: 0;
background-image: linear-gradient(
to right,
var(--fg-muted-4),
var(--fg-color),
var(--fg-muted-4)
);
background-clip: text;
color: transparent;
& + p {
display: inline;
}
}
.tags {
display: inline-flex;
justify-content: center;
margin-block-start: 1rem;
}
}
#buttons-container {
display: flex;
position: fixed;
flex-direction: column;
gap: 0.5rem;
inset-block-end: 1rem;
inset-inline-end: 1rem;
@media only screen and (max-width: 720px) {
position: static;
flex-direction: row-reverse;
// justify-content: center;
margin-block-start: 2rem;
}
summary,
#go-to-top,
#share,
#issue {
display: inline-block;
transition: var(--transition);
box-shadow: var(--edge-highlight);
border-radius: 999px;
background-color: var(--fg-muted-1);
padding: 0.5rem;
color: var(--fg-muted-4);
line-height: 0;
&:hover {
background-color: var(--fg-muted-2);
color: var(--fg-muted-5);
}
&:active {
transform: var(--active);
}
.icon {
transition: var(--transition);
}
}
details {
position: relative;
box-shadow: none;
border-radius: 0;
background-color: transparent;
padding: 0;
&[open] summary ~ * {
transform-origin: bottom right;
animation: button-dropdown-open var(--transition);
:root[dir*="rtl"] & {
transform-origin: bottom left;
animation: button-dropdown-open-rtl var(--transition);
}
@keyframes button-dropdown-open {
from {
transform: scale(0.5) translate(1rem, 1rem);
opacity: 0;
}
}
@keyframes button-dropdown-open-rtl {
from {
transform: scale(0.5) translate(-1rem, 1rem);
opacity: 0;
}
}
}
summary {
&::before {
display: none;
}
}
}
summary + div {
-webkit-backdrop-filter: var(--blur);
display: flex;
position: absolute;
flex-direction: column;
z-index: 1;
backdrop-filter: var(--blur);
inset-block-end: 0;
inset-inline-end: 3rem;
box-shadow: var(--edge-highlight), var(--shadow-glass);
border-radius: var(--rounded-corner);
background-color: var(--glass-bg);
padding: 1rem;
width: min(calc(var(--container-width) / 3), calc(90vw - 3rem));
max-height: 50vh;
@media only screen and (max-width: 720px) {
inset-inline-end: 2.5rem;
width: min(calc(var(--container-width) / 3), calc(90vw - 2.5rem));
}
strong.title {
color: var(--fg-muted-4);
}
div {
--mask: linear-gradient(
to bottom,
transparent,
black 1rem,
black calc(100% - 1rem),
transparent
);
-webkit-mask-image: var(--mask);
flex: 1;
mask-image: var(--mask);
margin: 0 -1rem -1rem;
padding: 1rem;
padding-block-start: 0;
overflow: auto;
}
ol,
ul {
margin: 0;
padding-inline-start: 0.75rem;
font-size: var(--font-size-small);
&:first-child {
margin-block-start: 0.75rem;
}
li::marker {
color: var(--fg-muted-5);
}
a {
color: var(--fg-muted-5);
}
}
}
#toc .icon {
-webkit-mask-image: var(--icon-toc);
mask-image: var(--icon-toc);
:root[dir*="rtl"] & {
transform: scaleX(-1);
}
}
#backlinks {
.icon {
-webkit-mask-image: var(--icon-backlink);
mask-image: var(--icon-backlink);
:root[dir*="rtl"] & {
transform: scaleX(-1);
}
}
summary + div {
width: min(calc(var(--container-width) / 3), calc(90vw - 5rem));
}
}
#go-to-top {
@media only screen and (max-width: 720px) {
display: none;
}
.icon {
-webkit-mask-image: var(--icon-top);
mask-image: var(--icon-top);
}
}
#share .icon {
-webkit-mask-image: var(--icon-share);
mask-image: var(--icon-share);
}
#issue .icon {
-webkit-mask-image: var(--icon-bug);
mask-image: var(--icon-bug);
}
}

72
sass/_buttons.scss Normal file
View file

@ -0,0 +1,72 @@
.buttons {
display: flex;
flex-direction: row;
justify-content: space-between;
margin-block-start: 4rem;
&.centered {
justify-content: space-around;
}
a {
text-decoration: none;
}
button {
appearance: none;
cursor: pointer;
border: none;
font-family: inherit;
&:disabled {
cursor: not-allowed;
&:hover {
background-color: var(--fg-muted-1);
color: var(--fg-muted-5);
}
&:active {
transform: none;
}
}
}
a,
button {
transition: var(--transition);
box-shadow: var(--edge-highlight);
border-radius: var(--rounded-corner);
background-color: var(--fg-muted-1) !important;
padding: 0.75rem 1rem;
color: var(--fg-muted-5);
font-weight: bold;
font-size: var(--font-size-small);
line-height: 1;
&:hover {
background-color: var(--fg-muted-3) !important;
color: var(--fg-color);
}
&:active {
transform: var(--active);
}
&.colored {
box-shadow: none;
background-color: transparent;
color: var(--accent-color);
&:hover {
box-shadow: var(--edge-highlight);
background-color: var(--accent-color-alpha);
}
}
&.big {
border-radius: 999px;
padding: 1rem 1.5rem;
}
}
}

74
sass/_code.scss Normal file
View file

@ -0,0 +1,74 @@
// CODE
pre,
code,
kbd,
samp {
font-family: var(--font-monospace-code);
}
code:not(pre code) {
box-shadow: var(--edge-highlight);
border-radius: var(--rounded-corner-small);
background-color: var(--fg-muted-1);
padding: 0.125rem 0.375rem;
color: var(--red-fg);
font-size: var(--font-size-small-em);
}
pre {
margin: 1rem 0 1rem;
box-shadow: var(--edge-highlight), var(--shadow);
border-radius: var(--rounded-corner);
background-color: var(--fg-muted-1);
padding: 1rem;
max-width: 100vw;
overflow: auto;
line-height: normal;
table {
box-shadow: none;
border-radius: 0;
background-color: transparent;
table-layout: auto;
overflow: hidden;
tr {
&:nth-child(even) {
background-color: transparent;
}
th,
td {
padding: 0;
}
th {
background-color: transparent;
font-weight: normal;
}
}
}
// The line number cells
table td:nth-of-type(1) {
-webkit-user-select: none;
user-select: none;
text-align: center;
}
mark {
display: block;
box-shadow: none;
border-radius: 0; // Unset code block border radius
background-color: var(--fg-muted-1);
padding: 0; // Unset mark padding
color: var(
--fg-color
); // Unset mark color from accent color to text color
}
// The line numbers already provide some kind of left/right padding
&[data-linenos] {
padding: 1rem 0;
}
}

471
sass/_comments.scss Normal file
View file

@ -0,0 +1,471 @@
#comments {
#qrcode {
float: inline-end;
transform-origin: right;
box-sizing: content-box;
margin-inline-start: 1rem;
margin-block-start: 3rem;
margin-block-end: 0;
background-color: white;
padding: 0.75rem;
width: 7.8125rem; // 125px
height: 7.8125rem; // 125px
:root[dir*="rtl"] & {
transform-origin: left;
}
@media only screen and (max-width: 720px) {
display: none;
}
}
.buttons {
justify-content: start;
gap: 0.25rem;
margin-block-start: 2rem;
#load-comments:disabled {
--shimmer: rgb(
from var(--accent-color) r g b / calc(var(--color-opacity) * 2)
);
animation: loading-shimmer var(--transition-long) ease-in-out
alternate infinite;
transition: none;
background-image: linear-gradient(
to right,
var(--fg-muted-1) 50%,
var(--shimmer) 75%,
var(--fg-muted-1) 100%
);
background-size: 200%;
background-color: transparent;
&:hover {
background-color: transparent;
}
@keyframes loading-shimmer {
to {
background-position-x: -200%;
}
}
}
}
#comments-wrapper {
display: flex;
flex-direction: column;
gap: 2rem;
margin-block-start: 2rem;
#comments-status {
color: var(--fg-muted-4);
font-weight: bold;
font-size: var(--font-size-x-large);
text-align: center;
}
.comment {
display: grid;
grid-template-columns: min-content;
grid-template-areas:
"avatar name "
"avatar time "
"avatar post "
"...... media "
"...... card "
"...... interactions";
column-gap: 1rem;
justify-items: start;
&.comment-reply {
position: relative;
border-radius: 0.25rem;
border-inline-start: 0.25rem solid var(--fg-muted-2);
padding-inline-start: 1rem;
&:has(+ .comment-reply) {
border-end-start-radius: 0;
}
& + .comment-reply {
margin-block-start: -2rem;
border-start-start-radius: 0;
padding-block-start: 2rem;
}
}
.avatar-link {
position: relative;
grid-area: avatar;
width: 4rem;
height: 4rem;
.avatar {
transition: var(--transition);
margin: 0;
background-size: cover;
width: 100%;
height: 100%;
&:hover {
transform: rotate(10deg) var(--hover);
border-radius: var(--rounded-corner);
}
&:active {
transform: var(--active);
}
}
}
.author {
display: flex;
grid-area: name;
align-items: center;
gap: 0.25rem;
font-weight: bold;
.instance {
transition: var(--transition);
box-shadow: var(--edge-highlight);
border-radius: 999px;
background-color: var(--fg-muted-1);
padding: 0.375rem 0.75rem;
color: var(--fg-muted-5);
font-size: var(--font-size-small);
line-height: 1;
text-decoration: none;
&:hover {
background-color: var(--fg-muted-5);
color: var(--fg-contrast);
text-decoration: none;
}
&:active {
transform: var(--active);
}
&.op {
background-color: var(--accent-color-alpha);
padding-inline-start: 0.4375rem;
color: var(--accent-color);
&:hover {
background-color: var(--accent-color);
color: var(--contrast-color);
&::before {
background-color: var(--contrast-color);
}
}
&::before {
-webkit-mask-image: var(--icon-verified);
display: inline-block;
vertical-align: -0.1875rem;
mask-image: var(--icon-verified);
mask-size: cover;
transition: var(--transition);
margin-inline-end: 0.25rem;
background-color: var(--accent-color);
width: 1rem;
height: 1rem;
content: "";
}
:root[dir*="rtl"] & {
padding: 0.375rem 0.5rem 0.375rem 0.75rem;
}
}
}
}
.mention {
display: inline-block;
transition: var(--transition);
box-shadow: var(--edge-highlight);
border-radius: var(--rounded-corner-small);
background-color: var(--accent-color-alpha);
padding: 0.25rem 0.375rem;
line-height: 1;
text-decoration: none;
&:hover {
background-color: var(--accent-color);
color: var(--contrast-color);
}
&:active {
transform: var(--active);
}
&.hashtag {
background-color: var(--fg-muted-1);
color: var(--fg-muted-5);
&:hover {
background-color: var(--fg-muted-5);
color: var(--fg-contrast);
}
}
}
time {
grid-area: time;
margin-block-start: 0.5rem;
font-size: var(--font-size-small);
a {
color: var(--fg-muted-5);
&:after {
background-color: var(--fg-muted-5);
}
}
}
details {
&[open] {
border-radius: var(--rounded-corner-small);
background-image: linear-gradient(
to right,
transparent,
transparent 0.5rem,
var(--fg-muted-1) 0.5rem,
var(--fg-muted-1) calc(100% - 0.5rem),
transparent calc(100% - 0.5rem),
transparent
),
linear-gradient(
to right,
transparent,
transparent 0.5rem,
var(--bg-color) 0.5rem,
var(--bg-color) calc(100% - 0.5rem),
transparent calc(100% - 0.5rem),
transparent
),
repeating-linear-gradient(
45deg,
var(--contrast-color),
var(--contrast-color) 0.25rem,
var(--accent-color) 0.25rem,
var(--accent-color) 0.5rem
);
summary {
border-radius: 0;
background-image: none;
}
}
summary {
border-radius: var(--rounded-corner-small);
background-image: linear-gradient(
to right,
transparent,
transparent 0.5rem,
var(--fg-muted-1) 0.5rem,
var(--fg-muted-1) calc(100% - 0.5rem),
transparent calc(100% - 0.5rem),
transparent
),
linear-gradient(
to right,
transparent,
transparent 0.5rem,
var(--bg-color) 0.5rem,
var(--bg-color) calc(100% - 0.5rem),
transparent calc(100% - 0.5rem),
transparent
),
repeating-linear-gradient(
45deg,
var(--contrast-color),
var(--contrast-color) 0.25rem,
var(--accent-color) 0.25rem,
var(--accent-color) 0.5rem
);
}
}
main {
grid-area: post;
margin: 1rem 0 0;
width: 100%;
:first-child {
margin-block-start: 0;
}
:last-child {
margin-block-end: 0;
}
}
.attachments {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
grid-area: media;
gap: 0.5rem;
margin-block-start: 1rem;
img,
video {
margin: 0;
}
}
.card {
grid-area: card;
transition: var(--transition);
margin-block-start: 1rem;
width: min(calc(var(--container-width) / 2), 100%);
font-weight: normal;
text-decoration: none;
&:hover:not(:active) {
img {
transform: var(--hover);
box-shadow: var(--edge-highlight), var(--shadow-raised);
border-radius: var(--rounded-corner-small);
}
figcaption {
border-radius: var(--rounded-corner);
background-color: var(--fg-muted-2);
}
}
&:active {
transform: var(--active);
}
figure {
display: flex;
flex-direction: column;
gap: 0.25rem;
margin: 0;
img {
margin: 0;
border-radius: var(--rounded-corner)
var(--rounded-corner) var(--rounded-corner-small)
var(--rounded-corner-small);
aspect-ratio: 16 / 9;
object-fit: cover;
& + figcaption {
border-radius: var(--rounded-corner-small)
var(--rounded-corner-small)
var(--rounded-corner) var(--rounded-corner);
}
}
figcaption {
display: flex;
flex-direction: column;
gap: 0.25rem;
transition: var(--transition);
box-shadow: var(--edge-highlight);
border-radius: var(--rounded-corner);
background-color: var(--fg-muted-1);
padding: 1rem;
color: var(--fg-color);
font-size: var(--font-size-medium);
text-align: start;
p {
margin: 0;
color: var(--fg-muted-5);
font-size: var(--font-size-small);
}
}
}
}
footer {
display: flex;
grid-area: interactions;
gap: 0.25rem;
margin-block-start: 1rem;
.boosts,
.faves {
transition: var(--transition);
border-radius: 999px;
background-color: transparent;
padding: 0.5rem 0.75rem;
padding-inline-start: 0.625rem;
line-height: 1;
font-variant-numeric: tabular-nums;
text-decoration: none;
.icon {
vertical-align: -0.125em;
transition: var(--transition-longer);
margin-inline-end: 0.25rem;
}
&:hover {
box-shadow: var(--edge-highlight);
text-decoration: none;
}
&:active {
transform: var(--active);
}
}
.boosts {
color: var(--purple-fg);
&:hover {
background-color: var(--purple-bg);
.icon {
transform: rotate(180deg);
:root[dir*="rtl"] & {
transform: scaleX(-1) rotate(180deg);
}
}
}
.icon {
-webkit-mask-image: var(--icon-boosts);
mask-image: var(--icon-boosts);
:root[dir*="rtl"] & {
transform: scaleX(-1);
}
}
}
.faves {
color: var(--yellow-fg);
&:hover {
background-color: var(--yellow-bg);
.icon {
transform: rotate(72deg);
:root[dir*="rtl"] & {
transform: rotate(-72deg);
}
}
}
.icon {
-webkit-mask-image: var(--icon-star);
mask-image: var(--icon-star);
}
}
}
}
}
}

104
sass/_crt.scss Normal file
View file

@ -0,0 +1,104 @@
.crt {
margin: 1rem 0 1rem;
box-shadow: var(--edge-highlight), var(--shadow-glow);
border-radius: var(--rounded-corner);
background-image: radial-gradient(
color-mix(in srgb, var(--accent-color) 30%, black),
color-mix(in srgb, var(--accent-color) 10%, black) 80%,
color-mix(in srgb, var(--accent-color) 5%, black)
);
pre {
--text-shadow-1: hsl(from var(--accent-color) h s l / 0.5);
--text-shadow-2: hsl(from var(--accent-color) h calc(s * 2) l);
animation: flicker 0.25s alternate infinite;
margin: 0;
box-shadow: none;
background-color: transparent !important;
padding: 1rem 1rem;
color: var(--accent-color) !important;
text-shadow:
var(--text-shadow-1) 0 0 0.25rem,
var(--text-shadow-2) 0 0 0.75rem;
@keyframes flicker {
25% {
opacity: 0.95;
}
50% {
opacity: 0.85;
}
75% {
opacity: 1;
}
to {
opacity: 0.9;
}
}
}
}
.scanlines {
position: relative;
overflow: hidden;
&::before {
display: block;
position: absolute;
z-index: 1;
animation: scanlines 0.1s linear infinite;
inset: 0;
background-image: repeating-linear-gradient(
to bottom,
rgb(0 0 0 / 0.25),
rgb(0 0 0 / 0.25) 0.125rem,
transparent 0.125rem,
transparent 0.25rem
);
pointer-events: none;
content: "";
@keyframes scanlines {
to {
background-position-y: 0.25rem;
}
}
}
&::after {
--scanline-color: rgb(from var(--accent-color) r g b / 0.05);
display: block;
position: absolute;
animation: scanline 5s linear infinite;
inset: 0;
background-image: linear-gradient(
to bottom,
transparent,
var(--scanline-color) 16rem
);
background-size: auto 16rem;
background-repeat: no-repeat;
background-position-y: -16rem;
pointer-events: none;
content: "";
@keyframes scanline {
to {
background-position-y: calc(100% + 16rem);
}
}
}
}
.cursor {
animation: cursor-blink 1s infinite;
@keyframes cursor-blink {
50% {
opacity: 0;
}
}
}

16
sass/_emoji.scss Normal file
View file

@ -0,0 +1,16 @@
.emoji {
display: inline-block;
vertical-align: bottom;
transition: var(--transition);
cursor: zoom-in;
margin: 0;
box-shadow: none;
border-radius: 0;
background-color: transparent;
width: 1.5em;
height: 1.5em;
&:hover {
transform: scale(2);
}
}

16
sass/_external.scss Normal file
View file

@ -0,0 +1,16 @@
a.external::after {
-webkit-mask-image: var(--icon-external);
display: inline-block;
opacity: var(--dim-opacity);
mask-image: var(--icon-external);
mask-size: cover;
margin-inline-start: 0.25rem;
background-color: currentColor;
width: max(0.75rem, 0.75em);
height: max(0.75rem, 0.75em);
content: "";
:root[dir*="rtl"] & {
transform: scaleX(-1);
}
}

12
sass/_feed.scss Normal file
View file

@ -0,0 +1,12 @@
h1 a:has(.icon.feed) {
color: currentColor;
}
h1 .icon.feed {
-webkit-mask-image: var(--icon-feed);
vertical-align: -0.375rem;
mask-image: var(--icon-feed);
margin-inline-start: 0.5rem;
width: 1em;
height: 1em;
}

137
sass/_footer.scss Normal file
View file

@ -0,0 +1,137 @@
#site-footer {
grid-area: footer;
margin-block-end: 2rem;
text-align: center;
nav {
display: inline-block;
margin: 0 auto 1rem;
box-shadow: var(--edge-highlight);
border-radius: 1.375rem;
background-color: var(--fg-muted-1);
padding: 0.25rem;
max-width: 90%;
ul {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
gap: 0.25rem;
margin: 0;
padding: 0;
}
li {
display: flex;
margin: 0;
padding: 0;
list-style: none;
@media only screen and (max-width: 480px) {
flex: 0 0 100%;
}
}
a {
flex: 1;
transition: var(--transition);
border-radius: 999px;
padding: 0.375rem 0.75rem;
color: var(--fg-muted-4);
text-align: center;
text-decoration: none;
&.active {
box-shadow: var(--edge-highlight);
background-color: var(--accent-color-alpha);
color: var(--accent-color);
&:hover {
background-color: var(--accent-color);
color: var(--contrast-color);
}
}
&:hover {
box-shadow: var(--edge-highlight);
background-color: var(--fg-muted-1);
color: var(--fg-muted-5);
}
&:active {
transform: var(--active);
}
}
}
#socials {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 0.5rem;
margin: 1rem auto 0;
padding: 0;
li {
margin: 0;
padding: 0;
list-style: none;
}
a {
display: block;
transition: var(--transition);
border-radius: 999px;
padding: 0.5rem;
color: var(--fg-muted-4);
line-height: 0;
&:hover {
box-shadow: var(--edge-highlight);
background-color: var(--fg-muted-1);
color: var(--fg-muted-5);
}
&:active {
transform: var(--active);
}
.icon {
-webkit-mask-image: var(--icon);
mask-image: var(--icon);
transition: var(--transition);
width: 1.5rem;
height: 1.5rem;
}
span {
display: none;
}
}
}
p {
margin: 1rem auto;
}
.link {
display: inline-block;
transition: var(--transition);
box-shadow: var(--edge-highlight);
border-radius: var(--rounded-corner-small);
background-color: var(--accent-color-alpha);
padding: 0.25rem 0.375rem;
line-height: 1;
text-decoration: none;
&:hover {
background-color: var(--accent-color);
color: var(--contrast-color);
}
&:active {
transform: var(--active);
}
}
}

View file

@ -0,0 +1,6 @@
.footnotes-list {
p {
margin-block-start: 0;
margin-block-end: 0;
}
}

140
sass/_general.scss Normal file
View file

@ -0,0 +1,140 @@
* {
box-sizing: border-box;
}
:root {
scroll-behavior: smooth;
scrollbar-color: var(--accent-color) transparent;
accent-color: var(--accent-color);
font-size: 16px;
}
body {
text-wrap: pretty;
display: grid; // Put footer at the bottom for short pages, such as the 404
grid-template-rows: auto minmax(auto, 1fr) auto; // Header, stuff, footer
grid-template-areas:
"nav"
"main"
"footer";
margin: 0;
background-color: var(--bg-color);
min-height: 100vh;
color: var(--fg-color);
line-height: 1.5;
font-family: var(--font-system-ui), var(--font-emoji);
overflow-wrap: break-word;
&:has(#sidebar) {
grid-template-columns: 1fr min(var(--container-width), 90%) 1fr;
grid-template-areas:
"nav nav nav"
"sidebar main ."
"footer footer footer";
@media only screen and (max-width: 1200px) {
grid-template-areas:
"nav nav nav"
". sidebar ."
". main ."
"footer footer footer";
}
}
}
// Style text selection to use accent color
::selection {
background-color: var(--accent-color);
color: var(--contrast-color);
}
// Make focused anchor not get covered by nav,
// and flash it with accent color when jumping to it
:target:not(#main-content) {
transition:
all var(--transition),
scroll-margin-block-start 0s;
scroll-margin-block-start: 15vh;
color: var(--accent-color);
text-shadow: var(--text-shadow-glow);
}
// Custom focus indicator
:focus-visible {
animation: focus-in var(--transition);
outline: 0.125rem solid var(--accent-color);
outline-offset: 0.125rem;
}
// Fallback for older browsers
@supports not selector(:focus-visible) {
:focus {
animation: focus-in var(--transition);
outline: 0.125rem solid var(--accent-color);
outline-offset: 0.125rem;
}
}
@keyframes focus-in {
from {
outline: 0.5rem solid transparent;
outline-offset: 0.25rem;
}
}
main {
margin: 4.25rem auto 4rem;
width: min(var(--container-width), 90%);
}
#sidebar {
display: flex;
position: sticky;
top: 0;
grid-area: sidebar;
opacity: 0.2;
transition: var(--transition);
height: 100vh;
&:hover {
opacity: 1;
}
@media only screen and (max-width: 1200px) {
position: static;
opacity: 1;
margin-block-start: 4.25rem;
margin-block-end: -4.25rem;
padding: 0;
height: auto;
}
& > div {
--mask: linear-gradient(to bottom,
transparent,
black 1rem,
black calc(100% - 1rem),
transparent);
-webkit-mask-image: var(--mask);
mask-image: var(--mask);
padding: 1rem;
overflow: auto;
}
& + main {
grid-area: main;
margin: 0;
margin-block-start: 4.25rem;
margin-block-end: 4rem;
width: auto;
}
}
@media (prefers-reduced-motion) {
*,
*::before,
*::after {
animation-duration: 0s !important;
transition-duration: 0s !important;
}
}

4
sass/_hidden.scss Normal file
View file

@ -0,0 +1,4 @@
.hidden {
display: none;
visibility: hidden;
}

11
sass/_icon.scss Normal file
View file

@ -0,0 +1,11 @@
i.icon {
display: inline-block;
mask-size: cover;
background-color: currentColor;
width: 1rem;
height: 1rem;
font-style: normal;
font-variant: normal;
line-height: 0;
text-rendering: auto;
}

30
sass/_iframe.scss Normal file
View file

@ -0,0 +1,30 @@
iframe {
display: block;
margin: 1rem auto;
box-shadow: var(--edge-highlight), var(--shadow);
border: none;
border-radius: var(--rounded-corner);
background-color: var(--fg-muted-1);
width: 100%;
max-width: 100%;
&.mastodon-embed {
aspect-ratio: 3 / 4;
width: min(calc(var(--container-width) / 2), 100%);
}
&.vimeo-embed,
&.youtube-embed {
aspect-ratio: 16 / 9;
}
&:fullscreen {
box-shadow: none;
border-radius: 0;
}
&:-webkit-full-screen {
box-shadow: none;
border-radius: 0;
}
}

203
sass/_input.scss Normal file
View file

@ -0,0 +1,203 @@
input[type="radio"],
input[type="checkbox"],
input[type="color"] {
position: relative;
appearance: none;
transition: var(--transition);
cursor: pointer;
border: 0.15rem solid var(--fg-muted-2);
background-color: var(--fg-muted-1);
width: 1rem;
height: 1rem;
&:hover {
background-color: var(--fg-muted-2);
}
&:disabled {
opacity: var(--disabled-opacity);
cursor: not-allowed;
&:hover {
background-color: var(--fg-muted-1);
&:checked {
background-color: var(--accent-color);
}
}
}
}
input[type="radio"],
input[type="checkbox"] {
&::before {
display: block;
position: absolute;
transform: scale(0.5);
opacity: 0;
transition: var(--transition);
background-color: var(--contrast-color);
content: "";
}
&:checked {
border: 0.15rem solid transparent;
background-color: var(--accent-color);
&::before {
transform: scale(1);
opacity: 1;
}
}
}
input[type="radio"] {
vertical-align: -0.1875em;
border-radius: 50%;
&::before {
inset-block-start: 0.125rem;
inset-inline-start: 0.125rem;
border-radius: 50%;
width: 0.5rem;
height: 0.5rem;
}
}
input[type="checkbox"] {
vertical-align: -0.1875em;
border-radius: calc(var(--rounded-corner-small) / 2);
&::before {
-webkit-mask-image: var(--icon-checkmark);
transform-origin: bottom left;
mask-image: var(--icon-checkmark);
mask-size: cover;
inset-block-start: -0.125rem;
inset-inline-start: -0.125rem;
width: 1rem;
height: 1rem;
}
&.switch {
vertical-align: -0.375rem;
box-shadow: var(--edge-highlight);
border: none;
border-radius: 999px;
width: 2.5rem;
height: 1.5rem;
&.big {
vertical-align: -0.625rem;
width: 3rem;
height: 2rem;
&::before {
width: 1.5rem;
height: 1.5rem;
}
}
&::before {
transform: none;
transform-origin: center;
opacity: 1;
mask-image: none;
transition: var(--transition);
inset-block-start: 0.25rem;
inset-inline-start: 0.25rem;
box-shadow: var(--shadow);
border-radius: 50%;
background-color: white;
width: 1rem;
height: 1rem;
}
&:checked {
background-color: var(--accent-color);
&::before {
transform: translateX(1rem);
background-color: var(--contrast-color);
:root[dir*="rtl"] & {
transform: translateX(-1rem);
}
}
}
&:disabled {
&::before {
box-shadow: none;
}
}
}
}
input[type="color"] {
vertical-align: -0.375em;
box-shadow: var(--edge-highlight);
border: none;
border-radius: var(--rounded-corner-small);
padding: 0.25rem;
width: 3rem;
height: 2rem;
&::-moz-color-swatch {
border: none;
border-radius: calc(var(--rounded-corner-small) - 0.25rem);
}
&::-webkit-color-swatch-wrapper {
padding: 0;
}
&::-webkit-color-swatch {
border-radius: calc(var(--rounded-corner-small) - 0.25rem);
}
}
input[type="range"] {
appearance: none;
transition: var(--transition);
cursor: pointer;
box-shadow: var(--edge-highlight);
border-radius: 999px;
background: var(--accent-color);
width: 100%;
height: 0.5rem;
&::-webkit-slider-thumb {
appearance: none;
filter: brightness(0.9);
transition: var(--transition);
cursor: grab;
box-shadow: var(--shadow);
border-radius: 999px;
background-color: white;
width: 1.5rem;
height: 1.5rem;
&:active {
transform: var(--active);
cursor: grabbing;
}
}
&::-moz-range-thumb {
appearance: none;
transition: var(--transition);
cursor: grab;
box-shadow: var(--shadow);
border: none;
border-radius: 999px;
background-color: white;
width: 1.5rem;
height: 1.5rem;
&:active {
transform: var(--active);
cursor: grabbing;
}
}
}

164
sass/_media.scss Normal file
View file

@ -0,0 +1,164 @@
img,
video {
display: block;
margin: 1rem auto;
box-shadow: var(--edge-highlight), var(--shadow);
border-radius: var(--rounded-corner);
background-color: var(--fg-muted-1);
max-width: 100%;
&.full,
&[src*="#full"] {
width: 100%;
}
&.full-bleed,
&[src*="#full-bleed"] {
margin-inline-start: calc((-100vw + 100%) / 2);
margin-inline-end: calc((-100vw + 100%) / 2);
width: 100vw;
max-width: 100vw;
}
&.start,
&.end,
&[src*="#start"],
&[src*="#end"] {
margin: 0;
width: 30%;
@media only screen and (max-width: 720px) {
float: none;
margin-inline-start: 0;
margin-inline-end: 0;
margin-block-start: 1rem;
margin-block-end: 1rem;
width: 100%;
}
}
&.start,
&[src*="#start"] {
float: inline-start;
transform-origin: left;
margin-inline-end: 1rem;
:root[dir*="rtl"] & {
transform-origin: right;
}
}
&.end,
&[src*="#end"] {
float: inline-end;
transform-origin: right;
margin-inline-start: 1rem;
:root[dir*="rtl"] & {
transform-origin: left;
}
}
&.pixels,
&[src*="#pixels"] {
image-rendering: pixelated;
}
&.transparent,
&.full-bleed,
&[src*="#transparent"],
&[src*="#full-bleed"] {
box-shadow: none;
border-radius: 0;
background-color: transparent;
}
&.spoiler,
&[src*="#spoiler"] {
opacity: var(--dim-opacity);
clip-path: inset(0 0 0 0 round var(--rounded-corner));
filter: blur(1rem);
&:hover,
&:active {
opacity: 1;
clip-path: inset(
-0.75rem -0.75rem -0.75rem -0.75rem round
var(--rounded-corner-small)
);
filter: none;
}
&.solid,
&[src*="#solid"] {
clip-path: none;
filter: brightness(0) contrast(0.5);
box-shadow: none;
&:hover,
&:active {
filter: none;
}
}
}
}
img {
transition: var(--transition-longer);
&:not(
.no-hover,
.full-bleed,
[src*="#no-hover"],
[src*="#full-bleed"],
.emoji
) {
cursor: zoom-in;
&:hover {
position: relative;
transform: var(--hover);
z-index: 1;
box-shadow: var(--edge-highlight), var(--shadow-raised);
border-radius: var(--rounded-corner-small);
}
&.start,
&.end,
&[src*="#start"],
&[src*="#end"] {
&:hover {
transform: scale(2);
}
@media only screen and (max-width: 720px) {
transform-origin: center;
&:hover {
transform: var(--hover);
}
}
}
&.transparent,
&[src*="#transparent"] {
&:hover {
box-shadow: none;
}
}
}
}
a img:not(.no-hover, .full-bleed, [src*="#no-hover"], [src*="#full-bleed"]) {
cursor: pointer;
}
video:fullscreen {
box-shadow: none;
border-radius: 0;
}
video:-webkit-full-screen {
box-shadow: none;
border-radius: 0;
}

478
sass/_nav.scss Normal file
View file

@ -0,0 +1,478 @@
#handle {
position: fixed;
z-index: 999;
transition: var(--transition);
margin: 0 auto;
inset-block-start: 0;
inset-inline-end: 0;
inset-inline-start: 0;
width: min(var(--container-width), 90%);
height: 4.25rem;
&::before {
position: absolute;
transition: var(--transition);
margin: 0 auto;
inset-block-start: 0.5rem;
inset-inline-end: 0;
inset-inline-start: 0;
box-shadow: var(--edge-highlight);
border-radius: 999px;
background-color: var(--accent-color);
width: min(calc(var(--container-width) / 4), 100%);
height: 0.5rem;
content: "";
}
&:hover::before,
&:has(+ #site-nav:hover)::before,
&:has(+ #site-nav *:focus-visible, + #site-nav *:focus)::before {
transform: translateY(-1rem) scale(0.5);
opacity: 0;
}
&:hover + #site-nav,
& + #site-nav:hover,
& + #site-nav:has(*:focus-visible, *:focus) {
transform: none;
opacity: 1;
pointer-events: auto;
&::before {
-webkit-backdrop-filter: var(--blur);
backdrop-filter: var(--blur);
}
}
& + #site-nav {
position: fixed;
transform: translateY(-1rem) scale(0.5);
transform-origin: top;
opacity: 0;
transition: var(--transition);
margin: 0 auto;
width: max-content;
pointer-events: none;
&::before {
-webkit-backdrop-filter: saturate(1) blur(0);
backdrop-filter: saturate(1) blur(0);
transition: var(--transition);
}
}
}
#site-nav {
position: sticky;
grid-area: nav;
z-index: 999;
margin: 1rem auto 0;
inset-block-start: 1rem;
inset-inline-end: 0;
inset-inline-start: 0;
border-radius: 1.625rem;
max-width: min(var(--container-width), 90%);
@media only screen and (max-width: 480px) {
position: relative;
margin: 0 auto;
}
&::before {
-webkit-backdrop-filter: var(--blur);
position: absolute;
z-index: -1;
backdrop-filter: var(--blur);
inset: 0;
box-shadow: var(--edge-highlight), var(--shadow-glass);
border-radius: 1.625rem;
background-color: var(--glass-bg);
content: "";
}
nav {
padding: 0.5rem;
& > a {
-webkit-backdrop-filter: var(--blur);
position: absolute;
left: 50%;
transform: translateX(-50%);
opacity: 0;
z-index: 999;
backdrop-filter: var(--blur);
transition: var(--transition);
inset-block-start: 0;
box-shadow: var(--edge-highlight), var(--shadow-glass);
border-radius: 999px;
background-color: var(--glass-bg);
padding: 0.625rem 0.75rem;
pointer-events: none;
line-height: 1;
text-decoration: none;
&:focus {
opacity: 1;
inset-block-start: calc(100% + 0.5rem);
}
}
ul {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
gap: 0.25rem;
margin: 0;
padding: 0;
}
li {
display: flex;
margin: 0;
padding: 0;
list-style: none;
@media only screen and (max-width: 480px) {
&:not(:has(.circle)) {
flex: 0 0 100%;
}
}
}
a,
summary {
flex: 1;
transition: var(--transition);
box-shadow: none;
border-radius: 999px;
background-color: transparent;
padding: 0.625rem 0.75rem;
font-weight: bold;
line-height: 1;
list-style: none;
text-align: center;
text-decoration: none;
}
a.active {
box-shadow: var(--edge-highlight);
background-color: var(--accent-color-alpha);
color: var(--accent-color);
&:hover {
background-color: var(--accent-color);
color: var(--contrast-color);
}
}
#home a {
color: var(--fg-muted-5);
font-weight: 800;
&:hover {
color: var(--fg-color);
}
&.active {
color: var(--accent-color);
&:hover {
color: var(--contrast-color);
}
}
.icon {
-webkit-mask-image: var(--icon-home);
vertical-align: -0.125em;
mask-image: var(--icon-home);
transition: var(--transition);
margin-inline-end: 0.25rem;
}
}
.divider {
align-self: stretch;
margin: 0 0.25rem;
background-color: var(--fg-muted-2);
width: max(1px, 0.0625em);
@media only screen and (max-width: 480px) {
display: none;
}
}
a,
#search button,
#language-switcher summary,
#theme-switcher summary,
#theme-switcher button,
summary {
color: var(--fg-muted-4);
&:hover {
box-shadow: var(--edge-highlight);
background-color: var(--fg-muted-1);
color: var(--fg-muted-5);
}
&:active {
transform: var(--active);
}
}
.circle {
padding: 0.625rem 0.625rem;
line-height: 0;
&::before {
display: none;
}
.icon {
vertical-align: -0.125em;
transition: var(--transition);
}
}
button {
appearance: none;
transition: var(--transition);
cursor: pointer;
border: none;
border-radius: 999px;
background-color: transparent;
font-size: var(--font-size-medium);
}
details {
display: flex;
position: relative;
flex: 1;
box-shadow: none;
border-radius: 0;
background-color: transparent;
padding: 0;
&[open] ul {
animation: dropdown-open var(--transition);
@keyframes dropdown-open {
from {
transform: scale(0.5) translate(-50%, -1rem);
opacity: 0;
}
}
}
ul {
-webkit-backdrop-filter: var(--blur);
position: absolute;
left: 50%;
flex-direction: column;
transform: translateX(-50%);
transform-origin: top left;
z-index: 1;
backdrop-filter: var(--blur);
inset-block-start: 3.25rem;
box-shadow: var(--edge-highlight), var(--shadow-glass);
border-radius: calc(var(--rounded-corner) + 0.25rem);
background-color: var(--glass-bg);
padding: 0.25rem;
li {
width: 100%;
white-space: nowrap;
a {
border-radius: var(--rounded-corner);
text-align: start;
}
}
}
@media only screen and (max-width: 480px) {
&:has(summary:not(.circle)) ul {
inset-block-start: 2.75rem;
}
}
}
#search .icon {
-webkit-mask-image: var(--icon-search);
mask-image: var(--icon-search);
:root[dir*="rtl"] & {
transform: scaleX(-1);
}
}
#feed .icon {
-webkit-mask-image: var(--icon-feed);
mask-image: var(--icon-feed);
:root[dir*="rtl"] & {
transform: scaleX(-1);
}
}
#repo .icon {
-webkit-mask-image: var(--icon-git);
mask-image: var(--icon-git);
}
#language-switcher .icon {
-webkit-mask-image: var(--icon-languages);
mask-image: var(--icon-languages);
}
#theme-switcher {
ul {
flex-direction: row;
flex-wrap: nowrap;
border-radius: 999px;
}
.active {
box-shadow: var(--edge-highlight);
background-color: var(--accent-color-alpha);
color: var(--accent-color);
&:hover {
background-color: var(--accent-color);
color: var(--contrast-color);
}
}
#theme-system .icon,
.icon {
-webkit-mask-image: var(--icon-theme-system);
mask-image: var(--icon-theme-system);
:root[dir*="rtl"] & {
transform: scaleX(-1);
}
}
#theme-light .icon,
.icon.light {
-webkit-mask-image: var(--icon-theme-light);
mask-image: var(--icon-theme-light);
}
#theme-dark .icon,
.icon.dark {
-webkit-mask-image: var(--icon-theme-dark);
mask-image: var(--icon-theme-dark);
:root[dir*="rtl"] & {
transform: scaleX(-1);
}
}
}
}
#search-container {
transform: scale(0.5) translateY(-2.75rem);
opacity: 0;
transition: var(--transition);
padding: 0 0.5rem 0;
height: 0;
pointer-events: none;
&.active {
transform: none;
opacity: 1;
padding: 0 0.5rem 0.5rem;
height: 2.75rem;
pointer-events: all;
}
}
#search-bar {
box-shadow: var(--edge-highlight);
border: none;
border-radius: 999px;
background: var(--fg-muted-1);
padding: 0 0.75rem;
width: 100%;
height: 2.25rem;
color: inherit;
font-size: var(--font-size-medium);
&::placeholder {
opacity: 1;
color: var(--fg-muted-4);
}
}
#search-results-container {
-webkit-backdrop-filter: var(--blur);
display: flex;
position: absolute;
backdrop-filter: var(--blur);
inset-block-start: calc(100% + 0.5rem);
inset-inline-start: 0;
box-shadow: var(--edge-highlight), var(--shadow-glass);
border-radius: calc(var(--rounded-corner) + 0.5rem);
background-color: var(--glass-bg);
width: 100%;
max-height: 50vh;
}
#search-results {
--mask: linear-gradient(to bottom,
transparent,
black 1rem,
black calc(100% - 1rem),
transparent);
-webkit-mask-image: var(--mask);
display: none;
flex: 1;
flex-direction: column;
gap: 0.5rem;
mask-image: var(--mask);
padding: 0.5rem;
overflow: auto;
.item {
display: inline-flex;
flex-direction: column;
box-shadow: var(--edge-highlight);
border-radius: var(--rounded-corner);
background-color: var(--fg-muted-1);
padding: 0.5rem;
a {
width: fit-content;
&::after {
content: "";
:root[dir*="rtl"] & {
content: "";
}
}
}
span {
color: var(--fg-muted-5);
&:first-of-type,
&.more-matches {
margin-block-start: 0.5rem;
border-block-start: max(1px, 0.0625rem) solid var(--fg-muted-2);
padding-block-start: 0.25rem;
}
&.more-matches {
font-size: var(--font-size-small);
}
strong {
color: var(--fg-color);
}
}
}
}
}

276
sass/_normalize.scss Normal file
View file

@ -0,0 +1,276 @@
// Document
// ==========================================================================
//
// 1. Correct the line height in all browsers.
// 2. Prevent adjustments of font size after orientation changes in iOS.
//
:where(html) {
-webkit-text-size-adjust: 100%; // 2
text-size-adjust: 100%; // 2
line-height: 1.15; // 1
}
// Sections
// ==========================================================================
//
// Correct the font size and margin on `h1` elements within `section` and
// `article` contexts in Chrome, Edge, Firefox, and Safari.
//
:where(h1) {
margin-block-start: 0.67em;
margin-block-end: 0.67em;
font-size: 2em;
}
// Grouping content
// ==========================================================================
//
// Remove the margin on nested lists in Chrome, Edge, and Safari.
//
:where(dl, ol, ul) :where(dl, ol, ul) {
margin-block-start: 0;
margin-block-end: 0;
}
//
// 1. Add the correct box sizing in Firefox.
// 2. Correct the inheritance of border color in Firefox.
//
:where(hr) {
box-sizing: content-box; // 1
height: 0; // 1
color: inherit; // 2
}
// Text-level semantics
// ==========================================================================
//
// Add the correct text decoration in Safari.
//
:where(abbr[title]) {
text-decoration: underline;
text-decoration: underline dotted;
}
//
// Add the correct font weight in Chrome, Edge, and Safari.
//
:where(b, strong) {
font-weight: bolder;
}
//
// 1. Correct the inheritance and scaling of font size in all browsers.
// 2. Correct the odd `em` font sizing in all browsers.
//
:where(code, kbd, pre, samp) {
font-size: 1em; // 2
font-family: monospace, monospace; // 1
}
//
// Add the correct font size in all browsers.
//
:where(small) {
font-size: 80%;
}
// Tabular data
// ==========================================================================
//
// 1. Correct table border color in Chrome, Edge, and Safari.
// 2. Remove text indentation from table contents in Chrome, Edge, and Safari.
//
:where(table) {
border-color: currentColor; // 1
text-indent: 0; // 2
}
// Forms
// ==========================================================================
//
// Remove the margin on controls in Safari.
//
:where(button, input, select) {
margin: 0;
}
//
// Remove the inheritance of text transform in Firefox.
//
:where(button) {
text-transform: none;
}
//
// Correct the inability to style buttons in iOS and Safari.
//
:where(
button,
input:is([type="button" i], [type="reset" i], [type="submit" i])
) {
-webkit-appearance: button;
}
//
// Add the correct vertical alignment in Chrome, Edge, and Firefox.
//
:where(progress) {
vertical-align: baseline;
}
//
// Remove the inheritance of text transform in Firefox.
//
:where(select) {
text-transform: none;
}
//
// Remove the margin in Firefox and Safari.
//
:where(textarea) {
margin: 0;
}
//
// 1. Correct the odd appearance in Chrome, Edge, and Safari.
// 2. Correct the outline style in Safari.
//
:where(input[type="search" i]) {
-webkit-appearance: textfield; // 1
outline-offset: -2px; // 2
}
//
// Correct the cursor style of increment and decrement buttons in Safari.
//
::-webkit-inner-spin-button,
::-webkit-outer-spin-button {
height: auto;
}
//
// Correct the text style of placeholders in Chrome, Edge, and Safari.
//
::-webkit-input-placeholder {
opacity: 0.54;
color: inherit;
}
//
// Remove the inner padding in Chrome, Edge, and Safari on macOS.
//
::-webkit-search-decoration {
-webkit-appearance: none;
}
//
// 1. Correct the inability to style upload buttons in iOS and Safari.
// 2. Change font properties to `inherit` in Safari.
//
::-webkit-file-upload-button {
-webkit-appearance: button; // 1
font: inherit; // 2
}
//
// Remove the inner border and padding of focus outlines in Firefox.
//
:where(
button,
input:is(
[type="button" i],
[type="color" i],
[type="reset" i],
[type="submit" i]
)
)::-moz-focus-inner {
border-style: none;
padding: 0;
}
//
// Restore the focus outline styles unset by the previous rule in Firefox.
//
:where(
button,
input:is(
[type="button" i],
[type="color" i],
[type="reset" i],
[type="submit" i]
)
)::-moz-focusring {
outline: 1px dotted ButtonText;
}
//
// Remove the additional :invalid styles in Firefox.
//
:where(:-moz-ui-invalid) {
box-shadow: none;
}
// Interactive
// ==========================================================================
//
// Add the correct styles in Safari.
//
:where(dialog) {
position: absolute;
right: 0;
left: 0;
margin: auto;
border: solid;
background-color: white;
padding: 1em;
width: -moz-fit-content;
width: fit-content;
height: -moz-fit-content;
height: fit-content;
color: black;
}
:where(dialog:not([open])) {
display: none;
}
//
// Add the correct display in all browsers.
//
:where(summary) {
display: list-item;
}

3
sass/_not-found.scss Normal file
View file

@ -0,0 +1,3 @@
#not-found {
width: min(calc(var(--container-width) / 5), 100%);
}

69
sass/_post-nav.scss Normal file
View file

@ -0,0 +1,69 @@
#post-nav {
display: flex;
flex-direction: row;
gap: 0.25rem;
@media only screen and (max-width: 720px) {
flex-direction: column;
}
.post-nav-item {
flex: 1;
transition: var(--transition);
border-radius: var(--rounded-corner);
padding: 1rem;
padding-block-end: 0.75rem;
min-width: 0;
text-decoration: none;
&:hover {
box-shadow: var(--edge-highlight);
background-color: var(--accent-color-alpha);
.post-title {
color: var(--accent-color);
}
}
&:active {
transform: var(--active);
}
&.post-nav-prev .nav-arrow::before {
content: "";
:root[dir*="rtl"] & {
content: "";
}
}
&.post-nav-next {
text-align: end;
.nav-arrow::after {
content: "";
:root[dir*="rtl"] & {
content: "";
}
}
}
.nav-arrow {
margin-block-end: 0.75rem;
color: var(--fg-muted-5);
font-weight: normal;
line-height: 1;
}
.post-title {
display: block;
transition: var(--transition);
max-width: 90vw;
overflow: hidden;
color: var(--fg-color);
text-overflow: ellipsis;
white-space: nowrap;
}
}
}

98
sass/_pre-container.scss Normal file
View file

@ -0,0 +1,98 @@
.pre-container {
margin: 1rem 0 1rem;
box-shadow: var(--edge-highlight), var(--shadow);
border-radius: var(--rounded-corner);
.header {
--shimmer: rgb(
from var(--accent-color) r g b / calc(var(--color-opacity) * 2)
);
display: flex;
justify-content: space-between;
align-items: center;
border-radius: var(--rounded-corner) var(--rounded-corner) 0 0;
background-image: linear-gradient(
to right,
var(--fg-muted-1) 50%,
var(--shimmer) 75%,
var(--fg-muted-1) 100%
);
background-size: 200%;
padding: 0.25rem;
height: 2.5rem;
span {
margin-inline-start: 0.75rem;
color: var(--fg-muted-5);
font-weight: bold;
line-height: 1;
}
button {
appearance: none;
transition: var(--transition);
cursor: pointer;
border: none;
border-radius: var(--rounded-corner-small);
background-color: transparent;
padding: 0.5rem;
color: var(--fg-muted-4);
line-height: 0;
&:hover {
box-shadow: var(--edge-highlight);
background-color: var(--fg-muted-1);
color: var(--fg-muted-5);
}
&:active {
transform: var(--active);
}
&:disabled {
cursor: not-allowed;
&:active {
transform: none;
}
}
.icon {
-webkit-mask-image: var(--icon-copy);
mask-image: var(--icon-copy);
transition: var(--transition);
:root[dir*="rtl"] & {
transform: scaleX(-1);
}
}
}
&.active {
animation: active-shimmer var(--transition-long);
button {
box-shadow: var(--edge-highlight);
background-color: var(--accent-color-alpha);
color: var(--accent-color);
.icon {
-webkit-mask-image: var(--icon-done);
mask-image: var(--icon-done);
}
}
@keyframes active-shimmer {
to {
background-position-x: -200%;
}
}
}
}
pre {
margin: 0;
box-shadow: none;
border-radius: 0 0 var(--rounded-corner) var(--rounded-corner);
}
}

26
sass/_sparkline.scss Normal file
View file

@ -0,0 +1,26 @@
.sparkline {
display: flex;
flex-direction: row;
align-items: flex-end;
float: inline-end;
gap: 0.25rem;
margin: 1rem 0;
width: 6rem;
height: 2rem;
div {
flex: 1;
transform-origin: bottom;
transition: var(--transition);
background-image: linear-gradient(
to top,
var(--accent-color) var(--bar-height),
transparent var(--bar-height)
);
height: 100%;
&:hover {
height: 200%;
}
}
}

22
sass/_spoiler.scss Normal file
View file

@ -0,0 +1,22 @@
span.spoiler {
filter: blur(0.25rem);
transition: var(--transition);
&:hover,
&:active {
filter: none;
}
&.solid {
filter: none;
border-radius: var(--rounded-corner-small);
background-color: var(--fg-muted-4);
color: transparent;
&:hover,
&:active {
background-color: transparent;
color: inherit;
}
}
}

60
sass/_statements.scss Normal file
View file

@ -0,0 +1,60 @@
.statement-container {
margin: 1rem 0;
box-shadow: var(--edge-highlight);
border-radius: var(--rounded-corner);
padding: 1rem;
:last-child {
margin-block-end: 0;
}
& > :nth-child(2) {
margin-block-start: 0.5rem;
}
li::marker {
color: inherit;
}
a {
color: inherit;
}
.title {
color: inherit;
.icon {
margin-inline-end: 0.375rem;
}
}
&.archive {
background-color: var(--purple-bg);
color: var(--purple-fg);
.icon {
-webkit-mask-image: var(--icon-archive);
mask-image: var(--icon-archive);
}
}
&.disclaimer {
background-color: var(--red-bg);
color: var(--red-fg);
.icon {
-webkit-mask-image: var(--icon-warning);
mask-image: var(--icon-warning);
}
}
&.trigger {
background-color: var(--yellow-bg);
color: var(--yellow-fg);
.icon {
-webkit-mask-image: var(--icon-warning);
mask-image: var(--icon-warning);
}
}
}

24
sass/_table.scss Normal file
View file

@ -0,0 +1,24 @@
table {
box-shadow: var(--edge-highlight);
border-radius: var(--rounded-corner);
border-collapse: collapse;
background-color: var(--fg-muted-1);
width: 100%;
overflow: hidden;
tr {
&:nth-child(even) {
background-color: var(--fg-muted-1);
}
th,
td {
padding: 0.5rem 1rem;
}
th {
background-color: var(--fg-muted-2);
font-weight: bold;
}
}
}

69
sass/_tags.scss Normal file
View file

@ -0,0 +1,69 @@
.tags {
display: flex;
flex-wrap: wrap;
gap: 0.25rem;
margin: 0;
margin-block-start: 2rem;
padding: 0;
list-style: none;
li {
display: flex;
transition: var(--transition);
margin: 0;
padding: 0;
&:active {
transform: var(--active);
}
}
a {
transition: var(--transition);
box-shadow: var(--edge-highlight);
border-radius: 999px;
background-color: var(--fg-muted-1);
padding: 0.375rem 0.75rem;
color: var(--fg-muted-5);
font-size: var(--font-size-small);
line-height: 1;
text-decoration: none;
white-space: nowrap;
&:hover {
background-color: var(--accent-color-alpha);
color: var(--accent-color);
text-decoration: none;
.count {
background-color: var(--accent-color-alpha);
color: var(--accent-color);
}
}
&:has(.tag) {
padding: 0;
}
.tag {
display: inline-block;
padding-inline-start: 0.75rem;
padding-inline-end: 0.5rem;
padding-block-start: 0.375rem;
padding-block-end: 0.375rem;
}
.count {
display: inline-block;
transition: var(--transition);
border-start-end-radius: 999px;
border-end-end-radius: 999px;
background-color: var(--fg-muted-1);
padding-inline-start: 0.5rem;
padding-inline-end: 0.625rem;
padding-block-start: 0.375rem;
padding-block-end: 0.375rem;
font-variant-numeric: tabular-nums;
}
}
}

5
sass/_title.scss Normal file
View file

@ -0,0 +1,5 @@
strong.title {
color: var(--accent-color);
font-size: var(--font-size-x-large);
line-height: 1;
}

312
sass/_typography.scss Normal file
View file

@ -0,0 +1,312 @@
// HEADINGS
h1,
h2,
h3,
h4,
h5,
h6 {
text-wrap: balance;
margin: 2rem 0 1rem;
font-weight: lighter;
line-height: normal;
font-family: var(--font-antique);
letter-spacing: -0.05em;
}
h1 {
font-size: var(--font-size-xxx-large);
}
h2 {
font-size: var(--font-size-xx-large);
}
h3 {
font-size: var(--font-size-x-large);
}
h4 {
font-size: var(--font-size-large);
}
h5 {
font-size: var(--font-size-medium);
}
h6 {
font-size: var(--font-size-small);
}
small {
color: var(--fg-muted-5);
font-size: var(--font-size-small-em);
}
abbr[title] {
cursor: help;
text-decoration: underline;
text-decoration-style: dotted;
text-decoration-thickness: max(1px, 0.0625em);
}
figcaption {
color: var(--fg-muted-4);
font-size: var(--font-size-small-em);
text-align: center;
}
blockquote {
margin: 0;
border-radius: 0.25rem;
border-inline-start: 0.25rem solid var(--accent-color);
padding-inline-start: 0.75rem;
color: var(--fg-muted-5);
}
mark,
del,
ins,
samp,
q {
box-shadow: var(--edge-highlight);
border-radius: var(--rounded-corner-small);
padding: 0.125rem 0.375rem;
}
mark {
background-color: var(--accent-color-alpha);
color: var(--accent-color);
}
del {
background-color: var(--red-bg);
color: var(--red-fg);
text-decoration: line-through;
text-decoration-thickness: max(1px, 0.0625em);
}
ins {
background-color: var(--green-bg);
color: var(--green-fg);
text-decoration: underline;
text-decoration-thickness: max(1px, 0.0625em);
}
samp {
background-color: var(--fg-muted-1);
color: var(--fg-muted-5);
font-size: var(--font-size-small-em);
}
q {
background-color: var(--fg-muted-1);
color: var(--fg-muted-5);
font-style: italic;
}
u {
text-decoration: underline;
text-decoration-style: wavy;
text-decoration-color: var(--red-fg);
text-decoration-thickness: max(1px, 0.0625em);
}
progress {
appearance: none;
box-shadow: var(--edge-highlight);
border: none;
border-radius: 999px;
background-color: var(--fg-muted-1);
width: 100%;
height: 0.5rem;
color: var(--accent-color);
&:indeterminate {
background-color: var(--fg-muted-1);
}
&:indeterminate::-moz-progress-bar {
background-color: transparent;
}
&::-webkit-progress-bar {
background-color: transparent;
}
&::-moz-progress-bar {
border-radius: 999px;
background-color: var(--accent-color);
}
&::-webkit-progress-value {
border-radius: 999px;
background-color: var(--accent-color);
}
}
kbd {
display: inline-block;
transition: var(--transition);
cursor: pointer;
box-shadow:
var(--edge-highlight),
inset 0 -0.125rem 0 var(--fg-muted-2);
border-radius: var(--rounded-corner-small);
background-color: var(--fg-muted-1);
padding: 0.125rem 0.375rem;
font-size: var(--font-size-small-em);
// Small nice thingy, keys can be pressed!
&:active {
transform: translateY(0.125rem);
box-shadow: inset 0 0.0625rem 0 var(--fg-muted-2);
background-color: var(--fg-muted-2);
}
}
a {
color: var(--accent-color);
font-weight: bold;
text-decoration-thickness: max(1px, 0.0625em);
&:hover {
text-decoration-style: wavy;
}
}
hr {
margin: 2rem auto;
border: none;
border-block-start: 0.1875rem double var(--fg-muted-2);
overflow: visible;
text-align: center;
&::after {
position: relative;
inset-block-start: -1.25rem;
content: "";
color: var(--fg-muted-4);
font-size: var(--font-size-x-large);
}
}
dt {
font-weight: bold;
}
dd {
margin-inline-start: 1.5rem;
margin-block-end: 1rem;
}
aside {
float: right;
margin-inline-start: 1rem;
box-shadow: var(--edge-highlight);
border-radius: var(--rounded-corner);
background-color: var(--accent-color-alpha);
padding: 1rem;
width: 30%;
:root[dir*="rtl"] & {
float: left;
}
@media only screen and (max-width: 720px) {
float: none;
margin-inline-start: 0;
width: 100%;
}
:first-child {
margin-block-start: 0;
}
:last-child {
margin-block-end: 0;
}
}
details {
summary {
cursor: pointer;
box-shadow: var(--edge-highlight);
border-radius: var(--rounded-corner);
background-color: var(--fg-muted-1);
padding: 1rem;
color: var(--fg-muted-5);
font-weight: bold;
line-height: 1;
list-style: none;
&::marker,
&::-webkit-details-marker {
display: none;
}
&::before {
-webkit-mask-image: var(--icon-down);
display: inline-block;
vertical-align: -0.125em;
mask-image: var(--icon-down);
transition: var(--transition);
margin-inline-end: 0.25rem;
background-color: currentColor;
width: 1rem;
height: 1rem;
content: "";
}
}
:last-child {
margin-block-end: 0;
}
&[open] {
box-shadow: var(--edge-highlight);
border-radius: var(--rounded-corner);
background-color: var(--fg-muted-1);
padding: 1rem;
summary {
box-shadow: none;
border-radius: 0;
background-color: transparent;
padding: 0;
&::before {
transform: scaleY(-1);
}
& ~ * {
animation: details-open var(--transition);
@keyframes details-open {
from {
transform: translateY(-1rem);
opacity: 0;
}
}
}
}
}
}
figure {
margin-inline: 0;
}
ol,
ul {
padding-inline-start: 1.5rem;
}
li {
margin: 0.125rem 0;
padding-inline-start: 0.25rem;
&::marker {
color: var(--accent-color);
font-weight: bold;
}
}

204
sass/_variables.scss Normal file
View file

@ -0,0 +1,204 @@
@mixin theme-variables {
:root {
@content ("light");
}
[data-theme="dark"] {
@content ("dark");
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
@content ("dark");
}
}
}
@include theme-variables using ($theme) {
@if $theme == "dark" {
// COLORS
--bg-color: color-mix(in srgb, var(--accent-color) 10%, black);
--bg-overlay: linear-gradient(rgb(0 0 0 / 0.9), rgb(0 0 0 / 0.9));
--fg-color: rgb(255 255 255);
--fg-contrast: rgb(0 0 0 / 0.8);
--fg-muted-1: rgb(255 255 255 / 0.05);
--fg-muted-2: rgb(255 255 255 / 0.1);
--fg-muted-3: rgb(255 255 255 / 0.2);
--fg-muted-4: rgb(255 255 255 / 0.5);
--fg-muted-5: rgb(255 255 255 / 0.6);
--glass-bg: rgb(25 25 25 / 0.7);
--blue-fg: rgb(153 193 241);
--brown-fg: rgb(205 171 143);
--green-fg: rgb(143 240 164);
--purple-fg: rgb(220 138 221);
--red-fg: rgb(246 97 81);
--yellow-fg: rgb(248 228 92);
color-scheme: dark;
}
@else {
// COLORS
--bg-color: color-mix(in srgb, var(--accent-color) 20%, white);
--bg-overlay: linear-gradient(rgb(255 255 255 / 0.8), rgb(255 255 255 / 0.8));
--fg-color: rgb(0 0 0 / 0.8);
--fg-contrast: rgb(255 255 255);
--fg-muted-1: rgb(0 0 0 / 0.05);
--fg-muted-2: rgb(0 0 0 / 0.1);
--fg-muted-3: rgb(0 0 0 / 0.2);
--fg-muted-4: rgb(0 0 0 / 0.5);
--fg-muted-5: rgb(0 0 0 / 0.6);
--glass-bg: rgb(242 242 242 / 0.7);
--accent-color-alpha: rgb(from var(--accent-color) r g b / var(--color-opacity));
--blue-bg: rgb(from var(--blue-fg) r g b / var(--color-opacity));
--blue-fg: rgb(53 132 228);
--brown-bg: rgb(from var(--brown-fg) r g b / var(--color-opacity));
--brown-fg: rgb(99 69 44);
--green-bg: rgb(from var(--green-fg) r g b / var(--color-opacity));
--green-fg: rgb(38 162 105);
--purple-bg: rgb(from var(--purple-fg) r g b / var(--color-opacity));
--purple-fg: rgb(145 65 172);
--red-bg: rgb(from var(--red-fg) r g b / var(--color-opacity));
--red-fg: rgb(224 27 36);
--yellow-bg: rgb(from var(--yellow-fg) r g b / var(--color-opacity));
--yellow-fg: rgb(156 110 3);
// CONTRAST COLOR
--l: clamp(0, (l / 0.623 - 1) * -infinity, 1);
--a: calc(var(--l) + (var(--dim-opacity) * (1 - var(--l))));
--contrast-color: oklch(from var(--accent-color) var(--l) 0 h / var(--a));
// CONTAINERS
--container-width: 1024px;
// CORNERS
--rounded-corner: 0.75rem;
--rounded-corner-small: 0.5rem;
// FILTERS
--blur: saturate(1.8) blur(0.75rem);
// FONT SIZES
--font-size-xx-small: 0.625rem;
--font-size-x-small: 0.75rem;
--font-size-small: 0.875rem;
--font-size-medium: 1rem;
--font-size-large: 1.25rem;
--font-size-x-large: 1.5rem;
--font-size-xx-large: 2rem;
--font-size-xxx-large: 3rem;
// FONT SIZES (RELATIVE)
--font-size-xx-small-em: 0.625em;
--font-size-x-small-em: 0.75em;
--font-size-small-em: 0.875em;
--font-size-medium-em: 1em;
--font-size-large-em: 1.25em;
--font-size-x-large-em: 1.5em;
--font-size-xx-large-em: 2em;
--font-size-xxx-large-em: 3em;
// FONTS
--font-system-ui: system-ui, sans-serif;
--font-transitional: Charter, "Bitstream Charter", "Sitka Text", Cambria,
serif;
--font-old-style: "Iowan Old Style", "Palatino Linotype",
"URW Palladio L", P052, serif;
--font-humanist: Seravek, "Gill Sans Nova", Ubuntu, Calibri,
"DejaVu Sans", source-sans-pro, sans-serif;
--font-geometric-humanist: Avenir, Montserrat, Corbel, "URW Gothic",
source-sans-pro, sans-serif;
--font-classical-humanist: Optima, Candara, "Noto Sans", source-sans-pro,
sans-serif;
--font-neo-grotesque: Inter, Roboto, "Helvetica Neue", "Arial Nova",
"Nimbus Sans", Arial, sans-serif;
--font-monospace-slab-serif: "Nimbus Mono PS", "Courier New", monospace;
--font-monospace-code: ui-monospace, "Cascadia Code", "Source Code Pro",
Menlo, Consolas, "DejaVu Sans Mono", monospace;
--font-industrial: Bahnschrift, "DIN Alternate",
"Franklin Gothic Medium", "Nimbus Sans Narrow", sans-serif-condensed,
sans-serif;
--font-rounded-sans: ui-rounded, "Hiragino Maru Gothic ProN", Quicksand,
Comfortaa, Manjari, "Arial Rounded MT", "Arial Rounded MT Bold",
Calibri, source-sans-pro, sans-serif;
--font-slab-serif: Rockwell, "Rockwell Nova", "Roboto Slab",
"DejaVu Serif", "Sitka Small", serif;
--font-antique: Superclarendon, "Bookman Old Style", "URW Bookman",
"URW Bookman L", "Georgia Pro", Georgia, serif;
--font-didone: Didot, "Bodoni MT", "Noto Serif Display",
"URW Palladio L", P052, Sylfaen, serif;
--font-handwritten: "Segoe Print", "Bradley Hand", Chilanka, TSCu_Comic,
casual, cursive;
--font-emoji: "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol",
"Noto Color Emoji";
// OPACITY
--color-opacity: 0.1;
--dim-opacity: 0.8;
--disabled-opacity: 0.6;
// SHADOWS
--edge-highlight: inset 0 0.0625rem 0 rgb(255 255 255 / 0.1);
--shadow: 0 0 0 0.0625rem rgb(0 0 0 / 0.03),
0 0.0625rem 0.1875rem 0.0625rem rgb(0 0 0 / 0.07),
0 0.125rem 0.375rem 0.125rem rgb(0 0 0 / 0.03);
--shadow-raised: 0 0 0 0.0625rem rgb(0 0 0 / 0.06),
0 0.125rem 0.375rem 0.125rem rgb(0 0 0 / 0.14),
0 0.25rem 0.75rem 0.25rem rgb(0 0 0 / 0.06);
--shadow-glass: 0 0.75rem 1.5rem -1rem rgb(0 0 0 / 0.5);
--shadow-glow: 0 0 0 0.0625rem var(--accent-color-alpha),
0 0.125rem 0.375rem 0.125rem var(--accent-color-alpha),
0 0.25rem 1.5rem 0.25rem var(--accent-color-alpha);
--text-shadow-glow: var(--accent-color-alpha) 0 0 0.25rem,
var(--accent-color) 0 0 0.75rem;
// STATES
--hover: scale(1.1);
--active: scale(0.9);
// TRANSITIONS
--transition: 0.2s;
--transition-longer: 0.4s;
--transition-long: 0.8s;
// ICONS (16px)
--icon-archive: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath d='M2 1C.89 1 0 1.89 0 3v2c0 .751.41 1.402 1.018 1.744A2 2 0 0 0 1 7v7c0 1.11.89 2 2 2h10c1.11 0 2-.89 2-2V7a2 2 0 0 0-.018-.256A2 2 0 0 0 16 5V3c0-1.11-.89-2-2-2zm0 2h12v2H2zm1 4h10v7H3zm3 1v1h4V8z'/%3E%3C/svg%3E");
--icon-backlink: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath d='M5 14a1 1 0 0 1-.707-.293l-4-4a1 1 0 0 1 0-1.414l4-4a1 1 0 1 1 1.414 1.414L3.414 8H12c1.117 0 2-.883 2-2s-.883-2-2-2c-.55 0-1-.45-1-1s.45-1 1-1c2.2 0 4 1.8 4 4s-1.8 4-4 4H3.414l2.293 2.293A1 1 0 0 1 5 14m0 0'/%3E%3C/svg%3E");
--icon-boosts: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath d='M8 1v2H4C1.8 3 0 4.8 0 7v2a4 4 0 0 0 1.02 2.672 1 1 0 1 0 1.488-1.336A1.97 1.97 0 0 1 2 9V7c0-1.125.875-2 2-2h4v2h1v-.008a1 1 0 0 0 .707-.285l2-2a1 1 0 0 0 0-1.414l-2-2A1 1 0 0 0 9 1.008V1zm6.29 3a1 1 0 0 0-.72.258.993.993 0 0 0-.078 1.41c.317.355.508.816.508 1.34v2c0 1.125-.875 2-2 2H8V9H7v.008a1 1 0 0 0-.707.285l-2 2a1 1 0 0 0 0 1.414l2 2c.187.184.441.29.707.285V15h1v-1.992h4c2.2 0 4-1.805 4-4v-2a4 4 0 0 0-1.02-2.676A1 1 0 0 0 14.29 4m0 0'/%3E%3C/svg%3E");
--icon-bug: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath d='M5 0c-.55 0-1 .45-1 1 0 .691.39 1.285.8 1.605C4.298 3.277 4 4.105 4 5H3V4H1v1c0 .832.563 1.523 1.05 1.77.493.246.95.23.95.23h1v1H1v2h3v1H3s-.457-.016-.95.23C1.563 11.477 1 12.168 1 13v3h2v-3h1.129c.45 1.719 2.016 3 3.871 3s3.422-1.281 3.871-3H13v3h2v-3c0-.832-.562-1.523-1.05-1.77-.493-.246-.95-.23-.95-.23h-1v-1h3V8h-3V7h1s.457.016.95-.23C14.437 6.523 15 5.832 15 5V4h-2v1h-1c0-.895-.297-1.723-.8-2.395.41-.32.8-.914.8-1.605 0-.55-.45-1-1-1s-1 .45-1 1a1 1 0 0 0-.645.238C8.93 1.086 8.473 1 8 1s-.93.086-1.355.238A1 1 0 0 0 6 1c0-.55-.45-1-1-1m2.969 5.031h.062A.97.97 0 0 1 9 6v3.063a.97.97 0 0 1-.969.968H7.97A.97.97 0 0 1 7 9.062V6a.97.97 0 0 1 .969-.969m0 6h.062A.97.97 0 0 1 9 12v.063a.97.97 0 0 1-.969.968H7.97a.97.97 0 0 1-.97-.969V12a.97.97 0 0 1 .969-.969m0 0'/%3E%3C/svg%3E");
--icon-caution: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath d='M5 0a1 1 0 0 0-.707.293l-4 4A1 1 0 0 0 0 5v6a1 1 0 0 0 .293.707l4 4A1 1 0 0 0 5 16h6a1 1 0 0 0 .707-.293l4-4A1 1 0 0 0 16 11V5a1 1 0 0 0-.293-.707l-4-4A1 1 0 0 0 11 0zm.414 2h5.172L14 5.414v5.172L10.586 14H5.414L2 10.586V5.414zM8 4c-.554 0-1 .446-1 1v3c0 .554.446 1 1 1s1-.446 1-1V5c0-.554-.446-1-1-1m0 6a1 1 0 0 0-1 1 1 1 0 0 0 1 1 1 1 0 0 0 1-1 1 1 0 0 0-1-1'/%3E%3C/svg%3E");
--icon-checkmark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath d='M13.754 4.668c.176-.2.262-.461.246-.723a1 1 0 0 0-.34-.687 1 1 0 0 0-.726-.246 1 1 0 0 0-.688.34L5.95 10.547 3.707 8.3A1 1 0 0 0 2 9.01a1 1 0 0 0 .293.708l3 3c.195.195.465.3.742.293.278-.012.535-.133.719-.344zm0 0'/%3E%3C/svg%3E");
--icon-copy: url("data:image/svg+xml,%3Csvg viewBox='0 0 16 16' height='16' width='16' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0 3c0-1.645 1.355-3 3-3h5c1.645 0 3 1.355 3 3 0 .55-.45 1-1 1s-1-.45-1-1c0-.57-.43-1-1-1H3c-.57 0-1 .43-1 1v5c0 .57.43 1 1 1 .55 0 1 .45 1 1s-.45 1-1 1c-1.645 0-3-1.355-3-3zm5 5c0-1.645 1.355-3 3-3h5c1.645 0 3 1.355 3 3v5c0 1.645-1.355 3-3 3H8c-1.645 0-3-1.355-3-3zm2 0v5c0 .57.43 1 1 1h5c.57 0 1-.43 1-1V8c0-.57-.43-1-1-1H8c-.57 0-1 .43-1 1m0 0'/%3E%3C/svg%3E");
--icon-done: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath d='M7.883 0q-.486.008-.965.074a7.98 7.98 0 0 0-4.602 2.293 8.01 8.01 0 0 0-1.23 9.664 8.015 8.015 0 0 0 9.02 3.684 8 8 0 0 0 5.89-7.75 1 1 0 1 0-2 .008 5.986 5.986 0 0 1-4.418 5.816 5.996 5.996 0 0 1-6.762-2.766 5.99 5.99 0 0 1 .922-7.25 5.99 5.99 0 0 1 7.239-.984 1 1 0 0 0 1.363-.371c.273-.48.11-1.09-.371-1.367A8 8 0 0 0 9.492.14 8 8 0 0 0 7.882 0m7.15 1.998-.1.002a1 1 0 0 0-.687.34L7.95 9.535 5.707 7.29A1 1 0 0 0 4 8a1 1 0 0 0 .293.707l3 3c.195.195.465.3.742.293.277-.012.535-.133.719-.344l7-8A1 1 0 0 0 16 2.934a1 1 0 0 0-.34-.688 1 1 0 0 0-.627-.248'/%3E%3C/svg%3E");
--icon-down: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath d='m2.293 6.707 5 5a1 1 0 0 0 1.414 0l5-5a1 1 0 1 0-1.414-1.414L8 9.586 3.707 5.293a1 1 0 1 0-1.414 1.414m0 0'/%3E%3C/svg%3E");
--icon-feed: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath d='M1.988 1.988V3c.008.547.453.984 1 .988.004-.004.008-.004.012-.004v.028A8.977 8.977 0 0 1 11.988 13a.991.991 0 0 0 1 .984h1V13h-.004c0-.004 0-.004.004-.008C13.984 7.02 9.184 2.148 3.242 2.02A1.004 1.004 0 0 0 3 1.988v-.004zm0 4V7c.008.547.453.984 1 .988.004-.004.008-.004.012-.004V8a4.985 4.985 0 0 1 4.996 4.844 1.002 1.002 0 0 0 .988 1.145c.008-.005.012-.005.016-.005v.004h.984V13H10c0-3.793-3.047-6.898-6.82-6.992 0-.004-.004-.004-.004-.004A.892.892 0 0 0 3 5.988v-.004zm2 4a1.999 1.999 0 1 0-.002 3.998 1.999 1.999 0 0 0 .002-3.998m0 0'%3E%3C/path%3E%3C/svg%3E");
--icon-fire: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath d='M9.184.973C4.957 3.113 5.586 6.273 7 9 5.762 7.36 5.016 5.48 5 4 3.375 5 2 7 2 9a6.005 6.005 0 0 0 3.969 5.648c-.492-.84-.367-1.96.703-3.015 2.14-2.11 1.558-2.735 1.558-2.735 2.266 1.149-.32 3.137.418 3.88.563.558 1.758-1.391 1.77-2.184.848.96 1.246 1.676 1.246 2.75q0 .273-.035.52c2.512-1.805 3.273-5.41.46-8.184C8.095 1.742 9.185.973 9.185.973m0 0'/%3E%3C/svg%3E");
--icon-first: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath d='M3 2v12h2V8.414l5.293 5.293a1 1 0 1 0 1.414-1.414L7.414 8l4.293-4.293a1 1 0 1 0-1.414-1.414L5 7.586V2zm0 0'/%3E%3C/svg%3E");
--icon-git: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath d='M8 0a1 1 0 0 0-.707.293L5.707 1.875l5.238 5.234c.176-.07.364-.109.555-.109A1.5 1.5 0 1 1 10 8.5q0-.276.102-.535L8.05 5.915v4.19a1.5 1.5 0 1 1-1-.035V4.914L4.859 2.727.293 7.293a1 1 0 0 0 0 1.414l7 7a1 1 0 0 0 1.414 0l7-7a1.007 1.007 0 0 0 0-1.414l-7-7A1 1 0 0 0 8 0m0 0'/%3E%3C/svg%3E");
--icon-home: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath d='M8 .361a2.2 2.2 0 0 0-1.41.51L2.129 4.59A3.14 3.14 0 0 0 1 7v6c0 1.66 1.34 3 3 3h8c1.66 0 3-1.34 3-3V7c0-.93-.414-1.813-1.129-2.41L9.411.87A2.2 2.2 0 0 0 8 .361m0 2.002a.2.2 0 0 1 .129.047l4.46 3.719c.263.215.411.535.411.871v6c0 .547-.453 1-1 1h-2V9c0-.55-.45-1-1-1H7c-.55 0-1 .45-1 1v5H4c-.547 0-1-.453-1-1V7c0-.336.148-.656.41-.871L7.871 2.41A.2.2 0 0 1 8 2.363'/%3E%3C/svg%3E");
--icon-important: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath d='M3 0C1.355 0 0 1.355 0 3v7c0 1.256.893 2.14 2 2.584V15a1 1 0 0 0 1.707.707L6.414 13H13c1.645 0 3-1.355 3-3V3c0-1.645-1.355-3-3-3zm0 2h10c.571 0 1 .429 1 1v7c0 .571-.429 1-1 1H6a1 1 0 0 0-.707.293L4 12.586V12a1 1 0 0 0-1-1c-.571 0-1-.429-1-1V3c0-.571.429-1 1-1m5 1c-.554 0-1 .446-1 1v2c0 .554.446 1 1 1s1-.446 1-1V4c0-.554-.446-1-1-1m0 5a1 1 0 0 0-1 1 1 1 0 0 0 1 1 1 1 0 0 0 1-1 1 1 0 0 0-1-1'/%3E%3C/svg%3E");
--icon-info: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath d='M8 0C3.59 0 0 3.59 0 8s3.59 8 8 8 8-3.59 8-8-3.59-8-8-8m0 2c3.332 0 6 2.668 6 6s-2.668 6-6 6-6-2.668-6-6 2.668-6 6-6m0 1.875a1.125 1.125 0 1 0 0 2.25 1.125 1.125 0 0 0 0-2.25M6.477 7A.5.5 0 0 0 6.5 8H7v3h-.5a.499.499 0 1 0 0 1h3a.499.499 0 1 0 0-1H9V7zm0 0'/%3E%3C/svg%3E");
--icon-languages: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath d='M3.98 1v3H1v2h2.947a4.8 4.8 0 0 1-.592 1.871c-.425.758-1.101 1.488-2.062 2.45l1.414 1.413c.92-.92 1.703-1.728 2.283-2.697.38.632.844 1.196 1.377 1.768l.668-2.309a6 6 0 0 1-.41-.625A4.75 4.75 0 0 1 6.033 6h1.53l.511-2H6V1zm5.24 1L6 15h2l.781-3h4.438L14 15h2L12.781 2zm1.562 2h.438l1.5 6H9.28z'/%3E%3C/svg%3E");
--icon-last: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath d='M11 2v5.586L5.707 2.293a1 1 0 1 0-1.414 1.414L8.586 8l-4.293 4.293a1 1 0 1 0 1.414 1.414L11 8.414V14h2V2zm0 0'/%3E%3C/svg%3E");
--icon-lightbulb: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath d='M7.996 0C5.16 0 2.703 2 2.125 4.777c-.527 2.535.688 5.036 2.871 6.325L5 12a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1v-.898c2.184-1.293 3.402-3.797 2.871-6.332A6.01 6.01 0 0 0 7.996 0m0 2a3.994 3.994 0 0 1 3.918 3.18 3.99 3.99 0 0 1-2.312 4.484 1.01 1.01 0 0 0-.602.914V11H6.996v-.418a1 1 0 0 0-.598-.914 3.994 3.994 0 0 1-2.316-4.484A3.99 3.99 0 0 1 7.996 2m-.998 4a.5.5 0 0 0-.354.852l1 1a.5.5 0 0 0 .708 0l1-1a.5.5 0 0 0 0-.707.507.507 0 0 0-.707 0l-.649.648-.644-.648A.5.5 0 0 0 6.998 6M6 14v1c0 .555.445 1 1 1h2c.555 0 1-.445 1-1v-1z'/%3E%3C/svg%3E");
--icon-link: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath d='M4 4C1.8 4 0 5.8 0 8s1.8 4 4 4v-2c-1.125 0-2-.875-2-2s.875-2 2-2h3c1.125 0 2 .875 2 2 0 .84-.496 1.535-1.207 1.84l.785 1.84A4 4 0 0 0 11 8c0-2.2-1.8-4-4-4zm8 2v2c1.125 0 2 .875 2 2s-.875 2-2 2H9c-1.125 0-2-.875-2-2 0-.828.484-1.516 1.184-1.828l-.817-1.828A4.01 4.01 0 0 0 5 10c0 2.2 1.8 4 4 4h3c2.2 0 4-1.8 4-4s-1.8-4-4-4'/%3E%3C/svg%3E");
--icon-next: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath d='m6.707 13.707 5-5a1 1 0 0 0 0-1.414l-5-5a1 1 0 1 0-1.414 1.414L9.586 8l-4.293 4.293a1 1 0 1 0 1.414 1.414m0 0'/%3E%3C/svg%3E");
--icon-pencil: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath d='M12.277.832c-.578.008-1.168.23-1.691.754l-9 9A2 2 0 0 0 1 12v3h3c.531 0 1.04-.21 1.414-.586l9-9c1.79-1.789.082-4.39-1.89-4.57-.083-.012-.165-.012-.247-.012M10.5 4.437 11.563 5.5 4.5 12.563 3.438 11.5zm0 0'/%3E%3C/svg%3E");
--icon-poop: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath d='M8.963 1.514A4 4 0 0 1 5 5H4c-1.108 0-2 .892-2 2s.892 2 2 2H3c-1.662 0-3 1.338-3 3s1.338 3 3 3h10c1.662 0 3-1.338 3-3s-1.338-3-3-3h-1c1.108 0 2-.892 2-2s-.892-2-2-2h-1a4 4 0 0 0-2.037-3.486'/%3E%3C/svg%3E");
--icon-previous: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath d='m9.293 13.707-5-5a1 1 0 0 1 0-1.414l5-5a1 1 0 1 1 1.414 1.414L6.414 8l4.293 4.293a1 1 0 1 1-1.414 1.414m0 0'/%3E%3C/svg%3E");
--icon-right: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath d='m5.707 1.293 6 6a1 1 0 0 1 0 1.414l-6 6a1 1 0 1 1-1.414-1.414L9.586 8 4.293 2.707a1 1 0 1 1 1.414-1.414m0 0'/%3E%3C/svg%3E");
--icon-search: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath d='M6.57.063c-3.578 0-6.5 2.921-6.5 6.5 0 3.578 2.922 6.5 6.5 6.5a6.46 6.46 0 0 0 3.83-1.256l2.975 2.974c.957.938 2.363-.5 1.406-1.437l-2.96-2.961a6.46 6.46 0 0 0 1.25-3.82c0-3.579-2.923-6.5-6.5-6.5m0 2c2.5 0 4.5 2.003 4.5 4.5 0 2.5-2 4.5-4.5 4.5-2.496 0-4.5-2-4.5-4.5 0-2.497 2.004-4.5 4.5-4.5'/%3E%3C/svg%3E");
--icon-share: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath d='M8 1a1 1 0 0 0-.5.135 1 1 0 0 0-.207.158l-3 3a1 1 0 0 0 0 1.414 1 1 0 0 0 1.414 0L7 4.414V10a1 1 0 0 0 1 1 1 1 0 0 0 1-1V4.414l1.293 1.293a1 1 0 0 0 1.414 0 1 1 0 0 0 0-1.414L8.738 1.326 8.7 1.287a1 1 0 0 0-.195-.15l-.008-.004a1 1 0 0 0-.236-.098h-.004A1 1 0 0 0 8 1M4 7c-1.645 0-3 1.355-3 3v2c0 1.645 1.355 3 3 3h8c1.645 0 3-1.355 3-3v-2c0-1.645-1.355-3-3-3a1 1 0 0 0 0 2c.564 0 1 .436 1 1v2c0 .564-.436 1-1 1H4c-.564 0-1-.436-1-1v-2c0-.564.436-1 1-1a1 1 0 0 0 0-2'/%3E%3C/svg%3E");
--icon-star: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath d='M8 0a1 1 0 0 0-.95.684l-1.448 4.34-4.59-.016C.032 5.004-.371 6.266.43 6.828l3.625 2.555-1.5 4.285c-.317.902.687 1.691 1.492 1.172l4.004-2.594 3.894 2.586c.801.531 1.817-.258 1.5-1.16l-1.504-4.29 3.645-2.577c.789-.563.394-1.809-.574-1.813l-4.66-.015L8.949.69A1 1 0 0 0 8 0m0 0'/%3E%3C/svg%3E");
--icon-theme-dark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath d='M.918 8.004a7.072 7.072 0 0 0 14.102.793 1.01 1.01 0 0 0-.457-.957 1 1 0 0 0-1.063-.004 3.9 3.9 0 0 1-2.031.578 3.89 3.89 0 0 1-3.883-3.883c0-.715.203-1.422.578-2.031a1 1 0 0 0-.004-1.062c-.207-.32-.578-.5-.957-.458A7.07 7.07 0 0 0 .918 8.004M5.586 4.53a5.877 5.877 0 0 0 8.965 5.004l-1.52-.96a5.09 5.09 0 0 1-5.035 4.507 5.09 5.09 0 0 1-5.078-5.078 5.09 5.09 0 0 1 4.508-5.035l-.961-1.52a5.9 5.9 0 0 0-.88 3.082m0 0'/%3E%3C/svg%3E");
--icon-theme-light: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath d='M8.004-.008a1 1 0 0 0-1 1v1a1 1 0 1 0 2 0v-1c0-.554-.445-1-1-1M3.053 2.035a1 1 0 0 0-.26.035.994.994 0 0 0-.45 1.672l.708.707a1 1 0 1 0 1.414-1.414l-.707-.707a1 1 0 0 0-.705-.293m9.9.012a1 1 0 0 0-.707.293l-.707.707a1 1 0 1 0 1.414 1.414l.707-.707a1 1 0 0 0-.707-1.707M8 4C5.785 4 4 5.785 4 8s1.785 4 4 4 4-1.785 4-4-1.785-4-4-4m0 2c1.098 0 2 .902 2 2s-.902 2-2 2-2-.902-2-2 .902-2 2-2m-7.004.984a1 1 0 1 0 0 2h1a1 1 0 1 0 0-2zM14 7c-.55 0-1 .45-1 1s.45 1 1 1h1c.55 0 1-.45 1-1s-.45-1-1-1zM3.748 11.234a1 1 0 0 0-.705.293l-.711.707a1.007 1.007 0 0 0 0 1.414c.39.391 1.027.391 1.418 0l.707-.707a1 1 0 0 0-.709-1.707m8.49.006q-.131 0-.261.033a1.01 1.01 0 0 0-.707.711 1 1 0 0 0 .261.965l.707.707a.995.995 0 0 0 1.672-.445 1 1 0 0 0-.258-.969l-.707-.707a1 1 0 0 0-.707-.295m-4.246 1.756c-.554 0-1 .445-1 1v1a1 1 0 1 0 2 0v-1a1 1 0 0 0-1-1'/%3E%3C/svg%3E");
--icon-theme-system: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath d='M8 0C3.594 0 0 3.594 0 8s3.594 8 8 8 8-3.594 8-8-3.594-8-8-8m0 1.941c3.36 0 6.059 2.7 6.059 6.059s-2.7 6.059-6.059 6.059zm0 0'/%3E%3C/svg%3E");
--icon-toc: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath d='M2 3a1 1 0 1 0 0 2 1 1 0 0 0 0-2m4 0c-.554 0-1 .446-1 1s.446 1 1 1h8c.554 0 1-.446 1-1s-.446-1-1-1ZM2 7a1 1 0 1 0 0 2 1 1 0 0 0 0-2m4 0c-.554 0-1 .446-1 1s.446 1 1 1h8c.554 0 1-.446 1-1s-.446-1-1-1Zm-4 4a1 1 0 1 0 0 2 1 1 0 0 0 0-2m4 0c-.554 0-1 .446-1 1s.446 1 1 1h8c.554 0 1-.446 1-1s-.446-1-1-1z'/%3E%3C/svg%3E");
--icon-top: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath d='M1 11a1 1 0 0 1 .293-.707l6-6a1 1 0 0 1 1.414 0l6 6a1 1 0 1 1-1.414 1.414L8 6.414l-5.293 5.293A1 1 0 0 1 1 11m0 0'/%3E%3C/svg%3E");
--icon-verified: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath d='M5.21 1.27A3.7 3.7 0 0 1 8 0c1.113 0 2.11.492 2.79 1.27a3.68 3.68 0 0 1 2.866 1.074A3.68 3.68 0 0 1 14.73 5.21C15.54 5.914 16 6.93 16 8s-.46 2.086-1.27 2.79a3.68 3.68 0 0 1-1.074 2.866 3.68 3.68 0 0 1-2.867 1.074C10.086 15.54 9.07 16 8 16s-2.086-.46-2.79-1.27a3.68 3.68 0 0 1-2.866-1.074A3.68 3.68 0 0 1 1.27 10.79 3.7 3.7 0 0 1 0 8c0-1.113.492-2.11 1.27-2.79a3.68 3.68 0 0 1 1.074-2.866A3.68 3.68 0 0 1 5.21 1.27m5.75 5.242a.613.613 0 0 0-.437-.98.61.61 0 0 0-.562.265L7.305 9.512 5.973 8.18a.616.616 0 0 0-.868.87l1.844 1.845a.61.61 0 0 0 .485.18.63.63 0 0 0 .453-.255zm0 0'/%3E%3C/svg%3E");
--icon-warning: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath d='M8 .844c-.907 0-1.814.444-2.291 1.334l-.002.002-5.178 9.697C-.45 13.704.928 16.002 3 16h10c2.072.002 3.45-2.296 2.47-4.123L10.294 2.18l-.002-.002C9.814 1.288 8.907.844 8 .844M8 2.77c.201 0 .403.118.53.353l5.177 9.697.002.002c.307.573-.057 1.18-.707 1.178H2.998c-.65.001-1.014-.605-.707-1.178l.002-.002 5.18-9.699c.126-.233.327-.351.527-.351M8 5c-.554 0-1 .446-1 1v3c0 .554.446 1 1 1s1-.446 1-1V6c0-.554-.446-1-1-1m0 6a1 1 0 0 0-1 1 1 1 0 0 0 1 1 1 1 0 0 0 1-1 1 1 0 0 0-1-1'/%3E%3C/svg%3E");
// MINI ICONS (12px)
--icon-external: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' %3E%3Cpath d='m6 6 5-5M7 1h4v4M4 2H3a2 2 0 0 0-2 2v5a2 2 0 0 0 2 2h5a2 2 0 0 0 2-2V8' style='fill:none;stroke:black;stroke-linejoin:round;stroke-linecap:round;stroke-width:2'/%3E%3C/svg%3E");
}
}

View file

@ -0,0 +1,9 @@
.visually-hidden {
clip: rect(0 0 0 0);
position: absolute !important;
clip-path: inset(100%);
width: 1px !important;
height: 1px !important;
overflow: hidden;
white-space: nowrap;
}

Some files were not shown because too many files have changed in this diff Show more