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
6 changes: 4 additions & 2 deletions docs/app/(home)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@ export default function HomePage() {
align="left"
subtitle="Open Standard for Generative UI"
showPlaygroundButton={false}
githubRepoUrl="https://github.com/thesysdev/openui"
githubButtonLabel="Star us on GitHub"
showTagline={false}
/* No GitHub CTA in the hero on either breakpoint: the header carries
the star count already. Dropping githubRepoUrl takes the desktop
button; the mobile banner defaults on, so it is turned off here. */
showGitHubBanner={false}
/>
<LogoStrip />
<Tagline />
Expand Down
83 changes: 58 additions & 25 deletions docs/app/(home)/sections/CloudSection/CloudSection.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,13 @@

/* Each feature is a card with a subtle fill (no stroke). */
.feature {
/* Named so the artwork panel can cancel it and run to the card's edges. */
--card-pad: clamp(1.5rem, 2vw, 2rem);

display: flex;
flex-direction: column;
gap: 0.875rem;
padding: clamp(1.5rem, 2vw, 2rem);
padding: var(--card-pad);
border-radius: 16px;
background: rgba(255, 255, 255, 0.05);
}
Expand All @@ -68,6 +71,56 @@
color: rgba(255, 255, 255, 0.24);
}

/* Artwork panel for the cards that lead the grid. Off by default: on a phone
the card collapses to an icon + title row, which a panel would break. It is
switched on with the wide layout below. */
.shot {
display: none;
}

.shotImage {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
}

/* This band runs opposite to the page: black by default, light in dark mode. So
the tile is chosen to suit the CARD, which means the pairing reads inverted
against every other light/dark swap on the site. */
.shotOnLight {
display: none;
}

[data-theme="dark"] .shotOnDark {
display: none;
}

[data-theme="dark"] .shotOnLight {
display: block;
}

@media (min-width: 768px) {
.shot {
position: relative;
display: block;
/* Pulled most of the way out to the card's edges, keeping a slim inset. The
full card padding read as a second frame around a tile that already has
its own margins; none at all read as a banner. */
margin: calc(0.75rem - var(--card-pad)) calc(0.75rem - var(--card-pad)) 1.25rem;
/* The tiles' own ratio, so nothing is cropped. */
aspect-ratio: 720 / 400;
overflow: hidden;
border-radius: 12px;
}

/* The panel already says what the glyph would, so the glyph steps aside. */
.feature:has(.shot) .icon {
display: none;
}
}

