More visual tweaks

This commit is contained in:
Ian Keane 2026-08-26 17:54:57 -04:00
parent 537e279780
commit 0b0cc6fda5
4 changed files with 71 additions and 87 deletions

View file

@ -40,8 +40,8 @@
,(date-range (field tc 'date-start) (field tc 'date-end))) ,(date-range (field tc 'date-start) (field tc 'date-end)))
(div (@ (class "title-meta")) (div (@ (class "title-meta"))
,(field tc 'company) " • " ,(field tc 'location)) ,(field tc 'company) " • " ,(field tc 'location))
(div (@ (class "title-blurb")) (p (@ (class "title-blurb"))
,(bullets (field tc 'blurb))))) ,(string-join (field tc 'blurb) " "))))
;; Emit a title card followed by its project cards as a flat list of siblings ;; Emit a title card followed by its project cards as a flat list of siblings
(define (render-title-flat tc) (define (render-title-flat tc)
@ -130,6 +130,43 @@
(list `(h2 (@ (class "sec-heading")) ,cur-label)) (list `(h2 (@ (class "sec-heading")) ,cur-label))
cur-items))))))) cur-items)))))))
;; ─── Column distribution ───────────────────────────────────────────────────────
;; Wrap all items in two independent divs, splitting at the midpoint.
;; Because each col is a separate block formatting context, expanding a card
;; pushes siblings down within that col only — no cross-column movement,
;; no hover loop.
;;
;; num-columns: change to 3 if your resume gets too long, and update the
;; CSS grid-template-columns to match.
(define num-columns 3)
(define (heading? item)
(and (pair? item) (eq? (car item) 'h2)))
(define (list-ref* lst i)
(if (= i 0) (car lst) (list-ref* (cdr lst) (- i 1))))
(define (split-into-columns items n)
(let* ((total (length items))
(chunk (inexact->exact (ceiling (/ total n)))))
(let loop ((i 0) (start 0) (remaining n) (acc '()))
(if (= remaining 1)
(reverse (cons `(div (@ (class "col")) ,@(list-tail items start)) acc))
(let* ((raw-end (min (+ start chunk) total))
;; Walk backwards from the cut point: if the last item
;; in this slice is a heading (orphaned at col bottom),
;; move it to the next column.
(end (let nudge ((e raw-end))
(if (and (> e (+ start 1))
(heading? (list-ref* items (- e 1))))
(nudge (- e 1))
e))))
(loop (+ i 1) end (- remaining 1)
(cons `(div (@ (class "col"))
,@(list-tail (list-head items end) start))
acc)))))))
;; ─── Front matter ───────────────────────────────────────────────────────────── ;; ─── Front matter ─────────────────────────────────────────────────────────────
(define (render-front-matter fm) (define (render-front-matter fm)
@ -143,40 +180,19 @@
(define (render-resume res) (define (render-resume res)
(let* ((fm (field res 'front-matter)) (let* ((fm (field res 'front-matter))
(items (sections->flat-items (field res 'sections)))) (items (sections->flat-items (field res 'sections)))
(cols (split-into-columns items num-columns)))
`(*TOP* `(*TOP*
(html (@ (lang "en")) (html (@ (lang "en"))
(head (head
(meta (@ (charset "UTF-8"))) (meta (@ (charset "UTF-8")))
(meta (@ (name "viewport") (content "width=device-width, initial-scale=1"))) (meta (@ (name "viewport") (content "width=device-width, initial-scale=1")))
(title ,(field fm 'name)) (title ,(field fm 'name))
(link (@ (rel "stylesheet") (href "style.css"))) (link (@ (rel "stylesheet") (href "style.css"))))
;; Measure the front-matter height after every resize/zoom and
;; set .columns height to exactly fill the remaining space.
(script "
function setColHeight() {
var fm = document.querySelector('.front-matter');
var col = document.querySelector('.columns');
if (!fm || !col) return;
var used = fm.getBoundingClientRect().height
+ parseInt(getComputedStyle(document.body).paddingTop) * 2;
col.style.height = (window.innerHeight - used) + 'px';
}
window.addEventListener('load', setColHeight);
window.addEventListener('resize', setColHeight);
// zoom via ctrl+/- fires resize in most browsers, but also poll as fallback
var _lastW = window.innerWidth, _lastH = window.innerHeight;
setInterval(function() {
if (window.innerWidth !== _lastW || window.innerHeight !== _lastH) {
_lastW = window.innerWidth; _lastH = window.innerHeight;
setColHeight();
}
}, 200);
"))
(body (body
,(render-front-matter fm) ,(render-front-matter fm)
(main (@ (class "columns")) (main (@ (class "columns"))
,@items)))))) ,@cols))))))
(display "<!DOCTYPE html>\n") (display "<!DOCTYPE html>\n")
(sxml->xml (render-resume resume)) (sxml->xml (render-resume resume))

File diff suppressed because one or more lines are too long

View file

@ -91,9 +91,7 @@
(list (list
(make-title (make-title
"Senior Software Engineer" "Acme Corp" "Remote" "Jun 2022" "Present" "Senior Software Engineer" "Acme Corp" "Remote" "Jun 2022" "Present"
'("Led a team of 4 engineers on the core platform." '("Led a team of 4 engineers on the core platform, driving adoption of internal developer tooling and mentoring junior engineers through architecture proposals and code review.")
"Drove adoption of internal developer tooling."
"Reviewed architecture proposals and mentored junior engineers.")
(list (list
(make-project (make-project
"Distributed Config Service" "Mar 2023" "Present" "Distributed Config Service" "Mar 2023" "Present"
@ -111,8 +109,7 @@
'("Go" "Bleve" "React" "OpenAPI")))) '("Go" "Bleve" "React" "OpenAPI"))))
(make-title (make-title
"Software Engineer II" "Acme Corp" "Remote" "Jan 2020" "May 2022" "Software Engineer II" "Acme Corp" "Remote" "Jan 2020" "May 2022"
'("Owned the authentication service end-to-end." '("Owned the authentication service end-to-end, reducing API latency by 35% through targeted caching improvements.")
"Reduced API latency by 35% through caching improvements.")
(list (list
(make-project (make-project
"Auth Service Rewrite" "Jan 2020" "Dec 2020" "Auth Service Rewrite" "Jan 2020" "Dec 2020"
@ -135,9 +132,7 @@
(list (list
(make-title (make-title
"Full-Stack Engineer" "Startup XYZ" "Athens, GA" "Jul 2017" "Dec 2019" "Full-Stack Engineer" "Startup XYZ" "Athens, GA" "Jul 2017" "Dec 2019"
'("Built product features across the React frontend and Node.js backend." '("Built product features across the React frontend and Node.js backend, maintained CI/CD pipelines and AWS infrastructure, and collaborated directly with design and product teams.")
"Maintained CI/CD pipelines and AWS infrastructure."
"Collaborated directly with design and product teams.")
(list (list
(make-project (make-project
"Real-Time Dashboard" "Feb 2019" "Dec 2019" "Real-Time Dashboard" "Feb 2019" "Dec 2019"
@ -197,8 +192,8 @@
(list 'resume (list 'resume
(cons 'front-matter my-front-matter) (cons 'front-matter my-front-matter)
(cons 'sections (list my-education (cons 'sections (list my-education
job-acme
job-startup job-startup
job-acme
my-hobby-projects my-hobby-projects
my-interests my-interests
my-favorite-software)))) my-favorite-software))))

View file

@ -65,31 +65,26 @@ a { color: var(--accent); text-decoration: none; }
} }
/* ─── Column layout ──────────────────────────────────────────────────────────── */ /* ─── Column layout ──────────────────────────────────────────────────────────── */
/* Two independent .col divs. Expanding a card pushes siblings within
its own col only cards never shift between columns, no hover loop.
To go to 3 cols: set num-columns=3 in generate.scm, regenerate,
and change repeat(2,...) to repeat(3,...) below. */
.columns { .columns {
column-width: 22em; display: grid;
column-gap: 2em; grid-template-columns: repeat(3, 1fr);
column-fill: balance; gap: 0 2em;
align-items: start;
} }
/* Prevent any card from being split across a column boundary */ .col { min-width: 0; }
.sec-heading,
.title-card,
.project-card,
.education-card,
.interests,
.favorite-software {
break-inside: avoid;
}
/* Keep each section heading attached to whatever follows it */ @media (max-width: 600px) {
.sec-heading { .columns { grid-template-columns: 1fr; }
break-after: avoid;
} }
/* ─── Section headings ───────────────────────────────────────────────────────── */ /* ─── Section headings ───────────────────────────────────────────────────────── */
.sec-heading { .sec-heading {
font-size: 0.7rem; font-size: 0.95rem;
font-weight: 800;
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.12em; letter-spacing: 0.12em;
color: var(--accent); color: var(--accent);
@ -114,8 +109,7 @@ a { color: var(--accent); text-decoration: none; }
.project-name, .project-name,
.edu-degree { .edu-degree {
font-weight: 700; font-weight: 700;
font-size: 0.88rem; font-size: 0.78rem;}
}
.date-range { .date-range {
font-size: 0.72rem; font-size: 0.72rem;
@ -138,14 +132,12 @@ a { color: var(--accent); text-decoration: none; }
margin-bottom: 4px; margin-bottom: 4px;
} }
.title-blurb ul { padding-left: 12px; } p.title-blurb {
.title-blurb li { font-size: 0.68rem;
font-size: 0.82rem;
line-height: 1.5; line-height: 1.5;
margin-bottom: 1px;
color: rgba(255,255,255,0.85); color: rgba(255,255,255,0.85);
margin-top: 3px;
} }
.title-blurb li::before { content: "- "; color: var(--muted); }
/* ─── Project card ───────────────────────────────────────────────────────────── */ /* ─── Project card ───────────────────────────────────────────────────────────── */
.project-card { .project-card {
@ -173,15 +165,15 @@ a { color: var(--accent); text-decoration: none; }
opacity: 0.7; opacity: 0.7;
} }
.project-name { font-size: 0.83rem; } .project-name { font-size: 0.72rem; }
.project-summary ul { padding-left: 12px; margin-top: 2px; } .project-summary ul { padding-left: 12px; margin-top: 2px; }
.project-summary li { .project-summary li {
font-size: 0.8rem; font-size: 0.68rem;
line-height: 1.5; line-height: 1.5;
color: rgba(255,255,255,0.85); color: rgba(255,255,255,0.85);
} }
.project-summary li::before { content: "- "; color: var(--muted); } .project-summary li::before { content: " "; color: var(--muted); }
.project-details { .project-details {
max-height: 0; max-height: 0;
@ -196,12 +188,12 @@ a { color: var(--accent); text-decoration: none; }
} }
.project-details ul { padding-left: 12px; margin-top: 4px; } .project-details ul { padding-left: 12px; margin-top: 4px; }
.project-details li { .project-details li {
font-size: 0.78rem; font-size: 0.68rem;
color: var(--muted); color: var(--muted);
line-height: 1.5; line-height: 1.5;
margin-bottom: 1px; margin-bottom: 1px;
} }
.project-details li::before { content: "- "; } .project-details li::before { content: " "; }
.tech-list { .tech-list {
display: flex; display: flex;
@ -233,7 +225,7 @@ a { color: var(--accent); text-decoration: none; }
margin-bottom: 1px; margin-bottom: 1px;
color: rgba(255,255,255,0.85); color: rgba(255,255,255,0.85);
} }
.edu-bullets li::before { content: "- "; color: var(--muted); } .edu-bullets li::before { content: " "; color: var(--muted); }
.edu-extras { .edu-extras {
max-height: 0; max-height: 0;
@ -254,7 +246,7 @@ a { color: var(--accent); text-decoration: none; }
line-height: 1.5; line-height: 1.5;
margin-bottom: 1px; margin-bottom: 1px;
} }
.edu-extras li::before { content: "- "; } .edu-extras li::before { content: " "; }
/* ─── Interests — 2-column micro-card grid ───────────────────────────────────── */ /* ─── Interests — 2-column micro-card grid ───────────────────────────────────── */
.interests { .interests {