Portfolio design system

Night is home, paper is the case study. One typeface, one teal, glass only as floating chrome.

Every specimen on this page is the live component, painted with the site's own tokens. The code tabs read each component's file when the site is built.

Principles

  • “Is it as obvious as knowing to sit in a chair?”
  • “Prominence = importance.”
  • “State before words.”
  • “Fact ≠ forecast.”
  • “Actionable looks actionable.”
  • “One element, one job.”
  • “SCALE leads the hierarchy — weight stays quiet. … Size makes things loud, not weight.”
  • “Main-site palette: one set, shared by the homepage and every page. Two surfaces, two line greys, three inks, one teal; paper has one ink and one muted ink. Data hues are for drawn product screens only.”
  • “Radius, three steps: pill, card 16px, inner 8px. 50% for circles.”
  • “Elevation is tone, not shadow: night → raised → floating glass.”
  • “Teal as a scalpel (not glow).”

Brand and voice

A design system is the vocabulary. This is the reason.

I didn't pull this palette from a trend deck. I built the view I see when I close my eyes.

I grew up in Mumbai, on the water, and my first home in America was by Boston Harbor. And nothing — nothing — has ever matched the feeling of the ocean and the waves for me. It's the one landscape that has felt like home on two coasts, an entire ocean apart — and I've carried it with me everywhere since. So the waves in this site aren't decoration. They're the closest thing I have to a place that has always been mine.

Then there's the light. I've painted since I was five, I keep my rooms dim, and I'm most awake at night — so the northern lights are, quite simply, the most beautiful thing I can imagine watching. For a painter, colour that dances on its own is a kind of magic. A dim room, moving light, deep water — that isn't a mood board. It's just where my mind goes when it's happiest.

This whole site is those two loves in a single frame: the ocean below, the aurora above, and everything I've made floating in the middle of it. It's my most beautiful daydream, rendered — the view I'd want to live inside.

So no — this isn't a portfolio with a nice background. It's me showing you the inside of my imagination. If you want to know who I am, you're already standing in it.

The dark
Focus. I keep my rooms dim and I work at night — the site should feel the way my studio feels.
The teal
The one colour I trust to lead. Everything else exists to support it.
One voice, quietly
I dropped the second font, then the heavy weights. One typeface, a restrained 300–600 range, and size does the shouting — the restraint is the statement.
The brain you can step into
The closest I could get to showing you the inside of my head — not just what I made.
The light that moves when you arrive
The aurora. Always. Dancing colour above, deep water below.
The stillness underneath it
A dim room at night. Somewhere I would actually want to sit.

The system in brief

Modes

Night (--bg-cinematic) is the homepage. Paper (--paper) is the white case-study page; the first-light transition moves between the two.

Accent

Teal: the color of deep ocean + aurora. It is the system, not decoration.

Buttons

Three looks: primary (solid teal, all caps), the one main action in a view; secondary (the soft fill, all caps), beside a primary or in a row of controls; tertiary (teal small caps, no fill, always an icon) for longer labels in the reading flow.

Controls

One master button in three looks, 40px, Hanken at 400; a label of more than two words is tertiary. Icon-only is a 40px circle in the soft fill. Toggles are their own component.

Type

One typeface, Hanken Grotesk. No monospace face; every label is Hanken held in weight, caps and tracking.

Weight

600 for display and headings, 500 for labels, 350 for body, 300 for the quietest asides.

Surface

One control surface, the soft fill. In the page it is see-through; floating over pictures or scrolling content it is the same colour made opaque. There is no glass.

Depth

Nothing casts a shadow and no button has a border. Surfaces rise by tone: night, then raised.

Status

A status is information, not an action: dot and plain text, no pill, no outline.

Motion

Blur-to-focus entering, focus-to-blur leaving. No crossfades.

Color

One set, the same on the homepage and every page. Night has two surfaces, two greys for lines and marks, three inks and one teal. Paper has its own inks and a darker teal so it clears contrast on white. Every older token name points at this set.

Night

Surfaces, lines, inks, teal. --glow is teal on night; --teal is teal on paper and in fills.

--night

#03070a

--raised

#0b1517

--line

#222b29

--mark

#5d6966

Aa

--ink-1

#f2f5f4

