How to Make a “Scroll to Select” Form Control
The element is a fairly straightforward concept: focus on it to reveal a set of
s that can be selected as the input’s value. That’s a great pattern and I’m not suggesting we change it. That said, I do enjoy poking at things and found an interesting way to turn a
into a dial of sorts — where options are selected by scrolling them into position, not totally unlike a combination lock or iOS date pickers. Anyone who’s expanded a
for selecting a country knows how painfully long lists can be and this could be one way to prevent that.
Here’s what I’m talking about:
It’s fairly common knowledge that styling in CSS is not the easiest thing in the world. But here’s the trick: we’re not working with at all. No, we’re not going to do anything like building our own by jamming a bunch of JavaScript into a
<section class=scroll-container>
<label for="madrid" class="scroll-item">
Madrid
<abbr>MAD</abbr>
<input id="madrid" type="radio" name="items">
</label>
<label for="malta" class="scroll-item">
Malta
<abbr>MLA</abbr>
<input id="malta" type="radio" name="items">
</label>
<!-- etc. -->
</section>
What we need is to style the list of selectable controls where we are capable of managing their sizes and spacing in CSS. I’ve gone with a group of labels with nested radio boxes as far as the markup goes. The exact styling is totally up to you, of course, but you can use these base styles I wrote up if you want a starting point.
.scroll-container {
/* SIZING & LAYOUT */
--itemHeight: 60px;
--itemGap: 10px;
--containerHeight: calc((var(--itemHeight) * 7) + (var(--itemGap) * 6));
width: 400px;
height: var(--containerHeight);
align-items: center;
row-gap: var(--itemGap);
border-radius: 4px;
/* PAINT */
--topBit: calc((var(--containerHeight) - var(--itemHeight))/2);
--footBit: calc((var(--containerHeight) + var(--itemHeight))/2);
background: linear-gradient(
rgb(254 251 240),
rgb(254 251 240) var(--topBit),
rgb(229 50 34 / .5) var(--topBit),
rgb(229 50 34 / .5) var(--footBit),
rgb(254 251 240)
var(--footBit));
box-shadow: 0 0 10px #eee;
}
A couple of details on this:
-
--itemHeight
is the height of each item in the list. -
--itemGap
is meant to be the space between two items. - The
--containerHeight
variable is the .scroll-container’s height. It’s the sum of the item sizes and the gaps between them, ensuring that we display, at maximum, seven items at once. (An odd number of items gives us a nice balance where the selected item is directly in the vertical center of the list). - The background is a striped gradient that highlights the middle area, i.e., the location of the currently selected item.
- The
--topBit
and –-footBit
variables are color stops that visually paint in the middle area (which is orange in the demo) to represent the currently selected item.
I’ll arrange the controls in a vertical column with flexbox declared on the .scroll-container:
.scroll-container {
display: flex;
flex-direction: column;
/* rest of styles */
}
With layout work done, we can focus on the scrolling part of this. If you haven’t worked with CSS Scroll Snapping before, it’s a convenient way to direct a container’s scrolling behavior. For example, we can tell the .scroll-container
that we want to enable scrolling in the vertical direction. That way, it’s possible to scroll to the rest of the items that are not in view.
.scroll-container {
overflow-y: scroll;
/* rest of styles */
}
Next, we reach for the scroll-snap-style
property that can be used to tell the .scroll-container
that we want scrolling to stop on an item — not near an item, but directly on it.
.scroll-container {
overflow-y: scroll;
scroll-snap-type: y mandatory;
/* rest of styles */
}
Now items “snap” onto an item instead of allowing a scroll to end wherever it wants. One more little detail I like to include is overscroll-behavior
, specifically along the y-axis as far as this demo goes:
.scroll-container {
overflow-y: scroll;
scroll-snap-type: y mandatory;
overscroll-behavior-y: none;
/* rest of styles */
}
overscroll-behavior-y: none
isn’t required to make this work, but when someone scrolls through the .scroll-container
(along the y-axis), scrolling stops once the boundary is reached, and any further continued scrolling action will not trigger scrolling in any nearby scroll containers. Just a form of defensive CSS.
Time to move to the items inside the scroll container. But before we go there, here are some base styles for the items themselves that you can use as a starting point:
.scroll-item {
/* SIZING & LAYOUT */
width: 90%;
box-sizing: border-box;
padding-inline: 20px;
border-radius: inherit;
/* PAINT & FONT */
background: linear-gradient(to right, rgb(242 194 66), rgb(235 122 51));
box-shadow: 0 0 4px rgb(235 122 51);
font: 16pt/var(--itemHeight) system-ui;
color: #fff;
input { appearance: none; }
abbr { float: right; } /* The airport code */
}
As I mentioned earlier, the --itemHeight
variable is setting as the size of each item and we’re declaring it on the flex
property — flex: 0 0 var(--itemHeight)
. Margin is added before and after the first and last items, respectively, so that every item can reach the middle of the container through scrolling.
The scroll-snap-align
property is there to give the .scroll-container
a snap point for the items. A center alignment, for instance, snaps an item’s center (vertical center, in this case) with the .scroll-container
‘s center (vertical center as well). Since the items are meant to be selected through scrolling alone pointer-events: none
is added to prevent selection from clicks.
One last little styling detail is to set a new background on an item when it is in a :checked
state:
.scroll-item {
/* Same styles as before */
/* If input="radio" is :checked */
&:has(:checked) {
background: rgb(229 50 34);
}
}
But wait! You’re probably wondering how in the world an item can be :checked
when we’re removing pointer-events
. Good question! We’re all finished with styling, so let’s move on to figuring some way to “select” an item purely through scrolling. In other words, whatever item scrolls into view and “snaps” into the container’s vertical center needs to behave like a typical form control selection. Yes, we’ll need JavaScript for that.
let observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
with(entry) if(isIntersecting) target.children[1].checked = true;
});
}, {
root: document.querySelector(`.scroll-container`), rootMargin: `-51% 0px -49% 0px`
});
document.querySelectorAll(`.scroll-item`).forEach(item => observer.observe(item));
The IntersectionObserver
object is used to monitor (or “observe”) if and when an element (called a target
) crosses through (or “intersects”) another element. That other element could be the viewport itself, but in this case, we’re observing the .scroll-container
for when a .scroll-item
intersects it. We’ve established the observed boundary with rootMargin:"-51% 0px -49% 0px"
.
A callback function is executed when that happens, and we can use that to apply changes to the target element, which is the currently selected .scroll-item
. In our case, we want to select a .scroll-item
that is at the halfway mark in the .scroll-container
: target.children[1].checked = true
.
That completes the code. Now, as we scroll through the items, whichever one snaps into the center position is the selected item. Here’s a look at the final demo again:
Let’s say that, instead of selecting an item that snaps into the .scroll-container
‘s vertical center, the selection point we need to watch is the top of the container. No worries! All we do is update the scroll-snap-align
property value from center to start in the CSS and remove the :first-of-type
‘s top margin. From there, it’s only a matter of updating the scroll container’s background gradient so that the color stops highlight the top instead of the center. Like this:
And if one of the items has to be pre-selected when the page loads, we can get its position in JavaScript (getBoundingClientRect()
) and use the scrollTo()
method to scroll the container to where that specific item’s position is at the point of selection (which we’ll say is the center in keeping with our original demo). We’ll append a .selecte
d class on that .scroll-item
.
<section class="scroll-container">
<!-- more items -->
<label class="scroll-items selected">
2024
<input type=radio name=items />
</label>
<!-- more items -->
</section>
Let’s select the .selected
class, get its dimensions, and automatically scroll to it on page load:
let selected_item = (document.querySelector(".selected")).getBoundingClientRect();
let scroll_container = document.querySelector(".scroll-container");
scroll_container.scrollTo(0, selected_item.top - scroll_container.offsetHeight - selected_item.height);
It’s a little tough to demo this in a typical CodePen embed, so here’s a live demo in a GitHub Page (source code). I’ll drop a video in as well:
That’s it! You can build up this control or use it as a starting point to experiment with different layouts, styles, animations, and such. It’s important the UX clearly conveys to the users how the selection is done and which item is currently selected. And if I was doing this in a production environment, I’d want to make sure there’s a good fallback experience for when JavaScript might be unavailable and that my markup performs well on a screen reader.
References and further reading
- A Few Functional Uses for Intersection Observer to Know When an Element is in View (Preethi Sam)
- An Explanation of How the Intersection Observer Watches (Travis Almand)
- Practical CSS Scroll Snapping (Max Kohler)
- The Current State of Styling Selects in 2019 (Chris Coyier)
- CSS Flexbox Layout Guide (CSS-Tricks)
- CSS flex property (CSS-Tricks)
- CSS Scroll Snap Properties (MDN)
- scrollTo() (MDN)
How to Make a “Scroll to Select” Form Control originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.
3 Essential Design Trends, October 2024
You have to get creative when you lack a strong visual. This month’s design trends roundup focuses on this concept.
The Timeless Power Of Spreadsheets
Part of me can’t believe I’m writing this article. Applying the insights of Leonardo da Vinci or Saul Bass to web design is more my groove, but sometimes you simply have to write about spreadsheets. You have to advocate for them. Because someone should.
In a checkered career spanning copywriting, journalism, engineering, and teaching, I’ve seen time and time again how powerful and useful spreadsheets are in all walks of life. The cold, hard truth is that you — yes, you — likely have an enormous amount to gain by understanding how spreadsheets work. And, more importantly, how they can work for you.
That’s what this piece is about. It’s a rallying cry, with examples of spreadsheets’ myriad uses and how they can actually, in the right circumstances, be the bedrock of altogether inspiring, lovely things.
Cellular Organisms
Spreadsheets have been around for thousands of years. Papyrus remnants have been discovered from as far back as 4,600 BC. Their going digital in the late ‘70s was a major factor in the rise of personal computing. Much is (rightly) made of the cultural transformation brought about by the printing press. The digital spreadsheet, not so much.
For as long as people have had projects and data to organize, spreadsheets have been indispensable. They were the original databases.
Spreadsheets don’t always get a lot of attention these days. For organization and workflow, we usually find ourselves in the worlds of Trello, Jira, or GitHub Projects. Datasets live in Oracle, MongoDB, and the like. There are good reasons for these services emerging — everything has its place — but
I do get the sense that specialized tooling causes us to skip over the flexibility and power that today’s spreadsheet editors provide.
This is especially true for smaller projects and ones in their early stages. Yes, sometimes only a huge database will do, but often spreadsheets are more than fit for purpose.
Benefits
What makes spreadsheets so great? We’ll get into a few real-world examples in a second, but several qualities hold true. They include the following:
-
Collaboration
Cloud-based editors like Google Sheets give groups of people a space in which to collaborate on data. They can serve as a middle ground for people working on different parts of the same project. -
Structure
It’s inherent to spreadsheets that they’ll get you thinking about the ‘shape’ of the information you’re dealing with. In the same way that a blank piece of paper invites fluidity of thought, tables coax out frameworks — and both have their place -
Flexibility
Spreadsheets can evolve in real time, which is especially useful during the formative stages of a project when the shape of the ‘data’ is still being established. Adding a field is as simple as naming a column, and the ability to weave in formulas makes it easy to infer other values from the ones you have. With stuff like the Google Sheets API, you can even scrape data directly from the spreadsheet -
Power
You’d be surprised how much you can do in spreadsheets. Sometimes, you don’t even need bespoke dashboards; you can do it all in the editor. From data visualization to pivot tables, spreadsheet editors come with a bunch of powerful out-of-the-box features. -
They translate into other data formats
Spreadsheets are one small jump from the mighty CSV. When the time is right, spreadsheets can still become raw data if you want them to.
Such is the flexibility and power of spreadsheets, and what’s listed here is scratching the surface. Their fundamental strength of organizing data has made them useful for thousands of years, while contemporary enhancements have taken them to the next level.
Case Studies
Below are a few examples from my own experiences that showcase these benefits in the real world. They’re obviously slanted towards my interests, but hopefully, they illustrate the usefulness of spreadsheets in different contexts.
Galaxies (Of The Guardian)
I work as a software engineer at Guardian News & Media, a place where 10% of the time, i.e., one work day every two weeks, is yours to spend on independent learning, side projects, and so on, is part of the working culture. An ongoing project of mine has been Galaxies (of the Guardian), a D3-powered org chart that represents departments as a series of interrelated people, teams, and streams.
What you see above is powered by information stored and edited in spreadsheets. A lambda scraps departmental information using the aforementioned Google Sheets API, then reformats into a shape Galaxies plays nicely with.
This approach has had several benefits. The earliest iterations of Galaxies were only possible because there was already a spreadsheet being maintained by those who needed to keep track of who worked where. Techies and non-techies alike are able to update information easily, and it is transparent to anyone who works inside the organization.
For anyone interested, I wrote a piece about how Galaxies works on the Guardian engineering blog. Suffice it to say here, spreadsheets were — and remain — the engine of the whole thing.
Food Bank Britain
My background is in journalism, and I still freelance in my own time. As my coding skills have improved, I’ve naturally gravitated towards data journalism, even teaching it for a year at my old journalism school.
Spreadsheets are inseparable from a lot of modern journalism — and, indeed, copyrighting in general. The digital world is awash with data, and good luck making sense of it without a working knowledge of spreadsheets.
For example, a piece I wrote for the Byline Times about foodbanks earlier this year simply wouldn’t have been possible without spreadsheets. It was by collating data from the Trussell Trust, the Independent Food Aid Network, and national census reports that I was able to map out the sheer scale of the UK’s food bank network.
Granted, the map is more visually engaging. But then that’s the idea. It’s the same information, just presented more pointedly.
There are plenty of other instances of spreadsheets being instrumental at the Guardian alone. Typerighter, the newspaper’s automated house style checker, began life as a subeditor’s spreadsheet. User research and bug tracking for the new Feast cooking app, which I worked on during its formative stages, was tracked and discussed in spreadsheets.
And, of course, countless pieces of quality journalism at the Guardian and beyond continue to be powered by them.
Another Cell In The Table
If this piece has got you to at least consider learning more about spreadsheets and spreadsheet editors, you’re in luck. There are countless free learning resources available on the web. Here are a few excellent beginner videos to help you on your way:
- Google Sheets Tutorial for Beginners
- How to use Pivot Tables in Google Sheets
- Excel Tutorial for Beginners
- Pivot Table Excel Tutorial
As for spreadsheet editors, the big three these days are probably Google Sheets, Microsoft Excel, and LibreOffice Calc (for the open source devotees out there). They all work much the same way. And as you get comfortable with their functionality, new avenues will open.
Data is the lifeblood of the modern web, and spreadsheets remain one of the most accessible, flexible ways to organize, analyze, and share it. As I hope the examples I’ve shared with you show, spreadsheets aren’t inherently boring. They can be, but when used in the right ways, they become the engines of dynamic, impactful work.
The way they go is up to you.
Quick Hit #21
Seeing a lot more headlines decrying JavaScript and pumping up PHP. Always interesting to see which direction the front-end zeitgeist is leaning.
Quick Hit #21 originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.
CSSWG Minutes Telecon (2024-09-18)
For the past two months, all my livelihood has gone towards reading, researching, understanding, writing, and editing about Anchor Positioning, and with many Almanac entries published and a full Guide guide on the way, I thought I was ready to tie a bow on it all and call it done. I know that Anchor Positioning is still new and settling in. The speed at which it’s moved, though, is amazing. And there’s more and more coming from the CSSWG!
That all said, I was perusing the last CSSWG minutes telecon and knew I was in for more Anchor Positioning when I came to the following resolution:
Whenever you are comparing names, and at least one is tree scoped, then both are tree scoped, and the scoping has to be exact (not subtree) (Issue #10526: When does
anchor-scope
“match” a name?)
Resolutions aren’t part of the specification or anything, but the strongest of indications about where they’re headed. So, I thought this was a good opportunity not only to take a peek at what we might get in anchor-scope
and touch on other interesting bits from the telecon.
Remember that you can subscribe and read the full minutes on W3C.org. 🙂
What’s anchor-scope
?
To register an anchor, we can give it a distinctive anchor-name
and then absolutely positioned elements with a matching position-anchor
are attached to it. Even though it may look like it, anchor-name
doesn’t have to be unique — we may reuse an anchor element inside a component with the same anchor-name
.
<ul>
<li>
<div class="anchor">Anchor 1</div>
<div class="target">Target 1</div>
</li>
<li>
<div class="anchor">Anchor 2</div>
<div class="target">Target 2</div>
</li>
<li>
<div class="anchor">Anchor 3</div>
<div class="target">Target 3</div>
</li>
</ul>
However, if we try to connect them with CSS,
.anchor {
anchor-name: --my-anchor;
}
.target {
position: absolute;
position-anchor: --my-anchor;
position-area: top right;
}
We get an unpleasant surprise where instead of each .anchor
have their .target
positioned at its top-right edge, meaning they all pile up on the last .anchor
instance. We can see it better by rotating each target a little. You’ll want to check out the next demo in Chrome 125+ to see the behavior:
The anchor-scope
property should make an anchor element only discoverable by targets in their individual subtree. So, the prior example would be fixed in the future like this:
.anchor {
anchor-name: --my-anchor;
anchor-scope: --my-anchor;
}
This is fairly straightforward — anchor-scope
makes the anchor element available only in that specific subtree. But then we have to ask another question: What should the anchor-scope
own scope be? We can’t have an anchor-scope-scope
property and then an anchor-scope-scope-scope
and so on… so which behavior should it be?
This is what started the conversation, initially from a GitHub issue:
When an
anchor-scope
is specified with a, it scopes the name to that subtree when the anchor name is “matching”. The problem is that this matching can be interpreted in at least three ways: (Assuming that
anchor-scope
is a tree-scoped reference, which is also not clear in the spec):
- It matches by the ident part of the name only, ignoring any tree-scope that would be associated with the name, or
- It matches by exact match of the ident part and the associated tree-scope, or
- It matches by some mechanism similar to dereferencing of tree-scoped references, where it’s a match when the tree-scope of the anchor-scope-name is an inclusive ancestor of the tree-scope of the anchor query.
And then onto the CSSWG Minutes:
TabAtkins: In anchor positioning, anchor names and references are tree scoped. The
anchor-scope
property that scopes, does not say whether the names are tree scoped or not. Question to decide: should they be?TabAtkins: I think the answer should be yes. If you have an anchor in a shadow tree with a part involved, then problems result if anchor scopes are not tree scoped. This is bad, so I think it should be tree scoped sounds pretty reasonable makes sense to me as far as I can understand it 🙂
This solution of the scope of scoping properties expanded towards View Transitions, which also rely on tree scoping to work:
khush: Thinking about this in the context of view transitions: in that API you give names and the tree scope has to be the same for them to match. There is another view transitions feature where I’m not sure if the spec says it’s tree scoped
khush: Want to make sure that feature is covered by the more general resolution
TabAtkins: Proposed more general resolution: whenever you are comparing names, and at least one is tree scoped, then both are tree scoped, and the scoping has to be exact (not subtree)
So the scope of anchor-scope
is tree-scoped. Say that five times fast!
RESOLVED: whenever you are comparing names, and at least one is tree scoped, then both are tree scoped, and the scoping has to be exact (not subtree)
The next resolution was pretty straightforward. Besides allowing a , the
anchor-scope
property can take an all
keyword, which means that all anchors are tree-scoped, while the says that specific anchor is three-scoped. So, the question was if
all
is also a tree-scoped value.
TabAtkins:
anchor-scope
, in addition to idents, can take the keyword ‘all
‘, which scopes all names. Should this be a tree-scoped ‘all
‘? (i.e. only applies to the current tree scope)TabAtkins: Proposed resolution: the ‘
all
‘ keyword is also tree-scoped in the same way sgtm +1, again same pattern withview-transition-group
RESOLVED: the ‘
all
‘ keyword is tree-scoped
The conversation switched gears toward new properties coming in the CSS Scroll Snap Module Level 2 draft, which is all about changing the user’s initial scroll with CSS. Taking anexample directly from the spec, say we have an image carousel:
<div class="carousel">
<img src="img1.jpg">
<img src="img2.jpg">
<img src="img3.jpg" class="origin">
<img src="img4.jpg">
<img src="img5.jpg">
</div>
We could start our scroll to show another image by setting it’s scroll-start-targe
to auto
:
.carousel {
overflow-inline: auto;
}
.carousel .origin {
scroll-start-target: auto;
}
As of right now, the only way to complete this is using JavaScript to scroll an element into view:
document.querySelector(".origin").scrollIntoView({
behavior: "auto",
block: "center",
inline: "center"
});
The last example is probably a carousel that is only scrollable in the inline direction. Still, there are doubts as far when the container is scrollable in both the inline and block directions. As seen in the initial GitHub issue:
The scroll snap 2 spec says that when there are multiple elements that could be
scroll-start-targets
for a scroll container “user-agents should select the one which comes first in tree order“.Selecting the first element in tree-order seems like a natural way to resolve competition between multiple targets which would be scrolled to in one particular axis but is perhaps not as flexible as might be needed for the 2d case where an author wants to scroll to one item in one axis and another item in the other axis.
And back to the CSSWG minutes:
DavidA: We have a property we’re adding called
scroll-start-target
that indicates if an element within a scroll container, then the scroll should start with that element onscreen. Question is what happens if there are multiple targets?
DavidA: Propose to do it in reverse-DOM order, this would result in the first one applied last and then be on screen. Also, should only change the scroll position if you have to.
After discussing why we have to define scroll-start-target
when we have scroll-snap-align
, the discussion went on discuss the reverse-DOM order:
fantasai: There was a bunch of discussion about regular vs reverse-DOM order. Where did we end up and why?
flackr: Currently, we expect that it scrolls to the first item in DOM order. We probably want that to still happen. That is why the proposal is to scroll to each item in sequence in reverse-DOM order.
So we are coming in reverse to scroll the element, but only as required so the following elements are showing as much as possible:
flackr: There is also the issue of nearest…
fantasai: Can you explain nearest?
flackr: Same as scroll into view
fantasai: ?
flackr: This is needed with you scroll multiple things into view and want to find a good position (?)
fantasai: You scroll in reverse-DOM order…when you add the spec can you make it really clear that this is the end result of the algorithm?
flackr: Yes absolutely
fantasai: Otherwise it seems to make sense
And so it was resolved:
Proposed resolution 2: When
scroll-start-target
targets multiple elements, scroll to each in reverse DOM order with text to specify priority is the first item
Lastly, there was the debate about the text-underline-position
, that when set to auto
says, “The user agent may use any algorithm to determine the underline’s position; however it must be placed at or under the alphabetic baseline.” The discussion was about whether the auto
value should automatically adjust the underlined position to match specific language rules, for example, at the right of the text for vertical writing modes, like Japanese and Mongolian.
fantasai: The initial value of
text-underline-position
isauto
, which is defined as “find a good place to put the underline”.
Three options there: (1) under alphabetical baseline, (2) fully below text (good for lots-of-descenders cases), (3) for vertical text on the RHS
fantasai: auto value is defined in the spec about ‘how far down below the text’, but doesn’t say things about flipping. The current spec says “at or below”. In order to handle language-specific aspects, there is a default UA style sheet that for Chinese and Japanese and Korean there are differences for those languages. A couple of implementations do this
fantasai: Should we change the spec to mention these things?
fantasai: Or should we stick with the UA stylesheet approach?
The thing is that Chrome and Firefox already place the underline on the right in vertical Japanese when text-underline-position
is auto
.
The group was left wiuth three options:
A) Keep spec as-is, update Gecko + Blink to match (using UA stylesheet for language switch)
B) Introduce auto totext-emphasis-positio
n and use it in bothtext-emphasis-position
andtext-underline-position
to effect language switches
C) Adopt inconsistent behavior:text-underline-position
uses ‘auto
‘ andtext-emphasis-position
uses UA stylesheetMany CSSWG members like Emilio Cobos, TabAtkins, Miriam Suzanne, Rachel Andrew and fantasai casted their votes, resulting in the following resolution:
RESOLVED: add auto value for text-emphasis-position, and change the meaning of text-underline-position: auto to care about left vs right in vertical text
I definitely encourage you to read at the full minutes! Or if you don’t have the time, you can there’s a list just of resolutions.
CSSWG Minutes Telecon (2024-09-18) originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.
Staying Relevant: A Guide for Brands in the Era of Gen Z Trends
In today’s fast-paced digital world, brands constantly face the challenge of staying relevant to younger consumers. For Gen Z, traditional marketing strategies often fall flat. This age group are as brand-savvy as any top marketer and members of this generation engage in a personal branding project by building online personas that are as integral to their identity as any offline habits.
Gen Z is a switched-on, brand-cynical generation. To connect with this age group, you must understand them, and the unstable world they’ve built their identity in. A life online and unstable social, political and economic boundaries have led this generation to be drawn to short-term trends and memes that resonate with their sense of nostalgia, often not for distant decades, but for more recent times.
The exploding “brat summer” trend, with its early-naughties low-fi aesthetic, is the perfect example of this. This viral trend has quickly reached every corner of our culture and even initiated a high-profile ‘rebrand’ for the Kamala Harris presidential campaign.
If it’s good enough for a presidential contender, it should be good enough for your brand. But as the aesthetic of ‘Brat Summer’ compresses a nostalgic ache for simpler times with the accelerated trend-slash-meme production of the internet machine, it’s tough for brands to keep up.
Nostalgia branding might be about looking backward, but Gen Z is creating short-term nostalgic trends in a very forward-thinking way. Let’s find out how your brand can leverage the what’s trending treadmill to engage with a younger audience.
Why Nostalgia Branding is so Powerful for a Gen Z Audience
Nostalgia branding taps into powerful emotions, making it an effective strategy for connecting with any age group: 77% of consumers call this evergreen branding trend interesting. By evoking fond memories and a sense of comfort, consumers build a strong connection to the brand in question. This has traditionally been done by leaning on older eras: the ‘70s, ’80s or ’90s, and by targeting a time that your target audience remembers.
However, nostalgia hits different for Gen Z, a generation raised online with a mismatched set of cultural references at their fingertips. Despite being born long after ‘80s beach culture exemplified by sunglasses brand Vacation, the visual aesthetic is instantly understood. But Gen Z aren’t just drawn to long-term nostalgic vision: they’re also nostalgic for elements of their own formative years, and even the very recent past.
A Shorter Cycle of Nostalgia Trends
The internet has accelerated the cycle of trends and the present turns into the past quicker than ever, making it ripe for nostalgia, and nostalgia branding. What used to take decades to become nostalgic now takes just a few years. Social media platforms like TikTok, Instagram, and Twitter have created environments where trends can go viral and fade away in the blink of an eye. This creates opportunities for brands to quickly adapt and leverage these trends.
“Nostalgia” can be for anything now: while previous efforts to implement nostalgia in branding have been casting back decades for eras where different value systems seemed more prominent (eg family, freedom, etc), now, Gen Z (and to an extent Millennials) will lean into an almost immediate nostalgia for things that happened in the recent past: Brat’s aesthetic is for an early-internet era, a simpler time.
Brat: A Case Study in Recent Nostalgia
“Brat” is a trend that encapsulates Gen Z’s unique take on nostalgia. It draws inspiration from early 2000s pop culture, particularly the rebellious and carefree attitudes seen in movies, TV shows, and music from that era. Think mini skirts, chunky highlights, and a vibe of spirited defiance.
Nobody expects it to last forever, with 80% of 18-25s anticipating this trend sticking around for fewer than a few months, but that’s part of the point. This should guide brand strategy: be fun, carefree and colorful, but be ready to move on to keep up with your audience.
Brands can take a leaf out of the “brat summer” playbook by understanding what resonates with Gen Z. They appreciate the irony and humor in looking back at trends from a decade ago as if they were ancient history. By tapping into this recent nostalgia, brands can create content that feels both familiar and fresh.
How Brands Can Leverage Online Trends to Engage Audiences
An accelerated cycle of memes and trends feeds into a potent nostalgic cocktail that can create highly effecting, and effective, marketing strategies, but it’s also overwhelming for brands trying to keep up.
Nevertheless, the opportunity for engagement, as well as creating a sense of authenticity, is huge when you successfully tap into short-term cycles of internet nostalgia. Here’s how….
1. Stay on Top of Trends
Keep an eye on social media platforms where trends emerge. Tools like Google Trends, Twitter’s trending topics, and TikTok’s Discover page can help you stay up to date.
While the specifics of a trend can’t be anticipated — nobody expected the neon tones and lower-case typography of Brat — the “something” summer is now a well-established template. Hot girl summer and hot vax summer are in the rearview mirror, and Brat summer is turning up the heat this July. Brands should be aware of wider trends and be ready to leverage memes and microtrends that fit.
2. Be Authentic
Faced with an increasingly superficial online world, Gen Z values authenticity: both in the brands they love and in their own lives. When leveraging nostalgia, it’s important to do so in a way that feels genuine. Avoid pandering, and instead, create content that creates relevance between your brand and a nostalgic trend, like Old Spice’s old-timey barber shop.
3. Collaborate with Influencers
Influencers who resonate with Gen Z can help amplify your message. Partner with influencers who align with the trend to create sponsored content that feels organic and relatable. A collaboration between luxury fashion brand Marc Jacobs and a TikTok account recreating soap opera drama with Sylvanian Families was one recent hit among a Gen Z audience.
4. Use Visual Storytelling
Visual platforms like Instagram and TikTok are perfect for nostalgia branding where past aesthetics can be used to create arresting visual content, such as Vacation’s consistent, sun-kissed ‘70s look. Nostalgia is about evoking an emotional response to times gone by, but given that half our brain’s processing power is devoted to the visual, this is best done with powerful imagery.
5. Engage in Conversations
By hitching your wagon to the latest trends, your brand is joining the conversation. Don’t forget to comment on posts, share user-generated content, and use hashtags to make your brand part of the community. By engaging in a meaningful way, you’ll ensure you’re not just responding to the latest trends, but playing your part in creating the next one.
Conclusion
Brands must be careful when leveraging short-term trends to build long-term engagement. While younger consumers see the Brat aesthetic everywhere, we found that less than 6% of over-45s are familiar with the trend. Know your audience before you lean into a meme otherwise, your efforts could fall flat.
For younger consumers, however, tapping into timely trends creates a perception of authenticity in the brands they see online. Connecting with Gen Z requires an understanding of their unique relationship with trends and nostalgia. Short-term trends like “brat summer” offer a glimpse into how recent nostalgia can be a powerful tool for engagement.
By staying ahead of trends, being authentic, collaborating with influencers, creating interactive content, using visual storytelling, and engaging in conversations, brands can build meaningful connections with younger consumers.
Featured image by Brooke Lark on Unsplash
The post Staying Relevant: A Guide for Brands in the Era of Gen Z Trends appeared first on noupe.
Quick Hit #20
Having fun with Bramus’ new Caniuse CLI tool. This’ll save lots of trips to the Caniuse site!
Quick Hit #20 originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.
Quick Hit #19
Two possible syntaxes for CSS masonry, one draft specification, and you get to share your opinions.
Quick Hit #19 originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.
A Beginner’s Guide to Using BlueSky for Business Success
In today’s fast-paced digital world, businesses are always on the lookout for new ways to connect with their audience. BlueSky, a decentralized social media platform, is quickly gaining attention as a fresh alternative to traditional platforms like Twitter and Instagram. While it’s still early days for BlueSky, its unique structure offers a big opportunity for businesses to build communities and engage with users in a more transparent, user-focused way.
SVG Coding Examples: Useful Recipes For Writing Vectors By Hand
Even though I am the kind of front-end engineer who manually cleans up SVG files when they are a mess, I never expected to become one of those people. You know, those crazy people that draw with code.
But here we are.
I dove deep into SVG specs last winter when I created a project to draw Calligraphy Grids, and even though I knew the basic structures and rules of SVG, it was only then that I fully tried to figure out and understand what all of those numbers meant and how they interacted with each other.
And, once you get the hang of it, it is actually very interesting and quite fun to code SVG by hand.
No ahead
We won’t go into more complex SVG shapes like paths in this article, this is more about practical information for simple SVGs. When it comes to drawing curves, I still recommend using a tool like Illustrator or Affinity. However, if you are super into compounding your lines, a path is useful. Maybe we’ll do that in Part 2.
Also, this guide focuses mostly on practical examples that illustrate some of the math involved when drawing SVGs. There is a wonderful article here that goes a bit deeper into the specs, which I recommend reading if you’re more interested in that: “A Practical Guide To SVG And Design Tools.”
Drawing With Math. Remember Coordinate Systems?
Illustrator, Affinity, and all other vector programs are basically just helping you draw on a coordinate system, and then those paths and shapes are stored in SVG files.
If you open up these files in an editor, you’ll see that they are just a bunch of paths that contain lots of numbers, which are coordinates in that coordinate system that make up the lines.
But, there is a difference between the all-powerful and the other, more semantic elements like
,
,
,
,
, and
.
These elements are not that hard to read and write by hand, and they open up a lot of possibilities to add animation and other fun stuff. So, while most people might only think of SVGs as never-pixelated, infinitely scaling images, they can also be quite comprehensive pieces of code.
How Does SVG Work? unit != unit
Before we get started on how SVG elements are drawn, let’s talk about the ways units work in SVG because they might be a bit confusing when you first get started.
The beauty of SVG is that it’s a vector format, which means that the units are somewhat detached from the browser and are instead just relative to the coordinate system you’re working in.
That means you would not use a unit within SVG but rather just use numbers and then define the size of the document you’re working with.
So, your width
and height
might be using CSS rem
units, but in your viewBox
, units become just a concept that helps you in establishing sizing relationships.
What Is The viewBox
?
The viewBox
works a little bit like the CSS aspect-ratio
property. It helps you establish a relationship between the width and the height of your coordinate system and sets up the box you’re working in. I tend to think of the viewBox
as my “document” size.
Any element that is placed within the SVG with bigger dimensions than the viewBox
will not be visible. So, the viewBox
is the cutout of the coordinate system we’re looking through. The width
and height
attributes are unnecessary if there is a viewBox
attribute.
So, in short, having an SVG with a viewBox
makes it behave a lot like a regular image. And just like with images, it’s usually easiest to just set either a width
or a height
and let the other dimension be automatically sized based on the intrinsic aspect ratio dimensions.
So, if we were to create a function that draws an SVG, we might store three separate variables and fill them in like this:
`<svg
width="${svgWidth}"
viewBox="0 0 ${documentWidth} ${documentHeight}"
xmlns="http://www.w3.org/2000/svg"
>`;
SVG Things Of Note
There is a lot to know about SVG: When you want to reuse an image a lot, you may want to turn it into a symbol
that can then be referenced with a use
tag, you can create sprites, and there are some best practices when using them for icons, and so on.
Unfortunately, this is a bit out of the scope of this article. Here, we’re mainly focusing on designing SVG files and not on how we can optimize and use them.
However, one thing of note that is easier to implement from the start is accessibility.
SVGs can be used in an tag, where
alt
tags are available, but then you lose the ability to interact with your SVG code, so inlining might be your preference.
When inlining, it’s easiest to declare role="img"
and then add a
tag with your image title.
Note: You can check out this article for SVG and Accessibility recommendations.
<svg
role="img"
[...attr]
>
<title>An accessible title</title>
<!-- design code -->
</svg>
Drawing SVG With JavaScript
There is usually some mathematics involved when drawing SVGs. It’s usually fairly simple arithmetic (except, you know, in case you draw calligraphy grids and then have to dig out trigonometry…), but I think even for simple math, most people don’t write their SVGs in pure HTML and thus would like to use algebra.
At least for me, I find it much easier to understand SVG Code when giving meaning to numbers, so I always stick to JavaScript, and by giving my coordinates names, I like them immeasurable times more.
So, for the upcoming examples, we’ll look at the list of variables with the simple math and then JSX-style templates for interpolation, as that gives more legible syntax highlighting than string interpolations, and then each example will be available as a CodePen.
To keep this Guide framework-agnostic, I wanted to quickly go over drawing SVG elements with just good old vanilla JavaScript.
We’ll create a container element in HTML that we can put our SVG into and grab that element with JavaScript.
<div data-svg-container></div>
<script src="template.js"></script>
To make it simple, we’ll draw a rectangle that covers the entire
viewBox
and uses a fill.
Note: You can add all valid CSS values as fills, so a fixed color, or something like currentColor
to access the site’s text color or a CSS variable would work here if you’re inlining your SVG and want it to interact with the page it’s placed in.
Let’s first start with our variable setup.
// vars
const container = document.querySelector("[data-svg-container]");
const svgWidth = "30rem"; // use any value with units here
const documentWidth = 100;
const documentHeight = 100;
const rectWidth = documentWidth;
const rectHeight = documentHeight;
const rectFill = "currentColor"; // use any color value here
const title = "A simple square box";
Method 1: Create Element and Set Attributes
This method is easier to keep type-safe (if using TypeScript) — uses proper SVG elements and attributes, and so on — but it is less performant and may take a long time if you have many elements.
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
const titleElement = document.createElementNS("http://www.w3.org/2000/svg", "title");
const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
svg.setAttribute("width", svgWidth);
svg.setAttribute("viewBox", 0 0 ${documentWidth} ${documentHeight}
);
svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
svg.setAttribute("role", "img");
titleElement.textContent = title;
rect.setAttribute("width", rectWidth);
rect.setAttribute("height", rectHeight);
rect.setAttribute("fill", rectFill);
svg.appendChild(titleElement);
svg.appendChild(rect);
container.appendChild(svg);
Here, you can see that with the same coordinates, a polyline won’t draw the line between the blue and the red dot, while a polygon will. However, when applying a fill, they take the exact same information as if the shape was closed, which is the right side of the graphic, where the polyline makes it look like a piece of a circle is missing.
This is the second time where we have dealt with quite a bit of repetition, and we can have a look at how we could leverage the power of JavaScript logic to render our template faster.
But first, we need a basic implementation like we’ve done before. We’re creating objects for the circles, and then we’re chaining the cx
and cy
values together to create the points
attribute. We’re also storing our transforms in variables.
const polyDocWidth = 200;
const polyDocHeight = 200;
const circleOne = { cx: 25, cy: 80, r: 10, fill: "red" };
const circleTwo = { cx: 40, cy: 20, r: 5, fill: "lime" };
const circleThree = { cx: 70, cy: 60, r: 8, fill: "cyan" };
const points = ${circleOne.cx},${circleOne.cy} ${circleTwo.cx},${circleTwo.cy} ${circleThree.cx},${circleThree.cy}
;
const moveToTopRight = translate(${polyDocWidth / 2}, 0)
;
const moveToBottomRight = translate(${polyDocWidth / 2}, ${polyDocHeight / 2})
;
const moveToBottomLeft = translate(0, ${polyDocHeight / 2})
;
And then, we apply the variables to the template, using either a polyline
or polygon
element and a fill
attribute that is either set to none
or a color value.
<svg
width={svgWidth}
viewBox={`0 0 ${polyDocWidth} ${polyDocHeight}`}
xmlns="http://www.w3.org/2000/svg"
role="img"
>
<title>Composite shape comparison</title>
<g>
<circle
cx={circleOne.cx}
cy={circleOne.cy}
r={circleOne.r}
fill={circleOne.fill}
/>
<circle
cx={circleTwo.cx}
cy={circleTwo.cy}
r={circleTwo.r}
fill={circleTwo.fill}
/>
<circle
cx={circleThree.cx}
cy={circleThree.cy}
r={circleThree.r}
fill={circleThree.fill}
/>
<polyline
points={points}
fill="none"
stroke="black"
/>
</g>
<g transform={moveToTopRight}>
<circle
cx={circleOne.cx}
cy={circleOne.cy}
r={circleOne.r}
fill={circleOne.fill}
/>
<circle
cx={circleTwo.cx}
cy={circleTwo.cy}
r={circleTwo.r}
fill={circleTwo.fill}
/>
<circle
cx={circleThree.cx}
cy={circleThree.cy}
r={circleThree.r}
fill={circleThree.fill}
/>
<polyline
points={points}
fill="white"
stroke="black"
/>
</g>
<g transform={moveToBottomLeft}>
<circle
cx={circleOne.cx}
cy={circleOne.cy}
r={circleOne.r}
fill={circleOne.fill}
/>
<circle
cx={circleTwo.cx}
cy={circleTwo.cy}
r={circleTwo.r}
fill={circleTwo.fill}
/>
<circle
cx={circleThree.cx}
cy={circleThree.cy}
r={circleThree.r}
fill={circleThree.fill}
/>
<polygon
points={points}
fill="none"
stroke="black"
/>
</g>
<g transform={moveToBottomRight}>
<circle
cx={circleOne.cx}
cy={circleOne.cy}
r={circleOne.r}
fill={circleOne.fill}
/>
<circle
cx={circleTwo.cx}
cy={circleTwo.cy}
r={circleTwo.r}
fill={circleTwo.fill}
/>
<circle
cx={circleThree.cx}
cy={circleThree.cy}
r={circleThree.r}
fill={circleThree.fill}
/>
<polygon
points={points}
fill="white"
stroke="black"
/>
</g>
</svg>
And here’s a version of it to play with:
See the Pen SVG Polygon / Polyline (simple) [forked] by Myriam.
Dealing With Repetition
When it comes to drawing SVGs, you may find that you’ll be repeating a lot of the same code over and over again. This is where JavaScript can come in handy, so let’s look at the composite example again and see how we could optimize it so that there is less repetition.
Observations:
- We have three circle elements, all following the same pattern.
- We create one repetition to change the
fill
style for the element. - We repeat those two elements one more time, with either a
polyline
or apolygon
. - We have four different
transforms
(technically, no transform is a transform in this case).
This tells us that we can create nested loops.
Let’s go back to just a vanilla implementation for this since the way loops are done is quite different across frameworks.
You could make this more generic and write separate generator functions for each type of element, but this is just to give you an idea of what you could do in terms of logic. There are certainly still ways to optimize this.
I’ve opted to have arrays for each type of variation that we have and wrote a helper function that goes through the data and builds out an array of objects with all the necessary information for each group. In such a short array, it would certainly be a viable option to just have the data stored in one element, where the values are repeated, but we’re taking the DRY thing seriously in this one.
The group array can then be looped over to build our SVG HTML.
const container = document.querySelector("[data-svg-container]");
const svgWidth = 200;
const documentWidth = 200;
const documentHeight = 200;
const halfWidth = documentWidth / 2;
const halfHeight = documentHeight / 2;
const circles = [
{ cx: 25, cy: 80, r: 10, fill: "red" },
{ cx: 40, cy: 20, r: 5, fill: "lime" },
{ cx: 70, cy: 60, r: 8, fill: "cyan" },
];
const points = circles.map(({ cx, cy }) => ${cx},${cy}
).join(" ");
const elements = ["polyline", "polygon"];
const fillOptions = ["none", "white"];
const transforms = [
undefined,
translate(${halfWidth}, 0)
,
translate(0, ${halfHeight})
,
translate(${halfWidth}, ${halfHeight})
,
];
const makeGroupsDataObject = () => {
let counter = 0;
const g = [];
elements.forEach((element) => {
fillOptions.forEach((fill) => {
const transform = transforms[counter++];
g.push({ element, fill, transform });
});
});
return g;
};
const groups = makeGroupsDataObject();
// result:
// [
// {
// element: "polyline",
// fill: "none",
// },
// {
// element: "polyline",
// fill: "white",
// transform: "translate(100, 0)",
// },
// {
// element: "polygon",
// fill: "none",
// transform: "translate(0, 100)",
// },
// {
// element: "polygon",
// fill: "white",
// transform: "translate(100, 100)",
// }
// ]
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.setAttribute("width", svgWidth);
svg.setAttribute("viewBox", 0 0 ${documentWidth} ${documentHeight}
);
svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
svg.setAttribute("role", "img");
svg.innerHTML = "<title>Composite shape comparison</title>";
groups.forEach((groupData) => {
const circlesHTML = circles
.map((circle) => {
return <circle
cx="${circle.cx}"
cy="${circle.cy}"
r="${circle.r}"
fill="${circle.fill}"
/>
;
})
.join("");
const polyElementHTML = <${groupData.element}
points="${points}"
fill="${groupData.fill}"
stroke="black"
/>
;
const group = <g ${groupData.transform ?
transform="${groupData.transform}": ""}>
${circlesHTML}
${polyElementHTML}
</g>
;
svg.innerHTML += group;
});
container.appendChild(svg);
And here’s the Codepen of that:
See the Pen SVG Polygon / Polyline (JS loop version) [forked] by Myriam.
More Fun Stuff
Now, that’s all the basics I wanted to cover, but there is so much more you can do with SVG. There is more you can do with transform
; you can use a mask
, you can use a marker
, and so on.
We don’t have time to dive into all of them today, but since this started for me when making Calligraphy Grids, I wanted to show you the two most satisfying ones, which I, unfortunately, can’t use in the generator since I wanted to be able to open my generated SVGs in Affinity and it doesn’t support pattern
.
Okay, so pattern
is part of the defs
section within the SVG, which is where you can define reusable elements that you can then reference in your SVG.
Graph Grid with pattern
If you think about it, a graph is just a bunch of horizontal and vertical lines that repeat across the x- and y-axis.
So, pattern
can help us with that. We can create a and then reference a
pattern
in the fill
attribute of the rect
. The pattern then has its own width
, height
, and viewBox
, which defines how the pattern is repeated.
So, let’s say we want to perfectly center our graph grid in any given width or height, and we want to be able to define the size of our resulting squares (cells).
Once again, let’s start with the JavaScipt variables:
const graphDocWidth = 226;
const graphDocHeight = 101;
const cellSize = 5;
const strokeWidth = 0.3;
const strokeColor = "currentColor";
const patternHeight = (cellSize / graphDocHeight) * 100;
const patternWidth = (cellSize / graphDocWidth) * 100;
const gridYStart = (graphDocHeight % cellSize) / 2;
const gridXStart = (graphDocWidth % cellSize) / 2;
Now, we can apply them to the SVG element:
<svg
width={svgWidth}
viewBox={`0 0 ${graphDocWidth} ${graphDocHeight}`}
xmlns="http://www.w3.org/2000/svg"
role="img"
>
<defs>
<pattern
id="horizontal"
viewBox={`0 0 ${graphDocWidth} ${strokeWidth}`}
width="100%"
height={`${patternHeight}%`}
>
<line
x1="0"
x2={graphDocWidth}
y1={gridYStart}
y2={gridYStart}
stroke={strokeColor}
stroke-width={strokeWidth}
/>
</pattern>
<pattern
id="vertical"
viewBox={`0 0 ${strokeWidth} ${graphDocHeight}`}
width={`${patternWidth}%`}
height="100%"
>
<line
y1={0}
y2={graphDocHeight}
x1={gridXStart}
x2={gridXStart}
stroke={strokeColor}
stroke-width={strokeWidth}
/>
</pattern>
</defs>
<title>A graph grid</title>
<rect
width={graphDocWidth}
height={graphDocHeight}
fill="url(#horizontal)"
/>
<rect
width={graphDocWidth}
height={graphDocHeight}
fill="url(#vertical)"
/>
</svg>
And this is what that then looks like:
See the Pen SVG Graph Grid [forked] by Myriam.
Dot Grid With pattern
If we wanted to draw a dot grid instead, we could simply repeat a circle. Or, we could alternatively use a line with a stroke-dasharray
and stroke-dashoffset
to create a dashed line. And we’d only need one line in this case.
Starting with our JavaScript variables:
const dotDocWidth = 219;
const dotDocHeight = 100;
const cellSize = 4;
const strokeColor = "black";
const gridYStart = (dotDocHeight % cellSize) / 2;
const gridXStart = (dotDocWidth % cellSize) / 2;
const dotSize = 0.5;
const patternHeight = (cellSize / dotDocHeight) * 100;
And then adding them to the SVG element:
<svg
width={svgWidth}
viewBox={`0 0 ${dotDocWidth} ${dotDocHeight}`}
xmlns="http://www.w3.org/2000/svg"
role="img"
>
<defs>
<pattern
id="horizontal-dotted-line"
viewBox={`0 0 ${dotDocWidth} ${dotSize}`}
width="100%"
height={`${patternHeight}%`}
>
<line
x1={gridXStart}
y1={gridYStart}
x2={dotDocWidth}
y2={gridYStart}
stroke={strokeColor}
stroke-width={dotSize}
stroke-dasharray={`0,${cellSize}`}
stroke-linecap="round"
></line>
</pattern>
</defs>
<title>A Dot Grid</title>
<rect
x="0"
y="0"
width={dotDocWidth}
height={dotDocHeight}
fill="url(#horizontal-dotted-line)"
></rect>
</svg>
And this is what that looks like:
See the Pen SVG Dot Grid [forked] by Myriam.
Conclusion
This brings us to the end of our little introductory journey into SVG. As you can see, coding SVG by hand is not as scary as it seems. If you break it down into the basic elements, it becomes quite like any other coding task:
- We analyze the problem,
- Break it down into smaller parts,
- Examine each coordinate and its mathematical breakdown,
- And then put it all together.
I hope that this article has given you a starting point into the wonderful world of coded images and that it gives you the motivation to delve deeper into the specs and try drawing some yourself.