feat: new website design and update to Vegvisir 3.1 (#2)

Brand new design for the website following the new design language I used for [version 2.0 of my personal website](https://codeberg.org/vlw/vlw.se/releases/tag/2.0.0).

Reviewed-on: https://codeberg.org/vegvisir/website/pulls/2
Co-authored-by: vlw <victor@vlw.se>
Co-committed-by: vlw <victor@vlw.se>
This commit is contained in:
Victor Westerlund 2025-03-05 11:16:54 +00:00 committed by Victor Westerlund
parent e5a0a8169c
commit 7a8fc36ec0
236 changed files with 2822 additions and 2480 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
vegvisir

3
.gitmodules vendored Normal file
View file

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

8
Consts.php Normal file
View file

@ -0,0 +1,8 @@
<?php
namespace Website;
/**
* # Docs
*/
const VERSION_LATEST = "/public/docs/v3.1.4/";

View file

@ -1,9 +1,18 @@
# Vegvisir Website # Vegvisir Website
Source code for the [Vegvisir website](https://vegvisir.vlw.se). The website is build from the ground up using the [Vegvisir](https://codeberg.org/vegvisir/vegvisir) web framework to highlight the framework's features. Source code for the [Vegvisir website](https://vegvisir.vlw.se). The website is build from the ground up using the [Vegvisir](https://codeberg.org/vegvisir/vegvisir) web framework to provide documentation and highlight the framework's features.
# Installation # Installation
To run this website locally, follow the [installation instructions for the Vegvisir framework](https://vegvisir.vlw.se/docs/installation). After that, all you have to do is clone this repo and point Vegvisir to the cloned folder. 1. Clone this repo
```sh
git clone https://codeberg.org/vegvisir/website
```
No additional steps are required! 2. Run the [Vegvisir install script](https://codeberg.org/vegvisir/install) from the root directory of the repo
```sh
cd /path/to/website
```
```sh
curl -fsSL https://href.vlw.se/vegvisir/install | bash
```

View file

@ -1,12 +0,0 @@
<style><?= VV::css("modules/docs/contribute/style.css") ?></style>
<section class="contribute">
<container>
<h1>Contribute? 💕</h1>
<p>Have you found a problem om this page? Would you like to help make this little project a bit better?</p>
<ul>
<li><a href="/"><button class="solid"><p>Quick-report a problem on this page</p></button></a></li>
<li><a href="/"><button class="shade"><p>Report a bug</p></button></a></li>
<li><a href="/"><button class="shade"><p>Learn how to contribute</p></button></a></li>
</ul>
</container>
</section>

View file

@ -1,29 +0,0 @@
section.contribute {
grid-area: contribute;
width: 100%;
background-color: rgba(var(--primer-color-deep), .1);
}
section.contribute container {
justify-content: center;
flex-direction: column;
}
section.contribute ul {
display: flex;
padding: var(--padding);
list-style: none;
gap: var(--padding);
}
/* # Size queries */
@media (max-width: 950px) {
section.contribute ul {
flex-direction: column;
}
section.contribute button {
width: 100%;
}
}

View file

@ -1,24 +0,0 @@
<ul>
<li><a href="/docs/API/JS/Navigation"><button><p><code>vegvisir.Navigation</code></p></button></a></li>
<ul>
<p>Instance variables</p>
<li><a href="/docs/API/JS/Navigation/abort"><button><p><code>Navigation.abort</code></p></button></a></li>
<li><a href="/docs/API/JS/Navigation/options"><button><p><code>Navigation.options</code></p></button></a></li>
</ul>
<ul>
<p>Instance methods</p>
<li><a href="/docs/API/JS/Navigation/constructor"><button><p><code>Navigation.constructor</code></p></button></a></li>
<li><a href="/docs/API/JS/Navigation/navigate"><button><p><code>Navigation.navigate()</code></p></button></a></li>
</ul>
<ul>
<p>Static variables</p>
<li><a href="/docs/API/JS/Navigation/MODE"><button><p><code>vegvisir.Navigation.MODE</code></p></button></a></li>
<li><a href="/docs/API/JS/Navigation/TARGET"><button><p><code>vegvisir.Navigation.TARGET</code></p></button></a></li>
<li><a href="/docs/API/JS/Navigation/EVENTS"><button><p><code>vegvisir.Navigation.EVENTS</code></p></button></a></li>
<li><a href="/docs/API/JS/Navigation/POSITION"><button><p><code>vegvisir.Navigation.POSITION</code></p></button></a></li>
</ul>
<ul>
<p>Static methods</p>
<li><a href="/docs/API/JS/Navigation/bindElements"><button><p><code>vegvisir.Navigation.bindElements()</code></p></button></a></li>
</ul>
</ul>

View file

@ -1,12 +0,0 @@
<ul>
<li><a href="/docs/API/PHP/VV"><button><p><code>VV</code></p></button></a></li>
<ul>
<p>Static methods</p>
<li><a href="/docs/API/PHP/VV/js"><button><p><code>VV::js()</code></p></button></a></li>
<li><a href="/docs/API/PHP/VV/css"><button><p><code>VV::css()</code></p></button></a></li>
<li><a href="/docs/API/PHP/VV/root"><button><p><code>VV::root()</code></p></button></a></li>
<li><a href="/docs/API/PHP/VV/embed"><button><p><code>VV::embed()</code></p></button></a></li>
<li><a href="/docs/API/PHP/VV/shell"><button><p><code>VV::shell()</code></p></button></a></li>
<li><a href="/docs/API/PHP/VV/include"><button><p><code>VV::include()</code></p></button></a></li>
</ul>
</ul>

View file

@ -1,33 +0,0 @@
<?php
enum Snippet: string {
// Path relative from root
private const REL_BASE_PATH = "modules/snippet/snippets/";
// Languages
case JAVASCRIPT = ".js";
case CSS = ".css";
case PLAINTEXT = ".txt";
case PHP = ".php";
public static function put(string $name, self $lang): string {
// Create path relative from base with filename and language extension (from enum value)
$path = VV::root(self::REL_BASE_PATH . $name . $lang->value);
// Bail out if snippet can't be found
if (!is_file($path)) {
return "<pre><code class='language-plaintext'>!REF#</code></pre>";
}
// Use enum name in lowercase as highlight.js lanuage definer
$output = "<pre><code class='language-" . strtolower($lang->name) . "'>";
$output .= htmlspecialchars(file_get_contents($path));
$output .= "</code></pre>";
return $output;
}
}
?>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/github-dark.min.css"/>
<script type="module"><?= VV::js("modules/snippet/snippet.js") ?></script>

View file

@ -1,19 +0,0 @@
import hljs from "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/es/highlight.min.js";
import php from "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/es/languages/php.min.js";
import css from "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/es/languages/css.min.js";
import plaintext from "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/es/languages/plaintext.min.js";
import javascript from "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/es/languages/javascript.min.js";
hljs.registerLanguage("php", php);
hljs.registerLanguage("css", css);
hljs.registerLanguage("plaintext", plaintext);
hljs.registerLanguage("javascript", javascript);
// Initialize syntax highlighting for all code snippets on this page
const highlightElements = () => {
[...document.querySelectorAll("pre code")].forEach(element => hljs.highlightElement(element));
}
document.querySelector("[vv-shell-id='6ccb0465']").addEventListener(vegvisir.Navigation.EVENTS.FINISHED, () => highlightElements);
highlightElements();

View file

@ -1,3 +0,0 @@
{
target: HTMLElement
}

View file

@ -1,4 +0,0 @@
static Navigation.EVENTS = {
STARTED: "navstarted",
FINISHED: "navfinished"
}

View file

@ -1,3 +0,0 @@
document.addEventListener(vegvisir.Navigation.EVENTS.STARTED, (event) => {
console.log(event.detail.target);
});

View file

@ -1,4 +0,0 @@
static Navigation.MODE = {
REPLACE: "replace",
INSERT: "insert"
}

View file

@ -1,6 +0,0 @@
static Navigation.POSITION = {
AFTEREND: "afterend",
BEFOREEND: "beforeend",
AFTERBEGIN: "afterbegin",
BEFOREBEGIN: "beforebegin"
}

View file

@ -1,6 +0,0 @@
static Navigation.TARGET = {
TOP: "_top",
SELF: "_self",
BLANK: "_blank",
PARENT: "_parent"
};

View file

@ -1 +0,0 @@
<a href="/some-page" target="_self">Click here to replace this button with the contents of /some-page</a>

View file

@ -1,4 +0,0 @@
<div vv-loading="false" vv-page="/">
<p>...</p>
<a href="/some-page" target="_parent">Click here to replace this button and its siblings with /some-page</a>
</a>

View file

@ -1 +0,0 @@
Navigation.abort: AbortController

View file

@ -1 +0,0 @@
static Navigation.bindElements(): void

View file

@ -1,5 +0,0 @@
// The programatically appended anchor tag will not have Vegvisir event listeners on it yet
document.body.appendChild(document.createElement("a"));
// It will have Vegvisir event listeners after this method is called
vegvisir.Navigation.bindElements();

View file

@ -1,4 +0,0 @@
new globalThis.vegvisir.Navigation(
URL|String url = window.location,
Object options = vegvisir.Navigation.options
): Navigation

View file

@ -1,2 +0,0 @@
// Pathname string with leading slash
const nav = new vegvisir.Navigation("/some-page");

View file

@ -1,2 +0,0 @@
// Pathname string without leading slash and a search parameter
const nav = new vegvisir.Navigation("some-page?foo=bar");

View file

@ -1,6 +0,0 @@
// URL object with a pathname and search parameter
const url = new URL(window.location);
url.pathname = "/some-page";
url.searchParams.set("foo", "bar");
const nav = new vegvisir.Navigation(url);

View file

@ -1,4 +0,0 @@
const nav = new vegvisir.Navigation("/some-page");
// Will navigate the top shell to /some-page
nav.navigate();

View file

@ -1,5 +0,0 @@
const target = document.querySelector("#target");
const nav = new vegvisir.Navigation("/some-page");
// Will replace the contents of an element with id #target with the contents of /some-page
nav.navigate(target);

View file

@ -1,8 +0,0 @@
const target = document.querySelector("#target");
const nav = new vegvisir.Navigation("/some-page");
// Will place the contents of /some-page after the target element as a sibling
nav.navigate(target, vegvisir.Navigation.POSITION.AFTEREND);
// Tip: You can also pass an insertAdjacentElement position string directly
nav.navigate(target, "afterend");

View file

@ -1,5 +0,0 @@
Navigation.navigate(
target: HTMLELement = Navigation.#rootShellElement
position: Navigation.POSITION = Navigation.POSITION.BEFOREEND,
mode: Navigation.MODE = Navigation.MODE.REPLACE
): void

View file

@ -1,6 +0,0 @@
const target = document.querySelector("#update-this-element");
const button = document.querySelector("#click-me-to-refresh");
const nav = new vegvisir.Navigation("/page-with-new-content");
button.addEventListener("click", () => nav.navigate(target));

View file

@ -1,3 +0,0 @@
{
pushHistory: boolean
}

View file

@ -1,4 +0,0 @@
<style><?= VV::css("public/asssets/css/style.css") ?></style>
// Without file extension (appended automatically)
<style><?= VV::css("public/asssets/css/style.css") ?></style>

View file

@ -1 +0,0 @@
<style><?= VV::css("/var/www/other-project/.../style.css", true) ?></style>

View file

@ -1 +0,0 @@
"const hello = ()=>console.log('Hello world');hello()"

View file

@ -1,4 +0,0 @@
VV::css(
string $pathname,
bool $relative = true
): string

View file

@ -1,6 +0,0 @@
/* File: /var/www/my-website/public/assets/css/style.css */
p {
color: blue;
background-color: red;
}

View file

@ -1,4 +0,0 @@
// File: /var/www/my-website/public/index.php
<style><?= VV::js("public/assets/css/style.css") ?></style>
<p>Some content...</p>

View file

@ -1,4 +0,0 @@
<style><?= VV::css("public/asssets/css/style.css") ?></style>
// Without file extension (appended automatically)
<style><?= VV::css("public/asssets/css/style.css") ?></style>

View file

@ -1 +0,0 @@
<style><?= VV::css("/var/www/other-project/.../style.css", true) ?></style>

View file

@ -1 +0,0 @@
"const hello = ()=>console.log('Hello world');hello()"

View file

@ -1,4 +0,0 @@
VV::embed(
string $pathname,
bool $relative = true
): string

View file

@ -1,3 +0,0 @@
/* File: /var/www/my-website/public/assets/media/logo.svg */
<svg viewBox="0 0 0 0" xmlns="http://www.w3.org/2000/svg">...</svg>

View file

@ -1,5 +0,0 @@
// File: /var/www/my-website/shells/document.php
<header>
<?= VV::embed("public/assets/media/logo.svg") ?>
</header>

View file

@ -1,4 +0,0 @@
<?= VV::include("public/modules/banner.php") ?>
// Without file extension (appended automatically)
<?= VV::include("public/modules/banner") ?>

View file

@ -1 +0,0 @@
<?= VV::include("/var/www/other-project/.../banner.php", true) ?>

View file

@ -1 +0,0 @@
"const hello = ()=>console.log('Hello world');hello()"

View file

@ -1,4 +0,0 @@
VV::include(
string $pathname,
bool $relative = true
): never

View file

@ -1,5 +0,0 @@
/* File: /var/www/my-website/modules/banner.php */
<banner>
<p>Important text!</p>
</banner>

View file

@ -1,6 +0,0 @@
// File: /var/www/my-website/public/index.php
<header>
<p>Some header content</p>
<?= VV::include("modules/banner") ?>
</header>

View file

@ -1,4 +0,0 @@
<script><?= VV::js("public/asssets/js/script.js") ?></script>
// Without file extension (appended automatically)
<script><?= VV::js("public/asssets/js/script") ?></script>

View file

@ -1 +0,0 @@
<script><?= VV::js("/var/www/other-project/.../script.js", true) ?></script>

View file

@ -1 +0,0 @@
"const hello = ()=>console.log('Hello world');hello()"

View file

@ -1,4 +0,0 @@
VV::js(
string $pathname,
bool $relative = true
): string

View file

@ -1,5 +0,0 @@
// File: /var/www/my-website/public/assets/js/script.js
const hello = () => {
console.log("Hello world!");
}

View file

@ -1,4 +0,0 @@
// File: /var/www/my-website/public/index.php
<p>Some content...</p>
<script><?= VV::js("public/assets/js/script.js") ?></script>

View file

@ -1 +0,0 @@
VV::root("/src/databases");

View file

@ -1 +0,0 @@
"/var/www/my-website/src/databases"

View file

@ -1,9 +0,0 @@
// File: /var/www/my-website/src/MyClass.php
<?php
namespace MyWebsite\Src;
class MyClass {
// ...
}

View file

@ -1,11 +0,0 @@
// File: /var/www/my-website/public/index.php
<?php
use MyWebsite\Src\MyClass;
// Leading slash is optional!
require_once VV::root("/src/MyClass.php");
?>
<p>A normal Vegvisir page whch has access to MyClass</p>

View file

@ -1,3 +0,0 @@
VV::root(
string $pathname = ""
): string

View file

@ -1 +0,0 @@
<?= VV::shell("shells/docs") ?>

View file

@ -1,3 +0,0 @@
VV::shell(
string $pathname = ""
): never

View file

@ -1,4 +0,0 @@
// File: /var/www/my-website/public/some-page.php
<p>Inner content</p>
<?= VV::shell("shells/outer-content") ?>

View file

@ -1,5 +0,0 @@
// File: /var/www/my-website/shells/some-shell.php
<p>This paragraph will be above the wrapped content</p>
<vv-shell></vv-shell>
<p>This parahraph will be below the wrapped content</p>

View file

@ -1 +0,0 @@
git clone https://codeberg.org/vegvisir/vegvisir.git --depth 1

View file

@ -1 +0,0 @@
composer install --optimize-autoloader

View file

@ -1,16 +0,0 @@
# You might need to alter this block to suit your NGINX configuration
# The important thing is that all requests should be routed to /public/index.php
server {
listen 80;
server_name _;
root /var/www/vegvisir/public;
location / {
try_files /index.php =503;
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
}

View file

@ -1 +0,0 @@
mkdir /var/www/my-website

View file

@ -1 +0,0 @@
cp -p /var/www/vegvisir/.env.example.ini /var/www/vegvisir/.env.ini

View file

@ -1,10 +0,0 @@
# /var/www/vegvisir/.env.ini
; +--------------------+
; | Base configuration |
; +--------------------+
; An absolute path to the root directory of your website
root_path = "/var/www/my-website"
...

View file

@ -1,267 +0,0 @@
:root {
--primer-color-deep: 0, 128, 255;
--primer-color-light: 135, 255, 255;
--color-deep: rgba(var(--primer-color-deep));
--color-light: rgba(var(--primer-color-light));
--border-style-width: 1px;
--border-style: solid var(--border-style-width) rgba(var(--primer-color-deep), .2);
--padding: 10px;
--running-size: 70px;
--max-width: 1400px;
}
/* # Cornerstones */
* {
margin: 0;
color: inherit;
font-size: inherit;
box-sizing: border-box;
font-family: monospace;
}
body {
font-size: 15px;
overflow-x: hidden;
overscroll-behavior: none;
}
body.menuOpen {
overflow: hidden;
}
a {
color: inherit;
display: contents;
text-decoration: none;
}
/* # Components */
:is(h1, h2, h3, p, li) > a {
--underline-tickness: 3px;
display: initial;
text-decoration: underline;
text-decoration-color: var(--color-accent);
text-underline-offset: var(--underline-tickness);
text-decoration-thickness: var(--underline-tickness);
}
@media (hover: hover) {
:is(h1, h2, h3, p, li) > a:hover {
text-decoration-color: var(--color-deep);
}
}
h1 {
font-size: 30px;
color: var(--color-accent);
}
h2 {
font-size: 25px;
}
h3 {
font-size: 18px;
}
/* ## Container */
container {
margin: auto;
height: 100%;
display: flex;
width: clamp(200px, 100%, 80vw);
max-width: var(--max-width);
align-items: center;
gap: var(--padding);
padding: var(--padding) 0;
}
container.split {
display: grid;
grid-template-columns: repeat(2, 1fr);
}
container.split.reverse div:last-child {
order: -1;
}
container.split > div {
display: flex;
flex-direction: column;
align-items: baseline;
gap: var(--padding);
}
/* ## Button */
button {
border: unset;
fill: black;
cursor: pointer;
padding: 10px 15px;
border-radius: 4px;
background-color: rgba(0, 0, 0, 0);
border: solid var(--border-style-width) transparent;
}
button.solid {
fill: white;
color: white;
background-color: var(--color-deep);
}
button.shade {
background-color: rgba(0, 0, 0, .05);
}
@media (hover: hover) {
button:hover {
background-color: rgba(var(--primer-color-light), .3);
}
button.solid:hover {
color: var(--color-light);
background-color: var(--color-deep);
}
}
button svg {
fill: inherit;
width: 1em;
}
/* # Content */
/* ## Runners */
:is(header, footer) ul {
display: flex;
padding-left: 0;
list-style: none;
gap: var(--padding);
}
:is(header, footer) ul:last-of-type {
margin-left: auto;
}
:is(header, footer) container > button {
display: none;
margin-left: auto;
}
/* ### Header */
header {
--border-width: 2px;
top: 0px;
position: sticky;
background-color: white;
height: calc(var(--running-size) + var(--border-style-width));
border-bottom: var(--border-style);
z-index: 1000;
}
header .logo {
height: 40px;
padding: 5px;
border-radius: 4px;
background-color: var(--color-deep);
}
[vv-top-page="/"] header:not(.transparent) a[href="/"] button,
[vv-top-page="/help"] header a[href="/help"] button,
[vv-top-page^="/docs"] header a[href="/docs"] button,
[vv-top-page="/demos"] header a[href="/demos"] button,
[vv-top-page="/why"] header a[href="/why"] button {
font-weight: bold;
color: var(--color-deep);
border: var(--border-style);
}
/* ### Footer */
footer {
padding: var(--padding);
color: var(--color-light);
background-color: var(--color-deep);
}
/* ### Menu */
menu {
display: none;
position: fixed;
top: var(--running-size);
left: 0;
width: 100svw;
padding: calc(var(--padding) * 2);
height: calc(100svh - var(--running-size));
background-color: var(--color-deep);
}
body.menuOpen menu {
display: initial;
}
menu ul {
list-style: none;
padding-left: unset;
}
menu button {
color: white;
width: 100%;
margin-top: var(--padding);
}
/* # Size queries */
@media (max-width: 950px) {
container {
min-width: unset;
width: 100%;
padding: calc(var(--padding) * 2);
}
container.split {
display: flex;
flex-direction: column;
}
container.split.reverse {
flex-direction: column-reverse;
}
header ul button.solid,
:is(header, footer) ul:not(:last-of-type) {
display: none;
}
:is(header, footer) container > button {
display: initial;
}
footer :is(container, ul) {
flex-direction: column;
}
footer button,
footer ul:last-of-type {
width: 100%;
margin-left: unset;
}
}
@media (min-width: 950px) {
body.menuOpen menu {
display: none;
}
}

View file

@ -0,0 +1,8 @@
@font-face {
font-family: "Roboto Mono";
src:
url("/assets/fonts/roboto-mono.woff2") format("woff2 supports variations"),
url("/assets/fonts/roboto-mono.woff2") format("woff2-variations")
;
font-weight: 100 900;
}

View file

@ -1,26 +0,0 @@
/* # WIP */
section#wip {
background-color: rgba(var(--primer-color-light), .3);
}
section#wip container {
flex-direction: column;
gap: var(--padding);
}
/* # Websites */
section#websites container {
flex-direction: column;
}
section#websites ul {
list-style: none;
padding-left: unset;
}
section#websites button {
margin-top: var(--padding);
width: 100%;
}

View file

@ -0,0 +1,22 @@
aside {
grid-area: aside;
padding: var(--padding);
}
aside > p {
text-align: center;
margin: var(--padding) 0;
}
aside nav {
display: flex;
flex-direction: column;
gap: calc(var(--padding) / 2);
}
aside .spacer {
width: 100%;
height: 1px;
background-color: rgba(var(--primer-color-accent), .1);
margin: var(--padding) 0;
}

View file

@ -0,0 +1,86 @@
/* # Overrides */
header {
position: relative;
border-bottom: none;
}
/* # Sections */
/* ## Header */
section.header {
top: 0;
width: 100%;
z-index: 100;
display: flex;
color: white;
position: sticky;
overflow: hidden;
grid-area: header;
align-items: stretch;
height: calc(var(--running-size) - var(--padding));
grid-template-rows: var(--running-size);
background-color: var(--color-accent);
box-shadow:
0 2px 0 0 white,
20svw 0 0 0 var(--color-accent),
-20svw 0 0 0 var(--color-accent)
;
}
/* ### Nav */
section.header nav {
display: flex;
align-items: center;
padding: var(--padding);
justify-content: baseline;
}
section.header nav:last-of-type {
justify-content: end;
gap: calc(var(--padding) / 2);
}
/* ### Spacer */
section.header nav > div {
width: 2px;
height: 80%;
margin: 0 var(--padding);
background-color: rgba(0, 0, 0, .07);
}
/* ### Buttons */
section.header button.inline:not(.solid):hover,
section.header button.inline {
fill: white;
color: white;
}
section.header button.inline.solid {
color: var(--color-accent);
background-color: white;
}
/* ## Menu */
section.header button.menu {
display: none;
}
/* # Size queries */
@media (max-width: 950px) {
section.header > *:not(button.menu) {
display: none;
}
section.header button.menu {
width: 100%;
justify-content: center;
display: flex;
}
}

View file

@ -0,0 +1,89 @@
vv-shell {
display: grid;
grid-template-rows: calc(var(--running-size) - var(--padding)) 1fr var(--running-size);
grid-template-areas:
"header header"
"aside main"
"footer footer"
;
grid-template-columns: 300px 1fr;
}
/* # Main */
main {
--primer-color-accent: 0, 0, 0;
--color-accent: black;
gap: var(--padding);
display: flex;
padding: var(--padding);
grid-area: main;
font-family: sans-serif;
flex-direction: column;
}
main code {
color: white;
padding: 0 5px;
font-weight: normal;
font-family: monospace;
border-radius: 6px;
background-color: #212121;
}
main h2 {
font-size: 1.5em;
}
main h3 {
font-size: 1em;
margin-bottom: 5px;
}
/* # Sections */
section.md {
gap: 5px;
display: flex;
align-items: baseline;
flex-direction: column;
}
section.md :is(ol, ul) li {
margin: calc(var(--padding) / 2) 0;
}
/* # Size queries */
@media (max-width: 950px) {
vv-shell {
grid-template-rows: calc(var(--running-size) - var(--padding)) 1fr var(--running-size);
grid-template-areas:
"header"
"main"
"footer"
;
grid-template-columns: 100svw;
}
aside {
display: none;
}
vv-shell.menu-open {
grid-template-areas:
"header"
"aside"
"footer"
;
}
vv-shell.menu-open aside {
display: initial;
}
vv-shell.menu-open main {
display: none;
}
}

View file

@ -0,0 +1,4 @@
vv-shell {
margin: auto;
text-align: center;
}

View file

@ -0,0 +1,56 @@
vv-shell {
gap: var(--padding);
padding: var(--padding);
}
/* # Sections */
/* ## Heading */
section.heading {
text-align: center;
}
/* ## Grid */
section.grid {
gap: var(--padding);
display: grid;
grid-template-columns: repeat(3, 1fr);
}
section.grid button {
width: 100%;
flex-direction: column;
justify-content: space-between;
}
section.grid button img {
width: 100%;
border-radius: 6px;
}
section.grid button > div {
display: flex;
text-align: right;
gap: var(--padding);
align-items: center;
}
section.grid button p {
color: black;
}
/* # Size queries */
@media (max-width: 1000px) {
section.grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 550px) {
section.grid {
grid-template-columns: 1fr;
}
}

View file

@ -0,0 +1,32 @@
vv-shell {
padding: 0 var(--padding);
}
/* # Section */
/* ## This stays */
section#this-stays {
display: flex;
align-items: center;
text-align: center;
fill: var(--color-accent);
justify-content: space-evenly;
height: var(--running-size);
}
section#this-stays svg {
height: 70%;
}
section#this-stays svg:last-child {
transform: rotateY(180deg);
}
/* # Size queries */
@media (max-width: 950px) {
section#this-stays {
height: calc(var(--running-size) * 2);
}
}

