@webship.co/workspace 0.0.0-stage → 1.0.0-beta1
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.
- package/PLUGINS.md +249 -0
- package/README.md +379 -2
- package/WORKSPACES.md +69 -0
- package/actions/ai-items.js +293 -0
- package/actions/audio.js +48 -0
- package/actions/build-order.js +79 -0
- package/actions/checks.js +95 -0
- package/actions/claude-session.js +104 -0
- package/actions/commands-center.js +258 -0
- package/actions/composer.js +267 -0
- package/actions/ddev-addons.js +90 -0
- package/actions/ddev-mail.js +80 -0
- package/actions/ddev.js +237 -0
- package/actions/folders.js +165 -0
- package/actions/images-svg.js +33 -0
- package/actions/images.js +145 -0
- package/actions/navigation.js +190 -0
- package/actions/node.js +222 -0
- package/actions/notes.js +55 -0
- package/actions/plugins.js +36 -0
- package/actions/project-make.js +28 -0
- package/actions/rag.js +202 -0
- package/actions/row-actions.js +262 -0
- package/actions/screens.js +270 -0
- package/actions/testing.js +121 -0
- package/actions/video-edit.js +54 -0
- package/ai-inventory.js +182 -0
- package/ai-items.js +202 -0
- package/assistant-cast.js +176 -0
- package/assistant-suggestions.js +91 -0
- package/assistant.js +484 -0
- package/audio-voice.js +528 -0
- package/auth-google.js +260 -0
- package/build-ai.js +52 -0
- package/build-order-page.js +191 -0
- package/build-order.js +313 -0
- package/builder-args.js +117 -0
- package/changelog.js +302 -0
- package/chat-threads.js +134 -0
- package/checks.js +105 -0
- package/claude-args.js +282 -0
- package/claude-sessions.js +129 -0
- package/command-author.js +117 -0
- package/command-block.js +20 -0
- package/commands/defaults.yml +159 -0
- package/commands-page.js +460 -0
- package/commands.js +568 -0
- package/cucumber.js +43 -0
- package/ddev-addons-page.js +124 -0
- package/ddev-addons.js +154 -0
- package/ddev-custom-commands.js +96 -0
- package/ddev-domains.js +84 -0
- package/ddev-import.js +93 -0
- package/ddev-mail-page.js +167 -0
- package/ddev-mail.js +110 -0
- package/ddev-options.js +145 -0
- package/ddev-snapshots.js +126 -0
- package/ddev.js +145 -0
- package/docs/DEVELOPER-GUIDE.md +224 -0
- package/docs/README.md +252 -0
- package/docs/REFERENCE.md +684 -0
- package/docs/USER-GUIDE.md +697 -0
- package/docs/screenshots/assistant/avatar-board.png +0 -0
- package/docs/screenshots/assistant/avatar-coffee.png +0 -0
- package/docs/screenshots/assistant/avatar-colours.png +0 -0
- package/docs/screenshots/assistant/avatar-icons.png +0 -0
- package/docs/screenshots/assistant/avatar-moves.png +0 -0
- package/docs/screenshots/assistant/avatar-pick-cookieman.png +0 -0
- package/docs/screenshots/assistant/avatar-settings-voices.png +0 -0
- package/docs/screenshots/assistant/avatar-status.png +0 -0
- package/docs/screenshots/create-card/create-card.png +0 -0
- package/docs/screenshots/create-card/start-ai.png +0 -0
- package/docs/screenshots/guide-clone-address.png +0 -0
- package/docs/screenshots/guide-clone-worktree-off.png +0 -0
- package/docs/screenshots/guide-clone-worktree-on.png +0 -0
- package/docs/screenshots/guide-ddev-menu.png +0 -0
- package/docs/screenshots/guide-domains.png +0 -0
- package/docs/screenshots/guide-home.png +0 -0
- package/docs/screenshots/guide-ide-menu.png +0 -0
- package/docs/screenshots/guide-remove-refusal.png +0 -0
- package/docs/screenshots/guide-rows-worktree.png +0 -0
- package/docs/screenshots/guide-screens.png +0 -0
- package/docs/screenshots/guide-settings.png +0 -0
- package/docs/screenshots/guide-workspace-page.png +0 -0
- package/docs/screenshots/home/card-settings.png +0 -0
- package/docs/screenshots/home/drag-reorder.png +0 -0
- package/docs/screenshots/home/edit-card.png +0 -0
- package/docs/screenshots/images/claude-code-image.png +0 -0
- package/docs/screenshots/images/edit-with-ai.png +0 -0
- package/docs/screenshots/images/gallery.png +0 -0
- package/docs/screenshots/images/generate-description.png +0 -0
- package/docs/screenshots/images/generate-plan.png +0 -0
- package/docs/screenshots/images/providers.png +0 -0
- package/docs/screenshots/images/svg-form.png +0 -0
- package/docs/screenshots/images/svg-result.png +0 -0
- package/docs/screenshots/images/upload.png +0 -0
- package/docs/screenshots/new-folder/2-form.png +0 -0
- package/docs/screenshots/new-folder/8-ddev-options.png +0 -0
- package/docs/screenshots/new-folder/files-folder.png +0 -0
- package/docs/screenshots/projects/ddev-custom-commands.png +0 -0
- package/docs/screenshots/projects/state-chip-stop.png +0 -0
- package/docs/screenshots/readme/dashboard-dark.png +0 -0
- package/docs/screenshots/readme/dashboard-light.png +0 -0
- package/docs/screenshots/readme/dev-dark.png +0 -0
- package/docs/screenshots/readme/dev-light.png +0 -0
- package/docs/screenshots/readme/docs-dark.png +0 -0
- package/docs/screenshots/readme/docs-light.png +0 -0
- package/docs/screenshots/readme/icons/ai.svg +4 -0
- package/docs/screenshots/readme/icons/code.svg +4 -0
- package/docs/screenshots/readme/icons/docs.svg +4 -0
- package/docs/screenshots/readme/icons/services.svg +4 -0
- package/docs/screenshots/readme/icons/sites.svg +4 -0
- package/docs/screenshots/readme/test-report-dark.png +0 -0
- package/docs/screenshots/readme/test-report-light.png +0 -0
- package/docs/screenshots/readme/test-scenarios.png +0 -0
- package/docs/screenshots/readme/test-steps.png +0 -0
- package/docs/screenshots/readme/videos-dark.png +0 -0
- package/docs/screenshots/readme/videos-light.png +0 -0
- package/docs/screenshots/screens-menu.png +0 -0
- package/docs/screenshots/screens-new.png +0 -0
- package/docs/screenshots/settings/auth-google-lists.png +0 -0
- package/docs/screenshots/settings/delete-1-row.png +0 -0
- package/docs/screenshots/settings/delete-2-confirm.png +0 -0
- package/docs/screenshots/settings/delete-3-type-name.png +0 -0
- package/docs/screenshots/settings/delete-4-type-yes.png +0 -0
- package/event-log.js +59 -0
- package/files-browser.js +247 -0
- package/folders.js +121 -0
- package/format.js +73 -0
- package/git-info.js +81 -0
- package/github-auth.js +62 -0
- package/html.js +10 -0
- package/icons.js +83 -0
- package/images-ai.js +447 -0
- package/images-create.js +226 -0
- package/images-page.js +417 -0
- package/images-records.js +39 -0
- package/images-render.js +36 -0
- package/images-svg-core.js +214 -0
- package/images-svg-sanitize.js +188 -0
- package/images-svg.js +241 -0
- package/images.js +270 -0
- package/jobs.js +430 -0
- package/linger.js +21 -0
- package/lists.js +292 -0
- package/media-info.js +136 -0
- package/navigation-page.js +264 -0
- package/navigation.js +394 -0
- package/notes.js +0 -0
- package/package.json +47 -4
- package/palette-index.js +46 -0
- package/playwright.config.js +23 -0
- package/plugin-install.js +239 -0
- package/plugin-loader.js +466 -0
- package/plugin-manifest.js +83 -0
- package/plugin-settings.js +44 -0
- package/plugins/workspace-notes/index.js +59 -0
- package/plugins-page.js +103 -0
- package/project-make.js +241 -0
- package/project-name.js +210 -0
- package/public/assistant-avatar-moves.js +646 -0
- package/public/assistant-avatar-ui.js +1223 -0
- package/public/assistant-avatar.js +530 -0
- package/public/avatar/druplicon.vrm +0 -0
- package/public/css/base.css +260 -0
- package/public/css/components/ai-assistant.css +263 -0
- package/public/css/components/assistant-avatar.css +279 -0
- package/public/css/components/assistant-views.css +198 -0
- package/public/css/components/build-order.css +74 -0
- package/public/css/components/clone-as-a-git-worktree.css +30 -0
- package/public/css/components/command-palette.css +61 -0
- package/public/css/components/commands-center.css +139 -0
- package/public/css/components/confirm-dialog.css +40 -0
- package/public/css/components/create-card.css +8 -0
- package/public/css/components/ddev-menu.css +23 -0
- package/public/css/components/deep-chat.css +17 -0
- package/public/css/components/density.css +11 -0
- package/public/css/components/floating-assistant-workspace-pages.css +55 -0
- package/public/css/components/folders.css +5 -0
- package/public/css/components/full-height-left-assistant-sidebar-all-pages.css +39 -0
- package/public/css/components/full-height-right-actions-rail-pages-that-have-actions.css +267 -0
- package/public/css/components/grid-and-list.css +203 -0
- package/public/css/components/help-page.css +36 -0
- package/public/css/components/hero.css +16 -0
- package/public/css/components/images.css +70 -0
- package/public/css/components/layout-rhythm-and-spacing-polish.css +139 -0
- package/public/css/components/mic-on-any-field-that-wants-typing.css +416 -0
- package/public/css/components/modal-fit.css +113 -0
- package/public/css/components/page-width.css +23 -0
- package/public/css/components/plugins-page.css +9 -0
- package/public/css/components/project-cards.css +256 -0
- package/public/css/components/project-rows.css +390 -0
- package/public/css/components/reordering-the-workspace-cards.css +85 -0
- package/public/css/components/responsive-home-and-the-workspace-pages.css +457 -0
- package/public/css/components/running-commands-as-toasts.css +327 -0
- package/public/css/components/settings-editor-core-config-yml.css +828 -0
- package/public/css/components/short-results-as-a-toast-at-the-top.css +203 -0
- package/public/css/components/sound-player.css +84 -0
- package/public/css/components/suggest-agents-skills-commands.css +112 -0
- package/public/css/components/tab-modal.css +142 -0
- package/public/css/components/terminals.css +244 -0
- package/public/css/components/test-report-media-one-file-per-row-paged.css +144 -0
- package/public/css/components/top-toolbar-logo-breadcrumb.css +107 -0
- package/public/css/components/video-edit.css +6 -0
- package/public/css/components/web-ide-open-with.css +23 -0
- package/public/css/components/working-indicators.css +158 -0
- package/public/css/components/workspace-cards.css +79 -0
- package/public/favicon.ico +0 -0
- package/public/favicon.png +0 -0
- package/public/htmx.min.js +1 -0
- package/public/logo.png +0 -0
- package/public/media-player.js +215 -0
- package/public/mic.js +255 -0
- package/public/narration.js +110 -0
- package/public/palette.js +278 -0
- package/public/sortable.js +251 -0
- package/public/suggest.js +337 -0
- package/public/terminal.js +903 -0
- package/public/themes/default/theme.css +267 -0
- package/public/themes/ember/theme.css +180 -0
- package/public/themes/press/theme.css +290 -0
- package/public/themes/slate/theme.css +182 -0
- package/public/touch-icon.png +0 -0
- package/public/ui.js +2721 -0
- package/public/vendor/ace/LICENSE.txt +24 -0
- package/public/vendor/ace/ace.js +23 -0
- package/public/vendor/ace/ext-language_tools.js +8 -0
- package/public/vendor/ace/ext-searchbox.js +8 -0
- package/public/vendor/ace/mode-css.js +8 -0
- package/public/vendor/ace/mode-html.js +8 -0
- package/public/vendor/ace/mode-javascript.js +8 -0
- package/public/vendor/ace/mode-markdown.js +8 -0
- package/public/vendor/ace/mode-sh.js +8 -0
- package/public/vendor/ace/mode-yaml.js +8 -0
- package/public/vendor/ace/theme-textmate.js +8 -0
- package/public/vendor/ace/theme-tomorrow_night.js +8 -0
- package/public/vendor/ace/worker-yaml.js +1 -0
- package/public/vendor/deep-chat.LICENSE +21 -0
- package/public/vendor/deep-chat.bundle.js +1 -0
- package/public/vendor/fonts/dm-serif-text-LICENSE.txt +93 -0
- package/public/vendor/fonts/dm-serif-text-latin-400-italic.woff2 +0 -0
- package/public/vendor/fonts/dm-serif-text-latin-400-normal.woff2 +0 -0
- package/public/vendor/fonts/figtree-LICENSE.txt +93 -0
- package/public/vendor/fonts/figtree-latin-400-italic.woff2 +0 -0
- package/public/vendor/fonts/figtree-latin-400-normal.woff2 +0 -0
- package/public/vendor/fonts/figtree-latin-600-italic.woff2 +0 -0
- package/public/vendor/fonts/figtree-latin-600-normal.woff2 +0 -0
- package/public/vendor/fonts/jetbrains-mono-LICENSE.txt +93 -0
- package/public/vendor/fonts/jetbrains-mono-latin-400-normal.woff2 +0 -0
- package/public/vendor/fonts/ubuntu-mono-LICENSE.txt +98 -0
- package/public/vendor/fonts/ubuntu-mono-latin-wght-normal.woff2 +0 -0
- package/public/vendor/tabler-LICENSE.txt +21 -0
- package/public/vendor/tabler-sprite.svg +1 -0
- package/public/vendor/three/LICENSE +21 -0
- package/public/vendor/three/addons/loaders/GLTFLoader.js +4860 -0
- package/public/vendor/three/addons/utils/BufferGeometryUtils.js +1501 -0
- package/public/vendor/three/addons/utils/SkeletonUtils.js +496 -0
- package/public/vendor/three/three.core.min.js +6 -0
- package/public/vendor/three/three.module.min.js +6 -0
- package/public/vendor/three-vrm/LICENSE +22 -0
- package/public/vendor/three-vrm/three-vrm.module.min.js +983 -0
- package/public/vendor/uikit-icons.min.js +1 -0
- package/public/vendor/uikit.min.css +1 -0
- package/public/vendor/uikit.min.js +1 -0
- package/public/vendor/webship-icons/ui/bookmark-outlined.svg +1 -0
- package/public/vendor/webship-icons/ui/headset.svg +1 -0
- package/public/vendor/webship-icons/ui/menu.svg +1 -0
- package/public/vendor/webship-icons/ui/pause.svg +1 -0
- package/public/vendor/webship-icons/ui/play.svg +1 -0
- package/public/vendor/webship-icons/ui/share.svg +1 -0
- package/public/vendor/webship-icons/ui/sound-off.svg +1 -0
- package/public/vendor/webship-icons/ui/sound-on.svg +1 -0
- package/public/vendor/webship-icons/ui/stop.svg +1 -0
- package/public/vendor/xterm/addon-fit.js +2 -0
- package/public/vendor/xterm/addon-unicode11.js +2 -0
- package/public/vendor/xterm/addon-web-links.js +2 -0
- package/public/vendor/xterm/xterm.css +218 -0
- package/public/vendor/xterm/xterm.js +2 -0
- package/rag.js +169 -0
- package/reader.js +132 -0
- package/repo-blobs.js +66 -0
- package/report-pdf.js +337 -0
- package/row-layout.js +1439 -0
- package/screenrc +37 -0
- package/screens.js +1063 -0
- package/scripts/code-server-start.sh +190 -0
- package/scripts/images-claude-finish.js +107 -0
- package/scripts/screen-host.sh +114 -0
- package/scripts/screen-runner.js +362 -0
- package/scripts/screen-then.js +47 -0
- package/scripts/update.sh +350 -0
- package/server.js +7923 -0
- package/settings-file.js +423 -0
- package/speech.js +297 -0
- package/tab-modal.js +75 -0
- package/test-reports.js +260 -0
- package/themes.js +381 -0
- package/transcripts.js +168 -0
- package/urls.js +13 -0
- package/validate.js +18 -0
- package/version-check.js +94 -0
- package/video-edit.js +588 -0
- package/waiting.js +85 -0
- package/workspace-card-tools.js +41 -0
- package/workspace-delete.js +139 -0
- package/workspace-types.js +85 -0
- package/workspaces.js +553 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/* ---------- Grid or list, on the home page and inside a workspace ----------
|
|
2
|
+
*
|
|
3
|
+
* Two different questions wearing one control. The home page is a grid of small cards and the sensible
|
|
4
|
+
* choices are how many across; a workspace is a list of project rows, each carrying a dozen buttons,
|
|
5
|
+
* and the sensible default there is one to a row with the whole width to lay them out in.
|
|
6
|
+
*
|
|
7
|
+
* The state is on <html> — data-home-view and data-rows-view, written by the pre-paint script from the
|
|
8
|
+
* shared cookie — so a layout is never drawn one way and then rearranged.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
.cards-head {
|
|
12
|
+
display: flex;
|
|
13
|
+
align-items: center;
|
|
14
|
+
justify-content: space-between;
|
|
15
|
+
gap: 0.75rem;
|
|
16
|
+
flex-wrap: wrap;
|
|
17
|
+
margin-bottom: 0.5rem;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/* The list is its own section, and on a workspace that builds projects it follows the Build form —
|
|
21
|
+
which ended flush against it: the form's last control and "Projects · how to lay them out" touched at
|
|
22
|
+
zero pixels, so one question ran straight into the next. A rule as well as the space, because they
|
|
23
|
+
are two different things to do rather than one thing with a gap in it. */
|
|
24
|
+
.build-form + #webship-workspace-projects,
|
|
25
|
+
.build-form + [id="webship-workspace-projects"] {
|
|
26
|
+
margin-top: 1.75rem;
|
|
27
|
+
padding-top: 1.5rem;
|
|
28
|
+
border-top: 1px solid var(--line);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/* The three controls sit together: how many across, and how wide the page is, are one question. */
|
|
32
|
+
.layout-controls { display: inline-flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
|
|
33
|
+
|
|
34
|
+
/* The switcher: one segmented control, so the options read as one question with several answers. */
|
|
35
|
+
.view-switcher {
|
|
36
|
+
display: inline-flex;
|
|
37
|
+
align-items: center;
|
|
38
|
+
border: 1px solid var(--line);
|
|
39
|
+
border-radius: 62.4375rem;
|
|
40
|
+
padding: 0.125rem;
|
|
41
|
+
background: var(--surface-card);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.view-pick,
|
|
45
|
+
.page-width-pick {
|
|
46
|
+
min-width: 2.125rem;
|
|
47
|
+
display: inline-flex;
|
|
48
|
+
align-items: center;
|
|
49
|
+
gap: 0.2rem;
|
|
50
|
+
min-width: 2rem;
|
|
51
|
+
height: 1.75rem;
|
|
52
|
+
padding: 0 0.5rem;
|
|
53
|
+
border: 0;
|
|
54
|
+
border-radius: 62.4375rem;
|
|
55
|
+
background: transparent;
|
|
56
|
+
color: var(--text-subtle);
|
|
57
|
+
cursor: pointer;
|
|
58
|
+
font-size: 0.75rem;
|
|
59
|
+
line-height: 1;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.view-pick:hover,
|
|
63
|
+
.page-width-pick:hover { color: var(--link); }
|
|
64
|
+
.view-pick.is-current,
|
|
65
|
+
.page-width-pick.is-current { background: var(--subtle-brand); color: var(--link); }
|
|
66
|
+
/* Drawn to Tabler's rules, so they sit with the sprite icons rather than beside them: the stroke is
|
|
67
|
+
stated once here, as .tabler-icon does for the 4,736 in the sprite. */
|
|
68
|
+
.view-mark { display: block; width: 1.125rem; height: 1.125rem; }
|
|
69
|
+
.view-pick .view-mark { opacity: .85; }
|
|
70
|
+
.view-pick.is-current .view-mark,
|
|
71
|
+
.page-width-pick.is-current .view-mark { opacity: 1; }
|
|
72
|
+
|
|
73
|
+
/* ---------- the home page's workspace cards ---------- */
|
|
74
|
+
|
|
75
|
+
/* UIKit's uk-child-width-* classes are what the grid ships with, so they are overridden rather than
|
|
76
|
+
removed: the markup keeps working with no JavaScript, and these take over when a choice is made.
|
|
77
|
+
!important is not needed — the attribute selector out-specifies the class. */
|
|
78
|
+
html[data-home-view="grid-1"] #workspace-cards > * { width: 100%; }
|
|
79
|
+
|
|
80
|
+
/* One across is a column of CARDS, not one card stretched across the window: a 1300px-wide card with
|
|
81
|
+
its name centred in the middle of it is a banner, and it would be the same shape as the list view
|
|
82
|
+
with none of its density. Capped and centred, so it still reads as the card it is. */
|
|
83
|
+
html[data-home-view="grid-1"] .workspace-card { max-width: 32rem; margin-inline: auto; }
|
|
84
|
+
html[data-home-view="grid-2"] #workspace-cards > * { width: 50%; }
|
|
85
|
+
html[data-home-view="grid-3"] #workspace-cards > * { width: 33.333%; }
|
|
86
|
+
html[data-home-view="grid-4"] #workspace-cards > * { width: 25%; }
|
|
87
|
+
html[data-home-view="grid-5"] #workspace-cards > * { width: 20%; }
|
|
88
|
+
html[data-home-view="grid-6"] #workspace-cards > * { width: 16.666%; }
|
|
89
|
+
|
|
90
|
+
/* Past four across the card is narrower than its own status label, so the label wraps instead of
|
|
91
|
+
dragging the card wider — the bug that put a horizontal scrollbar on this page once before. */
|
|
92
|
+
html[data-home-view="grid-5"] .workspace-card .uk-label,
|
|
93
|
+
html[data-home-view="grid-6"] .workspace-card .uk-label { white-space: normal; }
|
|
94
|
+
|
|
95
|
+
/* What uk-text-center and uk-display-block used to do, said here so the list view can undo it. */
|
|
96
|
+
.workspace-card { display: block; text-align: center; }
|
|
97
|
+
|
|
98
|
+
/* List: the card unfolds onto one line — icon, name, what it is, how many — so thirty workspaces can
|
|
99
|
+
be read down a column instead of scanned across a wall of tiles. */
|
|
100
|
+
html[data-home-view="list"] #workspace-cards > * { width: 100%; }
|
|
101
|
+
html[data-home-view="list"] .workspace-card {
|
|
102
|
+
display: flex;
|
|
103
|
+
align-items: center;
|
|
104
|
+
gap: 0.875rem;
|
|
105
|
+
text-align: left;
|
|
106
|
+
padding: 0.5rem 0.875rem;
|
|
107
|
+
}
|
|
108
|
+
html[data-home-view="list"] .workspace-card .icon { margin: 0; flex: none; }
|
|
109
|
+
html[data-home-view="list"] .workspace-card .uk-card-title { flex: none; width: 12rem; }
|
|
110
|
+
html[data-home-view="list"] .workspace-card .uk-text-meta { flex: 1; margin: 0; }
|
|
111
|
+
html[data-home-view="list"] .workspace-card .ws-card-handle { position: static; flex: none; }
|
|
112
|
+
html[data-home-view="list"] .workspace-card .uk-label { margin: 0; flex: none; }
|
|
113
|
+
|
|
114
|
+
/* ---------- a workspace's project rows ---------- */
|
|
115
|
+
|
|
116
|
+
/* List is the default and is what the rows already are, so only the grids need saying. A row in a grid
|
|
117
|
+
keeps everything it has — the buttons wrap instead of sitting on one line. */
|
|
118
|
+
html[data-rows-view^="grid"] #webship-workspace-projects .project-row { margin: 0; }
|
|
119
|
+
html[data-rows-view="grid-1"] #webship-workspace-projects,
|
|
120
|
+
html[data-rows-view="grid-2"] #webship-workspace-projects,
|
|
121
|
+
html[data-rows-view="grid-3"] #webship-workspace-projects {
|
|
122
|
+
display: grid;
|
|
123
|
+
gap: 0.75rem;
|
|
124
|
+
align-items: start;
|
|
125
|
+
}
|
|
126
|
+
/* Same for a workspace: one across is a column of cards down the middle of the page, which is what
|
|
127
|
+
makes it a different answer from the list rather than a second name for it. */
|
|
128
|
+
html[data-rows-view="grid-1"] #webship-workspace-projects { grid-template-columns: 1fr; }
|
|
129
|
+
html[data-rows-view="grid-1"] #webship-workspace-projects .project-row { max-width: 46rem; margin-inline: auto; width: 100%; }
|
|
130
|
+
html[data-rows-view="grid-2"] #webship-workspace-projects { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
131
|
+
html[data-rows-view="grid-3"] #webship-workspace-projects { grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
|
132
|
+
|
|
133
|
+
/* The controls above the rows are not rows: they span the grid rather than becoming a cell in it. */
|
|
134
|
+
html[data-rows-view^="grid"] #webship-workspace-projects > :is(h3, p, .list-controls, .list-pager, .cards-head, .uk-alert) {
|
|
135
|
+
grid-column: 1 / -1;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/* Below the breakpoint where a workspace page stacks anyway, a multi-column grid of rows is a column
|
|
139
|
+
of slivers — so the grids collapse and the list is what is left. */
|
|
140
|
+
@media (max-width: 63.99em) {
|
|
141
|
+
html[data-rows-view="grid-2"] #webship-workspace-projects,
|
|
142
|
+
html[data-rows-view="grid-3"] #webship-workspace-projects { grid-template-columns: 1fr; }
|
|
143
|
+
html[data-home-view="grid-3"] #workspace-cards > *,
|
|
144
|
+
html[data-home-view="grid-4"] #workspace-cards > *,
|
|
145
|
+
html[data-home-view="grid-5"] #workspace-cards > *,
|
|
146
|
+
html[data-home-view="grid-6"] #workspace-cards > * { width: 50%; }
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/* On a page with no list, the width control sits at the end of the heading row rather than in the
|
|
150
|
+
middle of it — the heading is what names the page, and this is a preference beside it. */
|
|
151
|
+
.page-heading .width-switcher { order: 9; margin-left: auto; }
|
|
152
|
+
|
|
153
|
+
/* The help page is a document, so its control goes at the top right on its own rather than beside a
|
|
154
|
+
heading it does not have. */
|
|
155
|
+
.help-head { justify-content: flex-end; margin-bottom: 0; }
|
|
156
|
+
|
|
157
|
+
/* The Screens page, in one, two or three columns.
|
|
158
|
+
*
|
|
159
|
+
* Both children SPAN the container first, and that is not decoration: #webship-workspace-projects is
|
|
160
|
+
* itself a grid — the same one the project rows are laid out in — so a single wrapper dropped into it
|
|
161
|
+
* becomes one cell. Without the span, the toolbar took the first column and the whole list the second,
|
|
162
|
+
* and half the page was empty. The list owns its own columns inside the width it is given. One is the default — see ui.js — and the grid is
|
|
163
|
+
`minmax(0, 1fr)` rather than plain `1fr` so a long session name cannot push a column wider than its
|
|
164
|
+
share: `dev--amd-website_starter-1-0-x-site-template--build` is one unbroken token, and a grid track sized
|
|
165
|
+
to its content is how a list of them ends up scrolling sideways. */
|
|
166
|
+
.screens-toolbar { grid-column: 1 / -1; }
|
|
167
|
+
.screens-list { grid-column: 1 / -1; display: grid; gap: 0; grid-template-columns: minmax(0, 1fr); }
|
|
168
|
+
.screens-list[data-cols="2"] { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 1rem; }
|
|
169
|
+
.screens-list[data-cols="3"] { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0 1rem; }
|
|
170
|
+
|
|
171
|
+
/* Below a laptop there is room for one, whatever was chosen on a wider screen. */
|
|
172
|
+
@media (max-width: 63.9375em) {
|
|
173
|
+
.screens-list[data-cols="2"],
|
|
174
|
+
.screens-list[data-cols="3"] { grid-template-columns: minmax(0, 1fr); }
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.screens-cols { display: inline-flex; gap: .15rem; margin-right: .35rem; }
|
|
178
|
+
|
|
179
|
+
/* The Screens modal is as wide as what it is showing.
|
|
180
|
+
*
|
|
181
|
+
* It was a flat 1100px whatever was in it, so three columns of session cards came out at 338px each —
|
|
182
|
+
* narrower than the row of buttons inside them — while 463px of a 1563px window went unused. The width
|
|
183
|
+
* follows the column count now, and `min(…, 94vw)` is what keeps it inside the window: a fixed 1900px
|
|
184
|
+
* dialog on a 1440px laptop is a modal with its right-hand column off the screen.
|
|
185
|
+
*
|
|
186
|
+
* The numbers are what each card ends up with, measured rather than picked from a scale: 1146px at one
|
|
187
|
+
* column, 715 at two, 479 at three on a 1634px window — and the cap is what decides the last of those,
|
|
188
|
+
* not the 1900. A card narrower than that is what the page's own three-column view already renders at
|
|
189
|
+
* 412px, so this is the same trade, made where there is more room to give. */
|
|
190
|
+
#editor-modal[data-screens-cols] .uk-modal-dialog { width: min(94vw, 1200px); }
|
|
191
|
+
#editor-modal[data-screens-cols="2"] .uk-modal-dialog { width: min(94vw, 1500px); }
|
|
192
|
+
#editor-modal[data-screens-cols="3"] .uk-modal-dialog { width: min(94vw, 1900px); }
|
|
193
|
+
|
|
194
|
+
/* A session's glyph is a picture of the terminal it opens, scaled with the room the row has. It
|
|
195
|
+
dims when the session has ended and draws the eye, once, when one waits for an answer. */
|
|
196
|
+
.term-glyph { flex: none; width: clamp(3.5rem, 5vw, 5.5rem); height: auto; }
|
|
197
|
+
.screens-list[data-cols="3"] .term-glyph { width: 3.5rem; }
|
|
198
|
+
.screen-tile.is-dead .term-glyph { filter: grayscale(1); opacity: 0.5; }
|
|
199
|
+
.screen-tile.is-waiting .term-glyph-cursor { animation: term-glyph-blink 1.1s steps(2, start) infinite; }
|
|
200
|
+
@keyframes term-glyph-blink { to { visibility: hidden; } }
|
|
201
|
+
@media (prefers-reduced-motion: reduce) {
|
|
202
|
+
.screen-tile.is-waiting .term-glyph-cursor { animation: none; }
|
|
203
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/* The guide: contents and search on the left, the text on the right. */
|
|
2
|
+
.help-layout { display: grid; grid-template-columns: 15rem minmax(0, 48rem); gap: 2rem; align-items: start; justify-content: center; }
|
|
3
|
+
.help-toc { position: sticky; top: 5rem; max-height: calc(100dvh - 6rem); overflow-y: auto; scrollbar-width: thin; font-size: 0.875rem; }
|
|
4
|
+
.help-find.uk-input { height: 2.25rem; border-radius: 0.5rem; margin-bottom: 0.75rem; }
|
|
5
|
+
.help-toc-label { margin: 0 0 0.25rem; font-size: 0.6875rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-subtle); }
|
|
6
|
+
.help-toc ul { list-style: none; margin: 0; padding: 0; }
|
|
7
|
+
.help-toc ul ul { padding-left: 0.75rem; font-size: 0.8125rem; }
|
|
8
|
+
.help-toc li[hidden] { display: none; }
|
|
9
|
+
.help-toc a { display: block; padding: 0.25rem 0.5rem; border-radius: 0.375rem; color: var(--text-default); text-decoration: none; }
|
|
10
|
+
.help-toc a:hover { background: var(--subtle-brand); color: var(--link); }
|
|
11
|
+
.help-toc a.is-current { background: var(--subtle-brand); color: var(--link); font-weight: 600; }
|
|
12
|
+
.help-toc a:focus-visible { outline: 2px solid var(--brand-primary); outline-offset: -2px; }
|
|
13
|
+
.help-section[hidden] { display: none; }
|
|
14
|
+
.help-section h2 { scroll-margin-top: 5rem; }
|
|
15
|
+
mark.help-hit { background: var(--subtle-brand); color: inherit; border-radius: 0.125rem; padding: 0 0.0625rem; }
|
|
16
|
+
@media (max-width: 59.9375em) {
|
|
17
|
+
.help-layout { grid-template-columns: 1fr; }
|
|
18
|
+
.help-toc { position: static; max-height: none; }
|
|
19
|
+
}
|
|
20
|
+
/* base.css underlines a link in a list item; contents are navigation, not prose. */
|
|
21
|
+
.help-toc li a:not(.uk-button):not(.uk-link-reset) { text-decoration: none; }
|
|
22
|
+
/* "?" beside a page's heading opens the part of the guide about that page. */
|
|
23
|
+
.page-help {
|
|
24
|
+
flex: none; display: inline-flex; align-items: center; justify-content: center; width: 1.75rem; height: 1.75rem;
|
|
25
|
+
margin-left: 0.75rem; border: 1px solid var(--line); border-radius: 50%; color: var(--text-subtle);
|
|
26
|
+
font-weight: 700; font-size: 0.875rem; text-decoration: none;
|
|
27
|
+
}
|
|
28
|
+
.page-help:hover { border-color: var(--brand-primary); color: var(--link); background: var(--subtle-brand); text-decoration: none; }
|
|
29
|
+
.page-help:focus-visible { outline: 2px solid var(--brand-primary); outline-offset: 2px; }
|
|
30
|
+
/* A code block's Copy button. */
|
|
31
|
+
.help-body pre.has-copy { position: relative; padding-right: 4.5rem; }
|
|
32
|
+
.help-copy {
|
|
33
|
+
position: absolute; top: 0.375rem; right: 0.375rem; padding: 0.125rem 0.5rem; border: 1px solid var(--line);
|
|
34
|
+
border-radius: 0.375rem; background: var(--surface-card); color: var(--text-default); font: inherit; font-size: 0.75rem; cursor: pointer;
|
|
35
|
+
}
|
|
36
|
+
.help-copy:hover { border-color: var(--brand-primary); color: var(--link); }
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/* ---------- Hero ---------- */
|
|
2
|
+
|
|
3
|
+
.logo {
|
|
4
|
+
width:5.25rem;
|
|
5
|
+
height:5.25rem;
|
|
6
|
+
margin: 0 auto;
|
|
7
|
+
border-radius: 50%;
|
|
8
|
+
background: linear-gradient(150deg, var(--brand-primary), var(--brand-secondary));
|
|
9
|
+
display: flex;
|
|
10
|
+
align-items: center;
|
|
11
|
+
justify-content: center;
|
|
12
|
+
font-size:2.5rem;
|
|
13
|
+
box-shadow: 0 10px 24px rgba(30, 135, 240, .35);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.logo-sm { width:4.125rem; height:4.125rem; font-size:1.875rem; }
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/* The images gallery uses the shared file list (.file-list, .fx-row) the docs and videos lists draw;
|
|
2
|
+
these are only what a picture needs on top of it. */
|
|
3
|
+
html[data-files-view^="grid"] .img-row .fx-thumb { cursor: zoom-in; }
|
|
4
|
+
html[data-files-view^="grid"] .img-row .fx-thumb.is-vector img { object-fit: contain; padding: 1.25rem; box-sizing: border-box; }
|
|
5
|
+
.fx-source { display: block; font-size: 0.72rem; font-weight: 400; color: var(--link); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
6
|
+
|
|
7
|
+
/* The viewer, in the editor modal. */
|
|
8
|
+
.img-viewer-head { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; margin-bottom: 0.5rem; }
|
|
9
|
+
.img-viewer-head h3 { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
10
|
+
.img-stage {
|
|
11
|
+
display: flex;
|
|
12
|
+
align-items: center;
|
|
13
|
+
justify-content: center;
|
|
14
|
+
max-height: 65vh;
|
|
15
|
+
overflow: auto;
|
|
16
|
+
border-radius: 0.6rem;
|
|
17
|
+
background: var(--surface-muted);
|
|
18
|
+
}
|
|
19
|
+
.img-stage.is-fit { overflow: hidden; }
|
|
20
|
+
.img-stage.is-fit img { max-width: 100%; max-height: 65vh; width: auto; height: auto; object-fit: contain; }
|
|
21
|
+
.img-stage:not(.is-fit) { justify-content: flex-start; align-items: flex-start; }
|
|
22
|
+
.img-stage:not(.is-fit) img { max-width: none; max-height: none; }
|
|
23
|
+
.img-nav { display: flex; justify-content: center; gap: 0.5rem; margin: 0.6rem 0; }
|
|
24
|
+
.img-info { display: grid; grid-template-columns: max-content 1fr; gap: 0.2rem 1rem; margin: 0.5rem 0; }
|
|
25
|
+
.img-info dt { color: var(--text-subtle); font-weight: 400; }
|
|
26
|
+
.img-info dd { margin: 0; overflow-wrap: anywhere; }
|
|
27
|
+
.img-made { border-top: 1px solid var(--line); padding-top: 0.5rem; }
|
|
28
|
+
.img-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0.75rem 0; }
|
|
29
|
+
.img-rename-row { gap: 0.5rem; }
|
|
30
|
+
.img-rename-row .uk-input { max-width: 24rem; }
|
|
31
|
+
|
|
32
|
+
/* Upload: a drop zone that is also the file picker. */
|
|
33
|
+
.img-drop {
|
|
34
|
+
display: flex;
|
|
35
|
+
flex-direction: column;
|
|
36
|
+
align-items: center;
|
|
37
|
+
gap: 0.35rem;
|
|
38
|
+
padding: 2rem 1rem;
|
|
39
|
+
border: 2px dashed var(--line-strong);
|
|
40
|
+
border-radius: 0.6rem;
|
|
41
|
+
text-align: center;
|
|
42
|
+
cursor: pointer;
|
|
43
|
+
color: var(--text-default);
|
|
44
|
+
}
|
|
45
|
+
.img-drop.is-over, .img-drop:hover { border-color: var(--brand-primary); background: var(--subtle-brand); }
|
|
46
|
+
.img-drop-text { font-weight: 600; }
|
|
47
|
+
.img-upload-log { margin-top: 0.75rem; max-height: 14rem; overflow: auto; }
|
|
48
|
+
.img-upload-log .msg { margin: 0; }
|
|
49
|
+
.img-edit { margin-top: 0.5rem; border-top: 1px solid var(--line); padding-top: 0.5rem; }
|
|
50
|
+
.img-edit > summary { cursor: pointer; font-weight: 600; }
|
|
51
|
+
|
|
52
|
+
/* What a generation made, before it is opened. */
|
|
53
|
+
.img-made-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr)); gap: 0.75rem; margin-top: 0.75rem; }
|
|
54
|
+
.img-made-thumb {
|
|
55
|
+
display: block; width: 100%; aspect-ratio: 4 / 3; padding: 0; overflow: hidden; cursor: zoom-in;
|
|
56
|
+
border: 1px solid var(--line); border-radius: 0.6rem; background: var(--surface-muted);
|
|
57
|
+
}
|
|
58
|
+
.img-made-thumb img { width: 100%; height: 100%; object-fit: contain; display: block; }
|
|
59
|
+
|
|
60
|
+
/* An AI prompt is a few lines: its editor leaves room for the provider's fields below it. */
|
|
61
|
+
#editor-modal .img-ai .ace-host:not(.ace-host-single),
|
|
62
|
+
#editor-modal .img-ai-edit .ace-host:not(.ace-host-single) { height: clamp(7rem, 20vh, 12rem) !important; }
|
|
63
|
+
|
|
64
|
+
/* Generate SVG: the palette as colour swatches, and its chips in the plan. */
|
|
65
|
+
.svg-palette { border: 0; padding: 0; margin: 0.5rem 0 0; display: flex; flex-wrap: wrap; gap: 0.75rem; }
|
|
66
|
+
.svg-palette legend { width: 100%; }
|
|
67
|
+
.svg-swatch { display: inline-flex; align-items: center; gap: 0.35rem; font-size: 0.85rem; }
|
|
68
|
+
.svg-swatch input[type="color"] { width: 2rem; height: 1.6rem; padding: 0; border: 1px solid var(--line); border-radius: 0.3rem; background: none; cursor: pointer; }
|
|
69
|
+
.svg-chip { display: inline-block; width: 1rem; height: 1rem; border-radius: 0.2rem; border: 1px solid var(--line); vertical-align: middle; margin-right: 0.15rem; }
|
|
70
|
+
.img-stage picture { display: contents; }
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/* ---------- Layout rhythm & spacing polish ---------- */
|
|
2
|
+
|
|
3
|
+
.page-body {
|
|
4
|
+
padding-top:1.75rem;
|
|
5
|
+
padding-bottom:3rem;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/* Toolbar breathing room */
|
|
9
|
+
.toolbar .uk-navbar-item { padding:0 0.75rem 0 0; min-height:4rem; }
|
|
10
|
+
|
|
11
|
+
/* Workspace cards: even inner padding + consistent vertical spacing */
|
|
12
|
+
.workspace-card { padding:1.5rem 1rem 1.25rem; height: 100%; }
|
|
13
|
+
.workspace-card .icon { margin-bottom:0.75rem; }
|
|
14
|
+
.workspace-card .uk-card-title { margin-top:0.125rem; font-size:1.0625rem; }
|
|
15
|
+
.workspace-card .uk-text-meta { margin-top:0.125rem; min-height: 2.6em; display: block; }
|
|
16
|
+
.workspace-card .uk-label { margin-top:0.625rem; }
|
|
17
|
+
|
|
18
|
+
/* Equal-height card cells */
|
|
19
|
+
.workspace-grid > div, .uk-grid > .uk-position-relative { display: flex; }
|
|
20
|
+
.uk-grid > .uk-position-relative > .workspace-card { flex: 1; }
|
|
21
|
+
|
|
22
|
+
/* Workspaces panel: balanced padding */
|
|
23
|
+
.workspaces-card, .uk-card-body { border-radius:0.75rem; }
|
|
24
|
+
|
|
25
|
+
/* Section headings inside cards */
|
|
26
|
+
.webship-workspace-page .uk-card-body h3 { margin-top:1.5rem; }
|
|
27
|
+
.webship-workspace-page .uk-card-body h3:first-child { margin-top: 0; }
|
|
28
|
+
|
|
29
|
+
/* Project rows: consistent inner spacing */
|
|
30
|
+
.project-row { padding:0.875rem 1.125rem; margin-bottom:0.625rem; }
|
|
31
|
+
|
|
32
|
+
/* Footer: tighter */
|
|
33
|
+
footer.uk-section { padding-top:1.25rem; padding-bottom:1.625rem; }
|
|
34
|
+
|
|
35
|
+
/* Very large screens: cap content width, keep it centered comfortably */
|
|
36
|
+
@media (min-width: 112.5em) {
|
|
37
|
+
.uk-container-xlarge { max-width:97.5rem; }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* Tablets: assistant stacks above with sane spacing */
|
|
41
|
+
@media (max-width: 59.9375em) {
|
|
42
|
+
.page-body { padding-top:1.125rem; }
|
|
43
|
+
.assistant-panel { margin-bottom:0.25rem; }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* Phones: tighter cards, full-width floating assistant */
|
|
47
|
+
@media (max-width: 40em) {
|
|
48
|
+
.workspace-card { padding:1.125rem 0.75rem 1rem; }
|
|
49
|
+
.workspace-card .uk-text-meta { min-height: 0; }
|
|
50
|
+
.project-row { padding:0.75rem 0.875rem; }
|
|
51
|
+
.project-row .uk-flex { gap:0.5rem; }
|
|
52
|
+
.assistant-float {
|
|
53
|
+
right:0.625rem;
|
|
54
|
+
left:0.625rem;
|
|
55
|
+
width: auto;
|
|
56
|
+
bottom:5.375rem;
|
|
57
|
+
}
|
|
58
|
+
.assistant-launcher { left:0.875rem; bottom:0.875rem; }
|
|
59
|
+
.toolbar-logo span { font-size:1.0625rem; }
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/* The user guide at /help. Rendered from markdown, so it is the one page whose content is
|
|
63
|
+
* long-form prose rather than controls: it needs a measure that can be read, and tables that
|
|
64
|
+
* do not force the page sideways. */
|
|
65
|
+
.help-page { max-width: 52rem; }
|
|
66
|
+
.help-page h1 { margin-top: 0; }
|
|
67
|
+
.help-page h2 { margin-top: 2.25rem; }
|
|
68
|
+
.help-page h3 { margin-top: 1.5rem; }
|
|
69
|
+
.help-page p, .help-page li { line-height: 1.65; }
|
|
70
|
+
.help-page table { width: 100%; display: block; overflow-x: auto; }
|
|
71
|
+
.help-page td, .help-page th { padding: .35rem .6rem; vertical-align: top; }
|
|
72
|
+
/* The guide is mostly command lines, and they are longer than a phone is wide:
|
|
73
|
+
* `bash ~/workspace/core/scripts/trust-https.sh` measured 383px in a 345px viewport and
|
|
74
|
+
* pushed the whole document sideways. Two different fixes, because they are two cases:
|
|
75
|
+
* - a BLOCK of code scrolls inside itself, and must be told it cannot exceed its column;
|
|
76
|
+
* - INLINE code in a sentence has to wrap, and `overflow-wrap: anywhere` is what breaks a
|
|
77
|
+
* path with no spaces in it. (`word-break: break-word` is non-standard and did not.) */
|
|
78
|
+
/* Inline code in the guide is mostly file paths and commands, and the site's own `code`
|
|
79
|
+
* styling sets white-space: nowrap — which BEATS overflow-wrap entirely, so a 425px path sat
|
|
80
|
+
* in a 345px viewport and pushed the document sideways. Both properties are needed, and the
|
|
81
|
+
* nowrap has to be undone first; overflow-wrap alone did nothing, which is what made this
|
|
82
|
+
* look like the rule was not applying.
|
|
83
|
+
* Blocks keep `pre` semantics and scroll inside themselves instead. */
|
|
84
|
+
.help-page :not(pre) > code {
|
|
85
|
+
white-space: normal;
|
|
86
|
+
overflow-wrap: anywhere;
|
|
87
|
+
}
|
|
88
|
+
/* Pandoc wraps a fenced block as div.sourceCode > pre > code, and it is the DIV that has to
|
|
89
|
+
* be constrained: constraining only the pre left the div sizing itself to an 784px line in a
|
|
90
|
+
* 345px viewport, which is what actually pushed the page. */
|
|
91
|
+
.help-page pre,
|
|
92
|
+
.help-page div.sourceCode { max-width: 100%; overflow-x: auto; }
|
|
93
|
+
.help-page pre code { overflow-wrap: normal; white-space: pre; }
|
|
94
|
+
.help-page hr { margin: 2rem 0 1rem; }
|
|
95
|
+
|
|
96
|
+
/* ---- keyboard access ------------------------------------------------------------------
|
|
97
|
+
* Measured by walking Tab through /dev: the page's own content — the build form, the project
|
|
98
|
+
* list — was tab stop EIGHTEEN, after the toolbar, the whole assistant panel and eleven rail
|
|
99
|
+
* controls. That is the cost of a rich chrome, and a skip link is the standard answer.
|
|
100
|
+
*
|
|
101
|
+
* Invisible until focused, then a real target at the top-left. It must never be
|
|
102
|
+
* `display: none` or `visibility: hidden`: both remove it from the tab order, which is the
|
|
103
|
+
* usual way a skip link ends up not working. */
|
|
104
|
+
.skip-link {
|
|
105
|
+
position: absolute;
|
|
106
|
+
left: .5rem;
|
|
107
|
+
top: -3rem;
|
|
108
|
+
z-index: 1100;
|
|
109
|
+
padding: .5rem .85rem;
|
|
110
|
+
background: var(--brand-primary);
|
|
111
|
+
color: var(--text-inverse);
|
|
112
|
+
border-radius: 0 0 8px 8px;
|
|
113
|
+
font-size: .8125rem;
|
|
114
|
+
text-decoration: none;
|
|
115
|
+
transition: top .12s ease-out;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.skip-link:focus,
|
|
119
|
+
.skip-link:focus-visible {
|
|
120
|
+
top: 0;
|
|
121
|
+
outline: 2px solid var(--text-default);
|
|
122
|
+
outline-offset: 2px;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
@media (prefers-reduced-motion: reduce) {
|
|
126
|
+
.skip-link { transition: none; }
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/* The target takes focus programmatically, so it must not draw a ring of its own when the
|
|
130
|
+
* skip link sends the caret there. */
|
|
131
|
+
#main-content:focus { outline: none; }
|
|
132
|
+
|
|
133
|
+
/* UIKit's checkbox draws no focus ring at all, so it was the one control on the page a
|
|
134
|
+
* keyboard user could land on without seeing where they were. */
|
|
135
|
+
.uk-checkbox:focus-visible,
|
|
136
|
+
.uk-radio:focus-visible {
|
|
137
|
+
outline: 2px solid var(--brand-primary);
|
|
138
|
+
outline-offset: 2px;
|
|
139
|
+
}
|