Skip to content

feat(ui): adopt the shared public-site layout from mctl-design 0.15.0 - #860

Merged
mashkovd merged 5 commits into
mainfrom
feat/site-shell-0.15
Oct 23, 2026
Merged

mashkovd merged 5 commits into
mainfrom
feat/site-shell-0.15

Conversation

@mashkovd

Copy link
Copy Markdown
Member

Why

mctl.ai, docs.mctl.ai and tg.mctl.ai drifted apart in layout. The owner settled on one shell, which mctl-design 0.15.0 ships as site.css (mctlhq/mctl-design#171):

  1. Hero: text on the left, optional visual on the right, one column below 960px.
  2. Hero, sections and card grids use the full container (1400px content + 20px gutters). Long-form pages keep the 720px reading width.
  3. Section titles left-aligned, footer full-bleed and centred.

On tg.mctl.ai the 920px .wrap produced an 872px content column (284-1156 at a 1440 viewport), so nothing lined up with the header brand or the sibling sites.

What changes

  • Chrome (internal/ui/chrome.go): the header is .mctl-site-header > .mctl-site-container.mctl-site-header__inner > .mctl-site-brand. The footer is .mctl-site-footer > .mctl-site-container.mctl-site-footer__inner with __links / __copy. Both now sit directly in <body>, so the 100vw / calc(50% - 50vw) breakout is gone. Nav, the CSS-only mobile menu checkbox and the theme toggle are unchanged.
  • Pages: the <div class="wrap"> wrapper is replaced by <main class="mctl-site-container page">.
    • Landing: the hero is .mctl-site-hero with __text, __title, __lede and __actions. Every section is .mctl-site-section with an h2.mctl-site-section__title. The use-case cards are .mctl-site-grid. The local hero, section-title, .cta-row and .card-grid rules are deleted.
    • Docs, platforms, demo, security, privacy, terms, product updates and the Local Bridge guide are long-form, so they wrap their content in .mctl-site-prose (720px).
    • Strict-CSP pages (connect, manage, enable-access, demo login): header and footer move out of the wrapper and the card sits in .mctl-site-container.
  • CSS: the pin moves from 0.14.0 to 0.15.0, and site.css joins mctl.css and global.css in the set. The strict-CSP pages cannot load external CSS, so assets/site.css is a verbatim mirror of the published file. It is inlined there through ui.ComponentsCSS, and inlined on content pages as the CDN fallback before the CDN link, the same arrangement tokens.css already uses. tokens.css gains the layout and subtitle roles that site.css reads (prose width, section padding, stack, subtitle and body roles). Their values are unchanged from 0.14.0.
  • CSP: no change needed. Content pages already allow https://ui.mctl.ai on style-src. Strict-CSP pages inline everything. There is no SRI.
  • Tests: updated deliberately for the new class names and pin. New TestSiteCSSMirrorMatchesPinnedVersion fails if the mirror's version header drifts from DesignVersion. New TestNoLocalPageWrap fails if .wrap, 100vw, .topbar { or max-width: 920px come back.

This ships together with #845 (merged, unreleased), whose .topbar-inner fix this PR replaces with .mctl-site-container.

Measured locally (1440 and 375 viewports)

1440 375
Header brand left 20 20
Theme toggle / burger right edge 1420 355
Landing hero title left 20 (text column 20-809) 20-355
Landing section titles (all 9) 20-1420 20-355
Card grid / first card / third card 20-1420 / 20-471 / 969-1420 1 column, 20-355
Long-form pages: h1 and prose 20-740 (720px) 20-355
Strict-CSP card (.auth-main) 460-980, brand 20 20-355
Footer 0-1440, items centred 0-375
documentElement.scrollWidth 1440 375, nothing past the right edge

The mobile menu was checked too: the checkbox opens a full-width panel under the 64px header. Light and dark themes both checked.

The 0.15.0 CDN directory returned 404 at test time because #171 was not merged yet. So the local check ran on the inlined mirror, which is the fallback path. With 0.15.0 published, the CDN copy loads with identical content.

Merge order

Merge only after mctlhq/mctl-design#171 is merged and https://ui.mctl.ai/0.15.0/{mctl,global,site}.css serves 200. Until then the pages render from the inline fallback.

🤖 Generated with Claude Code

mctl.ai, docs.mctl.ai and tg.mctl.ai had drifted apart in layout. The
owner settled on one shell, which mctl-design 0.15.0 ships as site.css.
tg.mctl.ai's 920px .wrap gave an 872px content column (284-1156 at a 1440
viewport), so nothing lined up with the header brand or the sibling sites.

The header and footer now sit full-bleed in <body> and use the
.mctl-site-* classes, so the 100vw breakout hacks are gone. The landing
hero, section titles and card grid use the full container (1400px plus
20px gutters, i.e. 20-1420 at 1440). Every other page is long-form and
keeps the 720px reading width via .mctl-site-prose.

