Building Emirates Holidays in Scandinavia from scratch
From launch marketing and building the CRM database to leading the Scandinavian sales and service team.
The living design system for hampusmalmstrom.se. Everything here is built from the same code as the site: the tokens are read straight from app/globals.css and the components are the real ones. The rules are in MLMSTRM.md – this page shows how they are built right now. Component names are exactly the ones in MLMSTRM.md.
Every component is shown in light and dark mode side by side. The page around them follows the mode switch at the top right.
Fixed hex codes, the same in both modes. Components use roles – color names only in filled accent boxes.
#FAFAFA#333333#377771#4B2E39#32021F#A9D8B8#BEFFC7#E6F2EA#3D3D3D#CCCCCC#A5979C#846779| Role | Light mode | Dark mode |
|---|---|---|
--bg | offwhite | charcoal |
--surface | tint | graphite |
--text | charcoal | offwhite |
--text-muted | plum | silver |
--text-secondary | charcoal | silver |
--subtitle | plum | offwhite |
--primary | teal | pastel |
--meta | teal | pastel |
--accent-line | mint | teal |
--accent-line-soft | mint | mint |
--button-bg | teal | pastel |
--button-text | offwhite | charcoal |
--button-hover-bg | pastel | offwhite |
--button-hover-text | charcoal | charcoal |
--highlight | pastel | pastel |
--dot-1 | teal | teal |
--dot-2 | plum | plum-light |
--dot-3 | mint | pastel |
--dot-4 | wine | wine-light |
--dot-5 | charcoal | offwhite |
--card-title | plum | offwhite |
--tag-text | plum | pastel |
--card-bg | transparent | graphite |
--card-border | mint | transparent |
--number-4-bg | tint | graphite |
--number-4-text | charcoal | offwhite |
--number-4-note | plum | pastel |
| Token | Light mode | Dark mode | Use |
|---|---|---|---|
--when-light | block | none | display for things that only show in light mode (birds, sun icon) |
--when-dark | none | block | …and for things that only show in dark mode |
--knob-x | 0px | 32px | the mode switch knob: on the left |
--is-light | (empty) | initial | Picks values that come from the element itself (toneDark) |
--is-dark | initial | (empty) | Picks values that come from the element itself (toneDark) |
| Token | Value | px and use |
|---|---|---|
--sans | var(--font-montserrat), "Montserrat", system-ui, sans-serif | – |
--sans-italic | var(--font-montserrat-italic), "Montserrat", system-ui, sans-serif | – |
--serif | var(--font-gelasio), Georgia, serif | – |
--serif-italic | var(--font-gelasio-italic), Georgia, serif | – |
| Token | Value | px and use | Example |
|---|---|---|---|
--fs-xs | 0.75rem | 12 – tags, source lines, meta lines in cards, legend | Aa |
--fs-sm | 0.875rem | 14 – metarader, navigation, statusrad, eyebrow, footer | Aa |
--fs-body-sm | 1rem | 16 – text in cards and lists, text links | Aa |
--fs-body | 1.0625rem | 17 – body text, buttons, traits | Aa |
--fs-lead | 1.1875rem | 19 – lead text, small card titles | Aa |
--fs-title | 1.375rem | 22 – kortrubriker, arbetsgivare | Aa |
--fs-stat | 2.125rem | 34 – WoW-namnet | Aa |
--fs-hero | clamp(2rem, min(15cqi, max(3.25rem, 7.4vw)), 7rem) | 32–112, cqi = hero-kolumnen | Aa |
--fs-section | clamp(2.5rem, 5.4vw, 4.75rem) | 40–76 | Aa |
--fs-display | clamp(2.5rem, 4.8vw, 4.25rem) | 40–68 | Aa |
--fs-hero-sub | clamp(1.1875rem, 1.8vw, 1.5625rem) | 19–25 | Aa |
--fs-contact-label | clamp(1.0625rem, 1.6vw, 1.3125rem) | 17–21 | Aa |
--fs-contact-value | clamp(0.9375rem, 1.5vw, 1.1875rem) | 15–19 | Aa |
| Token | Value | px and use |
|---|---|---|
--lh-hero | 0.95 | – |
--lh-display | 1 | – |
--lh-heading | 1.25 | – |
--lh-snug | 1.4 | – |
--lh-text | 1.6 | – |
--lh-long | 1.7 | – |
| Token | Value | px and use | Example |
|---|---|---|---|
--space-1 | 0.25rem | 4 | |
--space-2 | 0.5rem | 8 | |
--space-3 | 0.75rem | 12 | |
--space-4 | 1rem | 16 | |
--space-5 | 1.5rem | 24 | |
--space-6 | 2rem | 32 | |
--space-7 | 3rem | 48 | |
--space-8 | 4rem | 64 |
| Token | Value | px and use | Example |
|---|---|---|---|
--card-pad | var(--space-5) | – | |
--gutter | clamp(1.25rem, 4vw, 3rem) | 20–48 – page margin (20 px from 500 px width, 48 px at 1200 px) | |
--max | 77.5rem | 1240 px | |
--space | clamp(2.5rem, 5vw, 4.5rem) | 40–72 – luft mellan sektioner, luft under kontaktblocket | |
--space-block | clamp(1.5rem, 4.5vw, 4rem) | 24–64 – inner space in large blocks and surfaces, space above the hero |
| Token | Value | px and use | Example |
|---|---|---|---|
--r-xs | 0.5rem | 8 | |
--r-sm | 0.75rem | 12 | |
--r-md | 1rem | 16 | |
--r-lg | 1.5rem | 24 | |
--r-xl | 2rem | 32 | |
--r-2xl | 3rem | 48 | |
--r-portrait | 5.5rem | 88 |
| Token | Value | px and use |
|---|---|---|
--dur-hover | 150ms | hover and focus |
--dur-pulse | 3200ms | pulse of the live dot, one cycle (loops) |
--ease-out | ease-out | – |
Montserrat for the name, headings and UI text; Gelasio for body text. Shown in the page’s current mode.
The name · Montserrat Black 900, uppercase, --fs-hero (32–112), --lh-hero, --primary
Hejsan, I’mHampus.
Hero subtitle · Montserrat Bold 700, --fs-hero-sub (19–25), --lh-snug, --subtitle, max 34ch
Digital marketing & business development.
Section title · Montserrat Black 900, uppercase, --fs-section (40–76), compact --fs-display (40–68), --primary
Experience
Card title · Montserrat ExtraBold 800, --fs-title (22), --card-title
Warehouse Manager
Employer · Montserrat Bold 700, --fs-title (22), --text
Emirates Holidays
Lead text · Gelasio Regular 400, --fs-lead (19), --lh-text
A few things I’ve built, launched and learned from along the way.
Body text · Gelasio Regular 400 and Italic, --fs-body (17), --lh-long, max 65ch
Digital marketer and business developer based in Helsingborg, Sweden, where I live with my cat Tusse. 15 years in retail, travel and hospitality.
Meta line · Montserrat Medium Italic 500, --fs-sm (14), --meta
Eyebrow · Montserrat Bold 700, uppercase, --fs-sm (14), letter-spacing 0.08em, --text-muted
Very important numbers
UI text · Montserrat SemiBold 600, --fs-sm (14) – navigation, status, tags, tools
About · Experience · Contact
In the same order as the component table in MLMSTRM.md. Components that are hidden on the home page (the story cards and Off the clock) are shown here too.
--bg. 1 px line in --accent-line below once the page has been scrolled. The name --primary, --fs-sm ExtraBold, letter-spacing 0.08em. Links --text, --fs-sm SemiBold, hover (mouse only) --primary with a 2 px underline 6 px from the text in --accent-line. Inner space --space-5, below 48rem --space-3.Light mode
Dark mode
--accent-line, --r-md. Knob 36 × 36 px, 4 px from the edge, --r-sm, filled with --button-bg and an icon in --button-text (Lucide Sun/Moon, 18 px, stroke width 1.75). The knob position is --knob-x.--button-hover-bg with the icon in --button-hover-text. Slides in --dur-hover; with reduced motion it changes place without sliding. No colors fade during the mode change itself (the class theme-switching on <html> turns the transitions off for two frames, the knob excepted).Light mode
Dark mode
--fs-section, --lh-display, --primary. Bar 72 × 7 px in --accent-line, --space-4 above.--fs-display (Experience, Tools, the 404 page). In the contact block offwhite – the bar keeps --accent-line. It is an h2 by default; the 404 page uses h1.Light mode
Dark mode
--fs-sm, letter-spacing 0.08em, uppercase, --text-muted. Leaf 14 px with --space-3 between.Light mode
Very important numbers
Dark mode
Very important numbers
--fs-title, --card-title.--fs-lead in the story cards. Offwhite in filled widgets.Light mode
Warehouse Manager
Testing the future of grocery shopping
Dark mode
Warehouse Manager
Testing the future of grocery shopping
--fs-sm, --meta, --lh-heading.--fs-xs in cards. Pastel on filled surfaces.Light mode
Dark mode
--button-bg with text in --button-text, Montserrat Bold --fs-body, padding --space-4 / --space-6 (16/32), --r-md.--button-hover-bg and --button-hover-text, --dur-hover. Focus: the global focus ring.Light mode
Dark mode
--r-md, 2 px border. Clickable: filled with --button-bg, icon in --button-text. Not clickable: only border and icon in --primary.Light mode
Dark mode
--fs-body-sm, --primary. Underline 3 px in --accent-line, 6 px from the text. Space --space-3 above and below.Light mode
Dark mode
--primary. Underline 1 px in --primary, 3 px from the text.Light mode
One of them is my webshop Naughtypanda.se, with gifts that have a cheeky twist.
Dark mode
One of them is my webshop Naughtypanda.se, with gifts that have a cheeky twist.
--primary, no underline.--accent-line, 6 px from the text – the same as the menu. The contact rows use 4 px.--accent-line, --r-md, padding --space-2 / --space-4. Montserrat SemiBold --fs-sm, --subtitle. Leaf dot in --primary – a deliberate exception from --dot-1…5. The dot is a Live dot.Light mode
Open to new roles in southern Sweden and Copenhagen
Dark mode
Open to new roles in southern Sweden and Copenhagen
--accent-line, --fs-xs SemiBold, --tag-text, padding --space-1 / --space-2, --r-xs.Light mode
Dark mode
--bg, Montserrat SemiBold. S: --fs-sm, --r-xs, dot 9 px. M: --fs-body-sm, --r-sm, dot 10 px. L: --fs-lead, --r-sm, dot 11 px.Light mode
Dark mode
.leaf-dot, size via --size (default 10 px), radius 40 % of the size. Color --dot-1…5 by the area’s place in the list. No border, in light and dark mode.Light mode
Dark mode
.leaf-dot.live-dot: Leaf dot (10 px) in --primary. 2 px ring in the same color with the dot’s shape (radius 40 % of the size). Used in the Status line, the Ongoing row and the Eyebrow of a version in progress. A planned version gets no dot.--dur-pulse (3.2 s) – in the first half the ring grows to 2.4 times its size and fades out, in the second half it rests. No glow. Reduced motion: no ring.Light mode
Open to new roles in southern Sweden and Copenhagen
In progress: v2.0
Planned: v2.1
Dark mode
Open to new roles in southern Sweden and Copenhagen
In progress: v2.0
Planned: v2.1
--r-xs. M 52 px (44 px in small mode, below 26rem) with --r-md. The color is set by the context.Light mode
Dark mode
--card-bg, 1 px border in --card-border, --r-xl. Inner space --card-pad (--space-5). The story card’s image slot is a toned place (--tone / --tone-dark).Light mode
Some of the things I’ve been part of building, and what I learned along the way.
From launch marketing and building the CRM database to leading the Scandinavian sales and service team.
Scan & Go, a Google Assistant test and the Netto app 3.0 concept with AKQA.
[One sentence: what you did and what came out of it.]
Gifts with a cheeky twist. I run all of it myself, from sourcing to marketing.
Dark mode
Some of the things I’ve been part of building, and what I learned along the way.
From launch marketing and building the CRM database to leading the Scandinavian sales and service team.
Scan & Go, a Google Assistant test and the Netto app 3.0 concept with AKQA.
[One sentence: what you did and what came out of it.]
Gifts with a cheeky twist. I run all of it myself, from sourcing to marketing.
.surface: --surface, --r-xl, inner space --space-block (24–64).--surface, the Surface gets the page background --bg, so the box shows against the band. A Surface in a row with cards has the cards’ inner space --card-pad, so they look alike.Light mode
Digital marketer and business developer based in Helsingborg, Sweden, where I live with my cat Tusse. 15 years in retail, travel and hospitality, most of them in digital. I built my first websites at 15. I started my first web design business at 16. It had three clients. I’m not a developer (anymore), but this one I’m building myself in Next.js using AI, to learn something new.
I spot details quickly, and probably point them out a bit too often. Give me a problem nobody really knows how to solve, and I’m happy to nerd out on it.
Outside work it’s a lot of travel and digital projects, often more than I have time for. One of them is my webshop Naughtypanda.se, with gifts that have a cheeky twist, which I run on my own on the side. Every now and then I play some World of Warcraft or get lost in Transport Fever or Satisfactory.

