design.md

Five design systems reverse-engineered from live CSS, each written up as a guideline an agent can build from, and each one built out below as working pages. Only Lemma publishes its own brand page. Everything else was read off the DOM.

5
systems extracted
2,270
lines of guideline
10
live example pages
3
installed as agent skills
Built from the guidelines
examples/lemma/example-landing.html Marketing page with a scattered plate field that draws itself in, a scroll-pinned failure taxonomy, tabbed feature views, and a validated demo form. open full page ↗
The index

Lemma

uselemma.ai

Zero radius everywhere. The page is built from one-pixel rules instead of cards, and diagrams live in numbered plates labelled LEM. 1.1 like figures in a proof.

Britti Sans + IBM Plex Mono 2 live examples skill installed
lemma-design.md744 lines2026-09-11 →

Curated Supply

curated.supply

One type size does almost everything. Every visible run but four is 14px/20px, so hierarchy comes from colour, not size, and one gold word carries the whole editorial hand.

Geist + Geist Mono 3 live examples skill installed
curated-supply-design.md335 lines2026-09-18 →

mal.gg

mal.gg/yc

The data is the interface. 12px default, the scale tops out at 20px and is used once, every numeral is mono, and one 120ms animation is the entire motion budget.

Geist + Geist Mono 2 live examples skill installed
mal-gg-design.md701 lines2026-09-21 →

Luma

luma.com

Identity from colour, shape and softness rather than a typeface. Squircle corner smoothing, translucency as material, one cranberry accent spent carefully.

System UI (no webfont) 2 live examples not installed
luma-design.md386 lines2026-08-24 →

YouTube Shorts

youtube.com/shorts

Near-black surfaces with translucent white layers instead of fills. The 9:16 rounded player, a 48px circle action rail, a 240px guide. Dark only.

Roboto (system, no webfont) 1 live example not installed
youtube-shorts-design.md104 lines2026-09-10 →
design.md/lemma-design.md

Lemma

extracted from uselemma.ai on 2026-09-11

Zero radius everywhere. The page is built from one-pixel rules instead of cards, and diagrams live in numbered plates labelled LEM. 1.1 like figures in a proof.

744 lines Britti Sans + IBM Plex Mono skills/user/lemma-design
examples/lemma/example-landing.html Marketing page with a scattered plate field that draws itself in, a scroll-pinned failure taxonomy, tabbed feature views, and a validated demo form. open full page ↗
examples/lemma/components.htmlopen full page ↗

Design surfaces like Lemma

Act as an excellent Lemma designer, information architect, and design engineer. The standard is that a stranger could not tell your page from a page Lemma's own team shipped. That is achieved through three things and almost nothing else: a strict typographic hierarchy in Britti Sans, a page built out of one-pixel rules instead of cards, and a single indigo accent spent only where it carries meaning.

Lemma is a reliability platform for AI agents. It catches regressions, spots failures, and improves agents in production before users do. The name comes from mathematics: a lemma is a small, proven proposition used as a building block for a larger proof. That idea is the whole design system. Every visual decision is doing the same job the name does, which is presenting evidence as something proven rather than something asserted.

Provenance

Everything below traces to one of three sources, all official Lemma surfaces, all read on September 11, 2026:

  1. uselemma.ai/brand, Lemma's own published brand page, with a live color palette, typeface specimens, and a type scale.
  2. uselemma.ai/brand/lemma-brand-guidelines.pdf, the official 15-page brand guidelines deck, version marked LEMMA 26'.
  3. Live CSS extraction from uselemma.ai/, uselemma.ai/blog/a-taxonomy-of-agent-failures, uselemma.ai/careers, and uselemma.ai/changelog, plus the production JavaScript bundles, for everything the published guidelines do not cover: component recipes, hover states, prose theming, layout rhythm, motion, and the visualization color assignments.

Rules for using this file:

  • On any conflict between this file and the live site, the live site wins. This is a dated snapshot and Lemma iterates.
  • Two genuine conflicts exist between Lemma's own published guidelines and Lemma's own production site. Both are called out inline and marked DISCREPANCY. Do not silently pick one.
  • Never source Lemma values from Brandfetch, Dribbble mockups, *-brand-kit sites, or Figma Community files. Lemma publishes its own kit at uselemma.ai/brand, so there is no reason to guess.
  • docs.uselemma.ai is Mintlify, a third-party docs platform. It is a Lemma surface but it is not Lemma-authored design. Do not extract patterns from it.
  • Britti Sans and IBM Plex Mono are treated differently. Britti Sans is licensed and Lemma states it is not available for download. IBM Plex Mono is open source. See Typography.

Lemma product and brand context

Lemma's brand page states the intent directly: the palette is "built on a single primary hue, a deep and precise indigo that communicates reliability and technical rigor," the neutrals "are never decorative, they serve hierarchy," and the type system is "strict: mixing sizes outside their intended roles breaks the system's legibility."

The distinctive material choices, each one actionable:

  • Zero radius, everywhere. Every Lemma-authored element computes border-radius: 0px. Buttons carry an explicit rounded-none. The only rounded corners on the entire site belong to simulated third-party UI (a mock Slack message, a mock macOS window) where the roundness is quoting someone else's product. Square corners are the single fastest tell of belonging.
  • The page is built from one-pixel rules, not cards. Sections separate with border-t in #D4D4DD. The main container carries xl:border-l xl:border-r, so the content column reads as a ruled measure running down the page. Absolutely positioned w-px bg-border elements act as structural verticals. There is not a single box-shadow anywhere in Lemma-authored CSS.
  • The plate system. Diagrams live inside thin #755CFE outlined rectangles with a small caption bar fused to the top edge, labeled in mono as LEM. 1.1, LEM. 2.3. The contents are plotter-style wireframes, point clouds, harmonographs, gravitational-lens fields, drawn in with stroke-dashoffset at 0.6 to 0.7 opacity. This is the figure-numbering convention of a mathematical paper, applied to a marketing page. It is the brand's most recognizable move.
  • Mono is a semantic role, not a decorative one. IBM Plex Mono appears on exactly four things: every button label, uppercase eyebrows and section labels, machine identifiers (TRC-02F, LEM. 1.1), and status words. Never on prose, never on headings.
  • Color is a state channel. Neutral is the resting state. #755CFE Lady Blue marks the healthy or active path. #EE5557 marks a regression or failure. Nothing is colored because it is important.
  • Customer logos are flattened to #6E6885 via CSS mask-image, never rendered in their own brand colors.
  • Light only. color-scheme: light, no dark tokens in Lemma's own layer, no theme switcher. The only dark-mode CSS on the site belongs to the cookie-consent and toast libraries.

Kinship, not impersonation

Most artifacts built from this file will be Zhiyuan's own projects, hackathon submissions, or integrations, not official Lemma surfaces. Keep the line visible.

You may borrow: the palette, the type scale, the square-corner rule, the hairline architecture, the mono-label idiom, the plate figure convention as a visual technique, the button recipes, the motion budget, and the starter stylesheet below.

You may not: use the Lemma symbol or logotype as a project mark, reproduce the isometric stacked-plane logo, put LEM. x.y plate numbers on work that is not Lemma's, imply Lemma authorship or endorsement, or replicate the platform.uselemma.ai app shell. If the artifact is an integration or a project built on Lemma, say so in words. A Lemma logo used as an attribution credit inside a clearly-third-party page is fine. A Lemma logo in a header position is not.

Use this priority order

When requirements compete, protect them in this order:

  1. Preserve supplied facts, metrics, agent traces, formulas, units, and customer data exactly as given. Never invent a failure rate or a trace ID that reads as real.
  2. Preserve the caller's framework, routes, and existing Lemma or Tailwind foundation.
  3. Make the reader's question and the strongest supported answer immediately clear.
  4. Establish belonging through Britti Sans, the strict type scale, square corners, and hairline structure, in that order.
  5. Choose a composition specific to this material. A plate figure must encode something real or it does not ship.
  6. Refine responsive behavior, hover, and detail without weakening hierarchy.

Foundation

Typography

--font-britti-sans: "brittiSans", "brittiSans Fallback", Arial, sans-serif;
--font-ibm-plex-mono: "IBM Plex Mono", "IBM Plex Mono Fallback", ui-monospace, monospace;

Britti Sans is a licensed typeface. Lemma's own brand page says plainly: "Typefaces are licensed and not available for download." Lemma self-hosts three OTF weights (400, 500, 600) from its own origin. You cannot lift those files. For anything that is not an official Lemma surface, license Britti Sans from its foundry or substitute a geometric-humanist grotesk with a similar single-storey feel and slightly narrow set. Lemma's own fallback declaration is local("Arial"), which tells you they accepted a plain fallback rather than a webfont substitute, so do not reach for a Google Fonts lookalike and call it Britti Sans.

IBM Plex Mono is open source under the OFL and available from Google Fonts. Use the real thing. Weights loaded: 300, 400, 500.

The type scale, from Lemma's published guidelines, verified against live rendering:

RoleSizeWeightLine heightTrackingFace
Display64px500 Medium100%-1%Britti Sans
H148px500 Medium100%-1%Britti Sans
H232px400 Regular105%-1%Britti Sans
H324px400 Regular105%-1%Britti Sans
Body L18px400 Regular130%see belowBritti Sans
Body M16px400 Regular130%see belowBritti Sans
Body S14px400 Regular130%see belowBritti Sans
Label12px400 Regular125%+0.10em to +0.15em, uppercaseIBM Plex Mono

DISCREPANCY 1, heading weight. The published guidelines assign Medium (500) to Display and H1. Every production heading on uselemma.ai renders at Regular (400) with font-normal written explicitly into the class. Both are official Lemma. Prescription: use 400 for anything that sits in a page alongside body copy, because that is what every shipped Lemma page does and mixing the two reads as inconsistent. Reserve 500 for isolated large-format display use such as a cover slide or a poster, which is the context the PDF specimen is showing.

DISCREPANCY 2, letter spacing on body. The guidelines specify -1% for body as well as headlines. Production sets a global letter-spacing: 0.32px on <body>, which is +0.02em at 16px, the opposite sign. Headings do carry real negative tracking (-0.01em and -0.02em), matching the spec. Prescription: follow production. Negative tracking at 15px to 18px in Britti Sans closes the counters and hurts legibility, which is the likely reason they changed it.

Responsive heading behavior as shipped:

  • Marketing H1: 33px at mobile, 40px from lg, 48px from 3xl, line-height: 1.1.
  • Marketing H2: 30px, 36px from md, 44px from 3xl, line-height: 1.2.
  • Article H1: 30px, 48px from md, 60px from lg, line-height: 1.1, tracking -0.02em.
  • Article H2 30px / H3 24px, both line-height: 1.15, tracking -0.01em.

Mono label tracking is not one value. Lemma uses a deliberate ladder and picks by density: 0.06em, 0.08em, 0.1em, 0.12em, 0.14em, 0.15em. Tighter for inline machine IDs, wider for standalone section eyebrows. A 10px eyebrow at 0.15em and a 12px logo-row label at 0.14em are both shipped. Never set a mono label below 0.06em and never above 0.15em.

Rules that hold everywhere:

  • Headings never exceed weight 500, and on web they are 400. There is no 600 or 700 heading anywhere on a Lemma page. Emphasis comes from size and color, not weight.
  • Every uppercase string is IBM Plex Mono. There is no uppercase Britti Sans on the site except one footer label at 12px, which is the exception that proves it. If you are about to set uppercase Britti Sans, you probably want mono.
  • Sentence case for headings and prose. Uppercase mono for labels and buttons. Never title case.
  • Prose measure is max-w-[72ch], which renders at about 721px at 18px.

Color

Lemma publishes a named palette. Use the names, they tell you intent.

Core palette (from uselemma.ai/brand):

NameHexRGBRole
whitething#F2F2F2242, 242, 242Page background, everywhere
Light Gray#D5D5D5213, 213, 213Accent light, dividers in print
Teal#58E4B688, 228, 182Positive state, sparingly
Lady Blue#755CFE117, 92, 254Primary brand anchor
bluepurplething#322C8A50, 44, 138Deep indigo, gradient stop
Admiral#1D195629, 25, 86All text, primary button fill

Lemma's own words: "Primary color: #755CFE. This is the anchor of the brand, it should appear in every application of the identity."

Palette note. The brand PDF names #F4F4F4 "Wild Sans" where the live brand page names #F2F2F2 "whitething." The production <html> background computes to #F2F2F2. Use #F2F2F2.

Neutral extensions (Lemma: "never decorative, they serve hierarchy"):

NameHex
Gray Hint#E6E6E6
New Gray#A4A4A4
Power Gray#717171
Carbon Fiber#262626
Shocking Black#111111

The tinted grays are the ones you will actually use. Production text is never neutral gray, it is always an indigo-tinted gray derived from Admiral. This is the single most-missed detail in the system.

TokenHexUse
--color-gray-tint-300#D3D3E5Faintest tinted line
--color-gray-tint-400#A1A0B8Mono metadata, inactive IDs
--color-gray-tint-500#706E8BTertiary text
--color-gray-tint-600#52506FSecondary text, dense
--color-muted-foreground / --color-gray#6C6991Nav links, captions, eyebrows
logo-row gray#6E6885Masked customer logos only

Semantic roles as shipped:

TokenValueNotes
--color-background#F2F2F2
--color-card / --color-popover#FFFFFFWhite is a surface, not the page
--color-foreground / --color-primary#1D1956
--color-primary-foreground#F2F2F2Not white
--color-brand-purple#755CFE
--color-brand-purple-deep#5D4EE7Links, shine base
--color-brand-navy-light#322C8A
--color-line / --color-border#D4D4DDThe hairline. Highest-frequency border on the site
--color-hairline#E6E6EFSofter rule
--color-hairline-soft#F3F3FCSoftest rule
--color-secondary / --color-muted#E6E6E6
neutral surface#E8E8E8Secondary button, list rows
neutral surface hover#E0E0E0The paired hover. Always this pair
--color-input#E5E5EE
--color-ring#755CFEFocus ring
--color-accent-light#D5D5D5

The three rules that actually govern color:

  1. Secondary text is Admiral at 65% opacity, not a gray. Production writes text-primary/65, which computes to oklab(0.262919 0.0168294 -0.104771 / 0.65). That is #1D1956 at 65%. Use color-mix(in oklab, #1D1956 65%, transparent) or rgb(29 25 86 / 0.65). Setting body copy to #6C6991 instead looks close and reads wrong, because the opacity version picks up the #F2F2F2 page underneath.
  2. State color is a three-part recipe, never a flat fill. Lemma renders every state chip and callout as border: 1px solid <color>/40 plus background: <color>/8 plus full-strength text. Applied to #EE5557 in production as border-[#EE5557]/40 bg-[#EE5557]/[0.08]. A left-rule variant uses border-l-2 at full strength with bg at 5%.
  3. The accent is spent on the healthy path, not on emphasis. In the trace visualizations the source reads let l = "#755CFE", s = "#EE5557", u = ["#322C8A", "#1D1956", "#5D4EE7"], which is primary, regression, and a depth ramp. Healthy lines are #2BC392, regression lines are #EE5557. Do not turn a heading, a price, or a recommendation purple because it matters.

Data and state colors (from production bundles, not the published palette, so treat as observed rather than sanctioned):

HexRole
#EE5557Regression, failure, issue. The one you will need
#2BC392Healthy series line
#58E4B6Teal, positive, from the official palette
#5D4EE7Deep purple, links and depth ramp
#322C8ADepth ramp
#A6A9B3Neutral chart gray

Colors such as #34C759, #FF3B30, #3C82F7, #1D1C1D, #1264A3 appear in the bundle but belong to simulated iOS and Slack chrome. They are not Lemma colors. Do not lift them.

Gradients. Lemma uses exactly two, both scoped:

