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