View file

@ -1,188 +1,76 @@
:root { vv-shell {
--wavelength: 20vw; overflow-x: hidden;
padding: 0 var(--padding);
} }
body { /* # Components */
background-color: var(--color-deep);
}
section h2 { code-demo {
color: white; width: 100%;
background-color: black; max-width: 700px;
}
header.transparent {
color: white;
background-color: transparent;
border-color: rgba(255, 255, 255, .1);
}
header.transparent .logo {
fill: var(--color-deep);
}
header.transparent button {
fill: white;
}
header.transparent button.solid {
fill: var(--color-deep);
color: var(--color-deep);
background-color: white;
} }
/* # Sections */ /* # Sections */
/* ## Divider */
section.divider {
width: 100%;
overflow: hidden;
line-height: 0;
background-color: white;
}
section.divider svg {
position: relative;
display: block;
width: calc(148% + 1.3px);
height: 79px;
}
section.divider .shape-fill {
fill: var(--color-deep);
}
/* ## Intro */ /* ## Intro */
section#intro { section#intro {
display: grid; min-height: min(50svh, 1000px);
color: white;
min-height: 300px;
} }
section#intro h1 { section#intro .text h1 {
font-size: 50px; font-size: 50px;
line-height: 50px;
} }
section#intro div { /* ### Compass */
display: flex;
flex-direction: column; section#intro div.compass {
justify-content: center; align-self: center;
align-items: baseline;
} }
/* ### Waves */ section#intro div.compass svg {
--size: 50svh;
section.waves { width: var(--size);
--easing: .2; height: var(--size);
position: absolute;
position: relative;
height: 300px;
user-select: none;
pointer-events: none;
z-index: -1; z-index: -1;
} }
section.waves img { section#intro div.compass svg.points {
margin: auto; opacity: .2;
margin-top: -14%; animation: spin 10s alternate infinite linear;
width: 50%;
transform-origin: 50% 100%;
animation: ship 6s alternate infinite ease;
} }
@keyframes ship { @keyframes spin {
0% { to { transform: rotate(30deg); }
transform: translate(0, -7px) rotate(-7deg);
}
100% {
transform: translate(5px, 10px) rotate(4deg);
}
} }
section.waves .wave { section#intro div.compass svg:nth-child(2) {
position: absolute; --size: 60svh;
width: 100%;
height: 100%; animation-duration: 15s;
transform: scale(1.5); animation-direction: alternate-reverse;
bottom: 70px;
animation: wave 7s alternate infinite cubic-bezier(var(--easing), 0, calc(1 - var(--easing)), 1);
} }
@keyframes wave { /* ## Assets */
to { transform: scale(1.5) translateX(100px); }
}
section.waves + section { /* ## Freedom */
background-color: #4ca6ff;
}
section.waves .wave:first-child { section#freedom > div > svg {
background-image: url("/assets/media/waves/0.svg"); height: 300px;
}
section.waves .wave:last-child {
animation-duration: 5s;
background-image: url("/assets/media/waves/1.svg");
}
/* ## Softnav */
section#softnav {
color: white;
background: linear-gradient(0deg, rgba(0,128,255,1) 0%, rgba(76,166,255,1) 100%);
}
/* ## BYOE */
section.info {
background-color: white;
}
section.info container {
min-height: 400px;
}
section.info svg {
justify-self: center;
width: 400px;
}
/* ## Lead */
section#lead h1 {
color: white;
text-align: center;
font-weight: normal;
}
/* ## Free */
section#free {
box-shadow: inset 0 0 20px 20px white, inset 0 0 140px 20px white;
background-color: rgba(255, 255, 255, .9);
background-blend-mode: screen;
background-image: url("/assets/media/gnu.png");
} }
/* # Size queries */ /* # Size queries */
@media (max-width: 950px) { @media (max-width: 950px) {
section.waves { section#intro {
display: none; min-height: min(40svh, 1000px);
} }
section.info container { section#intro div.compass {
min-height: unset; opacity: .3;
} z-index: -1;
section.info svg {
width: 300px;
}
section#lead h1 {
font-size: 20px;
} }
} }

