Your design system,
finally in one place.
Everything a build needs to look like us: forest darks, cream lights, one bright green, bold rounded headlines with an italic that carries the feeling.
globals.css.Forest darks, cream lights, one bright green.
Pages alternate section grounds: forest-900 → white → cream. Green never fills a section background — it lives in buttons, emphasis words, marks, and dots. The gradient appears once per page at most, on the hero's italic line. Text on dark is white with on-dark-muted for body.
Bold heads, italic feeling.
Poppins 700
60–68 / 1.12 / -1%
Poppins 700
44 / 1.18
Poppins 600
24 / 1.3
Hanken Grotesk 400
17 / 1.7
IBM Plex Mono 400
12 / caps / +18–22%
Every editorial headline carries exactly one italic emphasis phrase — green on light, green-dark or the hero gradient on dark. Product headings (form labels, dialog titles, table headers, error states) run upright. Poppins never sets body copy; Hanken never sets headlines. Mono eyebrows are green and may be flanked by short dashes on light sections, or sit inside an outlined capsule on dark.
Green glows, pills everywhere.
two-line label + white circular arrow
<button className="inline-flex items-center gap-2.5 rounded-full px-7 py-[15px] font-display text-[15px] font-semibold text-white shadow-glow [background-image:var(--gradient-button)] transition duration-150 hover:brightness-[1.08] active:scale-[0.98]"> Build your artist profile <span>→</span> </button>
// outline — secondary actions on light "rounded-full border border-pill-border bg-transparent px-7 py-[15px] font-display text-[15px] font-semibold text-heading transition duration-150 hover:border-ink active:scale-[0.98]" // solid ink — sign-up style "rounded-full bg-ink px-7 py-[15px] font-display text-[15px] font-semibold text-white transition duration-150 hover:bg-forest-900" // on dark — border white/45, never a fill "rounded-full border border-white/45 px-7 py-[15px] ... text-white" // disabled — no glow, no gradient "cursor-not-allowed rounded-full bg-disabled px-7 py-[15px] text-faint"
Three things we commit to, on every dark section.
SEAL
(asset)
Every artist is a real person.
Numbered mono label in green-400, bold head with italic emphasis, muted body. Columns divide with 1px white/12% rules — no card backgrounds.
Your work is never training data.
Dark sections carry ambience: faint dashed circles, dashed connector lines between avatars, and a soft radial lift behind focal points.
Find the people behind the work.
Glowing marks (green box-shadow at 40%) are reserved for the brand seal and primary CTAs. Section markers sit right-aligned.
joined by 1px dashed white/30% connectors
Cards, badges, and the quote.
Cards are always white — even on cream — radius 20, one soft shadow. The VERIFIED pill is mono caps on green tint. Quotes get a 3px green left border and bold italic Poppins. Capsule badges (outline + tracked mono caps) are eyebrows; dash-flanked mono eyebrows introduce light sections.
// every content card — white even on cream "rounded-[20px] border border-card-border bg-white p-5 shadow-card" // verified pill — the badge of the whole system "inline-flex items-center gap-1.5 rounded-full border border-green-tint-border bg-green-tint px-3 py-1.5 font-mono text-[10px] tracking-[0.18em] text-green-600" // quote — 3px green rule, bold italic Poppins "border-l-[3px] border-green-500 py-2 pl-7" "font-display text-[32px] font-bold italic leading-[1.25] text-heading"
Tony and Chelsea Northrup's gear reviews tend to cut through the hype pretty efficiently, and this one on the Insta360 Luna Ultra is no exception. Worth a look if you're curious…
Layout, spacing, forms.
Sections pad 120px vertical. Cards pad 20–24. Content maxes at 1240px with centered heroes.
<label className="font-mono text-[11px] uppercase tracking-[0.18em] text-green-emphasis">Artist name</label> <input className="rounded-xl border border-border bg-white px-4 py-3.5 text-[15px] text-heading outline-none transition focus:border-green-500 focus:shadow-[0_0_0_3px_var(--color-green-tint)]" /> // error state: swap border + label to text-danger / border-danger
150ms ease. Hover: brighten greens 8%, darken solids one step, links → green. Press: scale .98. Glow only on primary CTAs and the seal. No parallax, no bounces.
Numbers that tell the story.
A portfolio built around your work
Feature blocks index with mono NN / LABEL (slash form). § markers are reserved for page-level sections; the slash form counts features within one.
Choosing work is a room of its own.
A library is unbounded, so browsing it inline gives the least-used control on a page the most space on it. The picker is a modal: the whole viewport when browsing is wanted, none of it when it is not. Every tile is a fixed square with the work contained on a mat — one footprint for every shape, and the difference shows as mat rather than a cut edge. Search runs over titles, tags and file names, and appears only above twelve works. Open it here and check it at 375px as well as full width:
1 chosen — the badge is the position in the book, not a tick.
A page is furniture; the work is the guest.
Layout is a separate axis from design: every look is available in every layout, because where things sit and what colour they are are different questions. Margins and the caption band are percentages of the PAGE, never the viewport — the same page is ~350px wide on a phone and inside a 1400px desktop spread, so a viewport unit would render the identical page at two different sizes. Type is derived the same way.



