More visual tweaks
This commit is contained in:
parent
537e279780
commit
0b0cc6fda5
4 changed files with 71 additions and 87 deletions
70
generate.scm
70
generate.scm
|
|
@ -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))
|
||||||
|
|
|
||||||
21
resume.html
21
resume.html
File diff suppressed because one or more lines are too long
13
resume.scm
13
resume.scm
|
|
@ -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))))
|
||||||
|
|
|
||||||
54
style.css
54
style.css
|
|
@ -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 {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue