Skip to content

feat(design): Quackback AI from a website, and brands with more character - #880

Closed
mortondev wants to merge 23 commits into
feat/portal-design-v2-2-home-partsfrom
feat/portal-design-v2-3-ai-brand
Closed

mortondev wants to merge 23 commits into
feat/portal-design-v2-2-home-partsfrom
feat/portal-design-v2-3-ai-brand

Conversation

@mortondev

@mortondev mortondev commented Nov 1, 2026 •

Copy link
Copy Markdown
Member

Quackback AI can start a design from a website, and the design can carry a brand's character: its type, a second colour, bands, buttons and imagery.

In this PR

  • Quackback AI changes any part of the design, reads websites and screenshots, finds deep brand colours, and its first look is a whole portal
  • A website start brings its logo; monochrome and bold brands keep their character
  • Type with character, the logo a design chooses in the header, and thirteen more bundled typefaces
  • A second brand colour and bands that use it, band layouts, a header on the band, and Home's composer in the band
  • Buttons with their own shape, status markers in a brand's style, SVG wordmarks and an image in the band
  • Designs saved by a newer build still render

Replaces

#860, #861, #862, #863, #864, #865, #866, #867

Third of five stacked PRs.

…ds websites well

Quackback AI had a list of fields it could set: the brand, the header, the
band, the welcome message and the feed. It could not build Home, change the
menu or the words, or write own wording. It now has one tool: set any field
of the design document by its path, to any value it takes, each change
checked by the schema and the contrast solver as a person's would be. Plain
text stands for rich text and for a translated field's English, a font id
for a bundled font, and a Home block can be written as its type and width.
What it refuses comes back with why, in its reply.

Its output is untrusted, so it may not bring in a stored file or a link the
person did not give, or a Home block for a page that is off. It writes CSS
only where PORTAL_DESIGN_AI_CSS is on, which it is not by default, and only
CSS the check passes on a plan with custom CSS. Home is a section a person
can pick in the preview.

Reading a website for its brand now reads its CSS and manifest as well as
its head: brand colours weighed by where the site uses them, its page
background, dark or light, and its fonts. It says who it is, tries the
address with and without www, follows a meta refresh once, and reports a
site that answers with a bot check or a refusal as blocked, offering a
screenshot instead, rather than reading the block page as the site. On 80
real sites it read 72 (before: 31 of 48) and found a brand colour on 63
(before: 5).
Three things the first AI-only designs showed. A Home layout with one block
whose page is off was refused whole; the block is now left out and the
layout kept. Own wording written as plain text was refused when the
language had none yet. A dark colour was refused while the design had no
dark colours at all.
…s, and Home has no holes

What the first AI-only designs showed.

A screenshot start reached the model only as colours, and the brand colour
was the brightest one: a deep green or a navy never counted, so a forest-
green brand came back sage with its copper accent leading. A brand colour is
now judged by its saturation, fading only toward black, and the largest
group of such pixels leads the palette. The image itself, downscaled, goes
to the model too, so it can read the type and the shapes; a model that does
not take images still has the colours. The website reader uses the same
rule, so a deep brand colour on a site counts as well.

A Home block left its row short when the next block would not fit beside
it, or when a block beside it had its page off: a status block on a
workspace with no status page left a hole. The row's last block now grows
to fill it. Quackback AI is told to pair blocks of a like height, as a
short composer beside a long list leaves white space below it.
…fits a phone

A brand colour read from an image was the mean of its hue's pixels, so the
edges anti-aliasing blends into a dark page pulled an electric lime down to
olive. It is now the hue's most common shade, which a solid fill wins.

On a phone the centred header's menu, wider than the screen, was cut off at
its start where it could not be scrolled to, and the centred logo ran into
the buttons at the end. The menu now centres only while it fits, and the
logo leads on small screens.
A starting look set a preset, three colours, two fonts and a headline; Home,
the words and the wording waited for later requests. Each look is now a
complete design: mode and dark colours, fonts and heading weight, corners,
header, menu, band, cards, density and width, both headlines, the words,
the composer's prompt and button, and Home with its blocks for the pages the
workspace has. The prompt is a short design system: how a brand becomes
each choice. Each look goes through the same checks as any change from the
model. The directions move to their own module.
…d brands keep their character

Measured on twelve brands, the first look's weakest marks were for brand:
every company showed a letter for a logo, monochrome brands came back with
an accent picked from the edges of their CSS, and brands that fill their
pages with colour came back softened.

A website start now takes the site's logo as the workspace's when it has
none and the person may change the workspace's settings, and only a logo
the lookup is sure of. The website reader also notes the colour its buttons
are filled with, neutrals included, and whether it fills large areas with
its brand colour, and the design system keeps a monochrome brand
monochrome and gives a bold one its full-colour band. The example look
moves to a brand unlike any of those measured.
Measured with a smaller model, a quarter of starts came back with no looks:
the complete looks are a long answer, and one that did not fit the schema
ended the start. It is now asked for once more before the start fails.
…d button, and names a monochrome brand

From the judges' notes on round two. A white site read as dark when its CSS
painted the body black for dark mode, so dark-mode blocks and dark-theme
rules are no longer read. A brand whose buttons are a colour now leads with
that colour, over an accent used elsewhere. A brand whose buttons are black
is named monochrome in the brief, so its first look stays monochrome.
Every start that failed in the eval was cut off mid-answer at 4000 tokens:
three complete looks are long, and some models reason first.
Reading a site's CSS cannot see the page: generated class names hide its
buttons, and dark-mode rules can read as its look. A screenshot shows the
model the page as it is. With a website typed, adding a screenshot reads
both together, and the website field says so. A look's text that runs past
its field is trimmed at a word rather than refusing the start.
…decoded once

The portal's Home and Appearance both imported home.ts once Home filled
its rows, which split it into a chunk of its own and cost Appearance a
request. Row filling now lives in home-spans.ts, which only the portal
imports.

The website reader decoded a page's entities one after another, so an
escaped one, such as <, was decoded twice. It now decodes in one
pass.
Stored designs were parsed strictly, so after a rollback a design with a
field or option value only a newer build knows failed to parse: visitors
saw the plain portal and Appearance could not open. Stored reads now set
those aside and log them: an unknown key is dropped, an unknown option
value reads as the default, and a menu item of an unknown kind is left
out. Writes stay strict.
…sign chooses

A design can now set its headline scale (standard, large or display),
tight heading tracking, upper-case headlines and upper-case labels for
the menu and buttons, and set a phrase of a headline apart in the accent
colour or highlighted. The accent colour is moved until it reads on the
band. Absent choices read as today's type.

The header now follows the design's logo display: a wordmark file, with
its dark file in dark mode; the mark alone; or, new, the name set in the
heading font as a wordmark for brands without a logo file. A wordmark
with no file yet shows the mark and name. The admin preview follows the
draft's logo.
…placeholders

A design can set a second brand colour, in light and optionally dark
(dark keeps light's when it has none). Two new bands use it: support fills
with it, and blend runs the brand colour into it, in a line or as a soft
mesh beside the words with the page's own colour under them. A mesh gives
way to the plain page on phones, where the words cross it. Band text is
picked to read at 4.5:1 everywhere it sits, and a blend no text can read
across is refused like any unreadable pair. Highlighted phrases take the
second colour when there is one.

Composer and title placeholders now use the secondary text colour, which
the solver keeps at 4.5:1, rather than a faded copy of it.
… in the band

A design can now align the band's words left or centre (absent follows a
centred header, as before), shape the band as an inset panel within the
page or set its words on a panel of the second colour, and put the header
in the band's colour. On the band the header's button keeps the brand
colour where it reads there, else takes the band's text colour.

Home's composer can move into the band under the headline, with a filled
button that takes its own line on phones; the grid keeps every other
block's place. A full-width contact block lays out as one row, so it no
longer leaves a gap beside the composer.
…rand's style

A design can give buttons, and the composer that carries one, their own
shape: square, rounded or pill, whatever the corners; absent follows the
corners as today. The shape stays inside the portal.

Status markers can be drawn in the ink colour, as tags tinted with each
status's colour, or in the logo's own colours, where each status takes
the logo colour nearest its hue and greys stay grey. Status dots now
paint from a variable, so a design's style overrides them without
!important.
…tton shape

Buttons kept a radius of their own inside the portal, which dropped the
rounded-item class every other control shares. The portal's item radius
now follows the button shape, which follows the corners until a design
sets one, so buttons, badges and tabs keep their one class.
A design's wordmark can now be uploaded as an SVG, the format most logos
come in. Storage serves SVG as a sandboxed download, which an image tag
still shows, so it never runs as a page; the workspace logo, which emails
carry, keeps to raster images.

A design can set an image in its band: beside the words (under them on
phones), along the band's bottom on wider screens, behind the words under
a veil of the band's colour, or tiled as a pattern under a lighter veil.
Only images from the workspace's own storage are shown, and the admin
preview follows the draft's image.
The band now reads the draft's band image, so the feedback page test's
mock of the preview context provides it.
…faces among them

Inter Tight, Sora, Outfit, Figtree, Work Sans, Instrument Sans, Noto Sans,
Rubik, Karla, Libre Franklin, Barlow Condensed, Oswald and Source Serif 4,
all open licence. Each loads only when a design uses it, at the weights a
heading can take, and the build's font manifest picks them up from the
loader as before.
The design's new bundled faces add their catalogue to the entry chunk.
Five admin settings pages sat at their script ceiling and go over it by
about 8 KB; their ceilings move to CI's readings. No request or render
counts change.
@mortondev

Copy link
Copy Markdown
Member Author

Superseded by the re-cut builder stack on current main: #917 (engine) → #918 (server) → #919 (portal) → #920 (builder) → #921 (AI) → #922 (tooling), with the critique's wave 1 in #924. The new stack merges main once, renumbers the migrations to 0310+, and has CI green with all review threads addressed.

@mortondev mortondev closed this Nov 7, 2026
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.

1 participant