plexora 0.0.1__py3-none-any.whl
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.
- plexora/__init__.py +129 -0
- plexora/__main__.py +15 -0
- plexora/_url.py +105 -0
- plexora/api/__init__.py +55 -0
- plexora/api/dataset.py +398 -0
- plexora/api/http.py +19 -0
- plexora/api/plugin.py +541 -0
- plexora/api/store.py +221 -0
- plexora/cli.py +777 -0
- plexora/client/dist/354_bundle.js +2 -0
- plexora/client/dist/354_bundle.js.LICENSE.txt +20 -0
- plexora/client/dist/418_bundle.js +2 -0
- plexora/client/dist/418_bundle.js.LICENSE.txt +1 -0
- plexora/client/dist/693b935cb1f907814be9c6199a68c217.svg +19 -0
- plexora/client/dist/713c134ff47cd328a5b711526f151082.svg +18 -0
- plexora/client/dist/770_bundle.js +1 -0
- plexora/client/dist/ff743f408972e0e96ca9153ed5ae4c83.svg +23 -0
- plexora/client/dist/vendor_bundle.js +2 -0
- plexora/client/dist/vendor_bundle.js.LICENSE.txt +308 -0
- plexora/client/external/openseadragon-bin-2.4.0/LICENSE.txt +28 -0
- plexora/client/external/openseadragon-bin-2.4.0/canvas-overlay-hd.js +140 -0
- plexora/client/external/openseadragon-bin-2.4.0/changelog.txt +501 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/.gitattributes +17 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/LICENSE.txt +116 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/Toolbar.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/fullpage.psd +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/home.psd +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/next.psd +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/previous.psd +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/rotateleft.psd +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/rotateright.psd +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/zoom.psd +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/README.md +9 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_grouphover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_hover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_pressed.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_rest.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_grouphover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_hover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_pressed.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_rest.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_grouphover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_hover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_pressed.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_rest.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_grouphover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_hover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_pressed.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_rest.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_grouphover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_hover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_pressed.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_rest.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_grouphover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_hover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_pressed.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_rest.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_grouphover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_hover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_pressed.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_rest.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_grouphover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_hover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_pressed.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_rest.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-scalebar.js +562 -0
- plexora/client/src/css/import.css +474 -0
- plexora/client/src/css/main.css +903 -0
- plexora/client/src/css/openProject.css +363 -0
- plexora/client/src/css/quickView.css +166 -0
- plexora/client/src/css/tokens.css +51 -0
- plexora/client/src/css/viewer.css +1655 -0
- plexora/client/src/img/apple-touch-icon.png +0 -0
- plexora/client/src/img/favicon.ico +0 -0
- plexora/client/src/img/logo.ai +8892 -33
- plexora/client/src/img/logo.svg +1 -0
- plexora/client/src/img/logo_with_text.ai +9011 -34
- plexora/client/src/img/logo_with_text.svg +1 -0
- plexora/client/src/js/main.js +704 -0
- plexora/client/src/js/pluginRegistry.js +108 -0
- plexora/client/src/js/services/appStatus.js +286 -0
- plexora/client/src/js/services/browsePicker.js +73 -0
- plexora/client/src/js/services/dataLayer.js +400 -0
- plexora/client/src/js/services/datasetContext.js +143 -0
- plexora/client/src/js/services/glRenderer.js +143 -0
- plexora/client/src/js/services/importFormValidation.js +257 -0
- plexora/client/src/js/services/numericData.js +100 -0
- plexora/client/src/js/services/passVariablesToFrontend.js +20 -0
- plexora/client/src/js/services/simpleEventHandler.js +25 -0
- plexora/client/src/js/vendor.js +29 -0
- plexora/client/src/js/views/channelList.js +663 -0
- plexora/client/src/js/views/colorSwatchPicker.js +202 -0
- plexora/client/src/js/views/columnClassifier.js +146 -0
- plexora/client/src/js/views/coordinateField.js +133 -0
- plexora/client/src/js/views/dataSourceField.js +276 -0
- plexora/client/src/js/views/imageViewer.js +3306 -0
- plexora/client/src/js/views/miniMap.js +787 -0
- plexora/client/src/js/views/navbarControls.js +178 -0
- plexora/client/src/js/views/openProjectPage.js +239 -0
- plexora/client/src/js/views/projectEdit.js +335 -0
- plexora/client/src/js/views/quickViewLanding.js +136 -0
- plexora/client/src/js/views/rainbow.js +326 -0
- plexora/client/src/js/views/requirementsModal.js +572 -0
- plexora/client/src/js/views/rgbImageViewer.js +115 -0
- plexora/client/src/js/views/searchableSelect.js +411 -0
- plexora/client/src/js/views/segmentationProgress.js +122 -0
- plexora/client/src/js/views/toolLoader.js +747 -0
- plexora/client/src/js/views/viewerControls.js +689 -0
- plexora/client/src/js/views/viewerManager.js +434 -0
- plexora/client/src/js/views/viewerSidebar.js +1117 -0
- plexora/client/src/js/workers/tileDecoder.js +130 -0
- plexora/client/src/shaders/frag.glsl +528 -0
- plexora/client/src/shaders/vert.glsl +12 -0
- plexora/client/templates/base.html +212 -0
- plexora/client/templates/index.html +238 -0
- plexora/client/templates/open_project.html +96 -0
- plexora/client/templates/project_columns.html +86 -0
- plexora/client/templates/project_edit.html +196 -0
- plexora/client/templates/upload.html +118 -0
- plexora/connect.py +528 -0
- plexora/datasource.py +782 -0
- plexora/jupyter.py +406 -0
- plexora/notebook_env.py +232 -0
- plexora/paths.py +486 -0
- plexora/plugins/__init__.py +12 -0
- plexora/plugins/cell_explorer/__init__.py +100 -0
- plexora/plugins/cell_explorer/server/__init__.py +11 -0
- plexora/plugins/cell_explorer/server/routes.py +167 -0
- plexora/plugins/cell_explorer/server/state.py +281 -0
- plexora/plugins/cell_explorer/server/values.py +145 -0
- plexora/plugins/cell_explorer/server/variables.py +367 -0
- plexora/plugins/cell_explorer/static/cellExplorerApi.js +158 -0
- plexora/plugins/cell_explorer/static/cellExplorerColors.js +316 -0
- plexora/plugins/cell_explorer/static/cellExplorerContinuous.js +403 -0
- plexora/plugins/cell_explorer/static/cellExplorerFigureBridge.js +133 -0
- plexora/plugins/cell_explorer/static/cellExplorerLegend.js +188 -0
- plexora/plugins/cell_explorer/static/cellExplorerRoiBridge.js +822 -0
- plexora/plugins/cell_explorer/static/cellExplorerSidebarController.js +826 -0
- plexora/plugins/cell_explorer/static/cellExplorerState.js +372 -0
- plexora/plugins/cell_explorer/static/cell_explorer.css +752 -0
- plexora/plugins/cell_explorer/templates/cell_explorer/panel.html +137 -0
- plexora/plugins/figure_builder/__init__.py +125 -0
- plexora/plugins/figure_builder/server/__init__.py +7 -0
- plexora/plugins/figure_builder/server/compose.py +365 -0
- plexora/plugins/figure_builder/server/export.py +784 -0
- plexora/plugins/figure_builder/server/export_jobs.py +148 -0
- plexora/plugins/figure_builder/server/operations.py +556 -0
- plexora/plugins/figure_builder/server/pixels.py +142 -0
- plexora/plugins/figure_builder/server/provenance.py +174 -0
- plexora/plugins/figure_builder/server/render.py +278 -0
- plexora/plugins/figure_builder/server/repository.py +701 -0
- plexora/plugins/figure_builder/server/routes.py +594 -0
- plexora/plugins/figure_builder/server/schema.py +1023 -0
- plexora/plugins/figure_builder/server/sources.py +120 -0
- plexora/plugins/figure_builder/server/textmetrics.py +155 -0
- plexora/plugins/figure_builder/static/figureActions.js +363 -0
- plexora/plugins/figure_builder/static/figureBuilderApi.js +255 -0
- plexora/plugins/figure_builder/static/figureCanvas.js +2362 -0
- plexora/plugins/figure_builder/static/figureCaptureBoxes.js +369 -0
- plexora/plugins/figure_builder/static/figureCaptureDock.js +466 -0
- plexora/plugins/figure_builder/static/figureCaptureTool.js +1034 -0
- plexora/plugins/figure_builder/static/figureConfirm.js +136 -0
- plexora/plugins/figure_builder/static/figureContextBar.js +1193 -0
- plexora/plugins/figure_builder/static/figureContextMenu.js +253 -0
- plexora/plugins/figure_builder/static/figureDocumentState.js +275 -0
- plexora/plugins/figure_builder/static/figureExportUi.js +174 -0
- plexora/plugins/figure_builder/static/figureLibrary.js +247 -0
- plexora/plugins/figure_builder/static/figureQuickEdit.js +628 -0
- plexora/plugins/figure_builder/static/figureRichText.js +634 -0
- plexora/plugins/figure_builder/static/figureSceneSnapshot.js +361 -0
- plexora/plugins/figure_builder/static/figureSchema.js +218 -0
- plexora/plugins/figure_builder/static/figureSidebarController.js +1159 -0
- plexora/plugins/figure_builder/static/figureTextEditor.js +503 -0
- plexora/plugins/figure_builder/static/figureTextPanel.js +491 -0
- plexora/plugins/figure_builder/static/figureViewOptions.js +273 -0
- plexora/plugins/figure_builder/static/figureWorkspace.js +2030 -0
- plexora/plugins/figure_builder/static/figure_builder.css +3292 -0
- plexora/plugins/figure_builder/templates/figure_builder/library.html +74 -0
- plexora/plugins/figure_builder/templates/figure_builder/workspace.html +27 -0
- plexora/plugins/figure_builder/templates/figure_builder/workspace_body.html +458 -0
- plexora/plugins/gating/__init__.py +76 -0
- plexora/plugins/gating/server/__init__.py +0 -0
- plexora/plugins/gating/server/anndata_gates.py +367 -0
- plexora/plugins/gating/server/database.py +16 -0
- plexora/plugins/gating/server/model.py +320 -0
- plexora/plugins/gating/server/routes.py +235 -0
- plexora/plugins/gating/static/csvGatingList.js +947 -0
- plexora/plugins/gating/static/gating.css +289 -0
- plexora/plugins/gating/static/gatingApi.js +221 -0
- plexora/plugins/gating/static/gatingSidebarController.js +513 -0
- plexora/plugins/gating/templates/gating/legacy.html +26 -0
- plexora/plugins/gating/templates/gating/panel.html +69 -0
- plexora/plugins/roi/__init__.py +95 -0
- plexora/plugins/roi/server/__init__.py +8 -0
- plexora/plugins/roi/server/adapters.py +655 -0
- plexora/plugins/roi/server/geojson.py +267 -0
- plexora/plugins/roi/server/geometry.py +181 -0
- plexora/plugins/roi/server/mapping.py +169 -0
- plexora/plugins/roi/server/operations.py +293 -0
- plexora/plugins/roi/server/repository.py +215 -0
- plexora/plugins/roi/server/routes.py +409 -0
- plexora/plugins/roi/server/schema.py +323 -0
- plexora/plugins/roi/static/roi.css +583 -0
- plexora/plugins/roi/static/roiApi.js +177 -0
- plexora/plugins/roi/static/roiFigureBridge.js +107 -0
- plexora/plugins/roi/static/roiGeometry.js +350 -0
- plexora/plugins/roi/static/roiRenderer.js +278 -0
- plexora/plugins/roi/static/roiSidebarController.js +1038 -0
- plexora/plugins/roi/static/roiState.js +518 -0
- plexora/plugins/roi/static/roiTools.js +993 -0
- plexora/plugins/roi/templates/roi/panel.html +240 -0
- plexora/proxy.py +57 -0
- plexora/server/models/adapters/__init__.py +94 -0
- plexora/server/models/adapters/anndata_adapter.py +388 -0
- plexora/server/models/adapters/base.py +93 -0
- plexora/server/models/adapters/classify.py +192 -0
- plexora/server/models/adapters/csv_adapter.py +105 -0
- plexora/server/models/adapters/inspection.py +265 -0
- plexora/server/models/adapters/spatialdata_adapter.py +248 -0
- plexora/server/models/centroid_tiles.py +305 -0
- plexora/server/models/data_model.py +1659 -0
- plexora/server/models/database_model.py +169 -0
- plexora/server/models/project.py +1270 -0
- plexora/server/plugins.py +199 -0
- plexora/server/routes/browse_routes.py +41 -0
- plexora/server/routes/data_routes.py +294 -0
- plexora/server/routes/import_routes.py +603 -0
- plexora/server/routes/page_routes.py +139 -0
- plexora/server/routes/project_routes.py +438 -0
- plexora/server/routes/quick_view_routes.py +62 -0
- plexora/server/routes/system_routes.py +39 -0
- plexora/server/routes/tool_routes.py +418 -0
- plexora/server/utils/addHEColumns.py +11 -0
- plexora/server/utils/fast_png.py +97 -0
- plexora/server/utils/fullConversion.py +50 -0
- plexora/server/utils/native_dialog.py +168 -0
- plexora/server/utils/pre_normalization.py +47 -0
- plexora/server/utils/segmentation_pyramid.py +598 -0
- plexora/server/utils/smallestenclosingcircle.py +126 -0
- plexora/server/utils/tiffsurgeon.py +372 -0
- plexora/server_cli.py +48 -0
- plexora/test.py +0 -0
- plexora-0.0.1.dist-info/METADATA +341 -0
- plexora-0.0.1.dist-info/RECORD +248 -0
- plexora-0.0.1.dist-info/WHEEL +5 -0
- plexora-0.0.1.dist-info/entry_points.txt +12 -0
- plexora-0.0.1.dist-info/licenses/LICENSE +207 -0
- plexora-0.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,3292 @@
|
|
|
1
|
+
/* Figure Builder's own appearance.
|
|
2
|
+
|
|
3
|
+
Every selector here is a .fb- class or an #fb_ id, all of which this plugin
|
|
4
|
+
renders itself. Nothing styles a core element: a plugin's stylesheet stops
|
|
5
|
+
applying the moment its tool is closed, so a rule for a core control would
|
|
6
|
+
leave that control unstyled on every page without the tool -- which is the
|
|
7
|
+
regression tests/test_plugin_css_boundary.py exists to catch, reported by a
|
|
8
|
+
user after gating.css hid one of core's file inputs.
|
|
9
|
+
|
|
10
|
+
Design tokens come from core's tokens.css, loaded by base.html on every page,
|
|
11
|
+
so this plugin follows the app's palette instead of inventing one that drifts
|
|
12
|
+
from it at the next theme change. */
|
|
13
|
+
|
|
14
|
+
.fb-muted {
|
|
15
|
+
color: var(--text-muted);
|
|
16
|
+
font-size: 12px;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/* -- the library ------------------------------------------------------- */
|
|
20
|
+
|
|
21
|
+
.fb-card-sources {
|
|
22
|
+
color: var(--text-muted);
|
|
23
|
+
display: block;
|
|
24
|
+
font-size: 11px;
|
|
25
|
+
overflow: hidden;
|
|
26
|
+
padding: 0 var(--space-3) var(--space-3);
|
|
27
|
+
text-overflow: ellipsis;
|
|
28
|
+
white-space: nowrap;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.fb-thumb {
|
|
32
|
+
aspect-ratio: 4 / 3;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/* A figure whose database could not be read is shown, not hidden -- it is the
|
|
36
|
+
one the user most needs to see. Muted and unclickable, so it reads as
|
|
37
|
+
"damaged" rather than as "empty". */
|
|
38
|
+
.fb-card-damaged {
|
|
39
|
+
opacity: 0.65;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.fb-card-damaged .project-card-name {
|
|
43
|
+
padding-top: var(--space-3);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* -- one figure's page ------------------------------------------------- */
|
|
47
|
+
/*
|
|
48
|
+
ONE workspace, and the canvas is the whole of it.
|
|
49
|
+
|
|
50
|
+
Nothing here is a column any more. The rail and the tray are cards lying ON
|
|
51
|
+
the canvas, which runs edge to edge underneath them -- the arrangement every
|
|
52
|
+
figure editor has converged on, and the reason is that a figure is a
|
|
53
|
+
document with an aspect ratio: giving permanent columns to controls that are
|
|
54
|
+
used in bursts costs the page the width it needs to be legible, all the time,
|
|
55
|
+
to save a click sometimes.
|
|
56
|
+
|
|
57
|
+
The canvas is padded rather than overlapped, though. A floating panel that
|
|
58
|
+
covers the artwork is the version of this that looks right in a screenshot
|
|
59
|
+
and is infuriating to work in, because the thing under it is the thing you
|
|
60
|
+
are comparing against. The padding is a variable, so opening the tray slides
|
|
61
|
+
the page clear of it and closing it gives the width back. */
|
|
62
|
+
|
|
63
|
+
/*
|
|
64
|
+
LIGHT, on a page whose app is dark.
|
|
65
|
+
|
|
66
|
+
This is the only light surface in Plexora, and it is not a style choice. The
|
|
67
|
+
subject of this page is a sheet of white paper that will be printed, and
|
|
68
|
+
every judgement made here -- is that grey too pale, is that label readable,
|
|
69
|
+
do these two panels match -- is a judgement about ink on white. Dark chrome
|
|
70
|
+
around a white page puts the eye's adaptation somewhere the paper is not, and
|
|
71
|
+
makes every panel look brighter and more contrasted than it will be in the
|
|
72
|
+
journal. The photo editors that stay dark do it because their subject is a
|
|
73
|
+
backlit image; ours is paper.
|
|
74
|
+
|
|
75
|
+
Done by re-pointing CORE's tokens for this subtree rather than by rewriting
|
|
76
|
+
two hundred declarations. Every .fb- rule inside .fb-workspace, including the
|
|
77
|
+
ones further down this file, resolves against these. It cannot leak: the
|
|
78
|
+
library page, the capture dock and the viewer are all outside this element,
|
|
79
|
+
and Quick Edit puts the dark values back for its own subtree (see below) --
|
|
80
|
+
its subject IS a backlit image. */
|
|
81
|
+
.fb-workspace {
|
|
82
|
+
--surface-0: #f4f5f8;
|
|
83
|
+
--surface-1: #ffffff;
|
|
84
|
+
--surface-2: #f1f3f7;
|
|
85
|
+
--surface-3: rgba(15, 23, 42, 0.06);
|
|
86
|
+
--border-subtle: #e4e7ee;
|
|
87
|
+
--border-strong: #ccd2dd;
|
|
88
|
+
--text-primary: #16202e;
|
|
89
|
+
--text-secondary: #3f4a5c;
|
|
90
|
+
--text-muted: #78839a;
|
|
91
|
+
/* Sky blue is a dark-theme accent; on white it is a highlighter. The
|
|
92
|
+
selection colour has to work in both places this page puts it -- chrome
|
|
93
|
+
on white, and handles over a fluorescence image -- so it is one step
|
|
94
|
+
deeper and one step more saturated. */
|
|
95
|
+
--accent-channel: #2563eb;
|
|
96
|
+
--accent-channel-soft: rgba(37, 99, 235, 0.28);
|
|
97
|
+
/* What legibly sits ON the accent. It flips with the accent inside Quick
|
|
98
|
+
Edit, where the surround goes back to dark and the accent back to sky. */
|
|
99
|
+
--fb-on-accent: #ffffff;
|
|
100
|
+
/* Core's danger red is #f87171, chosen to be read on a dark surface. This
|
|
101
|
+
workspace is the one light surface in the app, and on a white card it is
|
|
102
|
+
a pink -- illegible as text and, filled behind white lettering on the
|
|
103
|
+
delete button, worse. The same value .fb-menu already carries for the
|
|
104
|
+
same reason. */
|
|
105
|
+
--accent-danger: #d64545;
|
|
106
|
+
|
|
107
|
+
--fb-rail: 64px;
|
|
108
|
+
/* Wide enough for two thumbnails side by side at a size worth looking at.
|
|
109
|
+
A third column would have to come out of the page's width, and the page
|
|
110
|
+
is the thing being judged. */
|
|
111
|
+
--fb-tray: 312px;
|
|
112
|
+
--fb-gap: 12px;
|
|
113
|
+
/* One height for every field on a sidebar -- the select, the stepper and
|
|
114
|
+
the segmented tracks are three different constructions and were three
|
|
115
|
+
different heights, which showed as a ragged right-hand column. */
|
|
116
|
+
--fb-control-h: 30px;
|
|
117
|
+
--fb-desk: #f4f5f8;
|
|
118
|
+
--fb-hover: rgba(15, 23, 42, 0.06);
|
|
119
|
+
--fb-tint: rgba(37, 99, 235, 0.10);
|
|
120
|
+
--fb-card-radius: 14px;
|
|
121
|
+
/* Two shadows, not one: the tight pair is what makes a card read as lifted
|
|
122
|
+
a few millimetres rather than as pasted on. */
|
|
123
|
+
--fb-float: 0 1px 2px rgba(15, 23, 42, 0.06),
|
|
124
|
+
0 8px 28px rgba(15, 23, 42, 0.12);
|
|
125
|
+
|
|
126
|
+
background: var(--surface-0);
|
|
127
|
+
color: var(--text-primary);
|
|
128
|
+
display: flex;
|
|
129
|
+
flex-direction: column;
|
|
130
|
+
height: calc(100vh - 48px);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/* -- the floating bars --------------------------------------------------- */
|
|
134
|
+
/*
|
|
135
|
+
The topbar and the status bar are not surfaces. The desk runs from the top of
|
|
136
|
+
the window to the bottom of it and the CONTROLS float on it, in cards cut to
|
|
137
|
+
their contents -- the same card as the rail and the tray, because a page with
|
|
138
|
+
three kinds of floating panel and two full-width strips is a page caught
|
|
139
|
+
half-way through a redesign.
|
|
140
|
+
|
|
141
|
+
The gap between two clusters is what says which controls are related. That is
|
|
142
|
+
the job the grey pill inside a solid bar was doing, done without a box inside
|
|
143
|
+
a box. */
|
|
144
|
+
|
|
145
|
+
/* 56 = 12 of desk above the clusters and exactly the cluster below, so the gap
|
|
146
|
+
between the topbar and the top of the tray card is the tray's own 12px inset
|
|
147
|
+
and nothing else. The status bar is the same arithmetic upside down. */
|
|
148
|
+
.fb-topbar {
|
|
149
|
+
align-items: center;
|
|
150
|
+
background: transparent;
|
|
151
|
+
display: flex;
|
|
152
|
+
flex: 0 0 auto;
|
|
153
|
+
gap: var(--fb-gap);
|
|
154
|
+
height: 56px;
|
|
155
|
+
padding: var(--fb-gap) var(--fb-gap) 0;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.fb-topbar-spacer {
|
|
159
|
+
flex: 1;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.fb-bar-cluster {
|
|
163
|
+
align-items: center;
|
|
164
|
+
background: var(--surface-1);
|
|
165
|
+
border: 1px solid var(--border-subtle);
|
|
166
|
+
border-radius: var(--fb-card-radius);
|
|
167
|
+
box-shadow: var(--fb-float);
|
|
168
|
+
display: flex;
|
|
169
|
+
flex: 0 0 auto;
|
|
170
|
+
gap: 2px;
|
|
171
|
+
height: 44px;
|
|
172
|
+
min-width: 0;
|
|
173
|
+
padding: 0 6px;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/* The only cluster that gives width back. It holds the title, and a long one
|
|
177
|
+
should shorten rather than push the controls off the end of the bar. */
|
|
178
|
+
.fb-bar-cluster-lead {
|
|
179
|
+
flex: 0 1 auto;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/* Inside a cluster a button carries no chrome of its own -- the card is
|
|
183
|
+
already the box. */
|
|
184
|
+
.fb-bar-cluster .fb-button {
|
|
185
|
+
background: transparent;
|
|
186
|
+
border-color: transparent;
|
|
187
|
+
height: 32px;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.fb-bar-cluster .fb-button:hover {
|
|
191
|
+
background: var(--fb-hover);
|
|
192
|
+
border-color: transparent;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/* A control that is its own cluster. Export is the only one on the page: it is
|
|
196
|
+
the end of the work rather than a step in it, and standing alone is how it
|
|
197
|
+
says so.
|
|
198
|
+
|
|
199
|
+
Two classes, deliberately: .fb-button sets a height further down the file,
|
|
200
|
+
and a single-class rule up here would lose to it on source order alone. */
|
|
201
|
+
.fb-button.fb-bar-solo {
|
|
202
|
+
box-shadow: var(--fb-float);
|
|
203
|
+
flex: 0 0 auto;
|
|
204
|
+
height: 44px;
|
|
205
|
+
padding: 0 var(--space-5);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/* Related controls inside one card, separated rather than boxed again. */
|
|
209
|
+
.fb-bar-divider {
|
|
210
|
+
background: var(--border-subtle);
|
|
211
|
+
flex: 0 0 auto;
|
|
212
|
+
height: 20px;
|
|
213
|
+
margin: 0 4px;
|
|
214
|
+
width: 1px;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/* No longer a pill of its own: the cluster is the box, and this is the row
|
|
218
|
+
inside it. */
|
|
219
|
+
.fb-topbar-group {
|
|
220
|
+
align-items: center;
|
|
221
|
+
display: flex;
|
|
222
|
+
gap: 2px;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.fb-back {
|
|
226
|
+
align-items: center;
|
|
227
|
+
border-radius: 8px;
|
|
228
|
+
color: var(--text-muted);
|
|
229
|
+
display: inline-flex;
|
|
230
|
+
height: 32px;
|
|
231
|
+
justify-content: center;
|
|
232
|
+
text-decoration: none;
|
|
233
|
+
width: 32px;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.fb-back:hover {
|
|
237
|
+
background: var(--fb-hover);
|
|
238
|
+
color: var(--text-primary);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.fb-title-input {
|
|
242
|
+
background: transparent;
|
|
243
|
+
border: 1px solid transparent;
|
|
244
|
+
border-radius: 8px;
|
|
245
|
+
color: var(--text-primary);
|
|
246
|
+
font-size: 15px;
|
|
247
|
+
font-weight: 600;
|
|
248
|
+
max-width: 340px;
|
|
249
|
+
min-width: 140px;
|
|
250
|
+
padding: 6px var(--space-2);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.fb-title-input:hover {
|
|
254
|
+
background: var(--fb-hover);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
.fb-title-input:focus {
|
|
258
|
+
background: var(--surface-1);
|
|
259
|
+
border-color: var(--accent-channel);
|
|
260
|
+
box-shadow: 0 0 0 3px var(--fb-tint);
|
|
261
|
+
outline: none;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/* One word, always in the same place. A status that moves, or that only
|
|
265
|
+
appears when something is wrong, is a status nobody learns to read. */
|
|
266
|
+
.fb-save-status {
|
|
267
|
+
color: var(--text-muted);
|
|
268
|
+
font-size: 12px;
|
|
269
|
+
min-width: 92px;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
.fb-status-failed,
|
|
273
|
+
.fb-status-unreadable {
|
|
274
|
+
color: #d64545;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.fb-status-conflict {
|
|
278
|
+
color: #b8770f;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.fb-icon-button {
|
|
282
|
+
align-items: center;
|
|
283
|
+
background: transparent;
|
|
284
|
+
border: none;
|
|
285
|
+
border-radius: 8px;
|
|
286
|
+
color: var(--text-secondary);
|
|
287
|
+
cursor: pointer;
|
|
288
|
+
display: inline-flex;
|
|
289
|
+
font-size: 12px;
|
|
290
|
+
height: 30px;
|
|
291
|
+
justify-content: center;
|
|
292
|
+
padding: 0;
|
|
293
|
+
width: 32px;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
.fb-icon-button:hover:not(:disabled) {
|
|
297
|
+
background: var(--fb-hover);
|
|
298
|
+
color: var(--text-primary);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/* Dimmed rather than hidden: Undo that disappears when there is nothing to undo
|
|
302
|
+
is a button that seems to move around the bar. */
|
|
303
|
+
.fb-icon-button:disabled {
|
|
304
|
+
cursor: default;
|
|
305
|
+
opacity: 0.35;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
.fb-button {
|
|
309
|
+
align-items: center;
|
|
310
|
+
background: var(--surface-1);
|
|
311
|
+
border: 1px solid var(--border-subtle);
|
|
312
|
+
border-radius: 9px;
|
|
313
|
+
color: var(--text-primary);
|
|
314
|
+
cursor: pointer;
|
|
315
|
+
display: inline-flex;
|
|
316
|
+
font-size: 13px;
|
|
317
|
+
font-weight: 500;
|
|
318
|
+
gap: var(--space-2);
|
|
319
|
+
height: 34px;
|
|
320
|
+
padding: 0 var(--space-3);
|
|
321
|
+
text-decoration: none;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/* The Download link in the export dialog is an <a> that is `hidden` until
|
|
325
|
+
there is a file. `display: inline-flex` beats the UA's [hidden] rule, so a
|
|
326
|
+
class that sets display has to answer for it -- without this the dialog
|
|
327
|
+
offers a download of nothing from the moment it opens. */
|
|
328
|
+
.fb-button[hidden] {
|
|
329
|
+
display: none;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
.fb-button:hover {
|
|
333
|
+
background: var(--surface-2);
|
|
334
|
+
border-color: var(--border-strong);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
.fb-button-primary {
|
|
338
|
+
background: var(--accent-channel);
|
|
339
|
+
border-color: transparent;
|
|
340
|
+
color: var(--fb-on-accent);
|
|
341
|
+
font-weight: 600;
|
|
342
|
+
padding: 0 var(--space-4);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
.fb-button-primary:hover {
|
|
346
|
+
background: #1d4fd8;
|
|
347
|
+
border-color: transparent;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
.fb-select {
|
|
351
|
+
background: var(--surface-1);
|
|
352
|
+
border: 1px solid var(--border-subtle);
|
|
353
|
+
border-radius: var(--radius-sm);
|
|
354
|
+
color: var(--text-primary);
|
|
355
|
+
font-size: 13px;
|
|
356
|
+
max-width: 220px;
|
|
357
|
+
padding: 5px var(--space-2);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/* Inside a topbar group the select carries no chrome of its own: the group is
|
|
361
|
+
already a box, and a box inside a box reads as two controls. */
|
|
362
|
+
.fb-select-quiet {
|
|
363
|
+
background: transparent;
|
|
364
|
+
border-color: transparent;
|
|
365
|
+
font-size: 12px;
|
|
366
|
+
height: 30px;
|
|
367
|
+
max-width: 150px;
|
|
368
|
+
padding: 0 var(--space-2);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
.fb-zoom-readout {
|
|
372
|
+
background: transparent;
|
|
373
|
+
border: none;
|
|
374
|
+
border-radius: 8px;
|
|
375
|
+
color: var(--text-secondary);
|
|
376
|
+
cursor: pointer;
|
|
377
|
+
font-size: 12px;
|
|
378
|
+
font-variant-numeric: tabular-nums;
|
|
379
|
+
height: 30px;
|
|
380
|
+
min-width: 52px;
|
|
381
|
+
padding: 0 2px;
|
|
382
|
+
text-align: center;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
.fb-zoom-readout:hover {
|
|
386
|
+
background: var(--fb-hover);
|
|
387
|
+
color: var(--text-primary);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/* A card like everything else on the desk. Full-bleed, it was the one element
|
|
391
|
+
left that still cut the window in half. */
|
|
392
|
+
.fb-banner {
|
|
393
|
+
align-items: flex-start;
|
|
394
|
+
background: var(--surface-2);
|
|
395
|
+
border-left: 3px solid var(--border-strong);
|
|
396
|
+
border-radius: var(--fb-card-radius);
|
|
397
|
+
box-shadow: var(--fb-float);
|
|
398
|
+
display: flex;
|
|
399
|
+
flex: 0 0 auto;
|
|
400
|
+
gap: var(--space-3);
|
|
401
|
+
margin: var(--fb-gap) var(--fb-gap) 0;
|
|
402
|
+
padding: var(--space-3);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
.fb-banner[hidden] {
|
|
406
|
+
display: none;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
.fb-banner-warning {
|
|
410
|
+
background: #fdf6e7;
|
|
411
|
+
border-left-color: #d99b1c;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
.fb-banner-danger {
|
|
415
|
+
background: #fdefef;
|
|
416
|
+
border-left-color: #d64545;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
.fb-banner-detail {
|
|
420
|
+
color: var(--text-secondary);
|
|
421
|
+
font-size: 12px;
|
|
422
|
+
margin-top: 2px;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
.fb-banner-actions {
|
|
426
|
+
display: flex;
|
|
427
|
+
gap: var(--space-2);
|
|
428
|
+
margin-top: var(--space-2);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/* No columns. The canvas is the only thing laid out here; the rail and the
|
|
432
|
+
tray are positioned against this box and lie on top of it. Quick Edit is the
|
|
433
|
+
one panel that still takes width away, because reframing a panel means
|
|
434
|
+
watching the figure change while you do it. */
|
|
435
|
+
.fb-body {
|
|
436
|
+
display: flex;
|
|
437
|
+
flex: 1 1 auto;
|
|
438
|
+
min-height: 0;
|
|
439
|
+
position: relative;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/* -- the floating cards -------------------------------------------------- */
|
|
443
|
+
|
|
444
|
+
.fb-card {
|
|
445
|
+
background: var(--surface-1);
|
|
446
|
+
border: 1px solid var(--border-subtle);
|
|
447
|
+
border-radius: var(--fb-card-radius);
|
|
448
|
+
box-shadow: var(--fb-float);
|
|
449
|
+
position: absolute;
|
|
450
|
+
z-index: 50;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/* -- the tool rail ----------------------------------------------------- */
|
|
454
|
+
/*
|
|
455
|
+
Level 1: the only controls that are always visible, and there are four.
|
|
456
|
+
Everything that acts on a selection is in the floating bar -- which is where
|
|
457
|
+
Scale bar and Legend went; everything rare is in a menu. There is no Select
|
|
458
|
+
tool: the drawing tools are one-shot, so "no tool" is where the pointer
|
|
459
|
+
already is nearly all the time.
|
|
460
|
+
|
|
461
|
+
Vertically centred rather than pinned to the top, and only as tall as it
|
|
462
|
+
needs to be. A rail that runs the full height is a column with a background,
|
|
463
|
+
which is the thing this stopped being.
|
|
464
|
+
|
|
465
|
+
Labelled, at the cost of twenty pixels of width: four icons in a stack with
|
|
466
|
+
nothing under them is four things to hover before starting, every session,
|
|
467
|
+
because "a square" is not a word. */
|
|
468
|
+
|
|
469
|
+
.fb-tool-rail {
|
|
470
|
+
align-items: center;
|
|
471
|
+
display: flex;
|
|
472
|
+
flex-direction: column;
|
|
473
|
+
gap: 2px;
|
|
474
|
+
left: var(--fb-gap);
|
|
475
|
+
max-height: calc(100% - var(--fb-gap) * 2);
|
|
476
|
+
overflow-y: auto;
|
|
477
|
+
padding: var(--space-2) 0;
|
|
478
|
+
scrollbar-width: none;
|
|
479
|
+
top: 50%;
|
|
480
|
+
transform: translateY(-50%);
|
|
481
|
+
width: var(--fb-rail);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
.fb-tool {
|
|
485
|
+
align-items: center;
|
|
486
|
+
background: transparent;
|
|
487
|
+
border: none;
|
|
488
|
+
border-radius: 10px;
|
|
489
|
+
color: var(--text-secondary);
|
|
490
|
+
cursor: pointer;
|
|
491
|
+
display: flex;
|
|
492
|
+
flex: 0 0 auto;
|
|
493
|
+
flex-direction: column;
|
|
494
|
+
font-size: 15px;
|
|
495
|
+
gap: 5px;
|
|
496
|
+
height: 52px;
|
|
497
|
+
justify-content: center;
|
|
498
|
+
margin: 0;
|
|
499
|
+
padding: 0;
|
|
500
|
+
transition: background var(--duration-fast, 120ms) ease,
|
|
501
|
+
color var(--duration-fast, 120ms) ease;
|
|
502
|
+
width: 52px;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
.fb-tool-label {
|
|
506
|
+
font-size: 9.5px;
|
|
507
|
+
font-weight: 500;
|
|
508
|
+
letter-spacing: 0.01em;
|
|
509
|
+
line-height: 1;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
.fb-tool:hover:not(:disabled) {
|
|
513
|
+
background: var(--fb-hover);
|
|
514
|
+
color: var(--text-primary);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/* A tinted tile rather than a solid block of accent. Panels is lit for as long
|
|
518
|
+
as the tray is open, which is most of the time, and one saturated square
|
|
519
|
+
sitting beside the page for all of it pulls the eye off the page. */
|
|
520
|
+
.fb-tool.is-active {
|
|
521
|
+
background: var(--fb-tint);
|
|
522
|
+
color: var(--accent-channel);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
.fb-tool:disabled {
|
|
526
|
+
cursor: default;
|
|
527
|
+
opacity: 0.4;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/* Counts what is waiting in the tray, on the button that opens it. The tray is
|
|
531
|
+
shut most of the time now, and a capture arriving into a closed drawer with
|
|
532
|
+
nothing to say so is a capture the user thinks was lost. */
|
|
533
|
+
.fb-tool-badge {
|
|
534
|
+
background: var(--accent-channel);
|
|
535
|
+
border: 2px solid var(--surface-1);
|
|
536
|
+
border-radius: 999px;
|
|
537
|
+
color: var(--fb-on-accent);
|
|
538
|
+
font-size: 9px;
|
|
539
|
+
font-weight: 700;
|
|
540
|
+
line-height: 1.5;
|
|
541
|
+
min-width: 18px;
|
|
542
|
+
padding: 0 4px;
|
|
543
|
+
position: absolute;
|
|
544
|
+
right: 2px;
|
|
545
|
+
text-align: center;
|
|
546
|
+
top: 2px;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
.fb-tool-badge:empty {
|
|
550
|
+
display: none;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
.fb-tool-holder {
|
|
554
|
+
flex: 0 0 auto;
|
|
555
|
+
position: relative;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
.fb-tool-rail-gap {
|
|
559
|
+
border-top: 1px solid var(--border-subtle);
|
|
560
|
+
flex: 0 0 auto;
|
|
561
|
+
margin: var(--space-1) 0;
|
|
562
|
+
width: 28px;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/* -- the text sidebar --------------------------------------------------
|
|
566
|
+
Contextual, not fixed: shown only while a text object is selected or is
|
|
567
|
+
being typed into. That is the difference from the properties column this
|
|
568
|
+
workspace removed, which was three hundred permanent pixels spent on
|
|
569
|
+
controls that were relevant some of the time.
|
|
570
|
+
|
|
571
|
+
Built from core's tokens, so it takes the workspace's light palette without
|
|
572
|
+
knowing that is what it is doing. */
|
|
573
|
+
/* Height comes from the contents, not from the window. It was pinned to both
|
|
574
|
+
`top` and `bottom`, which left a short panel as a tall card with a third of it
|
|
575
|
+
empty -- and the emptiness reads as "something failed to load" rather than as
|
|
576
|
+
"there is nothing more to set". */
|
|
577
|
+
/* -- every secondary sidebar, at the same size and in the same place ----- */
|
|
578
|
+
/*
|
|
579
|
+
The tray and the text panel share ONE strip beside the rail, and only one of
|
|
580
|
+
them is in it at a time (FigureWorkspace.showSidebar). They used to differ in
|
|
581
|
+
every way two panels can: the tray was pinned top AND bottom while the panel
|
|
582
|
+
hung from the top alone, and their z-indexes were 49 and 6 -- so opening the
|
|
583
|
+
second drew it underneath the first, invisible and still taking clicks.
|
|
584
|
+
|
|
585
|
+
Centred on the canvas rather than hung from its top edge, which is what the
|
|
586
|
+
rail beside them already does. A short panel starting level with a tall one
|
|
587
|
+
and stopping a third of the way down reads as a panel that failed to finish
|
|
588
|
+
loading; centred, it reads as a panel with three controls in it. */
|
|
589
|
+
.fb-side-panel {
|
|
590
|
+
display: flex;
|
|
591
|
+
flex-direction: column;
|
|
592
|
+
left: calc(var(--fb-rail) + var(--fb-gap) * 2);
|
|
593
|
+
max-height: calc(100% - var(--fb-gap) * 2);
|
|
594
|
+
min-height: 0;
|
|
595
|
+
position: absolute;
|
|
596
|
+
top: 50%;
|
|
597
|
+
transform: translateY(-50%);
|
|
598
|
+
/* Below .fb-card's own 50, which is where the tray already sat -- kept so
|
|
599
|
+
this change moves the panels and nothing else. */
|
|
600
|
+
z-index: 49;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
.fb-side-panel[hidden] { display: none; }
|
|
604
|
+
|
|
605
|
+
/* Title on the left, whatever the panel counts in the middle, its way out on
|
|
606
|
+
the right -- in that order, at that height, for every one of them. The close
|
|
607
|
+
button used to be the tray's alone, so the text panel was a card that
|
|
608
|
+
appeared by itself and could only be dismissed by deselecting.
|
|
609
|
+
|
|
610
|
+
The rule underneath is what separates the panel's NAME from its contents. It
|
|
611
|
+
matters most on the text panel, whose first control is a full-width select:
|
|
612
|
+
without it the title looked like a caption belonging to that select rather
|
|
613
|
+
than to the card. Drawn here and not per panel, so every sidebar is divided
|
|
614
|
+
in the same place. */
|
|
615
|
+
.fb-side-heading {
|
|
616
|
+
align-items: center;
|
|
617
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
618
|
+
display: flex;
|
|
619
|
+
gap: var(--space-2);
|
|
620
|
+
margin-bottom: var(--space-3);
|
|
621
|
+
padding: var(--space-3) var(--space-2) var(--space-2) var(--space-3);
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
.fb-side-title {
|
|
625
|
+
color: var(--text-primary);
|
|
626
|
+
flex: 1;
|
|
627
|
+
font-size: 14px;
|
|
628
|
+
font-weight: 600;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/* Wide enough for the row layout, which is what it is all spent on: the name
|
|
632
|
+
column and a segmented track of four buttons have to be legible side by side,
|
|
633
|
+
and at the 232px this panel started at "Horizontal align" wrapped onto two
|
|
634
|
+
lines. 260 stopped the wrapping and left the four alignment buttons 32px
|
|
635
|
+
each -- square icon buttons packed edge to edge, with the track's own frame
|
|
636
|
+
touching them. 300 is what gives every control room to be a control. */
|
|
637
|
+
.fb-text-panel {
|
|
638
|
+
gap: 0;
|
|
639
|
+
overflow-y: auto;
|
|
640
|
+
/* 16 rather than 12, and part of the same decision as the extra width: a
|
|
641
|
+
card of stacked controls reads as a form when there is air around the
|
|
642
|
+
block and as a strip of widgets when there is not. */
|
|
643
|
+
padding: 0 var(--space-4) var(--space-4);
|
|
644
|
+
width: 300px;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
/* The heading is flush with the card and the controls are inset, which is the
|
|
648
|
+
tray's arrangement too -- so the two line up along their left edge whichever
|
|
649
|
+
one is showing.
|
|
650
|
+
|
|
651
|
+
The separator therefore runs the width of the CONTENT, not of the card: the
|
|
652
|
+
panel scrolls, `overflow-y: auto` makes `overflow-x` auto with it, and a rule
|
|
653
|
+
pulled out to the card's edges with negative margins would put a horizontal
|
|
654
|
+
scrollbar on a panel 232px wide. Ranged with the controls is the right answer
|
|
655
|
+
anyway -- it is the top of the content, and it starts where the content
|
|
656
|
+
does. */
|
|
657
|
+
.fb-text-panel .fb-side-heading {
|
|
658
|
+
padding-left: 0;
|
|
659
|
+
padding-right: 0;
|
|
660
|
+
padding-top: var(--space-3);
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
/* -- one property per row ------------------------------------------------ */
|
|
664
|
+
/*
|
|
665
|
+
Name on the left, control on the right, and every control starting at the
|
|
666
|
+
same x. A GRID and not a flex row with `space-between`: flex would range each
|
|
667
|
+
control against its own name, so the column of fields would step in and out
|
|
668
|
+
by the length of the word beside it and there would be no column at all.
|
|
669
|
+
|
|
670
|
+
The name column is a fixed 104px because that is what the longest of them
|
|
671
|
+
("Horizontal align") needs at this size, with a little air after it; sizing it
|
|
672
|
+
`auto` would let one long name move every control on the panel.
|
|
673
|
+
|
|
674
|
+
The row is taller than the control in it -- 38 against 30 -- so the panel is a
|
|
675
|
+
list of properties with space between them rather than a stack of boxes. */
|
|
676
|
+
.fb-field {
|
|
677
|
+
align-items: center;
|
|
678
|
+
display: grid;
|
|
679
|
+
gap: var(--space-2);
|
|
680
|
+
grid-template-columns: 104px minmax(0, 1fr);
|
|
681
|
+
min-height: 38px;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
/* Quieter than the panel's title and quieter than the values, which is the
|
|
685
|
+
order they matter in: the name is what you scan past to reach the control. */
|
|
686
|
+
.fb-field-name {
|
|
687
|
+
color: var(--text-secondary);
|
|
688
|
+
font-size: 12px;
|
|
689
|
+
line-height: 1.3;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
.fb-field-control {
|
|
693
|
+
align-items: center;
|
|
694
|
+
display: flex;
|
|
695
|
+
gap: var(--space-2);
|
|
696
|
+
min-width: 0;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
/* `min-width: 0` on the children as well: a grid or flex item's default minimum
|
|
700
|
+
is its CONTENT, so a long font name would push the control column wider than
|
|
701
|
+
the panel and hand it a horizontal scrollbar. */
|
|
702
|
+
.fb-field-control > .fb-input,
|
|
703
|
+
.fb-field-control > .fb-segmented,
|
|
704
|
+
.fb-field-control > .fb-stepper,
|
|
705
|
+
.fb-field-control > .fb-input-color {
|
|
706
|
+
/* Stated rather than inherited: the only thing setting it on this page is
|
|
707
|
+
Bootstrap's reboot, which vendor_bundle.js injects at runtime, and one
|
|
708
|
+
fixed height is not worth making conditional on a bundle loading. */
|
|
709
|
+
box-sizing: border-box;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
.fb-field-control > .fb-input,
|
|
713
|
+
.fb-field-control > .fb-segmented,
|
|
714
|
+
.fb-field-control > .fb-stepper {
|
|
715
|
+
flex: 1 1 auto;
|
|
716
|
+
min-width: 0;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
/* One height for every control in the column, so the panel reads down a single
|
|
720
|
+
edge. The select is the odd one out -- .fb-input is sized by its padding and
|
|
721
|
+
ends up a pixel or two short of the two controls drawn with an explicit
|
|
722
|
+
height -- and a `min-height` settles it without touching .fb-input anywhere
|
|
723
|
+
else on the page. */
|
|
724
|
+
.fb-field-control > .fb-input { min-height: var(--fb-control-h); }
|
|
725
|
+
|
|
726
|
+
/* The swatch keeps its own width; the hex field takes what is left. */
|
|
727
|
+
.fb-field-control > .fb-input-color { flex: 0 0 auto; }
|
|
728
|
+
|
|
729
|
+
/* -- focus inside a sidebar ---------------------------------------------- */
|
|
730
|
+
/*
|
|
731
|
+
viewer.css declares a bare `:focus-visible { outline: 2px solid
|
|
732
|
+
var(--accent-channel); outline-offset: 2px }` and loads AFTER this file, so
|
|
733
|
+
with no rule here every field on this panel drew a fat blue rectangle two
|
|
734
|
+
pixels clear of itself -- floating over the row above, over the row below,
|
|
735
|
+
and over the card's own edge, because an outline takes no space in layout.
|
|
736
|
+
On a 300px panel of stacked controls that reads as damage rather than as
|
|
737
|
+
focus.
|
|
738
|
+
|
|
739
|
+
Replaced, not removed: the ring is how a keyboard user finds their place, and
|
|
740
|
+
deleting it here would be a real regression. What goes in its place is the
|
|
741
|
+
control's own frame lighting up -- the border takes the accent and a soft ring
|
|
742
|
+
sits just outside it -- which is inside the box, so nothing moves and nothing
|
|
743
|
+
overlaps.
|
|
744
|
+
|
|
745
|
+
Two classes beat one pseudo-class whatever order the files load in, which is
|
|
746
|
+
why this is scoped to the panel rather than written `:focus-visible`. */
|
|
747
|
+
.fb-side-panel :focus-visible {
|
|
748
|
+
outline: none;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
.fb-side-panel .fb-input:focus,
|
|
752
|
+
.fb-side-panel .fb-stepper:focus-within {
|
|
753
|
+
border-color: var(--accent-channel);
|
|
754
|
+
box-shadow: 0 0 0 2px var(--accent-channel-soft);
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
.fb-side-panel .fb-segmented-button:focus-visible,
|
|
758
|
+
.fb-side-panel .fb-icon-button:focus-visible,
|
|
759
|
+
.fb-side-panel input[type="checkbox"]:focus-visible {
|
|
760
|
+
box-shadow: 0 0 0 2px var(--accent-channel-soft);
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
/* -- the size stepper ---------------------------------------------------- */
|
|
764
|
+
/*
|
|
765
|
+
One control, not three: a shared frame with the field between its two
|
|
766
|
+
buttons, so "minus, 14, plus" reads as a single thing to set rather than as a
|
|
767
|
+
number that happens to have buttons near it. The frame is .fb-input's, drawn
|
|
768
|
+
here rather than by nesting one, because a nested .fb-input would put a
|
|
769
|
+
border inside a border. */
|
|
770
|
+
.fb-stepper {
|
|
771
|
+
align-items: stretch;
|
|
772
|
+
background: var(--surface-2);
|
|
773
|
+
border: 1px solid var(--border-subtle);
|
|
774
|
+
border-radius: var(--radius-sm);
|
|
775
|
+
display: flex;
|
|
776
|
+
height: var(--fb-control-h);
|
|
777
|
+
overflow: hidden;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
.fb-stepper-button {
|
|
781
|
+
align-items: center;
|
|
782
|
+
background: transparent;
|
|
783
|
+
border: none;
|
|
784
|
+
color: var(--text-secondary);
|
|
785
|
+
cursor: pointer;
|
|
786
|
+
display: flex;
|
|
787
|
+
flex: 0 0 auto;
|
|
788
|
+
font-size: 10px;
|
|
789
|
+
justify-content: center;
|
|
790
|
+
padding: 0;
|
|
791
|
+
transition: background-color 100ms ease, color 100ms ease;
|
|
792
|
+
width: 26px;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
.fb-stepper-button:hover {
|
|
796
|
+
background: var(--fb-hover);
|
|
797
|
+
color: var(--text-primary);
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
.fb-stepper-value {
|
|
801
|
+
background: transparent;
|
|
802
|
+
border: none;
|
|
803
|
+
color: var(--text-primary);
|
|
804
|
+
flex: 1 1 auto;
|
|
805
|
+
font-size: 13px;
|
|
806
|
+
font-variant-numeric: tabular-nums;
|
|
807
|
+
min-width: 0;
|
|
808
|
+
padding: 0;
|
|
809
|
+
text-align: center;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
.fb-stepper-value:focus { outline: none; }
|
|
813
|
+
|
|
814
|
+
/* Nothing here suppresses the platform spinners any more, because there are
|
|
815
|
+
none to suppress: the field is `type="text"` with `inputmode="decimal"`. It
|
|
816
|
+
was a number input, and a number input reports `selectionStart` as null --
|
|
817
|
+
so the panel could not put the caret back after its own redraw and typing
|
|
818
|
+
"20" produced 02. The two buttons were always doing the spinners' job
|
|
819
|
+
anyway. FigureTextPanel.stepper carries the rest of that reasoning. */
|
|
820
|
+
|
|
821
|
+
/* -- the hex code beside the swatch -------------------------------------- */
|
|
822
|
+
/*
|
|
823
|
+
The swatch alone can only be read by looking at it, which is no use when the
|
|
824
|
+
answer has to go in a methods section or match a co-author's. Tabular figures
|
|
825
|
+
so the six characters do not shift width as they are typed. */
|
|
826
|
+
.fb-input-hex {
|
|
827
|
+
font-size: 12px;
|
|
828
|
+
font-variant-numeric: tabular-nums;
|
|
829
|
+
letter-spacing: 0.02em;
|
|
830
|
+
text-transform: lowercase;
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
/* -- segmented controls -------------------------------------------------- */
|
|
834
|
+
/*
|
|
835
|
+
One track holding mutually exclusive choices, rather than loose icon buttons
|
|
836
|
+
in a row: an alignment has exactly one answer at a time, and the shared track
|
|
837
|
+
is what says so before anything has been pressed. The pressed state is a
|
|
838
|
+
raised tile rather than a filled accent block -- four of those in a column
|
|
839
|
+
was more colour than the rest of this workspace uses anywhere. */
|
|
840
|
+
.fb-segmented {
|
|
841
|
+
background: var(--surface-2);
|
|
842
|
+
border: 1px solid var(--border-subtle);
|
|
843
|
+
border-radius: var(--radius-sm);
|
|
844
|
+
display: flex;
|
|
845
|
+
gap: 2px;
|
|
846
|
+
height: var(--fb-control-h);
|
|
847
|
+
padding: 2px;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
.fb-segmented-button {
|
|
851
|
+
align-items: center;
|
|
852
|
+
background: transparent;
|
|
853
|
+
border: none;
|
|
854
|
+
border-radius: calc(var(--radius-sm) - 2px);
|
|
855
|
+
color: var(--text-secondary);
|
|
856
|
+
cursor: pointer;
|
|
857
|
+
display: inline-flex;
|
|
858
|
+
flex: 1 1 0;
|
|
859
|
+
font-size: 12px;
|
|
860
|
+
justify-content: center;
|
|
861
|
+
min-width: 0;
|
|
862
|
+
padding: 0;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
.fb-segmented-button:hover:not(:disabled) {
|
|
866
|
+
background: var(--fb-hover);
|
|
867
|
+
color: var(--text-primary);
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
/* Tinted and ringed, not merely raised. The lifted-tile treatment works on a
|
|
871
|
+
grey track against a grey card, and Bold sitting on a WHITE card was a white
|
|
872
|
+
tile on a near-white ground -- so the one thing the button had to say, that
|
|
873
|
+
the mark is currently on, was the thing that could not be seen. It also has
|
|
874
|
+
to be seen from across the panel, because it is what tells you a second click
|
|
875
|
+
will take the bold off again. */
|
|
876
|
+
.fb-segmented-button.is-on {
|
|
877
|
+
background: var(--fb-tint);
|
|
878
|
+
box-shadow: inset 0 0 0 1px var(--accent-channel-soft);
|
|
879
|
+
color: var(--accent-channel);
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
.fb-segmented-button.is-on:hover {
|
|
883
|
+
background: var(--accent-channel-soft);
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
/* -- the colour swatch and the unit suffix ------------------------------- */
|
|
887
|
+
/*
|
|
888
|
+
A bare <input type="color"> draws the platform's own bevelled well, which was
|
|
889
|
+
the one control on the panel that matched nothing else on the page. Stripped
|
|
890
|
+
back to a swatch inside an .fb-input frame. */
|
|
891
|
+
/* Two classes, because .fb-input is declared further down this file and its
|
|
892
|
+
`width: 100%` would otherwise win the cascade on equal specificity and
|
|
893
|
+
stretch the swatch across the row.
|
|
894
|
+
|
|
895
|
+
`width` and `height`, and NOT the logical `inline-size`/`block-size` this
|
|
896
|
+
block used to be written in. They are the same property in a horizontal
|
|
897
|
+
writing mode, so the `width: auto` that was left below them -- sorted after
|
|
898
|
+
`padding`, the way every block in this file is sorted -- simply won, and the
|
|
899
|
+
swatch collapsed to its 3px of padding and its border: an eight-pixel slice
|
|
900
|
+
of colour beside the hex field, which read as the swatch being missing
|
|
901
|
+
rather than as the swatch being small. One physical property per dimension
|
|
902
|
+
is the only way that cannot happen again. */
|
|
903
|
+
.fb-input.fb-input-color {
|
|
904
|
+
cursor: pointer;
|
|
905
|
+
height: var(--fb-control-h);
|
|
906
|
+
padding: 3px;
|
|
907
|
+
width: 44px;
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
.fb-input-color::-webkit-color-swatch-wrapper { padding: 0; }
|
|
911
|
+
|
|
912
|
+
.fb-input-color::-webkit-color-swatch {
|
|
913
|
+
border: none;
|
|
914
|
+
border-radius: 2px;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
.fb-input-color::-moz-color-swatch {
|
|
918
|
+
border: none;
|
|
919
|
+
border-radius: 2px;
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
/* "deg" spelled out took as much room as the field it labelled. */
|
|
923
|
+
.fb-input-suffixed {
|
|
924
|
+
align-items: center;
|
|
925
|
+
display: inline-flex;
|
|
926
|
+
gap: 4px;
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
.fb-input-suffix {
|
|
930
|
+
color: var(--text-muted);
|
|
931
|
+
font-size: 11px;
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
/* The row's name is the label, so the box stands on its own at the left of the
|
|
935
|
+
control column -- level with the fields above it, not centred in a cell of
|
|
936
|
+
its own. */
|
|
937
|
+
.fb-text-panel input[type="checkbox"] {
|
|
938
|
+
accent-color: var(--accent-channel);
|
|
939
|
+
height: 15px;
|
|
940
|
+
margin: 0;
|
|
941
|
+
width: 15px;
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
/* -- the panel tray ----------------------------------------------------- */
|
|
945
|
+
/*
|
|
946
|
+
A card the rail opens, with its own title and its own way out. It is the
|
|
947
|
+
only panel this page has, and the one place captured panels land.
|
|
948
|
+
|
|
949
|
+
Full height, unlike the rail: it is a list, and a list wants the window. */
|
|
950
|
+
|
|
951
|
+
/* Geometry, centring and z are .fb-side-panel's. What is left here is the one
|
|
952
|
+
thing that is genuinely the tray's: it is a LIST, so it takes the height it
|
|
953
|
+
is allowed rather than the height of its contents. */
|
|
954
|
+
.fb-tray-panel {
|
|
955
|
+
height: calc(100% - var(--fb-gap) * 2);
|
|
956
|
+
overflow: hidden;
|
|
957
|
+
width: var(--fb-tray);
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
.fb-tray-count {
|
|
961
|
+
background: var(--surface-2);
|
|
962
|
+
border-radius: 999px;
|
|
963
|
+
color: var(--text-muted);
|
|
964
|
+
font-size: 11px;
|
|
965
|
+
font-variant-numeric: tabular-nums;
|
|
966
|
+
padding: 1px 7px;
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
.fb-tray-count:empty {
|
|
970
|
+
display: none;
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
/* The tray's one action, at the width of the card. A small button floating in
|
|
974
|
+
a column this narrow reads as something left behind rather than as the way
|
|
975
|
+
in -- and on an empty tray it is the only thing to do. */
|
|
976
|
+
.fb-tray-add {
|
|
977
|
+
padding: 0 var(--space-3) var(--space-2);
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
.fb-tray-add-button {
|
|
981
|
+
justify-content: center;
|
|
982
|
+
width: 100%;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
/* A filter, dressed as one.
|
|
986
|
+
|
|
987
|
+
It narrows the list under it as the characters arrive: there is nothing to
|
|
988
|
+
submit and nothing to get wrong, so the field is a recess in the card rather
|
|
989
|
+
than a control with a border. What it had was a three-pixel accent halo on
|
|
990
|
+
focus, which announced a commitment it never asks for -- in the one colour
|
|
991
|
+
this page reserves for "this is selected". */
|
|
992
|
+
.fb-tray-search {
|
|
993
|
+
align-items: center;
|
|
994
|
+
background: var(--surface-2);
|
|
995
|
+
border: 1px solid transparent;
|
|
996
|
+
border-radius: 9px;
|
|
997
|
+
color: var(--text-muted);
|
|
998
|
+
display: flex;
|
|
999
|
+
font-size: 11px;
|
|
1000
|
+
gap: var(--space-2);
|
|
1001
|
+
margin: 0 var(--space-3) var(--space-2);
|
|
1002
|
+
padding: 0 var(--space-2) 0 var(--space-3);
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
.fb-tray-search:focus-within {
|
|
1006
|
+
background: var(--surface-1);
|
|
1007
|
+
border-color: var(--border-strong);
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
.fb-tray-search[hidden] {
|
|
1011
|
+
display: none;
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
.fb-tray-search input {
|
|
1015
|
+
appearance: none;
|
|
1016
|
+
background: transparent;
|
|
1017
|
+
border: none;
|
|
1018
|
+
color: var(--text-primary);
|
|
1019
|
+
flex: 1 1 auto;
|
|
1020
|
+
font-family: inherit;
|
|
1021
|
+
font-size: 12px;
|
|
1022
|
+
height: 32px;
|
|
1023
|
+
min-width: 0;
|
|
1024
|
+
outline: none;
|
|
1025
|
+
padding: 0;
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
/* WebKit draws its own clear button for type=search, at a size and a moment of
|
|
1029
|
+
its own choosing and in one engine only. The × beside this is ours, and it
|
|
1030
|
+
is in every browser. */
|
|
1031
|
+
.fb-tray-search input::-webkit-search-cancel-button,
|
|
1032
|
+
.fb-tray-search input::-webkit-search-decoration {
|
|
1033
|
+
appearance: none;
|
|
1034
|
+
display: none;
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
.fb-tray-search-clear {
|
|
1038
|
+
background: transparent;
|
|
1039
|
+
border: none;
|
|
1040
|
+
border-radius: 6px;
|
|
1041
|
+
color: var(--text-muted);
|
|
1042
|
+
cursor: pointer;
|
|
1043
|
+
flex: 0 0 auto;
|
|
1044
|
+
font-size: 10px;
|
|
1045
|
+
height: 22px;
|
|
1046
|
+
padding: 0;
|
|
1047
|
+
width: 22px;
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
.fb-tray-search-clear:hover {
|
|
1051
|
+
background: var(--fb-hover);
|
|
1052
|
+
color: var(--text-primary);
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
.fb-tray-search-clear[hidden] {
|
|
1056
|
+
display: none;
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
/* Two across, mostly picture, and as tall as it needs to be.
|
|
1060
|
+
|
|
1061
|
+
A panel is recognised by what it LOOKS like -- this is a tray of
|
|
1062
|
+
photographs, and "panel_3" and "panel_4" are not how anyone tells two fields
|
|
1063
|
+
of the same slide apart. One per row spent two thirds of the card's width on
|
|
1064
|
+
a filename and showed six thumbnails; two across shows twelve at nearly two
|
|
1065
|
+
and a half times the area each.
|
|
1066
|
+
|
|
1067
|
+
Not three: the tray would have to be wider than the sheet's margin for a
|
|
1068
|
+
third column to be worth looking at, and it is the page that should have the
|
|
1069
|
+
width.
|
|
1070
|
+
|
|
1071
|
+
WRAPPED FLEX, not a grid, and that is not a style preference. A grid row is
|
|
1072
|
+
sized before its items are laid out, and a tile whose height comes from an
|
|
1073
|
+
aspect ratio applied to a percentage width has no height to offer at that
|
|
1074
|
+
point: every row came out a couple of dozen pixels tall and every thumbnail
|
|
1075
|
+
spilled over the two below it. A flex line takes its height from items whose
|
|
1076
|
+
widths are already definite -- `calc()` of the card's own width -- so the
|
|
1077
|
+
line is as tall as the taller of its two tiles and nothing overlaps.
|
|
1078
|
+
|
|
1079
|
+
`align-items: flex-start` for the same reason the grid wanted it: stretched
|
|
1080
|
+
to the line, a wide strip beside a tall crop would gain a band of empty card
|
|
1081
|
+
under its picture and stop being the shape it is. Uneven bottoms are what a
|
|
1082
|
+
tray of photographs looks like; the card scrolls. */
|
|
1083
|
+
.fb-tray-strip {
|
|
1084
|
+
align-content: flex-start;
|
|
1085
|
+
align-items: flex-start;
|
|
1086
|
+
display: flex;
|
|
1087
|
+
flex: 1 1 auto;
|
|
1088
|
+
flex-wrap: wrap;
|
|
1089
|
+
gap: var(--space-2);
|
|
1090
|
+
min-height: 0;
|
|
1091
|
+
overflow-y: auto;
|
|
1092
|
+
padding: 0 var(--space-3) var(--space-3);
|
|
1093
|
+
scrollbar-width: thin;
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
/* Two per line exactly: the pair plus the gap between them is the whole width,
|
|
1097
|
+
so a third can never fit and neither can be squeezed. `border-box` is not
|
|
1098
|
+
inherited from anywhere on this page -- without it the border would push the
|
|
1099
|
+
pair past 100% and the tray would silently go back to one per line. */
|
|
1100
|
+
.fb-tray-item {
|
|
1101
|
+
border: 1px solid var(--border-subtle);
|
|
1102
|
+
border-radius: 10px;
|
|
1103
|
+
box-sizing: border-box;
|
|
1104
|
+
cursor: grab;
|
|
1105
|
+
display: block;
|
|
1106
|
+
flex: 0 0 auto;
|
|
1107
|
+
min-width: 0;
|
|
1108
|
+
overflow: hidden;
|
|
1109
|
+
position: relative;
|
|
1110
|
+
width: calc((100% - var(--space-2)) / 2);
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
.fb-tray-item:hover {
|
|
1114
|
+
border-color: var(--border-strong);
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
/* A ring, not a tint. The tile is almost entirely image now, and tinting the
|
|
1118
|
+
few pixels of card around it is a selection you cannot see. */
|
|
1119
|
+
.fb-tray-item.is-selected {
|
|
1120
|
+
border-color: var(--accent-channel);
|
|
1121
|
+
box-shadow: 0 0 0 2px var(--accent-channel-soft);
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
/* The shape it actually is.
|
|
1125
|
+
|
|
1126
|
+
These were cropped to a common 4:3, which made a 2:1 strip look like a
|
|
1127
|
+
square field and a tall crop look like neither -- in a tray whose whole job
|
|
1128
|
+
is "which one is this", where the answer is the SHAPE as often as it is the
|
|
1129
|
+
content. The tiles are different heights now, and the card scrolls, which is
|
|
1130
|
+
what a tray is for.
|
|
1131
|
+
|
|
1132
|
+
The ratio is set inline from the panel's own viewport, so the tile is the
|
|
1133
|
+
right shape before any of the pictures have arrived: twelve lazy images
|
|
1134
|
+
resizing their tiles as they load would move the one being reached for out
|
|
1135
|
+
from under the pointer. `auto 4 / 3` is what is left for a panel with no
|
|
1136
|
+
viewport worth trusting -- the picture's own ratio once it has loaded, and a
|
|
1137
|
+
box of roughly the right size until then rather than a two-pixel sliver.
|
|
1138
|
+
|
|
1139
|
+
`contain` is the safety net for a preview that does not match the region it
|
|
1140
|
+
was taken from: letterboxed is a gap nobody acts on, cropped is a shape they
|
|
1141
|
+
might. */
|
|
1142
|
+
.fb-tray-item img {
|
|
1143
|
+
aspect-ratio: auto 4 / 3;
|
|
1144
|
+
background: #0b0f15;
|
|
1145
|
+
display: block;
|
|
1146
|
+
height: auto;
|
|
1147
|
+
object-fit: contain;
|
|
1148
|
+
width: 100%;
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
/* The name, on hover.
|
|
1152
|
+
|
|
1153
|
+
Held back rather than dropped: it is the answer to "which of these two
|
|
1154
|
+
similar fields is it", which is a question asked occasionally, and it was
|
|
1155
|
+
being paid for with two thirds of every row permanently. Over the picture
|
|
1156
|
+
rather than under it, so revealing it moves nothing -- a caption that
|
|
1157
|
+
appears in the layout would make the whole grid jump under the pointer. */
|
|
1158
|
+
.fb-tray-item-text {
|
|
1159
|
+
background: linear-gradient(to top, rgba(6, 10, 16, 0.9),
|
|
1160
|
+
rgba(6, 10, 16, 0.6) 55%, rgba(6, 10, 16, 0));
|
|
1161
|
+
bottom: 0;
|
|
1162
|
+
color: #ffffff;
|
|
1163
|
+
display: flex;
|
|
1164
|
+
flex-direction: column;
|
|
1165
|
+
gap: 1px;
|
|
1166
|
+
left: 0;
|
|
1167
|
+
opacity: 0;
|
|
1168
|
+
padding: 16px var(--space-2) 5px;
|
|
1169
|
+
pointer-events: none;
|
|
1170
|
+
position: absolute;
|
|
1171
|
+
right: 0;
|
|
1172
|
+
transition: opacity var(--duration-fast, 120ms) ease;
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
/* Selected tiles keep theirs up: having picked four out of twelve, which four
|
|
1176
|
+
is exactly what you want to be able to read without hovering each one. */
|
|
1177
|
+
.fb-tray-item:hover .fb-tray-item-text,
|
|
1178
|
+
.fb-tray-item.is-selected .fb-tray-item-text {
|
|
1179
|
+
opacity: 1;
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
.fb-tray-item-name {
|
|
1183
|
+
font-size: 11px;
|
|
1184
|
+
overflow: hidden;
|
|
1185
|
+
text-overflow: ellipsis;
|
|
1186
|
+
white-space: nowrap;
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
/* A panel from a source with no physical calibration has nothing to say on the
|
|
1190
|
+
second line. */
|
|
1191
|
+
.fb-tray-item-meta {
|
|
1192
|
+
color: rgba(255, 255, 255, 0.78);
|
|
1193
|
+
font-size: 10px;
|
|
1194
|
+
overflow: hidden;
|
|
1195
|
+
text-overflow: ellipsis;
|
|
1196
|
+
white-space: nowrap;
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
.fb-tray-item-meta:empty {
|
|
1200
|
+
display: none;
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
.fb-tray-empty {
|
|
1204
|
+
padding: 0 var(--space-4) var(--space-4);
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
.fb-tray-empty[hidden] {
|
|
1208
|
+
display: none;
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
.fb-tray-empty p {
|
|
1212
|
+
font-size: 12px;
|
|
1213
|
+
line-height: 1.5;
|
|
1214
|
+
margin: 0 0 var(--space-2);
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
/* A flex item in the strip, so it has to be told to take the whole line rather
|
|
1218
|
+
than sit in half of one with nothing beside it. */
|
|
1219
|
+
.fb-tray-nomatch {
|
|
1220
|
+
font-size: 12px;
|
|
1221
|
+
padding: var(--space-2);
|
|
1222
|
+
width: 100%;
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
/* -- the canvas --------------------------------------------------------- */
|
|
1226
|
+
|
|
1227
|
+
.fb-main {
|
|
1228
|
+
display: flex;
|
|
1229
|
+
flex: 1 1 auto;
|
|
1230
|
+
flex-direction: column;
|
|
1231
|
+
min-height: 0;
|
|
1232
|
+
min-width: 0;
|
|
1233
|
+
position: relative;
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
/* The canvas is a scroll surface, not a viewport: panning a page is scrolling
|
|
1237
|
+
it, which every pointing device already knows how to do. Space-drag is the
|
|
1238
|
+
second way, for the trackpad hand that is already on the page.
|
|
1239
|
+
|
|
1240
|
+
The left padding clears the RAIL, and only the rail. It used to grow when
|
|
1241
|
+
the tray opened, which moved the sheet sideways every time somebody looked
|
|
1242
|
+
at their panels: the page slid, the zoom no longer framed what it had
|
|
1243
|
+
framed, and the thing being composed moved because of a drawer. The tray
|
|
1244
|
+
floats over the desk beside the sheet instead -- on a window wide enough to
|
|
1245
|
+
compose a figure there is desk to spare there, and on one that is not, a
|
|
1246
|
+
card lying over the margin for as long as it is open is a cheaper price than
|
|
1247
|
+
a page that will not hold still. */
|
|
1248
|
+
.fb-canvas-scroll {
|
|
1249
|
+
background: var(--fb-desk);
|
|
1250
|
+
flex: 1 1 auto;
|
|
1251
|
+
min-height: 0;
|
|
1252
|
+
overflow: auto;
|
|
1253
|
+
padding: var(--space-5) var(--space-5) var(--space-4);
|
|
1254
|
+
padding-left: calc(var(--fb-rail) + var(--fb-gap) * 2 + var(--space-4));
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
.fb-canvas-scroll.is-pannable {
|
|
1258
|
+
cursor: grab;
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
.fb-canvas-scroll.is-panning {
|
|
1262
|
+
cursor: grabbing;
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
/* The sheet and the two things that belong to it. Shrink-wrapped and centred
|
|
1266
|
+
as one block, so the size caption sits on the page's left edge and the Add
|
|
1267
|
+
page button under its middle -- both of which drift off it if they are
|
|
1268
|
+
centred independently in a scroll surface. */
|
|
1269
|
+
.fb-sheet {
|
|
1270
|
+
margin: 0 auto;
|
|
1271
|
+
width: max-content;
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
.fb-sheet-caption {
|
|
1275
|
+
color: var(--text-muted);
|
|
1276
|
+
font-size: 11px;
|
|
1277
|
+
font-variant-numeric: tabular-nums;
|
|
1278
|
+
padding-bottom: 6px;
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
/* Rulers are fixed to the viewport rather than to the page, so the scroll area
|
|
1282
|
+
makes room for them rather than containing them. */
|
|
1283
|
+
.fb-main.has-rulers .fb-canvas-scroll {
|
|
1284
|
+
margin-left: 20px;
|
|
1285
|
+
margin-top: 20px;
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
.fb-ruler {
|
|
1289
|
+
background: var(--surface-1);
|
|
1290
|
+
display: none;
|
|
1291
|
+
position: absolute;
|
|
1292
|
+
z-index: 30;
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
.fb-main.has-rulers .fb-ruler {
|
|
1296
|
+
display: block;
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
.fb-ruler-h {
|
|
1300
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
1301
|
+
left: 20px;
|
|
1302
|
+
top: 0;
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
.fb-ruler-v {
|
|
1306
|
+
border-right: 1px solid var(--border-subtle);
|
|
1307
|
+
left: 0;
|
|
1308
|
+
top: 20px;
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
.fb-ruler-corner {
|
|
1312
|
+
background: var(--surface-1);
|
|
1313
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
1314
|
+
border-right: 1px solid var(--border-subtle);
|
|
1315
|
+
display: none;
|
|
1316
|
+
height: 20px;
|
|
1317
|
+
left: 0;
|
|
1318
|
+
position: absolute;
|
|
1319
|
+
top: 0;
|
|
1320
|
+
width: 20px;
|
|
1321
|
+
z-index: 31;
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
.fb-main.has-rulers .fb-ruler-corner {
|
|
1325
|
+
display: block;
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
/* -- the status bar ------------------------------------------------------ */
|
|
1329
|
+
/*
|
|
1330
|
+
Zoom lives down here rather than in the topbar, beside the readout it
|
|
1331
|
+
changes and away from the controls that change the DOCUMENT. The topbar acts
|
|
1332
|
+
on the figure -- title, pages, undo, export -- and zoom acts on nothing at
|
|
1333
|
+
all: it is where the eye is, not what the figure is, and the two were sitting
|
|
1334
|
+
in one row looking like the same kind of thing.
|
|
1335
|
+
|
|
1336
|
+
OUTSIDE .fb-body, not inside the canvas. The floating cards are positioned
|
|
1337
|
+
against the body and run to its bottom edge; with the bar in there, the tray
|
|
1338
|
+
ended underneath it -- and raising the tray's z-index instead would have put
|
|
1339
|
+
a panel over a permanent control. Taking the bar out of the box the cards
|
|
1340
|
+
float in makes the collision impossible rather than settled by number.
|
|
1341
|
+
|
|
1342
|
+
Like the topbar, the bar itself is not a surface: the desk runs to the foot
|
|
1343
|
+
of the window and the zoom cluster floats on it. What is on the left stays
|
|
1344
|
+
ambient -- a count and a reminder -- because putting a sentence nobody clicks
|
|
1345
|
+
into a card of its own would give it the weight of a control. */
|
|
1346
|
+
|
|
1347
|
+
.fb-statusbar {
|
|
1348
|
+
align-items: center;
|
|
1349
|
+
background: transparent;
|
|
1350
|
+
display: flex;
|
|
1351
|
+
flex: 0 0 auto;
|
|
1352
|
+
gap: var(--space-3);
|
|
1353
|
+
height: 56px;
|
|
1354
|
+
padding: 0 var(--fb-gap) var(--fb-gap);
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
.fb-statusbar-count {
|
|
1358
|
+
color: var(--text-secondary);
|
|
1359
|
+
font-size: 12px;
|
|
1360
|
+
font-variant-numeric: tabular-nums;
|
|
1361
|
+
padding-left: var(--space-2);
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
.fb-statusbar-hint {
|
|
1365
|
+
color: var(--text-muted);
|
|
1366
|
+
font-size: 11px;
|
|
1367
|
+
overflow: hidden;
|
|
1368
|
+
text-overflow: ellipsis;
|
|
1369
|
+
white-space: nowrap;
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
.fb-statusbar-hint kbd {
|
|
1373
|
+
background: var(--surface-2);
|
|
1374
|
+
border: 1px solid var(--border-subtle);
|
|
1375
|
+
border-radius: 4px;
|
|
1376
|
+
font-family: inherit;
|
|
1377
|
+
font-size: 10px;
|
|
1378
|
+
padding: 1px 4px;
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
/* The one layer FigureCanvas.render() never touches. Anything that has to
|
|
1382
|
+
survive a re-render -- the context bar, an open popover, an in-place text
|
|
1383
|
+
editor -- is mounted here rather than inside the page surface, which is
|
|
1384
|
+
replaced wholesale on every document change. */
|
|
1385
|
+
.fb-overlay-layer {
|
|
1386
|
+
inset: 0;
|
|
1387
|
+
pointer-events: none;
|
|
1388
|
+
position: absolute;
|
|
1389
|
+
z-index: 60;
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
.fb-overlay-layer > * {
|
|
1393
|
+
pointer-events: auto;
|
|
1394
|
+
position: absolute;
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
/* Rounded, bordered, and shadowed just enough to lift. On a light desk a heavy
|
|
1398
|
+
drop shadow is what makes a mockup look like a mockup: what says "sheet of
|
|
1399
|
+
paper" here is the hairline edge, and the shadow only has to stop the white
|
|
1400
|
+
page dissolving into the near-white desk. */
|
|
1401
|
+
/* `isolation` is the fix for a real bug and not a tidy-up. Panels draw at
|
|
1402
|
+
`z-index: z` and annotations at `1000 + z`, and NOTHING between them and the
|
|
1403
|
+
document root made a stacking context -- .fb-page-surface and .fb-page are
|
|
1404
|
+
both positioned with `z-index: auto`, which does not. So a caption at 1001
|
|
1405
|
+
competed directly with .fb-overlay-layer at 60, and won: every popover opened
|
|
1406
|
+
from the floating bar rendered UNDER the object it was about to change, which
|
|
1407
|
+
is the one place it is guaranteed to be over something. Confining those
|
|
1408
|
+
numbers to the page is what makes the overlay reliably above them, and it
|
|
1409
|
+
costs nothing else -- the ordering inside the page is unchanged. */
|
|
1410
|
+
.fb-page {
|
|
1411
|
+
isolation: isolate;
|
|
1412
|
+
background: #ffffff;
|
|
1413
|
+
border-radius: 4px;
|
|
1414
|
+
box-shadow: 0 0 0 1px rgba(15, 23, 42, 0.10),
|
|
1415
|
+
0 10px 24px rgba(15, 23, 42, 0.10);
|
|
1416
|
+
position: relative;
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
/* A page with no background at all, drawn as the conventional checkerboard.
|
|
1420
|
+
Leaving it literally transparent would show the desk through it, which reads
|
|
1421
|
+
as a page the colour of the app rather than as no page. */
|
|
1422
|
+
.fb-page.is-transparent {
|
|
1423
|
+
background-color: #ffffff;
|
|
1424
|
+
background-image:
|
|
1425
|
+
linear-gradient(45deg, #d6d6d6 25%, transparent 25%, transparent 75%, #d6d6d6 75%),
|
|
1426
|
+
linear-gradient(45deg, #d6d6d6 25%, transparent 25%, transparent 75%, #d6d6d6 75%);
|
|
1427
|
+
background-position: 0 0, 8px 8px;
|
|
1428
|
+
background-size: 16px 16px;
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
/* Under the page and as wide as it, which is where the page it makes would
|
|
1432
|
+
appear. Inside the scroll surface rather than in the status bar, so it
|
|
1433
|
+
scrolls away with the sheet it follows: it is part of the document, not part
|
|
1434
|
+
of the window. */
|
|
1435
|
+
.fb-add-page {
|
|
1436
|
+
align-items: center;
|
|
1437
|
+
background: transparent;
|
|
1438
|
+
border: 1px dashed var(--border-strong);
|
|
1439
|
+
border-radius: 9px;
|
|
1440
|
+
color: var(--text-muted);
|
|
1441
|
+
cursor: pointer;
|
|
1442
|
+
display: flex;
|
|
1443
|
+
font-size: 12px;
|
|
1444
|
+
gap: var(--space-2);
|
|
1445
|
+
justify-content: center;
|
|
1446
|
+
margin-top: var(--space-3);
|
|
1447
|
+
padding: 9px var(--space-4);
|
|
1448
|
+
width: 100%;
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
.fb-add-page:hover {
|
|
1452
|
+
background: var(--surface-1);
|
|
1453
|
+
border-color: var(--accent-channel);
|
|
1454
|
+
border-style: solid;
|
|
1455
|
+
color: var(--accent-channel);
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
/* Grid and margins are page CHILDREN, so a 5 mm square stays 5 mm at every
|
|
1459
|
+
zoom -- drawn in screen pixels they would drift off the millimetres they are
|
|
1460
|
+
supposed to represent the moment the zoom changed. */
|
|
1461
|
+
.fb-page-grid {
|
|
1462
|
+
background-image:
|
|
1463
|
+
linear-gradient(to right, rgba(90, 120, 150, 0.22) 1px, transparent 1px),
|
|
1464
|
+
linear-gradient(to bottom, rgba(90, 120, 150, 0.22) 1px, transparent 1px);
|
|
1465
|
+
inset: 0;
|
|
1466
|
+
pointer-events: none;
|
|
1467
|
+
position: absolute;
|
|
1468
|
+
z-index: 0;
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
.fb-page-grid[hidden],
|
|
1472
|
+
.fb-page-margins[hidden] {
|
|
1473
|
+
display: none;
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
.fb-page-margins {
|
|
1477
|
+
border: 1px dashed rgba(90, 120, 150, 0.5);
|
|
1478
|
+
pointer-events: none;
|
|
1479
|
+
position: absolute;
|
|
1480
|
+
z-index: 0;
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
.fb-page-surface {
|
|
1484
|
+
inset: 0;
|
|
1485
|
+
position: absolute;
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
/* Guides and the marquee sit above the panels and take no pointer events, so a
|
|
1489
|
+
snap line can never be the thing a drag grabs. */
|
|
1490
|
+
.fb-page-guides {
|
|
1491
|
+
inset: 0;
|
|
1492
|
+
pointer-events: none;
|
|
1493
|
+
position: absolute;
|
|
1494
|
+
z-index: 5000;
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
.fb-guide {
|
|
1498
|
+
background: var(--accent-channel);
|
|
1499
|
+
position: absolute;
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
.fb-guide-v {
|
|
1503
|
+
bottom: 0;
|
|
1504
|
+
top: 0;
|
|
1505
|
+
width: 1px;
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1508
|
+
.fb-guide-h {
|
|
1509
|
+
height: 1px;
|
|
1510
|
+
left: 0;
|
|
1511
|
+
right: 0;
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
.fb-marquee {
|
|
1515
|
+
background: rgba(120, 190, 255, 0.12);
|
|
1516
|
+
border: 1px dashed var(--accent-channel);
|
|
1517
|
+
position: absolute;
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
.fb-panel {
|
|
1521
|
+
border: 1px solid transparent;
|
|
1522
|
+
overflow: hidden;
|
|
1523
|
+
position: absolute;
|
|
1524
|
+
touch-action: none;
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
.fb-panel.is-selected {
|
|
1528
|
+
border-color: var(--accent-channel);
|
|
1529
|
+
box-shadow: 0 0 0 1px var(--accent-channel);
|
|
1530
|
+
overflow: visible;
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
/* Handles sit OUTSIDE the panel edge, so grabbing one never means clicking on
|
|
1534
|
+
the image and losing a pixel of the thing being sized. */
|
|
1535
|
+
.fb-handle {
|
|
1536
|
+
background: var(--accent-channel);
|
|
1537
|
+
border: 1px solid #ffffff;
|
|
1538
|
+
border-radius: 2px;
|
|
1539
|
+
height: 9px;
|
|
1540
|
+
position: absolute;
|
|
1541
|
+
width: 9px;
|
|
1542
|
+
z-index: 10;
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
.fb-handle-nw { cursor: nwse-resize; left: -5px; top: -5px; }
|
|
1546
|
+
.fb-handle-ne { cursor: nesw-resize; right: -5px; top: -5px; }
|
|
1547
|
+
.fb-handle-se { cursor: nwse-resize; bottom: -5px; right: -5px; }
|
|
1548
|
+
.fb-handle-sw { cursor: nesw-resize; bottom: -5px; left: -5px; }
|
|
1549
|
+
.fb-handle-n { cursor: ns-resize; left: calc(50% - 4px); top: -5px; }
|
|
1550
|
+
.fb-handle-s { bottom: -5px; cursor: ns-resize; left: calc(50% - 4px); }
|
|
1551
|
+
.fb-handle-e { cursor: ew-resize; right: -5px; top: calc(50% - 4px); }
|
|
1552
|
+
.fb-handle-w { cursor: ew-resize; left: -5px; top: calc(50% - 4px); }
|
|
1553
|
+
|
|
1554
|
+
/* Round, and standing clear above the top edge: on the outline it would sit
|
|
1555
|
+
under the `n` handle, and on a one-line caption those are the same pixels. */
|
|
1556
|
+
.fb-handle-rotate {
|
|
1557
|
+
border-radius: 50%;
|
|
1558
|
+
cursor: grab;
|
|
1559
|
+
left: calc(50% - 4px);
|
|
1560
|
+
top: -20px;
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
.fb-handle-rotate::before {
|
|
1564
|
+
border-left: 1px solid var(--accent-channel);
|
|
1565
|
+
content: "";
|
|
1566
|
+
height: 11px;
|
|
1567
|
+
left: 3px;
|
|
1568
|
+
position: absolute;
|
|
1569
|
+
top: 8px;
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
/* Drawn from what was recorded at capture time, never from the live plugins.
|
|
1573
|
+
Inside the panel here, because on screen that is where it reads; export
|
|
1574
|
+
places it as a vector block beside the raster. */
|
|
1575
|
+
.fb-panel-legend {
|
|
1576
|
+
background: rgba(0, 0, 0, 0.55);
|
|
1577
|
+
border-radius: 2px;
|
|
1578
|
+
display: flex;
|
|
1579
|
+
flex-direction: column;
|
|
1580
|
+
gap: 1px;
|
|
1581
|
+
left: 4px;
|
|
1582
|
+
max-width: 60%;
|
|
1583
|
+
padding: 3px 5px;
|
|
1584
|
+
position: absolute;
|
|
1585
|
+
top: 18px;
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
.fb-legend-row {
|
|
1589
|
+
align-items: center;
|
|
1590
|
+
color: #ffffff;
|
|
1591
|
+
display: flex;
|
|
1592
|
+
font-size: 9px;
|
|
1593
|
+
gap: 4px;
|
|
1594
|
+
line-height: 1.3;
|
|
1595
|
+
overflow: hidden;
|
|
1596
|
+
text-overflow: ellipsis;
|
|
1597
|
+
white-space: nowrap;
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
.fb-legend-swatch {
|
|
1601
|
+
border-radius: 1px;
|
|
1602
|
+
flex: 0 0 auto;
|
|
1603
|
+
height: 7px;
|
|
1604
|
+
width: 7px;
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
.fb-legend-ramp {
|
|
1608
|
+
border-radius: 1px;
|
|
1609
|
+
flex: 0 0 auto;
|
|
1610
|
+
height: 7px;
|
|
1611
|
+
width: 32px;
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
/* Drawn as a real bar rather than as text, and only ever when the source has a
|
|
1615
|
+
physical calibration -- see figureCanvas.scaleBarMarkup. */
|
|
1616
|
+
.fb-panel-scalebar {
|
|
1617
|
+
background: #ffffff;
|
|
1618
|
+
bottom: 8px;
|
|
1619
|
+
box-shadow: 0 0 2px rgba(0, 0, 0, 0.9);
|
|
1620
|
+
height: 3px;
|
|
1621
|
+
position: absolute;
|
|
1622
|
+
right: 6px;
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
.fb-panel-scalebar-label {
|
|
1626
|
+
bottom: 5px;
|
|
1627
|
+
color: #ffffff;
|
|
1628
|
+
font-size: 10px;
|
|
1629
|
+
position: absolute;
|
|
1630
|
+
right: 0;
|
|
1631
|
+
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.9);
|
|
1632
|
+
white-space: nowrap;
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1635
|
+
/* A panel whose source changed or went missing. Marked, never redrawn: a
|
|
1636
|
+
scientifically established panel is not re-rendered from materially different
|
|
1637
|
+
data because the data moved. */
|
|
1638
|
+
.fb-panel-badge {
|
|
1639
|
+
color: var(--accent-warning, #e0a44c);
|
|
1640
|
+
position: absolute;
|
|
1641
|
+
right: 4px;
|
|
1642
|
+
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.9);
|
|
1643
|
+
top: 2px;
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1646
|
+
.fb-panel-badge-missing {
|
|
1647
|
+
color: var(--accent-danger);
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
.fb-annotation {
|
|
1651
|
+
position: absolute;
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
/* No `line-height` and no `white-space` any more: the lines are positioned by
|
|
1655
|
+
FigureCanvas.textLayout and drawn as SVG with an explicit baseline, because
|
|
1656
|
+
the browser's own baseline comes from whichever font file it resolved and
|
|
1657
|
+
the PDF's comes from the AFM metrics -- about a quarter of a millimetre
|
|
1658
|
+
apart at 8pt, which no test could reach. The leading now lives in
|
|
1659
|
+
FigureRichText.LINE_HEIGHT, where both languages read it. */
|
|
1660
|
+
.fb-annotation-text {
|
|
1661
|
+
overflow: visible;
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
/* Visible overflow rather than clipped: a box narrower than a long word shows
|
|
1665
|
+
the word running past its edge, which is the truth about what will export. */
|
|
1666
|
+
.fb-text-svg {
|
|
1667
|
+
display: block;
|
|
1668
|
+
overflow: visible;
|
|
1669
|
+
pointer-events: none;
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
.fb-annotation-rect {
|
|
1673
|
+
border: 1px solid currentColor;
|
|
1674
|
+
}
|
|
1675
|
+
|
|
1676
|
+
.fb-annotation-ellipse {
|
|
1677
|
+
border: 1px solid currentColor;
|
|
1678
|
+
border-radius: 50%;
|
|
1679
|
+
}
|
|
1680
|
+
|
|
1681
|
+
.fb-annotation.is-selected {
|
|
1682
|
+
outline: 1px solid var(--accent-channel);
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1685
|
+
/* A line's element is its NORMALISED bounding box plus padding for the
|
|
1686
|
+
arrowhead, which for a long diagonal covers a large empty area. If it took
|
|
1687
|
+
clicks it would be a region where selecting the panels underneath quietly
|
|
1688
|
+
stopped working, so the ink takes them instead: a fat transparent line under
|
|
1689
|
+
the visible one (.fb-stroke-hit). */
|
|
1690
|
+
.fb-annotation-stroke {
|
|
1691
|
+
pointer-events: none;
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1694
|
+
.fb-annotation-stroke.is-selected {
|
|
1695
|
+
outline: none;
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1698
|
+
.fb-annotation-stroke svg {
|
|
1699
|
+
display: block;
|
|
1700
|
+
overflow: visible;
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
.fb-stroke-hit {
|
|
1704
|
+
pointer-events: stroke;
|
|
1705
|
+
stroke: transparent;
|
|
1706
|
+
stroke-width: 12;
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1709
|
+
/* Handles are the exception: they are the only part of a line's box that has to
|
|
1710
|
+
answer a press. */
|
|
1711
|
+
.fb-annotation .fb-handle {
|
|
1712
|
+
pointer-events: auto;
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1715
|
+
/* A line's two ENDS. Round, so they read as points rather than as the corners
|
|
1716
|
+
of a box that is not there. */
|
|
1717
|
+
.fb-handle-point {
|
|
1718
|
+
border-radius: 50%;
|
|
1719
|
+
cursor: crosshair;
|
|
1720
|
+
height: 10px;
|
|
1721
|
+
width: 10px;
|
|
1722
|
+
}
|
|
1723
|
+
|
|
1724
|
+
/* The shape being dragged out, before it exists. In the guides layer, which
|
|
1725
|
+
takes no pointer events and is not what render() replaces. */
|
|
1726
|
+
.fb-draft {
|
|
1727
|
+
background: rgba(120, 190, 255, 0.08);
|
|
1728
|
+
border: 1px dashed var(--accent-channel);
|
|
1729
|
+
position: absolute;
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1732
|
+
.fb-draft-ellipse {
|
|
1733
|
+
border-radius: 50%;
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
/* A drawing tool is armed: the whole surface is a place to start a shape, so
|
|
1737
|
+
the cursor says so rather than the rail 200 pixels away being the only clue. */
|
|
1738
|
+
.fb-page-surface.is-drawing {
|
|
1739
|
+
cursor: crosshair;
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
/* Typing into a text annotation where it sits. Mounted in the overlay layer,
|
|
1743
|
+
because the page surface is replaced on every document change and the change
|
|
1744
|
+
in question is the one the user is making. */
|
|
1745
|
+
/* A contenteditable, not a textarea -- a textarea holds one style for its whole
|
|
1746
|
+
contents, which is the limitation this feature exists to remove. It is
|
|
1747
|
+
absolutely positioned over the annotation it is editing, in the overlay layer
|
|
1748
|
+
that FigureCanvas.render() never touches. */
|
|
1749
|
+
.fb-text-editor {
|
|
1750
|
+
background: var(--surface-0);
|
|
1751
|
+
border: 1px solid var(--accent-channel);
|
|
1752
|
+
border-radius: 2px;
|
|
1753
|
+
margin: 0;
|
|
1754
|
+
padding: 0 1px;
|
|
1755
|
+
position: absolute;
|
|
1756
|
+
white-space: pre-wrap;
|
|
1757
|
+
z-index: 40;
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
/*
|
|
1761
|
+
No browser focus ring on anything that sits ON the sheet.
|
|
1762
|
+
|
|
1763
|
+
viewer.css declares a bare `:focus-visible { outline: 2px solid
|
|
1764
|
+
var(--accent-channel); outline-offset: 2px }`, and on this page it LOADS
|
|
1765
|
+
AFTER this file -- base.html links a plugin's stylesheets in <head>, and
|
|
1766
|
+
workspace.html links viewer.css inside the content block, which is in the
|
|
1767
|
+
body. Both selectors score (0,1,0), so source order decides and viewer.css
|
|
1768
|
+
wins: `.fb-text-editor { outline: none }` above was overruled and never did
|
|
1769
|
+
anything. Clicking into a caption drew a fat blue ring two pixels outside the
|
|
1770
|
+
editor's own blue border, which was itself inside the annotation's blue
|
|
1771
|
+
selection outline. Three blue boxes for one caption.
|
|
1772
|
+
|
|
1773
|
+
Scoped, not global. A focus ring is how a keyboard user finds the rail, the
|
|
1774
|
+
topbar and the sidebars, and removing it there would be a real regression.
|
|
1775
|
+
Inside the page it says nothing: selection on this canvas is drawn by the
|
|
1776
|
+
app -- the thin outline and the handles -- for pointer and keyboard alike,
|
|
1777
|
+
and the ring is a second, louder answer to a question already answered.
|
|
1778
|
+
|
|
1779
|
+
Specificity, not `!important`: two classes beat one pseudo-class whatever the
|
|
1780
|
+
order of the files. */
|
|
1781
|
+
.fb-workspace .fb-page :focus,
|
|
1782
|
+
.fb-workspace .fb-page :focus-visible,
|
|
1783
|
+
.fb-workspace .fb-text-editor:focus,
|
|
1784
|
+
.fb-workspace .fb-text-editor:focus-visible {
|
|
1785
|
+
outline: none;
|
|
1786
|
+
}
|
|
1787
|
+
|
|
1788
|
+
/* Nothing typographic here any more, and that is the point. Family, size,
|
|
1789
|
+
colour and leading are set on .fb-text-editor itself by
|
|
1790
|
+
FigureTextEditor.applyBoxType, from the annotation's own style -- so a
|
|
1791
|
+
character typed where no run yet exists inherits the BOX's type instead of
|
|
1792
|
+
the app's UI type, and a caption set to 1.5 leading is typed at 1.5.
|
|
1793
|
+
|
|
1794
|
+
The leading was written here as `line-height: 1.2` to match
|
|
1795
|
+
FigureRichText.LINE_HEIGHT, which is the DEFAULT and not this caption's. */
|
|
1796
|
+
.fb-text-editor-line {
|
|
1797
|
+
min-height: 1em;
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1800
|
+
.fb-text-editor-line:empty::after {
|
|
1801
|
+
content: "";
|
|
1802
|
+
display: inline-block;
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1805
|
+
.fb-panel-image {
|
|
1806
|
+
display: block;
|
|
1807
|
+
height: 100%;
|
|
1808
|
+
object-fit: cover;
|
|
1809
|
+
width: 100%;
|
|
1810
|
+
}
|
|
1811
|
+
|
|
1812
|
+
/* A preview that has not been rendered yet, or whose source is gone. Shown as
|
|
1813
|
+
an empty frame rather than as a broken-image glyph: the panel still exists,
|
|
1814
|
+
still lays out and still exports its vector furniture. */
|
|
1815
|
+
.fb-panel-image-missing {
|
|
1816
|
+
background: repeating-linear-gradient(45deg,
|
|
1817
|
+
var(--surface-2) 0 6px, var(--surface-1) 6px 12px);
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
.fb-panel-label {
|
|
1821
|
+
color: #ffffff;
|
|
1822
|
+
font-size: 13px;
|
|
1823
|
+
font-weight: 700;
|
|
1824
|
+
left: 4px;
|
|
1825
|
+
position: absolute;
|
|
1826
|
+
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.9);
|
|
1827
|
+
top: 2px;
|
|
1828
|
+
}
|
|
1829
|
+
|
|
1830
|
+
.fb-panel-title {
|
|
1831
|
+
bottom: 2px;
|
|
1832
|
+
color: #ffffff;
|
|
1833
|
+
font-size: 11px;
|
|
1834
|
+
left: 4px;
|
|
1835
|
+
position: absolute;
|
|
1836
|
+
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.9);
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1839
|
+
/* -- Quick Edit ---------------------------------------------------------- */
|
|
1840
|
+
/*
|
|
1841
|
+
A slide-over rather than a modal: the point of Quick Edit is that the figure
|
|
1842
|
+
stays visible while the panel is adjusted, so the canvas narrows and nothing
|
|
1843
|
+
is covered up.
|
|
1844
|
+
|
|
1845
|
+
The channel controls inside it are CORE's markup, styled by viewer.css (which
|
|
1846
|
+
workspace.html links for exactly this). Nothing here restyles them -- a
|
|
1847
|
+
plugin that repainted core's channel widget would make the two copies of it
|
|
1848
|
+
look different, which is the one thing reusing it was meant to avoid. */
|
|
1849
|
+
|
|
1850
|
+
/*
|
|
1851
|
+
The one dark thing left on a light page, and deliberately so: the subject of
|
|
1852
|
+
Quick Edit is a backlit fluorescence image, and the surround a colour balance
|
|
1853
|
+
is judged against has to be neutral-dark or every channel looks dimmer than
|
|
1854
|
+
it is. Core's own values, put back for this subtree -- which is also what the
|
|
1855
|
+
channel widget inside it was written for, since it is core's markup styled by
|
|
1856
|
+
viewer.css. */
|
|
1857
|
+
.fb-quickedit {
|
|
1858
|
+
--surface-0: #05070a;
|
|
1859
|
+
--surface-1: #0b0f15;
|
|
1860
|
+
--surface-2: #121820;
|
|
1861
|
+
--surface-3: rgba(255, 255, 255, 0.08);
|
|
1862
|
+
--border-subtle: rgba(255, 255, 255, 0.12);
|
|
1863
|
+
--border-strong: rgba(255, 255, 255, 0.22);
|
|
1864
|
+
--text-primary: #f8fafc;
|
|
1865
|
+
--text-secondary: #d8e1ed;
|
|
1866
|
+
--text-muted: #8ea0b8;
|
|
1867
|
+
--accent-channel: #38bdf8;
|
|
1868
|
+
--fb-on-accent: #06121f;
|
|
1869
|
+
|
|
1870
|
+
background: var(--surface-1);
|
|
1871
|
+
border-left: 1px solid var(--border-subtle);
|
|
1872
|
+
color: var(--text-primary);
|
|
1873
|
+
display: flex;
|
|
1874
|
+
flex-direction: column;
|
|
1875
|
+
min-height: 0;
|
|
1876
|
+
width: 420px;
|
|
1877
|
+
}
|
|
1878
|
+
|
|
1879
|
+
.fb-quickedit[hidden] {
|
|
1880
|
+
display: none;
|
|
1881
|
+
}
|
|
1882
|
+
|
|
1883
|
+
.fb-quickedit-head {
|
|
1884
|
+
align-items: center;
|
|
1885
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
1886
|
+
display: flex;
|
|
1887
|
+
gap: var(--space-2);
|
|
1888
|
+
padding: var(--space-2) var(--space-3);
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
.fb-quickedit-title {
|
|
1892
|
+
color: var(--text-primary);
|
|
1893
|
+
flex: 1;
|
|
1894
|
+
font-size: 13px;
|
|
1895
|
+
font-weight: 600;
|
|
1896
|
+
overflow: hidden;
|
|
1897
|
+
text-overflow: ellipsis;
|
|
1898
|
+
white-space: nowrap;
|
|
1899
|
+
}
|
|
1900
|
+
|
|
1901
|
+
/* Up to about half the height, so the figure keeps the rest. A mini viewer
|
|
1902
|
+
that filled the panel would be a second viewer, which is the thing this is
|
|
1903
|
+
deliberately not. */
|
|
1904
|
+
.fb-quickedit-view {
|
|
1905
|
+
background: #000000;
|
|
1906
|
+
flex: 0 0 auto;
|
|
1907
|
+
height: 46vh;
|
|
1908
|
+
max-height: 460px;
|
|
1909
|
+
min-height: 220px;
|
|
1910
|
+
position: relative;
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1913
|
+
.fb-quickedit-view canvas {
|
|
1914
|
+
cursor: grab;
|
|
1915
|
+
display: block;
|
|
1916
|
+
touch-action: none;
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1919
|
+
.fb-quickedit-view canvas:active {
|
|
1920
|
+
cursor: grabbing;
|
|
1921
|
+
}
|
|
1922
|
+
|
|
1923
|
+
.fb-quickedit-note {
|
|
1924
|
+
font-size: 11px;
|
|
1925
|
+
margin: 0;
|
|
1926
|
+
padding: var(--space-2) var(--space-3);
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1929
|
+
.fb-quickedit-channels {
|
|
1930
|
+
flex: 1 1 auto;
|
|
1931
|
+
min-height: 0;
|
|
1932
|
+
overflow-y: auto;
|
|
1933
|
+
padding: 0 var(--space-3) var(--space-3);
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1936
|
+
.fb-quickedit-actions {
|
|
1937
|
+
align-items: center;
|
|
1938
|
+
border-top: 1px solid var(--border-subtle);
|
|
1939
|
+
display: flex;
|
|
1940
|
+
gap: var(--space-2);
|
|
1941
|
+
padding: var(--space-2) var(--space-3);
|
|
1942
|
+
}
|
|
1943
|
+
|
|
1944
|
+
/* -- the contextual bar -------------------------------------------------- */
|
|
1945
|
+
/*
|
|
1946
|
+
What replaced the properties column. It exists only while something is
|
|
1947
|
+
selected, sits above it, and holds icons rather than labelled rows -- a bar
|
|
1948
|
+
floating over the page has to be small enough not to be in the way of the
|
|
1949
|
+
thing it is about. Every label is in the tooltip and in aria-label. */
|
|
1950
|
+
|
|
1951
|
+
/* `flex-wrap` because every button now carries a word as well as an icon, and
|
|
1952
|
+
a panel selection puts eleven of them in a row. Wrapping to a second line is
|
|
1953
|
+
the only one of the three answers that keeps them all readable -- truncating
|
|
1954
|
+
loses the labels this exists to add, and scrolling hides controls behind a
|
|
1955
|
+
gesture nobody would think to make on a toolbar.
|
|
1956
|
+
|
|
1957
|
+
With the labels UNDER the icons it very rarely has to: eleven stacked buttons
|
|
1958
|
+
are about 560px, where eleven side-by-side ones were past 900 and wrapped on
|
|
1959
|
+
any window narrower than a desktop. The rule stays because the widest case is
|
|
1960
|
+
still the window's to decide, not this file's.
|
|
1961
|
+
|
|
1962
|
+
Rounder and softer than the popovers it opens. This is the one piece of
|
|
1963
|
+
chrome that sits ON the artwork, so it has to read as a separate object
|
|
1964
|
+
floating above the sheet rather than as a region of it -- which is what the
|
|
1965
|
+
larger radius and the wider, softer shadow are doing. */
|
|
1966
|
+
.fb-context-bar {
|
|
1967
|
+
align-items: stretch;
|
|
1968
|
+
background: var(--surface-1);
|
|
1969
|
+
border: 1px solid var(--border-subtle);
|
|
1970
|
+
border-radius: var(--fb-card-radius);
|
|
1971
|
+
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.07),
|
|
1972
|
+
0 10px 30px rgba(15, 23, 42, 0.14);
|
|
1973
|
+
display: flex;
|
|
1974
|
+
flex-wrap: wrap;
|
|
1975
|
+
gap: 2px;
|
|
1976
|
+
max-width: calc(100% - 16px);
|
|
1977
|
+
padding: 5px 6px;
|
|
1978
|
+
white-space: nowrap;
|
|
1979
|
+
z-index: 10;
|
|
1980
|
+
}
|
|
1981
|
+
|
|
1982
|
+
.fb-context-bar[hidden] {
|
|
1983
|
+
display: none;
|
|
1984
|
+
}
|
|
1985
|
+
|
|
1986
|
+
/* Taller than it is wide, and sized from the WORD: `min-width` is what stops a
|
|
1987
|
+
two-letter label leaving a button the size of its icon, and the padding is
|
|
1988
|
+
what lets a long one grow past it. The proportion is the point -- a row of
|
|
1989
|
+
near-square tiles reads as a set of equals, where a row of wide flat ones
|
|
1990
|
+
reads as a sentence of unrelated words. */
|
|
1991
|
+
.fb-context-button {
|
|
1992
|
+
align-items: center;
|
|
1993
|
+
background: transparent;
|
|
1994
|
+
border: none;
|
|
1995
|
+
border-radius: 8px;
|
|
1996
|
+
color: var(--text-secondary);
|
|
1997
|
+
cursor: pointer;
|
|
1998
|
+
display: inline-flex;
|
|
1999
|
+
flex-direction: column;
|
|
2000
|
+
gap: 4px;
|
|
2001
|
+
justify-content: center;
|
|
2002
|
+
min-width: 48px;
|
|
2003
|
+
padding: 7px 8px 6px;
|
|
2004
|
+
transition: background-color 120ms ease, color 120ms ease,
|
|
2005
|
+
box-shadow 120ms ease, transform 120ms ease;
|
|
2006
|
+
}
|
|
2007
|
+
|
|
2008
|
+
/* The overflow, which has no word under it. Narrower, and its icon centred on
|
|
2009
|
+
the FULL height of the bar rather than pushed up to where the captioned icons
|
|
2010
|
+
sit -- which is what `align-items: stretch` on the bar is for: the button is
|
|
2011
|
+
as tall as its neighbours, so centring inside it centres inside the row. */
|
|
2012
|
+
.fb-context-button.is-wordless {
|
|
2013
|
+
min-width: 30px;
|
|
2014
|
+
padding: 0 6px;
|
|
2015
|
+
}
|
|
2016
|
+
|
|
2017
|
+
/* Bigger than the word it captions, because it is what the button is
|
|
2018
|
+
recognised by once the row has been learnt; the word is what makes learning
|
|
2019
|
+
it possible in the first place. */
|
|
2020
|
+
.fb-context-icon {
|
|
2021
|
+
font-size: 14px;
|
|
2022
|
+
line-height: 1;
|
|
2023
|
+
}
|
|
2024
|
+
|
|
2025
|
+
/* The word under the icon. Every one of them sits on the same baseline, which
|
|
2026
|
+
is the whole reason for stacking: a row of icons with words BESIDE them has
|
|
2027
|
+
as many text baselines as it has button widths. */
|
|
2028
|
+
.fb-context-label {
|
|
2029
|
+
font-size: 10px;
|
|
2030
|
+
font-weight: 500;
|
|
2031
|
+
letter-spacing: 0.01em;
|
|
2032
|
+
line-height: 1;
|
|
2033
|
+
}
|
|
2034
|
+
|
|
2035
|
+
/* What the bar is picked up by: two columns of dots in a lane of its own, so
|
|
2036
|
+
there is somewhere to press that is not a command. Drawn with a gradient
|
|
2037
|
+
rather than as six elements, which keeps it out of the DOM the bar rebuilds
|
|
2038
|
+
on every selection change. */
|
|
2039
|
+
.fb-context-grip {
|
|
2040
|
+
align-self: center;
|
|
2041
|
+
background-image: radial-gradient(circle at 1px 1px,
|
|
2042
|
+
var(--text-muted) 1px, transparent 0);
|
|
2043
|
+
background-position: center;
|
|
2044
|
+
background-repeat: repeat;
|
|
2045
|
+
background-size: 4px 5px;
|
|
2046
|
+
cursor: grab;
|
|
2047
|
+
flex: 0 0 auto;
|
|
2048
|
+
height: 20px;
|
|
2049
|
+
margin: 0 3px 0 1px;
|
|
2050
|
+
opacity: 0.4;
|
|
2051
|
+
transition: opacity 120ms ease;
|
|
2052
|
+
width: 7px;
|
|
2053
|
+
}
|
|
2054
|
+
|
|
2055
|
+
.fb-context-grip:hover {
|
|
2056
|
+
opacity: 0.75;
|
|
2057
|
+
}
|
|
2058
|
+
|
|
2059
|
+
.fb-context-bar.is-dragging {
|
|
2060
|
+
cursor: grabbing;
|
|
2061
|
+
user-select: none;
|
|
2062
|
+
}
|
|
2063
|
+
|
|
2064
|
+
.fb-context-bar.is-dragging .fb-context-grip {
|
|
2065
|
+
cursor: grabbing;
|
|
2066
|
+
opacity: 1;
|
|
2067
|
+
}
|
|
2068
|
+
|
|
2069
|
+
.fb-context-button:hover:not(:disabled) {
|
|
2070
|
+
background: var(--fb-hover);
|
|
2071
|
+
color: var(--text-primary);
|
|
2072
|
+
}
|
|
2073
|
+
|
|
2074
|
+
/* Pressed, and it has to be felt rather than read -- the press is over before
|
|
2075
|
+
there is time to look at it. */
|
|
2076
|
+
.fb-context-button:active:not(:disabled) {
|
|
2077
|
+
transform: scale(0.94);
|
|
2078
|
+
}
|
|
2079
|
+
|
|
2080
|
+
/* The button whose popover is open. Tinted rather than merely hovered: the
|
|
2081
|
+
popover is somewhere else on screen, and this is the only thing tying it back
|
|
2082
|
+
to the control that opened it. */
|
|
2083
|
+
.fb-context-button.is-open,
|
|
2084
|
+
.fb-context-button.is-open:hover:not(:disabled) {
|
|
2085
|
+
background: var(--fb-tint);
|
|
2086
|
+
color: var(--accent-channel);
|
|
2087
|
+
}
|
|
2088
|
+
|
|
2089
|
+
/* On, not merely hovered: a scale bar that is showing has to look different
|
|
2090
|
+
from one the pointer happens to be over. The same tint-and-ring the segmented
|
|
2091
|
+
buttons in the text sidebar use, so "this is currently on" looks the same
|
|
2092
|
+
wherever it is said -- it was a solid accent block here and a tinted ring
|
|
2093
|
+
there, which made them read as two different kinds of state. */
|
|
2094
|
+
.fb-context-button.is-on {
|
|
2095
|
+
background: var(--fb-tint);
|
|
2096
|
+
box-shadow: inset 0 0 0 1px var(--accent-channel-soft);
|
|
2097
|
+
color: var(--accent-channel);
|
|
2098
|
+
}
|
|
2099
|
+
|
|
2100
|
+
.fb-context-button.is-on:hover:not(:disabled) {
|
|
2101
|
+
background: var(--accent-channel-soft);
|
|
2102
|
+
color: var(--accent-channel);
|
|
2103
|
+
}
|
|
2104
|
+
|
|
2105
|
+
.fb-context-button:disabled {
|
|
2106
|
+
cursor: default;
|
|
2107
|
+
opacity: 0.32;
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2110
|
+
/* Delete is on the bar now rather than three levels down in "More", so it has
|
|
2111
|
+
to be the one button there that does not look like the others. Coloured on
|
|
2112
|
+
hover only: a permanently red icon in a row of grey ones reads as a warning
|
|
2113
|
+
about the selection rather than as an action available to it. */
|
|
2114
|
+
.fb-context-button.is-danger:hover:not(:disabled) {
|
|
2115
|
+
background: rgb(220 38 38 / 10%);
|
|
2116
|
+
color: #dc2626;
|
|
2117
|
+
}
|
|
2118
|
+
|
|
2119
|
+
/* Full height, hairline, and inset from the buttons by more than the gap
|
|
2120
|
+
between them -- the divider has to be read as the boundary of a GROUP, and at
|
|
2121
|
+
the same 2px spacing as everything else it reads as one more control. */
|
|
2122
|
+
.fb-context-divider {
|
|
2123
|
+
align-self: stretch;
|
|
2124
|
+
background: var(--border-subtle);
|
|
2125
|
+
flex: 0 0 auto;
|
|
2126
|
+
margin: 4px 5px;
|
|
2127
|
+
width: 1px;
|
|
2128
|
+
}
|
|
2129
|
+
|
|
2130
|
+
.fb-context-note {
|
|
2131
|
+
color: var(--text-muted);
|
|
2132
|
+
font-size: 11px;
|
|
2133
|
+
padding: 0 var(--space-2);
|
|
2134
|
+
}
|
|
2135
|
+
|
|
2136
|
+
/* Thirty characters at a size worth aiming at. Six columns because that is
|
|
2137
|
+
what fits the popover's own width without making it wider than the bar. */
|
|
2138
|
+
.fb-symbol-grid {
|
|
2139
|
+
display: grid;
|
|
2140
|
+
gap: 2px;
|
|
2141
|
+
grid-template-columns: repeat(6, 1fr);
|
|
2142
|
+
}
|
|
2143
|
+
|
|
2144
|
+
.fb-symbol {
|
|
2145
|
+
background: transparent;
|
|
2146
|
+
border: none;
|
|
2147
|
+
border-radius: 5px;
|
|
2148
|
+
color: var(--text-primary);
|
|
2149
|
+
cursor: pointer;
|
|
2150
|
+
font-size: 15px;
|
|
2151
|
+
height: 28px;
|
|
2152
|
+
line-height: 1;
|
|
2153
|
+
padding: 0;
|
|
2154
|
+
}
|
|
2155
|
+
|
|
2156
|
+
.fb-symbol:hover {
|
|
2157
|
+
background: var(--fb-tint);
|
|
2158
|
+
color: var(--accent-channel);
|
|
2159
|
+
}
|
|
2160
|
+
|
|
2161
|
+
/* Label left, field right, one line each -- the shape of a properties list, so
|
|
2162
|
+
Rotation, Width, Height and Units read down the same two columns. */
|
|
2163
|
+
.fb-tf-row {
|
|
2164
|
+
align-items: center;
|
|
2165
|
+
display: flex;
|
|
2166
|
+
gap: var(--space-2);
|
|
2167
|
+
justify-content: space-between;
|
|
2168
|
+
margin-bottom: 4px;
|
|
2169
|
+
}
|
|
2170
|
+
|
|
2171
|
+
.fb-tf-row:last-child { margin-bottom: 0; }
|
|
2172
|
+
|
|
2173
|
+
/*
|
|
2174
|
+
A popover arrives FROM its button, not out of nothing.
|
|
2175
|
+
|
|
2176
|
+
Eight frames of fade and a four-pixel rise: long enough for the eye to catch
|
|
2177
|
+
where it came from, short enough that nobody waiting on it notices waiting.
|
|
2178
|
+
The transform origin is the top edge because every one of these opens
|
|
2179
|
+
downwards from the bar -- `positionPopover` flips it above the bar only when
|
|
2180
|
+
the page has run out of room below, and being slightly wrong in that case
|
|
2181
|
+
costs less than the extra state it would take to be right. */
|
|
2182
|
+
.fb-context-popover {
|
|
2183
|
+
animation: fb-pop-in 120ms ease-out;
|
|
2184
|
+
background: var(--surface-1);
|
|
2185
|
+
border: 1px solid var(--border-subtle);
|
|
2186
|
+
border-radius: 10px;
|
|
2187
|
+
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.08),
|
|
2188
|
+
0 10px 30px rgba(15, 23, 42, 0.16);
|
|
2189
|
+
min-width: 190px;
|
|
2190
|
+
padding: var(--space-2);
|
|
2191
|
+
transform-origin: top center;
|
|
2192
|
+
z-index: 20;
|
|
2193
|
+
}
|
|
2194
|
+
|
|
2195
|
+
@keyframes fb-pop-in {
|
|
2196
|
+
from { opacity: 0; transform: translateY(-4px) scale(0.97); }
|
|
2197
|
+
to { opacity: 1; transform: none; }
|
|
2198
|
+
}
|
|
2199
|
+
|
|
2200
|
+
/* Asked for by the platform, and honoured: everything above is decoration on a
|
|
2201
|
+
state change that has already happened, so there is nothing to lose by
|
|
2202
|
+
cutting it. */
|
|
2203
|
+
@media (prefers-reduced-motion: reduce) {
|
|
2204
|
+
.fb-context-popover,
|
|
2205
|
+
.fb-menu {
|
|
2206
|
+
animation: none;
|
|
2207
|
+
}
|
|
2208
|
+
|
|
2209
|
+
.fb-context-button,
|
|
2210
|
+
.fb-menu-item {
|
|
2211
|
+
transition: none;
|
|
2212
|
+
}
|
|
2213
|
+
|
|
2214
|
+
.fb-context-button:active:not(:disabled) {
|
|
2215
|
+
transform: none;
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2218
|
+
/* Two panels drawing the same marker in different colours. Set apart rather
|
|
2219
|
+
than phrased as a warning: it may be deliberate, and the safe answer -- keep
|
|
2220
|
+
them separate -- is the first button. */
|
|
2221
|
+
.fb-conflict {
|
|
2222
|
+
border-top: 1px solid var(--border-subtle);
|
|
2223
|
+
margin-top: var(--space-2);
|
|
2224
|
+
padding-top: var(--space-2);
|
|
2225
|
+
}
|
|
2226
|
+
|
|
2227
|
+
.fb-conflict strong {
|
|
2228
|
+
color: #96650a;
|
|
2229
|
+
display: block;
|
|
2230
|
+
font-size: 12px;
|
|
2231
|
+
}
|
|
2232
|
+
|
|
2233
|
+
/* Something worth saying that is not worth stopping for. Bottom centre of the
|
|
2234
|
+
canvas, over the page rather than over the chrome, gone by itself. */
|
|
2235
|
+
.fb-toast {
|
|
2236
|
+
align-items: center;
|
|
2237
|
+
background: var(--surface-1);
|
|
2238
|
+
border: 1px solid var(--border-subtle);
|
|
2239
|
+
border-radius: 8px;
|
|
2240
|
+
bottom: 20px;
|
|
2241
|
+
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.08),
|
|
2242
|
+
0 10px 30px rgba(15, 23, 42, 0.16);
|
|
2243
|
+
color: var(--text-primary);
|
|
2244
|
+
display: flex;
|
|
2245
|
+
font-size: 12px;
|
|
2246
|
+
gap: var(--space-2);
|
|
2247
|
+
left: 50%;
|
|
2248
|
+
max-width: 520px;
|
|
2249
|
+
padding: var(--space-2) var(--space-3);
|
|
2250
|
+
transform: translateX(-50%);
|
|
2251
|
+
z-index: 80;
|
|
2252
|
+
}
|
|
2253
|
+
|
|
2254
|
+
.fb-toast-action {
|
|
2255
|
+
background: transparent;
|
|
2256
|
+
border: none;
|
|
2257
|
+
color: var(--accent-channel);
|
|
2258
|
+
cursor: pointer;
|
|
2259
|
+
font-size: 12px;
|
|
2260
|
+
font-weight: 600;
|
|
2261
|
+
padding: 0;
|
|
2262
|
+
white-space: nowrap;
|
|
2263
|
+
}
|
|
2264
|
+
|
|
2265
|
+
.fb-toast-close {
|
|
2266
|
+
background: transparent;
|
|
2267
|
+
border: none;
|
|
2268
|
+
color: var(--text-muted);
|
|
2269
|
+
cursor: pointer;
|
|
2270
|
+
font-size: 11px;
|
|
2271
|
+
padding: 0 0 0 var(--space-2);
|
|
2272
|
+
}
|
|
2273
|
+
|
|
2274
|
+
|
|
2275
|
+
.fb-popover-note {
|
|
2276
|
+
font-size: 11px;
|
|
2277
|
+
line-height: 1.35;
|
|
2278
|
+
margin: var(--space-2) 0 0;
|
|
2279
|
+
}
|
|
2280
|
+
|
|
2281
|
+
/* -- menus --------------------------------------------------------------- */
|
|
2282
|
+
/*
|
|
2283
|
+
Appended to <body> and positioned fixed, because a menu inside the topbar is
|
|
2284
|
+
clipped by it. */
|
|
2285
|
+
|
|
2286
|
+
/*
|
|
2287
|
+
Mounted on <body>, so it is the ONE part of this plugin's chrome that is
|
|
2288
|
+
outside .fb-workspace and cannot inherit the light values set there. It
|
|
2289
|
+
carries its own copy: without them a menu on the figure page would be core's
|
|
2290
|
+
near-white text on this card's white background, which is a menu you cannot
|
|
2291
|
+
read. Only figure pages build one, so there is nothing else to disturb. */
|
|
2292
|
+
.fb-menu {
|
|
2293
|
+
animation: fb-pop-in 110ms ease-out;
|
|
2294
|
+
transform-origin: top left;
|
|
2295
|
+
|
|
2296
|
+
--surface-1: #ffffff;
|
|
2297
|
+
--surface-3: rgba(15, 23, 42, 0.06);
|
|
2298
|
+
--border-subtle: #e4e7ee;
|
|
2299
|
+
--text-primary: #16202e;
|
|
2300
|
+
--text-muted: #78839a;
|
|
2301
|
+
--accent-danger: #d64545;
|
|
2302
|
+
|
|
2303
|
+
background: var(--surface-1);
|
|
2304
|
+
border: 1px solid var(--border-subtle);
|
|
2305
|
+
border-radius: 10px;
|
|
2306
|
+
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.08),
|
|
2307
|
+
0 10px 30px rgba(15, 23, 42, 0.16);
|
|
2308
|
+
display: flex;
|
|
2309
|
+
flex-direction: column;
|
|
2310
|
+
/* The project list under "Add panels" is as long as the server's project
|
|
2311
|
+
list, which is not a number this page gets to choose. Bounded and
|
|
2312
|
+
scrolled, rather than left to run off the bottom of the window. */
|
|
2313
|
+
max-height: min(70vh, 420px);
|
|
2314
|
+
min-width: 170px;
|
|
2315
|
+
overflow-y: auto;
|
|
2316
|
+
padding: 5px;
|
|
2317
|
+
position: fixed;
|
|
2318
|
+
z-index: 9000;
|
|
2319
|
+
}
|
|
2320
|
+
|
|
2321
|
+
/* Three columns: the icon in a fixed gutter on the left, the label taking
|
|
2322
|
+
whatever is left, the shortcut ranged right against the far edge. */
|
|
2323
|
+
.fb-menu-item {
|
|
2324
|
+
align-items: center;
|
|
2325
|
+
background: transparent;
|
|
2326
|
+
border: none;
|
|
2327
|
+
border-radius: 6px;
|
|
2328
|
+
color: var(--text-primary);
|
|
2329
|
+
cursor: pointer;
|
|
2330
|
+
display: flex;
|
|
2331
|
+
font-size: 13px;
|
|
2332
|
+
gap: var(--space-2);
|
|
2333
|
+
padding: 6px var(--space-2);
|
|
2334
|
+
text-align: left;
|
|
2335
|
+
transition: background-color 100ms ease;
|
|
2336
|
+
white-space: nowrap;
|
|
2337
|
+
width: 100%;
|
|
2338
|
+
}
|
|
2339
|
+
|
|
2340
|
+
/* A fixed gutter, present whether or not the row has an icon: a menu where only
|
|
2341
|
+
some rows are indented has a ragged left edge, and the eye reads the ragged
|
|
2342
|
+
edge before it reads any of the words. Menus where NO row has an icon do not
|
|
2343
|
+
draw it at all -- see FigureContextMenu.open. */
|
|
2344
|
+
.fb-menu-icon {
|
|
2345
|
+
display: inline-block;
|
|
2346
|
+
flex: 0 0 auto;
|
|
2347
|
+
font-size: 11px;
|
|
2348
|
+
opacity: 0.7;
|
|
2349
|
+
text-align: center;
|
|
2350
|
+
width: 14px;
|
|
2351
|
+
}
|
|
2352
|
+
|
|
2353
|
+
/* Takes the slack, which is what pushes the shortcut to the right edge --
|
|
2354
|
+
`margin-left: auto` on the key alone would work too, but this way the label
|
|
2355
|
+
also gets somewhere to be truncated if a menu is ever constrained. */
|
|
2356
|
+
.fb-menu-text {
|
|
2357
|
+
flex: 1 1 auto;
|
|
2358
|
+
overflow: hidden;
|
|
2359
|
+
text-overflow: ellipsis;
|
|
2360
|
+
}
|
|
2361
|
+
|
|
2362
|
+
/* The key. Right-aligned and set back a step in the hierarchy: it is a reminder
|
|
2363
|
+
for the next time rather than part of the row's meaning, and at the label's
|
|
2364
|
+
own weight it competed with the thing it annotates. The left padding is a
|
|
2365
|
+
minimum gap, so a long label and a long key never touch. */
|
|
2366
|
+
.fb-menu-key {
|
|
2367
|
+
color: var(--text-muted);
|
|
2368
|
+
flex: 0 0 auto;
|
|
2369
|
+
font-size: 11px;
|
|
2370
|
+
font-variant-numeric: tabular-nums;
|
|
2371
|
+
letter-spacing: 0.02em;
|
|
2372
|
+
padding-left: var(--space-4, 16px);
|
|
2373
|
+
}
|
|
2374
|
+
|
|
2375
|
+
.fb-menu-item:hover:not(:disabled) {
|
|
2376
|
+
background: var(--surface-3, rgba(255, 255, 255, 0.08));
|
|
2377
|
+
}
|
|
2378
|
+
|
|
2379
|
+
/* Dimmed WITH the row. A key at full muted-grey next to a greyed-out label
|
|
2380
|
+
reads as the brighter of the two, which points the eye at the one row in the
|
|
2381
|
+
menu that cannot be used. */
|
|
2382
|
+
.fb-menu-item:disabled .fb-menu-key,
|
|
2383
|
+
.fb-menu-item:disabled .fb-menu-icon {
|
|
2384
|
+
opacity: 0.45;
|
|
2385
|
+
}
|
|
2386
|
+
/* The destructive one, coloured rather than merely last: "Delete from figure"
|
|
2387
|
+
destroys a captured scene, and it sits two rows below "Remove from page",
|
|
2388
|
+
which does not. */
|
|
2389
|
+
.fb-menu-item.is-danger {
|
|
2390
|
+
color: var(--accent-danger);
|
|
2391
|
+
}
|
|
2392
|
+
|
|
2393
|
+
|
|
2394
|
+
.fb-menu-item:disabled {
|
|
2395
|
+
color: var(--text-muted);
|
|
2396
|
+
cursor: default;
|
|
2397
|
+
}
|
|
2398
|
+
|
|
2399
|
+
.fb-menu-note {
|
|
2400
|
+
color: var(--text-muted);
|
|
2401
|
+
font-size: 11px;
|
|
2402
|
+
overflow: hidden;
|
|
2403
|
+
padding: var(--space-2);
|
|
2404
|
+
text-overflow: ellipsis;
|
|
2405
|
+
}
|
|
2406
|
+
/* Always the same width, ticked or not, so the labels line up and the menu
|
|
2407
|
+
does not shuffle sideways as things are turned on. */
|
|
2408
|
+
.fb-menu-tick {
|
|
2409
|
+
display: inline-block;
|
|
2410
|
+
text-align: center;
|
|
2411
|
+
width: 14px;
|
|
2412
|
+
}
|
|
2413
|
+
|
|
2414
|
+
|
|
2415
|
+
.fb-menu-separator {
|
|
2416
|
+
background: var(--border-subtle);
|
|
2417
|
+
height: 1px;
|
|
2418
|
+
margin: 4px 0;
|
|
2419
|
+
}
|
|
2420
|
+
|
|
2421
|
+
/* -- small form parts ---------------------------------------------------- */
|
|
2422
|
+
|
|
2423
|
+
.fb-input {
|
|
2424
|
+
background: var(--surface-2);
|
|
2425
|
+
border: 1px solid var(--border-subtle);
|
|
2426
|
+
border-radius: var(--radius-sm);
|
|
2427
|
+
color: var(--text-primary);
|
|
2428
|
+
font-size: 13px;
|
|
2429
|
+
padding: 5px var(--space-2);
|
|
2430
|
+
width: 100%;
|
|
2431
|
+
}
|
|
2432
|
+
|
|
2433
|
+
.fb-input-quiet {
|
|
2434
|
+
background: transparent;
|
|
2435
|
+
border-color: transparent;
|
|
2436
|
+
font-size: 12px;
|
|
2437
|
+
padding: 3px 0;
|
|
2438
|
+
}
|
|
2439
|
+
|
|
2440
|
+
.fb-input-tiny {
|
|
2441
|
+
width: 58px;
|
|
2442
|
+
}
|
|
2443
|
+
|
|
2444
|
+
.fb-figure-row {
|
|
2445
|
+
align-items: center;
|
|
2446
|
+
display: flex;
|
|
2447
|
+
gap: var(--space-2);
|
|
2448
|
+
}
|
|
2449
|
+
|
|
2450
|
+
.fb-check {
|
|
2451
|
+
align-items: center;
|
|
2452
|
+
color: var(--text-secondary);
|
|
2453
|
+
display: flex;
|
|
2454
|
+
font-size: 12px;
|
|
2455
|
+
gap: var(--space-2);
|
|
2456
|
+
margin: var(--space-2) 0;
|
|
2457
|
+
}
|
|
2458
|
+
|
|
2459
|
+
/* -- dialogs ----------------------------------------------------------- */
|
|
2460
|
+
/*
|
|
2461
|
+
There is no sidebar panel to style any more: Figure Builder declares no
|
|
2462
|
+
tool_panel_slot, and everything it shows is either on the image (the dock,
|
|
2463
|
+
the viewfinder and the capture boxes, further down) or on the figure canvas.
|
|
2464
|
+
The rules that used to be here -- .fb-where, .fb-summary, .fb-edit-session
|
|
2465
|
+
and the rest -- went with the panel they described. */
|
|
2466
|
+
|
|
2467
|
+
/* A native <dialog>: modal, focus-trapped and Esc-dismissible without a line of
|
|
2468
|
+
script, and it cannot end up behind the canvas the way a positioned div can.
|
|
2469
|
+
The backdrop is the browser's; only the box itself is styled here. */
|
|
2470
|
+
.fb-dialog {
|
|
2471
|
+
background: var(--surface-1);
|
|
2472
|
+
border: 1px solid var(--border-subtle);
|
|
2473
|
+
border-radius: var(--radius-lg);
|
|
2474
|
+
box-shadow: var(--shadow-lg, 0 8px 32px rgba(0, 0, 0, 0.45));
|
|
2475
|
+
color: var(--text-primary);
|
|
2476
|
+
max-width: 460px;
|
|
2477
|
+
padding: var(--space-4);
|
|
2478
|
+
width: 90vw;
|
|
2479
|
+
}
|
|
2480
|
+
|
|
2481
|
+
.fb-dialog h2 {
|
|
2482
|
+
font-size: 17px;
|
|
2483
|
+
font-weight: 700;
|
|
2484
|
+
margin: 0 0 var(--space-2);
|
|
2485
|
+
}
|
|
2486
|
+
|
|
2487
|
+
.fb-dialog .fb-select {
|
|
2488
|
+
max-width: none;
|
|
2489
|
+
width: 100%;
|
|
2490
|
+
}
|
|
2491
|
+
|
|
2492
|
+
.fb-dialog::backdrop {
|
|
2493
|
+
background: rgba(5, 7, 10, 0.6);
|
|
2494
|
+
}
|
|
2495
|
+
|
|
2496
|
+
/* The same shape as core's .control-label, written out here rather than
|
|
2497
|
+
borrowed. The export dialog is Figure Builder's, and viewer.css is linked on
|
|
2498
|
+
this page for the channel widget alone -- two labels reaching into it for
|
|
2499
|
+
their type size was the kind of dependency that gets noticed the day
|
|
2500
|
+
somebody changes it for the viewer. */
|
|
2501
|
+
.fb-field-label {
|
|
2502
|
+
color: var(--text-muted);
|
|
2503
|
+
display: block;
|
|
2504
|
+
font-size: 11px;
|
|
2505
|
+
font-weight: 700;
|
|
2506
|
+
margin-bottom: 6px;
|
|
2507
|
+
text-transform: uppercase;
|
|
2508
|
+
}
|
|
2509
|
+
|
|
2510
|
+
.fb-dialog .fb-field-label:not(:first-of-type) {
|
|
2511
|
+
margin-top: var(--space-3);
|
|
2512
|
+
}
|
|
2513
|
+
|
|
2514
|
+
.fb-export-warnings {
|
|
2515
|
+
margin: var(--space-3) 0;
|
|
2516
|
+
}
|
|
2517
|
+
|
|
2518
|
+
.fb-export-warnings .fb-banner {
|
|
2519
|
+
margin-bottom: var(--space-2);
|
|
2520
|
+
}
|
|
2521
|
+
|
|
2522
|
+
.fb-dialog-actions {
|
|
2523
|
+
display: flex;
|
|
2524
|
+
flex-wrap: wrap;
|
|
2525
|
+
gap: var(--space-2);
|
|
2526
|
+
margin-top: var(--space-3);
|
|
2527
|
+
}
|
|
2528
|
+
|
|
2529
|
+
.fb-dialog-footer {
|
|
2530
|
+
border-top: 1px solid var(--border-subtle);
|
|
2531
|
+
padding-top: var(--space-3);
|
|
2532
|
+
}
|
|
2533
|
+
|
|
2534
|
+
/* -- asking a question --------------------------------------------------- */
|
|
2535
|
+
/*
|
|
2536
|
+
Narrower than the export sheet, because a question is read in one glance and
|
|
2537
|
+
a line of text 460px wide is not. FigureConfirm builds these; the reasoning
|
|
2538
|
+
for replacing `window.confirm` is in its docstring.
|
|
2539
|
+
|
|
2540
|
+
Ranged right, and the answers in order of consequence from left to right --
|
|
2541
|
+
Cancel first, the destructive one last and furthest from where the pointer
|
|
2542
|
+
already is. */
|
|
2543
|
+
.fb-confirm {
|
|
2544
|
+
max-width: 400px;
|
|
2545
|
+
}
|
|
2546
|
+
|
|
2547
|
+
.fb-confirm-body {
|
|
2548
|
+
color: var(--text-secondary);
|
|
2549
|
+
font-size: 13px;
|
|
2550
|
+
line-height: 1.5;
|
|
2551
|
+
margin: 0 0 var(--space-2);
|
|
2552
|
+
}
|
|
2553
|
+
|
|
2554
|
+
.fb-confirm-actions {
|
|
2555
|
+
justify-content: flex-end;
|
|
2556
|
+
margin-top: var(--space-4);
|
|
2557
|
+
}
|
|
2558
|
+
|
|
2559
|
+
/* Red, and filled like the primary button rather than merely tinted: this is
|
|
2560
|
+
the button that ends something, and it has to be told apart from the one
|
|
2561
|
+
beside it before it is pressed, not after. */
|
|
2562
|
+
.fb-button-danger {
|
|
2563
|
+
background: var(--accent-danger);
|
|
2564
|
+
border-color: transparent;
|
|
2565
|
+
color: #ffffff;
|
|
2566
|
+
font-weight: 600;
|
|
2567
|
+
padding: 0 var(--space-4);
|
|
2568
|
+
}
|
|
2569
|
+
|
|
2570
|
+
.fb-button-danger:hover {
|
|
2571
|
+
filter: brightness(1.08);
|
|
2572
|
+
}
|
|
2573
|
+
|
|
2574
|
+
/* -- the capture dock, on the image ------------------------------------ */
|
|
2575
|
+
/*
|
|
2576
|
+
Everything from here down is drawn OVER the viewer, so it takes its colours
|
|
2577
|
+
from literal translucent darks rather than from the surface tokens -- the
|
|
2578
|
+
same choice core makes for .viewer-channel-legend and the overview lens. A
|
|
2579
|
+
surface token is a colour for a panel with a known background; over tissue
|
|
2580
|
+
that happens to be bright, the light-theme value of one is unreadable.
|
|
2581
|
+
|
|
2582
|
+
Top-right because core's own furniture is elsewhere: project label top-left,
|
|
2583
|
+
channel legend bottom-right, overview lens bottom-left. */
|
|
2584
|
+
|
|
2585
|
+
.fb-dock {
|
|
2586
|
+
align-items: flex-end;
|
|
2587
|
+
display: flex;
|
|
2588
|
+
flex-direction: column;
|
|
2589
|
+
gap: 8px;
|
|
2590
|
+
/* The floor, for the moment before the first measurement. The real cap is
|
|
2591
|
+
an inline max-height from FigureCaptureDock.layout(), which stops the
|
|
2592
|
+
dock above core's channel legend -- a distance no stylesheet can know,
|
|
2593
|
+
since the legend's height is however many channels are on. */
|
|
2594
|
+
max-height: calc(100% - 24px);
|
|
2595
|
+
/* The dock's own box is only as wide as its widest child, but its padding
|
|
2596
|
+
and gaps are still surface: passing pointer events through them keeps a
|
|
2597
|
+
drag that starts in the gap on the image, where it was aimed. */
|
|
2598
|
+
pointer-events: none;
|
|
2599
|
+
position: absolute;
|
|
2600
|
+
right: 12px;
|
|
2601
|
+
top: 12px;
|
|
2602
|
+
z-index: 240;
|
|
2603
|
+
}
|
|
2604
|
+
|
|
2605
|
+
/* Capturing is off while a panel's view is on loan to the live viewer, so the
|
|
2606
|
+
control that arms it says so rather than silently refusing. The dock itself
|
|
2607
|
+
stays: it is carrying the session, and the way out of the edit is in it. */
|
|
2608
|
+
.fb-dock.is-editing .fb-orb {
|
|
2609
|
+
opacity: 0.35;
|
|
2610
|
+
pointer-events: none;
|
|
2611
|
+
}
|
|
2612
|
+
|
|
2613
|
+
.fb-dock-head {
|
|
2614
|
+
align-items: center;
|
|
2615
|
+
display: flex;
|
|
2616
|
+
/* The head and the error never give up height to the strip below them: one
|
|
2617
|
+
is what arms the mode and the other is what says why it failed. */
|
|
2618
|
+
flex: 0 0 auto;
|
|
2619
|
+
gap: 8px;
|
|
2620
|
+
pointer-events: auto;
|
|
2621
|
+
}
|
|
2622
|
+
|
|
2623
|
+
/* The instructions, and the shortcut when there are none to give. Beside the
|
|
2624
|
+
orb rather than under it, so it never covers the strip and never grows the
|
|
2625
|
+
column the user is trying to see past. */
|
|
2626
|
+
.fb-dock-caption {
|
|
2627
|
+
backdrop-filter: blur(6px);
|
|
2628
|
+
/* Text, not a control. At its full width it covers 420px of image, and a
|
|
2629
|
+
drag that started on it would hit a tooltip instead of the picture. */
|
|
2630
|
+
pointer-events: none;
|
|
2631
|
+
background: rgba(8, 12, 18, 0.72);
|
|
2632
|
+
border: 1px solid rgba(148, 163, 184, 0.28);
|
|
2633
|
+
border-radius: 999px;
|
|
2634
|
+
color: rgba(226, 232, 240, 0.92);
|
|
2635
|
+
font-size: 11px;
|
|
2636
|
+
line-height: 1.6;
|
|
2637
|
+
max-width: min(46vw, 420px);
|
|
2638
|
+
padding: 5px 12px;
|
|
2639
|
+
}
|
|
2640
|
+
|
|
2641
|
+
.fb-dock-caption kbd {
|
|
2642
|
+
background: rgba(148, 163, 184, 0.18);
|
|
2643
|
+
border: 1px solid rgba(148, 163, 184, 0.35);
|
|
2644
|
+
border-radius: 3px;
|
|
2645
|
+
font-size: 10px;
|
|
2646
|
+
padding: 0 4px;
|
|
2647
|
+
}
|
|
2648
|
+
|
|
2649
|
+
/* The one control that matters, and the only one that is a circle: it is the
|
|
2650
|
+
mode toggle, and a mode toggle has to be findable without reading. The gloss
|
|
2651
|
+
is two layers -- a highlight where the light would be and a vertical body --
|
|
2652
|
+
because a flat disc over a photograph reads as part of the photograph. */
|
|
2653
|
+
.fb-orb {
|
|
2654
|
+
align-items: center;
|
|
2655
|
+
backdrop-filter: blur(6px);
|
|
2656
|
+
background:
|
|
2657
|
+
radial-gradient(120% 120% at 30% 22%,
|
|
2658
|
+
rgba(255, 255, 255, 0.30) 0%,
|
|
2659
|
+
rgba(255, 255, 255, 0.07) 42%,
|
|
2660
|
+
rgba(255, 255, 255, 0) 62%),
|
|
2661
|
+
linear-gradient(180deg, rgba(30, 41, 59, 0.94), rgba(11, 17, 28, 0.96));
|
|
2662
|
+
border: 1px solid rgba(148, 163, 184, 0.38);
|
|
2663
|
+
border-radius: 50%;
|
|
2664
|
+
box-shadow: 0 8px 22px rgba(0, 0, 0, 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.22);
|
|
2665
|
+
color: rgba(226, 232, 240, 0.95);
|
|
2666
|
+
cursor: pointer;
|
|
2667
|
+
display: inline-flex;
|
|
2668
|
+
font-size: 18px;
|
|
2669
|
+
height: 54px;
|
|
2670
|
+
justify-content: center;
|
|
2671
|
+
transition: transform var(--duration-fast, 120ms) ease,
|
|
2672
|
+
box-shadow var(--duration-fast, 120ms) ease;
|
|
2673
|
+
width: 54px;
|
|
2674
|
+
}
|
|
2675
|
+
|
|
2676
|
+
.fb-orb:hover {
|
|
2677
|
+
transform: translateY(-1px);
|
|
2678
|
+
}
|
|
2679
|
+
|
|
2680
|
+
.fb-orb:active {
|
|
2681
|
+
transform: translateY(1px);
|
|
2682
|
+
}
|
|
2683
|
+
|
|
2684
|
+
/* Armed is a state the user has to be able to read from the corner of the eye:
|
|
2685
|
+
while it is on, a drag on the image does something different from what it
|
|
2686
|
+
does the rest of the time. */
|
|
2687
|
+
.fb-dock.is-armed .fb-orb {
|
|
2688
|
+
background:
|
|
2689
|
+
radial-gradient(120% 120% at 30% 22%,
|
|
2690
|
+
rgba(255, 255, 255, 0.42) 0%,
|
|
2691
|
+
rgba(255, 255, 255, 0.12) 42%,
|
|
2692
|
+
rgba(255, 255, 255, 0) 62%),
|
|
2693
|
+
linear-gradient(180deg, rgba(56, 189, 248, 0.95), rgba(14, 116, 178, 0.96));
|
|
2694
|
+
border-color: rgba(186, 230, 253, 0.9);
|
|
2695
|
+
box-shadow: 0 0 0 5px rgba(56, 189, 248, 0.18),
|
|
2696
|
+
0 8px 22px rgba(0, 0, 0, 0.5),
|
|
2697
|
+
inset 0 1px 0 rgba(255, 255, 255, 0.35);
|
|
2698
|
+
color: #04121f;
|
|
2699
|
+
}
|
|
2700
|
+
|
|
2701
|
+
/* The way out. Figure Builder has no card in the sidebar, so it has no X
|
|
2702
|
+
there either -- this one and the canvas's own remove the tool outright
|
|
2703
|
+
rather than folding it away. Small and beside the orb, never near it in
|
|
2704
|
+
weight: leaving the mode and leaving the tool are one keystroke apart and
|
|
2705
|
+
must not look alike. */
|
|
2706
|
+
.fb-dock-close {
|
|
2707
|
+
align-items: center;
|
|
2708
|
+
background: rgba(8, 12, 18, 0.7);
|
|
2709
|
+
border: 1px solid rgba(148, 163, 184, 0.3);
|
|
2710
|
+
border-radius: 50%;
|
|
2711
|
+
color: rgba(226, 232, 240, 0.85);
|
|
2712
|
+
cursor: pointer;
|
|
2713
|
+
display: inline-flex;
|
|
2714
|
+
font-size: 11px;
|
|
2715
|
+
height: 24px;
|
|
2716
|
+
justify-content: center;
|
|
2717
|
+
pointer-events: auto;
|
|
2718
|
+
width: 24px;
|
|
2719
|
+
}
|
|
2720
|
+
|
|
2721
|
+
.fb-dock-close:hover {
|
|
2722
|
+
background: rgba(220, 80, 80, 0.85);
|
|
2723
|
+
border-color: rgba(255, 190, 190, 0.7);
|
|
2724
|
+
color: #fff;
|
|
2725
|
+
}
|
|
2726
|
+
|
|
2727
|
+
/* A failure the user has to be able to see without a sidebar to put it in. */
|
|
2728
|
+
.fb-dock-error {
|
|
2729
|
+
background: rgba(120, 30, 30, 0.85);
|
|
2730
|
+
border: 1px solid rgba(255, 150, 150, 0.5);
|
|
2731
|
+
border-radius: 6px;
|
|
2732
|
+
color: #ffe8e8;
|
|
2733
|
+
flex: 0 0 auto;
|
|
2734
|
+
font-size: 11px;
|
|
2735
|
+
margin: 0;
|
|
2736
|
+
max-width: 260px;
|
|
2737
|
+
padding: 6px 9px;
|
|
2738
|
+
pointer-events: auto;
|
|
2739
|
+
}
|
|
2740
|
+
|
|
2741
|
+
|
|
2742
|
+
/* -- the session strip -------------------------------------------------- */
|
|
2743
|
+
|
|
2744
|
+
.fb-strip {
|
|
2745
|
+
backdrop-filter: blur(9px);
|
|
2746
|
+
background: rgba(8, 12, 18, 0.58);
|
|
2747
|
+
border: 1px solid rgba(148, 163, 184, 0.24);
|
|
2748
|
+
border-radius: 10px;
|
|
2749
|
+
color: rgba(226, 232, 240, 0.92);
|
|
2750
|
+
display: flex;
|
|
2751
|
+
flex-direction: column;
|
|
2752
|
+
min-height: 0;
|
|
2753
|
+
overflow: hidden;
|
|
2754
|
+
pointer-events: auto;
|
|
2755
|
+
width: 158px;
|
|
2756
|
+
}
|
|
2757
|
+
|
|
2758
|
+
.fb-strip-toggle {
|
|
2759
|
+
align-items: center;
|
|
2760
|
+
background: none;
|
|
2761
|
+
border: none;
|
|
2762
|
+
color: inherit;
|
|
2763
|
+
cursor: pointer;
|
|
2764
|
+
display: flex;
|
|
2765
|
+
font-size: 11px;
|
|
2766
|
+
font-weight: 600;
|
|
2767
|
+
gap: 6px;
|
|
2768
|
+
letter-spacing: 0.03em;
|
|
2769
|
+
padding: 8px 10px;
|
|
2770
|
+
text-transform: uppercase;
|
|
2771
|
+
width: 100%;
|
|
2772
|
+
}
|
|
2773
|
+
|
|
2774
|
+
.fb-strip-title {
|
|
2775
|
+
flex: 1;
|
|
2776
|
+
text-align: left;
|
|
2777
|
+
}
|
|
2778
|
+
|
|
2779
|
+
.fb-strip-count {
|
|
2780
|
+
background: rgba(148, 163, 184, 0.22);
|
|
2781
|
+
border-radius: 999px;
|
|
2782
|
+
font-size: 10px;
|
|
2783
|
+
min-width: 18px;
|
|
2784
|
+
padding: 1px 6px;
|
|
2785
|
+
text-align: center;
|
|
2786
|
+
}
|
|
2787
|
+
|
|
2788
|
+
.fb-strip-chevron {
|
|
2789
|
+
transition: transform var(--duration-fast, 120ms) ease;
|
|
2790
|
+
}
|
|
2791
|
+
|
|
2792
|
+
.fb-dock.is-collapsed .fb-strip-chevron {
|
|
2793
|
+
transform: rotate(-90deg);
|
|
2794
|
+
}
|
|
2795
|
+
|
|
2796
|
+
/* Collapsed leaves the header, not nothing: a strip that vanished would take
|
|
2797
|
+
the count with it, and the count is the reason to open it again. */
|
|
2798
|
+
.fb-dock.is-collapsed .fb-strip-body {
|
|
2799
|
+
display: none;
|
|
2800
|
+
}
|
|
2801
|
+
|
|
2802
|
+
.fb-strip-body {
|
|
2803
|
+
display: flex;
|
|
2804
|
+
flex-direction: column;
|
|
2805
|
+
/* Both of these are load-bearing for the scroll below: without min-height a
|
|
2806
|
+
flex child refuses to shrink below its content, and without overflow the
|
|
2807
|
+
shrunk box paints outside itself anyway. */
|
|
2808
|
+
min-height: 0;
|
|
2809
|
+
overflow: hidden;
|
|
2810
|
+
}
|
|
2811
|
+
|
|
2812
|
+
/* The list scrolls; the thumbnails keep their size.
|
|
2813
|
+
|
|
2814
|
+
`flex: 0 0 auto` on the items is the whole of it. A flex column shrinks its
|
|
2815
|
+
children before it lets itself overflow, so without it a twentieth capture
|
|
2816
|
+
squeezed the other nineteen into slivers -- and a sliver of one crop of
|
|
2817
|
+
tissue looks exactly like a sliver of another.
|
|
2818
|
+
|
|
2819
|
+
The height is whatever the dock has left rather than a fixed slice of the
|
|
2820
|
+
window: `.fb-dock` is already capped at the viewer's height, and a second cap
|
|
2821
|
+
here would scroll a list that still had room in a short window. */
|
|
2822
|
+
.fb-strip-items {
|
|
2823
|
+
display: flex;
|
|
2824
|
+
flex: 1 1 auto;
|
|
2825
|
+
flex-direction: column;
|
|
2826
|
+
gap: 6px;
|
|
2827
|
+
min-height: 0;
|
|
2828
|
+
overflow-y: auto;
|
|
2829
|
+
padding: 0 8px 8px;
|
|
2830
|
+
scrollbar-width: thin;
|
|
2831
|
+
}
|
|
2832
|
+
|
|
2833
|
+
.fb-strip-item {
|
|
2834
|
+
border: 1px solid rgba(148, 163, 184, 0.26);
|
|
2835
|
+
border-radius: 4px;
|
|
2836
|
+
flex: 0 0 auto;
|
|
2837
|
+
margin: 0;
|
|
2838
|
+
overflow: hidden;
|
|
2839
|
+
position: relative;
|
|
2840
|
+
}
|
|
2841
|
+
|
|
2842
|
+
.fb-strip-item img,
|
|
2843
|
+
.fb-strip-pending {
|
|
2844
|
+
aspect-ratio: 4 / 3;
|
|
2845
|
+
display: block;
|
|
2846
|
+
object-fit: cover;
|
|
2847
|
+
width: 100%;
|
|
2848
|
+
}
|
|
2849
|
+
|
|
2850
|
+
.fb-strip-pending {
|
|
2851
|
+
background: repeating-linear-gradient(45deg,
|
|
2852
|
+
rgba(148, 163, 184, 0.18) 0 6px, rgba(148, 163, 184, 0.08) 6px 12px);
|
|
2853
|
+
}
|
|
2854
|
+
|
|
2855
|
+
/* A capture that is not in a figure yet is marked rather than hidden: it is
|
|
2856
|
+
memory, and memory does not survive the next navigation. */
|
|
2857
|
+
.fb-strip-item.is-pending {
|
|
2858
|
+
border-color: var(--accent-warning, #e0a44c);
|
|
2859
|
+
}
|
|
2860
|
+
|
|
2861
|
+
.fb-strip-remove {
|
|
2862
|
+
align-items: center;
|
|
2863
|
+
background: rgba(2, 6, 23, 0.72);
|
|
2864
|
+
border: none;
|
|
2865
|
+
border-radius: 50%;
|
|
2866
|
+
color: rgba(226, 232, 240, 0.95);
|
|
2867
|
+
cursor: pointer;
|
|
2868
|
+
display: inline-flex;
|
|
2869
|
+
font-size: 10px;
|
|
2870
|
+
height: 20px;
|
|
2871
|
+
justify-content: center;
|
|
2872
|
+
/* Dim rather than absent: a control that appears only on hover is one that
|
|
2873
|
+
does not exist on a touchscreen. */
|
|
2874
|
+
opacity: 0.45;
|
|
2875
|
+
position: absolute;
|
|
2876
|
+
right: 3px;
|
|
2877
|
+
top: 3px;
|
|
2878
|
+
transition: opacity var(--duration-fast, 120ms) ease;
|
|
2879
|
+
width: 20px;
|
|
2880
|
+
}
|
|
2881
|
+
|
|
2882
|
+
.fb-strip-item:hover .fb-strip-remove,
|
|
2883
|
+
.fb-strip-remove:focus-visible {
|
|
2884
|
+
opacity: 1;
|
|
2885
|
+
}
|
|
2886
|
+
|
|
2887
|
+
.fb-strip-empty {
|
|
2888
|
+
color: rgba(148, 163, 184, 0.9);
|
|
2889
|
+
font-size: 11px;
|
|
2890
|
+
margin: 0;
|
|
2891
|
+
padding: 0 10px 10px;
|
|
2892
|
+
}
|
|
2893
|
+
|
|
2894
|
+
.fb-strip-empty[hidden] {
|
|
2895
|
+
display: none;
|
|
2896
|
+
}
|
|
2897
|
+
|
|
2898
|
+
.fb-strip-foot {
|
|
2899
|
+
border-top: 1px solid rgba(148, 163, 184, 0.2);
|
|
2900
|
+
display: flex;
|
|
2901
|
+
flex: 0 0 auto;
|
|
2902
|
+
flex-direction: column;
|
|
2903
|
+
gap: 6px;
|
|
2904
|
+
padding: 8px;
|
|
2905
|
+
}
|
|
2906
|
+
|
|
2907
|
+
.fb-strip-label {
|
|
2908
|
+
color: rgba(148, 163, 184, 0.9);
|
|
2909
|
+
font-size: 10px;
|
|
2910
|
+
letter-spacing: 0.04em;
|
|
2911
|
+
text-transform: uppercase;
|
|
2912
|
+
}
|
|
2913
|
+
|
|
2914
|
+
.fb-strip-figure-row {
|
|
2915
|
+
align-items: center;
|
|
2916
|
+
display: flex;
|
|
2917
|
+
gap: 6px;
|
|
2918
|
+
}
|
|
2919
|
+
|
|
2920
|
+
.fb-strip-figure,
|
|
2921
|
+
.fb-strip-add,
|
|
2922
|
+
.fb-strip-go {
|
|
2923
|
+
background: rgba(148, 163, 184, 0.16);
|
|
2924
|
+
border: 1px solid rgba(148, 163, 184, 0.28);
|
|
2925
|
+
border-radius: var(--radius-sm, 4px);
|
|
2926
|
+
color: rgba(226, 232, 240, 0.95);
|
|
2927
|
+
cursor: pointer;
|
|
2928
|
+
font-size: 12px;
|
|
2929
|
+
padding: 5px 8px;
|
|
2930
|
+
}
|
|
2931
|
+
|
|
2932
|
+
.fb-strip-figure {
|
|
2933
|
+
flex: 1;
|
|
2934
|
+
overflow: hidden;
|
|
2935
|
+
text-align: left;
|
|
2936
|
+
text-overflow: ellipsis;
|
|
2937
|
+
white-space: nowrap;
|
|
2938
|
+
}
|
|
2939
|
+
|
|
2940
|
+
.fb-strip-figure.is-empty {
|
|
2941
|
+
color: rgba(148, 163, 184, 0.85);
|
|
2942
|
+
font-style: italic;
|
|
2943
|
+
}
|
|
2944
|
+
|
|
2945
|
+
.fb-strip-add {
|
|
2946
|
+
flex: 0 0 auto;
|
|
2947
|
+
padding: 5px 7px;
|
|
2948
|
+
}
|
|
2949
|
+
|
|
2950
|
+
.fb-strip-go {
|
|
2951
|
+
background: rgba(56, 189, 248, 0.22);
|
|
2952
|
+
border-color: rgba(56, 189, 248, 0.5);
|
|
2953
|
+
font-weight: 600;
|
|
2954
|
+
}
|
|
2955
|
+
|
|
2956
|
+
/* One hover for the three buttons in the foot. This rule lost its body once --
|
|
2957
|
+
an edit landed between the brace and the declaration, and every rule after it
|
|
2958
|
+
as far as the capture boxes became a nested one that could never match. The
|
|
2959
|
+
symptom was the status line rendering at body size, which reads as a layout
|
|
2960
|
+
choice rather than as eighty lines of dead stylesheet. */
|
|
2961
|
+
.fb-strip-figure:hover,
|
|
2962
|
+
.fb-strip-add:hover,
|
|
2963
|
+
.fb-strip-go:hover {
|
|
2964
|
+
background: rgba(56, 189, 248, 0.32);
|
|
2965
|
+
}
|
|
2966
|
+
|
|
2967
|
+
/* Which capture the strip and the image are both pointing at. */
|
|
2968
|
+
.fb-strip-item.is-selected {
|
|
2969
|
+
border-color: rgba(56, 189, 248, 0.95);
|
|
2970
|
+
box-shadow: 0 0 0 1px rgba(56, 189, 248, 0.55);
|
|
2971
|
+
}
|
|
2972
|
+
|
|
2973
|
+
/* The same number as the tag on the box out on the image. Always shown here,
|
|
2974
|
+
unlike out there: in a 158px column it is the only way to tell two crops of
|
|
2975
|
+
similar tissue apart. */
|
|
2976
|
+
.fb-strip-index {
|
|
2977
|
+
background: rgba(2, 6, 23, 0.72);
|
|
2978
|
+
border-radius: 3px;
|
|
2979
|
+
bottom: 3px;
|
|
2980
|
+
color: rgba(226, 232, 240, 0.95);
|
|
2981
|
+
font-size: 10px;
|
|
2982
|
+
left: 3px;
|
|
2983
|
+
line-height: 1.6;
|
|
2984
|
+
padding: 0 5px;
|
|
2985
|
+
position: absolute;
|
|
2986
|
+
}
|
|
2987
|
+
|
|
2988
|
+
/* Compact status, not a headline: how many panels, how many pages, and whether
|
|
2989
|
+
the last write landed. It sits below a 158px button in a corner of the image,
|
|
2990
|
+
so it is the smallest legible thing in the dock and stays grey. It is there to
|
|
2991
|
+
be glanced at and confirmed, never to be read. */
|
|
2992
|
+
.fb-strip-meta {
|
|
2993
|
+
color: rgba(148, 163, 184, 0.78);
|
|
2994
|
+
font-size: 9px;
|
|
2995
|
+
letter-spacing: 0.01em;
|
|
2996
|
+
line-height: 1.45;
|
|
2997
|
+
}
|
|
2998
|
+
|
|
2999
|
+
/* -- the borrowed-viewer session ---------------------------------------- */
|
|
3000
|
+
/*
|
|
3001
|
+
Replaces the strip rather than sitting under it. While a panel's view is
|
|
3002
|
+
loaded into the live viewer, capturing is off: a shot taken then would be a
|
|
3003
|
+
panel of somebody else's scene, and a strip still offering to take one would
|
|
3004
|
+
be an offer the tool refuses. */
|
|
3005
|
+
|
|
3006
|
+
.fb-strip[hidden],
|
|
3007
|
+
.fb-dock-edit[hidden] {
|
|
3008
|
+
display: none;
|
|
3009
|
+
}
|
|
3010
|
+
|
|
3011
|
+
.fb-dock-edit {
|
|
3012
|
+
backdrop-filter: blur(9px);
|
|
3013
|
+
background: rgba(8, 12, 18, 0.68);
|
|
3014
|
+
border: 1px solid rgba(56, 189, 248, 0.45);
|
|
3015
|
+
border-radius: 10px;
|
|
3016
|
+
color: rgba(226, 232, 240, 0.92);
|
|
3017
|
+
display: flex;
|
|
3018
|
+
flex-direction: column;
|
|
3019
|
+
gap: 6px;
|
|
3020
|
+
padding: 10px;
|
|
3021
|
+
pointer-events: auto;
|
|
3022
|
+
width: 230px;
|
|
3023
|
+
}
|
|
3024
|
+
|
|
3025
|
+
.fb-dock-edit-head {
|
|
3026
|
+
align-items: center;
|
|
3027
|
+
display: flex;
|
|
3028
|
+
font-size: 12px;
|
|
3029
|
+
font-weight: 600;
|
|
3030
|
+
gap: 6px;
|
|
3031
|
+
}
|
|
3032
|
+
|
|
3033
|
+
.fb-dock-edit-body,
|
|
3034
|
+
.fb-dock-edit-note {
|
|
3035
|
+
color: rgba(148, 163, 184, 0.95);
|
|
3036
|
+
font-size: 11px;
|
|
3037
|
+
line-height: 1.5;
|
|
3038
|
+
margin: 0;
|
|
3039
|
+
}
|
|
3040
|
+
|
|
3041
|
+
.fb-dock-edit-note {
|
|
3042
|
+
color: var(--accent-warning, #e0a44c);
|
|
3043
|
+
}
|
|
3044
|
+
|
|
3045
|
+
.fb-dock-edit-actions {
|
|
3046
|
+
display: flex;
|
|
3047
|
+
gap: 6px;
|
|
3048
|
+
}
|
|
3049
|
+
|
|
3050
|
+
.fb-dock-edit-actions .fb-strip-figure {
|
|
3051
|
+
flex: 0 0 auto;
|
|
3052
|
+
}
|
|
3053
|
+
|
|
3054
|
+
/* -- the capture boxes, on the image ------------------------------------ */
|
|
3055
|
+
/*
|
|
3056
|
+
Where each capture came from, still there. Thin, unfilled and quiet at rest:
|
|
3057
|
+
a session can leave a dozen of these on one slide, and anything with a fill
|
|
3058
|
+
or a permanent label would end up being read as part of the tissue.
|
|
3059
|
+
|
|
3060
|
+
The container clips, which the wrapper it sits in does not: a box whose left
|
|
3061
|
+
edge is on screen and whose right edge is a mile off it would otherwise be a
|
|
3062
|
+
div a mile wide, laid over whatever is beside the viewer. */
|
|
3063
|
+
|
|
3064
|
+
.fb-boxes {
|
|
3065
|
+
overflow: hidden;
|
|
3066
|
+
pointer-events: none;
|
|
3067
|
+
position: absolute;
|
|
3068
|
+
z-index: 225;
|
|
3069
|
+
}
|
|
3070
|
+
|
|
3071
|
+
/* The BOX takes no clicks -- only the bands along its edges do (below). A
|
|
3072
|
+
filled hit area would turn every captured region into a place where panning,
|
|
3073
|
+
and every other tool's drag, quietly stopped working. */
|
|
3074
|
+
.fb-box {
|
|
3075
|
+
border: 1px solid rgba(186, 230, 253, 0.42);
|
|
3076
|
+
box-shadow: 0 0 0 1px rgba(2, 6, 23, 0.35);
|
|
3077
|
+
pointer-events: none;
|
|
3078
|
+
position: absolute;
|
|
3079
|
+
transition: border-color var(--duration-fast, 120ms) ease,
|
|
3080
|
+
box-shadow var(--duration-fast, 120ms) ease;
|
|
3081
|
+
}
|
|
3082
|
+
|
|
3083
|
+
.fb-box.is-hover {
|
|
3084
|
+
border-color: rgba(186, 230, 253, 0.85);
|
|
3085
|
+
box-shadow: 0 0 0 1px rgba(2, 6, 23, 0.55);
|
|
3086
|
+
}
|
|
3087
|
+
|
|
3088
|
+
/* Exactly one at a time, and everything else stays subdued: the point of the
|
|
3089
|
+
boxes is to be readable at a glance, which a wall of equally bright
|
|
3090
|
+
rectangles is not. */
|
|
3091
|
+
.fb-box.is-selected {
|
|
3092
|
+
border-color: rgba(56, 189, 248, 0.98);
|
|
3093
|
+
border-style: solid;
|
|
3094
|
+
box-shadow: 0 0 0 1px rgba(2, 6, 23, 0.7), 0 0 12px rgba(56, 189, 248, 0.35);
|
|
3095
|
+
}
|
|
3096
|
+
|
|
3097
|
+
/* Nine pixels of edge, straddling a one-pixel outline: a 1px hit target is a
|
|
3098
|
+
target nobody hits. */
|
|
3099
|
+
.fb-box-edge {
|
|
3100
|
+
cursor: pointer;
|
|
3101
|
+
pointer-events: auto;
|
|
3102
|
+
position: absolute;
|
|
3103
|
+
}
|
|
3104
|
+
|
|
3105
|
+
.fb-box-n { height: 9px; left: -4px; right: -4px; top: -5px; }
|
|
3106
|
+
.fb-box-s { bottom: -5px; height: 9px; left: -4px; right: -4px; }
|
|
3107
|
+
.fb-box-w { bottom: -4px; left: -5px; top: -4px; width: 9px; }
|
|
3108
|
+
.fb-box-e { bottom: -4px; right: -5px; top: -4px; width: 9px; }
|
|
3109
|
+
|
|
3110
|
+
/* The number that ties a box to its thumbnail. Hidden at rest for the same
|
|
3111
|
+
reason the outline is thin -- twelve numbered rectangles over tissue is a
|
|
3112
|
+
diagram, not a photograph. */
|
|
3113
|
+
.fb-box-tag {
|
|
3114
|
+
background: rgba(2, 6, 23, 0.78);
|
|
3115
|
+
border-radius: 3px;
|
|
3116
|
+
color: rgba(226, 232, 240, 0.95);
|
|
3117
|
+
cursor: pointer;
|
|
3118
|
+
font-size: 10px;
|
|
3119
|
+
left: -1px;
|
|
3120
|
+
line-height: 1.6;
|
|
3121
|
+
opacity: 0;
|
|
3122
|
+
padding: 0 5px;
|
|
3123
|
+
pointer-events: auto;
|
|
3124
|
+
position: absolute;
|
|
3125
|
+
top: -18px;
|
|
3126
|
+
transition: opacity var(--duration-fast, 120ms) ease;
|
|
3127
|
+
}
|
|
3128
|
+
|
|
3129
|
+
.fb-box.is-hover .fb-box-tag,
|
|
3130
|
+
.fb-box.is-selected .fb-box-tag {
|
|
3131
|
+
opacity: 1;
|
|
3132
|
+
}
|
|
3133
|
+
|
|
3134
|
+
.fb-box.is-selected .fb-box-tag {
|
|
3135
|
+
background: rgba(56, 189, 248, 0.95);
|
|
3136
|
+
color: #04121f;
|
|
3137
|
+
}
|
|
3138
|
+
|
|
3139
|
+
|
|
3140
|
+
|
|
3141
|
+
/* -- the viewfinder ----------------------------------------------------- */
|
|
3142
|
+
/*
|
|
3143
|
+
A dashed light border over a dark outline, so it reads on tissue of any
|
|
3144
|
+
brightness -- one stroke alone disappears against half the images this is
|
|
3145
|
+
used on. */
|
|
3146
|
+
|
|
3147
|
+
.fb-viewfinder {
|
|
3148
|
+
border: 1px dashed rgba(186, 230, 253, 0.95);
|
|
3149
|
+
border-radius: 2px;
|
|
3150
|
+
box-shadow: 0 0 0 1px rgba(2, 6, 23, 0.6), inset 0 0 0 1px rgba(2, 6, 23, 0.45);
|
|
3151
|
+
cursor: move;
|
|
3152
|
+
position: absolute;
|
|
3153
|
+
z-index: 230;
|
|
3154
|
+
}
|
|
3155
|
+
|
|
3156
|
+
/* Mid-redraw and mid-pan the frame must not be the thing under the pointer:
|
|
3157
|
+
the drag belongs to the image in both cases. */
|
|
3158
|
+
.fb-viewfinder.is-drawing,
|
|
3159
|
+
.fb-viewfinder.is-transparent {
|
|
3160
|
+
pointer-events: none;
|
|
3161
|
+
}
|
|
3162
|
+
|
|
3163
|
+
/* Locked onto a capture: the shutter will take THAT region and not whatever
|
|
3164
|
+
the frame is over, so the frame stops being a dashed rectangle you moved and
|
|
3165
|
+
becomes the solid one already on the image -- the same sky as a selected
|
|
3166
|
+
capture box, which is what it is sitting exactly on top of. */
|
|
3167
|
+
.fb-viewfinder.is-pinned {
|
|
3168
|
+
border-color: rgba(56, 189, 248, 0.98);
|
|
3169
|
+
border-style: solid;
|
|
3170
|
+
box-shadow: 0 0 0 1px rgba(2, 6, 23, 0.7), 0 0 12px rgba(56, 189, 248, 0.35);
|
|
3171
|
+
}
|
|
3172
|
+
/* Framing a panel that is on loan to the viewer, rather than aiming a shutter.
|
|
3173
|
+
Amber instead of sky, so the two states of the same rectangle cannot be
|
|
3174
|
+
mistaken for each other: one says "the next capture takes this", and this one
|
|
3175
|
+
says "this is where the panel's edges are". */
|
|
3176
|
+
.fb-viewfinder.is-framing {
|
|
3177
|
+
border-color: rgba(224, 164, 76, 0.98);
|
|
3178
|
+
border-style: solid;
|
|
3179
|
+
box-shadow: 0 0 0 1px rgba(2, 6, 23, 0.7), 0 0 12px rgba(224, 164, 76, 0.35);
|
|
3180
|
+
}
|
|
3181
|
+
|
|
3182
|
+
|
|
3183
|
+
.fb-viewfinder-caption {
|
|
3184
|
+
background: rgba(2, 6, 23, 0.72);
|
|
3185
|
+
border-radius: 3px;
|
|
3186
|
+
color: rgba(226, 232, 240, 0.95);
|
|
3187
|
+
font-size: 11px;
|
|
3188
|
+
left: 0;
|
|
3189
|
+
padding: 1px 6px;
|
|
3190
|
+
position: absolute;
|
|
3191
|
+
top: -21px;
|
|
3192
|
+
white-space: nowrap;
|
|
3193
|
+
}
|
|
3194
|
+
|
|
3195
|
+
.fb-viewfinder.is-pinned .fb-viewfinder-caption {
|
|
3196
|
+
background: rgba(8, 47, 73, 0.86);
|
|
3197
|
+
color: rgba(224, 242, 254, 0.98);
|
|
3198
|
+
}
|
|
3199
|
+
|
|
3200
|
+
/* :not(:hover) because this rule is more specific than .fb-shutter:hover and
|
|
3201
|
+
would otherwise win against it -- leaving the one button on the frame with no
|
|
3202
|
+
hover feedback at exactly the moment it has changed what it does. */
|
|
3203
|
+
.fb-viewfinder.is-pinned .fb-shutter:not(:hover) {
|
|
3204
|
+
background: rgba(8, 47, 73, 0.88);
|
|
3205
|
+
border-color: rgba(56, 189, 248, 0.6);
|
|
3206
|
+
}
|
|
3207
|
+
|
|
3208
|
+
.fb-vf-handle {
|
|
3209
|
+
background: rgba(186, 230, 253, 0.98);
|
|
3210
|
+
border: 1px solid rgba(2, 6, 23, 0.65);
|
|
3211
|
+
border-radius: 2px;
|
|
3212
|
+
height: 12px;
|
|
3213
|
+
position: absolute;
|
|
3214
|
+
width: 12px;
|
|
3215
|
+
}
|
|
3216
|
+
|
|
3217
|
+
.fb-vf-nw { cursor: nwse-resize; left: -6px; top: -6px; }
|
|
3218
|
+
.fb-vf-ne { cursor: nesw-resize; right: -6px; top: -6px; }
|
|
3219
|
+
.fb-vf-se { cursor: nwse-resize; bottom: -6px; right: -6px; }
|
|
3220
|
+
.fb-vf-sw { cursor: nesw-resize; bottom: -6px; left: -6px; }
|
|
3221
|
+
|
|
3222
|
+
/* On the frame, not in the dock: the shutter belongs to the thing it takes a
|
|
3223
|
+
picture of. It is a <button>, so it can never end up in the preview -- the
|
|
3224
|
+
preview is a crop of the viewer's canvases, and this is not one. */
|
|
3225
|
+
.fb-shutter {
|
|
3226
|
+
align-items: center;
|
|
3227
|
+
background: rgba(2, 6, 23, 0.78);
|
|
3228
|
+
border: 1px solid rgba(148, 163, 184, 0.45);
|
|
3229
|
+
border-radius: 999px;
|
|
3230
|
+
bottom: 8px;
|
|
3231
|
+
color: #f8fafc;
|
|
3232
|
+
cursor: pointer;
|
|
3233
|
+
display: inline-flex;
|
|
3234
|
+
font-size: 12px;
|
|
3235
|
+
gap: 6px;
|
|
3236
|
+
left: 50%;
|
|
3237
|
+
padding: 5px 10px;
|
|
3238
|
+
position: absolute;
|
|
3239
|
+
transform: translateX(-50%);
|
|
3240
|
+
}
|
|
3241
|
+
|
|
3242
|
+
/* The shortcut, written on the control it fires. The dock's caption lists it
|
|
3243
|
+
too, but the caption is at the other end of the frame and this is where the
|
|
3244
|
+
hand already is -- and a camera icon on its own says nothing about a key. */
|
|
3245
|
+
.fb-shutter-key {
|
|
3246
|
+
background: rgba(148, 163, 184, 0.22);
|
|
3247
|
+
border: 1px solid rgba(148, 163, 184, 0.4);
|
|
3248
|
+
border-radius: 3px;
|
|
3249
|
+
font-family: inherit;
|
|
3250
|
+
font-size: 10px;
|
|
3251
|
+
font-weight: 600;
|
|
3252
|
+
line-height: 1.5;
|
|
3253
|
+
padding: 0 4px;
|
|
3254
|
+
}
|
|
3255
|
+
|
|
3256
|
+
.fb-shutter:hover {
|
|
3257
|
+
background: rgba(56, 189, 248, 0.92);
|
|
3258
|
+
border-color: rgba(186, 230, 253, 0.9);
|
|
3259
|
+
color: #04121f;
|
|
3260
|
+
}
|
|
3261
|
+
|
|
3262
|
+
/* The key inverts with the button, or it stays a pale chip on a pale field. */
|
|
3263
|
+
.fb-shutter:hover .fb-shutter-key {
|
|
3264
|
+
background: rgba(2, 6, 23, 0.16);
|
|
3265
|
+
border-color: rgba(2, 6, 23, 0.32);
|
|
3266
|
+
}
|
|
3267
|
+
|
|
3268
|
+
/* No shutter blink any more: a shot ends capture mode, so the frame is removed
|
|
3269
|
+
at the moment it would have flashed. What says a capture happened is the frame
|
|
3270
|
+
going and the thumbnail arriving at the top of the strip. */
|
|
3271
|
+
|
|
3272
|
+
@media (prefers-reduced-motion: reduce) {
|
|
3273
|
+
.fb-orb,
|
|
3274
|
+
.fb-strip-chevron,
|
|
3275
|
+
.fb-strip-remove,
|
|
3276
|
+
.fb-box,
|
|
3277
|
+
.fb-box-tag {
|
|
3278
|
+
transition: none;
|
|
3279
|
+
}
|
|
3280
|
+
}
|
|
3281
|
+
|
|
3282
|
+
/* For screen readers only: the orb and the small round buttons carry icons,
|
|
3283
|
+
and an icon is not a name. */
|
|
3284
|
+
.fb-visually-hidden {
|
|
3285
|
+
clip: rect(0 0 0 0);
|
|
3286
|
+
clip-path: inset(50%);
|
|
3287
|
+
height: 1px;
|
|
3288
|
+
overflow: hidden;
|
|
3289
|
+
position: absolute;
|
|
3290
|
+
white-space: nowrap;
|
|
3291
|
+
width: 1px;
|
|
3292
|
+
}
|