Aa

--ink-2

#c9d1cf

Aa

--ink-3

#93a09c

--teal

#078b9c

--glow

#2fc6d5

Paper

The white case studies: one ink for headings and body, one muted ink for captions and meta.

--paper

#f6f7f7

Aa

--paper-ink

#1f2a2e

Aa

--paper-ink-muted

#485459

--paper-line

#dfe4e5

--paper-teal

#066a78

Data hues

One per hue, only inside the drawn product screens and charts. Never for the site's own chrome.

--accent-amber

#f59e0b

--semantic-emerald

#10b981

--semantic-blue

#3b82f6

--semantic-rose

#f43f5e

--semantic-orange

#f97316

--semantic-purple

#a855f7

--semantic-cyan

#06b6d4

Older names

Kept so existing code keeps working. Each one resolves to the set above; none adds a colour.

--palette-zinc-100 → --ink-1

--palette-zinc-200 → --ink-1

--palette-zinc-300 → --ink-2

--palette-zinc-400 → --ink-2

--palette-zinc-500 → --ink-3

--palette-zinc-600 → --ink-3

--palette-zinc-700 → --mark

--palette-zinc-800 → --line

--palette-zinc-900 → --raised

--palette-zinc-950 → --night

--bg-primary → --night

--bg-secondary → --raised

--bg-tertiary → --raised

--bg-cinematic → --night

--bg-ink-950 → --night

--bg-ink-900 → --raised

--bg-monitor → --night

--bg-monitor-alt → --raised

--text-heading → --ink-1

--text-primary → --ink-1

--text-body → --ink-2

--text-muted → --ink-3

--text-dim → --mark

--text-monitor → --ink-1

--text-monitor-muted → --ink-3

--text-monitor-dim → --ink-3

--accent-teal → --teal

--accent-teal-bright → --glow

--border-monitor → --line

--focus-ring → --accent-teal

--radius-sm → --r-inner

--section-padding-mobile → --space-20

--section-padding-tablet → --space-24

--section-padding-desktop → --space-32

Case-study scope, night and paper

The case studies read their own --cs-* tokens. Paper re-points them; each pair below is painted inside the real scope.

--cs-space

var(--night) · paper var(--paper)

--cs-silver

var(--ink-1) · paper var(--paper-ink)

--cs-slate

var(--ink-2) · paper var(--paper-ink)

--cs-steel

var(--ink-3) · paper var(--paper-ink-muted)

--cs-ink

var(--ink-1) · paper var(--paper-ink)

--cs-line

var(--line) · paper var(--paper-line)

--cs-reject

#b9556a · paper #a8324d

--cs-teal

var(--teal) · paper var(--paper-teal)

Typography

One family, Hanken Grotesk, and no monospace anywhere. Size leads the hierarchy; weight stays between 300 and 600.

Family

Aa Gg Rr 0123

--font-sans

'Hanken Grotesk', 'SF Pro Text', -apple-system, BlinkMacSystemFont, sans-serif

Roles

Made obvious.

Display

--text-6xl · --weight-heading

Same weight, scale sets the rank.

Heading

--text-4xl · --weight-heading

The chair tells you it is a chair.

Subheading

--text-xl · --weight-subheading

Body sits at 350, with relaxed leading for long-form reading.

Body

--text-base · --weight-body

Label voice: weight, caps, tracking

Label

--text-xs · --weight-label

Scale

The quick brown fox, 1,234

--text-xs

0.75rem

The quick brown fox, 1,234

--text-sm

0.875rem

The quick brown fox, 1,234

--text-base

1.0625rem

The quick brown fox, 1,234

--text-lg

1.1875rem

The quick brown fox, 1,234

--text-xl

1.3125rem

The quick brown fox, 1,234

--text-2xl

1.75rem

The quick brown fox, 1,234

--text-3xl

2.1875rem

The quick brown fox, 1,234

--text-4xl

2.5rem

The quick brown fox, 1,234

--text-5xl

3rem

The quick brown fox, 1,234

--text-6xl

4rem

Weights

Restraint is the statement

--weight-heading

600

Restraint is the statement

--weight-subheading

600

Restraint is the statement

--weight-label

500

Restraint is the statement

--weight-body

350

Restraint is the statement

--weight-light

300

Leading and tracking

Two lines of sample text to show the leading between them.

--leading-tight

1.1

Two lines of sample text to show the leading between them.

--leading-snug

1.25

Two lines of sample text to show the leading between them.

--leading-normal

1.47059

Two lines of sample text to show the leading between them.

--leading-relaxed

1.6

Two lines of sample text to show the leading between them.

--leading-loose

1.8

Letter spacing

--tracking-tighter

-0.05em

Letter spacing

--tracking-tight

-0.022em

Letter spacing

--tracking-normal

-0.011em

Letter spacing

--tracking-wide

0.025em

Letter spacing

--tracking-wider

0.05em

Spacing

A 4px rhythm. Every gap and pad steps off it.

--space-1

0.25rem

--space-2

0.5rem

--space-3

0.75rem

--space-4

1rem

--space-5

1.25rem

--space-6

1.5rem

--space-8

2rem

--space-10

2.5rem

--space-12

3rem

--space-16

4rem

--space-20

5rem

--space-24

6rem

--space-32

8rem

Radius

Three steps, each with one job. Circles use 50%. The 2px and 4px corners appear only inside drawn product screens.

--r-pill

999px · buttons, chips, the nav, icon buttons

--r-card

16px · pictures, tiles, panels, frames, chat bubbles

--r-inner

8px · focus outlines, kbd, list buttons, the scrollbar thumb

Elevation

Depth is tone, not shadow. Nothing on the site casts a shadow or carries a border; a surface rises by getting lighter, and a control that floats uses the soft fill made opaque.

Night

the page

Raised

tiles, panels, wells

Floating

nav and controls over pictures: the soft fill, opaque

--shadow-sm, --shadow-md, --shadow-lg, --shadow-xl remain only for the drawn product screens, where the real products float their menus.

Overlays and scrim

White and ink overlays for scrims, backdrops and tonal steps. Controls don't use them: every control is the solid teal or the soft fill.

--overlay-white-02

rgba(var(--white-rgb), 0.02)

--overlay-white-03

rgba(var(--white-rgb), 0.03)

--overlay-white-05

rgba(var(--white-rgb), 0.05)

--overlay-white-08

rgba(var(--white-rgb), 0.08)

--overlay-white-10

rgba(var(--white-rgb), 0.1)

--overlay-white-15

rgba(var(--white-rgb), 0.15)

--overlay-white-20

rgba(var(--white-rgb), 0.2)

--overlay-ink-55

rgba(var(--black-rgb), 0.55)

--overlay-ink-70

rgba(var(--black-rgb), 0.7)

--overlay-ink-75

rgba(var(--black-rgb), 0.75)

Motion

Every easing and duration token. Select a track to run it. Mirrored 1:1 for framer-motion.

Easings, at --duration-slow

--ease-cinematic

cubic-bezier(0.22, 1, 0.36, 1)

--ease-spring

cubic-bezier(0.34, 1.56, 0.64, 1)

--ease-smooth

cubic-bezier(0.4, 0, 0.2, 1)

--ease-expo

cubic-bezier(0.16, 1, 0.3, 1)

Durations, on --ease-cinematic

--duration-fast

200ms

--duration-normal

300ms

--duration-medium

400ms

--duration-slow

500ms

Contrast

Measured live in your browser from the computed tokens, with translucent fills composited over their real ground. Semantic hues never carry text in chrome, so there are no semantic text-on-tint pairs to test.

Night

PairSampleRatioWCAG AA

Heading text on page

--text-heading on --bg-primary

Aa 123measuring

Primary text on page

--text-primary on --bg-primary

Aa 123measuring

Body text on page

--text-body on --bg-primary

Aa 123measuring

Muted text on card

--text-muted on --bg-secondary

Aa 123measuring

Dim text on page

--text-dim on --bg-primary

Bar 3:1, large or decorative text only, per its token note

Aa 123measuring

Accent text on page

--accent-teal on --bg-primary

Aa 123measuring

Primary button label

--btn-primary-ink on --btn-primary

Aa 123measuring

Primary label on hover

--btn-primary-ink on --btn-primary-hover

Aa 123measuring

Secondary button label

--control-ink on --control-fill

Aa 123measuring

