[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M69BAGZFB3BTKYXM9FVGWBA8":3,"skill-same:01M69BAGZFB3BTKYXM9FVGWBA8":133},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":22,"linkOnly":23,"markdownContent":24,"markdownBody":25,"frontMatter":26,"refName":27,"commitSha":28,"skillDirectory":29,"forksCount":30,"repositoryHtmlUrl":31,"repositoryHomepageUrl":32,"ownerHtmlUrl":33,"ownerBlogUrl":34,"ownerDescription":35,"updatedAt":36,"install":37,"resources":50},"01M69BAGZFB3BTKYXM9FVGWBA8","features-variants",null,"Definitive guide to the app-local prototype feature\u002Fvariant system: configuration, `useFeature()`, URL params, and anti-patterns. USE WHEN: editing `apps\u002Fprototype\u002Fapp\u002Ffeatures.json`, using `useFeature()` in routes, creating design alternatives, A\u002FB comparisons, layout options, alternate versions, or switching between design proposals. Trigger keywords: feature, variant, version, option, alternate, prototype, design option, A\u002FB, comparison, switch between, try a different, another way, layout option, explore, side by side.","microsoft\u002Fcoreai-design-system","microsoft","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F6154722?v=4","01M698VYDXZ6JW5WH3CRV650R8","Microsoft","01M69B9ED3TGFRHF93Y8JF1Y18",2,"CC-BY-4.0","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fcoreai-design-system\u002Fblob\u002Fmain\u002Fapps\u002Fprototype\u002F.github\u002Fskills\u002Ffeatures-variants\u002FSKILL.md",[],[19,20,21],"features variants","design","prototype","2026-11-10 10:12:30",false,"---\nname: features-variants\ndescription: 'Definitive guide to the app-local prototype feature\u002Fvariant system: configuration, `useFeature()`, URL params, and anti-patterns. USE WHEN: editing `apps\u002Fprototype\u002Fapp\u002Ffeatures.json`, using `useFeature()` in routes, creating design alternatives, A\u002FB comparisons, layout options, alternate versions, or switching between design proposals. Trigger keywords: feature, variant, version, option, alternate, prototype, design option, A\u002FB, comparison, switch between, try a different, another way, layout option, explore, side by side.'\n---\n\n# Feature & Variant System Instructions\n\nPaths below are relative to `apps\u002Fprototype\u002F`.\n\n## What Is the Feature System?\n\nCoreAI Sandbox has a **built-in feature\u002Fvariant system** for tracking prototyped features and presenting multiple UI alternatives on the same page. It uses a neutral floating selector with small, elevated tab links and a brand-colored Prototype InteractionTag menu trigger to distinguish itself from the designs being prototyped.\n\nA **feature** represents a prototyped change from main. A feature may:\n\n- Have **variants** — multiple design alternatives that can be switched between (e.g., different layouts)\n- Be **variant-less** — a single prototyped change with no alternatives (just a link to the location of the change)\n\n**Multiple features can be active simultaneously** on the same page, each with their own independent variant selection.\n\n**You MUST use this system** whenever a user asks to:\n\n- Try a different layout, design, or approach for a page or section\n- Create an \"option B\" or \"version 2\" of something\n- Compare two or more designs side by side\n- Add an A\u002FB test or design alternative\n- Explore an alternate version of a feature\n\n**Do NOT** build a custom toggle, tab switcher, or parallel component tree. The feature system already handles all of this.\n\n> For a guided walk-through tailored to designer-style requests, invoke the `prototype-app-new-variant` skill.\n\n---\n\n## How It Works (Summary)\n\n1. **Features** are defined in `app\u002Ffeatures.json`. Each feature may have variant options. The registry is currently empty; add definitions to its top-level `features` array.\n2. Active variants are encoded in the URL as repeated `?variant=feature-id--variant-id` params — every combination is a shareable deep link.\n3. When a user navigates to a page with applicable features, the system **automatically populates** selections from valid URL values, then remembered client-navigation selections, then the first configured variant.\n4. A **draggable floating switcher strip** appears bottom-center by default showing each feature's variants as small, elevated router-backed tab links, when applicable variants exist and **Show feature selector** is enabled in the Prototype menu.\n5. Route components use the `useFeature(featureId)` hook to conditionally render based on the active variant.\n6. **Multiple features** can be active on the same page simultaneously with independent variant selections.\n\n---\n\n## Architecture — Key Files\n\n| File                                            | Purpose                                                                |\n| ----------------------------------------------- | ---------------------------------------------------------------------- |\n| `app\u002Ffeatures.json`                             | App configuration: all features, routes, and variant labels            |\n| `app\u002Ffeatures\u002FfeatureConfig.ts`                 | Feature schema, configuration validation, and route\u002FURL helpers        |\n| `app\u002Ffeatures\u002FFeatureContext.ts`                | Feature context and public context types                               |\n| `app\u002Ffeatures\u002FFeatureProvider.tsx`              | URL state, route matching, defaults, and floating selector composition |\n| `app\u002Ffeatures\u002FFeatureContext.ts` (`useFeature`) | Public hook for reading the active variant                             |\n| `app\u002Ffeatures\u002FFeatureFloatingUI.tsx`            | Floating variant selector                                              |\n| `app\u002Ffeatures\u002FFeatureMenu.tsx`                  | Prototype feature menu used by the app shell                           |\n\n---\n\n## URL Structure\n\nVariants are encoded as repeated `variant` query params:\n\n```\n# Single feature with one variant active\n?variant=agent-playground-layout--accordion\n\n# Two features active simultaneously\n?variant=agent-playground-layout--accordion&variant=code-tab--visible\n\n# Variant-less features have no variant query param (they apply only on matching routes)\n```\n\nEach `variant` param is a `feature-id--variant-id` pair using kebab-case string identifiers.\n\n---\n\n## Step-by-Step: Adding a New Variant to an Existing Feature\n\nIf a feature already exists in `app\u002Ffeatures.json` and has variants, add a new variant:\n\n### 1. Update `app\u002Ffeatures.json`\n\nAdd a new entry to the `variants` array of the existing feature:\n\n```json\n{\n  \"id\": \"your-new-variant\",\n  \"label\": \"Your New Design Name\"\n}\n```\n\nVariant `id` values are **kebab-case strings**. Choose a descriptive, URL-friendly name.\n\n### 2. Update the Route Component\n\nIn the route component that uses this feature, add a conditional branch for the new variant:\n\n```tsx\nimport { useFeature } from '~\u002Ffeatures\u002FFeatureContext';\n\nfunction MyPage() {\n  const variant = useFeature('agent-playground-layout');\n\n  if (variant === 'accordion') return \u003CAccordionLayout \u002F>;\n  if (variant === 'your-new-variant') return \u003CYourNewDesign \u002F>; \u002F\u002F ← new\n  return \u003CDefaultLayout \u002F>; \u002F\u002F first variant or null\n}\n```\n\nThat's it. The floating UI automatically shows the new tab link when the selector is visible on an applicable route.\n\n---\n\n## Step-by-Step: Creating a New Feature\n\n### Feature WITH Variants\n\nWhen the feature explores multiple design alternatives:\n\n#### 1. Add the feature to `app\u002Ffeatures.json`\n\nAdd this object to the registry's top-level `features` array:\n\n```json\n{\n  \"id\": \"agent-playground-layout\",\n  \"label\": \"Agent Playground Layout\",\n  \"notes\": \"What design question this feature explores.\",\n  \"defaultRoute\": \"\u002Fbuild\u002Fagents\u002F:agent\u002Fplayground\",\n  \"defaultParams\": { \"agent\": \"agent-001\" },\n  \"routes\": [\"\u002Fbuild\u002Fagents\u002F:agent\u002Fplayground\"],\n  \"variants\": [\n    { \"id\": \"side-panel\", \"label\": \"Side Panel (Current)\" },\n    { \"id\": \"accordion\", \"label\": \"Accordion Layout\" }\n  ]\n}\n```\n\n#### 2. Add `useFeature()` to the Route Component\n\n```tsx\nimport { useFeature } from '~\u002Ffeatures\u002FFeatureContext';\n\nfunction MyPage() {\n  const variant = useFeature('agent-playground-layout');\n\n  if (variant === 'accordion') return \u003CAccordionLayout \u002F>;\n  return \u003CDefaultLayout \u002F>; \u002F\u002F 'side-panel' or null\n}\n```\n\n### Feature WITHOUT Variants\n\nWhen the feature is a single prototyped change with no alternatives:\n\n#### 1. Add the feature to `app\u002Ffeatures.json`\n\nAdd this object to the registry's top-level `features` array:\n\n```json\n{\n  \"id\": \"dark-mode-revamp\",\n  \"label\": \"Dark Mode Revamp\",\n  \"notes\": \"Updated dark mode colors across settings page.\",\n  \"defaultRoute\": \"\u002Fsettings\u002Fappearance\",\n  \"routes\": [\"\u002Fsettings\u002Fappearance\"]\n}\n```\n\nNo `variants` array needed. The feature appears in the Prototype InteractionTag menu as a navigation link. No `useFeature()` call is needed in the route component.\n\n---\n\n**Field reference:**\n\n| Field           | Required | Description                                                                                                                    |\n| --------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------ |\n| `id`            | Yes      | Kebab-case string identifier (used in URL params)                                                                              |\n| `label`         | Yes      | Human-readable name shown in the floating UI                                                                                   |\n| `notes`         | Yes      | Design context — what question does this feature explore?                                                                      |\n| `defaultRoute`  | Yes      | The primary route to navigate to from the Prototype menu; must be in `routes`                                                  |\n| `defaultParams` | No       | Fallback values for `:param` segments (e.g., `{ \"agent\": \"agent-001\" }`); required when `defaultRoute` has required parameters |\n| `routes`        | Yes      | Route patterns this feature applies to. Supports `:param` wildcards                                                            |\n| `variants`      | No       | Array of `{ id: string, label: string }`. Omit for variant-less features                                                       |\n\n**Route pattern matching:** A pattern like `\u002Fbuild\u002Fagents\u002F:agent\u002Fplayground` matches any URL of that shape. The `:param` segments are wildcards matched segment-by-segment.\n\n---\n\n## `useFeature()` Return Values\n\n```tsx\nconst result = useFeature('feature-id');\n\u002F\u002F Returns:\n\u002F\u002F   string  — active variant id (e.g., 'accordion') if feature has variants\n\u002F\u002F   true    — if feature is variant-less and applies to current route\n\u002F\u002F   null    — if feature doesn't apply to current route\n```\n\n---\n\n## Multi-Feature Pages\n\nA single page can have **multiple features** active simultaneously. Each component on the page calls `useFeature()` with its own feature ID independently.\n\n```tsx\nconst layout = useFeature('agent-playground-layout');\nconst codeTab = useFeature('code-tab-visibility');\n\n\u002F\u002F Both return their own variant independently\n\u002F\u002F URL: ?variant=agent-playground-layout--accordion&variant=code-tab-visibility--visible\n```\n\nThe floating strip shows variant tab links for all applicable features with variants, separated by dividers.\n\n---\n\n## Variant Scope — What Can Change?\n\nA variant can change **anything** within its route component:\n\n- **Full page replacement**: Return entirely different component trees per variant\n- **Section swap**: Replace just one panel, card, or section\n- **Prop changes**: Same component with different props\n- **Style changes**: Different CSS classes per variant\n- **Data presentation**: Same data shown in different formats\n- **Conditional features**: Show\u002Fhide features like buttons, tabs, or panels\n\n### Example — Small Change (text variant)\n\n```tsx\nconst variant = useFeature('hero-text');\nreturn (\n  \u003CText preset=\"heading2\" foreground={1}>\n    {variant === 'version-b'\n      ? 'Start Building with AI'\n      : variant === 'version-c'\n        ? 'Your AI Development Platform'\n        : 'Welcome to CoreAI Sandbox'}\n  \u003C\u002FText>\n);\n```\n\n### Example — Full Layout Swap\n\n```tsx\nconst variant = useFeature('playground-layout');\nif (variant === 'accordion') return \u003CAccordionLayout \u002F>;\nif (variant === 'dialog') return \u003CDialogLayout \u002F>;\nreturn \u003CSidePanelLayout \u002F>;\n```\n\n---\n\n## Variant-Exclusive Routes\n\nSome variants introduce routes that only exist for that variant (e.g., a \"Code\" tab route that only exists when `go-to-code--tab` is active). If a user lands on a variant-exclusive route with the wrong variant selected, the route component should redirect:\n\n```tsx\nconst variant = useFeature('go-to-code');\n\n\u002F\u002F This route only exists for the \"tab\" variant — redirect otherwise\nif (variant !== 'tab') return \u003CNavigate to=\"..\u002Fplayground\" replace \u002F>;\n```\n\nKeep this guard **in the route component itself**, colocated with the route that knows it's conditional. Do not add redirect logic to `FeatureProvider.tsx`.\n\n---\n\n## Anti-Patterns — Do NOT Do These\n\n| ❌ Don't                                                                                                                             | ✅ Do Instead                                               |\n| ------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------- |\n| Build a custom toggle\u002Fswitch for design alternatives                                                                                 | Use `useFeature()` + `app\u002Ffeatures.json`                    |\n| Store variant state in localStorage or React state                                                                                   | URL query params (`?variant=...`) handle everything         |\n| Create separate routes for each design alternative                                                                                   | One route, conditional rendering via `useFeature()`         |\n| Import feature implementation internals from `app\u002Ffeatures\u002FfeatureConfig.ts` or `app\u002Ffeatures\u002FFeatureProvider.tsx` in app components | Use `useFeature()` from `~\u002Ffeatures\u002FFeatureContext`         |\n| Hard-code variant IDs as numbers                                                                                                     | Feature and variant IDs are **kebab-case strings**          |\n| Add a new variant switching UI element into the product design                                                                       | The floating tab-link UI is the switcher — it's meta-UI     |\n| Modify app-local feature-system internals for an app feature                                                                         | Only modify `app\u002Ffeatures.json` and the consuming component |\n\n---\n\n## Troubleshooting\n\n**Floating strip doesn't appear on my page:**\n\n- Check that the route pattern in `app\u002Ffeatures.json` matches your page's URL exactly (segment-by-segment)\n- `:param` segments match any value, but the number of segments must match\n- Ensure the applicable feature has variants and **Show feature selector** is enabled in the Prototype menu\n- Check browser console for errors\n\n**`useFeature()` returns null:**\n\n- Verify you're passing the **feature ID** (string), not the variant ID\n- Verify the feature's `routes` array includes a pattern matching the current page\n\n**Variant tab links show but clicking doesn't change anything:**\n\n- Ensure your component reads `useFeature()` and conditionally renders based on it\n- Make sure you're checking the correct variant IDs (kebab-case strings)\n\n**URL params get cleared when navigating:**\n\n- This is expected for off-route `variant` params: they are removed. When returning during client navigation, valid URL selections take priority, then remembered selections, then the first configured variant. A reload uses the URL or the first variant, not remembered client-navigation state. Unrelated query params are preserved.\n","# Feature & Variant System Instructions\n\nPaths below are relative to `apps\u002Fprototype\u002F`.\n\n## What Is the Feature System?\n\nCoreAI Sandbox has a **built-in feature\u002Fvariant system** for tracking prototyped features and presenting multiple UI alternatives on the same page. It uses a neutral floating selector with small, elevated tab links and a brand-colored Prototype InteractionTag menu trigger to distinguish itself from the designs being prototyped.\n\nA **feature** represents a prototyped change from main. A feature may:\n\n- Have **variants** — multiple design alternatives that can be switched between (e.g., different layouts)\n- Be **variant-less** — a single prototyped change with no alternatives (just a link to the location of the change)\n\n**Multiple features can be active simultaneously** on the same page, each with their own independent variant selection.\n\n**You MUST use this system** whenever a user asks to:\n\n- Try a different layout, design, or approach for a page or section\n- Create an \"option B\" or \"version 2\" of something\n- Compare two or more designs side by side\n- Add an A\u002FB test or design alternative\n- Explore an alternate version of a feature\n\n**Do NOT** build a custom toggle, tab switcher, or parallel component tree. The feature system already handles all of this.\n\n> For a guided walk-through tailored to designer-style requests, invoke the `prototype-app-new-variant` skill.\n\n---\n\n## How It Works (Summary)\n\n1. **Features** are defined in `app\u002Ffeatures.json`. Each feature may have variant options. The registry is currently empty; add definitions to its top-level `features` array.\n2. Active variants are encoded in the URL as repeated `?variant=feature-id--variant-id` params — every combination is a shareable deep link.\n3. When a user navigates to a page with applicable features, the system **automatically populates** selections from valid URL values, then remembered client-navigation selections, then the first configured variant.\n4. A **draggable floating switcher strip** appears bottom-center by default showing each feature's variants as small, elevated router-backed tab links, when applicable variants exist and **Show feature selector** is enabled in the Prototype menu.\n5. Route components use the `useFeature(featureId)` hook to conditionally render based on the active variant.\n6. **Multiple features** can be active on the same page simultaneously with independent variant selections.\n\n---\n\n## Architecture — Key Files\n\n| File                                            | Purpose                                                                |\n| ----------------------------------------------- | ---------------------------------------------------------------------- |\n| `app\u002Ffeatures.json`                             | App configuration: all features, routes, and variant labels            |\n| `app\u002Ffeatures\u002FfeatureConfig.ts`                 | Feature schema, configuration validation, and route\u002FURL helpers        |\n| `app\u002Ffeatures\u002FFeatureContext.ts`                | Feature context and public context types                               |\n| `app\u002Ffeatures\u002FFeatureProvider.tsx`              | URL state, route matching, defaults, and floating selector composition |\n| `app\u002Ffeatures\u002FFeatureContext.ts` (`useFeature`) | Public hook for reading the active variant                             |\n| `app\u002Ffeatures\u002FFeatureFloatingUI.tsx`            | Floating variant selector                                              |\n| `app\u002Ffeatures\u002FFeatureMenu.tsx`                  | Prototype feature menu used by the app shell                           |\n\n---\n\n## URL Structure\n\nVariants are encoded as repeated `variant` query params:\n\n```\n# Single feature with one variant active\n?variant=agent-playground-layout--accordion\n\n# Two features active simultaneously\n?variant=agent-playground-layout--accordion&variant=code-tab--visible\n\n# Variant-less features have no variant query param (they apply only on matching routes)\n```\n\nEach `variant` param is a `feature-id--variant-id` pair using kebab-case string identifiers.\n\n---\n\n## Step-by-Step: Adding a New Variant to an Existing Feature\n\nIf a feature already exists in `app\u002Ffeatures.json` and has variants, add a new variant:\n\n### 1. Update `app\u002Ffeatures.json`\n\nAdd a new entry to the `variants` array of the existing feature:\n\n```json\n{\n  \"id\": \"your-new-variant\",\n  \"label\": \"Your New Design Name\"\n}\n```\n\nVariant `id` values are **kebab-case strings**. Choose a descriptive, URL-friendly name.\n\n### 2. Update the Route Component\n\nIn the route component that uses this feature, add a conditional branch for the new variant:\n\n```tsx\nimport { useFeature } from '~\u002Ffeatures\u002FFeatureContext';\n\nfunction MyPage() {\n  const variant = useFeature('agent-playground-layout');\n\n  if (variant === 'accordion') return \u003CAccordionLayout \u002F>;\n  if (variant === 'your-new-variant') return \u003CYourNewDesign \u002F>; \u002F\u002F ← new\n  return \u003CDefaultLayout \u002F>; \u002F\u002F first variant or null\n}\n```\n\nThat's it. The floating UI automatically shows the new tab link when the selector is visible on an applicable route.\n\n---\n\n## Step-by-Step: Creating a New Feature\n\n### Feature WITH Variants\n\nWhen the feature explores multiple design alternatives:\n\n#### 1. Add the feature to `app\u002Ffeatures.json`\n\nAdd this object to the registry's top-level `features` array:\n\n```json\n{\n  \"id\": \"agent-playground-layout\",\n  \"label\": \"Agent Playground Layout\",\n  \"notes\": \"What design question this feature explores.\",\n  \"defaultRoute\": \"\u002Fbuild\u002Fagents\u002F:agent\u002Fplayground\",\n  \"defaultParams\": { \"agent\": \"agent-001\" },\n  \"routes\": [\"\u002Fbuild\u002Fagents\u002F:agent\u002Fplayground\"],\n  \"variants\": [\n    { \"id\": \"side-panel\", \"label\": \"Side Panel (Current)\" },\n    { \"id\": \"accordion\", \"label\": \"Accordion Layout\" }\n  ]\n}\n```\n\n#### 2. Add `useFeature()` to the Route Component\n\n```tsx\nimport { useFeature } from '~\u002Ffeatures\u002FFeatureContext';\n\nfunction MyPage() {\n  const variant = useFeature('agent-playground-layout');\n\n  if (variant === 'accordion') return \u003CAccordionLayout \u002F>;\n  return \u003CDefaultLayout \u002F>; \u002F\u002F 'side-panel' or null\n}\n```\n\n### Feature WITHOUT Variants\n\nWhen the feature is a single prototyped change with no alternatives:\n\n#### 1. Add the feature to `app\u002Ffeatures.json`\n\nAdd this object to the registry's top-level `features` array:\n\n```json\n{\n  \"id\": \"dark-mode-revamp\",\n  \"label\": \"Dark Mode Revamp\",\n  \"notes\": \"Updated dark mode colors across settings page.\",\n  \"defaultRoute\": \"\u002Fsettings\u002Fappearance\",\n  \"routes\": [\"\u002Fsettings\u002Fappearance\"]\n}\n```\n\nNo `variants` array needed. The feature appears in the Prototype InteractionTag menu as a navigation link. No `useFeature()` call is needed in the route component.\n\n---\n\n**Field reference:**\n\n| Field           | Required | Description                                                                                                                    |\n| --------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------ |\n| `id`            | Yes      | Kebab-case string identifier (used in URL params)                                                                              |\n| `label`         | Yes      | Human-readable name shown in the floating UI                                                                                   |\n| `notes`         | Yes      | Design context — what question does this feature explore?                                                                      |\n| `defaultRoute`  | Yes      | The primary route to navigate to from the Prototype menu; must be in `routes`                                                  |\n| `defaultParams` | No       | Fallback values for `:param` segments (e.g., `{ \"agent\": \"agent-001\" }`); required when `defaultRoute` has required parameters |\n| `routes`        | Yes      | Route patterns this feature applies to. Supports `:param` wildcards                                                            |\n| `variants`      | No       | Array of `{ id: string, label: string }`. Omit for variant-less features                                                       |\n\n**Route pattern matching:** A pattern like `\u002Fbuild\u002Fagents\u002F:agent\u002Fplayground` matches any URL of that shape. The `:param` segments are wildcards matched segment-by-segment.\n\n---\n\n## `useFeature()` Return Values\n\n```tsx\nconst result = useFeature('feature-id');\n\u002F\u002F Returns:\n\u002F\u002F   string  — active variant id (e.g., 'accordion') if feature has variants\n\u002F\u002F   true    — if feature is variant-less and applies to current route\n\u002F\u002F   null    — if feature doesn't apply to current route\n```\n\n---\n\n## Multi-Feature Pages\n\nA single page can have **multiple features** active simultaneously. Each component on the page calls `useFeature()` with its own feature ID independently.\n\n```tsx\nconst layout = useFeature('agent-playground-layout');\nconst codeTab = useFeature('code-tab-visibility');\n\n\u002F\u002F Both return their own variant independently\n\u002F\u002F URL: ?variant=agent-playground-layout--accordion&variant=code-tab-visibility--visible\n```\n\nThe floating strip shows variant tab links for all applicable features with variants, separated by dividers.\n\n---\n\n## Variant Scope — What Can Change?\n\nA variant can change **anything** within its route component:\n\n- **Full page replacement**: Return entirely different component trees per variant\n- **Section swap**: Replace just one panel, card, or section\n- **Prop changes**: Same component with different props\n- **Style changes**: Different CSS classes per variant\n- **Data presentation**: Same data shown in different formats\n- **Conditional features**: Show\u002Fhide features like buttons, tabs, or panels\n\n### Example — Small Change (text variant)\n\n```tsx\nconst variant = useFeature('hero-text');\nreturn (\n  \u003CText preset=\"heading2\" foreground={1}>\n    {variant === 'version-b'\n      ? 'Start Building with AI'\n      : variant === 'version-c'\n        ? 'Your AI Development Platform'\n        : 'Welcome to CoreAI Sandbox'}\n  \u003C\u002FText>\n);\n```\n\n### Example — Full Layout Swap\n\n```tsx\nconst variant = useFeature('playground-layout');\nif (variant === 'accordion') return \u003CAccordionLayout \u002F>;\nif (variant === 'dialog') return \u003CDialogLayout \u002F>;\nreturn \u003CSidePanelLayout \u002F>;\n```\n\n---\n\n## Variant-Exclusive Routes\n\nSome variants introduce routes that only exist for that variant (e.g., a \"Code\" tab route that only exists when `go-to-code--tab` is active). If a user lands on a variant-exclusive route with the wrong variant selected, the route component should redirect:\n\n```tsx\nconst variant = useFeature('go-to-code');\n\n\u002F\u002F This route only exists for the \"tab\" variant — redirect otherwise\nif (variant !== 'tab') return \u003CNavigate to=\"..\u002Fplayground\" replace \u002F>;\n```\n\nKeep this guard **in the route component itself**, colocated with the route that knows it's conditional. Do not add redirect logic to `FeatureProvider.tsx`.\n\n---\n\n## Anti-Patterns — Do NOT Do These\n\n| ❌ Don't                                                                                                                             | ✅ Do Instead                                               |\n| ------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------- |\n| Build a custom toggle\u002Fswitch for design alternatives                                                                                 | Use `useFeature()` + `app\u002Ffeatures.json`                    |\n| Store variant state in localStorage or React state                                                                                   | URL query params (`?variant=...`) handle everything         |\n| Create separate routes for each design alternative                                                                                   | One route, conditional rendering via `useFeature()`         |\n| Import feature implementation internals from `app\u002Ffeatures\u002FfeatureConfig.ts` or `app\u002Ffeatures\u002FFeatureProvider.tsx` in app components | Use `useFeature()` from `~\u002Ffeatures\u002FFeatureContext`         |\n| Hard-code variant IDs as numbers                                                                                                     | Feature and variant IDs are **kebab-case strings**          |\n| Add a new variant switching UI element into the product design                                                                       | The floating tab-link UI is the switcher — it's meta-UI     |\n| Modify app-local feature-system internals for an app feature                                                                         | Only modify `app\u002Ffeatures.json` and the consuming component |\n\n---\n\n## Troubleshooting\n\n**Floating strip doesn't appear on my page:**\n\n- Check that the route pattern in `app\u002Ffeatures.json` matches your page's URL exactly (segment-by-segment)\n- `:param` segments match any value, but the number of segments must match\n- Ensure the applicable feature has variants and **Show feature selector** is enabled in the Prototype menu\n- Check browser console for errors\n\n**`useFeature()` returns null:**\n\n- Verify you're passing the **feature ID** (string), not the variant ID\n- Verify the feature's `routes` array includes a pattern matching the current page\n\n**Variant tab links show but clicking doesn't change anything:**\n\n- Ensure your component reads `useFeature()` and conditionally renders based on it\n- Make sure you're checking the correct variant IDs (kebab-case strings)\n\n**URL params get cleared when navigating:**\n\n- This is expected for off-route `variant` params: they are removed. When returning during client navigation, valid URL selections take priority, then remembered selections, then the first configured variant. A reload uses the URL or the first variant, not remembered client-navigation state. Unrelated query params are preserved.\n",{"name":5,"description":7},"main","9e7876c47c30a6c44ca79915991bd5541d58764c","apps\u002Fprototype\u002F.github\u002Fskills\u002Ffeatures-variants",1,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fcoreai-design-system","https:\u002F\u002Fmicrosoft.github.io\u002Fcoreai-design-system\u002F","https:\u002F\u002Fgithub.com\u002Fmicrosoft","https:\u002F\u002Fopensource.microsoft.com","Open source projects and samples from Microsoft","2026-11-14 06:32:13",{"source":8,"skillName":5,"commands":38,"prompt":48,"zipAvailable":49},[39,42,45],{"tool":40,"command":41},"npx","npx skills add microsoft\u002Fcoreai-design-system --skill features-variants",{"tool":43,"command":44},"bunx","bunx skills add microsoft\u002Fcoreai-design-system --skill features-variants",{"tool":46,"command":47},"pnpm","pnpm dlx skills add microsoft\u002Fcoreai-design-system --skill features-variants","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fcoreai-design-system\u002Fblob\u002Fmain\u002Fapps\u002Fprototype\u002F.github\u002Fskills\u002Ffeatures-variants\u002FSKILL.md",true,[51,57,61,65,69,73,77,81,85,89,93,97,101,105,109,113,117,121,125,129],{"path":52,"resourceType":53,"resourceTypeDesc":54,"sizeBytes":55,"sourceHtmlUrl":56},"guides\u002Fadvanced-patterns.md","REFERENCE","参考文档",8773,"https:\u002F\u002Fgithub.com\u002Fapache\u002Fcamel-kit\u002Fblob\u002Fmain\u002Fkit\u002Fcamel-kit-core\u002Fsrc\u002Fmain\u002Fresources\u002Fskills\u002Fcamel-implement\u002Fguides\u002Fadvanced-patterns.md",{"path":58,"resourceType":53,"resourceTypeDesc":54,"sizeBytes":59,"sourceHtmlUrl":60},"guides\u002Fcomponent-loading.md",8123,"https:\u002F\u002Fgithub.com\u002Fapache\u002Fcamel-kit\u002Fblob\u002Fmain\u002Fkit\u002Fcamel-kit-core\u002Fsrc\u002Fmain\u002Fresources\u002Fskills\u002Fcamel-implement\u002Fguides\u002Fcomponent-loading.md",{"path":62,"resourceType":53,"resourceTypeDesc":54,"sizeBytes":63,"sourceHtmlUrl":64},"guides\u002Fdatamapper-approach-a.md",12133,"https:\u002F\u002Fgithub.com\u002Fapache\u002Fcamel-kit\u002Fblob\u002Fmain\u002Fkit\u002Fcamel-kit-core\u002Fsrc\u002Fmain\u002Fresources\u002Fskills\u002Fcamel-implement\u002Fguides\u002Fdatamapper-approach-a.md",{"path":66,"resourceType":53,"resourceTypeDesc":54,"sizeBytes":67,"sourceHtmlUrl":68},"guides\u002Fdatamapper-approach-b.md",6186,"https:\u002F\u002Fgithub.com\u002Fapache\u002Fcamel-kit\u002Fblob\u002Fmain\u002Fkit\u002Fcamel-kit-core\u002Fsrc\u002Fmain\u002Fresources\u002Fskills\u002Fcamel-implement\u002Fguides\u002Fdatamapper-approach-b.md",{"path":70,"resourceType":53,"resourceTypeDesc":54,"sizeBytes":71,"sourceHtmlUrl":72},"guides\u002Fdatamapper-groovy.md",11062,"https:\u002F\u002Fgithub.com\u002Fapache\u002Fcamel-kit\u002Fblob\u002Fmain\u002Fkit\u002Fcamel-kit-core\u002Fsrc\u002Fmain\u002Fresources\u002Fskills\u002Fcamel-implement\u002Fguides\u002Fdatamapper-groovy.md",{"path":74,"resourceType":53,"resourceTypeDesc":54,"sizeBytes":75,"sourceHtmlUrl":76},"guides\u002Fdatamapper-validation.md",20344,"https:\u002F\u002Fgithub.com\u002Fapache\u002Fcamel-kit\u002Fblob\u002Fmain\u002Fkit\u002Fcamel-kit-core\u002Fsrc\u002Fmain\u002Fresources\u002Fskills\u002Fcamel-implement\u002Fguides\u002Fdatamapper-validation.md",{"path":78,"resourceType":53,"resourceTypeDesc":54,"sizeBytes":79,"sourceHtmlUrl":80},"guides\u002Fdocker-compose.md",9639,"https:\u002F\u002Fgithub.com\u002Fapache\u002Fcamel-kit\u002Fblob\u002Fmain\u002Fkit\u002Fcamel-kit-core\u002Fsrc\u002Fmain\u002Fresources\u002Fskills\u002Fcamel-implement\u002Fguides\u002Fdocker-compose.md",{"path":82,"resourceType":53,"resourceTypeDesc":54,"sizeBytes":83,"sourceHtmlUrl":84},"guides\u002Fgraph-project-context.md",2716,"https:\u002F\u002Fgithub.com\u002Fapache\u002Fcamel-kit\u002Fblob\u002Fmain\u002Fkit\u002Fcamel-kit-core\u002Fsrc\u002Fmain\u002Fresources\u002Fskills\u002Fcamel-implement\u002Fguides\u002Fgraph-project-context.md",{"path":86,"resourceType":53,"resourceTypeDesc":54,"sizeBytes":87,"sourceHtmlUrl":88},"guides\u002Fmaven-dependencies.md",4643,"https:\u002F\u002Fgithub.com\u002Fapache\u002Fcamel-kit\u002Fblob\u002Fmain\u002Fkit\u002Fcamel-kit-core\u002Fsrc\u002Fmain\u002Fresources\u002Fskills\u002Fcamel-implement\u002Fguides\u002Fmaven-dependencies.md",{"path":90,"resourceType":53,"resourceTypeDesc":54,"sizeBytes":91,"sourceHtmlUrl":92},"guides\u002Forchestrator.md",14790,"https:\u002F\u002Fgithub.com\u002Fapache\u002Fcamel-kit\u002Fblob\u002Fmain\u002Fkit\u002Fcamel-kit-core\u002Fsrc\u002Fmain\u002Fresources\u002Fskills\u002Fcamel-implement\u002Fguides\u002Forchestrator.md",{"path":94,"resourceType":53,"resourceTypeDesc":54,"sizeBytes":95,"sourceHtmlUrl":96},"guides\u002Fpom-quarkus.md",11598,"https:\u002F\u002Fgithub.com\u002Fapache\u002Fcamel-kit\u002Fblob\u002Fmain\u002Fkit\u002Fcamel-kit-core\u002Fsrc\u002Fmain\u002Fresources\u002Fskills\u002Fcamel-implement\u002Fguides\u002Fpom-quarkus.md",{"path":98,"resourceType":53,"resourceTypeDesc":54,"sizeBytes":99,"sourceHtmlUrl":100},"guides\u002Fpom-spring-boot.md",8210,"https:\u002F\u002Fgithub.com\u002Fapache\u002Fcamel-kit\u002Fblob\u002Fmain\u002Fkit\u002Fcamel-kit-core\u002Fsrc\u002Fmain\u002Fresources\u002Fskills\u002Fcamel-implement\u002Fguides\u002Fpom-spring-boot.md",{"path":102,"resourceType":53,"resourceTypeDesc":54,"sizeBytes":103,"sourceHtmlUrl":104},"guides\u002Fproperties-generation.md",9502,"https:\u002F\u002Fgithub.com\u002Fapache\u002Fcamel-kit\u002Fblob\u002Fmain\u002Fkit\u002Fcamel-kit-core\u002Fsrc\u002Fmain\u002Fresources\u002Fskills\u002Fcamel-implement\u002Fguides\u002Fproperties-generation.md",{"path":106,"resourceType":53,"resourceTypeDesc":54,"sizeBytes":107,"sourceHtmlUrl":108},"guides\u002Froute-validation.md",7172,"https:\u002F\u002Fgithub.com\u002Fapache\u002Fcamel-kit\u002Fblob\u002Fmain\u002Fkit\u002Fcamel-kit-core\u002Fsrc\u002Fmain\u002Fresources\u002Fskills\u002Fcamel-implement\u002Fguides\u002Froute-validation.md",{"path":110,"resourceType":53,"resourceTypeDesc":54,"sizeBytes":111,"sourceHtmlUrl":112},"guides\u002Frun-script.md",2283,"https:\u002F\u002Fgithub.com\u002Fapache\u002Fcamel-kit\u002Fblob\u002Fmain\u002Fkit\u002Fcamel-kit-core\u002Fsrc\u002Fmain\u002Fresources\u002Fskills\u002Fcamel-implement\u002Fguides\u002Frun-script.md",{"path":114,"resourceType":53,"resourceTypeDesc":54,"sizeBytes":115,"sourceHtmlUrl":116},"guides\u002Fschema-generation.md",1124,"https:\u002F\u002Fgithub.com\u002Fapache\u002Fcamel-kit\u002Fblob\u002Fmain\u002Fkit\u002Fcamel-kit-core\u002Fsrc\u002Fmain\u002Fresources\u002Fskills\u002Fcamel-implement\u002Fguides\u002Fschema-generation.md",{"path":118,"resourceType":53,"resourceTypeDesc":54,"sizeBytes":119,"sourceHtmlUrl":120},"guides\u002Fsequential-http-calls.md",7972,"https:\u002F\u002Fgithub.com\u002Fapache\u002Fcamel-kit\u002Fblob\u002Fmain\u002Fkit\u002Fcamel-kit-core\u002Fsrc\u002Fmain\u002Fresources\u002Fskills\u002Fcamel-implement\u002Fguides\u002Fsequential-http-calls.md",{"path":122,"resourceType":53,"resourceTypeDesc":54,"sizeBytes":123,"sourceHtmlUrl":124},"guides\u002Fsmoke-test.md",10509,"https:\u002F\u002Fgithub.com\u002Fapache\u002Fcamel-kit\u002Fblob\u002Fmain\u002Fkit\u002Fcamel-kit-core\u002Fsrc\u002Fmain\u002Fresources\u002Fskills\u002Fcamel-implement\u002Fguides\u002Fsmoke-test.md",{"path":126,"resourceType":53,"resourceTypeDesc":54,"sizeBytes":127,"sourceHtmlUrl":128},"guides\u002Fyaml-catalog-rules.md",8510,"https:\u002F\u002Fgithub.com\u002Fapache\u002Fcamel-kit\u002Fblob\u002Fmain\u002Fkit\u002Fcamel-kit-core\u002Fsrc\u002Fmain\u002Fresources\u002Fskills\u002Fcamel-implement\u002Fguides\u002Fyaml-catalog-rules.md",{"path":130,"resourceType":53,"resourceTypeDesc":54,"sizeBytes":131,"sourceHtmlUrl":132},"guides\u002Fyaml-structure.md",12637,"https:\u002F\u002Fgithub.com\u002Fapache\u002Fcamel-kit\u002Fblob\u002Fmain\u002Fkit\u002Fcamel-kit-core\u002Fsrc\u002Fmain\u002Fresources\u002Fskills\u002Fcamel-implement\u002Fguides\u002Fyaml-structure.md",[134,155,165,175,182,193],{"skillNo":135,"name":136,"nameZh":6,"description":137,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":138,"categories":139,"keywords":148,"publishedAt":22},"01M69BB0EA71EEW0TWV6DDMF5S","coreai-ds-fluentui-react","Docs\u002Fskill v0.1.1; package v0.12.0. Use when building, styling, reviewing, or debugging React UI with @coreai-ds\u002Freact-components. Fetch CoreAI Markdown documentation over HTTPS for components, APIs, defaults, templates, tokens, and accessibility guidance.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fcoreai-design-system\u002Fblob\u002Fmain\u002Fpackages\u002Freact-components\u002Fagent\u002FSKILL.md",[140,145],{"categoryNo":141,"categoryName":142,"taxonomyType":143,"taxonomyTypeDesc":144},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",{"categoryNo":146,"categoryName":147,"taxonomyType":143,"taxonomyTypeDesc":144},"01JBSEEDCAT000000000000009","前端开发",[149,150,151,152,153,154],"react","coreai ds fluentui react","troubleshoot","components","MCP","documentation",{"skillNo":156,"name":157,"nameZh":6,"description":158,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":159,"categories":160,"keywords":161,"publishedAt":22},"01M69BAYVCBBZE7HY5RF871G5J","state-management","CoreAI Sandbox's simplified TanStack Query + mock-JSON data layer. USE WHEN: editing data-fetching hooks (`app\u002Fhooks\u002Fuse*.ts`), adding mock data in `app\u002Fdata\u002F`, or wiring a component to query state.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fcoreai-design-system\u002Fblob\u002Fmain\u002Fapps\u002Fprototype\u002F.github\u002Fskills\u002Fstate-management\u002FSKILL.md",[],[162,163,164],"state management","component","sandbox",{"skillNo":166,"name":167,"nameZh":6,"description":168,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":169,"categories":170,"keywords":171,"publishedAt":22},"01M69BAX7PT3JC7V84DVTSRWN4","start-app","Start the CoreAI Sandbox dev server and open it using available browser tools. USE WHEN: the user asks you to \"start the app\", \"run the dev server\", \"open the app\", or \"launch CoreAI Sandbox\".","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fcoreai-design-system\u002Fblob\u002Fmain\u002Fapps\u002Fprototype\u002F.github\u002Fskills\u002Fstart-app\u002FSKILL.md",[],[172,173,174,164],"start app","launch","run",{"skillNo":176,"name":177,"nameZh":6,"description":178,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":179,"categories":180,"keywords":181,"publishedAt":22},"01M69BAVXDBYFNKF45DQ1FPN40","routing","React Router v7 routing conventions for this SPA. USE WHEN: editing `app\u002Froutes.ts`, adding\u002Fremoving a route, or changing navigation.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fcoreai-design-system\u002Fblob\u002Fmain\u002Fapps\u002Fprototype\u002F.github\u002Fskills\u002Frouting\u002FSKILL.md",[],[177,149],{"skillNo":183,"name":184,"nameZh":6,"description":185,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":186,"categories":187,"keywords":190,"publishedAt":22},"01M69BASXQZQTQB2GH2V79FZ6Y","react-components","CoreAI Sandbox component usage: import boundaries, CoreAI MCP documentation, and local adapter inspection. USE WHEN: editing any `*.tsx` file, picking a component, or writing component imports.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fcoreai-design-system\u002Fblob\u002Fmain\u002Fapps\u002Fprototype\u002F.github\u002Fskills\u002Freact-components\u002FSKILL.md",[188,189],{"categoryNo":141,"categoryName":142,"taxonomyType":143,"taxonomyTypeDesc":144},{"categoryNo":146,"categoryName":147,"taxonomyType":143,"taxonomyTypeDesc":144},[152,149,191,192,153,163,164,154],"react components","writing",{"skillNo":194,"name":195,"nameZh":6,"description":196,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":197,"categories":198,"keywords":202,"publishedAt":22},"01M69BAR4147BKNY7H41DYMPGP","new-variant","Create a new design variant or alternative for a page using the built-in feature\u002Fvariant system. USE WHEN: a designer wants to try a different layout, create an \"option B\" or \"version 2\", compare designs side by side, add an A\u002FB alternative, or explore an alternate version. Do NOT build a custom toggle solution.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fcoreai-design-system\u002Fblob\u002Fmain\u002Fapps\u002Fprototype\u002F.github\u002Fskills\u002Fnew-variant\u002FSKILL.md",[199],{"categoryNo":200,"categoryName":201,"taxonomyType":143,"taxonomyTypeDesc":144},"01JBSEEDCAT000000000000015","视觉设计",[203,20,204],"new variant","create"]