ui-design
Design patterns and component guidelines for the Nomendex UI. Use when building dialogs, layouts, or fixing visual/layout issues.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Design patterns and component guidelines for the Nomendex UI. Use when building dialogs, layouts, or fixing visual/layout issues.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
| name | ui-design |
| description | Design patterns and component guidelines for the Nomendex UI. Use when building dialogs, layouts, or fixing visual/layout issues. |
Reference documentation for consistent UI patterns in Nomendex.
Jumbo dialogs (size="jumbo") are full-viewport dialogs (90vw x 90vh) for complex content like search interfaces, file browsers, or multi-pane layouts.
The jumbo dialog uses display: flex with flex-direction: column. The CommandDialogProvider automatically wraps content in a flex-growing container when size="jumbo":
// CommandDialogProvider handles this automatically for jumbo dialogs:
<DialogContent size="jumbo">
<DialogHeader className="shrink-0">
<DialogTitle>Title</DialogTitle>
<DialogDescription>Description</DialogDescription>
</DialogHeader>
{/* Content is auto-wrapped in: <div className="flex-1 min-h-0 flex flex-col"> */}
{dialogState.content}
</DialogContent>
shrink-0 - Prevents header from shrinkingflex-1 - Allows content to grow and fill spacemin-h-0 - Critical for flex children to allow shrinking below content size (enables overflow)overflow-y-auto - For scrollable sectionsIn flexbox, children have min-height: auto by default, which means they won't shrink below their content size. This breaks overflow scrolling. Adding min-h-0 allows the element to shrink, enabling overflow-y-auto to work.
Located at src/features/notes/search-notes-dialog.tsx, this demonstrates the full pattern.
openDialog({
title: "Search Notes",
description: "Search for text across all your notes",
content: <SearchNotesDialog />,
size: "jumbo",
});
<div className="flex flex-col h-full">
{/* Fixed top section - search input */}
<div
className="shrink-0 px-4 py-3 border-b"
style={{ borderColor: styles.borderDefault }}
>
<Input placeholder="Search notes..." />
</div>
{/* Two-column scrollable area */}
<div className="flex-1 flex min-h-0">
{/* Left column - results list */}
<div
className="w-1/2 overflow-y-auto border-r"
style={{ borderColor: styles.borderDefault }}
>
{/* Results items */}
</div>
{/* Right column - preview */}
<div
className="w-1/2 overflow-y-auto"
style={{ backgroundColor: styles.surfacePrimary }}
>
{/* Preview content */}
</div>
</div>
</div>
Use theme system colors for search highlights:
import { useTheme } from "@/hooks/useTheme";
const { currentTheme } = useTheme();
const { styles } = currentTheme;
// For inline text highlights (search matches)
<mark
style={{
backgroundColor: styles.semanticPrimary,
color: styles.semanticPrimaryForeground,
borderRadius: "2px",
padding: "0 2px",
}}
>
{matchedText}
</mark>
// For line/row highlights (match context)
<div
style={{
backgroundColor: isMatchLine ? styles.surfaceAccent : "transparent",
}}
>
{lineContent}
</div>
From useTheme().currentTheme.styles:
Surfaces (backgrounds):
surfacePrimary - Main backgroundsurfaceSecondary - Secondary/elevated backgroundsurfaceTertiary - Hover states, selected itemssurfaceAccent - Accent/highlight backgroundssurfaceMuted - Muted backgroundsContent (text):
contentPrimary - Main textcontentSecondary - Secondary textcontentTertiary - Muted/disabled textcontentAccent - Accent textBorders:
borderDefault - Standard bordersborderAccent - Accent bordersSemantic (actions/status):
semanticPrimary / semanticPrimaryForeground - Primary actions, highlightssemanticDestructive / semanticDestructiveForeground - Delete, dangersemanticSuccess / semanticSuccessForeground - Success statesImplement arrow key navigation for lists:
const [selectedIndex, setSelectedIndex] = React.useState(0);
React.useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === "ArrowDown") {
e.preventDefault();
setSelectedIndex(prev => (prev + 1) % results.length);
} else if (e.key === "ArrowUp") {
e.preventDefault();
setSelectedIndex(prev => (prev - 1 + results.length) % results.length);
} else if (e.key === "Enter") {
e.preventDefault();
// Open selected item
} else if (e.key === "Escape") {
e.preventDefault();
closeDialog();
}
};
document.addEventListener("keydown", handleKeyDown);
return () => document.removeEventListener("keydown", handleKeyDown);
}, [results, selectedIndex]);
const resultsContainerRef = React.useRef<HTMLDivElement>(null);
React.useEffect(() => {
if (resultsContainerRef.current && results.length > 0) {
const selectedElement = resultsContainerRef.current.querySelector(
`[data-index="${selectedIndex}"]`
);
if (selectedElement) {
selectedElement.scrollIntoView({ block: "nearest", behavior: "smooth" });
}
}
}, [selectedIndex, results.length]);
// In JSX:
<div ref={resultsContainerRef}>
{results.map((result, index) => (
<div key={result.id} data-index={index}>
{/* ... */}
</div>
))}
</div>
When opening a note from search results, scroll to the first match:
// In search dialog - pass scrollToLine when opening
const openNote = (fileName: string, scrollToLine?: number) => {
addNewTab({
pluginMeta: notesPluginSerial,
view: "editor",
props: { noteFileName: fileName, scrollToLine }
});
};
// Get first content match line
const contentMatches = result.matches.filter(m => m.line > 0);
const firstMatchLine = contentMatches.length > 0 ? contentMatches[0].line : undefined;
openNote(result.fileName, firstMatchLine);
The NotesView component accepts scrollToLine prop and scrolls the ProseMirror editor to that line on initial load.
Custom themed scrollbars for macOS WKWebView apps. Use when styling scrollbars in the native macOS app, fixing scrollbar theming issues, implementing custom scroll containers that work in WKWebView, or debugging scroll position persistence issues with tabs.
Guide for implementing keyboard navigation and focus indicators across macOS native app (WKWebView) and web browser versions. Use when adding focusable elements, fixing Tab key navigation, or debugging focus ring visibility issues.
Guide for working with workspace tabs, tab management, and duplicate tab prevention. Use when fixing tab bugs, adding tab features, or working with openTab/addNewTab functions.
Guide for working with the ProseMirror-based notes editor. Use when editing notes feature code, fixing editor bugs, working with todos/checkboxes, wiki_links, decorations, or serialization.
Increment app version numbers. Use when user asks to bump version, increment version, or prepare a release.
Create new Claude Skills with proper structure, frontmatter, and best practices. Use when the user wants to create a skill, build a skill, make a new skill, or scaffold skill files.