Skip to content
Open
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
5 changes: 4 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,7 @@ pnpm-lock.yaml
/storybook-static/

# Golden snapshots are written verbatim; prettier would drift them out of sync.
**/__golden__/
**/__golden__/

# Generated VS Code theme dump; minified single line by design.
.storybook/themes/generated/themes.json
25 changes: 25 additions & 0 deletions .storybook/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Storybook

Stories render inside an environment that mirrors a real VS Code webview.

## Themes

`themes/generated/themes.json` holds the exact `--vscode-*` variables a real
VS Code instance injects per built-in theme, and `default-styles.css` is the
stylesheet VS Code injects into every webview. `preview.ts` applies both, plus
the `data-vscode-theme-kind` body attribute.

To refresh the snapshots after a VS Code release, bump `VSCODE_VERSION` in
`themes/sync.mjs` and run:

```sh
xvfb-run -a pnpm sync:vscode-themes
```

The script launches the pinned VS Code via `@vscode/test-electron`, cycles
the four built-in themes, and dumps what each webview receives.

## Fonts

VS Code uses the host OS font, so the dumped `--vscode-font-family` resolves
per machine; rendering inside actual VS Code is always exact.
102 changes: 7 additions & 95 deletions .storybook/global.css
Original file line number Diff line number Diff line change
@@ -1,108 +1,20 @@
html {
/* Storybook stand-ins for what the VS Code host provides outside the
webview document: the sidebar surface color and viewport sizing.
Typography and text color come from themes/generated/default-styles.css. */

html,
#storybook-preview-wrapper {
background: var(--vscode-sideBar-background);
}

body {
margin: 0;
/* Real webviews pad the body 0 20px; stories frame themselves. */
padding: 0;
overflow: hidden;
}

#storybook-preview-wrapper {
background-color: var(--vscode-sideBar-background);
}

#root {
overscroll-behavior-x: none;
background-color: var(--vscode-sideBar-background);
color: var(--vscode-editor-foreground);
font-family: var(--vscode-font-family);
font-weight: var(--vscode-font-weight);
font-size: var(--vscode-font-size);
margin: 0;
max-width: 100%;
/* arbitrary size choice for the rough VSCode sidebar size */
width: 300px;
}

img,
video {
max-width: 100%;
max-height: 100%;
}

a,
a code {
color: var(--vscode-textLink-foreground);
}

p > a {
text-decoration: var(--text-link-decoration);
}

a:hover {
color: var(--vscode-textLink-activeForeground);
}

a:focus,
input:focus,
select:focus,
textarea:focus {
outline: 1px solid -webkit-focus-ring-color;
outline-offset: -1px;
}

code {
font-family: var(--monaco-monospace-font);
color: var(--vscode-textPreformat-foreground);
background-color: var(--vscode-textPreformat-background);
padding: 1px 3px;
border-radius: 4px;
}

pre code {
padding: 0;
}

blockquote {
background: var(--vscode-textBlockQuote-background);
border-color: var(--vscode-textBlockQuote-border);
}

kbd {
background-color: var(--vscode-keybindingLabel-background);
color: var(--vscode-keybindingLabel-foreground);
border-style: solid;
border-width: 1px;
border-radius: 3px;
border-color: var(--vscode-keybindingLabel-border);
border-bottom-color: var(--vscode-keybindingLabel-bottomBorder);
box-shadow: inset 0 -1px 0 var(--vscode-widget-shadow);
vertical-align: middle;
padding: 1px 3px;
}

::-webkit-scrollbar {
width: 10px;
height: 10px;
}

::-webkit-scrollbar-corner {
background-color: var(--vscode-editor-background);
}

::-webkit-scrollbar-thumb {
background-color: var(--vscode-scrollbarSlider-background);
}
::-webkit-scrollbar-thumb:hover {
background-color: var(--vscode-scrollbarSlider-hoverBackground);
}
::-webkit-scrollbar-thumb:active {
background-color: var(--vscode-scrollbarSlider-activeBackground);
}
::highlight(find-highlight) {
background-color: var(--vscode-editor-findMatchHighlightBackground);
}
::highlight(current-find-highlight) {
background-color: var(--vscode-editor-findMatchBackground);
}
4 changes: 0 additions & 4 deletions .storybook/playground-themes.d.ts

This file was deleted.

35 changes: 15 additions & 20 deletions .storybook/preview.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
/// <reference types="vite/client" />

import codiconCssUrl from "@vscode/codicons/dist/codicon.css?url";
import { theme as darkTheme } from "@vscode-elements/webview-playground/dist/themes/dark-v2.js";
import { theme as hcDarkTheme } from "@vscode-elements/webview-playground/dist/themes/hc-dark.js";
import { theme as hcLightTheme } from "@vscode-elements/webview-playground/dist/themes/hc-light.js";
import { theme as lightTheme } from "@vscode-elements/webview-playground/dist/themes/light-v2.js";
import { createElement, useEffect } from "react";

import "./global.css";
import "./themes/generated/default-styles.css";
import themeDumps from "./themes/generated/themes.json";

