Skip to content

Commit f928844

Browse files
committed
Frontend: Refactor lightbox theme colors and chip-selector tokens photoprism#4966
- Centralize sidebar/PhotoSwipe caption link styling via new `.text-html` class in `text.css`; drops the previous `info`-token overload. - Swap lightbox `surface-variant` ↔ `on-surface-variant` so Vuetify tooltips render dark natively (remove `.v-tooltip` override). Lift `on-surface` to `#f9fafb`, `primary` to `#F2F3F3`, `highlight` to `#424041`. - Add cross-theme `add` color token to `ui.js` (dark `#3a6e73`, light `#cce3de`); register `add` and `remove` in Vuetify color variations. - Rename chip-selector classes from color-named to action-named (`chip--add` / `chip--remove` / `chip--default` plus `--mixed` partial variants); double selectors (`.chip.chip--*`) to win over the UA `[role="button"] { color: inherit }` reset. - Tune `remove` saturation in `ui.js`, `default`, and `lavender` themes. - Bind face-marker chrome in `meta.css` to theme tokens: default rect + removing rect → `on-surface` / `remove`; Cancel pill → `button`; Remove pill → `remove`; pill text → `on-surface`. - Remove the hardcoded `menuBgColor` literal in `lightbox.vue` and the redundant `.meta-caption a:hover` rule in `lightbox.css`. - Update chip-selector vitest for renamed classes; Prettier auto-format in `views.css` and `vuetify.css`.
1 parent 7ed0fc4 commit f928844

12 files changed

Lines changed: 143 additions & 135 deletions

File tree

frontend/src/component/input/chip-selector.vue

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -156,13 +156,13 @@ export default {
156156
}
157157
158158
if (item.action === "add") {
159-
classes.push(item.mixed ? `${baseClass}--green-light` : `${baseClass}--green`);
159+
classes.push(item.mixed ? `${baseClass}--add-mixed` : `${baseClass}--add`);
160160
} else if (item.action === "remove") {
161-
classes.push(item.mixed ? `${baseClass}--red-light` : `${baseClass}--red`);
161+
classes.push(item.mixed ? `${baseClass}--remove-mixed` : `${baseClass}--remove`);
162162
} else if (item.mixed) {
163-
classes.push(`${baseClass}--gray-light`);
163+
classes.push(`${baseClass}--default-mixed`);
164164
} else {
165-
classes.push(`${baseClass}--gray`);
165+
classes.push(`${baseClass}--default`);
166166
}
167167
168168
return classes;

