wso2/oxygen-ui/oxygen-layout

Claude Code and agent skill by wso2/oxygen-ui

Trust tier 2
  • community
LLM review pending
Source check pending

Checking that the SKILL.md file is still in the repository.

Generate application layouts with AppShell, Header, and Sidebar. Use when creating app shells, navigation structures, or dashboard layouts.

Install

npx vskill@latest install wso2/oxygen-ui/oxygen-layout

SKILL.md

Generate Oxygen UI Layout

Instructions

  1. Read .claude/oxygen-ui/patterns.md for layout patterns
  2. Use AppShell as the main wrapper
  3. Configure Header with navigation
  4. Set up Sidebar with menu items

Critical Rules

  • Always use OxygenUIThemeProvider at the root
  • Use compound component pattern for AppShell, Header, Sidebar
  • Import icons from @wso2/oxygen-ui-icons-react
  • Use theme tokens for all styling
  • Set a browser tab title on every page using [Specific page/resource] | [Product] (see below)
  • Write all UI text in sentence case (see below)
  • Group sidebar items with section titles and collapsible groups (see below)

UI Text

Write all titles, action text, and content in sentence case. Capitalize only the first word, proper nouns, product names, and acronyms.

text
✓ User management ✓ API keys ✓ Sign out
✗ User Management ✗ API Keys ✗ SIGN OUT
  • Applies to page titles, headings, navigation items, sidebar categories, header actions, and menu items
  • Write the text in sentence case in the source; never change case with textTransform: 'uppercase' or 'capitalize'

Browser Tab Title

Every page must set a browser tab title in this format, so users can tell tabs, bookmarks, and history entries apart. Render a React 19 <title> element in the page component rather than assigning document.title:

text
[Specific page/resource] | [Product]

Examples:

text
All projects | Acme Console
Environments | Acme Console
  • Put the specific page or resource first and the product name last
  • Separate them with | (space, pipe, space)
  • Use the same product name on every page
  • On the product home or landing page, use the product name only: Acme Console
  • For nested resources, go from most to least specific: Environments | Payments API | Acme Console
  • For dynamic names, use the resource name once it loads; until then show the page type (Project | Acme Console), never Loading...
  • On error pages, name the error: Page Not Found | Acme Console
tsx
function ProjectsPage() {
return (
<>
<title>All projects | Acme Console</title>
{/* page content */}
</>
);
}

Side Panel Navigation

Group sidebar items with section titles and collapsible groups. Titles keep the main areas visible; collapsible groups keep the panel short as items grow.

text
Section title Main areas users need to see at a glance Sidebar.CategoryLabel
Collapsible group Secondary items, or a parent with sub-pages Sidebar.Item with nested Sidebar.Item children

If users visit an item often, put it under a title. Otherwise, put it in a collapsible group.

Section titles:

  • Keep titles short and clear; they are labels only, never clickable
  • Limit the number of titled sections; don't give every group a title

Collapsible groups:

  • The parent only opens and closes the group; never give it a page or a link
  • Expand the group that contains the active page by default
  • Remember each user's open and closed groups (Sidebar does not store this; see below)
  • One level of nesting only; never put a collapsible group inside another
  • Give child items icons that differ from the parent's icon

General:

  • Put Settings, Notifications, and Help in Sidebar.Footer, outside the scrolling Sidebar.Nav
  • To let users collapse the sidebar from the bottom, put <Sidebar.CollapseToggle /> last in Sidebar.Footer; it uses the AppShell toggleSidebar action, or pass onToggle
  • Add a menu search when the panel has many items
  • Don't mix the rules: titles never toggle, collapsible parents never navigate
