Versions Dot HTML
A place to capture the experiments and versions of this site.
Feb 5, 2025
Not a site update but a code snippet instead.
I'm sure we've all seen sites using a typewriter effect by this point. I've built a few versions of them for client projects, generally using JS libraries or custom JS. After seeing another version from the excellent Kevin Powell, I figured it was time to take a crack at building a lightweight version for myself.
In the end I wound up combining his approach with the effect from another Codepen. The below code is designed to connect to an ACF block in WordPress. The demo that follows uses infinite
in the animation purely for demonstration purposes. In the wild, this would use forwards
instead.
CSS can now handle everything – including the calculations necessary to make this feel lifelike! The key elements are:
overflow: hidden
to keep text out of view until needed.- The
steps()
animation timing function to reveal one letter at a time. - And finally, the
ch
unit in combination with a monospaced font. In monospace (fixed-width) fonts, where all characters are the same width, 1ch equals one character.
<?php
$typewriter_content = get_field('typewriter_content');
$typewriter_length = count($typewriter_content);
$typewriter_speed = get_field('typewriter_speed') ?: 3;
?>
.typewriter {
display: flex;
justify-content: center;
text-align: center;
margin: 0;
}
.typewriter-inner {
font-family: 'Courier New', Courier, monospace;
overflow: hidden;
white-space: nowrap;
margin-inline: 0;
width: max-content;
animation: typing <?php echo $typewriter_speed. 's';?> steps(<?php echo $typewriter_length; ?>) forwards;
}
@keyframes typing {
from { width: 0 }
to { width: <?php echo $typewriter_length . 'ch'; ?> }
}
<section id="<?php echo esc_attr($block['id']); ?>" class="typewriter">
<h2 class="typewriter-inner"><?php echo $typewriter_content; ?></h2>
</section>
Demo
This text content is 40 characters long.
Next steps: While I like the way this is running, I'd like to put in some work to make the CSS responsive, which would entail wrapping text onto multiple lines.
v2.3: Current
Added detail on ongoing updates to AIFKK and Prescott's.
Styling for mobile view of code snippets on Versions page.
Built progress bar for mobile view, usinganimation-timeline: scroll()
The use of scroll-driven animations is still restricted to Chromium-based browsers at this time, so this is a nice feature that will only show up for some site visitors for the time being. I snagged this code from a larger exploration of scroll timelines by Lee Meyer on CSS-Tricks, and it builds on an earlier keyframe scroll animation demo by Chris Coyier.
It starts with anhr
divider in the nav. The scroll() CSS function accounts for the position on the page, in the vertical axis by default. As outlined in MDN, "The position in the scroll range is converted into a percentage of progress — 0% at the start and 100% at the end."
Then it's as simple as connecting an animation that goes from 0 to 100% with an animation timeline that pulls in the the scroll position from 0 to 100!
@media (max-width: 900px) and (prefers-reduced-motion: no-preference) {
@supports (animation-timeline: scroll()) {
.scroll-progress {
display: block;
margin: 0;
height: 3px;
width: 0%;
background-color: var(--accent-color);
animation: scroll-progress 1s linear;
animation-timeline: scroll();
}
}
}
@keyframes scroll-progress {
to {
background-color: var(--accent-color);
width: 100%;
}
}
v2.2
Rewrote much of the Intro to focus more on my work and goals. Moved the About section to the end of the homepage. Re-styled project cards.
v2.1
Finally moved onto my own domain at here at hogan.work, currently powered by Cloudflare Pages! Given that I'm not using any build tools, the deployment process is virtually identical and takes no time at all.
v2: Container Queries
I wanted to improve responsiveness for the case study elements, and get more practice with CSS Grid and container queries. Time to rework these case studies into responsive cards!
By assigning the various card elements togrid-areas,
I was able to gain more control over the elements across breakpoints.
.project {
container-type: inline-size;
margin: 0 auto;
...
}
.project-card {
...
grid-template-areas: "logo" "content" "screengrabs";
grid-template-columns: minmax(0, 1fr);
.logo-wrapper {
grid-area: logo;
}
...
}
A client in the medical supply field needed a new site for their group of brands, while also rolling out updates to individual brands, taking a templated approach. Several vital behind-the-scenes pieces of functionality, including:
- A Group Events feed that uses custom RSS and category tags to power event calendars on group site and on websites of multiple group brands
- Custom navigation to open brand tab in tablist, defaulting to a link to standalone brand website as a fallback for users without JavaScript enabled
When our card container gets to an appropriate size, the card switches to a two-column layout. Usingminmax(0, 1fr)
overrides the default behavior that could allow card content like images or video to overflow the card. And the .
placeholder is required in the grid-template-areas
declaration to allow a grid cell to go unfilled.
@container (min-width: 640px) {
.project-card {
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
column-gap: 6%;
grid-template-areas: ". logo" "screengrabs content";
}
...
}
Then alternating the images & content as we were before is as simple as changing the positioning of the grid areas and the slider controls:
.project:nth-child(even) {
@container (min-width: 640px) {
.project-card {
grid-template-areas: "logo ." "content screengrabs";
}
.project-card .swiper .swiper-controls {
justify-content: flex-end;
margin-inline-end: 1.6rem;
}
}
}
A client in the medical supply field needed a new site for their group of brands, while also rolling out updates to individual brands, taking a templated approach. Several vital behind-the-scenes pieces of functionality, including:
- A Group Events feed that uses custom RSS and category tags to power event calendars on group site and on websites of multiple group brands
- Custom navigation to open brand tab in tablist, defaulting to a link to standalone brand website as a fallback for users without JavaScript enabled
A great opportunity for collaboration with outside designers. Animation and responsiveness settings throughout the site provide an unexpectedly engaging experience for an infrastructure giant.
- Oversized typography and animations that work seamlessly across screensizes
- Translation tool with custom formatting to account for longer words in certain langauges
- Product catalog with custom ACF backend
v1: Moving away from Bootstrap
The first version of this site was powered by the Bootstrap library. It has some nice functionality built in, but was definitely more than this little project needed!
Moving to a barebones CSS framework with minimal, targeted JS libraries for sliders and videos made a major difference in load times and maintainability.
Case Studies swap image and content from left to right in a familiar pattern, using rows and columns more or less the same way that Bootstrap would.
Next, I focused on some fun CSS styling and animations, starting with a frosted glass effect on the project cards. Later I implemented a scroll-out effect for the cards, using animation and keyframes.
See my progress on GitHubA client in the medical supply field needed a new site for their group of brands, while also rolling out updates to individual brands, taking a templated approach. Several vital behind-the-scenes pieces of functionality, including:
- A Group Events feed that uses custom RSS and category tags to power event calendars on group site and on websites of multiple group brands
- Custom navigation to open brand tab in tablist, defaulting to a link to standalone brand website as a fallback for users without JavaScript enabled
A great opportunity for collaboration with outside designers. Animation and responsiveness settings throughout the site provide an unexpectedly engaging experience for an infrastructure giant.
- Oversized typography and animations that work seamlessly across screensizes
- Translation tool with custom formatting to account for longer words in certain langauges
- Product catalog with custom ACF backend