Skip to content
Merged
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
8 changes: 8 additions & 0 deletions .changeset/fair-rules-heal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@tiny-design/tokens': minor
'@tiny-design/mcp': patch
---

Add seed-driven token foundations to `@tiny-design/tokens` with an internal `primitive -> semantic -> component` model, a shared `compile-brand-theme` runtime export, stricter build validation, and richer registry metadata including resolved token values for downstream tooling.

Fix `@tiny-design/mcp` token extraction so MCP clients receive concrete resolved token values instead of unresolved token references.
72 changes: 2 additions & 70 deletions apps/docs/src/containers/theme-studio/defaults.ts
Original file line number Diff line number Diff line change
@@ -1,75 +1,7 @@
import { DEFAULT_BRAND_SEED_FIELDS } from '@tiny-design/tokens/compile-brand-theme';
import type { ThemeEditorDraft, ThemeEditorFields, ThemeMode } from './types';

const DEFAULT_FONT_SANS = '"Instrument Sans", "Inter", system-ui, sans-serif';
const DEFAULT_FONT_MONO = '"JetBrains Mono", "SFMono-Regular", monospace';

export const DEFAULT_FIELDS: ThemeEditorFields = {
primary: '#6e41bf',
primaryForeground: '#ffffff',
secondary: '#f5f5f7',
secondaryForeground: '#16181d',
accent: '#f3eefa',
accentForeground: '#6e41bf',
success: '#52c41a',
successForeground: '#ffffff',
info: '#1890ff',
infoForeground: '#ffffff',
warning: '#fa8c16',
warningForeground: '#ffffff',
danger: '#dc2626',
dangerForeground: '#ffffff',
base: '#ffffff',
baseForeground: 'rgba(0, 0, 0, 0.85)',
card: '#ffffff',
cardForeground: '#111827',
popover: '#ffffff',
popoverForeground: '#111827',
muted: '#f5f5f5',
mutedForeground: 'rgba(0, 0, 0, 0.55)',
border: '#d9d9d9',
input: '#d9d9d9',
ring: '#6e41bf',
chart1: '#6e41bf',
chart2: '#1890ff',
chart3: '#52c41a',
chart4: '#fa8c16',
chart5: '#eb2f96',
sidebar: '#12131a',
sidebarForeground: '#f8fafc',
sidebarPrimary: '#6e41bf',
sidebarPrimaryForeground: '#ffffff',
sidebarAccent: '#23173f',
sidebarAccentForeground: '#efe8ff',
sidebarBorder: '#2a2d36',
sidebarRing: '#8b62d0',
fontSans: DEFAULT_FONT_SANS,
fontMono: DEFAULT_FONT_MONO,
fontSizeBase: '14px',
lineHeightBase: '1.5',
h1Size: '40px',
h2Size: '32px',
letterSpacing: '-0.02em',
radius: '0.3rem',
shadowControl: 'none',
shadowCard: '0 20px 55px rgba(17, 24, 39, 0.08)',
shadowFocus: '0 0 0 3px rgba(110, 65, 191, 0.22)',
buttonRadius: '0.3rem',
inputRadius: '0.3rem',
cardRadius: '0.3rem',
fieldPaddingSm: '8px',
fieldPaddingMd: '8px',
fieldPaddingLg: '8px',
buttonPaddingSm: '8px',
buttonPaddingMd: '15px',
buttonPaddingLg: '20px',
fieldHeightSm: '24px',
fieldHeightMd: '35px',
fieldHeightLg: '44px',
buttonHeightSm: '24px',
buttonHeightMd: '35px',
buttonHeightLg: '44px',
cardPadding: '18px',
};
export const DEFAULT_FIELDS: ThemeEditorFields = { ...DEFAULT_BRAND_SEED_FIELDS };

