| @@ -50,9 +50,9 @@ | ||
| 50 | 50 | position: relative; |
| 51 | 51 | display: flex; |
| 52 | 52 | flex-direction: column; |
| 53 | 53 | align-items: center; |
| 54 | - width: 40px; | |
| 54 | + width: var( --os-dock-item-size, 40px ); | |
| 55 | 55 | flex-shrink: 0; |
| 56 | 56 | } |
| 57 | 57 | |
| 58 | 58 | /* Primary icon button — opens or focuses the page. */ |
| @@ -60,12 +60,12 @@ | ||
| 60 | 60 | position: relative; |
| 61 | 61 | display: flex; |
| 62 | 62 | align-items: center; |
| 63 | 63 | justify-content: center; |
| 64 | - width: 40px; | |
| 65 | - height: 40px; | |
| 64 | + width: var( --os-dock-item-size, 40px ); | |
| 65 | + height: var( --os-dock-item-size, 40px ); | |
| 66 | 66 | border: none; |
| 67 | - border-radius: 10px; | |
| 67 | + border-radius: var( --os-dock-item-radius, 10px ); | |
| 68 | 68 | cursor: pointer; |
| 69 | 69 | padding: 0; |
| 70 | 70 | background-color: transparent; |
| 71 | 71 | /* |
| @@ -98,8 +98,21 @@ | ||
| 98 | 98 | background-color: var( --os-dock-item-bg-focused, transparent ); |
| 99 | 99 | color: var( --os-dock-icon-color-hover, var( --os-ui-fg-on-accent, #fff ) ); |
| 100 | 100 | } |
| 101 | 101 | |
| 102 | +/* | |
| 103 | + * DOCK_ITEM_FOCUSED texture slot — the face of the tile whose window is | |
| 104 | + * in front, and of any tile held down: a key that stays pressed while | |
| 105 | + * its window is up. Falls back to the resting face. | |
| 106 | + */ | |
| 107 | +.os-dock__item--focused .os-dock__item-primary, | |
| 108 | +.os-dock__item-primary:active { | |
| 109 | + background-image: var( | |
| 110 | + --os-dock-item-image-focused, | |
| 111 | + var( --os-dock-item-image, none ) | |
| 112 | + ); | |
| 113 | +} | |
| 114 | + | |
| 102 | 115 | .os-dock__item-primary:hover { |
| 103 | 116 | background-color: var( |
| 104 | 117 | --os-dock-item-bg-hover, |
| 105 | 118 | rgba( 255, 255, 255, 0.15 ) |
| @@ -107,13 +120,13 @@ | ||
| 107 | 120 | color: var( |
| 108 | 121 | --os-dock-icon-color-hover, |
| 109 | 122 | var( --os-ui-fg-on-accent, #fff ) |
| 110 | 123 | ); |
| 111 | - transform: scale( 1.1 ); | |
| 124 | + transform: var( --os-dock-item-hover-transform, scale( 1.1 ) ); | |
| 112 | 125 | } |
| 113 | 126 | |
| 114 | 127 | .os-dock__item-primary:active { |
| 115 | - transform: scale( 0.95 ); | |
| 128 | + transform: var( --os-dock-item-press-transform, scale( 0.95 ) ); | |
| 116 | 129 | } |
| 117 | 130 | |
| 118 | 131 | .os-dock__item-primary:focus-visible { |
| 119 | 132 | outline: 2px solid var( |
| @@ -529,9 +542,9 @@ | ||
| 529 | 542 | |
| 530 | 543 | /* Shared tooltip — placement-specific anchor modifiers applied by JS. */ |
| 531 | 544 | .os-dock__tooltip { |
| 532 | 545 | position: fixed; |
| 533 | - padding: 6px 12px; | |
| 546 | + padding: var( --os-tooltip-padding, 6px 12px ); | |
| 534 | 547 | background: var( |
| 535 | 548 | --os-tooltip-bg, |
| 536 | 549 | var( --os-ui-scrim, rgba( 0, 0, 0, 0.85 ) ) |
| 537 | 550 | ); |
| @@ -538,13 +551,15 @@ | ||
| 538 | 551 | color: var( |
| 539 | 552 | --os-tooltip-fg, |
| 540 | 553 | var( --os-ui-fg-on-accent, #fff ) |
| 541 | 554 | ); |
| 542 | - font-size: 12px; | |
| 555 | + font-size: var( --os-tooltip-font-size, 12px ); | |
| 543 | 556 | font-weight: 400; |
| 544 | 557 | line-height: 1.4; |
| 545 | 558 | white-space: nowrap; |
| 546 | - border-radius: 6px; | |
| 559 | + border: var( --os-tooltip-border, 0 ); | |
| 560 | + border-radius: var( --os-tooltip-radius, 6px ); | |
| 561 | + box-shadow: var( --os-tooltip-shadow, none ); | |
| 547 | 562 | pointer-events: none; |
| 548 | 563 | z-index: calc( var( --os-z-dock ) + 1 ); |
| 549 | 564 | opacity: 0; |
| 550 | 565 | transition: opacity 0.15s ease, transform 0.15s ease; |
| @@ -651,9 +666,9 @@ | ||
| 651 | 666 | transform: translateY( -50% ); |
| 652 | 667 | width: 4px; |
| 653 | 668 | height: 4px; |
| 654 | 669 | border-radius: 50%; |
| 655 | - background: var( --os-dock-item-outline, #fff ); | |
| 670 | + background: var( --os-dock-active-indicator, var( --os-dock-item-outline, #fff ) ); | |
| 656 | 671 | } |
| 657 | 672 | |
| 658 | 673 | .os-dock[ data-os-dock-placement="left" ] |
| 659 | 674 | .os-dock__item--focused::before { |
| @@ -677,9 +692,9 @@ | ||
| 677 | 692 | width: 6px; |
| 678 | 693 | height: 6px; |
| 679 | 694 | border-radius: 50%; |
| 680 | 695 | background: transparent; |
| 681 | - border: 1px solid var( --os-dock-item-outline, rgba( 255, 255, 255, 0.85 ) ); | |
| 696 | + border: 1px solid var( --os-dock-active-indicator, var( --os-dock-item-outline, rgba( 255, 255, 255, 0.85 ) ) ); | |
| 682 | 697 | } |
| 683 | 698 | |
| 684 | 699 | /* Right dock: indicator on the opposite edge (inside-facing). */ |
| 685 | 700 | .os-dock[ data-os-dock-placement="right" ] |
| @@ -691,9 +706,9 @@ | ||
| 691 | 706 | transform: translateY( -50% ); |
| 692 | 707 | width: 4px; |
| 693 | 708 | height: 4px; |
| 694 | 709 | border-radius: 50%; |
| 695 | - background: var( --os-dock-item-outline, #fff ); | |
| 710 | + background: var( --os-dock-active-indicator, var( --os-dock-item-outline, #fff ) ); | |
| 696 | 711 | } |
| 697 | 712 | |
| 698 | 713 | .os-dock[ data-os-dock-placement="right" ] |
| 699 | 714 | .os-dock__item--focused::before { |
| @@ -707,9 +722,9 @@ | ||
| 707 | 722 | width: 6px; |
| 708 | 723 | height: 6px; |
| 709 | 724 | border-radius: 50%; |
| 710 | 725 | background: transparent; |
| 711 | - border: 1px solid var( --os-dock-item-outline, rgba( 255, 255, 255, 0.85 ) ); | |
| 726 | + border: 1px solid var( --os-dock-active-indicator, var( --os-dock-item-outline, rgba( 255, 255, 255, 0.85 ) ) ); | |
| 712 | 727 | } |
| 713 | 728 | |
| 714 | 729 | /* "Open another" chip — left: right of tile; right: left of tile. */ |
| 715 | 730 | .os-dock[ data-os-dock-placement="left" ] |
| @@ -860,16 +875,22 @@ | ||
| 860 | 875 | * viewport floor. Absolutely positioned so it floats as pure chrome |
| 861 | 876 | * rather than stealing vertical space from the shell body. |
| 862 | 877 | * ------------------------------------------------------------------ */ |
| 863 | 878 | |
| 879 | +/* | |
| 880 | + * The pill's geometry is tokenized so a theme can draw a different | |
| 881 | + * object along the same edge: a full-width bar flush with the floor | |
| 882 | + * is `width: 100%`, `bottom: 0`, radius 0. The work area measures the | |
| 883 | + * rail's real box, so windows keep clear of whatever shape it takes. | |
| 884 | + */ | |
| 864 | 885 | .os-dock[ data-os-dock-placement="bottom" ] { |
| 865 | 886 | position: absolute; |
| 866 | 887 | inset-inline-start: 0; |
| 867 | 888 | inset-inline-end: 0; |
| 868 | - bottom: 12px; | |
| 889 | + bottom: var( --os-dock-floating-offset, 12px ); | |
| 869 | 890 | margin: 0 auto; |
| 870 | - width: fit-content; | |
| 871 | - max-width: calc( 100% - 32px ); | |
| 891 | + width: var( --os-dock-floating-width, fit-content ); | |
| 892 | + max-width: var( --os-dock-floating-max-width, calc( 100% - 32px ) ); | |
| 872 | 893 | flex-direction: row; |
| 873 | 894 | justify-content: center; |
| 874 | 895 | /* Top padding is 4px instead of 8px so that the inner `__scroll` |
| 875 | 896 | * wrapper can claim its own 4px padding-top for badge clearance |
| @@ -875,11 +896,11 @@ | ||
| 875 | 896 | * wrapper can claim its own 4px padding-top for badge clearance |
| 876 | 897 | * (the tile's badge sits at top: -3px). Net vertical chrome stays |
| 877 | 898 | * symmetric: 4px pill padding + 4px wrapper padding above the |
| 878 | 899 | * tile, 8px pill padding below — tile is 8px from both edges. */ |
| 879 | - padding: 4px 12px 8px; | |
| 880 | - gap: 6px; | |
| 881 | - border-radius: 18px; | |
| 900 | + padding: var( --os-dock-floating-padding, 4px 12px 8px ); | |
| 901 | + gap: var( --os-dock-floating-gap, 6px ); | |
| 902 | + border-radius: var( --os-dock-floating-radius, 18px ); | |
| 882 | 903 | |
| 883 | 904 | /* Warmer, more saturated glass tint than vertical placements so |
| 884 | 905 | * the floating pill reads as a distinct piece of chrome rather |
| 885 | 906 | * than a section of the sidebar. |
| @@ -895,10 +916,10 @@ | ||
| 895 | 916 | background-color: var( |
| 896 | 917 | --os-dock-floating-bg, |
| 897 | 918 | rgba( 22, 22, 26, 0.42 ) |
| 898 | 919 | ); |
| 899 | - backdrop-filter: blur( 28px ) saturate( 170% ); | |
| 900 | - -webkit-backdrop-filter: blur( 28px ) saturate( 170% ); | |
| 920 | + backdrop-filter: var( --os-dock-floating-backdrop, blur( 28px ) saturate( 170% ) ); | |
| 921 | + -webkit-backdrop-filter: var( --os-dock-floating-backdrop, blur( 28px ) saturate( 170% ) ); | |
| 901 | 922 | |
| 902 | 923 | /* |
| 903 | 924 | * Hairline + inset highlight. These used to be hardcoded, which |
| 904 | 925 | * meant a theme could restyle every dock placement EXCEPT this |
| @@ -944,9 +965,9 @@ | ||
| 944 | 965 | transform: translateX( -50% ); |
| 945 | 966 | width: 4px; |
| 946 | 967 | height: 4px; |
| 947 | 968 | border-radius: 50%; |
| 948 | - background: var( --os-dock-item-outline, #fff ); | |
| 969 | + background: var( --os-dock-active-indicator, var( --os-dock-item-outline, #fff ) ); | |
| 949 | 970 | } |
| 950 | 971 | |
| 951 | 972 | .os-dock[ data-os-dock-placement="bottom" ] |
| 952 | 973 | .os-dock__item--focused::before { |
| @@ -965,9 +986,9 @@ | ||
| 965 | 986 | width: 6px; |
| 966 | 987 | height: 6px; |
| 967 | 988 | border-radius: 50%; |
| 968 | 989 | background: transparent; |
| 969 | - border: 1px solid var( --os-dock-item-outline, rgba( 255, 255, 255, 0.85 ) ); | |
| 990 | + border: 1px solid var( --os-dock-active-indicator, var( --os-dock-item-outline, rgba( 255, 255, 255, 0.85 ) ) ); | |
| 970 | 991 | } |
| 971 | 992 | |
| 972 | 993 | /* |
| 973 | 994 | * "Show Desktop" — every live window on the active desktop minimized. |
| @@ -1039,15 +1060,13 @@ | ||
| 1039 | 1060 | display: flex; |
| 1040 | 1061 | flex-direction: row; |
| 1041 | 1062 | align-items: center; |
| 1042 | 1063 | justify-content: start; |
| 1043 | - justify-content: safe center; | |
| 1044 | - gap: 6px; | |
| 1064 | + justify-content: var( --os-dock-floating-justify, safe center ); | |
| 1065 | + gap: var( --os-dock-floating-gap, 6px ); | |
| 1045 | 1066 | flex: 1 1 auto; |
| 1046 | 1067 | min-width: 0; |
| 1047 | - padding-top: 4px; | |
| 1048 | - padding-bottom: 6px; | |
| 1049 | - padding-inline: 4px; | |
| 1068 | + padding: var( --os-dock-floating-track-padding, 4px 4px 6px ); | |
| 1050 | 1069 | overflow-x: auto; |
| 1051 | 1070 | /* |
| 1052 | 1071 | * Authored `visible`, computed `auto` — see the note above. Kept as |
| 1053 | 1072 | * the honest statement of intent: nothing here wants a vertical |
| @@ -1065,14 +1084,16 @@ | ||
| 1065 | 1084 | .os-dock[ data-os-dock-placement="bottom" ] .os-dock__pinned { |
| 1066 | 1085 | display: flex; |
| 1067 | 1086 | flex-direction: row; |
| 1068 | 1087 | align-items: center; |
| 1069 | - gap: 6px; | |
| 1088 | + gap: var( --os-dock-floating-gap, 6px ); | |
| 1070 | 1089 | flex-shrink: 0; |
| 1071 | 1090 | /* Match the scroll wrapper's badge clearance so system tiles sit |
| 1072 | 1091 | * on the same vertical baseline as the menu tiles. */ |
| 1073 | - padding-top: 4px; | |
| 1074 | - padding-bottom: 6px; | |
| 1092 | + padding: var( --os-dock-floating-pinned-padding, 4px 0 6px ); | |
| 1093 | + /* A well the system tiles sit in, when a theme draws one. */ | |
| 1094 | + background: var( --os-dock-pinned-bg, transparent ); | |
| 1095 | + box-shadow: var( --os-dock-pinned-shadow, none ); | |
| 1075 | 1096 | } |
| 1076 | 1097 | |
| 1077 | 1098 | /* Separator — the line between two groups of tiles. The system one |
| 1078 | 1099 | * lives at the leading edge of `__pinned` (flex flow handles the |
| @@ -1082,12 +1103,12 @@ | ||
| 1082 | 1103 | * note on the vertical placements above for why there is only one. */ |
| 1083 | 1104 | .os-dock[ data-os-dock-placement="bottom" ] |
| 1084 | 1105 | .os-dock__separator { |
| 1085 | 1106 | position: relative; |
| 1086 | - width: 2px; | |
| 1107 | + width: var( --os-dock-separator-width, 2px ); | |
| 1087 | 1108 | /* Fixed height — `60%` collapses against the floating pill's |
| 1088 | 1109 | intrinsic height, leaving the line effectively invisible. */ |
| 1089 | - height: 34px; | |
| 1110 | + height: var( --os-dock-separator-height, 34px ); | |
| 1090 | 1111 | /* Symmetric horizontal margin so the divider sits centered in the |
| 1091 | 1112 | * inter-tile gap: the 6px flex gap on each side (`__scroll`→`__pinned` |
| 1092 | 1113 | * on the left, `__pinned`'s own gap on the right) plus an equal |
| 1093 | 1114 | * margin per side balances out. An asymmetric margin here nudges the |
| @@ -1093,23 +1114,34 @@ | ||
| 1093 | 1114 | * margin per side balances out. An asymmetric margin here nudges the |
| 1094 | 1115 | * divider — and the whole menu cluster with it — off the pill's |
| 1095 | 1116 | * center. Wider than the tile gap on purpose: the clusters need to |
| 1096 | 1117 | * read as groups before the line between them means anything. */ |
| 1097 | - margin: auto 14px; | |
| 1118 | + margin: auto var( --os-dock-separator-margin, 14px ); | |
| 1098 | 1119 | border-radius: 2px; |
| 1099 | - background: linear-gradient( | |
| 1100 | - to bottom, | |
| 1101 | - transparent 0%, | |
| 1102 | - var( --os-dock-divider, rgba( 217, 46, 227, 0.7 ) ) 28%, | |
| 1103 | - var( --os-dock-divider, rgba( 217, 46, 227, 0.7 ) ) 72%, | |
| 1104 | - transparent 100% | |
| 1120 | + /* | |
| 1121 | + * The glowing seam by default. A theme drawing an etched groove | |
| 1122 | + * instead answers `--os-dock-separator-bg` with a flat colour and | |
| 1123 | + * `--os-dock-separator-shadow` with the highlight beside it. | |
| 1124 | + */ | |
| 1125 | + background: var( | |
| 1126 | + --os-dock-separator-bg, | |
| 1127 | + linear-gradient( | |
| 1128 | + to bottom, | |
| 1129 | + transparent 0%, | |
| 1130 | + var( --os-dock-divider, rgba( 217, 46, 227, 0.7 ) ) 28%, | |
| 1131 | + var( --os-dock-divider, rgba( 217, 46, 227, 0.7 ) ) 72%, | |
| 1132 | + transparent 100% | |
| 1133 | + ) | |
| 1105 | 1134 | ); |
| 1106 | - box-shadow: 0 0 10px | |
| 1107 | - color-mix( | |
| 1108 | - in srgb, | |
| 1109 | - var( --os-dock-divider, rgba( 217, 46, 227, 0.7 ) ) 45%, | |
| 1110 | - transparent | |
| 1111 | - ); | |
| 1135 | + box-shadow: var( | |
| 1136 | + --os-dock-separator-shadow, | |
| 1137 | + 0 0 10px | |
| 1138 | + color-mix( | |
| 1139 | + in srgb, | |
| 1140 | + var( --os-dock-divider, rgba( 217, 46, 227, 0.7 ) ) 45%, | |
| 1141 | + transparent | |
| 1142 | + ) | |
| 1143 | + ); | |
| 1112 | 1144 | flex-shrink: 0; |
| 1113 | 1145 | } |
| 1114 | 1146 | |
| 1115 | 1147 | /* Tooltip — above the hovered tile. JS writes the horizontal center |
| @@ -1195,9 +1227,9 @@ | ||
| 1195 | 1227 | order: 1; |
| 1196 | 1228 | } |
| 1197 | 1229 | |
| 1198 | 1230 | .os-dock__item[ data-system-id="os-exit" ] .os-dock__item-primary { |
| 1199 | - border-radius: 50%; | |
| 1231 | + border-radius: var( --os-dock-exit-radius, 50% ); | |
| 1200 | 1232 | background-color: transparent; |
| 1201 | 1233 | box-shadow: inset 0 0 0 1px var( |
| 1202 | 1234 | --os-dock-exit-ring, |
| 1203 | 1235 | var( --os-dock-border, rgba( 255, 255, 255, 0.18 ) ) |
| @@ -1210,9 +1242,9 @@ | ||
| 1210 | 1242 | * inline for the horizontal pill, block for the vertical pillars. |
| 1211 | 1243 | */ |
| 1212 | 1244 | .os-dock[ data-os-dock-placement="bottom" ] |
| 1213 | 1245 | .os-dock__item[ data-system-id="os-exit" ] { |
| 1214 | - margin-inline-start: 10px; | |
| 1246 | + margin-inline-start: var( --os-dock-exit-gap, 10px ); | |
| 1215 | 1247 | } |
| 1216 | 1248 | |
| 1217 | 1249 | .os-dock[ data-os-dock-placement="left" ] |
| 1218 | 1250 | .os-dock__item[ data-system-id="os-exit" ], |
| @@ -1239,9 +1271,9 @@ | ||
| 1239 | 1271 | |
| 1240 | 1272 | .os-dock[ data-os-dock-placement="bottom" ] |
| 1241 | 1273 | .os-dock__item[ data-system-id="os-exit" ] |
| 1242 | 1274 | .os-dock__item-primary:hover { |
| 1243 | - transform: translateY( 2px ); | |
| 1275 | + transform: var( --os-dock-exit-hover-transform, translateY( 2px ) ); | |
| 1244 | 1276 | } |
| 1245 | 1277 | |
| 1246 | 1278 | .os-dock[ data-os-dock-placement="left" ] |
| 1247 | 1279 | .os-dock__item[ data-system-id="os-exit" ] |