diff --git a/README.md b/README.md
index 946ce60..2dfeb53 100644
--- a/README.md
+++ b/README.md
@@ -367,6 +367,230 @@ frontend/src/services/dynamicComponents.ts (~300 lines)
---
+---
+
+# Theme System
+
+Agent UI includes a powerful theming engine with visual editor, presets, and design tokens for consistent styling across dynamic components.
+
+## Overview
+
+The theme system provides:
+
+- **Visual Editor**: Edit CSS variables with live preview
+- **Presets**: System themes (Dark/Light) + custom user themes
+- **Persistence**: Themes saved to SQLite database
+- **Export/Import**: Share themes as JSON files
+- **Design Tokens**: Structured guide for LLM agents to follow
+
+## Accessing the Theme Editor
+
+Navigate to `/themes` in the UI or click the palette icon in the sidebar.
+
+## Theme Structure
+
+Themes are organized by categories:
+
+```javascript
+{
+ "colors": {
+ "bg-primary": "#0f0f14",
+ "bg-secondary": "#16161d",
+ "bg-hover": "#1e1e28",
+ "bg-tertiary": "#252530",
+ "border-color": "#2a2a3a"
+ },
+ "text": {
+ "text-primary": "#e4e4e7",
+ "text-secondary": "#a1a1aa",
+ "text-muted": "#52525b"
+ },
+ "accent": {
+ "accent": "#6366f1",
+ "accent-hover": "#818cf8",
+ "accent-muted": "rgba(99, 102, 241, 0.2)",
+ "accent-text": "#ffffff"
+ },
+ "semantic": {
+ "success": "#22c55e",
+ "success-bg": "rgba(34, 197, 94, 0.15)",
+ "warning": "#eab308",
+ "warning-bg": "rgba(234, 179, 8, 0.15)",
+ "error": "#ef4444",
+ "error-bg": "rgba(239, 68, 68, 0.15)",
+ "info": "#3b82f6",
+ "info-bg": "rgba(59, 130, 246, 0.15)"
+ },
+ "spacing": {
+ "radius-sm": "4px",
+ "radius-md": "8px",
+ "radius-lg": "12px",
+ "radius-full": "9999px"
+ },
+ "typography": {
+ "font-sans": "Inter, system-ui, sans-serif",
+ "font-mono": "JetBrains Mono, monospace"
+ },
+ "effects": {
+ "shadow-sm": "0 1px 2px rgba(0,0,0,0.3)",
+ "shadow-md": "0 4px 12px rgba(0,0,0,0.4)",
+ "transition-fast": "0.15s ease"
+ }
+}
+```
+
+## MCP Tools
+
+### `get_design_tokens`
+
+Returns design tokens for the active theme. Use this to create components with consistent styling.
+
+```javascript
+{
+ category: "all" | "colors" | "text" | "accent" | "semantic" | "spacing" | "typography" | "effects"
+}
+```
+
+**Response example:**
+
+```
+Design Tokens del tema "Dark":
+
+[COLORS]
+ --bg-primary: #0f0f14
+ --bg-secondary: #16161d
+ ...
+
+GUÍA DE USO:
+- Usa var(--nombre-variable) en CSS
+- Los componentes dinámicos tienen acceso a $theme.getVariable('nombre')
+- Colores semánticos: success, warning, error, info (con -bg para fondos)
+- Radius: radius-sm (4px), radius-md (8px), radius-lg (12px), radius-full (9999px)
+```
+
+## Theme API in Dynamic Components
+
+Components have access to `$theme` helper:
+
+```javascript
+// Get CSS variable value
+$theme.getVariable('accent') // "#6366f1"
+
+// Set variable temporarily (runtime only)
+$theme.setVariable('accent', '#ff0000')
+
+// Get all design tokens
+$theme.getTokens()
+
+// Get active theme object
+$theme.getActiveTheme()
+
+// Get current variables
+$theme.getVariables()
+```
+
+### Example: Theme-Aware Component
+
+```javascript
+{
+ id: "themed-card",
+ name: "ThemedCard",
+ template: `
+
+
{{ title }}
+
Current accent: {{ accentColor }}
+
+
+ `,
+ setup: `
+ const accentColor = ref($theme.getVariable('accent'));
+
+ const randomizeAccent = () => {
+ const hue = Math.floor(Math.random() * 360);
+ const newColor = \`hsl(\${hue}, 70%, 60%)\`;
+ $theme.setVariable('accent', newColor);
+ accentColor.value = newColor;
+ };
+
+ return { title: props.title, accentColor, randomizeAccent };
+ `,
+ style: `
+ .card {
+ padding: var(--radius-lg);
+ background: var(--bg-secondary);
+ border: 1px solid var(--border-color);
+ border-radius: var(--radius-md);
+ }
+ .card h3 { color: var(--text-primary); }
+ .card p { color: var(--text-secondary); }
+ .card button {
+ background: var(--accent);
+ color: var(--accent-text);
+ border: none;
+ padding: 0.5rem 1rem;
+ border-radius: var(--radius-sm);
+ cursor: pointer;
+ }
+ .card button:hover { background: var(--accent-hover); }
+ `,
+ props: ["title"],
+ imports: ["ref"],
+ componentProps: { title: "Themed Card" }
+}
+```
+
+## API Endpoints (Themes)
+
+| Endpoint | Method | Description |
+|----------|--------|-------------|
+| `/api/themes` | GET | List all themes |
+| `/api/themes` | POST | Create/update theme |
+| `/api/themes/active` | GET | Get active (default) theme |
+| `/api/themes/:id` | GET | Get theme by ID |
+| `/api/themes/:id` | DELETE | Delete theme |
+| `/api/themes/:id/default` | POST | Set as default theme |
+| `/api/themes/export/:id` | GET | Export theme as JSON |
+| `/api/design-tokens` | GET | Get design tokens guide |
+
+## Theme Editor Features
+
+### Desktop View
+
+- **Sidebar**: List of system and custom themes
+- **Editor**: Category tabs (Colors, Text, Accent, etc.)
+- **Variables Grid**: Color pickers and input fields
+- **Live Preview**: Buttons, cards, badges, inputs
+
+### Mobile View
+
+- **Compact Header**: Theme dropdown + action buttons
+- **Collapsible Variables**: Toggle to show/hide editors
+- **Responsive Preview**: Adapts to screen size
+
+### Actions
+
+- **Save**: Saves current changes (creates copy if editing system theme)
+- **Reset**: Reverts unsaved changes
+- **Export**: Downloads theme as JSON file
+- **Clone**: Creates a copy of any theme
+- **Set Default**: Makes theme load on startup
+
+## File Structure
+
+```
+frontend/src/
+├── stores/theme.ts # Pinia store
+├── services/themeService.ts # API client + utilities
+├── pages/ThemesPage.vue # Main editor page
+└── components/themes/
+ ├── ColorPicker.vue # HSL color picker
+ ├── VariableEditor.vue # Variable input (color/size/text)
+ ├── ThemePreview.vue # Live preview component
+ └── ThemeListItem.vue # Theme list item with actions
+```
+
+---
+
## Tech Stack
- **Frontend**: Vue 3, Vite, Pinia, TypeScript
diff --git a/frontend/src/components/themes/ColorPicker.vue b/frontend/src/components/themes/ColorPicker.vue
index faf08f2..2cfa7ce 100644
--- a/frontend/src/components/themes/ColorPicker.vue
+++ b/frontend/src/components/themes/ColorPicker.vue
@@ -277,4 +277,41 @@ input[type="range"]::-webkit-slider-thumb {
border-radius: 6px;
border: 1px solid rgba(255, 255, 255, 0.1);
}
+
+/* Mobile responsive */
+@media (max-width: 600px) {
+ .color-preview {
+ padding: 0.25rem 0.375rem;
+ }
+
+ .color-swatch {
+ width: 20px;
+ height: 20px;
+ }
+
+ .color-value {
+ font-size: 0.7rem;
+ }
+
+ .color-dropdown {
+ width: 220px;
+ padding: 0.75rem;
+ left: 50%;
+ transform: translateX(-50%);
+ }
+
+ .slider-group label {
+ width: 55px;
+ font-size: 0.7rem;
+ }
+
+ .slider-group span {
+ width: 35px;
+ font-size: 0.65rem;
+ }
+
+ .large-preview {
+ height: 32px;
+ }
+}
diff --git a/frontend/src/components/themes/ThemePreview.vue b/frontend/src/components/themes/ThemePreview.vue
index 0988aa6..f487c6b 100644
--- a/frontend/src/components/themes/ThemePreview.vue
+++ b/frontend/src/components/themes/ThemePreview.vue
@@ -249,4 +249,69 @@ defineProps<{
background: var(--info-bg);
color: var(--info);
}
+
+/* Mobile responsive */
+@media (max-width: 768px) {
+ .theme-preview {
+ padding: 0.75rem;
+ }
+
+ .preview-section {
+ margin-bottom: 1rem;
+ }
+
+ .color-swatches {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: 0.5rem;
+ }
+
+ .swatch {
+ height: 40px;
+ }
+
+ .swatch span {
+ font-size: 0.6rem;
+ }
+
+ .component-samples {
+ flex-direction: column;
+ }
+
+ .sample-btn {
+ width: 100%;
+ text-align: center;
+ }
+
+ .sample-card {
+ padding: 0.75rem;
+ }
+
+ .sample-card h5 {
+ font-size: 0.875rem;
+ }
+
+ .sample-card p {
+ font-size: 0.8rem;
+ }
+
+ .status-badges {
+ gap: 0.375rem;
+ }
+
+ .badge {
+ padding: 0.2rem 0.5rem;
+ font-size: 0.7rem;
+ }
+}
+
+@media (max-width: 480px) {
+ .color-swatches {
+ grid-template-columns: 1fr;
+ }
+
+ .swatch {
+ height: 36px;
+ }
+}
diff --git a/frontend/src/components/themes/VariableEditor.vue b/frontend/src/components/themes/VariableEditor.vue
index 90fd940..6c88a58 100644
--- a/frontend/src/components/themes/VariableEditor.vue
+++ b/frontend/src/components/themes/VariableEditor.vue
@@ -169,4 +169,35 @@ function handleInputChange(e: Event) {
outline: none;
border-color: var(--accent);
}
+
+/* Mobile responsive */
+@media (max-width: 600px) {
+ .variable-editor {
+ padding: 0.75rem;
+ }
+
+ .variable-header {
+ margin-bottom: 0.5rem;
+ }
+
+ .variable-name {
+ font-size: 0.8rem;
+ }
+
+ .variable-key {
+ font-size: 0.65rem;
+ }
+
+ .size-input input,
+ .text-input {
+ padding: 0.4rem;
+ font-size: 0.8rem;
+ }
+
+ .size-preview,
+ .radius-preview {
+ width: 24px;
+ height: 24px;
+ }
+}
diff --git a/frontend/src/pages/ThemesPage.vue b/frontend/src/pages/ThemesPage.vue
index 5454421..857430a 100644
--- a/frontend/src/pages/ThemesPage.vue
+++ b/frontend/src/pages/ThemesPage.vue
@@ -10,6 +10,7 @@ const store = useThemeStore()
const activeCategory = ref('colors')
const variablesCollapsed = ref(false)
+const mobileDropdownOpen = ref(false)
const newThemeName = ref('')
const showNewThemeModal = ref(false)
const showCloneModal = ref(false)
@@ -141,12 +142,58 @@ onMounted(() => {