Came home with me from a cat shelter in 2022, when he was about two years old. Now he runs the warehouse for Naughtypanda.se, and no box arrives without a thorough inspection.
Dark mode
Digital marketer and business developer based in Helsingborg, Sweden, where I live with my cat Tusse. 15 years in retail, travel and hospitality, most of them in digital. I built my first websites at 15. I started my first web design business at 16. It had three clients. I’m not a developer (anymore), but this one I’m building myself in Next.js using AI, to learn something new.
I spot details quickly, and probably point them out a bit too often. Give me a problem nobody really knows how to solve, and I’m happy to nerd out on it.
Outside work it’s a lot of travel and digital projects, often more than I have time for. One of them is my webshop Naughtypanda.se, with gifts that have a cheeky twist, which I run on my own on the side. Every now and then I play some World of Warcraft or get lost in Transport Fever or Satisfactory.

Came home with me from a cat shelter in 2022, when he was about two years old. Now he runs the warehouse for Naughtypanda.se, and no box arrives without a thorough inspection.
--highlight; box 4 --number-4-*. --r-lg, inner space --space-5. Widgets plum and teal, --r-xl, --card-pad. Large headings offwhite, pastel only for small texts.Light mode
Dark mode
flex-basis formula with flex-wrap, minimum width 300 px, --space-5 between). All cards --card-pad, --r-xl (leaf shape). Meta line (small, --fs-xs) at the top, then a Card title. Spotify card: Filled box in plum. Spotify’s icon in white at the top right, 24 px (at least 21), unmodified, --space-4 clear space to the text. Four tracks in 2 × 2: cover 1:1, uncropped, 4 px corners (8 px from 64rem), nothing on top; title in Montserrat Bold --fs-sm, offwhite; artist in a Meta line (small), pastel. The whole track links to Spotify; hover underlines the title (2 px) like a Plain link. Coffee card (new): a Surface (--bg on the band). Four rows from content/en/coffee.md: an uppercase label like the Eyebrow (--fs-xs, Bold, letter-spacing 0.08em, --text-muted) and the value in Montserrat Bold --fs-body-sm, --text, divided like a Divided list row (--space-3 above and below). Under the rows a cut-out photo of the bag (transparent PNG, image in coffee.md, optional) fills the height left in the card, at least 14rem, --space-5 above, aligned to the bottom. No date, no Fallback state. WoW card: Filled box in teal. The character’s name as Card title, offwhite; "Level 80 Druid · Doomhammer" in a Meta line, pastel; the full-body render (Blizzard’s main-raw, transparent PNG) in a box that takes the height left in the card (at least 18rem, --space-4 above) and reaches into the card’s padding on the sides; the image is scaled so the character fills the box and only its transparent edges are cut; the source line "Data from Blizzard" at the bottom, --fs-xs, pastel. Images are served through next/image from our own server.Light mode
Dark mode
--fs-body-sm, offwhite, --space-4 above. Spotify: "Spotify isn’t answering right now. Safe to assume I’m still listening." WoW: "Can’t reach Azeroth right now. Thildir is probably out hunting for another mount." The Coffee card is static and has none.Light mode
Dark mode
--accent-line only between the rows, none below the last. Row spacing --space-5. In the contact block (filled box, wine) the divider is plum via --divider.--r-2xl, inner space --space-block, space below --space. Heading offwhite, email (hello@hampusmalmstrom.se) and LinkedIn in pastel, icon boxes plum with the icon in pastel, focus ring pastel. Hover (mouse only): 2 px underline 4 px from the text, in the text’s pastel.Light mode
Email is the quickest way to reach me, and I’m always happy to grow my network on LinkedIn.
Dark mode
Email is the quickest way to reach me, and I’m always happy to grow my network on LinkedIn.
--accent-line-soft above and below, space --space-6. Dots 34 px.Light mode
Dark mode
--surface across the full width, the content in .wrap (max --max, page margin --gutter). Width 100 %: .band is a container, which otherwise has no width of its own. Space --space above and below.Light mode
Digital Marketing Specialist · Helsingborg
Marketing Manager, then Sales & Service Manager · Copenhagen
From finance to digital · Falkenberg, Køge & Aarhus
IHM Business School
Hyper Island
Implement Consulting Group
Dark mode
Digital Marketing Specialist · Helsingborg
Marketing Manager, then Sales & Service Manager · Copenhagen
From finance to digital · Falkenberg, Køge & Aarhus
IHM Business School
Hyper Island
Implement Consulting Group
.divided-list, --accent-line), Icon box M (--r-md) with initials in offwhite on teal, plum or wine as the fallback. Course name in --fs-title Bold, school in --text-secondary --fs-body-sm (Gelasio), years in a Meta line (--meta, --fs-sm). Row spacing --space-5.Light mode
IHM Business School
Hyper Island
Implement Consulting Group
Dark mode
IHM Business School
Hyper Island
Implement Consulting Group
.leaf-dot.live-dot, 10 px, --primary) and the status text in a Meta line (--meta, --fs-sm, Medium Italic) in the right-hand column, --space-2 between. The dot is decorative (aria-hidden), the text is enough for screen readers. The row is as tall as the other rows. The component is OngoingStatus.content/courses/, the end date of the last week; otherwise the last year of the period up to and including 31 Dec), after that the years are shown. The course file decides. Reduced motion: no ring. A current employer gets no dot, only "Since 20XX".Light mode
IHM Business School
Hyper Island
Implement Consulting Group
Dark mode
IHM Business School
Hyper Island
Implement Consulting Group
The page /build. It is built from existing components; new are Step row, Icon box in the Version variant and Decision note. The Article title is used here too, outside the story page. The footer links and the link from /build to this design system are all Plain links. /privacy uses the Page header, Card title for its sections, body text and a Meta line; it adds no component.
--fs-display (40–68), --lh-display, --card-title. No text-transform: capitals stay as written. Subtitle as in the hero: --fs-hero-sub, --subtitle, max 40ch. Space above --space-block. No meta line.Light mode
How I build this site
I run the project, Claude Code writes the code.
Dark mode
How I build this site
I run the project, Claude Code writes the code.
--meta) on the left, since it is a real sequence. The step as a small Card title (--fs-lead, ExtraBold), one line of Gelasio in --fs-body-sm, --text-secondary. Who does the step as a Tag (--fs-xs, --r-xs, --accent-line): Me, Claude or Claude Code, several tags if several do it. The tool in a small Meta line (--fs-xs). Row spacing --space-5.Light mode
It starts with something I want to see on the site.
Claude grills the idea, round after round, before anything is decided.
Every decision becomes a card, tied to a version on the roadmap.
Claude writes one set of instructions for Claude Code.
Claude Code builds on its own branch and reports back, assumptions included.
I go through a preview of the change and send feedback.
When I approve, the change is merged and goes live.
Dark mode
It starts with something I want to see on the site.
Claude grills the idea, round after round, before anything is decided.
Every decision becomes a card, tied to a version on the roadmap.
Claude writes one set of instructions for Claude Code.
Claude Code builds on its own branch and reports back, assumptions included.
I go through a preview of the change and send feedback.
When I approve, the change is merged and goes live.
--fs-sm, Bold, letter-spacing 0.08em, --text-muted). Version in progress: "In progress: v1.4" with a Live dot on the Eyebrow and a Divided list row (name --fs-body Bold, status as text in a Meta line on the right, no dot: "Live · 22 Oct", "In progress" or "Next"). The Live dot sits on the version, never on the rows (changed 2026-10-22): one dot per thing in progress. Several versions in progress get one dot each. The groups are sorted ascending by version number. Goal line: one line about the version’s goal directly under the Eyebrow (Gelasio --fs-body-sm, --text-secondary). A planned version ("Planned: v3.0", no dot) has only the goal line, no list. The version up next is shown the same way with "Next" on the Eyebrow ("Next: v2.1", no dot). A version in progress with a goal shows the goal line and then the rows; small fix versions have no goal line. The goal does not follow to Shipped. Built with: the tool name as a Plain link to the tool’s official site, in Montserrat Bold --fs-body-sm, the use in Gelasio --fs-body-sm (--text-secondary), row spacing --space-4.Light mode
Next: v2.1
A way to show where I've been: places I've travelled to, and destinations I've sold during my years in travel.
Planned: v3.0
Stories from my career, with more of the how and why than a CV has room for. Plus the numbers on the home page shuffle on every visit.
The framework the site is built on
Hosting, and a preview of every change
The code, with every change as a pull request
Sparring partner and author of the briefs
Writes the code
Backlog, roadmap, decisions and the site’s copy
Where briefs, reports and images change hands
The icons
Montserrat and Gelasio
Visitor stats, no cookies needed
Dark mode
Next: v2.1
A way to show where I've been: places I've travelled to, and destinations I've sold during my years in travel.
Planned: v3.0
Stories from my career, with more of the how and why than a CV has room for. Plus the numbers on the home page shuffle on every visit.
The framework the site is built on
Hosting, and a preview of every change
The code, with every change as a pull request
Sparring partner and author of the briefs
Writes the code
Backlog, roadmap, decisions and the site’s copy
Where briefs, reports and images change hands
The icons
Montserrat and Gelasio
Visitor stats, no cookies needed
--r-md) filled in plum, teal or wine in turn, newest first. The version number without the "v" ("1.2") in Montserrat ExtraBold, --fs-sm, offwhite. The version’s name as a Card title (--fs-title), the date right-aligned in a Meta line, 2–5 lines in Gelasio --fs-body-sm (--text-secondary) as a list with small Leaf dots (8 px, --dot-2) as bullets. Built like the experience list.Light mode
Decision: If Spotify or Blizzard doesn’t answer, the cards stay put and show a cheeky line instead of going blank.
Decision: The 404 page got one of the site’s birds at first. It pushed the content down on mobile, so it flew off again.
Learned: A living design system and a rulebook keep the design consistent, however many chats are involved.
Dark mode
Decision: If Spotify or Blizzard doesn’t answer, the cards stay put and show a cheeky line instead of going blank.
Decision: The 404 page got one of the site’s birds at first. It pushed the content down on mobile, so it flew off again.
Learned: A living design system and a rulebook keep the design consistent, however many chats are involved.
--fs-body-sm, --lh-snug, --text-secondary. Starts with "Decision:" or "Learned:" in the same style. No box, no icon. --space-3 above.Light mode
Decision: pages are built from existing components, and a new one is written into the rules first.
Dark mode
Decision: pages are built from existing components, and a new one is written into the rules first.
--fs-sm SemiBold, --text-muted, --space-7 below. The link is a Plain link on the right, on the same row as ©: /build first, then /privacy, a gap of --space-5 between.Light mode
Dark mode
public/brand/, unchanged. Switches to the -mork files in dark mode via --when-light / --when-dark. Every image has width and height from the file’s viewBox, so the page does not jump when the image loads.Light mode
Dark mode
--primary, 3 px from the element (pastel in the contact block). Selected text: background --accent-line, text --text. Skip link: --button-bg / --button-text, Montserrat Bold, --r-sm.Light mode
Select this sentence.
Dark mode
Select this sentence.
app/opengraph-image.tsx. Background offwhite. Portrait in the leaf shape with no frame and the duo bird, at the hero measurements × 1.5 (photo 360 × 450 px, radius 132 px = --r-portrait × 1.5). Heading Montserrat Black, uppercase, teal. Subtitle Montserrat Bold, plum. The texts are read from the hero.Light mode
Dark mode
currentColor, 2 px from the text. Square corners.Light mode
Side project · [Since year]
Dark mode
Side project · [Since year]