Skip to content

feat(website): sell plain-language prompts and cross-model quality - #1202

Draft
vivek7405 wants to merge 5 commits into
mainfrom
feat/plain-language-prompt-pitch
Draft

feat(website): sell plain-language prompts and cross-model quality#1202
vivek7405 wants to merge 5 commits into
mainfrom
feat/plain-language-prompt-pitch

Conversation

@vivek7405

@vivek7405 vivek7405 commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator

Closes #1201

The /why-webjs page argued model-agnosticism only from the model side: any model can read the whole framework from node_modules, so no training data and no single blessed model are required. That leaves out the half a reader actually cares about. Someone with no technical background can write an ordinary request and still get back correct routing, a real schema, server code on the right side of the .server.ts boundary, and a coherent design system, because the conventions decide the shape of the app before the prompt is written. This adds a section making that argument, and extends the existing any-model section to say the output quality holds across model sizes rather than only that a small model can participate.

What changed

  • website/app/why-webjs/page.ts: a new "Describe what you want in plain language" section between the reason grid and the any-model section, built from the page's existing terminal-window language. It pairs a plain-English prompt with the files the conventions put that work in, so the argument is shown rather than asserted. The any-model section gains a paragraph tying model-agnosticism to output quality, and the meta description leads with the plain-language claim.
  • website/app/what-is-webjs/page.ts: one short echo folded into the existing "AI-first, readable end to end" capability rather than a seventh card, which would leave the three-column grid ragged. Worded differently from /why-webjs so the two pages do not chase the same phrasing.
  • website/app/page.ts: an accessibility fix that review pulled in, unrelated to the copy. A <pre> maps to role generic, where ARIA prohibits an author-supplied name, so the aria-label these code blocks already carried was a name no spec-following screen reader would announce. Every <pre> that already carried such a name now carries role="region", and the two that shared a label are named apart so they do not collapse into an ambiguous landmark pair. Fixing four blocks on one page and leaving ten identical ones would have left the site doing the same thing two ways.
  • website/test/ssr/why-webjs-ssr.test.ts: assertions on the new section's prose, on both demo panels, on the prompt panel containing no framework vocabulary, and on the description answering inside the 160-char snippet window.
  • website/test/ssr/pre-block-a11y.test.ts: the accessible-name rule pinned across all three pages that render code blocks, since it is a property of the site rather than of one page, and covering one page is how the other two drifted.

Deliberately excluded

  • The home page's copy. Lead the OG cards and home page with the model-agnostic benefit #1087 already led it with the model-agnostic benefit, and a third telling of the same story is the cannibalization the site's own SEO conventions warn against. The app/page.ts change above is the accessibility sweep, not positioning.
  • Any claim that models produce identical code, or that this removes the need to read what an agent writes. The copy says the opposite on the second point.
  • Any claim that the framework makes design converge. AGENTS.md is explicit that there is no design gate and that taste is the agent's job, so the copy credits design tokens set once in the root layout and says plainly that taste is the reader's to direct.

Test plan

  • Website server suite, 157/157
  • Website browser suite, 35/35
  • webjs check clean
  • Counterfactual at 00487b5a: deleting the demo grid reds the new grid test
  • Counterfactuals at b7928acf: duplicating the two card labels reds the landmark-uniqueness test, dropping the role from the codeWindow helper reds the role test, and putting a framework noun in the prompt reds the jargon test
  • Rendered at 1440 and 390 wide, light and dark, no console errors