Tertiary button label

--btn-tertiary-ink on --bg-primary

Aa 123measuring

Icon on the control fill

--control-ink on --control-fill

Aa 123measuring

Toggle, chosen segment

--toggle-on-ink on --toggle-on

Aa 123measuring

Floating control label

--control-ink on --control-float

Aa 123measuring

Focus ring against page

--focus-ring on --bg-primary

Aa 123measuring

Paper

PairSampleRatioWCAG AA

Ink (headings and body) on paper

--cs-ink on --cs-space

Aa 123measuring

Muted ink (captions) on paper

--cs-steel on --cs-space

Aa 123measuring

Teal link on paper

--cs-teal on --cs-space

Aa 123measuring

Primary button label

--btn-primary-ink on --btn-primary

Aa 123measuring

Primary label on hover

--btn-primary-ink on --btn-primary-hover

Aa 123measuring

Secondary button label

--control-ink on --control-fill

Aa 123measuring

Tertiary button label

--btn-tertiary-ink on --cs-space

Aa 123measuring

Icon on the control fill

--control-ink on --control-fill

Aa 123measuring

Toggle, chosen segment

--toggle-on-ink on --toggle-on

Aa 123measuring

Focus ring against paper

--focus-ring on --cs-space

Aa 123measuring

Components

One component per job. Each one below shows where it belongs, its states side by side, its other versions, a live copy to try, and its real source.

Button

The master button. Every action on the site, in three looks: primary (solid teal, all caps) for the one main action in a view; secondary (the soft control fill, all caps) beside a primary or in a row of controls; tertiary (teal true small caps, no fill, always an icon, colour on hover) for longer labels in the reading flow. A label of more than two words is always tertiary. Labels are nouns; the icon carries the action. Icon-only is a 40px circle in the soft control fill. Hanken Grotesk at 400, 40px tall.

Use when

  • primary: the one main action in a view, one or two words: Reach out, Download, Try again
  • secondary: beside a primary or in a row of controls, one or two words: Ask Anu, Resume, Case study, Live app, Open, Deck, a way back (pass back)
  • tertiary: longer labels in the reading flow: the drill-downs (pass aria-expanded), the videos, My life outside work
  • icon only: pass icon and no label (close, zoom, play, arrows, the case-study Back)

Don't

  • Two primary buttons side by side
  • A primary or secondary label longer than two words (it renders as tertiary)
  • A verb in the label when the icon already says it (Case study →, not Read the case study)
  • A status label: use the status dot and text
  • A selected state: use the toggle

States and variants

Hover, focus and pressed are forced from the site's own CSS, so they sit side by side.

VariantDefaultHoverFocusPressedDisabled
primary, night
Not in code
secondary, night
Not in code
tertiary, night
Not in code
icon only, night
Not in code
primary, paper
Not in code
secondary, paper
Not in code
tertiary, paper
Not in code
icon only, paper
Not in code

Button has no disabled state in code. One size; the colours come from the --btn-* and --control-* tokens, which flip on paper.

More versions

Primary, label only
Primary, label + icon
More than two words: tertiary, even when asked for primary
Secondary, a way back: the arrow leads
A way back: the arrow leads
External link icon
Disclosure: the case-study drill-down

Live, try it

IconButton

The 40px circle for every icon-only action: lightbox close, zoom, play, the arrows, the case-study Back. One size, one surface: the soft fill, opaque because it often sits over pictures.

Use when

  • An icon everyone reads without a caption (close, play, next, back)
  • Controls over pictures, video or the lightbox

Don't

  • Primary actions, which deserve words: use Button
  • A second icon button styled a little differently

States and variants

Hover, focus and pressed are forced from the site's own CSS, so they sit side by side.

VariantDefaultHoverFocusPressedDisabled
night
Not in code
paper
Not in code

IconButton has no disabled state in code.

More versions

Close
Zoom
Play, filled glyph
Previous and next

Live, try it

FILL_PILL and FLOAT_PILL

The one control surface, the soft fill, defined once as class strings. FILL_PILL is see-through, for controls in the page; FLOAT_PILL is the same colour made opaque, for anything that floats. They look the same at rest. There is no glass.

