react-x11 2.51.0 → 2.52.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/README.md +87 -54
  2. package/docs/README.md +410 -0
  3. package/docs/accessibility.md +628 -0
  4. package/docs/appearance.md +416 -0
  5. package/docs/architecture/accessibility.md +470 -0
  6. package/docs/architecture/animation.md +707 -0
  7. package/docs/architecture/container-queries.md +525 -0
  8. package/docs/architecture/custom-layout.md +334 -0
  9. package/docs/architecture/drag-and-drop.md +941 -0
  10. package/docs/architecture/element-layer-contents.md +622 -0
  11. package/docs/architecture/frame-pacing.md +255 -0
  12. package/docs/architecture/grid-layout.md +358 -0
  13. package/docs/architecture/protocol-efficiency.md +357 -0
  14. package/docs/architecture/video.md +1149 -0
  15. package/docs/architecture/windowed-regions.md +854 -0
  16. package/docs/click-to-component.md +125 -0
  17. package/docs/clipboard.md +281 -0
  18. package/docs/components.md +1749 -0
  19. package/docs/dbus.md +277 -0
  20. package/docs/debugging.md +464 -0
  21. package/docs/desktop-calendar.md +284 -0
  22. package/docs/desktop.md +648 -0
  23. package/docs/devtools.md +250 -0
  24. package/docs/drag-and-drop.md +663 -0
  25. package/docs/ecosystem/animation.md +394 -0
  26. package/docs/ecosystem/data-fetching.md +201 -0
  27. package/docs/ecosystem/dev-tooling.md +436 -0
  28. package/docs/ecosystem/forms.md +313 -0
  29. package/docs/ecosystem/headless.md +219 -0
  30. package/docs/ecosystem/i18n.md +89 -0
  31. package/docs/ecosystem/icons.md +410 -0
  32. package/docs/ecosystem/layout.md +216 -0
  33. package/docs/ecosystem/routing.md +146 -0
  34. package/docs/ecosystem/state.md +361 -0
  35. package/docs/ecosystem/testing.md +500 -0
  36. package/docs/ecosystem/theming.md +540 -0
  37. package/docs/ecosystem.md +473 -0
  38. package/docs/elements.md +2762 -0
  39. package/docs/embedding.md +288 -0
  40. package/docs/events.md +1025 -0
  41. package/docs/extending.md +2242 -0
  42. package/docs/eyedropper.md +223 -0
  43. package/docs/filedialog.md +258 -0
  44. package/docs/frame.md +580 -0
  45. package/docs/gl.md +170 -0
  46. package/docs/globalmenu.md +278 -0
  47. package/docs/glx.md +137 -0
  48. package/docs/macos.md +3295 -0
  49. package/docs/notifications.md +164 -0
  50. package/docs/packaging.md +611 -0
  51. package/docs/permissions.md +148 -0
  52. package/docs/react-features.md +430 -0
  53. package/docs/remote.md +223 -0
  54. package/docs/scale.md +339 -0
  55. package/docs/security.md +132 -0
  56. package/docs/styling.md +1928 -0
  57. package/docs/system.md +599 -0
  58. package/docs/testing.md +514 -0
  59. package/docs/typescript.md +117 -0
  60. package/docs/uri-schemes.md +380 -0
  61. package/docs/wayland-backend.md +591 -0
  62. package/docs/wayland.md +545 -0
  63. package/docs/windows-embedding.md +271 -0
  64. package/docs/windows-gl.md +221 -0
  65. package/docs/windows-integrations.md +233 -0
  66. package/docs/windows.md +1404 -0
  67. package/package.json +10 -3
  68. package/src/bus.js +39 -26
  69. package/src/cli/create.d.ts +43 -0
  70. package/src/cli/create.js +305 -0
  71. package/src/cli/env.js +484 -0
  72. package/src/cli/index.js +29 -2
  73. package/src/cli/templates/AGENTS.template.md +195 -0
  74. package/src/cli/templates/DESIGN.template.md +269 -0
  75. package/src/desktopintegration.js +2 -1
  76. package/src/desktopsettings.js +193 -24
  77. package/src/filedialog.js +3 -3
  78. package/src/keyboardstate.js +28 -0
  79. package/src/node.d.ts +10 -10
  80. package/src/portal.js +31 -0
  81. package/src/screencolor.js +5 -10
  82. package/src/types/system.d.ts +8 -6
  83. package/src/wayland/app.js +59 -0
  84. package/src/wayland/clipboard.js +14 -7
  85. package/src/wayland/connection.js +3 -0
  86. package/src/wayland/context2d.js +129 -10
  87. package/src/wayland/protocols/index.json +6 -0
  88. package/src/wayland/protocols/xdg-foreign-unstable-v2.json +1 -0
  89. package/src/wayland/seat.js +28 -1
  90. package/src/wayland/swapchain.js +23 -0
  91. package/src/wayland/xkb.js +137 -0