Low Tide, Second Morning
- Medium
- Oil on linen
Painted over two mornings on the same stretch of shoreline, waiting for the water to pull back far enough to show the sandbar. The second morning was colder and the light was better.
4 of 12
Section
Waterfalls
Eight years of returning to the same four rivers, mostly in spring, mostly at the hour before the sun clears the ridge.
Explain it from the viewer backward.
The welcome is DEFERRED past the art. Nothing greets a collector inside someone’s book — every reference product in the category puts its onboarding in front of a new arrival, and here the arrival came to look at paintings. This panel waits on the other side of sign-up, and only for someone who came from a book and has none of their own. It is also the answer to the one verdict the whole reference set fails: they all explain to a marketing manager, and none says what the ARTIST’s viewer will experience.
Welcome
You just saw what a collector sees. Yours works the same way.
Four steps, about a minute. Nothing you make here is public until you send someone the link.
- Name itOne line. It goes on the cover, above your name.
- Pick your workFrom the artwork already in your ArtHelper library — nothing to upload again.
- Put it in orderDrag them. The order is the book; there is no layout to fight.
- Send the linkThey open it on their phone, signed out, with nothing to install — exactly as you just did.
Your books are built from your ArtHelper library. Add artwork there if you have not yet, and it will be here when you come back.
A moment is a flash; a fact stays on the page.
One surface, mounted once, bottom-right — the same place ArtHelper announces outcomes, because an artist moves between these apps. Success auto-dismisses; an instruction stays until dismissed. Validation lives in the form, standing state lives on the page, and a silent catch on a user action is a missing flash. These buttons fire the real thing:
Every token, as you type it.
Everything above is backed by a token in app/globals.css under the Tailwind v4 @theme block. Reference the token; never re-hardcode a hex that already exists.
--font-displayfont-displayPoppins · headlines only--font-sansfont-sansHanken Grotesk · body + UI--font-monofont-monoIBM Plex Mono · counted / official8pxrounded-lgthumbnails12pxrounded-xlinputs16pxrounded-2xlpanels20pxrounded-[20px]cards999pxrounded-fullpills--shadow-cardshadow-cardwhite cards only--shadow-glowshadow-glowprimary CTA + seal only--gradient-button[background-image:var(--gradient-button)]primary pill fill--gradient-hero[background-image:var(--gradient-hero)]hero italic, ≤1× per page{/* white */}
<section className="mx-auto max-w-page px-6 py-[72px] sm:px-12">
{/* cream — the alternating band */}
<section className="bg-cream px-6 py-[72px] sm:px-12">
{/* forest — text flips to on-dark, rules to white/12% */}
<section className="bg-forest-900 px-6 py-[72px] text-on-dark sm:px-12"><h2 className="font-display text-[44px] font-bold leading-[1.18] text-heading">
The world is becoming{" "}
<em className="italic text-green-emphasis">more automated</em>. Trust is rare.
</h2>
// on dark, the emphasis token changes: text-green-dark// dash-flanked mono eyebrow (light sections) <div className="flex items-center gap-2.5 font-mono text-xs uppercase tracking-[0.22em] text-green-emphasis"> <span aria-hidden className="h-px w-5 bg-green-emphasis" /> Foundations <span aria-hidden className="h-px w-5 bg-green-emphasis" /> </div> // right-aligned page-section marker "font-mono text-xs uppercase tracking-[0.18em] text-muted" → —— § 01 · Color // within a section, count features with the slash form instead: 01 / PORTFOLIO
Two names to watch. text-muted is caption text (#7C8880) while bg-mutedis shadcn's cream fill — they are deliberately split, and new code should prefer text-caption. And never restyle a vendored component in components/ui/ or components/ai-elements/: fix the alias layer in globals.css instead — one mapping, every component follows.