/* Adapted from https://codyhouse.co/gem/vertical-timeline/ with significant * modifications. * */ :root { --side-spacing: 0; --background-color: rgb(232, 239, 245); --accent: hsl(205, 26%, 84%); --timeline-line-color: hsl(205, 38%, 89%); --timeline-line-width: 4px; --timeline-line-half-width: calc(var(--timeline-line-width) / 2); --content-background-color: white; --content-padding: 1.25rem; --icon-width: 60px; --icon-half-width: calc(var(--icon-width) / 2); --icon-border-width: 4px; --icon-border-color: white; --icon-color: white; --icon-image-width: 40px; --icon-size: 1.5rem; --icon-side-spacing: 1.5rem; --date-color: hsl(0, 0%, 48%); --arrow-size: 7px; } [data-theme="dark"] { --background-color: #2C2F33; --content-background-color: #23272A; --accent: hsl(0, 0%, 34%); --timeline-line-color: var(--accent); --icon-border-color: hsl(0, 0%, 50%); } @media (max-width: 1023px) { :root { --side-spacing: 1.25rem; --icon-side-spacing: var(--side-spacing); --icon-width: 40px; --icon-image-width: 30px; --icon-size: 1.1rem; } } /* Use a tighter margin and smaller icon dimensions on the most narrow screens. * */ @media (max-width: 600px) { :root { --side-spacing: .75rem; --icon-side-spacing: .5rem; --timeline-line-width: 3px; --icon-width: 30px; --icon-border-width: 3px; --icon-image-width: 24px; --icon-size: .9rem; } } /* Containers */ .timeline { overflow: hidden; padding: 3rem 0; background-color: var(--background-color); } .timeline .container { position: relative; padding: 1rem var(--side-spacing); } /* The line that goes through all the icons */ .timeline .container::before { content: ''; position: absolute; height: 100%; width: var(--timeline-line-width); background-color: var(--timeline-line-color); z-index: 1; } @media (min-width: 1023px) { .timeline .container::before { left: calc(50% - var(--timeline-line-half-width)); } } @media (max-width: 1023px) { .timeline .container::before { margin-left: calc(var(--icon-half-width) - var(--timeline-line-half-width)); } } /* Each timeline item */ .timeline-item { display: flex; align-items: flex-start; margin-bottom: 2.5rem; } /* Arrow indicator on the top left/right of each content box */ .timeline-content::before { content: ''; position: absolute; border: solid var(--arrow-size) transparent; border-right-color: var(--accent); height: 0; } @media (max-width: 1023px) { .timeline-content::before { margin-left: calc(0px - var(--content-padding) - 14px); margin-top: calc(0px - var(--arrow-size) / 2); } } @media (max-width: 600px) { .timeline-content::before { margin-top: calc(0px - var(--icon-half-width) + var(--arrow-size)); } } @media (min-width: 1023px) { .timeline-content::before { right: 100%; margin-top: var(--arrow-size); } .timeline-item:nth-child(odd) .timeline-content::before { border-left-color: var(--accent); border-right-color: transparent; left: 100%; right: unset; } } /* Visual container of the timeline item */ .timeline-content.box { padding: var(--content-padding); box-shadow: var(--accent) 0px 3px 0px 0px; background-color: var(--content-background-color); flex-grow: 1; } @media (min-width: 1023px) { .timeline-item:nth-child(odd) { flex-direction: row-reverse; } /* On desktop, the content boxes are anchored with respect to the vertical * center of the screen, set using `left`/`right` properties depending on * even and odd children of timeline items. * */ .timeline-content.box { width: 45%; flex-grow: 0; position: relative; --content-position: calc(50% + var(--icon-border-width) + var(--icon-half-width)); left: var(--content-position); margin-left: var(--icon-side-spacing); } .timeline-item:nth-child(odd) .timeline-content.box { left: unset; right: var(--content-position); margin-right: var(--icon-side-spacing); } } @media (max-width: 1023px) { .timeline-content.box { margin-left: calc(var(--icon-side-spacing) + var(--arrow-size)); } } /* The icon and date components are grouped together in a flexbox to ensure * there are center-aligned vertically on both desktop and mobile screens. This * also makes it easy to both have dates' horizontal position follow that of * the icon, and reverse their ordering for alternate timeline items on * desktop. * */ .timeline-icon-date { z-index: 4; display: flex; align-items: baseline; } @media (min-width: 1023px) { .timeline-icon-date { order: 1; /* Arbitrary container width to prevent wrapping of the date text */ width: 50%; align-items: center; gap: var(--icon-side-spacing); position: absolute; left: calc(50% - var(--icon-half-width)); } .timeline-item:nth-child(even) .timeline-icon-date { flex-direction: row-reverse; left: unset; right: calc(50% - var(--icon-half-width)); } } /* Icon */ .timeline-icon { display: flex; justify-content: center; align-items: center; flex-shrink: 0; border-radius: 50%; height: var(--icon-width); width: var(--icon-width); /* The border does not actually use the border property because this border * needs to cover the bottom box-shadow, which is easier if the border is * implemented as a box-shadow. * */ box-shadow: 0 0 0 var(--icon-border-width) var(--icon-border-color), inset 0 2px 0 rgba(0,0,0,.08), 0 3px 0 4px rgba(0,0,0,.05); margin-top: var(--icon-border-width); /* Font Awesome icon size and color */ color: var(--icon-color); font-size: var(--icon-size); } /* Icons that use a custom image */ .timeline-icon img { width: var(--icon-image-width); height: var(--icon-image-width); } /* Icons that use the pydis logo */ .timeline-icon img.pydis { /* Visually centering the pydis logo requires a margin adjustment here * due to the right and bottom box shadow on the logo which is not very * visible on the page. * * XXX: Hardcoded; Unresponsive to actual image dimensions. * */ margin-top: 1px; margin-left: 1px; } .timeline-date { font-size: .9rem; color: var(--date-color); } @media (min-width: 1023px) { .timeline-item:nth-child(even) .timeline-date { left: auto; right: 50%; text-align: right; } } @media (max-width: 1023px) { .timeline-date { position: absolute; /* On mobile, place the date at the top of the text box left-aligned * with the other text in the box. When margin-left is zero, the date * is left-aligned to the left of the icon (right of box-shadow). * */ margin-left: calc(var(--icon-width) + var(--icon-side-spacing) + var(--arrow-size) + var(--content-padding)); margin-top: .75rem; } .timeline-content.content h3 { /* Make space for the date text */ margin-top: 1.25rem; } } /* Icon background colors */ .pastel-red { background-color: #ff7878 !important; } [data-theme="dark"] .pastel-red { background-color: #a44848 !important; } .pastel-orange { background-color: #ffbf76 !important; } [data-theme="dark"] .pastel-orange { background-color: #967147 !important; } .pastel-green { background-color: #8bd6a7 !important; } [data-theme="dark"] .pastel-green { background-color: #649f7a !important; } .pastel-blue { background-color: #8edbec !important; } [data-theme="dark"] .pastel-blue { background-color: #3d7986 !important; } .pastel-purple { background-color: #cbb1ff !important; } [data-theme="dark"] .pastel-purple { background-color: #8775ad !important; } .pastel-pink { background-color: #f6acff !important; } [data-theme="dark"] .pastel-pink { background-color: #79507e !important; } .pastel-lime { background-color: #b6df3a !important; } [data-theme="dark"] .pastel-lime { background-color: #7c9a21 !important; } .pastel-dark-blue { /* Works well for both themes */ background-color: #576297 !important; } img, video, svg { max-width: 100% } /* Bounce-in and bounce-out animations, desktop-only */ @media (min-width: 1023px) { .timeline-icon--hidden, .timeline-content--hidden, .timeline-date--hidden { visibility: hidden; } .timeline-icon--bounce-in { animation: icon-bounce 0.6s; } .timeline-content--bounce-in, .timeline-date--bounce-in { animation: item-bounce-left 0.6s; } .timeline-item:nth-child(even) .timeline-content--bounce-in, .timeline-item:nth-child(even) .timeline-date--bounce-in { animation-name: item-bounce-right; } .timeline-icon--bounce-out { animation: icon-bounce-out 0.6s; } .timeline-content--bounce-out, .timeline-date--bounce-out { animation: item-bounce-out 0.6s; } } @keyframes icon-bounce { 0% { opacity: 0; transform: scale(0.5); } 60% { opacity: 1; transform: scale(1.2); } 100% { transform: scale(1); } } @keyframes item-bounce-left { 0% { opacity: 0; transform: translateX(-100px) } 60% { opacity: 1; transform: translateX(20px) } 100% { transform: translateX(0) } } @keyframes item-bounce-right { 0% { opacity: 0; transform: translateX(100px) } 60% { opacity: 1; transform: translateX(-20px) } 100% { transform: translateX(0) } } @keyframes icon-bounce-out { 0% { opacity: 1; transform: scale(1); } 60% { transform: scale(1.2); } 100% { opacity: 0; transform: scale(0.5); } } @keyframes item-bounce-out { 0% { opacity: 1; transform: translateX(0); } 60% { transform: translateX(20px); } 100% { opacity: 0; transform: translateX(-100px); } }