Toggle to dark or light mode
Change languageComing soon: MADS is in English only

Design system · v0.1 draft

MA Design System (MADS)

The shared foundation for my projects on web, iOS and Android. Every token has one exact name, so a person or an AI agent can point to it without ambiguity.

Spacing

mads.space

The spacing scale used for padding, margins and gaps between elements. It sits on an 8px grid and doubles at each step from 8px upward.

Namingmads.space.{0–8}

Major grid · 8px

Always pick a step from the scale first. Steps 2–8 are 8px multiples.

Minor grid · 4px

When no step fits, use a multiple of 4px (12, 24, 48…). Use mads.space.1 for tight gaps such as icon to label.

Units

1 web px = 1 iOS pt = 1 Android dp. Web uses rem at a 16px root, so spacing follows the user's text size.

TokenWebiOSAndroidScale

Fonts

mads.font

Three typefaces from Google Fonts, one per role. If Google Fonts can't load, for example on a network that blocks it, each role falls back to fonts already on the device.

Namingmads.font.{display | body | mono}
Showing Google Fonts
Headings mads.font.display

Spacing on an 8px grid

Unbounded

Fallback Arial Rounded → Trebuchet MS → sans-serif

Text mads.font.body

Every token has one exact name, so a person or an agent can point to it.

Onest

Fallback Segoe UI → system font

Labels mads.font.mono

mads.space.4 = 32px

JetBrains Mono

Fallback SF Mono → Menlo → Consolas → monospace

TokenTypefaceWeightsWebiOSAndroid
mads.font.displayHeadings UnboundedFallback: SF Pro Rounded 200–900 --mads-font-display MadsFont.display() MadsFont.display@font/mads_display
mads.font.bodyText OnestFallback: SF Pro 100–900 --mads-font-body MadsFont.body() MadsFont.body@font/mads_body
mads.font.monoLabels JetBrains MonoFallback: SF Mono 100–800+ italics --mads-font-mono MadsFont.mono() MadsFont.mono@font/mads_mono

Web

Loaded from Google Fonts with display=swap, so text shows straight away in the fallback font and switches once the font arrives.

iOS

Bundle the font files in the app. If one is missing, the code uses the matching Apple font: SF Pro Rounded, SF Pro or SF Mono.

Android

Downloaded through Google Play services. Until they arrive, or if they can't, Android uses its own sans-serif and monospace fonts.

Text

mads.text

Four sizes for text, labels, captions, footnotes and all other copy that isn't a heading. Each style sets a font size and a line height, and uses mads.font.body unless you pair it with another font.

Namingmads.text.{small | default | large | xlarge}
Click any sample to type your own text
TokenSize / lineWebiOSAndroid

Labels

For labels, use the Labels styles instead. They use mads.font.mono in capitals.

Scaling

Sizes follow the user's text size setting: rem on web, Dynamic Type on iOS and sp on Android. Each iOS style scales with the Apple style of the same size.

Units

Sizes are given in px. That is pt on iOS and sp on Android, where text uses sp rather than dp.

Headings

mads.heading

Seven heading sizes, from a hero title down to the smallest section heading. Every heading uses mads.font.display (Unbounded) in bold. The hero size grows with the screen width.

Namingmads.heading.{xsmall | small | default | large | xlarge | xxlarge | hero}
Click any sample to type your own text
TokenSize / lineHTMLWebiOSAndroid

Weight

All sizes use Bold (700), set once in --mads-heading-weight. Hero is ExtraBold (800) with −0.03em tracking.

HTML levels

The suggested level keeps the page outline in order: xxlarge for h1 down to xsmall for h6. hero is also an h1, used once at the top of a landing page instead of xxlarge. Choose the level by structure and the size by look.

Units

Sizes are given in px: rem on web, pt on iOS and sp on Android, so headings follow the user's text size setting.

Labels

mads.label

Labels work as secondary headers or highlights that add context or extra information. They use mads.font.mono (JetBrains Mono) and are always bold and in capitals.

Namingmads.label.{xsmall | small | default}
Click any sample to type your own text

In use

Foundations

Spacing on an 8px grid

New Web iOS Android
TokenSize / lineWebiOSAndroid

Capitals

Write the label in normal case and let the style capitalise it. Screen readers then read words, not letters.

Weight

Labels are always Bold (700), at every size. It is set once in --mads-label-weight.

Units

Sizes are given in px: rem on web, pt on iOS and sp on Android, so labels follow the user's text size setting.

Colours

mads.color

Nine colours: four neutrals for text and structure, and five bright accents. Each swatch shows how readable white and Dark text are on top of it. Click a swatch to copy its hex value.

