01. Why I built it

Figma kept lying to me. So I moved the source of truth into code.

Every Pinnlo screen I drew in Figma looked good on the canvas and fell apart in the browser. Spacing drifted, states were missing, hover behaviours were implicit. I stopped trusting Figma as the source of truth and built a design system in HTML instead. Every component rendered, interactive, and ready to copy into the real product.

24

Components across buttons, cards, inputs, surfaces, and nav. Each with states.

6

Categories: colour, type, space, motion, components, patterns.

1

Source of truth. The HTML is the spec, the prototype, and the export, all at once.

Live components + source

Buttons

3 variants · ripple hover
buttons.csscss
.btn {
  position: relative;
  min-width: 97px;
  min-height: 44px;
  padding: 0 16px;
  border-radius: 6px;
  font-family: 'Space Grotesk', sans-serif;
  font-size: 12px;
  font-weight: 500;
  overflow: hidden;
}

.btn .ripple-grid {
  position: absolute;
  inset: 0;
  display: grid;
  grid-template-columns: repeat(10, 1fr);
  grid-template-rows: repeat(5, 1fr);
  pointer-events: none;
}

@keyframes cellRipple {
  0%  { background: rgba(251,146,60,0.5); }
  40% { background: rgba(244,114,182,0.4); }
  70% { background: rgba(147,197,253,0.3); }
  100%{ background: transparent; }
}

Three variants with a 10×5 ripple grid that radiates Orange → Pink → Blue from the cursor on hover.

Strategy Card

190×254 · ripple grid

Strategy Name

An app for strategy…

Created by: Maria, Dec 9

Last Updated: Dec 11, 4:25pm

strategy-card.csscss
.strategy-card {
  width: 190px;
  height: 254px;
  background: #ffffff;
  border-radius: 12px;
  box-shadow: 0 4px 20px rgba(0,0,0,0.08);
  border: 1px solid rgba(0,0,0,0.08);
  overflow: hidden;
  position: relative;
}

.content-inner {
  position: absolute;
  inset: 8px;
  background: rgba(255,255,255,0.85);
  border-radius: 8px;
}

.grid-bg {
  background-image:
    linear-gradient(rgba(0,0,0,0.03) 1px, transparent 1px),
    linear-gradient(90deg, rgba(0,0,0,0.03) 1px, transparent 1px);
  background-size: 16px 16px;
}

The primary card. Grid background behind a frosted inset, with the signature ripple hover animating through the cells.

3D Box Loader

8 boxes · isometric · pure CSS
loading.csscss
.loader-3d {
  width: 200px;
  height: 320px;
  transform-style: preserve-3d;
}

.loader-box div {
  width: 48px;
  height: 48px;
  transform: rotateY(-47deg) rotateX(-15deg)
             rotateZ(15deg) scale(0);
}

.box0 div, .box3 div, .box6 div { background: var(--orange); }
.box1 div, .box4 div, .box7 div { background: var(--pink); }
.box2 div, .box5 div            { background: var(--blue); }

Eight boxes fly in from offscreen, stack in isometric perspective, then drop through a lit ground plane. 3s cycle, zero JavaScript.

02. How I worked

From Figma to code, step by step.

  1. 01
    Audited the UI surface

    Screenshotted every Pinnlo screen. Counted buttons, cards, inputs. Most were subtly different. That was the problem to solve.

  2. 02
    Explored in Figma first

    Set colour, type, and spacing tokens as variables. Built component variants. Stress-tested states, density, dark mode.

  3. 03
    Translated each component into HTML

    Took the Figma spec and rebuilt it in raw HTML + CSS. No framework. Tokens became CSS custom properties.

  4. 04
    Interactive states in the browser, not Figma

    Hover, focus, active, disabled, loading, written once in CSS and reused. Figma can't show these honestly; code can.

  5. 05
    Ported components into React

    Each HTML prototype became a React component in Pinnlo. Same tokens, same names, same behaviour.

  6. 06
    Kept the HTML library as the reference

    When something drifts in production, I open the HTML library and copy it again. It's the canonical version.

03. Design × engineering

HTML is a better Figma than Figma.

Figma is a drawing tool pretending to be a spec. Browsers are a spec pretending to be nothing. When the design system lives in HTML, there's no translation layer and no drift: the prototype is the spec, the spec is the demo, and the demo is what ships. Tokens defined once in CSS custom properties propagate through every surface (light mode, dark mode, Pinnlo brand, client brand) without any rebuild.

04. What I learned

Building a design system taught me to design less.

Most of the job wasn't designing components. It was deleting them. Every near-duplicate button, card, or input was a decision I'd failed to make earlier. Pruning the system forced me to commit to one answer per problem.

Tokens are the most important thing in a design system, and they're invisible. Naming them well (--surface-muted, not--grey-200) is what lets the system outlive the brand it was built for.

The best proof a system works is how fast you forget it's there. By month three, I was shipping Pinnlo features without thinking about components at all, which is exactly the point.