Haralt
Brand
BRAND · COMPONENTS

Components.

The building blocks we make Haralt with, straight from src/components/haralt – with the states they have. Only what recurs across pages is here; what belongs to a single screen is left out.

Actions and choices
k1

Buttons

ui/button.tsx · ui/button-link.tsx

Radius 999, weight 700. Heights lg 52 · md 48 · default 44 · sm 38 · xs 32. Arrow only on step buttons. Disabled: opacity .45. A button that is waiting keeps its full colour and gets a spinner and a waiting text – see Loading.

VARIANTS
STATES
Disabled
Disabled
Loading
SIZES
Dark
n3

Chips

haralt/Chip.tsx

Selected = ink/white. Unselected = cream 96 %, hover 92 %. Green is not “selected”. White chips are suggestions under the search field.

Selected
Unselected
Disabled
n1

Pill bar and segmented choice

haralt/PillBar.tsx · haralt/Segmented.tsx

White pill bar, 4 px padding. Active = ink with white text (stays black on hover), inactive hover = cream. Same pattern for the menu, Map/List and NO/EN. The segmented choice is a cream pill with equal-width options, for two or three choices in a form. Breadcrumb and avatar belong to the page header.

My page
n2

Form fields

haralt/Field.tsx · ui/switch.tsx

No border: cream 96 %, radius 14. Focus = 1.5 px green + white. Error = 1.5 px red. Disabled = same fill, muted text. The placeholder is always an example.

Error
Disabled
SWITCH
n6

Radio choice

haralt/RadioCard.tsx

Selected = 1.5 px ink edge + 18 px ring with a 5 px stroke. Unselected = 88 % edge, ink on hover. Dashed edge for the option outside the list. While the choice is being checked, the subtitle becomes a waiting text and a spinner takes the status's place. When several can be chosen, use the check circle: 22 px, green with ✓.

PICK UP FROM A LOCKER
SEVERAL CHOICES
Content and status
m7

Status labels

haralt/StatusLabel.tsx

Green = active/available (dot only on “ongoing”), amber + clock = waiting on someone, red = declined/error, grey = done. Brand yellow = delivery method, not state. The order flow also has strong surfaces for what is happening now: ink, green and white. Fact labels follow the status label.

STATE
ONGOINGAVAILABLE NOWAWAITING REPLYRENTED · BACK 18:00DECLINEDRETURNEDDELIVERY
STRONG SURFACES
ON THE WAY · 17:40RENTAL RUNNINGSETTLEDOffer sent
FACT LABELS
1 h 12 min40 kr so farSogn
k11

Product card and image stack

haralt/ProductTile.tsx · haralt/ImageStack.tsx

White, radius 22, 1:1 image with object-fit: contain and no background. Hover: lift 2 px + shadow. Without an image, a cream surface. The last tile in a grid is always the dashed “Missing something?”. Things that belong together show as a stack of at most three: small tiles in lists, tilted pills at the top of an order.

STACK
Empty
TILTED
n6

Numbers and facts

haralt/StatTile.tsx · haralt/FactTile.tsx

Stat tile: value 18/900 over a muted label, on white or dark. Fact tile: uppercase label over a value 15/800, in a grid that fills the width – on cream, green and dark cards. Progress is 6 px with rounded ends.

40 h
battery
IP67
rainproof
40 h
battery
IP67
rainproof
Daily cap reached in 4 h 58 min
DELIVERED
Friday 18:05
NEXT DAY
Saturday 18:05
PRICE
249 kr/day
DELIVERED
Friday 18:05
NEXT DAY
Saturday 18:05
PRICE
249 kr/day
DELIVERED
Friday 18:05
NEXT DAY
Saturday 18:05
PRICE
249 kr/day
m6

Price box and receipt

haralt/PriceBox.tsx

Cream price box, radius 18: lines in secondary text, the total with a divider above. A green total is the daily cap on the product page. The receipt is a white card max 520 with the price box.

RECEIPT · #4788
Dyson V8 Absolute
Saturday 12 Sep · Sogn Studentby, locker 2
Unlock10 kr
Use · 1 h 30 min at 40 kr/hour60 kr
Paid70 kr
Unlock10 kr
Use, per hour40 kr
Daily cap – never more than240 kr

