Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@
components.** It server-renders every page and component to real HTML,
needs no build step or bundler, and runs on Node 24+ or Bun.

Nothing is hidden from your agent. The framework ships in `node_modules` as
plain JavaScript, so an agent opens the router or the renderer it is calling
You get production-ready architecture from your very first prompt, and none of
it is hidden from your agent. The framework ships in `node_modules` as plain
JavaScript, so an agent opens the router or the renderer it is calling
instead of recalling an API from training data, and your app code is served to
the browser exactly as written. Any model debugs the running app against the
real source, with no single blessed model, on the web components and standard
Expand Down
2 changes: 1 addition & 1 deletion website/app/layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const TITLE = 'WebJs: Full-stack web components framework, no build step';
// The true version of that idea is about LOCATION, not volume: the source sits
// in the app's own node_modules at the installed version, so an agent opens
// the file it needs instead of recalling an API from training data.
const DESCRIPTION = 'A full-stack web components framework with no build step, built for AI agents. Server-rendered pages, server actions, and file-based routing. Node 24+ or Bun.';
const DESCRIPTION = 'An AI-first full-stack JavaScript web components framework with no build step. Production-ready architecture from your very first prompt. Node 24+ or Bun.';

const NAV = [
{ label: 'Docs', href: DOCS_START_PATH, ext: false },
Expand Down
6 changes: 3 additions & 3 deletions website/app/page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ export default function LandingPage() {
title's longest line from 768 up. Re-measure all of them before
touching any of the three numbers; they are chosen against the wrap
points, not picked. -->
<h1 class="font-display font-extrabold text-[clamp(1.5rem,0.53rem+5.5vw,4.125rem)] leading-[0.98] tracking-[-0.038em] mx-auto mt-2 mb-6 max-w-[64rem] text-balance">
<h1 class="font-display font-extrabold text-hero-h1 leading-[0.98] tracking-[-0.038em] mx-auto mt-2 mb-6 max-w-[64rem] text-balance">
Conventions your agent follows.<br>Architecture you still own.
</h1>
<!-- Two sentences. The second one is the PAGE'S THESIS, and it took a
Expand Down Expand Up @@ -535,8 +535,8 @@ export default function LandingPage() {
bought that back).
-->
<p class="text-hero-lede leading-[1.3] text-fg-muted max-w-[64rem] mx-auto mb-9 text-balance">
<span class="text-fg font-medium">WebJs is a full-stack web components framework with no build step.</span>
Nothing is hidden from your agent, or from you.
<span class="text-fg font-medium">WebJs is an AI-first full-stack JavaScript web components framework with no build step.</span>
You get production-ready architecture from your very first prompt.
</p>
<div class="flex gap-3 justify-center flex-wrap items-center">
<a class=${BTN_PRIMARY} href=${DOCS_START_PATH}>
Expand Down
17 changes: 9 additions & 8 deletions website/app/what-is-webjs/page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,17 +117,17 @@ export function generateMetadata() {
title: TITLE,
description: DESCRIPTION,
url: CANONICAL,
image: `${SITE_URL}/public/og.png`,
image: `${SITE_URL}/public/og-what.png`,
'image:width': '1200',
'image:height': '630',
'image:alt': 'WebJs, a full-stack JavaScript framework built on web components',
'image:alt': 'What is WebJs? An AI-first full-stack JavaScript web framework built on web components',
'site_name': 'WebJs',
},
twitter: {
card: 'summary_large_image',
title: TITLE,
description: DESCRIPTION,
image: `${SITE_URL}/public/og.png`,
image: `${SITE_URL}/public/og-what.png`,
},
jsonLd: [
{
Expand Down Expand Up @@ -234,18 +234,19 @@ export default function WhatIsWebJs() {
return html`
<main id="main" tabindex="-1" class="focus:outline-none">

<section class="text-center px-6 pt-12 md:pt-16 lg:pt-24 pb-10 md:pb-16">
<h1 class="font-display font-extrabold text-display leading-[1.04] tracking-[-0.035em] mx-auto mt-4 mb-6 max-w-[14ch] text-balance">
<section class="text-center px-6 pt-12 md:pt-20 lg:pt-28 pb-10 md:pb-16">
<h1 class="font-display font-extrabold text-hero-h1 leading-[1.04] tracking-[-0.035em] mx-auto mt-2 mb-6 max-w-[14ch] text-balance">
What is WebJs?
</h1>
<p class="text-lede leading-[1.6] text-fg-muted max-w-[56ch] mx-auto mb-6 text-pretty">
<p class="text-hero-lede leading-[1.3] text-fg-muted max-w-[64rem] mx-auto mb-6 text-balance">
<strong class="text-fg font-semibold">WebJs is an AI-first full-stack JavaScript web
framework built on web components.</strong> It server-renders every page and component to
real HTML, needs no build step or bundler, and runs on Node 24+ or Bun.
</p>
<p class="text-base leading-[1.7] text-fg-muted max-w-[56ch] mx-auto mb-8 text-pretty">
Nothing is hidden from your agent. The framework ships in node_modules as plain
JavaScript, so an agent opens the router or the renderer it is calling instead of
You get production-ready architecture from your very first prompt, and none of it is
hidden from your agent. The framework ships in node_modules as plain JavaScript, so
an agent opens the router or the renderer it is calling instead of
recalling an API from training data, and your app code is served to the browser
exactly as written. Any model debugs the running app against the real source, with
no single blessed model, on the web components and standard HTML every model
Expand Down
33 changes: 20 additions & 13 deletions website/app/why-webjs/page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@ import { DOCS_START_PATH, GH_URL, NEW_TAB } from '#lib/links.ts';
*
* The developer pitch page. Where the home page shows the framework's shape
* (progressive enhancement, the three-file stack, the weight stats), this page
* makes the single argument that matters most for the AI era: an agent can read
* the whole framework, so it needs no training data and no single blessed model
* to build a WebJs app well.
* makes the single argument that matters most for the AI era: an agent reads the
* framework source it is calling, from the app's own node_modules at the version
* installed there, so it needs no training data and no single blessed model to
* build a WebJs app well. The claim is about LOCATION, not volume. Nothing reads
* the whole framework (see the comment above DESCRIPTION in app/layout.ts for
* the line counts), and the located version is the stronger claim anyway,
* because it holds however large the framework grows.
*
* It deliberately reuses the home page's design language (the KICKER label, the
* section rhythm, the terminal "windows", the bento grid, and the closing CTA
Expand Down Expand Up @@ -109,17 +113,20 @@ export default function Why() {
return html`
<main id="main" tabindex="-1" class="focus:outline-none">

<section class="text-center px-6 pt-12 md:pt-16 lg:pt-24 pb-10 md:pb-16">
<h1 class="font-display font-extrabold text-display leading-[1.04] tracking-[-0.035em] mx-auto mt-4 mb-4 max-w-[16ch] text-balance">
<section class="text-center px-6 pt-12 md:pt-20 lg:pt-28 pb-10 md:pb-16">
<h1 class="font-display font-extrabold text-hero-h1 leading-[1.04] tracking-[-0.035em] mx-auto mt-2 mb-6 max-w-[64rem] text-balance">
The framework your AI agent already understands
</h1>
<p class="text-lede leading-[1.6] text-fg-muted max-w-[56ch] mx-auto mb-8 text-pretty">
WebJs is a full-stack JavaScript framework with no build step, so
nothing is hidden from your agent. The framework ships in node_modules as
plain JavaScript, so an agent opens the file it is calling instead of
recalling an API from training data, and your app code is served to the
browser exactly as written. Any model debugs the running app against the
real source, on the web components and standard HTML it already knows.
<p class="text-hero-lede leading-[1.3] text-fg-muted max-w-[64rem] mx-auto mb-6 text-balance">
<strong class="text-fg font-semibold">WebJs is a full-stack JavaScript framework with no
build step, so nothing is hidden from your agent.</strong> The framework ships in
node_modules as plain JavaScript, at the version your app installed.
</p>
<p class="text-base leading-[1.7] text-fg-muted max-w-[56ch] mx-auto mb-8 text-pretty">
An agent opens the file it is calling instead of recalling an API from training data,
and your app code is served to the browser exactly as written. Any model debugs the
running app against the real source, on the web components and standard HTML it
already knows.
</p>
<div class="flex gap-3 justify-center flex-wrap mb-8">
<a class=${BTN_PRIMARY} href=${DOCS_START_PATH}>
Expand Down Expand Up @@ -165,7 +172,7 @@ server/src/ssr.js: const html = await renderToString(tree)
</figure>
</div>
<div class="flex flex-col min-w-0">
<p class="font-mono font-semibold text-xs leading-[1.4] tracking-widest uppercase text-fg-subtle mb-2.5 ml-1">Your app code, served to the browser as written</p>
<p class="font-mono font-semibold text-xs leading-[1.4] tracking-widest uppercase text-fg-subtle mb-2.5 ml-1">Your app code, served as written</p>
<figure class=${WIN}>
<figcaption class=${WINBAR}>${DOTS}<span class=${WINNAME}>terminal</span></figcaption>
<pre class="scroll-thin m-0 p-4 overflow-x-auto font-mono text-sm leading-[1.7] [tab-size:2] flex-1" role="region" tabindex="0" aria-label="Fetching an app module served unbundled"><code><span class="text-accent">$</span> curl localhost:5001/components/counter.ts
Expand Down
13 changes: 13 additions & 0 deletions website/public/input.css
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,19 @@
--text-section: clamp(20px, 3vw, 26px);

--text-display: clamp(2.9rem, 1.55rem + 4.5vw, 5.5rem);
/* The marketing HERO h1, shared by /, /what-is-webjs and /why-webjs so the
three read as one page family. It is a SEPARATE step from --text-display
(which the /brand h1 still uses) because a hero headline and a page title
want different floors: this curve's floor is 1.5rem where --text-display's
is 2.9rem, and 2.9rem is reached by the preferred term at a 476px viewport,
so every phone rendered one identical size and the type stopped responding
exactly where it mattered.

It lived inline on the home page until the three heroes were aligned. The
numbers are solved against WRAP POINTS rather than picked, so the long
comment above the home h1 in app/page.ts is the reference for what each one
costs. Re-measure ALL THREE heroes before touching any of them now. */
--text-hero-h1: clamp(1.5rem, 0.53rem + 5.5vw, 4.125rem);
--text-h2: clamp(1.7rem, 1.2rem + 1.4vw, 2.5rem);
/* Two lede steps, because the landing hero and a page lede are not the same
role and were fighting over one number. The hero wants to be half the
Expand Down
Binary file added website/public/og-what.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified website/public/og-why.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified website/public/og.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
67 changes: 67 additions & 0 deletions website/scripts/generate-og-what.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* Regenerate public/og-what.png, the 1200x630 social card for /what-is-webjs.
*
* node scripts/generate-og-what.mjs
*
* That page had no card of its own and unfurled with the site-wide og.png,
* whose headline is the product tagline. It is the page built to answer one
* question (exact-match title, h1 and slug, a definition in the first 160
* characters, a visible FAQ backing FAQPage JSON-LD), so it is the likeliest of
* the three to be shared AS an answer, and it was the one arriving under a card
* that answered something else.
*
* The shell (palette, faces, lockup, top row, panels, footer, render) lives in
* scripts/lib/og-card.mjs and is shared with the other two cards. Only this
* card's own content is here.
*/
import { resolve } from 'node:path';
import { T, PANEL_CSS, panels, renderCard } from './lib/og-card.mjs';

// The page's own h1, which is also its <title> and its URL slug. The whole
// point of the page is that those three match the question, so the card has no
// business paraphrasing it.
const TITLE = 'What is <span class="accent">WebJs</span>?';

// The definition sentence from the page, which is also the first 160 characters
// of its meta description, so the card and the search result agree.
const SUB = '<b>An AI-first full-stack JavaScript web framework built on web components,</b> server-rendered with no build step.';

// The definition above says what it IS. These say what that MEANS, and they
// carry the feature terms the site-wide meta description gave up to fit.
const FACTS = [
{
label: 'Every page',
text: 'Server-rendered to real HTML. <span class="q">It reads, navigates and submits before a single script runs.</span>',
},
{
label: 'Every component',
text: 'A native custom element. <span class="q">Server actions and file-based routing, on Node 24+ or Bun.</span>',
},
];

const TAGS = 'WEB COMPONENTS &nbsp;&middot;&nbsp; SSR &nbsp;&middot;&nbsp; NO BUILD STEP';

await renderCard({
out: resolve(process.argv[2] || 'public/og-what.png'),
fit: { from: 62, to: 30 },
css: `${PANEL_CSS}
.frame{ padding:56px 76px; }
h1{
font-family:'Inter Tight',sans-serif; font-weight:800;
/* Starts larger than the other two because this headline is four words and
has room the others do not. The fit pass still owns the final value. */
font-size:62px; line-height:1.05; letter-spacing:-0.035em;
max-width:20ch;
}
.sub{
font-size:22px; line-height:1.4; color:${T.fgMuted}; font-weight:400;
max-width:40ch; margin-top:-12px;
}
.sub b{ color:${T.fg}; font-weight:600; }`,
body: `<div class="mid">
<h1>${TITLE}</h1>
<div class="sub">${SUB}</div>
${panels(FACTS)}
</div>`,
tags: TAGS,
});
Loading
Loading