Files
xamxam/app/public/assets/css/tfe.css
T
Pontoporeia 8accb88452 update: CSS per-page split and lightningcss unusedSymbols report
docs: record CSS template inventory + decide unusedSymbols-before-split ordering

feat(css): add content-source collector + dynamic-class safelist for unusedSymbols report

- scripts/css-content-sources.mjs: buildCorpus() gathers templates/public/src
  PHP + first-party js/app JS (vendor excluded), returns {corpus, sources,
  totalBytes, safelist, prefixes}
- Mined 22 exact runtime classes + 5 DB/state-derived suffix prefixes from
  status-badge.php, SystemController statusClass/logLineClass, and class=<?=?>
  ternaries
- docs/css-split-analysis.md notes content-corpus section
- td: #11 collect-content-sources done; feeds #12 report script

feat(css): unusedSymbols report script + just css-report recipe

- scripts/css-unused-report.mjs: per-bundle class/id extraction vs buildCorpus()
  corpus + safelists; measures reclaimable bytes via lightningcss transform
  unusedSymbols (report-only, no stripping to disk)
- just css-report: rebuild CSS then run the report
- css-content-sources.mjs: add VENDOR_CLASS_PREFIXES (filepond--*, htmx-*)
- RESULT: 216,383B total, ~6.2KB (2.9%) reclaimable; FilePond/HTMX exclusion
  corrected inflated 26% (56KB) false-positive down to honest 2.9%
- docs/css-split-analysis.md findings table + TODO 12/13 done

docs(css): record go/no-go decision — split NO-GO, pruning conditional-go

- Decision analysis in docs/css-split-analysis.md
- ~6.2KB (2.9%) reclaimable of 216KB; base.min.css only 484B (2.3%)
- SPLIT NO-GO: base.css already well-used; parked u/w/x/y(/z) as deferred
- PRUNING conditional-go on hand-verifiable dead selers from source, never dist;
  re-run just css-report after each edit; keep needs-review + vendor-prefix cls
- td: task 14 done; split stream 41 tasks -> 2 pending / 32 done / 7 deferred

todo: defer CSS pruning stream (10/u/w/x/y/z), context updated
2026-09-18 16:26:36 +02:00

629 lines
13 KiB
CSS

