Design foundations

The portfolio uses primitive values, semantic roles, and component treatments. These specimens consume the same CSS treatments and custom properties as the site.

Light and dark theme contract

The site follows your system preference until you choose a theme with the navigation toggle. These fixed specimens show both contracts together. Text, surfaces, controls, and brand sections use paired roles.

Light

Primary body text

Secondary metadata stays legible.

Raised surface / dialog content

Brand grouping with its paired foreground.

Dark

Primary body text

Secondary metadata stays legible.

Raised surface / dialog content

Brand grouping with its paired foreground.

Color roles

Page--color-page-bg
Raised surface--color-surface
Subtle surface--color-surface-subtle
Accent + inverse text
Brand surface + text

Typography

Body / Source Serif 4 / 19px / 1.5 line height. Responsive variants preserve the composition of each page.

Label / Albert Sans / 16px / semibold

Spacing

The root is 75%: normally 12px. Each half-rem advances the 6px rhythm. Shared gutters and section padding have semantic roles.

0.5rem
1rem
2rem
5rem

Component treatments

Project tag

Raised card

Shared radius and complete shadow recipe.

Button

Use button on an anchor for navigation or a native button for an action. Filled, accent intent, light surface, and regular size are defaults. Add button--outline, button--neutral, button--on-dark or button--on-accent, and button--large or button--compact independently. Surface means the background behind the control; on dark/accent surfaces both intents use inverse contrast.

Every row shows filled and outline appearances in accent and neutral intent. Hover, press, or Tab to inspect shared states. These specimen buttons have no application action.

Light surface

Dark surface

Accent surface

Size and accessories

Trailing icon

Icons use button__icon before or after the text, aria-hidden="true" on SVGs, and empty alt text on decorative images. Keep a visible text label. Native disabled belongs on buttons; an unavailable link should omit href and use aria-disabled="true". ARIA alone does not prevent navigation. No loading state is needed by current controls.

<a href="/" class="button button--outline button--neutral">Home</a>
<button type="button" class="button button--compact">Pause scroll</button>

The About card is itself a button: its Learn More affordance is a noninteractive div class="button button--outline". Never nest a link or button inside it. The carousel keeps its pressed state and pause/resume behavior; shared styling handles its geometry and ordinary states.

Project card

One component handles every project. Titles and metadata wrap naturally as content grows; CSS adapts the grid from two columns to one on smaller screens. No standard or wide variant is needed.

Testimonial

A standalone quote uses the same template as carousel slides. Portrait and destination are optional; this generic example omits both.

“A clear, thoughtful experience makes all the difference.”
Example colleagueProduct Designer at Example Company

The homepage, case studies, design foundations, and 404 page share the site-footer build-time include. It renders the contact CTA followed by the contact footer, after the page's main landmark.

Use <!-- include:site-footer --> for default copy, or <!-- include:site-footer {"variant":"404"} --> for the 404 self-credit joke. Edit templates/site-footer.html for shared copy, email and résumé destinations, and credit links. No other parameters are supported.

The underlined “designed” link opens this design-system page; “built” opens an article about frontend craft and AI assistance. Both inherit inverse text on the dark CTA surface and display a visible keyboard focus outline.

Graphic playground

The homepage hero combines a bold sans-serif headline, a serif introduction, shared pill buttons, and a decorative grid of interface icons. The graphic shapes keep sharp corners; interactive controls keep the site’s rounded button treatment.

playground-hero.css owns the composition and highlight sweep. playground-hero.js draws nine tiles, using the palette tokens in primitives.css. Activate the artwork with a click, tap, Enter, or Space to remix colors, icons, and shapes. The Pause motion button stops drift and pointer response; remix remains available as a static change. The static SVG is the fallback when JavaScript or canvas is unavailable.

Animation stops outside the viewport and in hidden tabs. Reduced motion disables continuous animation and highlight sweeps. The shared contact CTA reveals its highlight once when scrolled into view, using swiss-cta.js; its copy remains visible without JavaScript.

Cursor family

Fine pointers use native SVG cursors in cursor.css: a broad black arrow for ordinary content, a chunky pointing hand for actions, and an open hand for the testimonial marquee that closes while dragging. The --cursor-default, --cursor-interactive, --cursor-grab, and --cursor-grabbing properties include standard cursor fallbacks. Editable fields retain the text cursor, and touch devices retain their native behavior.

Motion and accessibility

Interaction, entrance, exit, and dialog durations have separate roles. Reduced-motion preferences suppress entrance and continuous animations while keeping content visible. Native dialogs retain Escape dismissal and return focus to their trigger.

GOOD THINGS START WITH A CONVERSATION.

LET’S MAKE
IT HAPPEN.

I'd be happy to chat about how I can help your business and customers with my creative experience. I'm currently looking to join a team as a full-time product designer for opportunities anywhere remotely in the United States.

By the way, I designed & built this site myself!