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/desktop.css +424 -23 1.1.61.1.10 View file →
@@ -156,8 +156,27 @@
156 156 inset-inline-end: 0 !important;
157 157 }
158 158
159 159 /*
160 + * No top-level item may be taller than the bar.
161 + *
162 + * Core lays the two item groups out as floats, and a float that
163 + * overflows the 32px bar overflows invisibly. A host may lay a group
164 + * out as a flex row instead — WordPress.com's Debug Bar does, to
165 + * order its own item first — and then every item stretches to the
166 + * tallest one and the group's background paints the difference under
167 + * the windows' title bars. Our own items were that tallest one once
168 + * (an `inline-flex` item on a 32px line box grew to 37px; fixed at
169 + * the source in `includes/admin-bar.php`), but any item from any
170 + * plugin can be, so the cap lives here: the bar's own height token,
171 + * which is also the 46px mobile bar. Submenus are absolutely
172 + * positioned and unaffected.
173 + */
174 +body.os-active #wpadminbar .ab-top-menu > li {
175 + max-height: var(--wp-admin--admin-bar--height, 32px);
176 +}
177 +
178 +/*
160 179 * Admin-bar presentation modes — OS Settings → Appearance → Admin bar,
161 180 * persisted as `adminBarMode` and emitted as a
162 181 * `os-admin-bar-<mode>` body class by PHP on first paint and
163 182 * re-written by the shell's apply pass on every change.
@@ -166,9 +185,11 @@
166 185 * starts below the bar. The two modes below both take the bar out of
167 186 * the shell's way, so the shell reclaims the full viewport in each.
168 187 *
169 188 * Every selector here carries two body classes, so it outranks the
170 - * one-class pin rule above without needing to escalate anything.
189 + * one-class pin rule above on specificity; the one property the pin
190 + * marks `!important` (`inset-block-start`) is re-declared `!important`
191 + * where a mode moves the bar, and wins the same way.
171 192 */
172 193
173 194 /*
174 195 * Dynamic — the bar parks off the top edge leaving a peek strip, and
@@ -173,19 +194,36 @@
173 194 /*
174 195 * Dynamic — the bar parks off the top edge leaving a peek strip, and
175 196 * slides back in when the pointer reaches the top of the viewport or
176 197 * something inside it takes keyboard focus. The classic Windows
177 - * auto-hide taskbar, and the reason `transform` is the right tool: a
178 - * transformed element keeps hit-testing at its PAINTED position, so
179 - * the parked remainder stops catching the pointer the moment it goes
180 - * off-screen. What DOES catch it is the peek strip plus the reveal
181 - * zone added below.
198 + * auto-hide taskbar.
199 + *
200 + * It parks by moving its `inset-block-start`, NOT by a `transform`,
201 + * and that is load-bearing. A transformed element becomes the
202 + * containing block for every `position: fixed` descendant, and the
203 + * bar has descendants like that which it does not own: the
204 + * WordPress.com notifications panel is `position: fixed; top: 32px;
205 + * bottom: 0` inside `#wp-admin-bar-notes`. Measured against a
206 + * transformed 32px bar instead of the viewport, that panel computes to
207 + * zero height — the button "did nothing" in this mode and worked in
208 + * `static`. `translate`, `filter`, `perspective`, `contain` and
209 + * `will-change: transform` all create the same containing block;
210 + * `tests/vitest/admin-bar-dynamic.test.ts` keeps every one of them off
211 + * this rule. Moving `inset-block-start` hit-tests the same way — the
212 + * part above the viewport is simply outside it — so what catches the
213 + * pointer is still only the peek strip plus the reveal zone below.
214 + *
215 + * The pin above sets `inset-block-start: 0 !important`; these carry
216 + * `!important` too and win on specificity. The bar's height is Core's
217 + * own token, so the parked offset follows the 46px mobile bar as well.
182 218 */
183 219 body.os-active.os-admin-bar-dynamic #wpadminbar {
184 - transform: translateY(
185 - calc(-100% + var(--os-admin-bar-peek, 4px))
186 - );
187 - transition: transform 180ms ease;
220 + /* One number for the slide; the reveal zone below waits on it. */
221 + --os-admin-bar-slide: 180ms;
222 + inset-block-start: calc(
223 + var(--os-admin-bar-peek, 4px) - var(--wp-admin--admin-bar--height, 32px)
224 + ) !important;
225 + transition: inset-block-start var(--os-admin-bar-slide, 180ms) ease;
188 226 }
189 227
190 228 body.os-active.os-admin-bar-dynamic #wpadminbar:hover,
191 229 body.os-active.os-admin-bar-dynamic #wpadminbar:focus-within,
@@ -197,9 +235,9 @@
197 235 * would drop the notch back up under the pointer.
198 236 */
199 237 body.os-active.os-admin-bar-dynamic:has( .os-notch:hover ) #wpadminbar,
200 238 body.os-active.os-admin-bar-dynamic:has( .os-notch:focus-visible ) #wpadminbar {
201 - transform: translateY(0);
239 + inset-block-start: 0 !important;
202 240 }
203 241
204 242 /*
205 243 * Reveal zone — an invisible extension of the bar's hit area, hanging
@@ -219,8 +257,25 @@
219 257 position: absolute;
220 258 inset-inline: 0;
221 259 inset-block-start: 100%;
222 260 height: var(--os-admin-bar-reveal-zone, 16px);
261 + /*
262 + * The zone changes size only once the bar has come to rest — a
263 + * step, not a slide, held back for the length of the bar's own
264 + * slide. That delay is what keeps the reveal stable. Collapsed the
265 + * instant hover began, the zone was gone while the bar was still
266 + * on its way down: a pointer resting in the band the zone had
267 + * covered was outside the bar's box for the rest of the slide, so
268 + * hover dropped, the bar turned back, the zone returned under the
269 + * pointer, hover resumed — the bar flickered up and down for as
270 + * long as the pointer stayed near the edge. Held for the slide,
271 + * the zone still hangs off the moving bar and the two together
272 + * always cover the pointer that tripped it; by the time the zone
273 + * goes, the bar's own box is over that pointer. The same delay on
274 + * the way back keeps the zone from reappearing under a pointer
275 + * that just left the bar while the bar is still retracting.
276 + */
277 + transition: height 0s linear var(--os-admin-bar-slide, 180ms);
223 278 }
224 279
225 280 /*
226 281 * ...but only while the bar is parked. Left standing once the bar is
@@ -228,9 +283,10 @@
228 283 * clicks on whatever window is up there.
229 284 *
230 285 * Collapsing it is also what keeps the reveal stable: the pointer
231 286 * that tripped the zone is, post-reveal, inside the bar's real box,
232 - * so `:hover` holds without the zone's help.
287 + * so `:hover` holds without the zone's help — and it collapses only
288 + * once the bar is there (the transition above).
233 289 */
234 290 body.os-active.os-admin-bar-dynamic #wpadminbar:hover::after,
235 291 body.os-active.os-admin-bar-dynamic #wpadminbar:focus-within::after,
236 292 body.os-active.os-admin-bar-dynamic #wpadminbar:active::after {
@@ -250,9 +306,11 @@
250 306 }
251 307 }
252 308
253 309 @media (prefers-reduced-motion: reduce) {
254 - body.os-active.os-admin-bar-dynamic #wpadminbar {
310 + body.os-active.os-admin-bar-dynamic #wpadminbar,
311 + body.os-active.os-admin-bar-dynamic #wpadminbar::after {
312 + /* The bar jumps, so the zone has nothing to wait for. */
255 313 transition: none;
256 314 }
257 315 }
258 316
@@ -336,12 +394,26 @@
336 394 /*
337 395 * Desktop shell — fills the viewport below the admin bar.
338 396 * Uses position: fixed so it's unaffected by any containing-block
339 397 * quirks in #wpwrap/#wpcontent.
398 + *
399 + * The inset is the bar's MEASURED bottom edge, `--os-admin-bar-height`
400 + * (written on <html> by `src/admin-bar-height.ts`), with Core's own
401 + * token behind it for the first paint. Core's token is Core's promise
402 + * about Core's bar; a host can make the bar taller, give it a border
403 + * or push it down under a strip of its own — WordPress.com's staff
404 + * debug chrome does — and the shell then started under the bar by
405 + * exactly the difference. The measured token is absent whenever the
406 + * bar has no box at the top edge (hidden, mobile, solo, fullscreen,
407 + * parked in the dynamic mode), so every mode that rebinds Core's
408 + * token below still resolves through it.
340 409 */
341 410 .os-shell {
342 411 position: fixed;
343 - inset-block-start: var(--wp-admin--admin-bar--height, 32px);
412 + inset-block-start: var(
413 + --os-admin-bar-height,
414 + var(--wp-admin--admin-bar--height, 32px)
415 + );
344 416 inset-inline-start: 0;
345 417 inset-inline-end: 0;
346 418 inset-block-end: 0;
347 419 display: flex;
@@ -556,9 +628,12 @@
556 628
557 629 /* Below 783px WP collapses the admin bar to 46px. */
558 630 @media screen and (max-width: 782px) {
559 631 .os-shell {
560 - inset-block-start: var(--wp-admin--admin-bar--height, 46px);
632 + inset-block-start: var(
633 + --os-admin-bar-height,
634 + var(--wp-admin--admin-bar--height, 46px)
635 + );
561 636 }
562 637 }
563 638
564 639 /*
@@ -974,10 +1049,12 @@
974 1049 .os-widgets__card-close {
975 1050 position: absolute;
976 1051 top: 6px;
977 1052 inset-inline-end: 6px;
978 - width: 22px;
979 - height: 22px;
1053 + /* 24px is the WCAG 2.2 SC 2.5.8 floor. The glyph inside is unchanged;
1054 + only the target grows. */
1055 + width: 24px;
1056 + height: 24px;
980 1057 display: flex;
981 1058 align-items: center;
982 1059 justify-content: center;
983 1060 padding: 0;
@@ -1062,10 +1139,10 @@
1062 1139 corner-absolute — reset position + always-visible opacity. */
1063 1140 .os-widgets__chrome .os-widgets__card-close {
1064 1141 position: static;
1065 1142 opacity: 0.7;
1066 - width: 20px;
1067 - height: 20px;
1143 + width: 24px;
1144 + height: 24px;
1068 1145 }
1069 1146
1070 1147 .os-widgets__chrome .os-widgets__card-close:hover,
1071 1148 .os-widgets__chrome .os-widgets__card-close:focus-visible {
@@ -1079,9 +1156,10 @@
1079 1156 * expose it (tapping it on a docked card would be a confusing
1080 1157 * no-op). The DOM node stays mounted either way so state flips
1081 1158 * don't rebuild chrome.
1082 1159 *
1083 - * Styling mirrors the inline close button: 20×20 round tile,
1160 + * Styling mirrors the inline close button: 24×24 round tile
1161 + * (the WCAG 2.2 SC 2.5.8 target-size floor),
1084 1162 * dimmed by default, full-opacity on hover / focus. Distinct hover
1085 1163 * color (theme accent, not red) signals "put back" vs. "remove."
1086 1164 */
1087 1165 .os-widgets__card-redock {
@@ -1092,10 +1170,10 @@
1092 1170 border: 0;
1093 1171 background: var( --os-ui-scrim, rgba( 0, 0, 0, 0.55 ) );
1094 1172 color: rgba( 255, 255, 255, 0.75 );
1095 1173 border-radius: 50%;
1096 - width: 20px;
1097 - height: 20px;
1174 + width: 24px;
1175 + height: 24px;
1098 1176 cursor: pointer;
1099 1177 opacity: 0.7;
1100 1178 transition:
1101 1179 opacity 0.15s ease,
@@ -1110,9 +1188,9 @@
1110 1188 .os-widgets__card-redock:hover,
1111 1189 .os-widgets__card-redock:focus-visible {
1112 1190 opacity: 1;
1113 1191 background: var( --wp-admin-theme-color, #2271b1 );
1114 - color: var( --os-ui-fg-on-accent, #fff );
1192 + color: var( --os-ui-accent-ink, var( --os-ui-fg-on-accent, #fff ) );
1115 1193 }
1116 1194
1117 1195 /*
1118 1196 * Floating widgets — absolute-positioned, inline top/left/width/height
@@ -1664,5 +1742,328 @@
1664 1742 .os-shell--revealing #os-area,
1665 1743 .os-shell--revealing .os-dock {
1666 1744 transition: none;
1667 1745 }
1746 +}
1747 +
1748 +/* A residency frame follows the window body without scrolling with its contents. */
1749 +.os-mio-residence {
1750 + position: absolute;
1751 + z-index: 50;
1752 + pointer-events: none;
1753 + overflow: hidden;
1754 + container-type: inline-size;
1755 +}
1756 +
1757 +.os-mio-residence[hidden],
1758 +.os-mio-chat-launcher[hidden] {
1759 + display: none;
1760 +}
1761 +
1762 +.os-mio-chat-launcher {
1763 + position: absolute;
1764 + inset-inline-end: 20px;
1765 + inset-block-end: 18px;
1766 + pointer-events: auto;
1767 +}
1768 +
1769 +.os-mio-chat {
1770 + position: absolute;
1771 + inset-inline-end: 16px;
1772 + inset-block-end: 16px;
1773 + inline-size: min(380px, calc(100% - 32px));
1774 + max-block-size: calc(100% - 32px);
1775 + box-sizing: border-box;
1776 + display: flex;
1777 + flex-direction: column;
1778 + gap: 12px;
1779 + padding: 18px;
1780 + pointer-events: auto;
1781 + color: var(--os-mio-chat-fg, #1d2327);
1782 + background: var(--os-mio-chat-bg, #fff);
1783 + border: 1px solid var(--os-mio-chat-border, #c3c4c7);
1784 + border-radius: var(--os-mio-chat-radius, 16px);
1785 + box-shadow: 0 8px 40px var(--os-mio-chat-glow, #00000020);
1786 + animation: os-mio-chat-enter 180ms ease-out;
1787 +}
1788 +
1789 +.os-mio-chat header {
1790 + display: flex;
1791 + align-items: center;
1792 + gap: 12px;
1793 +}
1794 +
1795 +.os-mio-chat header strong {
1796 + letter-spacing: .14em;
1797 +}
1798 +
1799 +.os-mio-chat header span {
1800 + flex: 1;
1801 + font-size: 11px;
1802 + color: var(--os-mio-chat-muted, #646970);
1803 +}
1804 +
1805 +.os-mio-chat__log {
1806 + position: relative;
1807 + min-block-size: 60px;
1808 + max-block-size: 300px;
1809 + overflow: auto;
1810 + overscroll-behavior: contain;
1811 +}
1812 +
1813 +.os-mio-chat__message {
1814 + margin-block: 0 10px;
1815 + padding: 10px 12px;
1816 + line-height: 1.6;
1817 + white-space: pre-wrap;
1818 + overflow-wrap: anywhere;
1819 +}
1820 +
1821 +.os-mio-chat__message--user {
1822 + border-radius: 16px 16px 4px 16px;
1823 + background: var(--os-mio-chat-user-bg, #f0f0f1);
1824 +}
1825 +
1826 +.os-mio-chat__composer {
1827 + display: flex;
1828 + align-items: center;
1829 + gap: 8px;
1830 +}
1831 +
1832 +.os-mio-chat__composer os-textarea {
1833 + flex: 1;
1834 + min-inline-size: 0;
1835 +}
1836 +
1837 +.os-mio-chat__status,
1838 +.os-mio-chat small {
1839 + margin: 0;
1840 + font-size: 11px;
1841 + line-height: 1.5;
1842 + color: var(--os-mio-chat-muted, #646970);
1843 +}
1844 +
1845 +@keyframes os-mio-chat-enter {
1846 + from { opacity: 0; transform: translateY(6px) scale(.98); }
1847 + to { opacity: 1; transform: translateY(0) scale(1); }
1848 +}
1849 +
1850 +@media (prefers-reduced-motion: reduce) {
1851 + .os-mio-chat { animation: none; }
1852 +}
1853 +
1854 +.os-mio-residence > .os-mio { z-index: 0; }
1855 +.os-mio-chat, .os-mio-chat-launcher { z-index: 1; }
1856 +
1857 +.os-mio-chat [hidden] {
1858 + display: none !important;
1859 +}
1860 +
1861 +.os-mio-chat__message--assistant {
1862 + white-space: normal;
1863 +}
1864 +
1865 +.os-mio-chat__message p {
1866 + margin-block: 0 8px;
1867 +}
1868 +
1869 +.os-mio-chat__message ul,
1870 +.os-mio-chat__message ol {
1871 + margin-block: 8px;
1872 + padding-inline-start: 20px;
1873 +}
1874 +
1875 +.os-mio-chat__message a {
1876 + color: inherit;
1877 + text-decoration: underline;
1878 +}
1879 +
1880 +/* Shared MIO chrome: the same portrait with a softly revealed off-state slash. */
1881 +.os-mio-window-toggle svg {
1882 + width: 20px;
1883 + height: 20px;
1884 + overflow: visible;
1885 +}
1886 +
1887 +.os-mio-window-toggle__slash {
1888 + fill: none;
1889 + stroke: currentColor;
1890 + stroke-width: 2.4;
1891 + stroke-linecap: round;
1892 + opacity: 0;
1893 + stroke-dasharray: 26;
1894 + stroke-dashoffset: 26;
1895 + transition: opacity 240ms ease, stroke-dashoffset 240ms ease;
1896 +}
1897 +
1898 +.os-mio-window-toggle[data-mio-disabled="true"] .os-mio-window-toggle__slash {
1899 + opacity: 1;
1900 + stroke-dashoffset: 0;
1901 +}
1902 +
1903 +.os-mio-chat__activity {
1904 + display: flex;
1905 + align-items: center;
1906 + gap: 8px;
1907 + min-height: 18px;
1908 +}
1909 +
1910 +.os-mio-chat__thinking {
1911 + display: inline-flex;
1912 + align-items: center;
1913 + gap: 3px;
1914 + width: 24px;
1915 + flex-shrink: 0;
1916 + opacity: 0;
1917 + color: var(--os-mio-chat-muted, #646970);
1918 + transition: opacity 240ms ease;
1919 +}
1920 +
1921 +.os-mio-chat[data-thinking="true"] .os-mio-chat__thinking {
1922 + opacity: 1;
1923 +}
1924 +
1925 +.os-mio-chat__thinking i {
1926 + width: 4px;
1927 + height: 4px;
1928 + border-radius: 50%;
1929 + background: currentColor;
1930 + animation: os-mio-thinking 1.6s ease-in-out infinite;
1931 + animation-play-state: paused;
1932 +}
1933 +
1934 +.os-mio-chat__thinking i:nth-child(2) { animation-delay: 160ms; }
1935 +.os-mio-chat__thinking i:nth-child(3) { animation-delay: 320ms; }
1936 +.os-mio-chat[data-thinking="true"] .os-mio-chat__thinking i { animation-play-state: running; }
1937 +.os-mio-window-toggle[data-mio-thinking="true"] circle { animation: os-mio-thinking 2.4s ease-in-out infinite; }
1938 +
1939 +@keyframes os-mio-thinking {
1940 + 0%, 100% { opacity: 0.45; }
1941 + 50% { opacity: 1; }
1942 +}
1943 +
1944 +@media (prefers-reduced-motion: reduce) {
1945 + .os-mio-window-toggle__slash,
1946 + .os-mio-chat__thinking { transition: none; }
1947 + .os-mio-chat__thinking i,
1948 + .os-mio-window-toggle[data-mio-thinking="true"] circle { animation: none; }
1949 +}
1950 +
1951 +.os-mio-window-toggle {
1952 + max-inline-size: 30px;
1953 + opacity: 1;
1954 + visibility: visible;
1955 + transition: opacity 180ms ease, max-inline-size 240ms ease, visibility 0s;
1956 +}
1957 +
1958 +.os-mio-window-toggle[data-mio-available="false"] {
1959 + max-inline-size: 0;
1960 + opacity: 0;
1961 + visibility: hidden;
1962 + overflow: hidden;
1963 + pointer-events: none;
1964 + transition: opacity 180ms ease, max-inline-size 240ms ease, visibility 0s 180ms;
1965 +}
1966 +
1967 +@media (prefers-reduced-motion: reduce) {
1968 + .os-mio-window-toggle,
1969 + .os-mio-window-toggle[data-mio-available="false"] { transition: none; }
1970 +}
1971 +
1972 +/* The launcher stays available; the companion only speaks when invited. */
1973 +.os-mio > canvas,
1974 +.os-mio > .os-mio__handle {
1975 + transition: opacity 180ms ease, visibility 180ms;
1976 +}
1977 +
1978 +.os-mio[data-mio-visible="false"] > canvas,
1979 +.os-mio[data-mio-visible="false"] > .os-mio__handle {
1980 + opacity: 0;
1981 + visibility: hidden;
1982 + pointer-events: none;
1983 +}
1984 +
1985 +.os-mio-callout {
1986 + position: absolute;
1987 + z-index: 2;
1988 + display: flex;
1989 + align-items: center;
1990 + gap: 10px;
1991 + max-inline-size: calc(100% - 16px);
1992 + box-sizing: border-box;
1993 + padding-block: 6px;
1994 + padding-inline: 15px 6px;
1995 + border: 1px solid var(--os-mio-callout-border, #d6b8cb);
1996 + border-radius: 22px;
1997 + background: var(--os-mio-callout-bg, #ffe0cf);
1998 + color: var(--os-mio-callout-fg, #382b38);
1999 + box-shadow: 0 4px 16px var(--os-mio-chat-glow, #00000020);
2000 + font-size: 12px;
2001 + line-height: 1.5;
2002 + font-weight: 500;
2003 + pointer-events: auto;
2004 + animation: os-mio-chat-enter 180ms ease-out;
2005 +}
2006 +
2007 +.os-mio-callout os-button::part(button) {
2008 + inline-size: 26px;
2009 + block-size: 26px;
2010 + font-size: 17px;
2011 + line-height: 1;
2012 +}
2013 +
2014 +/* A small rounded tail joins the companion without a second outlined shape. */
2015 +.os-mio-callout::after {
2016 + content: "";
2017 + position: absolute;
2018 + inset-block-end: -4px;
2019 + inset-inline-end: 18px;
2020 + inline-size: 8px;
2021 + block-size: 8px;
2022 + background: inherit;
2023 + border-inline-end: inherit;
2024 + border-block-end: inherit;
2025 + border-end-end-radius: 3px;
2026 + transform: rotate(45deg);
2027 +}
2028 +
2029 +.os-mio-callout[hidden] { display: none; }
2030 +
2031 +@media (prefers-reduced-motion: reduce) {
2032 + .os-mio > canvas,
2033 + .os-mio > .os-mio__handle { transition: none; }
2034 + .os-mio-callout { animation: none; }
2035 +}
2036 +
2037 +/* Response controls belong to their message; all layout follows inline flow. */
2038 +.os-mio-chat__actions {
2039 + display: flex;
2040 + flex-wrap: wrap;
2041 + gap: 8px;
2042 + margin-block-start: 12px;
2043 +}
2044 +
2045 +.os-mio-chat__action {
2046 + display: flex;
2047 + flex-direction: column;
2048 + align-items: flex-start;
2049 + gap: 4px;
2050 + min-inline-size: 0;
2051 + max-inline-size: 100%;
2052 +}
2053 +
2054 +.os-mio-chat__action os-button::part(button) {
2055 + white-space: normal;
2056 + overflow-wrap: anywhere;
2057 +}
2058 +
2059 +.os-mio-chat__action-status {
2060 + font-size: 12px;
2061 + color: var(--os-mio-chat-muted, #646970);
2062 + overflow-wrap: anywhere;
2063 +}
2064 +
2065 +.os-mio-chat__composer os-textarea::part(textarea) {
2066 + white-space: pre-wrap;
2067 + overflow-wrap: anywhere;
2068 + overflow-x: hidden;
1668 2069 }