Story: Migrate Tabs to Lit Component
As a user I want clickable tabs that switch views So that I can navigate between Bolts, Specs, and Overview
As a user I want clickable tabs that switch views So that I can navigate between Bolts, Specs, and Overview
--- id: vscode-extension-story-wlm-024 unit: webview-lit-migration intent: 011-vscode-extension status: complete implemented: true priority: must created: 2025-12-26 ---
**As a** user **I want** clickable tabs that switch views **So that** I can navigate between Bolts, Specs, and Overview
// src/webview/components/tabs/view-tabs.ts
import { LitElement, html, css } from 'lit';
import { customElement, property } from 'lit/decorators.js';
export type TabId = 'bolts' | 'specs' | 'overview';
@customElement('view-tabs')
export class ViewTabs extends LitElement {
@property({ type: String }) activeTab: TabId = 'bolts';
static styles = css`
:host {
display: flex;
background: var(--vscode-editor-background);
border-bottom: 1px solid var(--vscode-sideBarSectionHeader-border);
}
button {
flex: 1;
padding: 10px;
font-size: 11px;
font-weight: 600;
text-align: center;
background: none;
border: none;
color: var(--vscode-descriptionForeground);
cursor: pointer;
border-bottom: 2px solid transparent;
transition: color 0.15s, border-color 0.15s;
}
button.active {
color: var(--accent-primary, #f97316);
border-bottom-color: var(--accent-primary, #f97316);
}
`;
render() {
return html`
<button
class=${this.activeTab === 'bolts' ? 'active' : ''}
@click=${() => this._selectTab('bolts')}>
⚡ Bolts
</button>
<button
class=${this.activeTab === 'specs' ? 'active' : ''}
@click=${() => this._selectTab('specs')}>
📋 Specs
</button>
<button
class=${this.activeTab === 'overview' ? 'active' : ''}
@click=${() => this._selectTab('overview')}>
📊 Overview
</button>
`;
}
private _selectTab(tab: TabId) {
this.dispatchEvent(new CustomEvent('tab-change', {
detail: { tab },
bubbles: true,
composed: true
}));
}
}
declare global {
interface HTMLElementTagNameMap {
'view-tabs': ViewTabs;
}
}// src/webview/components/app.ts
import { LitElement, html, css } from 'lit';
import { customElement, state } from 'lit/decorators.js';
import './tabs/view-tabs.js';
import type { TabId } from './tabs/view-tabs.js';
import { vscode } from '../index.js';
@customElement('specsmd-app')
export class SpecsmdApp extends LitElement {
@state() private activeTab: TabId = 'bolts';
static styles = css`
:host {
display: flex;
flex-direction: column;
height: 100vh;
}
`;
render() {
return html`
<view-tabs
.activeTab=${this.activeTab}
@tab-change=${this._handleTabChange}>
</view-tabs>
<!-- View content here -->
`;
}
private _handleTabChange(e: CustomEvent<{ tab: TabId }>) {
this.activeTab = e.detail.tab;
vscode.postMessage({ type: 'tabChange', tab: e.detail.tab });
}
}