.featureTitle {
margin: 0;
font-family: var(--home-font-display);
Expand Down Expand Up @@ -97,20 +150,8 @@
margin-top: clamp(1.25rem, 3vw, 2rem);
padding-bottom: clamp(1.5rem, 3vw, 2.5rem);
overflow: hidden;
-webkit-mask-image: linear-gradient(
to right,
transparent,
#000 3%,
#000 97%,
transparent
);
mask-image: linear-gradient(
to right,
transparent,
#000 3%,
#000 97%,
transparent
);
-webkit-mask-image: linear-gradient(to right, transparent, #000 3%, #000 97%, transparent);
mask-image: linear-gradient(to right, transparent, #000 3%, #000 97%, transparent);
}

.tickerTrack {
Expand Down Expand Up @@ -231,11 +272,7 @@
top: 0;
inset-inline: -1.25rem;
height: 1px;
background: linear-gradient(
to right,
transparent,
var(--home-border-on-dark) 15%
);
background: linear-gradient(to right, transparent, var(--home-border-on-dark) 15%);
}

/* Icon tile (dark variant of the stepper tile). */
Expand Down Expand Up @@ -397,11 +434,7 @@
}

[data-theme="dark"] .feature:not(:first-child)::before {
background: linear-gradient(
to right,
transparent,
rgba(0, 0, 0, 0.09) 15%
);
background: linear-gradient(to right, transparent, rgba(0, 0, 0, 0.09) 15%);
}

[data-theme="dark"] .icon {
Expand Down
117 changes: 74 additions & 43 deletions docs/app/(home)/sections/CloudSection/CloudSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
ShieldCheck,
type Icon,
} from "@phosphor-icons/react";
import Image from "next/image";
import { BevelButton } from "../../components/Button/BevelButton";
import { ExpandChevron } from "../../components/ExpandChevron";
import { useSingleOpenAccordion } from "../../components/MobileAccordion/useSingleOpenAccordion";
Expand All @@ -20,27 +21,32 @@ import styles from "./CloudSection.module.css";

export const CLOUD_SECTION_ID = "openui-cloud";

/* The first three lead the grid, so they carry artwork instead of a glyph: they
are the capabilities worth showing rather than naming. The tiles are the same
ones the Cloud page's FeaturesSection uses, so the two pages tell the story
with one set of illustrations. The icon stays on every row for phones, where
the card collapses to an icon + title line.

`light` and `dark` name the page theme each tile was drawn for, matching how
FeaturesSection labels them. Which one this band shows is inverted; see the
stylesheet. */
const FEATURES: {
Icon: Icon;
image?: { light: string; dark: string };
title: string;
description: string;
tags: string[];
}[] = [
{
Icon: Browsers,
title: "Production-grade rendering",
description:
"Tested, responsive components that render consistently across browsers, devices, and screen sizes.",
tags: [
"Pre-tested components",
"Cross-browser compatibility",
"Built-in accessibility",
"Responsive by default",
],
},
{
Icon: Presentation,
title: "Editable artifact generation",
image: {
/* No ?v= cache-buster like FeaturesSection carries: next/image rejects a
query string unless images.localPatterns allows one, and this is a new
reference with no cached copy to bust. */
light: "/openui-cloud/reports&presentation.png",
dark: "/openui-cloud/reports&presentations-dark.png",
},
title: "Reports and Slides Generation",
description:
"Generate static artifacts like slides and reports, and live artifacts like dashboards and pages.",
tags: [
Expand All @@ -51,44 +57,52 @@ const FEATURES: {
"Export as PDF",
],
},
{
Icon: Palette,
title: "Bring your design system",
description:
"Apply your fonts, colors, spacing, and component styles across every generated interface.",
tags: [
"Design tokens",
"Typography",
"Component variants",
"Brand configurations",
],
},
{
Icon: SealCheck,
title: "Output validation",
description:
"Detect and correct invalid model output before it turns into broken UI.",
image: {
light: "/openui-cloud/validation.svg",
dark: "/openui-cloud/validation-dark.svg",
},
title: "Built-in Output Validation",
description: "Detect and correct invalid model output before it turns into broken UI.",
tags: ["Error correction", "Model normalization"],
},
{
Icon: ShieldCheck,
image: {
light: "/openui-cloud/llm-gateway.svg",
dark: "/openui-cloud/llm-gateway-dark.svg",
},
title: "Model & provider resilience",
description:
"Keep generated UI working across model quirks, upgrades, slowdowns, and provider failures.",
tags: ["Version pinning", "Rollbacks", "Provider fallbacks"],
},
{
Icon: Eye,
title: "Observability & audit trail",
Icon: Browsers,
title: "Production-grade rendering",
description:
"Track performance, failures, cost, and what was rendered across your deployment.",
"Tested, responsive components that render consistently across browsers, devices, and screen sizes.",
tags: [
"Render success rates",
"Latency percentiles",
"Error frequency",
"Audit trail",
"Pre-tested components",
"Cross-browser compatibility",
"Built-in accessibility",
"Responsive by default",
],
},
{
Icon: Palette,
title: "Bring your design system",
description:
"Apply your fonts, colors, spacing, and component styles across every generated interface.",
tags: ["Design tokens", "Typography", "Component variants", "Brand configurations"],
},
{
Icon: Eye,
title: "Observability & audit trail",
description: "Track performance, failures, cost, and what was rendered across your deployment.",
tags: ["Render success rates", "Latency percentiles", "Error frequency", "Audit trail"],
},
];

const CHIPS = FEATURES.flatMap((feature) => feature.tags);
Expand All @@ -106,8 +120,7 @@ export function CloudSection() {
tone="dark"
title={
<>
Introducing OpenUI{" "}
<span className={styles.titleTag}>Cloud</span>
Introducing OpenUI <span className={styles.titleTag}>Cloud</span>
</>
}
subtitle="Production-ready Generative UI"
Expand All @@ -131,13 +144,31 @@ export function CloudSection() {
</div>

<div className={styles.grid}>
{FEATURES.map(({ Icon, title, description }, index) => {
{FEATURES.map(({ Icon, image, title, description }, index) => {
return (
<div
className={styles.feature}
key={index}
{...accordion.getToggleProps(index)}
>
<div className={styles.feature} key={index} {...accordion.getToggleProps(index)}>
{image && (
/* Both tiles ship; CSS picks one. Decorative either way: the
title and description already carry the meaning. */
<span className={styles.shot} aria-hidden="true">
<Image
className={`${styles.shotImage} ${styles.shotOnDark}`}
src={image.dark}
alt=""
width={720}
height={400}
loading="lazy"
/>
<Image
className={`${styles.shotImage} ${styles.shotOnLight}`}
src={image.light}
alt=""
width={720}
height={400}
loading="lazy"
/>
</span>
)}
<span className={styles.icon} aria-hidden="true">
<Icon size={28} weight="light" />
</span>
Expand Down
Loading