Namingmads.color.{name}

Neutrals

Accents

TokenHexRGBWebiOSAndroid

Contrast

AA means normal text is readable (4.5:1 or more). Large means only text from 24px, or 19px bold, is readable (3:1 or more). Below 3:1, use the colour for fills and shapes, not text.

Same everywhere

Hex values are sRGB and identical on web, iOS and Android. They don't change between light and dark mode yet.

Names

Tokens use lowercase words joined by hyphens, so AmberGold is mads.color.amber-gold. Swift and Kotlin use amberGold.

Themes

mads.theme

Colour roles that switch between light and dark mode. Components use these roles instead of fixed colours, so they follow the theme. Use the switch at the top of the page to see every component in the other mode.

Namingmads.theme.{role}

Contrast is measured against each theme's background. AA means normal text is readable; Large means only text from 24px, or 19px bold.

TokenLightDarkUse for

Following the device

Without a choice, the theme follows the device setting. The switch overrides it and the choice is remembered.

Accent text

Bright accents are too light to read on a light background, so each has a darker text version for light mode. Use these for coloured text; use mads.color for fills.

Shared colours

Light text and muted text are mads.color.dark and mads.color.dark-1. The other values are new and only exist as theme roles.

Shape and depth

mads.radius · gradient · shadow

Corner radii, gradients and shadows, all on the 4px grid.

Namingmads.radius.{size} · mads.gradient.{name} · mads.shadow.{name}

Radius

Gradients

Where and how to use each one is in Gradients.

Product, people and code

Shadows

mads.shadow.glow
mads.shadow.raised
TokenValueUse for

Grid

Every radius is a multiple of 4, so corners sit on the same grid as spacing.

Gradient direction

All gradients run left to right. The brand gradient is for display text and decoration, not for backgrounds behind small text.

Glow

The glow is for the brand gradient controls only: the theme switch and the primary button on hover.

Gradients

mads.gradient

Where and how gradients are used. Gradients are the signature of MADS, so they are kept for a few jobs and always follow the same colour order.

Namingmads.gradient.{name} · mads.gradient.ambient

The spectrum

The five accents form one line, in this order. A gradient only joins neighbours on the line, in either direction. Amber to Pink skips Orange but keeps the warm end together, so it is allowed for frames.

1 · AmberGold
2 · BlazeOrange
3 · NeonPink
4 · BlueViolet
5 · AzureBlue
orange-amber orange-pink amber-pink · new pink-violet violet-azure brand · 1 → 4 amber-azure · not allowed

Where gradients go

1 · Action orange-pink

The main action and the theme switch. The only gradient on controls, and only one gradient action per screen.

Lorem ipsum dolor sit

2 · Highlight brand

One or two words of a heading, or a big stat. heading.large and up only, one highlight per heading, never body text.

MA

3 · Brand mark and frames amber-pink · orange-pink

The MA logo, the icon gradient and the 2px outline round the avatar. Frames run top to bottom; nothing else is outlined in a gradient.

Technology55%
People30%
Product15%

4 · Topics in data violet-azure · pink-violet · orange-amber

Each topic keeps its own pair, so a bar is recognisable before its label is read. See the topic table below.

5 · Ambient glow ambient

Two soft radial glows behind the page, top right and bottom left. Their colours follow the section being read and fade over 1.2s.

Sticky header

6 · Depth and scrims surface · surface-raised · scrim

Quiet fades between theme surfaces for large panels, and a fade under sticky headers so content slides out of sight. Neutrals only.

Topics

TopicSolid accentGradientAmbient glowTypical use

Do and avoid

Lorem ipsum dolor

DoHighlight the one phrase that carries the heading.

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.

AvoidGradient body text. It is hard to read and the highlight loses its effect.

DoJoin neighbours on the spectrum.

AvoidJumping across it. AmberGold to AzureBlue turns muddy grey-green in the middle.

DoPair one gradient action with secondary buttons.

AvoidTwo gradient buttons side by side. Neither reads as the main action.

TokenValueUse for

Direction

Left to right for controls, text and bars. Top to bottom for frames and the logo legs. 160° for surface fades. Ambient glows are radial.

How many

One gradient action and one highlighted phrase per screen. The ambient glow is always there, but stays at 16% in light mode and 22% in dark.

Contrast

Text on a gradient is only allowed on the primary button. White on the orange-pink gradient is below 4.5:1, so keep button labels short.

Motion

mads.motion

Durations and easing curves shared by every animated element, so the whole interface moves the same way. The values match the existing buttons and icons.