View file

@ -1,15 +0,0 @@
/* # Sections */
/* ## Free */
section#freedom {
background-position: 50% 50%;
background-size: 400px;
background-color: var(--color-deep);
background-blend-mode: lighten;
background-image: url("/assets/media/gnu.png");
}
section#freedom svg {
filter: drop-shadow(0 0 40px rgba(255, 255, 255, .3));
}

384
public/assets/css/shell.css Normal file
View file

@ -0,0 +1,384 @@
:root {
--primer-color-accent: 0, 128, 255;
--color-accent: rgb(var(--primer-color-accent));
--padding: 20px;
--running-size: 80px;
}
/* # Cornerstones */
* {
margin: 0;
fill: inherit;
box-sizing: border-box;
font-family: inherit;
color: inherit;
font-size: inherit;
}
::-webkit-scrollbar {
display: none;
}
body {
display: grid;
justify-items: center;
grid-template-rows: var(--running-size) 1fr var(--running-size);
overscroll-behavior: none;
background-color: white;
color: black;
overflow-x: hidden;
min-height: 100svh;
font-size: 15px;
font-family: "Roboto Mono", sans-serif;
}
body::before {
transition: 1s opacity;
content: "";
position: absolute;
top: -5%;
left: 0;
width: 20%;
height: 5%;
border-radius: 100%;
z-index: 1000;
box-shadow:
0 0 30svh 10svh rgba(var(--primer-color-accent), .1),
0 0 30svh 60svh rgba(var(--primer-color-accent), .05),
0 0 30svh 150svh rgba(var(--primer-color-accent), .02)
;
opacity: 0;
}
/* "enable" the corner glow effect on initial load when a page has been fully loaded */
body[vv-top-page]::before {
opacity: 1;
}
a {
display: contents;
color: inherit;
text-decoration: none;
}
/* ## vv-shell */
vv-shell {
width: 100svw;
display: flex;
max-width: 1500px;
position: relative;
flex-direction: column;
}
/* # Components */
:is(h1, h2, h3, p, li) > a {
--underline-tickness: 3px;
display: initial;
text-decoration: underline;
text-decoration-thickness: var(--underline-tickness);
text-underline-offset: var(--underline-tickness);
text-decoration-color: var(--color-accent);
}
h1 {
font-size: 30px;
color: var(--color-accent);
}
h2 {
font-size: 30px;
}
h3 {
font-size: 25px;
}
/* ## Page transition */
[vv-loading] * {
transition: 200ms opacity;
}
[vv-loading="true"] * {
opacity: 0;
pointer-events: none;
}
[vv-loading="true"]::after {
content: "";
position: fixed;
top: 50%;
left: 50%;
width: 45px;
height: 49px;
background-size: contain;
image-rendering: pixelated;
transform: translate(-50%, -50%);
background-image: url("/assets/media/spinner.gif");
-webkit-filter: hue-rotate(var(--hue-accent));
filter: hue-rotate(var(--hue-accent));
}
/* ## Buttons */
button {
border: none;
background-color: transparent;
color: inherit;
fill: inherit;
cursor: pointer;
}
button * {
pointer-events: none;
}
/* ### Inline */
button.inline {
gap: 10px;
display: flex;
border-radius: 7px;
align-items: center;
fill: var(--color-accent);
color: var(--color-accent);
padding: calc(var(--padding) / 1.5);
background: linear-gradient(139deg, rgba(0, 0, 0, 0) 0%, rgba(var(--primer-color-accent), .1) 100%);
}
button.inline:not(.solid, .sly) {
box-shadow:
0 0 0 2px rgba(var(--primer-color-accent), .1),
10px 7px 40px 3px rgba(var(--primer-color-accent), .06)
;
}
button.inline svg {
flex: none;
height: 1em;
}
button.inline svg:last-child {
width: 1.5em;
margin-left: auto;
}
button.inline svg.chevron:last-child {
transform: rotate(-90deg);
}
/* #### Solid */
button.inline.solid {
fill: white;
color: white;
border-color: var(--color-accent);
background-color: var(--color-accent);
border: solid 2px rgba(var(--primer-color-accent), 1);
}
/* #### Sly */
button.sly {
border-color: transparent;
background: transparent;
box-shadow: none;
}
button.sly > svg.chevron {
transition: 200ms width;
width: 0;
}
button.sly:hover > svg.chevron {
width: 1.5em;
}
/* ## Header */
header {
--border-style: solid 1px rgba(0, 0, 0, .07);
top: 0;
width: 100%;
z-index: 100;
display: grid;
position: sticky;
overflow: hidden;
align-items: stretch;
backdrop-filter: blur(20px);
height: var(--running-size);
border-bottom: var(--border-style);
-webkit-backdrop-filter: blur(20px);
grid-template-rows: var(--running-size);
background-color: rgba(255, 255, 255, .8);
grid-template-columns: var(--running-size) repeat(2, 1fr);
}
/* ### Nav */
header nav {
display: flex;
align-items: center;
padding: var(--padding);
justify-content: baseline;
}
header nav:last-of-type {
justify-content: end;
gap: calc(var(--padding) / 2);
}
:is(header, footer) nav.m {
display: none;
}
/* ### Spacer */
header nav > div {
width: 2px;
height: 80%;
margin: 0 var(--padding);
background-color: rgba(0, 0, 0, .07);
}
/* ### Buttons */
header button {
--icon-size: 25px;
display: grid;
border-left: var(--border-style);
grid-template-columns: 1fr;
align-items: center;
justify-items: center;
padding: var(--padding);
gap: var(--padding);
fill: var(--color-accent);
font-size: 13px;
color: rgba(255, 255, 255, .5);
cursor: pointer;
}
header button:not(.logo) svg {
width: var(--icon-size);
}
header button.search p {
display: none;
}
/* ## Footer */
footer {
width: 100%;
display: grid;
color: white;
position: sticky;
overflow: hidden;
align-items: stretch;
height: var(--running-size);
grid-template-rows: var(--running-size);
background-color: var(--color-accent);
grid-template-columns: repeat(2, 1fr);
}
/* ### Nav */
footer nav {
display: flex;
align-items: center;
padding: var(--padding);
justify-content: baseline;
}
footer nav:last-of-type {
justify-content: end;
gap: calc(var(--padding) / 2);
}
/* ### Spacer */
footer nav > div {
width: 2px;
height: 80%;
margin: 0 var(--padding);
background-color: rgba(0, 0, 0, .07);
}
/* ### Buttons */
footer button.inline:not(.solid):hover,
footer button.inline {
fill: white;
color: white;
}
footer button.inline.solid {
color: var(--color-accent);
background-color: white;
}
/* # Feature queries */
@media (hover: hover) {
:is(h1, h2, h3, p, li) > a:hover {
text-underline-offset: 1px;
text-decoration-thickness: calc(var(--underline-tickness) * 2);
color: var(--color-accent);
}
/* # Components */
button.inline {
transition-duration: 300ms;
transition-property: background-color, border-color, box-shadow, color, fill;
}
button:hover {
border-color: rgba(255, 255, 255, .2);
background-color: rgba(255, 255, 255, .1);
}
button.inline:hover {
fill: var(--color-accent);
color: var(--color-accent);
}
button.inline:not(.solid):hover {
box-shadow:
0 0 0 2px rgba(var(--primer-color-accent), 1),
10px 7px 30px 3px rgba(var(--primer-color-accent), .07)
;
}
button.solid:hover {
border-color: rgba(var(--primer-color-accent), .2);
background-color: rgba(var(--primer-color-accent), .2);
box-shadow: 0 -10px 20px 10px rgba(var(--primer-color-accent), .05);
}
}
/* # Size queries */
@media (max-width: 950px) {
footer,
header {
grid-template-columns: var(--running-size) 1fr;
}
:is(header, footer) nav.m {
display: flex;
padding-left: calc(var(--padding) / 2);
justify-content: space-between;
}
:is(header, footer) nav:not(.m) {
display: none;
}
}

