Pages for Businesses, not pixels.

Render a page from a record, wear it in a Business's Theme, and hand the next one to your agent.

bash
pnpm add @mdxui/site@7.0.0-alpha.0
Try it
pages/harbor/home.jsonJSON
{
  "$schema": "https://next.mdxui.dev/schema.json",
  "theme": "quaypoint",
  "template": "api-product",
  "title": "Harbor tides: every berth, every tide",
  "description": "Tide tables for every berth in the harbor, as one API: heights, times and the window your keel clears.",
  "frame": "none",
  "sections": [
    {
      "kind": "nav",
      "shell": { "inset": "bleed", "density": "none", "surface": "glass", "sticky": true },
      "block": {
        "type": "nav",
        "menu": "none",
        "brand": { "href": "/", "text": "Harbor tides", "mark": "waves" },
        "links": [
          ["/pricing", "Pricing"],
          ["/integrations", "Integrations"],
          ["/blog", "Blog"]
        ],
        "cta": { "href": "/contact", "label": "Talk to the harbor" }
      }
    },
    {
      "kind": "hero",
      "shell": { "inset": "contained", "density": "hero" },
      "block": {
        "type": "hero",
        "layout": "centered",
        "stack": 560,
        "title": "Every berth, every tide.",
        "lede": "One call returns the tide table for any berth in the harbor: the times, the heights and the window your keel clears.",
        "actions": [
          { "href": "/pricing", "label": "See the plans", "kind": "solid" },
          { "href": "/contact", "label": "Talk to the harbor", "kind": "ghost" }
        ],
        "fine": ["1,000 calls a month free", "Heights to the chart datum"]
      }
    },
    {
      "kind": "band",
      "shell": { "inset": "contained", "density": "compact" },
      "block": { "type": "command-row", "command": "npm i harbor-tides", "note": "Node.js 22 or later" }
    },
    {
      "kind": "section",
      "shell": { "inset": "contained", "density": "section" },
      "block": {
        "type": "feature-grid",
        "cellHead": "icon",
        "head": { "eyebrow": "One call returns", "title": "The whole tide, per berth." },
        "items": [
          { "icon": "waves", "title": "High and low water", "text": "Times and heights for 14 days, at the berth itself." },
          { "icon": "anchor", "title": "Keel clearance", "text": "The hours a given draft clears, from the charted depth." },
          { "icon": "map-pin", "title": "Datum named", "text": "Every height names its chart datum." }
        ]
      }
    },
    {
      "kind": "section",
      "shell": { "inset": "contained", "density": "section", "surface": "plate" },
      "block": {
        "type": "api-call",
        "method": "GET",
        "path": "/v1/berths/{berth}/tides",
        "note": "One berth, 14 days",
        "examples": [
          {
            "label": "Pier 4, today",
            "request": "curl https://api.harbor-tides.example/v1/berths/pier-4/tides?days=1",
            "status": "200 OK",
            "response": "{\n  \"berth\": \"pier-4\",\n  \"datum\": \"chart datum\",\n  \"tides\": [\n    { \"at\": \"04:12\", \"type\": \"low\", \"m\": 0.6 },\n    { \"at\": \"10:31\", \"type\": \"high\", \"m\": 4.1 },\n    { \"at\": \"16:40\", \"type\": \"low\", \"m\": 0.8 },\n    { \"at\": \"22:58\", \"type\": \"high\", \"m\": 3.9 }\n  ]\n}"
          }
        ]
      }
    },
    {
      "kind": "section",
      "shell": { "inset": "contained", "density": "section" },
      "block": {
        "type": "endpoints",
        "head": { "eyebrow": "The API", "title": "Four routes, one shape." },
        "rows": [
          { "method": "GET", "path": "/v1/berths", "text": "Every berth in a harbor, with its charted depth" },
          { "method": "GET", "path": "/v1/berths/{berth}/tides", "text": "High and low water for one berth" },
          { "method": "GET", "path": "/v1/berths/{berth}/window", "text": "The hours a given draft clears" },
          { "method": "POST", "path": "/v1/alerts", "text": "A webhook before a window closes" }
        ]
      }
    },
    {
      "kind": "section",
      "shell": { "inset": "contained", "density": "section" },
      "block": {
        "type": "feature-split",
        "head": { "eyebrow": "For the bridge", "title": "Plan the passage, not the math." },
        "items": [
          { "icon": "clock", "title": "Windows, not tables", "text": "Ask for a draft and get the hours it clears." },
          { "icon": "bell", "title": "Alerts before the turn", "text": "A webhook fires an hour before a window closes." },
          { "icon": "shield-check", "title": "One source per berth", "text": "Each answer names its station and its datum." }
        ],
        "specimen": {
          "blocks": [
            {
              "type": "terminal",
              "variant": "plain",
              "title": "A 3.2 m draft at pier 4",
              "lang": "json",
              "lines": ["{", "  \"draft\": 3.2,", "  \"opens\": \"08:05\",", "  \"closes\": \"12:50\"", "}"]
            }
          ]
        }
      }
    },
    {
      "kind": "section",
      "shell": { "inset": "contained", "density": "section" },
      "block": {
        "type": "bento",
        "head": { "eyebrow": "Where it runs", "title": "In the tools a crew opens." },
        "panels": [
          {
            "title": "In a script",
            "text": "One package, typed.",
            "specimen": {
              "type": "terminal",
              "variant": "plain",
              "title": "window.ts",
              "lang": "ts",
              "lines": ["const w = await tides.window('pier-4', 3.2);"]
            }
          },
          {
            "title": "In an agent",
            "text": "One prompt, one berth.",
            "specimen": {
              "type": "agent-prompt",
              "label": "Ask an agent",
              "lines": ["Fetch the tide window for a 3.2 m draft at pier 4 today."]
            }
          },
          {
            "title": "On a chart plotter",
            "text": "Through the Chartplot integration.",
            "specimen": {
              "type": "api-call",
              "method": "POST",
              "path": "/v1/alerts",
              "examples": [
                { "label": "Alert", "request": "{ \"berth\": \"pier-4\", \"draft\": 3.2 }", "status": "201 Created", "response": "{ \"id\": \"al_42\" }" }
              ]
            }
          }
        ]
      }
    },
    {
      "kind": "section",
      "shell": { "inset": "contained", "density": "section" },
      "block": {
        "type": "compare",
        "head": { "eyebrow": "Against station tables", "title": "The berth, not the nearest station." },
        "cons": {
          "label": "Station tables",
          "items": ["Heights at the station", "One PDF per month", "Datum in the small print", "Math done by hand"]
        },
        "pros": {
          "label": "Harbor tides",
          "items": ["Heights at the berth", "Fourteen days per call", "Datum on every height", "The window computed"]
        }
      }
    },
    {
      "kind": "section",
      "shell": { "inset": "contained", "density": "section" },
      "block": {
        "type": "steps",
        "head": { "eyebrow": "How it works", "title": "From berth to window." },
        "steps": ["Name the berth", "Give the draft", "Read the window", "Set an alert before it closes"]
      }
    },
    {
      "kind": "section",
      "shell": { "inset": "contained", "density": "section" },
      "block": {
        "type": "text-stack",
        "layout": "stack",
        "head": { "eyebrow": "Where the numbers come from", "title": "Charted, measured, named." },
        "items": [
          { "title": "Charted depth", "text": "Each berth carries the depth from the latest harbor survey." },
          { "title": "Measured tide", "text": "Predictions are corrected every 6 minutes from the harbor gauge." },
          { "title": "Named datum", "text": "Every height says which datum it stands on." }
        ]
      }
    },
    {
      "kind": "section",
      "shell": { "inset": "contained", "density": "section" },
      "block": {
        "type": "agent-prompt",
        "label": "Paste into your agent",
        "lines": ["Install harbor-tides from npm.", "Fetch the tide window for a 3.2 m draft at pier 4 today.", "Print when it opens and when it closes."]
      }
    },
    {
      "kind": "closing",
      "shell": { "inset": "contained", "density": "closing" },
      "block": {
        "type": "closing",
        "layout": "stack",
        "head": { "eyebrow": "Free to start", "title": "Ask for your berth.", "lede": "A harbor master adds any berth in the harbor within one working day." },
        "actions": [
          { "href": "/contact", "label": "Talk to the harbor", "kind": "solid" }
        ]
      }
    },
    {
      "kind": "footer",
      "shell": { "inset": "contained", "density": "footer" },
      "block": {
        "type": "footer",
        "layout": "row",
        "brand": { "href": "/", "text": "Harbor tides", "mark": "waves" },
        "blurb": "Tide tables for every berth, as one API.",
        "columns": [
          {
            "title": "Product",
            "links": [
              ["/pricing", "Pricing"],
              ["/integrations", "Integrations"]
            ]
          },
          {
            "title": "Harbor",
            "links": [
              ["/blog", "Blog"],
              ["/contact", "Contact"],
              ["/terms", "Terms"]
            ]
          }
        ],
        "note": ["Harbor tides is a worked example on mdxui.dev.", "Rendered by @mdxui/site."]
      }
    }
  ]
}

Harbor tides is a worked example, not a customer · API product · its own Theme is quaypoint

Tasks

Build a…

Each kind of Business has a worked Site: real records, one Theme, a 404 and a contact flow. Open the one closest to yours and copy its records.

What ships

Every page carries one badge. Alpha ships on npm, Spec is written ahead of the package, and Study defines nothing.

Alpha: Ships on npm at 7.0.0-alpha.0@mdxui/siteOn npm at 7.0.0-alpha.0: the schema, the renderer and 27 Section types
Alpha: Ships on npm at 7.0.0-alpha.0@mdxui/themesOn npm at 7.0.0-alpha.0: the Theme contract and 5 Themes
Alpha: Ships on npm at 7.0.0-alpha.0HTML and JSONrenderPage, and the record served beside every page
Spec: Written ahead of the packageMarkdown, share image, emailRenderings written ahead of the package
Spec: Written ahead of the packageTheme playgroundcompileTheme runs; a whole page in your own Theme does not
Study: A study of the work. Defines nothing.App, View, DashboardThirteen App types studied; no App package

For agents

An agent reads the same docs as text, starts from a record that parses and checks its work against the schema.