Use when

  • FILL_PILL: toggle tracks and chips in the page
  • FLOAT_PILL: icon-only buttons, Back to top, lightbox controls, the floating nav track

Don't

  • A second fill for floating controls
  • Re-typing the fill values by hand

States and variants

Hover, focus and pressed are forced from the site's own CSS, so they sit side by side.

VariantDefaultHoverFocusPressed
FILL_PILL, night
FLOAT_PILL, night
FLOAT_PILL, paper

No border and no shadow. The colours come from --control-fill and --control-float, which flip on paper.

Case-study status

StatusBadge: a dot and plain caps text. Information, never an action, so it is deliberately not a pill.

Use when

  • The state of the work in a case-study hero
  • A section label inside a case study

Don't

  • Anything clickable
  • A pill, outline or teal text, which would read as a button

States and versions

Live
Paper
Design complete
Paper, longer label

Homepage

The homepage is the night page, built from its own file. Four type sizes, the site palette, three radii and the one control set the case studies use. Every value here is read from that file.

Type: four sizes

--fs-display
The chair line, the about-me list, section headings, Let’s talk
As obvious as a chair.
--fs-title
Tile names, quotes, testimonials, the email address
As obvious as a chair.
--fs-body
Hero lines, receipts, the facts line
As obvious as a chair.
--fs-label
Labels, captions, buttons, status, tags, footer
As obvious as a chair.

Color

--night
#03070a

--raised
#0b1517

--ink-1
#f2f5f4

--ink-2
#c9d1cf

--ink-3
#93a09c

--ink-off
rgba(242,245,244,.2)

--glow
#2fc6d5

--glow-fill
rgba(47,198,213,.14)

--fill
rgba(255,255,255,.055)

--emerald
#34d3a6

Controls: one set

Reach out →
Primary button
View demo ↗
Tertiary button
×
Icon button
Hiring
Toggle chip, chosen
How I work
Toggle chip

Tile

WardenDecides what AI agents may do. Pausing one actually stops it.Personal tool · live demo · 299 testsView demo ↗

The picture fills edge to edge; the title sits underneath. The whole tile opens the case study, and the button opens the demo.

Rules

  • Four type sizes: display 64, title 28, body 17, label 14.
  • Three button looks: primary (solid teal, all caps), secondary (the soft fill, all caps) and tertiary (teal small caps, an icon, colour on hover); more than two words is tertiary. Icon-only is the 40px circle in the soft fill. The Ask topics are a toggle.
  • Primary button: Reach out and Ask.
  • Secondary button: Résumé and Ask Anu in the header; Ask Anu beside Reach out; the tile links Case study, Demo and Live app; Design systems.
  • Tertiary in two lines: the stories link after every tile, the arrow at the end.
  • Labels are nouns, not verbs: the icon carries the action (Case study →, Live app ↗), which keeps them to two words so they can be buttons.
  • Icon button: a 40px circle in the soft fill.
  • Toggle, chips form: the Ask topics on the control fill; the chosen one lifts.
  • Tiles: the whole tile opens the case study; the button beside the status opens the demo.

Rules

Do

  • Blur-to-focus (entering) and focus-to-blur (leaving) instead — everywhere.

  • Every action on the site is this one Button: the same height, weight, press and focus, in three looks.

  • Case studies: no scroll-reveal, no decorative hover, no glow behind visuals. The ONLY motion is functional.

  • Every control takes its fill and its hover and press states from one file, so they feel identical everywhere.

Don’t

  • No hex values, no Tailwind default-palette colors (text-red-500 etc.) — only semantic tokens.

  • NO crossfades. NO opacity fades.

  • No border, no glow, no shadow on any button.

  • Locked experiences: the brain experience, HeroAurora and the case-study trailers. Never modify their internals.

Spec decisions

None on this page. Every value here resolves to a site token or a case-study token. Where the site has no token, the page says so instead of inventing one: the full pill radius and the chip radius are Tailwind's, and there are no chart tokens.

The master spec fills those gaps for its shared components with --radius-md, --radius-lg and --radius-xl (10, 16 and 24px), semantic tints at 12%, and --border-strong at white 14%. The live site does not use them.

Never a loose end.

A design system is only finished when it leaves nothing dangling, so this page ends the way every path should: with a clear way forward.