diff --git a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/Errors/error-overlay-floating-header/error-overlay-floating-header.tsx b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/Errors/error-overlay-floating-header/error-overlay-floating-header.tsx
index 99074949000f3..4124bf0a4322c 100644
--- a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/Errors/error-overlay-floating-header/error-overlay-floating-header.tsx
+++ b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/Errors/error-overlay-floating-header/error-overlay-floating-header.tsx
@@ -2,7 +2,7 @@ import type { ReadyRuntimeError } from '../../../helpers/get-error-by-type'
import type { VersionInfo } from '../../../../../../../../server/dev/parse-version-info'
import { ErrorOverlayPagination } from '../error-overlay-pagination/error-overlay-pagination'
-import { VersionStalenessInfo } from '../../VersionStalenessInfo'
+import { VersionStalenessInfo } from '../../VersionStalenessInfo/VersionStalenessInfo'
import { noop as css } from '../../../helpers/noop-template'
type ErrorOverlayFloatingHeaderProps = {
diff --git a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/VersionStalenessInfo/VersionStalenessInfo.tsx b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/VersionStalenessInfo/VersionStalenessInfo.tsx
index 03787cb8ca35d..17032c0d1fd87 100644
--- a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/VersionStalenessInfo/VersionStalenessInfo.tsx
+++ b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/VersionStalenessInfo/VersionStalenessInfo.tsx
@@ -1,9 +1,12 @@
import type { VersionInfo } from '../../../../../../../server/dev/parse-version-info'
+import { noop as css } from '../../helpers/noop-template'
export function VersionStalenessInfo({
versionInfo,
+ isTurbopack = !!process.env.TURBOPACK,
}: {
versionInfo: VersionInfo | undefined
+ isTurbopack?: boolean
}) {
if (!versionInfo) return null
const { staleness } = versionInfo
@@ -12,11 +15,11 @@ export function VersionStalenessInfo({
if (!text) return null
return (
-
-
-
+
+
+
{text}
- {' '}
+ {' '}
{staleness === 'fresh' ||
staleness === 'newer-than-npm' ||
staleness === 'unknown' ? null : (
@@ -28,7 +31,7 @@ export function VersionStalenessInfo({
(learn more)
)}
- {process.env.TURBOPACK ? ' (Turbopack)' : ''}
+ {isTurbopack && Turbopack}
)
}
@@ -37,7 +40,7 @@ export function getStaleness({ installed, staleness, expected }: VersionInfo) {
let text = ''
let title = ''
let indicatorClass = ''
- const versionLabel = `Next.js (${installed})`
+ const versionLabel = `Next.js ${installed}`
switch (staleness) {
case 'newer-than-npm':
case 'fresh':
@@ -47,18 +50,18 @@ export function getStaleness({ installed, staleness, expected }: VersionInfo) {
break
case 'stale-patch':
case 'stale-minor':
- text = `${versionLabel} out of date`
+ text = `${versionLabel} (stale)`
title = `There is a newer version (${expected}) available, upgrade recommended! `
indicatorClass = 'stale'
break
case 'stale-major': {
- text = `${versionLabel} is outdated`
+ text = `${versionLabel} (outdated)`
title = `An outdated version detected (latest is ${expected}), upgrade is highly recommended!`
indicatorClass = 'outdated'
break
}
case 'stale-prerelease': {
- text = `${versionLabel} is outdated`
+ text = `${versionLabel} (stale)`
title = `There is a newer canary version (${expected}) available, please upgrade! `
indicatorClass = 'stale'
break
@@ -70,3 +73,64 @@ export function getStaleness({ installed, staleness, expected }: VersionInfo) {
}
return { text, indicatorClass, title }
}
+
+export const styles = css`
+ .nextjs-container-build-error-version-status {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ gap: var(--size-1);
+
+ padding: var(--size-1_5) var(--size-2);
+ background: var(--color-background-100);
+ box-shadow: var(--shadow-sm);
+
+ border: 1px solid var(--color-gray-400);
+ border-radius: var(--rounded-full);
+
+ color: var(--color-gray-900);
+ font-size: var(--size-font-11);
+ font-weight: 500;
+ line-height: var(--size-4);
+ }
+
+ .version-staleness-indicator.fresh {
+ fill: var(--color-green-800);
+ stroke: var(--color-green-300);
+ }
+ .version-staleness-indicator.stale {
+ fill: var(--color-amber-800);
+ stroke: var(--color-amber-300);
+ }
+ .version-staleness-indicator.outdated {
+ fill: var(--color-red-800);
+ stroke: var(--color-red-300);
+ }
+
+ .turbopack-text {
+ background: linear-gradient(280deg, #0096ff 0%, #ff1e56 100%);
+ background-clip: text;
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ }
+
+ @media (prefers-color-scheme: dark) {
+ .turbopack-text {
+ background: linear-gradient(280deg, #45b2ff 0%, #ff6d92 100%);
+ }
+ }
+`
+
+function Eclipse({ className }: { className: string }) {
+ return (
+
+ )
+}
diff --git a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/VersionStalenessInfo/index.tsx b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/VersionStalenessInfo/index.tsx
deleted file mode 100644
index 51b4b085355b7..0000000000000
--- a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/VersionStalenessInfo/index.tsx
+++ /dev/null
@@ -1,2 +0,0 @@
-export { styles } from './styles'
-export { VersionStalenessInfo } from './VersionStalenessInfo'
diff --git a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/VersionStalenessInfo/styles.ts b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/VersionStalenessInfo/styles.ts
deleted file mode 100644
index d18218051bf13..0000000000000
--- a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/VersionStalenessInfo/styles.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { noop as css } from '../../helpers/noop-template'
-
-const styles = css`
- .nextjs-container-build-error-version-status {
- flex: 1;
- text-align: right;
- font-size: var(--size-font-small);
- }
- .nextjs-container-build-error-version-status span {
- display: inline-block;
- width: 10px;
- height: 10px;
- border-radius: 5px;
- background: var(--color-ansi-bright-black);
- }
- .nextjs-container-build-error-version-status span.fresh {
- background: var(--color-ansi-green);
- }
- .nextjs-container-build-error-version-status span.stale {
- background: var(--color-ansi-yellow);
- }
- .nextjs-container-build-error-version-status span.outdated {
- background: var(--color-ansi-red);
- }
-`
-
-export { styles }
diff --git a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/VersionStalenessInfo/version-staleness-info.stories.tsx b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/VersionStalenessInfo/version-staleness-info.stories.tsx
new file mode 100644
index 0000000000000..1b4391dc67400
--- /dev/null
+++ b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/VersionStalenessInfo/version-staleness-info.stories.tsx
@@ -0,0 +1,94 @@
+import type { Meta, StoryObj } from '@storybook/react'
+import { VersionStalenessInfo } from './VersionStalenessInfo'
+import { withShadowPortal } from '../../storybook/with-shadow-portal'
+
+const meta: Meta = {
+ title: 'VersionStalenessInfo',
+ component: VersionStalenessInfo,
+ parameters: {
+ layout: 'centered',
+ },
+ decorators: [withShadowPortal],
+}
+
+export default meta
+type Story = StoryObj
+
+// Mock version info for different scenarios
+const mockVersionInfo = {
+ fresh: {
+ installed: '15.0.0',
+ expected: '15.0.0',
+ staleness: 'fresh' as const,
+ },
+ stalePatch: {
+ installed: '15.0.0',
+ expected: '15.0.1',
+ staleness: 'stale-patch' as const,
+ },
+ staleMinor: {
+ installed: '15.0.0',
+ expected: '15.1.0',
+ staleness: 'stale-minor' as const,
+ },
+ staleMajor: {
+ installed: '14.0.0',
+ expected: '15.0.0',
+ staleness: 'stale-major' as const,
+ },
+ stalePrerelease: {
+ installed: '15.0.0-canary.0',
+ expected: '15.0.0-canary.1',
+ staleness: 'stale-prerelease' as const,
+ },
+ newerThanNpm: {
+ installed: '15.0.0-canary.1',
+ expected: '15.0.0-canary.0',
+ staleness: 'newer-than-npm' as const,
+ },
+}
+
+export const Fresh: Story = {
+ args: {
+ versionInfo: mockVersionInfo.fresh,
+ },
+}
+
+export const StalePatch: Story = {
+ args: {
+ versionInfo: mockVersionInfo.stalePatch,
+ },
+}
+
+export const StaleMinor: Story = {
+ args: {
+ versionInfo: mockVersionInfo.staleMinor,
+ },
+}
+
+export const StaleMajor: Story = {
+ args: {
+ versionInfo: mockVersionInfo.staleMajor,
+ },
+}
+
+export const StalePrerelease: Story = {
+ args: {
+ versionInfo: mockVersionInfo.stalePrerelease,
+ },
+}
+
+export const NewerThanNpm: Story = {
+ args: {
+ versionInfo: mockVersionInfo.newerThanNpm,
+ },
+}
+
+export const Turbopack: Story = {
+ args: {
+ versionInfo: {
+ ...mockVersionInfo.fresh,
+ },
+ isTurbopack: true,
+ },
+}
diff --git a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/styles/Base.tsx b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/styles/Base.tsx
index 329326bed98fd..28062f96a8d7c 100644
--- a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/styles/Base.tsx
+++ b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/styles/Base.tsx
@@ -14,6 +14,8 @@ export function Base() {
--size-gap-triple: 24px;
--size-gap-quad: 32px;
+ --size-font-11: 11px;
+ --size-font-smaller: 12px;
--size-font-small: 14px;
--size-font: 16px;
--size-font-big: 20px;
diff --git a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/styles/ComponentStyles.tsx b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/styles/ComponentStyles.tsx
index 035f4e3e37aea..5cde9df7126d5 100644
--- a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/styles/ComponentStyles.tsx
+++ b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/styles/ComponentStyles.tsx
@@ -7,7 +7,7 @@ import { styles as overlay } from '../components/Overlay/styles'
import { styles as footer } from '../components/Errors/error-overlay-footer/styles'
import { styles as terminal } from '../components/Terminal/styles'
import { styles as toast } from '../components/Toast'
-import { styles as versionStaleness } from '../components/VersionStalenessInfo'
+import { styles as versionStaleness } from '../components/VersionStalenessInfo/VersionStalenessInfo'
import { styles as buildErrorStyles } from '../container/BuildError'
import { styles as containerErrorStyles } from '../container/Errors'
import { styles as containerRuntimeErrorStyles } from '../container/RuntimeError'
diff --git a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/styles/colors.tsx b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/styles/colors.tsx
index 3d32b3c99917c..90f7236c5a8a0 100644
--- a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/styles/colors.tsx
+++ b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/styles/colors.tsx
@@ -6,11 +6,11 @@ export function Colors() {