Template:UI/projects library/styles.css

From Miniscope
Jump to: navigation, search
/* TemplateStyles for {{UI/projects_library}} — curated Projects
   directory. Sections: header (heading + lead + stats) and a
   responsive card grid of project cards. Each card has an image
   area on top (Has card image, or a status-keyed gradient
   fallback for projects without one), a status chip overlaid on
   the image, and a body with title, line-clamped description,
   and a related-content count strip.

   Sanitizer constraints (MW 1.44.5 / css-sanitizer 5.5.0): same as
   the other library stylesheets — var() in `background`, `color`,
   `border` shorthand only; geometry stays literal. `display:
   -webkit-box` is rejected, so the description uses the max-height
   + overflow:hidden approximation rather than -webkit-line-clamp
   (same workaround pattern as guides_library/styles.css). */

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

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

.projects-library-stats {
	margin-bottom: 1.5em;
}

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

   Explicit column counts at each breakpoint rather than
   auto-fit/minmax for the same reason workshops_library does it:
   auto-fit collapses to a single 1fr track when only one project
   exists, which stretches a lone card to the full grid width and
   blows up the image. Explicit columns keep the card sized
   uniformly regardless of result count.

   gap stays literal (16px) per the geometry-stays-literal
   sanitizer constraint. */
.projects-library-grid {
	display: grid;
	grid-template-columns: repeat(4, 1fr);
	gap: 16px;
	margin-top: 1em;
}

@media (max-width: 1200px) {
	.projects-library-grid {
		grid-template-columns: repeat(3, 1fr);
	}
}

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

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

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

/* No explicit background — the card inherits the page background so
   the body section toggles with the wiki's light/dark theme. Same
   pattern as guides_library/publications_library entries: only chips
   and other accent surfaces use the labki-bg-subtle token, while card
   surfaces themselves stay transparent. Border + hover lift still
   define the card visually. */
.projects-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 on the
   base rule accepts var()). */
.projects-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) {
	.projects-library-card {
		transition: border-color 0.15s ease, box-shadow 0.15s ease;
	}
	.projects-library-card:hover {
		transform: none;
	}
}

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

   4:3 aspect ratio via the padding-bottom hack (height: 0 +
   padding-bottom: 75%) — matches the Has_card_image property's
   recommended 4:3 aspect so center-cropping is minimal. MW
   1.44.5 / css-sanitizer 5.5.0 rejects the modern `aspect-ratio`
   property (lands in 5.6+). */
.projects-library-card-image {
	position: relative;
	width: 100%;
	height: 0;
	padding-bottom: 75%;
	overflow: hidden;
}

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

/* Status-keyed gradient fallback — renders when no Has card image
   is set. Same hex palette as the chip variants below, blended
   into a 135deg gradient for visual interest. Literal hex per the
   var()-in-background-shorthand-only sanitizer constraint. */
.projects-library-card-image-fallback-active {
	background: linear-gradient(135deg, #2E7D32 0%, #1B5E20 100%);
}

.projects-library-card-image-fallback-maintained {
	background: linear-gradient(135deg, #1976D2 0%, #0D47A1 100%);
}

.projects-library-card-image-fallback-development {
	background: linear-gradient(135deg, #C77700 0%, #8B5200 100%);
}

.projects-library-card-image-fallback-planned {
	background: linear-gradient(135deg, #2774AE 0%, #003B5C 100%);
}

.projects-library-card-image-fallback-deprecated {
	background: linear-gradient(135deg, #6B7280 0%, #374151 100%);
}

.projects-library-card-image-fallback-cancelled {
	background: linear-gradient(135deg, #B91C1C 0%, #7F1D1D 100%);
}

.projects-library-card-image-fallback-other {
	background: linear-gradient(135deg, #94a3b8 0%, #475569 100%);
}

/* ---------- Status chip ----------

   Positioned absolute to overlay the top-right of the image area.
   Same semantic palette as the previous row-layout chip. */
.projects-library-card-status {
	position: absolute;
	top: 10px;
	right: 10px;
	z-index: 1;
	font-size: 0.7rem;
	font-weight: 600;
	letter-spacing: 0.05em;
	text-transform: uppercase;
	padding: 2px 9px;
	border-radius: 999px;
	white-space: nowrap;
	box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
}

.projects-library-card-status-active {
	background: #2E7D32;
	color: #ffffff;
}

.projects-library-card-status-maintained {
	background: #1976D2;
	color: #ffffff;
}

.projects-library-card-status-development {
	background: #C77700;
	color: #ffffff;
}

.projects-library-card-status-planned {
	background: var(--labki-accent, #2774AE);
	color: #ffffff;
}

.projects-library-card-status-deprecated {
	background: #6B7280;
	color: #ffffff;
}

.projects-library-card-status-cancelled {
	background: #B91C1C;
	color: #ffffff;
}

.projects-library-card-status-other {
	background: var(--labki-bg-subtle, #f4f6f8);
	color: var(--labki-text, #2c2c2c);
	border: 1px solid var(--labki-border, #d8dde2);
}

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

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

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

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

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

/* Description — line-clamped at 3 lines via max-height + overflow.
   The full description renders on the per-project masthead; the
   card preview just needs enough text to disambiguate projects.
   Avoids -webkit-line-clamp because css-sanitizer 5.5.0 rejects
   `display: -webkit-box` (same workaround as guides_library). At
   line-height 1.5 and font-size 0.9rem, 4.5em ≈ 3 lines. */
.projects-library-card-description {
	font-size: 0.9rem;
	line-height: 1.5;
	color: var(--labki-text, #2c2c2c);
	max-height: 4.5em;
	overflow: hidden;
}

/* Count chip strip — N Guides · M FAQs. Pushed to the bottom of
   the body via margin-top: auto so cards with shorter descriptions
   still align their count strips at the same y-offset within the
   grid. */
.projects-library-card-counts {
	display: flex;
	flex-wrap: wrap;
	gap: 0.4em;
	margin-top: auto;
	padding-top: 0.4em;
	font-size: 0.8rem;
}

.projects-library-card-count {
	display: inline-block;
	padding: 2px 9px;
	border-radius: 999px;
	background: var(--labki-bg-subtle, #f4f6f8);
	color: var(--labki-text-muted, #5a6a7a);
	border: 1px solid var(--labki-border, #d8dde2);
	font-weight: 500;
	white-space: nowrap;
}

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

.projects-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;
}