From e19cad20da6c90020fc699612550ee9f8f132d94 Mon Sep 17 00:00:00 2001 From: Bryan Wade Date: Fri, 4 Sep 2026 13:35:43 -0700 Subject: [PATCH 1/3] Make demo apps easier to deploy Add a Demo Apps entry to the Developers sidebar and a wide visual card directory for the current Comfy examples. Each card offers Comfy Cloud or Deploy Your Own, then rotates horizontally to the selected path's sample-specific steps. Constraint: Existing example source and workflow downloads retain their GitHub access controls Constraint: Demo card interaction must remain compatible with Mint's MDX hydration model Rejected: Direct external deployment links only | Users need the API key, workflow, Build, and endpoint handoff before leaving the page Rejected: Page-defined MDX components | Mint removes them from its client no-JS rendering pass Confidence: medium Scope-risk: narrow Reversibility: clean Directive: Keep the Comfy Cloud and Deploy Your Own paths aligned with the provider templates and Developer Platform Builds flow Tested: Mint local preview; actual browser card flip checks; MDX parse; SVG asset HTTP checks; verified Discord Render repository; git diff --check Not-tested: External Vercel, Render, and Developer Platform authenticated handoffs; zoomed or translated reverse-card layouts --- development/samples/overview.mdx | 133 +++++++++++++++++++++++++++ docs.json | 8 +- images/samples/discord-image-bot.svg | 21 +++++ images/samples/img2img-web-app.svg | 20 ++++ images/samples/sketch-to-image.svg | 19 ++++ samples-cards.css | 31 +++++++ samples-cards.js | 34 +++++++ 7 files changed, 265 insertions(+), 1 deletion(-) create mode 100644 development/samples/overview.mdx create mode 100644 images/samples/discord-image-bot.svg create mode 100644 images/samples/img2img-web-app.svg create mode 100644 images/samples/sketch-to-image.svg create mode 100644 samples-cards.css create mode 100644 samples-cards.js diff --git a/development/samples/overview.mdx b/development/samples/overview.mdx new file mode 100644 index 0000000000..c8b24e8ab1 --- /dev/null +++ b/development/samples/overview.mdx @@ -0,0 +1,133 @@ +--- +title: "Demo Apps" +description: "Start from a working demo app built on the Comfy API." +mode: "wide" +hideFooterPagination: true +--- + +Start from a working demo app, then adapt the workflow and interface for your own project. Choose Comfy Cloud for the fastest path, or Deploy Your Own when you need your own models or nodes. + +
+
+
+
+ Preview of the image-to-image web app +
+

Image-to-image web app

+

Upload an image, run a Comfy workflow, and show the result in a Next.js app.

+
+ + +
+ +
+
+ + +
+
+ +
+
+
+ Preview of the sketch-to-image app +
+

Sketch to image

Draw or import a guide, then generate an image from the guide and a prompt.

+
+ +
+
+ + +
+
+ +
+
+
+ Preview of the Discord image bot +
+

Discord image bot

Add an `/imagine` command to a Discord server and run the bundled Comfy workflow.