export function createDraft(
id: string,
Expand Down
125 changes: 51 additions & 74 deletions apps/docs/src/containers/theme-studio/editor-config.ts
Original file line number Diff line number Diff line change
@@ -1,111 +1,88 @@
import type { FieldKey, ThemeEditorColorGroup } from './types';
import type { ThemeEditorSeedGroup } from './types';

export const COLOR_GROUPS: ThemeEditorColorGroup[] = [
export const SEED_COLOR_GROUPS: ThemeEditorSeedGroup[] = [
{
title: 'Primary',
title: 'Brand Core',
description: 'Primary, accent, and supporting brand surfaces.',
tier: 'core',
fields: [
{ key: 'primary', label: 'Background' },
{ key: 'primaryForeground', label: 'Foreground' },
{ key: 'primary', label: 'Primary' },
{ key: 'primaryForeground', label: 'On Primary' },
{ key: 'secondary', label: 'Secondary' },
{ key: 'secondaryForeground', label: 'On Secondary' },
{ key: 'accent', label: 'Accent' },
{ key: 'accentForeground', label: 'On Accent' },
],
},
{
title: 'Secondary',
fields: [
{ key: 'secondary', label: 'Background' },
{ key: 'secondaryForeground', label: 'Foreground' },
],
},
{
title: 'Accent',
fields: [
{ key: 'accent', label: 'Background' },
{ key: 'accentForeground', label: 'Foreground' },
],
},
{
title: 'Status',
title: 'Feedback States',
description: 'Semantic status hues that propagate through alerts, tags, and feedback UI.',
tier: 'core',
fields: [
{ key: 'success', label: 'Success' },
{ key: 'successForeground', label: 'Success FG' },
{ key: 'successForeground', label: 'On Success' },
{ key: 'info', label: 'Info' },
{ key: 'infoForeground', label: 'Info FG' },
{ key: 'infoForeground', label: 'On Info' },
{ key: 'warning', label: 'Warning' },
{ key: 'warningForeground', label: 'Warning FG' },
{ key: 'warningForeground', label: 'On Warning' },
{ key: 'danger', label: 'Danger' },
{ key: 'dangerForeground', label: 'Danger FG' },
{ key: 'dangerForeground', label: 'On Danger' },
],
},
{
title: 'Base',
title: 'Surfaces & Text',
description: 'Page, card, popover, and muted surfaces plus their text companions.',
tier: 'core',
fields: [
{ key: 'base', label: 'Background' },
{ key: 'baseForeground', label: 'Foreground' },
{ key: 'base', label: 'Page' },
{ key: 'baseForeground', label: 'On Page' },
{ key: 'card', label: 'Card' },
{ key: 'cardForeground', label: 'On Card' },
{ key: 'popover', label: 'Popover' },
{ key: 'popoverForeground', label: 'On Popover' },
{ key: 'muted', label: 'Muted' },
{ key: 'mutedForeground', label: 'On Muted' },
],
},
{
title: 'Card',
fields: [
{ key: 'card', label: 'Background' },
{ key: 'cardForeground', label: 'Foreground' },
],
},
{
title: 'Popover',
fields: [
{ key: 'popover', label: 'Background' },
{ key: 'popoverForeground', label: 'Foreground' },
],
},
{
title: 'Muted',
fields: [
{ key: 'muted', label: 'Background' },
{ key: 'mutedForeground', label: 'Foreground' },
],
},
{
title: 'Border & Input',
title: 'Lines & Focus',
description: 'Borders, field chrome, and the interaction ring seed.',
tier: 'core',
fields: [
{ key: 'border', label: 'Border' },
{ key: 'input', label: 'Input' },
{ key: 'ring', label: 'Ring' },
],
},
{
title: 'Chart',
title: 'Data Visualization',
description: 'Chart palette seeds for data-heavy views.',
tier: 'advanced',
fields: [
{ key: 'chart1', label: 'Chart 1' },
{ key: 'chart2', label: 'Chart 2' },
{ key: 'chart3', label: 'Chart 3' },
{ key: 'chart4', label: 'Chart 4' },
{ key: 'chart5', label: 'Chart 5' },
{ key: 'chart1', label: 'Series 1' },
{ key: 'chart2', label: 'Series 2' },
{ key: 'chart3', label: 'Series 3' },
{ key: 'chart4', label: 'Series 4' },
{ key: 'chart5', label: 'Series 5' },
],
},
{
title: 'Sidebar',
title: 'Sidebar Shell',
description: 'Dedicated app-shell seeds for navigation-heavy layouts.',
tier: 'advanced',
fields: [
{ key: 'sidebar', label: 'Background' },
{ key: 'sidebarForeground', label: 'Foreground' },
{ key: 'sidebarPrimary', label: 'Primary' },
{ key: 'sidebarPrimaryForeground', label: 'Primary FG' },
{ key: 'sidebarAccent', label: 'Accent' },
{ key: 'sidebarAccentForeground', label: 'Accent FG' },
{ key: 'sidebarBorder', label: 'Border' },
{ key: 'sidebarRing', label: 'Ring' },
{ key: 'sidebar', label: 'Sidebar' },
{ key: 'sidebarForeground', label: 'On Sidebar' },
{ key: 'sidebarPrimary', label: 'Sidebar Primary' },
{ key: 'sidebarPrimaryForeground', label: 'On Sidebar Primary' },
{ key: 'sidebarAccent', label: 'Sidebar Accent' },
{ key: 'sidebarAccentForeground', label: 'On Sidebar Accent' },
{ key: 'sidebarBorder', label: 'Sidebar Border' },
{ key: 'sidebarRing', label: 'Sidebar Ring' },
],
},
];

export const CORE_COLOR_GROUP_TITLES = new Set([
'Primary',
'Secondary',
'Accent',
'Status',
'Base',
'Muted',
'Border & Input',
]);

export const FONT_OPTIONS = [
'ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif',
'"Instrument Sans", "Inter", system-ui, sans-serif',
Expand Down
40 changes: 31 additions & 9 deletions apps/docs/src/containers/theme-studio/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,28 @@ const ThemeStudioPage = (): React.ReactElement => {
const [codeVisible, setCodeVisible] = useState(false);
const [importText, setImportText] = useState('');
const [importError, setImportError] = useState<string | null>(null);
const [status, setStatus] = useState<string>('Editing local draft');
const [status, setStatus] = useState<string>('Editing local seed draft');
const globalMode: ThemeMode = resolvedTheme === 'dark' ? 'dark' : 'light';

useEffect(() => {
localStorage.setItem(DRAFT_KEY, JSON.stringify(draft));
}, [draft]);

const themeDocument = useMemo(() => buildThemeDocumentFromDraft(draft), [draft]);
const seedJson = useMemo(
() =>
JSON.stringify(
{
meta: draft.meta,
mode: draft.mode,
presetId: draft.presetId,
fields: draft.fields,
},
null,
2
),
[draft]
);
const themeJson = useMemo(() => generateThemeDocumentJSON(themeDocument), [themeDocument]);
const cssVars = useMemo(() => generateThemeCssVariables(themeDocument), [themeDocument]);
const changedTokens = useMemo(() => compareThemeAgainstBase(themeDocument), [themeDocument]);
Expand Down Expand Up @@ -105,13 +119,13 @@ const ThemeStudioPage = (): React.ReactElement => {

const resetToPreset = () => {
setDraft((current) => applyPresetToDraft(current.presetId, current));
setStatus('Reset to preset defaults');
setStatus('Reset seed draft to preset defaults');
};

const handlePresetChange = (presetId: string) => {
setDraft((current) => applyPresetToDraft(presetId, current));
setStatus(
`Applied ${THEME_EDITOR_PRESETS.find((preset) => preset.id === presetId)?.name ?? 'preset'}`
`Applied ${THEME_EDITOR_PRESETS.find((preset) => preset.id === presetId)?.name ?? 'preset'} seed preset`
);
};

Expand All @@ -129,8 +143,8 @@ const ThemeStudioPage = (): React.ReactElement => {
setImportError(null);
setStatus(
validation.warnings.length > 0
? 'Imported theme document with validation warnings'
: 'Imported theme document'
? 'Imported theme document and remapped it into seed groups with validation warnings'
: 'Imported theme document and remapped it into seed groups'
);
} catch {
setImportError('Invalid theme document JSON');
Expand Down Expand Up @@ -253,7 +267,8 @@ const ThemeStudioPage = (): React.ReactElement => {
Paste a Tiny theme document JSON export to replace the current global theme.
</Paragraph>
<Text type="secondary">
Preset selection and all editor controls will sync to the imported values.
The studio will map semantic and component token overrides back into the seed groups
shown in the editor.
</Text>
</div>
<Textarea
Expand All @@ -275,7 +290,9 @@ const ThemeStudioPage = (): React.ReactElement => {
bodyStyle={{ maxHeight: 'min(74vh, 760px)', overflow: 'auto' }}
cancelText="Close"
confirmText={
draft.activeCodeView === 'json'
draft.activeCodeView === 'seeds'
? 'Copy Seed JSON'
: draft.activeCodeView === 'json'
? 'Copy JSON'
: draft.activeCodeView === 'css'
? 'Copy CSS'
Expand All @@ -285,7 +302,9 @@ const ThemeStudioPage = (): React.ReactElement => {
draft.activeCodeView === 'tokens' ? { style: { display: 'none' } } : undefined
}
onConfirm={
draft.activeCodeView === 'json'
draft.activeCodeView === 'seeds'
? () => handleCopy(seedJson, 'Seed JSON')
: draft.activeCodeView === 'json'
? () => handleCopy(themeJson, 'Theme JSON')
: draft.activeCodeView === 'css'
? () => handleCopy(cssVars, 'CSS variables')
Expand All @@ -296,7 +315,7 @@ const ThemeStudioPage = (): React.ReactElement => {
<div>
<Text strong>Output</Text>
<Text type="secondary">
{activePreset.name} · {status}
{activePreset.name} · {status} · seed draft compiles to theme JSON and CSS vars
</Text>
</div>
<Segmented
Expand All @@ -307,6 +326,9 @@ const ThemeStudioPage = (): React.ReactElement => {
}
/>
</div>
{draft.activeCodeView === 'seeds' ? (
<pre className="theme-studio__code-block">{seedJson}</pre>
) : null}
{draft.activeCodeView === 'json' ? (
<pre className="theme-studio__code-block">{themeJson}</pre>
) : null}
Expand Down
Loading
Loading