Update demo with expanded component showcase and styling

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Giuliano Silvestro
2026-02-13 22:06:25 +10:00
parent 348186a395
commit 2ed8feffc0
3 changed files with 1393 additions and 27 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -7,9 +7,59 @@ import {
AiConfidenceMeterComponent, AiConfidenceMeterComponent,
AiSkeletonLoaderComponent, AiSkeletonLoaderComponent,
AiCodeBlockComponent, AiCodeBlockComponent,
AiConversationHistoryComponent AiConversationHistoryComponent,
AiFeedbackComponent,
AiProgressStepperComponent,
AiTokenCounterComponent,
AiRateLimitIndicatorComponent,
AiSessionHeaderComponent,
AiModelSelectorComponent,
AiSourceCitationComponent,
AiDiffViewerComponent,
AiErrorStateComponent,
AiResponseActionsComponent,
AiToolCallDisplayComponent,
AiSuggestedPromptsComponent,
AiCardComponent,
AiCardGridComponent,
AiFileUploadComponent,
AiPromptTemplatesComponent,
AiMarkdownRendererComponent,
AiAnnotationLayerComponent,
AiImageGalleryComponent,
VoiceCommandComponent,
PredictiveInputComponent,
SmartNotificationComponent,
AiChatComponent,
AiAgentActivityComponent,
AiAgentHandoffComponent,
AiAgentMessageComponent,
AiAgentProfileComponent,
AiAgentSelectorComponent,
AiAgentSwitcherComponent
} from 'ai-elements-ui';
import type {
Conversation,
AiStep,
TokenUsage,
RateLimitInfo,
SessionInfo,
AiModel,
AiSource,
AiError,
ToolCall,
SuggestedPrompt,
CanvasCard,
PromptTemplate,
Annotation,
GeneratedImage,
AiNotification,
AiAgent,
AgentMessage,
AgentConversation,
AgentHandoff,
AgentActivityEntry
} from 'ai-elements-ui'; } from 'ai-elements-ui';
import type { Conversation } from 'ai-elements-ui';
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
@@ -22,7 +72,36 @@ import type { Conversation } from 'ai-elements-ui';
AiConfidenceMeterComponent, AiConfidenceMeterComponent,
AiSkeletonLoaderComponent, AiSkeletonLoaderComponent,
AiCodeBlockComponent, AiCodeBlockComponent,
AiConversationHistoryComponent AiConversationHistoryComponent,
AiFeedbackComponent,
AiProgressStepperComponent,
AiTokenCounterComponent,
AiRateLimitIndicatorComponent,
AiSessionHeaderComponent,
AiModelSelectorComponent,
AiSourceCitationComponent,
AiDiffViewerComponent,
AiErrorStateComponent,
AiResponseActionsComponent,
AiToolCallDisplayComponent,
AiSuggestedPromptsComponent,
AiCardComponent,
AiCardGridComponent,
AiFileUploadComponent,
AiPromptTemplatesComponent,
AiMarkdownRendererComponent,
AiAnnotationLayerComponent,
AiImageGalleryComponent,
VoiceCommandComponent,
PredictiveInputComponent,
SmartNotificationComponent,
AiChatComponent,
AiAgentActivityComponent,
AiAgentHandoffComponent,
AiAgentMessageComponent,
AiAgentProfileComponent,
AiAgentSelectorComponent,
AiAgentSwitcherComponent
], ],
templateUrl: './app.component.html', templateUrl: './app.component.html',
styleUrl: './app.component.scss' styleUrl: './app.component.scss'
@@ -31,6 +110,9 @@ export class AppComponent {
// Demo state // Demo state
currentView = signal<'canvas' | 'components'>('components'); currentView = signal<'canvas' | 'components'>('components');
avatarState = signal<'idle' | 'thinking' | 'speaking'>('idle'); avatarState = signal<'idle' | 'thinking' | 'speaking'>('idle');
darkMode = signal(false);
sidebarOpen = signal(false);
currentTheme = signal<'apple' | 'mist'>('apple');
// Conversations // Conversations
conversations: Conversation[] = [ conversations: Conversation[] = [
@@ -49,6 +131,226 @@ export class AppComponent {
const results = Array.from({ length: 10 }, (_, i) => fibonacci(i)); const results = Array.from({ length: 10 }, (_, i) => fibonacci(i));
console.log(results);`; console.log(results);`;
// Feedback
feedbackMessageId = 'msg-demo-001';
// Progress Stepper
steps: AiStep[] = [
{ id: '1', label: 'Analyzing query', description: 'Parsing your request', status: 'complete', substeps: [
{ id: '1a', label: 'Tokenizing input', status: 'complete' },
{ id: '1b', label: 'Extracting intent', status: 'complete' }
]},
{ id: '2', label: 'Searching knowledge base', description: 'Finding relevant information', status: 'complete' },
{ id: '3', label: 'Generating response', description: 'Composing answer', status: 'active', substeps: [
{ id: '3a', label: 'Drafting content', status: 'active' },
{ id: '3b', label: 'Fact checking', status: 'pending' }
]},
{ id: '4', label: 'Review & citations', description: 'Adding source references', status: 'pending' }
];
// Token Counter
tokenUsage: TokenUsage = { used: 3420, limit: 8192, promptTokens: 1200, completionTokens: 2220 };
// Rate Limit
rateLimit: RateLimitInfo = {
requestsUsed: 42,
requestsLimit: 60,
tokensUsed: 28500,
tokensLimit: 100000,
resetsAt: new Date(Date.now() + 45000),
tier: 'Pro'
};
// Session Header
session: SessionInfo = {
id: 'sess-abc123',
model: 'Claude Opus 4.6',
context: 'Code Review',
tokenUsage: { used: 3420, limit: 8192, promptTokens: 1200, completionTokens: 2220 },
startedAt: new Date(Date.now() - 1800000),
lastActivity: new Date()
};
// Model Selector
models: AiModel[] = [
{ id: 'opus-4.6', name: 'Claude Opus 4.6', provider: 'Anthropic', description: 'Most capable model', contextWindow: 200000, capabilities: ['reasoning', 'code', 'analysis', 'vision'], badge: 'Latest' },
{ id: 'sonnet-4.5', name: 'Claude Sonnet 4.5', provider: 'Anthropic', description: 'Balanced performance', contextWindow: 200000, capabilities: ['reasoning', 'code', 'analysis'] },
{ id: 'haiku-4.5', name: 'Claude Haiku 4.5', provider: 'Anthropic', description: 'Fast and efficient', contextWindow: 200000, capabilities: ['code', 'analysis'] },
{ id: 'gpt-4o', name: 'GPT-4o', provider: 'OpenAI', description: 'Multimodal flagship', contextWindow: 128000, capabilities: ['reasoning', 'code', 'vision'] },
{ id: 'gemini-2', name: 'Gemini 2.0 Flash', provider: 'Google', description: 'Fast multimodal model', contextWindow: 1000000, capabilities: ['reasoning', 'code', 'vision'] }
];
selectedModelId = 'opus-4.6';
// Source Citations
sources: AiSource[] = [
{ id: '1', title: 'Angular Signals Documentation', url: 'https://angular.dev/guide/signals', type: 'web', snippet: 'Angular Signals is a system that granularly tracks how and where your state is used...', confidence: 0.95 },
{ id: '2', title: 'Component Design Patterns', author: 'Angular Team', type: 'document', snippet: 'Best practices for building reusable Angular components...', confidence: 0.88 },
{ id: '3', title: 'TypeScript 5.7 Release Notes', url: 'https://devblogs.microsoft.com/typescript/', type: 'web', snippet: 'New features include improved type inference and performance...', confidence: 0.82 }
];
// Diff Viewer
diffBefore = `function greet(name: string) {
console.log("Hello " + name);
return name;
}`;
diffAfter = `function greet(name: string, greeting = 'Hello') {
const message = \`\${greeting}, \${name}!\`;
console.log(message);
return message;
}`;
// Error State
sampleError: AiError = {
code: 'RATE_LIMIT_EXCEEDED',
message: 'You\'ve exceeded the rate limit for this model.',
severity: 'warning',
details: 'Current usage: 62/60 requests per minute. Limit resets in 45 seconds.',
retryable: true,
helpUrl: 'https://docs.example.com/rate-limits'
};
// Response Actions
responseMessageId = 'msg-resp-001';
// Tool Calls
toolCalls: ToolCall[] = [
{ id: 'tc-1', name: 'search_codebase', description: 'Searching for relevant files', status: 'complete', startedAt: new Date(Date.now() - 3000), completedAt: new Date(Date.now() - 1500), result: '3 files found' },
{ id: 'tc-2', name: 'read_file', description: 'Reading src/app/app.component.ts', status: 'complete', startedAt: new Date(Date.now() - 1500), completedAt: new Date(Date.now() - 800) },
{ id: 'tc-3', name: 'run_tests', description: 'Executing test suite', status: 'running', startedAt: new Date(Date.now() - 500) },
{ id: 'tc-4', name: 'deploy', description: 'Deploy to staging', status: 'pending' }
];
// Suggested Prompts
suggestedPrompts: SuggestedPrompt[] = [
{ id: '1', text: 'Explain this code', icon: 'code', category: 'Code', description: 'Get a detailed explanation of selected code' },
{ id: '2', text: 'Find bugs', icon: 'bug', category: 'Code', description: 'Scan for potential issues' },
{ id: '3', text: 'Write tests', icon: 'test', category: 'Testing', description: 'Generate unit tests' },
{ id: '4', text: 'Refactor this', icon: 'refactor', category: 'Code', description: 'Suggest improvements' },
{ id: '5', text: 'Summarize changes', icon: 'diff', category: 'Git', description: 'Summarize recent changes' }
];
// Cards
demoCards: CanvasCard[] = [
{ id: '1', title: 'Neural Network Basics', subtitle: 'Machine Learning', description: 'Introduction to neural network architectures and training.', badges: [{ label: 'Popular', variant: 'success' }], actions: [{ id: 'view', label: 'View', variant: 'primary' }, { id: 'save', label: 'Save', variant: 'secondary' }] },
{ id: '2', title: 'Transformer Architecture', subtitle: 'Deep Learning', description: 'How attention mechanisms revolutionized NLP.', badges: [{ label: 'Advanced', variant: 'warning' }], actions: [{ id: 'view', label: 'View', variant: 'primary' }] },
{ id: '3', title: 'Prompt Engineering', subtitle: 'AI Techniques', description: 'Best practices for crafting effective prompts.', badges: [{ label: 'New', variant: 'info' }], actions: [{ id: 'view', label: 'View', variant: 'primary' }] }
];
// Prompt Templates
promptTemplates: PromptTemplate[] = [
{ id: '1', name: 'Code Review', description: 'Review code for bugs and improvements', content: 'Review the following {{language}} code for bugs, performance issues, and best practices:\n\n{{code}}', category: 'Development', variables: [{ name: 'language', label: 'Language', type: 'select', options: ['TypeScript', 'Python', 'Go', 'Rust'] }, { name: 'code', label: 'Code', type: 'textarea', required: true }], isFavorite: true, usageCount: 24 },
{ id: '2', name: 'Explain Concept', description: 'Get a clear explanation of a topic', content: 'Explain {{topic}} in simple terms, suitable for a {{level}} audience.', category: 'Learning', variables: [{ name: 'topic', label: 'Topic', type: 'text', required: true }, { name: 'level', label: 'Level', type: 'select', options: ['beginner', 'intermediate', 'advanced'] }], usageCount: 18 },
{ id: '3', name: 'Write Tests', description: 'Generate test cases', content: 'Write comprehensive unit tests for:\n\n{{code}}', category: 'Development', variables: [{ name: 'code', label: 'Code to test', type: 'textarea', required: true }], usageCount: 15 }
];
// Markdown
markdownContent = `## AI Response
Here's a summary of the changes:
- **Added** new authentication middleware
- **Fixed** the race condition in \`useEffect\`
- **Removed** deprecated API calls
> Note: These changes require a database migration.
\`\`\`typescript
const result = await auth.verify(token);
\`\`\`
Visit the [documentation](https://example.com) for more details.`;
// Annotations
annotationText = 'Angular Signals provide a way to define reactive state. When a signal value changes, any computed signals or effects that depend on it are automatically updated. This makes it easy to build responsive UIs without manual change detection.';
annotations: Annotation[] = [
{ id: '1', startOffset: 0, endOffset: 15, text: 'Angular Signals', note: 'Core reactivity primitive in Angular 16+', color: '#ffeb3b', createdAt: new Date() },
{ id: '2', startOffset: 96, endOffset: 113, text: 'computed signals', note: 'Derived values that auto-update', color: '#4caf50', createdAt: new Date() },
{ id: '3', startOffset: 198, endOffset: 214, text: 'change detection', note: 'Zone.js-based detection is the alternative', color: '#2196f3', createdAt: new Date() }
];
// Image Gallery
demoImages: GeneratedImage[] = [
{ id: '1', url: 'https://picsum.photos/seed/ai1/400/300', thumbnailUrl: 'https://picsum.photos/seed/ai1/200/150', prompt: 'A futuristic city skyline at sunset', model: 'DALL-E 3', size: '1024x1024', createdAt: new Date(), isFavorite: true },
{ id: '2', url: 'https://picsum.photos/seed/ai2/400/300', thumbnailUrl: 'https://picsum.photos/seed/ai2/200/150', prompt: 'An abstract neural network visualization', model: 'DALL-E 3', size: '1024x1024', createdAt: new Date() },
{ id: '3', url: 'https://picsum.photos/seed/ai3/400/300', thumbnailUrl: 'https://picsum.photos/seed/ai3/200/150', prompt: 'A robot learning to paint', model: 'Midjourney', size: '1024x1024', createdAt: new Date() }
];
// Notifications
demoNotifications: AiNotification[] = [
{ id: '1', title: 'Model Updated', message: 'Claude Opus 4.6 is now available', priority: 'high', category: 'System', timestamp: new Date(), read: false, aiSummary: 'New model release with improved reasoning' },
{ id: '2', title: 'Rate Limit Warning', message: 'Approaching 80% of hourly limit', priority: 'medium', category: 'Usage', timestamp: new Date(Date.now() - 300000), read: false },
{ id: '3', title: 'Task Complete', message: 'Code review finished with 3 suggestions', priority: 'low', category: 'Tasks', timestamp: new Date(Date.now() - 600000), read: true }
];
// Multi-Agent: Sample Agents
sampleAgents: AiAgent[] = [
{ id: 'research', name: 'Research Agent', description: 'Searches the web, reads documents, and synthesizes findings into concise reports.', status: 'online', capabilities: ['web-search', 'summarization', 'fact-checking'], category: 'Research', model: 'Claude Opus 4.6', accentColor: '#6366f1', fallbackInitials: 'RA', isFavorite: true },
{ id: 'code', name: 'Code Agent', description: 'Writes, reviews, and debugs code across multiple languages and frameworks.', status: 'online', capabilities: ['code-generation', 'debugging', 'refactoring', 'testing'], category: 'Development', model: 'Claude Sonnet 4.5', accentColor: '#10b981', fallbackInitials: 'CA' },
{ id: 'writing', name: 'Writing Agent', description: 'Crafts polished prose, edits drafts, and adapts tone for different audiences.', status: 'busy', capabilities: ['copywriting', 'editing', 'translation'], category: 'Creative', model: 'Claude Opus 4.6', accentColor: '#f59e0b', fallbackInitials: 'WA' },
{ id: 'data', name: 'Data Agent', description: 'Analyzes datasets, creates visualizations, and extracts actionable insights.', status: 'online', capabilities: ['data-analysis', 'visualization', 'statistics'], category: 'Research', model: 'Claude Sonnet 4.5', accentColor: '#ec4899', fallbackInitials: 'DA', isFavorite: true },
{ id: 'creative', name: 'Creative Agent', description: 'Generates images, brainstorms ideas, and designs creative concepts.', status: 'offline', capabilities: ['ideation', 'image-generation', 'storytelling'], category: 'Creative', model: 'Claude Haiku 4.5', accentColor: '#8b5cf6', fallbackInitials: 'CR' }
];
selectedAgentId = signal('research');
// Multi-Agent: Messages
agentMessages: AgentMessage[] = [
{ id: 'm1', role: 'user', content: 'Can you research the latest trends in AI agents?', timestamp: new Date(Date.now() - 120000) },
{ id: 'm2', role: 'assistant', content: 'I\'ll look into that for you. The key trends in AI agents for 2026 include multi-agent collaboration, tool-use orchestration, and memory-augmented reasoning.', timestamp: new Date(Date.now() - 90000), agent: { agentId: 'research', agentName: 'Research Agent', agentFallbackInitials: 'RA', accentColor: '#6366f1' } },
{ id: 'm3', role: 'system', content: '', timestamp: new Date(Date.now() - 60000), systemEvent: { type: 'handoff', agentId: 'research', agentName: 'Research Agent', targetAgentId: 'code', targetAgentName: 'Code Agent', reason: 'Implementation requested', timestamp: new Date(Date.now() - 60000) } },
{ id: 'm4', role: 'assistant', content: 'I can help implement an agent orchestration system. Here\'s a basic architecture using an event-driven pattern with a central coordinator.', timestamp: new Date(Date.now() - 30000), agent: { agentId: 'code', agentName: 'Code Agent', agentFallbackInitials: 'CA', accentColor: '#10b981' } },
{ id: 'm5', role: 'system', content: '', timestamp: new Date(Date.now() - 15000), systemEvent: { type: 'joined', agentId: 'writing', agentName: 'Writing Agent', timestamp: new Date(Date.now() - 15000) } }
];
// Multi-Agent: Conversations
agentConversations: AgentConversation[] = [
{ id: 'conv-1', agent: this.sampleAgents[0], unreadCount: 0, lastMessage: 'Here are the key findings...', lastActivity: new Date(), isActive: true },
{ id: 'conv-2', agent: this.sampleAgents[1], unreadCount: 3, lastMessage: 'Code review complete.', lastActivity: new Date(Date.now() - 30000), isActive: false },
{ id: 'conv-3', agent: this.sampleAgents[2], unreadCount: 1, lastMessage: 'Draft is ready for review.', lastActivity: new Date(Date.now() - 60000), isActive: false },
{ id: 'conv-4', agent: this.sampleAgents[3], unreadCount: 0, lastMessage: 'Analysis exported to CSV.', lastActivity: new Date(Date.now() - 300000), isActive: false }
];
activeConversationId = signal('conv-1');
// Multi-Agent: Handoff
sampleHandoff: AgentHandoff = {
id: 'handoff-1',
sourceAgent: this.sampleAgents[0],
targetAgent: this.sampleAgents[1],
reason: 'Implementation of agent orchestration system requested by user',
context: 'The user wants to build an event-driven multi-agent architecture with a central coordinator pattern.',
status: 'in-progress',
initiatedAt: new Date(Date.now() - 5000)
};
sampleHandoffComplete: AgentHandoff = {
id: 'handoff-2',
sourceAgent: this.sampleAgents[1],
targetAgent: this.sampleAgents[2],
reason: 'Code complete, documentation needed',
status: 'complete',
initiatedAt: new Date(Date.now() - 60000),
completedAt: new Date(Date.now() - 30000)
};
// Multi-Agent: Activity
activityEntries: AgentActivityEntry[] = [
{ id: 'act-1', agentId: 'research', agentName: 'Research Agent', agentFallbackInitials: 'RA', action: 'Searching web for AI agent trends', detail: 'Querying multiple sources including academic papers and tech blogs.', status: 'complete', startedAt: new Date(Date.now() - 120000), completedAt: new Date(Date.now() - 90000) },
{ id: 'act-2', agentId: 'research', agentName: 'Research Agent', agentFallbackInitials: 'RA', action: 'Synthesizing findings', status: 'complete', startedAt: new Date(Date.now() - 90000), completedAt: new Date(Date.now() - 60000) },
{ id: 'act-3', agentId: 'code', agentName: 'Code Agent', agentFallbackInitials: 'CA', action: 'Generating orchestration architecture', detail: 'Building event-driven coordinator with TypeScript interfaces.', status: 'active', startedAt: new Date(Date.now() - 30000) },
{ id: 'act-4', agentId: 'code', agentName: 'Code Agent', agentFallbackInitials: 'CA', action: 'Running test suite', status: 'active', startedAt: new Date(Date.now() - 10000) },
{ id: 'act-5', agentId: 'writing', agentName: 'Writing Agent', agentFallbackInitials: 'WA', action: 'Failed to access document store', status: 'error', startedAt: new Date(Date.now() - 15000), completedAt: new Date(Date.now() - 14000) }
];
onAgentSelect(agent: AiAgent) {
this.selectedAgentId.set(agent.id);
console.log('[Demo] Agent selected:', agent.name);
}
onConversationSwitch(conversation: AgentConversation) {
this.activeConversationId.set(conversation.id);
console.log('[Demo] Conversation switched:', conversation.agent.name);
}
setView(view: 'canvas' | 'components') { setView(view: 'canvas' | 'components') {
this.currentView.set(view); this.currentView.set(view);
} }
@@ -57,10 +359,30 @@ console.log(results);`;
console.log('Selected conversation:', conversation); console.log('Selected conversation:', conversation);
} }
toggleTheme() {
const next = this.currentTheme() === 'apple' ? 'mist' : 'apple';
document.body.classList.remove(`theme-${this.currentTheme()}`);
document.body.classList.add(`theme-${next}`);
this.currentTheme.set(next);
}
toggleDarkMode() {
this.darkMode.update(v => !v);
document.body.classList.toggle('theme-dark', this.darkMode());
}
toggleSidebar() {
this.sidebarOpen.update(v => !v);
}
cycleAvatarState() { cycleAvatarState() {
const states: ('idle' | 'thinking' | 'speaking')[] = ['idle', 'thinking', 'speaking']; const states: ('idle' | 'thinking' | 'speaking')[] = ['idle', 'thinking', 'speaking'];
const currentIndex = states.indexOf(this.avatarState()); const currentIndex = states.indexOf(this.avatarState());
const nextIndex = (currentIndex + 1) % states.length; const nextIndex = (currentIndex + 1) % states.length;
this.avatarState.set(states[nextIndex]); this.avatarState.set(states[nextIndex]);
} }
onLog(event: string, data?: unknown) {
console.log(`[Demo] ${event}`, data);
}
} }

View File

@@ -3,8 +3,9 @@
@use '../../sda-frontend/libs/base-ui/src/styles/tokens/semantic'; @use '../../sda-frontend/libs/base-ui/src/styles/tokens/semantic';
@use '../../sda-frontend/libs/base-ui/src/styles/tokens/component'; @use '../../sda-frontend/libs/base-ui/src/styles/tokens/component';
// Import Apple theme (applies via class) // Import themes (applied via class)
@use '../../sda-frontend/libs/base-ui/src/styles/themes/apple'; @use '../../sda-frontend/libs/base-ui/src/styles/themes/apple';
@use '../../sda-frontend/libs/base-ui/src/styles/themes/mist';
// Base resets // Base resets
*, *,
@@ -15,9 +16,12 @@
padding: 0; padding: 0;
} }
html, html {
body {
height: 100%; height: 100%;
}
body {
min-height: 100%;
font-family: var(--font-family-sans, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif); font-family: var(--font-family-sans, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif);
font-size: var(--font-size-base, 16px); font-size: var(--font-size-base, 16px);
line-height: var(--line-height-base, 1.5); line-height: var(--line-height-base, 1.5);
@@ -27,7 +31,7 @@ body {
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
// Apply theme class to body // Default theme class (overridden by JS theme switching)
body { body {
@extend .theme-apple; @extend .theme-apple;
} }
@@ -57,11 +61,51 @@ body {
color: var(--color-text-primary); color: var(--color-text-primary);
} }
.app__sidebar-toggle {
display: none;
align-items: center;
justify-content: center;
width: 36px;
height: 36px;
border: none;
background: transparent;
border-radius: var(--radius-md, 8px);
cursor: pointer;
color: var(--color-text-secondary);
transition: all 0.2s ease;
&:hover {
background: var(--color-bg-tertiary, #ebebeb);
color: var(--color-text-primary);
}
}
.app__nav { .app__nav {
display: flex; display: flex;
align-items: center;
gap: var(--spacing-md, 16px); gap: var(--spacing-md, 16px);
} }
.app__theme-toggle {
display: flex;
align-items: center;
justify-content: center;
width: 36px;
height: 36px;
border: 1px solid var(--color-border-primary, #e0e0e0);
background: var(--color-bg-tertiary, #ebebeb);
border-radius: var(--radius-md, 8px);
cursor: pointer;
font-size: 18px;
line-height: 1;
transition: all 0.2s ease;
margin-left: var(--spacing-sm, 8px);
&:hover {
background: var(--color-bg-primary, #fff);
}
}
.app__nav-link { .app__nav-link {
padding: var(--spacing-sm, 8px) var(--spacing-md, 16px); padding: var(--spacing-sm, 8px) var(--spacing-md, 16px);
color: var(--color-text-secondary); color: var(--color-text-secondary);
@@ -90,17 +134,24 @@ body {
overflow: hidden; overflow: hidden;
} }
.app__sidebar-backdrop {
display: none;
}
.app__sidebar { .app__sidebar {
width: 280px; width: 280px;
background: var(--color-bg-secondary, #f5f5f5); background: var(--color-bg-secondary, #f5f5f5);
border-right: 1px solid var(--color-border-primary, #e0e0e0); border-right: 1px solid var(--color-border-primary, #e0e0e0);
overflow-y: auto; overflow-y: auto;
flex-shrink: 0;
transition: transform 0.3s ease;
} }
.app__content { .app__content {
flex: 1; flex: 1;
overflow-y: auto; overflow-y: auto;
padding: var(--spacing-lg, 24px); padding: var(--spacing-lg, 24px);
background-color: var(--color-bg-primary);
} }
// Demo sections // Demo sections
@@ -146,3 +197,67 @@ body {
border-bottom: 1px solid var(--color-border-primary, #e0e0e0); border-bottom: 1px solid var(--color-border-primary, #e0e0e0);
} }
} }
// =============================================================================
// RESPONSIVE — Tablet & Mobile
// =============================================================================
// Tablet: sidebar becomes a slide-over drawer
@media (max-width: 1024px) {
.app__sidebar-toggle {
display: flex;
}
.app__sidebar {
position: fixed;
top: 0;
left: 0;
bottom: 0;
z-index: 200;
transform: translateX(-100%);
box-shadow: none;
&--open {
transform: translateX(0);
box-shadow: 4px 0 24px rgba(0, 0, 0, 0.15);
}
}
.app__sidebar-backdrop {
display: block;
position: fixed;
inset: 0;
z-index: 199;
background: rgba(0, 0, 0, 0.4);
animation: fadeIn 0.2s ease;
}
}
// Mobile: tighter spacing, single-column demo grid
@media (max-width: 640px) {
.app__header {
padding: var(--spacing-sm, 8px) var(--spacing-md, 16px);
}
.app__nav-link {
padding: var(--spacing-xs, 4px) var(--spacing-sm, 8px);
font-size: var(--font-size-sm, 14px);
}
.app__content {
padding: var(--spacing-md, 16px);
}
.demo-section {
padding: var(--spacing-md, 16px);
}
.demo-grid {
grid-template-columns: 1fr;
}
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}