xref: /plugin/annotations/style.css (revision 86c7806d6d41bce7c6d00acbee1316c62845cabb)
1/**
2 * Annotations plugin — stylesheet.
3 *
4 * Colour tokens use DokuWiki's __xxx__ replacement syntax so the theme
5 * controls the palette.  Hard-coded colours are limited to the annotation-
6 * specific highlights (amber / green) which are intentionally opinionated.
7 *
8 * FF78 ESR safe: no :has(), :not() with selectors, aspect-ratio, container
9 * queries, or CSS nesting.
10 *
11 * The two highlight hues are driven by CSS custom properties that action.php
12 * injects from the color_open / color_resolved config (as "r,g,b" triplets);
13 * every fill/border/marker/pill tint below is that hue at a different opacity.
14 * The :root fallbacks here keep the built-in amber/green palette when the
15 * injected <style> is absent (e.g. annotations off, or a stripped template).
16 *
17 * NOTE: every rgba() that contains var() is LESS-escaped as ~"rgba(var(…), a)".
18 * DokuWiki compiles plugin CSS through lesserphp, which otherwise evaluates
19 * rgba() at compile time, reads var() as 0, and bakes the colour to #000000.
20 * The escape makes lesserphp emit the declaration verbatim so the browser
21 * resolves the custom property at render time. Keep new var()-based colours
22 * escaped the same way.
23 */
24
25:root {
26    --ann-open-rgb: var(--ann-open-rgb);     /* amber — open / unresolved */
27    --ann-resolved-rgb: var(--ann-resolved-rgb); /* green — resolved */
28}
29
30/* =========================================================================
31   Highlight spans
32   ========================================================================= */
33
34.ann-highlight-open {
35    background-color: ~"rgba(var(--ann-open-rgb), 0.35)";   /* amber-300 @ 35% */
36    border-bottom: 2px solid ~"rgba(var(--ann-open-rgb), 0.7)";
37    border-radius: 2px;
38    cursor: pointer;
39    transition: background-color 0.15s ease;
40}
41
42.ann-highlight-open:hover {
43    background-color: ~"rgba(var(--ann-open-rgb), 0.55)";
44}
45
46.ann-highlight-resolved {
47    background-color: ~"rgba(var(--ann-resolved-rgb), 0.30)";  /* green-300 @ 30% */
48    border-bottom: 2px solid ~"rgba(var(--ann-resolved-rgb), 0.6)";
49    border-radius: 2px;
50    cursor: pointer;
51    transition: background-color 0.15s ease;
52}
53
54.ann-highlight-resolved:hover {
55    background-color: ~"rgba(var(--ann-resolved-rgb), 0.50)";
56}
57
58/* =========================================================================
59   Counter bar
60   ========================================================================= */
61
62#ann-counter-bar {
63    display: flex;
64    align-items: center;
65    flex-wrap: wrap;
66    gap: 0.5em;
67    padding: 0.4em 0.75em;
68    margin-bottom: 0.75em;
69    background: __background_alt__;
70    border: 1px solid __border__;
71    border-radius: 4px;
72    color: __text__;
73}
74
75.ann-orphan-link {
76    color: __link__;
77    text-decoration: underline;
78    cursor: pointer;
79}
80
81/* =========================================================================
82   Gutter markers
83   ========================================================================= */
84
85/* Markers are appended to document.body as position:absolute elements so
86   that no template overflow:hidden can clip them. Top/left are set inline
87   via JS using getBoundingClientRect() + scroll offsets. All markers share
88   the same X position (left edge of the .page column) so they form a tidy
89   vertical column in the document margin. */
90.ann-gutter-marker {
91    position: absolute;
92    padding: 0;
93    border: none;
94    background: none;
95    color: ~"rgba(var(--ann-open-rgb), 0.8)"; /* amber — open */
96    cursor: pointer;
97    display: flex;
98    align-items: center;
99    justify-content: center;
100    z-index: 1000;
101    transition: color 0.15s ease, transform 0.12s ease;
102}
103
104/* Suppress any UA button background on all interactive states. */
105.ann-gutter-marker:hover,
106.ann-gutter-marker:focus,
107.ann-gutter-marker:active {
108    background: none;
109    outline: none;
110    box-shadow: none;
111}
112
113.ann-gutter-marker:hover {
114    color: ~"rgba(var(--ann-open-rgb), 1)";
115}
116
117.ann-gutter-marker[data-status="resolved"] {
118    color: ~"rgba(var(--ann-resolved-rgb), 0.8)"; /* green — resolved */
119}
120
121.ann-gutter-marker[data-status="resolved"]:hover {
122    color: ~"rgba(var(--ann-resolved-rgb), 1)";
123}
124
125/* =========================================================================
126   Annotation panel
127   ========================================================================= */
128
129.ann-panel {
130    margin: 0.75em 0 1em;
131    border: 1px solid __border__;
132    border-left: 3px solid ~"rgba(var(--ann-open-rgb), 0.7)";
133    background: __background_alt__;
134    box-shadow: 0 2px 8px rgba(0,0,0,0.10);
135    contain: layout;
136}
137
138/* =========================================================================
139   Thread entries (annotation + replies)
140   ========================================================================= */
141
142.ann-thread-entry {
143    padding: 0.65em 0.85em;
144    border-bottom: 1px solid __border__;
145}
146
147.ann-thread-entry:last-of-type {
148    border-bottom: none;
149}
150
151/* Root annotation entry: lighter background so it stands out from the panel. */
152.ann-thread-entry.ann-annotation {
153    background: __background__;
154}
155
156/* Reply entries: inset card with the same lighter background. */
157.ann-reply {
158    margin-left: 1.5em;
159    background: __background__;
160    border-left: 2px solid __border__;
161}
162
163/* =========================================================================
164   Meta row (avatar, author, time, status, close button)
165   ========================================================================= */
166
167.ann-meta {
168    display: flex;
169    align-items: center;
170    gap: 0.4em;
171    margin-bottom: 0.35em;
172    flex-wrap: wrap;
173}
174
175.ann-avatar {
176    display: inline-flex;
177    align-items: center;
178    justify-content: center;
179    width: 1.8em;
180    height: 1.8em;
181    border-radius: 50%;
182    background: __link__;
183    color: #fff;
184    font-size: 0.75em;
185    font-weight: 700;
186    flex-shrink: 0;
187}
188
189.ann-author {
190    font-weight: 600;
191    color: __text__;
192}
193
194.ann-time {
195    color: __text_alt__;
196    font-size: 0.85em;
197}
198
199.ann-status {
200    display: inline-block;
201    padding: 0.1em 0.45em;
202    border-radius: 10em;
203    font-size: 0.78em;
204    font-weight: 600;
205    letter-spacing: 0.02em;
206}
207
208.ann-status-open {
209    background: ~"rgba(var(--ann-open-rgb), 0.25)";
210    color: #92400e;
211    border: 1px solid ~"rgba(var(--ann-open-rgb), 0.5)";
212}
213
214.ann-status-resolved {
215    background: ~"rgba(var(--ann-resolved-rgb), 0.25)";
216    color: #166534;
217    border: 1px solid ~"rgba(var(--ann-resolved-rgb), 0.4)";
218}
219
220/* =========================================================================
221   Body text and quoted selection
222   ========================================================================= */
223
224.ann-body {
225    white-space: pre-wrap;
226    word-break: break-word;
227    color: __text__;
228    margin-bottom: 0.4em;
229}
230
231.ann-quote {
232    margin-bottom: 0.65em;
233    padding: 0.25em 0.6em;
234    border-left: 3px solid ~"rgba(var(--ann-open-rgb), 0.6)";
235    color: __text__;
236    font-style: italic;
237    font-size: 0.9em;
238    background: ~"rgba(var(--ann-open-rgb), 0.08)";
239    border-radius: 0 2px 2px 0;
240}
241
242/* =========================================================================
243   Action buttons row
244   ========================================================================= */
245
246.ann-actions {
247    display: flex;
248    gap: 0.5em;
249    flex-wrap: wrap;
250    margin-top: 0.65em;
251}
252
253.ann-btn {
254    display: inline-block;
255    padding: 0.2em 0.55em;
256    font-size: 0.85em;
257    border: 1px solid __border__;
258    border-radius: 3px;
259    background: __background__ !important;
260    color: __text__;
261    cursor: pointer;
262    line-height: 1.4;
263    transition: background-color 0.12s ease;
264}
265
266.ann-btn:hover {
267    background: __background_alt__ !important;
268}
269
270.ann-btn-primary {
271    background: __link__ !important;
272    border-color: __link__;
273    color: #fff;
274}
275
276.ann-btn-primary:hover {
277    background: __link__ !important;
278    color: #fff;
279    opacity: 0.88;
280}
281
282/* Disabled state: prevent UA-stylesheet background overrides and ensure
283   primary buttons keep their link colour. */
284.ann-btn:disabled,
285.ann-btn[disabled] {
286    background: __background__;
287    opacity: 0.55;
288    cursor: not-allowed;
289}
290
291.ann-btn:disabled:hover,
292.ann-btn[disabled]:hover {
293    background: __background__;
294}
295
296.ann-btn-primary:disabled,
297.ann-btn-primary[disabled] {
298    background: __link__;
299    color: #fff;
300}
301
302.ann-btn-primary:disabled:hover,
303.ann-btn-primary[disabled]:hover {
304    background: __link__;
305    opacity: 0.55;
306}
307
308/* Spinner shown while an AJAX request is in flight (set by setBusy()).
309   The translate(-50%,-50%) is baked into every keyframe so it stays perfectly
310   centred regardless of sub-pixel rounding and never jigles. */
311@keyframes ann-spin {
312    from { transform: translate(-50%, -50%) rotate(0deg); }
313    to   { transform: translate(-50%, -50%) rotate(360deg); }
314}
315
316.ann-btn-busy {
317    position: relative;
318    color: transparent;
319}
320
321.ann-btn-busy::after {
322    content: '';
323    position: absolute;
324    top: 50%;
325    left: 50%;
326    width: 0.75em;
327    height: 0.75em;
328    border: 2px solid rgba(0, 0, 0, 0.25);
329    border-top-color: rgba(0, 0, 0, 0.75);
330    border-radius: 50%;
331    animation: ann-spin 0.6s linear infinite;
332}
333
334.ann-btn-primary.ann-btn-busy::after {
335    border-color: rgba(255, 255, 255, 0.35);
336    border-top-color: rgba(255, 255, 255, 0.9);
337}
338
339.ann-btn-danger {
340    border-color: #dc2626;
341    color: #dc2626;
342}
343
344.ann-btn-danger:hover {
345    background: rgba(220, 38, 38, 0.08);
346}
347
348.ann-btn-admin {
349    border-color: __border__;
350    color: __text__;
351}
352
353/* Close button — lives inside .ann-meta, pushed right via margin-left:auto (set in JS). */
354.ann-close {
355    font-size: 1.35em;
356    line-height: 1;
357    padding: 0.05em 0.3em;
358    border: none;
359    background: none !important;
360    color: __text_alt__;
361    cursor: pointer;
362    flex-shrink: 0;
363}
364
365.ann-close:hover {
366    color: __text__;
367    background: none !important;
368}
369
370/* =========================================================================
371   Reply form + new-annotation form
372   ========================================================================= */
373
374.ann-reply-form,
375.ann-new-form {
376    padding: 0.65em 0.85em;
377}
378
379.ann-new-form {
380    margin: 0.75em 0 1em;
381    border: 1px solid __border__;
382    border-left: 3px solid ~"rgba(var(--ann-open-rgb), 0.7)";
383    background: __background_alt__;
384    contain: layout;
385}
386
387.ann-body-input {
388    display: block;
389    width: 100%;
390    box-sizing: border-box;
391    padding: 0.4em 0.6em;
392    font: inherit;
393    border: 1px solid __border__;
394    background: __background__;
395    color: __text__;
396    resize: vertical;
397    margin-bottom: 0.65em;
398}
399
400.ann-body-input:focus {
401    outline: 1px solid __link__;
402}
403
404.ann-form-row {
405    display: flex;
406    gap: 0.4em;
407}
408
409.ann-form-row + .ann-quote {
410    margin-top: 0.65em;
411}
412
413/* =========================================================================
414   Selection tooltip
415   ========================================================================= */
416
417.ann-tooltip {
418    position: absolute;
419    z-index: 9000;
420    border-radius: 4px;
421    box-shadow: 0 2px 8px rgba(0,0,0,0.15);
422}
423
424/* Static amber glow drawing the eye to the freshly-revealed "Annotate" button.
425   The rgba() is LESS-escaped (~"…") so DokuWiki's CSS compiler passes it through
426   verbatim instead of evaluating var() to black; the browser resolves the
427   custom property at render time. FF78 ESR safe. */
428.ann-tooltip .ann-btn {
429    box-shadow: 0 0 8px 2px ~"rgba(var(--ann-open-rgb), 0.45)", 0 1px 4px rgba(0,0,0,0.25);
430}
431
432/* =========================================================================
433   Orphan drawer
434   ========================================================================= */
435
436.ann-orphan-drawer {
437    display: flow-root; /* BFC: shrinks to avoid overlapping the floated TOC */
438    margin-bottom: 0.65em;
439    padding: 0.75em 1em;
440    border: 1px dashed __border__;
441    border-radius: 4px;
442    background: __background_alt__;
443}
444
445.ann-orphan-drawer h4 {
446    margin: 0 0 0.3em;
447    font-size: 0.95em;
448    color: __text__;
449}
450
451.ann-orphan-note {
452    font-size: 0.85em;
453    color: __text__;
454    margin-bottom: 0.75em;
455}
456
457/* =========================================================================
458   Resolved annotation panels: shift to green accent
459   ========================================================================= */
460
461.ann-panel[data-status="resolved"] {
462    border-left-color: ~"rgba(var(--ann-resolved-rgb), 0.6)";
463}
464