tsx
const storageKey = `side-panel-groups:${userId}`;
const [expandedMenus, setExpandedMenus] = useState<Record<string, boolean>>({});
// Load the user's saved groups on the client, after mount (safe for SSR).
useEffect(() => {
try {
const saved = JSON.parse(localStorage.getItem(storageKey) ?? '{}');
setExpandedMenus((prev) => ({ ...saved, ...prev }));
} catch {
// Storage is unavailable or the saved value is invalid.
}
}, [storageKey]);
// Open the group that contains the active page whenever it changes.
useEffect(() => {
if (activeGroupId) {
setExpandedMenus((prev) => ({ ...prev, [activeGroupId]: true }));
}
}, [activeGroupId]);
const toggleExpand = (id: string) => {
setExpandedMenus((prev) => {
const next = { ...prev, [id]: !prev[id] };
try {
localStorage.setItem(storageKey, JSON.stringify(next));
} catch {
// Storage is unavailable.
}
return next;
});
};
<Sidebar
collapsed={collapsed}
activeItem={activeItem}
expandedMenus={expandedMenus}
onSelect={setActiveItem}
onToggleExpand={toggleExpand}
>
<Sidebar.Nav>
<Sidebar.Category>
<Sidebar.Item id="home">
<Sidebar.ItemIcon><Home size={20} /></Sidebar.ItemIcon>
<Sidebar.ItemLabel>Home</Sidebar.ItemLabel>
</Sidebar.Item>
</Sidebar.Category>
<Sidebar.Category>
<Sidebar.CategoryLabel>Build</Sidebar.CategoryLabel>
<Sidebar.Item id="projects">
<Sidebar.ItemIcon><FolderOpen size={20} /></Sidebar.ItemIcon>
<Sidebar.ItemLabel>Projects</Sidebar.ItemLabel>
</Sidebar.Item>
{/* Collapsible group: nest child items inside the parent */}
<Sidebar.Item id="developer-tools">
<Sidebar.ItemIcon><Wrench size={20} /></Sidebar.ItemIcon>
<Sidebar.ItemLabel>Developer tools</Sidebar.ItemLabel>
<Sidebar.Item id="api-keys">
<Sidebar.ItemIcon><KeyRound size={20} /></Sidebar.ItemIcon>
<Sidebar.ItemLabel>API keys</Sidebar.ItemLabel>
</Sidebar.Item>
<Sidebar.Item id="webhooks">
<Sidebar.ItemIcon><Webhook size={20} /></Sidebar.ItemIcon>
<Sidebar.ItemLabel>Webhooks</Sidebar.ItemLabel>
</Sidebar.Item>
</Sidebar.Item>
</Sidebar.Category>
</Sidebar.Nav>
<Sidebar.Footer>
<Sidebar.Category>
<Sidebar.Item id="settings">
<Sidebar.ItemIcon><Settings size={20} /></Sidebar.ItemIcon>
<Sidebar.ItemLabel>Settings</Sidebar.ItemLabel>
</Sidebar.Item>
</Sidebar.Category>
</Sidebar.Footer>
</Sidebar>

Complete App Shell Layout