/* Primary button hover overlay only */
linear-gradient(135deg, #322C8A 0%, #5D4EE7 50%, #1D1956 100%);

/* The logo mark itself, in the SVG. Do not reuse. */
linear-gradient(#8470FF, #2D2870);

There are no decorative gradients, no glows, no meshes, no grain. A gradient on a Lemma page is either a button hover or a mask used to fade a marquee edge.

Shape

Radius is zero. This is the rule with no exceptions.

border-radius: 0;

Every Lemma-authored button, input, chip, card, plate, list row, and callout computes 0px. Buttons carry rounded-none explicitly, which means it was a decision, not an oversight. The Tailwind radius scale is present in the stylesheet (--radius-sm through --radius-2xl) but Lemma-authored markup never reaches for it.

The only rounded corners you will find are inside deliberate simulations of other products: a rounded-[4px] Slack button, a macOS traffic-light window. If your artifact is quoting another product's UI, round it the way that product rounds it. Otherwise, zero.

There is no corner smoothing, no squircle, no corner-shape. Circles exist only as data points in SVG.

Elevation and depth

There are no shadows. The extractor found zero box-shadow declarations in Lemma-authored CSS. Every shadow token on the site belongs to the cookie-consent library.

Depth comes from four techniques instead, in order of how often Lemma uses them:

  1. Hairline rules. 1px solid #D4D4DD is the workhorse. #E6E6EF and #F3F3FC when a rule needs to recede.
  2. Surface value change. #F2F2F2 page, #E8E8E8 resting interactive surface, #FFFFFF card. Three steps, all flat.
  3. Hairline outlines instead of borders on media. Images in prose get outline: 1px solid rgb(0 0 0 / 0.1) with outline-offset: -1px, which keeps the image at its exact box while still having an edge.
  4. Half-pixel plate strokes. 0.5px solid #755CFE on plate frames, which on a retina display renders as a genuinely finer line than 1px and is part of why the plates read as drafting rather than UI.

If you feel the need for a shadow, you have a hierarchy problem. Fix it with a rule or a surface step.

Layout

Breakpoints, as configured:

NameMin width
sm640px / 40rem
md768px / 48rem
lg1024px / 64rem
xl1280px / 80rem
2xl1536px / 96rem
3xl1920px / 120rem (custom, Lemma-added)

The 3xl breakpoint is real and used for genuine large-display scaling: H1 goes 40 to 48, H2 goes 36 to 44, button text goes 11 to 13. Build it in.

Container widths as shipped:

  • Main content column: max-w-7xl (1280px), widening to a custom max-w-8xl at 3xl.
  • The content column carries xl:border-l xl:border-r border-line, so it is a visibly ruled measure, not an invisible container.
  • Narrow reading column inside sections: max-w-[1040px] px-6.
  • Centered hero copy: max-w-2xl px-4 sm:px-6.
  • Full-bleed hero plate field: max-w-[1480px].
  • Prose: max-w-[72ch].

Section separation is border-t border-line plus vertical padding, never a card and never a background change. A typical section is relative z-[1] border-t border-line bg-background with pb-20 md:pb-28.

Spacing is Tailwind's 0.25rem base. Lemma adds one custom token, --spacing-subhead: 0.5rem, exposed as mb-subhead and mt-subhead, used exclusively for the gap between a section heading and its supporting paragraph. That is the only bespoke spacing value in the system, which tells you how much they cared about that one relationship.

Long-form vertical rhythm, from the prose config: h2 gets margin-top: 6rem and margin-bottom: 1.5rem; h3 gets 3rem and 1rem. Those are large and deliberate. Do not compress them.

Motion

/* Durations, by frequency of use */
--duration-fast:    150ms;  /* all interactive state changes */
--duration-medium:  300ms;  /* overlays, opacity reveals */
--duration-slow:    400ms;  /* entrance transitions */

/* Easing */
--ease-out:     cubic-bezier(0, 0, 0.2, 1);      /* the default */
--ease-in-out:  cubic-bezier(0.4, 0, 0.2, 1);
--ease-draw:    cubic-bezier(0.33, 1, 0.68, 1);  /* the shine sweep */

150ms ease-out is the house transition and covers essentially every hover and focus change. cubic-bezier(0.34, 1.56, 0.64, 1), an overshoot curve, appears twice on the entire site. Treat it as effectively unused.

What moves:

  • Buttons press. active:scale-[0.96] on every button, transitioning color, background-color, box-shadow, scale at 150ms ease-out. This is small and it is the detail that makes the UI feel like theirs.
  • The shine sweep. .lemma-shine puts a 250%-wide linear-gradient(100deg, ...) behind text with background-clip: text, then animates background-position over 0.9s cubic-bezier(0.33, 1, 0.68, 1) on hover of .lemma-shine-host. Base color #5D4EE7, glint #C9C2FF. One sweep, once, on hover.
  • Plates draw themselves in via stroke-dashoffset: 100 to 0, settling at opacity: 0.6 to 0.7. Named keyframes in production: lemma-gravlens-draw, lemma-harmograph-draw, lemma-sphere-draw, lemma-cube-draw, lemma-cube-flip, lemma-fade-in, lemma-blog-fade-up, lemma-reel, lemma-scroll-pulse.
  • Scroll-driven sticky sections. The failure-taxonomy section pins with sticky top-0 h-screen and advances state as you scroll.

What does not move: nothing bounces, nothing parallaxes, no section reveals on scroll as a decoration, no auto-playing marquee carries meaning, no typing cursors.

Every single animated component on the site ships a @media (prefers-reduced-motion: reduce) block that sets animation: none !important. This is not optional garnish in this system. If you add a Lemma-style animation, you add the reduced-motion block with it.

Components

Buttons

One base recipe, two variants, no third.

Base:      inline-flex items-center justify-center gap-2
           font-mono text-[11px] 3xl:text-[13px] uppercase font-normal
           px-4 py-2   rounded-none   whitespace-nowrap
           transition-[color,background-color,box-shadow,scale] duration-150 ease-out
           active:scale-[0.96]
           focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#755CFE] focus-visible:ring-offset-2
           disabled:pointer-events-none disabled:opacity-50
           [&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:pointer-events-none
VariantBackgroundTextHover
Primary#1D1956#FFFFFFlinear-gradient(135deg, #322C8A, #5D4EE7 50%, #1D1956) overlay fading opacity: 0 to 1 over 300ms ease-out
Secondary#E8E8E8#1D1956#E0E0E0

The primary hover is implemented as a group with overflow-hidden and an absolute inset-0 gradient span at opacity-0 group-hover:opacity-100, with the label in a sibling relative z-10 span. Reproduce it that way rather than transitioning background-image, which does not interpolate.

Compact size, used in mobile nav: px-3 py-2 text-[10px]. Generous size, used in page CTAs: px-5 py-2.5 text-[11px].

Real labels from the site: BOOK A DEMO, LOGIN, READ THE DOCS, ALL POSTS. Always uppercase, always mono, always short.

List rows

Lemma's answer to a card grid. Used for job listings and index pages.

flex items-center gap-4 md:gap-8
bg-[#E8E8E8] p-3 pr-5
transition-colors hover:bg-[#E0E0E0]

Square corners, no border, no shadow, primary label at 16px Britti Sans, secondary metadata right-aligned in #6C6991. Note it reuses the exact same #E8E8E8 / #E0E0E0 pair as the secondary button. That pairing is a system, use it for any neutral interactive surface.

Plate figures

The signature component. Structure as shipped:

<figure class="flex flex-col">
  <!-- caption bar, fused to the top of the frame -->
  <div class="flex shrink-0 items-center px-2.5 py-1.5 border border-[#755CFE]">
    <span class="font-mono text-[10px] text-[#755CFE]">LEM. 1.1</span>
  </div>
  <!-- body, borders on three sides so the caption bar's bottom edge is shared -->
  <div style="border-left:0.5px solid #755CFE;
              border-right:0.5px solid #755CFE;
              border-bottom:0.5px solid #755CFE;">
    <svg><!-- plotter-style wireframe, stroke #755CFE, opacity 0.6-0.7 --></svg>
  </div>
</figure>

A minimal variant drops the label and uses a bare height: 6px top strip with all four borders, which reads as a tab.

Rules for plates:

  • A plate must encode something real. A harmonograph standing in for "iteration," a lens field for "convergence," a point cloud for a trace distribution. A plate that is pure ornament breaks the entire premise of the system.
  • Stroke is #755CFE at 0.5px to 1px. Fill is transparent or the page background. Never a solid purple plate.
  • The label is mono, 10px, #755CFE, and it is either a plate number or a very short caption. Never a sentence.
  • Plates are scattered at absolute positions around a centered hero, not arranged in a tidy grid. The careers page uses explicit xl:absolute xl:left-[2%] xl:top-[49.2%] percentage placement inside an aspect-[940/465] frame, collapsing to a lg:grid-cols-3 stack below xl.

State chips and callouts

/* inline chip */
border border-[#EE5557]/40  bg-[#EE5557]/[0.08]
px-2 py-0.5  font-mono text-[10px] uppercase  text-[#EE5557]

/* callout */
border-l-2 border-[#EE5557]  bg-[#EE5557]/[0.05]  px-3 py-2.5

Swap #EE5557 for #755CFE or #2BC392 for other states. The 40 / 8 / 100 opacity ratio holds.

Trace blocks

The failure-taxonomy field: a staggered grid of square-cornered blocks, each 1px solid outlined, carrying a mono ID (TRC-02F) in #A1A0B8. Neutral blocks are outlined only. Failure blocks fill #EE5557 with white text, an uppercase mono category line, and the offending output in quotes. The visual argument is that failures are a small colored minority inside a large neutral field, which is exactly the product's claim.

Long-form prose

Complete theme, verbatim from the production Tailwind Typography config:

ElementTreatment
Measuremax-w-[72ch], centered
Body18px, line-height: 1.6, #1D1956
Lede24px, line-height: 1.375, #1D1956
Headingsline-height: 1.15, tracking: -0.01em, text-balance, #1D1956
h2margin-top: 6rem, margin-bottom: 1.5rem
h3margin-top: 3rem, margin-bottom: 1rem
Links#5D4EE7, weight 400, underline, underline-offset: 0.15em, decoration at 40% opacity, going to full opacity on hover
Bold#1D1956, same as body
Inline codebackground: #E6E6E6 at 70%, px-1.5 py-0.5, 0.9em, weight 400, backtick pseudo-elements removed
Blockquoteborder-left: 2px solid #755CFE, background: #FFFFFF, px-5 py-4, not italic, weight 400, #1D1956, smart quote pseudo-elements removed
Bullets and counters#6C6991
Captions#6C6991
hr, th, td borders#D4D4DD
Imagesoutline in rgb(0 0 0 / 0.1), outline-offset: -1px, no radius
Paragraphs and list itemstext-pretty

Note the two removals. Lemma strips Tailwind Typography's decorative backtick marks around inline code and its curly-quote marks around blockquotes. Both are default ornaments and both were deleted. That is the taste of this system in miniature.

Article eyebrow: mono, 10px, tracking: 0.15em, uppercase, #6C6991, margin-bottom: 1rem. Back link: mono, 10px, tracking: 0.1em, uppercase, #6C6991, hovering to #755CFE.

Header: logo left as symbol plus wordmark, center nav in Britti Sans at 14px #6C6991 hovering to #1D1956, right pair of LOGIN secondary and BOOK A DEMO primary. No border under it, no blur, no shadow.

Announcement bar: full-bleed #755CFE, white text, centered, with a dismiss X. This is the only place Lady Blue is used as a large background field on the marketing site, and it earns it by being temporary and dismissible.

Footer: product name and one-line description left, then RESOURCES and SOCIAL columns under uppercase 12px headings, copyright line, contact email. Section headings in the footer are the one uppercase Britti Sans exception on the site.

Voice and copy

Sentence case for headings and prose. Uppercase for mono labels and buttons. Never title case.

Real headlines, which show the register better than any description: "Stop guessing why your agents fail." "Agents in production fail in unpredictable ways." "Surface failures you didn't know to look for." "Get notified on what matters." "Fix it where you work." "Ship with confidence." "Trust is non-negotiable." "Every release, documented." "Help us build the future of continual learning."

The pattern: short declarative sentence, ends in a period, names a concrete failure or a concrete action. Second person is used freely. Contractions are used freely. No hype adjectives, no "revolutionary," no "powerful."

Hard rules:

  • No em dashes. Lemma's own guidelines PDF uses one in prose, the production site does not. Use commas, periods, or parentheses.
  • Body copy after a heading is one or two sentences, not a paragraph.
  • Mono labels are one or two words: ISSUES, LIVE ALERTS, AGENTS, COMING SOON, TRUSTED BY TEAMS SHIPPING AGENTS.
  • Trace and plate identifiers follow a fixed shape: LEM. <section>.<index> for figures, TRC-<3 hex> for traces. Keep the format if you use them, change the prefix if the work is not Lemma's.

Starter stylesheet

Drop-in tokens using Lemma's own variable names, so future live inspection of uselemma.ai maps onto this file one-to-one.

/* Lemma design tokens
   Extracted from uselemma.ai on 2026-09-11.
   Sources: uselemma.ai/brand, lemma-brand-guidelines.pdf, live production CSS.
   Light only. Lemma ships no dark theme. */

:root {
  /* Type */
  --font-britti-sans: "brittiSans", Arial, sans-serif;   /* licensed, self-host only */
  --font-ibm-plex-mono: "IBM Plex Mono", ui-monospace, monospace;

  --type-display: 64px;
  --type-h1:      48px;
  --type-h2:      32px;
  --type-h3:      24px;
  --type-body-l:  18px;
  --type-body-m:  16px;
  --type-body-s:  14px;
  --type-label:   12px;

  --leading-heading: 1.1;
  --leading-h2:      1.2;
  --leading-prose:   1.6;
  --leading-body:    1.3;

  --tracking-heading: -0.01em;
  --tracking-display: -0.02em;
  --tracking-body:     0.02em;   /* production, not the PDF spec. See DISCREPANCY 2 */
  --tracking-label:    0.1em;    /* ladder: 0.06 0.08 0.1 0.12 0.14 0.15 */

  --weight-regular:  400;
  --weight-medium:   500;        /* large-format display only */
  --weight-semibold: 600;        /* exists in the family, unused on web */

  /* Core palette */
  --lemma-whitething:  #f2f2f2;
  --lemma-light-gray:  #d5d5d5;
  --lemma-teal:        #58e4b6;
  --lemma-lady-blue:   #755cfe;
  --lemma-bluepurple:  #322c8a;
  --lemma-admiral:     #1d1956;

  /* Neutral extensions */
  --lemma-gray-hint:      #e6e6e6;
  --lemma-new-gray:       #a4a4a4;
  --lemma-power-gray:     #717171;
  --lemma-carbon-fiber:   #262626;
  --lemma-shocking-black: #111111;

  /* Semantic, as named in production */
  --color-background:        #f2f2f2;
  --color-foreground:        #1d1956;
  --color-primary:           #1d1956;
  --color-primary-foreground:#f2f2f2;
  --color-card:              #ffffff;
  --color-popover:           #ffffff;
  --color-secondary:         #e6e6e6;
  --color-muted:             #e6e6e6;
  --color-muted-foreground:  #6c6991;
  --color-brand-purple:      #755cfe;
  --color-brand-purple-deep: #5d4ee7;
  --color-brand-navy-light:  #322c8a;
  --color-line:              #d4d4dd;
  --color-border:            #d4d4dd;
  --color-hairline:          #e6e6ef;
  --color-hairline-soft:     #f3f3fc;
  --color-input:             #e5e5ee;
  --color-ring:              #755cfe;
  --color-accent-light:      #d5d5d5;

  /* Indigo-tinted grays. Use these, not neutral grays */
  --color-gray-tint-300: #d3d3e5;
  --color-gray-tint-400: #a1a0b8;
  --color-gray-tint-500: #706e8b;
  --color-gray-tint-600: #52506f;

  /* Interactive neutral surface pair */
  --surface-neutral:       #e8e8e8;
  --surface-neutral-hover: #e0e0e0;

  /* State */
  --state-regression: #ee5557;
  --state-healthy:    #2bc392;
  --state-positive:   #58e4b6;
  --chart-neutral:    #a6a9b3;

  /* Shape. There is exactly one value */
  --radius: 0;

  /* Motion */
  --duration-fast:   150ms;
  --duration-medium: 300ms;
  --duration-slow:   400ms;
  --ease-out:    cubic-bezier(0, 0, 0.2, 1);
  --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
  --ease-draw:   cubic-bezier(0.33, 1, 0.68, 1);

  /* Layout */
  --container-content: 80rem;   /* max-w-7xl */
  --container-reading: 1040px;
  --container-prose:   72ch;
  --spacing-subhead:   0.5rem;
  --breakpoint-3xl:    120rem;
}

html { background: var(--color-background); color-scheme: light; }

body {
  font-family: var(--font-britti-sans);
  font-size: var(--type-body-m);
  font-weight: var(--weight-regular);
  line-height: 1.5;
  letter-spacing: var(--tracking-body);
  color: var(--color-foreground);
  background: var(--color-background);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 {
  font-weight: var(--weight-regular);
  letter-spacing: var(--tracking-heading);
  line-height: var(--leading-heading);
  color: var(--color-foreground);
  text-wrap: balance;
}

h1 { font-size: clamp(33px, 4vw, var(--type-h1)); }
h2 { font-size: clamp(30px, 3vw, var(--type-h2)); line-height: var(--leading-h2); }
h3 { font-size: var(--type-h3); line-height: 1.15; }

.lemma-secondary-text { color: rgb(29 25 86 / 0.65); }   /* never a flat gray */

.lemma-label {
  font-family: var(--font-ibm-plex-mono);
  font-size: 10px;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-muted-foreground);
}

.lemma-section { border-top: 1px solid var(--color-line); padding-block: 5rem 7rem; }

.lemma-measure {
  max-width: var(--container-content);
  margin-inline: auto;
}
@media (min-width: 80rem) {
  .lemma-measure {
    border-left: 1px solid var(--color-line);
    border-right: 1px solid var(--color-line);
  }
}

/* Buttons */
.lemma-btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 0.5rem 1rem;
  border: 0;
  border-radius: var(--radius);
  font-family: var(--font-ibm-plex-mono);
  font-size: 11px;
  font-weight: var(--weight-regular);
  text-transform: uppercase;
  white-space: nowrap;
  overflow: hidden;
  cursor: pointer;
  transition: color var(--duration-fast) var(--ease-out),
              background-color var(--duration-fast) var(--ease-out),
              scale var(--duration-fast) var(--ease-out);
}
.lemma-btn:active { scale: 0.96; }
.lemma-btn:focus-visible {
  outline: 2px solid var(--color-ring);
  outline-offset: 2px;
}
@media (min-width: 120rem) { .lemma-btn { font-size: 13px; } }

.lemma-btn--primary { background: var(--color-primary); color: #fff; }
.lemma-btn--primary::before {
  content: "";
  position: absolute;
  inset: 0;
  opacity: 0;
  background: linear-gradient(135deg, #322c8a 0%, #5d4ee7 50%, #1d1956 100%);
  transition: opacity var(--duration-medium) var(--ease-out);
}
.lemma-btn--primary:hover::before { opacity: 1; }
.lemma-btn--primary > * { position: relative; z-index: 10; }

.lemma-btn--secondary { background: var(--surface-neutral); color: var(--color-primary); }
.lemma-btn--secondary:hover { background: var(--surface-neutral-hover); }

/* Interactive list row */
.lemma-row {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0.75rem 1.25rem 0.75rem 0.75rem;
  background: var(--surface-neutral);
  border-radius: var(--radius);
  transition: background-color var(--duration-fast) var(--ease-out);
}
.lemma-row:hover { background: var(--surface-neutral-hover); }

/* State chip */
.lemma-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.125rem 0.5rem;
  border: 1px solid rgb(238 85 87 / 0.4);
  background: rgb(238 85 87 / 0.08);
  color: var(--state-regression);
  font-family: var(--font-ibm-plex-mono);
  font-size: 10px;
  text-transform: uppercase;
  border-radius: var(--radius);
}

/* Plate figure */
.lemma-plate { display: flex; flex-direction: column; }
.lemma-plate__caption {
  display: flex;
  align-items: center;
  padding: 0.375rem 0.625rem;
  border: 1px solid var(--color-brand-purple);
  font-family: var(--font-ibm-plex-mono);
  font-size: 10px;
  color: var(--color-brand-purple);
}
.lemma-plate__body {
  border-left: 0.5px solid var(--color-brand-purple);
  border-right: 0.5px solid var(--color-brand-purple);
  border-bottom: 0.5px solid var(--color-brand-purple);
}
.lemma-plate svg { stroke: var(--color-brand-purple); fill: none; opacity: 0.7; }

/* Shine sweep */
.lemma-shine {
  --shine-base: var(--color-brand-purple-deep);
  --shine-glint: #c9c2ff;
  color: var(--shine-base);
  background-image: linear-gradient(100deg,
    var(--shine-base) 0%, var(--shine-base) 46%,
    var(--shine-glint) 50%,
    var(--shine-base) 54%, var(--shine-base) 100%);
  background-size: 250% 100%;
  background-position: 100% 0;
  background-repeat: no-repeat;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}
@keyframes lemma-shine { to { background-position: -150% 0; } }
.lemma-shine-host:hover .lemma-shine {
  animation: lemma-shine 0.9s var(--ease-draw) 1;
}

@media (prefers-reduced-motion: reduce) {
  .lemma-shine-host:hover .lemma-shine,
  .lemma-plate svg { animation: none !important; }
  .lemma-btn { transition: none; }
}

Reject these reflexes

Each of these breaks belonging on sight.

  • Any border radius at all. Rounded buttons, rounded cards, rounded-lg, pills, capsules. The system is square. This is the fastest possible tell.
  • Box shadows. There are none. A shadow on a Lemma-style page is an admission that the hierarchy failed.
  • A card grid. Three equal bordered boxes under a centered heading is the exact composition Lemma never uses. Use a ruled section, a list of flat rows, or a plate field.
  • Neutral gray body text. Secondary text is Admiral at 65% opacity. #666, #888, and text-gray-500 all read as a different brand.
  • A dark theme. Lemma ships light only and has no toggle. Do not invent one, and do not add a theme switcher.
  • Bold headings. No heading exceeds 500, and on web every one is 400. font-bold on an H1 is wrong.
  • Uppercase Britti Sans. Uppercase belongs to IBM Plex Mono, with tracking.
  • Mono used as body copy or headings. It is a label and identifier face, not a display face.
  • Decorative gradients, glows, meshes, blobs, grain, glass, or noise overlays. The only gradients are one button hover and the logo mark.
  • Purple used for emphasis. Lady Blue marks state and the healthy path. A purple heading or a purple price is a misuse of the one accent the brand has.
  • Full-color customer logos. They are masked to #6E6885.
  • A plate that encodes nothing. A wireframe sphere floating next to unrelated copy is decoration wearing the costume of evidence, which is the exact opposite of what a lemma is.
  • Fabricated trace IDs, failure rates, or metrics used as visual filler. If the artifact shows agent data, it either shows real data or it is visibly labeled as an example.
  • LEM. x.y plate numbers on non-Lemma work. Change the prefix.
  • Tailwind Typography defaults left on. Lemma strips the backticks around inline code and the curly quotes around blockquotes, and un-italicizes blockquotes. Leaving those on is visible.
  • Animation without a prefers-reduced-motion block. Every animated component on the site has one.
  • Em dashes.
  • Title Case Headings. Sentence case, always.

Do not overcorrect into a sterile black-and-white wireframe either. Lemma's restraint is precise hierarchy, real evidence, exact alignment, and one accent used with intent. It is not the absence of design.

Review checklist

Run this against a finished page.

  1. Squint test. Is there one dominant object per viewport, and is the hierarchy still legible with the words blurred?
  2. Corners. Zoom to 400% on every button, input, chip, row, image, and container. Is every corner square? Is the only exception a deliberate quote of another product's UI?
  3. Shadows. Search the computed styles for box-shadow. Should be zero.
  4. Type roles. Is every size drawn from the eight-role scale? Is every heading weight 400? Is every uppercase string IBM Plex Mono with tracking between 0.06em and 0.15em?
  5. Secondary text. Is it #1D1956 at 65%, or did a neutral gray sneak in?
  6. Accent spend. Every use of #755CFE: does it mark a state, a healthy path, a link, or a plate stroke? If it is marking importance, remove it.
  7. Structure. Do sections separate with 1px #D4D4DD rules rather than cards or background changes? Does the content column carry its left and right rails at xl?
  8. Evidence. Does every plate, chart, and trace block encode something real, on a stated scale, with honest units? Is any number invented?
  9. Motion. Is every transition at 150ms ease-out unless it is an overlay at 300ms? Does every animation have a prefers-reduced-motion: reduce block?
  10. Long-form. Is the measure 72ch? Are links #5D4EE7 underlined at 40% decoration opacity? Are the Tailwind Typography backticks and curly quotes stripped?
  11. Responsive. Does it recompose at 640, 768, 1024, 1280, 1536, and 1920 without overflow, character-level wrapping, or a stranded plate?
  12. Accessibility. One descriptive h1, ordered headings, landmarks, a skip link, native controls, semantic tables, visible 2px #755CFE focus rings at 2px offset, WCAG AA contrast, and no meaning carried by color alone. Measured ratios against the #F2F2F2 page, so you do not have to guess:
ForegroundRatio on #F2F2F2Verdict
#1D1956 Admiral14.15:1Passes everything
#5D4EE7 purple deep5.06:1Passes AA for body text. This is why links are this color and not Lady Blue
#6C6991 muted4.61:1Passes AA for body text
#755CFE Lady Blue3.98:1Large text and UI boundaries only. Fails AA for small body copy
#EE5557 regression3.09:1Large text and UI boundaries only
#A1A0B8 gray-tint-4002.28:1Fails AA entirely. Lemma uses it for inert machine IDs. Never let it carry meaning alone

White on #1D1956 is 15.84:1 and Admiral on #E8E8E8 is 12.93:1, so both button variants are clear.

  1. Identity. If this is not an official Lemma surface: is the Lemma mark absent from any header position, is authorship unambiguous, and are the plate numbers re-prefixed?

The target is Lemma's judgment, not Lemma's decoration.

design.md/curated-supply-design.md

Curated Supply

extracted from curated.supply on 2026-09-18

One type size does almost everything. Every visible run but four is 14px/20px, so hierarchy comes from colour, not size, and one gold word carries the whole editorial hand.

335 lines Geist + Geist Mono skills/user/curated-supply-design
examples/curated/example-discover.html Editorial hero with a working newsletter pill, then a flat unranked grid of 19 objects with category chips, live search, brand filtering from the meta line, sort, saved items, a brand index and Show More. open full page ↗
examples/curated/components.htmlopen full page ↗

Design surfaces like Curated Supply

Act as an excellent Curated Supply designer and design engineer. The standard is that Justin Farrugia could not tell your page from one he shipped. That comes from four things and almost nothing else: Geist at 14px for nearly all text, a three-tone grey paper system (#f2f2f2 page, #ffffff plates, #141414 ink), pill geometry on every interactive element, and one gold word as the only hierarchy signal in an otherwise flat grid.

Curated Supply is a curated product-discovery site plus a weekly newsletter. Its editorial premise is "quiet luxury": well-made objects that endure, shown flat and unranked, with a $50 kettle scale next to a $270,000 car. The design does the same job the premise does. Nothing shouts, nothing is decorated, and taste is communicated by restraint rather than by chrome.

Provenance

Everything below traces to live CSS extraction from www.curated.supply on September 18, 2026, read from four pages: / (Discover grid + hero), /products/studio-display (detail page), /about (long-form copy, contact form), and /browse. The site is built in Framer (--framer-* custom properties, --token-<uuid> color tokens, Framer form and search components). There is no published brand page, design.md, or style guide. Nothing here comes from Brandfetch, Dribbble, or any third-party kit.

Rules for using this file:

  • On any conflict between this file and the live site, the live site wins. This is a dated snapshot.
  • Framer emits many hidden 12px sans-serif elements and default-blue rgb(0,0,238) link colors on wrapper nodes. Those are Framer defaults, not design decisions. Every value in this file was read from visible, text-bearing or surface-bearing elements only.
  • Values marked unconfirmed were declared in CSS but not observed changing on the live page.

Curated Supply product and brand context

The About page states the standard in plain declaratives, and they are the design brief:

"This website exists because the world doesn't need more stuff, it needs better stuff."

"It's not about how things look, but how they endure. Not status objects, but standards. Not what they do, but how well it's done."

"Design that respects your time and attention."

The distinctive material choices, each one actionable:

  • One type size does almost everything. Of every visible text run on the home page, all but four are 14px / 20px Geist. The exceptions are exactly one 40px H1, one 16px subline, and the 24px/32px headings on detail pages. Hierarchy comes from color (#141414 vs #737373), not from size.
  • Two weights only: 400 and 500. 500 is spent on button labels, the active filter chip, and detail-page headings. There is no 600 or 700 anywhere.
  • Mono is for numbers. Geist Mono Variable appears on exactly one thing: prices ($1,599), with letter-spacing: 0.14px. Never on labels, never on headings, never as decoration.
  • Three greys make the whole surface. Page #f2f2f2, plates #ffffff, ink #141414, secondary #737373, hairline #e2e2e2. There is not a single gradient, glow, or drop shadow. The only box-shadow on the site is a 1px inset ring used as a chip border.
  • Everything interactive is a pill. Inputs 40px, buttons 28px, chips 32px. Plates (cards, image mounts) are 16px and 24px. Nothing is square, nothing is 4px or 6px.
  • One gold word is the entire editorial hierarchy. Staff Pick in #b99651 (with a small star glyph) sits top-left inside a card plate. There is no "featured" size, no accent border, no badge background. That single colored word is how the editor's hand shows.
  • The meta line is a navigation device. Under every image: Brand · Category as two separate links in #737373, then Name in #141414 on the left and $Price in mono on the right. Every card is three affordances in one.

Kinship, not impersonation

This file governs work that should belong to the Curated Supply material family (a feed, a directory, a product grid, an editorial index) while being its own product. Borrow freely: the type rule, the three-grey paper system, pill geometry, the card anatomy, the Brand · Category meta idiom, the single-accent-word hierarchy, the flat unranked grid. Do not borrow: the "Curated Supply" wordmark, Justin's About copy, the category or list taxonomy, or the affiliate disclosure language.

Use this priority order

  1. Content legibility. 14px/20px Geist at #141414 on #f2f2f2 or #ffffff. Never drop below #737373 for text that must be read.
  2. The user's task. A feed must feel like a feed; do not force the product-card anatomy onto content that is not a product.
  3. Belonging. Type rule, greys, pills, the meta line, one accent word.
  4. Host framework. Keep native form controls and focus rings working; Framer removes the outline but the site never relied on it.
  5. Polish. Only after the above. There is very little polish to add; the idiom is deliberately plain.

Foundation

Typography

--font-sans: "Geist Variable", "Geist", "Geist Placeholder", sans-serif;
--font-mono: "Geist Mono Variable", "Geist Mono", monospace;

The site loads Geist and Geist Mono from Google Fonts (fonts.gstatic.com/s/geist/v5/..., static 400 and 500 plus a variable face). A Framer search modal also loads Fragment Mono, which is never used in visible content and is not part of the system. Self-hosting Geist via geist npm or @fontsource-variable/geist is equivalent.

RoleSize / lineWeightTrackingColorWhere
Display40px / 44px400-0.8px#141414one H1 per page ("Discover well-designed, carefully curated products")
Section heading32px / 35.2px500-0.64px#141414H3 on detail and about pages ("About", "Contact")
Detail price24px / 26.4px500normal#141414H5 on product page
Lede16px / 24px4000.16px#737373 (about page uses #666666)hero subline, product description
Body / UI14px / 20px400normal or 0.14px#141414 primary, #737373 secondaryeverything else
Button / active14px / 20px5000.14px#141414 or #ffffffSubscribe, Purchase Link, active chip
Number14px / 20px mono4000.14px#141414prices in cards

Rules:

  • Bold is 500, never 600 or 700. The display H1 is 400.
  • Display and section headings carry negative tracking (-0.02em of their size). Body and UI carry none or +0.01em. Do not track body text negative.
  • Line height for 14px is exactly 20px; for 16px exactly 24px. Both are 1.43 and 1.5 respectively, not a shared ratio.
  • Do not introduce a 12px, 13px, 18px or 20px size. If a label feels like it needs to be smaller, make it #737373 instead.

Color

Framer tokens as resolved on the live site. Names are descriptive; Framer stores them as UUIDs.

Token (descriptive)ValueRole
paper#f2f2f2page background, inactive chip fill, textarea fill on about
paper-soft#f5f5f5secondary surface (rare)
plate#ffffffcard plates, image mounts, input pills, "Show More" button
plate-near#fdfdfddeclared, not observed in use
hairline#e2e2e2footer rule, chip inset ring, disclosure rule
hairline-soft#e8e8e8, #ebebebdeclared secondary rules
ink#141414primary text, primary button fill
ink-soft#2e2e2edeclared, not observed in use
muted#737373secondary text, meta links, placeholder-level labels
muted-deep#666666about-page lede
disabled#ccccccdeclared
gold#b99651Staff Pick text and star. The only chromatic color on the site.
scrim#0c0c0c3320% ink overlay for modals

Rules that govern usage:

  • Light only. color-scheme: normal, no dark tokens, no theme switch. The prefers-color-scheme: dark media query in the stylesheet is Framer boilerplate. Do not add a dark mode to belong here.
  • Hierarchy is #141414 vs #737373. Primary text and the primary action are ink. Everything supporting is muted. There is no third text tone in common use.
  • Surfaces stack in exactly two steps: paper under plate. Never plate on plate, never a third grey panel.
  • Gold is spent on one word per card, at most, and never as a fill. No gold borders, no gold buttons, no gold icons other than the star beside "Staff Pick".
  • Link hover goes from #737373 to #141414 (--framer-link-hover-text-color: #141414, transition: color 0.4s). No underline appears on hover. Ink-colored links do not change on hover.
  • Zero gradients. Zero drop shadows. Zero glows.

Shape

RadiusUse
40pxtext inputs (height 40px, so a full pill)
32pxfilter chips (height 36px, full pill)
28pxbuttons (height 36px to 40px, full pill)
24pxlarge image plate on detail pages (1368 x 720 with 40px inner padding)
16pxcard plate, textarea
8pximage inside a card plate

corner-shape: superellipse(1) is Framer's default, which is a plain circular corner. No squircles. No 4px or 6px anywhere.

Elevation and depth

There is none. The only box-shadow on the site is the inactive chip ring:

box-shadow: rgb(226, 226, 226) 0px 0px 0px 1px inset;  /* #e2e2e2 hairline, inset so layout does not shift */

Depth comes from #ffffff plates on #f2f2f2 paper and from nothing else. Borders are never used for cards. Inputs and buttons have no border and no ring at rest.

Layout

  • Page gutter 36px at a 1440 viewport (outer 20px + inner 16px). Content width 1368px.
  • Product grid: 3 columns, 16px gap, columns 445.33px each. Card plate is 445 x 447.
  • Hero text is centered in a 540px measure (x=450 at 1440). Newsletter form is 428px wide, centered.
  • Detail page: H1 left-aligned at the gutter, then a full-width 24px-radius white plate holding the hero image with 40px padding, then a 540px description column.
  • Sections separate by whitespace and one #e2e2e2 hairline above the footer. No section backgrounds.
  • Breakpoints from the stylesheet: <= 809.98px (phone), 810px to 1199.98px (tablet), >= 1200px (desktop).

Motion

--ease: cubic-bezier(0.44, 0, 0.56, 1);   /* symmetric ease-in-out */
--duration: 0.4s;                          /* 92 of 94 declared transitions */
--duration-fast: 0.3s;                     /* 2 declared */

Only color transitions on links were observed. No transform on hover, no scale on cards, no image zoom, no fade-in on scroll. The one keyframe on the site (__framer-blink-input) is a Framer search-modal internal. Reduced-motion is moot because nothing moves.

Unconfirmed: Framer components declare transition: all on wrappers; nothing was observed animating other than link color.

Components

Card (product / item)

The most copied and most misapplied element. Recipe at 1440:

plate      445 x 447, #ffffff, radius 16px, overflow hidden, no shadow, no border
image link padding 24px 16px 8px; image mount 413 x 355, radius 8px, object-fit contain
           (product on transparent or white, never a photo crop)
badge      absolute 16px / 16px inside the image link: star glyph + "Staff Pick", 14px Geist 400, #b99651
meta row   padding 0 16px 16px; flex row, gap 16px, justify space-between
  left col flex column, gap 4px
    line 1 "Brand" link #737373 · "Category" link #737373  (the dot is its own 14px #737373 text, gap 6px)
    line 2 "Name" 14px #141414, single line, overflow clip
  right    "$1,599" 14px Geist Mono 400 #141414, tracking 0.14px

Judgment: the card is a plate, not a bordered box. The image gets its own 8px radius inside the 16px plate so the two radii read as nested. The name never wraps. If content has no price, leave the right column empty rather than inventing a number-shaped stand-in.

Filter chip

inactive   height 36px, padding 8px 12px 8px 10px, #f2f2f2 fill, radius 32px,
           inset ring 1px #e2e2e2, label 14px Geist Variable 400 #737373 tracking 0.14px
active     same box, #ffffff fill, no ring, label #141414

The active chip is the white one on grey paper. Do not invert to a dark chip.

Buttons

VariantRecipeLabel
Primary#141414 fill, #ffffff text, radius 28px, padding 8px 12px, height 36px, gap 4px for a trailing arrow"Purchase Link", "Send message"
Secondary#ffffff fill, #141414 text, radius 28px, padding 8px 12px, height 40px"Show More"
Inline texttransparent, #141414, 500, sits inside an input pill"Subscribe"

Labels are 14px, weight 500, tracking 0.14px. Sentence case ("Show More" is the one title-case exception on the site; prefer sentence case for new labels).

Inputs

text input  wrapper #ffffff, radius 40px, padding 10px 12px, height 40px, no border, no shadow
            input 14px Geist 400 #141414, placeholder #737373 ("name@email.com", "Your name")
textarea    #ffffff, radius 16px, height 148px
focus       Framer sets outline: none and swaps in --framer-input-focused-* (values unset on this site),
            so visually nothing changes. For accessibility, add a 1px inset ring in #141414 on
            :focus-visible; this is the one place to improve on the source.
inline form the newsletter field holds a transparent "Subscribe" text button flush right inside the pill

Navigation

Top bar: wordmark left, then "Discover / Browse / Blog / About" as 14px Geist Variable links, padding 6px 12px, height 36px, current page #141414, others #737373. Small centered eyebrow "Updated every Sunday" in #737373 above the H1. No sticky header, no blur, no border.

Hairline #e2e2e2 full width, then four columns of 14px links (Navigation, Info, Categories, Brands, Lists) with headings in #141414 and links in #737373, disclosure paragraph in #737373, then © 2026 ... · Made by @handle.

Voice and copy

  • Sentence case everywhere except the historical "Show More".
  • Headings are single declarative sentences without a period: "Discover well-designed, carefully curated products", "Built for those who care what they bring into their lives".
  • The About page uses short contrastive declaratives: "Not status objects, but standards." Reuse the form, not the words.
  • First person singular from the editor ("I back what I share"). Social proof is a number in a sentence ("join 4,500+ readers"), never a badge.
  • Real labels: Discover, Browse, Blog, About, Subscribe, Show More, Purchase Link, Send message, Sort by: Featured, Staff Pick, All, Tech, Workspace, Home, Carry, Books.
  • No em dashes, no emoji, no exclamation marks in UI copy.

Starter stylesheet

/* Extracted from www.curated.supply on 2026-09-18. Framer site; token names are descriptive. */
:root {
  color-scheme: light;

  --font-sans: "Geist Variable", "Geist", sans-serif;
  --font-mono: "Geist Mono Variable", "Geist Mono", monospace;

  --paper: #f2f2f2;
  --plate: #ffffff;
  --hairline: #e2e2e2;
  --ink: #141414;
  --muted: #737373;
  --muted-deep: #666666;
  --gold: #b99651;
  --scrim: rgba(12, 12, 12, 0.2);

  --r-input: 40px;
  --r-chip: 32px;
  --r-button: 28px;
  --r-plate-lg: 24px;
  --r-plate: 16px;
  --r-image: 8px;

  --gutter: 36px;
  --content-max: 1368px;
  --grid-gap: 16px;

  --ease: cubic-bezier(0.44, 0, 0.56, 1);
  --duration: 0.4s;
}

html { background: var(--paper); color: var(--ink); font: 400 14px/20px var(--font-sans); -webkit-font-smoothing: antialiased; }

h1 { font-size: 40px; line-height: 44px; font-weight: 400; letter-spacing: -0.8px; margin: 0; }
h3 { font-size: 32px; line-height: 35.2px; font-weight: 500; letter-spacing: -0.64px; margin: 0; }
.lede { font-size: 16px; line-height: 24px; letter-spacing: 0.16px; color: var(--muted); }
.muted { color: var(--muted); }
.num { font-family: var(--font-mono); letter-spacing: 0.14px; }

a { color: inherit; text-decoration: none; transition: color var(--duration) var(--ease); }
a.meta { color: var(--muted); }
a.meta:hover { color: var(--ink); }

/* Card */
.card { background: var(--plate); border-radius: var(--r-plate); overflow: hidden; display: flex; flex-direction: column; }
.card__media { position: relative; padding: 24px 16px 8px; }
.card__img { display: block; width: 100%; aspect-ratio: 413 / 355; object-fit: contain; border-radius: var(--r-image); }
.card__badge { position: absolute; top: 16px; left: 16px; display: flex; gap: 6px; align-items: center; color: var(--gold); }
.card__meta { display: flex; justify-content: space-between; gap: 16px; padding: 0 16px 16px; }
.card__meta-left { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.card__kicker { display: flex; gap: 6px; color: var(--muted); }
.card__title { color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Chip */
.chip { height: 36px; padding: 8px 12px 8px 10px; border-radius: var(--r-chip); background: var(--paper);
        box-shadow: inset 0 0 0 1px var(--hairline); color: var(--muted); letter-spacing: 0.14px; border: 0; font: inherit; }
.chip[aria-pressed="true"] { background: var(--plate); box-shadow: none; color: var(--ink); }

/* Buttons */
.btn { height: 36px; padding: 8px 12px; border-radius: var(--r-button); border: 0; font: inherit; font-weight: 500; letter-spacing: 0.14px; display: inline-flex; align-items: center; gap: 4px; }
.btn--primary { background: var(--ink); color: var(--plate); }
.btn--secondary { background: var(--plate); color: var(--ink); height: 40px; }
.btn--inline { background: transparent; color: var(--ink); padding: 0; height: 20px; }

/* Input pill */
.field { display: flex; align-items: center; height: 40px; padding: 10px 12px; background: var(--plate); border-radius: var(--r-input); }
.field input { flex: 1; min-width: 0; border: 0; background: transparent; font: inherit; color: var(--ink); outline: none; }
.field input::placeholder { color: var(--muted); }
.field:focus-within { box-shadow: inset 0 0 0 1px var(--ink); }  /* addition for a11y; source has no visible focus */
.textarea { background: var(--plate); border-radius: var(--r-plate); padding: 10px 12px; border: 0; font: inherit; }

/* Grid */
.grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--grid-gap); }
@media (max-width: 1199.98px) { .grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 809.98px) { .grid { grid-template-columns: 1fr; } :root { --gutter: 16px; } }

hr { border: 0; border-top: 1px solid var(--hairline); margin: 0; }

Reject these reflexes

  • Do not add a dark mode. The site is light only. A dark toggle is the fastest way to stop belonging.
  • Do not use 600 or 700. Emphasis is 500, and usually it is #141414 against #737373 at the same weight.
  • Do not add sizes. No 12px captions, no 13px meta, no 18px subheads, no 20px card titles. Fourteen does the work.
  • Do not put mono on labels, eyebrows, or headings. Mono means "this is a number".
  • Do not draw borders on cards. A card is a white plate on grey paper. If it needs separating, it is already separated.
  • Do not add shadows, gradients, glows, blur, or glass. There are none.
  • Do not make buttons rectangular or 6px-rounded. Every control is a full pill.
  • Do not invert the active chip to dark. Active is white on paper; inactive is paper with a hairline ring.
  • Do not spend gold on anything but one editorial word per item. No gold buttons, borders, fills, or icons.
  • Do not animate cards on hover. No lift, no scale, no image zoom. Only link color moves, and only muted-to-ink.
  • Do not rank or sort visibly by price or popularity in the default view. The flat grid is the editorial stance.
  • Do not write hype copy. No "curated for you", no exclamation marks, no "discover the best". One quiet sentence.
  • Do not narrate the mechanism into the interface. No "powered by", no "AI-picked", no counts of likes as a badge.
  • Do not use a photo crop as the card image. Product on white or transparent, object-fit: contain, inside its own 8px radius.

Review checklist

  1. Squint test: the page reads as grey paper with white plates and one or two gold words. Nothing else has color.
  2. Every text run is 14px/20px Geist except at most one 40px H1 and one 16px lede per view (plus 24/32 headings on detail pages).
  3. Only weights 400 and 500 appear.
  4. Every number that matters is in Geist Mono with 0.14px tracking; nothing else is mono.
  5. Text colors are exactly #141414 or #737373 (plus #666666 lede on long-form). No opacity-based greys.
  6. Inputs are 40px pills, buttons 28px pills, chips 32px pills, plates 16px or 24px, images 8px inside plates.
  7. Zero box-shadow except the chip's 1px inset hairline. Zero gradients.
  8. Cards: plate, image, badge top-left, Brand · Category muted links, name ink, number mono right.
  9. Hover changes only link color, muted to ink, over 0.4s.
  10. Grid is 3 / 2 / 1 columns at 1200 / 810 breakpoints with 16px gaps and 36px gutters.
  11. Copy is sentence case, one declarative sentence per heading, no em dashes, no emoji.
  12. Identity check: no Curated Supply wordmark, taxonomy, or About copy reused. Only the material language.
design.md/mal-gg-design.md

mal.gg

extracted from mal.gg/yc on 2026-09-21

The data is the interface. 12px default, the scale tops out at 20px and is used once, every numeral is mono, and one 120ms animation is the entire motion budget.

701 lines Geist + Geist Mono skills/user/mal-gg-design
examples/malgg/example-directory.html 684 fellows across 12 cohorts with faceted filters that count against each other, 3-state toggles, dedupe by company, a live coverage meter, three views and a counted empty state. open full page ↗
examples/malgg/components.htmlopen full page ↗

Design data UI like mal.gg

mal.gg/yc is a directory of 11,704 Y Combinator founders with 20 filter dimensions, three view modes and 586 pages of results, and the largest text anywhere on it is 20px. That restraint is the whole system. This file tells you how to build something that would sit next to it without looking louder, softer or more decorated.

The governing idea: the data is the interface. Chrome earns its pixels by encoding a fact. A color means a season. A count means how many rows an option would return. A percentage means how much of the dataset actually has that field. Nothing is styled to look considered. It looks considered because nothing is styled without a reason.

Provenance

Extracted live from https://www.mal.gg/yc on 2026-09-21 by Aside, using skills/user/design-system-extraction/scripts/extract-tokens.js, plus targeted getComputedStyle probes and raw reads of the served Tailwind chunks (/_next/static/immutable/chunks/40ottiodm84ga.css and siblings).

Pages sampled: /yc (table view), /yc?view=cards, /yc?view=people, /yc?q=zzzzqqqq (empty state), /yc?page=300 and /yc?q=X25 (batch color coverage), and / (the author's personal homepage, which is a different design layer, see below).

No official guideline exists. /design.md, /llms.txt, /brand and /style-guide all return 404. There is no published design system, no brand page, no engineering blog. Every value in this file was read off the live DOM or the served CSS. Nothing here is from a third-party brand kit, and you should not trust one if you find it.

Built by Mal, lead project designer and co-founder of Keitoto (keitoto.com), a design and development studio. The /yc header credits "Built by Keitoto". Stack is Next.js on Vercel, Tailwind CSS v4, next/font for Geist, CSS Modules for the font variable classes.

The live site wins on conflict. This file is a dated snapshot. Re-extract if the site visibly diverges.

What makes this thing work

Six choices, each one actionable:

  1. The type scale tops out at 20px and it is used once. 12px is the default (645 elements), 14px is the emphasis size (350), 11px is metadata (67). The H1 "Founder Directory" is 20px. There is no display type, no hero, no marketing voice inside the product.
  2. Every numeral is Geist Mono. Counts, percentages, batch codes, handles, page numbers. Prose is Geist Sans. The split is total and it is the single most recognizable thing about the interface.
  3. Two material primitives, three lines of CSS each. .yc-card (plate) and .yc-panel (container). Every surface on the page is one of those two. There is no third.
  4. Color is spent only on meaning. Neutral ink for everything, then a six-tone palette where each tone is attached to a specific fact: a season, an industry, a person's initials. Nothing is colored for emphasis.
  5. One animation exists. yc-fade-in, 120ms, 2px of travel. Hover is an instant background swap with no transition at all.
  6. Counts are everywhere and they are honest. Every filter option carries the number of rows it would return. Approximations are prefixed ≈. A coverage meter above the results states what fraction of the current result set actually has each field.

Use this priority order

When two rules conflict, the earlier one wins.

  1. Data legibility. A row must be scannable at 12px with 64px of height. Nothing may reduce the number of rows visible or slow the eye down the column.
  2. Truth about the dataset. If a field is 20% populated, say 20%. If a count is approximate, mark it. Never let styling imply completeness the data does not have.
  3. The two material primitives. Use .yc-card or .yc-panel. Do not invent a third surface treatment to solve a layout problem.
  4. Restraint in the type scale. Reach for weight 500 or a tone color before reaching for a larger size.
  5. Semantic color discipline. A tone must encode a fact. If you cannot name the fact, use ink.
  6. Polish. Only after the five above are clean.

Foundation

Typography

--font-yc-sans: "Geist", "Geist Fallback", ui-sans-serif, system-ui, sans-serif;
--font-yc-mono: "Geist Mono", "Geist Mono Fallback", ui-monospace, SFMono-Regular, Menlo, monospace;

Both are real webfonts, loaded through next/font as variable faces (weight range 100 900), served self-hosted from /_next/static/immutable/media/*.woff2. Confirmed loaded at runtime via document.fonts.check().

A trap worth knowing: the site-wide Tailwind default is still --font-sans: "Inter", inherited from the author's personal homepage, and <body> still computes to Inter. Nothing inside /yc uses it. Every element resolves to Geist because the directory applies font-yc-sans / font-yc-mono explicitly. If you copy this, set your root font to Geist and skip the dead Inter declaration.

Type scale. Measured by frequency across the live table view:

SizeCountRole
12px645The default. Table cells, buttons, filter rows, menu items, search input, everything.
14px350Emphasis inside a row. The founder's name, the company name, the industry cell.
11px67Mono metadata. Batch codes (W27), X handles (@cancelik).
16px34The one paragraph of running prose per surface, and the search icon box.
13px20Avatar initials.
15px20Rare. Secondary headings inside panels.
10px2The toggle sub-count under a chip label.
20px1The page H1. Only instance on the site.

Weights are 400 and 500. That is the system. 616 elements at 400, 521 at 500, and exactly 2 at 600 (the H1). Never use 700. Never use a weight below 400.

Line height equals font size almost everywhere. 647 elements at line-height: 12px against 645 at font-size: 12px. This is Tailwind's leading-none, applied deliberately: every label and cell is a single line, so leading is dead space. The exceptions are 24px (13 elements, the one block of explanatory prose) and 19.6px (4 elements, a 14px paragraph at 1.4).

Letter spacing is a real token here, not a garnish.

/* body and UI text, 475 elements */    letter-spacing: -0.1px;
/* the H1, once */                      letter-spacing: -0.2px;
/* panel section labels, 10 elements */ letter-spacing: 0.72px;

Negative tracking on everything small, positive tracking only on the uppercase section labels. Mono text carries normal tracking and is never tracked out.

Color

Light only. Zero occurrences of prefers-color-scheme across all three served stylesheets, zero .dark selectors, no theme toggle, color-scheme: normal. There is no dark mode to copy, and adding one is not "completing" the system. If you need dark, you are designing it yourself and it will not be theirs.

Neutrals. Note that the darkest ink is #0a0a0a and the page is #fafafa. Neither is pure.

--yc-text-primary:   #0a0a0a;  /* 496 elements. Row content, headings. */
--yc-text-secondary: #5b5f66;  /*  41. Inactive segmented labels. */
--yc-text-tertiary:  #8a8f98;  /*  78. Handles, section labels, ellipses. */
--yc-text-muted:     #737373;  /* 419. Column headers, secondary cells, kbd. */
--yc-text-link:      #1e46d6;

--yc-bg-page:         #fafafa;  /* the only page background */
--yc-bg-surface:      #ffffff;  /* every plate and panel */
--yc-bg-subtle:       #f5f6f7;  /* table header row, segmented control track */
--yc-bg-hover:        #f3f4f6;  /* the only hover state in the product */
--yc-bg-selected:     #eff3ff;
--yc-bg-row-selected: #eef0f3;

--yc-border-default: #e4e5e8;  /* panel borders, control borders, the shadow ring */
--yc-border-strong:  #d3d5da;  /* scrollbar thumb, checkbox outlines */
--yc-border-subtle:  #efeff1;  /* row dividers inside a panel */

Three ink greys carry 95% of the text. Three borders carry every division. Learn the difference between --yc-border-default (#e4e5e8, separates a surface from the page) and --yc-border-subtle (#efeff1, separates a row from its neighbour inside a surface). Getting that backwards is the fastest way to look wrong.

Accents. Four, and they are not interchangeable.

--yc-accent-indigo:      #5b5bf5;  /* primary brand accent */
--yc-accent-indigo-soft: #efeefe;
--yc-accent-blue:        #2c5bff;
--yc-accent-focus:       #3177f0;  /* focus rings and counts only */

The six-tone semantic palette. Every tone ships with a soft companion. This pairing is the whole badge, chip and avatar system.

ToneForegroundSoft background
green#0e8f5e#e9f7f0
gold#b7791f#fdf4e3
blue#2563eb#eaf1fe
orange#d0622b#fdf0e8
pink#ce3560#fdebf0
purple#6a59cc#f0edfb

Six hues, well separated, no near-duplicates. There is no red and no yellow. There is no scale within a tone (no green-100 through green-900). One foreground and one background per tone, and that is the entire ramp.

Batch season colors are a separate hardcoded set, and this is deliberate. They are written as Tailwind arbitrary values (text-[#a560f5]), not tokens, because they belong to the dataset rather than the interface:

W (Winter)  #3177f0   blue
S (Summer)  #d67f02   amber
F (Fall)    #a560f5   purple
X (Spring)  #0e8f5e   green

Verified across batches S05 through W27. The point to steal: a data-derived color mapping is not part of your theme. Keep it out of your token file so nobody reuses "batch purple" as a UI accent.

Where color is spent, in full. Nowhere else.

  • Batch code, by season.
  • Industry cell, one tone per vertical.
  • Avatar initials, tone chosen per person.
  • An active filter chip, which adopts the tone of what it filters (Hiring now goes green: border-yc-green bg-yc-green-soft text-yc-green).
  • Links, #1e46d6, underline on hover.
  • Counts and focus rings, #3177f0.

No gradients. No glows. No colored shadows. Zero gradient tokens resolve to anything other than transparent.

Shape

--radius-sm:  0.25rem;  /*  4px */
--radius-md:  0.375rem; /*  6px */
--radius-lg:  0.5rem;   /*  8px */
--radius-xl:  0.75rem;  /* 12px */
--radius-2xl: 1rem;     /* 16px */

Actual usage, ranked: 8px (42 elements), 9999px (40), 12px (35), 16px (2), 10px (1), 6px (1).

The rule that falls out of that:

  • 16px for a panel. Outer containers only. There are two on the page.
  • 12px for a plate. Every .yc-card: buttons, popovers, chips, search field, segmented track, feed cards.
  • 8px for something inside a plate. Segmented items, kbd keys, checkboxes, small badges.
  • 9999px for circles only, which in practice means avatars and the scrollbar thumb.

No squircles, no corner smoothing. corner-shape computes to superellipse(1), which is the browser default, not a design decision. Nothing on this site is sharp-cornered.

Elevation and depth

Three recipes, verbatim.

/* Plate. The ring IS the border. */
--yc-shadow-card:
  0 1px 2px 0 #5258660f,
  0 0 0 1px #e4e5e8,
  0 1px 2px 0 #0000000a,
  0 1px 2px 0 #00000005;

/* Floating chrome. Same ring, real depth. */
--yc-shadow-popover:
  0 12px 32px -8px #0f17292e,
  0 0 0 1px #e4e5e8,
  0 2px 6px 0 #0000000f;

/* Panel. A real 1px border plus the softest possible lift. */
box-shadow: 0 3px 6px -1px rgba(0,0,0,0.03), 0 1px 2px rgba(0,0,0,0.04);
border: 1px solid var(--yc-border-default);

The technique to take away: a plate does not get a border. It gets a 0 0 0 1px spread shadow in --yc-border-default, stacked with three near-invisible 1px blurs. The ring is crisp at any radius, never doubles against a neighbouring border, and costs no layout box. A panel, which is a real container rather than a floating object, gets an actual border instead.

Two more, both tiny:

/* Toolbar chips. The entire elevation of a 36px control. */
filter: drop-shadow(0 1px 1px rgba(15,23,41,0.05));

/* Focus. Inset, sub-pixel, no halo. */
box-shadow: inset 0 0 0 0.8px rgba(49,119,240,0.4);

Focus is an inset ring. It never grows the element and never bleeds outside it. Copy this.

Scrollbars are styled and it matters at this density:

.yc-thin-scroll::-webkit-scrollbar       { width: 6px; height: 6px; }
.yc-thin-scroll::-webkit-scrollbar-thumb { background: var(--yc-border-strong); border-radius: 999px; }
.yc-thin-scroll::-webkit-scrollbar-track { background: transparent; }

Layout

main       mx-auto max-w-[1440px] px-8 py-8 flex flex-col gap-4
           mobile: px-4 py-5
body row   flex items-start gap-4
           mobile: flex-col items-stretch
sidebar    .yc-panel w-[340px] shrink-0 self-start overflow-hidden
           mobile: hidden
results    .yc-panel flex-1 min-w-0 overflow-hidden

At a 1440px viewport: 1376 content, 340 filter panel, 16 gap, 1020 results panel.

Everything is 16px. Page gutter is 32, every gap between blocks is 16, padding inside a card is 16, gaps inside a row are 12, gaps inside a cell are 8 or 6. Tailwind's --spacing: 0.25rem base, used almost exclusively at 4 (16px) and 3 (12px).

Sections do not separate with whitespace. They separate by being a panel. There is one vertical rhythm on the page and it never breathes; density is the point.

The table grid is explicit and fixed:

grid-template-columns:
  32px                  /* avatar */
  minmax(220px, 1.4fr)  /* founder + company */
  120px 120px 130px     /* role, batch, industry */
  minmax(150px, 1fr)    /* location */
  72px                  /* team */
  minmax(160px, 0.9fr); /* X handle */
min-width: 1000px;

Fixed columns for short enumerable values, minmax only for free text. The container scrolls horizontally with .yc-thin-scroll rather than letting columns collapse.

Row metrics:

RowHeightBackgroundDividerType
Header48px (h-12)#f5f6f7bottom #e4e5e812px / 500 / -0.1px / #737373
Data64px (h-16)#ffffffbottom #efeff112px / 400

Both at px-4. Hover on a data row sets background to #f3f4f6 and changes nothing else.

Breakpoints are Tailwind v4 defaults (40/48/64/80/96rem). The directory only ever uses md (768px) and xl (1280px), plus max-md: for the mobile layer. Below md the filter panel is hidden entirely and the table is replaced by the People list.

Three view modes, one dataset:

  • Table (default). The explicit grid above.
  • Cards. grid grid-cols-1 gap-3 p-4 md:grid-cols-2 xl:grid-cols-3, each card .yc-card rounded-xl p-4 flex flex-col gap-3 hover:bg-yc-hover, about 160px tall.
  • People. A flat list, flex items-center gap-3 px-4 py-3 border-b border-yc-line-subtle. The densest of the three, and the one that survives on mobile.

Motion

The entire motion budget of the site:

@keyframes yc-fade-in {
  0%   { opacity: 0; transform: translateY(2px); }
  100% { opacity: 1; transform: none; }
}
.yc-fade-in { animation: 0.12s ease-out yc-fade-in; }

120 milliseconds. Two pixels. One easing curve. One keyframe in the entire stylesheet. It is applied to popovers and dropdowns on open, and to nothing else.

There is no hover transition. --default-transition-duration: 0.15s and --default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1) exist as Tailwind defaults and are not used on any interactive element. A row hover is an instant background swap. This reads as fast rather than unfinished, because at 64px rows a 150ms crossfade on every row you sweep past is visual noise.

Hover states are gated behind @media (hover: hover), so nothing flickers on touch.

Reduced motion is handled by forcing entrance opacity to 1:

@media (prefers-reduced-motion: reduce) {
  [data-page-motion].js [data-motion] { opacity: 1; }
}

No skeletons, no spinners, no shimmer anywhere in the CSS.

Components

The two primitives

.yc-card {
  background: var(--yc-bg-surface);
  box-shadow: var(--yc-shadow-card);
  border-radius: 12px;
}

.yc-panel {
  background: var(--yc-bg-surface);
  border: 1px solid var(--yc-border-default);
  border-radius: 16px;
  box-shadow: 0 3px 6px -1px rgba(0,0,0,0.03), 0 1px 2px rgba(0,0,0,0.04);
}

Panel for a region of the page that owns scroll and contains rows. There are two: the filter sidebar and the results container. Card for everything that floats, toggles, or is a discrete object: buttons, kbd keys, the search field, the segmented control track, popovers, feed cards.

If you find yourself writing a third surface class, you have a layout problem, not a material problem.

Control size ladder

HeightClassRadiusUse
40pxh-1012Toggle chips in the toolbar (On X, Hiring now, One contact per company)
36pxh-912Outer controls: search field, sort button, filter counter, segmented track
28pxh-78 (12 when selected)Items inside a segmented control
24pxh-612Kbd keys and inline mono badges
20pxsize-58Checkboxes

Button, bordered (the sort control)

flex h-9 items-center gap-2 rounded-xl
border border-yc-line bg-yc-surface px-2.5
text-[12px] leading-none
drop-shadow-[0_1px_1px_rgba(15,23,41,0.05)]
hover:bg-yc-hover

12px / 400 / #0a0a0a on white, 1px #e4e5e8 border. Hover changes background only. No border change, no shadow change, no lift.

Toggle chip, with its active state

inactive:  h-10 rounded-xl border border-yc-line bg-yc-surface
           pl-4 pr-3 text-[12px] leading-3 text-yc-ink-2
active:    border-yc-green bg-yc-green-soft text-yc-green

This is the most transferable idea in the component set. An active filter does not turn "brand color". It adopts the semantic tone of the thing it filters. Hiring now goes green because hiring is a positive-availability fact. Border, background and text all shift to the same tone family at once, so the chip reads as a single colored object rather than a bordered box with colored text inside it.

Each chip carries its live count in Geist Mono at 10px or 12px in --yc-text-secondary, directly under or beside the label.

Segmented control

track: .yc-card h-9 bg-yc-subtle p-1 flex items-center
item:  h-7 rounded-lg px-2 text-[12px] font-medium leading-none gap-2
       selected:   .yc-card text-yc-ink       (white plate rises out of the grey track)
       unselected: text-yc-ink-2 hover:text-yc-ink

The selected item is literally the same .yc-card material as everything else. The track is --yc-bg-subtle. Selection is a plate on a recess, with no indicator, no underline, no pill animation.

Search field

wrapper: .yc-card flex h-9 items-center justify-between py-1 pl-3 pr-2
icon:    16px SVG, leading
input:   min-w-0 flex-1 bg-transparent text-[12px] leading-none tracking-[-0.1px]
         text-yc-ink outline-none placeholder:text-yc-ink-muted
kbd:     .yc-card h-6 rounded-lg px-2 font-yc-mono text-[12px] font-medium
         text-yc-ink-muted, hidden below md

The input carries no border, no background and no radius of its own. The wrapper is the control. The input is just text.

Popover / dropdown

.yc-fade-in absolute right-0 top-[calc(100%+6px)] z-40
w-[340px] rounded-xl border border-yc-line bg-yc-surface overflow-clip
box-shadow: var(--yc-shadow-popover)
mobile: fixed inset-x-4 bottom-4 top-auto w-auto

340px, the same width as the filter panel. One width constant governs both pieces of floating and docked chrome. The 6px offset from the trigger is the only place a non-4-multiple spacing value appears.

Section labels inside a popover:

12px / 500 / letter-spacing 0.72px / color #737373 or #8a8f98
text-transform: none  /* the copy is literally uppercase in the markup */

Avatar

inline-flex shrink-0 items-center justify-center rounded-full font-medium
size 32px, text 13px / 500
bg-yc-{tone}-soft  text-yc-{tone}

Two-letter initials on a soft tone background with the matching tone foreground, cycling through all six tones. No images, no image fallback chain, no gradient fill. At 11,704 records this is the correct call: consistent, instant, and it never breaks.

Badge and inline metadata

batch pill:  .yc-card inline-flex h-6 items-center rounded-lg px-2
             font-yc-mono text-[12px] font-medium, color = season color
batch code:  font-yc-mono text-[11px] font-medium, color = season color (no plate)
handle:      shrink-0 font-yc-mono text-[11px] text-yc-ink-3
count:       font-yc-mono text-[12px] font-medium tracking-[-0.1px]

Icons

Untitled UI, served as individual SVG files from /yc/figma/<name>.svg. Sizes: 16px in the search field, 14px in buttons and toolbar controls, 12px for the command glyph. No CSS filters, no opacity tricks, no icon font. The SVG carries its own stroke color.

Representative names, useful if you are matching the set: search-md, grid-01, user-01, list, switch-vertical-01, filter-funnel-02, columns-03, star-05, refresh-cw-01, marker-pin-01, chevron-selector-vertical, x-close.

The coverage meter

Sits above the results, right-aligned, and it is the signature component of the whole product.

flex items-center gap-2 text-[12px] font-medium leading-none
label: "Known for these results" in --yc-text-tertiary
pairs: <field name> in ink + <percentage> in Geist Mono

Rendering as Known for these results Role 41% Bio 78% Ex-company 20% School 25%, recomputed against the current filter set, each pair carrying a tooltip that explains the gap in plain language.

Build this. Any interface over a scraped, parsed or merged dataset should state its own coverage. It converts "this directory is incomplete" from a thing users discover into a thing the product says first.

The two design layers

mal.gg runs two systems from one stylesheet, and the split is instructive.

The personal homepage (/) is a card-stack collage on a dotted board. Inter at 16px, a single Gasoek One wordmark ("MAL", 16px, #d9d9d9), white cards at a 2px radius scattered across --color-board: #fafafa with --color-board-dot: #e6e6e6. It has its own token family: --color-card, --color-ink, --color-hairline, --color-rule, --card-scale, --logo-scale.

The directory (/yc) overrides all of it. Geist instead of Inter, the --yc-* namespace instead of --color-*, 12px instead of 16px, radius 12/16 instead of 2.

The lesson: a tool inside a personal site is allowed to be a different design system. Mal did not force the directory to wear the homepage's collage language. He gave it a scoped namespace and let it be a product. If you are building a tool inside a portfolio, do the same, and name your tokens with a prefix so the boundary is visible in the CSS.

Voice and copy

Sentence case everywhere, with two deliberate exceptions.

Table column headers are literal uppercase strings in the markup, not text-transform: FOUNDER, ROLE, BATCH, INDUSTRY, LOCATION, TEAM, X_url. Note X_url. It is the raw database field name, snake case and all, sitting in production next to six clean words. That is not sloppiness, it is the same honesty move as the coverage meter: this is a view over a table, and the table has a column called x_url.

Panel section labels are uppercase strings with 0.72px tracking: SORT BY, Company · Lifecycle, Founder · Background. The middot separates the subject from the facet, and it is the site's one piece of ornament.

Every option carries its count. Batch 49, Tags 337, Ex-company 148, Top company 91, Has GitHub 1,182. Approximations are prefixed with ≈: AI (text) ≈2,274, Agents ≈437.

Options get a one-line mechanic, not a benefit. From the sort menu:

Top companies first     91 top companies
Most reachable          personal handle + bio + known role
Relevance               needs a search query

Lowercase, no punctuation, states what the sort actually does. "Most reachable" would be marketing on its own; personal handle + bio + known role makes it a definition.

Tooltips explain the data, not the UI. The Role tooltip reads: "Role is parsed from founder titles in yc.db. Known for 41% of these rows, the rest have no title at all, mostly 2019 to 2023. Those rows stay in the result and are counted as Unknown, never as No." It names the source table, admits the gap, dates the gap, and states the semantics of a missing value. No interface has ever needed to explain a hover target this well and that is exactly why it works.

The empty state is the best copy on the site. It does four things in sequence:

No founders match this filter
Dimensions combine with AND, values inside one dimension with OR.
Loosening one dimension brings results back, each option below is
counted against the rest of your filters.

Drop the search "zzzzqqqq"     11,704 results   [Apply]
Keep every filter, remove the free-text match.

[Clear all filters]  [Reset to newest batch]

Name the state, explain the boolean semantics that caused it, offer specific escape hatches with the row count each one would restore, then the blunt resets. Nobody writes empty states this way. Do it.

No em dashes, no exclamation marks, no emoji, no sentences addressed to the user. The product never says "we", "you", "let's" or "oops".

Starter stylesheet

Drop-in tokens using the source site's own naming, so a future live inspection maps one to one onto this file.

/* mal.gg /yc design tokens. Extracted 2026-09-21. Light only, by design. */

:root {
  /* Type */
  --font-yc-sans: "Geist", "Geist Fallback", ui-sans-serif, system-ui, sans-serif;
  --font-yc-mono: "Geist Mono", "Geist Mono Fallback", ui-monospace, SFMono-Regular, Menlo, monospace;

  /* Ink */
  --yc-text-primary:   #0a0a0a;
  --yc-text-secondary: #5b5f66;
  --yc-text-tertiary:  #8a8f98;
  --yc-text-muted:     #737373;
  --yc-text-link:      #1e46d6;

  /* Surface */
  --yc-bg-page:         #fafafa;
  --yc-bg-surface:      #ffffff;
  --yc-bg-subtle:       #f5f6f7;
  --yc-bg-hover:        #f3f4f6;
  --yc-bg-selected:     #eff3ff;
  --yc-bg-row-selected: #eef0f3;

  /* Line */
  --yc-border-default: #e4e5e8;
  --yc-border-strong:  #d3d5da;
  --yc-border-subtle:  #efeff1;

  /* Accent */
  --yc-accent-indigo:      #5b5bf5;
  --yc-accent-indigo-soft: #efeefe;
  --yc-accent-blue:        #2c5bff;
  --yc-accent-focus:       #3177f0;

  /* Semantic tones. One foreground, one soft background, no ramp. */
  --yc-tone-green:  #0e8f5e;  --yc-tone-green-soft:  #e9f7f0;
  --yc-tone-gold:   #b7791f;  --yc-tone-gold-soft:   #fdf4e3;
  --yc-tone-blue:   #2563eb;  --yc-tone-blue-soft:   #eaf1fe;
  --yc-tone-orange: #d0622b;  --yc-tone-orange-soft: #fdf0e8;
  --yc-tone-pink:   #ce3560;  --yc-tone-pink-soft:   #fdebf0;
  --yc-tone-purple: #6a59cc;  --yc-tone-purple-soft: #f0edfb;

  /* Elevation */
  --yc-shadow-card:
    0 1px 2px 0 #5258660f,
    0 0 0 1px #e4e5e8,
    0 1px 2px 0 #0000000a,
    0 1px 2px 0 #00000005;
  --yc-shadow-popover:
    0 12px 32px -8px #0f17292e,
    0 0 0 1px #e4e5e8,
    0 2px 6px 0 #0000000f;
  --yc-shadow-panel:
    0 3px 6px -1px rgba(0,0,0,0.03),
    0 1px 2px rgba(0,0,0,0.04);
  --yc-shadow-chip: 0 1px 1px rgba(15,23,41,0.05);

  /* Shape */
  --yc-radius-inner: 8px;
  --yc-radius-plate: 12px;
  --yc-radius-panel: 16px;

  /* Metrics */
  --yc-panel-width: 340px;
  --yc-row-height: 64px;
  --yc-header-height: 48px;
}

html { background: var(--yc-bg-page); color-scheme: light; }

body {
  font-family: var(--font-yc-sans);
  font-size: 12px;
  line-height: 1;
  letter-spacing: -0.1px;
  color: var(--yc-text-primary);
}

/* The two primitives. There is no third. */
.yc-card {
  background: var(--yc-bg-surface);
  box-shadow: var(--yc-shadow-card);
  border-radius: var(--yc-radius-plate);
}

.yc-panel {
  background: var(--yc-bg-surface);
  border: 1px solid var(--yc-border-default);
  border-radius: var(--yc-radius-panel);
  box-shadow: var(--yc-shadow-panel);
}

.yc-thin-scroll::-webkit-scrollbar       { width: 6px; height: 6px; }
.yc-thin-scroll::-webkit-scrollbar-thumb { background: var(--yc-border-strong); border-radius: 999px; }
.yc-thin-scroll::-webkit-scrollbar-track { background: transparent; }

/* The only animation. */
@keyframes yc-fade-in {
  from { opacity: 0; transform: translateY(2px); }
  to   { opacity: 1; transform: none; }
}
.yc-fade-in { animation: 0.12s ease-out yc-fade-in; }

@media (prefers-reduced-motion: reduce) {
  .yc-fade-in { animation: none; opacity: 1; }
}

/* --- sample components --- */

/* Toolbar control */
.yc-control {
  display: flex; align-items: center; gap: 8px;
  height: 36px; padding: 0 10px;
  border: 1px solid var(--yc-border-default);
  border-radius: var(--yc-radius-plate);
  background: var(--yc-bg-surface);
  filter: drop-shadow(var(--yc-shadow-chip));
  font-size: 12px; line-height: 1;
  color: var(--yc-text-primary);
}
@media (hover: hover) {
  .yc-control:hover { background: var(--yc-bg-hover); } /* no transition, on purpose */
}
.yc-control:focus-visible {
  outline: none;
  box-shadow: inset 0 0 0 0.8px rgba(49,119,240,0.4);
}

/* Toggle chip: active state adopts the tone of what it filters */
.yc-chip[data-active="true"][data-tone="green"] {
  border-color: var(--yc-tone-green);
  background: var(--yc-tone-green-soft);
  color: var(--yc-tone-green);
}

/* Table */
.yc-table-header {
  height: var(--yc-header-height);
  background: var(--yc-bg-subtle);
  border-bottom: 1px solid var(--yc-border-default);
  padding: 0 16px;
  font-size: 12px; font-weight: 500; letter-spacing: -0.1px;
  color: var(--yc-text-muted);
}
.yc-table-row {
  height: var(--yc-row-height);
  background: var(--yc-bg-surface);
  border-bottom: 1px solid var(--yc-border-subtle);
  padding: 0 16px;
  font-size: 12px;
}
@media (hover: hover) {
  .yc-table-row:hover { background: var(--yc-bg-hover); }
}

/* Every numeral, everywhere */
.yc-num {
  font-family: var(--font-yc-mono);
  font-size: 12px; font-weight: 500;
  letter-spacing: -0.1px;
  font-variant-numeric: tabular-nums;
}

Note: font-variant-numeric: tabular-nums is my addition, not theirs. Geist Mono is already monospaced so it is a no-op there, but it protects you if you ever fall back to the sans for a numeral.

Reject these reflexes

  • Do not add a dark mode. The site has none, in zero stylesheets. A dark variant is your design, not theirs, and it will not be recognizable as this system.
  • Do not raise the type scale. If a thing needs emphasis, go from 400 to 500, or from 12px to 14px. Do not introduce 18, 24, 32 or a display size. There is exactly one 20px element.
  • Do not use weight 600 or 700. 400 and 500. The only 600 is the page H1.
  • Do not add leading. line-height: 1 on labels and cells is correct here. Only running prose gets 1.4 or more.
  • Do not add hover transitions. Hover is an instant background swap. A 150ms crossfade on a 64px row makes a table feel slower, not smoother.
  • Do not animate anything except a popover entrance. 120ms, 2px, ease-out. No spring, no scale, no stagger, no shimmer, no skeleton.
  • Do not give a plate a real border. Use the 0 0 0 1px #e4e5e8 ring inside --yc-shadow-card. Reserve real borders for panels and controls.
  • Do not use pure black or pure white text. Ink is #0a0a0a, the page is #fafafa.
  • Do not build a tonal ramp. There is one foreground and one soft background per tone. No green-50 through green-900.
  • Do not spend a tone on emphasis. A color must name a fact: a season, a vertical, a person, an active availability filter. If you cannot say what the color means, use ink.
  • Do not promote a data-derived color into your token file. Season colors live as arbitrary values precisely so nobody reuses "batch purple" as a button.
  • Do not add gradients, glows, colored shadows, glassmorphism or backdrop blur. None appear anywhere in the extraction.
  • Do not center the content or pad it out. Max width 1440, gutter 32, and the panels fill it. This is not a marketing page.
  • Do not let a column collapse responsively. Fix the enumerable columns, minmax the free-text ones, and scroll horizontally with a 6px thin scrollbar.
  • Do not use photo avatars. Two-letter initials on a soft tone. At scale this is the robust choice and it is part of the look.
  • Do not ship a filter option without its count. A filter that does not tell you how many rows it returns is a guess. Mark approximations with ≈.
  • Do not write a bare empty state. Explain the boolean semantics, then offer specific loosenings with the row count each would restore.
  • Do not write friendly product copy. No "we", "you", "let's", "oops", "Nice!", no exclamation marks, no emoji, no em dashes. State the mechanic.
  • Do not narrate the machinery into the interface. Tooltips may explain the data ("parsed from founder titles in yc.db"). Nothing may explain the implementation, the stack, or the fact that this was generated.

Review checklist

Run this against a finished page.

  1. Squint test. Does it read as one grey page with white plates and a handful of colored dots? If you see a colored block larger than a chip, something is over-colored.
  2. Type audit. Every font size is 10, 11, 12, 13, 14, 15, 16 or the single 20. Every weight is 400 or 500. Count your 600s: the answer should be zero or one.
  3. Numeral audit. Every digit on the page is in Geist Mono. Find one that is not.
  4. Tracking. Small text is at -0.1px. Uppercase section labels are at +0.72px. Mono is at normal.
  5. Material audit. Every surface is .yc-card or .yc-panel. If there is a third, justify it or delete it.
  6. Border audit. Plates use the ring shadow. Panels and controls use a real 1px #e4e5e8. Row dividers use #efeff1. No doubled borders anywhere.
  7. Color audit. For each colored element, name the fact it encodes in one word. Any element that fails this becomes ink.
  8. Motion audit. Grep for transition. The only animation should be a 120ms popover fade. Hover must be instant.
  9. Hover audit. Every hover changes background to #f3f4f6 and nothing else. No lift, no shadow change, no border change, no content reveal.
  10. Focus audit. Focus is an inset 0 0 0 0.8px rgba(49,119,240,0.4) ring. It does not grow the element.
  11. Honesty audit. Does every filter show its count? Does the page state its own data coverage? Are approximations marked?
  12. Empty state. Does it explain why, offer counted escape hatches, and provide a blunt reset?
  13. Copy audit. Sentence case, no em dashes, no exclamation marks, no first or second person. Option subtitles state mechanics, not benefits.
  14. Density. At a 1440px viewport, are you showing at least 20 rows without scrolling past the fold? If not, something grew.
  15. Identity. If this is not mal.gg, it must not carry Mal's or Keitoto's name, the MAL Gasoek One wordmark, or any implication of authorship. The tokens, geometry and component recipes are free to borrow. The identity is not.
design.md/luma-design.md

Luma

extracted from luma.com on 2026-08-24

Identity from colour, shape and softness rather than a typeface. Squircle corner smoothing, translucency as material, one cranberry accent spent carefully.

386 lines System UI (no webfont) not installed as a skill
examples/luma/example-event.html A themed event page derived from one host color, with a validated three-step request-to-join flow that ends in a ticket and the only confetti in the system. open full page ↗
examples/luma/components.htmlopen full page ↗

Design Luma-ecosystem projects like Luma

Act as a Luma designer and design engineer. Make the artifact feel like it was built by the same small team that builds luma.com: warm, soft, quietly playful, and obsessively finished. Shape the experience and the interface together; do not restyle a generic SaaS layout with pink accents and call it done.

Provenance

There is no official Luma brand kit, press page, or published design system. Everything in this document was extracted from luma.com's production CSS and live pages (.theme-root token system, .lux-button components, event theme pages) in August 2026, plus Luma's own help docs (help.luma.com) and founder writing. Third-party "Luma brand kits" on Brandfetch, Dribbble, or Figma Community are guesses; do not use them. When this document and the live site disagree, the live site wins: inspect luma.com and update this file.

Luma product and brand context

Luma is an events platform. Its tagline is "Delightful events start here." Its founder has written that the team "cares tremendously about delivering a delightful experience" and that most of its CSS and design is done in house by a deliberately small team. The product carries that philosophy visibly:

  • System-native, not typographic branding. The UI renders in the OS system font. Luma's identity comes from color, shape, softness, and motion, not from a custom typeface.
  • Soft geometry. Everything rounds generously. Cards and modals use squircle corner smoothing, not plain border radius.
  • Translucency as material. Surfaces layer white-at-low-opacity over the canvas instead of using hard borders and flat fills. Menus and floating chrome blur what is behind them.
  • One warm accent. Cranberry pink is the brand color. It appears in links, tints, and brand moments, while primary buttons stay neutral. Color is spent carefully so events themselves (cover art, host themes) can be loud.
  • Small delight, never spectacle. A springy easing curve, an animated spark logo, confetti reserved for celebratory moments. The base experience is calm.

A Luma-ecosystem project should feel like a good neighbor to the product: a visitor moving from luma.com to your page should feel no texture change.

Kinship, not impersonation

These projects are community and ambassador work, not official Luma surfaces. Borrow the material language (tokens, geometry, softness, motion), not the identity:

  • Do not present the page as authored by Luma. Name your project clearly.
  • Use the Luma spark logo and wordmark only when referring to Luma itself (for example "Hosted on Luma", a link to an event), never as your own project mark.
  • Cranberry is safe to use as an accent because it reads as ecosystem affinity; do not recreate the luma.com header or pretend to be the app shell.

The Luma spark, for reference when linking back to Luma, is a four-cusp star built from circle arcs, drawn in currentColor:

<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 133 134">
  <path fill="currentColor" d="M133 67C96.282 67 66.5 36.994 66.5 0c0 36.994-29.782 67-66.5 67 36.718 0 66.5 30.006 66.5 67 0-36.994 29.782-67 66.5-67"/>
</svg>

Use this priority order

When requirements compete, protect them in this order:

  1. Preserve the project's actual content, facts, and task constraints.
  2. Preserve the host framework and delivery surface; this system is CSS-level and framework-agnostic.
  3. Make the visitor's job (find the event, RSVP, see the schedule, use the tool) immediately doable.
  4. Establish Luma belonging through the token foundation: system font, neutral canvas, squircles, translucent surfaces, cranberry accent, both themes.
  5. Choose a composition specific to the material; avoid both generic SaaS defaults and a pixel-clone of a luma.com page.
  6. Refine motion, hover states, and details without breaking the calm.

Foundation

Typography

Luma loads no webfont for its UI. Use the exact stacks:

--font: -apple-system, BlinkMacSystemFont, "Apple Color Emoji", Inter, Roboto,
        "Segoe UI", "Helvetica Neue", Arial, "Noto Sans", sans-serif;
--mono-font: "SF Mono", Menlo, Monaco, Consolas, "Courier New", Courier, monospace;

This is a load-bearing brand decision. Loading Inter, Geist, or any grotesk as a webfont makes the page instantly feel foreign next to Luma on a Mac (where Luma renders in San Francisco). Let the OS decide.

Type scale (rem-based, 16px root):

TokenSizeUse
--font-size-xxxs0.625rem (10px)fine print, badges
--font-size-xxs0.75rem (12px)metadata, timestamps
--font-size-xs0.8125rem (13px)secondary UI text
--font-size-sm0.875rem (14px)labels, section subtitles, small buttons
--font-size-md1rem (16px)body, inputs, medium buttons
--font-size-lg1.125rem (18px)section titles
--font-size-xl1.25rem (20px)large titles
--font-size-xxl1.375rem (22px)page/tab titles
--font-size-xxxl1.5rem (24px)large page titles

Display sizes go beyond the scale (in-app page titles render around 28px, event titles up to 38px) but the weight never exceeds 600.

Weights: 300 light, 400 regular, 500 medium, 600 bold. Bold is 600, never 700. Buttons and labels use 500; titles use 600. Line heights: 1.2 titles, 1.3 reduced/compact, 1.5 body default.

Color

Neutrals first. Luma's black is warm-dark, not pure:

--white: #fff;
--black: rgb(21, 21, 21);   /* #151515: the dark-mode canvas and light-mode text */
--pure-black: #000;          /* reserved; almost never a surface */

Gray ramp: 10 #f8f8f8, 20 #ececec, 30 #e0e0e0, 40 #d4d4d4, 50 #b5b5b5, 60 #959595, 70 #757575, 80 #555, 90 #353535, 100 #232323.

Accent families, each a 5..90 ramp (50 shown, the light-mode default step):

Family50Note
cranberry#f31a7cthe brand accent; --brand-color resolves here
barney#ab46ddviolet (yes, it is really named barney)
purple#682fff
blue#146aeb
green#3cbd2csuccess
yellow#eaab26warning (light mode uses the 60 step #d69712)
orange#f8712b
red#ed2b32error

Full cranberry ramp, since it is the brand: 5 #fef4f9, 10 #fde2ef, 20 #fcc6de, 30 #f98dbe, 40 #f6539d, 50 #f31a7c, 60 #d5176d, 70 #b6145d, 80 #98104e, 90 #790d3e. Washes: pale = accent at ~13% alpha (#f31a7c22), faint = ~7% (#f31a7c11).

Semantic mapping:

RoleLightDark
Page backgroundgray-10 #f8f8f8#151515
Raised surfacewhitegray-100 #232323
Text primary#151515white
Text secondarygray-70gray-40
Text tertiarygray-50gray-60
Accent stepfamily-50family-30 (lighter, brighter)
Dividerblack at 8%white at 8%

Two rules inside that table matter most for belonging:

  • Dark mode is not inversion. Accents shift from the 50 step to the 30 step so they stay luminous on #151515. Surfaces become white-at-low-opacity instead of solid grays.
  • The opacity ladder replaces most borders. Luma keeps a ladder of the "ink" color (black in light, white in dark) at 2, 4, 8, 16, 24, 32, 48, 64, 80 percent. Dividers are 8%, hover fills are pale washes, card surfaces in dark are 4%.

The brand gradient (logo moments and rare hero accents only, never section backgrounds):

/* light */ linear-gradient(-45deg, #8a18a8 0%, #ce2756 51.6%, #e7a90d 100%);
/* dark  */ linear-gradient(-45deg, #d118ff 0%, #f32861 51.6%, #ffbe19 100%);
/* homepage logotype (radial): #ab46dd -> #f31a7c 36% -> #f8712b 51% -> #eaab26 77% */

Shape

Radius scale: 0.25rem small, 0.5rem default, 1rem large, 1.5rem cards. Modals use 1rem. Avatars and round buttons are full circles.

Luma applies squircle corner smoothing on top of the radius wherever the shape is large enough to notice:

.rounded-card {
  border-radius: 1.5rem;
  corner-shape: squircle; /* progressive enhancement; plain radius is the fallback */
}

Use corner-shape: squircle with the radius doubled relative to what a plain rounded corner would use (Luma pairs squircle 1rem where plain radius would be 0.5rem). Nothing on a Luma page has a sharp corner.

Elevation and glass

Shadows are layered five-stop ramps at very low alpha, one set for light theme and a stronger set for dark:

--shadow-xs: 0 1px 4px #0000001a;
--shadow-sm: 0 1px 3px #00000005, 0 2px 7px #00000008, 0 3px 14px #0000000a,
             0 7px 29px #0000000d, 0 20px 80px #0000000f;
--shadow:    0 1.6px 3px #00000005, 0 4.2px 7px #00000008, 0 8px 14px #0000000a,
             0 17.5px 29px #0000000d, 0 48px 80px #0000000f;
--shadow-modal: 0 0 0 1px var(--opacity-8), 0 3px 3px #00000008, 0 8px 7px #0000000a,
             0 17px 14px #0000000d, 0 35px 29px #0000000f, 0 -4px 4px 0 #0000000a inset;

Note the modal recipe: a 1px ring drawn in the shadow (not a border), then the ramp, then a subtle inset. Floating chrome (menus, sticky bars) uses a near-opaque surface plus backdrop-filter: blur(16px).

Card material:

  • Light: rgba(255,255,255,.8) fill, 1px solid white border.
  • Dark: rgba(255,255,255,.04) fill, 1px border at the same 4% white.
  • Hover: border steps up to the 16% opacity rung and a soft shadow fades in.

Layout

Content column is narrow and centered: --max-width: 820px default, 960px wide pages, 1080px extra-wide, with 1rem horizontal padding. Event pages are a two-column split: square cover art left, content right, collapsing to a single column on mobile. Luma pages breathe through spacing, not boxes; sections separate with whitespace and 8%-opacity dividers, not rules and panels.

Motion

--transition:       all .3s cubic-bezier(.4, 0, .2, 1);   /* default */
--fast-transition:  all .2s cubic-bezier(.4, 0, .2, 1);   /* hovers  */
--slow-transition:  all .6s cubic-bezier(.4, 0, .2, 1);
--bounce-transition: all .3s cubic-bezier(.54, 1.12, .38, 1.11); /* delight */

Everything that changes state transitions; nothing snaps. The bounce curve (slight overshoot) is the signature: use it for things appearing, toggles, and small celebratory moments. Loading states shimmer (a moving gray gradient), they do not spin. Reserve particles and confetti for genuinely celebratory moments (a successful RSVP), and respect reduced-motion preferences.

Components

Buttons

The recipe (from .lux-button): squircle radius 1rem, weight 500, generous horizontal padding, transition on all.

SizeHeightFontPadding
small30px14px7px 10px
medium38px16px10px 14px
large44px16px12px 16px

Variants, and where Luma spends color:

  • Primary (solid): neutral, not pink. Light theme: gray-90 #353535 fill, white text, hover gray-80. Dark theme: white fill, near-black text, hover gray-20. The main CTA on a page ("Register", "Request to Join") is this.
  • Light (translucent): ink at 8% fill, text at 64%, no visible border. Used for secondary actions ("View All", filters).
  • Brand/tinted: cranberry (or the event color) fills only appear in brand moments and themed event pages.
  • Icon-only buttons are circles; buttons with icons keep a small gap and 500 weight.

Inputs

Heights 28px small, 36px medium, 42px large (content-box plus 1px border). Surface-colored fill, 1px border in gray-20 (light) or gray-90 (dark), default radius, 16px text. Focus swaps the border to the primary text color; no glow rings.

Cards

Squircle 1.5rem, translucent material (above), padding around 0.75rem 1rem for rows and denser lists, more for feature cards. Cards are clickable as a whole ("hoverable actionable"), lifting slightly on hover via border and shadow, not scale.

Menus and modals

Menus: near-opaque elevated surface (#fffd light, gray-100 at 80% dark) with blur(16px) behind, 0.5rem radius, hairline border at low opacity. Modals: 1rem squircle radius, the ring-plus-ramp modal shadow, dimmed overlay (#b5b5b599 light, #0009 dark).

Avatars and pills

Avatars are circles, stacked with slight cutout overlap when showing attendee lists. Pills (tags, filters) are fully rounded, small type (12 to 14px), translucent or pale-wash fills.

The theming architecture

Luma's most distinctive engineering-design idea, and the one worth copying in any ecosystem project, is a three-layer theme cascade:

  1. .theme-root / .theme-root.dark owns the global light/dark decision. Every token above flips here.
  2. .tint-root (.light / .dark) lets any subtree override the scheme locally (a dark event card embedded in a light page).
  3. .event-theme applies per-event branding. From one host-picked color, the system generates a full --brand-5 through --brand-90 ramp, each step with -transparent (0%), -translucent (25%), and -thick-translucent (80%) variants, then remaps surfaces onto it: tinted dark pages set the canvas to a deep shade of the brand color, cards to --brand-70/80, buttons and links to legible brand steps. Modifier classes (tinted, pattern, shader, emoji, confetti, floral, high-contrast) layer effects, and a high-contrast mode swaps in adjusted brand values.

Host-facing theming (per help.luma.com): 40+ named themes grouped as Minimal, Confetti, Emoji, Pattern, Seasonal, Warp, and Special Effects. Each bundles a display typeface (loaded per-event, for example Degular via Adobe Fonts, layered in front of the system stack for the event title only), a color scheme, light/dark variants, and optional particles. Custom colors are auto-adjusted for contrast.

The transferable lessons:

  • One accent in, whole theme out. Ask for a single color, derive a ramp, map semantics onto ramp steps. Never hand-pick twelve colors.
  • Keep the chrome neutral. Buttons, text, and surfaces stay in the neutral system so event content and cover art can carry the personality.
  • Display fonts are content, not UI. A decorative typeface may style the one big title; body, buttons, and labels stay in the system stack.

Voice and copy

  • Short, warm, plain words. "View All", "Request to Join", "You're in."
  • Title case for section and page headings ("Discover Events"), sentence case inside components.
  • Friendly, never corporate; enthusiasm through brevity and the occasional well-placed spark, not exclamation-mark walls.
  • Never use em dashes. Use commas, periods, or parentheses.

Starter stylesheet

Drop-in foundation distilled from the extracted values. Class names follow Luma's own convention so live-site inspection maps 1:1:

/* luma-tokens.css: distilled from luma.com production CSS, 2026-08 */
.theme-root {
  --font: -apple-system, BlinkMacSystemFont, "Apple Color Emoji", Inter, Roboto,
          "Segoe UI", "Helvetica Neue", Arial, "Noto Sans", sans-serif;
  --mono-font: "SF Mono", Menlo, Monaco, Consolas, "Courier New", Courier, monospace;

  --font-size-xs: .8125rem; --font-size-sm: .875rem; --font-size-md: 1rem;
  --font-size-lg: 1.125rem; --font-size-xl: 1.25rem; --font-size-xxl: 1.375rem;
  --font-weight-medium: 500; --font-weight-bold: 600;
  --title-line-height: 1.2; --default-line-height: 1.5;

  --white: #fff; --black: #151515;
  --gray-10: #f8f8f8; --gray-20: #ececec; --gray-30: #e0e0e0; --gray-40: #d4d4d4;
  --gray-50: #b5b5b5; --gray-60: #959595; --gray-70: #757575; --gray-80: #555;
  --gray-90: #353535; --gray-100: #232323;

  --cranberry: #f31a7c; --barney: #ab46dd; --purple: #682fff; --blue: #146aeb;
  --green: #3cbd2c; --yellow: #d69712; --orange: #f8712b; --red: #ed2b32;

  --brand-color: var(--cranberry);
  --brand-pale-bg-color: #f31a7c22;
  --brand-faint-bg-color: #f31a7c11;

  --primary-color: var(--black);
  --secondary-color: var(--gray-70);
  --tertiary-color: var(--gray-50);
  --primary-bg-color: var(--white);
  --page-bg-color: var(--gray-10);
  --divider-color: rgba(21, 21, 21, .08);
  --opacity-8: rgba(21, 21, 21, .08);
  --opacity-16: rgba(21, 21, 21, .16);

  --card-bg-color: rgba(255, 255, 255, .8);
  --card-border-color: #fff;
  --primary-button-bg-color: var(--gray-90);
  --primary-button-color: var(--white);
  --input-border-color: var(--gray-20);

  --small-border-radius: .25rem; --border-radius: .5rem;
  --large-border-radius: 1rem; --card-border-radius: 1.5rem;

  --shadow-xs: 0 1px 4px #0000001a;
  --shadow-sm: 0 1px 3px #00000005, 0 2px 7px #00000008, 0 3px 14px #0000000a,
               0 7px 29px #0000000d, 0 20px 80px #0000000f;
  --shadow-modal: 0 0 0 1px var(--opacity-8), 0 3px 3px #00000008, 0 8px 7px #0000000a,
               0 17px 14px #0000000d, 0 35px 29px #0000000f, 0 -4px 4px 0 #0000000a inset;
  --backdrop-blur: blur(16px);

  --transition: all .3s cubic-bezier(.4, 0, .2, 1);
  --fast-transition: all .2s cubic-bezier(.4, 0, .2, 1);
  --bounce-transition: all .3s cubic-bezier(.54, 1.12, .38, 1.11);

  --max-width: 820px; --max-width-wide-page: 960px; --horizontal-padding: 1rem;

  --default-gradient: linear-gradient(-45deg, #8a18a8 0%, #ce2756 51.6%, #e7a90d 100%);

  background: var(--page-bg-color);
  color: var(--primary-color);
  font-family: var(--font);
  line-height: var(--default-line-height);
}

.theme-root.dark {
  --cranberry: #f98dbe; --barney: #d27cfe; --purple: #b596ff; --blue: #76adff;
  --green: #77d86b; --yellow: #f2ca77; --orange: #fba67a; --red: #ff766d;

  --primary-color: var(--white);
  --secondary-color: var(--gray-40);
  --tertiary-color: var(--gray-60);
  --primary-bg-color: var(--black);
  --page-bg-color: var(--black);
  --divider-color: rgba(255, 255, 255, .08);
  --opacity-8: rgba(255, 255, 255, .08);
  --opacity-16: rgba(255, 255, 255, .16);

  --card-bg-color: rgba(255, 255, 255, .04);
  --card-border-color: rgba(255, 255, 255, .04);
  --primary-button-bg-color: var(--white);
  --primary-button-color: var(--black);
  --input-border-color: var(--gray-90);

  --default-gradient: linear-gradient(-45deg, #d118ff 0%, #f32861 51.6%, #ffbe19 100%);
}

/* sample components in the Luma idiom */
.lux-card {
  background: var(--card-bg-color);
  border: 1px solid var(--card-border-color);
  border-radius: var(--card-border-radius);
  corner-shape: squircle;
  transition: var(--transition);
}
.lux-card:hover { border-color: var(--opacity-16); box-shadow: var(--shadow-sm); }

.lux-button {
  font: var(--font-weight-medium) var(--font-size-md)/1 var(--font);
  background: var(--primary-button-bg-color);
  color: var(--primary-button-color);
  border: 1px solid transparent;
  border-radius: var(--large-border-radius);
  corner-shape: squircle;
  padding: 10px 14px;
  cursor: pointer;
  transition: var(--transition);
}
.lux-button.light { background: var(--opacity-8); color: var(--secondary-color); }

Reject these reflexes

Any of these instantly breaks belonging next to Luma:

  • A custom webfont for UI text. The system stack is the single strongest tell.
  • Font-weight 700+, tracked all-caps labels, or tiny gray body text.
  • Pure #000 canvases or stark white-on-black with hard corners. Luma's dark is #151515 and everything is rounded.
  • Sharp corners anywhere, or timid 2 to 4px radii on cards that should be 24px squircles.
  • Solid opaque cards with heavy visible borders instead of translucent material with hairline self-colored borders.
  • Corporate blue CTAs, or cranberry-filled primary buttons everywhere (Luma's main buttons are neutral; pink is an accent).
  • Gradients as section backgrounds, glows, neon, or glassmorphism theatrics. The brand gradient is scarce.
  • Dark mode as a naive inversion that keeps 50-step accents (they go muddy; shift to the 30 step).
  • Spinners where a shimmer belongs; snap state changes with no transition; parallax or scroll-jacking.
  • Dense dashboard aesthetics: dividers everywhere, boxed sections, icon tiles, badge soup.
  • Em dashes in copy.

Review checklist

Before shipping, verify against the live site, not memory:

  1. Squint test: on both themes, does the page read as calm neutral surfaces + one warm accent + generous rounding? Could a screenshot sit in a folder of luma.com screenshots without sticking out?
  2. Font check: open on a Mac; the UI must render in San Francisco, no webfont request for body text.
  3. Corner check: nothing sharp; big surfaces are squircles.
  4. Dark check: canvas #151515, surfaces white at 4 to 8%, accents at the light ramp steps.
  5. Motion check: hovers transition at .2 to .3s, at least one small bounce-curve delight, shimmer loading, reduced-motion respected.
  6. Identity check: the project is clearly yours; Luma's name and spark appear only where they refer to Luma.
design.md/youtube-shorts-design.md

YouTube Shorts

extracted from youtube.com/shorts on 2026-09-10

Near-black surfaces with translucent white layers instead of fills. The 9:16 rounded player, a 48px circle action rail, a 240px guide. Dark only.

104 lines Roboto (system, no webfont) not installed as a skill
examples/yts/example-feed.html A full desktop short-video feed: guide, search, a snapping 9:16 player with a 48px action rail, and a comments and description panel that opens beside it. open full page ↗
examples/yts/components.htmlopen full page ↗

YouTube Shorts design language (extracted 2026-09-10)

Purpose: make FlyFeed look native to the YouTube Shorts desktop surface without impersonating it. Provenance: every value below was read from the live site with getComputedStyle on youtube.com/shorts/{id} (logged in, dark theme, 1440px viewport) plus a stylesheet census (1,464 custom properties, 25,938 rules). YouTube publishes no design guideline; its own token names are now hashed (--tffc2fd3a644f6275), so the names here are descriptive, not theirs. Re-inspect if the site diverges. No official YouTube docs were used because none exist.

Kinship, not impersonation

Borrow: the material language (near-black surfaces, translucent white layers, Roboto, the 9:16 rounded player, the 48px circle action rail, the 240px guide, pill buttons). Do not borrow: the YouTube logo, the #FF0033 play-button mark, the Shorts glyph, the wordmark, or any claim of affiliation. FlyFeed uses its own mark and name.

Tokens

:root {
  --bg: #0f0f0f;                       /* html background, the only true surface */
  --bg-raised: #121212;                /* search box fill */
  --line: #303030;                     /* 1px borders (search box) */
  --text: #f1f1f1;                     /* primary text and icons */
  --text-2: #aaaaaa;                   /* secondary text */
  --text-3: #717171;                   /* disabled, census rank 3 grey */
  --white: #ffffff;                    /* overlay text ON the video is pure white, not #f1f1f1 */
  --layer-05: rgba(255,255,255,0.05);
  --layer-10: rgba(255,255,255,0.1);   /* action buttons, nav buttons, hover, active guide row */
  --layer-20: rgba(255,255,255,0.2);   /* pressed */
  --scrim-30: rgba(0,0,0,0.3);
  --scrim-60: rgba(0,0,0,0.6);
  --scrim-80: rgba(0,0,0,0.8);
  --blue: #3ea6ff;                     /* links, call to action */
  --red: #ff0033;                      /* YouTube brand red. Reserved: do NOT use as an accent */
  --font: Roboto, Arial, sans-serif;   /* no webfont is loaded; system Roboto or Arial fallback */
}

Typography scale actually in use (census): 10, 11, 12, 14, 16, 18, 24px. Weights 400 and 500 only; 700 appears 35 times in 2,500 and should be treated as absent.

RoleSize / lineWeightColor
Guide (sidebar) row14 / 20400, active 500--text
Overlay channel handle14 / 20400--white
Overlay title14 / 20400--white
Action rail count label12 / 18400--text
Pill button (Subscribe)12 / 32500black on white
Search input16400--white

Geometry

  • Masthead: 56px tall, padding: 0 16px, transparent over --bg. Content starts at y=64 (8px gap).
  • Guide (left nav): 240px wide. Rows are 40px tall, 204px wide, inset 12px, radius 10px, icon 24px at x=72 for the label. Active row: weight 500 and --layer-10 fill.
  • Player: 9:16, 452.25 x 804 at this viewport (height = viewport - 104), radius 12px, overflow: hidden, object-fit: cover, black letterbox. Horizontally centered in the content area, not the viewport.
  • Overlay host: flex row, justify-content: space-between, align-items: flex-end, width = player + 72. i.e. [player][12px][48px rail][12px].
  • Action rail: column of 48x48 circles (radius 24), fill --layer-10, icon 24px --text, label 12/18 centered beneath. Each button+label block is 78px tall. Rail sits OUTSIDE the video, bottom-aligned to the player. Order on YouTube: like, dislike (hidden here), comments, share, remix, more.
  • Bottom metadata: inside the player, full width, linear-gradient(0deg, rgba(0,0,0,.4), rgba(0,0,0,0)), radius 0 0 12px 12px, 76px tall, 16px inset. Stack: [avatar 32px circle, 8px, handle, 8px, Subscribe pill] then [title].
  • Top gradient: 48px scrim at the top of the player for controls.
  • Nav up/down: 56px circles (radius 28), --layer-10, 24px icon, pinned right of the content area, vertically centered.
  • Search box: 46px tall, radius 40px, --bg-raised, 1px solid --line, padding 0 6px 0 16px.
  • Radii in use: 50% (avatars), 24 (48px buttons), 28 (56px buttons), 16 (32px pills), 12 (player), 10 (guide rows), 8, 4, 2.

Behavior

  • Feed is a vertical stack of players; one is active; up/down moves exactly one.
  • Autoplay muted; a single click on the video toggles pause.
  • Hover on any --layer-10 control brightens to --layer-20.
  • Counts are compact (12M, 29K, 289).
  • Sentence case everywhere. Labels are one word.

Reject list

  • No gradients except the two video scrims. No glows, no colored shadows.
  • No accent color on chrome. The only saturated colors on screen belong to the video.
  • Never #000 as a page background; the page is #0f0f0f and the video is what is black.
  • No 700 weight. No letter-spacing. No uppercase labels.
  • No borders on buttons; translucent fills carry the affordance.
  • Never place the action rail inside the video on desktop.
  • Do not reproduce the YouTube logo, the Shorts glyph, or the brand red on any control.

Review checklist

  • [ ] Page background is exactly #0f0f0f; text is #f1f1f1, not white, except on video.
  • [ ] Player is 9:16, radius 12, height = viewport - 104.
  • [ ] Rail buttons are 48px circles at 10% white, labels 12px beneath, rail outside the video.
  • [ ] Guide rows are 40px, radius 10, 14/20, active = 500 + 10% white.
  • [ ] Only Roboto/Arial, only weights 400/500.
  • [ ] No YouTube marks anywhere.
design.md/method

How these were made

skills/user/design-system-extraction

Every file in this index was reverse-engineered from a live site rather than copied from a brand kit. The procedure is the same each time.

The rule that decides everything

Read the site, not a description of the site. Brandfetch entries, Dribbble mockups and Figma Community kits are guesses made by people who do not work there. Every value in these files was read off the served CSS or a getComputedStyle probe on a visible element, and each file records the exact pages sampled and the date.

Steps

  1. Check for a published source first. Lemma has a real brand page and a 15-page PDF, so that wins over inference. The other four publish nothing; /design.md, /brand, /style-guide and /llms.txt all 404, and everything comes off the DOM.
  2. Run the token extractor across several pages, not one. A home page alone will not show the empty state, the table view, or the detail page, and those are where the real rules live.
  3. Census the values rather than collecting them. Counting that 645 elements are 12px and 350 are 14px tells you which is the default and which is emphasis. A list of every size present tells you nothing.
  4. Separate framework defaults from decisions. Framer emits hidden 12px sans-serif nodes and rgb(0,0,238) links nobody chose.
  5. Write the rule, not the value. "14px/20px Geist for everything, hierarchy from colour" is usable. A table of 40 hex codes is not.
  6. Mark conflicts instead of resolving them. Where Lemma's published guidelines disagree with Lemma's production site, both are stated and flagged.
  7. State the kinship boundary: what to borrow (geometry, scale, material language) and what never to borrow (wordmarks, logos, taxonomy, copy).

Proving it

A guideline is only finished when something can be built from it without looking back at the original site. Every example page in this index was built from its file alone, with invented names and content, so the file is doing the work and not the source.

Shelf life

Each file is a dated snapshot and says so at the top: on any conflict between the file and the live site, the live site wins. Re-extract when a site visibly diverges.

Installing one as a skill

Three of the five live in skills/user/<brand>-design/SKILL.md, symlinked into ~/.claude/skills, ~/.cursor/skills and ~/.agents/skills, so any agent can load them by name. Luma and YouTube Shorts are still standalone files.

Extracted with skills/user/design-system-extraction Snapshots, not contracts. The live site always wins.