Files
kgva/assets/css/custom.css
T
kgva CMS 37fd39f488 feat: peek column to the left of the file list, mirroring the info text on the right
replaces the last commits attempt (hover updating the main preview directly) with what was actually asked for: the main preview now stays on the locked file, untouched by hover; a separate 18rem-wide column to the left of the file list — same width and spot as .proj-info on the right — shows whichever OTHER image is under the mouse while something is locked, and disappears again once the mouse leaves the list or lands back on the locked row itself.\n\npositioned via plain absolute positioning against .filebrowser (not scroll-tracked — it scrolls together with the list naturally), sitting inside the padding .proj-layout already reserves to keep the file list anchored. only enabled above 1300px, with a safety margin over the ~1168px point where that reserved space stops being wide enough; caught and fixed a bug in the same pass where .proj-peek had no display:none fallback below that breakpoint and would otherwise render as a bare block in-flow.
2026-08-11 19:41:06 +00:00

519 lines
17 KiB
CSS

/* ══════════════════════════════════════════════════════════════
custom.css — extends shibui WITHOUT touching the theme.
- top bar reads like a terminal prompt: "/ karimgabrielevarano_ ls"
- clicking "ls" lists the pages, stacked, inverted on hover
- only the work index breaks the narrow column
remove this file → original shibui look returns.
══════════════════════════════════════════════════════════════ */
/* plain Iosevka instead of the theme's "Iosevka Curly Slab" — same family,
calmer letterforms (straight $, no slab foot on digits). self-hosted,
subset to exactly the characters used on the site (fonttools --unicodes),
so it's smaller than the curly-slab cut it replaces despite covering en+de
plus the shell/tree symbols (→ ← … · ▶ ⛶ ☕ ✕ ❚ box-drawing). overrides the
theme's --font-family-* vars below (custom.css loads after site.css). */
@font-face {
font-family: "Iosevka";
font-style: normal;
font-weight: 400;
font-display: swap;
src: url("/fonts/iosevka-400.woff2") format("woff2");
}
@font-face {
font-family: "Iosevka";
font-style: normal;
font-weight: 700;
font-display: swap;
src: url("/fonts/iosevka-700.woff2") format("woff2");
}
:root {
--font-family-mono: "Iosevka", ui-monospace, monospace;
--font-family-sans: "Iosevka", ui-monospace, monospace;
}
/* self-hosted serif, used only for the project-info side panel */
@font-face {
font-family: "Libre Baskerville";
font-style: normal;
font-weight: 400;
font-display: swap;
src: url("/fonts/libre-baskerville-400.woff2") format("woff2");
}
@font-face {
font-family: "Libre Baskerville";
font-style: italic;
font-weight: 400;
font-display: swap;
src: url("/fonts/libre-baskerville-400italic.woff2") format("woff2");
}
@font-face {
font-family: "Libre Baskerville";
font-style: normal;
font-weight: 700;
font-display: swap;
src: url("/fonts/libre-baskerville-700.woff2") format("woff2");
}
:root { --font-family-serif: "Libre Baskerville", Georgia, serif; }
/* ─────────────────────── SHELL PROMPT ─────────────────────── */
.prompt {
font-family: var(--font-family-mono);
display: flex;
/* stay on ONE line: when the command gets too wide, clip the left (older)
part terminal-style and keep the caret/right end visible (shell.js scrolls
it to the end). previously flex-wrap:wrap dropped to a 2nd line and bugged. */
flex-wrap: nowrap;
align-items: flex-start;
gap: 0;
line-height: 1.5;
overflow: hidden;
white-space: nowrap;
}
.prompt__user { font-weight: 700; }
.prompt__path { color: var(--color-text-muted); }
/* breadcrumb segments look like plain prompt text at rest, fill on hover */
.prompt__seg {
margin-left: 0;
border-bottom: none;
background: none;
color: inherit;
}
.prompt__seg:hover {
background: var(--color-text-primary);
color: var(--color-bg-primary);
border-bottom: none;
}
/* the typeable command — looks like shell text, native caret hidden so our
blinking "_" is the cursor */
.prompt__input {
margin-left: 0.6ch;
min-width: 0;
font-weight: 700;
color: var(--color-text-primary);
white-space: pre;
outline: none;
caret-color: transparent;
}
/* a command "typed" by the file browser on hover/freeze — dimmed so it reads
as a hint, not something you committed. any real keystroke discards it. */
.prompt__input.is-ghost { color: var(--color-text-muted); font-weight: 400; }
/* blinking terminal cursor, sits right after the typed text */
.caret {
font-weight: 700;
color: var(--color-text-primary);
animation: blink 1.1s steps(1) infinite;
}
@keyframes blink { 50% { opacity: 0; } }
/* always-open file menu (the shell's "ls") — only shown on the home page;
on subpages it stays in the DOM (hidden) so typed commands still resolve */
/* stays the file list's normal width even when .topbar is widened (below) to
line up the shell/lang-switch with a wider info panel elsewhere — the home
menu was never meant to stretch, only the prompt row + toggle were */
.menu { margin: var(--spacing-sm) 0 0; max-width: var(--container-width); }
body:not(.home) .menu { display: none; }
/* easter-egg output line — takes no space until it actually has output */
.prompt__out {
font-family: var(--font-family-mono);
color: var(--color-text-muted);
margin: 0;
white-space: pre-wrap;
}
.prompt__out:not(:empty) { margin: 0.35rem 0 0; }
/* ─────────────────────── HOME IMAGE ─────────────────────── */
/* sits low enough that opening the ls overlay never moves it */
.home .hero__img { margin: 2rem 0 0; text-align: left; }
.hero__img img { width: 100%; height: auto; margin: 0; }
/* ─────────────────── WORK INDEX (ls -l style) ─────────────────── */
.tui { font-family: var(--font-family-mono); }
.tui__cmd { margin: 0; color: var(--color-text-muted); }
.tui__sym { color: var(--color-text-muted); }
.tui__total { margin: 0 0 0.6rem; }
.tui__ls { list-style: none; margin: 0; padding: 0; }
.tui__ls li { margin: 0; }
.tui__ls a {
display: grid;
grid-template-columns: 11ch 5ch 1fr;
gap: 0 1ch;
margin: 0;
padding: 0 0.25em;
border-bottom: none;
color: var(--color-text-primary);
white-space: nowrap;
}
.tui__perm,
.tui__date { color: var(--color-text-muted); }
.tui__ls a:hover,
.tui__ls a.is-selected {
background: var(--color-text-primary);
color: var(--color-bg-primary);
}
.tui__ls a:hover .tui__perm,
.tui__ls a:hover .tui__date,
.tui__ls a:hover .tui__name,
.tui__ls a.is-selected .tui__perm,
.tui__ls a.is-selected .tui__date,
.tui__ls a.is-selected .tui__name { color: var(--color-bg-primary); }
/* preview pane (ranger-style) below the listing */
.tui__preview { margin: 1.25rem 0 0; }
.tui__preview img {
width: 100%;
aspect-ratio: 3 / 2;
object-fit: cover;
height: auto;
margin: 0;
display: block;
transition: opacity 0.25s ease;
}
/* portfolio index only (not project pages): the big preview is itself a
second way into the already-previewed project, next to clicking its row
again */
.filebrowser:not(.tui--files) .tui__preview img { cursor: pointer; }
/* project page: image files (no date column), show full image (contain) */
.tui--files .tui__ls a { grid-template-columns: 11ch 1fr; }
/* long listings (>8 entries) scroll instead of pushing the preview down */
.tui--files .tui__ls {
max-height: 12em; /* ~8 rows at line-height 1.5 */
overflow-y: auto;
}
.tui--files .tui__preview img {
aspect-ratio: auto; /* keep each image's own ratio — no letterboxing */
width: auto;
max-width: 100%;
max-height: 80vh;
height: auto;
object-fit: contain;
background: none;
}
/* info.md text preview */
.tui__text { font-size: var(--font-size-base); text-align: left; }
.tui__text p { text-align: left; }
.tui__text > :first-child { margin-top: 0; }
.tui__text > :last-child { margin-bottom: 0; }
/* ─────────────────── PROJECT INFO PANEL ─────────────────── */
/* info.md (the project description / about text) lives outside the
filebrowser now — on wide viewports it sits to the right of the file
list, clearing the "$ ls" / "total N" shell lines up top; on narrow ones
there's no room for two columns, so it just stacks below instead.
the file list itself always stays exactly where the single centered
column normally sits — same position with or without an info panel,
so nothing shifts around depending on whether a page has text. */
.proj-layout { display: block; }
.proj-info {
margin: 1.5rem 0 0;
font-family: var(--font-family-serif);
font-size: 0.85em;
line-height: 1.6;
text-align: justify;
/* justified needs hyphenation, otherwise a narrow column like this one gets
ugly gaps between words; :lang() on <html> (set per-page by Hugo) picks
the right hyphenation dictionary for en vs. de automatically. */
hyphens: auto;
-webkit-hyphens: auto;
text-wrap: pretty;
}
.proj-info > :first-child { margin-top: 0; }
.proj-info > :last-child { margin-bottom: 0; }
@media (min-width: 1100px) {
.proj-layout {
width: 100vw;
margin-left: calc(50% - 50vw);
/* pushes the file list back out to the same spot the narrow centered
column always occupies — this is a fixed position, not a re-centered
pair, so it doesn't move depending on whether .proj-info exists */
padding-left: calc((100vw - var(--container-width)) / 2);
padding-right: 1.5rem;
box-sizing: border-box;
display: flex;
align-items: flex-start;
gap: 1.5rem;
}
.proj-layout .filebrowser { flex: 0 0 var(--container-width); }
.proj-info {
flex: 0 0 18rem;
margin-top: 3.6rem; /* clear the two shell lines above the file list */
}
/* shell line + lang-switch always stretch out to the file-list + info-panel
width, on every page — not just ones that happen to render an info
panel — so the toggle sits in the same spot site-wide */
.topbar {
width: calc(var(--container-width) + 1.5rem + 18rem);
}
}
/* small peek column — mirrors .proj-info's spot and width on the OTHER side
of the file list, showing whichever image is under the mouse while
something is locked. sits inside the space .proj-layout already reserves
to keep the file list anchored (see above), so it needs that reserved
padding to be at least 18rem + the gap wide to have room; 1300px keeps a
safety margin above the ~1168px point where that stops being true. plain
absolute positioning against .filebrowser — no scroll-tracking needed,
it just scrolls together with the file list it's attached to. */
.proj-peek { display: none; }
@media (min-width: 1300px) {
.filebrowser { position: relative; }
.proj-peek {
display: block;
position: absolute;
top: 0;
right: 100%;
margin-right: 1.5rem;
width: 18rem;
opacity: 0;
visibility: hidden;
transition: opacity 0.15s ease;
pointer-events: none;
}
.proj-peek.is-visible { opacity: 1; visibility: visible; }
.proj-peek img {
width: 100%;
height: auto;
max-height: 70vh;
object-fit: contain;
display: block;
margin: 0;
}
}
/* prev/next as shell "cd" commands — same vocabulary as the rest of the site */
.proj-nav {
font-family: var(--font-family-mono);
margin: 1.5rem 0 0;
display: flex;
gap: 1.5ch;
}
.proj-nav a[rel="next"] { margin-left: auto; } /* → always pinned right */
.proj-nav a {
border-bottom: none;
color: var(--color-text-muted);
}
.proj-nav a:hover {
background: var(--color-text-primary);
color: var(--color-bg-primary);
border-bottom: none;
}
/* studies path → clickable taxonomy filters, but indistinguishable from plain
text (no underline, no hover, inherits the surrounding colour) */
.proj-path__seg,
.proj-path__seg:hover {
margin-left: 0;
border-bottom: none;
background: none;
color: inherit;
}
.proj-path__seg--last { font-weight: 700; }
/* secret retro games (doom, simcity, pacman, tetris) — each runs inline as
<name>.sh inside a fake .<name> folder, same file-listing chrome as any
other page. sized to the classic 4:3 DOS/arcade aspect ratio. */
.game-player {
width: 100%;
aspect-ratio: 4 / 3;
background: #000;
}
.game-player iframe {
width: 100%;
height: 100%;
border: 0;
display: block;
}
/* about: terminal-style metadata / contact list */
.meta {
display: grid;
grid-template-columns: max-content 1fr;
column-gap: 1.5ch;
row-gap: 0.15rem;
font-family: var(--font-family-mono);
margin: var(--spacing-base) 0 0;
}
.meta dt { color: var(--color-text-muted); }
.meta dd { margin: 0; }
.meta a { margin-left: 0; }
/* `dark` / `light` theme toggle — overrides the theme's colour variables */
html.dark {
color-scheme: dark;
--color-bg-primary: hsl(0 0% 8%);
--color-bg-secondary: hsl(0 0% 12%);
--color-border: hsl(0 0% 22%);
--color-selection-bg: hsl(0 0% 32%);
--color-text-primary: hsl(0 0% 92%);
--color-text-muted: hsl(0 0% 58%);
--color-text-code: hsl(0 0% 78%);
}
/* `sl` — a steam locomotive that rolls across the screen */
.sl {
position: fixed;
top: 38%;
left: 0;
margin: 0;
z-index: 9998;
white-space: pre;
font-family: var(--font-family-mono);
font-size: 12px;
line-height: 1;
color: var(--color-text-primary);
pointer-events: none;
}
/* boot splash — systemd/Arch-style log, but in the site's own look:
light background, same mono font, constrained to the site column width */
#boot { display: none; }
html.booting #boot {
display: block;
position: fixed;
inset: 0;
z-index: 10000;
overflow-y: auto;
scrollbar-width: none;
padding: 0.8rem 0;
background: var(--color-bg-primary);
cursor: none;
}
html.booting #boot::-webkit-scrollbar { display: none; }
.boot__log {
width: 100%;
max-width: var(--container-width);
margin: 0 auto;
padding: 0 1.5rem;
border: 0;
background: none;
box-shadow: none;
border-radius: 0;
font-family: var(--font-family-mono);
font-size: var(--font-size-small);
line-height: 1.6;
color: var(--color-text-primary);
white-space: pre-wrap;
word-break: break-word;
}
.boot__line { white-space: pre-wrap; }
.boot__ok { font-weight: 700; }
.boot__dim { color: var(--color-text-muted); }
/* text isn't selectable anywhere — except the command line, which you type in */
body {
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
}
.prompt__input {
-webkit-user-select: text;
-moz-user-select: text;
user-select: text;
}
/* dotted content + footer links: unchanged at rest, invert to black on hover.
include the header language switch so en/de behave like every other link. */
main a:hover,
footer a:hover,
.lang-switch:hover {
background: var(--color-text-primary);
color: var(--color-bg-primary);
border-bottom-color: transparent;
}
/* self-hosted video — custom terminal-style player (no native controls, so
only our own cursor shows; same-origin keeps the pixel cursor working) */
.player {
position: relative;
width: 100%;
aspect-ratio: 16 / 9;
background: #000;
font-family: var(--font-family-mono);
overflow: hidden;
}
.player video { width: 100%; height: 100%; display: block; object-fit: contain; background: #000; }
.player__big {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 2.4rem;
color: #fff;
background: none;
border: 0;
}
.player.is-playing .player__big { display: none; }
.player__bar {
position: absolute;
left: 0; right: 0; bottom: 0;
display: flex;
align-items: center;
gap: 1ch;
padding: 0.5rem 1ch;
color: #fff;
font-size: var(--font-size-small);
background: linear-gradient(transparent, rgba(0, 0, 0, 0.55));
opacity: 0;
transition: opacity 0.2s ease;
}
.player:hover .player__bar,
.player:not(.is-playing) .player__bar { opacity: 1; }
.player__bar button { font: inherit; color: #fff; background: none; border: 0; padding: 0 0.4ch; line-height: 1; }
.player__bar button:hover { background: #fff; color: #000; }
.player__scrub { flex: 1; height: 3px; background: rgba(255, 255, 255, 0.3); position: relative; }
.player__fill { position: absolute; left: 0; top: 0; bottom: 0; width: 0; background: #fff; }
.player__time { white-space: nowrap; font-variant-numeric: tabular-nums; color: #fff; }
.player:fullscreen { width: 100vw; height: 100vh; aspect-ratio: auto; }
/* all images always in colour (selection drives what's shown, not hover) */
img { filter: none; }
/* make the [hidden] attribute win over .tui__preview img { display:block } */
[hidden] { display: none !important; }
.tui__status {
font-family: var(--font-family-mono);
font-size: var(--font-size-small);
color: var(--color-text-muted);
margin-top: 0.4rem;
}
/* frozen/locked preview indicator */
.filebrowser.is-frozen .tui__status { color: var(--color-text-primary); }
.filebrowser.is-frozen .tui__status::after { content: " ● locked"; }
/* hide every scrollbar site-wide — content still scrolls (wheel/touch/keys),
just no native chrome. keeps the clean terminal surface everywhere. */
* {
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* old Edge/IE */
}
*::-webkit-scrollbar { /* WebKit/Blink */
display: none;
}
/* ─────────────────── LANGUAGE SWITCH ─────────────────── */
/* top-right toggle on the shell row: shows only the *other* language,
click to switch. you never see both at once. */
.topbar { position: relative; }
.lang-switch {
position: absolute;
top: 0;
right: 0;
margin-left: 0;
font-family: var(--font-family-mono);
color: var(--color-text-muted);
border-bottom: 1px dotted;
z-index: 2;
}
/* hover state comes from the shared "main a:hover, footer a:hover,
.lang-switch:hover" rule above (invert to black bg + white text) — a
second, later rule here used to override just the text color back to
dark, leaving black-on-black on hover. */
/* no static width reservation needed here anymore — syncLangSwitch() (shell.js)
already hides the toggle the moment a long line would reach it */