tsx
import { useState } from 'react';
import {
OxygenUIThemeProvider,
OxygenTheme,
AppShell,
Header,
Sidebar,
Footer,
Box,
ColorSchemeToggle,
UserMenu,
} from '@wso2/oxygen-ui';
import {
HomeIcon,
DashboardIcon,
SettingsIcon,
UsersIcon,
FileIcon,
} from '@wso2/oxygen-ui-icons-react';
function ProjectsPage() {
return (
<>
<title>All projects | My Application</title>
{/* Page content goes here */}
<h1>All projects</h1>
</>
);
}
function App() {
const [collapsed, setCollapsed] = useState(false);
const [activeItem, setActiveItem] = useState('home');
return (
<OxygenUIThemeProvider theme={OxygenTheme}>
<AppShell>
<AppShell.Navbar>
<Header>
<Header.Toggle
collapsed={collapsed}
onToggle={() => setCollapsed(!collapsed)}
/>
<Header.Brand>
<Header.BrandLogo>
<img src="/logo.svg" alt="Logo" height={32} />
</Header.BrandLogo>
<Header.BrandTitle>My Application</Header.BrandTitle>
</Header.Brand>
<Header.Spacer />
<Header.Actions>
<ColorSchemeToggle />
<UserMenu
user={{ name: 'John Doe', email: 'john@example.com' }}
onSignOut={() => console.log('Sign out')}
/>
</Header.Actions>
</Header>
</AppShell.Navbar>
<AppShell.Sidebar>
<Sidebar
collapsed={collapsed}
activeItem={activeItem}
onSelect={setActiveItem}
>
<Sidebar.Nav>
<Sidebar.Category>
<Sidebar.CategoryLabel>Main</Sidebar.CategoryLabel>
<Sidebar.Item id="home">
<Sidebar.ItemIcon><HomeIcon size={20} /></Sidebar.ItemIcon>
<Sidebar.ItemLabel>Home</Sidebar.ItemLabel>
</Sidebar.Item>
<Sidebar.Item id="dashboard">
<Sidebar.ItemIcon><DashboardIcon size={20} /></Sidebar.ItemIcon>
<Sidebar.ItemLabel>Dashboard</Sidebar.ItemLabel>
</Sidebar.Item>
</Sidebar.Category>
<Sidebar.Category>
<Sidebar.CategoryLabel>Management</Sidebar.CategoryLabel>
<Sidebar.Item id="users">
<Sidebar.ItemIcon><UsersIcon size={20} /></Sidebar.ItemIcon>
<Sidebar.ItemLabel>Users</Sidebar.ItemLabel>
<Sidebar.ItemBadge>12</Sidebar.ItemBadge>
</Sidebar.Item>
<Sidebar.Item id="files">
<Sidebar.ItemIcon><FileIcon size={20} /></Sidebar.ItemIcon>
<Sidebar.ItemLabel>Files</Sidebar.ItemLabel>
</Sidebar.Item>
</Sidebar.Category>
</Sidebar.Nav>
<Sidebar.Footer>
<Sidebar.Item id="settings">
<Sidebar.ItemIcon><SettingsIcon size={20} /></Sidebar.ItemIcon>
<Sidebar.ItemLabel>Settings</Sidebar.ItemLabel>
</Sidebar.Item>
</Sidebar.Footer>
</Sidebar>
</AppShell.Sidebar>
<AppShell.Main>
<Box sx={{ p: 3 }}>
<ProjectsPage />
</Box>
</AppShell.Main>
<AppShell.Footer>
<Footer companyName="WSO2 LLC" />
</AppShell.Footer>
</AppShell>
</OxygenUIThemeProvider>
);
}
export default App;

Header with Context Switchers

tsx
<Header>
<Header.Toggle collapsed={collapsed} onToggle={toggleSidebar} />
<Header.Brand>
<Header.BrandLogo><Logo /></Header.BrandLogo>
<Header.BrandTitle>App Name</Header.BrandTitle>
</Header.Brand>
<Header.Switchers>
<OrganizationSwitcher />
<ProjectSwitcher />
</Header.Switchers>
<Header.Spacer />
<Header.Actions>
<SearchBar />
<NotificationButton />
<ColorSchemeToggle />
<UserMenu user={user} />
</Header.Actions>
</Header>

Minimal Header (No Switchers)

tsx
<Header minimal>
<Header.Brand>
<Header.BrandTitle>Simple App</Header.BrandTitle>
</Header.Brand>
<Header.Spacer />
<Header.Actions>
<ColorSchemeToggle />
</Header.Actions>
</Header>

Dashboard Layout with Stats

tsx
import { Grid, Paper, Typography, Box } from '@wso2/oxygen-ui';
function DashboardContent() {
return (
<Box sx={{ p: 3 }}>
<Typography variant="h4" gutterBottom>Dashboard</Typography>
<Grid container spacing={3}>
<Grid item xs={12} sm={6} md={3}>
<Paper sx={{ p: 3 }}>
<Typography color="text.secondary" variant="body2">Total users</Typography>
<Typography variant="h4" fontWeight="bold">12,345</Typography>
</Paper>
</Grid>
<Grid item xs={12} sm={6} md={3}>
<Paper sx={{ p: 3 }}>
<Typography color="text.secondary" variant="body2">Revenue</Typography>
<Typography variant="h4" fontWeight="bold">$45,678</Typography>
</Paper>
</Grid>
{/* More stats... */}
</Grid>
</Box>
);
}

Security

  • Security scanPassed, 100 of 100Pattern checks on the skill's files.
  • Model reviewNot runA model review is scheduled for the next rescan.

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
50
Repository forks
45
7-day trend score
20

Stars and forks are for the whole GitHub repository, not this skill alone. Refreshed 20 Mar 2026, 7 months ago.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/wso2/oxygen-ui/oxygen-layout/badge)](https://verified-skill.com/skills/wso2/oxygen-ui/oxygen-layout)

Something wrong with this skill or its page? Tell us.