diff --git a/frontend/ui/storybook/package.json b/frontend/ui/storybook/package.json
new file mode 100644
index 0000000..7a8824c
--- /dev/null
+++ b/frontend/ui/storybook/package.json
@@ -0,0 +1,32 @@
+{
+ "name": "@frontend/ui-storybook",
+ "version": "0.0.1",
+ "private": true,
+ "license": "BSD-3-Clause",
+ "type": "module",
+ "scripts": {
+ "build": "storybook build --config-dir ./src",
+ "dev": "storybook dev --config-dir ./src -p 3000"
+ },
+ "devDependencies": {
+ "@storybook/addon-styling-webpack": "3.0.0",
+ "@storybook/addon-webpack5-compiler-swc": "4.0.2",
+ "@storybook/nextjs": "10.0.7",
+ "@storybook/react-webpack5": "10.0.7",
+ "@types/node": "22.19.3",
+ "@types/react": "19.2.2",
+ "@types/react-dom": "19.2.2",
+ "@vanilla-extract/css": "1.17.4",
+ "@vanilla-extract/dynamic": "2.1.5",
+ "@vanilla-extract/webpack-plugin": "2.3.22",
+ "css-loader": "7.1.2",
+ "mini-css-extract-plugin": "2.9.2",
+ "next": "16.1.6",
+ "react": "19.2.4",
+ "react-dom": "19.2.4",
+ "storybook": "10.0.7",
+ "style-loader": "4.0.0",
+ "typescript": "5.5.4",
+ "webpack": "5.100.2"
+ }
+}
diff --git a/frontend/ui/storybook/src/main.ts b/frontend/ui/storybook/src/main.ts
new file mode 100644
index 0000000..a0e3717
--- /dev/null
+++ b/frontend/ui/storybook/src/main.ts
@@ -0,0 +1,59 @@
+import type { StorybookConfig } from '@storybook/nextjs'
+
+import { VanillaExtractPlugin } from '@vanilla-extract/webpack-plugin'
+import MiniCssExtractPlugin from 'mini-css-extract-plugin'
+
+const config: StorybookConfig = {
+ stories: ['../../**/stories/*.stories.@(js|jsx|mjs|ts|tsx|mdx)'],
+ staticDirs: ['../../theme/assets'],
+ addons: [
+ '@storybook/addon-webpack5-compiler-swc',
+ {
+ name: '@storybook/addon-styling-webpack',
+ options: {
+ plugins: [new VanillaExtractPlugin({ identifiers: 'short' }), new MiniCssExtractPlugin()],
+ rules: [
+ {
+ test: /\.css$/,
+ sideEffects: true,
+ use: ['style-loader', { loader: 'css-loader', options: {} }],
+ exclude: /\.vanilla\.css$/,
+ },
+ {
+ test: /\.vanilla\.css$/i,
+ sideEffects: true,
+ use: [MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { url: false } }],
+ },
+ ],
+ },
+ },
+ ],
+ env: (envConfig) => ({
+ ...envConfig,
+ IS_STORYBOOK: 'true',
+ }),
+ framework: {
+ name: '@storybook/nextjs',
+ options: {},
+ },
+ webpackFinal: async (webpackConfig) => {
+ webpackConfig.resolve = webpackConfig.resolve || {}
+ webpackConfig.resolve.extensionAlias = {
+ '.js': ['.ts', '.tsx', '.js', '.jsx'],
+ '.mjs': ['.mts', '.mjs'],
+ '.cjs': ['.cts', '.cjs'],
+ }
+
+ if (webpackConfig.resolve.fallback) {
+ webpackConfig.resolve.fallback = {
+ ...webpackConfig.resolve.fallback,
+ assert: false,
+ url: false,
+ }
+ }
+
+ return webpackConfig
+ },
+}
+
+export default config
diff --git a/frontend/ui/storybook/src/preview.tsx b/frontend/ui/storybook/src/preview.tsx
new file mode 100644
index 0000000..3eeda68
--- /dev/null
+++ b/frontend/ui/storybook/src/preview.tsx
@@ -0,0 +1,23 @@
+import type { Preview } from '@storybook/nextjs'
+
+const preview: Preview = {
+ parameters: {
+ layout: 'fullscreen',
+ controls: {
+ matchers: {
+ color: /(background|color)$/i,
+ date: /Date$/i,
+ },
+ },
+ backgrounds: {
+ options: {
+ light: { name: 'Light', value: 'rgba(235, 235, 235, 1)' },
+ },
+ },
+ },
+ initialGlobals: {
+ backgrounds: { value: 'light' },
+ },
+}
+
+export default preview
diff --git a/frontend/ui/theme/src/fonts/sf-pro-display.tsx b/frontend/ui/theme/src/fonts/sf-pro-display.tsx
index b4acf27..e22d5e9 100644
--- a/frontend/ui/theme/src/fonts/sf-pro-display.tsx
+++ b/frontend/ui/theme/src/fonts/sf-pro-display.tsx
@@ -1,18 +1,31 @@
-import localFont from 'next/font/local'
+///
-export const sfProDisplayFont = localFont({
+import type { ReactNode } from 'react'
+
+import localFont from 'next/font/local'
+
+import { fontsVars } from '../constants/index.js'
+
+const sfProDisplayLocal = localFont({
src: [
- {
- path: '../../assets/fonts/sf-pro-display/SF-Pro-Display-Regular.ttf',
- weight: '400',
- style: 'normal',
- },
- {
- path: '../../assets/fonts/sf-pro-display/SF-Pro-Display-Semibold.ttf',
- weight: '600',
- style: 'normal',
- },
+ { path: '../../assets/fonts/sf-pro-display/SF-Pro-Display-Regular.ttf', weight: '400' },
+ { path: '../../assets/fonts/sf-pro-display/SF-Pro-Display-Semibold.ttf', weight: '600' },
],
display: 'swap',
- variable: '--font-sf-pro-display',
})
+
+const sfProDisplayFallback = {
+ style: {
+ fontFamily: `-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif`,
+ },
+}
+
+const sfProDisplay = process.env.IS_STORYBOOK === 'true' ? sfProDisplayFallback : sfProDisplayLocal
+
+export const SfProDisplayFont = (): ReactNode => (
+
+)
diff --git a/frontend/ui/theme/src/semantic/typography.ts b/frontend/ui/theme/src/semantic/typography.ts
index 7600c4a..6578b61 100644
--- a/frontend/ui/theme/src/semantic/typography.ts
+++ b/frontend/ui/theme/src/semantic/typography.ts
@@ -67,18 +67,36 @@ export const typography = {
lineHeight: lineHeights.xl,
fontWeight: fontWeights.regular,
},
+ bodyLargeSemibold: {
+ fontFamily: fonts.primary,
+ fontSize: fontSizes.xl,
+ lineHeight: lineHeights.xl,
+ fontWeight: fontWeights.semibold,
+ },
bodyMedium: {
fontFamily: fonts.primary,
fontSize: fontSizes.lg,
lineHeight: lineHeights.lg,
fontWeight: fontWeights.regular,
},
+ bodyMediumSemibold: {
+ fontFamily: fonts.primary,
+ fontSize: fontSizes.lg,
+ lineHeight: lineHeights.lg,
+ fontWeight: fontWeights.semibold,
+ },
bodySmall: {
fontFamily: fonts.primary,
fontSize: fontSizes.md,
lineHeight: lineHeights.md,
fontWeight: fontWeights.regular,
},
+ bodySmallSemibold: {
+ fontFamily: fonts.primary,
+ fontSize: fontSizes.md,
+ lineHeight: lineHeights.md,
+ fontWeight: fontWeights.semibold,
+ },
captionSmall: {
fontFamily: fonts.primary,
@@ -86,6 +104,12 @@ export const typography = {
lineHeight: lineHeights.sm,
fontWeight: fontWeights.regular,
},
+ captionSmallSemibold: {
+ fontFamily: fonts.primary,
+ fontSize: fontSizes.sm,
+ lineHeight: lineHeights.sm,
+ fontWeight: fontWeights.semibold,
+ },
labelSmall: {
fontFamily: fonts.primary,
@@ -93,4 +117,10 @@ export const typography = {
lineHeight: lineHeights.xs,
fontWeight: fontWeights.regular,
},
+ labelSmallSemibold: {
+ fontFamily: fonts.primary,
+ fontSize: fontSizes.xs,
+ lineHeight: lineHeights.xs,
+ fontWeight: fontWeights.semibold,
+ },
} as const
diff --git a/frontend/ui/theme/src/store/provider.tsx b/frontend/ui/theme/src/store/provider.tsx
index af9a556..eef3248 100644
--- a/frontend/ui/theme/src/store/provider.tsx
+++ b/frontend/ui/theme/src/store/provider.tsx
@@ -6,6 +6,7 @@ import { ThemeProvider as NextThemeProvider } from 'next-themes'
import { ThemeEnum } from '../constants/index.js'
import { THEME_KEY } from '../constants/index.js'
+import { SfProDisplayFont } from '../fonts/index.js'
import { UseTheme } from './use-theme.js'
import { darkTheme } from '../theme.css.js'
import { lightTheme } from '../theme.css.js'
@@ -23,6 +24,7 @@ export const ThemeProvider = ({ children, forcedTheme }: ThemeProviderProps) =>
}}
>
+
{children}
)
diff --git a/frontend/ui/theme/src/tokens/dark/colors/colors.ts b/frontend/ui/theme/src/tokens/dark/colors/colors.ts
index 282d2af..264f42f 100644
--- a/frontend/ui/theme/src/tokens/dark/colors/colors.ts
+++ b/frontend/ui/theme/src/tokens/dark/colors/colors.ts
@@ -12,6 +12,14 @@ export const primary = {
'primary.heavy': 'rgba(153, 155, 249, 1)',
}
+export const background = {
+ 'background.main': 'rgba(255, 255, 255, 0.03)',
+ 'background.hover': 'rgba(255, 255, 255, 0.01)',
+ 'background.tooltip': 'rgba(0, 0, 0, 0.85)',
+ 'background.border': 'rgba(255, 255, 255, 0.05)',
+ 'background.border-hover': 'rgba(255, 255, 255, 0.25)',
+}
+
export const accent = {
'accent.info-light': 'rgba(143, 82, 204, 0.3)',
'accent.info': 'rgba(199, 169, 230, 1)',
diff --git a/frontend/ui/theme/src/tokens/dark/index.ts b/frontend/ui/theme/src/tokens/dark/index.ts
index 222dec8..84df06e 100644
--- a/frontend/ui/theme/src/tokens/dark/index.ts
+++ b/frontend/ui/theme/src/tokens/dark/index.ts
@@ -6,6 +6,7 @@ import { layoutThemeTokens } from '../base/layout/index.js'
export const darkColors = {
...colors.main,
...colors.primary,
+ ...colors.background,
...colors.accent,
...colors.contrast,
...colors.contrastInverted,
diff --git a/frontend/ui/theme/src/tokens/light/colors/colors.ts b/frontend/ui/theme/src/tokens/light/colors/colors.ts
index 5e179da..edfceeb 100644
--- a/frontend/ui/theme/src/tokens/light/colors/colors.ts
+++ b/frontend/ui/theme/src/tokens/light/colors/colors.ts
@@ -12,6 +12,14 @@ export const primary = {
'primary.heavy': 'rgba(15, 17, 161, 1)',
}
+export const background = {
+ 'background.main': 'rgba(255, 255, 255, 0.15)',
+ 'background.hover': 'rgba(0, 0, 0, 0.05)',
+ 'background.tooltip': 'rgba(51, 51, 51, 1)',
+ 'background.border': 'rgba(0, 0, 0, 0.05)',
+ 'background.border-hover': 'rgba(0, 0, 0, 0.15)',
+}
+
export const accent = {
'accent.info-light': 'rgba(143, 82, 204, 0.15)',
'accent.info': 'rgba(110, 66, 153, 1)',
diff --git a/frontend/ui/theme/src/tokens/light/index.ts b/frontend/ui/theme/src/tokens/light/index.ts
index bbddccd..6fbb3d2 100644
--- a/frontend/ui/theme/src/tokens/light/index.ts
+++ b/frontend/ui/theme/src/tokens/light/index.ts
@@ -6,6 +6,7 @@ import { layoutThemeTokens } from '../base/layout/index.js'
export const lightColors = {
...colors.main,
...colors.primary,
+ ...colors.background,
...colors.accent,
...colors.contrast,
...colors.contrastInverted,