Timeline

haralt/Timeline.tsx

What has happened and what is coming, with 18 px dots: green ✓ = happened, yellow with a dark edge = next, white = later, grey ✓ = last in a finished run. The line is green between two things that have happened.

ONGOING
  1. Request sentMon 14:02
  2. Haralt confirmed the packageMon 15:30
  3. Paid – booking confirmedMon 16:10
  4. Delivery Fri 17–20 (neste)
  5. Pickup whenever you like
FINISHED
  1. Request sentMon 14:02
  2. Paid – booking confirmedMon 16:10
  3. Delivered to Kirkeveien 12Fri 18:05
  4. Picked up – settledMon 10:20
n4

Icon rows – light and dark

haralt/IconRow.tsx

38 px icon box radius 8, Lucide 18 px, title 14/700, subtitle 12.5. Always three points. Dark card: yellow eyebrow, inverted icon.

HOW IT WORKS
UnlockGo to the locker and tap “Unlock”
Take it and close the doorThe rental starts when the locker is unlocked
Return itTap «Return» on the website or in the app – that stops the rental
GOOD TO KNOW
Wear and tear is coveredNormal use costs nothing extra
Something went wrong?Tell us and we'll sort it
Around the clockReturn it when it suits you
n5

Settings card

haralt/SettingsCard.tsx

Same skeleton for all: icon box + title 17/800 + subtitle, content, footer row with a divider – note left, action right. “Delete” is always its own card at the bottom.

Payment

Charged when you return.
VISA •••• 4421

Delete account

Your account, profile and cards are deleted. Rentals and purchases are kept without your name, as Norwegian accounting law requires.
You must have returned everything first.
Feedback and loading
n6 · m6

Status banner

haralt/StatusBanner.tsx

Green = confirmed, dark = needs action. Always its own card, never inside a white one. The large variant, with a timestamp eyebrow and a personal title, is the confirmation after a submission.

Your booking is binding
Order confirmation sent to kari@epost.no
ORDER #4821
All set – accept within 24 hours
We've checked the warehouse: everything you asked for is free on Friday 11 Sep.
ORDER #4821
SENT · TUESDAY 22 SEP AT 14:36
Thanks, Kari. We're reading it now.
You'll get a reply by email as soon as we can.
m2

Error box

haralt/ErrorBox.tsx

Red is only used here. Placed right above the button it concerns. Text = what happened + what did not happen + at least one way out as a link.

DECLINED
m5

Empty state

haralt/EmptyState.tsx

Never a dead end: 56 px “?” circle, title with the search term in quotes, one secondary + one primary action.

We don't have “popcorn machine” on the shelves yet

But we can get most things. Tell us what you need and when, and we'll reply as soon as we can.

m3

Toast

haralt/toast.tsx · ui/sonner.tsx

Dark pill bottom centre, 2.8 s, a new toast replaces the last. Only for actions without their own screen – never for anything the user must read carefully.

m4

Modal – confirm and block

haralt/Modal.tsx · ui/dialog.tsx

Ink overlay at 55 %, click outside closes. Card min(460px, 92vw), radius 26, padding 30. Filled red primary only when the action is actually available; a block is shown as an error box with a way out.

l1–l5

Loading

haralt/Skeleton.tsx · haralt/Waiting.tsx · ui/button.tsx

Show as little as possible, as late as possible. Nothing shows under 300 ms. A page being fetched gets a skeleton shaped like its content – never a logo or a spinner in the middle of an empty page. A page change that takes over 300 ms gets a 3 px green line at the top. A button that is waiting gets a spinner and a waiting text in full colour, and stays at least 500 ms once shown.

SKELETON, NOT A LOGO

Same shape, radius and padding as the card that is coming. Each card pulses, slightly staggered. At most six cards or three rows.

TOP LINE AFTER 300 MS

A 3 px green stripe on a cream track. After 10 s a way out appears, after 30 s an error box.

Things to rent
A BUTTON THAT IS WAITING

A spinner where the icon was, a present-tense verb instead of the label. Dots when a person is working, a spinner when a machine is.

Tap to try

What do you need today?

Ask, and we'll handle the rest. If we don't have it, we'll get it.

Just ask