Template:UI/posts library/styles.css

From Miniscope
Jump to: navigation, search
/* TemplateStyles for {{UI/posts_library}} — curated Posts directory.
   Header (heading + lead) and a responsive card grid of post cards.
   Each card has a 4:3 image area on top (Has card image, or a flat
   gradient fallback), then a body with title, a date · author meta
   line, and a line-clamped description teaser.

   Sanitizer constraints (MW 1.44.5 / css-sanitizer 5.5.0): same as the
   other library stylesheets — var() in `background`, `color`, and the
   `border` shorthand only; geometry stays literal. `display:
   -webkit-box` is rejected, so the description teaser uses the
   max-height + overflow:hidden approximation rather than
   -webkit-line-clamp. `aspect-ratio` is rejected too, so the image
   area uses the padding-bottom hack. */

.posts-library {
	margin: 0 0 2em 0;
}

.posts-library-lead {
	font-size: 1.05rem;
	color: var(--labki-text-muted, #5a6a7a);
	margin: 0 0 1em 0;
	max-width: 60em;
}

/* ---------- Card grid ----------

   Explicit column counts at each breakpoint rather than
   auto-fit/minmax: auto-fit collapses to a single 1fr track when only
   one post exists, which stretches a lone card to full width and blows
   up the image. Explicit columns keep cards sized uniformly regardless
   of result count. gap stays literal per the geometry-literal
   constraint. */
.posts-library-grid {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: 16px;
	margin-top: 1em;
}

@media (max-width: 1000px) {
	.posts-library-grid {
		grid-template-columns: repeat(2, 1fr);
	}
}

@media (max-width: 600px) {
	.posts-library-grid {
		grid-template-columns: 1fr;
	}
}

/* ---------- Card ----------

   No explicit background — the card inherits the page background so the
   body toggles with the wiki's light/dark theme (same pattern as
   projects_library). Border + hover lift define the card visually. */
.posts-library-card {
	position: relative;
	display: flex;
	flex-direction: column;
	border: 1px solid var(--labki-border, #d8dde2);
	border-radius: 8px;
	overflow: hidden;
	transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
}

/* Literal #2774AE matches --labki-accent — sanitizer rejects var() for
   border-color longhand (only the `border` shorthand accepts var()). */
.posts-library-card:hover {
	border-color: #2774AE;
	box-shadow: 0 6px 16px -4px rgba(0, 0, 0, 0.10);
	transform: translateY(-2px);
}

@media (prefers-reduced-motion: reduce) {
	.posts-library-card {
		transition: border-color 0.15s ease, box-shadow 0.15s ease;
	}
	.posts-library-card:hover {
		transform: none;
	}
}

/* ---------- Image area ----------

   4:3 frame (padding-bottom: 75%) — matches the Has card image
   recommended source and the post-page hero, so a 4:3 upload shows
   uncropped across every surface. object-fit: cover trims only
   off-ratio uploads. */
.posts-library-card-image {
	position: relative;
	width: 100%;
	height: 0;
	padding-bottom: 75%;
	overflow: hidden;
}

.posts-library-card-image img {
	position: absolute;
	top: 0;
	left: 0;
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/* Flat gradient fallback for posts without a hero image. Literal hex
   per the var()-in-background-shorthand-only constraint; accent-toned
   so imageless cards still feel on-brand. */
.posts-library-card-image-fallback {
	background: linear-gradient(135deg, #2774AE 0%, #003B5C 100%);
}

/* ---------- Card body ---------- */

.posts-library-card-body {
	display: flex;
	flex-direction: column;
	gap: 0.45em;
	padding: 0.85em 1em 1em 1em;
	flex: 1;
}

.posts-library-card-title {
	font-size: 1.05rem;
	font-weight: 600;
	line-height: 1.3;
}

.posts-library-card-title a,
.posts-library-card-title a:visited {
	color: var(--labki-text, #2c2c2c);
	text-decoration: none;
}

.posts-library-card-title a:hover {
	text-decoration: underline;
}

/* Date · author meta line. Mid-dot separator drawn between items via
   :not(:last-child)::after so an empty author leaves no dangling dot. */
.posts-library-card-meta {
	display: flex;
	flex-wrap: wrap;
	gap: 0 8px;
	font-size: 0.82rem;
	color: var(--labki-text-muted, #5a6a7a);
	line-height: 1.4;
}

.posts-library-card-meta > span:not(:last-child)::after {
	content: "·";
	margin-left: 8px;
	color: var(--labki-text-faint, #a0a8b0);
}

/* Description teaser — line-clamped at 3 lines via max-height +
   overflow. Avoids -webkit-line-clamp (sanitizer rejects
   display: -webkit-box). At line-height 1.5 and 0.9rem, 4.5em ≈ 3
   lines. */
.posts-library-card-description {
	font-size: 0.9rem;
	line-height: 1.5;
	color: var(--labki-text, #2c2c2c);
	max-height: 4.5em;
	overflow: hidden;
}

/* ---------- Empty state ---------- */

.posts-library-empty {
	padding: 1.5em;
	color: var(--labki-text-muted, #5a6a7a);
	font-style: italic;
	text-align: center;
	background: var(--labki-bg-subtle, #f4f6f8);
	border-radius: 4px;
	grid-column: 1 / -1;
}