diff --git a/packages/frontend/.storybook/.gitignore b/packages/frontend/.storybook/.gitignore
index 0de26507c6..649b36b848 100644
--- a/packages/frontend/.storybook/.gitignore
+++ b/packages/frontend/.storybook/.gitignore
@@ -1,8 +1,9 @@
-# (cd path/to/frontend; pnpm tsc --jsx react --jsxFactory h .storybook/generate.tsx && node .storybook/generate.js)
+# (cd path/to/frontend; pnpm tsc -p .storybook)
+# (cd path/to/frontend; node .storybook/generate.js)
 /generate.js
-# (cd path/to/frontend; pnpm tsc .storybook/preload-locale.ts && node .storybook/preload-locale.js)
+# (cd path/to/frontend; node .storybook/preload-locale.js)
 /preload-locale.js
 /locale.ts
-# (cd path/to/frontend; pnpm tsc .storybook/preload-theme.ts && node .storybook/preload-theme.js)
+# (cd path/to/frontend; node .storybook/preload-theme.js)
 /preload-theme.js
 /themes.ts
diff --git a/packages/frontend/.storybook/generate.tsx b/packages/frontend/.storybook/generate.tsx
index 0a4ec39108..8ce2af772c 100644
--- a/packages/frontend/.storybook/generate.tsx
+++ b/packages/frontend/.storybook/generate.tsx
@@ -4,7 +4,7 @@ import { basename, dirname } from 'node:path/posix';
 import { promisify } from 'node:util';
 import { GENERATOR, type State, generate } from 'astring';
 import type * as estree from 'estree';
