Styleguide

Every component and token on this site, rendered from the real includes. Clean theme, following the light or dark setting.

Colours

  • --color-bg
  • --color-surface
  • --color-text
  • --color-text-muted
  • --color-border
  • --color-rule-strong
  • --color-accent
  • --color-on-accent
  • --color-plate
  • --color-grid

Type

  • --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.

Spacing

  • --space-14px
  • --space-28px
  • --space-312px
  • --space-416px
  • --space-520px
  • --space-624px
  • --space-832px
  • --space-1040px
  • --space-1248px
  • --space-1664px
  • --space-2496px

Radii and motion

  • --radius-00
  • --radius-13px
  • --radius-full999px
  • --duration-1150ms
  • --duration-2300ms
  • --duration-3600ms
  • --duration-morph350ms
  • --duration-morph-short250ms

Icons

  • arrow-left
  • arrow-right
  • arrow-up-right
  • calendar
  • external-link

Site header + nav

Compare side by side

The sample has no current link. On a real page, the current link is underlined in the accent.

Markup contract
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.

Theme switch

Compare side by side

On a real page it's fixed to the bottom right. Hidden without JS, and the site is then Clean.

Theme

Markup contract
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.

Contact block

Compare side by side

Lives in the footer on every page.

Markup contract
section.contact > .section-head + ul.contact__links.

Section head

Compare side by side

Work

6 cases

Markup contract
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.

Button / link

Compare side by side

Markup contract
a.button > label + optional .icon. Set `button` = { label, href, variant, icon }.
Variant is an attribute: data-variant="primary | secondary | external".

Hero

Compare side by side

Vedran Jukić

Designer · Coder · Agentic Whisperer

I build design systems teams actually use, and I design in code.

Simple takes work.

Markup contract
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.

Now

Compare side by side

Employer and industries only. No clients, no screens.

Now

Since 2025

Studio
Inverse Design Studio, Berlin
Role
Lead product designer and engineer
Focus
AI-native and agentic products, tokenized design systems, AI in the design workflow
Clients
Tech and automotive. Under NDA, so no screens here.
Walkthrough on request
Markup contract
section.now > .section-head + dl.facts + .button.

Theme sandbox

Compare side by side

Hidden without JS, because there is nothing to switch. The controls only work on the real section on Home.

This site

3 themes

This whole site can change its look. Same content, three themes, one set of tokens.

Clean / Comic / Vector

Markup contract
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.

Case card

Compare side by side

Hover or tab to it to see the padding redline.

ByrdView Design System

2020–2024byrd technologies

A design system built from the ground up as the sole designer, using 10% of my weekly time.

Senior, then lead product designer

Markup contract
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.

Case list

Compare side by side

Ordered by the order field in each case's front matter.

  1. ByrdView Design System

    2020–2024byrd technologies

    A design system built from the ground up as the sole designer, using 10% of my weekly time.

    Senior, then lead product designer

  2. Actionable reporting

    2023byrd technologies

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

    Lead designer

  3. Warehousing App

    2023byrd technologies

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

    Lead designer

  4. Assisted comparison

    2020Visual Meta

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

    Design lead

  5. Boxie

    2022byrd technologies

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

    Concept and illustration

  6. Vector Arcade

    2026Personal project

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

    Design and code

Markup contract
ol.case-list > li > .case-card. Reads collections.work, ordered by front matter `order`.

Page header

Compare side by side

For plain pages. The picture is optional.

Markup contract
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.

Case header

Compare side by side

On a real case page, the cover sits beside the text from 900px.

All work

ByrdView Design System

A design system built from the ground up as the sole designer, using 10% of my weekly time.

Client
byrd technologies
Year
2020–2024
Role
Senior, then lead product designer
Links
  • 2 months for the redesign, against a 2-quarter estimate

  • 96 → ~11 colour values

  • 58 components, 240 variants

  • 152 layered icons

Markup contract
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.

Next case

Compare side by side

Closes every case page. The last case leads back to the first.

Markup contract
p.next-case > a > .next-case__label + .next-case__title (with the arrow icon).
Set `nextCase` to a case from collections.work.

Prose

Compare side by side

Rendered Markdown. Has no include of its own.

A section heading

Body text keeps a comfortable measure. It can hold bold words and a plain link.

A smaller heading

  • A list item
  • Another list item
  1. A numbered item
  2. Another numbered item
Markup contract
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.

Figure

Compare side by side

On the plate, which is transparent in Clean. data-size="small" is for illustrations: no plate, cropped and narrower.

Angled view of the new dashboard: warehouse capacity and stock type charts, an expanded notifications panel, and suggestions with Replenish and Dismiss actions
Detail of the dashboard designs
Markup contract
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.

Walkthrough

Compare side by side

A flow of similar screens, one at a time. Needs a caption per screen. Without JS the figures stack.

Wireframe: product grid with a Compare checkbox on each card
Compare on each product
Wireframe: two products selected, with a comparison bar at the bottom
Comparison bar
Markup contract
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/).

Persona

Compare side by side

Comic-style portrait of Getit Garrett in a red hoodie, thinking about prices and discounts, beside a red Air Jordan 1 sneaker

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.

Markup contract
div.persona > .figure + p. Set `persona` = { src, alt, name, text }.
In Markdown, write the same block by hand (see src/work/_template/).

Stat

Compare side by side

58 components and 240 variants

Markup contract
p.stat > strong.stat__value + span.stat__label. Set `stat` = { value, label }.

Quote

Compare side by side

Accessibility allows users of all abilities to understand, use and enjoy the UI.

ByrdView accessibility guidelines
Markup contract
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.

Callout

Compare side by side

No case uses one yet.

Markup contract
aside.callout > .callout__label + .callout__body. Set `callout` = { label, text }.
Decoration uses ::before/::after only (a speech bubble in Comic).

Flow

Compare side by side

A short decision flow. A plain ordered list, so it reads the same without the lines.

  1. New UI need

  2. Does it already exist?

    Yes Use it

    No
  3. Can an existing one adapt?

    Yes Amend it

    No
  4. Used in two or more places?

    Yes Add to the library

    No
  5. Keep it as a snowflake

    Second use Add to the library

Deciding what joins the library
Markup contract
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.