View file

@ -1,274 +0,0 @@
body {
background-color: rgba(var(--primer-color-deep), .01);
}
[vv-shell-id="/"] {
display: grid;
min-height: calc(100svh - var(--running-size) - var(--border-style-width));
grid-template-areas:
"aside main"
"contribute contribute"
;
grid-template-columns: 400px 1fr;
grid-template-rows: 1fr 200px;
gap: calc(var(--padding) * 2);
margin: auto;
}
[vv-shell-id="6ccb0465"] {
grid-area: main;
display: flex;
flex-direction: column;
gap: calc(var(--padding) * 2);
margin-top: calc(var(--padding) * 2);
padding-right: calc(var(--padding) * 2);
}
hr {
border: unset;
border-top: var(--border-style);
}
/* # Aside */
aside {
grid-area: aside;
height: 100%;
display: flex;
flex-direction: column;
padding: var(--padding) 0;
padding-left: calc(var(--padding) * 2);
border-right: var(--border-style);
}
aside button {
width: calc(100% - (var(--padding) * 2));
text-align: left;
}
aside hr {
margin: calc(var(--padding) * 2) 0;
}
aside ul {
list-style: none;
padding-left: 0;
}
aside ul ul {
padding-left: var(--padding);
}
aside ul > p {
margin: var(--padding) 0;
}
/* ---- */
aside .cc + ul {
display: none;
}
aside .cc.php button {
background-color: rgba(122, 134, 184, .2);
}
aside .cc.js button {
background-color: rgba(240, 219, 79, .3);
}
[vv-page^="/docs/API/PHP"] aside .cc.php + ul,
[vv-page^="/docs/API/JS"] aside .cc.js + ul {
display: initial;
}
/* # Collapsible */
details {
border: 1px solid #aaa;
border-radius: 4px;
padding: 0.5em 0.5em 0;
}
summary {
margin: -0.5em -0.5em 0;
padding: 0.5em;
cursor: pointer;
}
details[open] {
width: 100%;
padding: 0.5em;
}
details[open] summary {
border-bottom: 1px solid #aaa;
margin-bottom: 0.5em;
}
/* # Sections */
/* ## Inset */
section.inset {
padding-left: var(--padding);
}
/* ## Markdown */
section.md container {
padding-top: 0;
flex-direction: column;
align-items: baseline;
}
section.md :is(h1, h2, h3) {
cursor: pointer;
}
section.md h1::before {
content: "#";
opacity: 1;
padding: 0 10px;
margin-right: .5em;
color: var(--color-light);
background-color: var(--color-deep);
}
section.md h2::before {
content: "#";
opacity: 1;
margin-right: .5em;
color: var(--color-deep);
}
/* ## Code inline */
:is(h1, h2, h3, a, p, quote) > code {
padding: 5px;
border-radius: 6px;
white-space: nowrap;
font-family: 'Courier New', monospace;
background-color: rgba(0, 0, 0, .05);
}
code.tag::before {
content: "<";
}
code.tag::after {
content: ">";
}
/* ## Code block */
section.md pre {
width: 100%;
max-width: calc(100svw - (var(--padding) * 4));
tab-size: 3;
overflow: scroll;
}
section.md pre code {
--copy-size: 37px;
--copy-inset: 5px;
--copy-border-size: 1px;
overflow: scroll;
position: relative;
padding-right: calc(var(--copy-size) + (var(--copy-inset) * 2) + var(--padding));
border-radius: 6px;
}
section.md pre code::after {
content: "📋";
display: grid;
font-size: 20px;
cursor: pointer;
position: absolute;
border-radius: 4px;
align-items: center;
justify-items: center;
top: var(--copy-inset);
width: var(--copy-size);
right: var(--copy-inset);
height: var(--copy-size);
border: solid 1px rgba(255, 255, 255, .1);
background-color: rgba(255, 255, 255, .1);
}
@media (hover: hover) {
section.md h1:hover::before {
background-color: black;
}
section.md :is(h1, h2, h3):hover::after {
content: " <- click to copy link";
font-size: .5em;
opacity: .5;
}
section.md pre code:hover::after {
border: solid 1px rgba(255, 255, 255, .3);
background-color: rgba(255, 255, 255, .2);
}
}
/* ## Menu */
section.menu {
display: none;
fill: white;
color: white;
grid-area: menu;
cursor: pointer;
background-color: rgba(var(--primer-color-deep), .8);
}
section.menu container svg {
width: 1em;
}
section.menu p::before {
content: "Open ";
}
/* # Size queries */
@media (max-width: 950px) {
[vv-shell-id="/"] {
grid-template-areas:
"menu"
"main"
"contribute"
;
grid-template-columns: 1fr;
grid-template-rows: var(--running-size) 1fr 300px;
}
body.docsMenuOpen [vv-shell-id="/"] {
grid-template-areas:
"menu"
"aside"
;
grid-template-rows: var(--running-size) 1fr;
}
body.docsMenuOpen [vv-shell-id="6ccb0465"],
body.docsMenuOpen section.contribute,
body:not(.docsMenuOpen) aside {
display: none;
}
/* ---- */
[vv-shell-id="6ccb0465"] {
margin-top: unset;
}
/* ---- */
section.menu {
display: initial;
}
}