-import * as glob from 'glob';
+import glob from 'glob';
 import { format } from 'prettier';
 
 interface SatisfiesExpression extends estree.BaseExpression {
@@ -16,23 +16,69 @@ interface SatisfiesExpression extends estree.BaseExpression {
 const generator = {
 	...GENERATOR,
 	SatisfiesExpression(node: SatisfiesExpression, state: State) {
-		if (node.expression.type === 'ArrowFunctionExpression') {
-			state.write('(');
-			this[node.expression.type](node.expression, state);
-			state.write(')');
-		} else {
-			this[node.expression.type](node.expression, state);
+		switch (node.expression.type) {
+			case 'ArrowFunctionExpression': {
+				state.write('(');
+				this[node.expression.type](node.expression, state);
+				state.write(')');
+				break;
+			}
+			default: {
+				// @ts-ignore
+				this[node.expression.type](node.expression, state);
+				break;
+			}
 		}
 		state.write(' satisfies ');
 		this[node.reference.type](node.reference, state);
 	},
 }
 
+type SplitCamel<T extends string, YC extends string = '', YN extends readonly string[] = []> = T extends `${infer XH}${infer XR}`
+	? XR extends ''
+		? [...YN, Uncapitalize<`${YC}${XH}`>]
+		: XH extends Uppercase<XH>
+			? SplitCamel<XR, Lowercase<XH>, [...YN, YC]>
+			: SplitCamel<XR, `${YC}${XH}`, YN>
+	: YN;
+
+// @ts-ignore
+type SplitKebab<T extends string> = T extends `${infer XH}-${infer XR}`
+	? [XH, ...SplitKebab<XR>]
+	: [T];
+
+type ToKebab<T extends readonly string[]> = T extends readonly [infer XO extends string]
+	? XO
+	: T extends readonly [infer XH extends string, ...infer XR extends readonly string[]]
+		? `${XH}${XR extends readonly string[] ? `-${ToKebab<XR>}` : ''}`
+		: '';
+
+// @ts-ignore
+type ToPascal<T extends readonly string[]> = T extends readonly [infer XH extends string, ...infer XR extends readonly string[]]
+	? `${Capitalize<XH>}${ToPascal<XR>}`
+	: '';
+
 function h<T extends estree.Node>(component: T['type'], props: Omit<T, 'type'>): T {
 	const type = component.replace(/(?:^|-)([a-z])/g, (_, c) => c.toUpperCase());
 	return Object.assign(props, { type }) as T;
 }
 
+declare global {
+	namespace JSX {
+		type Element = never;
+		type ElementClass = never;
+		type ElementAttributesProperty = never;
+		type ElementChildrenAttribute = never;
+		type IntrinsicAttributes = never;
+		type IntrinsicClassAttributes<T> = never;
+		type IntrinsicElements = {
+			[T in keyof typeof generator as ToKebab<SplitCamel<Uncapitalize<T>>>]: {
+				[K in keyof Omit<Parameters<typeof generator[T]>[0], 'type'>]?: Parameters<typeof generator[T]>[0][K];
+			};
+		};
+	}
+}
+
 function toStories(component: string): string {
 	const msw = `${component.slice(0, -'.vue'.length)}.msw`;
 	const implStories = `${component.slice(0, -'.vue'.length)}.stories.impl`;
@@ -52,14 +98,14 @@ function toStories(component: string): string {
 				<property
 					key={<identifier name="layout" />}
 					value={<literal value={`${dir}/`.startsWith('src/pages/') ? 'fullscreen' : 'centered'} />}
-					kind="init"
+					kind={"init" as const}
 				/>,
 				...hasMsw
 					? [
 							<property
 								key={<identifier name="msw" />}
 								value={<identifier name="msw" />}
-								kind="init"
+								kind={"init" as const}
 								shorthand
 							/>,
 						]
@@ -107,13 +153,12 @@ function toStories(component: string): string {
 								specifiers={[
 									<import-default-specifier
 										local={identifier}
-										imported={identifier}
 									/>,
 								]}
 							/>,
 						],
 				<variable-declaration
-					kind="const"
+					kind={"const" as const}
 					declarations={[
 						<variable-declarator
 							id={<identifier name="meta" />}
@@ -125,12 +170,12 @@ function toStories(component: string): string {
 												<property
 													key={<identifier name="title" />}
 													value={literal}
-													kind="init"
+													kind={"init" as const}
 												/>,
 												<property
 													key={<identifier name="component" />}
 													value={identifier}
-													kind="init"
+													kind={"init" as const}
 												/>,
 											]}
 										/>
@@ -148,7 +193,7 @@ function toStories(component: string): string {
 							<export-named-declaration
 								declaration={
 									<variable-declaration
-										kind="const"
+										kind={"const" as const}
 										declarations={[
 											<variable-declarator
 												id={<identifier name="Default" />}
@@ -169,7 +214,7 @@ function toStories(component: string): string {
 																							<property
 																								key={<identifier name="argTypes" />}
 																								value={<identifier name="argTypes" />}
-																								kind="init"
+																								kind={"init" as const}
 																								shorthand
 																							/>,
 																						]}
@@ -190,13 +235,13 @@ function toStories(component: string): string {
 																															<property
 																																key={identifier}
 																																value={identifier}
-																																kind="init"
+																																kind={"init" as const}
 																																shorthand
 																															/>,
 																														]}
 																													/>
 																												}
-																												kind="init"
+																												kind={"init" as const}
 																											/>,
 																											<property
 																												key={<identifier name="props" />}
@@ -213,12 +258,12 @@ function toStories(component: string): string {
 																														]}
 																													/>
 																												}
-																												kind="init"
+																												kind={"init" as const}
 																											/>,
 																											<property
 																												key={<identifier name="template" />}
 																												value={<literal value={`<${identifier.name} v-bind="$props" />`} />}
-																												kind="init"
+																												kind={"init" as const}
 																											/>,
 																										]}
 																									/>
@@ -230,12 +275,12 @@ function toStories(component: string): string {
 																			/>
 																		}
 																		method
-																		kind="init"
+																		kind={"init" as const}
 																	/>,
 																	<property
 																		key={<identifier name="parameters" />}
 																		value={parameters}
-																		kind="init"
+																		kind={"init" as const}
 																	/>,
 																]}
 															/>
diff --git a/packages/frontend/.storybook/tsconfig.json b/packages/frontend/.storybook/tsconfig.json
new file mode 100644
index 0000000000..01aa9db6eb
--- /dev/null
+++ b/packages/frontend/.storybook/tsconfig.json
@@ -0,0 +1,22 @@
+{
+	"compilerOptions": {
+		"strict": true,
+		"allowUnusedLabels": false,
+		"allowUnreachableCode": false,
+		"exactOptionalPropertyTypes": true,
+		"noFallthroughCasesInSwitch": true,
+		"noImplicitOverride": true,
+		"noImplicitReturns": true,
+		"noPropertyAccessFromIndexSignature": true,
+		"noUncheckedIndexedAccess": true,
+		"noUnusedLocals": true,
+		"noUnusedParameters": true,
+		"checkJs": true,
+		"esModuleInterop": true,
+		"skipLibCheck": true,
+		"forceConsistentCasingInFileNames": true,
+		"jsx": "react",
+		"jsxFactory": "h"
+	},
+	"files": ["./generate.tsx", "./preload-locale.ts", "./preload-theme.ts"]
+}
diff --git a/packages/frontend/tsconfig.json b/packages/frontend/tsconfig.json
index 54e5219b56..4d582daa3c 100644
--- a/packages/frontend/tsconfig.json
+++ b/packages/frontend/tsconfig.json
@@ -43,5 +43,8 @@
 		".eslintrc.js",
 		"./**/*.ts",
 		"./**/*.vue"
+	],
+	"exclude": [
+		".storybook/**/*",
 	]
 }