Skip to content

The bar for generated pages

Every page a product generates meets one bar: accessible, found by search engines, fast on a phone, and written in every locale.

From a deploy to a page that passes

Your own pages meet the same bar as the generated ones. Filled: what a reader sees.

The numbers

The bar is part of the generator, so a generated page meets it by construction. These are the lines it holds.

MeasureThe bar
Text contrast4.5:1; 3:1 for large text, outlines and focus rings
Targets24 by 24 pixels, or spaced as far apart
Largest paint2.5 seconds at the 75th percentile on a phone
Response to input200 milliseconds at the 75th percentile
Layout shift0.1 or less
Automated accessibility checks0 violations at 390 and 1440 pixels wide, in both schemes

Speed is measured on a phone over a slow network, with every page readable before any script runs.

Five readers, one page

Each row is a reader the generator builds for, and what it does for them.

The readerWhat they getHow the page is built for it
Someone on a screen reader or a keyboardEvery page, form and drawing in words, in order, with focus always visibleWCAG 2.2 AA in both schemes: contrast, a label on every control, heading order, keyboard paths, motion only when allowed. Each form from a Verb names every field, marks the required ones, and shows a refusal's cure beside its field
Someone searchingA page per Noun and task whose title, description and card say what it answersMetadata from the file's comments, a canonical URL on the Host, structured data for the product and each price, sitemap.xml and robots.txt
Someone on a phone and a slow networkThe price and the first answer in under three seconds, nothing moving under their thumbNo framework runtime on a static page, images at the size the screen needs, fonts cut to the characters used
Someone in another languageTheir locale, dates, numbers and money in their format, right-to-left where the script runs that wayA path per locale and hreflang between them
An agentThe same words, at the same address, in the locale it asks forA Markdown twin per page, llms.txt in reading order, and JSON at /

Each image and drawing carries words from the file's descriptions, and each chart has its table. Test, every branch and every review link answer noindex. A superseded Version's pages point to the live one, and a renamed Noun or Verb answers its old path with 301 while its Version answers.

Your own pages and your brand

site.pages and docs.guides meet the same bar.

Check refuses a page that fails with page_below_bar and the line that fails, and nothing of that deploy serves.

brand.color holds 4.5:1 on the light page, and the dark scheme takes the nearest lighter step that holds there. Check refuses a colour that fails with brand_contrast.

Example

The Dataset API adds German and a page of its own, and keeps its brand colour.

definition.ts
19 lines
import { define, secret } from 'apis.dev'

/** Any company by its domain, with its headcount and sector, in English and German. */
export default define({
  name: 'companyfacts', domain: 'companyfacts.apis.dev',
  brand: { logo: './logo.svg', color: '#0b6e4f' },        // 6.3:1 on white; lighter in dark
  site: { pages: { '/coverage': './coverage.mdx' } },     // checked against the bar
  docs: { guides: ['./guides/enrich-a-crm.mdx'] },
  locales: ['en', 'de'],                                  // a path, a sitemap and hreflang each
  sources: {
    firms: { origin: 'https://firms.example.com', key: secret('FIRMS_KEY') },
  },
  Company: {
    domain:    { type: 'url!#',   from: 'firms', map: 'website' },
    employees: { type: 'number?', from: 'firms', map: 'headcount' },
    sector:    { type: 'string?', from: 'firms', map: 'industry' },
  },
  offer: { 'Company.get': 0.002, 'Company.search': 0.005 },
})

Change the colour to a light green, and check names the one key that fails and the nearest colour that holds.

Response
HTTP/1.1 200 OK
Content-Type: application/json
X-Cost: price=0, currency="USD"
X-Request-Id: req_93f1a6c0e2d7b4581c9a

{
  "ok": false,
  "problems": [
    {
      "code": "brand_contrast",
      "at": "/brand/color",
      "message": "#7fd1ae reads 1.8:1 on the light page; #2b7a5c holds 4.5:1"
    }
  ]
}

Run it as npx apis.dev check, POST https://apis.dev/v1/products/companyfacts/checks, or check on the docs MCP.

Measured from outside

The Verifier runs the accessibility checks and the speed budget at each deploy and each day, and the status page prints them.

Each deploy renders and checks every page in every locale before any of it serves; the next Version supersedes a page, which stays readable. A failed measurement writes an Event, reaches your alert route, and shows on the status page.