UI Skins
All checks were successful
Deploy / deploy-staging (push) Successful in 11s
Deploy / deploy-production (push) Has been skipped

This commit is contained in:
2026-06-08 00:30:18 +02:00
parent 538175e52e
commit a8b197592b
14 changed files with 476 additions and 57 deletions

View File

@@ -1,7 +1,6 @@
:root {
color-scheme: light;
--shell-text: #f8fafc;
--shell-panel: rgba(15, 23, 42, 0.42);
--shell-border: rgba(255, 255, 255, 0.16);
--card-bg: rgba(255, 255, 255, 0.14);
--window-bg: #f8fafc;
@@ -10,7 +9,18 @@
--accent: #60a5fa;
--taskbar-bg: rgba(15, 23, 42, 0.8);
--shadow: 0 16px 40px rgba(15, 23, 42, 0.35);
font-family: "IBM Plex Sans", "Segoe UI", sans-serif;
--desktop-font: "IBM Plex Sans", "Segoe UI", sans-serif;
--taskbar-height: 60px;
--taskbar-left: 24px;
--taskbar-right: 24px;
--taskbar-bottom: 24px;
--taskbar-width: auto;
--taskbar-radius: 22px;
--taskbar-justify: flex-start;
--stage-top-offset: 0px;
--icon-top: 24px;
--icon-left: 24px;
font-family: var(--desktop-font);
}
* {
@@ -24,24 +34,44 @@ body {
color: var(--shell-text);
}
body[data-skin="apple"] {
--accent: #fb7185;
--taskbar-bg: rgba(45, 45, 45, 0.65);
--window-bg: #fffbeb;
--window-header-bg: #fff1d6;
}
body[data-skin="linux"] {
--accent: #f97316;
--taskbar-bg: rgba(17, 24, 39, 0.86);
--window-bg: #f9fafb;
--window-header-bg: #eceff3;
}
.desktop-shell {
min-height: 100vh;
}
.system-bar {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 140;
display: flex;
align-items: center;
justify-content: space-between;
height: 32px;
padding: 0 14px;
background: rgba(244, 244, 245, 0.82);
color: #111827;
backdrop-filter: blur(20px);
}
.system-bar-left,
.system-bar-right {
display: flex;
align-items: center;
gap: 10px;
font-size: 13px;
}
.system-brand {
font-weight: 700;
}
.system-divider {
width: 1px;
height: 14px;
background: rgba(17, 24, 39, 0.2);
}
.eyebrow {
margin: 0 0 12px;
text-transform: uppercase;
@@ -79,8 +109,8 @@ h1 {
.desktop-icons {
position: absolute;
top: 24px;
left: 24px;
top: var(--icon-top);
left: var(--icon-left);
z-index: 10;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
@@ -123,7 +153,7 @@ h1 {
.widget-zone {
position: absolute;
top: 24px;
top: calc(var(--stage-top-offset) + 24px);
right: 24px;
display: grid;
gap: 14px;
@@ -298,14 +328,17 @@ h1 {
.taskbar {
position: fixed;
left: 24px;
right: 24px;
bottom: 24px;
left: var(--taskbar-left);
right: var(--taskbar-right);
bottom: var(--taskbar-bottom);
width: var(--taskbar-width);
display: flex;
align-items: center;
gap: 16px;
justify-content: var(--taskbar-justify);
min-height: var(--taskbar-height);
padding: 12px 16px;
border-radius: 22px;
border-radius: var(--taskbar-radius);
background: var(--taskbar-bg);
backdrop-filter: blur(18px);
border: 1px solid var(--shell-border);
@@ -322,6 +355,18 @@ h1 {
cursor: pointer;
}
.start-button {
display: inline-flex;
align-items: center;
gap: 8px;
}
.start-button-icon {
font-size: 11px;
font-weight: 700;
opacity: 0.8;
}
.taskbar-app-button.is-focused {
background: var(--accent);
color: #0f172a;
@@ -334,7 +379,7 @@ h1 {
.start-menu {
position: fixed;
left: 24px;
bottom: 102px;
bottom: calc(var(--taskbar-height) + 28px);
z-index: 130;
display: grid;
align-content: start;
@@ -485,6 +530,7 @@ h1 {
left: 16px;
right: 16px;
bottom: 88px;
transform: none;
width: auto;
grid-template-columns: 1fr;
}

View File

@@ -12,10 +12,14 @@ if (payloadNode) {
const startMenuWidgets = document.getElementById('start-menu-widgets');
const startMenuActions = document.getElementById('start-menu-actions');
const traySkins = document.getElementById('tray-skins');
const traySkinsTop = document.getElementById('tray-skins-top');
const clockNode = document.getElementById('clock');
const clockTopNode = document.getElementById('clock-top');
const windows = new Map();
const activeWidgets = new Set(payload.widgets.active_ids);
const activeSkin = payload.meta.active_skin;
const skinProfile = payload.meta.skin_profile || {};
const maximizeOverSystemBar = Boolean(skinProfile.maximize_over_system_bar);
const storageScope = payload.desktop.storage_scope || 'guest';
const storageKey = `desktop.kusche.berlin.window-state.${storageScope}`;
let zIndex = 20;
@@ -57,10 +61,12 @@ if (payloadNode) {
}
const taskbar = document.querySelector('.taskbar');
const systemBar = document.querySelector('.system-bar');
const stageRect = windowsRoot.getBoundingClientRect();
const taskbarRect = taskbar ? taskbar.getBoundingClientRect() : null;
const systemBarRect = systemBar ? systemBar.getBoundingClientRect() : null;
topDesktopInset = 0;
topDesktopInset = systemBarRect ? Math.round(systemBarRect.height) : 0;
sideDesktopInset = 0;
bottomDesktopInset = taskbarRect
? Math.max(0, Math.round(stageRect.bottom - taskbarRect.top))
@@ -82,15 +88,19 @@ if (payloadNode) {
};
const renderClock = () => {
if (!clockNode) {
return;
}
clockNode.textContent = new Intl.DateTimeFormat('de-DE', {
const formatted = new Intl.DateTimeFormat('de-DE', {
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
}).format(new Date());
if (clockNode) {
clockNode.textContent = formatted;
}
if (clockTopNode) {
clockTopNode.textContent = formatted;
}
};
const navigateToSkin = (skin) => {
@@ -98,20 +108,22 @@ if (payloadNode) {
};
const renderTraySkins = () => {
if (!traySkins) {
return;
}
[traySkins, traySkinsTop].forEach((container) => {
if (!container) {
return;
}
traySkins.innerHTML = '';
container.innerHTML = '';
payload.meta.available_skins.forEach((skin) => {
const button = document.createElement('button');
button.type = 'button';
button.className = `tray-skin-button${skin === activeSkin ? ' is-active' : ''}`;
button.textContent = skin.slice(0, 3).toUpperCase();
button.title = skin;
button.addEventListener('click', () => navigateToSkin(skin));
traySkins.appendChild(button);
payload.meta.available_skins.forEach((skin) => {
const button = document.createElement('button');
button.type = 'button';
button.className = `tray-skin-button${skin === activeSkin ? ' is-active' : ''}`;
button.textContent = skin.slice(0, 3).toUpperCase();
button.title = skin;
button.addEventListener('click', () => navigateToSkin(skin));
container.appendChild(button);
});
});
};
@@ -184,12 +196,13 @@ if (payloadNode) {
record.state.maximized = true;
record.node.classList.add('is-maximized');
measureDesktopBounds();
const maximizedTopInset = maximizeOverSystemBar ? 0 : topDesktopInset;
record.node.style.left = `${sideDesktopInset}px`;
record.node.style.top = `${topDesktopInset}px`;
record.node.style.top = `${maximizedTopInset}px`;
record.node.style.width = sideDesktopInset === 0 ? '100%' : `calc(100% - ${sideDesktopInset * 2}px)`;
record.node.style.height = activeSkin === 'apple'
record.node.style.height = maximizeOverSystemBar
? '100%'
: `calc(100% - ${topDesktopInset + bottomDesktopInset}px)`;
: `calc(100% - ${maximizedTopInset + bottomDesktopInset}px)`;
focusWindow(windowId);
saveWindowState();
return;
@@ -417,12 +430,13 @@ if (payloadNode) {
if (record.state.maximized) {
record.node.classList.add('is-maximized');
measureDesktopBounds();
const maximizedTopInset = maximizeOverSystemBar ? 0 : topDesktopInset;
record.node.style.left = `${sideDesktopInset}px`;
record.node.style.top = `${topDesktopInset}px`;
record.node.style.top = `${maximizedTopInset}px`;
record.node.style.width = sideDesktopInset === 0 ? '100%' : `calc(100% - ${sideDesktopInset * 2}px)`;
record.node.style.height = activeSkin === 'apple'
record.node.style.height = maximizeOverSystemBar
? '100%'
: `calc(100% - ${topDesktopInset + bottomDesktopInset}px)`;
: `calc(100% - ${maximizedTopInset + bottomDesktopInset}px)`;
}
node.addEventListener('mousedown', () => focusWindow(record.id));

View File

@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
return [
'id' => 'apple',
'label' => 'Apple',
'family' => 'apple',
'wallpaper' => 'radial-gradient(circle at top, #ffe7ba 0%, #ff9a62 38%, #531b53 100%)',
'top_bar' => true,
'taskbar_mode' => 'dock',
'taskbar_label' => 'Launchpad',
'taskbar_icon' => 'Mac',
'window_controls' => 'mac',
'maximize_over_system_bar' => true,
];

View File

@@ -0,0 +1,50 @@
body[data-skin="apple"] {
--accent: #fb7185;
--taskbar-bg: rgba(45, 45, 45, 0.65);
--window-bg: #fffbeb;
--window-header-bg: #fff1d6;
--desktop-font: "SF Pro Display", "Helvetica Neue", sans-serif;
--taskbar-height: 72px;
--taskbar-left: 50%;
--taskbar-right: auto;
--taskbar-bottom: 18px;
--taskbar-width: auto;
--taskbar-radius: 24px;
--taskbar-justify: center;
--stage-top-offset: 34px;
--icon-top: 56px;
}
body[data-skin="apple"] .system-bar {
background: rgba(244, 244, 245, 0.82);
color: #111827;
}
body[data-skin="apple"] .taskbar {
transform: translateX(-50%);
border: 1px solid rgba(255, 255, 255, 0.26);
box-shadow: 0 18px 40px rgba(17, 24, 39, 0.22);
}
body[data-skin="apple"] .start-button {
order: 2;
min-width: 92px;
justify-content: center;
background: rgba(255, 255, 255, 0.24);
}
body[data-skin="apple"] .tray {
order: 1;
flex: 0 0 auto;
}
body[data-skin="apple"] .taskbar-apps {
order: 3;
justify-content: center;
}
body[data-skin="apple"] .start-menu {
left: 50%;
bottom: 106px;
transform: translateX(-50%);
}

View File

@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
return [
'id' => 'base',
'label' => 'Base',
'family' => 'base',
'wallpaper' => 'linear-gradient(160deg, #1d4ed8 0%, #0f172a 100%)',
'top_bar' => false,
'taskbar_mode' => 'taskbar',
'taskbar_label' => 'Menu',
'taskbar_icon' => 'OS',
'window_controls' => 'windows',
'maximize_over_system_bar' => false,
];

View File

@@ -0,0 +1,24 @@
.tray-skins,
.system-skins {
display: inline-flex;
align-items: center;
gap: 6px;
}
.tray-skin-button {
min-width: 38px;
height: 28px;
padding: 0 10px;
border: 1px solid rgba(255, 255, 255, 0.18);
border-radius: 999px;
background: rgba(255, 255, 255, 0.08);
color: inherit;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.04em;
cursor: pointer;
}
.tray-skin-button.is-active {
background: rgba(255, 255, 255, 0.24);
}

View File

@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
return [
'id' => 'linux',
'label' => 'Linux Mint',
'family' => 'linux-mint',
'wallpaper' => 'linear-gradient(140deg, #123329 0%, #1f5d4f 45%, #0f172a 100%)',
'top_bar' => false,
'taskbar_mode' => 'panel',
'taskbar_label' => 'Menu',
'taskbar_icon' => 'Mint',
'window_controls' => 'linux',
'maximize_over_system_bar' => false,
];

View File

@@ -0,0 +1,33 @@
body[data-skin="linux"] {
--accent: #86efac;
--taskbar-bg: linear-gradient(180deg, rgba(33, 46, 42, 0.98), rgba(21, 31, 28, 0.98));
--window-bg: #f9fafb;
--window-header-bg: #eceff3;
--desktop-font: "Noto Sans", "Ubuntu", sans-serif;
--taskbar-height: 48px;
--taskbar-left: 0;
--taskbar-right: 0;
--taskbar-bottom: 0;
--taskbar-width: 100%;
--taskbar-radius: 0;
--taskbar-justify: flex-start;
}
body[data-skin="linux"] .taskbar {
border-left: 0;
border-right: 0;
border-bottom: 0;
backdrop-filter: none;
box-shadow: 0 -10px 24px rgba(0, 0, 0, 0.16);
}
body[data-skin="linux"] .start-button {
border-radius: 10px;
background: linear-gradient(180deg, rgba(92, 185, 118, 0.24), rgba(55, 110, 75, 0.28));
}
body[data-skin="linux"] .start-menu {
left: 12px;
bottom: 58px;
border-radius: 18px 18px 0 0;
}

View File

@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
return [
'id' => 'windows',
'label' => 'Windows',
'family' => 'windows',
'wallpaper' => 'linear-gradient(160deg, #1d4ed8 0%, #0f172a 100%)',
'top_bar' => false,
'taskbar_mode' => 'taskbar',
'taskbar_label' => 'Start',
'taskbar_icon' => 'Win',
'window_controls' => 'windows',
'maximize_over_system_bar' => false,
];

View File

@@ -0,0 +1,37 @@
body[data-skin="windows"] {
--accent: #60a5fa;
--taskbar-bg: linear-gradient(180deg, rgba(24, 39, 75, 0.94), rgba(15, 23, 42, 0.96));
--window-bg: #f8fafc;
--window-header-bg: linear-gradient(180deg, #eff6ff, #dbeafe);
--desktop-font: "Segoe UI", "IBM Plex Sans", sans-serif;
--taskbar-height: 54px;
--taskbar-left: 0;
--taskbar-right: 0;
--taskbar-bottom: 0;
--taskbar-width: 100%;
--taskbar-radius: 0;
--taskbar-justify: flex-start;
--icon-top: 22px;
--icon-left: 24px;
}
body[data-skin="windows"] .taskbar {
padding: 8px 14px;
border-left: 0;
border-right: 0;
border-bottom: 0;
backdrop-filter: blur(12px);
}
body[data-skin="windows"] .start-button {
min-width: 94px;
justify-content: center;
border-radius: 8px;
background: rgba(255, 255, 255, 0.14);
}
body[data-skin="windows"] .start-menu {
left: 12px;
bottom: 62px;
border-radius: 14px 14px 0 0;
}