@visns-studio/visns-components 6.6.3 → 6.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1658 -18
- package/package.json +10 -4
- package/src/components/DataGrid.jsx +619 -158
- package/src/components/Fetch.jsx +80 -3
- package/src/components/Form.jsx +9 -0
- package/src/components/Navigation.jsx +109 -50
- package/src/components/Notification.jsx +279 -9
- package/src/components/TableFilter.jsx +14 -1
- package/src/components/auth/AuthBrandPanel.jsx +43 -0
- package/src/components/auth/AuthLoading.jsx +78 -0
- package/src/components/auth/AuthShell.jsx +59 -0
- package/src/components/auth/ClientAuth.jsx +161 -0
- package/src/components/auth/ClientAuthFrame.jsx +59 -0
- package/src/components/auth/ClientLogin.jsx +266 -55
- package/src/components/auth/ClientOTPVerify.jsx +587 -115
- package/src/components/auth/ImpersonateGate.jsx +254 -0
- package/src/components/auth/Login.jsx +134 -41
- package/src/components/auth/LogoutScreen.jsx +112 -0
- package/src/components/auth/Reset.jsx +134 -77
- package/src/components/auth/TwoFactorAuth.jsx +475 -297
- package/src/components/auth/Verify.jsx +237 -126
- package/src/components/auth/authEndpoints.js +105 -0
- package/src/components/auth/authFont.js +23 -0
- package/src/components/auth/authHelpers.js +465 -0
- package/src/components/auth/clientAuthProtocols.js +240 -0
- package/src/components/auth/useOptionalRouter.js +49 -0
- package/src/components/callQueue/CallQueuePop.jsx +1502 -0
- package/src/components/callQueue/CallQueueSettings.jsx +508 -0
- package/src/components/callQueue/callQueueHelpers.js +280 -0
- package/src/components/callQueue/callQueueSettingsHelpers.js +75 -0
- package/src/components/columns/AutoGrowCell.jsx +141 -0
- package/src/components/columns/ColumnRenderers.jsx +53 -7
- package/src/components/generic/GenericAuth.jsx +163 -96
- package/src/components/generic/GenericDetail.jsx +215 -90
- package/src/components/generic/GenericIndex.jsx +20 -47
- package/src/components/generic/GenericMain.jsx +5 -0
- package/src/components/generic/GroupedReportRenderer.jsx +1 -5
- package/src/components/generic/StandardModal.jsx +17 -5
- package/src/components/generic/reportSemanticSteps/SemanticEntityStep.jsx +1 -6
- package/src/components/generic/reportSemanticSteps/SemanticFieldsStep.jsx +4 -11
- package/src/components/generic/reportSemanticSteps/SemanticFiltersStep.jsx +9 -27
- package/src/components/generic/reportSemanticSteps/SemanticGroupingStep.jsx +5 -13
- package/src/components/generic/reportSemanticSteps/SemanticParameterPrompt.jsx +5 -13
- package/src/components/generic/reportSemanticSteps/SemanticPreviewStep.jsx +14 -31
- package/src/components/generic/reportSemanticSteps/SemanticRelationsStep.jsx +3 -9
- package/src/components/generic/reportSemanticSteps/SemanticValueInput.jsx +2 -15
- package/src/components/navActive.js +60 -0
- package/src/components/notify/desktopNotifications.js +256 -0
- package/src/components/sketch/SketchField.jsx +12 -2
- package/src/components/sms/SmsComposeModal.jsx +495 -0
- package/src/components/sms/SmsInbox.jsx +596 -0
- package/src/components/sms/SmsInboxBadge.jsx +468 -0
- package/src/components/sms/SmsLineSettings.jsx +854 -0
- package/src/components/sms/SmsThreadPanel.jsx +1184 -0
- package/src/components/sms/smsEndpoints.js +56 -0
- package/src/components/sms/smsHelpers.js +1075 -0
- package/src/components/sms/smsLiveState.js +132 -0
- package/src/components/sms/useSmsLive.js +307 -0
- package/src/components/styles/CallQueuePop.module.scss +646 -0
- package/src/components/styles/CallQueueSettings.module.scss +460 -0
- package/src/components/styles/ClientAuth.module.scss +229 -0
- package/src/components/styles/DataGrid.module.scss +10 -0
- package/src/components/styles/GenericDetail.module.scss +153 -26
- package/src/components/styles/GenericIndex.module.scss +13 -0
- package/src/components/styles/GenericMain.module.scss +19 -2
- package/src/components/styles/ImpersonateGate.module.scss +85 -0
- package/src/components/styles/Login.module.scss +13 -122
- package/src/components/styles/LogoutScreen.module.scss +89 -0
- package/src/components/styles/Navigation.module.scss +509 -223
- package/src/components/styles/Notification.module.scss +103 -0
- package/src/components/styles/Reset.module.scss +52 -186
- package/src/components/styles/Sms.module.scss +2382 -0
- package/src/components/styles/TableFilter.module.scss +118 -98
- package/src/components/styles/TwoFactorAuth.module.scss +115 -176
- package/src/components/styles/Vault.module.scss +1983 -0
- package/src/components/styles/Verify.module.scss +57 -180
- package/src/components/styles/_authBrandPanel.scss +163 -0
- package/src/components/styles/_authShell.scss +369 -0
- package/src/components/styles/global-datagrid.css +29 -0
- package/src/components/utils/buildEnv.js +67 -0
- package/src/components/utils/displayValue.js +94 -0
- package/src/components/utils/useDensity.js +345 -9
- package/src/components/vault/OtpChip.jsx +208 -0
- package/src/components/vault/PasswordGenerator.jsx +145 -0
- package/src/components/vault/QrScanner.jsx +326 -0
- package/src/components/vault/VaultAccessLog.jsx +225 -0
- package/src/components/vault/VaultConfirmPanel.jsx +113 -0
- package/src/components/vault/VaultEntryForm.jsx +740 -0
- package/src/components/vault/VaultManager.jsx +1000 -0
- package/src/components/vault/VaultQuickSearch.jsx +681 -0
- package/src/components/vault/qrDecode.js +257 -0
- package/src/components/vault/useDebouncedValue.js +22 -0
- package/src/components/vault/useVaultReveal.js +160 -0
- package/src/components/vault/vaultClipboard.js +33 -0
- package/src/components/vault/vaultEndpoints.js +40 -0
- package/src/components/vault/vaultFit.js +116 -0
- package/src/components/vault/vaultHelpers.js +547 -0
- package/src/components/vault/vaultNavigation.jsx +58 -0
- package/src/components/vault/vaultOtp.js +105 -0
- package/src/index.js +233 -1
|
@@ -8,6 +8,14 @@
|
|
|
8
8
|
.btn {
|
|
9
9
|
@include button-primary;
|
|
10
10
|
}
|
|
11
|
+
|
|
12
|
+
/* The secondary half of the pair. A detail page's action bar is one primary
|
|
13
|
+
action and, when editing, one way back out of it — the way out was painted
|
|
14
|
+
in error red, the loudest colour in the palette, sitting next to the Save it
|
|
15
|
+
competes with. */
|
|
16
|
+
.btnSecondary {
|
|
17
|
+
@include button-secondary;
|
|
18
|
+
}
|
|
11
19
|
.grid {
|
|
12
20
|
width: 100%;
|
|
13
21
|
display: flex;
|
|
@@ -86,6 +94,29 @@
|
|
|
86
94
|
gap: 0.5rem;
|
|
87
95
|
}
|
|
88
96
|
|
|
97
|
+
/* `page.tabLayout: "underline"` — tabs above the panel rather than beside it.
|
|
98
|
+
See the note on `tabsUnderlined` in GenericDetail.jsx for when a page wants
|
|
99
|
+
this. The row becomes a column and both children take the full width; the
|
|
100
|
+
rail gives up its card, because an underline strip is not a panel. */
|
|
101
|
+
.grid__subrow--stacked {
|
|
102
|
+
flex-direction: column;
|
|
103
|
+
flex-wrap: nowrap;
|
|
104
|
+
gap: 0;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.grid__subnav--underline {
|
|
108
|
+
flex: 0 0 auto;
|
|
109
|
+
width: 100%;
|
|
110
|
+
padding: 0;
|
|
111
|
+
margin: 8px 0 0;
|
|
112
|
+
background: none;
|
|
113
|
+
border: none;
|
|
114
|
+
border-radius: 0;
|
|
115
|
+
/* The one hairline under the whole strip, which the selected tab's accent
|
|
116
|
+
edge sits on top of. The strip's own items draw no border. */
|
|
117
|
+
border-bottom: 1px solid var(--border-color, #e1e1e1);
|
|
118
|
+
}
|
|
119
|
+
|
|
89
120
|
/* The tab rail. Identical spec to `.grid__subnav` in GenericIndex.module.scss
|
|
90
121
|
— the two pages share one rail, so keep them in step.
|
|
91
122
|
|
|
@@ -224,7 +255,14 @@
|
|
|
224
255
|
|
|
225
256
|
.gridtxt__header {
|
|
226
257
|
width: 100%;
|
|
227
|
-
|
|
258
|
+
/* Flex so an optional right-hand meta line (`section.meta`) can sit on the
|
|
259
|
+
heading's baseline at the far edge — the card-header shape used across
|
|
260
|
+
the dashboards: title left, quiet facts right. */
|
|
261
|
+
display: flex;
|
|
262
|
+
align-items: baseline;
|
|
263
|
+
justify-content: space-between;
|
|
264
|
+
gap: var(--spacing-md, 1rem);
|
|
265
|
+
flex-wrap: wrap;
|
|
228
266
|
background: none;
|
|
229
267
|
box-sizing: border-box;
|
|
230
268
|
/* Was a centred, tinted bar. A section heading is read at the start of
|
|
@@ -239,16 +277,36 @@
|
|
|
239
277
|
border-bottom: 1px solid var(--border-color, #e1e1e1);
|
|
240
278
|
border-radius: 0;
|
|
241
279
|
margin-bottom: var(--spacing-md, 1rem);
|
|
242
|
-
|
|
280
|
+
/* Promoted from a 0.8rem uppercase eyebrow to a plain 0.9rem heading in
|
|
281
|
+
the page's ink. The eyebrow treatment now belongs to the field labels
|
|
282
|
+
inside the card (`.overviewLabel`), and two levels of hierarchy cannot
|
|
283
|
+
both be small-caps-muted-0.8rem without collapsing into one. */
|
|
284
|
+
font-size: var(--font-size-md, 0.9rem);
|
|
243
285
|
font-weight: var(--font-weight-semibold, 600);
|
|
244
|
-
letter-spacing: 0
|
|
245
|
-
text-transform:
|
|
286
|
+
letter-spacing: 0;
|
|
287
|
+
text-transform: none;
|
|
246
288
|
text-align: left;
|
|
247
|
-
color: var(--
|
|
289
|
+
color: var(--heading-color, var(--paragraph-color, #221e33));
|
|
248
290
|
}
|
|
249
291
|
|
|
250
|
-
.gridtxt__header span {
|
|
251
|
-
font-weight:
|
|
292
|
+
.gridtxt__header > span:first-child {
|
|
293
|
+
font-weight: var(--font-weight-semibold, 600);
|
|
294
|
+
min-width: 0;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/* The quiet facts on the right of a card header — a count, a last-updated
|
|
298
|
+
stamp. Deliberately weaker than the heading it shares a line with: it
|
|
299
|
+
describes the card, it does not name it. */
|
|
300
|
+
.gridtxt__meta {
|
|
301
|
+
display: inline-flex;
|
|
302
|
+
align-items: baseline;
|
|
303
|
+
flex-wrap: wrap;
|
|
304
|
+
gap: 0 var(--spacing-md, 1rem);
|
|
305
|
+
font-size: var(--font-size-sm, 0.8rem);
|
|
306
|
+
font-weight: var(--font-weight-regular, 400);
|
|
307
|
+
letter-spacing: 0;
|
|
308
|
+
text-transform: none;
|
|
309
|
+
color: var(--muted-color, rgba(var(--paragraph-color-rgb, 34, 30, 51), 0.6));
|
|
252
310
|
}
|
|
253
311
|
|
|
254
312
|
/* Optional one-line subtitle under a section heading (GenericDetail renders it
|
|
@@ -321,31 +379,97 @@
|
|
|
321
379
|
}
|
|
322
380
|
}
|
|
323
381
|
|
|
324
|
-
.
|
|
382
|
+
/* The overview field list.
|
|
383
|
+
|
|
384
|
+
Every field used to be its own bordered, tinted box, so a summary of six
|
|
385
|
+
facts arrived as six competing containers and the card around them counted
|
|
386
|
+
for nothing. The card is the container; the fields inside it are label/value
|
|
387
|
+
pairs on the card's own surface, laid on a two-column grid so the second
|
|
388
|
+
column's labels line up with the first's instead of drifting with whatever
|
|
389
|
+
the row above happened to be tall enough to need.
|
|
390
|
+
|
|
391
|
+
Selectors are `.gridtxt > ul.customer__overview` rather than
|
|
392
|
+
`.customer__overview`, because `.gridtxt > ul li` above is more specific
|
|
393
|
+
than a single class and would otherwise keep the hairline box. */
|
|
394
|
+
.gridtxt > ul.customer__overview {
|
|
325
395
|
width: 100%;
|
|
326
|
-
display:
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
396
|
+
display: grid;
|
|
397
|
+
grid-template-columns: repeat(
|
|
398
|
+
var(--detail-overview-columns, 2),
|
|
399
|
+
minmax(0, 1fr)
|
|
400
|
+
);
|
|
401
|
+
gap: var(--spacing-md, 1rem) var(--spacing-xl, 1.75rem);
|
|
330
402
|
list-style: none;
|
|
331
403
|
box-sizing: border-box;
|
|
332
|
-
padding:
|
|
404
|
+
padding: 0.35rem 0 0.85rem;
|
|
333
405
|
margin: 0;
|
|
334
406
|
|
|
335
407
|
li {
|
|
336
|
-
width:
|
|
337
|
-
|
|
408
|
+
width: auto;
|
|
409
|
+
min-width: 0;
|
|
410
|
+
display: flex;
|
|
411
|
+
flex-direction: column;
|
|
412
|
+
gap: 0.15rem;
|
|
413
|
+
padding: 0;
|
|
414
|
+
margin: 0;
|
|
338
415
|
box-sizing: border-box;
|
|
339
|
-
border:
|
|
416
|
+
border: none;
|
|
417
|
+
border-radius: 0;
|
|
418
|
+
background: none;
|
|
340
419
|
color: var(--paragraph-color);
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
border-radius: 5px;
|
|
420
|
+
font-size: 0.85rem;
|
|
421
|
+
line-height: 1.45;
|
|
344
422
|
|
|
345
423
|
p {
|
|
346
424
|
font-size: 0.85rem;
|
|
347
425
|
}
|
|
348
426
|
}
|
|
427
|
+
|
|
428
|
+
/* `size: "full"` fields — a description, a note — run the width of the
|
|
429
|
+
card rather than being squeezed into one column. */
|
|
430
|
+
.fw-grid-item {
|
|
431
|
+
grid-column: 1 / -1;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
@media (max-width: 759px) {
|
|
436
|
+
.gridtxt > ul.customer__overview {
|
|
437
|
+
grid-template-columns: minmax(0, 1fr);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/* The label above a value. The eyebrow treatment the section heading used to
|
|
442
|
+
wear: small, muted, letter-spaced — enough to read as a label and not enough
|
|
443
|
+
to compete with the value under it. */
|
|
444
|
+
.overviewLabel {
|
|
445
|
+
display: block;
|
|
446
|
+
font-size: var(--font-size-xs, 0.72rem);
|
|
447
|
+
font-weight: var(--font-weight-semibold, 600);
|
|
448
|
+
letter-spacing: 0.06em;
|
|
449
|
+
text-transform: uppercase;
|
|
450
|
+
line-height: 1.3;
|
|
451
|
+
color: var(--muted-color, rgba(var(--paragraph-color-rgb, 34, 30, 51), 0.6));
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
.overviewValue {
|
|
455
|
+
display: block;
|
|
456
|
+
min-width: 0;
|
|
457
|
+
font-size: 0.85rem;
|
|
458
|
+
line-height: 1.5;
|
|
459
|
+
color: var(--paragraph-color, #221e33);
|
|
460
|
+
/* A long unbroken value (a URL, a reference) must not push the grid
|
|
461
|
+
column wider than its share. */
|
|
462
|
+
overflow-wrap: anywhere;
|
|
463
|
+
|
|
464
|
+
a {
|
|
465
|
+
color: var(--primary-color);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/* "Not set", said out loud. A label followed by nothing reads as a page that
|
|
470
|
+
failed to load rather than as a field nobody has filled in. */
|
|
471
|
+
.overviewEmpty {
|
|
472
|
+
color: var(--muted-color, rgba(var(--paragraph-color-rgb, 34, 30, 51), 0.45));
|
|
349
473
|
}
|
|
350
474
|
|
|
351
475
|
.progress {
|
|
@@ -577,18 +701,21 @@
|
|
|
577
701
|
}
|
|
578
702
|
}
|
|
579
703
|
|
|
704
|
+
/* The floating action bar. It stays pinned bottom-right — every detail page
|
|
705
|
+
in every project is laid out around it — but its buttons are the shared
|
|
706
|
+
controls now: `display: block; float: left` overrode the button mixin's own
|
|
707
|
+
inline-flex centring, which is why an icon and its label sat off-centre, and
|
|
708
|
+
the 0.35em padding undercut the shared control height so these buttons were
|
|
709
|
+
visibly shorter than every other button in the app. */
|
|
580
710
|
.polActions {
|
|
581
711
|
position: fixed;
|
|
582
712
|
bottom: 15px;
|
|
583
713
|
right: 20px;
|
|
584
714
|
width: auto;
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
float: left;
|
|
590
|
-
padding: 0.35em 1em;
|
|
591
|
-
margin: 0 0.15em;
|
|
715
|
+
display: flex;
|
|
716
|
+
align-items: center;
|
|
717
|
+
gap: var(--spacing-sm, 0.5rem);
|
|
718
|
+
z-index: 20;
|
|
592
719
|
}
|
|
593
720
|
|
|
594
721
|
.oppoverview {
|
|
@@ -112,6 +112,12 @@
|
|
|
112
112
|
flex-wrap: wrap;
|
|
113
113
|
height: auto;
|
|
114
114
|
gap: 0.5rem;
|
|
115
|
+
/* Not `stretch`. The grid card is the one that decides how tall this row
|
|
116
|
+
is — it has measured itself against the window. A tab rail with more
|
|
117
|
+
entries than that is taller than the window allows, and stretching the
|
|
118
|
+
grid card to match it pushes the pager below the fold. The rail scrolls
|
|
119
|
+
inside itself instead (below). */
|
|
120
|
+
align-items: flex-start;
|
|
115
121
|
}
|
|
116
122
|
|
|
117
123
|
/* The tab rail. Identical spec to `.grid__subnav` in GenericDetail.module.scss
|
|
@@ -126,6 +132,13 @@
|
|
|
126
132
|
padding: var(--spacing-xs, 0.25rem);
|
|
127
133
|
margin: 8px 0;
|
|
128
134
|
height: auto;
|
|
135
|
+
/* A long rail scrolls itself rather than the page. The offset is the app
|
|
136
|
+
chrome stacked above it; a consuming project with a different shell can
|
|
137
|
+
retune it with `--index-subnav-offset`. It only decides where the rail
|
|
138
|
+
starts scrolling, so it does not have to be exact. */
|
|
139
|
+
max-height: calc(100vh - var(--index-subnav-offset, 200px));
|
|
140
|
+
overflow-y: auto;
|
|
141
|
+
overscroll-behavior: contain;
|
|
129
142
|
box-shadow: none;
|
|
130
143
|
}
|
|
131
144
|
|
|
@@ -64,8 +64,19 @@
|
|
|
64
64
|
— the density hook for the bar's own height — belongs here rather than
|
|
65
65
|
in Navigation.module.scss, where the rest of the set is documented. */
|
|
66
66
|
padding: var(--nav-bar-padding, var(--spacing-sm) var(--spacing-md));
|
|
67
|
-
/*
|
|
68
|
-
|
|
67
|
+
/* Where the bar stops. Without it the navy ran straight into the page's
|
|
68
|
+
off-white with nothing to mark the edge, which is most of why the bar
|
|
69
|
+
read as flat. */
|
|
70
|
+
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
|
|
71
|
+
/* Separates the bar from the page when content scrolls under it.
|
|
72
|
+
`var(--shadow-sm)` carried no fallback, so in a project that does not
|
|
73
|
+
declare that token the declaration was invalid and thrown away — there
|
|
74
|
+
was no shadow at all. */
|
|
75
|
+
box-shadow: var(
|
|
76
|
+
--shadow-sm,
|
|
77
|
+
0 1px 2px rgba(0, 0, 0, 0.12),
|
|
78
|
+
0 6px 16px rgba(0, 0, 0, 0.06)
|
|
79
|
+
);
|
|
69
80
|
}
|
|
70
81
|
|
|
71
82
|
.headerAlternate {
|
|
@@ -76,6 +87,12 @@
|
|
|
76
87
|
width: 100%;
|
|
77
88
|
z-index: 995;
|
|
78
89
|
padding: var(--nav-bar-padding, 0.35rem 1rem);
|
|
90
|
+
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
|
|
91
|
+
box-shadow: var(
|
|
92
|
+
--shadow-sm,
|
|
93
|
+
0 1px 2px rgba(0, 0, 0, 0.06),
|
|
94
|
+
0 6px 16px rgba(0, 0, 0, 0.04)
|
|
95
|
+
);
|
|
79
96
|
}
|
|
80
97
|
|
|
81
98
|
.content {
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Impersonation hand-off.
|
|
3
|
+
*
|
|
4
|
+
* Ported from the Next.js portal's styles/impersonate.css. Two states, one
|
|
5
|
+
* card: the spinner while the token is being validated, and the error card if
|
|
6
|
+
* it is not accepted. Colours come from the consuming app's token layer with
|
|
7
|
+
* the portal's own values as fallbacks, so it looks the same there and
|
|
8
|
+
* sensible everywhere else.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
.loading,
|
|
12
|
+
.error {
|
|
13
|
+
display: flex;
|
|
14
|
+
justify-content: center;
|
|
15
|
+
align-items: center;
|
|
16
|
+
min-height: 100vh;
|
|
17
|
+
background-color: var(--background-color, #f8f9fa);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.card {
|
|
21
|
+
width: 100%;
|
|
22
|
+
max-width: 500px;
|
|
23
|
+
padding: 2rem;
|
|
24
|
+
border-radius: 10px;
|
|
25
|
+
background-color: var(--surface-color, #fff);
|
|
26
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
|
27
|
+
text-align: center;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.title {
|
|
31
|
+
margin: 0 0 1.5rem;
|
|
32
|
+
color: var(--primary-color, #0276ba);
|
|
33
|
+
font-size: clamp(1.5rem, 2.5vw, 2rem);
|
|
34
|
+
font-weight: 600;
|
|
35
|
+
line-height: 1.25;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.message {
|
|
39
|
+
margin: 0 0 1.5rem;
|
|
40
|
+
color: var(--paragraph-color, #58585a);
|
|
41
|
+
line-height: 1.5;
|
|
42
|
+
/* An error message can arrive from the server as one long unbroken token
|
|
43
|
+
(a JWT complaint, a URL); it must not push the card wider than the
|
|
44
|
+
viewport. */
|
|
45
|
+
overflow-wrap: anywhere;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.spinner {
|
|
49
|
+
display: inline-block;
|
|
50
|
+
width: 50px;
|
|
51
|
+
height: 50px;
|
|
52
|
+
border: 5px solid rgba(2, 118, 186, 0.2);
|
|
53
|
+
border-top-color: var(--primary-color, #0276ba);
|
|
54
|
+
border-radius: 50%;
|
|
55
|
+
animation: impersonate-spin 1s ease-in-out infinite;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.button {
|
|
59
|
+
padding: 0.75rem 1.5rem;
|
|
60
|
+
border: none;
|
|
61
|
+
border-radius: 10px;
|
|
62
|
+
background-color: var(--primary-color, #0276ba);
|
|
63
|
+
color: #fff;
|
|
64
|
+
font-family: inherit;
|
|
65
|
+
font-size: 1rem;
|
|
66
|
+
text-decoration: none;
|
|
67
|
+
cursor: pointer;
|
|
68
|
+
transition: background-color 0.3s;
|
|
69
|
+
|
|
70
|
+
&:hover {
|
|
71
|
+
background-color: var(--primary-color-darker, #025d94);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@keyframes impersonate-spin {
|
|
76
|
+
to {
|
|
77
|
+
transform: rotate(360deg);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@media (prefers-reduced-motion: reduce) {
|
|
82
|
+
.spinner {
|
|
83
|
+
animation-duration: 3s;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
* @fontsource, so there is no webfont request to fail.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
+
@use './authBrandPanel' as brand;
|
|
15
|
+
|
|
14
16
|
.auth {
|
|
15
17
|
/* Local aliases only — every brand value comes from the consuming app's
|
|
16
18
|
token layer so this library stays reusable. The fallbacks are neutral
|
|
@@ -32,7 +34,11 @@
|
|
|
32
34
|
// photograph the larger share makes it the subject and the form the task.
|
|
33
35
|
grid-template-columns: 52fr 48fr;
|
|
34
36
|
background: var(--paper);
|
|
35
|
-
|
|
37
|
+
// Read from a token first, so an app can put its own face on the whole
|
|
38
|
+
// auth journey without forking this file. 'Barlow' itself is imported by
|
|
39
|
+
// the screens (see auth/authFont.js) — declaring it here without loading
|
|
40
|
+
// it anywhere was why this screen quietly fell back to system-ui.
|
|
41
|
+
font-family: var(--auth-font-family, 'Barlow', system-ui, sans-serif);
|
|
36
42
|
color: var(--ink);
|
|
37
43
|
|
|
38
44
|
@media (max-width: 900px) {
|
|
@@ -42,115 +48,10 @@
|
|
|
42
48
|
|
|
43
49
|
/* --- Brand panel --------------------------------------------------------- */
|
|
44
50
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
background-position: center 62%;
|
|
50
|
-
overflow: hidden;
|
|
51
|
-
|
|
52
|
-
// A scrim anchored to the bottom-left, where the copy sits — not a flat
|
|
53
|
-
// wash over the whole image, which would dull the dusk light that makes
|
|
54
|
-
// the photograph worth using.
|
|
55
|
-
//
|
|
56
|
-
// The foot is a near-solid plate rather than a fade. The brand line has to
|
|
57
|
-
// hold against whatever the photograph is doing behind it, and this one is
|
|
58
|
-
// bright exactly where the type sits, which left the brand line washing
|
|
59
|
-
// into the photograph. Held high — 0.88 still at 18%, which is where the ink
|
|
60
|
-
// block ends — so the whole block reads on one ground, then released
|
|
61
|
-
// quickly to 0.06 at the top, so the sky above is still a photograph and
|
|
62
|
-
// not a tint.
|
|
63
|
-
//
|
|
64
|
-
// Shared verbatim with the supervisor PWA
|
|
65
|
-
// (prime-web-nextjs/app/login/Login.module.scss). If one moves, move both.
|
|
66
|
-
&::after {
|
|
67
|
-
content: '';
|
|
68
|
-
position: absolute;
|
|
69
|
-
inset: 0;
|
|
70
|
-
background:
|
|
71
|
-
linear-gradient(
|
|
72
|
-
to top,
|
|
73
|
-
rgba(16, 25, 42, 0.94) 0%,
|
|
74
|
-
rgba(16, 25, 42, 0.88) 18%,
|
|
75
|
-
rgba(16, 25, 42, 0.62) 42%,
|
|
76
|
-
rgba(16, 25, 42, 0.22) 70%,
|
|
77
|
-
rgba(16, 25, 42, 0.06) 100%
|
|
78
|
-
),
|
|
79
|
-
linear-gradient(
|
|
80
|
-
to right,
|
|
81
|
-
rgba(16, 25, 42, 0.5) 0%,
|
|
82
|
-
rgba(16, 25, 42, 0) 55%
|
|
83
|
-
);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
@media (max-width: 900px) {
|
|
87
|
-
display: none;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
.brandInk {
|
|
92
|
-
position: absolute;
|
|
93
|
-
left: clamp(2rem, 4vw, 3.5rem);
|
|
94
|
-
right: clamp(2rem, 4vw, 3.5rem);
|
|
95
|
-
bottom: clamp(2rem, 5vh, 3.5rem);
|
|
96
|
-
z-index: 1;
|
|
97
|
-
color: #fff;
|
|
98
|
-
animation: rise 0.7s cubic-bezier(0.2, 0.7, 0.3, 1) both;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
.eyebrow {
|
|
102
|
-
margin: 0 0 0.75rem;
|
|
103
|
-
font-size: 0.72rem;
|
|
104
|
-
font-weight: 700;
|
|
105
|
-
letter-spacing: 0.22em;
|
|
106
|
-
// Stated, not left to the cascade: global.css sets
|
|
107
|
-
// `p { line-height: var(--para-height) }`, which is 1.6 here. That put the
|
|
108
|
-
// kicker on a taller line than the same kicker in the PWA, so the red dash
|
|
109
|
-
// sat at a different height and the gap beneath it read as wider.
|
|
110
|
-
line-height: 1.4;
|
|
111
|
-
text-transform: uppercase;
|
|
112
|
-
color: rgba(255, 255, 255, 0.75);
|
|
113
|
-
// The single use of the logo's red in the whole screen.
|
|
114
|
-
padding-left: 2.1rem;
|
|
115
|
-
position: relative;
|
|
116
|
-
|
|
117
|
-
&::before {
|
|
118
|
-
content: '';
|
|
119
|
-
position: absolute;
|
|
120
|
-
left: 0;
|
|
121
|
-
top: 50%;
|
|
122
|
-
width: 1.5rem;
|
|
123
|
-
height: 2px;
|
|
124
|
-
background: var(--flag);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
.brandLine {
|
|
129
|
-
margin: 0 0 0.6rem;
|
|
130
|
-
font-size: clamp(2.4rem, 4.2vw, 3.6rem);
|
|
131
|
-
font-weight: 700;
|
|
132
|
-
line-height: 1;
|
|
133
|
-
// Tight tracking at display size — Barlow opens up as it scales.
|
|
134
|
-
letter-spacing: -0.02em;
|
|
135
|
-
// Stated rather than inherited from .brandInk: this is the one line on the
|
|
136
|
-
// screen that has to be white, and finding that out should not mean
|
|
137
|
-
// reading the parent.
|
|
138
|
-
color: #fff;
|
|
139
|
-
text-wrap: balance;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
.brandSub {
|
|
143
|
-
margin: 0;
|
|
144
|
-
max-width: 34ch;
|
|
145
|
-
font-size: 1rem;
|
|
146
|
-
font-weight: 300;
|
|
147
|
-
line-height: 1.5;
|
|
148
|
-
// No-op against this app's globals, but the PWA's own `p` rule tracks body
|
|
149
|
-
// copy at 0.015rem, so the value is pinned in both files rather than left
|
|
150
|
-
// to whichever global layer the screen happens to be rendered under.
|
|
151
|
-
letter-spacing: 0;
|
|
152
|
-
color: rgba(255, 255, 255, 0.82);
|
|
153
|
-
}
|
|
51
|
+
// Shared with AuthShell and the client portal's split layout — see
|
|
52
|
+
// _authBrandPanel.scss. It was copied into each of them before 6.8.3, which is
|
|
53
|
+
// the arrangement where one copy quietly stops matching the others.
|
|
54
|
+
@include brand.auth-brand-panel(hide);
|
|
154
55
|
|
|
155
56
|
/* --- Form panel ---------------------------------------------------------- */
|
|
156
57
|
|
|
@@ -493,18 +394,8 @@
|
|
|
493
394
|
color: var(--muted);
|
|
494
395
|
}
|
|
495
396
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
@keyframes rise {
|
|
499
|
-
from {
|
|
500
|
-
opacity: 0;
|
|
501
|
-
transform: translateY(0.75rem);
|
|
502
|
-
}
|
|
503
|
-
to {
|
|
504
|
-
opacity: 1;
|
|
505
|
-
transform: none;
|
|
506
|
-
}
|
|
507
|
-
}
|
|
397
|
+
// One entrance, staggered between the two panels — see _authBrandPanel.scss.
|
|
398
|
+
@include brand.auth-rise-keyframes;
|
|
508
399
|
|
|
509
400
|
@media (prefers-reduced-motion: reduce) {
|
|
510
401
|
.brandInk,
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sign out.
|
|
3
|
+
*
|
|
4
|
+
* Ported from the Next.js portal's styles/logout-page.css. One card, one
|
|
5
|
+
* spinner, one and a half seconds — long enough to read "logging out" and be
|
|
6
|
+
* sure it happened, short enough not to feel like a wait.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
.page {
|
|
10
|
+
display: flex;
|
|
11
|
+
align-items: center;
|
|
12
|
+
justify-content: center;
|
|
13
|
+
min-height: 60vh;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.card {
|
|
17
|
+
max-width: 400px;
|
|
18
|
+
margin: 0 auto;
|
|
19
|
+
padding: 3rem;
|
|
20
|
+
border-radius: 10px;
|
|
21
|
+
background-color: var(--surface-color, #fff);
|
|
22
|
+
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.05);
|
|
23
|
+
text-align: center;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.icon {
|
|
27
|
+
display: flex;
|
|
28
|
+
align-items: center;
|
|
29
|
+
justify-content: center;
|
|
30
|
+
width: 80px;
|
|
31
|
+
height: 80px;
|
|
32
|
+
margin: 0 auto 1.5rem;
|
|
33
|
+
border-radius: 50%;
|
|
34
|
+
background-color: rgba(2, 118, 186, 0.1);
|
|
35
|
+
color: var(--primary-color, #0276ba);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.title {
|
|
39
|
+
margin: 0 0 0.5rem;
|
|
40
|
+
color: var(--primary-color, #0276ba);
|
|
41
|
+
font-size: 1.8rem;
|
|
42
|
+
font-weight: 600;
|
|
43
|
+
line-height: 1.2;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.message {
|
|
47
|
+
margin: 0;
|
|
48
|
+
color: var(--paragraph-color, #58585a);
|
|
49
|
+
font-size: 1rem;
|
|
50
|
+
line-height: 1.5;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.spinner {
|
|
54
|
+
width: 40px;
|
|
55
|
+
height: 40px;
|
|
56
|
+
margin: 2rem auto 0;
|
|
57
|
+
border: 3px solid rgba(2, 118, 186, 0.2);
|
|
58
|
+
border-top-color: var(--primary-color, #0276ba);
|
|
59
|
+
border-radius: 50%;
|
|
60
|
+
animation: logout-spin 1s linear infinite;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@keyframes logout-spin {
|
|
64
|
+
to {
|
|
65
|
+
transform: rotate(360deg);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
@media (max-width: 576px) {
|
|
70
|
+
.card {
|
|
71
|
+
margin: 0 1rem;
|
|
72
|
+
padding: 2rem;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.icon {
|
|
76
|
+
width: 60px;
|
|
77
|
+
height: 60px;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.title {
|
|
81
|
+
font-size: 1.5rem;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
@media (prefers-reduced-motion: reduce) {
|
|
86
|
+
.spinner {
|
|
87
|
+
animation-duration: 3s;
|
|
88
|
+
}
|
|
89
|
+
}
|