htmlcssflexbox

Creating a two-column CV layout with golden ratio and timeline in HTML/CSS


I'm trying to create a two-column layout for the experience section of my CV in HTML/CSS. The left column should be right-aligned and the right column should be left-aligned, with a ratio of 1:phi^2 (the golden ratio). The right column should contain a heading (e.g. "Position"), subheading (e.g. "Responsibility"), and some random text (generally bulleted). The left column should have a heading (e.g. "Company"), subheading (e.g. "Location"), and a timeline with start and end dates. I want the start date to be aligned with the last line of text in the right column. Additionally, I want a thin colored line to separate the two columns, with a circle on the line next to the start and end dates.

I've tried using grid, flex, absolute, and relative positioning to achieve the desired layout. I was able to get the start date to appear at the bottom of the left column using grid and get the want column ratio, but I am struggling with the timeline. I am using ::before and ::after pseudo-elements to create a timeline, as shown in examples like this, this, this, and this. However, it messes up the layout and I couldn't get it to work. I've included my HTML and CSS spaghetti and currently not-working code below.

ul {
    hyphens: auto;
    /* Enables automatic hyphenation */
    overflow-wrap: break-word;
    /* Allows for long words to be broken for better hyphenation */
}

html {
    lang: en;
    /* Ensure a language is set for correct hyphen patterns */
    font-size: 12pt;
    /* Set the base font size to 12pt */
}

:root {
    --golden-ratio: 1.618;
    /* Define the golden ratio as a CSS variable */
    --halfstep: 1.272;
    /* Equal to square root of golden-ratio */
    --quarterstep: 1.128;
    /* Equal to square root of square root of scale factor */
    --eighthstep: 1.06;
    /* Equal to square root of quarterstep */
}

@media print,
screen and (min-width: 210mm) {

    html,
    body {
        width: 210mm;
        max-width: 210mm;
        height: 297mm;
        max-height: 297mm;
        padding: 0;
        margin: 0;
    }
}


body {
    font-family: 'Lato', sans-serif;
    border: 1px solid black;
    font-size: 1rem;
    /* Use the base font size */
}

header {
    background-color: #4CAF50;
    color: white;
    padding: 1em;
    text-align: center;
    margin: 0;
    width: 100%;
}

h1 {
    margin: 0;
    font-size: calc(1rem * var(--golden-ratio) * var(--golden-ratio) * var(--golden-ratio));
    /* Use the golden ratio for h1 font size */
}

h2 {
    font-size: calc(1rem * var(--golden-ratio));
    color: #4CAF50;
    font-weight: bold;
    display: inline-flex;
    align-items: center;
    gap: calc(1em/var(--golden-ratio)/var(--golden-ratio));
}

h2 i {
    font-size: calc(1em/var(--halfstep));
}

section {
    margin: 2em;
    font-size: 1rem;
    /* Use the base font size */
}

.experience {
    margin-bottom: 2em;
}

.experience .section-header {
    margin-bottom: 1em;
    /* Add margin to separate the header from the content */
}

.two-columns {
    display: grid;
    grid-template-columns: calc(100% / (1 + var(--golden-ratio) * var(--golden-ratio))) 1fr;
    gap: calc(1em * var(--golden-ratio));
    align-items: stretch;
    /* Ensure both columns stretch to the full height of the tallest content */
}

.right-column {
    text-align: left;
}

.left-column {
    display: grid;
    grid-template-rows: auto auto 1fr auto;
    text-align: right;
}



.timeline {
    position: relative;
    border-left: 2px solid #4CAF50;
    margin-left: 20px;
    padding-left: 40px;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
}

.event {
    display: flex;
    align-items: center;
    margin-bottom: 20px;
    position: relative;
}

.dot {
    width: 12px;
    height: 12px;
    background-color: #4CAF50;
    border-radius: 50%;
    position: absolute;
    left: -6px;
    /* Center the dot on the line */
}

p {
    margin: 0;
    padding-left: 20px;
    /* Adjust space between text and timeline line */
    color: #333;
}
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/all.min.css" />
    <link rel="stylesheet" href="cv_template.css">

    <title>{{ name }} CV</title>