View file

@ -0,0 +1,113 @@
code-demo {
--primer-color-base: 31, 31, 31;
--color-base: rgb(var(--primer-color-base));
--height: calc(var(--padding) * 2.5);
display: block;
min-width: 700px;
border-radius: 12px;
font-family: monospace;
background-color: var(--color-base);
}
/* # Header */
code-demo .header {
display: flex;
overflow-x: scroll;
height: var(--height);
border-bottom: solid 1px rgba(255, 255, 255, .1);
}
code-demo .header button.inline {
border: solid 1px rgba(255, 255, 255, .1);
border-bottom: unset;
border-radius: unset;
box-shadow: unset;
}
code-demo .header button.inline:hover {
box-shadow: unset;
}
code-demo .header button:first-child {
margin-left: var(--height);
}
code-demo .header button.inline + button.inline {
border-left: unset;
}
/* ## Active */
code-demo .header button {
--primer-color-accent: 255, 255, 255;
--color-accent: rgb(var(--primer-color-accent));
}
code-demo .header button.active {
border-top: solid 4px var(--color-accent);
pointer-events: none;
}
/* ## Languages */
code-demo .header button[data-lang="js"] {
--primer-color-accent: 241, 224, 90;
--color-accent: rgb(var(--primer-color-accent));
}
code-demo .header button[data-lang="css"] {
--primer-color-accent: 81, 154, 186;
--color-accent: rgb(var(--primer-color-accent));
}
code-demo .header button[data-lang="php"] {
--primer-color-accent: 160, 116, 196;
--color-accent: rgb(var(--primer-color-accent));
}
code-demo .header button[data-lang="html"] {
--primer-color-accent: 221, 120, 49;
--color-accent: rgb(var(--primer-color-accent));
}
/* # Body */
code-demo .body {
overflow: scroll;
padding: var(--padding);
}
code-demo .body .view-line {
white-space: pre;
}
/* ## Tabs */
code-demo .body [data-file] {
display: none;
}
code-demo .body [data-file].active {
display: initial;
}
code-demo .body a {
display: inline;
text-decoration: underline;
text-decoration-color: white;
}
code-demo .body a:hover {
text-decoration-thickness: 2px;
}
/* # Size queries */
@media (max-width: 950px) {
code-demo {
width: 100%;
min-width: unset;
}
}