The design CSS pin moves to 0.15.0 and adds site.css to the set. The
strict-CSP pages cannot load external CSS, so site.css is mirrored
verbatim and inlined there, and the content pages inline it as the CDN
fallback, as tokens.css already does. A test keeps the mirror's version
in step with the pin.
@claude

claude Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Claude finished @mashkovd's task in 1m 31s —— View job


Review of PR #860 (commit 60a4cf3)

  • Gather context
  • Review the diff
  • Post findings and verdict

Verdict: approved. No P1 or P2 findings, 2 P3 nits.

What I checked

  • Markup balance: connect, manage and enable-access now open <div class="mctl-site-container"> after TopbarLite, and their foot strings close it before FooterLite. Demo login and Local Bridge activate reuse enableFoot, so they balance too. All eight content pages open main.page and .mctl-site-prose, and close both before the footer.
  • No leftover references: nothing in internal/ still uses .wrap, .topbar, .topbar-inner, .card-grid, .cta-row or the old footer .links / .copy rules. .section-head is still in use on docs and product updates, and its rules are kept.
  • Cascade: components.css comes after site.css, and where an element rule like h1, h2 or p overlaps a .mctl-site-* rule, the class selector wins. The mobile .site-nav a border still overrides .mctl-site-header a, as .topbar a did before. Footer link colour and hover now come from .mctl-site-footer__links a.
  • Tokens: every custom property site.css reads is in the inlined tokens.css (wordmark, header, content width, gutter, surface, accent, and the new layout and subtitle roles). So the strict-CSP pages and the CDN-fallback path render without the CDN.
  • CSP and security: no new external origins. Strict-CSP pages only get more inline CSS, which style-src 'unsafe-inline' already allows. No new template inputs.
  • Scoping: section { margin-top: 64px } is now .page section. I checked whether any other page relied on the global rule: the Go-built pages use no <section> elements, and the landing spaces its sections with .mctl-site-section padding.

P3 (inline)

  1. internal/ui/chrome_test.go:230: siteCSS[:strings.Index(siteCSS, "\n")] panics if the mirror has no newline, instead of failing with the message. strings.Cut avoids that.
  2. internal/ui/chrome.go:29: one package-doc line is now about 120 columns. Re-wrap it to match the rest.

Not blocking, for context: the merge-order note in the description still applies. Until ui.mctl.ai/0.15.0/{mctl,global,site}.css serves 200, content pages render from the inline fallback. They render correctly that way, but the CDN mctl.css and global.css would be missing.

Comment thread internal/ui/chrome_test.go Resolved
Comment thread internal/ui/chrome.go Resolved

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No P1/P2 findings (2 P3). Good to merge.

@github-actions

Copy link
Copy Markdown
Contributor

Antigravity (agy) review — informational

Model: gemini-3.8-flash-high · commit 60a4cf3.

No significant issues found.

@github-actions

Copy link
Copy Markdown
Contributor

Skipping — this commit (3432cf4) already has a Claude review verdict. Push a new commit, or check the run log if you expected a fresh pass and the previous run actually failed.

@github-actions

Copy link
Copy Markdown
Contributor

Antigravity (agy) review — informational

Model: gemini-3.8-flash-high · commit 3432cf4.

No significant issues found.

@github-actions

Copy link
Copy Markdown
Contributor

Skipping — this commit (011ed0f) already has a Claude review verdict. Push a new commit, or check the run log if you expected a fresh pass and the previous run actually failed.

@github-actions

Copy link
Copy Markdown
Contributor

Antigravity (agy) review — informational

Model: gemini-3.8-flash-high · commit 011ed0f.

No significant issues found.

@github-actions

Copy link
Copy Markdown
Contributor

Skipping — this commit (9e02160) already has a Claude review verdict. Push a new commit, or check the run log if you expected a fresh pass and the previous run actually failed.

@github-actions

Copy link
Copy Markdown
Contributor

Antigravity (agy) review — informational

Model: gemini-3.8-flash-high · commit 9e02160.

No significant issues found.

@github-actions

Copy link
Copy Markdown
Contributor

Skipping — this commit (bf49b0c) already has a Claude review verdict. Push a new commit, or check the run log if you expected a fresh pass and the previous run actually failed.

@github-actions

Copy link
Copy Markdown
Contributor

Antigravity (agy) review — informational

Model: gemini-3.8-flash-high · commit bf49b0c.

No significant issues found.

@mashkovd
mashkovd merged commit e6b401e into main Oct 23, 2026
14 checks passed
@mashkovd
mashkovd deleted the feat/site-shell-0.15 branch October 23, 2026 18:57
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