@azure-fluent-storybook/components
v0.4.0
Published
Azure Portal–styled Fluent v9 components and themes
Readme
Azure Storybook
Fluent UI React v9 component library themed as Azure Portal, built with Storybook 10.
Quick Start
npm install
npm run dev # Start Storybook dev serverScripts
| Command | Description |
| ------------------------ | --------------------------------------- |
| npm run dev | Start Storybook dev server on port 6006 |
| npm run build | Build static Storybook site |
| npm run build:registry | Generate component-registry.json |
| npm run build:all | Build registry + Storybook |
| npm run lint | Run ESLint |
| npm run format | Run Prettier |
| npm run chromatic | Push to Chromatic for visual testing |
Structure
src/
├── components/ # Azure-specific composed components
│ ├── AzureBreadcrumb.tsx
│ ├── CommandBar.tsx
│ ├── FilterBar.tsx
│ └── PageTitleBar.tsx
├── stories/
│ ├── foundations/ # Design tokens: Colors, Typography, Spacing, Shadows
│ ├── components/ # Fluent v9 components: Button, Input, Card, etc.
│ ├── composed/ # Composed component stories
│ └── templates/ # Full page templates (ResourceListPage)
├── themes/
│ ├── azureThemes.ts # Azure light/dark/HC themes
│ ├── coherenceTokens.ts # Structured token reference
│ └── index.ts
└── component-registry.json # Auto-generated LLM context
llm-context/ # LLM-optimized documentation
├── azure-theme.md # Theme tokens quick reference
├── component-patterns.md # Azure composition patterns
└── styling-guide.md # Griffel + token usage guide
scripts/
├── extract-tokens.ts # One-time token extraction from Coherence CDN
└── generate-registry.ts # Story metadata → registry JSONTheme System
Three themes extracted from Azure Portal's Coherence design system:
- Azure Light — default, white background with Azure blue (#0f6cbd) brand
- Azure Dark — dark mode with matching brand ramp
- High Contrast — WCAG AAA compliant
Toggle themes in the Storybook toolbar.
Using the Theme System in a Consuming App
Install the package:
npm install @azure-fluent-storybook/components @fluentui/react-componentsThe package exposes a 3-axis theming system matching the Storybook toolbar:
| Axis | API | What it controls |
| --- | --- | --- |
| Product | getAllProductThemes() | Brand ramp + product-specific token overrides (Azure, SRE Agent) |
| Appearance | AppearanceMode type | Light / Dark / High Contrast base |
| Design System | getAllSkins() | Skin-level token overrides (Fluent 2, Coherence, Ibiza, Fluent 1, Azure Fluent, CoreAI) |
List available products, appearances, and design systems
import {
getAllProductThemes,
getAllSkins,
type AppearanceMode,
} from '@azure-fluent-storybook/components';
const products = getAllProductThemes();
// → [{ id: 'azure', displayName: 'Azure', ... }, { id: 'sre-agent', ... }]
const skins = getAllSkins();
// → [{ id: 'fluent2', displayName: 'Fluent 2' }, { id: 'coherence', ... }, ...]
const appearances: AppearanceMode[] = ['light', 'dark', 'high-contrast'];Resolve a theme and render
import { FluentProvider } from '@fluentui/react-components';
import { resolveTheme } from '@azure-fluent-storybook/components';
import type { AppearanceMode, DesignSystemId } from '@azure-fluent-storybook/components';
function App() {
const [product, setProduct] = useState('azure');
const [appearance, setAppearance] = useState<AppearanceMode>('light');
const [designSystem, setDesignSystem] = useState<DesignSystemId>('fluent2');
const theme = resolveTheme(product, appearance, designSystem);
return (
<FluentProvider theme={theme}>
{/* your app */}
</FluentProvider>
);
}Build a theme switcher
import {
getAllProductThemes,
getAllSkins,
resolveTheme,
type AppearanceMode,
type DesignSystemId,
} from '@azure-fluent-storybook/components';
function ThemeSwitcher({ onThemeChange }) {
const products = getAllProductThemes();
const skins = getAllSkins();
const appearances: AppearanceMode[] = ['light', 'dark', 'high-contrast'];
const [productId, setProductId] = useState('azure');
const [appearance, setAppearance] = useState<AppearanceMode>('light');
const [skinId, setSkinId] = useState<DesignSystemId>('fluent2');
useEffect(() => {
onThemeChange(resolveTheme(productId, appearance, skinId));
}, [productId, appearance, skinId]);
return (
<div style={{ display: 'flex', gap: 12 }}>
<select value={productId} onChange={e => setProductId(e.target.value)}>
{products.map(p => <option key={p.id} value={p.id}>{p.displayName}</option>)}
</select>
<select value={appearance} onChange={e => setAppearance(e.target.value as AppearanceMode)}>
{appearances.map(a => <option key={a} value={a}>{a}</option>)}
</select>
<select value={skinId} onChange={e => setSkinId(e.target.value as DesignSystemId)}>
{skins.map(s => <option key={s.id} value={s.id}>{s.displayName}</option>)}
</select>
</div>
);
}Register custom products or design system skins
import { registerProductTheme, registerSkin } from '@azure-fluent-storybook/components';
registerProductTheme({
id: 'my-product',
displayName: 'My Product',
description: 'Custom product theme',
brand: myBrandRamp,
lightOverrides: { /* ... */ },
});
registerSkin({
id: 'my-skin' as DesignSystemId,
displayName: 'My Skin',
description: 'Custom design system',
sections: { colors: {}, shape: {}, elevation: {}, density: {}, typography: {} },
});Component Taxonomy
| Category | Description | | --------------- | ------------------------------------------------------------------------------ | | Foundations | Color swatches, typography scale, spacing, shadows | | Components | Stock Fluent v9: Button, Input, Card, Dialog, DataGrid, DataDisplay, Selection | | Composed | Azure-specific: Breadcrumb, PageTitleBar, CommandBar, FilterBar | | Templates | Full pages: ResourceListPage (resource list with all composed components) |
LLM Optimization
This Storybook is designed for LLM context:
.copilot-instructions.md— project conventions for Copilotllm-context/— structured docs for theme, patterns, and stylingcomponent-registry.json— machine-readable component index- CSF3 stories with autodocs for self-documenting APIs
Deployment
Deployed to Azure Static Web Apps via GitHub Actions.
Required secrets:
AZURE_STATIC_WEB_APPS_API_TOKEN— SWA deployment tokenCHROMATIC_PROJECT_TOKEN— Chromatic project token
Agentation Integration
Stakeholders can annotate UI issues directly in the deployed Storybook. Annotations automatically become GitHub Issues assigned to GitHub Copilot for automated fix PRs.
Architecture
Browser (Storybook)
→ POST /api/feedback (SWA managed function)
→ GitHub repository_dispatch
→ GitHub Actions (.github/workflows/agentation-feedback.yml)
→ GitHub Issue (assigned to Copilot)
→ Copilot PREnvironment Variables
Set the following in Azure Portal → Static Web App → Configuration → Application Settings:
| Variable | Required | Description |
| --- | --- | --- |
| GITHUB_TOKEN | ✅ Yes | GitHub token with contents: write scope. Used by the /api/feedback proxy to trigger repository_dispatch. |
Recommended token types (in order of preference):
- GitHub App installation token — longest-lived, fine-grained permissions
- Fine-grained PAT — set expiry to 1 year, scope to
unthinkmedia/AzureStorybook, permission:Contents: Read and write - Classic PAT —
reposcope (broader than needed but works)
⚠️ Never commit the token. Set it only in Azure Portal or as a GitHub Actions secret.
How Stakeholders Use It
- Visit the deployed Storybook at the Azure SWA URL
- Click the Agentation toolbar icon in the story canvas
- Click on any UI element to annotate it — add a comment describing the issue
- Click "Send Annotations" to submit all annotations as a single GitHub Issue
- The issue is auto-assigned to Copilot, which will create a fix PR
Local Development
To run the API function locally alongside Storybook:
Option A: Azure Functions Core Tools
# Terminal 1 — Storybook
npm run dev
# Terminal 2 — API function
cd api
npm install
# Create local settings (not committed)
echo '{"IsEncrypted":false,"Values":{"FUNCTIONS_WORKER_RUNTIME":"node","GITHUB_TOKEN":"your-token-here"}}' > local.settings.json
npx func startOption B: SWA CLI (proxies both together)
npm install -g @azure/static-web-apps-cli
npm run build
swa start storybook-static --api-location api
api/local.settings.jsonis already in.gitignore— safe to create locally.
Tech Stack
- React 18 + TypeScript 5.7
- Fluent UI React v9 (
@fluentui/react-components) - Storybook 10 + Vite 6
- Griffel CSS-in-JS
- Chromatic visual regression
- Azure Static Web Apps