import type { Preview } from "@storybook/react-vite";
import type { WebviewApi } from "vscode-webview";
Expand Down Expand Up @@ -44,22 +42,21 @@ if (
document.head.appendChild(link);
}

// VS Code injects --vscode-font-family at runtime, but the upstream
// vscode-elements theme data omits it. Set a static default so
// Storybook (and Chromatic) renders with a predictable sans-serif
// stack instead of falling back to the browser default (Times).
const VSCODE_FONT_FAMILY =
'"Segoe WPC", "Segoe UI", system-ui, "Ubuntu", "Droid Sans", sans-serif';

const THEMES: Record<
string,
{ variables: Array<[string, string]>; kind: string }
/**
* Theme variable dumps captured from a real VS Code instance.
* Regenerate with `pnpm sync:vscode-themes`.
*/
const THEMES: Readonly<
Record<string, { variables: readonly string[][]; kind: string }>
> = {
light: { variables: lightTheme, kind: "vscode-light" },
dark: { variables: darkTheme, kind: "vscode-dark" },
"high-contrast": { variables: hcDarkTheme, kind: "vscode-high-contrast" },
light: { variables: themeDumps.themes.light, kind: "vscode-light" },
dark: { variables: themeDumps.themes.dark, kind: "vscode-dark" },
"high-contrast": {
variables: themeDumps.themes["high-contrast"],
kind: "vscode-high-contrast",
},
"high-contrast-light": {
variables: hcLightTheme,
variables: themeDumps.themes["high-contrast-light"],
kind: "vscode-high-contrast-light",
},
};
Expand Down Expand Up @@ -100,7 +97,6 @@ const preview: Preview = {

useEffect(() => {
const root = document.documentElement.style;
root.setProperty("--vscode-font-family", VSCODE_FONT_FAMILY);

// Mirror VS Code's body attribute so theme-aware hooks work in Storybook.
document.body.setAttribute("data-vscode-theme-kind", kind);
Expand All @@ -115,7 +111,6 @@ const preview: Preview = {
variables.forEach(([property]) => {
root.removeProperty(property);
});
root.removeProperty("--vscode-font-family");
document.body.removeAttribute("data-vscode-theme-kind");
};
}, [variables, kind]);
Expand Down
100 changes: 100 additions & 0 deletions .storybook/themes/generated/default-styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/* Generated by pnpm sync:vscode-themes from VS Code 1.128.0. Do not edit by hand. */
@layer vscode-default {
html {
scrollbar-color: var(--vscode-scrollbarSlider-background)
var(--vscode-editor-background);
}

body {
overscroll-behavior-x: none;
background-color: transparent;
color: var(--vscode-editor-foreground);
font-family: var(--vscode-font-family);
font-weight: var(--vscode-font-weight);
font-size: var(--vscode-font-size);
margin: 0;
padding: 0 20px;
}

img,
video {
max-width: 100%;
max-height: 100%;
}

a,
a code {
color: var(--vscode-textLink-foreground);
}

p > a {
text-decoration: var(--text-link-decoration);
}

a:hover {
color: var(--vscode-textLink-activeForeground);
}

a:focus,
input:focus,
select:focus,
textarea:focus {
outline: 1px solid -webkit-focus-ring-color;
outline-offset: -1px;
}

code {
font-family: var(--monaco-monospace-font);
color: var(--vscode-textPreformat-foreground);
background-color: var(--vscode-textPreformat-background);
padding: 1px 3px;
border-radius: 4px;
}

pre code {
padding: 0;
}

blockquote {
background: var(--vscode-textBlockQuote-background);
border-color: var(--vscode-textBlockQuote-border);
}

kbd {
background-color: var(--vscode-keybindingLabel-background);
color: var(--vscode-keybindingLabel-foreground);
border-style: solid;
border-width: 1px;
border-radius: 3px;
border-color: var(--vscode-keybindingLabel-border);
border-bottom-color: var(--vscode-keybindingLabel-bottomBorder);
box-shadow: inset 0 -1px 0 var(--vscode-widget-shadow);
vertical-align: middle;
padding: 1px 3px;
}

::-webkit-scrollbar {
width: 10px;
height: 10px;
}

::-webkit-scrollbar-corner {
background-color: var(--vscode-editor-background);
}

::-webkit-scrollbar-thumb {
background-color: var(--vscode-scrollbarSlider-background);
}
::-webkit-scrollbar-thumb:hover {
background-color: var(--vscode-scrollbarSlider-hoverBackground);
}
::-webkit-scrollbar-thumb:active {
background-color: var(--vscode-scrollbarSlider-activeBackground);
}
::highlight(find-highlight) {
background-color: var(--vscode-editor-findMatchHighlightBackground);
}
::highlight(current-find-highlight) {
background-color: var(--vscode-editor-findMatchBackground);
}
}
1 change: 1 addition & 0 deletions .storybook/themes/generated/themes.json

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions .storybook/themes/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "theme-sync-fixture",
"description": "Fixture extension loaded by sync.mjs (pnpm sync:vscode-themes)",
"publisher": "coder",
"version": "0.0.1",
"private": true,
"type": "commonjs",
"engines": {
"vscode": "^1.105.0"
}
}
Loading