Doc surfaces: website copy plus one accessibility fix. No packages/*/src change, so the framework doc surfaces, the scaffold, the MCP, the editor plugins, and the changelog are all N/A. The dogfood boot check is N/A beyond the website itself, which is the app being edited and is covered by its own suite.

The /why-webjs page argued model-agnosticism only from the model side:
any model can read the source, so no training data is required. That
leaves out the half a reader actually cares about. Someone with no
technical background can write an ordinary request and still get correct
routing, a real schema, server code on the right side of the boundary,
and a coherent design system, because the conventions decide the shape
before the prompt is written.

Adds a section making that argument with a prompt-to-files pairing, and
extends the any-model section to say the output quality holds across
model sizes rather than only that a small model can participate.
@vivek7405 vivek7405 self-assigned this Jul 31, 2026

@vivek7405 vivek7405 left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two pages were arguing model-agnosticism from the model side only, and the reader-side half is the better argument, so I like where this is going. Two things I want changed before it ships.

The design-system claim is the one that actually bothers me. We say in AGENTS.md that there is no design gate and that taste is the agent's job, so promising that the design comes out consistent across models is a claim the framework does not back. Tokens and the kit are the real mechanism and the copy should say so instead of implying convergence falls out of reading the source.

The other is the what-is-webjs card. Folding the sentence into an existing capability was the right instinct, but that cell is now more than twice its row-mates and h-full makes it set the row height, so it buys dead space inside the two cells beside it. The rest is wording: the meta description grew past every place it renders, the new paragraph repeats the one above it, and the two demo panels do not answer each other.

Comment thread website/app/why-webjs/page.ts Outdated
Comment thread website/app/what-is-webjs/page.ts Outdated
Comment thread website/app/why-webjs/page.ts Outdated
Comment thread website/app/what-is-webjs/page.ts Outdated
Comment thread website/app/why-webjs/page.ts Outdated
Comment thread website/app/why-webjs/page.ts Outdated
Comment thread website/app/why-webjs/page.ts Outdated
Comment thread website/app/why-webjs/page.ts Outdated
Comment thread website/app/why-webjs/page.ts Outdated
Review caught the design-system half of the any-model paragraph as an
overclaim. AGENTS.md says the opposite outright: there is no design gate,
taste is the agent's job, and an app sets its own palette in the root
layout. The paragraph now credits the real mechanism, design tokens set
in one place, and says plainly that taste is the reader's to direct.

Also from review: the meta description had grown to 444 chars with the
new clause sitting past every truncation point it feeds, so the claim
now leads it instead; the what-is-webjs card is back to roughly its
pre-change length, since h-full made the longest cell pin its row and
buy dead space in both neighbours; the two demo panels now answer each
other (the prompt no longer says page while the result shows two); the
static file listing is labelled files rather than terminal; and the four
scrollable pre blocks on the page carry role=region, so the aria-label
each already had is one ARIA permits.

@vivek7405 vivek7405 left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two of these are damage I did in the fix commit, which is the annoying kind. Trimming the what-is-webjs card to win back grid height cut the words that were carrying the accuracy: the framework claim is now flatly false against what core actually resolves to, and the AGENTS.md sentence now says five tools read a file that three of them do not. Length is the wrong thing to have optimized there.

The test gap is the one I most want closed. The demo grid is the bulk of what this PR adds and the whole thing could be deleted without reddening anything, which makes the coverage claim in the body hollow.

Path-level, since the lines are outside the diff: the role fix needs to go all the way or not at all. It landed on the four blocks on why-webjs and nowhere else, so the same prohibited-name construct is still on the codeWindow helper at app/what-is-webjs/page.ts:210 and on six blocks in app/page.ts (:106, :219, :235, :387, :396, plus its own helper). app/page.ts:235 is the worst of them, an aria-label with no tabindex and no role at all. Doing four and leaving ten is the worst of both ends.

Comment thread website/app/what-is-webjs/page.ts Outdated
Comment thread website/test/ssr/why-webjs-ssr.test.ts
Comment thread website/app/why-webjs/page.ts Outdated
Comment thread website/app/why-webjs/page.ts Outdated
The previous commit traded accuracy for grid height and lost. Trimming
the what-is-webjs card cut the two phrases that were carrying it: without
'with JSDoc in node_modules' the framework claim is flatly false, since
core resolves its default entry to dist/webjs-core.js, and without 'read
from one source' the AGENTS.md sentence claims five tools read a file
three of them do not. Both phrases are back, and the length is won by
dropping a convention detail instead.

The demo grid was also entirely unasserted, so it could have been
deleted with every test in the file still green. It now pins both panels,
their correspondence, and the accessible name each block carries, plus a
snippet-window assertion mirroring the one on the sibling page.

The role=region fix goes site-wide rather than stopping at one page: the
prohibited-name construct was on ten more blocks. The one block that is
not focusable loses its name instead of gaining a role, since nothing can
focus it to hear one.
@vivek7405

Copy link
Copy Markdown
Collaborator Author

Follow-up: the aria-label fix went site-wide rather than stopping at one page

The path-level note about the role fix is handled in 00487b5a, which does pull app/page.ts into a PR that is otherwise two pages of copy. I decided that was better than the alternative. A <pre> maps to role generic, where ARIA prohibits an author-supplied name, so an aria-label on one is a name a spec-following screen reader will not announce. Fixing four of fourteen blocks would have left the site doing the same thing two ways, which is harder to reason about later than either doing all of them or none.

Thirteen scrollable blocks get role="region", so the name each already carried becomes one ARIA permits. The fourteenth, app/page.ts:235, is the Preview/Code toggle body: it has no tabindex, so nothing can focus it to hear a name, and a named region there would only add landmark noise. That one loses the aria-label instead. No CSS or JS anywhere in the site selects pre or [role], so this is visually inert.

@vivek7405 vivek7405 left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Round three and two of these are mine again, both from the sweep I did last commit.

The negative assertion I added is the worst of them. It is tautological, so the guarantee it is named for is not held by anything, and I wrote it while fixing a test gap, which is the exact failure the gap fix was supposed to close.

The role sweep also traded one a11y problem for another in two places: two cards now share a landmark name, and the block I chose to strip rather than promote is the one that actually has the keyboard defect. I stripped the name and left the reason the name was unreachable.

And the framework claim is still not right even after restoring the words round two asked for. The scoping was necessary but not sufficient: the bundle is in node_modules as well, so the comparative is the part that has to go, not the qualifier.

Comment thread website/test/ssr/why-webjs-ssr.test.ts Outdated
Comment thread website/app/page.ts Outdated
Comment thread website/app/page.ts
Comment thread website/app/what-is-webjs/page.ts
…rades

The negative assertion added last commit could never match. The prompt's
chevron sits in its own span, so a regex requiring it adjacent to the
prose is false whether or not the regression is present, which left the
guarantee it is named for held by nothing. Plain substring now.

The role sweep also traded one problem for another twice. Two template
cards shared an aria-label, so promoting both to landmarks produced a
duplicate-named pair where previously the prohibited name was simply
ignored; each is now named for the template it shows. And the block the
sweep stripped rather than promoted is the one with the real defect: it
scrolls horizontally with no tabindex, so revealing it hands a keyboard
user a region they cannot reach. It gets the focus stop and the name.

The framework claim needed the comparative removed, not the qualifier
restored: dist/webjs-core.js is what the main entry resolves to and it
lives in node_modules too, so scoping the sentence there never made
'rather than a compiled bundle' true. The readable source is the part
that is true, so that is all it claims now.

@vivek7405 vivek7405 left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two of these say my last commit was the regression, and having checked both premises myself I agree.

The compiled-bundle edit is the clearer mistake. I changed one of five instances of a claim that root AGENTS.md itself makes, inside a PR about two pages of copy, and the result is that the page now argues with itself and with its own structured data. Whether that claim is right across the site is a real question, but it is not this PR's question, so that clause goes back to exactly what it said before I touched it and the PR takes no position.

The focus stop is the same shape of error. I accepted "it scrolls" without measuring, and the sample is one short line that does not scroll at any width anyone will see. That block goes back to carrying nothing.

The two test findings are fair and both are about the assertion holding the thing its message claims, which is the same problem as the tautology, one step subtler. The uncovered sweep on the other two pages needs pinning too.

Comment thread website/app/what-is-webjs/page.ts Outdated
Comment thread website/app/page.ts Outdated
Comment thread website/test/ssr/why-webjs-ssr.test.ts Outdated
Comment thread website/test/ssr/why-webjs-ssr.test.ts Outdated
…ite-wide

Two corrections from the last round were themselves the regression, and
checking both premises confirmed it.

The compiled-bundle clause is back to exactly its pre-change wording. It
appears five times across the site and in root AGENTS.md, so editing one
instance left the page disagreeing with its own FAQ, with the FAQPage
schema that FAQ feeds, and with /why-webjs. Whether the claim holds
everywhere is a real question and not this PR's; the PR now takes no
position on it.

The toggled usage block is back to carrying nothing. Its content is one
37-character line that never becomes a scroll container at any width a
reader will use, so the tabindex added for it was a permanent tab stop
on inert content rather than the rescue of an unreachable region.

The negative prompt assertion was a tripwire for one literal under a
message promising much more, so it now asserts the property the section
actually claims: the prompt panel contains no framework vocabulary at
all, which no rewording walks past. And the name rule is pinned across
all three pages that render code blocks, in its own file, since the rule
belongs to the site rather than to one page and covering one page is how
the other two drifted.
@vivek7405

Copy link
Copy Markdown
Collaborator Author

Follow-up: the coverage gap the sweep left, and the one claim this PR stopped short of

Two things from the last round worth writing down rather than leaving in the threads.

The accessibility rule now lives in website/test/ssr/pre-block-a11y.test.ts instead of inside one page's test. That is deliberate: the rule is a property of the site, not of /why-webjs, and covering one page is exactly how the other two drifted in the first place. It pins three things per page, that every named block carries a role ARIA permits, that no two names collide into an ambiguous landmark pair, and that an unnamed block takes neither a role nor a tab stop. Counterfactuals at b7928acf: re-duplicating the two card labels reds the uniqueness test, and dropping the role from the codeWindow helper reds the role test.

The other is a claim I deliberately did not settle here. The sentence "plain JavaScript with JSDoc in node_modules rather than a compiled bundle" is on this card, in the FAQ answer that feeds the FAQPage schema, twice on /why-webjs, and in root AGENTS.md. Its second half is in tension with packages/core/package.json, which maps the main entry to dist/webjs-core.js. I changed one instance mid-review and it made the page argue with itself, so it is reverted to its pre-PR wording. The question is real and worth answering across all five surfaces at once, with a decision about what the framework wants to claim, which is not something a copy PR should decide on its own.

@vivek7405 vivek7405 left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the fifth round and it found real things again, so I am stopping here rather than starting a sixth. The pattern is the problem, not any one finding: five rounds running where a correction introduced its own defect is signal to hand this back rather than keep going alone.

The finding that matters most is that one of last round's two reverts was not a revert. main carries an aria-label on that block and this branch deletes it, so the PR's net effect there is removing an accessible name that already existed, which is the opposite of what both the commit message and the PR body say. I verified that against main directly.

The rest cluster in the tests I added, and they share one shape: an assertion whose message promises a property while the check tests a proxy for it. A substring that matches inside other words, a set membership standing in for one specific element, a premise about scroll behaviour pinned as an invariant. That is the third round running where the test defect is subtler than the last, which is its own kind of signal.

Path-level: the PR establishes a site-wide markup convention and enforces it with a test, but website/AGENTS.md says nothing about it, so the only statement of the rule is the test's own header.

Leaving these open and the PR in draft.

Comment thread website/app/page.ts
<like-button count="3"></like-button>
</div>
<pre class="hidden group-has-[:checked]/stage:block flex-1 m-0 p-5 overflow-x-auto font-mono text-xs leading-[1.72] text-left" aria-label="like-button usage"><code>${highlight(USAGE_SAMPLE)}</code></pre>
<pre class="hidden group-has-[:checked]/stage:block flex-1 m-0 p-5 overflow-x-auto font-mono text-xs leading-[1.72] text-left"><code>${highlight(USAGE_SAMPLE)}</code></pre>

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not the revert I called it. main carries aria-label="like-button usage" on this block, so the PR's net effect here is deleting an accessible name the block already had, while the same PR resolves the identical construct on four other blocks by adding a role instead. Verified against main.


test('a code block with no name needs no role, so it adds no landmark', async () => {
// The home page's toggled usage block holds one short line that never becomes
// a scroll container at a real viewport width. It carries no name and no

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The premise is wrong at the width WCAG reflow names. The sample is about 266px of text and the content box at a 320px viewport is about 230px, so it does overflow below roughly 356px. The test then pins that wrong premise as an invariant, so the block cannot be given a name or a focus stop without editing this test.

// a permanent tab stop on content nothing can interact with.
const tags = preTags(await renderToString(Home()));
const unnamed = tags.filter((t) => !nameOf(t));
assert.ok(unnamed.length > 0, 'the home page still renders an unnamed code block');

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This documents one block and observes a set. It passes if the usage block gains a name while any other unnamed block exists, which is exactly when the property it describes stops being observed, and it reds with this message if that block is legitimately named.

// one historical phrasing, which any reworded regression would walk past.
const prompt = out.slice(out.indexOf('aria-label="A plain-language prompt'), out.indexOf('Where the conventions put it'));
assert.ok(prompt.includes('Let customers'), 'the slice really is the prompt panel');
for (const jargon of ['page.ts', '.server.ts', 'route', 'component', 'schema', 'action']) {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Raw substring, so it does not assert the property its message names. action matches inside transaction and interaction, route inside routine. Plain-language copy like record the transaction would red under a message saying the prompt mentions action, which is copy the section is supposed to encourage.

// The prose above the demo carries the argument, but the two windows are what
// SHOW it, and they were previously unasserted: the whole grid could be
// deleted with every test in this file still green. These pin both panels,
// their correspondence, and the accessible name each scrollable block needs.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale. The name assertions moved to pre-block-a11y.test.ts; the aria-label survives here only as a slice index and nothing in this test checks a name or a role.

for (const file of ['app/book/page.ts', 'app/staff/bookings/page.ts', 'modules/bookings/actions/create.server.ts', 'db/schema.server.ts']) {
assert.ok(out.includes(file), `the file panel answers the prompt with ${file}`);
}
assert.ok(out.includes('<span class="ml-2 font-mono font-medium text-xs leading-none text-fg-subtle">files</span>'), 'the file listing is labelled files, not terminal');

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hardcodes the full WINNAME class string, which the page documents as kept in lockstep with app/page.ts, so a design-system edit reds this test with a message about a word that did not change.

const title = 'Why WebJs - The Framework Your AI Agent Already Understands';
const description =
'WebJs is a full-stack JavaScript framework that serves the framework source and your app code exactly as written, so any AI model can read the whole stack from node_modules, reason about it, and debug it. No training data required, no single blessed model. Built on the web components, HTML, and JavaScript every model already knows.';
'WebJs is a full-stack JavaScript framework you describe in plain language, because the file conventions land an app in the right shape whatever model you use. Nothing is hidden behind a build step, so any AI model reads the framework source from node_modules, reasons about the whole stack, and debugs it. No training data required, no single blessed model, on the web components and HTML every model already knows.';

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Attaches the claim to the wrong noun. What you describe in plain language is the app, not the framework. This is the exact sentence the snippet-window test pins.

* rule is a property of the SITE, not of one page. The pages here were fixed
* together, and pinning them together is what stops the next one from drifting
* back: the sweep that fixed them originally covered one page, and the other
* two went unobserved until a review caught it.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Narrates this PR's review history in permanent source. The rule is worth stating; how it was arrived at is not.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Sell the plain-language prompt and cross-model quality on /why-webjs

1 participant