slack status without the slack status.zzstoatzz.io/
quickslice

Simplify picker layout and styling

Changed files
+25 -141
templates
+25 -141
templates/status.html
··· 200 </div> 201 <button type="button" class="emoji-picker-close" id="emoji-picker-close" aria-label="close emoji picker">โœ•</button> 202 </div> 203 - <div class="emoji-picker-preview"> 204 - <div class="emoji-preview-block"> 205 - <span class="emoji-preview-label">current</span> 206 - <div class="emoji-preview-emoji" id="emoji-preview-current"> 207 - {% if let Some(current) = current_status.as_ref() %} 208 - {% if current.status.starts_with("custom:") %} 209 - {% let emoji_name = current.status.strip_prefix("custom:").unwrap() %} 210 - <img src="/emojis/{{emoji_name}}.png" alt="{{emoji_name}}" title="{{emoji_name}}" 211 - onerror="this.onerror=null; this.src='/emojis/{{emoji_name}}.gif';"> 212 - {% else %} 213 - <span title="{{ current.status }}">{{ current.status }}</span> 214 - {% endif %} 215 - {% else %} 216 - <span title="happy">๐Ÿ˜Š</span> 217 - {% endif %} 218 - </div> 219 - </div> 220 - <div class="emoji-preview-block"> 221 - <span class="emoji-preview-label">next</span> 222 - <div class="emoji-preview-emoji" id="emoji-preview-next"> 223 - {% if let Some(current) = current_status.as_ref() %} 224 - {% if current.status.starts_with("custom:") %} 225 - {% let emoji_name = current.status.strip_prefix("custom:").unwrap() %} 226 - <img src="/emojis/{{emoji_name}}.png" alt="{{emoji_name}}" title="{{emoji_name}}" 227 - onerror="this.onerror=null; this.src='/emojis/{{emoji_name}}.gif';"> 228 - {% else %} 229 - <span title="{{ current.status }}">{{ current.status }}</span> 230 - {% endif %} 231 - {% else %} 232 - <span title="happy">๐Ÿ˜Š</span> 233 - {% endif %} 234 - </div> 235 - </div> 236 - </div> 237 <div class="emoji-search-container"> 238 <input type="text" 239 id="emoji-search" ··· 1115 } 1116 1117 .emoji-picker { 1118 - width: min(720px, 100%); 1119 - max-height: min(90vh, 720px); 1120 background: var(--bg-secondary); 1121 border: 1px solid var(--border-color); 1122 border-radius: clamp(var(--radius), 2vw, 24px); 1123 box-shadow: 0 32px 80px rgba(0, 0, 0, 0.45); 1124 display: flex; 1125 flex-direction: column; 1126 - gap: 1rem; 1127 - padding: clamp(1.25rem, 5vw, 2rem); 1128 overflow: hidden; 1129 } 1130 1131 .emoji-picker-header { 1132 display: flex; ··· 1169 outline: none; 1170 } 1171 1172 - .emoji-picker-preview { 1173 - display: flex; 1174 - gap: 1rem; 1175 - background: var(--bg-primary); 1176 - border-radius: var(--radius); 1177 - border: 1px solid var(--border-color); 1178 - padding: 1rem; 1179 - } 1180 - 1181 - .emoji-preview-block { 1182 - flex: 1; 1183 - display: flex; 1184 - flex-direction: column; 1185 - align-items: center; 1186 - gap: 0.5rem; 1187 - } 1188 - 1189 - .emoji-preview-label { 1190 - font-size: 0.75rem; 1191 - text-transform: uppercase; 1192 - letter-spacing: 0.08em; 1193 - color: var(--text-tertiary); 1194 - } 1195 - 1196 - .emoji-preview-emoji { 1197 - width: clamp(3.25rem, 16vw, 4.5rem); 1198 - height: clamp(3.25rem, 16vw, 4.5rem); 1199 - display: flex; 1200 - align-items: center; 1201 - justify-content: center; 1202 - font-size: clamp(2rem, 10vw, 3rem); 1203 - border-radius: var(--radius); 1204 - background: var(--bg-secondary); 1205 - border: 1px solid rgba(255, 255, 255, 0.05); 1206 - overflow: hidden; 1207 - } 1208 - 1209 - .emoji-preview-emoji img { 1210 - width: 100%; 1211 - height: 100%; 1212 - object-fit: contain; 1213 - } 1214 - 1215 - .emoji-preview-emoji span { 1216 - display: block; 1217 - } 1218 - 1219 .emoji-search-container { 1220 margin: 0; 1221 } ··· 1251 1252 .emoji-categories { 1253 display: flex; 1254 - gap: 0.5rem; 1255 overflow-x: auto; 1256 padding-bottom: 0.5rem; 1257 border-bottom: 1px solid rgba(255, 255, 255, 0.06); ··· 1289 .emoji-grid { 1290 flex: 1; 1291 display: grid; 1292 - grid-template-columns: repeat(auto-fill, minmax(56px, 1fr)); 1293 - gap: 0.5rem; 1294 padding-right: 0.25rem; 1295 overflow-y: auto; 1296 } 1297 1298 .emoji-option { 1299 - background: var(--bg-primary); 1300 - border: 1px solid transparent; 1301 - font-size: 2rem; 1302 cursor: pointer; 1303 - border-radius: 16px; 1304 - transition: transform 0.15s ease, border-color 0.2s ease, background 0.2s ease; 1305 display: flex; 1306 align-items: center; 1307 justify-content: center; 1308 width: 100%; 1309 aspect-ratio: 1; 1310 - position: relative; 1311 } 1312 1313 - .emoji-option:hover, 1314 .emoji-option:focus-visible { 1315 - background: var(--bg-tertiary); 1316 - border-color: var(--accent); 1317 - transform: translateY(-2px); 1318 - outline: none; 1319 } 1320 1321 .emoji-option.custom-emoji img { 1322 - width: 70%; 1323 - height: 70%; 1324 object-fit: contain; 1325 } 1326 1327 - .emoji-option.custom-emoji::after { 1328 - content: ''; 1329 - position: absolute; 1330 - inset: 8%; 1331 - border-radius: 12px; 1332 - pointer-events: none; 1333 - } 1334 - 1335 @media (max-width: 640px) { 1336 .emoji-picker-overlay { 1337 padding: 0; ··· 1343 height: 100%; 1344 max-height: none; 1345 border-radius: 0; 1346 - padding: 1.25rem 1rem 1rem; 1347 - gap: 0.75rem; 1348 - } 1349 - 1350 - .emoji-picker-preview { 1351 - padding: 0.75rem; 1352 } 1353 1354 .emoji-grid { 1355 - grid-template-columns: repeat(auto-fill, minmax(64px, 1fr)); 1356 } 1357 } 1358 ··· 2142 const emojiGrid = document.getElementById('emoji-grid'); 2143 const selectedEmoji = document.getElementById('selected-emoji'); 2144 const statusInput = document.getElementById('status-input'); 2145 - const emojiPreviewCurrent = document.getElementById('emoji-preview-current'); 2146 - const emojiPreviewNext = document.getElementById('emoji-preview-next'); 2147 let lastFocusBeforeEmojiPicker = null; 2148 2149 // Clear time picker ··· 2153 const expiresSelect = document.getElementById('expires_in'); 2154 2155 const isEmojiPickerOpen = () => emojiPickerOverlay && !emojiPickerOverlay.classList.contains('hidden'); 2156 - 2157 - const syncPreviewWithSelection = () => { 2158 - if (emojiPreviewNext && selectedEmoji) { 2159 - emojiPreviewNext.innerHTML = selectedEmoji.innerHTML; 2160 - } 2161 - }; 2162 - 2163 - syncPreviewWithSelection(); 2164 2165 const openEmojiPicker = () => { 2166 if (!emojiPickerOverlay || !emojiPicker) return; ··· 2181 if (emojiSearch) emojiSearch.focus(); 2182 }, 60); 2183 } 2184 - 2185 - if (emojiPreviewCurrent) { 2186 - const currentDisplay = document.querySelector('.current-status .status-emoji'); 2187 - if (currentDisplay) { 2188 - emojiPreviewCurrent.innerHTML = currentDisplay.innerHTML; 2189 - } else if (selectedEmoji) { 2190 - emojiPreviewCurrent.innerHTML = selectedEmoji.innerHTML; 2191 - } 2192 - } 2193 - 2194 - syncPreviewWithSelection(); 2195 2196 loadCustomEmojis().then(() => { 2197 loadEmojiCategory('frequent'); ··· 2314 } 2315 2316 statusInput.value = emoji; 2317 - syncPreviewWithSelection(); 2318 closeEmojiPicker(); 2319 checkForChanges(); 2320 }); ··· 2342 // Display the image in the selected emoji area 2343 selectedEmoji.innerHTML = `<img src="${img.src}" alt="${img.alt}" style="width: 100%; height: 100%; object-fit: contain;">`; 2344 statusInput.value = emojiValue; 2345 - syncPreviewWithSelection(); 2346 closeEmojiPicker(); 2347 checkForChanges(); 2348 }); ··· 2361 const emoji = e.target.getAttribute('data-emoji'); 2362 selectedEmoji.textContent = emoji; 2363 statusInput.value = emoji; 2364 - syncPreviewWithSelection(); 2365 closeEmojiPicker(); 2366 checkForChanges(); 2367 }); ··· 2595 } 2596 2597 statusInput.value = emojiValue; 2598 - syncPreviewWithSelection(); 2599 closeEmojiPicker(); 2600 checkForChanges(); 2601 // Clear search when emoji is selected
··· 200 </div> 201 <button type="button" class="emoji-picker-close" id="emoji-picker-close" aria-label="close emoji picker">โœ•</button> 202 </div> 203 <div class="emoji-search-container"> 204 <input type="text" 205 id="emoji-search" ··· 1081 } 1082 1083 .emoji-picker { 1084 + width: min(960px, 94vw); 1085 + height: min(90vh, 820px); 1086 background: var(--bg-secondary); 1087 border: 1px solid var(--border-color); 1088 border-radius: clamp(var(--radius), 2vw, 24px); 1089 box-shadow: 0 32px 80px rgba(0, 0, 0, 0.45); 1090 display: flex; 1091 flex-direction: column; 1092 + gap: 1.25rem; 1093 + padding: clamp(1.25rem, 5vw, 2.5rem); 1094 overflow: hidden; 1095 } 1096 + 1097 1098 .emoji-picker-header { 1099 display: flex; ··· 1136 outline: none; 1137 } 1138 1139 .emoji-search-container { 1140 margin: 0; 1141 } ··· 1171 1172 .emoji-categories { 1173 display: flex; 1174 + gap: 0.75rem; 1175 overflow-x: auto; 1176 padding-bottom: 0.5rem; 1177 border-bottom: 1px solid rgba(255, 255, 255, 0.06); ··· 1209 .emoji-grid { 1210 flex: 1; 1211 display: grid; 1212 + grid-template-columns: repeat(auto-fill, minmax(72px, 1fr)); 1213 + gap: 0.75rem; 1214 padding-right: 0.25rem; 1215 overflow-y: auto; 1216 } 1217 1218 .emoji-option { 1219 + background: transparent; 1220 + border: none; 1221 + font-size: 2.4rem; 1222 cursor: pointer; 1223 + transition: transform 0.15s ease; 1224 display: flex; 1225 align-items: center; 1226 justify-content: center; 1227 width: 100%; 1228 aspect-ratio: 1; 1229 + } 1230 + 1231 + .emoji-option:hover { 1232 + transform: translateY(-3px) scale(1.05); 1233 } 1234 1235 .emoji-option:focus-visible { 1236 + outline: 2px solid var(--accent); 1237 + outline-offset: 6px; 1238 + border-radius: 16px; 1239 } 1240 1241 .emoji-option.custom-emoji img { 1242 + width: 75%; 1243 + height: 75%; 1244 object-fit: contain; 1245 } 1246 1247 @media (max-width: 640px) { 1248 .emoji-picker-overlay { 1249 padding: 0; ··· 1255 height: 100%; 1256 max-height: none; 1257 border-radius: 0; 1258 + padding: 1.5rem 1rem; 1259 + gap: 1rem; 1260 } 1261 1262 .emoji-grid { 1263 + grid-template-columns: repeat(auto-fill, minmax(72px, 1fr)); 1264 + gap: 0.5rem; 1265 } 1266 } 1267 ··· 2051 const emojiGrid = document.getElementById('emoji-grid'); 2052 const selectedEmoji = document.getElementById('selected-emoji'); 2053 const statusInput = document.getElementById('status-input'); 2054 let lastFocusBeforeEmojiPicker = null; 2055 2056 // Clear time picker ··· 2060 const expiresSelect = document.getElementById('expires_in'); 2061 2062 const isEmojiPickerOpen = () => emojiPickerOverlay && !emojiPickerOverlay.classList.contains('hidden'); 2063 2064 const openEmojiPicker = () => { 2065 if (!emojiPickerOverlay || !emojiPicker) return; ··· 2080 if (emojiSearch) emojiSearch.focus(); 2081 }, 60); 2082 } 2083 2084 loadCustomEmojis().then(() => { 2085 loadEmojiCategory('frequent'); ··· 2202 } 2203 2204 statusInput.value = emoji; 2205 closeEmojiPicker(); 2206 checkForChanges(); 2207 }); ··· 2229 // Display the image in the selected emoji area 2230 selectedEmoji.innerHTML = `<img src="${img.src}" alt="${img.alt}" style="width: 100%; height: 100%; object-fit: contain;">`; 2231 statusInput.value = emojiValue; 2232 closeEmojiPicker(); 2233 checkForChanges(); 2234 }); ··· 2247 const emoji = e.target.getAttribute('data-emoji'); 2248 selectedEmoji.textContent = emoji; 2249 statusInput.value = emoji; 2250 closeEmojiPicker(); 2251 checkForChanges(); 2252 }); ··· 2480 } 2481 2482 statusInput.value = emojiValue; 2483 closeEmojiPicker(); 2484 checkForChanges(); 2485 // Clear search when emoji is selected