PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.10
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.10
1.1.10 1.1.9 1.1.8 1.1.7 1.1.6 1.1.5 1.1.4 1.1.3 1.1.2 1.1.1 1.1.0 1.0.1 1.0.0 0.9.8 0.9.7 0.9.6 0.9.4 0.9.5 0.9.3 0.9.2 0.9.1 0.9.0 0.8.9 0.8.8 0.8.7 All 34 releases
← All changes | assets/css/dock.css +414 -74 1.0.01.1.10 View file →
@@ -87,8 +87,19 @@
87 87 color: var( --os-dock-icon-color, rgba( 255, 255, 255, 0.7 ) );
88 88 transition: background-color 0.15s ease, transform 0.15s ease, color 0.15s ease;
89 89 }
90 90
91 +/*
92 + * The tile whose window is in front sits on a lit plate. Declared
93 + * before the hover rule at the same specificity so the pointer still
94 + * wins while it is over the tile. Falls back to nothing, which is
95 + * what every placement drew before the token existed.
96 + */
97 +.os-dock__item--focused .os-dock__item-primary {
98 + background-color: var( --os-dock-item-bg-focused, transparent );
99 + color: var( --os-dock-icon-color-hover, var( --os-ui-fg-on-accent, #fff ) );
100 +}
101 +
91 102 .os-dock__item-primary:hover {
92 103 background-color: var(
93 104 --os-dock-item-bg-hover,
94 105 rgba( 255, 255, 255, 0.15 )
@@ -249,9 +260,9 @@
249 260 padding: 0;
250 261 border: 2px solid var( --os-dock-bg, rgba( 0, 0, 0, 0.4 ) );
251 262 border-radius: 50%;
252 263 background: var( --wp-admin-theme-color, #2271b1 );
253 - color: var( --os-ui-fg-on-accent, #fff );
264 + color: var( --os-ui-accent-ink, var( --os-ui-fg-on-accent, #fff ) );
254 265 cursor: pointer;
255 266 transition: background-color 0.15s ease, transform 0.15s ease;
256 267 z-index: 1;
257 268 }
@@ -716,34 +727,51 @@
716 727 .os-dock__item-new:hover {
717 728 transform: translateY( -50% ) scale( 1.1 );
718 729 }
719 730
720 -/* Separator — horizontal hairline between menu tiles and system
721 - * tiles. With the `__scroll` / `__pinned` split the system separator
722 - * lives at the top of `__pinned`; flex-flow handles the "system
723 - * tiles at the bottom" placement, no margin-top: auto needed. The
724 - * `--group` variant is used inline between core and plugin menu
725 - * tiles in `__scroll`; it keeps the hairline styling and sits
726 - * exactly where it's inserted. */
731 +/* Separator — the line between two groups of tiles. With the
732 + * `__scroll` / `__pinned` split the system separator lives at the top
733 + * of `__pinned`; flex-flow handles the "system tiles at the bottom"
734 + * placement, no margin-top: auto needed. The `--group` variant is used
735 + * inline between core and plugin menu tiles in `__scroll` and sits
736 + * exactly where it's inserted.
737 + *
738 + * ONE treatment for every division in the rail. The selector carries
739 + * only the base class on purpose: `--group` elements carry it too, so
740 + * both boundaries resolve here and cannot drift apart. Two weights in
741 + * one short rail read as two unrelated ideas rather than one system,
742 + * which is why the earlier loud-plus-hairline pairing is gone.
743 + *
744 + * A gradient, not a flat fill: the line is brightest where the eye
745 + * lands and gone by the time it reaches the rail's padding, so it
746 + * never terminates in a visible stub against the glass. The soft glow
747 + * around it is what keeps a 2px line from disappearing into the dock
748 + * tint; nothing is drawn on the line itself. */
727 749 .os-dock[ data-os-dock-placement="left" ]
728 750 .os-dock__separator,
729 751 .os-dock[ data-os-dock-placement="right" ]
730 752 .os-dock__separator {
753 + position: relative;
731 754 width: 60%;
732 - height: 1px;
733 - margin: 8px auto 4px;
734 - background: var( --os-dock-border );
755 + height: 2px;
756 + margin: 12px auto;
757 + border-radius: 2px;
758 + background: linear-gradient(
759 + to right,
760 + transparent 0%,
761 + var( --os-dock-divider, rgba( 217, 46, 227, 0.7 ) ) 28%,
762 + var( --os-dock-divider, rgba( 217, 46, 227, 0.7 ) ) 72%,
763 + transparent 100%
764 + );
765 + box-shadow: 0 0 10px
766 + color-mix(
767 + in srgb,
768 + var( --os-dock-divider, rgba( 217, 46, 227, 0.7 ) ) 45%,
769 + transparent
770 + );
735 771 flex-shrink: 0;
736 772 }
737 773
738 -.os-dock[ data-os-dock-placement="left" ]
739 - .os-dock__separator--group,
740 -.os-dock[ data-os-dock-placement="right" ]
741 - .os-dock__separator--group {
742 - margin: 10px auto;
743 - background: rgba( 255, 255, 255, 0.22 );
744 -}
745 -
746 774 /*
747 775 * Hide the system separator when nothing precedes it — on a clean
748 776 * install (or a low-cap user with zero menu items) the separator
749 777 * would render as a stray hairline with nothing to divide.
@@ -763,8 +791,9 @@
763 791 .os-dock__separator {
764 792 display: none;
765 793 }
766 794
795 +
767 796 /*
768 797 * Drop an empty `__scroll` from the flex flow entirely. When only
769 798 * system tiles are present (e.g. just the Recycle Bin), the empty
770 799 * wrapper would otherwise still occupy the rail: its `flex-grow`
@@ -932,32 +961,29 @@
932 961 border: 1px solid var( --os-dock-item-outline, rgba( 255, 255, 255, 0.85 ) );
933 962 }
934 963
935 964 /*
936 - * Global "Show Desktop" indicator — set on `<body>` by the dock when
937 - * every live window on the active desktop is in the minimized state.
938 - * The bottom-dock pill picks up a soft theme-color ring so the user
939 - * has a global cue that the windows haven't disappeared, they're just
940 - * minimized. ArrowUp / a wallpaper click / clicking any dock tile
941 - * brings them back; without this the empty wallpaper looks identical
942 - * to a freshly-loaded desktop with no windows open.
965 + * "Show Desktop" — every live window on the active desktop minimized.
966 + *
967 + * The dock does not change when this happens. It used to: the bottom
968 + * pill and the vertical rails each picked up a 1px inset ring, on the
969 + * reasoning that an empty wallpaper otherwise looks identical to a
970 + * desktop that never had anything on it.
971 + *
972 + * The reasoning was sound and the ring was still the wrong answer. It
973 + * outlined the whole dock — the one surface on screen that is always
974 + * present and never the subject — to say something about windows that
975 + * are not on the dock at all. Every tile that has minimized windows
976 + * already says so, precisely and locally, by swapping its solid
977 + * indicator dot for a hollow ring. That is the cue: it points at the
978 + * tiles the windows are under, in the place the user will click to get
979 + * them back.
980 + *
981 + * `body.os-show-desktop-active` is still set (see `src/dock.ts`) and
982 + * still public, so a theme or plugin that wants a global cue can paint
983 + * one. Core does not.
943 984 */
944 -body.os-show-desktop-active
945 - .os-dock[ data-os-dock-placement="bottom" ] {
946 - box-shadow:
947 - inset 0 0 0 1px var( --wp-admin-theme-color, rgba( 255, 255, 255, 0.45 ) ),
948 - inset 0 1px 0 rgba( 255, 255, 255, 0.08 ),
949 - 0 10px 32px rgba( 0, 0, 0, 0.45 );
950 -}
951 985
952 -/* Vertical docks get the same treatment along their inner edge. */
953 -body.os-show-desktop-active
954 - .os-dock[ data-os-dock-placement="left" ],
955 -body.os-show-desktop-active
956 - .os-dock[ data-os-dock-placement="right" ] {
957 - box-shadow: inset 0 0 0 1px var( --wp-admin-theme-color, rgba( 255, 255, 255, 0.45 ) );
958 -}
959 -
960 986 /* "Open another" chip — floats above the tile's top-right corner so
961 987 * it doesn't collide with neighbouring tiles in the horizontal row. */
962 988 .os-dock[ data-os-dock-placement="bottom" ]
963 989 .os-dock__item-new {
@@ -973,11 +999,21 @@
973 999
974 1000 /*
975 1001 * Bottom-dock inner wrappers — `__scroll` carries the menu tiles and
976 1002 * absorbs horizontal overflow; `__pinned` carries the system tiles and
977 - * stays anchored to the trailing edge. Padding-top on `__scroll` gives
978 - * the badge (top: -3px on the tile) room so `overflow-y: hidden`
979 - * doesn't crop it. `min-width: 0` lets the wrapper shrink below its
1003 + * stays anchored to the trailing edge.
1004 + *
1005 + * **The padding around `__scroll` is what keeps badges visible, and it
1006 + * has to be on both axes.** `overflow-x: auto` below cannot coexist
1007 + * with `overflow-y: visible`: per spec the visible axis computes to
1008 + * `auto`, so this wrapper is a scroll container in BOTH directions
1009 + * however it is authored, and anything a child hangs outside its
1010 + * padding box is clipped. A badge sits at `top: -3px; right: -3px` on
1011 + * its tile, and the LAST tile's edge is the wrapper's edge once the
1012 + * content is wide enough to scroll — which is why this showed up as
1013 + * "the badge on the last plugin looks cut off" rather than as a
1014 + * general problem. The inline padding is symmetric so the tile cluster
1015 + * stays centred in the pill. `min-width: 0` lets the wrapper shrink below its
980 1016 * content's natural width, so the dock pill's `max-width` is what
981 1017 * decides when scroll kicks in instead of the content forcing the
982 1018 * pill wider. `justify-content: center` keeps tiles balanced inside
983 1019 * the pill when content fits; when it overflows, scroll engages and
@@ -992,9 +1028,16 @@
992 1028 flex: 1 1 auto;
993 1029 min-width: 0;
994 1030 padding-top: 4px;
995 1031 padding-bottom: 6px;
1032 + padding-inline: 4px;
996 1033 overflow-x: auto;
1034 + /*
1035 + * Authored `visible`, computed `auto` — see the note above. Kept as
1036 + * the honest statement of intent: nothing here wants a vertical
1037 + * scrollbar, and `scrollbar-width: none` plus the padding is what
1038 + * makes that true in practice.
1039 + */
997 1040 overflow-y: visible;
998 1041 scrollbar-width: none;
999 1042 }
1000 1043
@@ -1013,49 +1056,46 @@
1013 1056 padding-top: 4px;
1014 1057 padding-bottom: 6px;
1015 1058 }
1016 1059
1017 -/* Separator — vertical hairline between menu tiles and system tiles.
1018 - * Lives at the leading edge of `__pinned`; flex flow handles the
1060 +/* Separator — the line between two groups of tiles. The system one
1061 + * lives at the leading edge of `__pinned` (flex flow handles the
1019 1062 * "pinned at the trailing edge" placement, no margin-inline-start:
1020 - * auto needed. The `--group` variant keeps the hairline styling for
1021 - * the inline core→plugin boundary inside `__scroll` and sits where
1022 - * inserted. */
1063 + * auto needed); the `--group` one sits inline inside `__scroll` where
1064 + * it was inserted. Both resolve through this single rule — see the
1065 + * note on the vertical placements above for why there is only one. */
1023 1066 .os-dock[ data-os-dock-placement="bottom" ]
1024 1067 .os-dock__separator {
1025 - width: 1px;
1068 + position: relative;
1069 + width: 2px;
1026 1070 /* Fixed height — `60%` collapses against the floating pill's
1027 - intrinsic height, leaving the hairline effectively invisible.
1028 - 28px matches the dock tile inner glyph area. */
1029 - height: 28px;
1071 + intrinsic height, leaving the line effectively invisible. */
1072 + height: 34px;
1030 1073 /* Symmetric horizontal margin so the divider sits centered in the
1031 1074 * inter-tile gap: the 6px flex gap on each side (`__scroll`→`__pinned`
1032 - * on the left, `__pinned`'s own gap on the right) plus an equal 6px
1075 + * on the left, `__pinned`'s own gap on the right) plus an equal
1033 1076 * margin per side balances out. An asymmetric margin here nudges the
1034 1077 * divider — and the whole menu cluster with it — off the pill's
1035 - * center. */
1036 - margin: auto 6px;
1037 - background: var( --os-dock-border );
1078 + * center. Wider than the tile gap on purpose: the clusters need to
1079 + * read as groups before the line between them means anything. */
1080 + margin: auto 14px;
1081 + border-radius: 2px;
1082 + background: linear-gradient(
1083 + to bottom,
1084 + transparent 0%,
1085 + var( --os-dock-divider, rgba( 217, 46, 227, 0.7 ) ) 28%,
1086 + var( --os-dock-divider, rgba( 217, 46, 227, 0.7 ) ) 72%,
1087 + transparent 100%
1088 + );
1089 + box-shadow: 0 0 10px
1090 + color-mix(
1091 + in srgb,
1092 + var( --os-dock-divider, rgba( 217, 46, 227, 0.7 ) ) 45%,
1093 + transparent
1094 + );
1038 1095 flex-shrink: 0;
1039 1096 }
1040 1097
1041 -/*
1042 - * Group separator — sits inline between the core cluster and the
1043 - * plugin cluster. Brighter than the menu-vs-system separator because
1044 - * it carries user meaning ("this is where your installed apps
1045 - * begin"), and flanked by extra gap so the boundary reads at a
1046 - * glance rather than disappearing into the dock tint.
1047 - */
1048 -.os-dock[ data-os-dock-placement="bottom" ]
1049 - .os-dock__separator--group {
1050 - width: 1px;
1051 - height: 60%;
1052 - margin: auto 10px;
1053 - margin-inline-start: 10px;
1054 - background: rgba( 255, 255, 255, 0.22 );
1055 - flex-shrink: 0;
1056 -}
1057 -
1058 1098 /* Tooltip — above the hovered tile. JS writes the horizontal center
1059 1099 * + a top near the tile's top edge; CSS translates the tooltip up by
1060 1100 * its own height so it clears the tile. */
1061 1101 .os-dock__tooltip--above {
@@ -1095,5 +1135,305 @@
1095 1135 }
1096 1136
1097 1137 .os-dock__item:not( .os-dock__item--system ):active {
1098 1138 cursor: grabbing;
1139 +}
1140 +
1141 +/* ------------------------------------------------------------------
1142 + * The way out — `Exit OpenStation`
1143 + *
1144 + * Every other system tile opens something you can close again. This
1145 + * one leaves the desktop, and with the admin bar hidden by default it
1146 + * is the only route back to classic admin. Drawn like its neighbours
1147 + * it read as one more launcher, which is the same "some tiles do a
1148 + * different kind of thing" confusion the single dock set out to fix,
1149 + * just moved to the other end of the rail.
1150 + *
1151 + * Three signals, none of them colour. Danger red would overstate it
1152 + * (nothing is destroyed, the session is saved and the desktop is one
1153 + * click away) and Pulse is already spent on the seam:
1154 + *
1155 + * 1. **Last, always.** `order: 1` rather than registration order —
1156 + * native-window tiles from plugins sync in after boot and would
1157 + * otherwise land behind it.
1158 + * 2. **Its own gap**, wider than the rail's 6px, so it reads as
1159 + * sitting outside the set rather than at the end of it. A second
1160 + * divider would have said this too, and would have cost the rail
1161 + * the one-structural-line rule it just earned.
1162 + * 3. **A different silhouette** — a ring rather than a plate. Shape
1163 + * survives every theme, every dock texture, greyscale and a
1164 + * colour-blind reading of the rail; a tint survives none of them
1165 + * reliably.
1166 + *
1167 + * And it leans toward the edge it leads to on hover instead of lifting
1168 + * toward the pointer, because every other tile's lift means "I will
1169 + * come to you" and this one means the opposite.
1170 + *
1171 + * Keyed on the tile id, the same idiom the Recycle Bin badge uses
1172 + * above. `os-exit` is frozen in `src/exit-openstation.ts`
1173 + * (`EXIT_OPENSTATION_TILE_ID`) and `dock-exit-tile.test.ts` pins the
1174 + * attribute this selector depends on.
1175 + * ------------------------------------------------------------------ */
1176 +
1177 +.os-dock__item[ data-system-id="os-exit" ] {
1178 + order: 1;
1179 +}
1180 +
1181 +.os-dock__item[ data-system-id="os-exit" ] .os-dock__item-primary {
1182 + border-radius: 50%;
1183 + background-color: transparent;
1184 + box-shadow: inset 0 0 0 1px var(
1185 + --os-dock-exit-ring,
1186 + var( --os-dock-border, rgba( 255, 255, 255, 0.18 ) )
1187 + );
1188 + color: var( --os-dock-exit-icon, rgba( 255, 255, 255, 0.55 ) );
1189 +}
1190 +
1191 +/*
1192 + * The gap runs along the rail's own axis, so it follows the placement:
1193 + * inline for the horizontal pill, block for the vertical pillars.
1194 + */
1195 +.os-dock[ data-os-dock-placement="bottom" ]
1196 + .os-dock__item[ data-system-id="os-exit" ] {
1197 + margin-inline-start: 10px;
1198 +}
1199 +
1200 +.os-dock[ data-os-dock-placement="left" ]
1201 + .os-dock__item[ data-system-id="os-exit" ],
1202 +.os-dock[ data-os-dock-placement="right" ]
1203 + .os-dock__item[ data-system-id="os-exit" ] {
1204 + margin-block-start: 8px;
1205 +}
1206 +
1207 +/*
1208 + * Hover: the ring closes up and the tile moves TOWARD its edge. The
1209 + * generic tile rule scales up by 1.1, so these have to out-specify it
1210 + * rather than sit alongside it.
1211 + */
1212 +.os-dock__item[ data-system-id="os-exit" ] .os-dock__item-primary:hover,
1213 +.os-dock__item[ data-system-id="os-exit" ]
1214 + .os-dock__item-primary:focus-visible {
1215 + background-color: transparent;
1216 + box-shadow: inset 0 0 0 1px var(
1217 + --os-dock-exit-ring-hover,
1218 + var( --os-dock-item-outline, rgba( 255, 255, 255, 0.7 ) )
1219 + );
1220 + color: var( --os-dock-icon-color-hover, var( --os-ui-fg-on-accent, #fff ) );
1221 +}
1222 +
1223 +.os-dock[ data-os-dock-placement="bottom" ]
1224 + .os-dock__item[ data-system-id="os-exit" ]
1225 + .os-dock__item-primary:hover {
1226 + transform: translateY( 2px );
1227 +}
1228 +
1229 +.os-dock[ data-os-dock-placement="left" ]
1230 + .os-dock__item[ data-system-id="os-exit" ]
1231 + .os-dock__item-primary:hover {
1232 + transform: translateX( -2px );
1233 +}
1234 +
1235 +.os-dock[ data-os-dock-placement="right" ]
1236 + .os-dock__item[ data-system-id="os-exit" ]
1237 + .os-dock__item-primary:hover {
1238 + transform: translateX( 2px );
1239 +}
1240 +
1241 +/* The press keeps the shared cue: every tile in the rail dips on
1242 + * :active, and this one should not feel unresponsive by comparison. */
1243 +.os-dock__item[ data-system-id="os-exit" ] .os-dock__item-primary:active {
1244 + transform: scale( 0.95 );
1245 +}
1246 +
1247 +@media ( prefers-reduced-motion: reduce ) {
1248 + .os-dock[ data-os-dock-placement="bottom" ]
1249 + .os-dock__item[ data-system-id="os-exit" ]
1250 + .os-dock__item-primary:hover,
1251 + .os-dock[ data-os-dock-placement="left" ]
1252 + .os-dock__item[ data-system-id="os-exit" ]
1253 + .os-dock__item-primary:hover,
1254 + .os-dock[ data-os-dock-placement="right" ]
1255 + .os-dock__item[ data-system-id="os-exit" ]
1256 + .os-dock__item-primary:hover {
1257 + transform: none;
1258 + }
1259 +}
1260 +
1261 +/* ------------------------------------------------------------------
1262 + * Dynamic dock behavior — OpenStation Preferences → Appearance →
1263 + * Desktop layout, persisted as `dockBehavior` (the dock) and
1264 + * `sideDockBehavior` (the Split sidebar) and worn by each rail as
1265 + * `data-os-dock-behavior` — an attribute PER RAIL rather than a body
1266 + * class, because Split's two rails answer independently. PHP stamps
1267 + * the dock on first paint; the apply pass and `src/dock-behavior.ts`
1268 + * re-stamp on every change and rebuild. `static` is the absence of
1269 + * rules.
1270 + *
1271 + * A dynamic rail that is not `os-dock--revealed` is PARKED: it
1272 + * collapses into a thin indicator line hugging its edge — the iOS
1273 + * home indicator, one line that says "there is a dock here" — and
1274 + * expands back into the full rail when the pointer comes for it.
1275 + * `src/dock-behavior.ts` owns the flip (a full-width edge zone, the
1276 + * rail's own box, its flyouts, keyboard focus) and runs it through
1277 + * the View Transitions API, so the line morphs into the pill and
1278 + * back; the rules below only describe the two resting states and
1279 + * tune the morph. There is deliberately no `:hover` here — a state
1280 + * CSS flipped on its own would jump instead of morphing.
1281 + *
1282 + * The line is the rail itself, not a separate element: same node,
1283 + * same `view-transition-name`, which is what lets the browser animate
1284 + * one box into the other. Its children are hidden while parked so
1285 + * the line is a line. A side rail leaves the flex row for the
1286 + * duration, so the area grows to the full width and the rail rides
1287 + * over it when summoned; the work area reserves nothing for a
1288 + * dynamic rail (`src/work-area/index.ts`).
1289 + * ------------------------------------------------------------------ */
1290 +
1291 +/*
1292 + * Side rails overlay the area in both states — the flex row must not
1293 + * shrink for a rail that is only sometimes there.
1294 + */
1295 +.os-dock[ data-os-dock-behavior="dynamic" ][ data-os-dock-placement="left" ],
1296 +.os-dock[ data-os-dock-behavior="dynamic" ][ data-os-dock-placement="right" ] {
1297 + position: absolute;
1298 + inset-block: 0;
1299 +}
1300 +
1301 +.os-dock[ data-os-dock-behavior="dynamic" ][ data-os-dock-placement="left" ] {
1302 + inset-inline-start: 0;
1303 +}
1304 +
1305 +.os-dock[ data-os-dock-behavior="dynamic" ][ data-os-dock-placement="right" ] {
1306 + inset-inline-end: 0;
1307 +}
1308 +
1309 +/*
1310 + * Parked — the indicator line. Everything that makes the pill a
1311 + * pill (padding, border, blur, texture, tile gap) comes off, and the
1312 + * box is the line's size. `overflow: hidden` clips whatever a tile
1313 + * paints past its box during the morph.
1314 + */
1315 +.os-dock[ data-os-dock-behavior="dynamic" ]:not( .os-dock--revealed ) {
1316 + min-width: 0;
1317 + min-height: 0;
1318 + max-width: none;
1319 + padding: 0;
1320 + gap: 0;
1321 + border: 0;
1322 + border-radius: 999px;
1323 + overflow: hidden;
1324 + background-color: var(
1325 + --os-dock-indicator-bg,
1326 + var( --os-dock-item-outline, rgba( 255, 255, 255, 0.55 ) )
1327 + );
1328 + background-image: none;
1329 + backdrop-filter: none;
1330 + -webkit-backdrop-filter: none;
1331 + box-shadow: none;
1332 + cursor: pointer;
1333 +}
1334 +
1335 +.os-dock[ data-os-dock-behavior="dynamic" ]:not( .os-dock--revealed ) > * {
1336 + display: none;
1337 +}
1338 +
1339 +.os-dock[ data-os-dock-behavior="dynamic" ][ data-os-dock-placement="bottom" ]:not( .os-dock--revealed ) {
1340 + width: var( --os-dock-indicator-length, 180px );
1341 + height: var( --os-dock-indicator-thickness, 5px );
1342 + bottom: 8px;
1343 +}
1344 +
1345 +.os-dock[ data-os-dock-behavior="dynamic" ][ data-os-dock-placement="left" ]:not( .os-dock--revealed ),
1346 +.os-dock[ data-os-dock-behavior="dynamic" ][ data-os-dock-placement="right" ]:not( .os-dock--revealed ) {
1347 + width: var( --os-dock-indicator-thickness, 5px );
1348 + height: var( --os-dock-indicator-length, 180px );
1349 + inset-block: calc( 50% - var( --os-dock-indicator-length, 180px ) / 2 );
1350 +}
1351 +
1352 +.os-dock[ data-os-dock-behavior="dynamic" ][ data-os-dock-placement="left" ]:not( .os-dock--revealed ) {
1353 + inset-inline-start: 8px;
1354 +}
1355 +
1356 +.os-dock[ data-os-dock-behavior="dynamic" ][ data-os-dock-placement="right" ]:not( .os-dock--revealed ) {
1357 + inset-inline-end: 8px;
1358 +}
1359 +
1360 +/*
1361 + * The morph. With the API, `src/dock-behavior.ts` names the rail for
1362 + * the duration of one flip and the browser animates the old box into
1363 + * the new one; the rail's contents cross-fade underneath. The root is
1364 + * opted out of the snapshot so the rest of the desktop keeps
1365 + * running while the line becomes a pill — a frozen page for 260ms
1366 + * on every reveal would be the animation costing more than it
1367 + * gives. Without the API, the properties that can transition do.
1368 + */
1369 +@supports ( view-transition-name: none ) {
1370 + html.os-dock-vt {
1371 + view-transition-name: none;
1372 + }
1373 +
1374 + html.os-dock-vt::view-transition-old( root ),
1375 + html.os-dock-vt::view-transition-new( root ) {
1376 + animation: none;
1377 + }
1378 +
1379 + /*
1380 + * The transition layer must not catch the pointer. By default it
1381 + * does — the whole page reads as inert until the morph settles —
1382 + * and with the root opted out of the snapshot that is worse than
1383 + * pointless: the live desktop is right there under a layer that
1384 + * swallows every mouseup and every pointerenter for 260ms. The
1385 + * behavior module already holds a park back while a button is
1386 + * down; this is the other half, so a hover that lands on a tile
1387 + * while the rail is still morphing out starts its hover-intent
1388 + * timer instead of waiting for the next pointer movement.
1389 + */
1390 + html.os-dock-vt::view-transition {
1391 + pointer-events: none;
1392 + }
1393 +
1394 + ::view-transition-group( os-dock-os-dock ),
1395 + ::view-transition-group( os-dock-os-side-dock ) {
1396 + animation-duration: 260ms;
1397 + animation-timing-function: cubic-bezier( 0.22, 1, 0.36, 1 );
1398 + }
1399 +
1400 + ::view-transition-old( os-dock-os-dock ),
1401 + ::view-transition-old( os-dock-os-side-dock ) {
1402 + animation: os-dock-vt-out 120ms ease-out both;
1403 + }
1404 +
1405 + ::view-transition-new( os-dock-os-dock ),
1406 + ::view-transition-new( os-dock-os-side-dock ) {
1407 + animation: os-dock-vt-in 180ms ease-out 80ms both;
1408 + }
1409 +
1410 + @keyframes os-dock-vt-out {
1411 + to {
1412 + opacity: 0;
1413 + }
1414 + }
1415 +
1416 + @keyframes os-dock-vt-in {
1417 + from {
1418 + opacity: 0;
1419 + }
1420 + }
1421 +}
1422 +
1423 +@supports not ( view-transition-name: none ) {
1424 + .os-dock[ data-os-dock-behavior="dynamic" ] {
1425 + transition:
1426 + width 240ms ease,
1427 + height 240ms ease,
1428 + opacity 240ms ease,
1429 + border-radius 240ms ease,
1430 + bottom 240ms ease,
1431 + inset-block 240ms ease;
1432 + }
1433 +}
1434 +
1435 +@media ( prefers-reduced-motion: reduce ) {
1436 + .os-dock[ data-os-dock-behavior="dynamic" ] {
1437 + transition: none;
1438 + }
1099 1439 }