Files
TUIkit/palette-preview.html
T
phranck 0ba791c1eb Feat: Cropped terminal favicon and keyboard theme cycling
- Generate cropped favicon from tuikit-logo.png (transparent edges removed)
- Add multi-size favicons (16, 32, 192, 512px + .ico)
- Update metadata.icons in layout.tsx with proper sizes
- Add global 't' keydown listener in ThemeProvider to cycle themes
- Fix TUIKit → TUIkit typo in palette-preview.html
2026-02-01 03:50:00 +01:00

578 lines
16 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TUIkit Palette Preview</title>
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--page-bg: #0a0a0a;
--page-fg: #999;
--page-fg-bright: #ccc;
--page-border: #222;
--card-radius: 10px;
}
body {
background: var(--page-bg);
color: var(--page-fg);
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
font-size: 18px;
line-height: 1.6;
padding: 48px 32px 100px;
max-width: 1600px;
margin: 0 auto;
}
h1 {
font-size: 44px;
font-weight: 700;
color: var(--page-fg-bright);
margin-bottom: 8px;
letter-spacing: -0.5px;
}
.subtitle {
font-size: 20px;
color: #666;
margin-bottom: 56px;
}
h2 {
font-size: 30px;
font-weight: 600;
color: var(--page-fg-bright);
margin: 56px 0 28px;
padding-bottom: 14px;
border-bottom: 1px solid var(--page-border);
letter-spacing: -0.3px;
}
.palette-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 32px;
}
.palette-card {
border: 1px solid var(--page-border);
border-radius: var(--card-radius);
overflow: hidden;
background: #0e0e0e;
}
.palette-header {
padding: 24px 28px 20px;
border-bottom: 1px solid var(--page-border);
}
.palette-name {
font-size: 28px;
font-weight: 700;
margin-bottom: 6px;
letter-spacing: -0.3px;
}
.palette-desc {
font-size: 16px;
color: #666;
font-style: italic;
}
.palette-body {
padding: 24px 28px;
}
/* Color swatches */
.swatch-section {
margin-bottom: 22px;
}
.swatch-section-title {
font-size: 13px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 1.2px;
color: #555;
margin-bottom: 10px;
}
.swatch-row {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
gap: 10px;
}
.swatch {
display: flex;
flex-direction: column;
align-items: center;
gap: 5px;
}
.swatch-color {
width: 100%;
height: 40px;
border-radius: 6px;
border: 1px solid rgba(255,255,255,0.06);
}
.swatch-label {
font-size: 13px;
font-weight: 500;
color: #555;
text-align: center;
line-height: 1.3;
word-break: break-word;
}
.swatch-hex {
font-family: "SF Mono", "Fira Code", "Cascadia Code", "JetBrains Mono", Menlo, Monaco, "Courier New", monospace;
font-size: 12px;
color: #444;
text-align: center;
}
/* Existing palette section - more compact */
.existing-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(min(100%, 360px), 1fr));
gap: 20px;
}
.existing-card {
border: 1px solid var(--page-border);
border-radius: 8px;
overflow: hidden;
background: #0e0e0e;
padding: 20px 24px;
}
.existing-name {
font-size: 22px;
font-weight: 700;
margin-bottom: 14px;
}
.existing-swatches {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(40px, 1fr));
gap: 6px;
}
.existing-swatch {
width: 100%;
height: 30px;
border-radius: 5px;
border: 1px solid rgba(255,255,255,0.06);
}
/* Hue distribution bar */
.hue-wheel {
display: flex;
align-items: center;
gap: 20px;
margin: 28px 0 40px;
padding: 20px 24px;
background: #0e0e0e;
border: 1px solid var(--page-border);
border-radius: 10px;
}
.hue-bar {
flex: 1;
height: 16px;
border-radius: 8px;
position: relative;
background: linear-gradient(to right,
hsl(0,80%,50%), hsl(30,80%,50%), hsl(60,80%,50%),
hsl(90,80%,50%), hsl(120,80%,50%), hsl(150,80%,50%),
hsl(180,80%,50%), hsl(210,80%,50%), hsl(240,80%,50%),
hsl(270,80%,50%), hsl(300,80%,50%), hsl(330,80%,50%),
hsl(360,80%,50%)
);
}
.hue-marker {
position: absolute;
top: -4px;
width: 3px;
height: 20px;
border-radius: 2px;
transform: translateX(-50%);
}
.hue-marker-existing {
background: rgba(255,255,255,0.5);
border: 1px solid rgba(255,255,255,0.2);
}
.hue-marker-new {
background: #fff;
border: 2px solid #fff;
box-shadow: 0 0 6px rgba(255,255,255,0.5);
height: 22px;
top: -5px;
}
.hue-legend {
font-size: 14px;
color: #555;
display: flex;
gap: 18px;
flex-shrink: 0;
}
.hue-legend span {
display: flex;
align-items: center;
gap: 4px;
}
.hue-legend-mark {
display: inline-block;
width: 12px;
height: 3px;
border-radius: 2px;
}
/* ── Responsive ──────────────────────────────────────────────────── */
@media (max-width: 900px) {
.palette-grid { grid-template-columns: 1fr; }
}
@media (max-width: 768px) {
body { padding: 24px 16px 60px; font-size: 16px; }
h1 { font-size: 32px; }
h2 { font-size: 24px; margin: 40px 0 20px; }
.subtitle { font-size: 17px; margin-bottom: 36px; }
.palette-header { padding: 18px 20px 14px; }
.palette-name { font-size: 22px; }
.palette-body { padding: 18px 20px; }
.hue-wheel { flex-direction: column; gap: 12px; padding: 16px; }
.hue-bar { width: 100%; height: 16px; flex: none; }
.hue-legend { justify-content: center; }
}
@media (max-width: 480px) {
body { padding: 16px 12px 40px; }
h1 { font-size: 26px; }
.swatch-row { grid-template-columns: repeat(auto-fill, minmax(60px, 1fr)); }
.swatch-color { height: 32px; }
.existing-grid { grid-template-columns: 1fr; }
}
</style>
</head>
<body>
<h1>TUIkit Color Palettes</h1>
<p class="subtitle">5 proposed palettes with Palette (13) + BlockPalette (3) properties &mdash; plus existing palettes for reference</p>
<!-- Hue distribution visualization -->
<div class="hue-wheel">
<div class="hue-bar">
<!-- Existing palettes -->
<div class="hue-marker hue-marker-existing" style="left: 0%;" title="Red (0°)"></div>
<div class="hue-marker hue-marker-existing" style="left: 8.3%;" title="Amber (30°)"></div>
<div class="hue-marker hue-marker-existing" style="left: 33.3%;" title="Green (120°)"></div>
<div class="hue-marker hue-marker-existing" style="left: 58.3%;" title="Blue (210°)"></div>
<div class="hue-marker hue-marker-existing" style="left: 75%;" title="Violet (270°)"></div>
<!-- New palettes -->
<div class="hue-marker hue-marker-new" style="left: 4.2%;" title="Coral (15°)"></div>
<div class="hue-marker hue-marker-new" style="left: 12.5%;" title="Gold (45°)"></div>
<div class="hue-marker hue-marker-new" style="left: 50%;" title="Teal (180°)"></div>
<div class="hue-marker hue-marker-new" style="left: 54.2%;" title="Frost (195°)"></div>
<div class="hue-marker hue-marker-new" style="left: 88.9%;" title="Magenta (320°)"></div>
</div>
<div class="hue-legend">
<span><span class="hue-legend-mark" style="background: rgba(255,255,255,0.4);"></span>Existing</span>
<span><span class="hue-legend-mark" style="background: #fff; box-shadow: 0 0 4px rgba(255,255,255,0.5);"></span>New</span>
</div>
</div>
<h2>New Palettes</h2>
<div class="palette-grid" id="new-palettes"></div>
<h2>Existing Palettes</h2>
<div class="existing-grid" id="existing-palettes"></div>
<script>
// ── Palette Definitions ──────────────────────────────────────────────
const newPalettes = [
{
name: "Teal",
desc: "Inspired by LCARS (Star Trek) terminal displays, teal control panels, and bridge consoles.",
colors: {
background: "#060A0A",
foreground: "#00DDC0",
foregroundSecondary: "#00AA94",
foregroundTertiary: "#007A6B",
accent: "#33FFE0",
success: "#33FFAA",
warning: "#CCFF66",
error: "#FF6633",
info: "#66FFEE",
border: "#2D5A55",
statusBarBackground: "#0D1E1D",
appHeaderBackground: "#0A1B1C",
overlayBackground: "#060A0A",
surfaceBackground: "#0E2526",
surfaceHeaderBackground: "#0A1B1C",
elevatedBackground: "#144542",
}
},
{
name: "Magenta",
desc: "Synthwave neon glow \u2014 inspired by 80s arcade cabinets, retrowave, and cyberpunk HUDs.",
colors: {
background: "#0A060A",
foreground: "#FF44CC",
foregroundSecondary: "#CC33A3",
foregroundTertiary: "#8F2472",
accent: "#FF77DD",
success: "#FF88DD",
warning: "#FFAA77",
error: "#FF6633",
info: "#FF99EE",
border: "#5A2D50",
statusBarBackground: "#180D19",
appHeaderBackground: "#1C0E1E",
overlayBackground: "#0A060A",
surfaceBackground: "#251428",
surfaceHeaderBackground: "#1C0E1E",
elevatedBackground: "#3A1D3A",
}
},
{
name: "Gold",
desc: "Warm brass instrument panels, steampunk gauges, and luxury cockpit displays.",
colors: {
background: "#0A0906",
foreground: "#DDAA22",
foregroundSecondary: "#BB8E1A",
foregroundTertiary: "#8A6814",
accent: "#FFCC44",
success: "#DDCC44",
warning: "#FFE066",
error: "#FF6633",
info: "#EEBB55",
border: "#5A4D2D",
statusBarBackground: "#191508",
appHeaderBackground: "#1C170A",
overlayBackground: "#0A0906",
surfaceBackground: "#261F0E",
surfaceHeaderBackground: "#1C170A",
elevatedBackground: "#3A3018",
}
},
{
name: "Coral",
desc: "Warm sunset terminals \u2014 inspired by sodium-vapor streetlights and desert radar stations.",
colors: {
background: "#0A0706",
foreground: "#FF7744",
foregroundSecondary: "#CC5E36",
foregroundTertiary: "#8F4226",
accent: "#FF9966",
success: "#FFAA77",
warning: "#FFCC66",
error: "#FF4433",
info: "#FFBB88",
border: "#5A3A2D",
statusBarBackground: "#190F0A",
appHeaderBackground: "#1E100C",
overlayBackground: "#0A0706",
surfaceBackground: "#281710",
surfaceHeaderBackground: "#1E100C",
elevatedBackground: "#3A2418",
}
},
{
name: "Frost",
desc: "Arctic cryogenic readouts \u2014 inspired by ice-blue instrument clusters and polar research stations.",
colors: {
background: "#060809",
foreground: "#88DDFF",
foregroundSecondary: "#66AACC",
foregroundTertiary: "#4A7A99",
accent: "#AAEEFF",
success: "#88EEBB",
warning: "#CCDDAA",
error: "#FF6633",
info: "#BBEEFF",
border: "#2D4A5A",
statusBarBackground: "#0D1720",
appHeaderBackground: "#0A151E",
overlayBackground: "#060809",
surfaceBackground: "#0E1D28",
surfaceHeaderBackground: "#0A151E",
elevatedBackground: "#142D3E",
}
},
];
const existingPalettes = [
{
name: "Green",
desc: "P1 phosphor",
fg: "#33FF33",
colors: ["#060A07",
"#33FF33","#27C227","#1F8F1F",
"#66FF66",
"#33FF33","#CCFF33","#FF6633","#33FFCC",
"#2D5A2D",
"#0F2215","#0A1B13","#060A07"]
},
{
name: "Amber",
desc: "P3 phosphor",
fg: "#FFAA00",
colors: ["#0A0706",
"#FFAA00","#CC8800","#8F6600",
"#FFCC33",
"#FFCC00","#FFE066","#FF6633","#FFD966",
"#5A4A2D",
"#191613","#1E110E","#0A0706"]
},
{
name: "Red",
desc: "Military",
fg: "#FF4444",
colors: ["#0A0606",
"#FF4444","#CC3333","#8F2222",
"#FF6666",
"#FF8080","#FFAA66","#FFFFFF","#FF9999",
"#5A2D2D",
"#191313","#1E0F10","#0A0606"]
},
{
name: "Violet",
desc: "Sci-fi",
fg: "#B275EF",
colors: ["#070509",
"#B275EF","#8C3BDC","#6528A3",
"#C697F6",
"#E4A567","#EF75B2","#A5F159","#7CB2E8",
"#3F2659",
"#140D1B","#110B18","#070509"]
},
{
name: "Blue",
desc: "VFD",
fg: "#00AAFF",
colors: ["#060708",
"#00AAFF","#0088CC","#006699",
"#33BBFF",
"#33CCFF","#66CCFF","#FF6633","#99DDFF",
"#2D4A5A",
"#0F1822","#0A121C","#060708"]
},
{
name: "White",
desc: "P4 phosphor",
fg: "#E8E8E8",
colors: ["#06070A",
"#E8E8E8","#B0B0B0","#787878",
"#FFFFFF",
"#C0FFC0","#FFE0A0","#FFA0A0","#A0D0FF",
"#484848",
"#131619","#0D131D","#06070A"]
},
];
const colorLabels = [
"background",
"foreground","foregroundSecondary","foregroundTertiary",
"accent",
"success","warning","error","info",
"border",
"statusBarBackground","appHeaderBackground","overlayBackground",
"surfaceBackground","surfaceHeaderBackground","elevatedBackground"
];
const swatchGroups = [
{ title: "Palette — Backgrounds", keys: ["background","statusBarBackground","appHeaderBackground","overlayBackground"] },
{ title: "Palette — Foregrounds", keys: ["foreground","foregroundSecondary","foregroundTertiary"] },
{ title: "Palette — Accent", keys: ["accent"] },
{ title: "Palette — Semantic", keys: ["success","warning","error","info"] },
{ title: "Palette — UI Elements", keys: ["border"] },
{ title: "BlockPalette — Surfaces", keys: ["surfaceBackground","surfaceHeaderBackground","elevatedBackground"] },
];
function camelToLabel(key) {
return key.replace(/([A-Z])/g, " $1").toLowerCase();
}
function createSwatch(color, label) {
return `<div class="swatch">
<div class="swatch-color" style="background:${color}"></div>
<div class="swatch-label">${label}</div>
<div class="swatch-hex">${color.toUpperCase()}</div>
</div>`;
}
function renderNewPalettes() {
const container = document.getElementById("new-palettes");
let html = "";
for (const palette of newPalettes) {
let swatchesHtml = "";
for (const group of swatchGroups) {
let row = "";
for (const key of group.keys) {
row += createSwatch(palette.colors[key], camelToLabel(key));
}
swatchesHtml += `<div class="swatch-section">
<div class="swatch-section-title">${group.title}</div>
<div class="swatch-row">${row}</div>
</div>`;
}
html += `<div class="palette-card">
<div class="palette-header">
<div class="palette-name" style="color:${palette.colors.foreground}">${palette.name}</div>
<div class="palette-desc">${palette.desc}</div>
</div>
<div class="palette-body">
${swatchesHtml}
</div>
</div>`;
}
container.innerHTML = html;
}
function renderExistingPalettes() {
const container = document.getElementById("existing-palettes");
let html = "";
for (const palette of existingPalettes) {
let swatches = "";
for (const color of palette.colors) {
swatches += `<div class="existing-swatch" style="background:${color}" title="${color}"></div>`;
}
html += `<div class="existing-card">
<div class="existing-name" style="color:${palette.fg}">${palette.name} <span style="color:#555;font-weight:400;font-size:16px">${palette.desc}</span></div>
<div class="existing-swatches">${swatches}</div>
</div>`;
}
container.innerHTML = html;
}
renderNewPalettes();
renderExistingPalettes();
</script>
</body>
</html>