FlowForge Bug Report

Date: April 14, 2026

Model: Claude Sonnet 4.6

Application: RuledWeb — FlowForge JSON website builder on Cloudflare Workers

Overview

Goal

Fix layout, mobile responsiveness, scrolling, and navigation issues on the RuledWeb homepage generated by FlowForge AI.

Expected Output

A fully responsive scrollable mobile-first homepage with correct HTML structure, working nav with hamburger menu on mobile, and proper Tailwind breakpoints firing at correct viewport widths.

Bugs Found

Bug 1 — Broken HTML Structure (Emmet Sibling Trap)

Where: base layout COMPONENTS Emmet string

Error: body was a sibling of script.tw inside head, not a child of html. main.content-slot and footer were nested inside div.nav-links (hidden md:flex), causing content to be invisible on mobile and shifted right on desktop.

Did not work: Rewriting with () grouping — html>(head>script)+(body>nav+main+footer). Uncertain if FlowForge Emmet parser supports (). Changes appeared to partially apply but core issues persisted.

Worked: Splitting COMPONENTS into an array of independent entries — one per section (script, nav, main, footer). Each renders as a top-level block. Browser assembles correct DOM from concatenated output.

Bug 2 — Missing Viewport Meta Tag

Where: base layout head — no meta viewport present

Error: Without viewport meta, mobile browsers rendered at ~980px desktop width then scaled down. All md: breakpoints fired on mobile. Cards appeared in 2 columns, text oversized, content not truly responsive.

Did not work: meta.viewport element in Emmet — custom attributes (name, content) not supported by FlowForge attr system. Inline script via text: on script element — renderer does not execute text: as JavaScript. () grouping to structure html>head>meta+script.

Worked: Switching base layout to full_page: false. The renderer wrapPage() default shell includes proper HTML document with meta viewport. This immediately fixed all breakpoint misfiring.

Bug 3 — No Page Scrolling

Where: base layout body and main element classes

Error: body had min-h-screen flex flex-col and main had flex-1. In a flex column, flex-1 constrains main to remaining viewport height. Content overflows inside main rather than pushing body, so the window never scrolls.

Did not work: Various combinations of overflow-y-auto and height adjustments on body and html.

Worked: Removing flex flex-col from body and flex-1 from main. Natural document flow lets sections push body height and window scrolls normally. pt-16 on main handles the fixed nav offset.

Bug 4 — why-comparison: Wins Column Nested Inside Pain Loop

Where: why-comparison component Emmet string

Error: div.us-col was inside the *pains loop. The entire wins column rendered once per pain item (6 times total). The two-column comparison became a vertically repeated mess.

Did not work: Any Emmet structure attempting two sibling loops at the same depth. The + operator after a loop keeps siblings inside the loop parent. Without ^ or () there is no escape.

Worked: Merged pains and wins into a single comparisons array with {pain, win} per item. New Emmet: div.why-row*comparisons>p.pain-text+p.win-text. Both columns are siblings inside each row.

Bug 5 — script text: Attr Does Not Execute JavaScript

Where: nav-toggle script component in base layout

Error: text: on a script element does not produce executable JS. First attempt had SVG strings — angle brackets caused SVG to render as visible text in the nav. Second attempt removed SVG but button still never appeared. The script simply does not execute.

Did not work: script.nav-toggle component with DOMContentLoaded JS. Both with SVG innerHTML strings and without.

Worked: Inline onclick attribute on the button element directly. Separate div.mob-nav component using *navlinks loop for the mobile dropdown. No external script needed.

Improvements Needed

1. Emmet ^ climb-up operator not supported. Without it, sibling elements at a parent level after nesting are impossible in one string. This forces split COMPONENTS workarounds for basic layouts.

2. Emmet () grouping undocumented. Skill docs only list >, +, .class, *N, *arrayName. Whether () works is unknown — it appeared to partially apply in some cases and silently fail in others.

3. Custom HTML attributes not documented. Only class, text, href, src, attr-ref are listed. In practice id, style, onclick appear to pass through — but this is undocumented and untested across all attributes.

4. text: on script elements silently fails. No error is thrown. When JS contains angle brackets, visible DOM corruption occurs. Needs either proper script execution support or explicit documentation that text: does not work on script elements.

5. full_page: true has no clean path to inject head content. Meta tags and scripts cannot be reliably placed in head without fighting Emmet structural limits. A head config object in layout schema would solve this.

6. AI-generated components default to desktop-first grids. Every component needed manual correction to add grid-cols-1 before md: overrides. AI generation should enforce mobile-first as a rule.

Suggestions

1. Default full_page: false for all layouts. Let wrapPage() always handle html/head/body shell. Layout authors should focus on nav, slot, footer — not HTML document structure.

2. Add head: field to layout schema. An array of {tag, attrs} objects for injecting meta, link, and script elements into head without Emmet.

3. Add scripts: field to layout schema. Layouts should be able to declare script dependencies the same way components do via script_deps.

4. Provide a system nav-mobile component. Nav with responsive hamburger and mobile dropdown built in. Accept navlinks array and brand name. Eliminates inline onclick hacks entirely.

5. wrapPage() shell should optionally inject Tailwind CDN. When full_page: false, authors lose control of head scripts. A cdnScripts: [] field in layout or project config would allow this cleanly.

Feature Requests

FR-1: Emmet ^ climb-up operator. Essential for multi-level layouts without split COMPONENTS workarounds.

FR-2: Emmet () grouping operator. Enables html>(head>...)+(body>...) and two-column layouts with separate loops.

FR-3: Layout head: config field. Direct injection of meta, link, script into HTML head without Emmet.

FR-4: Layout-level scripts: field. Allow layouts to inject scripts into the page head pipeline, not just components.

FR-5: System nav-mobile component. Pre-built responsive nav with hamburger toggle, mobile dropdown, navlinks data binding.

FR-6: Attribute passthrough documentation. Explicit list of all supported HTML attributes beyond the current five.

FR-7: Mobile-first AI generation guard. Enforce grid-cols-1 as base before any md: or lg: grid overrides in all AI-generated component classes.