View file

@ -0,0 +1,61 @@
nav-menu {
display: none;
}
@media (max-width: 950px) {
/* # Overrides */
body.menu-open {
overflow: hidden;
}
/* ## Header */
body.menu-open header {
background-color: var(--color-accent);
}
body.menu-open header nav p {
color: white;
}
/* # Nav menu */
nav-menu {
top: var(--running-size);
left: 0;
width: 100%;
overflow-y: scroll;
height: calc(100svh - var(--running-size));
display: none;
position: fixed;
align-items: center;
flex-direction: column;
background-color: white;
}
nav-menu.active {
display: flex;
}
/* ## Sections */
nav-menu nav {
gap: calc(var(--padding) / 2);
width: 100%;
max-width: 600px;
display: flex;
padding: var(--padding);
flex-direction: column;
}
nav-menu nav:last-child {
margin-top: auto;
}
nav-menu nav.horizontal {
gap: var(--padding);
flex-direction: row;
justify-content: center;
}
}

View file

@ -0,0 +1,71 @@
/* # Split */
section.split {
display: grid;
grid-template-columns: repeat(2, 1fr);
margin: 10svh 0;
}
section.split.center {
justify-items: center;
}
section.split > div {
grid-row: 1;
display: flex;
align-items: center;
flex-direction: column;
justify-content: center;
}
section.split.reverse > div:last-child {
grid-column: 1;
}
section.split.reverse > div:first-child {
grid-column: 2;
}
/* ## Text */
section.split .text {
width: 70%;
display: flex;
align-items: baseline;
flex-direction: column;
}
section.split.reverse .text {
align-items: end;
justify-self: end;
text-align: right;
}
/* ## Buttons */
section.split .buttons {
display: flex;
gap: var(--padding);
margin-top: var(--padding);
}
/* # Size queries */
@media (max-width: 950px) {
section.split {
display: flex;
margin: 5svh 0;
gap: var(--padding);
flex-direction: column;
}
section.split .text {
width: 100%;
}
section.split .buttons {
width: 100%;
flex-direction: column;
gap: calc(var(--padding) / 2);
}
}

View file