Namingmads.motion.duration.{fast | base | slow} · mads.motion.easing.{standard | spring | entrance}
TokenValueUse for

Pick the curve

Standard for state changes such as hover and colour. Spring for things that move into place, like the switch knob. Entrance for elements arriving on screen.

Scroll reveal

Sections rise 48px into place as they scroll in, with mads-reveal. Content is always visible where the browser does not support it.

Reduced motion

When the device asks for less motion, every animation stops and states change instantly.

Icons

mads.icon

Seven icons. The chevrons and Home are drawn from the chevron in the middle of the MA logo; Settings is a six-tooth gear with a centre dot; Ideas is a light bulb with a base line. Each is a single line on a 24px grid with the same 3.5 stroke, so the icons scale, take any colour and animate cleanly.

Namingmads.icon.{chevron.up | chevron.down | chevron.left | chevron.right | home | settings | ideas}
Colour
Stroke 3.5, fixed

Hover or tap a tile to play its motion. The small row shows each icon at 16, 24 and 32px.

In motion

Disclosure

One chevron turns 180° with a slight overshoot when a section opens.

8px grid with 4px minor steps: mads.space.0–8
24px frame, 20px live area, 3.5 stroke.

Chevron up to Home

Home is the up chevron with walls and a floor, so one shape grows into the other.

Pager

Left and right chevrons push in the direction of travel when pressed.

Draw on

Each icon is one continuous line, so it can trace itself in when a screen loads.

Construction

Frame24 × 24, with a 2px margin (20px live area)
mads.icon.stroke3.5 on the 24 grid, the same for every icon. Round caps and joins.
Chevron angle7 : 5 run to rise, the slope of the M in the logo
Chevron bounds17.5 × 8.5, centred
Home bounds17.5 × 18.5, centred, closed with a floor
Settings bounds17.6 × 18.3, centred. Six teeth (tips at radius 7.5), body arcs at radius 5.6, and a 3.5 centre dot drawn as a zero-length round-capped line
Ideas bounds11.3 × 18.6, centred. A round bulb (radius 4.76 around 12, 9.2) narrowing into a closed neck with rounded corners, then a 3.6-wide base line at y 19.6
ColourFollows the text colour (currentColor), so any mads.color works
TokenFileWebiOSAndroid

Buttons

mads.button

The primary button: a pill with a BlazeOrange to NeonPink gradient, White text in mads.text.large and no border. It is the first component built only from MADS tokens.

Namingmads.button.primary.{part}
On White
On Dark
Default

Gradient from left to right.

Hover

Gradient shifts slightly towards pink, with a soft glow.

Pressed

Shrinks to 98% while held.

Focus

NeonPink ring for keyboard users.

Disabled

40% opacity, no hover.

TokenValueNotes
mads.button.primary.backgroundblaze-orange → neon-pinkLeft to right.
mads.button.primary.textmads.color.whiteNo border.
mads.button.primary.fontmads.text.largeOnest 15 / 24. With the padding, the button is 48 tall.
mads.button.primary.radius999 (pill)Fully rounded ends at any height.
mads.button.primary.padding12 / 24Vertical / horizontal, on the 4px minor grid.
mads.button.primary.gapmads.space.2 (8)Between the text and an icon.
mads.button.primary.min-height44Minimum comfortable tap size on iOS.

Contrast

White on BlazeOrange is 3.26:1 and on NeonPink 3.83:1. Both are below the 4.5:1 needed for 15px text, so the label may be hard to read for some people.

Icons

An optional icon sits after the text, 1em in size and in the text colour. On hover it nudges 1px in the direction it points.

Motion

Hover takes 0.6s, press 0.08s. With reduced motion turned on, states change instantly.

Secondary, ghost, icon and round buttons

Paired with the primary button and built from the theme roles, so they work in light and dark mode.

Default

Soft fill with a thin line.

Hover

Fill and line get slightly stronger.

Pressed

Shrinks to 98%, like the primary.

Focus

Same NeonPink ring as the primary.

Disabled

40% opacity.

Default

Secondary fill and line, as a circle.

Hover

Fill and line get slightly stronger.

Pressed

Shrinks to 98%.

Focus

Same NeonPink ring.

Disabled

40% opacity.

TokenValueNotes

Secondary

For the second action next to a primary button, such as Get in touch beside the main call to action. Same size, text and shape as the primary.

Ghost

For navigation links and low-emphasis actions. Mono text, no fill until hover. Mark the current page with aria-current.

Icon

A 40 circle holding a 20px MADS icon, for previous/next and other icon-only actions. Always give it an accessible name.

Round

