Files
linkvault/app/templates/base.html
Erik Thiele fdfddf8ccf Restyle link cards as a uniform tile grid
Cards previously had ragged heights because summaries vary in length.
Follow the community-scripts.org card pattern instead: fixed-height
title and summary blocks (clamped to 2 and 3 lines with an ellipsis),
a colored letter avatar derived from the domain, category and
manufacturer as badges in the header, tags in a single clipped row,
and domain plus added-date in a footer.

The avatar color is derived locally from the domain, so no external
favicon requests are made.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-20 15:34:27 +02:00

142 lines
7.0 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block title %}LinkVault{% endblock %}</title>
<script src="https://unpkg.com/htmx.org@2.0.3"></script>
<style>
:root {
--bg: #0f172a; --panel: #1e293b; --panel2: #273449;
--text: #e2e8f0; --muted: #94a3b8; --accent: #6366f1;
--accent-hover: #818cf8; --border: #334155; --danger: #ef4444;
}
* { box-sizing: border-box; }
body {
margin: 0; font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
background: var(--bg); color: var(--text); line-height: 1.5;
}
a { color: var(--accent-hover); text-decoration: none; }
a:hover { text-decoration: underline; }
header.topbar {
display: flex; align-items: center; justify-content: space-between;
padding: 12px 24px; background: var(--panel); border-bottom: 1px solid var(--border);
}
header.topbar .brand { font-weight: 700; font-size: 1.2rem; }
header.topbar .brand span { color: var(--accent-hover); }
.user-info { color: var(--muted); font-size: .9rem; display: flex; gap: 12px; align-items: center; }
button, input[type=submit] {
background: var(--accent); color: white; border: none; border-radius: 8px;
padding: 9px 16px; font-size: .95rem; cursor: pointer; font-weight: 500;
}
button:hover { background: var(--accent-hover); }
button.ghost { background: transparent; border: 1px solid var(--border); color: var(--muted); }
button.ghost:hover { background: var(--panel2); color: var(--text); }
button.danger { background: transparent; color: var(--muted); padding: 4px 8px; font-size: .8rem; }
button.danger:hover { color: var(--danger); background: transparent; }
input[type=text], input[type=email], input[type=password], input[type=url] {
background: var(--panel2); border: 1px solid var(--border); color: var(--text);
border-radius: 8px; padding: 10px 12px; font-size: .95rem; width: 100%;
}
input:focus, select:focus { outline: none; border-color: var(--accent); }
select {
background: var(--panel2); border: 1px solid var(--border); color: var(--text);
border-radius: 8px; padding: 6px 10px; font-size: .85rem; font-family: inherit;
cursor: pointer;
}
.layout { display: flex; gap: 24px; margin: 24px auto; padding: 0 24px; }
.sidebar { width: 240px; flex-shrink: 0; }
.main { flex: 1; min-width: 0; }
.panel { background: var(--panel); border: 1px solid var(--border); border-radius: 12px; padding: 16px; margin-bottom: 16px; }
/* Linkliste: bis zu drei Karten pro Zeile, je nach Fensterbreite.
Karten einer Zeile sind gleich hoch; die Zusammenfassung wird
dafür auf drei Zeilen gekürzt. */
#links { display: grid; grid-template-columns: minmax(0, 1fr); gap: 16px; }
#links > .panel { margin-bottom: 0; }
#links > #empty-state { grid-column: 1 / -1; }
.link-card { display: flex; flex-direction: column; }
.card-head { display: flex; align-items: flex-start; gap: 8px; margin-bottom: 10px; }
.card-avatar {
flex-shrink: 0; width: 34px; height: 34px; border-radius: 9px;
display: inline-flex; align-items: center; justify-content: center;
font-weight: 700; font-size: 1rem;
background: hsl(var(--h) 45% 22%); color: hsl(var(--h) 80% 78%);
border: 1px solid hsl(var(--h) 40% 32%);
}
.card-badges {
display: flex; gap: 4px; flex: 1; min-width: 0;
overflow: hidden; max-height: 22px; align-items: flex-start;
}
.card-actions { display: flex; gap: 4px; flex-shrink: 0; }
.badge {
font-size: .7rem; padding: 2px 8px; border-radius: 99px;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%;
}
.badge-cat { background: var(--accent); color: #fff; }
.badge-man { background: #0e7490; color: #cffafe; }
.badge-tag { background: var(--panel2); color: var(--muted); }
/* Feste Höhen für Titel und Zusammenfassung: so beginnt jeder Block auf
gleicher Höhe und das Kürzen mit "…" greift zuverlässig. */
.card-title {
font-weight: 600; font-size: 1rem; line-height: 1.35; margin-bottom: 6px;
display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
overflow: hidden; height: calc(2 * 1.35 * 1rem); flex: none;
}
.card-summary {
margin: 0 0 12px; font-size: .875rem; color: var(--muted); line-height: 1.5;
display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
overflow: hidden; height: calc(3 * 1.5 * .875rem); flex: none;
}
.card-spacer { flex: 1 1 auto; }
/* Tags: nur eine Zeile, Rest wird abgeschnitten hält die Karten gleich hoch. */
.card-tags {
display: flex; gap: 4px;
overflow: hidden; max-height: 22px; flex-wrap: wrap;
}
.card-foot {
padding-top: 10px; border-top: 1px solid var(--border);
display: flex; justify-content: space-between; gap: 8px;
font-size: .75rem; color: var(--muted);
}
.card-domain { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.card-foot > span:last-child { flex-shrink: 0; }
@media (min-width: 1000px) { #links { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1500px) { #links { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
/* Auf schmalen Geräten die Seitenleiste über den Inhalt stellen. */
@media (max-width: 800px) {
.layout { flex-direction: column; gap: 16px; padding: 0 16px; }
.sidebar { width: auto; }
}
.facet-group h3 { font-size: .8rem; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); margin: 16px 0 8px; }
.facet { display: flex; justify-content: space-between; padding: 5px 8px; border-radius: 6px; font-size: .9rem; }
.facet:hover { background: var(--panel2); text-decoration: none; }
.facet.active { background: var(--accent); color: white; }
.facet .count { color: var(--muted); font-size: .8rem; }
.facet.active .count { color: #e0e7ff; }
.auth-wrap { max-width: 380px; margin: 8vh auto; }
.auth-wrap .panel { padding: 28px; }
.auth-wrap h1 { margin-top: 0; }
.field { margin-bottom: 14px; }
.field label { display: block; font-size: .85rem; color: var(--muted); margin-bottom: 5px; }
.error { background: #7f1d1d; color: #fecaca; padding: 10px 12px; border-radius: 8px; margin-bottom: 14px; font-size: .9rem; }
.muted { color: var(--muted); }
.htmx-indicator { opacity: 0; transition: opacity .2s; }
.htmx-request .htmx-indicator { opacity: 1; }
.htmx-request.htmx-indicator { opacity: 1; }
footer.app-footer {
text-align: center; padding: 16px 24px; margin-top: 24px;
color: var(--muted); font-size: .8rem; border-top: 1px solid var(--border);
}
</style>
</head>
<body>
{% block body %}{% endblock %}
<footer class="app-footer">
&copy; {{ app_author }} &middot; Version {{ app_version }} &middot; {{ app_host }}
</footer>
</body>
</html>