ByrdView Design System
A design system built from the ground up as the sole designer, using 10% of my weekly time.
Senior, then lead product designer

Every component and token on this site, rendered from the real includes. Clean theme, following the light or dark setting.
--color-bg--color-surface--color-text--color-text-muted--color-border--color-rule-strong--color-accent--color-on-accent--color-plate--color-grid--step-5Simple takes work.--step-4Simple takes work.--step-3Simple takes work.--step-2Simple takes work.--step-1Simple takes work.--step-0Simple takes work.--step--1Simple takes work.--step--2Simple takes work.--space-14px--space-28px--space-312px--space-416px--space-520px--space-624px--space-832px--space-1040px--space-1248px--space-1664px--space-2496px--radius-00--radius-13px--radius-full999px--duration-1150ms--duration-2300ms--duration-3600ms--duration-morph350ms--duration-morph-short250msThe sample has no current link. On a real page, the current link is underlined in the accent.
header.site-header > .wrapper > .site-header__inner > .site-header__name + nav > ul.site-header__links.
The current page link gets aria-current="page". Work gets aria-current="true" on case pages.
The header is full width and .wrapper sits inside it, so its background reaches both edges of the window.
On a real page site-header.js sets data-reveal, and data-hidden on the way down.
On a real page it's fixed to the bottom right. Hidden without JS, and the site is then Clean.
fieldset.theme-switch > legend + .theme-switch__options > label.theme-switch__option (input[type=radio] + span), then p.theme-switch__status.
Its shape never changes between themes. Only the colours follow, through tokens, and no theme adds decoration.
Native radios give arrow keys, the current state and announcements. The status is a polite live region. On a real page the fieldset starts hidden and theme-switch.js shows it.
Lives in the footer on every page.
section.contact > .section-head + ul.contact__links.
6 cases
div.section-head > h2.section-head__title + optional p.section-head__note.
Set `head` = { id, title, note }. `idPrefix` keeps ids unique when a sample repeats.
a.button > label + optional .icon. Set `button` = { label, href, variant, icon }.
Variant is an attribute: data-variant="primary | secondary | external".
Designer · Coder · Agentic Whisperer
I build design systems teams actually use, and I design in code.
Simple takes work.
section.hero > .hero__inner > .hero__grid (12 spans) + .hero__name + .hero__role + .hero__measure + .hero__lede + .hero__tagline.
.hero__grid and .hero__measure are decoration (aria-hidden). The measure's height is the token it labels.
.hero__cut holds words the tagline edits away. They're aria-hidden and start closed, so the tagline reads the same without motion.
Employer and industries only. No clients, no screens.
Since 2025
section.now > .section-head + dl.facts + .button.
Hidden without JS, because there is nothing to switch. The controls only work on the real section on Home.
3 themes
This whole site can change its look. Same content, three themes, one set of tokens.
Switch looks any time with the control in the bottom right corner.
Clean / Comic / Vector
section.theme-sandbox > .section-head + .theme-sandbox__layout > .theme-sandbox__copy + .theme-sandbox__stage.
.theme-sandbox__copy holds the intro, the button and the hint. .theme-sandbox__stage holds a label and an iframe of /theme-demo/.
The button plays the sequence, and theme-sandbox.js runs it: a redline to the preview, the three themes in turn, then a redline to the real switch.
On a real page the section starts hidden and theme-sandbox.js shows it.
Hover or tab to it to see the padding redline.
A design system built from the ground up as the sole designer, using 10% of my weekly time.
Senior, then lead product designer

article.case-card > .case-card__body (.case-card__title, .case-card__meta, .case-card__summary, .case-card__role)
+ figure.case-card__media + .case-card__deco (aria-hidden, with .case-card__label).
Title comes first for screen readers; CSS moves the cover and meta up. The whole card is one link (stretched title link).
State hook: --card-active is 0 at rest, 1 on hover or keyboard focus. Themes drive their own effects from it.
.case-card__deco sits on the body, so it relies on the 4:3 cover. .case-card__label holds the real token value.
Set `item` to a case from collections.work.
Set `morph` to true where the cover should move into the case header. Each name may appear only once on a page.
Ordered by the order field in each case's front matter.
A design system built from the ground up as the sole designer, using 10% of my weekly time.
Senior, then lead product designer

A dashboard people only clicked through, turned into notifications and suggestions that cut support resolution time.
Lead designer

A UX redesign of an Android app for warehouse staff, delivered in stages.
Lead designer

Leading a remote design team to help online shoppers compare, save and decide, from kickoff to high-fidelity designs.
Design lead

