diff --git a/.pugrc.js b/.pugrc.js index 7183759..d10d0b9 100644 --- a/.pugrc.js +++ b/.pugrc.js @@ -5,21 +5,21 @@ module.exports = { app_name: "Fappen", manifest: require('./manifest.json'), navigation: { - Frontpage: "/pages/index.pug", - Stregsystem: "/pages/stregsystem.pug", - Songbook: "/pages/songbook/index.pug", - Events: "/pages/events.pug", - Links: "/pages/links.pug", - Offline: "/pages/offline.pug" + Frontpage: ["/pages/index.pug", "🏠", "Go to the front page"], + Stregsystem: ["/pages/stregsystem.pug", "💵", "Browse our collection of wares"], + Songbook: ["/pages/songbook/index.pug", "🎼", "Browse our collection of songs"], + Events: ["/pages/events.pug", "📅", "List upcoming events"], + Links: ["/pages/links.pug", "🌐", "Look at nice links"], + Offline: ["/pages/offline.pug", "✈", "Enter offline-mode"], + TenFoot: ["/pages/tenfoot/toggle.pug", "➜", "Toggle 10-foot display"], }, links: { - Slack: "https://fklubben.slack.com", - Discord: "https://discord.gg/6DBvANjs3g", - Facebook: "https://www.facebook.com/fklub", - Github: "https://github.com/f-klubben", - Fiki: "https://fklub.dk", - Stregsystem: "https://stregsystem.fklub.dk" - + Slack: ["https://fklubben.slack.com"], + Discord: ["https://discord.gg/6DBvANjs3g"], + Facebook: ["https://www.facebook.com/fklub"], + Github: ["https://github.com/f-klubben"], + Fiki: ["https://fklub.dk"], + Stregsystem: ["https://stregsystem.fklub.dk"] }, disable_worker: process.env.disable_worker === "true" || true, } diff --git a/components/base_layout.pug b/components/page_layout.pug similarity index 66% rename from components/base_layout.pug rename to components/page_layout.pug index d03056d..67f7c56 100644 --- a/components/base_layout.pug +++ b/components/page_layout.pug @@ -7,6 +7,15 @@ block pug_meta doctype html html(lang="en") +head(title) + link(rel="stylesheet" href="/styles/base_10foot.scss") + script. + try { + if (localStorage.getItem('tenFoot') === '1') { + document.documentElement.classList.add('ten-foot'); + } + } catch (error) { + console.warn('Unable to restore display mode.', error); + } block head body header @@ -17,7 +26,7 @@ html(lang="en") ul.border-outer each key in Object.keys(navigation) .border-inner - a(href=navigation[key]) + a(href=navigation[key][0]) li= key button.darkmode-button darkmode main diff --git a/components/service_menu.pug b/components/service_menu.pug index fc5232a..59cde16 100644 --- a/components/service_menu.pug +++ b/components/service_menu.pug @@ -1,7 +1,12 @@ mixin menu(links, target) link(rel="stylesheet" href="/styles/service-menu.scss") - section.service-links.border-outer + link(rel="stylesheet" href="/styles/service-menu_10foot.scss") + section.service-menu.border-outer each key in Object.keys(links) - a(href=links[key] target=target) + a.service-link(href=links[key][0] target=target) + if links[key].length > 1 + .service-icon=links[key][1] .border-inner h3=key + if links[key].length > 2 + .service-description=links[key][2] diff --git a/package.json b/package.json index 1cf9dbc..2065433 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "scripts": { "start": "parcel pages/index.pug pages/songbook/songs/*.pug --dist-dir build/dist", "build-songs": "python util/build_songs.py", - "build": "parcel build pages/index.pug pages/songbook/songs/*.pug --dist-dir build/dist", + "build": "npm run build-songs && parcel build pages/index.pug pages/songbook/songs/*.pug --dist-dir build/dist", "cors-proxy": "node cors-proxy.js" }, "devDependencies": { diff --git a/pages/events.pug b/pages/events.pug index a20f80d..7484deb 100644 --- a/pages/events.pug +++ b/pages/events.pug @@ -1,4 +1,4 @@ -extends ../components/base_layout +extends ../components/page_layout block pug_meta - const page_title = "Events"; diff --git a/pages/index.pug b/pages/index.pug index b3000b3..82781b6 100644 --- a/pages/index.pug +++ b/pages/index.pug @@ -1,4 +1,4 @@ -extends ../components/base_layout +extends ../components/page_layout include ../components/service_menu block content diff --git a/pages/links.pug b/pages/links.pug index 60cba4b..1b9f0af 100644 --- a/pages/links.pug +++ b/pages/links.pug @@ -1,4 +1,4 @@ -extends ../components/base_layout +extends ../components/page_layout include ../components/service_menu block content diff --git a/pages/offline.pug b/pages/offline.pug index 940a200..8a27199 100644 --- a/pages/offline.pug +++ b/pages/offline.pug @@ -1,4 +1,4 @@ -extends ../components/base_layout +extends ../components/page_layout block head link(rel="stylesheet", href="/styles/offline.scss") diff --git a/pages/songbook/index.pug b/pages/songbook/index.pug index 5dd0143..d0fd213 100644 --- a/pages/songbook/index.pug +++ b/pages/songbook/index.pug @@ -1,4 +1,4 @@ -extends ../../components/base_layout.pug +extends ../../components/page_layout block head link(rel="stylesheet" href="/styles/songbook.scss") script(type="module", src="/scripts/songbook.ts") diff --git a/pages/stregsystem.pug b/pages/stregsystem.pug index 35a6c3c..941c9c8 100644 --- a/pages/stregsystem.pug +++ b/pages/stregsystem.pug @@ -1,4 +1,4 @@ -extends ../components/base_layout +extends ../components/page_layout block pug_meta - const page_title = "Stregsystemet"; diff --git a/pages/tenfoot/toggle.pug b/pages/tenfoot/toggle.pug new file mode 100644 index 0000000..2f1c55d --- /dev/null +++ b/pages/tenfoot/toggle.pug @@ -0,0 +1,13 @@ +include ../../components/core + +doctype html +html(lang="en") + body + script. + try { + const tenFoot = localStorage.getItem('tenFoot') === '1'; + localStorage.setItem('tenFoot', tenFoot ? '0' : '1'); + } catch (error) { + console.warn('Unable to change display mode.', error); + } + window.location.href = '/'; diff --git a/scripts/index.ts b/scripts/index.ts index 2308777..75582ed 100644 --- a/scripts/index.ts +++ b/scripts/index.ts @@ -47,6 +47,45 @@ function toggle_sidebar() { else sidebar.classList.add('active'); } + +function init_tenfoot_navigation() { + if (!document.documentElement.classList.contains('ten-foot')) return; + + const links = Array.from(document.querySelectorAll('.service-menu .service-link')); + if (links.length === 0) return; + + document.addEventListener('keydown', event => { + if (event.target instanceof HTMLInputElement || event.target instanceof HTMLTextAreaElement || event.target instanceof HTMLSelectElement) return; + + const current_index = links.indexOf(document.activeElement as HTMLAnchorElement); + let next_index: number | undefined; + + switch (event.key) { + case 'ArrowRight': + case 'ArrowDown': + next_index = current_index < 0 ? 0 : (current_index + 1) % links.length; + break; + case 'ArrowLeft': + case 'ArrowUp': + next_index = current_index < 0 ? links.length - 1 : (current_index - 1 + links.length) % links.length; + break; + case 'Home': + next_index = 0; + break; + case 'End': + next_index = links.length - 1; + break; + } + + if (next_index === undefined) return; + event.preventDefault(); + links[next_index].focus(); + links[next_index].scrollIntoView({behavior: 'smooth', inline: 'center', block: 'nearest'}); + }); + + links[0].focus(); +} + function toggle_darkmode(){ let theme = document.body.classList.toggle("dark-theme")? "dark" : "light"; localStorage.setItem("theme", theme); @@ -83,6 +122,7 @@ void (() => { ?.forEach(node => { node.addEventListener('click', toggle_sidebar); }); + init_tenfoot_navigation(); /* Dark Mode stuff */ diff --git a/styles/base_10foot.scss b/styles/base_10foot.scss new file mode 100644 index 0000000..8457778 --- /dev/null +++ b/styles/base_10foot.scss @@ -0,0 +1,73 @@ +html.ten-foot { + $primary-gradient-start: #1a1a2e; + $primary-gradient-end: #16213e; + $text-color: #ffffff; + $focus-color: #00d4ff; + + &, + body { + background: linear-gradient(135deg, $primary-gradient-start 0%, $primary-gradient-end 100%); + color: $text-color; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; + overflow-x: hidden; + } + + body { + height: 100dvh; + display: flex; + flex-direction: column; + } + + main { + flex: 1; + padding: clamp(1rem, 4vw, 4rem); + display: flex; + flex-direction: column; + overflow: hidden; + align-items: stretch; + } + + .big-logo { + display: none; + } + + .center { + text-align: center; + } + + .flex-center { + align-content: center; + justify-content: center; + } + + header { + display: none; + } + + .spinner { + animation: rotation 5s infinite linear; + } + + @keyframes rotation { + from { + transform: rotate(0deg); + } + to { + transform: rotate(359deg); + } + } + + .border-outer { + border: inherit !important; + } + + .border-inner { + margin: 0 !important; + border: 0 !important; + } + + *:focus { + outline: 4px solid $focus-color; + outline-offset: 4px; + } +} diff --git a/styles/service-menu.scss b/styles/service-menu.scss index 33e4124..15e1f26 100644 --- a/styles/service-menu.scss +++ b/styles/service-menu.scss @@ -1,4 +1,4 @@ -.service-links { +.service-menu { width: 50%; box-sizing: border-box; text-align: center; @@ -7,4 +7,8 @@ .service-body { padding: 1em; +} + +.service-icon, .service-description { + display: none; } \ No newline at end of file diff --git a/styles/service-menu_10foot.scss b/styles/service-menu_10foot.scss new file mode 100644 index 0000000..6d75478 --- /dev/null +++ b/styles/service-menu_10foot.scss @@ -0,0 +1,113 @@ +// ten-foot.scss +// Variables +$primary-gradient-start: #1a1a2e; +$primary-gradient-end: #16213e; +$text-color: #ffffff; +$card-bg: rgba(255, 255, 255, 0.1); +$card-bg-hover: rgba(255, 255, 255, 0.15); +$card-border: rgba(255, 255, 255, 0.2); +$card-border-hover: rgba(255, 255, 255, 0.4); +$focus-color: #00d4ff; + +// Mixins +@mixin card-shadow { + box-shadow: 0 20px 60px rgba(0, 0, 0, 0.4); +} + +@mixin text-shadow { + text-shadow: 0 4px 8px rgba(0, 0, 0, 0.5); +} + +@mixin smooth-transition($properties: all, $duration: 0.3s) { + transition: $properties $duration ease; +} + +// Only apply rules on 10-foot mode +html.ten-foot { + // Service Grid + .service-menu { + width: 100%; + flex: 1; + display: flex; + align-items: center; + gap: clamp(2rem, 3vw, 4rem); + + overflow-x: auto; + overflow-y: hidden; + + scroll-behavior: smooth; + scroll-snap-type: x mandatory; + padding-inline: clamp(1rem, 3vw, 3rem); + + /* IE / Edge Legacy */ + -ms-overflow-style: none; + } + + .service-menu::-webkit-scrollbar { + display: none; /* Chrome, Safari, Edge */ + } + + .service-menu > a { + // Un-linkify + display: block; + text-decoration: none; + color: inherit; + } + + .service-menu > .service-link { + flex: 0 0 auto; // prevents shrinking + margin: 0; + + display: flex; + flex-direction: column; + align-items: center; + gap: 2em; + + text-align: center; + box-sizing: border-box; + background: $card-bg; + backdrop-filter: blur(10px); + border-radius: 2rem; + padding: 3em; + @include smooth-transition; + border: 2px solid $card-border; + cursor: pointer; + font-size: clamp(0.75rem, 1vw, 1.25rem); + min-height: clamp(24rem, 62vh, 40rem); + width: clamp(20rem, 25vw, 30rem); + scroll-margin-inline: 3rem; + scroll-snap-align: center; + justify-content: center; + + &:hover, + &:focus-visible { + transform: scale(1.05); + background: $card-bg-hover; + border-color: $focus-color; + @include card-shadow; + z-index: 1; + } + + h3 { + font-size: 3em; + font-weight: 600; + line-height: 1.15; + margin: 0; + } + + .service-icon { + display: block; + font-size: 6em; + filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.3)); + } + + .service-description { + display: block; + font-size: 1.8em; + opacity: 0.95; + font-weight: 500; + line-height: 1.6; + } + + } +} diff --git a/util/template/song.pug b/util/template/song.pug index 05c2a6c..ce89c4b 100644 --- a/util/template/song.pug +++ b/util/template/song.pug @@ -1,4 +1,4 @@ -extends ../../../components/base_layout +extends ../../../components/page_layout block head link(rel="stylesheet" href="/styles/songbook.scss") link(rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css")