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
2 changes: 1 addition & 1 deletion biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"useIgnoreFile": true
},
"files": {
"includes": ["**", "!**/sst-env.d.ts", "!packages/engine/src/registry/generated"]
"includes": ["**", "!**/sst-env.d.ts", "!packages/engine/src/registry/generated", "!packages/landing/design*.html"]
},
"formatter": {
"enabled": true,
Expand Down
2 changes: 2 additions & 0 deletions lefthook.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pre-commit:
- "**/sst-env.d.ts"
- "**/generated/**"
- "openspec/changes/archive/**"
- "packages/landing/design*.html"
run: bun biome check --write --unsafe {staged_files}
stage_fixed: true
biome-lint:
Expand All @@ -25,6 +26,7 @@ pre-commit:
- "**/sst-env.d.ts"
- "**/generated/**"
- "openspec/changes/archive/**"
- "packages/landing/design*.html"
run: bun biome check --error-on-warnings {staged_files}
circleci-pack:
priority: 4
Expand Down
261 changes: 192 additions & 69 deletions packages/landing/design.html → packages/landing/design-dark.html

Large diffs are not rendered by default.

311 changes: 311 additions & 0 deletions packages/landing/design-light.html

Large diffs are not rendered by default.

8 changes: 6 additions & 2 deletions packages/landing/src/components/Nav.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useCallback, useRef, useState } from 'react'
import { useCallback, useMemo, useRef, useState } from 'react'
import { useIsMobile } from '../hooks/useIsMobile'
import { useTheme } from '../hooks/useTheme.ts'
import { BrandMark } from './BrandMark'
import { MobileMenu } from './MobileMenu'
import styles from './Nav.module.css'
Expand All @@ -14,6 +15,7 @@ import { ThemeToggle } from './ThemeToggle'
*/
export function Nav() {
const isMobile = useIsMobile()
const [theme] = useTheme()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep the design link synced with theme toggles

In desktop sessions where the visitor uses the ThemeToggle, this separate useTheme() instance in Nav never observes the child component's local state update, so designLink keeps the theme resolved at the last Nav render. The page theme changes via the toggle's own hook, but the logo href can still point at the old /design-light or /design-dark route until a reload or OS preference event, sending users to the wrong themed design page.

Useful? React with 👍 / 👎.

const [menuOpen, setMenuOpen] = useState(false)
const triggerRef = useRef<HTMLButtonElement>(null)

Expand All @@ -27,6 +29,8 @@ export function Nav() {
setMenuOpen((v) => !v)
}, [])

const designLink = useMemo(() => `/design-${theme}`, [theme])
Comment thread
eXamadeus marked this conversation as resolved.
Comment thread
eXamadeus marked this conversation as resolved.

Comment thread
coderabbitai[bot] marked this conversation as resolved.
return (
<>
<div id="top" />
Expand All @@ -45,7 +49,7 @@ export function Nav() {
Agent Facets
</button>
) : (
<a className={styles.brand} href="#top" aria-label="Agent Facets — back to top">
<a className={styles.brand} href={designLink} aria-label="Agent Facets — Design System">
<BrandMark />
Agent Facets
</a>
Expand Down
3 changes: 2 additions & 1 deletion packages/landing/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite'
import { agentPromptPlugin } from './vite/agent-prompt-plugin'
import { brandTokensPlugin } from './vite/brand-tokens-plugin'
import { designSystemPlugin } from './vite/design-system-plugin.ts'

// Inject the CLI package's version at build time so the hero eyebrow can
// display the shipped CLI version. The CLI is the user-facing artifact;
Expand All @@ -18,7 +19,7 @@ const cliPkg = JSON.parse(readFileSync(fileURLToPath(cliPkgUrl), 'utf8')) as { v
const tokensEmitPath = fileURLToPath(new URL('./src/styles/tokens.generated.css', import.meta.url))

export default defineConfig({
plugins: [react(), brandTokensPlugin({ emitPath: tokensEmitPath }), agentPromptPlugin()],
plugins: [react(), brandTokensPlugin({ emitPath: tokensEmitPath }), agentPromptPlugin(), designSystemPlugin()],
define: {
__APP_VERSION__: JSON.stringify(cliPkg.version),
},
Expand Down
53 changes: 53 additions & 0 deletions packages/landing/vite/design-system-plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { readFileSync } from 'node:fs'
import { fileURLToPath } from 'node:url'
import type { Plugin } from 'vite'

const LIGHT_DESIGN_PATH = fileURLToPath(new URL('../design-light.html', import.meta.url))
const LIGHT_SERVED_PATH = '/design-light.html'
const LIGHT_ASSET_NAME = 'design-light.html'

const DARK_DESIGN_PATH = fileURLToPath(new URL('../design-dark.html', import.meta.url))
const DARK_SERVED_PATH = '/design-dark.html'
const DARK_ASSET_NAME = 'design-dark.html'

export function designSystemPlugin(): Plugin {
return {
name: 'design-system',
// Emit the .html into the build output so the static-site upload serves
// it at the apex URL the prompt body references.
generateBundle() {
this.emitFile({
type: 'asset',
fileName: LIGHT_ASSET_NAME,
source: readFileSync(LIGHT_DESIGN_PATH, 'utf8'),
})
this.emitFile({
type: 'asset',
fileName: DARK_ASSET_NAME,
source: readFileSync(DARK_DESIGN_PATH, 'utf8'),
})
},
// Serve the same file at /design in dev so the hosted URL
// works locally and edits hot-reload through Vite's `?raw` watcher.
configureServer(server) {
server.middlewares.use((req, res, next) => {
const url = req.url?.split('?')[0]
if (url !== LIGHT_SERVED_PATH && url !== DARK_SERVED_PATH) return next()

if (url === LIGHT_SERVED_PATH) {
res.setHeader('Content-Type', 'text/html; charset=utf-8')
res.end(readFileSync(LIGHT_DESIGN_PATH, 'utf8'))
return
}

if (url === DARK_SERVED_PATH) {
res.setHeader('Content-Type', 'text/html; charset=utf-8')
res.end(readFileSync(DARK_DESIGN_PATH, 'utf8'))
return
}
Comment thread
eXamadeus marked this conversation as resolved.

throw new Error('Uh oh – design system plugin is malfunctioning!')
Comment thread
eXamadeus marked this conversation as resolved.
Comment thread
eXamadeus marked this conversation as resolved.
Comment thread
eXamadeus marked this conversation as resolved.
})
},
}
}