Style guide
Last updated
Style guide 1.0
The system behind aitormurillo.com. Every value is read from the code as of 1 Oct 2026 (frontend/, Nuxt 4 + Tailwind v4).
Audience: designers and front-end developers. Each rule gives the design value first and, where one exists, the token, utility or Vue component that implements it, in Mute.
Principles
One size, one weight.
Hierarchy comes from position on the grid, case and colour, never from scale.
Text is the interface.
Every control is a word: no buttons with fills or borders, no icons. The only glyphs are + (rotated 45° for Close), ×, ↗, ↑, ⌘K.
Light, with colour from the work.
White ground, black type, one grey, one blue, one hairline grey. All other colour comes from project media.
Everything starts on a column edge.
The header’s five blocks are the master columns for the whole site.
Motion has two curves.
Reveal, for masks and slides; settle, for entrances and zoom.
Hover is an enhancement.
Hover effects exist only on devices that can hover; keyboard focus mirrors them where they carry meaning.
Type
Aitor Murillo
Geist Mono Regular, 12/16, everywhere. Hierarchy comes from position, case and colour.
ABCDEFGHIJKLMNOPQRSTUVWXYZ
abcdefghijklmnopqrstuvwxyz
0123456789 àáçèéíïòóúüñ ·,.:;!?¿¡()“”‘’
Ŀŀ ı Œœ € ™ + × ↗ ↑ ← → ⌘K
Running text · CommonTextContent
Every control is a word. Paragraphs are 16px apart and the measure is four columns, about 45–60 characters.
- Bullet lists use a disc,
- with a 2ch indent.
Inline links are always underlined, because blue alone is too close to black.
Family
Geist Mono, regular, self-hosted and subset (Latin, Latin-1, Ŀŀ ıŒœ €™ and arrows)
--font-primary · /fonts/GeistMono-Regular.woff2, preload, font-display: swap
Size / line height
12px / 16px, everywhere
--text-base / --text-base--line-height
Weight / tracking
400 / 0
Wrapping
text-wrap: pretty on everything
body
Fallback
ui-monospace, monospace (also covers glyphs outside the subset, e.g. Japanese)
--font-primary
UPPERCASE
The logo, project names (always: list, cards, caption, project title on mobile), language codes, the error page.
Sentence case
Everything else: header copy, body, labels, credits, controls.
Line breaks
Kept where a CMS field allows them: tagline, address, stack.
whitespace-pre-line
Truncation
SINGLE LINE AND AN ELLIPSIS FOR LIST NAMES
List-row names and credits on desktop, card names. The credit revealed on card hover wraps in full.
Colour
Ground
#FFFFFF
--color-white
On black 21:1
Ink
#000000
--color-black
On white 21:1
Mute
#767676
--color-gray
4.54:1 on white, 4.6:1 on black (AA)
Link / active
#0000EE
--color-blue
9.4:1 on white, 2.2:1 on black: not for text on black
Hairline
#E6E6E6
--color-gray-light
Rules and media frames, never text
Active
Blue: current main-nav item, active language, active word in the archive’s view toggle. On the home the active language stays white.
Inactive
Mute: inactive languages, inactive view word, other pages in the mobile menu. Opacity is never used for inactive states.
Inactive → hover
Goes to the surrounding text colour: black on white, white on the home. Text buttons go Mute → black.
Dim
Opacity 0.2, only to push context back: non-hovered list rows; the project page and header block 2 behind More projects.
Missing media
A solid black block. Every media frame has a black ground.
Selection
Blue background, white text.
Grounds
Home: black. Every other page: white. The ground changes colour during the page transition, never by a jump.
Over imagery
Home only. Top gradient 180px, rgba(0,0,0,.32) → transparent. Bottom gradient 200px, transparent → rgba(0,0,0,.36). Labels on media elsewhere use a white block behind black text, never a scrim.
Grid, spacing and layout
12 columns on desktop, 6 below 768px (Tailwind’s md). 20px side margin and 10px gutter at every size. Nested layouts reuse the parent’s tracks with grid-cols-subgrid, so a nested column always lands on a page column.
container + main-grid · main-gap
1 · Logo
1–2
2 · Per-page copy
3–6, the only block that changes
3 · Main nav
7–8
4 · Secondary nav, languages
9–10
5 · Clock
11–12, right
Spacing, in multiples of the 4px unit
4
Horizontal padding of a white label on media
px-1
8
Vertical padding of table and list rows
py-2
10
Gutter; label inset on cards; gallery gap; mobile row padding top
2.5
16
Paragraph gap; gap between banner rows
4
20
Side margin; header padding and row gap; fixed elements’ distance from the edge
5
24
Gap between groups in the project text column
6
32
Footer row gap on mobile
8
40
Gap between groups in the mobile menu; project text to its URL
10
48
Section gap on mobile (About); title-to-body in legal and project
12
96
Section gap on desktop (About); footer row gap on desktop
24
120
Space under the content before the footer
pb-30
Where content starts (all pages but the home)
Desktop
32vh
Mobile
172px (the archive’s two-row header + 48px)
--page-content-top. pt-page-content subtracts the measured --header-height. The project’s fixed desktop column uses --page-content-top directly. This page starts at 32vh.
Layers
50
Cookie banner
10
Header; More projects button and carousel; home caption
5
Home gradients
3
Hovered list row, above the preview
2
List preview layer; current home slide
1
Previous home slide during a wipe; the shown list preview
Header
Fixed to the top, full width, 20px vertical padding, 20px between rows. Its height is measured and published as --header-height before first paint. The header of this page is the Legal variant.
CommonHeader
Home · transparent, white type, tagline in block 2
Site tagline from Site Global, set on two lines.
- Archive
- About
About · white, address in block 2, current page blue
Address from site settings 08000 Barcelona
- Archive
- About
Project · transparent, title and Close in block 2, Close top-aligned with the logo and title, blocks 3–5 hidden. Block 2 dims to 0.2 (500ms) while More projects is open.
Mobile · logo 1–3, Menu 4–6 right. Only the Archive adds a second row. The menu is a modal dialog; the page behind is blurred 8px with no grey veil.
Change view from listto grid
Search
Site tagline from Site Global, set on two lines.
- Home
- Archive
- About
Address from site settings 08000 Barcelona
Clock
Two lines, Barcelona time (Europe/Madrid), formatted in the active language, updated on the minute.
CommonHeaderClock
Mobile menu
Content from 24vh down. Any link closes it; it also closes when the window grows past 768px. Close is focused on open.
CommonHeaderMenu · --spacing-content-top
h1
The home’s site name is the page’s h1; elsewhere it is a link.
Components
Text link
Black → blue on hover. Current page blue. External links open in a new tab; in footer menus they carry ↗ on the right.
CommonLink · CommonHeaderNavLink
Inline link
Blue and always underlined in running text. Standalone blue links (the project URL, “View programme ↗”) underline on hover only.
CommonContentLink
Text button
Mute → black on hover, 200ms. The inactive view word, “× Clear” and “Clear search”.
CommonTextButton
Close
+ rotated 45°, then “Close”. Black, hover blue. Project, mobile menu, More projects, cookie settings.
CommonCloseLabel
Language switcher
Uppercase, separated by a space. Active blue (white on the home), inactive Mute, hover to the surrounding colour. Each link carries lang and hreflang.
I18nSwitcher
Credits
Design by Aitor Murillo / Collab with Studio Norte
On the project page, “in Collaboration with Y” on its own line. Collaborators with a website link to it (Mute → blue, new tab). Categories read “Type, Sector”. Empty fields are omitted, never replaced by a dash.
ProjectCollaborators
Table row
1px hairline on top, 8px vertical padding. About (services, collaborators, teaching) and the footer menus. Every row on this page is one.
Archive controls, list rows and grid cards · interactive. Hover a row or a card; try the search (⌘K).
Full archive, 8 projects.
Change view from grid to
- Archive
- About
- Atlas EditorialDesign by Aitor Murillo
- ObradorDesign by Aitor Murillo / Collab with Studio Norte
- Mare NostrumDesign by Aitor Murillo
- HalcyonDesign by Aitor Murillo / Collab with Estudi Pla
- VermellDesign by Aitor Murillo
- Lumen FundacióDesign by Aitor Murillo / Collab with Studio Norte
- Cal SastreDesign by Aitor Murillo
- Ona RecordsDesign by Aitor Murillo
Archive controls
Count announced via aria-live. View toggle: current view blue, the other a text button; default grid. Search: borderless, max 320px, blue caret, no focus outline; ⌘K hint becomes Esc while focused, × Clear with a query. Live, accent-insensitive, every word must match, across name, year, design, collaboration, type and sector. Search and view last for the visit and aren’t in the URL.
ArchiveControls
List row
Year 1–2 · NAME 3–6 · Design by 7–9 · Type, Sector 10–12. Mobile: year col 1, the rest stacked on 2–6, 10px top and 12px bottom padding. On hover or focus the other rows dim to 0.2 (250ms), the row drops its rule and its words get white blocks, and the first gallery image shows fixed in the viewport centre on cols 4–9, uncropped.
ArchiveProjectList
Project card
The project’s gallery frame: 4:3 on black, the first gallery image at 66% width and its own ratio over a blurred copy. NAME on a white label 10px from the bottom-left. 4 columns wide on desktop (3 per row), full width on mobile. At rest a grayscale image on a gray-light field; hover or focus fades the blurred copy in and brings the colour back with the zoom; colour and blur on touch. The More projects carousel uses the same card. On hover or focus the image zooms to 1.06 over a still blurred copy (500ms, settle) and the label grows to reveal the credit (450ms, settle). Siblings don’t dim. Mobile shows the NAME label only.
ArchiveProjectGrid · ProjectCard
Cookie banner · interactive. Fixed to the bottom on the site; shown in place here.
Cookies
Copy from Site Global → Cookie Banner Text, with a link to the cookie policy.
Cookie banner
Full width with the 20px margins, white, hairline on top, 20px vertical padding, no shadow. The analytics switch is Off by default; the current word is black. “Settings” becomes “× Close”, “Accept” becomes “Save preferences” once the switch changes. Never taller than the screen: only the settings list scrolls, without a visible scrollbar. Mobile: label, text, then the three actions on two columns each.
CommonCookieBanner
Footer
Closes Archive, About and Legal; never on the Home or a Project. The footer of this page is the specimen. Mobile: everything stacked on 1–6, the two menus read as one table, 32px between rows, “Last updated” 1–4 and “Top ↑” 5–6.
CommonFooter · CommonLastUpdated
Pages
Home
Full screen, no page scroll, black ground, one featured project per gesture. Media at full width of cols 3–10 on desktop, 1–6 on mobile, at its own ratio (never cropped). Behind it a full-bleed blurred copy (120%, blur 60px, brightness 0.8). Caption 20px from the bottom, white.
HomeFeaturedProjects · HomeProjectCaption · CommonMediaBlur
Archive
Controls in header block 2; the grid (default) or the list. Empty search: “No projects match “query”.” in Mute, then “Clear search”.
Project
Desktop holds still. Text on 3–5: description; credits, categories (links, one per line) and stack in Mute; groups 24px apart; URL, without a label, pinned to the bottom. Gallery on 7–12, an infinite vertical loop, wheel anywhere scrolls it. Year in Mute under the NAME. Mobile: one column, NAME and year on top, gallery below without the loop. Close returns to the Archive with its search and view.
ProjectGalleryFrame
More projects
“+ More projects” fixed 20px from the bottom-left (white block on mobile), “+ Close” while open. Carousel 52px from the bottom: 3 cards visible on desktop, 2 on mobile, infinite loop from the next project. Page and block 2 dim to 0.2. Closes with Close, Esc or a click on the dimmed page.
ProjectMoreProjects
About
Mute label on 1–2, content beside; sections 96px apart (48px on mobile). Intro 3–6, portrait 4:3 on 9–12. Services rows alternate 4 and 6 columns wide. Collaboration: every collaborator, alphabetical, down 4 columns, with a two-digit count in Mute, linking to the filtered Archive; those without projects in Mute, not links. Biography 3–6. Teaching: a definition table, then “View programme ↗”. Contact 3–6, address in Mute on 9–10.
Legal
Running text on 3–6: paragraphs, bullet lists and inline links only. Desktop: title and “Last updated” in header block 2. Mobile: they open the content, text 48px below.
Error
No header or footer. Centred, uppercase. The link stays in the visitor’s language. Other errors: “Sorry, something went wrong. Please try again later.”
404
The page could not be found.
Media
Home frame · the media’s own ratio
8 of 12 cols / full width
List preview · the gallery frame
6 of 12 cols; the media 2/3 of that
Gallery frame · 4:3, media at its own ratio (never cropped), 66% wide, over a blurred copy (40px)
2/3 of 6 cols / 2/3 of the width; also the project card in the archive grid and More projects, 4 of 12 cols. A video sits on the gray field instead.
About portrait · 4:3
4 of 12 cols / full width
Cover
None: the first gallery item stands in for it on the home, the archive list and grid, More projects, and as the share image when the project’s SEO has none.
Images
Sanity CDN, srcset 320–2400px, never wider than the original. Each use declares its width in sizes. Lazy by default; the home’s first slide and a project’s first image load with high priority.
CommonMediaImage
Video
Muted, looping, inline, no controls, object-fit: cover, same frame as the image. The image underneath is the poster and carries the alt text; the video is hidden from assistive tech. Cards play only while in view.
CommonMediaLoop · VideoLoop
Missing media
Leaves the frame’s black ground.
Share image
1200×630, cropped around the CMS hotspot.
Motion
Reveal
--ease-reveal
cubic-bezier(0.76, 0, 0.24, 1)
Masks (wipes) and slides
1200ms wipe bottom → top + zoom 1.14 → 1 over 1800ms
2026
ATLAS EDITORIAL
Design by Aitor Murillo
Settle
--ease-settle
cubic-bezier(0.2, 0.7, 0.2, 1)
Entrances, zoom, label reveals
Home caption: rise 8px and fade in over 600ms
Durations
--page-transition-duration 300ms: page fade and every colour that changes with it. Tailwind default 150ms, cubic-bezier(0.4, 0, 0.2, 1), for link colour without an explicit duration.
Masks
clip-path: inset() wipes. clip-to-top → clip-none wipes downward from the top edge; clip-to-bottom → clip-none wipes upward from the bottom edge.
utils/wipe.js · wipeIn, wipeOut, zoomIn
Page change
Outgoing page fades out 300ms, then the new one fades in 300ms
Header ground, type colour and body ground fade with the fade-out. The header’s height is held while a page leaves.
Home slide
Clip wipe 1200ms reveal; media zooms 1.14 → 1 over 1800ms; blurred field crossfades 1400ms
Forward wipes bottom → top, back top → bottom. Caption out 180ms ease-in; new one rises 8px and fades in 600ms settle, from 450ms. Input locked during the wipe.
Archive entrance
Items rise and fade in turn (settle). List 650ms, 30ms step, 8px. Grid 900ms, 60ms step, 16px
The first screen in CSS from first paint and on each view change; the rest rises as it scrolls into view, each batch in turn. Delay capped at 1000ms per batch. Filtering doesn’t replay it.
Page entrance
Blocks rise and fade in turn (settle). 900ms, 60ms step, 16px, as the Archive’s grid
About, Legal and the Style guide, each heading and block of text; the About portrait keeps its own wipe. First screen and scroll as in the Archive.
List preview
In: wipe 620ms reveal + zoom 1.1 → 1 over 1000ms. Out: wipe top → bottom 520ms
Down the list wipes up, up wipes down. 110ms dwell; one wipe at a time. Undecoded images wait up to 400ms. Row dim 250ms.
Card hover
Media 1 → 1.06, 500ms settle; credit row 0fr → 1fr, 450ms settle
Frame size never changes.
More projects
Cards rise from below the screen, 800ms cubic-bezier(.7,0,.2,1), 70ms step
Close falls in reverse at 35ms. First 8 cards stagger. Page dim 500ms.
About portrait
Wipe bottom → top 1200ms reveal + zoom 1.14 → 1 over 1800ms
Once the image has loaded or failed.
Mobile menu
Panel wipes top → bottom 700ms reveal; reversed on close
The blur behind fades over the same 700ms.
Cookie banner
Wipes up from the bottom edge 700ms reveal; back down on dismiss
Also on first appearance.
Cookie settings
Row grows 0fr → 1fr while the list wipes top → bottom, 700ms reveal
The rest of the banner rises with it.
Colour hovers
150ms default; 200ms on text buttons and footer rows
Reduced motion
With prefers-reduced-motion: reduce, every wipe, zoom, stagger, rise, carousel animation and smooth scroll is skipped: things appear and disappear in place. Colour fades remain.
Interaction
Home
One project per gesture: wheel or vertical swipe past 30px (gesture ends after 160ms idle), or ↑ ↓ ← →.
Archive
⌘K / Ctrl+K focuses the search (not past an open dialog); Esc clears it and leaves.
Project
The wheel anywhere scrolls the gallery (desktop). Esc closes More projects, then the project.
More projects
Vertical wheel scrolls the carousel sideways. Focus moves to the first real card on open and back to the toggle on close.
Touch / no hover
No list dimming or preview, no card zoom or credit reveal.
URLs
/, /archive/, /about/, /archive/{slug}/, /legal/{slug}/, localised under /es/ and /ca/ (e.g. /es/archivo/). Search and view aren’t in the URL; Back works.
Cross-links
A category on a project, or a collaborator on About, opens the Archive with the search set to it.
Accessibility
Contrast
All text meets WCAG AA (Mute 4.54:1 on white, 4.6:1 on black). Blue is never text on black. The hairline grey is never text.
Links
Inline links are underlined; colour is never the only cue inside running text.
Semantics
nav for menus, dialog for the mobile menu, time with datetime for years and dates, dl for teaching, role="switch" for cookie toggles.
Headings
One h1 per page: the site name on the home, visually hidden where the header already shows the title.
Hidden but present
Folded or off-screen UI is inert (closed cookie settings, closed carousel). Decorative media is alt="" or aria-hidden. Hidden home captions remain readable.
Alt text
From the CMS; otherwise the project’s title in galleries and on the home, the name on the About portrait, and the file name as a last resort.
Focus
Mobile menu focuses Close; cookie settings the first switch (without scrolling); More projects its first card; Back to top the first link.
Live regions
The archive count is announced as the search changes.
Keyboard parity
List previews and card reveals react to focus as to hover.
Language
<html lang> follows the locale; language links carry lang and hreflang.
Open item
There is no visible focus ring: :focus-visible uses a transparent outline, which forced-colors mode still draws.
Not yet built
Open item
There is no “skip to content” link.
Not yet built
Implementation
Tokens and utilities
--font-primary
font
Geist Mono, ui-monospace, monospace
--text-base, --text-base--line-height
type
12px / 16px
--color-black · white · gray · gray-light · blue
colour
#000 · #FFF · #767676 · #E6E6E6 · #0000EE
--ease-reveal, --ease-settle
easing
cubic-bezier(0.76,0,0.24,1) · cubic-bezier(0.2,0.7,0.2,1)
--page-transition-duration
duration
300ms
--page-content-top
layout
172px / 32vh
--header-height
layout (runtime)
Measured header height
--spacing-content-top
spacing
24vh, mobile menu content offset
container
utility
Max-width 100%, 20px side padding
main-grid
utility
6 / 12 columns, 10px gap
main-gap
utility
10px gap
pt-page-content
utility
Content top minus header height
clip-to-top, clip-to-bottom, clip-none
utility
Mask states
stagger-rise
utility
Children rise in turn (--stagger-*; --index or their place among siblings; data-own-entrance opts out)
scrollbar-none
utility
Scrolls without a visible bar
scripted:
variant
Applies only when JavaScript runs
can-hover:
variant
Applies only on devices that can hover