Header Integration

This commit is contained in:
Henrik Brüsecke 2026-09-05 13:28:49 +03:00
parent 59da4a7cc5
commit a37d322994
13 changed files with 80 additions and 3 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.1 KiB

View file

@ -0,0 +1,17 @@
.brand-link {
align-items: center;
color: inherit;
display: flex;
gap: 15px;
height: 100%;
text-decoration: none;
}
.brand-logo {
height: 100%;
}
.brand-title {
font-size: 20px;
font-weight: 750;
}

View file

@ -0,0 +1,30 @@
@media (min-width: 480px) {
.nav-link-account {
margin-left: 0;
}
.news-ticker {
display: inline;
}
}
@media (min-width: 960px) {
.screen-narrow {
display: none;
}
.screen-wide {
display: inline;
}
}
@media (min-height: 540px) {
.header {
height: 60px;
padding: 10px 10px 10px 15px;
}
.nav-link-account {
padding: 10px 15px;
}
}

View file

@ -0,0 +1,22 @@
body {
font-family: system-ui;
margin: 0;
}
.header {
background-color: #202020;
box-sizing: border-box;
color: white;
display: flex;
gap: 30px;
height: 40px;
padding: 5px 5px 5px 10px;
}
.screen-narrow {
display: inline;
}
.screen-wide {
display: none;
}

View file

@ -0,0 +1,9 @@
.brand-link:hover,
.nav-link:hover {
color: orange;
}
.nav-link-account:hover {
background-color: orange;
color: white;
}

View file

@ -0,0 +1,54 @@
.nav {
align-items: center;
color: inherit;
display: flex;
flex: 1;
gap: 15px;
min-width: 0;
}
.nav-link {
color: inherit;
font-size: 15px;
font-weight: 600;
text-decoration: none;
}
.nav-link-account {
background-color: #0d6efd;
border-radius: 5px;
margin-left: auto;
padding: 5px 10px;
}
.news-ticker {
color: #00FF41;
display: none;
flex: 1;
margin-left: auto;
max-width: 480px;
overflow: hidden;
white-space: nowrap;
}
.news-ticker-content {
animation: news-ticker-animation 10s linear infinite;
animation-play-state: paused;
display: inline-block;
padding-left: 100%;
}
@keyframes news-ticker-animation {
from {
transform: translateX(0);
}
/* Finish the animation early to create a delay between announcements */
90% {
transform: translateX(-100%);
}
to {
transform: translateX(-100%);
}
}

View file

@ -0,0 +1,42 @@
<!doctype html>
<html>
<head>
<link href="assets/favicon.ico" rel="icon">
<link href="css/brand.css" rel="stylesheet">
<link href="css/header.css" rel="stylesheet">
<link href="css/nav.css" rel="stylesheet">
<link href="css/hover.css" rel="stylesheet">
<link href="css/desktop.css" rel="stylesheet">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="news-ticker.js" type="module"></script>
<title>Instant Workstation</title>
</head>
<body>
<header class="header">
<a class="brand-link" href="/">
<img alt="" class="brand-logo" src="assets/logo.svg">
<span class="brand-title screen-wide">Instant Workstation</span>
</a>
<nav class="nav">
<a class="nav-link screen-narrow" href="https://code.instantworkstation.com">Code</a>
<a class="nav-link screen-narrow" href="https://docs.instantworkstation.com">Docs</a>
<a class="nav-link screen-wide" href="https://code.instantworkstation.com">Source Code</a>
<a class="nav-link screen-wide" href="https://docs.instantworkstation.com">Technical Documentation</a>
<span class="news-ticker">
<span class="news-ticker-content" id="news-ticker-content"></span>
</span>
<a class="nav-link nav-link-account" href="/account">Account</a>
</nav>
</header>
</body>
</html>

View file

@ -0,0 +1,46 @@
const response = await fetch('https://api.instantworkstation.com/workstations');
const workstations = await response.json();
const now = new Date();
const recent = new Date(now);
recent.setUTCDate(now.getUTCDate() - 30);
recent.setUTCHours(0, 0, 0, 0);
const recentWorkstations = workstations.filter(isRecent);
const news = recentWorkstations.flatMap(generateNews);
const ticker = document.getElementById('news-ticker-content');
let index = 0;
if (news.length > 0) {
ticker.textContent = news[index];
ticker.addEventListener('animationiteration', showNextAnnouncement);
ticker.style.animationPlayState = 'running';
}
function generateNews(workstation) {
return workstation.architectures.map(architecture => {
const workstationDetails = `${workstation.os} ${workstation.version}`;
const architectureDetails = `${architecture.cpu} ${architecture.bitness} ${architecture.endianness}`;
return `${workstationDetails} ${architectureDetails} is now available`;
});
}
function isRecent(workstation) {
const added = new Date(workstation.added);
return added >= recent;
}
function showNextAnnouncement() {
index++;
if (index < news.length) {
ticker.textContent = news[index];
} else {
ticker.removeEventListener('animationiteration', showNextAnnouncement);
ticker.style.animationPlayState = 'paused';
ticker.textContent = '';
}
}