+
+ +
+
+ + +
+
+
+ + +Both paths need a [Comfy API key](https://platform.comfy.org/profile/api-keys). For Deploy Your Own, download the workflow, use Builds to paste it and configure the serverless endpoint, wait for its URL, then paste that URL and the API key into your Vercel or Render deployment. + + +Want to add a demo app? See the [examples repository](https://github.com/Comfy-Org/comfy-examples). diff --git a/docs.json b/docs.json index 79f6017a87..a8f05c8697 100644 --- a/docs.json +++ b/docs.json @@ -3028,6 +3028,12 @@ } ] }, + { + "group": "Demo Apps", + "pages": [ + "development/samples/overview" + ] + }, { "group": "Comfy Router", "pages": [ @@ -13897,4 +13903,4 @@ "destination": "/tutorials/partner-nodes/wan/wan3-0" } ] -} \ No newline at end of file +} diff --git a/images/samples/discord-image-bot.svg b/images/samples/discord-image-bot.svg new file mode 100644 index 0000000000..4a60c6991c --- /dev/null +++ b/images/samples/discord-image-bot.svg @@ -0,0 +1,21 @@ + + + + + + + # imagine + + + C + /imagine + a lavender cat in sunlight + + C + Comfy bot + Your image is ready + + + + + diff --git a/images/samples/img2img-web-app.svg b/images/samples/img2img-web-app.svg new file mode 100644 index 0000000000..445abb678d --- /dev/null +++ b/images/samples/img2img-web-app.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + Upload an image + + + + + + + Generated image + + diff --git a/images/samples/sketch-to-image.svg b/images/samples/sketch-to-image.svg new file mode 100644 index 0000000000..681fd26b03 --- /dev/null +++ b/images/samples/sketch-to-image.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + Draw a guide + + + + + + Generate from sketch + + diff --git a/samples-cards.css b/samples-cards.css new file mode 100644 index 0000000000..b857b168b2 --- /dev/null +++ b/samples-cards.css @@ -0,0 +1,31 @@ +.sample-card-grid { display: grid; gap: 20px; grid-template-columns: repeat(2, minmax(0, 1fr)); margin-top: 28px; } +.sample-card { min-width: 0; perspective: 1200px; } +.sample-card-inner { height: 565px; position: relative; transform-style: preserve-3d; transition: transform 560ms cubic-bezier(.2, .75, .3, 1); } +.sample-card[data-flipped="true"] .sample-card-inner { transform: rotateY(180deg); } +.sample-card-face { backface-visibility: hidden; background: #fff; border: 1px solid #e5e1eb; border-radius: 12px; box-sizing: border-box; height: 100%; inset: 0; overflow: hidden; position: absolute; width: 100%; } +.sample-card-front { display: flex; flex-direction: column; } +.sample-card-back { opacity: 0; pointer-events: none; transform: rotateY(180deg); visibility: hidden; } +.sample-card[data-flipped="true"] .sample-card-front { pointer-events: none; transition: visibility 0s linear 280ms; visibility: hidden; } +.sample-card[data-flipped="true"][data-active-path="api"] .sample-card-back[data-sample-path="api"], .sample-card[data-flipped="true"][data-active-path="endpoint"] .sample-card-back[data-sample-path="endpoint"] { opacity: 1; pointer-events: auto; visibility: visible; } +.sample-card-preview { border-bottom: 1px solid #e5e1eb; display: block; width: 100%; } +.sample-card-body { display: flex; flex: 1; flex-direction: column; padding: 20px; } +.sample-card-body h2, .sample-path-body h2 { color: #49378b; font-size: 18px; font-weight: 700; letter-spacing: -.02em; margin: 0 0 8px; } +.sample-path-body h2 { text-align: center; } +.sample-card-body > p { color: #5f5966; font-size: 14px; line-height: 1.55; margin: 0; min-height: 66px; } +.sample-card-actions { display: flex; gap: 8px; margin-top: 20px; } +.sample-card-actions button { background: #fff; border: 1px solid #cfc8de; border-radius: 6px; color: #49378b; cursor: pointer; flex: 1; font-size: 13px; font-weight: 700; padding: 10px 12px; } +.sample-card-links { border-top: 1px solid #eeeaf1; display: flex; gap: 8px; margin-top: 16px; padding-top: 12px; } +.sample-card-links a { color: #625b6c; font-size: 12px; font-weight: 650; text-decoration: none; } +.sample-card-links span { color: #b3adba; } +.sample-path-body { padding: 20px; } +.sample-path-body > button { background: transparent; border: 0; color: #625b6c; cursor: pointer; font-size: 12px; font-weight: 700; padding: 0; } +.sample-path-eyebrow { color: #857a9e; font-size: 10px; font-weight: 700; letter-spacing: .08em; margin: 19px 0 5px; text-align: center; text-transform: uppercase; } +.sample-path-body > p:not(.sample-path-eyebrow) { color: #5f5966; font-size: 12px; line-height: 1.5; margin: 0 0 15px; } +.sample-path-steps { display: grid; gap: 8px; } +.sample-path-step { align-items: center; background: #faf9fc; border: 1px solid #e5e1ea; border-radius: 8px; display: flex; gap: 10px; padding: 10px 11px; } +.sample-path-number { align-items: center; background: #49378b; border-radius: 999px; color: #fff; display: inline-flex; flex: 0 0 21px; font-size: 11px; font-weight: 700; height: 21px; justify-content: center; } +.sample-path-copy { flex: 1; min-width: 0; } +.sample-path-copy strong { color: #312c36; display: block; font-size: 12px; } +.sample-path-copy span { color: #706a75; display: block; font-size: 11px; line-height: 1.35; margin-top: 2px; } +.sample-path-step a { color: #49378b; flex: 0 0 auto; font-size: 11px; font-weight: 700; text-decoration: none; } +@media (max-width: 768px) { .sample-card-grid { grid-template-columns: 1fr; } } diff --git a/samples-cards.js b/samples-cards.js new file mode 100644 index 0000000000..650e97f62d --- /dev/null +++ b/samples-cards.js @@ -0,0 +1,34 @@ +document.addEventListener("click", (event) => { + const openButton = event.target.closest("[data-sample-open]"); + const backButton = event.target.closest("[data-sample-back]"); + + if (openButton) { + const card = openButton.closest("[data-sample-card]"); + const path = openButton.dataset.sampleOpen; + const front = card.querySelector("[data-sample-front]"); + const selectedPath = card.querySelector(`[data-sample-path="${path}"]`); + event.preventDefault(); + card.dataset.activePath = path; + card.dataset.flipped = "true"; + front.inert = true; + front.setAttribute("aria-hidden", "true"); + card.querySelectorAll("[data-sample-path]").forEach((panel) => { + panel.inert = panel !== selectedPath; + panel.setAttribute("aria-hidden", panel === selectedPath ? "false" : "true"); + }); + } + + if (backButton) { + const card = backButton.closest("[data-sample-card]"); + const front = card.querySelector("[data-sample-front]"); + event.preventDefault(); + card.removeAttribute("data-flipped"); + card.removeAttribute("data-active-path"); + front.inert = false; + front.setAttribute("aria-hidden", "false"); + card.querySelectorAll("[data-sample-path]").forEach((panel) => { + panel.inert = true; + panel.setAttribute("aria-hidden", "true"); + }); + } +}); From 611096e15806b9934d9a76ea448e91a3d0e59302 Mon Sep 17 00:00:00 2001 From: Bryan Wade Date: Tue, 8 Sep 2026 13:53:22 -0700 Subject: [PATCH 2/3] Keep demo-card interactions usable with keyboard navigation The sample cards hid the active control after flipping without moving focus, and their fixed height could clip longer deployment paths on narrow viewports. Move focus between the trigger and visible panel, keep inactive faces inert and hidden, and allow the card to expand or scroll safely on small screens. Constraint: Mintlify hydrates these cards as plain client-side JavaScript. Rejected: Preserve the clicked element as focus target | it becomes inert when the selected path opens. Confidence: high Scope-risk: narrow Reversibility: clean Directive: Any future card state change must move focus into the visible, non-inert face. Tested: node --check samples-cards.js; docs.json parse; git diff --check Not-tested: Mint broken-links did not complete within the local command window; browser keyboard interaction requires CI or manual preview. --- samples-cards.css | 4 ++-- samples-cards.js | 12 ++++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/samples-cards.css b/samples-cards.css index b857b168b2..01dfc8786d 100644 --- a/samples-cards.css +++ b/samples-cards.css @@ -2,7 +2,7 @@ .sample-card { min-width: 0; perspective: 1200px; } .sample-card-inner { height: 565px; position: relative; transform-style: preserve-3d; transition: transform 560ms cubic-bezier(.2, .75, .3, 1); } .sample-card[data-flipped="true"] .sample-card-inner { transform: rotateY(180deg); } -.sample-card-face { backface-visibility: hidden; background: #fff; border: 1px solid #e5e1eb; border-radius: 12px; box-sizing: border-box; height: 100%; inset: 0; overflow: hidden; position: absolute; width: 100%; } +.sample-card-face { backface-visibility: hidden; background: #fff; border: 1px solid #e5e1eb; border-radius: 12px; box-sizing: border-box; height: 100%; inset: 0; overflow-y: auto; position: absolute; width: 100%; } .sample-card-front { display: flex; flex-direction: column; } .sample-card-back { opacity: 0; pointer-events: none; transform: rotateY(180deg); visibility: hidden; } .sample-card[data-flipped="true"] .sample-card-front { pointer-events: none; transition: visibility 0s linear 280ms; visibility: hidden; } @@ -28,4 +28,4 @@ .sample-path-copy strong { color: #312c36; display: block; font-size: 12px; } .sample-path-copy span { color: #706a75; display: block; font-size: 11px; line-height: 1.35; margin-top: 2px; } .sample-path-step a { color: #49378b; flex: 0 0 auto; font-size: 11px; font-weight: 700; text-decoration: none; } -@media (max-width: 768px) { .sample-card-grid { grid-template-columns: 1fr; } } +@media (max-width: 768px) { .sample-card-grid { grid-template-columns: 1fr; } .sample-card-inner { height: clamp(620px, 180vw, 760px); } } diff --git a/samples-cards.js b/samples-cards.js index 650e97f62d..6b8d828e13 100644 --- a/samples-cards.js +++ b/samples-cards.js @@ -16,19 +16,23 @@ document.addEventListener("click", (event) => { panel.inert = panel !== selectedPath; panel.setAttribute("aria-hidden", panel === selectedPath ? "false" : "true"); }); + selectedPath.querySelector("[data-sample-back]").focus(); } if (backButton) { const card = backButton.closest("[data-sample-card]"); const front = card.querySelector("[data-sample-front]"); + const path = card.dataset.activePath; + const openButton = front.querySelector(`[data-sample-open="${path}"]`); event.preventDefault(); - card.removeAttribute("data-flipped"); - card.removeAttribute("data-active-path"); - front.inert = false; - front.setAttribute("aria-hidden", "false"); card.querySelectorAll("[data-sample-path]").forEach((panel) => { panel.inert = true; panel.setAttribute("aria-hidden", "true"); }); + card.removeAttribute("data-flipped"); + card.removeAttribute("data-active-path"); + front.inert = false; + front.setAttribute("aria-hidden", "false"); + openButton.focus(); } }); From 06a10ae4170ed3f44ec86a9063e7dc88b63637fc Mon Sep 17 00:00:00 2001 From: Bryan Wade Date: Tue, 8 Sep 2026 14:00:08 -0700 Subject: [PATCH 3/3] State the Comfy Cloud subscription prerequisite The sample-app quick starts named the API key but omitted the required eligible Comfy Cloud subscription. State that prerequisite in each managed-endpoint path so users know the account requirement before deploying. Constraint: Keep the card flow to two steps. Confidence: high Scope-risk: narrow Reversibility: clean Directive: Keep managed-endpoint prerequisites identical across sample cards. Tested: docs.json parse; git diff --check Not-tested: Mint preview --- development/samples/overview.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/development/samples/overview.mdx b/development/samples/overview.mdx index c8b24e8ab1..d579a9dc00 100644 --- a/development/samples/overview.mdx +++ b/development/samples/overview.mdx @@ -30,7 +30,7 @@ Start from a working demo app, then adapt the workflow and interface for your ow

Comfy Cloud

Use Image-to-image web app with Comfy Cloud

Use Comfy Cloud as the managed endpoint. Create an API key, then deploy a copy of the app.

-
1
Create API keyCreate and save a Comfy API key.
Create API key ↗
+
1
Create API keyCreate and save a Comfy API key. An eligible Comfy Cloud subscription is also required.
Create API key ↗
2
Deploy app to VercelThe provider creates a copy of this sample in your account.
Deploy app ↗
@@ -68,7 +68,7 @@ Start from a working demo app, then adapt the workflow and interface for your ow

Use Sketch to image with Comfy Cloud

Use Comfy Cloud as the managed endpoint. Create an API key, then deploy a copy of the app.

-
1
Create API keyCreate and save a Comfy API key.
Create API key ↗
+
1
Create API keyCreate and save a Comfy API key. An eligible Comfy Cloud subscription is also required.
Create API key ↗
2
Deploy app to VercelThe provider creates a copy of this sample in your account.
Deploy app ↗
@@ -105,7 +105,7 @@ Start from a working demo app, then adapt the workflow and interface for your ow

Use Discord image bot with Comfy Cloud

Use Comfy Cloud as the managed endpoint. Create an API key, then deploy a copy of the app.

-
1
Create API keyCreate and save a Comfy API key.
Create API key ↗
+
1
Create API keyCreate and save a Comfy API key. An eligible Comfy Cloud subscription is also required.
Create API key ↗
2
Deploy app to RenderThe provider creates a copy of this sample in your account.
Deploy app ↗