Continuous timeline rail across card gaps; arrowhead cap
The previous border-spacing approach made each card stand on its own — but it also fragmented the rail. Each rail segment ended at the bottom of its row, leaving visible breaks between cards. Restructured each event row to wrap the avatar+content cells in a nested card table sitting in the row's left outer TD. The right TD carries the rail as its border-left. With the outer section table now using border-collapse:collapse, consecutive rows' left-borders touch and merge into one unbroken line that runs through the card gaps. Added a downward triangle TR after the last event of each section as a visual cap on the rail. Pure CSS (border-style trick), coloured to match the rail.
This commit is contained in:
+60
-47
@@ -375,35 +375,36 @@ $section_kicker_style = 'margin:0 0 18px;'
|
|||||||
. 'font-style:italic;font-weight:300;font-size:14px;'
|
. 'font-style:italic;font-weight:300;font-size:14px;'
|
||||||
. 'color:' . $palette['ink3'] . ';';
|
. 'color:' . $palette['ink3'] . ';';
|
||||||
|
|
||||||
// The card surface is built per-row out of avatar+content TDs (the
|
$card_padding_y = 18;
|
||||||
// outer table itself stays transparent). The timeline TD lives on the
|
$card_corner_radius = 8;
|
||||||
// page background and carries a thick rail on its left edge.
|
$row_gap = 12; // vertical gap between consecutive cards
|
||||||
$card_padding_y = 18;
|
|
||||||
$card_corner_radius = 8;
|
|
||||||
|
|
||||||
$avatar_cell_base = 'width:72px;vertical-align:middle;'
|
// Inner card table — full rounded surface containing the avatar +
|
||||||
. 'padding:' . $card_padding_y . 'px 0 ' . $card_padding_y . 'px 18px;'
|
// content cells. Each event row drops one of these into the outer
|
||||||
|
// section table's left column.
|
||||||
|
$card_outer_style = 'width:100%;'
|
||||||
. 'background:' . $palette['surface'] . ';'
|
. 'background:' . $palette['surface'] . ';'
|
||||||
. 'border-left:1px solid ' . $palette['border'] . ';';
|
. 'border:1px solid ' . $palette['border'] . ';'
|
||||||
|
. 'border-radius:' . $card_corner_radius . 'px;'
|
||||||
|
. 'box-shadow:0 1px 3px rgba(0,0,0,0.04);';
|
||||||
|
|
||||||
$content_cell_base = 'vertical-align:middle;'
|
$avatar_inner_td = 'width:72px;vertical-align:middle;'
|
||||||
. 'padding:' . $card_padding_y . 'px 18px ' . $card_padding_y . 'px 16px;'
|
. 'padding:' . $card_padding_y . 'px 0 ' . $card_padding_y . 'px 18px;';
|
||||||
. 'background:' . $palette['surface'] . ';'
|
|
||||||
. 'border-right:1px solid ' . $palette['border'] . ';'
|
$content_inner_td = 'vertical-align:middle;'
|
||||||
|
. 'padding:' . $card_padding_y . 'px 18px;'
|
||||||
. 'font-family:' . $font_stack . ';font-size:15px;line-height:1.4;'
|
. 'font-family:' . $font_stack . ';font-size:15px;line-height:1.4;'
|
||||||
. 'font-weight:300;color:' . $palette['ink'] . ';';
|
. 'font-weight:300;color:' . $palette['ink'] . ';';
|
||||||
|
|
||||||
$timeline_cell_base = 'width:140px;vertical-align:middle;'
|
// Outer row cells: card on the left, a small gutter, then the rail.
|
||||||
. 'padding:' . $card_padding_y . 'px 14px ' . $card_padding_y . 'px 24px;'
|
$outer_card_td = 'vertical-align:middle;padding-bottom:' . $row_gap . 'px;';
|
||||||
|
$outer_gutter_td = 'width:16px;padding-bottom:' . $row_gap . 'px;';
|
||||||
|
$outer_rail_td = 'width:140px;vertical-align:middle;'
|
||||||
|
. 'padding:0 14px ' . $row_gap . 'px 24px;'
|
||||||
. 'border-left:4px solid ' . $palette['rail'] . ';'
|
. 'border-left:4px solid ' . $palette['rail'] . ';'
|
||||||
. 'background:' . $palette['bg'] . ';'
|
|
||||||
. 'font-family:' . $font_stack . ';'
|
. 'font-family:' . $font_stack . ';'
|
||||||
. 'color:' . $palette['ink3'] . ';white-space:nowrap;';
|
. 'color:' . $palette['ink3'] . ';white-space:nowrap;';
|
||||||
|
|
||||||
// A 16px wide gutter cell sits between the card and the timeline so
|
|
||||||
// the rail visibly separates from the card edge.
|
|
||||||
$gutter_cell_style = 'width:16px;background:' . $palette['bg'] . ';';
|
|
||||||
|
|
||||||
// Renders one event row in the consistent 3-column layout shared by
|
// Renders one event row in the consistent 3-column layout shared by
|
||||||
// every section (avatar | content | date on the timeline rail).
|
// every section (avatar | content | date on the timeline rail).
|
||||||
$summary_kicker_style = 'margin:18px 0 8px;'
|
$summary_kicker_style = 'margin:18px 0 8px;'
|
||||||
@@ -431,10 +432,11 @@ $summary_item_style = 'padding:3px 0;';
|
|||||||
//
|
//
|
||||||
// $show_dot suppresses the dot on rows whose upcoming-day matches
|
// $show_dot suppresses the dot on rows whose upcoming-day matches
|
||||||
// the previous row, so each calendar day has exactly one dot.
|
// the previous row, so each calendar day has exactly one dot.
|
||||||
// Every row is now a self-contained mini-card (full border on all
|
// Each event row builds a fully-rounded inner card (a nested table)
|
||||||
// sides, rounded corners). Vertical gaps between rows come from the
|
// and parks it in the outer row's left TD; the rail TD on the right
|
||||||
// outer table's border-spacing — the rail naturally breaks between
|
// carries the continuous timeline rail as a left-border. The outer
|
||||||
// rows along with the cards.
|
// table uses border-collapse:collapse so adjacent rail TDs merge
|
||||||
|
// their left-borders into ONE unbroken line across the row gaps.
|
||||||
//
|
//
|
||||||
// $show_dot suppresses the dot AND the day+month line on rows whose
|
// $show_dot suppresses the dot AND the day+month line on rows whose
|
||||||
// upcoming-day matches the previous (only the year is then shown).
|
// upcoming-day matches the previous (only the year is then shown).
|
||||||
@@ -446,22 +448,20 @@ $event_row = static function (
|
|||||||
use (
|
use (
|
||||||
$record_avatars,
|
$record_avatars,
|
||||||
$date_parts,
|
$date_parts,
|
||||||
$avatar_cell_base,
|
$card_outer_style,
|
||||||
$content_cell_base,
|
$avatar_inner_td,
|
||||||
$timeline_cell_base,
|
$content_inner_td,
|
||||||
$gutter_cell_style,
|
$outer_card_td,
|
||||||
$card_corner_radius,
|
$outer_gutter_td,
|
||||||
|
$outer_rail_td,
|
||||||
$palette,
|
$palette,
|
||||||
): string {
|
): string {
|
||||||
$avatar_extra = 'border-top:1px solid ' . $palette['border'] . ';'
|
$card_html =
|
||||||
. 'border-bottom:1px solid ' . $palette['border'] . ';'
|
'<table role="presentation" cellpadding="0" cellspacing="0" border="0" '
|
||||||
. 'border-top-left-radius:' . $card_corner_radius . 'px;'
|
. 'style="' . $card_outer_style . '"><tr>'
|
||||||
. 'border-bottom-left-radius:' . $card_corner_radius . 'px;';
|
. '<td style="' . $avatar_inner_td . '">' . $record_avatars($fact) . '</td>'
|
||||||
|
. '<td style="' . $content_inner_td . '">' . $body_html . '</td>'
|
||||||
$content_extra = 'border-top:1px solid ' . $palette['border'] . ';'
|
. '</tr></table>';
|
||||||
. 'border-bottom:1px solid ' . $palette['border'] . ';'
|
|
||||||
. 'border-top-right-radius:' . $card_corner_radius . 'px;'
|
|
||||||
. 'border-bottom-right-radius:' . $card_corner_radius . 'px;';
|
|
||||||
|
|
||||||
$parts = $date_parts($fact);
|
$parts = $date_parts($fact);
|
||||||
|
|
||||||
@@ -472,9 +472,6 @@ $event_row = static function (
|
|||||||
. 'box-shadow:0 0 0 4px ' . $palette['bg'] . ';"></span>'
|
. 'box-shadow:0 0 0 4px ' . $palette['bg'] . ';"></span>'
|
||||||
: '<span style="display:inline-block;width:14px;margin-left:-31px;margin-right:14px;vertical-align:middle;"></span>';
|
: '<span style="display:inline-block;width:14px;margin-left:-31px;margin-right:14px;vertical-align:middle;"></span>';
|
||||||
|
|
||||||
// When the dot is suppressed, also drop the day+month (it's
|
|
||||||
// the same as the row above) and show only the year — a touch
|
|
||||||
// larger than the always-visible year so it reads on its own.
|
|
||||||
if ($show_dot) {
|
if ($show_dot) {
|
||||||
$date_html =
|
$date_html =
|
||||||
'<span style="display:inline-block;vertical-align:middle;">'
|
'<span style="display:inline-block;vertical-align:middle;">'
|
||||||
@@ -492,19 +489,32 @@ $event_row = static function (
|
|||||||
}
|
}
|
||||||
|
|
||||||
return '<tr>'
|
return '<tr>'
|
||||||
. '<td style="' . $avatar_cell_base . $avatar_extra . '">' . $record_avatars($fact) . '</td>'
|
. '<td style="' . $outer_card_td . '">' . $card_html . '</td>'
|
||||||
. '<td style="' . $content_cell_base . $content_extra . '">' . $body_html . '</td>'
|
. '<td style="' . $outer_gutter_td . '"></td>'
|
||||||
. '<td style="' . $gutter_cell_style . '"></td>'
|
. '<td style="' . $outer_rail_td . '">' . $dot_html . $date_html . '</td>'
|
||||||
. '<td style="' . $timeline_cell_base . '">' . $dot_html . $date_html . '</td>'
|
|
||||||
. '</tr>';
|
. '</tr>';
|
||||||
};
|
};
|
||||||
|
|
||||||
// Outer section table is transparent. border-spacing puts a 10px
|
// Outer section table uses border-collapse:collapse so the rail
|
||||||
// vertical gap between rows so each card stands on its own.
|
// border-lefts merge into a continuous line across all row gaps.
|
||||||
$card_open = '<table role="presentation" cellpadding="0" cellspacing="0" border="0" '
|
$card_open = '<table role="presentation" cellpadding="0" cellspacing="0" border="0" '
|
||||||
. 'style="width:100%;border-collapse:separate;border-spacing:0 10px;">';
|
. 'style="width:100%;border-collapse:collapse;">';
|
||||||
$card_close = '</table>';
|
$card_close = '</table>';
|
||||||
|
|
||||||
|
// Downward-pointing CSS triangle, coloured to match the rail, used as
|
||||||
|
// the closing arrow that visually caps each section's timeline.
|
||||||
|
$timeline_close_row = '<tr>'
|
||||||
|
. '<td></td>'
|
||||||
|
. '<td></td>'
|
||||||
|
. '<td style="height:18px;padding:0 14px 0 24px;'
|
||||||
|
. 'border-left:4px solid ' . $palette['rail'] . ';'
|
||||||
|
. 'font-size:0;line-height:0;vertical-align:bottom;">'
|
||||||
|
. '<span style="display:inline-block;width:0;height:0;'
|
||||||
|
. 'border-left:7px solid transparent;border-right:7px solid transparent;'
|
||||||
|
. 'border-top:11px solid ' . $palette['rail'] . ';'
|
||||||
|
. 'margin-left:-33px;margin-bottom:-1px;"></span>'
|
||||||
|
. '</td></tr>';
|
||||||
|
|
||||||
?><!doctype html>
|
?><!doctype html>
|
||||||
<html lang="<?= e(I18N::languageTag()) ?>">
|
<html lang="<?= e(I18N::languageTag()) ?>">
|
||||||
<head>
|
<head>
|
||||||
@@ -574,6 +584,7 @@ $card_close = '</table>';
|
|||||||
echo $event_row($fact, $body, $show_dot);
|
echo $event_row($fact, $body, $show_dot);
|
||||||
?>
|
?>
|
||||||
<?php endforeach ?>
|
<?php endforeach ?>
|
||||||
|
<?= $timeline_close_row ?>
|
||||||
<?= $card_close ?>
|
<?= $card_close ?>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
<?php if ($summary !== []) : ?>
|
<?php if ($summary !== []) : ?>
|
||||||
@@ -623,6 +634,7 @@ $card_close = '</table>';
|
|||||||
echo $event_row($fact, $body, $show_dot);
|
echo $event_row($fact, $body, $show_dot);
|
||||||
?>
|
?>
|
||||||
<?php endforeach ?>
|
<?php endforeach ?>
|
||||||
|
<?= $timeline_close_row ?>
|
||||||
<?= $card_close ?>
|
<?= $card_close ?>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
<?php if ($summary !== []) : ?>
|
<?php if ($summary !== []) : ?>
|
||||||
@@ -672,6 +684,7 @@ $card_close = '</table>';
|
|||||||
echo $event_row($fact, $body, $show_dot);
|
echo $event_row($fact, $body, $show_dot);
|
||||||
?>
|
?>
|
||||||
<?php endforeach ?>
|
<?php endforeach ?>
|
||||||
|
<?= $timeline_close_row ?>
|
||||||
<?= $card_close ?>
|
<?= $card_close ?>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
<?php if ($summary !== []) : ?>
|
<?php if ($summary !== []) : ?>
|
||||||
|
|||||||
Reference in New Issue
Block a user