From 78bcb32174c4ae36a5bc460704207c52959aa7b8 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 4 Oct 2026 01:32:53 +0000 Subject: [PATCH 1/7] docs(website): Stop wide tables and Grid code from overflowing on phones - Wrap MDX tables in a horizontal scroll container; the global display: table rule made wide tables widen the whole page - Grid columns use minmax(0, 1fr) so code blocks scroll instead of forcing the grid wider; wrap mode no longer splits words mid-token - Fix nested admonition fence that printed a stray ::: on useSuspense - Remove dead CSS (typo'd monospace rule, duplicate navbar rule, unused Demo.module.css) and unused Grid cols prop Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01WKtKRyLXafwpf1pNBLAuVW --- docs/core/api/useSuspense.md | 8 +-- website/src/components/Demo/Demo.module.css | 59 --------------------- website/src/components/Grid.module.css | 8 +-- website/src/components/Grid.tsx | 1 - website/src/css/root.css | 17 +++--- website/src/theme/MDXComponents.js | 10 ++++ 6 files changed, 26 insertions(+), 77 deletions(-) delete mode 100644 website/src/components/Demo/Demo.module.css diff --git a/docs/core/api/useSuspense.md b/docs/core/api/useSuspense.md index 98ddaecbb0a1..53b371733318 100644 --- a/docs/core/api/useSuspense.md +++ b/docs/core/api/useSuspense.md @@ -217,17 +217,17 @@ Cache policy is [Stale-While-Revalidate](https://tools.ietf.org/html/rfc5861) by ::: -:::react +::::react -::::info[React Native] +:::info[React Native] When using React Navigation, useSuspense() will trigger fetches on focus if the data is considered stale. -:::: - ::: +:::: + ## Types diff --git a/website/src/components/Demo/Demo.module.css b/website/src/components/Demo/Demo.module.css deleted file mode 100644 index 94c45a6cc4b7..000000000000 --- a/website/src/components/Demo/Demo.module.css +++ /dev/null @@ -1,59 +0,0 @@ -@media only screen and (min-device-width: 360px) and (max-device-width: 736px) { -} - -@media only screen and (min-width: 1024px) { -} - -@media only screen and (max-width: 1023px) { -} - -@media only screen and (min-width: 1400px) { -} - -@media only screen and (max-width: 1450px) { - .gridImageBlock { - display: flex; - flex-direction: column; - } -} - -@media only screen and (min-width: 1450px) { - .gridImageBlock { - display: flex; - flex-direction: row; - flex-wrap: wrap; - } -} - -.blockImage { - width: 60px; - height: 60px; - display: flex; - align-items: center; - justify-items: center; -} - -.blockImage img { - min-width: 60px; -} - -.blockImageFull { - display: flex; - align-items: center; - justify-items: center; - box-shadow: rgba(0, 0, 0, 0.45) 0px 10px 30px; - border-radius: 6px; - background: #1e1e1e; - margin-bottom: 40px; -} -.blockImageFull img, .blockImageFull video { - border-radius: 6px; -} -.blockImageFull video { - padding: 8px; -} - -.gridImageBlock { - justify-content: space-between; - margin-bottom: -40px; -} diff --git a/website/src/components/Grid.module.css b/website/src/components/Grid.module.css index 13961d81fca7..9941f1496367 100644 --- a/website/src/components/Grid.module.css +++ b/website/src/components/Grid.module.css @@ -1,22 +1,22 @@ .grid { display: grid; - grid-template-columns: minmax(min-content, 1fr) minmax(min-content, 1fr); + grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 15px; } .grid.wrap :global(.prism-code) { white-space: pre-wrap; - word-break: break-all; + overflow-wrap: anywhere; } @media only screen and (max-width: 768px) { .grid { - grid-template-columns: minmax(min-content, 1fr); + grid-template-columns: minmax(0, 1fr); } } @media only screen and (min-width: 997px) and (max-width: 1268px) { .grid { - grid-template-columns: minmax(min-content, 1fr); + grid-template-columns: minmax(0, 1fr); } } \ No newline at end of file diff --git a/website/src/components/Grid.tsx b/website/src/components/Grid.tsx index 0f46ee6d9761..e23fa8af6136 100644 --- a/website/src/components/Grid.tsx +++ b/website/src/components/Grid.tsx @@ -8,7 +8,6 @@ export default function Grid({ wrap = false, }: { children: ReactNode; - cols?: number; wrap?: boolean; }) { return ( diff --git a/website/src/css/root.css b/website/src/css/root.css index df91888ec535..14e0ced6514c 100644 --- a/website/src/css/root.css +++ b/website/src/css/root.css @@ -48,19 +48,10 @@ html[data-theme='light'] { --ifm-pre-background: var(--prism-background-color); } -.navbar .navbar__link { - font-family: var(--ifm-font-family-base); -} - .navbar .navbar__brand .navbar__title { font-family: var(--ifm-font-family-base); } -code, -pre { - font-family: var(-ifm-font-family-monospace); -} - html { font-family: var(--ifm-font-family-base); } @@ -70,3 +61,11 @@ table { width: 100%; font-variant-numeric: tabular-nums; } +/* MDX tables are wrapped (theme/MDXComponents) so wide ones scroll instead of widening the page */ +.table-scroll { + overflow-x: auto; + margin-bottom: var(--ifm-spacing-vertical); +} +.table-scroll > table { + margin-bottom: 0; +} diff --git a/website/src/theme/MDXComponents.js b/website/src/theme/MDXComponents.js index 90640295a062..dd09f1aa0f2c 100644 --- a/website/src/theme/MDXComponents.js +++ b/website/src/theme/MDXComponents.js @@ -1,8 +1,18 @@ // Import the original mapper import { RestEndpoint } from '@data-client/rest'; import MDXComponents from '@theme-original/MDXComponents'; +import React from 'react'; + +function Table(props) { + return ( +
+ + + ); +} export default { ...MDXComponents, RestEndpoint, + table: Table, }; From a0794b2fe985ce57939f065e79cb5b2c316fcb72 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 4 Oct 2026 01:32:53 +0000 Subject: [PATCH 2/7] docs(website): Polish homepage sections and demos page - Remove leftover hero overlap spacing that crowded the first demo - StackBlitz: framed placeholder with loading label, link-out card on phones, optional More Demos link - Homepage: tinted, consistent feature icons with a section heading; heading for the StackBlitz app section; prompt above the closing CTA - Demos page: title, per-demo description, Open in StackBlitz and Source on GitHub links, scrollable tabs on phones Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01WKtKRyLXafwpf1pNBLAuVW --- .../components/HomepageFeatures.module.css | 28 +- website/src/components/HomepageFeatures.tsx | 73 +----- website/src/components/StackBlitz.module.css | 52 ++++ website/src/components/StackBlitz.tsx | 67 ++++- website/src/pages/demos.module.css | 44 ++++ website/src/pages/demos.tsx | 243 ++++++++---------- website/src/pages/index.js | 38 ++- website/src/pages/index.module.css | 38 +-- website/static/img/typescript-mono.svg | 1 + 9 files changed, 342 insertions(+), 242 deletions(-) create mode 100644 website/src/components/StackBlitz.module.css create mode 100644 website/src/pages/demos.module.css create mode 100644 website/static/img/typescript-mono.svg diff --git a/website/src/components/HomepageFeatures.module.css b/website/src/components/HomepageFeatures.module.css index 8f8f01b36729..4f3d157f5e83 100644 --- a/website/src/components/HomepageFeatures.module.css +++ b/website/src/components/HomepageFeatures.module.css @@ -1,12 +1,32 @@ .features { - display: flex; - align-items: center; margin: 3.2rem 0; padding: 0; width: 100%; } +.heading { + text-align: center; + margin-bottom: 2rem; +} + +.feature { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + padding-left: var(--ifm-spacing-horizontal); + padding-right: var(--ifm-spacing-horizontal); +} + .featureSvg { - height: 60px; - width: 60px; + height: 56px; + width: 56px; + margin-bottom: 0.75rem; + color: var(--ifm-color-primary); +} + +@media screen and (max-width: 996px) { + .feature { + margin-bottom: 2rem; + } } diff --git a/website/src/components/HomepageFeatures.tsx b/website/src/components/HomepageFeatures.tsx index d68234be891e..e712c1e3e1bc 100644 --- a/website/src/components/HomepageFeatures.tsx +++ b/website/src/components/HomepageFeatures.tsx @@ -1,6 +1,4 @@ import Link from '@docusaurus/Link'; -import useBaseUrl from '@docusaurus/useBaseUrl'; -import ThemedImage from '@theme/ThemedImage'; import clsx from 'clsx'; import React from 'react'; @@ -8,28 +6,16 @@ import styles from './HomepageFeatures.module.css'; import ChemicalCompositionSvg from '../../static/img/chemical-composition.svg'; import FastCarSvg from '../../static/img/fast-car.svg'; import GrowingBarChartSvg from '../../static/img/growing-bar-chart.svg'; +import TypeScriptSvg from '../../static/img/typescript-mono.svg'; type SvgComponent = React.ComponentType>; -interface BaseFeature { +interface FeatureItem { description: React.ReactNode; title: string; -} - -interface SvgFeature extends BaseFeature { Svg: SvgComponent; - dark?: never; - light?: never; -} - -interface ThemedImageFeature extends BaseFeature { - dark: string; - light: string; - Svg?: never; } -type FeatureItem = SvgFeature | ThemedImageFeature; - const featureList: FeatureItem[] = [ { description: ( @@ -39,8 +25,7 @@ const featureList: FeatureItem[] = [ make it easy to avoid race conditions ), - light: '/img/typescript.svg', - dark: '/img/typescript.dark.svg', + Svg: TypeScriptSvg, title: 'Data Integrity', }, { @@ -82,61 +67,21 @@ const featureList: FeatureItem[] = [ }, ]; -function isSvgFeature(feature: FeatureItem): feature is SvgFeature { - return 'Svg' in feature && feature.Svg != null; -} - -function Feature(feature: FeatureItem) { - const { title, description } = feature; +function Feature({ title, description, Svg }: FeatureItem) { return ( -
-
- {isSvgFeature(feature) ? - - : - } -
-
-

{title}

-

{description}

-
+
+ +

{title}

+

{description}

); } -function SvgFeatureIcon({ Svg, title }: { Svg: SvgComponent; title: string }) { - return ; -} - -function ThemedFeatureIcon({ - light, - dark, - title, -}: { - light: string; - dark: string; - title: string; -}) { - return ( - - ); -} - export default function HomepageFeatures() { return (
+

Why Data Client

{featureList.map((props, idx) => ( diff --git a/website/src/components/StackBlitz.module.css b/website/src/components/StackBlitz.module.css new file mode 100644 index 000000000000..1d9d4751a05a --- /dev/null +++ b/website/src/components/StackBlitz.module.css @@ -0,0 +1,52 @@ +.wrapper { + position: relative; + margin-bottom: var(--ifm-spacing-vertical); + border-radius: var(--ifm-code-border-radius); + overflow: hidden; + background: var(--monoco-code-background); + box-shadow: var(--ifm-global-shadow-lw); +} + +/* Sits behind the transparent placeholder iframe; the loaded embed paints over it */ +.loading { + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + color: rgb(191, 199, 213); + font-size: 0.9rem; + letter-spacing: 0.02em; +} + +.frame { + position: relative; + display: block; + border: 0; + max-width: 100%; +} + +.card { + display: flex; + flex-direction: column; + align-items: center; + gap: 0.75rem; + margin-bottom: var(--ifm-spacing-vertical); + padding: 1.5rem 1rem; + border-radius: var(--ifm-code-border-radius); + background: var(--ifm-color-emphasis-100); + border: 1px solid var(--ifm-color-emphasis-200); + text-align: center; +} + +.cardTitle { + margin: 0; + font-weight: var(--ifm-font-weight-semibold); +} + +.cardLinks { + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 0.5rem; +} diff --git a/website/src/components/StackBlitz.tsx b/website/src/components/StackBlitz.tsx index 369f35c8e2f6..c04b70274a00 100644 --- a/website/src/components/StackBlitz.tsx +++ b/website/src/components/StackBlitz.tsx @@ -1,7 +1,9 @@ import Link from '@docusaurus/Link'; -import type { ReactElement } from 'react'; +import { useWindowSize } from '@docusaurus/theme-common'; +import type { CSSProperties, ReactElement } from 'react'; import { isGoogleBot } from './Playground/isMobileOrBot'; +import styles from './StackBlitz.module.css'; import { useHasIntersected } from './useHasIntersected'; export default function StackBlitz({ @@ -16,6 +18,8 @@ export default function StackBlitz({ file, ctl = '0', initialpath = '', + style, + moreDemos = true, }: { app?: string; repo?: string; @@ -28,6 +32,9 @@ export default function StackBlitz({ file: string; ctl?: string; initialpath?: string; + style?: CSSProperties; + /** Show the "More Demos" link below the embed */ + moreDemos?: boolean; }) { const embed = '1'; const params = new URLSearchParams({ @@ -41,17 +48,28 @@ export default function StackBlitz({ ctl, initialpath, }).toString(); - const src = - app ? - `https://stackblitz.com/github/reactive/${repo}/tree/master/examples/${app}?${params}` - : `https://stackblitz.com/github/reactive/${repo}/tree/master?${params}`; + const projectPath = + app ? `${repo}/tree/master/examples/${app}` : `${repo}/tree/master`; + const src = `https://stackblitz.com/github/reactive/${projectPath}?${params}`; + const openUrl = `https://stackblitz.com/github/reactive/${projectPath}?${new URLSearchParams({ file })}`; + const sourceUrl = `https://github.com/reactive/${projectPath}`; + const title = `${app ?? repo} demo on StackBlitz`; const [frameRef, hasIntersected] = useHasIntersected(); + // Embedded IDEs are cramped on phones; link out instead + const isMobile = useWindowSize() === 'mobile'; let embedElement: ReactElement; if (!hasIntersected || isGoogleBot) { embedElement = ( - + ); } else { embedElement = ( @@ -60,6 +78,9 @@ export default function StackBlitz({ width={width} height={height} ref={frameRef} + title={title} + className={styles.frame} + style={style} loading="lazy" sandbox="allow-scripts allow-same-origin" > @@ -68,12 +89,34 @@ export default function StackBlitz({ return ( <> - {embedElement} -

- - More Demos - -

+ {isMobile ? +
+

+ {app ? `Explore the ${app} example` : 'Explore the example'} +

+
+ + Open in StackBlitz + + + View source + +
+
+ :
+ + {embedElement} +
+ } + {moreDemos ? +

+ + More Demos + +

+ : null} ); } diff --git a/website/src/pages/demos.module.css b/website/src/pages/demos.module.css new file mode 100644 index 000000000000..01df4c990d56 --- /dev/null +++ b/website/src/pages/demos.module.css @@ -0,0 +1,44 @@ +.header { + padding: 1.5rem var(--ifm-spacing-horizontal) 0.5rem; +} +.header h1 { + margin-bottom: 0.25rem; +} +.header p { + margin: 0; + color: var(--ifm-color-emphasis-700); +} + +.tabs { + flex-wrap: nowrap; + overflow-x: auto; +} +.tabs > li { + white-space: nowrap; +} + +.caption { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + gap: 0.5rem 1rem; + padding: 0 var(--ifm-spacing-horizontal) 0.75rem; +} +.caption p { + margin: 0; +} + +.links { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; +} + +.frame { + display: block; + border: 0; + width: 100%; + height: calc(100vh - 240px); + min-height: 500px; +} diff --git a/website/src/pages/demos.tsx b/website/src/pages/demos.tsx index ad91cc599cd3..7e10a8941e57 100644 --- a/website/src/pages/demos.tsx +++ b/website/src/pages/demos.tsx @@ -1,149 +1,134 @@ +import Link from '@docusaurus/Link'; import Layout from '@theme/Layout'; import TabItem from '@theme/TabItem'; import Tabs from '@theme/Tabs'; import React from 'react'; +import styles from './demos.module.css'; import { searchParams } from '../utils/searchParams'; +interface Demo { + value: string; + label: string; + /** Directory under examples/ */ + app: string; + description: string; + /** Files opened in the editor */ + files: string; +} + +const demos: Demo[] = [ + { + value: 'todo', + label: 'Todo', + app: 'todo-app', + description: + 'React todo list on JSONPlaceholder with REST resources and optimistic updates.', + files: + 'src/resources/TodoResource.ts,src/pages/Home/NewTodo.tsx,src/pages/Home/TodoListItem.tsx,src/pages/Home/TodoList.tsx', + }, + { + value: 'github', + label: 'GitHub', + app: 'github-app', + description: + 'Issues and pull request browser on the live GitHub API, with pagination and nested entities.', + files: 'src/resources/Issue.tsx,src/pages/IssueList.tsx', + }, + { + value: 'nextjs', + label: 'NextJS SSR', + app: 'nextjs', + description: + 'Next.js App Router with server side rendering and client hydration of the normalized store.', + files: 'resources/TodoResource.ts,components/todo/TodoList.tsx', + }, + { + value: 'coin-app', + label: 'Live Coin Prices', + app: 'coin-app', + description: + 'Crypto prices streamed over WebSocket with a custom Manager, plus Queries that compute derived values.', + files: + 'src/resources/StreamManager.ts,src/resources/Ticker.ts,src/resources/fallbackQueries.ts,src/pages/Home/AssetPrice.tsx', + }, + { + value: 'vue-todo-app', + label: 'Vue Todo', + app: 'vue-todo-app', + description: + 'The todo app in Vue 3 with @data-client/vue composables and Vue Router.', + files: + 'src/resources/TodoResource.ts,src/resources/UserResource.ts,src/pages/UserTodos.vue,src/pages/UserList.vue', + }, +]; + +const repoPath = 'reactive/data-client/tree/master/examples'; + +function DemoPanel({ app, description, files }: Demo) { + const projectUrl = `https://stackblitz.com/github/${repoPath}/${app}`; + return ( + <> +
+

{description}

+
+ + Open in StackBlitz + + + Source on GitHub + +
+
+ + + ); +} + export default function DemoList() { return ( +
+

Demos

+

+ Complete example apps from the{' '} + examples directory, + running live in StackBlitz. +

+
({ value, label }))} groupId="Demos" + className={styles.tabs} > - - {/*Todo - */} - - - - {/* */} - - - - {/* */} - - - - {/* */} - - - - - + {demos.map(demo => ( + + + + ))}
); diff --git a/website/src/pages/index.js b/website/src/pages/index.js index 832e38c78ac9..29526234aeb2 100644 --- a/website/src/pages/index.js +++ b/website/src/pages/index.js @@ -66,8 +66,11 @@ function HomepageHeader() { function HomepageEnder() { return ( -
+
+

+ Start with one resource and one hook +

-
-
-
- -
+
+

A complete app

+

+ A GitHub issues and pull request browser on the live GitHub API, + built with REST resources and Suspense. +

+ +
+ + More demos +
-
+
diff --git a/website/src/pages/index.module.css b/website/src/pages/index.module.css index d723bddd7328..51decebf6265 100644 --- a/website/src/pages/index.module.css +++ b/website/src/pages/index.module.css @@ -8,19 +8,6 @@ text-align: center; position: relative; overflow: hidden; - /*margin-bottom: 10px;*/ - - /* background: linear-gradient( - transparent 212px, - var(--ifm-hero-fade-to-background-color) 100% - ), - url('../../static/img/rest_hooks_logo_grayscale.svg') - var(--ifm-hero-background-color) no-repeat; */ - background-position: 0, 100% calc(20% - 3rem); - - /* overlap */ - padding-bottom: 14.5rem; - margin-bottom: -12rem; } .logoWrapper { @@ -36,9 +23,7 @@ html[data-theme='light'] .logoWrapper { @media screen and (max-width: 996px) { .heroBanner { - padding: 2rem 0; - padding-bottom: 14rem; - margin-bottom: -13rem; + padding: 2rem 0 2.5rem; } } @@ -86,9 +71,7 @@ html[data-theme='light'] .buttons :global(.button.button--secondary) { margin-bottom: 20px; } .heroBanner { - padding: 1rem 0; - padding-bottom: 14rem; - + padding: 1rem 0 2rem; } } @@ -104,3 +87,20 @@ html[data-theme='light'] .buttons :global(.button.button--secondary) { z-index: 1; position: relative; } + +.sectionHeading { + text-align: center; + margin-bottom: 0.5rem; +} +.sectionLede { + text-align: center; + color: var(--ifm-color-emphasis-700); + margin-bottom: 1.5rem; +} +.ender { + flex-direction: column; + padding: 4rem 0; +} +.ender .sectionHeading { + margin-bottom: 1.5rem; +} diff --git a/website/static/img/typescript-mono.svg b/website/static/img/typescript-mono.svg new file mode 100644 index 000000000000..9f4165f52100 --- /dev/null +++ b/website/static/img/typescript-mono.svg @@ -0,0 +1 @@ + From 183a8a3dc52b0c98ddd4d0f4d17e6d8b307ef391 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 4 Oct 2026 01:38:51 +0000 Subject: [PATCH 3/7] docs(website): Keep StackBlitz embed mounted on phones Hide the embed wrapper instead of unmounting it so the intersection observer keeps its node; resizing past the mobile breakpoint now loads the embed instead of leaving it on the loading label. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01WKtKRyLXafwpf1pNBLAuVW --- website/src/components/StackBlitz.tsx | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/website/src/components/StackBlitz.tsx b/website/src/components/StackBlitz.tsx index c04b70274a00..c11baf664ff1 100644 --- a/website/src/components/StackBlitz.tsx +++ b/website/src/components/StackBlitz.tsx @@ -60,7 +60,7 @@ export default function StackBlitz({ const isMobile = useWindowSize() === 'mobile'; let embedElement: ReactElement; - if (!hasIntersected || isGoogleBot) { + if (!hasIntersected || isGoogleBot || isMobile) { embedElement = ( - ); - } else { - embedElement = ( - - ); - } - return ( <> {isMobile ? @@ -95,30 +58,45 @@ export default function StackBlitz({ {app ? `Explore the ${app} example` : 'Explore the example'}

- + Open in StackBlitz - + View source
- : null} - {/* Stays mounted (hidden on phones) so the intersection observer keeps - its node and the embed can load after resizing to desktop */} - - {moreDemos ? -

- - More Demos - -

- : null} + :
+ + +
+ } +

+ + More Demos + +

); } diff --git a/website/src/components/useHasIntersected.tsx b/website/src/components/useHasIntersected.tsx index 236bd69c5598..fc191f9dead4 100644 --- a/website/src/components/useHasIntersected.tsx +++ b/website/src/components/useHasIntersected.tsx @@ -1,13 +1,12 @@ -import { useEffect, useRef, useState } from 'react'; +import { useEffect, useState } from 'react'; export function useHasIntersected(options: Props = {}) { const { threshold = 0.1, root = null, rootMargin = '0%' } = options; - const ref = useRef(null); + // Callback ref (via state) so a node mounted after the first render is observed + const [node, ref] = useState(null); const [hasIntersected, setHasIntersected] = useState(false); useEffect(() => { - const node = ref.current; - if (!node || typeof IntersectionObserver !== 'function') { return; } @@ -30,7 +29,7 @@ export function useHasIntersected(options: Props = {}) { return () => { observer.disconnect(); }; - }, [threshold, root, rootMargin]); + }, [node, threshold, root, rootMargin]); return [ref, hasIntersected] as const; } diff --git a/website/src/pages/demos.module.css b/website/src/pages/demos.module.css index 01df4c990d56..ded75169ab93 100644 --- a/website/src/pages/demos.module.css +++ b/website/src/pages/demos.module.css @@ -9,10 +9,6 @@ color: var(--ifm-color-emphasis-700); } -.tabs { - flex-wrap: nowrap; - overflow-x: auto; -} .tabs > li { white-space: nowrap; } diff --git a/website/src/pages/demos.tsx b/website/src/pages/demos.tsx index 7e10a8941e57..1838f5dd834a 100644 --- a/website/src/pages/demos.tsx +++ b/website/src/pages/demos.tsx @@ -97,8 +97,6 @@ function DemoPanel({ app, description, files }: Demo) { hideNavigation: '1', terminalHeight: '0', })}`} - width="900" - height="700" > ); @@ -119,6 +117,7 @@ export default function DemoList() {

({ value, label }))} groupId="Demos" diff --git a/website/src/pages/index.js b/website/src/pages/index.js index 29526234aeb2..9d770ef2565c 100644 --- a/website/src/pages/index.js +++ b/website/src/pages/index.js @@ -146,17 +146,10 @@ export default function Home() { -
- - More demos - -
diff --git a/website/src/pages/index.module.css b/website/src/pages/index.module.css index 51decebf6265..c48e873b19d7 100644 --- a/website/src/pages/index.module.css +++ b/website/src/pages/index.module.css @@ -98,7 +98,6 @@ html[data-theme='light'] .buttons :global(.button.button--secondary) { margin-bottom: 1.5rem; } .ender { - flex-direction: column; padding: 4rem 0; } .ender .sectionHeading { diff --git a/website/static/img/typescript.dark.svg b/website/static/img/typescript.dark.svg deleted file mode 100644 index b9810f4b6d79..000000000000 --- a/website/static/img/typescript.dark.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/website/static/img/typescript.svg b/website/static/img/typescript.svg deleted file mode 100644 index b9ece8995daa..000000000000 --- a/website/static/img/typescript.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - From e71007f89add85425fc27d893e6f02a4566420d2 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 4 Oct 2026 16:21:48 +0000 Subject: [PATCH 5/7] fix(website): Swap StackBlitz embed for phone card with CSS to avoid layout shift useWindowSize() is 'ssr' on first paint, so phones briefly rendered the tall embed before swapping to the card. A media query renders the right variant from the server markup; the hidden iframe never intersects, so it still never loads on phones. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01WKtKRyLXafwpf1pNBLAuVW --- website/src/components/StackBlitz.module.css | 12 ++- website/src/components/StackBlitz.tsx | 82 ++++++++++---------- 2 files changed, 51 insertions(+), 43 deletions(-) diff --git a/website/src/components/StackBlitz.module.css b/website/src/components/StackBlitz.module.css index fcb03671bdf5..d130d0270a36 100644 --- a/website/src/components/StackBlitz.module.css +++ b/website/src/components/StackBlitz.module.css @@ -28,7 +28,7 @@ } .card { - display: flex; + display: none; flex-direction: column; align-items: center; gap: 0.75rem; @@ -51,3 +51,13 @@ justify-content: center; gap: 0.5rem; } + +/* Matches Docusaurus' mobile breakpoint (useWindowSize) */ +@media (max-width: 996px) { + .wrapper { + display: none; + } + .card { + display: flex; + } +} diff --git a/website/src/components/StackBlitz.tsx b/website/src/components/StackBlitz.tsx index 0fea73721ffe..bf539e96046a 100644 --- a/website/src/components/StackBlitz.tsx +++ b/website/src/components/StackBlitz.tsx @@ -1,5 +1,4 @@ import Link from '@docusaurus/Link'; -import { useWindowSize } from '@docusaurus/theme-common'; import { isGoogleBot } from './Playground/isMobileOrBot'; import styles from './StackBlitz.module.css'; @@ -47,51 +46,50 @@ export default function StackBlitz({ const projectUrl = `https://stackblitz.com/github/reactive/${projectPath}`; const [frameRef, hasIntersected] = useHasIntersected(); - // Embedded IDEs are cramped on phones; link out instead - const isMobile = useWindowSize() === 'mobile'; + // Embedded IDEs are cramped on phones, so CSS swaps in a link-out card. + // A media query (not useWindowSize) keeps the SSR markup right on first paint; + // the hidden iframe never intersects, so it never loads on phones. return ( <> - {isMobile ? -
-

- {app ? `Explore the ${app} example` : 'Explore the example'} -

-
- - Open in StackBlitz - - - View source - -
+
+

+ {app ? `Explore the ${app} example` : 'Explore the example'} +

+
+ + Open in StackBlitz + + + View source +
- :
- - -
- } +
+
+ + +

More Demos From fd48ca1a33564002af76c006340d9fe3e16ff47a Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 4 Oct 2026 21:20:48 +0000 Subject: [PATCH 6/7] docs(website): Drop added homepage section headings Remove the 'Why Data Client' heading above the features and the 'Start with one resource and one hook' heading in the ender, restoring the ender to its original markup. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01WKtKRyLXafwpf1pNBLAuVW --- website/src/components/HomepageFeatures.tsx | 1 - website/src/pages/index.js | 5 +---- website/src/pages/index.module.css | 6 ------ 3 files changed, 1 insertion(+), 11 deletions(-) diff --git a/website/src/components/HomepageFeatures.tsx b/website/src/components/HomepageFeatures.tsx index e9f679a31af8..de3b2edfe193 100644 --- a/website/src/components/HomepageFeatures.tsx +++ b/website/src/components/HomepageFeatures.tsx @@ -79,7 +79,6 @@ export default function HomepageFeatures() { return (

-

Why Data Client

{featureList.map((props, idx) => ( diff --git a/website/src/pages/index.js b/website/src/pages/index.js index 9d770ef2565c..16d82d7bbb4b 100644 --- a/website/src/pages/index.js +++ b/website/src/pages/index.js @@ -66,11 +66,8 @@ function HomepageHeader() { function HomepageEnder() { return ( -
+
-

- Start with one resource and one hook -

Date: Sun, 4 Oct 2026 22:46:13 +0000 Subject: [PATCH 7/7] docs(website): Restore original /demos page The added header, captions and links took space away from the demos themselves. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01WKtKRyLXafwpf1pNBLAuVW --- website/src/pages/demos.module.css | 40 ----- website/src/pages/demos.tsx | 242 +++++++++++++++-------------- 2 files changed, 129 insertions(+), 153 deletions(-) delete mode 100644 website/src/pages/demos.module.css diff --git a/website/src/pages/demos.module.css b/website/src/pages/demos.module.css deleted file mode 100644 index ded75169ab93..000000000000 --- a/website/src/pages/demos.module.css +++ /dev/null @@ -1,40 +0,0 @@ -.header { - padding: 1.5rem var(--ifm-spacing-horizontal) 0.5rem; -} -.header h1 { - margin-bottom: 0.25rem; -} -.header p { - margin: 0; - color: var(--ifm-color-emphasis-700); -} - -.tabs > li { - white-space: nowrap; -} - -.caption { - display: flex; - flex-wrap: wrap; - align-items: center; - justify-content: space-between; - gap: 0.5rem 1rem; - padding: 0 var(--ifm-spacing-horizontal) 0.75rem; -} -.caption p { - margin: 0; -} - -.links { - display: flex; - flex-wrap: wrap; - gap: 0.5rem; -} - -.frame { - display: block; - border: 0; - width: 100%; - height: calc(100vh - 240px); - min-height: 500px; -} diff --git a/website/src/pages/demos.tsx b/website/src/pages/demos.tsx index 1838f5dd834a..ad91cc599cd3 100644 --- a/website/src/pages/demos.tsx +++ b/website/src/pages/demos.tsx @@ -1,133 +1,149 @@ -import Link from '@docusaurus/Link'; import Layout from '@theme/Layout'; import TabItem from '@theme/TabItem'; import Tabs from '@theme/Tabs'; import React from 'react'; -import styles from './demos.module.css'; import { searchParams } from '../utils/searchParams'; -interface Demo { - value: string; - label: string; - /** Directory under examples/ */ - app: string; - description: string; - /** Files opened in the editor */ - files: string; -} - -const demos: Demo[] = [ - { - value: 'todo', - label: 'Todo', - app: 'todo-app', - description: - 'React todo list on JSONPlaceholder with REST resources and optimistic updates.', - files: - 'src/resources/TodoResource.ts,src/pages/Home/NewTodo.tsx,src/pages/Home/TodoListItem.tsx,src/pages/Home/TodoList.tsx', - }, - { - value: 'github', - label: 'GitHub', - app: 'github-app', - description: - 'Issues and pull request browser on the live GitHub API, with pagination and nested entities.', - files: 'src/resources/Issue.tsx,src/pages/IssueList.tsx', - }, - { - value: 'nextjs', - label: 'NextJS SSR', - app: 'nextjs', - description: - 'Next.js App Router with server side rendering and client hydration of the normalized store.', - files: 'resources/TodoResource.ts,components/todo/TodoList.tsx', - }, - { - value: 'coin-app', - label: 'Live Coin Prices', - app: 'coin-app', - description: - 'Crypto prices streamed over WebSocket with a custom Manager, plus Queries that compute derived values.', - files: - 'src/resources/StreamManager.ts,src/resources/Ticker.ts,src/resources/fallbackQueries.ts,src/pages/Home/AssetPrice.tsx', - }, - { - value: 'vue-todo-app', - label: 'Vue Todo', - app: 'vue-todo-app', - description: - 'The todo app in Vue 3 with @data-client/vue composables and Vue Router.', - files: - 'src/resources/TodoResource.ts,src/resources/UserResource.ts,src/pages/UserTodos.vue,src/pages/UserList.vue', - }, -]; - -const repoPath = 'reactive/data-client/tree/master/examples'; - -function DemoPanel({ app, description, files }: Demo) { - const projectUrl = `https://stackblitz.com/github/${repoPath}/${app}`; - return ( - <> -
-

{description}

-
- - Open in StackBlitz - - - Source on GitHub - -
-
- - - ); -} - export default function DemoList() { return ( -
-

Demos

-

- Complete example apps from the{' '} - examples directory, - running live in StackBlitz. -

-
({ value, label }))} + values={[ + { label: 'Todo', value: 'todo' }, + { label: 'GitHub', value: 'github' }, + { label: 'NextJS SSR', value: 'nextjs' }, + { label: 'Live Coin Prices', value: 'coin-app' }, + { label: 'Vue Todo', value: 'vue-todo-app' }, + ]} groupId="Demos" - className={styles.tabs} > - {demos.map(demo => ( - - - - ))} + + {/*Todo + */} + + + + {/* */} + + + + {/* */} + + + + {/* */} + + + + +
);