// pantunes.dev · design system
Design System
The tokens, type, components and accessibility decisions behind this site. Everything here is rendered with the same stylesheet the site uses, and the colors and contrast ratios are read straight from the source tokens at build time, so this page can’t drift from the real thing.
Palette: Monokai · Stack: SCSS + custom properties · Target: WCAG 2.2 AA
Color
A dark Monokai base: warm near-black grounds, off-white text with hue-matched greys, and four saturated accents used sparingly. Green marks interaction and focus, cyan is the primary action, orange is secondary information.
Accents
-
--accent#a6e22e -
--accent-btn#66d9ef -
--accent-ink#1a1a1a -
--green#a6e22e -
--cyan#66d9ef -
--orange#fd971f -
--pink#ff6b9d
Backgrounds
-
--bg-hero#272822 -
--bg-about#20211b -
--bg-contact#20211b -
--bg-card#31322b -
--bg-terminal#1b1a17
Text
-
--white#fff -
--ink#f8f8f2 -
--ink-2#cfcec3 -
--ink-3#c9c8bd -
--ink-4#a8a99c -
--ink-muted#b1a993
Borders
-
--border#3a3b32 -
--border-2#41423a -
--border-hov#5a5b50
Effects
-
--red#f92672 -
--black#000 -
--bg-video#020205
Window chrome
-
--chrome-top#3a3833 -
--chrome-bottom#322f2a -
--chrome-title#b9b6a8 -
--chrome-ink#d7d6cb -
--dot-close#ff5f57 -
--dot-close-ink#511a17 -
--dot-min#febc2e -
--dot-max#28c840
Contrast
Every text token against every background token, computed with the WCAG 2.x relative-luminance formula. AA needs 4.5:1 for body text and 3:1 for large text (24px, or 18.7px bold). Pairs marked AA Large are only used for headings or UI accents, never for body copy.
| Text \ Background | --bg-hero | --bg-about | --bg-card | --bg-terminal |
|---|---|---|---|---|
--white |
Aa 14.86:1 AAA | Aa 16.22:1 AAA | Aa 12.95:1 AAA | Aa 17.40:1 AAA |
--ink |
Aa 13.94:1 AAA | Aa 15.22:1 AAA | Aa 12.15:1 AAA | Aa 16.33:1 AAA |
--ink-2 |
Aa 9.39:1 AAA | Aa 10.25:1 AAA | Aa 8.18:1 AAA | Aa 11.00:1 AAA |
--ink-3 |
Aa 8.83:1 AAA | Aa 9.64:1 AAA | Aa 7.70:1 AAA | Aa 10.34:1 AAA |
--ink-4 |
Aa 6.24:1 AA | Aa 6.81:1 AA | Aa 5.44:1 AA | Aa 7.31:1 AAA |
--ink-muted |
Aa 6.35:1 AA | Aa 6.92:1 AA | Aa 5.53:1 AA | Aa 7.43:1 AAA |
--green |
Aa 9.58:1 AAA | Aa 10.46:1 AAA | Aa 8.35:1 AAA | Aa 11.22:1 AAA |
--cyan |
Aa 9.01:1 AAA | Aa 9.84:1 AAA | Aa 7.85:1 AAA | Aa 10.55:1 AAA |
--orange |
Aa 6.81:1 AA | Aa 7.43:1 AAA | Aa 5.93:1 AA | Aa 7.97:1 AAA |
--pink |
Aa 5.55:1 AA | Aa 6.06:1 AA | Aa 4.84:1 AA | Aa 6.50:1 AA |
Component pairs
| Where | Text | Background | Ratio |
|---|---|---|---|
| Primary button (CTA) | --accent-ink |
--accent-btn |
Aa 10.56:1 AAA |
| Submit button | --bg-hero |
--accent |
Aa 9.58:1 AAA |
| Text selection | --bg-hero |
--pink |
Aa 5.55:1 AA |
| Input placeholder | --ink-muted |
--bg-card |
Aa 5.53:1 AA |
| Skip link | --bg-hero |
--accent |
Aa 9.58:1 AAA |
Typography
Three self-hosted families. Space Grotesk carries headings and UI, Hanken Grotesk carries long-form reading, and Space Mono carries labels, code and the terminal voice. Display sizes scale fluidly with clamp().
20 years building a web that works for everyone.
About me
What I do
Code, accessibility, tight deadlines, demanding clients — different facets of the same craft.
// In 1999 the web became my playground; in 2006, my profession. Body copy stays near 65 characters per line for comfortable reading.
// 20 years · web · html · css · js · a11y
Spacing & layout
Content sits in a single 1280px column with a 20px gutter. Sibling groups are laid out with flex or grid and gap, never per-element margins. Section rhythm is fluid, and wide media (the hero video, the work band) caps at 1520px.
| Value | Use |
|---|---|
--max-w: 1280px | Content column |
--px: 20px | Side gutter at every width |
1520px | Max width of full-bleed media |
clamp(50px, 10vw, 100px) | Vertical section padding |
gap: 16px · 24px · 80px | Cards · stacks · two-column layouts |
radius: 3px · 4px · 8px · 10px | Inputs & tags · cards & chips · CTA · terminal window |
500 · 560 · 700 · 860px | Breakpoints: compact hero · skills grid collapses · hero stacks and nav hides · about stacks |
Components
Live components with the site’s real classes. Hover them, or press Tab to walk through their focus states.
Buttons
.btn-cta · .btn-submit · .btn-submit:disabled — min-height 44px
Chips, tags & small controls
.agent-chip · .exp-tag · .client-role · .site-nav-lang · .clients-toggle
Form fields
.form-group · .field-label · [aria-invalid="true"] + .field-hint — errors are announced with role="alert" on submit
Cards
-
A11y auditingReports free of false-positive noise, prioritised by real impact.
- sbar.com.br SBAR concept → deploy direct client
- tohowater.com Toho Water Authority tech leadfront-enda11y while at Taoti
.skill-card · .client-card.is-own · .client-card.is-taoti — origin is stated in text, not only color
Terminal
.modal-box · .sh-bar · .sh-line + .sh-g .sh-c .sh-o .sh-d .sh-w — role="dialog", aria-modal, Esc to close, focus returns to the trigger
Motion
Motion is short, eased out and tied to a moment: a heading types itself when its section arrives, content rises into place, and only two things loop. Every animation has a reduced-motion path.
| Pattern | Timing | Reduced motion |
|---|---|---|
| Heading typing | 30–50ms / char (hero) · 55–85ms (sections) | Full text shown at once |
| Reveal | opacity + translateY(8px) · .5s ease · 150–300ms stagger | Everything visible at load |
| Hover lift | translateY(-5px) · .22s | No lift |
| Cursor blink | 1.1s step-end ∞ | — |
| Agent-ready pulse | 2s ease-out ∞ | Static ring |
| Work band | 60s linear ∞ | Starts paused; pause button always present |
| Easing | --ease-out: cubic-bezier(0.25, 1, 0.5, 1) | — |
Accessibility
Accessibility is part of the system, not a pass at the end. These are the rules every component follows.
- 1.4.3Body text meets 4.5:1 on its background. The contrast matrix above is the source of truth.
- 1.4.1Color never carries meaning alone: card origin, form errors and required fields are also stated in text.
- 2.4.7Every interactive element has a visible focus ring, 2px, in the accent of its context.
- 2.4.11Focused elements are never hidden: the work band stops and scrolls the focused card into view.
- 2.2.2Anything that moves on its own for more than 5 seconds can be paused, and the choice is remembered.
- 2.3.3prefers-reduced-motion is respected by every animation.
- 2.5.8Primary controls are at least 44px tall; small controls are at least 32px.
- 4.1.2Native elements first: real buttons, links, labels and lists; ARIA only where HTML has no equivalent.
How it’s built
The stack behind this site, kept deliberately small.
- Pages
- PHP templates rendered to static HTML at build time, in English and Portuguese.
- Styles
- SCSS with CSS custom properties. The same tokens drive this page.
- JavaScript
- Vanilla JS bundled with webpack. No framework.
- Hosting
- Vercel, deployed on every push to GitHub, with a preview for each branch.
- Functions
- The contact form and the MCP server run as Vercel Functions.
- For agents
- llms.txt, resume.json, AGENTS.md and an MCP server at
/api/mcp. - Security
- Strict Content Security Policy with no inline scripts, same-origin form endpoints, and rate limits.
- Accessibility
- Audited with axe-core against WCAG 2.2 AA, with contrast on gradients checked by hand.
- Tools
- Built with Claude Code as a pair programmer.