diff --git a/.storybook/main.ts b/.storybook/main.ts
index 14b9305e21..e9a012df18 100644
--- a/.storybook/main.ts
+++ b/.storybook/main.ts
@@ -1,7 +1,7 @@
import type { StorybookConfig } from '@storybook-vue/nuxt'
const config = {
- stories: ['../.storybook/*.mdx', '../app/**/*.stories.@(js|ts)'],
+ stories: ['../.storybook/*.mdx', '../app/**/*.@(mdx|stories.@(js|ts))'],
addons: [
'@storybook/addon-a11y',
'@storybook/addon-docs',
diff --git a/app/colors.mdx b/app/colors.mdx
new file mode 100644
index 0000000000..13822c0db5
--- /dev/null
+++ b/app/colors.mdx
@@ -0,0 +1,110 @@
+import { Meta, ColorPalette, ColorItem } from '@storybook/addon-docs/blocks'
+import { theme } from '../uno.theme.ts'
+
+
+
+# Colors
+
+The npmx color system is built on CSS custom properties with support for light/dark themes and multiple background variants.
+
+Color values are defined in [uno.theme.ts](../uno.theme.ts).
+
+## Background Colors
+
+
+ ({
+ ...acc,
+ [key === 'DEFAULT' ? 'bg' : `bg-${key}`]: value,
+ }),
+ {},
+ )}
+ />
+
+
+## Foreground Colors
+
+
+ ({
+ ...acc,
+ [key === 'DEFAULT' ? 'fg' : `fg-${key}`]: value,
+ }),
+ {},
+ )}
+ />
+
+
+## Border Colors
+
+
+ ({
+ ...acc,
+ [key === 'DEFAULT' ? 'border' : `border-${key}`]: value,
+ }),
+ {},
+ )}
+ />
+
+
+## Accent Colors
+
+
+ ({
+ ...acc,
+ [key === 'DEFAULT' ? 'accent' : `accent-${key}`]: value,
+ }),
+ {},
+ )}
+ />
+
+
+## Syntax Highlighting
+
+
+
+
+
+## Badge Colors
+
+
+
+
+
+## Provider Brand Colors
+
+
+
+
+
+---
+
+## Related
+
+- [Design System Guidelines](./?path=/docs/guidelines--docs)
diff --git a/app/design-system-guidelines.mdx b/app/design-system-guidelines.mdx
new file mode 100644
index 0000000000..3bcdceb9bd
--- /dev/null
+++ b/app/design-system-guidelines.mdx
@@ -0,0 +1,86 @@
+import { Meta } from '@storybook/addon-docs/blocks'
+
+
+
+# Guidelines
+
+## Containers
+
+We use three container types depending on content density.
+
+**Main Container** is the default layout container. It should be used for pages with minimal or non-text content (e.g. graphs, dashboards).
+
+**Content Container** is used for text-heavy layouts. All long-form content must be placed inside this container. The maximum readable width is **768px**.
+
+**Extra Container** is used for dense layouts (e.g. code, detailed views) where additional horizontal space is required.
+
+A right sidebar (304px) may be used for secondary content when needed.
+
+Text and input fields should not exceed **768px** in width
+
+## Element Sizes
+
+Interactive element sizes:
+
+- 24 — 2xs (rare)
+- 28 — xs (compact)
+- 32 — sm (minimum recommended)
+- 36 — md (default)
+- 42 — lg
+- 48 — xl
+- 56 — 2xl
+- 64 — 3xl
+
+## Typography
+
+- Base font size: **16px**
+- Minimum font size: **14px** (use sparingly)
+
+Text casing:
+
+- **Uppercase** — for headings
+- **Lowercase** — for UI elements
+- **Normal** - for body content
+
+[See Typography](./?path=/docs/tokens-typography--docs)
+
+## Surfaces & Sections
+
+To visually separate content blocks (grouped content, cards, settings sections), use subtle background variations:
+
+- `bg-muted`
+- `bg-subtle`
+
+## Borders & Radius
+
+- subtle border
+- light border radius
+- soft "glass-like" appearance
+
+Only avatars should have fully circular shapes.
+
+## Colors
+
+- Primary surfaces: **white** (`fg`)
+- Secondary or less important content: use softer, muted tones
+
+[See Colors](./?path=/docs/tokens-colors--docs)
+
+## Layout Stability
+
+Interfaces must remain visually stable across screens and pages. Avoid layout shifts. If shifts are unavoidable, place elements where movement is not critical to user interaction.
+
+## Responsiveness specifics
+
+On smaller screens:
+
+- Button text may be hidden (icons only)
+- Keyboard shortcuts may be hidden
+
+## General Principles
+
+- Preserve the meaning of elements (avoid links like button, etc)
+- Maintain sufficient spacing
+- Keep interfaces accessible
+- Prefer simple, predictable layouts
+- Design components to be easy to implement and scale
diff --git a/app/typography.mdx b/app/typography.mdx
new file mode 100644
index 0000000000..6d48c4840f
--- /dev/null
+++ b/app/typography.mdx
@@ -0,0 +1,166 @@
+import { Meta, Typeset } from '@storybook/addon-docs/blocks'
+import { theme } from '../uno.theme.ts'
+
+
+
+export const wind4FontSizes = {
+ 'xs': '0.75rem',
+ 'sm': '0.875rem',
+ 'base': '1rem',
+ 'lg': '1.125rem',
+ 'xl': '1.25rem',
+ '2xl': '1.5rem',
+ '3xl': '1.875rem',
+ '4xl': '2.25rem',
+ '5xl': '3rem',
+}
+
+# Typography
+
+The npmx typography system uses Geist for sans-serif text and Geist Mono for monospace code.
+
+Typography values are defined in `uno.theme.ts`.
+
+## Font Families
+
+### Sans Serif — Geist
+
+
+
+
+
+### Monospace — Geist Mono
+
+Code and monospace typography scale:
+
+
+
+
+
+## Font Sizes
+
+The complete typography scale combines custom micro sizes (`5xs`-`2xs` from our theme) with standard Wind4 preset sizes (`xs`-`5xl`).
+
+**Custom Micro Sizes (from `theme.text`):**
+
+- `5xs` = {theme.text['5xs'].fontSize} (8px)
+- `4xs` = {theme.text['4xs'].fontSize} (9px)
+- `3xs` = {theme.text['3xs'].fontSize} (10px)
+- `2xs` = {theme.text['2xs'].fontSize} (11px)
+
+**Wind4 Standard Sizes:**
+
+- `xs` = {wind4FontSizes['xs']} (12px)
+- `sm` = {wind4FontSizes['sm']} (14px)
+- `base` = {wind4FontSizes['base']} (16px)
+- `lg` = {wind4FontSizes['lg']} (18px)
+- `xl` = {wind4FontSizes['xl']} (20px)
+- `2xl` = {wind4FontSizes['2xl']} (24px)
+- `3xl` = {wind4FontSizes['3xl']} (30px)
+- `4xl` = {wind4FontSizes['4xl']} (36px)
+- `5xl` = {wind4FontSizes['5xl']} (48px)
+
+## Font Weights
+
+
+
+
+
+
+
+
+
+
+
+## Line Height
+
+Base line height is set to **1.6** for optimal readability. This provides comfortable spacing for multi-line text blocks while maintaining density for UI components.
+
+## Typography Features
+
+- **Font smoothing**: Antialiased rendering (`-webkit-font-smoothing: antialiased`) applied globally
+- **Text rendering**: `optimizeLegibility` applied globally for consistent cross-browser rendering
+
+---
+
+## Related
+
+- [Design System Guidelines](./?path=/docs/guidelines--docs) - See typography guidelines and usage
+- [Colors](./?path=/docs/colors--docs) - View the color system
diff --git a/chromatic.config.json b/chromatic.config.json
index cb44a68276..7e58430015 100644
--- a/chromatic.config.json
+++ b/chromatic.config.json
@@ -3,5 +3,5 @@
"onlyChanged": true,
"autoAcceptChanges": "main",
"exitZeroOnChanges": false,
- "externals": [".storybook/**", "uno.config.ts"]
+ "externals": [".storybook/**", "uno.config.ts", "uno.theme.ts"]
}
diff --git a/uno.config.ts b/uno.config.ts
index fae3f45785..473c37c82c 100644
--- a/uno.config.ts
+++ b/uno.config.ts
@@ -6,9 +6,9 @@ import {
transformerDirectives,
transformerVariantGroup,
} from 'unocss'
-import type { Theme } from '@unocss/preset-wind4/theme'
import { presetRtl } from './uno-preset-rtl'
import { presetA11y } from './uno-preset-a11y'
+import { theme } from './uno.theme'
const customIcons = {
'agent-skills':
@@ -40,102 +40,7 @@ export default defineConfig({
...(process.env.CI ? [] : [presetRtl(), presetA11y()]),
].filter(Boolean),
transformers: [transformerDirectives(), transformerVariantGroup()],
- theme: {
- spacing: { DEFAULT: '4px' },
- font: {
- mono: "'Geist Mono', monospace",
- sans: "'Geist', system-ui, -apple-system, sans-serif",
- },
- text: {
- '2xs': { fontSize: '0.6875rem' }, // 11px
- '3xs': { fontSize: '0.625rem' }, // 10px
- '4xs': { fontSize: '0.5625rem' }, // 9px
- '5xs': { fontSize: '0.5rem' }, // 8px
- },
- colors: {
- // Minimal black & white palette with subtle grays
- bg: {
- DEFAULT: 'var(--bg)',
- subtle: 'var(--bg-subtle)',
- muted: 'var(--bg-muted)',
- elevated: 'var(--bg-elevated)',
- },
- fg: {
- DEFAULT: 'var(--fg)',
- muted: 'var(--fg-muted)',
- subtle: 'var(--fg-subtle)',
- },
- border: {
- DEFAULT: 'var(--border)',
- subtle: 'var(--border-subtle)',
- hover: 'var(--border-hover)',
- },
- accent: {
- DEFAULT: 'var(--accent)',
- fallback: 'var(--accent-muted)',
- },
- // Syntax highlighting colors (inspired by GitHub Dark)
- syntax: {
- fn: 'var(--syntax-fn)',
- str: 'var(--syntax-str)',
- kw: 'var(--syntax-kw)',
- comment: 'var(--syntax-comment)',
- },
- badge: {
- orange: 'var(--badge-orange)',
- yellow: 'var(--badge-yellow)',
- green: 'var(--badge-green)',
- cyan: 'var(--badge-cyan)',
- blue: 'var(--badge-blue)',
- indigo: 'var(--badge-indigo)',
- purple: 'var(--badge-purple)',
- pink: 'var(--badge-pink)',
- },
- // Playground provider brand colors
- provider: {
- stackblitz: '#1389FD',
- codesandbox: '#FFCC00',
- codepen: '#47CF73',
- replit: '#F26207',
- gitpod: '#FFAE33',
- vue: '#4FC08D',
- nuxt: '#00DC82',
- vite: '#646CFF',
- jsfiddle: '#0084FF',
- typescript: '#3178C6',
- solid: '#2C4F7C',
- svelte: '#FF3E00',
- tailwind: '#06B6D4',
- storybook: '#FF4785',
- marko: '#CC0067',
- },
- },
- animation: {
- keyframes: {
- 'skeleton-pulse': '{0%, 100% { opacity: 0.4 } 50% { opacity: 0.7 }}',
- 'fade-in': '{from { opacity: 0 } to { opacity: 1 }}',
- 'slide-up':
- '{from { opacity: 0; transform: translateY(8px) } to { opacity: 1; transform: translateY(0) }}',
- 'scale-in':
- '{from { opacity: 0; transform: scale(0.95) } to { opacity: 1; transform: scale(1) }}',
- },
- durations: {
- 'skeleton-pulse': '2s',
- 'fade-in': '0.3s',
- 'slide-up': '0.4s',
- 'scale-in': '0.2s',
- },
- timingFns: {
- 'skeleton-pulse': 'ease-in-out',
- 'fade-in': 'ease-out',
- 'slide-up': 'cubic-bezier(0.22, 1, 0.36, 1)',
- 'scale-in': 'cubic-bezier(0.22, 1, 0.36, 1)',
- },
- counts: {
- 'skeleton-pulse': 'infinite',
- },
- },
- } satisfies Theme,
+ theme,
shortcuts: [
// Layout
['container', 'max-w-6xl mx-auto px-4 sm:px-6'],
diff --git a/uno.theme.ts b/uno.theme.ts
new file mode 100644
index 0000000000..960a65a438
--- /dev/null
+++ b/uno.theme.ts
@@ -0,0 +1,98 @@
+import type { Theme } from '@unocss/preset-wind4/theme'
+
+export const theme = {
+ spacing: { DEFAULT: '4px' },
+ font: {
+ mono: "'Geist Mono', monospace",
+ sans: "'Geist', system-ui, -apple-system, sans-serif",
+ },
+ text: {
+ '2xs': { fontSize: '0.6875rem' }, // 11px
+ '3xs': { fontSize: '0.625rem' }, // 10px
+ '4xs': { fontSize: '0.5625rem' }, // 9px
+ '5xs': { fontSize: '0.5rem' }, // 8px
+ },
+ colors: {
+ // Minimal black & white palette with subtle grays
+ bg: {
+ DEFAULT: 'var(--bg)',
+ subtle: 'var(--bg-subtle)',
+ muted: 'var(--bg-muted)',
+ elevated: 'var(--bg-elevated)',
+ },
+ fg: {
+ DEFAULT: 'var(--fg)',
+ muted: 'var(--fg-muted)',
+ subtle: 'var(--fg-subtle)',
+ },
+ border: {
+ DEFAULT: 'var(--border)',
+ subtle: 'var(--border-subtle)',
+ hover: 'var(--border-hover)',
+ },
+ accent: {
+ DEFAULT: 'var(--accent)',
+ fallback: 'var(--accent-muted)',
+ },
+ // Syntax highlighting colors (inspired by GitHub Dark)
+ syntax: {
+ fn: 'var(--syntax-fn)',
+ str: 'var(--syntax-str)',
+ kw: 'var(--syntax-kw)',
+ comment: 'var(--syntax-comment)',
+ },
+ badge: {
+ orange: 'var(--badge-orange)',
+ yellow: 'var(--badge-yellow)',
+ green: 'var(--badge-green)',
+ cyan: 'var(--badge-cyan)',
+ blue: 'var(--badge-blue)',
+ indigo: 'var(--badge-indigo)',
+ purple: 'var(--badge-purple)',
+ pink: 'var(--badge-pink)',
+ },
+ // Playground provider brand colors
+ provider: {
+ stackblitz: '#1389FD',
+ codesandbox: '#FFCC00',
+ codepen: '#47CF73',
+ replit: '#F26207',
+ gitpod: '#FFAE33',
+ vue: '#4FC08D',
+ nuxt: '#00DC82',
+ vite: '#646CFF',
+ jsfiddle: '#0084FF',
+ typescript: '#3178C6',
+ solid: '#2C4F7C',
+ svelte: '#FF3E00',
+ tailwind: '#06B6D4',
+ storybook: '#FF4785',
+ marko: '#CC0067',
+ },
+ },
+ animation: {
+ keyframes: {
+ 'skeleton-pulse': '{0%, 100% { opacity: 0.4 } 50% { opacity: 0.7 }}',
+ 'fade-in': '{from { opacity: 0 } to { opacity: 1 }}',
+ 'slide-up':
+ '{from { opacity: 0; transform: translateY(8px) } to { opacity: 1; transform: translateY(0) }}',
+ 'scale-in':
+ '{from { opacity: 0; transform: scale(0.95) } to { opacity: 1; transform: scale(1) }}',
+ },
+ durations: {
+ 'skeleton-pulse': '2s',
+ 'fade-in': '0.3s',
+ 'slide-up': '0.4s',
+ 'scale-in': '0.2s',
+ },
+ timingFns: {
+ 'skeleton-pulse': 'ease-in-out',
+ 'fade-in': 'ease-out',
+ 'slide-up': 'cubic-bezier(0.22, 1, 0.36, 1)',
+ 'scale-in': 'cubic-bezier(0.22, 1, 0.36, 1)',
+ },
+ counts: {
+ 'skeleton-pulse': 'infinite',
+ },
+ },
+} satisfies Theme