directory card: distribute the content so the ask row lines up across every card

The card reads as three blocks — the head/clients/focus block at the top,
the description centred in the free space, and the charts + ask row + links
block sitting on the card's floor. Two CSS lines and two renderer lines do it:

  .dev-bio   margin-block: auto  — the auto margins absorb the card's free
             space equally above and below the description, so it centres
             between the two blocks and everything after it is pushed down
             onto the floor. No wrapper element, no markup change, and the
             description is never clipped to fit.

  .dev-links min-height: 36px   — the row's line is reserved at the height
             its 36 px hit areas already give it.

  renderer   the .dev-links row and the .dev-bio container are ALWAYS in the
             DOM, even with nothing in them. An absent container is what lets
             the ask row drop a line (0 links) or float up off the floor (no
             description — optional on the application form). Reserved, not
             filled: no empty anchor, no empty icon, no placeholder text, and
             a record's own words are rendered as given.
This commit is contained in:
webmaster committed 2026-11-03 21:34:24 +00:00
1 parent bd0881f882
commit 634f4fcdf1
1 file changed
+29 -5
+29 -5
View File
@@ -518,7 +518,15 @@
color: var(--brass-deep); color: var(--brass-deep);
} }
.dev-bio { .dev-bio {
margin: 0; /* The card reads as three blocks: the head/clients/focus block at the top, the
description centred in the free space, and the charts + ask row + links block
sitting on the card's floor. In a flex column an auto block margin absorbs the
card's free space equally above and below the description, which centres it
between the two blocks AND pushes the charts, the ask row and the links row
down — so the ask button lands at the same height on every card in the row.
No wrapper element, no markup change. The description is never clipped to
achieve this: it renders whatever the form gave it, up to 230 characters. */
margin-block: auto;
font-size: 0.9rem; font-size: 0.9rem;
line-height: 1.65; line-height: 1.65;
color: var(--ink-soft); color: var(--ink-soft);
@@ -562,6 +570,13 @@
align-items: center; align-items: center;
flex-wrap: wrap; flex-wrap: wrap;
gap: 0.3rem; gap: 0.3rem;
/* The row is reserved, not filled: the script always keeps this container in the
DOM, even for a card with no link value at all, and this holds its line at the
height the 36 px hit areas already give it. An empty container is invisible but
occupies the same height as one full of logos, so the ask row above it sits at
the same height on every card — including a card with no logos. No empty anchor,
no empty icon, no placeholder text. */
min-height: 36px;
} }
.dev-links a { .dev-links a {
color: var(--midnight); color: var(--midnight);
@@ -2515,8 +2530,14 @@
var focus = capFocus(rec.Focus); var focus = capFocus(rec.Focus);
if (focus) card.appendChild(liveEl("p", "dev-focus", focus)); if (focus) card.appendChild(liveEl("p", "dev-focus", focus));
var bio = capDescription(rec.description); // The description container is ALWAYS in the DOM, like the links row further down:
if (bio) card.appendChild(liveEl("p", "dev-bio", bio)); // it is the element whose auto block margins centre it and push the bottom block
// onto the card's floor. A description is optional on the application form, and
// with no container the free space would have nothing to sit on — the charts, the
// ask row and the links row would float up off the floor and the ask button would
// miss the line every other card in the row is on. Reserved, not filled: an empty
// container reads as nothing, and a freelancer's own words are never clipped.
card.appendChild(liveEl("p", "dev-bio", capDescription(rec.description)));
var rows = liveAxisRows(rec); var rows = liveAxisRows(rec);
if (rows.length) { if (rows.length) {
@@ -2557,11 +2578,14 @@
// while ODOO_EXPERTS_QUOTE_WEBHOOK is set). The address itself is never rendered — // while ODOO_EXPERTS_QUOTE_WEBHOOK is set). The address itself is never rendered —
// that is the whole point of the button. // that is the whole point of the button.
if (canAsk(rec)) card.setAttribute("data-relay", "1"); if (canAsk(rec)) card.setAttribute("data-relay", "1");
if (links.length) { // The links row is ALWAYS in the DOM — even with nothing in it. An empty container
// costs exactly one reserved line (see .dev-links min-height) and keeps the ask row
// above it at the same height on every card; an ABSENT container would let the ask
// row drop a line on a card with no link value. Reserved, not filled: no empty
// anchor, no empty icon, no placeholder text.
var row = liveEl("p", "dev-links"); var row = liveEl("p", "dev-links");
links.forEach(function (a) { row.appendChild(a); }); links.forEach(function (a) { row.appendChild(a); });
card.appendChild(row); card.appendChild(row);
}
if (!canAsk(rec)) { if (!canAsk(rec)) {
card.appendChild(liveEl("p", "dev-contact-none", card.appendChild(liveEl("p", "dev-contact-none",
"We have no way to pass your question to this freelancer.")); "We have no way to pass your question to this freelancer."));