@ -0,0 +1,27 @@
/* # Syntax highliting */
.mtk1 { color: #cccccc; }
.mtk2 { color: #1f1f1f; }
.mtk3 { color: #d4d4d4; }
.mtk4 { color: #000080; }
.mtk5 { color: #6a9955; }
.mtk6 { color: #569cd6; }
.mtk7 { color: #b5cea8; }
.mtk8 { color: #646695; }
.mtk9 { color: #d7ba7d; }
.mtk10 { color: #9cdcfe; }
.mtk11 { color: #f44747; }
.mtk12 { color: #ce9178; }
.mtk13 { color: #6796e6; }
.mtk14 { color: #808080; }
.mtk15 { color: #d16969; }
.mtk16 { color: #dcdcaa; }
.mtk17 { color: #4ec9b0; }
.mtk18 { color: #c586c0; }
.mtk19 { color: #4fc1ff; }
.mtk20 { color: #c8c8c8; }
.mtk21 { color: #cd9731; }
.mtk22 { color: #b267e6; }
.mtki { font-style: italic; }
.mtkb { font-weight: bold; }
.mtks { text-decoration: line-through; }

Binary file not shown.

View file

@ -0,0 +1,45 @@
const PUBLIC_ELEMENT_STYLESHEET_DIR = "/assets/css/snippets/";
export class CustomElement extends HTMLElement {
constructor() {
super();
}
/**
* Return a pathname to a custom element CSS stylesheet
* @param {String} stylesheet
* @returns {String}
*/
static #getElementStylesheetHref(stylesheet) {
return `${PUBLIC_ELEMENT_STYLESHEET_DIR}${stylesheet}.css`;
}
/**
* Include a stylesheet for a custom element
* @param {String} stylesheet
*/
importElementStylesheet(stylesheet) {
if (document.head.querySelector(`link[href="${CustomElement.#getElementStylesheetHref(stylesheet)}"]`)) {
return;
}
const element = document.createElement("link");
element.href = CustomElement.#getElementStylesheetHref(stylesheet);
element.rel = "stylesheet";
document.head.appendChild(element);
}
connectedCallback() {
if ("connected" in this) {
this.connected();
}
}
disconnectedCallback() {
if ("disconnected" in this) {
this.disconnected();
}
}
}

View file

@ -0,0 +1,9 @@
// Toggle documentation menu with CSS when button in sub-header is clicked
document.querySelector("section.header button.menu").addEventListener("click", () => {
vegvisir.Navigation.shellElement.classList.toggle("menu-open");
});
// Close documentation menu when shell is being navigated
vegvisir.Navigation.shellElement.addEventListener(vegvisir.Navigation.EVENTS.STARTED, () => {
vegvisir.Navigation.shellElement.classList.remove("menu-open");
});

View file

@ -0,0 +1,9 @@
import { TAG_NAME } from "/assets/js/snippets/HTMLNavMenuElement.mjs";
// Bind toggles for the nav menu
[...document.querySelectorAll("[data-menu]")].forEach(element => {
element.addEventListener("click", () => document.querySelector(TAG_NAME).toggle());
});
// Scroll page to the top on navigations
document.addEventListener(vegvisir.Navigation.EVENTS.FINISHED, () => window.scrollTo({ top: 0 }));

View file

@ -1,9 +0,0 @@
// Handle docs menu open/close
{
const CLASSNAME_DOCS_MENU_OPEN = "docsMenuOpen";
// Toggle docs menu on button click
document.querySelector("section.menu").addEventListener("click", () => document.body.classList.toggle(CLASSNAME_DOCS_MENU_OPEN));
// Hide docs menu on navigation
document.addEventListener(vegvisir.Navigation.EVENTS.STARTED, () => document.body.classList.remove(CLASSNAME_DOCS_MENU_OPEN));
}

View file

@ -1,9 +0,0 @@
// Handle global menu open/close events
{
const CLASSNAME_MENU_OPEN = "menuOpen";
// Toggle menu on menu button click
document.querySelector("header .menuToggle").addEventListener("click", () => document.body.classList.toggle(CLASSNAME_MENU_OPEN));
// Close menu on navigation
document.addEventListener(vegvisir.Navigation.EVENTS.STARTED, () => document.body.classList.remove(CLASSNAME_MENU_OPEN));
}

View file

@ -0,0 +1,40 @@
import { CustomElement } from "../CustomElement.mjs";
export const TAG_NAME = "code-demo";
const SELECTOR_HEADER_BUTTON = ".header button";
class HTMLCodeDemoElement extends CustomElement {
constructor() {
super();
this.importElementStylesheet(this.constructor.name);
}
/**
* Set a tab as active by passing an HTMLElement with a data-lang attribute
* @param {HTMLElement} target
*/
#setActiveTab(target) {
[...this.querySelectorAll("[data-file]")].forEach(element => {
element.classList.remove("active");
// Set active if current element lang is target lang
if (element.dataset.file === target.dataset.file) {
element.classList.add("active");
}
});
}
connected() {
// Bind event listeners for language tabs
[...this.querySelectorAll(SELECTOR_HEADER_BUTTON)].forEach(element => {
element.addEventListener("click", (event) => this.#setActiveTab(event.target.closest(SELECTOR_HEADER_BUTTON)))
});
// Make the first tab active on load
this.#setActiveTab(this.querySelector(SELECTOR_HEADER_BUTTON));
}
}
globalThis.customElements.define(TAG_NAME, HTMLCodeDemoElement);

View file

@ -0,0 +1,36 @@
import { CustomElement } from "../CustomElement.mjs";
export const TAG_NAME = "nav-menu";
class HTMLNavMenuElement extends CustomElement {
constructor() {
super();
this.importElementStylesheet(this.constructor.name);
}
open() {
document.body.classList.add("menu-open");
this.classList.add("active");
}
close() {
this.classList.remove("active");
// Remove classname from body tag if this was the last menu that was open
if (!document.querySelector(TAG_NAME + ".active")) {
document.body.classList.remove("menu-open");
}
}
toggle() {
this.classList.contains("active") ? this.close() : this.open();
}
connected() {
// Close menu when a link is clicked
[...document.querySelectorAll("a")].forEach(element => element.addEventListener("click", () => this.close()));
}
}
globalThis.customElements.define(TAG_NAME, HTMLNavMenuElement);

View file

@ -0,0 +1 @@
<svg viewBox="0 0 106.3 101.4" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="m1 48.5 2-1.9A51 51 0 0 0 13.7 31a203.8 203.8 0 0 0 6-11.9 389.6 389.6 0 0 1 5.5-10.6l2.5-4.7a7 7 0 0 1 4-3.5c1.9-.6 3.3 0 4.5 1.5 1.1 1.6 2 3.6 2.7 6A443.4 443.4 0 0 1 42.4 21 231.5 231.5 0 0 0 52 45a77.7 77.7 0 0 0 5.9 9.3c.9 1.2 1.8 2 2.9 2.4a48.4 48.4 0 0 1 3 1.6 3.3 3.3 0 0 1 .5 4 3.3 3.3 0 0 1-2.6 1.6c-5.7.4-3.7-8 .8-6.4a3.3 3.3 0 0 1 2.2 3.4 3.3 3.3 0 0 1-2.7 3h-1a675 675 0 0 0-1.3-.4 17 17 0 0 1-3-2.2 17 17 0 0 1-3.6-3.8 88.4 88.4 0 0 1-5.8-10 166.4 166.4 0 0 1-7.9-15.7 183 183 0 0 1-6.1-15.5c-.6-1.8-1.2-4.2-1.8-7.1C30 2.4 35 4.6 33.4 7a1049.1 1049.1 0 0 0-8 14.7A234.1 234.1 0 0 0 19.9 34a61.3 61.3 0 0 1-5.5 10.4 69.6 69.6 0 0 1-8.7 9.5A3.5 3.5 0 0 1 0 52.1a3.5 3.5 0 0 1 .7-3.2l.3-.4z"/><path d="m34.7 34.7-.4 2a33 33 0 0 0 3 21.7 48.6 48.6 0 0 0 14 17.5A90.7 90.7 0 0 0 65 85.4a48.9 48.9 0 0 0 11.2 4c1.6.4 3.1.7 4.7.9l4.6.5c1.6.2 3.3.4 5.2.4 2 0 3.6.2 5 .4s2.8.2 4 .2h2.8a4.7 4.7 0 0 1 4 4.2 4.7 4.7 0 0 1-3 5c-.5.2-1 .3-1.6.3-6.8.4-6.2-9.8 0-9.5a4.7 4.7 0 0 1 4.4 3.7 4.7 4.7 0 0 1-2.4 5.3l-1.5.5a975.2 975.2 0 0 0-1.4 0c-2.5 0-5-.3-7.4-.8L89 100a50.3 50.3 0 0 1-10-1.6L74 97a363.1 363.1 0 0 1-10.7-4 38.5 38.5 0 0 1-7.8-4.8 1150 1150 0 0 0-9-6.8 77.2 77.2 0 0 1-12.5-13 36.3 36.3 0 0 1-5.1-8.9l-2-4.5c-.5-1.7-.9-3.9-1.2-6.6a35.5 35.5 0 0 1 .7-13.2 32.1 32.1 0 0 1 1-3.4 4 4 0 0 1 6.6-.2 4 4 0 0 1 .8 2.7v.5z"/></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -0,0 +1 @@
<svg class="points" viewBox="0 0 170.34 170.34" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path style="fill:none;stroke:#000;stroke-width:8px;stroke-miterlimit:0;stroke-dashoffset:10px;stroke-opacity:1" d="M335.16 141.72a88.59 88.59 0 0 1 0 2.3m-.1 2.93c-.01.38-.03.77-.06 1.15m-.23 2.91-.13 1.15m-.38 2.9-.18 1.14m-.53 2.87-.23 1.13m-.68 2.85-.29 1.11m-.82 2.8c-.11.38-.23.74-.35 1.1m-.95 2.77-.4 1.08m-1.1 2.7-.46 1.07m-1.23 2.65-.51 1.03m-1.37 2.59-.56 1m-1.49 2.52-.61.97m-1.62 2.44-.66.95m-1.73 2.35-.71.9m-1.85 2.27-.75.88m-1.97 2.16-.79.84m-2.07 2.06-.83.8m-2.17 1.96-.87.75m-2.27 1.85-.9.71m-2.36 1.74-.94.66m-2.44 1.61-.98.62m-2.51 1.49-1 .56m-2.6 1.36c-.34.17-.68.35-1.03.51m-2.65 1.23-1.06.46m-2.7 1.1-1.09.4m-2.76.96-1.1.35m-2.8.82-1.12.29m-2.84.67-1.13.24m-2.88.53-1.14.18m-2.9.38-1.14.12m-2.91.24c-.39.02-.77.05-1.16.06m-2.92.1a88.59 88.59 0 0 1-2.3 0m-2.92-.1c-.39-.01-.77-.04-1.16-.06m-2.9-.24-1.16-.12m-2.9-.38-1.13-.18m-2.88-.53-1.13-.24m-2.84-.67-1.12-.3m-2.8-.81-1.1-.35m-2.76-.96-1.08-.4m-2.71-1.1-1.06-.46m-2.65-1.23-1.04-.5m-2.58-1.37-1-.56m-2.52-1.5-.98-.6m-2.44-1.62-.94-.66m-2.35-1.74-.91-.7m-2.27-1.86-.87-.75m-2.17-1.96-.83-.8m-2.07-2.06-.8-.84m-1.96-2.16-.75-.88m-1.85-2.26-.7-.91m-1.74-2.35-.66-.95m-1.62-2.44-.61-.97m-1.5-2.52-.55-1m-1.37-2.59-.5-1.03m-1.24-2.65-.46-1.06m-1.1-2.71-.4-1.08m-.95-2.76-.35-1.1m-.82-2.8-.3-1.12m-.67-2.85-.23-1.13m-.53-2.87-.18-1.14m-.38-2.9-.13-1.15m-.23-2.9-.07-1.16m-.1-2.92a88.59 88.59 0 0 1 0-2.3m.1-2.93.07-1.15m.23-2.91.13-1.15m.38-2.9.18-1.14m.53-2.87.23-1.13m.68-2.85.29-1.11m.82-2.8c.11-.38.23-.74.35-1.1m.95-2.77.4-1.08m1.1-2.7.46-1.07m1.23-2.65.51-1.03m1.37-2.59.56-1m1.49-2.52.61-.97m1.62-2.44.66-.95m1.73-2.35.71-.9m1.85-2.27.75-.88m1.96-2.16.8-.84m2.07-2.06.83-.8m2.17-1.96.87-.75m2.27-1.85.9-.71m2.36-1.74.94-.66m2.44-1.61.98-.62m2.51-1.49 1-.56m2.6-1.36c.34-.17.68-.35 1.03-.51m2.65-1.23 1.06-.46m2.7-1.1 1.09-.4m2.76-.96 1.1-.35m2.8-.82 1.12-.29m2.84-.67 1.13-.24m2.88-.53 1.14-.18m2.9-.38 1.14-.12m2.91-.24c.39-.02.77-.05 1.16-.06m2.92-.1a83.58 83.58 0 0 1 2.3 0m2.92.1c.39.01.77.04 1.16.06m2.9.24 1.16.12m2.9.38 1.13.18m2.88.53 1.13.24m2.84.67 1.12.3m2.8.81 1.1.35m2.76.96 1.08.4m2.71 1.1 1.06.46m2.65 1.23 1.04.5m2.58 1.37 1 .56m2.52 1.5.98.6m2.44 1.62.94.66m2.35 1.74.91.7m2.27 1.86.87.75m2.17 1.96.83.8m2.07 2.06.8.84m1.96 2.16.75.88m1.85 2.26.7.91m1.74 2.35.66.95m1.62 2.44.61.97m1.5 2.52.55 1m1.37 2.59.5 1.03m1.24 2.65.46 1.06m1.1 2.71.4 1.08m.95 2.76.35 1.1m.82 2.8.3 1.12m.67 2.85.23 1.13m.53 2.87.18 1.14m.38 2.9.13 1.15m.23 2.9.07 1.16m.1 2.92v1.16" transform="translate(-168.83 -57.7)"/></svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View file

@ -0,0 +1 @@
<svg class="compass" viewBox="0 0 113.53 111.01" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><g style="fill:#0080ff;fill-opacity:1"><g fill="none" style="fill:#0080ff;fill-opacity:1"><path class="solid" d="M12 22 0 0h24z" style="display:inline;fill:#0080ff;fill-opacity:1" transform="rotate(180 44.15 27.75) scale(2.52299)"/><path class="stroke" d="M12 17.82 20.63 2H3.37L12 17.82M12 22 0 0h24z" style="display:inline;fill:#0080ff;fill-opacity:1" transform="rotate(180 44.15 27.75) scale(2.52299)"/></g><g fill="none" style="fill:#0080ff;fill-opacity:1"><path class="solid" d="M12 22 0 0h24z" style="display:inline;fill:#0080ff;fill-opacity:1" transform="translate(27.75 55.5) scale(2.52299)"/><path class="stroke" d="M12 17.82 20.63 2H3.37L12 17.82M12 22 0 0h24z" style="display:inline;fill:#0080ff;fill-opacity:1" transform="translate(27.75 55.5) scale(2.52299)"/></g><g opacity=".5" style="fill:#0080ff;fill-opacity:1"><path class="solid" d="M24 22 12 0h24z" style="display:inline;fill:#0080ff;fill-opacity:1" transform="rotate(90 30.28 25.23) scale(2.52299)"/><path class="stroke" d="M24 17.82 32.63 2H15.37L24 17.82M24 22 12 0h24z" style="display:inline;fill:#0080ff;fill-opacity:1" transform="rotate(90 30.28 25.23) scale(2.52299)"/></g><g opacity=".5" style="fill:#0080ff;fill-opacity:1"><path class="solid" d="M24 22 12 0h24z" style="display:inline;fill:#0080ff;fill-opacity:1" transform="rotate(-90 87.05 29.02) scale(2.52299)"/><path class="stroke" d="M24 17.82 32.63 2H15.37L24 17.82M24 22 12 0h24z" style="display:inline;fill:#0080ff;fill-opacity:1" transform="rotate(-90 87.05 29.02) scale(2.52299)"/></g></g></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -0,0 +1 @@
<svg class="chevron" viewBox="0 0 78.743 46.968" xmlns="http://www.w3.org/2000/svg"><path d="m530.436 290.342 6.658 8.935c4.438 5.956 8.512 11.037 12.222 15.242 3.71 4.205 7.691 8.797 11.945 13.776a590.023 590.023 0 0 1 11.95 14.425c3.713 4.638 9.56 11.441 17.54 20.41 7.98 8.97 15.892 17.872 23.736 26.707a2267.256 2267.256 0 0 0 21.633 24.05c6.578 7.197 11.225 8.056 13.942 2.576 2.717-5.48 8.45-12.8 17.199-21.96 8.748-9.162 15.904-16.447 21.468-21.856a2287.547 2287.547 0 0 1 17.272-16.611 674.569 674.569 0 0 0 16.506-16.229c5.054-5.154 9.753-9.968 14.098-14.443 4.344-4.475 9.494-10.016 15.448-16.624 5.954-6.607 10.747-11.748 14.38-15.422 3.631-3.675 6.18-6.009 7.645-7.003a16.094 16.094 0 0 1 4.757-2.202 16.095 16.095 0 0 1 5.212-.568c1.768.096 3.474.47 5.12 1.126a16.094 16.094 0 0 1 4.492 2.701 16.094 16.094 0 0 1 3.394 3.995 16.094 16.094 0 0 1 1.941 4.87c.381 1.729.476 3.473.285 5.234a16.094 16.094 0 0 1-1.402 5.051 16.095 16.095 0 0 1-2.94 4.34c-15.004 15.842-39.417-8.906-22.252-23.487a16.094 16.094 0 0 1 4.491-2.703 16.094 16.094 0 0 1 5.12-1.127 16.095 16.095 0 0 1 5.211.567 16.095 16.095 0 0 1 4.758 2.2 16.094 16.094 0 0 1 3.805 3.606 16.094 16.094 0 0 1 2.456 4.631c.565 1.678.848 3.403.848 5.173 0 1.771-.283 3.495-.849 5.173a16.095 16.095 0 0 1-2.455 4.632l-1.606 2.112-5.37 4.582c-3.583 3.055-8.51 7.767-14.786 14.135a11933.445 11933.445 0 0 0-15.839 16.1c-4.284 4.364-8.943 9.26-13.975 14.688a75638.076 75638.076 0 0 1-15.826 17.063 2642.057 2642.057 0 0 0-15.89 17.273c-5.073 5.567-11.507 12.866-19.3 21.897-7.794 9.03-13.63 15.948-17.51 20.753a496.057 496.057 0 0 1-12.49 14.836c-4.447 5.085-9.976 7.77-16.588 8.052-6.612.283-12.583-2.325-17.913-7.822-5.33-5.498-11.117-11.861-17.361-19.091-6.245-7.23-11.274-13.11-15.087-17.64a385.872 385.872 0 0 0-11.496-13.06 905.91 905.91 0 0 1-12.117-13.426c-4.228-4.774-10.087-11.613-17.577-20.518-7.49-8.905-13.37-15.797-17.635-20.676-4.266-4.88-8.437-9.663-12.511-14.35-4.075-4.687-8.453-9.915-13.135-15.684-4.681-5.77-7.355-9.208-8.02-10.317a15.914 15.914 0 0 1-1.567-3.517 15.937 15.937 0 0 1 .724-11.305 15.914 15.914 0 0 1 2.003-3.288 15.914 15.914 0 0 1 2.733-2.713 15.913 15.913 0 0 1 3.302-1.98 15.914 15.914 0 0 1 3.68-1.132 15.914 15.914 0 0 1 3.845-.219c1.29.083 2.552.319 3.785.708 1.233.39 2.401.92 3.505 1.593a15.914 15.914 0 0 1 3.023 2.386l1.366 1.375z" transform="matrix(.26458 0 0 .26458 -132.758 -75.015)"/></svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M120-240v-80h720v80H120Zm0-200v-80h720v80H120Zm0-200v-80h720v80H120Z"/></svg>

After

Width:  |  Height:  |  Size: 151 B

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