The secondary button as a 44 circle with a 20px MADS icon and no text, for actions such as Settings next to other secondary buttons. Same fill, line, hover, press and focus as the secondary. Always give it an accessible name.

Toggles

mads.toggle

The light and dark mode switch: a gradient pill with a white knob that slides across, showing a sun or a moon. The switch at the top of this page is the same component.

Namingmads.toggle.theme
Light
Dark
Try it: changes this page
TokenValueNotes

Behaviour

It is a switch: on means dark mode. Without a saved choice it shows the device setting. Pressing it saves the choice, so the next visit opens in the same mode.

Motion

The knob slides with the spring curve in 350ms while the sun and moon swap with a quarter turn.

Size

56 × 36, on the 4px grid. The knob is 28 with a 4 inset.

Popovers

mads.popover

A round button that opens a floating card: the Ideas and Settings menus in the header of this page and of miguel-adan.com. The card grows out of its button, its rows slide in one after the other, and only one card is open at a time. The samples below are the same components the header uses.

Namingmads.popover · mads.popover.{row | link} · mads.language.switch
Try it: the Ideas and Settings menus from the header
Toggle to dark or light mode
Change languageComing soon: MADS is in English only
Link, EnglishEnglish
Not translated yet
TokenValueNotes

Behaviour

Pressing the button opens its card and closes any other. A second press, a tap or click outside the card, Esc or moving the focus out of it closes it again; Esc puts the focus back on the button.

Motion

The card grows out of the button as a circle while it settles 8 down into place (slow, entrance), then its rows slide in 10 up, 80ms apart. Settings turns 120° with the spring; Ideas turns AmberGold and plays its glow.

Accessibility

The button carries aria-expanded and aria-controls; the card is a labelled group. With reduced motion the card simply appears. The language switch is a link with hreflang, or greyed out with aria-disabled when a page has one language.

Cards

mads.card · mads.stat

Containers for grouped content: topic cards, compact cards, selectable cards and stats.

Namingmads.card · mads.card.compact · mads.card.selectable · mads.stat

Card

Lorem ipsum

Dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore.

Dolor sit

Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.

Amet consectetur

Duis aute irure dolor in reprehenderit in voluptate velit esse.

Compact card

Lorem ipsumDolor sit amet
ConsecteturAdipiscing elit
Sed do eiusmodTempor incididunt

Selectable card

The selected card fills its bar over 8 seconds, then the next one is selected. Click a card to pick it yourself.

Stat

20+Lorem ipsum dolor
5Sit amet consectetur
6Adipiscing elit sed
99.9%Eiusmod tempor
TokenValueNotes

Card

Groups a title and a short description. The diamond marker takes any mads.color to tie the card to a topic.

Selectable

A card that works as a tab. The selected one gets a surface background, an accent border and title, and a bar that shows how long until the next one.

Stat

A large number in the display font with a short label. Colour the number with an accent text role so it stays readable in both themes.

Chips and tags

mads.chip · mads.tag

Small labels for skills, places and media types.

Namingmads.chip · mads.chip.location · mads.tag
LoremIpsum dolorSit ametLorem · Ipsum · DolorVideoNew
TokenValueNotes

Chip

For a set of short keywords, such as skills or tools. Not clickable.

Location chip

A chip with a NeonPink dot, for a place or a live status.

Tag

A filled capital label that sits on top of media, such as VIDEO on a screen recording.

Progress

mads.progress

Bars that show a share of a whole or the time left.

Namingmads.progress · mads.progress.thin
Lorem0%
Ipsum0%
Dolor0%
TokenValueNotes

Bar

A labelled 8 tall bar with its value on the right. Fill it with one of the mads.gradient pairs.

Thin bar

A 4 tall bar without a label, for time running out, as in the selectable card.

Motion

Bars grow from the left over 600ms with the standard curve whenever the value changes.

Lists and quotes

mads.list · mads.quote

Patterns for longer content.

Namingmads.list.diamond · mads.quote · mads.text.gradient
  • Lorem ipsum dolor sit amet, consectetur adipiscing elit.
  • Sed do eiusmod tempor incididunt ut labore et dolore.
  • Ut enim ad minim veniam, quis nostrud exercitation.
  • Duis aute irure dolor in reprehenderit in voluptate.
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore.
TokenValueNotes

Diamond list

Each point sits in its own compact card with a rotated square as the bullet. Two columns on wide screens, one on phones.

Quote

A 3 wide accent line on the left with xlarge text. Use it for one standout sentence per section.

Gradient text

mads.text.gradient fills display text with the brand gradient. Keep it to large headings.