@@ -0,0 +1,195 @@
1
+ <!-- BEGIN:react-x11-agent-rules -->
2
+
3
+ # This is react-x11, not react-dom
4
+
5
+ This app is React drawing native desktop windows with react-x11
6
+ {{version}}. There is no DOM, no browser and no CSS. What you know about
7
+ React carries over: components, hooks, state, context, Suspense, error
8
+ boundaries. What you know about the DOM does not. Before writing UI, read the
9
+ docs installed with the package. They match the version this app runs and
10
+ are newer than your training data.
11
+
12
+ ## Where to look
13
+
14
+ **Docs**, installed with the package (paths from this file's directory):
15
+ `node_modules/react-x11/docs/`
16
+
17
+ | page | for |
18
+ | --------------------------------------------------- | ---------------------------------------------------------------------------- |
19
+ | `react-features.md` | **read first**: where a DOM habit breaks (refs, measuring, portals, effects) |
20
+ | `elements.md` | the host elements and their props |
21
+ | `styling.md` | the `style` prop: layout, paint, `:hover` blocks, theme tokens |
22
+ | `components.md` | `Button`, `Select`, `Dialog`, `MenuBar`, `Tabs`, `Table`, `SplitPane`, … |
23
+ | `events.md` | events, focus, keys, shortcuts |
24
+ | `appearance.md` | light and dark, the accent, reduced motion |
25
+ | `globalmenu.md` | where `MenuBar` goes on each platform |
26
+ | `testing.md` | `react-x11/test`: render headless, query, click, type, write a PNG |
27
+ | `accessibility.md` | roles, names, what a screen reader hears |
28
+ | `filedialog.md`, `notifications.md`, `clipboard.md` | the desktop around the app |
29
+ | `drag-and-drop.md`, `system.md`, `packaging.md` | more of it, and shipping the app |
30
+ | `ecosystem/*.md` | state, forms, routing, data fetching, i18n with libraries you know |
31
+ | `README.md` | the map of every page |
32
+
33
+ The same pages are online at
34
+ `https://sidorares.github.io/react-x11/docs/reference/<page>`.
35
+
36
+ **Types**: `node_modules/react-x11/src/index.d.ts` and
37
+ `node_modules/react-x11/src/types/*.d.ts` hold every element, style key, event
38
+ and widget with its props. Grep them for any prop you are not sure of.
39
+
40
+ **Examples** are not in the package. These are pinned to this version:
41
+ `https://github.com/sidorares/react-x11/tree/v{{version}}/examples`, raw files
42
+ at `https://raw.githubusercontent.com/sidorares/react-x11/v{{version}}/examples/<file>`.
43
+ `finder.jsx` and `settings.jsx` (sidebar and content), `tasks.jsx` (list and
44
+ input), `widgets.jsx` (every control), `dashboard.jsx`, `menu.jsx`,
45
+ `theming.jsx`. Read the closest one or two before writing a screen.
46
+
47
+ ## Rules you will trip on
48
+
49
+ Most of these throw with a message that names the fix. Read the message
50
+ before guessing.
51
+
52
+ - **Host elements are lowercase and few**: `<window>`, `<popup>`, `<box>`,
53
+ `<text>`, `<textinput>`, `<textarea>`, `<image>`, `<svg>`, `<canvas>`,
54
+ `<video>`, `<glarea>`. No `<div>`, `<span>`, `<button>`, `<input>`.
55
+ - **Everything drawn is inside a `<window>`.** `<window>` takes its geometry as
56
+ props, not style: `title`, `width`, `height`, `minWidth`, `minHeight`,
57
+ `resizable`.
58
+ - **Raw strings and numbers go only inside `<text>`.** `<box>hi</box>` throws.
59
+ A `<text>` inside a `<text>` is a styled span.
60
+ - **Style lives in `style`**, an object or an array (`[s.row, on && s.on]`,
61
+ falsy entries skipped). `<box padding={8}>` throws. There is no
62
+ `className`, no CSS file, no Tailwind, no styled-components.
63
+ - **State blocks paint only.** `':hover'`, `':active'`, `':focus'`,
64
+ `':focus-visible'`, `':focus-within'` and `':disabled'` inside `style` may set
65
+ colours, `background`, `opacity`, `outline`, `borderRadius`. Not padding,
66
+ size or `border` (its width is layout).
67
+ - **Flexbox is React Native's direction with CSS's shrink**: `flexDirection`
68
+ defaults to `'column'`, and children of a column stretch to its full width.
69
+ A `Button` in a column becomes a slab: give it `alignSelf: 'flex-start'`, or
70
+ put buttons in a `flexDirection: 'row'` box. `display: 'grid'` and `gap`
71
+ work as in CSS.
72
+ - **A pane smaller than its content scrolls** only with
73
+ `overflow: 'scroll'` (or `'hidden'`, or `minHeight: 0`). For 1000+ rows use
74
+ `Table`, which is virtualized.
75
+ - **Colours are theme tokens**: `'$background'`, `'$surface'`, `'$text'`,
76
+ `'$textMuted'`, `'$border'`, `'$accent'`, `'$accentText'`, `'$accentHover'`,
77
+ `'$accentActive'`, `'$surfaceHover'`, `'$surfaceActive'`,
78
+ `'$hoverBackground'`/`'$hoverText'` (a selected row), `'$danger'`,
79
+ `'$success'`, `'$warning'`, `'$info'`, `'$link'`. They follow the desktop's
80
+ light or dark scheme and its accent. A hard-coded grey is wrong in one of
81
+ the two. `useTheme()` gives the resolved values, for `<canvas>` drawing.
82
+ - **Events**: `onClick`, `onMouseDown`/`Up`/`Move`/`Enter`/`Leave`, `onWheel`,
83
+ `onContextMenu`, and `onKeyDown`/`onKeyUp` on an element that is
84
+ `focusable`. `Button` takes **`onPress`**. Every input's `onChange` gets an
85
+ event: read `ev.value`. Keys: compare `ev.keysym` with `XK_RETURN`,
86
+ `XK_ESCAPE`, `XK_DOWN`, … from `'react-x11/keysyms'`.
87
+ - **Shortcuts**: `[['Control', 'S']]` is ⌘S on macOS and Ctrl+S elsewhere. Put
88
+ them on `MenuBar` items (`shortcut`) or use `useAccelerator(chord, fn)`.
89
+ Never bind Copy, Paste, Cut, Select All or Undo in a menu while a text field
90
+ can have focus: the fields handle those already, and both would run.
91
+ - **Web UI libraries do not work** (MUI, Radix, shadcn, recharts, react-dom
92
+ anything). Plain JavaScript libraries do: zustand, zod, date-fns, `fetch`.
93
+ - **No `localStorage`.** Persist with
94
+ `createSettings({ appId, defaults })` from `react-x11`
95
+ (`const [value, setValue] = settings.use('key')`) or with `node:fs`.
96
+ - **Paths are absolute**: `new URL('./data.json', import.meta.url)`. On macOS
97
+ the app's JavaScript runs on a worker thread, where `process.chdir()` throws.
98
+
99
+ ## Bigger components
100
+
101
+ Charts, a data table, a tree, tabs, markdown, a code editor, a rich text
102
+ editor, a calendar and date picker, a colour picker, a timeline, a reorderable
103
+ list, flow graphs, maps, formulas and a 3D scene are in
104
+ `@react-x11/components`. Install it, then import from its subpaths, not the
105
+ package root: `@react-x11/components/charts`, `/table`, `/tree`, `/tabs`,
106
+ `/markdown`, `/code-editor`, `/rich-text-editor`, `/calendar`,
107
+ `/color-picker`, `/timeline`, `/reorder`, `/flow`, `/maps`, `/formula`,
108
+ `/three`. Its `README.md` and `docs/components/<name>.md` are in
109
+ `node_modules/@react-x11/components/`, and each subpath has its own `.d.ts`.
110
+
111
+ ## Commands
112
+
113
+ - `{{run}} start` runs the app once.
114
+ - `{{run}} dev` runs it with hot reload. It keeps running and is meant for a
115
+ person at the keyboard; do not wait for it to exit.
116
+ - `npx react-x11 help` lists every `REACT_X11_*` variable.
117
+ `REACT_X11_DEBUG_LAYOUT=1` outlines every laid-out box, for a layout that
118
+ does not do what you expect.
119
+
120
+ ## Check your work: run it and look at it
121
+
122
+ Passing tests are not the finish line. A clipped label, a stretched button or
123
+ a colour that is wrong in dark mode passes every test and is obvious in a
124
+ picture.
125
+
126
+ 1. Render the app headless and write a picture of each scheme.
127
+ `react-x11/test` runs a real X server inside the test process: no window
128
+ opens, nothing to install but `pngjs` (`npm install -D pngjs`).
129
+
130
+ ```jsx
131
+ // test/App.test.jsx — run with: node --import tsx --test test/*.test.jsx
132
+ import React from 'react';
133
+ import { mkdirSync } from 'node:fs';
134
+ import { afterEach, test } from 'node:test';
135
+ import {
136
+ cleanup,
137
+ renderX11,
138
+ screen,
139
+ toPNG,
140
+ userEvent,
141
+ } from 'react-x11/test';
142
+ import App from '../src/App.jsx';
143
+
144
+ afterEach(cleanup);
145
+ // a font file, so text measures the same every run; Linux finds one itself
146
+ const fonts = {
147
+ darwin: { 'sans-serif': '/System/Library/Fonts/Supplemental/Arial.ttf' },
148
+ win32: { 'sans-serif': 'C:\\Windows\\Fonts\\segoeui.ttf' },
149
+ }[process.platform];
150
+
151
+ for (const colorScheme of ['light', 'dark']) {
152
+ test(`renders in ${colorScheme}`, async () => {
153
+ const { ctx, window: win } = await renderX11(<App />, {
154
+ wrap: false, // App renders its own <window>
155
+ fonts,
156
+ colorScheme,
157
+ });
158
+ // drive it the way a person would, then assert on what shows
159
+ // await userEvent.click(screen.getByRole('button', { name: 'Add' }));
160
+ // screen.getByText('1 item');
161
+ mkdirSync('shots', { recursive: true });
162
+ const { width, height } = win; // the size the app's window came out
163
+ await toPNG(ctx, `shots/${colorScheme}.png`, { width, height });
164
+ });
165
+ }
166
+ ```
167
+
168
+ Queries are Testing Library's: `getByText`, `getByRole(role, { name })`,
169
+ `getByPlaceholder`, `findBy*`, `queryBy*`, and `userEvent.click`, `type`,
170
+ `key`, `hover`. A `getBy*` that matches twice throws: add
171
+ `{ exact: true }` or narrow with `within(node)`.
172
+
173
+ 2. **Open both PNGs and look at them.** Fix what looks wrong: clipped or
174
+ overlapping text, a cramped or empty window, rows that do not line up, a
175
+ colour that ignores dark mode, a control with no hover or pressed state.
176
+ Render again. Two or three passes is normal.
177
+ 3. The headless render is the X11 backend: native control bezels, the macOS
178
+ menu bar and SF Symbols do not appear in it, and bold may come out regular.
179
+ Judge layout, spacing, colour and text from it. Then run `{{run}} start`
180
+ for a few seconds to confirm the real app starts with nothing in its
181
+ output.
182
+
183
+ ## Design
184
+
185
+ Read [DESIGN.md](DESIGN.md) before writing UI. It says how this app should
186
+ look and how to get each piece out of react-x11.
187
+
188
+ <!-- END:react-x11-agent-rules -->
189
+
190
+ The block above comes from react-x11 {{version}}. Keep notes about this app
191
+ below it, so the block can be replaced with a newer version's.
192
+
193
+ ## This app
194
+
195
+ <!-- What it is, who it is for, and the decisions an agent should keep to. -->
@@ -0,0 +1,269 @@
1
+ # Design
2
+
3
+ How this app should look and behave. People and coding agents both read it
4
+ before changing the UI.
5
+
6
+ The first section is this app's brief: fill it in. The rest is the house
7
+ style react-x11 {{version}} starts every app with: the conventions of the
8
+ desktops the app runs on, and how to get each one out of react-x11. Change
9
+ any of it that this app does differently. Then the agent follows your rules
10
+ and not the defaults.
11
+
12
+ ## This app
13
+
14
+ - **What it is**: _one line: what someone opens it to do._
15
+ - **Who uses it, and how often**: _all day, or once a week?_
16
+ - **Platforms**: macOS, Windows and Linux, each looking like itself. _Or
17
+ name the one that matters most._
18
+ - **Layout**: _sidebar and content, three panes, content and inspector, or
19
+ one compact window (see "Window anatomy")._
20
+ - **Density**: regular. _Compact for a data-heavy tool._
21
+ - **Colour**: the desktop's own, with the user's accent. No brand palette.
22
+ _If there is a brand colour, name it and the one or two places it may
23
+ appear._
24
+ - **Voice**: short and plain. Buttons are verbs ("Add Account", not "OK").
25
+
26
+ ## Look like the desktop it runs on
27
+
28
+ A desktop app is quiet, dense, aligned and driven from the keyboard. It is
29
+ light or dark because the desktop is. People notice in a second when an app is
30
+ a web page in a window.
31
+
32
+ react-x11 gives you most of this already:
33
+
34
+ - The theme tokens are the desktop's palette, its light or dark scheme and its
35
+ accent.
36
+ - The widgets wear AppKit's own bezels on macOS.
37
+ - `MenuBar` goes where the platform keeps menus.
38
+
39
+ Keep those defaults, and spend the effort on layout, copy and behaviour.
40
+
41
+ ## Window anatomy
42
+
43
+ - **One main window** with a clear structure. Pick one of the standard
44
+ layouts:
45
+ - **Sidebar and content** (Mail, Files, Explorer): a list of places or
46
+ collections on the left, 200–260 px wide, then the content. This is the
47
+ default for anything with collections of things.
48
+ - **Sidebar, list and detail**: three columns, with the middle list
49
+ 260–320 px wide.
50
+ - **Content and inspector**: a 240–300 px panel on the right with the
51
+ properties of the selection.
52
+ - **One compact window** for a single-purpose utility: a timer, a
53
+ converter. This needs no sidebar.
54
+ - **A toolbar** under the title bar: a 40–48 px row with the few most-used
55
+ actions as icon buttons, and a search field at the trailing end.
56
+ - **Dialogs for short tasks**: a modal dialog for a three-field "New item…"
57
+ form, and a popup anchored to what it edits for a small editor. Never take
58
+ over the whole window for a short form.
59
+ - **Size**: 900×600 by default for a sidebar app, 720×480 for a simple one,
60
+ about 360×480 for a utility. **Always set a minimum size** (about 600×400
61
+ for a sidebar app), so the layout never collapses.
62
+
63
+ ## Menus and shortcuts
64
+
65
+ People try a shortcut before they look for a button. Write shortcuts with
66
+ `Control`: it is ⌘ on macOS and Ctrl elsewhere. The standard menus, in order:
67
+
68
+ - **File**: New (Control+N), Open… (Control+O), Close Window (Control+W), Save
69
+ (Control+S), Save As… (Control+Shift+S), Export…, Print… (Control+P)
70
+ - **Edit**: Undo, Redo, Cut, Copy, Paste, Delete, Select All, Find…
71
+ (Control+F)
72
+ - **View**: Show or Hide Sidebar, view modes (Control+1, Control+2, …), Zoom
73
+ In and Zoom Out
74
+ - _the app's own_: its own verbs (Item, Format, Playback, …), with shortcuts
75
+ for the frequent ones
76
+ - **Help**: _App name_ Help
77
+
78
+ And the rules around them:
79
+
80
+ - **Where the menu shows**: on macOS `MenuBar` becomes the system menu bar.
81
+ On Linux it goes to the desktop's panel where one hosts menus (KDE
82
+ Plasma), and is drawn in the window everywhere else, Windows included.
83
+ - **Settings and Quit**: on macOS, react-x11 adds the app menu itself, with
84
+ Quit (⌘Q) only. Put "Settings…" (Control+,) at the end of your first menu.
85
+ On Windows and Linux, put Settings… in Edit or File, and Quit (Control+Q) at
86
+ the end of File.
87
+ - An item that opens a window or asks for more ends in "…". An item that does
88
+ not apply is disabled, not hidden. Every frequent action is in a menu, even
89
+ when it also has a button.
90
+ - The text fields already handle Copy, Paste, Cut, Select All, Undo and Redo,
91
+ with a right-click menu of their own. Do not put those shortcuts on menu
92
+ items while a field can have focus: both would run.
93
+
94
+ ## Type
95
+
96
+ - **Base size**: 13 on macOS, which is AppKit's body size, and 14 elsewhere,
97
+ which is react-x11's default. Set it once at the root (see the recipes
98
+ below). Never set `fontFamily`: the default is the system's UI face.
99
+ - **Scale**: base for body, controls and lists; base −2 in `'$textMuted'` for
100
+ secondary text and captions; base +2 and +4, semibold (600), for titles; base
101
+ +9 for a rare large title. Use at most three sizes on one screen.
102
+ Hierarchy comes from weight and the muted colour, not from size jumps.
103
+ - A number that changes while you watch (a timer, a price, a count) uses
104
+ `fontVariantNumeric: 'tabular-nums'`.
105
+ - A one-line label that can overflow: `textWrap: 'nowrap'` and
106
+ `textOverflow: 'ellipsis'`.
107
+
108
+ ## Colour
109
+
110
+ - **Semantic tokens only**: `'$background'` for the window, `'$surface'` for
111
+ lists, fields and raised areas, `'$text'`, `'$textMuted'`, `'$border'` for
112
+ separators, `'$accent'`, `'$hoverBackground'` and `'$hoverText'` for a
113
+ selected row, and `'$danger'`, `'$success'`, `'$warning'`, `'$info'` for
114
+ status. A hard-coded grey is wrong in light or in dark.
115
+ - **The accent is the user's**, not yours. Use it for the primary button, the
116
+ selection, focus and toggles.
117
+ - **Colour highlights; it is not the ground.** Most of a window is neutral.
118
+ Big saturated panels are a web look.
119
+ - **Light and dark both look intended.** Shadows almost vanish in dark, so
120
+ separation comes from a slightly lighter surface instead.
121
+
122
+ ## Spacing and sizes
123
+
124
+ - A 4 and 8 px grid: 20 px window margins (16 compact), 8 px between related
125
+ controls, 16–20 px between groups, 6–12 px of padding inside a row.
126
+ - Let the widgets size themselves: they follow the platform. Rows you draw
127
+ yourself: 28 px on macOS, 32–36 px elsewhere.
128
+ - Corners match the widgets'. `useTheme().radius` is their radius.
129
+ - Separators are 1 px lines in `'$border'`, not borders around every box.
130
+ - Align everything to a few vertical lines. Form labels are either
131
+ right-aligned against a column of fields or stacked above them in a narrow
132
+ pane.
133
+
134
+ ## Interaction
135
+
136
+ - **Every input gets an answer on the event that started it**: a hover tint,
137
+ a distinct pressed state on mouse down, then the action on release.
138
+ - **Keyboard**: Tab moves focus, Space and Return activate, arrows move
139
+ through lists and grids, Return confirms a dialog, Escape cancels it,
140
+ Control+F focuses search, Delete removes the selection. The focus ring
141
+ shows on keyboard focus.
142
+ - **Selection**: one click selects (a filled row in `'$hoverBackground'`),
143
+ and a double click or Return opens.
144
+ - **Right click** shows a context menu with the item's verbs.
145
+ - **Dialog buttons** are named for what they do ("Delete 3 Items", not
146
+ "Yes"). On macOS and GNOME, Cancel comes first and the action is last, at
147
+ the trailing edge. On Windows and KDE the action comes first, then Cancel.
148
+ - **Destructive actions** can be undone, or are confirmed in a dialog whose
149
+ button says what it deletes.
150
+ - **Empty states**: a short centred line in `'$textMuted'` ("No Notes"), an
151
+ optional one-line explanation, and at most one button. Never a blank pane.
152
+ - **Success is shown in place**, not in a "Saved!" dialog. Dialogs are for
153
+ decisions.
154
+
155
+ ## Icons
156
+
157
+ - Icons label places and actions in sidebars and toolbars. They do not
158
+ decorate paragraphs. Never use emoji as UI icons.
159
+ - Use the platform's own icons, which are monoline, sized like the text and
160
+ drawn in its colour: `<image src={{ symbol }} />` draws an SF Symbol on
161
+ macOS (`'folder'`) and an icon from the freedesktop theme on Linux
162
+ (`'folder-symbolic'`). A name the desktop lacks draws nothing, and Windows
163
+ has no icon theme to draw from. So an icon is never the only label, and an
164
+ app that needs icons everywhere ships its own.
165
+ - Affordances (chevrons, check, close, plus, more) come from `<Icon name>`,
166
+ which is the same everywhere.
167
+
168
+ ## Motion and accessibility
169
+
170
+ - Motion is subtle and fast: 120–200 ms for hover, selection and disclosure.
171
+ Nothing bounces, and nothing animates on a timer the user did not start.
172
+ With reduced motion on, cut or cross-fade instead.
173
+ - Every control has a name a screen reader can say: an icon-only button needs
174
+ an `aria-label`. Text contrast is at least 4.5:1. Nothing is reachable by
175
+ mouse only. State is never shown by colour alone: add an icon or a word.
176
+
177
+ ## The same, in react-x11
178
+
179
+ - **Window size and minimum**: `<window title="…" width={900} height={600}
180
+ minWidth={600} minHeight={400}>`
181
+ - **The app's name on macOS**: `createRoot({ cocoa: { appName: 'Notes' } })`:
182
+ what the menu bar and ⌘-Tab show. Without it they say "node".
183
+ - **Base type size**: `<ThemeProvider value={{ fontSize: process.platform ===
184
+ 'darwin' ? 13 : 14 }}>` around the app.
185
+ - **Menu bar**: `<MenuBar menus={[{ label: 'File', items: [...] }]} />` inside
186
+ the window. Item: `{ label, onSelect, shortcut: [['Control', 'N']], enabled,
187
+ items }`, or `{ type: 'separator' }`. A check item: `toggleType:
188
+ 'checkmark', toggleState: 0 or 1`.
189
+ - **A shortcut with no menu item**: `useAccelerator([['Control', 'K']],
190
+ handler)`
191
+ - **Sidebar**: a `<box>` with `width: 220`, `backgroundColor: '$surface'`,
192
+ `borderEndWidth: 1`, `borderColor: '$border'`, `padding: 8`, `gap: 2`. Rows:
193
+ `flexDirection: 'row'`, `alignItems: 'center'`, `gap: 8`, `paddingInline:
194
+ 8`, `borderRadius: 6`, `':hover': { backgroundColor: '$surfaceHover' }`, and
195
+ when selected `backgroundColor: '$hoverBackground'` with text in
196
+ `'$hoverText'`. Section headers: base −2, `fontWeight: 600`, `'$textMuted'`.
197
+ See `examples/finder.jsx` and `settings.jsx`.
198
+ - **Resizable split**: `<SplitPane direction="row" defaultSize={240}
199
+ min={180}>` with two children.
200
+ - **Toolbar**: a row under the title bar: `flexDirection: 'row'`, `alignItems:
201
+ 'center'`, `gap: 8`, `height: 44`, `paddingInline: 12`, `borderBottomWidth:
202
+ 1`, `borderColor: '$border'`. Icon buttons in it, then `<textinput
203
+ placeholder="Search" />` last.
204
+ - **Buttons**: `<Button onPress={…}>`. `primary` is the default action, one
205
+ per view. `variant="outline"` for a secondary one, `variant="ghost"
206
+ size="small"` for toolbar and inline icon buttons. Never size a `Button`
207
+ yourself: `style` sizes its slot.
208
+ - **Other controls**: `Checkbox`, `Switch`, `RadioGroup` and `Radio`,
209
+ `Slider`, `Select` (`options`, `value`), `ProgressBar`, `Tooltip label="…"`,
210
+ `<textinput placeholder>`, `<textarea>`. Every `onChange(ev)` gives
211
+ `ev.value`.
212
+ - **Tabs**: `<Tabs items={[{ id, label, content }]} />`
213
+ - **Modal dialog**: `<Dialog open title="…" onClose={…}
214
+ actions={<>…buttons…</>}>`: a real window that keeps focus. Escape and the
215
+ close button call `onClose`. `autoFocus` on its first field.
216
+ - **Context menu and popover**: `<ContextMenu items={[…]}>` around the node it
217
+ belongs to. `<popup anchor={{ to: ref, placement: 'bottom' }}>` for a custom
218
+ popover.
219
+ - **Lists, tables, trees**: `Table` (`columns: [{ id, label, width, value,
220
+ align }]`, `rows`, `onActivate`) for sortable, virtualized data. `Tree` from
221
+ `@react-x11/components/tree`. Draw short lists yourself.
222
+ - **Hover and press on your own box**: `cursor: 'pointer'`, `':hover': {
223
+ backgroundColor: '$surfaceHover' }`, `':active': { backgroundColor:
224
+ '$surfaceActive' }`, `transition: 120`.
225
+ - **Focus and keys**: `focusable` (and `tabIndex`) on a custom control, and
226
+ the focus ring comes with it. `onKeyDown={(ev) => ev.keysym === XK_DOWN &&
227
+ …}` with keysyms from `react-x11/keysyms`. `role` and `aria-label` so a
228
+ screen reader can name it.
229
+ - **Light, dark, reduced motion**: tokens follow the desktop.
230
+ `useSystemAppearance()` gives `{ colorScheme, accent, reducedMotion, … }`,
231
+ and `transition: reducedMotion ? 0 : 150` respects it.
232
+ - **A tinted alert**: `backgroundColor: tint(theme.danger, 0.12)` with `tint`
233
+ from `react-x11/style` and `theme` from `useTheme()`, and `'$danger'` for
234
+ its text.
235
+ - **Files, notifications, settings**: `useFileDialog()` gives `openFile()` and
236
+ `saveFile()`, which resolve to paths or `null`. `useNotifier()` gives
237
+ `notify({ summary, body })`: check `available` first and never depend on it.
238
+ `createSettings({ appId, defaults })` remembers state.
239
+ - **Charts**: `@react-x11/components/charts`, at least 200 px tall, with
240
+ series in `'$accent'`, `'$info'`, `'$success'`, and a tooltip.
241
+
242
+ ## What makes it look like a website (avoid)
243
+
244
+ - **A hero header**: a big title and tagline at the top of the window. The
245
+ window's title is in the title bar, and content starts with content.
246
+ - **Card soup**: every item in a rounded, shadowed, padded card on a grey
247
+ page. Use lists with separators and selection. Keep cards for things that
248
+ are visual: a gallery, a dashboard tile.
249
+ - **A centred landing page**: content in a narrow column with empty sides.
250
+ Fill the window and align to the leading edge.
251
+ - **Phone and web patterns**: hamburger menus, bottom tab bars, floating
252
+ action buttons. Use the menu bar, a sidebar, a toolbar.
253
+ - **Wide buttons**: the column-stretch trap, and buttons styled as links.
254
+ Buttons are compact and sit in a row at the trailing edge or in a toolbar.
255
+ - **Gradients, glows, neon, emoji icons, big shadows**, and everything
256
+ rounded to 16 px or more.
257
+ - **A palette of your own that ignores dark mode**, or replaces the user's
258
+ accent.
259
+ - **Sample data that looks fake**: "Lorem ipsum", "Item 1, Item 2". Seed 10–30
260
+ plausible, varied records. It is the cheapest polish there is.
261
+ - **Buttons that do nothing.** Cut them, or disable them and say why.
262
+
263
+ ## Before calling it done
264
+
265
+ Look at a picture of every screen, in light and in dark. Is there one
266
+ obvious primary area? Do the leading edges line up, and are the row heights
267
+ consistent? Is there one accent-coloured thing per region, and is nothing
268
+ clipped? If it looks like part of the operating system rather than a
269
+ landing page, it is done.
@@ -8,7 +8,8 @@
8
8
  //
9
9
  // `appearance` the light/dark, accent, contrast and reduced-motion ladder
10
10
  // (src/appearance.js) — the settings portal, then macOS, then
11
- // XSETTINGS
11
+ // XSETTINGS — and, on the Wayland backend, the interaction
12
+ // settings read off the same portal (src/desktopsettings.js)
12
13
  // `a11y` the AT-SPI bridge, started from `createRoot()`
13
14
  // (src/a11y.js, docs/accessibility.md)
14
15
  // `globalMenu` a `MenuBar` handing its menu to the panel instead of drawing