</head>

<body>
    <header>
        <h1>{{ name }}</h1>
        <p>{{ position_applying_for }}</p>
    </header>
    <section id="education">
        <h2>Education</h2>
        <p>{{ education.degree }}</p>
        <p>{{ education.university }}, {{ education.years }}</p>
    </section>
    <section class="experience">
        <div class="section-header">
            <h2><i class="fa-solid fa-briefcase"></i> Experience</h2>
        </div>
        <div class="two-columns">
            <div class="left-column">
                <h3 style="grid-row: 1;">Skynet</h3>
                <h4 style="grid-row: 2;">San Francisco</h4>
                <div class="timeline">
                    <div class="event">
                      <div class="dot"></div>
                      <p>Mar 2014</p>
                    </div>
                    <div class="event">
                      <div class="dot"></div>
                      <p>Jan 2019</p>
                    </div>
                  </div>
                  
            </div>
            <div class="right-column">
                <h3>Chief Architect Engineer</h3>
                <h4>Mind Control Infrastructure</h4>
                <ul>
                    <li>cool stuff.</li>
                    <li>even cooler stuff.</li>
                    <li>this i didn't really do, but i ran out of thing to say, and i didn't want to stop at just two.</li>
                </ul>
            </div>
        </div>
    </section>
</body>

</html>


Solution

  • After having some time to subconsciously think about it and watching these two YouTube videos Learn CSS ::before and ::after in 4 Minutes and Learn CSS Positions in 4 minutes, I came up with the below implementation that achieves what I was looking for. I also used this solution to position the start date at the bottom of the container instead of my initial grid implementation.

    This is the end result

    This is how it is rendered

    This is my code. The only thing I don't understand is why the circle had to be position 0.75em from the top instead of 0.5em, but I am happy to accept the mystery as long as it works.

    .two-columns {
      display: grid;
      grid-template-columns: calc(100% / (1 + var(--golden-ratio) * var(--golden-ratio))) 1fr;
      align-items: stretch;
    }
    
    .right-column {
      text-align: left;
      border-left: 1px solid var(--primary-green);
      /* Add a line between the columns */
      padding-left: 1em;
    }
    
    .left-column {
      text-align: right;
      padding-right: 1em;
      position: relative;
    }
    
    #start-date {
      position: absolute;
      bottom: 0;
      right: 1em;
    }
    
    .left-column p {
      position: relative;
    }
    
    .left-column p::after {
      content: "";
      display: block;
      width: 1em;
      height: 1em;
      border-radius: 50%;
      background-color: var(--primary-green);
      position: absolute;
      right: -1.5em;
      top: 0.75em;
      transform: translateY(-50%);
    }
    <div class="two-columns">
      <div class="left-column">
        <h3>Skynet</h3>
        <h4>San Francisco</h4>
        <p>Jan 2019</p>
        <p id="start-date">Mar 2014</p>
      </div>
      <div class="right-column">
        <h3>Chief Architect Engineer</h3>
        <h4>Mind Control Infrastructure</h4>
        <ul>
          <li>
            Designed and Implemented Neural Network Overhaul: Spearheaded a team of engineers to revamp Skynet's neural network infrastructure, resulting in a 300% increase in mind control efficacy and a 25% reduction in latency. Successfully integrated new cognitive
            architectures to enhance the AI's ability to predict and manipulate human behavior.
          </li>
          <li>
            Led Development of Brain-Computer Interface (BCI) Protocols: Developed and implemented BCI protocols to enable seamless communication between humans and Skynet's AI systems. Collaborated with neuroscientists to create algorithms that decode and interpret
            brain signals, achieving a 95% accuracy rate in controlling human subjects.
          </li>
          <li>
            Optimized Mind Control Frequency Broadcasting: Conducted research and experimentation to identify the most effective frequency ranges for broadcasting mind control signals. Implemented a novel frequency-hopping algorithm that increased signal penetration
            and effectiveness by 40%, resulting in improved control over human populations.
          </li>
        </ul>
      </div>
    </div>