A mascot for empty and error states that grew into a company-wide character.
Concept and illustration

Two small browser games, a tribute to the glowing vector arcade machines of the early ’80s.
Design and code

ol.case-list > li > .case-card. Reads collections.work, ordered by front matter `order`.
For plain pages. The picture is optional.
Lead product designer. 20+ years, from print to product to design in code.

header.page-header > optional p.page-header__label + h1.page-header__title + optional p.page-header__summary + optional .page-header__measure + optional figure.page-header__media.
Reads label, title, summary, image, imageAlt, rail from the page's front matter.
.page-header__measure is decoration (aria-hidden), on rail pages with a picture. Its height is the token it labels.
page-inspect.js makes its readout live; without it the readout describes the measure itself.
The sizes are larger than the shown picture on purpose: a bigger image scaled down keeps fine detail like hair.
On a real case page, the cover sits beside the text from 900px.
A design system built from the ground up as the sole designer, using 10% of my weekly time.

2 months for the redesign, against a 2-quarter estimate
96 → ~11 colour values
58 components, 240 variants
152 layered icons
header.case-header > .case-header__back, .case-header__title, .case-header__summary,
figure.case-header__media, dl.case-header__facts (holds ul.case-header__outcomes and ul.case-header__links),
ul.case-header__stats > li > p.stat.
Reads title, summary, cover, client, year, role, outcomes, links, stats. The last three are optional.
`stats` = [{ value, label }] shows a row of big numbers, for cases with several results. Use it instead of `outcomes`.
Set `morph` to true on the real case page, so the card's cover moves into this one.
Closes every case page. The last case leads back to the first.
p.next-case > a > .next-case__label + .next-case__title (with the arrow icon).
Set `nextCase` to a case from collections.work.
Rendered Markdown. Has no include of its own.
Body text keeps a comfortable measure. It can hold bold words and a plain link.
div.prose > rendered Markdown. Text stays at --measure; .figure, .gallery and .walkthrough use the full width. On a case page from 900px, h2 and h3 sit in a left rail.
On the plate, which is transparent in Clean. data-size="small" is for illustrations: no plate, cropped and narrower.

figure.figure > picture > img + optional figcaption.figure__caption. Set `figure` = { src, alt, caption }.
In Markdown the same markup comes from the rule in eleventy.config.js.
data-row keeps two in a row on phones, for small illustrations.



div.gallery > .figure (2 or more). Set `gallery` to a list of { src, alt, caption }.
Three images get three columns, any other number gets two. Phones get one.
A flow of similar screens, one at a time. Needs a caption per screen. Without JS the figures stack.


div.walkthrough[data-label] > .figure (2 or more, each with a caption).
Set `walkthrough` = { label, steps: [{ src, alt, caption }] }. The caption names the step.
assets/js/walkthrough.js adds .walkthrough__nav (.walkthrough__steps and .walkthrough__tip), wraps each figure in .walkthrough__screen and sets data-ready. Without it the figures stack.
In Markdown, write the same block by hand (see src/work/_template/).

Getit Garrett
Garrett wants to buy Air Jordan 1 sneakers and he is looking for the best deal. He is our user with a SPECIFIC need.
div.persona > .figure + p. Set `persona` = { src, alt, name, text }.
In Markdown, write the same block by hand (see src/work/_template/).
58 components and 240 variants
p.stat > strong.stat__value + span.stat__label. Set `stat` = { value, label }.
Accessibility allows users of all abilities to understand, use and enjoy the UI.
blockquote.quote > p + optional footer.quote__source. Set `quote` = { text, source }.
In Markdown, `>` gives the same root class through the rule in eleventy.config.js.
No case uses one yet.
aside.callout > .callout__label + .callout__body. Set `callout` = { label, text }.
Decoration uses ::before/::after only (a speech bubble in Comic).
A short decision flow. A plain ordered list, so it reads the same without the lines.
New UI need
Does it already exist?
Yes Use it
NoCan an existing one adapt?
Yes Amend it
NoUsed in two or more places?
Yes Add to the library
NoKeep it as a snowflake
Second use Add to the library
figure.flow > ol.flow__list > li.flow__step, then optional figcaption.flow__caption.
A step holds p.flow__box (the question), p.flow__branch (span.flow__label + span.flow__box--end, the outcome) and span.flow__down (the way on).
li.flow__step--last holds the fallback outcome and its way back: .flow__target names where it leads, and sits visually hidden where a line shows it.
Set `flow` = { start, steps: [{ question, outcome }], fallback: { outcome, label, target }, caption }.
In Markdown, write the same markup by hand. No JS: it reads as a plain ordered list.