Files
ServerKit/frontend/index.html
T
Juan DenisandClaude Opus 4.8 e1088aff71 feat(frontend): skeleton loaders, branded AppLoader, favicon refresh
Replace spinners with skeleton placeholders across page/section loaders
(EmptyState, DataTable, and ~page surfaces), add a reusable Skeleton
component + _skeleton.scss, rework AppLoader into a branded "server
powering on" loader, and refresh the favicon set (incl. SVG icon link).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 21:44:40 -04:00

51 lines
2.7 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
<meta name="description" content="ServerKit - Server Management Dashboard" />
<title>ServerKit</title>
<!-- Fonts are SELF-HOSTED (see src/styles/base/_fonts.scss → /fonts/*.woff2).
No Google Fonts / third-party CDN: a self-hosted control panel must not
leak every user's IP + usage to Google. Falls back to system-ui until
the woff2 files are present (run frontend/scripts/fetch-fonts.mjs). -->
<script>
// Prevent flash of unstyled content (FOUC) by setting theme + accent before React loads
(function() {
var d = document.documentElement;
d.setAttribute('data-theme', localStorage.getItem('theme') || 'dark');
var accent = localStorage.getItem('accent_color');
if (accent) {
var m = accent.match(/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i);
if (m) {
var r = parseInt(m[1],16), g = parseInt(m[2],16), b = parseInt(m[3],16);
var hx = function(v){ return Math.max(0,Math.min(255,v)).toString(16).padStart(2,'0'); };
var dk = function(v){ return Math.round(v*0.88); };
var br = function(v){ return Math.round(v + (255-v)*0.30); }; // toward white
var dm = function(v){ return Math.round(v*0.70); };
d.style.setProperty('--accent-primary', accent);
d.style.setProperty('--accent', accent);
d.style.setProperty('--accent-hover', '#'+hx(dk(r))+hx(dk(g))+hx(dk(b)));
d.style.setProperty('--accent-bright', '#'+hx(br(r))+hx(br(g))+hx(br(b)));
d.style.setProperty('--accent-dim', '#'+hx(dm(r))+hx(dm(g))+hx(dm(b)));
d.style.setProperty('--accent-bg', 'rgba('+r+','+g+','+b+',0.13)');
d.style.setProperty('--accent-bg-soft', 'rgba('+r+','+g+','+b+',0.07)');
d.style.setProperty('--accent-glow', 'rgba('+r+','+g+','+b+',0.35)');
d.style.setProperty('--accent-shadow', 'rgba('+r+','+g+','+b+',0.3)');
}
}
})();
</script>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>