Design language
Notion, faithfully. Warm ink, small radii, quiet chrome, instant hover.
src/app/globals.css. Nothing in a component should ever hard-code a hex, a radius, or a shadow.0. Brand vs. chrome
Your logo is pills, gradients and saturated color. Notion is hairlines, 3px corners and muted ink. Both are right โ they just govern different halves of the product.
The split: Notion owns the chrome โ sidebar, menus, forms, settings. The brand owns the canvas and the landing page. And the three pills in your mark are already a legend: purple routes, blue services, amber data.
1. Ink, not gray
The foundation is #37352F โ a brown-black. Every gray in the product is that same ink at low alpha, which is why the surfaces read as paper instead of glass. A cool neutral gray anywhere in this app is a bug.
2. Surfaces
Depth comes from tint, not from shadow. Shadows are reserved for things that genuinely float โ popovers and modals.
3. Type
Inter, tightening as it gets bigger. Body sits at 16px with 1.5 line-height and never drops below 14px for anything a user reads.
4. Controls
Buttons are quiet. secondary is the workhorse โ a hairline box on the page. primary is rationed to one per view, and most actions are ghost.
5. Hover is the interface
Nothing shows chrome until the cursor is near it. Rows fill at 20ms; handles, menus and add-buttons fade in at 100ms. Hover the rows below โ and the left gutter of any block on this page.
6. Color, used sparingly
Nine hues, each with a text value and a background tint. They label things โ a node type, a kind of change โ they never decorate.
POST /v1/billing/webhookservices and models.7. Radius & elevation
Radii stay small โ 3px is the hover-chip value and the single most recognizable dimension in the system. Shadows are layered hairline + soft + wide, never one big blur.
8. Code
@router.post("/v1/billing/webhook")
async def stripe_webhook(request: Request):
event = verify_signature(await request.body())
await handle(event)
return {"ok": True}