Skip to content

Public pages

Greet signed-out visitors with a front page, and, where you allow it, let them read chosen records without an account.

examples/apps/lintel/landing.ts
export const lintelLanding = landing(
hero(
'Homes in three Denver buildings, managed by people who answer',
// …
[{ label: 'See homes to let', to: 'listings' }, applyTo('request-viewing', 'Ask for a viewing', 'secondary')],
{
// …
highlights: ['No application fee', 'Repairs booked within a day', 'Pets welcome'],
},
),
[
embed('listings', { title: 'Available now', limit: 6, sort: 'available_on' }),
// …
],
{ label: 'Lintel: homes to let in Denver' },

See it in the sample (examples/apps/lintel/landing.ts:6) (abridged: the full page adds questions and a closing call to action). Lintel declares it as welcome: lintelLanding in views, and opens it for signed-out visitors with home: [{ view: 'welcome', signedOut: true }, …].

Lintel signed out: the top bar with the brand, the public entries and Sign in, the hero’s heading, text, actions and highlights over a photo of a building, Available now below.

  • At /, signed out, the landing in the App’s own look under a top bar, the brand, the public entries and Sign in, in place of the staff sidebar; signed in, / is the App’s home, and /welcome previews the landing.
  • The hero’s photo, a row of a public file resource, beside the words (layout: 'split'; Lintel’s hero sets its words over the photo with layout: 'overlay'), and lists of public records as cards below it.
  • With slug: true on a resource, a public record’s address carries its name (/vehicles/1-2024-toyota-corolla-le), with a real <title> and a canonical link.
  • Use a landing when an App that signs people in has visitors who should learn what it is first; an App has at most one, and it needs auth.
  • Use auth.public when visitors should read real records (a catalog, a forum); without it a landing reads no data at all, only what the App’s definition says.
  • Use a public command form instead when visitors only send something (an application, a report), and nothing is read back.
Helper or key What it does Status
hero(heading, text?, actions, { image, layout, search, highlights }) The one heading; one to three actions: { label, to }, signIn(label), applyTo(form, label) Stable
features, steps, faq, text, contact Points, numbered steps, questions, prose, links; up to twelve sections in all Stable
callToAction(title, text?, actions) A closing prompt with one to three actions Stable
embed(view, { limit, sort }), search(view, filters) Records of a cards list; a search over a list’s filters (both need auth.public); a filter the list suggests suggests here too Stable
auth.public { read: [...], fields }: resources a signed-out visitor reads, read-only Stable
a resource’s slug: true A public record’s address carries its name; any other spelling answers 301 Stable

Everything on a landing is plain text, never HTML or Markdown, with length limits checked at load; a link is https://, mailto: or tel:. The footer is the App’s ui.footer. Every key: views reference, auth reference.

  • “A landing view requires auth” → add auth with a provider; an App without sign-in has no signed-out visitor to greet.
  • An embed or search refused without auth.public → name the list’s tables in auth.public.read, within the roles’ own read.
  • [authority-lock] after adding auth.public → the grant is part of authority.lock; review it, then tablewalk check --app . --write-authority and commit the lock.
  • A link to http:// or javascript: refused → use https://, mailto: or tel:.