/* ============================================================
TFE INDIVIDUAL PAGE (tfe.php)
Root class: .tfe-main
============================================================ */
.tfe-main {
display: flex;
flex: 1;
min-height: 0;
overflow: hidden;
/* Left padding keeps content aligned with the rest of the page;
no right padding so the right column (and its scrollbar) sits
flush against the viewport edge. */
padding-left: var(--space-m);
}
/* Stacked article layout — fills main height */
.tfe-layout {
display: flex;
flex-direction: column;
gap: var(--space-xl);
width: 100%;
height: 100%;
min-height: 0;
}
/* Row 1: Author above Title (kept for backward compat, no longer used) */
.tfe-header-row {
display: flex;
flex-direction: column;
gap: var(--space-3xs);
}
/* Two-column layout: left 40vw (info), right fills remaining (files) */
.tfe-content-row {
display: grid;
grid-template-columns: 49vw 1fr;
width: 100%;
height: 100%;
min-height: 0;
}
.tfe-left-column {
display: grid;
overflow-y: auto;
min-width: 0;
padding: var(--space-xl) var(--space-m) var(--space-xl) var(--space-m);
padding-right: 0.5ch;
gap: 1ch;
}
.tfe-right-column {
display: flex;
flex-direction: column;
overflow-y: auto;
min-width: 0;
padding: var(--space-xl) var(--space-m) var(--space-xl) var(--space-m);
}
/* Left column: stacks author, title, meta, synopsis — scrolls independently */
.tfe-left-column {
gap: var(--space-m);
/* space between content and the column scrollbar */
padding-right: var(--space-xs);
}
/* Right column: static (no padding), scrollbar pinned to the far right edge */
.tfe-right-column {
/* no horizontal padding — the inner .tfe-files carries the gutter */
}
/* Files container inside right column: padded on the right so content
sits clear of the column's right-edge scrollbar */
.tfe-files {
display: grid;
gap: var(--space-m);
}
.tfe-files:has(.pdf-expanded) {
height: 100%;
display: flex;
flex-direction: column;
gap: var(--space-m);
flex: 1;
min-height: 0;
padding-right: var(--space-xs);
}
.tfe-file-item {
width: 100%;
min-width: 0;
border-radius: var(--radius);
}
.tfe-file-item:last-of-type {
padding-bottom: 4ch;
}
.tfe-file-item:has(.pdf-embed-wrapper) {
overflow: visible;
}
.tfe-file-item:has(.pdf-expanded) {
min-height: 0;
overflow: visible;
}
.tfe-file-item:has(.tfe-website-wrapper) {
min-height: 0;
overflow: visible;
}
/* When a PDF is expanded, freeze the right column and hide
sibling file items so they can't render above the expanded view.
Also trim column padding so the PDF fills more vertical space. */
.tfe-right-column:has(.pdf-expanded) {
overflow: hidden;
padding-top: var(--space-xs);
padding-bottom: var(--space-xs);
}
.tfe-right-column:has(.pdf-expanded) .tfe-file-item:not(:has(.pdf-expanded)) {
display: none;
}
.tfe-file-item img {
width: 100%;
height: auto;
display: block;
border-radius: var(--radius);
user-select: none;
-webkit-user-drag: none;
}
.tfe-file-item embed,
.tfe-file-item video {
width: 100%;
max-width: 100%;
display: block;
border: none;
}
.tfe-file-item video {
max-height: 500px;
}
.tfe-file-item embed,
.tfe-file-item iframe {
width: 100%;
max-width: 100%;
height: 100%;
min-height: 400px;
display: block;
border: none;
}
/* ── Website embed wrapper (like PDF overlay) ── */
.tfe-website-wrapper {
position: relative;
width: 100%;
display: grid;
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
overflow: hidden;
}
.tfe-website-badge {
position: absolute;
top: var(--space-xs);
left: var(--space-xs);
z-index: 2;
display: inline-block;
padding: 0.15em 0.6em;
font-size: var(--step--2);
font-weight: 500;
line-height: 1.4;
color: color-mix(in srgb, var(--accent-primary) 90%, #000);
background: color-mix(in srgb, var(--accent-primary) 12%, transparent);
border: 1px solid color-mix(in srgb, var(--accent-primary) 25%, transparent);
border-radius: var(--radius);
}
.tfe-website-open-btn {
position: absolute;
bottom: var(--space-xs);
right: var(--space-xs);
z-index: 2;
display: grid;
place-items: center;
width: 40px;
height: 40px;
background: var(--accent-primary);
color: #fff;
border: none;
border-radius: var(--radius);
text-decoration: none;
cursor: pointer;
box-shadow: 0 2px 8px rgb(0 0 0 / 0.25);
transition: transform 0.15s, box-shadow 0.15s;
}
.tfe-website-open-btn:hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgb(0 0 0 / 0.35);
background: var(--accent-primary);
color: #fff;
}
.tfe-file-item figcaption {
font-size: var(--step--2);
color: var(--text-secondary);
margin: var(--space-3xs) 0 0;
font-style: italic;
}
/* Contextual note above synopsis */
.tfe-context-note {
font-style: italic;
font-size: var(--step--1);
line-height: 1.6;
color: var(--text-secondary);
margin: 0;
padding: var(--space-xs) var(--space-s);
background: color-mix(in srgb, var(--accent-primary) 10%, var(--bg-primary));
border-radius: var(--radius);
}
/* Author (p) — inline with title */
.tfe-author {
font-size: var(--step-1);
font-weight: 400;
color: var(--text-primary);
margin: 0;
line-height: 1.3;
word-break: normal;
overflow-wrap: break-word;
hyphens: auto;
}
/* Title (h1) — inherits global scale from base.css */
.tfe-title {
margin: 0;
font-size: var(--step-2);
word-break: normal;
overflow-wrap: break-word;
hyphens: auto;
}
/* Metadata block */
.tfe-meta {
display: grid;
gap: var(--space-3xs);
font-size: var(--step--1);
line-height: 1.4;
margin: 0;
}
/* Each metadata line */
.tfe-meta-item {
margin: 0;
font-weight: 700;
word-break: normal;
overflow-wrap: break-word;
hyphens: auto;
}
.tfe-meta-label {
font-weight: 300;
}
.tfe-meta-item a {
color: inherit;
font-weight: 700;
text-decoration: none;
}
.tfe-meta-item a:hover {
color: var(--accent-primary);
}
/* Note field: align label to top, value in italics */
.tfe-meta-item.tfe-meta-note {
align-items: start;
}
.tfe-note-value {
font-style: italic;
}
/* Synopsis paragraph */
.tfe-synopsis-text {
font-size: var(--step--1);
line-height: 1.7;
color: var(--text-primary);
margin: 0;
}
/* Audio player */
.tfe-audio {
width: 100%;
margin: 0;
display: block;
}
/* "Not available" and "no files" notices */
.tfe-restricted,
.tfe-no-files {
font-size: var(--step--1);
color: var(--text-secondary);
font-style: italic;
padding: var(--space-s) 0;
margin: 0;
}
/* ============================================================
RESTRICTED ACCESS UI
============================================================ */
.tfe-restricted-access {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: var(--space-m);
display: grid;
gap: var(--space-m);
}
.tfe-restricted-message {
font-size: var(--step--1);
color: var(--text-secondary);
line-height: 1.6;
margin: 0;
}
.tfe-restricted-message strong {
color: var(--text-primary);
display: block;
margin-bottom: var(--space-3xs);
font-size: var(--step-0);
}
.tfe-access-request-form {
display: grid;
gap: var(--space-s);
}
.tfe-access-request-form .form-group {
display: grid;
gap: var(--space-3xs);
}
.tfe-access-request-form label {
font-size: var(--step--1);
font-weight: 400;
color: var(--text-primary);
}
.tfe-access-request-form input[type="email"],
.tfe-access-request-form textarea {
padding: var(--space-2xs) var(--space-3xs);
}
.tfe-btn-request-access {
margin-top: var(--space-3xs);
}
.tfe-btn-request-access:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.tfe-access-message {
font-size: var(--step--1);
padding: var(--space-2xs);
border-radius: var(--radius);
margin-top: var(--space-3xs);
}
.tfe-access-success {
background: #f0fff4;
border: 1px solid #48bb78;
color: #22543d;
}
.tfe-access-error {
background: #fff5f5;
border: 1px solid #fc8181;
color: #742a2a;
}
.tfe-access-request-form input.input-error {
border-color: #fc8181;
outline-color: #fc8181;
}
/* ============================================================
PDF.js IN-PAGE VIEWER (pdf-viewer.js)
============================================================ */
/*── Base wrapper ───────────────────────────── */
.pdf-embed-wrapper {
position: relative;
width: 100%;
height: auto;
display: grid;
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
transition: height 0.35s ease, border-color 0.35s ease;
outline: none;
}
/* ── Collapsed state: thumbnail tile ────────── */
.pdf-collapsed {
cursor: pointer;
overflow: hidden;
display: grid;
place-items: center;
min-height: 200px;
max-height: 350px;
}
.pdf-thumbnail {
display: block;
max-width: 100%;
max-height: 100%;
object-fit: contain;
}
/* ── Overlay veil + enter button ────────────── */
.pdf-overlay {
position: absolute;
inset: 0;
z-index: 1;
display: grid;
place-items: center;
background: rgb(0 0 0 / 0.15);
backdrop-filter: blur(4px) saturate(0.6);
transition: background 0.2s;
}
.pdf-overlay:hover {
background: rgb(0 0 0 / 0.08);
}
/* Document type pill — top-left of the overlay */
.pdf-overlay-badge {
position: absolute;
top: var(--space-xs);
left: var(--space-xs);
z-index: 2;
display: inline-block;
padding: 0.15em 0.6em;
font-size: var(--step--2);
font-weight: 500;
line-height: 1.4;
color: color-mix(in srgb, var(--accent-primary) 90%, #000);
background: color-mix(in srgb, var(--accent-primary) 12%, transparent);
border: 1px solid color-mix(in srgb, var(--accent-primary) 25%, transparent);
border-radius: var(--radius);
}
.pdf-enter-btn {
display: inline-grid;
grid-auto-flow: column;
align-items: center;
gap: var(--space-2xs);
padding: var(--space-xs) var(--space-m);
background: var(--accent-primary);
color: #fff;
border: none;
border-radius: var(--radius);
font-size: var(--step-0);
font-weight: 500;
cursor: pointer;
box-shadow: 0 2px 8px rgb(0 0 0 / 0.2);
transition: transform 0.15s, box-shadow 0.15s;
}
.pdf-enter-btn:hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgb(0 0 0 / 0.25);
}
/* ── Expanded state: wrapper becomes scroll panel ── */
/* Disable root-level overscroll (rubberbanding) while a PDF is open. */
body.pdf-viewer-open {
overscroll-behavior: none;
}
.pdf-expanded {
height: 100%;
min-height: 400px;
overflow-y: auto;
overflow-x: hidden;
overscroll-behavior-y: contain;
border: 2px solid var(--accent-primary);
display: grid;
justify-items: center;
background: color-mix(in srgb, var(--accent-secondary) 22%, transparent);
}
/* ── Toolbar ────────────────────────────────── */
.pdf-toolbar {
position: sticky;
justify-self: stretch;
top: 0;
z-index: 2;
display: flex;
align-items: center;
gap: var(--space-3xs);
padding: var(--space-2xs) var(--space-xs);
background: var(--bg-primary);
border-bottom: 1px solid var(--border);
border-radius: var(--radius);
flex-shrink: 0;
font-size: var(--step--1);
user-select: none;
overscroll-behavior: none;
}
.pdf-toolbar-btn {
display: inline-grid;
place-items: center;
width: 2em;
height: 2em;
padding: 0;
border: 1px solid var(--border);
border-radius: var(--radius);
background: var(--surface);
color: var(--text-primary);
cursor: pointer;
font-size: inherit;
line-height: 1;
transition: background 0.15s;
}
.pdf-toolbar-btn:hover {
background: color-mix(in srgb, var(--accent-primary) 15%, var(--surface));
}
.pdf-toolbar-close {
margin-right: var(--space-xs);
}
.pdf-toolbar-label {
min-width: 4.5em;
text-align: center;
color: var(--text-secondary);
font-variant-numeric: tabular-nums;
}
.pdf-toolbar-scale {
min-width: 3em;
}
/* ── Canvas ─────────────────────────────────── */
.pdf-canvas {
display: block;
width: 100%;
height: auto;
box-shadow: 0 1px 4px rgb(0 0 0 / 0.12);
background: #fff;
flex-shrink: 0;
}
.pdf-canvas:first-of-type {
margin-top: var(--space-xs);
}
.pdf-canvas:last-of-type {
margin-bottom: var(--space-xs);
}
.pdf-canvas + .pdf-canvas {
margin-top: var(--space-2xs);
}
/* ── Responsive ─────────────────────────────── */
@media (max-width: 900px) {
.tfe-main {
overflow-y: auto;
}
.tfe-content-row {
grid-template-columns: 1fr;
height: auto;
}
.tfe-left-column,
.tfe-right-column {
overflow-y: visible;
padding: var(--space-3xs);
}
.tfe-title {
font-size: var(--step-2);
}
/* PDF expands to full viewport on mobile */
.pdf-expanded {
position: fixed;
inset: 0;
z-index: 100;
height: auto;
min-height: unset;
background: color-mix(in srgb, var(--accent-secondary) 92%, #000 8%);
border: none;
border-radius: 0;
}
.pdf-toolbar {
border-radius: 0 0 var(--radius) var(--radius);
}
.tfe-file-iframe {
height: 100dvh;
}
}
@media (max-width: 600px) {
.tfe-main {
padding: var(--space-m) var(--space-s) var(--space-l);
}
.tfe-title {
font-size: var(--step-1);
}
}