Files
zeekay 48f9b8faf0 Serve Zen, and stop serving Inter's bytes
The Geist->Zen rename reached the stylesheets and stopped there. Four layers
were still handing users another vendor's face.

frontend/public still carried Inter.woff2/woff, the real upstream binaries
signed RSMS, and fonts.scss declared three static Inter weights on top of the
variable face; one variable file spans 100-900, so those three collapse into it.
The Zen pair here named only Hanzo in its copyright, which OFL does not allow
for a cut of Geist — @hanzo/font's identity check reads family, vendor tag and
both notices out of the name table and refuses to publish a face that fails, so
the bytes now come from there.

common/tailwind/dist/tailwind.css is a build artifact. .gitignore names it,
`insightscli doctor` deletes it, nothing imports it (the app imports the source
tailwind.css), and a single upstream merge dragged it in. Its
--default-font-family still said MatterSQ, two renames behind the config it is
supposed to come from. Untracked rather than hand-edited.

@hanzo/quill's tokens set --font-sans behind [data-quill], a later declaration
than base.scss's :root, so every component inside a quill subtree resolved to
the token's own Inter stack instead of the app's. The token now leads with Zen,
and each package's own build regenerates its dist.

The preload lookup in build.mjs matched /Inter(-hash)?\.woff2/ and throws when
it resolves empty, so it had to move with the file. It was also warming the
second family in --font-sans rather than the first.

Two smaller ones: the region picker fetched Inter from fonts.googleapis.com on
an unauthenticated page, and quill's storybook depended on
@fontsource-variable/inter, which declares the family "Inter Variable" — a name
no stack in this repo asks for, so it downloaded and painted nothing.
2026-08-28 17:12:24 -07:00

163 lines
5.4 KiB
TypeScript

import '~/styles'
import { Controls, Description, Primary, Stories, Subtitle, Title } from '@storybook/addon-docs/blocks'
import type { Meta, Parameters, Preview } from '@storybook/react'
import { configure as configureTestingLibrary } from '@testing-library/dom'
import { apiHostOrigin } from 'lib/utils/apiHost'
import { loadInsightsJS } from '~/loadInsightsJS'
import { worker } from '~/mocks/browser'
import { defaultMocks } from '~/mocks/handlers'
import { getStorybookAppContext } from './app-context'
import { withChartCanvasSnapshot } from './decorators/withChartCanvasSnapshot'
import { withFeatureFlags } from './decorators/withFeatureFlags'
import { withKea } from './decorators/withKea'
import { withMockDate } from './decorators/withMockDate'
import { withPageUrl } from './decorators/withPageUrl'
import { withTheme } from './decorators/withTheme'
// Play functions start as soon as React commits the first render, but scene-based stories keep
// loading data for several seconds after that — longer on a busy CI runner. testing-library's
// default 1s findBy*/waitFor timeout loses that race and fails plays spuriously, so give every
// story a load-tolerant default budget instead of making each play pass its own timeout.
configureTestingLibrary({ asyncUtilTimeout: 15000 })
const setupMsw = (): void => {
// Make sure the msw worker is started
worker.start({
quiet: true,
onUnhandledRequest(request, print) {
// MSW warns on all unhandled requests, but we don't necessarily care
const pathAllowList = ['/images/']
const { pathname } = new URL(request.url)
if (pathAllowList.some((path) => pathname.startsWith(path))) {
return
}
// Otherwise, default MSW warning behavior
print.warning()
},
})
;(window as any).__mockServiceWorker = worker
;(window as any).INSIGHTS_APP_CONTEXT = getStorybookAppContext()
}
setupMsw()
const setupInsightsJs = (): void => {
// Make sure we don't hit production insights. We want to control requests to,
// e.g. `/decide/` for feature flags
window.JS_INSIGHTS_HOST = apiHostOrigin()
loadInsightsJS()
}
setupInsightsJs()
// Canvas charts measure text at paint time and never repaint when fonts finish loading, so hold
// every story's render until the app fonts (the set the visual-regression runner preloads) are in.
export const loaders: Preview['loaders'] = [
async () => {
await Promise.all(['400', '500', '700', '800'].map((weight) => document.fonts.load(`${weight} 16px Zen`)))
.then(() => document.fonts.ready)
.catch(() => undefined)
return {}
},
]
/** Storybook global parameters. See https://storybook.js.org/docs/react/writing-stories/parameters#global-parameters */
export const parameters: Parameters = {
actions: { argTypesRegex: '^on[A-Z].*', disabled: true },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
options: {
// automatically show code panel
showPanel: false,
storySort: {
method: 'alphabetical',
order: [
'Elements',
['Overview', 'Utilities', 'Icons'],
'Components',
'Forms',
['Field'],
'Filters',
'Layout',
],
},
},
viewMode: 'docs',
// auto-expand code blocks in docs
docs: {
source: { state: 'closed' },
},
msw: {
mocks: defaultMocks,
},
}
// Setup storybook global decorators. See https://storybook.js.org/docs/react/writing-stories/decorators#global-decorators
export const decorators: Meta['decorators'] = [
// Make sure the msw service worker is started, and reset the handlers to defaults.
withKea,
// Allow us to time travel to ensure our stories don't change over time.
// To mock a date for a story, set the `mockDate` parameter.
withMockDate,
// Allow us to easily set feature flags in stories.
withFeatureFlags,
// Set theme from global context
withTheme,
// Set the page URL
withPageUrl,
// Suppress quill-charts canvas painting for snapshot stories that opt in via testOptions.skipCanvasDraw
withChartCanvasSnapshot,
]
const preview: Preview = {
parameters: {
actions: { argTypesRegex: '^on[A-Z].*' },
// We don't want axe-core tests in visual review
a11y: { test: 'off' },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
docs: {
page: () => (
<>
<Title />
<Subtitle />
<Description />
<Primary />
<Controls />
<Stories />
</>
),
},
},
globalTypes: {
theme: {
description: '',
defaultValue: 'light',
toolbar: {
title: 'Theme',
items: [
{ value: 'light', icon: 'sun', title: 'Light' },
{ value: 'dark', icon: 'moon', title: 'Dark' },
],
// change the title based on the selected value
dynamicTitle: true,
},
},
},
}
export default preview