Skip to main content
This page is everything you need to embed a widget on your page. The whole integration is one URL inside an <iframe> tag. There is no JavaScript to install and no events to listen for. Pick a widget, point it at a basket, choose a width, and you are done.

The URL

<widget-name> is one of hero-card, performance, overview, or pill-row. <slug> is the basket’s slug, which is the same value used in app.cesto.co/product/<slug>.

The four widgets

Widget<widget-name>Aspect ratioMin width
Hero cardhero-card5 : 4360 px
Performanceperformance15 : 8540 px
Overviewoverview2 : 1480 px
Pill rowpill-row15 : 2300 px
Aspect ratio is the recommended shape. Min width is the floor below which the widget starts shedding affordances (the Invest text collapses to an arrow, allocation pills hide, and so on). The widget still paints below that floor, it just simplifies.

Embed snippet

Drop this into any HTML page. Replace hero-card and ai-leaders-portfolio with the widget and basket slug you want.
That is the entire integration. The widget fetches its own data, renders the basket, and opens app.cesto.co/product/<slug> in a new tab when the user taps Invest.

Finding a basket slug

Every basket on app.cesto.co has a slug in its URL. Open the basket on the website and copy the last path segment, which is the slug:
In the URL above, the slug is the final segment: ai-leaders-portfolio. You can also discover slugs programmatically through the Cesto API at GET /products.

Sizing

The widget’s interior uses CSS container queries, so it scales proportionally with whatever pixel width its parent column gives it. The recommended pattern is width: 100% plus aspect-ratio, letting the page decide width and letting the aspect ratio decide height.
A few tips:
  • Keep width: 100% whenever possible and let the host column drive the width. The widget will reflow itself.
  • Use loading="lazy" for widgets below the fold so the iframe defers until it scrolls into view.
  • The widget host sets X-Frame-Options: ALLOWALL and frame-ancestors *, so any origin can embed it.

Theming

By default, every widget renders Cesto’s canonical dark theme, so it looks consistent wherever it’s embedded. If you want it to match your own site, you can override individual design tokens: colors, corner radius, fonts, and spacing.

Theme fields

Every field is optional. Pass any subset; anything you don’t set keeps the Cesto default.
FieldTypeWhat it controls
accent / accentForegroundCSS colorPrimary action color, and the text on it
background / foregroundCSS colorSurface, and body text
muted / mutedForegroundCSS colorSecondary surfaces, and their text
borderCSS colorBorders and dividers
positive / negativeCSS colorGains and losses
radiuspx or lengthCorner rounding
fontFamily / fontFamilyMonofont stackText and numeric fonts
paddingpx or lengthOuter content padding
maxWidthnone or lengthCap on content width
A bad value can’t break a widget. Each field is validated on its own: an invalid value (say, accent=notacolor) is dropped and falls back to the Cesto default, and unknown params are ignored.

Setting a theme

The simplest way is to add the tokens as query params on the iframe URL, right alongside basket. Remember to URL-encode colors (# becomes %23):
A widget’s body is transparent by default, so it blends into your page background. Set background explicitly if you want it to fill with its own color.

Live theming (optional)

To change the theme after load (for example, to follow a light/dark toggle on your page), the optional @cesto/embed-sdk host helper lets you push config in live and auto-resizes the iframe to fit its content:
You can match your host’s look by reading your own design tokens off the page and passing them straight in.

What stays fixed

Theming covers look, not content. A widget’s copy, its data, and the Invest destination (app.cesto.co/product/<slug>) are fixed and stay consistent with the rest of the Cesto product. If you need anything beyond a themeable basket card on your page, that is a different surface and not a widget.

Next

Open the playground

Try every widget against any live basket, right inside the docs.