Build Mixo

YESback to the verdict

0%0 of 17 items done

Saved on this device only. Tick prerequisites first, then work the phases in order · do not start one until the checks above it pass.

A landing page builder whose hard problems are keeping a page as data so regeneration cannot eat hand edits, publishing static files within a host's real limits with a rollback, and collecting email addresses from a static site without pretending to be a mailing list provider.

estimated effort weekendthe files for this build are in the project pack

RuntimeNext.js 15 and TypeScript for the editor, run locallyStorageSQLite holding pages as structured sectionsGenerationOne model behind a single interface, filling named fields onlyOutputStatic HTML, CSS and images written to a folder, no client frameworkPublishingPush the folder to a static host with a version tag and a rollback

Before step 1

Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.

  1. installfree

    Why Runs the editor and the build.

    Get it nodejs.org, or nvm install 22. open ↗

    Verify node --version prints v22 or higher

  2. API keypennies per page, and it is worth setting a hard cap

    Why Generation fills section fields. One provider behind one interface is enough, and the app must stay usable with the key absent.

    Get it Create a key with your chosen provider and set a spending limit on it.

    Verify A section generates and validates against its schema, and removing the key leaves the editor working

  3. free tiers are common for a personal site

    Why Publishing is the phase where guessed limits cause silent failures, so the numbers come from the host's own documentation.

    Get it Choose a static host, read its current limits page, and record the figures with the date. open ↗

    Verify The README carries the limits and the date, and the publish step checks against them

  4. have readyroughly ten to fifteen pounds a year

    Why A landing page on a host's subdomain cannot be moved without losing every link to it, and moving is the point of owning the output.

    Get it Register a domain and point it at the static host per its documentation.

    Verify The published site answers on your domain over HTTPS

  5. decidefree

    Why The generator checks numbers against this list, so writing it first is what makes the check meaningful rather than decorative.

    Get it Write down every figure and proof point that is true about the thing you are launching, with where each came from.

    Verify Each appears in the facts table with its evidence

Data model

Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.

- sites(id, name, domain, created_at, published_version)
- pages(id, site_id, path, title, meta_description, created_at, updated_at)
- sections(id, page_id, ordinal, kind, fields_json, locked, source) · source is generated, edited or imported
- facts(id, site_id, key, value, evidence) · every number that may appear on the page, with where it came from
- versions(id, site_id, built_at, manifest_json, bytes_total, file_count, published_at, rolled_back_at)
- signups(id, site_id, email, created_at, consent_text, source_page) · plain storage, with a documented export
- A section is typed fields, never a blob of markup, which is what makes regenerating one section safe for the others.

Environment variables

These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.

VariableNeededExampleWhere the value comes from
DATABASE_PATHrequired./data/sites.dbSites, pages, sections, facts and versions.
MODEL_API_KEYsecretoptionalsk-...redactedGeneration only. Without it the editor still builds and publishes.
MODEL_NAMEoptionala current small text modelRecorded on every generated section.
BUILD_DIRrequired./outStatic output. What gets published, and nothing else.
PAGE_WEIGHT_BUDGET_KBrequired500A page over this fails the build.
SIGNUP_ENDPOINT_URLoptionalhttps://forms.example.com/signupWhere the published form posts. Empty means the form is not rendered.

The build, in order

  1. A page that is data before it is HTML

    Sections are typed fields and one deterministic renderer turns them into a page.

    1. Six kinds that cover a landing page beats forty that cover everything.

      Files src/lib/sections.tssrc/lib/sections.test.ts

    2. Two ways to produce HTML means two ways to produce different HTML.

      Files src/render/page.ts

    3. A build you cannot reproduce is a build you cannot roll back to.

      Files tests/deterministic-render.test.ts

    done when · tick each as it passes
    watch out
    • Storing each section as a blob of HTML because the model produced HTML means every later edit is a string operation, two sections can define the same class name with different meanings, and nothing can be restyled without a find and replace across every page you ever generated. Type the fields, keep markup out of the database, and let the renderer own every tag.
  2. Generation that fills fields and invents no facts

    The model writes into named fields, and any number it produces must match a recorded fact.

    1. Structured fields in, structured fields out, or nothing.

      Files src/lib/generate.ts

    2. The one check that keeps a demo site from lying about a real business.

      Files src/lib/claims.tstests/claims.test.ts

    3. Nobody should lose a rewritten headline to a regeneration.

      Files src/lib/regenerate.ts

    done when · tick each as it passes
    watch out
    • Asking a model for a landing page and storing whatever comes back gets you 'trusted by over 2,000 teams' on the site of a product with four customers, and because it reads exactly like the rest of the copy nobody deletes it before the page goes live. Gate every numeric claim on a recorded fact, and refuse the section rather than publishing an invented one.
  3. A build that a browser can load on a bad connection

    The output is static files, sized and linked correctly, with no runtime requirement.

    1. A hero image is not a photograph archive.

      Files src/build/images.ts

    2. Publishing is the wrong time to find out.

      Files src/build/links.ts

    3. It is what makes the next phase's limits checkable.

      Files src/build/manifest.ts

    done when · tick each as it passes
    watch out
    • Shipping the original photographs straight from the upload puts four megabytes of hero image on a page whose entire text is two hundred words, and the person on a train sees nothing for eight seconds. Resize at build time to the widths the layout uses, record the total in the manifest, and fail the build over a stated page weight budget.
  4. Publishing inside the host's real limits, with a way back

    A deploy is a tagged version that can be rolled back, and the limits are checked before the push.

    1. Read them from the host's own documentation, and record when you read them.

      Files src/publish/limits.ts

    2. Rollback should not depend on your build still being reproducible today.

      Files src/publish/versions.ts

    3. Soft limits fail quietly, which is the worst way to fail.

      Files src/app/publish/page.tsx

    done when · tick each as it passes
    watch out
    • Treating publish as a file copy means the first bad deploy is live until you rebuild, and a host's soft build limit turns an afternoon of small fixes into a site that silently stops updating while you keep pressing publish. Tag every version, check the manifest against the host's stated limits first, and make rollback a single command that does not rebuild anything.
what this build does not replace
after v1, if you want it

Need the files? The project pack on the verdict page hands your agent the whole brief · more website builders.