| 1 | /* calendar.css — this app's own components. |
| 2 | * |
| 3 | * Prefix: cal-. Never rst- (rastrillo's namespace, and a future release |
| 4 | * may claim a name we took) and never estilo- (the suite's, and these |
| 5 | * are not suite components until a second app copies one). |
| 6 | * |
| 7 | * Grammar: the attribute spelling, matching rastrillo v3 — the KIND is |
| 8 | * a bare attribute, a VARIANT is a token in its value matched with ~=, |
| 9 | * and a PART is its own flat attribute. Adopted per screen, never per |
| 10 | * element. |
| 11 | * |
| 12 | * Every colour here is a --rst-* token or derived from one with |
| 13 | * color-mix. Nothing is a literal hex: the theme's own contrast table |
| 14 | * is measured, and a hex dropped in beside it is a value nobody has |
| 15 | * measured against anything. |
| 16 | */ |
| 17 | |
| 18 | /* ── The view switcher ─────────────────────────────────────────── */ |
| 19 | |
| 20 | /* One layout for all four views. Day, Week, Month and Agenda share this |
| 21 | * main box, so switching views never moves the header, the toolbar or |
| 22 | * the grid's edge (test/browser/views.mjs). 90rem, not rst-page's 64rem |
| 23 | * reading column: seven day columns at 64rem are ~134px, too narrow for a |
| 24 | * chip's time and title. rst-page's own inline padding is the gutter, the |
| 25 | * same 1rem as the estilo bar's, so at 1440 the view's edge lines up with |
| 26 | * the bar's brand. */ |
| 27 | [cal-view] { max-width: 90rem; } /* after tokens.css, so it beats rst-page's 64rem */ |
| 28 | |
| 29 | /* The bar is sticky, so an anchor or :target lands below it, not under |
| 30 | * it (shared-chrome spec §5.3). */ |
| 31 | html { scroll-padding-block-start: var(--estilo-bar-block-size); } |
| 32 | |
| 33 | /* The toolbar (layout.html "calendar-toolbar"): the steps at the start, |
| 34 | * the view switcher at the end, one row wherever it fits. Every label in |
| 35 | * it is the same in every view, so nothing in it moves. */ |
| 36 | [cal-bar] { |
| 37 | align-items: center; |
| 38 | display: flex; |
| 39 | flex-wrap: wrap; |
| 40 | gap: var(--rst-sp-3); |
| 41 | justify-content: space-between; |
| 42 | margin-block-end: var(--rst-sp-4); |
| 43 | } |
| 44 | |
| 45 | [cal-bar-steps] { |
| 46 | display: flex; |
| 47 | gap: var(--rst-sp-2); |
| 48 | } |
| 49 | |
| 50 | /* A view tab is as wide as its label in the current tab's weight (600, |
| 51 | * against 550), whether or not it is current: a hidden bold copy of the |
| 52 | * label (the cal-tab attribute) sizes the column. Without it the switcher |
| 53 | * narrowed and widened by a fraction of a pixel with every view, and at |
| 54 | * the end of the row that moved it. */ |
| 55 | [cal-tab] { display: inline-grid; text-align: center; } |
| 56 | [cal-tab]::after { |
| 57 | block-size: 0; |
| 58 | content: attr(cal-tab); |
| 59 | font-weight: 600; |
| 60 | overflow: hidden; |
| 61 | visibility: hidden; |
| 62 | } |
| 63 | |
| 64 | [cal-bar-spacer] { flex: 1 1 auto; } |
| 65 | |
| 66 | /* The day's sentence. It is the page's opening line, so it reads at |
| 67 | * body size rather than as a caption — a summary set in small grey |
| 68 | * type is a summary people learn to skip. */ |
| 69 | [cal-sentence] { |
| 70 | color: var(--rst-text); |
| 71 | margin: 0 0 var(--rst-sp-4); |
| 72 | max-width: 52rem; |
| 73 | } |
| 74 | |
| 75 | /* The agenda's scope line: a caption, not an answer, so it is muted. */ |
| 76 | [cal-sentence-muted] { color: var(--rst-text-muted); } |
| 77 | |
| 78 | /* ── Month ─────────────────────────────────────────────────────── */ |
| 79 | |
| 80 | /* A real table: a month IS tabular data, so the row and column headers |
| 81 | * come free for a screen reader and a scriptless page keeps working |
| 82 | * semantics a div grid would have to rebuild with ARIA. */ |
| 83 | [cal-month] { |
| 84 | border-collapse: collapse; |
| 85 | table-layout: fixed; |
| 86 | width: 100%; |
| 87 | } |
| 88 | |
| 89 | [cal-month] th { |
| 90 | color: var(--rst-text-muted); |
| 91 | font-size: var(--rst-fs-sm); |
| 92 | font-weight: 600; |
| 93 | padding: var(--rst-sp-2); |
| 94 | text-align: start; |
| 95 | } |
| 96 | |
| 97 | [cal-month] td { |
| 98 | border: 1px solid var(--rst-line); |
| 99 | height: 7.5rem; |
| 100 | padding: 0; |
| 101 | vertical-align: top; |
| 102 | } |
| 103 | |
| 104 | [cal-day-cell] { |
| 105 | display: flex; |
| 106 | flex-direction: column; |
| 107 | gap: 2px; |
| 108 | height: 100%; |
| 109 | padding: var(--rst-sp-1); |
| 110 | } |
| 111 | |
| 112 | /* Outside the month: dimmed, but never hidden. A month grid whose |
| 113 | * leading and trailing days are blank makes the first week look |
| 114 | * broken. */ |
| 115 | [cal-day-cell~="outside"] { opacity: 0.55; } |
| 116 | |
| 117 | [cal-day-cell~="weekend"] { background: var(--rst-surface-2); } |
| 118 | |
| 119 | [cal-day-num] { |
| 120 | color: var(--rst-text-muted); |
| 121 | display: block; |
| 122 | font-size: var(--rst-fs-sm); |
| 123 | padding: 2px; |
| 124 | text-decoration: none; |
| 125 | } |
| 126 | |
| 127 | [cal-day-num]:hover { text-decoration: underline; } |
| 128 | |
| 129 | /* Today. Three signals, never colour alone: a filled marker, bold |
| 130 | * weight, and the visually-hidden word "today" in the link's |
| 131 | * accessible name. */ |
| 132 | [cal-day-num~="today"] { |
| 133 | background: var(--rst-accent); |
| 134 | border-radius: var(--rst-radius-pill); |
| 135 | color: var(--rst-on-accent); |
| 136 | font-weight: 700; |
| 137 | inline-size: 1.6rem; |
| 138 | text-align: center; |
| 139 | } |
| 140 | |
| 141 | [cal-more] { |
| 142 | color: var(--rst-text-muted); |
| 143 | font-size: var(--rst-fs-xs); |
| 144 | padding-inline-start: 2px; |
| 145 | } |
| 146 | |
| 147 | /* ── Chips ─────────────────────────────────────────────────────── */ |
| 148 | |
| 149 | /* One event, wherever it appears. The hue is set inline per calendar |
| 150 | * as --cal-hue, and every colour below is mixed from it against a |
| 151 | * theme token — so a chip stays legible in both schemes without the |
| 152 | * calendar's colour being a measured value of its own. */ |
| 153 | [cal-chip] { |
| 154 | align-items: baseline; |
| 155 | background: color-mix(in oklab, hsl(var(--cal-hue, 220) 60% 50%) 14%, var(--rst-surface)); |
| 156 | border-inline-start: 3px solid hsl(var(--cal-hue, 220) 55% 45%); |
| 157 | border-radius: 0; |
| 158 | color: var(--rst-text); |
| 159 | display: flex; |
| 160 | font-size: var(--rst-fs-sm); |
| 161 | gap: var(--rst-sp-1); |
| 162 | overflow: hidden; |
| 163 | padding: 2px var(--rst-sp-1); |
| 164 | text-decoration: none; |
| 165 | white-space: nowrap; |
| 166 | } |
| 167 | |
| 168 | [cal-chip]:hover { text-decoration: underline; } |
| 169 | |
| 170 | [cal-chip-time] { |
| 171 | color: var(--rst-text-muted); |
| 172 | flex: none; |
| 173 | font-variant-numeric: tabular-nums; |
| 174 | } |
| 175 | |
| 176 | [cal-chip-title] { |
| 177 | overflow: hidden; |
| 178 | text-overflow: ellipsis; |
| 179 | } |
| 180 | |
| 181 | /* The past is dimmed AND written in the past tense — the tense is in |
| 182 | * the markup, so the meaning survives greyscale and a stylesheet that |
| 183 | * failed to load. */ |
| 184 | [cal-chip~="past"] { opacity: 0.6; } |
| 185 | |
| 186 | /* A cancelled event is struck through and says "Cancelled" in its own |
| 187 | * text. Never the line alone. */ |
| 188 | [cal-chip~="cancelled"] [cal-chip-title] { text-decoration: line-through; } |
| 189 | |
| 190 | /* A note is a thing that is true of a day without occupying it, so it |
| 191 | * is drawn as an outline rather than a filled block. */ |
| 192 | [cal-chip~="note"] { |
| 193 | background: transparent; |
| 194 | border: 1px dashed color-mix(in oklab, hsl(var(--cal-hue, 220) 55% 45%) 60%, var(--rst-line)); |
| 195 | border-inline-start-width: 3px; |
| 196 | } |
| 197 | |
| 198 | /* A commitment is yours rather than shared: a quieter fill than a |
| 199 | * meeting, so a day of your own work does not read as a day of |
| 200 | * meetings. */ |
| 201 | [cal-chip~="commitment"] { |
| 202 | background: color-mix(in oklab, hsl(var(--cal-hue, 220) 60% 50%) 7%, var(--rst-surface)); |
| 203 | } |
| 204 | |
| 205 | [cal-chip~="tentative"] { |
| 206 | border-inline-start-style: dotted; |
| 207 | } |
| 208 | |
| 209 | /* ── Day and week columns ──────────────────────────────────────── */ |
| 210 | |
| 211 | [cal-grid] { |
| 212 | display: grid; |
| 213 | gap: 0; |
| 214 | grid-template-columns: 3.5rem 1fr; |
| 215 | /* The grid scrolls inside the first screen, but never below 24rem: on |
| 216 | * a short window (or a phone on its side) 100vh - 20rem is a sliver. */ |
| 217 | max-block-size: max(24rem, calc(100vh - 20rem)); |
| 218 | overflow: auto; |
| 219 | } |
| 220 | |
| 221 | [cal-week-grid] { |
| 222 | display: grid; |
| 223 | gap: 0; |
| 224 | grid-template-columns: 3.5rem repeat(7, minmax(6rem, 1fr)); |
| 225 | } |
| 226 | |
| 227 | [cal-week-scroll] { |
| 228 | /* The grid scrolls inside the first screen, but never below 24rem: on |
| 229 | * a short window (or a phone on its side) 100vh - 20rem is a sliver. */ |
| 230 | max-block-size: max(24rem, calc(100vh - 20rem)); |
| 231 | overflow: auto; |
| 232 | } |
| 233 | |
| 234 | [cal-hours] { |
| 235 | border-inline-end: 1px solid var(--rst-line); |
| 236 | position: relative; |
| 237 | } |
| 238 | |
| 239 | [cal-hour] { |
| 240 | block-size: 3rem; |
| 241 | color: var(--rst-text-faint); |
| 242 | font-size: var(--rst-fs-xs); |
| 243 | font-variant-numeric: tabular-nums; |
| 244 | padding-inline-end: var(--rst-sp-2); |
| 245 | text-align: end; |
| 246 | /* The label sits at the line it names, not in the box below it. */ |
| 247 | transform: translateY(-0.5em); |
| 248 | } |
| 249 | |
| 250 | /* The day column. 72rem is 24 hours at 3rem each, so an hour label and |
| 251 | * the grid line beside it stay in step. */ |
| 252 | [cal-column] { |
| 253 | block-size: 72rem; |
| 254 | border-inline-end: 1px solid var(--rst-line); |
| 255 | position: relative; |
| 256 | } |
| 257 | |
| 258 | [cal-column-head] { |
| 259 | /* A fixed height, so the all-day row can stick exactly beneath it. */ |
| 260 | align-items: center; |
| 261 | block-size: var(--cal-week-head-size); |
| 262 | border-block-end: 1px solid var(--rst-line); |
| 263 | box-sizing: border-box; |
| 264 | display: flex; |
| 265 | justify-content: center; |
| 266 | padding: 0 var(--rst-sp-2); |
| 267 | position: sticky; |
| 268 | text-align: center; |
| 269 | /* 0, not --estilo-bar-block-size: the heads stick inside |
| 270 | * [cal-week-scroll], which is their own scroll box, so the sticky bar |
| 271 | * above the page never overlaps them. The bar's offset here left a 52px |
| 272 | * band of hour rows painted above the day names. */ |
| 273 | top: 0; |
| 274 | z-index: 2; |
| 275 | background: var(--rst-bg); |
| 276 | } |
| 277 | |
| 278 | [cal-column-head~="today"] { font-weight: 700; } |
| 279 | |
| 280 | [cal-week-grid] { --cal-week-head-size: 2.5rem; } |
| 281 | |
| 282 | /* The hour rules, drawn as a repeating background so 24 of them cost |
| 283 | * one declaration rather than 24 elements. */ |
| 284 | [cal-column-rules] { |
| 285 | background-image: repeating-linear-gradient( |
| 286 | to bottom, |
| 287 | var(--rst-line) 0, |
| 288 | var(--rst-line) 1px, |
| 289 | transparent 1px, |
| 290 | transparent 3rem |
| 291 | ); |
| 292 | inset: 0; |
| 293 | position: absolute; |
| 294 | } |
| 295 | |
| 296 | /* An empty half hour, as a link. This is what makes "click a time to |
| 297 | * create an event" work with no JavaScript: 48 ordinary links per day, |
| 298 | * each with a screen-reader label naming its time. */ |
| 299 | [cal-slot] { |
| 300 | display: block; |
| 301 | inset-inline: 0; |
| 302 | position: absolute; |
| 303 | } |
| 304 | |
| 305 | [cal-slot]:hover { |
| 306 | background: color-mix(in oklab, var(--rst-accent) 10%, transparent); |
| 307 | } |
| 308 | |
| 309 | [cal-slot]:focus-visible { |
| 310 | outline: 2px solid var(--rst-accent); |
| 311 | outline-offset: -2px; |
| 312 | } |
| 313 | |
| 314 | /* A placed event. top/height/left/width arrive as inline style from |
| 315 | * the server's own packer — rastrillo's CSP allows inline styles, so |
| 316 | * this needs no exception. */ |
| 317 | [cal-placed] { |
| 318 | position: absolute; |
| 319 | z-index: 1; |
| 320 | } |
| 321 | |
| 322 | [cal-placed] [cal-chip] { |
| 323 | block-size: 100%; |
| 324 | align-items: flex-start; |
| 325 | flex-direction: column; |
| 326 | gap: 0; |
| 327 | white-space: normal; |
| 328 | } |
| 329 | |
| 330 | /* Under an hour there is room for one line, so time and title share |
| 331 | * it. Stacked, the title is the second line and the first thing the |
| 332 | * chip's own overflow cuts off. */ |
| 333 | [cal-placed~="short"] [cal-chip] { |
| 334 | align-items: baseline; |
| 335 | flex-direction: row; |
| 336 | gap: var(--rst-sp-1); |
| 337 | line-height: 1.15; |
| 338 | padding-block: 0; |
| 339 | white-space: nowrap; |
| 340 | } |
| 341 | |
| 342 | /* The all-day strip above the timed grid. All-day events are not |
| 343 | * packed into the column: giving a birthday a height would make it |
| 344 | * look like a 24-hour meeting. */ |
| 345 | [cal-allday] { |
| 346 | border-block-end: 1px solid var(--rst-line); |
| 347 | display: flex; |
| 348 | flex-wrap: wrap; |
| 349 | gap: var(--rst-sp-1); |
| 350 | padding: var(--rst-sp-2); |
| 351 | } |
| 352 | |
| 353 | /* In the week the all-day strip is one ROW of the grid, a cell per |
| 354 | * day, so every day's cell is as tall as the busiest one and the |
| 355 | * timed columns below all start on the same line. */ |
| 356 | [cal-allday="week"] { |
| 357 | align-content: flex-start; |
| 358 | border-inline-end: 1px solid var(--rst-line); |
| 359 | flex-direction: column; |
| 360 | flex-wrap: nowrap; |
| 361 | min-inline-size: 0; |
| 362 | padding: var(--rst-sp-1); |
| 363 | } |
| 364 | |
| 365 | /* The week's all-day row sticks under the day headings. The grid opens |
| 366 | * scrolled to working hours, and a row that scrolled away with midnight |
| 367 | * hid every all-day event from the first screen. */ |
| 368 | [cal-allday="week"], [cal-allday-label] { |
| 369 | background: var(--rst-bg); |
| 370 | position: sticky; |
| 371 | top: var(--cal-week-head-size); |
| 372 | z-index: 2; |
| 373 | } |
| 374 | |
| 375 | [cal-allday-label] { |
| 376 | border-block-end: 1px solid var(--rst-line); |
| 377 | border-inline-end: 1px solid var(--rst-line); |
| 378 | color: var(--rst-text-faint); |
| 379 | font-size: var(--rst-fs-xs); |
| 380 | padding: var(--rst-sp-1) var(--rst-sp-2) 0 0; |
| 381 | text-align: end; |
| 382 | } |
| 383 | |
| 384 | /* ── The whisper ───────────────────────────────────────────────── */ |
| 385 | |
| 386 | /* An event authored in another zone. The primary time is always the |
| 387 | * reader's own; this is a note beside it, never the headline. */ |
| 388 | [cal-elsewhere] { |
| 389 | color: var(--rst-text-faint); |
| 390 | font-size: var(--rst-fs-xs); |
| 391 | } |
| 392 | |
| 393 | /* ── Narrow screens ────────────────────────────────────────────── */ |
| 394 | |
| 395 | /* At 320px the month reflows to a stacked list of days. A seven-column |
| 396 | * grid at that width is seven columns of nothing. |
| 397 | * |
| 398 | * Week keeps its two dimensions and scrolls sideways: a week view IS a |
| 399 | * two-dimensional comparison, and flattening it to a list makes it the |
| 400 | * agenda, which already exists. */ |
| 401 | @media (max-width: 40rem) { |
| 402 | [cal-month], [cal-month] thead { display: block; } |
| 403 | [cal-month] thead { display: none; } |
| 404 | [cal-month] tbody, [cal-month] tr { display: block; } |
| 405 | [cal-month] td { display: block; height: auto; min-height: 2.5rem; } |
| 406 | /* A day with nothing in it is not worth a row of its own on a |
| 407 | * phone. */ |
| 408 | [cal-month] td:not(:has([cal-chip])):not(:has([cal-day-num~="today"])) { display: none; } |
| 409 | [cal-day-cell] { flex-direction: row; align-items: baseline; gap: var(--rst-sp-2); } |
| 410 | |
| 411 | [cal-week-scroll] { overflow-x: auto; } |
| 412 | [cal-week-grid] { min-inline-size: 48rem; } |
| 413 | } |
| 414 | |
| 415 | /* ── Motion ────────────────────────────────────────────────────── */ |
| 416 | |
| 417 | @media (prefers-reduced-motion: reduce) { |
| 418 | [cal-slot], [cal-chip] { transition: none; } |
| 419 | } |
| 420 | |
| 421 | /* ── Quick add ─────────────────────────────────────────────────── */ |
| 422 | |
| 423 | /* The header's action group. Quick add sits beside New event rather |
| 424 | * than replacing it: one is the fast path, the other is the one that |
| 425 | * shows every field. */ |
| 426 | [cal-header-actions] { |
| 427 | align-items: center; |
| 428 | display: flex; |
| 429 | flex-wrap: wrap; |
| 430 | gap: var(--rst-sp-2); |
| 431 | } |
| 432 | |
| 433 | /* The disclosure is a <details>, so it opens with no script at all. |
| 434 | * Everything below is appearance; none of it is behaviour. */ |
| 435 | [cal-quick] { position: relative; } |
| 436 | |
| 437 | /* The closed state reads as a small text input, because that is what it |
| 438 | * behaves like — click it and type. The marker is removed in both |
| 439 | * spellings: Safari still wants the -webkit- pseudo-element and leaving |
| 440 | * it in puts a disclosure triangle inside what looks like a field. */ |
| 441 | [cal-quick] > summary { |
| 442 | align-items: center; |
| 443 | background: var(--rst-surface); |
| 444 | border: 1px solid var(--rst-line); |
| 445 | border-radius: var(--rst-radius-sm); |
| 446 | color: var(--rst-text-muted); |
| 447 | cursor: text; |
| 448 | display: flex; |
| 449 | font-size: var(--rst-fs-sm); |
| 450 | gap: var(--rst-sp-1); |
| 451 | list-style: none; |
| 452 | min-width: 12rem; |
| 453 | padding: var(--rst-sp-2) var(--rst-sp-3); |
| 454 | } |
| 455 | |
| 456 | [cal-quick] > summary::-webkit-details-marker { display: none; } |
| 457 | |
| 458 | [cal-quick] > summary:hover { border-color: var(--rst-accent); } |
| 459 | |
| 460 | [cal-quick][open] > summary { |
| 461 | border-color: var(--rst-accent); |
| 462 | color: var(--rst-text); |
| 463 | } |
| 464 | |
| 465 | /* The popover. Absolute so opening it does not push the calendar down |
| 466 | * the page — the grid behind stays exactly where it was, which is what |
| 467 | * makes this feel like a panel rather than a page change. */ |
| 468 | [cal-quick-form] { |
| 469 | background: var(--rst-surface); |
| 470 | border: 1px solid var(--rst-line); |
| 471 | /* A float, so the suite's float radius and shadow (estilo menu.css; |
| 472 | * shared-chrome spec §11), not a hand-built 6px and shadow of its own. */ |
| 473 | border-radius: var(--estilo-float-radius); |
| 474 | box-shadow: var(--rst-shadow-pop); |
| 475 | /* Border-box, here and on the textarea below: both are width: 100% |
| 476 | * on a phone, and content-box added the padding and border on top, |
| 477 | * so the open panel ran ~15px past the right edge at 390px and the |
| 478 | * page scrolled sideways. TestQuickAddOnANarrowScreen (make browser) |
| 479 | * measures it. */ |
| 480 | box-sizing: border-box; |
| 481 | /* Anchored to the END edge, so the panel opens back across the page |
| 482 | * instead of off the side of it. The box sits at the far right of the |
| 483 | * header, beside New event: anchored at the start edge a 30rem panel |
| 484 | * hangs ~28px past a 1280px viewport, which every string assertion in |
| 485 | * the Go suite happily passes. The browser gate measured it. */ |
| 486 | inset-inline-end: 0; |
| 487 | margin-block-start: var(--rst-sp-1); |
| 488 | padding: var(--rst-sp-3); |
| 489 | position: absolute; |
| 490 | top: 100%; |
| 491 | width: min(30rem, 88vw); |
| 492 | z-index: 30; |
| 493 | } |
| 494 | |
| 495 | [cal-quick-text] { |
| 496 | background: var(--rst-bg); |
| 497 | border: 1px solid var(--rst-line); |
| 498 | border-radius: var(--rst-radius-sm); |
| 499 | box-sizing: border-box; |
| 500 | color: var(--rst-text); |
| 501 | display: block; |
| 502 | font: inherit; |
| 503 | padding: var(--rst-sp-2); |
| 504 | resize: vertical; |
| 505 | width: 100%; |
| 506 | } |
| 507 | |
| 508 | /* iOS zooms the page into any text control under 16px, and tokens.css |
| 509 | * brings controls up to 16px where the pointer is coarse. The box above |
| 510 | * sets `font: inherit` with a selector that outweighs tokens.css's bare |
| 511 | * `textarea`, and fell back to 14px. Same rule, same max(), restated |
| 512 | * here at its weight. TestNoZoomOnTap (make browser) measures it. */ |
| 513 | @media (pointer: coarse) { |
| 514 | [cal-quick-text] { |
| 515 | font-size: max(1rem, 1em); |
| 516 | } |
| 517 | } |
| 518 | |
| 519 | [cal-quick-pane] { |
| 520 | background: var(--rst-surface-2); |
| 521 | border-radius: var(--rst-radius-sm); |
| 522 | font-size: var(--rst-fs-sm); |
| 523 | margin-block: var(--rst-sp-2); |
| 524 | padding: var(--rst-sp-2) var(--rst-sp-3); |
| 525 | } |
| 526 | |
| 527 | [cal-quick-blank] { color: var(--rst-text-muted); margin: 0; } |
| 528 | |
| 529 | [cal-quick-detail] { margin: 0; } |
| 530 | |
| 531 | [cal-quick-row] { |
| 532 | display: grid; |
| 533 | gap: var(--rst-sp-2); |
| 534 | grid-template-columns: 8rem 1fr; |
| 535 | padding-block: var(--rst-sp-1); |
| 536 | } |
| 537 | |
| 538 | [cal-quick-row] dt { color: var(--rst-text-muted); margin: 0; } |
| 539 | [cal-quick-row] dd { margin: 0; } |
| 540 | |
| 541 | /* The caveat rides in the LABEL. Somebody who reads only the left |
| 542 | * column still learns that nothing was emailed. */ |
| 543 | [cal-quick-caveat] { color: var(--rst-text-faint); font-size: var(--rst-fs-xs); } |
| 544 | |
| 545 | [cal-quick-people] { list-style: none; margin: 0; padding: 0; } |
| 546 | |
| 547 | [cal-quick-notes] { |
| 548 | color: var(--rst-text-muted); |
| 549 | font-size: var(--rst-fs-xs); |
| 550 | margin: var(--rst-sp-2) 0 0; |
| 551 | padding-inline-start: var(--rst-sp-4); |
| 552 | } |
| 553 | |
| 554 | [cal-quick-actions] { |
| 555 | align-items: center; |
| 556 | display: flex; |
| 557 | flex-wrap: wrap; |
| 558 | gap: var(--rst-sp-2); |
| 559 | } |
| 560 | |
| 561 | [cal-quick-hint] { |
| 562 | color: var(--rst-text-faint); |
| 563 | font-size: var(--rst-fs-xs); |
| 564 | flex: 1 1 12rem; |
| 565 | } |
| 566 | |
| 567 | /* Narrow: the popover stops floating and becomes part of the page. A |
| 568 | * 30rem panel pinned to a header on a 320px screen is off the side of |
| 569 | * it. */ |
| 570 | @media (max-width: 40rem) { |
| 571 | [cal-header-actions] { width: 100%; } |
| 572 | [cal-quick] { flex: 1 1 100%; } |
| 573 | [cal-quick] > summary { min-width: 0; } |
| 574 | [cal-quick-form] { |
| 575 | box-shadow: none; |
| 576 | position: static; |
| 577 | width: 100%; |
| 578 | } |
| 579 | [cal-quick-row] { grid-template-columns: 1fr; gap: 0; } |
| 580 | } |
| 581 | |
| 582 | /* The keyboard hint. Revealed by quickadd.js, never by the template: |
| 583 | * with no script Enter is just a newline, and advertising a shortcut |
| 584 | * the page cannot honour is worse than staying quiet. */ |
| 585 | [cal-quick-key] { |
| 586 | color: var(--rst-text-faint); |
| 587 | font-size: var(--rst-fs-xs); |
| 588 | } |
| 589 | |
| 590 | /* ── Booking pages, the host's side ─────────────────────────────── */ |
| 591 | |
| 592 | [cal-book-pane] { |
| 593 | background: var(--rst-surface-2); |
| 594 | border-radius: var(--rst-radius-sm); |
| 595 | font-size: var(--rst-fs-sm); |
| 596 | margin-block: var(--rst-sp-2) var(--rst-sp-4); |
| 597 | padding: var(--rst-sp-2) var(--rst-sp-3); |
| 598 | } |
| 599 | |
| 600 | [cal-book-blank] { color: var(--rst-text-muted); margin: 0; } |
| 601 | |
| 602 | [cal-book-lines] { margin: 0; padding-inline-start: var(--rst-sp-4); } |
| 603 | |
| 604 | /* Problems first, and unmistakably: the sentence cannot be saved while |
| 605 | * one stands. */ |
| 606 | [cal-book-problems] { |
| 607 | color: var(--rst-tone-negative-fg); |
| 608 | margin: 0 0 var(--rst-sp-2); |
| 609 | padding-inline-start: var(--rst-sp-4); |
| 610 | } |
| 611 | |
| 612 | [cal-book-page] { margin-block-end: var(--rst-sp-4); } |
| 613 | |
| 614 | [cal-book-page-body] { padding: var(--rst-sp-3); } |
| 615 | |
| 616 | [cal-book-url] { font-size: var(--rst-fs-sm); overflow-wrap: anywhere; } |
| 617 | |
| 618 | [cal-book-page-actions] { |
| 619 | align-items: flex-start; |
| 620 | display: flex; |
| 621 | flex-wrap: wrap; |
| 622 | gap: var(--rst-sp-3); |
| 623 | margin-block-start: var(--rst-sp-3); |
| 624 | } |
| 625 | |
| 626 | /* The publish sentence sits above its own button and nowhere else. */ |
| 627 | [cal-book-caveat] { |
| 628 | color: var(--rst-text-muted); |
| 629 | font-size: var(--rst-fs-xs); |
| 630 | margin: 0 0 var(--rst-sp-2); |
| 631 | max-width: 40rem; |
| 632 | } |
| 633 | |
| 634 | [cal-booked-when] { margin: 0; } |
| 635 | |
| 636 | /* ── Booking, the stranger's side ────────────────────────────────── */ |
| 637 | |
| 638 | [cal-book-zone-form] { |
| 639 | align-items: center; |
| 640 | display: flex; |
| 641 | flex-wrap: wrap; |
| 642 | gap: var(--rst-sp-2); |
| 643 | margin-block-end: var(--rst-sp-4); |
| 644 | } |
| 645 | |
| 646 | [cal-book-days] { list-style: none; margin: 0; padding: 0; } |
| 647 | [cal-book-days] li { padding-block: var(--rst-sp-1); } |
| 648 | [cal-book-days] small { color: var(--rst-text-muted); margin-inline-start: var(--rst-sp-2); } |
| 649 | |
| 650 | /* Each slot is its own form: a button that posts an instant. */ |
| 651 | [cal-book-slots] { |
| 652 | display: flex; |
| 653 | flex-wrap: wrap; |
| 654 | gap: var(--rst-sp-2); |
| 655 | margin-block: var(--rst-sp-3); |
| 656 | } |
| 657 | |
| 658 | [cal-book-details] { padding: var(--rst-sp-3); } |
| 659 | |
| 660 | [cal-book-manage-actions] { |
| 661 | align-items: center; |
| 662 | display: flex; |
| 663 | flex-wrap: wrap; |
| 664 | gap: var(--rst-sp-2); |
| 665 | margin-block: var(--rst-sp-4) var(--rst-sp-2); |
| 666 | } |
| 667 | |
| 668 | /* The booker's closing row (rastrillo's form-foot, reached through our |
| 669 | * own form's attribute: this file names no rst- selector). form-foot |
| 670 | * draws its buttons at the lg size, and on a phone "Hold this time" and |
| 671 | * "Pick another time" no longer fit side by side; without wrap the row |
| 672 | * squeezes them into two- and three-line labels. Wrapping puts the |
| 673 | * second button on a row of its own instead. |
| 674 | * TestFormFootOnANarrowScreen (make browser) measures it. */ |
| 675 | [cal-book-details] > div:has(> button[type="submit"]) { |
| 676 | flex-wrap: wrap; |
| 677 | } |
| 678 | |
| 679 | /* ── Layout that used to be style attributes ────────────────────────── |
| 680 | * rastrillo's CSP (fb806a2) refuses style attributes by default. These |
| 681 | * were constant, so they live here; what is still an attribute is a |
| 682 | * per-event or per-calendar value (serving.go says which, and why the |
| 683 | * policy keeps 'unsafe-inline' for them). */ |
| 684 | [cal-day-chips] { |
| 685 | display: flex; |
| 686 | flex-direction: column; |
| 687 | gap: var(--rst-sp-1); |
| 688 | padding: var(--rst-sp-3); |
| 689 | } |
| 690 | [cal-box-body] { padding: var(--rst-sp-3); } |
| 691 | [cal-reminder] { |
| 692 | display: flex; |
| 693 | align-items: center; |
| 694 | gap: var(--rst-sp-2); |
| 695 | } |
| 696 | [cal-reminder-add] { |
| 697 | display: flex; |
| 698 | align-items: flex-end; |
| 699 | gap: var(--rst-sp-2); |
| 700 | margin-top: var(--rst-sp-3); |
| 701 | } |
| 702 | [cal-feed-new] { margin-top: var(--rst-sp-4); } |
| 703 | /* A calendar's swatch on the Calendars list: its hue, the one value the |
| 704 | * attribute still carries. */ |
| 705 | [cal-dot] { |
| 706 | background: hsl(var(--cal-hue, 220) 55% 45%); |
| 707 | border-radius: var(--rst-radius-pill); |
| 708 | inline-size: 12px; |
| 709 | block-size: 12px; |
| 710 | } |
| 711 | |