[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M71C48JC4APZVEQJWQ395E5W":3,"skill-same:01M71C48JC4APZVEQJWQ395E5W":63},{"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":36,"linkOnly":37,"markdownContent":38,"markdownBody":39,"frontMatter":40,"refName":41,"commitSha":42,"skillDirectory":43,"forksCount":44,"repositoryHtmlUrl":45,"repositoryHomepageUrl":6,"ownerHtmlUrl":46,"ownerBlogUrl":6,"ownerDescription":47,"updatedAt":48,"install":49,"resources":62},"01M71C48JC4APZVEQJWQ395E5W","genjutsu-desktop-principles",null,"Desktop-specific UX principles - hover states, pointer precision, keyboard shortcuts, multi-window, focus management. Covers macOS, Windows, Linux, web desktop.","nexuslinkproductions\u002Fyuri-os","nexuslinkproductions","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F273246263?v=4","01M718YX9NZRCW50B59B8ZVDKN","Marcel Spatz","01M718YXAQ7530F4B1VKN6SFQV",2,"MIT","https:\u002F\u002Fgithub.com\u002Fnexuslinkproductions\u002Fyuri-os\u002Fblob\u002Fmain\u002Fskills\u002Fgenjutsu-desktop-principles\u002FSKILL.md",[],[19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35],"desktop","principles","genjutsu desktop principles","macos","ux principles","management","ux","web","focus","window","shortcuts","keyboard","multi","linux","states","hover","windows","2026-11-10 10:12:30",false,"---\nname: genjutsu-desktop-principles\ndescription: \"Desktop-specific UX principles - hover states, pointer precision, keyboard shortcuts, multi-window, focus management. Covers macOS, Windows, Linux, web desktop.\"\n---\n\n# Desktop Principles\n\n> Desktop UX context. Loaded when desktop is detected (macOS, Windows, Linux desktop, web desktop).\n> Concise rules here. Deep-dive in `references\u002F`.\n\n---\n\n## Hover States Are Mandatory\n\nHover is the primary affordance signal on desktop, the inverse of mobile. A pointer hovering over a target without immediate visual feedback feels broken: users rely on `:hover` to confirm an element is interactive before committing to a click. Every clickable surface must have a distinct hover style, ideally with a 100-200ms transition so the change is perceptible without feeling sluggish.\n\n**CSS - hover styles for interactive elements:**\n```css\n.button {\n  background: var(--surface);\n  transition: background 120ms ease-out, transform 120ms ease-out;\n}\n\n.button:hover {\n  background: var(--surface-hover);\n  transform: translateY(-1px);\n}\n\n.button:active {\n  transform: translateY(0);\n}\n```\n\n**SwiftUI - .onHover for macOS, .hoverEffect for iPadOS:**\n```swift\nstruct ToolbarButton: View {\n  @State private var hovering = false\n\n  var body: some View {\n    Image(systemName: \"square.and.arrow.up\")\n      .padding(8)\n      .background(hovering ? Color.gray.opacity(0.15) : .clear)\n      .onHover { hovering = $0 }\n      .animation(.easeOut(duration: 0.12), value: hovering)\n      .hoverEffect(.highlight) \u002F\u002F iPadOS pointer support, no-op on macOS\n  }\n}\n```\n\n**Compose Desktop - onPointerEvent or hoverable + interactionSource:**\n```kotlin\n@OptIn(ExperimentalComposeUiApi::class)\n@Composable\nfun ToolbarButton(onClick: () -> Unit) {\n  val interactionSource = remember { MutableInteractionSource() }\n  val hovered by interactionSource.collectIsHoveredAsState()\n\n  Box(\n    modifier = Modifier\n      .hoverable(interactionSource)\n      .background(if (hovered) Color.LightGray.copy(alpha = 0.15f) else Color.Transparent)\n      .clickable(onClick = onClick)\n      .padding(8.dp),\n  ) { Icon(Icons.Default.Share, contentDescription = \"Share\") }\n}\n```\n\n---\n\n## Pointer Precision\n\nMouse and trackpad pointers are far more accurate than thumbs, so desktop targets can be smaller than the 44pt mobile minimum. Common ranges are 24-32px for icon buttons, 28-36px for toolbar items. WCAG 2.5.8 (AA, target size minimum) sets the absolute floor at **24x24 CSS pixels** for non-mobile pointer input. Sub-24px targets need spacing or be grouped with sibling targets.\n\n**Fitts's Law in practice:** the time to acquire a target shrinks with size and grows with distance. Screen edges and corners are infinite-depth targets - the cursor stops there regardless of overshoot. Put high-frequency global controls (close window, system menu, app dock) in corners and along edges. macOS menubar and Windows taskbar are textbook applications: edge-anchored, zero-overshoot acquisition.\n\n---\n\n## Keyboard Shortcuts (first-class)\n\nDesktop users expect parity with native conventions. Missing `⌘+F` in a list-heavy app is not minimalism, it is a bug.\n\n| Action | macOS | Windows \u002F Linux |\n|---|---|---|\n| New | `⌘+N` | `Ctrl+N` |\n| Close window | `⌘+W` | `Ctrl+W` |\n| Quit app | `⌘+Q` | `Alt+F4` |\n| Settings \u002F Preferences | `⌘+,` | `Ctrl+,` |\n| Find | `⌘+F` | `Ctrl+F` |\n| Toggle (comment, sidebar...) | `⌘+\u002F` | `Ctrl+\u002F` |\n| Save | `⌘+S` | `Ctrl+S` |\n| Command palette | `⌘+K` or `⌘+Shift+P` | `Ctrl+K` or `Ctrl+Shift+P` |\n\n**Web - detect Ctrl vs Cmd correctly:**\n```js\n\u002F\u002F Prefer event.metaKey on macOS, event.ctrlKey elsewhere.\n\u002F\u002F navigator.platform is deprecated but still pragmatic; fall back to userAgent.\nconst isMac = \u002FMac|iPhone|iPad\u002F.test(navigator.platform || navigator.userAgent);\n\nwindow.addEventListener(\"keydown\", (e) => {\n  const cmdOrCtrl = isMac ? e.metaKey : e.ctrlKey;\n  if (cmdOrCtrl && e.key.toLowerCase() === \"k\") {\n    e.preventDefault();\n    openCommandPalette();\n  }\n});\n```\n\n**SwiftUI - .keyboardShortcut binds to menu commands:**\n```swift\nButton(\"New Document\", action: newDoc)\n  .keyboardShortcut(\"n\", modifiers: .command)\n\nButton(\"Find\", action: focusSearch)\n  .keyboardShortcut(\"f\", modifiers: .command)\n```\n\n**Compose Desktop - onKeyEvent + KeyShortcut:**\n```kotlin\n@OptIn(ExperimentalComposeUiApi::class)\nfun Modifier.commandShortcut(key: Key, onTrigger: () -> Unit) =\n  onKeyEvent { event ->\n    if (event.type == KeyEventType.KeyDown && event.isMetaPressed && event.key == key) {\n      onTrigger(); true\n    } else false\n  }\n\n\u002F\u002F In MenuBar:\nMenuBar {\n  Menu(\"File\") {\n    Item(\"New\", shortcut = KeyShortcut(Key.N, meta = true), onClick = ::newDoc)\n    Item(\"Find\", shortcut = KeyShortcut(Key.F, meta = true), onClick = ::focusSearch)\n  }\n}\n```\n\n---\n\n## Multi-Window Patterns\n\nDesktop users keep windows side by side. A new window is the right answer when:\n\n- A task runs long enough that the user wants to keep working in the main window (rendering, export, sync log).\n- The user is comparing two parallel contexts (two documents, two chats, two issues).\n- The app is document-based and each document is a peer (Pages, Figma files, Xcode projects).\n\nA new window is the wrong answer for transient confirmations, brief settings panels, or anything that can live in a sheet or popover.\n\n**SwiftUI - WindowGroup for document-style, Window for singletons:**\n```swift\n@main\nstruct MyApp: App {\n  var body: some Scene {\n    WindowGroup(\"Document\") { DocumentView() } \u002F\u002F peer windows, one per doc\n\n    Window(\"Inspector\", id: \"inspector\") { InspectorView() }\n      .windowResizability(.contentSize) \u002F\u002F tracks intrinsic content size\n\n    Settings { SettingsView() } \u002F\u002F ⌘+, target on macOS\n  }\n}\n```\n\n**Compose Desktop - Window composables, application scope:**\n```kotlin\nfun main() = application {\n  val docs = remember { mutableStateListOf(Document()) }\n\n  docs.forEach { doc ->\n    Window(onCloseRequest = { docs.remove(doc) }, title = doc.title) {\n      DocumentView(doc)\n    }\n  }\n\n  if (showInspector) {\n    Window(onCloseRequest = { showInspector = false }, title = \"Inspector\") {\n      InspectorView()\n    }\n  }\n}\n```\n\n**State sharing:** windows are *views* over the same model. Hold the source of truth in a singleton or a DI-scoped object (SwiftUI `@Observable` injected via environment, Compose `koin` or `viewModel`-equivalent). Never duplicate state per window - reconciling diverging copies is a graveyard of bugs.\n\n---\n\n## Focus Management\n\nKeyboard navigation is a first-class input on desktop. Tab order must be sane, focus rings must be visible, and removing them without an alternative is an accessibility regression.\n\n**SwiftUI - @FocusState drives field focus:**\n```swift\nstruct LoginForm: View {\n  enum Field { case email, password }\n\n  @State private var email = \"\"\n  @State private var password = \"\"\n  @FocusState private var focused: Field?\n\n  var body: some View {\n    VStack {\n      TextField(\"Email\", text: $email)\n        .focused($focused, equals: .email)\n        .onSubmit { focused = .password }\n      SecureField(\"Password\", text: $password)\n        .focused($focused, equals: .password)\n        .onSubmit(submit)\n    }\n    .onAppear { focused = .email }\n  }\n}\n```\n\n**Compose - FocusRequester + LocalFocusManager:**\n```kotlin\nval emailFocus = remember { FocusRequester() }\nval passwordFocus = remember { FocusRequester() }\nval focusManager = LocalFocusManager.current\n\nTextField(\n  value = email, onValueChange = { email = it },\n  modifier = Modifier.focusRequester(emailFocus).focusable(),\n  keyboardActions = KeyboardActions(onNext = { passwordFocus.requestFocus() }),\n)\n\nTextField(\n  value = password, onValueChange = { password = it },\n  modifier = Modifier.focusRequester(passwordFocus).focusable(),\n  keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus(); submit() }),\n)\n\nLaunchedEffect(Unit) { emailFocus.requestFocus() }\n```\n\n**Web - tabindex + :focus-visible:**\n```css\n.button {\n  \u002F* Never `outline: none` without an alternative. *\u002F\n}\n\n.button:focus-visible {\n  outline: 2px solid var(--ring);\n  outline-offset: 2px;\n}\n```\n```html\n\u003C!-- tabindex=\"0\" puts a non-interactive element in tab order -->\n\u003Cdiv role=\"button\" tabindex=\"0\" class=\"button\">Custom button\u003C\u002Fdiv>\n```\n\n---\n\n## Information Density\n\nDesktop users sit on a 13-32 inch screen with a precise pointer and full keyboard. They can - and want to - parse more information per viewport than on mobile. Use an 8px base grid (vs 4-8px mobile), persistent sidebars instead of bottom tabs, command palettes (`⌘K`) for power users, and dense data tables when the data warrants it. Linear, Things 3, and Notion are the touchstones: information-rich without feeling cramped, every pixel earns its keep.\n\n---\n\n## Subtle Animations Doctrine\n\nDesktop apps are stared at for hours. Animations that feel delightful once become unbearable on the hundredth repetition. Prefer short, purely functional motion: opacity and small translations under 200ms, no bounces on routine interactions, no playful overshoots on hover. Save expressive motion for one-shot moments (onboarding, success states), never daily UI.\n\n```css\n\u002F* BAD - every hover bounces for 600ms, exhausting after the third use *\u002F\n.card {\n  transition: transform 600ms cubic-bezier(0.34, 1.56, 0.64, 1);\n}\n.card:hover { transform: scale(1.05); }\n```\n\n```css\n\u002F* GOOD - 100ms opacity, almost subliminal, never tires *\u002F\n.card {\n  opacity: 0.92;\n  transition: opacity 100ms ease-out;\n}\n.card:hover { opacity: 1; }\n```\n\n---\n\n## Anti-Patterns (BAD \u002F GOOD)\n\n### 1. Hiding navigation behind a hamburger on desktop\n\n```html\n\u003C!-- BAD - 1440px viewport, infinite room, but nav is collapsed -->\n\u003Cheader>\n  \u003Cbutton class=\"hamburger\" aria-label=\"Menu\">☰\u003C\u002Fbutton>\n\u003C\u002Fheader>\n\u003Cnav class=\"drawer hidden\">...\u003C\u002Fnav>\n```\n\n```html\n\u003C!-- GOOD - persistent sidebar on desktop, collapsible if the user wants it -->\n\u003Caside class=\"sidebar\">\n  \u003Cnav>\n    \u003Ca href=\"\u002Finbox\">Inbox\u003C\u002Fa>\n    \u003Ca href=\"\u002Fprojects\">Projects\u003C\u002Fa>\n    \u003Ca href=\"\u002Farchive\">Archive\u003C\u002Fa>\n  \u003C\u002Fnav>\n  \u003Cbutton class=\"collapse-toggle\" aria-label=\"Collapse sidebar\">⇤\u003C\u002Fbutton>\n\u003C\u002Faside>\n```\n\n### 2. No keyboard shortcuts for primary actions\n\n```jsx\n\u002F\u002F BAD - \"New\" is buried in a menu, no shortcut, every creation is 3 clicks\n\u003CToolbar>\n  \u003CMenu>\n    \u003CMenuItem onClick={newDoc}>New document\u003C\u002FMenuItem>\n  \u003C\u002FMenu>\n\u003C\u002FToolbar>\n```\n\n```jsx\n\u002F\u002F GOOD - ⌘N baseline, surfaced in the menu, mirrored on the toolbar tooltip\n\u003CToolbar>\n  \u003Cbutton onClick={newDoc} title=\"New document (⌘N)\">\n    \u003CPlusIcon \u002F>\n  \u003C\u002Fbutton>\n\u003C\u002FToolbar>\n\u002F\u002F Bound globally:\nuseShortcut(\"mod+n\", newDoc);\nuseShortcut(\"mod+s\", save);\nuseShortcut(\"mod+f\", focusSearch);\n```\n\n### 3. Removing focus rings without an alternative\n\n```css\n\u002F* BAD - keyboard users now have no idea where focus is *\u002F\nbutton:focus { outline: none; }\n```\n\n```css\n\u002F* GOOD - :focus-visible keeps mouse clicks ring-free, keyboard navigation visible *\u002F\nbutton:focus { outline: none; }\nbutton:focus-visible {\n  outline: 2px solid var(--brand-500);\n  outline-offset: 2px;\n  border-radius: 6px;\n}\n```\n\n---\n\n## Quick Reference: Loading sub-skills\n\n| Need | Load |\n|---|---|\n| Keyboard patterns deep-dive | `references\u002Fkeyboard-patterns.md` |\n| Multi-window state | `references\u002Fmulti-window.md` |\n| SwiftUI animations | `..\u002Fswiftui-motion\u002FSKILL.md` |\n| Compose Desktop | `..\u002Fcompose-multiplatform\u002FSKILL.md` (when desktop is a CMP target) |\n\n---\n\n## Sources\n\n- Apple Human Interface Guidelines (macOS): https:\u002F\u002Fdeveloper.apple.com\u002Fdesign\u002Fhuman-interface-guidelines\u002Fmacos\n- Microsoft Fluent Design 2: https:\u002F\u002Ffluent2.microsoft.design\u002F\n- GNOME Human Interface Guidelines: https:\u002F\u002Fdeveloper.gnome.org\u002Fhig\u002F\n","# Desktop Principles\n\n> Desktop UX context. Loaded when desktop is detected (macOS, Windows, Linux desktop, web desktop).\n> Concise rules here. Deep-dive in `references\u002F`.\n\n---\n\n## Hover States Are Mandatory\n\nHover is the primary affordance signal on desktop, the inverse of mobile. A pointer hovering over a target without immediate visual feedback feels broken: users rely on `:hover` to confirm an element is interactive before committing to a click. Every clickable surface must have a distinct hover style, ideally with a 100-200ms transition so the change is perceptible without feeling sluggish.\n\n**CSS - hover styles for interactive elements:**\n```css\n.button {\n  background: var(--surface);\n  transition: background 120ms ease-out, transform 120ms ease-out;\n}\n\n.button:hover {\n  background: var(--surface-hover);\n  transform: translateY(-1px);\n}\n\n.button:active {\n  transform: translateY(0);\n}\n```\n\n**SwiftUI - .onHover for macOS, .hoverEffect for iPadOS:**\n```swift\nstruct ToolbarButton: View {\n  @State private var hovering = false\n\n  var body: some View {\n    Image(systemName: \"square.and.arrow.up\")\n      .padding(8)\n      .background(hovering ? Color.gray.opacity(0.15) : .clear)\n      .onHover { hovering = $0 }\n      .animation(.easeOut(duration: 0.12), value: hovering)\n      .hoverEffect(.highlight) \u002F\u002F iPadOS pointer support, no-op on macOS\n  }\n}\n```\n\n**Compose Desktop - onPointerEvent or hoverable + interactionSource:**\n```kotlin\n@OptIn(ExperimentalComposeUiApi::class)\n@Composable\nfun ToolbarButton(onClick: () -> Unit) {\n  val interactionSource = remember { MutableInteractionSource() }\n  val hovered by interactionSource.collectIsHoveredAsState()\n\n  Box(\n    modifier = Modifier\n      .hoverable(interactionSource)\n      .background(if (hovered) Color.LightGray.copy(alpha = 0.15f) else Color.Transparent)\n      .clickable(onClick = onClick)\n      .padding(8.dp),\n  ) { Icon(Icons.Default.Share, contentDescription = \"Share\") }\n}\n```\n\n---\n\n## Pointer Precision\n\nMouse and trackpad pointers are far more accurate than thumbs, so desktop targets can be smaller than the 44pt mobile minimum. Common ranges are 24-32px for icon buttons, 28-36px for toolbar items. WCAG 2.5.8 (AA, target size minimum) sets the absolute floor at **24x24 CSS pixels** for non-mobile pointer input. Sub-24px targets need spacing or be grouped with sibling targets.\n\n**Fitts's Law in practice:** the time to acquire a target shrinks with size and grows with distance. Screen edges and corners are infinite-depth targets - the cursor stops there regardless of overshoot. Put high-frequency global controls (close window, system menu, app dock) in corners and along edges. macOS menubar and Windows taskbar are textbook applications: edge-anchored, zero-overshoot acquisition.\n\n---\n\n## Keyboard Shortcuts (first-class)\n\nDesktop users expect parity with native conventions. Missing `⌘+F` in a list-heavy app is not minimalism, it is a bug.\n\n| Action | macOS | Windows \u002F Linux |\n|---|---|---|\n| New | `⌘+N` | `Ctrl+N` |\n| Close window | `⌘+W` | `Ctrl+W` |\n| Quit app | `⌘+Q` | `Alt+F4` |\n| Settings \u002F Preferences | `⌘+,` | `Ctrl+,` |\n| Find | `⌘+F` | `Ctrl+F` |\n| Toggle (comment, sidebar...) | `⌘+\u002F` | `Ctrl+\u002F` |\n| Save | `⌘+S` | `Ctrl+S` |\n| Command palette | `⌘+K` or `⌘+Shift+P` | `Ctrl+K` or `Ctrl+Shift+P` |\n\n**Web - detect Ctrl vs Cmd correctly:**\n```js\n\u002F\u002F Prefer event.metaKey on macOS, event.ctrlKey elsewhere.\n\u002F\u002F navigator.platform is deprecated but still pragmatic; fall back to userAgent.\nconst isMac = \u002FMac|iPhone|iPad\u002F.test(navigator.platform || navigator.userAgent);\n\nwindow.addEventListener(\"keydown\", (e) => {\n  const cmdOrCtrl = isMac ? e.metaKey : e.ctrlKey;\n  if (cmdOrCtrl && e.key.toLowerCase() === \"k\") {\n    e.preventDefault();\n    openCommandPalette();\n  }\n});\n```\n\n**SwiftUI - .keyboardShortcut binds to menu commands:**\n```swift\nButton(\"New Document\", action: newDoc)\n  .keyboardShortcut(\"n\", modifiers: .command)\n\nButton(\"Find\", action: focusSearch)\n  .keyboardShortcut(\"f\", modifiers: .command)\n```\n\n**Compose Desktop - onKeyEvent + KeyShortcut:**\n```kotlin\n@OptIn(ExperimentalComposeUiApi::class)\nfun Modifier.commandShortcut(key: Key, onTrigger: () -> Unit) =\n  onKeyEvent { event ->\n    if (event.type == KeyEventType.KeyDown && event.isMetaPressed && event.key == key) {\n      onTrigger(); true\n    } else false\n  }\n\n\u002F\u002F In MenuBar:\nMenuBar {\n  Menu(\"File\") {\n    Item(\"New\", shortcut = KeyShortcut(Key.N, meta = true), onClick = ::newDoc)\n    Item(\"Find\", shortcut = KeyShortcut(Key.F, meta = true), onClick = ::focusSearch)\n  }\n}\n```\n\n---\n\n## Multi-Window Patterns\n\nDesktop users keep windows side by side. A new window is the right answer when:\n\n- A task runs long enough that the user wants to keep working in the main window (rendering, export, sync log).\n- The user is comparing two parallel contexts (two documents, two chats, two issues).\n- The app is document-based and each document is a peer (Pages, Figma files, Xcode projects).\n\nA new window is the wrong answer for transient confirmations, brief settings panels, or anything that can live in a sheet or popover.\n\n**SwiftUI - WindowGroup for document-style, Window for singletons:**\n```swift\n@main\nstruct MyApp: App {\n  var body: some Scene {\n    WindowGroup(\"Document\") { DocumentView() } \u002F\u002F peer windows, one per doc\n\n    Window(\"Inspector\", id: \"inspector\") { InspectorView() }\n      .windowResizability(.contentSize) \u002F\u002F tracks intrinsic content size\n\n    Settings { SettingsView() } \u002F\u002F ⌘+, target on macOS\n  }\n}\n```\n\n**Compose Desktop - Window composables, application scope:**\n```kotlin\nfun main() = application {\n  val docs = remember { mutableStateListOf(Document()) }\n\n  docs.forEach { doc ->\n    Window(onCloseRequest = { docs.remove(doc) }, title = doc.title) {\n      DocumentView(doc)\n    }\n  }\n\n  if (showInspector) {\n    Window(onCloseRequest = { showInspector = false }, title = \"Inspector\") {\n      InspectorView()\n    }\n  }\n}\n```\n\n**State sharing:** windows are *views* over the same model. Hold the source of truth in a singleton or a DI-scoped object (SwiftUI `@Observable` injected via environment, Compose `koin` or `viewModel`-equivalent). Never duplicate state per window - reconciling diverging copies is a graveyard of bugs.\n\n---\n\n## Focus Management\n\nKeyboard navigation is a first-class input on desktop. Tab order must be sane, focus rings must be visible, and removing them without an alternative is an accessibility regression.\n\n**SwiftUI - @FocusState drives field focus:**\n```swift\nstruct LoginForm: View {\n  enum Field { case email, password }\n\n  @State private var email = \"\"\n  @State private var password = \"\"\n  @FocusState private var focused: Field?\n\n  var body: some View {\n    VStack {\n      TextField(\"Email\", text: $email)\n        .focused($focused, equals: .email)\n        .onSubmit { focused = .password }\n      SecureField(\"Password\", text: $password)\n        .focused($focused, equals: .password)\n        .onSubmit(submit)\n    }\n    .onAppear { focused = .email }\n  }\n}\n```\n\n**Compose - FocusRequester + LocalFocusManager:**\n```kotlin\nval emailFocus = remember { FocusRequester() }\nval passwordFocus = remember { FocusRequester() }\nval focusManager = LocalFocusManager.current\n\nTextField(\n  value = email, onValueChange = { email = it },\n  modifier = Modifier.focusRequester(emailFocus).focusable(),\n  keyboardActions = KeyboardActions(onNext = { passwordFocus.requestFocus() }),\n)\n\nTextField(\n  value = password, onValueChange = { password = it },\n  modifier = Modifier.focusRequester(passwordFocus).focusable(),\n  keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus(); submit() }),\n)\n\nLaunchedEffect(Unit) { emailFocus.requestFocus() }\n```\n\n**Web - tabindex + :focus-visible:**\n```css\n.button {\n  \u002F* Never `outline: none` without an alternative. *\u002F\n}\n\n.button:focus-visible {\n  outline: 2px solid var(--ring);\n  outline-offset: 2px;\n}\n```\n```html\n\u003C!-- tabindex=\"0\" puts a non-interactive element in tab order -->\n\u003Cdiv role=\"button\" tabindex=\"0\" class=\"button\">Custom button\u003C\u002Fdiv>\n```\n\n---\n\n## Information Density\n\nDesktop users sit on a 13-32 inch screen with a precise pointer and full keyboard. They can - and want to - parse more information per viewport than on mobile. Use an 8px base grid (vs 4-8px mobile), persistent sidebars instead of bottom tabs, command palettes (`⌘K`) for power users, and dense data tables when the data warrants it. Linear, Things 3, and Notion are the touchstones: information-rich without feeling cramped, every pixel earns its keep.\n\n---\n\n## Subtle Animations Doctrine\n\nDesktop apps are stared at for hours. Animations that feel delightful once become unbearable on the hundredth repetition. Prefer short, purely functional motion: opacity and small translations under 200ms, no bounces on routine interactions, no playful overshoots on hover. Save expressive motion for one-shot moments (onboarding, success states), never daily UI.\n\n```css\n\u002F* BAD - every hover bounces for 600ms, exhausting after the third use *\u002F\n.card {\n  transition: transform 600ms cubic-bezier(0.34, 1.56, 0.64, 1);\n}\n.card:hover { transform: scale(1.05); }\n```\n\n```css\n\u002F* GOOD - 100ms opacity, almost subliminal, never tires *\u002F\n.card {\n  opacity: 0.92;\n  transition: opacity 100ms ease-out;\n}\n.card:hover { opacity: 1; }\n```\n\n---\n\n## Anti-Patterns (BAD \u002F GOOD)\n\n### 1. Hiding navigation behind a hamburger on desktop\n\n```html\n\u003C!-- BAD - 1440px viewport, infinite room, but nav is collapsed -->\n\u003Cheader>\n  \u003Cbutton class=\"hamburger\" aria-label=\"Menu\">☰\u003C\u002Fbutton>\n\u003C\u002Fheader>\n\u003Cnav class=\"drawer hidden\">...\u003C\u002Fnav>\n```\n\n```html\n\u003C!-- GOOD - persistent sidebar on desktop, collapsible if the user wants it -->\n\u003Caside class=\"sidebar\">\n  \u003Cnav>\n    \u003Ca href=\"\u002Finbox\">Inbox\u003C\u002Fa>\n    \u003Ca href=\"\u002Fprojects\">Projects\u003C\u002Fa>\n    \u003Ca href=\"\u002Farchive\">Archive\u003C\u002Fa>\n  \u003C\u002Fnav>\n  \u003Cbutton class=\"collapse-toggle\" aria-label=\"Collapse sidebar\">⇤\u003C\u002Fbutton>\n\u003C\u002Faside>\n```\n\n### 2. No keyboard shortcuts for primary actions\n\n```jsx\n\u002F\u002F BAD - \"New\" is buried in a menu, no shortcut, every creation is 3 clicks\n\u003CToolbar>\n  \u003CMenu>\n    \u003CMenuItem onClick={newDoc}>New document\u003C\u002FMenuItem>\n  \u003C\u002FMenu>\n\u003C\u002FToolbar>\n```\n\n```jsx\n\u002F\u002F GOOD - ⌘N baseline, surfaced in the menu, mirrored on the toolbar tooltip\n\u003CToolbar>\n  \u003Cbutton onClick={newDoc} title=\"New document (⌘N)\">\n    \u003CPlusIcon \u002F>\n  \u003C\u002Fbutton>\n\u003C\u002FToolbar>\n\u002F\u002F Bound globally:\nuseShortcut(\"mod+n\", newDoc);\nuseShortcut(\"mod+s\", save);\nuseShortcut(\"mod+f\", focusSearch);\n```\n\n### 3. Removing focus rings without an alternative\n\n```css\n\u002F* BAD - keyboard users now have no idea where focus is *\u002F\nbutton:focus { outline: none; }\n```\n\n```css\n\u002F* GOOD - :focus-visible keeps mouse clicks ring-free, keyboard navigation visible *\u002F\nbutton:focus { outline: none; }\nbutton:focus-visible {\n  outline: 2px solid var(--brand-500);\n  outline-offset: 2px;\n  border-radius: 6px;\n}\n```\n\n---\n\n## Quick Reference: Loading sub-skills\n\n| Need | Load |\n|---|---|\n| Keyboard patterns deep-dive | `references\u002Fkeyboard-patterns.md` |\n| Multi-window state | `references\u002Fmulti-window.md` |\n| SwiftUI animations | `..\u002Fswiftui-motion\u002FSKILL.md` |\n| Compose Desktop | `..\u002Fcompose-multiplatform\u002FSKILL.md` (when desktop is a CMP target) |\n\n---\n\n## Sources\n\n- Apple Human Interface Guidelines (macOS): https:\u002F\u002Fdeveloper.apple.com\u002Fdesign\u002Fhuman-interface-guidelines\u002Fmacos\n- Microsoft Fluent Design 2: https:\u002F\u002Ffluent2.microsoft.design\u002F\n- GNOME Human Interface Guidelines: https:\u002F\u002Fdeveloper.gnome.org\u002Fhig\u002F\n",{"name":5,"description":7},"main","4c6d8d65f3d6096e2fc25b0845e481dda73c2794","skills\u002Fgenjutsu-desktop-principles",0,"https:\u002F\u002Fgithub.com\u002Fnexuslinkproductions\u002Fyuri-os","https:\u002F\u002Fgithub.com\u002Fnexuslinkproductions","Building deterministic AI governance and continuity systems. Workflow architect + visual storyteller","2026-11-11 16:42:59",{"source":8,"skillName":5,"commands":50,"prompt":60,"zipAvailable":61},[51,54,57],{"tool":52,"command":53},"npx","npx skills add nexuslinkproductions\u002Fyuri-os --skill genjutsu-desktop-principles",{"tool":55,"command":56},"bunx","bunx skills add nexuslinkproductions\u002Fyuri-os --skill genjutsu-desktop-principles",{"tool":58,"command":59},"pnpm","pnpm dlx skills add nexuslinkproductions\u002Fyuri-os --skill genjutsu-desktop-principles","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fnexuslinkproductions\u002Fyuri-os\u002Fblob\u002Fmain\u002Fskills\u002Fgenjutsu-desktop-principles\u002FSKILL.md",true,[],[64,87,111,141,170,189],{"skillNo":65,"name":66,"nameZh":6,"description":67,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":68,"categories":69,"keywords":70,"publishedAt":36},"01M71CG1AME5WC0A79AE65WT0X","zoom-out","Tell the agent to zoom out and give broader context or a higher-level perspective. Use when you're unfamiliar with a section of code or need to understand how it fits into the bigger picture.","https:\u002F\u002Fgithub.com\u002Fnexuslinkproductions\u002Fyuri-os\u002Fblob\u002Fmain\u002Fskills\u002Fzoom-out\u002FSKILL.md",[],[71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86],"zoom out","zoom","with","harness","how","section","re","true","understand","use","perspective","when","you","code","it","context",{"skillNo":88,"name":89,"nameZh":6,"description":90,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":91,"categories":92,"keywords":93,"publishedAt":36},"01M71CFY9ATD0BAXV6B14HFG7C","yuri-shura","6-perspective adversarial review for high-stakes turns (architecture decisions, refactor planning, deployment review) — fans out architect, adversary (7-vector attack), maintainer, ops, product, and security perspectives in parallel, then consolidates. Use when user says 'review this decision', '\u002Fshura', 'adversarial review', 'what could go wrong', 'architecture review', or 'deployment review'.","https:\u002F\u002Fgithub.com\u002Fnexuslinkproductions\u002Fyuri-os\u002Fblob\u002Fmain\u002Fskills\u002Fyuri-shura\u002FSKILL.md",[],[94,95,74,96,97,98,99,100,101,102,103,80,104,82,105,106,107,108,109,110],"yuri shura","parallel","architecture review","planning","for","deployment","vector","strategic review","decision","strategic","ops","workflow","decisions","architecture","review","perspectives","adversarial",{"skillNo":112,"name":113,"nameZh":6,"description":114,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":115,"categories":116,"keywords":122,"publishedAt":36},"01M71CFTTYW1HA2MNX38EV1GJ5","yuri-sales-intelligence","Unified sales and outreach skill generating cold outreach openers, social selling sequences, objection rebuttals, and ad creative for any paid platform — multi-persona aware, adapting tone and offer frame to buyer type. Use when user says 'write a cold email', 'outreach sequence', 'objection handling', 'ad copy', or 'sales pitch'.","https:\u002F\u002Fgithub.com\u002Fnexuslinkproductions\u002Fyuri-os\u002Fblob\u002Fmain\u002Fskills\u002Fyuri-sales-intelligence\u002FSKILL.md",[117],{"categoryNo":118,"categoryName":119,"taxonomyType":120,"taxonomyTypeDesc":121},"01JBSEEDCAT000000000000005","邮件与沟通","SCENARIO","使用场景",[123,124,125,126,127,128,129,130,131,82,132,133,134,135,136,137,138,98,139,140],"intelligence","sales intelligence","yuri sales intelligence","sales","cold","paid","social","persona","pitch","cold email","ad creative","write","sequences","sequence","platform","email","cold outreach","frame",{"skillNo":142,"name":143,"nameZh":6,"description":144,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":145,"categories":146,"keywords":155,"publishedAt":36},"01M71CFQMBFTFADJNGP0Z2WCYK","yuri-code-intelligence","Unified code quality intelligence layer that diagnoses code smells (Long Method, Feature Envy, design pattern issues), prescribes targeted Fowler refactoring techniques, and reviews code for security, performance, and clarity. Use when user says 'review this code', 'refactor this', 'what's wrong with this method', 'code smells', or 'clean this up'.","https:\u002F\u002Fgithub.com\u002Fnexuslinkproductions\u002Fyuri-os\u002Fblob\u002Fmain\u002Fskills\u002Fyuri-code-intelligence\u002FSKILL.md",[147,150],{"categoryNo":148,"categoryName":149,"taxonomyType":120,"taxonomyTypeDesc":121},"01JBSEEDCAT000000000000007","代码审查",{"categoryNo":151,"categoryName":152,"taxonomyType":153,"taxonomyTypeDesc":154},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[156,84,157,123,74,158,159,160,98,161,162,163,164,165,166,167,168,169,80,82],"yuri code intelligence","code intelligence","reviews","code quality","feature","performance","code review","long","up","issues","clean","techniques","improve","design",{"skillNo":171,"name":172,"nameZh":6,"description":173,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":174,"categories":175,"keywords":176,"publishedAt":36},"01M71CFP32RT6GD3GKNHCJP8VP","yuri-adw","Invoke when user asks to run the ADW loop \u002F agentic engineering loop \u002F software factory workflow.","https:\u002F\u002Fgithub.com\u002Fnexuslinkproductions\u002Fyuri-os\u002Fblob\u002Fmain\u002Fskills\u002Fyuri-adw\u002FSKILL.md",[],[177,178,179,180,181,182,183,184,185,82,186,105,187,188],"yuri adw","run","engineering loop","software factory","software","agentic engineering","factory","loop","invoke","engineering","agentic","user",{"skillNo":190,"name":191,"nameZh":6,"description":192,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":193,"categories":194,"keywords":207,"publishedAt":36},"01M71CFK4VPEKY6A73237P7RSN","writing-skills","Use when creating new skills, editing existing skills, or verifying skills work before deployment","https:\u002F\u002Fgithub.com\u002Fnexuslinkproductions\u002Fyuri-os\u002Fblob\u002Fmain\u002Fskills\u002Fwriting-skills\u002FSKILL.md",[195,198,201,204],{"categoryNo":196,"categoryName":197,"taxonomyType":120,"taxonomyTypeDesc":121},"01JBSEEDCAT000000000000008","测试与质量",{"categoryNo":199,"categoryName":200,"taxonomyType":120,"taxonomyTypeDesc":121},"01JBSEEDCAT000000000000018","写作与文案",{"categoryNo":202,"categoryName":203,"taxonomyType":120,"taxonomyTypeDesc":121},"01JBSEEDCAT000000000000023","Skill与智能体开发",{"categoryNo":205,"categoryName":206,"taxonomyType":153,"taxonomyTypeDesc":154},"01JBSEEDCAT000000000000036","内容创作者",[208,209,210,82,105,211,74,212,99,80,213],"writing","writing skills","new","work","editing","Existing"]