frontend/src/component/lightbox.vue

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,6 @@ export default {
188188
sidebarVisible: shouldShowSidebar(),
189189
hideCaption: shouldHideCaption() || shouldShowSidebar(),
190190
menuElement: null,
191-
menuBgColor: "#252525",
192191
menuVisible: false,
193192
lightbox: null, // Current PhotoSwipe lightbox instance.
194193
captionPlugin: null, // Current PhotoSwipe caption plugin instance.

frontend/src/component/lightbox/sidebar.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
@blur="onInlineFieldBlur"
6464
></v-textarea>
6565
<!-- eslint-disable-next-line vue/no-v-html -- captionHtml is encode-then-sanitized via $util.sanitizeHtml($util.encodeHTML(raw)); see captionHtml() computed -->
66-
<div v-else-if="model.Caption" class="text-body-2 meta-caption meta-scrollable" v-html="captionHtml"></div>
66+
<div v-else-if="model.Caption" class="text-body-2 meta-caption meta-scrollable text-html" v-html="captionHtml"></div>
6767
<div v-else class="meta-add-prompt" @click.stop="startEditing('caption')">{{ $gettext("Add a Caption") }}</div>
6868
<template v-if="isEditable" #append>
6969
<p-lightbox-sidebar-toolbar
@@ -429,7 +429,7 @@
429429
@blur="onInlineFieldBlur"
430430
></v-textarea>
431431
<!-- eslint-disable-next-line vue/no-v-html -- f.htmlValue points at a sanitized computed (e.g. notesHtml) which runs $util.sanitizeHtml($util.encodeHTML(raw)). -->
432-
<div v-else-if="f.display === 'html' && fieldHtml(f)" class="text-body-2 meta-scrollable" :class="`meta-${f.key}`" v-html="fieldHtml(f)"></div>
432+
<div v-else-if="f.display === 'html' && fieldHtml(f)" class="text-body-2 meta-scrollable text-html" :class="`meta-${f.key}`" v-html="fieldHtml(f)"></div>
433433
<div v-else-if="f.display !== 'html' && f.read(photo)" class="text-body-2 meta-scrollable" :class="`meta-${f.key}`">{{ f.read(photo) }}</div>
434434
<div v-else class="meta-add-prompt" @click.stop="startEditing(f.key)">{{ f.placeholder ? f.placeholder : f.label }}</div>
435435
<template v-if="isEditable" #append>

frontend/src/css/chip-selector.css

Lines changed: 72 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,112 +1,117 @@
11
/* Chip Selector Styles */
22
.chip-selector {
3-
display: flex;
4-
flex-direction: column;
5-
gap: 12px;
3+
display: flex;
4+
flex-direction: column;
5+
gap: 12px;
66
}
77

88
.chip-selector__title {
9-
font-weight: 500;
10-
font-size: 14px;
11-
color: rgba(var(--v-theme-on-surface), 0.87);
12-
margin-bottom: 8px;
9+
font-weight: 500;
10+
font-size: 14px;
11+
color: rgba(var(--v-theme-on-surface), 0.87);
12+
margin-bottom: 8px;
1313
}
1414

1515
.chip-selector__chips {
16-
display: flex;
17-
flex-wrap: wrap;
18-
gap: 8px;
19-
min-height: 40px;
20-
align-items: flex-start;
16+
display: flex;
17+
flex-wrap: wrap;
18+
gap: 8px;
19+
min-height: 40px;
20+
align-items: flex-start;
2121
}
2222

2323
.chip-selector__input-container {
24-
margin-top: 8px;
24+
margin-top: 8px;
2525
}
2626

2727
.chip-selector__input {
28-
width: 100%;
28+
width: 100%;
2929
}
3030

3131
/* Chip Styles */
3232
.chip {
33-
display: inline-flex;
34-
align-items: center;
35-
padding: 6px 12px;
36-
border-radius: 16px;
37-
font-size: 13px;
38-
font-weight: 500;
39-
cursor: pointer;
40-
user-select: none;
41-
min-height: 32px;
42-
transition: all 0.2s ease;
33+
display: inline-flex;
34+
align-items: center;
35+
padding: 6px 12px;
36+
border-radius: 16px;
37+
font-size: 13px;
38+
font-weight: 500;
39+
cursor: pointer;
40+
user-select: none;
41+
min-height: 32px;
42+
transition: all 0.2s ease;
4343
}
4444

4545
.chip__content {
46-
display: flex;
47-
align-items: center;
48-
gap: 6px;
46+
display: flex;
47+
align-items: center;
48+
gap: 6px;
4949
}
5050

5151
.chip__icon {
52-
font-size: 16px;
53-
opacity: 0.9;
52+
font-size: 16px;
53+
opacity: 0.9;
5454
}
5555

56-
/* Chip States */
57-
.chip--gray {
58-
background-color: rgb(var(--v-theme-secondary-light, var(--v-theme-surface-bright, var(--v-theme-surface))));
59-
color: rgb(var(--v-theme-on-surface));
60-
box-shadow: inset 0 0 0 1px rgba(var(--v-theme-on-surface), 0.12);
56+
/* Chip States — class names mirror the `item.action` value the chip-selector
57+
emits (`add` / `remove` / default), and `--mixed` is the partial-selection
58+
variant (action applies to only some of the selected items). Selectors are
59+
doubled (`.chip.chip--*`) so the `color` rules out-rank the UA-level
60+
`[role="button"] { color: inherit }` reset, which would otherwise strip the
61+
on-token foreground from a solid chip rendered as a button. */
62+
.chip.chip--default {
63+
background-color: rgb(var(--v-theme-secondary-light, var(--v-theme-surface-bright, var(--v-theme-surface))));
64+
color: rgb(var(--v-theme-on-surface));
65+
box-shadow: inset 0 0 0 1px rgba(var(--v-theme-on-surface), 0.12);
6166
}
6267

63-
.chip--gray-light {
64-
background-color: rgba(var(--v-theme-secondary-light, var(--v-theme-surface-bright, var(--v-theme-surface))), 0.55);
65-
color: rgb(var(--v-theme-on-surface));
66-
box-shadow: inset 0 0 0 1px rgba(var(--v-theme-on-surface), 0.12);
68+
.chip.chip--default-mixed {
69+
background-color: rgba(var(--v-theme-secondary-light, var(--v-theme-surface-bright, var(--v-theme-surface))), 0.55);
70+
color: rgb(var(--v-theme-on-surface));
71+
box-shadow: inset 0 0 0 1px rgba(var(--v-theme-on-surface), 0.12);
6772
}
6873

69-
.chip--green {
70-
background-color: rgb(var(--v-theme-download));
71-
color: rgb(var(--v-theme-on-download));
74+
.chip.chip--add {
75+
background-color: rgba(var(--v-theme-add), 0.8);
76+
color: rgb(var(--v-theme-on-add));
7277
}
7378

74-
.chip--green-light {
75-
background-color: rgba(var(--v-theme-download), 0.3);
76-
color: rgb(var(--v-theme-download));
79+
.chip.chip--add-mixed {
80+
background-color: rgba(var(--v-theme-add-darken-1), 0.45);
81+
color: rgb(var(--v-theme-on-add));
7782
}
7883

79-
.chip--red {
80-
background-color: rgb(var(--v-theme-error));
81-
color: rgb(var(--v-theme-on-error));
84+
.chip.chip--remove {
85+
background-color: rgba(var(--v-theme-remove), 0.8);
86+
color: rgb(var(--v-theme-on-remove));
8287
}
8388

84-
.chip--red-light {
85-
background-color: rgba(var(--v-theme-error), 0.3);
86-
color: rgb(var(--v-theme-error));
89+
.chip.chip--remove-mixed {
90+
background-color: rgba(var(--v-theme-remove-darken-1), 0.45);
91+
color: rgb(var(--v-theme-on-remove));
8792
}
8893

8994
/* Empty state */
9095
.chip-selector__empty {
91-
padding: 16px;
92-
text-align: center;
93-
color: rgba(var(--v-theme-on-surface), 0.6);
94-
font-style: italic;
96+
padding: 16px;
97+
text-align: center;
98+
color: rgba(var(--v-theme-on-surface), 0.6);
99+
font-style: italic;
95100
}
96101

97102
/* Mobile optimizations */
98103
@media (max-width: 600px) {
99-
.chip-selector__chips {
100-
gap: 6px;
101-
}
102-
103-
.chip {
104-
padding: 4px 8px;
105-
font-size: 12px;
106-
min-height: 28px;
107-
}
108-
109-
.chip__icon {
110-
font-size: 14px;
111-
}
104+
.chip-selector__chips {
105+
gap: 6px;
106+
}
107+
108+
.chip {
109+
padding: 4px 8px;
110+
font-size: 12px;
111+
min-height: 28px;
112+
}
113+
114+
.chip__icon {
115+
font-size: 14px;
116+
}
112117
}

frontend/src/css/lightbox.css

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -177,17 +177,6 @@
177177
text-align: start;
178178
}
179179

180-
.p-lightbox__container > .p-lightbox__sidebar .meta-caption a,
181-
.p-lightbox__container > .p-lightbox__sidebar .meta-notes a {
182-
text-decoration: underline;
183-
word-break: break-all;
184-
}
185-
186-
.p-lightbox__container > .p-lightbox__sidebar .meta-caption a:hover,
187-
.p-lightbox__container > .p-lightbox__sidebar .meta-notes a:hover {
188-
text-decoration: none;
189-
}
190-
191180
.p-lightbox__container > .p-lightbox__sidebar .meta-chip {
192181
display: inline-flex;
193182
align-items: center;
@@ -398,14 +387,6 @@
398387
line-height: 1.125rem;
399388
}
400389

401-
/* Flip surface/on-surface-variant for lightbox tooltips so they read as a
402-
dark popover instead of a near-white block over the dark sidebar.
403-
Tooltips are body-teleported, so the theme class lands on the popper itself. */
404-
.v-tooltip.v-theme--lightbox > .v-overlay__content {
405-
background: rgb(var(--v-theme-on-surface-variant));
406-
color: rgb(var(--v-theme-surface-variant));
407-
}
408-
409390
/* Media Content */
410391

411392
.pswp__content {
@@ -670,20 +651,6 @@
670651
touch-action: none;
671652
}
672653

673-
.pswp__dynamic-caption a {
674-
color: inherit;
675-
text-decoration: underline;
676-
white-space: normal;
677-
overflow-wrap: normal;
678-
word-wrap: normal;
679-
word-break: break-word;
680-
text-align: start;
681-
text-overflow: ellipsis;
682-
hyphens: auto;
683-
pointer-events: auto;
684-
touch-action: auto;
685-
}
686-
687654
.pswp__dynamic-caption h4 {
688655
font-weight: 600;
689656
word-wrap: normal;

frontend/src/css/meta.css

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@
3939
}
4040

4141
.p-meta-face-markers__rect {
42-
fill: rgba(255, 255, 255, 0.06);
43-
stroke: rgba(255, 255, 255, 0.9);
42+
fill: rgba(var(--v-theme-on-surface), 0.06);
43+
stroke: rgba(var(--v-theme-on-surface), 0.9);
4444
stroke-width: 2px;
4545
vector-effect: non-scaling-stroke;
4646
pointer-events: none;
@@ -62,8 +62,8 @@
6262
the inline remove-confirm pill is anchored below it. Red stroke
6363
plus a faint red fill makes the destructive intent obvious. */
6464
.p-meta-face-markers__rect--removing {
65-
stroke: rgba(244, 67, 54, 0.95);
66-
fill: rgba(244, 67, 54, 0.16);
65+
stroke: rgba(var(--v-theme-remove), 0.95);
66+
fill: rgba(var(--v-theme-remove), 0.16);
6767
stroke-width: 2.5px;
6868
}
6969

@@ -114,7 +114,7 @@
114114
justify-content: center;
115115
cursor: pointer;
116116
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.45);
117-
color: #fff;
117+
color: rgb(var(--v-theme-on-surface));
118118
transition: transform 0.08s ease-out;
119119
}
120120

@@ -127,11 +127,11 @@
127127
}
128128

129129
.p-meta-face-markers__btn--cancel {
130-
background: rgba(90, 90, 90, 0.95);
130+
background: rgba(var(--v-theme-button), 0.95);
131131
}
132132

133133
.p-meta-face-markers__btn--remove {
134-
background: rgba(211, 47, 47, 0.95);
134+
background: rgba(var(--v-theme-remove), 0.95);
135135
}
136136

137137
/* Back button — anchored top-left in LTR / top-right in RTL with its own

frontend/src/css/text.css

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,13 +153,38 @@ code,
153153
.text-link,
154154
p a.text-link {
155155
color: rgb(var(--v-theme-primary, #ffffff));
156-
text-decoration: none;
157156
}
158157

158+
.text-html a,
159+
.pswp__dynamic-caption a,
159160
.p-page a,
160161
.p-page footer .text-link,
161162
.p-page footer .body-link {
162163
color: rgb(var(--v-theme-on-surface, #ffffff));
164+
}
165+
166+
.text-html a,
167+
.pswp__dynamic-caption a {
168+
text-decoration-thickness: 1px;
169+
text-decoration: underline;
170+
word-break: break-all;
171+
white-space: normal;
172+
overflow-wrap: normal;
173+
word-wrap: normal;
174+
text-align: start;
175+
text-overflow: ellipsis;
176+
hyphens: auto;
177+
pointer-events: auto;
178+
touch-action: auto;
179+
}
180+
181+
.text-html a:hover,
182+
.pswp__dynamic-caption a:hover,
183+
.text-link,
184+
p a.text-link,
185+
.p-page a,
186+
.p-page footer .text-link,
187+
.p-page footer .body-link {
163188
text-decoration: none;
164189
}
165190

frontend/src/css/views.css

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -297,8 +297,8 @@
297297
}
298298

299299
.search-results.list-view .v-table > .v-table__wrapper > table > tbody > tr > td.result .preview,
300-
.search-results.list-view .v-table>.v-table__wrapper>table>tbody>tr>td.result .preview,
301-
.edit-batch.list-view .v-table>table>tbody>tr>td.result .preview {
300+
.search-results.list-view .v-table > .v-table__wrapper > table > tbody > tr > td.result .preview,
301+
.edit-batch.list-view .v-table > table > tbody > tr > td.result .preview {
302302
width: 50px;
303303
height: 50px;
304304
}

0 commit comments

Comments
 (0)